pax_global_header00006660000000000000000000000064136243500450014514gustar00rootroot0000000000000052 comment=116a1bb8f7a7bd5bf728ee8f15ace6b4a56b4d96 fotoxx-20.08/000077500000000000000000000000001362435004500130545ustar00rootroot00000000000000fotoxx-20.08/Makefile000066400000000000000000000103441362435004500145160ustar00rootroot00000000000000# fotoxx Makefile # # export CXX=g++ gnu compiler # export CXX=clang++ clang compiler # export DEBUG=x debug build with address checking ifeq ($(CXX), clang++) ifdef DEBUG CXXFLAGS += -Wall -g -O0 -fsanitize=address LDFLAGS += -fsanitize=address else CXXFLAGS += -Wall -g -O2 endif else ifdef DEBUG CXXFLAGS += -Wall -g -rdynamic -O0 -fsanitize=address -Wno-format-truncation LDFLAGS += -fsanitize=address else CXXFLAGS += -Wall -g -rdynamic -O2 -Wno-format-truncation endif endif PREFIX ?= /usr PKG_CONFIG ?= pkg-config # target install folders BINDIR = $(PREFIX)/bin SHAREDIR = $(PREFIX)/share/fotoxx DATADIR = $(SHAREDIR)/data ICONDIR = $(SHAREDIR)/icons IMAGEDIR = $(SHAREDIR)/images LOCALESDIR = $(SHAREDIR)/locales DOCDIR = $(PREFIX)/share/doc/fotoxx MANDIR = $(PREFIX)/share/man/man1 APPDATADIR = $(PREFIX)/share/appdata MENUFILE = $(PREFIX)/share/applications/fotoxx.desktop CFLAGS = $(CXXFLAGS) $(CPPFLAGS) -c \ `$(PKG_CONFIG) --cflags gtk+-3.0` \ -I/usr/include/clutter-1.0/ \ -I/usr/include/cogl/ \ -I/usr/include/json-glib-1.0/ \ -I/usr/include/clutter-gtk-1.0/ \ -I/usr/include/champlain-gtk-0.12/ \ -I/usr/include/champlain-0.12/ \ -I/usr/include/libchamplain-gtk-0.12/ \ -I/usr/include/libchamplain-0.12/ LIBS = `$(PKG_CONFIG) --libs gtk+-3.0` \ -lrt -lpthread -llcms2 -ltiff -lpng -ljpeg \ -lclutter-1.0 -lclutter-gtk-1.0 -lchamplain-0.12 -lchamplain-gtk-0.12 ALLFILES = fotoxx.o f.widgets.o f.file.o f.gallery.o f.albums.o f.area.o f.meta.o \ f.edit.o f.enhance.o f.effects.o f.warp.o f.combine.o f.mashup.o \ f.tools.o f.process.o f.pixmap.o zfuncs.o fotoxx: $(ALLFILES) $(CXX) $(LDFLAGS) $(ALLFILES) $(LIBS) -o fotoxx \ fotoxx.o: fotoxx.cc fotoxx.h $(CXX) fotoxx.cc $(CFLAGS) -o fotoxx.o \ f.widgets.o: f.widgets.cc fotoxx.h $(CXX) f.widgets.cc $(CFLAGS) \ f.file.o: f.file.cc fotoxx.h $(CXX) f.file.cc $(CFLAGS) \ f.gallery.o: f.gallery.cc fotoxx.h $(CXX) f.gallery.cc $(CFLAGS) \ f.albums.o: f.albums.cc fotoxx.h $(CXX) f.albums.cc $(CFLAGS) \ f.area.o: f.area.cc fotoxx.h $(CXX) f.area.cc $(CFLAGS) \ f.meta.o: f.meta.cc fotoxx.h $(CXX) f.meta.cc $(CFLAGS) \ f.edit.o: f.edit.cc fotoxx.h $(CXX) f.edit.cc $(CFLAGS) \ f.enhance.o: f.enhance.cc fotoxx.h $(CXX) f.enhance.cc $(CFLAGS) \ f.effects.o: f.effects.cc fotoxx.h $(CXX) f.effects.cc $(CFLAGS) \ f.warp.o: f.warp.cc fotoxx.h $(CXX) f.warp.cc $(CFLAGS) \ f.combine.o: f.combine.cc fotoxx.h $(CXX) f.combine.cc $(CFLAGS) \ f.mashup.o: f.mashup.cc fotoxx.h $(CXX) f.mashup.cc $(CFLAGS) \ f.tools.o: f.tools.cc fotoxx.h $(CXX) f.tools.cc $(CFLAGS) \ f.process.o: f.process.cc fotoxx.h $(CXX) f.process.cc $(CFLAGS) \ f.pixmap.o: f.pixmap.cc fotoxx.h $(CXX) f.pixmap.cc $(CFLAGS) \ zfuncs.o: zfuncs.cc zfuncs.h $(CXX) zfuncs.cc $(CFLAGS) \ ### -D DOCDIR=\"$(DOCDIR)\" SUSE only \ install: fotoxx uninstall mkdir -p $(DESTDIR)$(BINDIR) mkdir -p $(DESTDIR)$(DATADIR) mkdir -p $(DESTDIR)$(ICONDIR) mkdir -p $(DESTDIR)$(IMAGEDIR) mkdir -p $(DESTDIR)$(LOCALESDIR) mkdir -p $(DESTDIR)$(DOCDIR) mkdir -p $(DESTDIR)$(MANDIR) mkdir -p $(DESTDIR)$(PREFIX)/share/applications mkdir -p $(DESTDIR)$(APPDATADIR) cp -f fotoxx $(DESTDIR)$(BINDIR) cp -f -R data/* $(DESTDIR)$(DATADIR) cp -f -R images/* $(DESTDIR)$(IMAGEDIR) cp -f -R locales/* $(DESTDIR)$(LOCALESDIR) cp -f -R doc/* $(DESTDIR)$(DOCDIR) gzip -f -9 $(DESTDIR)$(DOCDIR)/changelog cp -f -R appdata/* $(DESTDIR)$(APPDATADIR) # man page cp -f doc/fotoxx.man fotoxx.1 gzip -f -9 fotoxx.1 cp fotoxx.1.gz $(DESTDIR)$(MANDIR) rm -f fotoxx.1.gz # menu (desktop) file cp -f fotoxx.desktop $(DESTDIR)$(MENUFILE) cp -f fotoxx.png $(DESTDIR)$(ICONDIR) uninstall: rm -f $(DESTDIR)$(BINDIR)/fotoxx rm -f -R $(DESTDIR)$(LOCALESDIR) 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-20.08/appdata/000077500000000000000000000000001362435004500144665ustar00rootroot00000000000000fotoxx-20.08/appdata/fotoxx.appdata.xml000066400000000000000000000023731362435004500201550ustar00rootroot00000000000000 fotoxx CC0 GPL-3.0+ fotoxx Photo Editor and Collection Manager

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

fotoxx views https://kornelix.net/fotoxx/images/fotoxx_views.jpg https://kornelix.net
fotoxx-20.08/data/000077500000000000000000000000001362435004500137655ustar00rootroot00000000000000fotoxx-20.08/data/KB-shortcuts20000066400000000000000000000011201362435004500163740ustar00rootroot00000000000000+ Zoom+ - Zoom- F File View G Gallery M Map View L List View A Current Album S Source Folder R Rename K KB Shortcuts T Trim/Rotate C Retouch E Upright 2 Cycle 2 3 Cycle 3 V View Meta U Undo Shift+U Redo Ctrl+S Save Ctrl+V New Version Ctrl+H Show Hidden Ctrl+D Done Ctrl+A Apply Ctrl+R Reset fotoxx-20.08/data/README000066400000000000000000000003071362435004500146450ustar00rootroot00000000000000 This folder contains files that are copied into the user's Fotoxx home folder ONLY IF they do not exist. They may be customized by the user. Fotoxx home folder: normally /home//.fotoxx fotoxx-20.08/data/custom_kernel/000077500000000000000000000000001362435004500166375ustar00rootroot00000000000000fotoxx-20.08/data/custom_kernel/blur5000066400000000000000000000006451362435004500176200ustar00rootroot00000000000000kernsize == 5 cell0101 == 1 cell0201 == 2 cell0301 == 3 cell0401 == 2 cell0501 == 1 cell0102 == 1 cell0202 == 2 cell0302 == 4 cell0402 == 2 cell0502 == 1 cell0103 == 2 cell0203 == 3 cell0303 == 9 cell0403 == 3 cell0503 == 2 cell0104 == 1 cell0204 == 2 cell0304 == 4 cell0404 == 2 cell0504 == 1 cell0105 == 1 cell0205 == 2 cell0305 == 3 cell0405 == 2 cell0505 == 1 fmul == 0.0175 fadd == 0 end fotoxx-20.08/data/custom_kernel/brighten3000066400000000000000000000002621362435004500204470ustar00rootroot00000000000000kernsize == 3 cell0101 == 0 cell0201 == 0 cell0301 == 0 cell0102 == 0 cell0202 == 1 cell0302 == 0 cell0103 == 0 cell0203 == 0 cell0303 == 0 fmul == 1.2 fadd == 8 end fotoxx-20.08/data/custom_kernel/color lines000066400000000000000000000002641362435004500207750ustar00rootroot00000000000000kernsize == 3 cell0101 == -2 cell0201 == -2 cell0301 == -1 cell0102 == -1 cell0202 == 0 cell0302 == 1 cell0103 == 1 cell0203 == 2 cell0303 == 2 fmul == 2 fadd == 0 end fotoxx-20.08/data/custom_kernel/colors3000066400000000000000000000002741362435004500201510ustar00rootroot00000000000000kernsize == 3 cell0101 == -1 cell0201 == -1 cell0301 == -1 cell0102 == -1 cell0202 == 9 cell0302 == -1 cell0103 == -1 cell0203 == -1 cell0303 == -1 fmul == 10 fadd == -700 end fotoxx-20.08/data/custom_kernel/darken3000066400000000000000000000002631362435004500201120ustar00rootroot00000000000000kernsize == 3 cell0101 == 0 cell0201 == 0 cell0301 == 0 cell0102 == 0 cell0202 == 1 cell0302 == 0 cell0103 == 0 cell0203 == 0 cell0303 == 0 fmul == 0.8 fadd == -0 end fotoxx-20.08/data/custom_kernel/denoise3000066400000000000000000000002651362435004500202760ustar00rootroot00000000000000kernsize == 3 cell0101 == 1 cell0201 == 2 cell0301 == 1 cell0102 == 2 cell0202 == 4 cell0302 == 2 cell0103 == 1 cell0203 == 2 cell0303 == 1 fmul == 0.0625 fadd == 0 end fotoxx-20.08/data/custom_kernel/emboss3000066400000000000000000000002631362435004500201360ustar00rootroot00000000000000kernsize == 3 cell0101 == 2 cell0201 == 1 cell0301 == 0 cell0102 == 1 cell0202 == 1 cell0302 == -1 cell0103 == 0 cell0203 == -1 cell0303 == -2 fmul == 1 fadd == 0 end fotoxx-20.08/data/custom_kernel/emboss3-B&W000066400000000000000000000002701362435004500205100ustar00rootroot00000000000000kernsize == 3 cell0101 == -2 cell0201 == -2 cell0301 == -1 cell0102 == -1 cell0202 == 0 cell0302 == 1 cell0103 == 1 cell0203 == 2 cell0303 == 2 fmul == 1.5 fadd == 200 end fotoxx-20.08/data/custom_kernel/emboss5000066400000000000000000000006441362435004500201430ustar00rootroot00000000000000kernsize == 5 cell0101 == -1 cell0201 == -1 cell0301 == 0 cell0401 == 0 cell0501 == 0 cell0102 == -1 cell0202 == -1 cell0302 == 0 cell0402 == 0 cell0502 == 0 cell0103 == 0 cell0203 == 0 cell0303 == 1 cell0403 == 0 cell0503 == 0 cell0104 == 0 cell0204 == 0 cell0304 == 0 cell0404 == 1 cell0504 == 1 cell0105 == 0 cell0205 == 0 cell0305 == 0 cell0405 == 1 cell0505 == 1 fmul == 1 fadd == 0 end fotoxx-20.08/data/custom_kernel/emboss5+color000066400000000000000000000006471362435004500212600ustar00rootroot00000000000000kernsize == 5 cell0101 == -1 cell0201 == -1 cell0301 == 0 cell0401 == 0 cell0501 == 0 cell0102 == -1 cell0202 == -1 cell0302 == 0 cell0402 == 0 cell0502 == 0 cell0103 == 0 cell0203 == 0 cell0303 == 1 cell0403 == 0 cell0503 == 0 cell0104 == 0 cell0204 == 0 cell0304 == 0 cell0404 == 1 cell0504 == 1 cell0105 == 0 cell0205 == 0 cell0305 == 0 cell0405 == 1 cell0505 == 1 fmul == 2 fadd == -100 end fotoxx-20.08/data/custom_kernel/last-used000066400000000000000000000006441362435004500204670ustar00rootroot00000000000000kernsize == 5 cell0101 == -1 cell0201 == -1 cell0301 == 0 cell0401 == 0 cell0501 == 0 cell0102 == -1 cell0202 == -1 cell0302 == 0 cell0402 == 0 cell0502 == 0 cell0103 == 0 cell0203 == 0 cell0303 == 1 cell0403 == 0 cell0503 == 0 cell0104 == 0 cell0204 == 0 cell0304 == 0 cell0404 == 1 cell0504 == 1 cell0105 == 0 cell0205 == 0 cell0305 == 0 cell0405 == 1 cell0505 == 1 fmul == 1 fadd == 0 end fotoxx-20.08/data/custom_kernel/outlines3000066400000000000000000000002701362435004500205060ustar00rootroot00000000000000kernsize == 3 cell0101 == -1 cell0201 == -1 cell0301 == -1 cell0102 == -1 cell0202 == 8 cell0302 == -1 cell0103 == -1 cell0203 == -1 cell0303 == -1 fmul == 1 fadd == 0 end fotoxx-20.08/data/custom_kernel/outlines5000066400000000000000000000006711362435004500205150ustar00rootroot00000000000000kernsize == 5 cell0101 == -1 cell0201 == -1 cell0301 == -1 cell0401 == -1 cell0501 == -1 cell0102 == -1 cell0202 == -1 cell0302 == -1 cell0402 == -1 cell0502 == -1 cell0103 == -1 cell0203 == -1 cell0303 == 24 cell0403 == -1 cell0503 == -1 cell0104 == -1 cell0204 == -1 cell0304 == -1 cell0404 == -1 cell0504 == -1 cell0105 == -1 cell0205 == -1 cell0305 == -1 cell0405 == -1 cell0505 == -1 fmul == 1 fadd == 0 end fotoxx-20.08/data/custom_kernel/shake3000066400000000000000000000002661362435004500177440ustar00rootroot00000000000000kernsize == 3 cell0101 == 2 cell0201 == 2 cell0301 == 2 cell0102 == 2 cell0202 == -12 cell0302 == 2 cell0103 == 2 cell0203 == 2 cell0303 == 2 fmul == 0.333 fadd == 0 end fotoxx-20.08/data/custom_kernel/sharpen3000066400000000000000000000002751362435004500203110ustar00rootroot00000000000000kernsize == 3 cell0101 == -1 cell0201 == -1 cell0301 == -1 cell0102 == -1 cell0202 == 16 cell0302 == -1 cell0103 == -1 cell0203 == -1 cell0303 == -1 fmul == 0.125 fadd == 0 end fotoxx-20.08/data/custom_kernel/sharpen5000066400000000000000000000006761362435004500203200ustar00rootroot00000000000000kernsize == 5 cell0101 == -1 cell0201 == -1 cell0301 == -1 cell0401 == -1 cell0501 == -1 cell0102 == -1 cell0202 == -1 cell0302 == -1 cell0402 == -1 cell0502 == -1 cell0103 == -1 cell0203 == -1 cell0303 == 40 cell0403 == -1 cell0503 == -1 cell0104 == -1 cell0204 == -1 cell0304 == -1 cell0404 == -1 cell0504 == -1 cell0105 == -1 cell0205 == -1 cell0305 == -1 cell0405 == -1 cell0505 == -1 fmul == 0.0625 fadd == 0 end fotoxx-20.08/data/custom_kernel/sketch3000066400000000000000000000002721362435004500201270ustar00rootroot00000000000000kernsize == 3 cell0101 == -1 cell0201 == -1 cell0301 == -1 cell0102 == -1 cell0202 == 8 cell0302 == -1 cell0103 == -1 cell0203 == -1 cell0303 == -1 fmul == 6 fadd == 300 end fotoxx-20.08/data/favorites/000077500000000000000000000000001362435004500157675ustar00rootroot00000000000000fotoxx-20.08/data/favorites/000.png000066400000000000000000000017501362435004500167770ustar00rootroot00000000000000PNG  IHDR((/:sBITOIDATXkA&kbJRABj--!DPSЃŻ?BP/#Hj0hARMM5ΌTlMp%{}o01Hīa\ B̀ST D"kr]7gϐ-Ex6/@b99Yq~=K-[jXӴRv* BHV2eH`m?F6;:hT~x ֭s[jdw] b4/ߺſ}Ԛ]>}bd2®]!5E K+ 9q{WsIR]mUq~5&&7Bg'ӯ]͛"NR###՘Jt: ! dB8cl~En|nN[X,In!p.1@) ۷<B~`p!4+JYZ,RKzB7J0W J]w2-p OO#/+W Ξa٪NSQf3!$HJtwեS<|LRUߡPi~,cccUxK\@בӉ\.@H>yRڿ$\.7N'ɅQ3DD6?E<~ܜ6_;7l>_uǖeȪv8vnXQ 9+ar`dY ;Isfj*,_咎aB=~`1)5<ѯ^?[ ^y3gxh@|z|C"*XQݻǏ4>_lq}?]Ep ҁd`mx-R.BH?CIENDB`fotoxx-20.08/data/favorites/001.png000066400000000000000000000023761362435004500170050ustar00rootroot00000000000000PNG  IHDR szzsBIT|dIDATXOUΕ-"K@ElTcb4i|>j&IL[F qCcJ6K);3LJJIg9s~ N>UU3 p_^LMMM9<<Q!If_$I.jM099s6=8I󤽽}mۭ\nRlPT<۶W1M!,b߾}/83555`6&&&~mmm=J{w!wnjiiysvvT*Bm mжm~;!mmm+r}yy9|ťD"!W}mL7oN>`}}cڶ$i}Rimnny4T1 ,;vH,˂rf]ו%)0 ~u/H0[( xY' ( zt2*iEtttdt]w] KLSQMpt:b7nZYYY AxBaq#TU}d [\*_ܾ}{R|u]eYgieΝ;hYցRtZ(H&~]H$<ٳgə3g "x~ ՐT0p#.\ĉ, >H?N($bX,v<|6OH|Y4xaFŲ/DBE<& T*E$ITejYE[x=.BTBAi\ni8+[DeFA$<Ӻ:*$,aB_j "j(BA SJ>tbccҊ387 !lvvsfL.ql&I2P>ollnn@h4ZTTΎ kލ;I"QV  VJ( 1@ P64֤vm``0::ddN.//~}P $q'7BB1P( {p&& IENDB`fotoxx-20.08/data/favorites/003.png000066400000000000000000000042371362435004500170050ustar00rootroot00000000000000PNG  IHDR ǍsBIT|dVIDATHMnFvybF;fd3irnE^X)kͅd (Ov_;ZC///ض88x<3b&vbIV+|Gj" C%RJ 0 }zwvvf/+8Cf|z%NOOA-ZO缾98g>^}4Mt:f3\%2fq3988DZ~zzoR2Ϲ% CRL&  lۦ( Y8UUQU{{{huEhX.# }0`ZyE//cJ) $IZQJyDQiZ\]]<(xxx I{R![y}-ˢ,K0$"(`v~GǤiZWMI99y^`ض] .6㘪ڜ4&MS(FJY#,*N<<>&s5,Y,Z684M>} exy!enՊ$IjgPkjBk bnKQ8UlJgX5xzzq{{777(%''"dZ1OX,iﹾ&B:6>(j~ CFNǍl"W4 }앗zR6}CJIQee)Y1L|>'c%_fZX,1N'(vż|AѨg&JI|߶a3E9v0n=WlpMzzzfna|5,K44>o}5}gJ'_לPU۶0,d4@c8b1Nspp@YP# !68D䄯_`0d:pttliE1m#6la-&eqR7:M|E髫+:J))R*^E),c4GjǏ^hZضMUU\\\.;;;ݱ7Z|h4lZDZ1 V~!eYՙp3_aH>.Fqvw-h4, ,Ƕ-\׭,"MӍ&\ש~/ۗKZW"ZQF$Y!B^OJTaheEEx 5qqqq2gϿIENDB`fotoxx-20.08/data/favorites/004.png000066400000000000000000000040611362435004500170010ustar00rootroot00000000000000PNG  IHDR sBITOIDATHV]LSB EAS:X2ZA+(0V\bD wAJ&qn$;*h`h mPhzx\{8I]߷NOOo! `,\.WGGI'::Z*fffTп|A0|'O:;;)  $ID"#D__}T[[p8IQ۷o2 <::h6'''E"ѭ[,((/Bŋ##JeYYYAرCxXL&biii W^=y$ ܾ}̙3@sȑRXbZ5D"tttw^fg/^T()))kj_^^jmmST'Np:"(=== հ0/e2lV*lЧO^pAVGGG_Bsر⢢Ba"""\.] HRfKII E1 80'//[=TTXRRrrfff! d2].~ᘞNHH% E}&` rپ⢃|>B!kwaaR,..NJJB*++M&VEfff8al͟[S~ނ\H kjGݺu afffTTIj]3::VFLg Fq͗.]JRnK0liiٳgׯ_x~f"#####9vcׯPqqa}}N7nY$`nn"""JJJ&&&jkkB pcW^j4vܙdZi.//_]]w`Xf=z0[>?44dX@"pYIIIj|ϗr4FA~Ν;Zjz< jii r4a `b?߼ywzzh4RE ٳg˖`0(޽k.0ӧOhtܛ6rt:l 8_vk݆Ào0 c˖-þ}pdž؎0Bl6] ZP(B L&SGGI!:88H+ǎ`0BR|~QQ6nht>|xSSSJp~?B׻~㉊bp8w[YYinn6 n•+Wء!$I\. hnmmmiix<_ t ð!i]OV*G211aǏ!n{~~?p||iBhllͶmJ,33r޿hoozeee|>b<{MuOO"399t:aL&Ǐu<<omA?x<;;at:ZN'SSS>yeeɓbSN"LNN666YVp8RN'j4g0Lcc֭F3G0cAIENDB`fotoxx-20.08/data/favorites/005.png000066400000000000000000000023621362435004500170040ustar00rootroot00000000000000PNG  IHDR szzsBIT|dIDATXŗmLe39Hl-a1d(SojrKs%Ul>87[X`6Y( A3H휫99 s_}]ϳ~ FnˁL`&0=* xAʁ ЛPaT]HTQyd#D N' ^ΜB>p8F닑Î@P񑑆s(c<@Hɟzro vG/F'q%&sІu(3N247 :ݎv@sI&^Dm-JtaO|74~ϴo%C,٩r0y54'9 S|eǦi"t:f}_$\3mvnn0?4-myŏ:3x: "+3p;Z6luYvKb)40Dz+k~}1~0h`-`~5}gDܩQTwp|"C[7i2H2EqqLX<0 : *| $.L uACW%ZSgtVeK`ܸհGd^7|^ /,ђpWkßzZYhaɲ߲eSa =s(0Tm˲kajb_0 PSvEO.]O N8X4+s8t7-ⵡfx0:O86>)fGھchB75@kvC'O D}TK'T;Ϋ݁ 6ׯ?*䭭ƴrkG^\.ר\.2{/UЍǨ%n(--Yo݄N] p:ں)7s 7$v21yiiRfj[$$A)D)f?\Ia+Ba+++BpUS\Ct4HP~>Ġb*HOO')) Z8zŔl,ag{x x8r/,|<?_l,<0̛11?a pWuHKle3ȕg~IENDB`fotoxx-20.08/data/favorites/006.png000066400000000000000000000017341362435004500170070ustar00rootroot00000000000000PNG  IHDR sBITOIDATHVMO*=n;N,h#n\` K EWBQ&Laz>IӧO[qDZmqιitZ|S뺶m p8dd21u`D]5MS0 ? D!xa$zpppvv40jpBEӤ(FT*5S> vyh4g,˺]u !$mll۶]8"!WWWz]bu:4M@|˲j7 e 4myy9aEkY|B\ȫIԇLDh|0ƊI$-BĠ~@B 0bLo"q3T8`!m{jcB!"ιaBju !iJ9P$ d2;;;mFaa2Ƃ (/IBժmnFUjjZ1BTUv:p1fB)5M3B!,XB(b⯶usrf)½=՛"06=>> PJKu ȲlLY@@$!0}~Fs~\}(}W "˲z^n!f!шs>?$(zt:ҭl6K)%!4 !(l6|?B`f՛_ 2LRYZZx(TJ$ T*]'beMf!T*1!`㵵 +biHZױȟGa >FIENDB`fotoxx-20.08/data/favorites/007.png000066400000000000000000000040621362435004500170050ustar00rootroot00000000000000PNG  IHDR sBITOIDATHVoWe.{=_b;:6I@TEQ*T@B} H ^@BH-Whh Ui6M\'M6-kzgg̜ÚMoaF]c Bg3& [uU6"@Bzɓ4IzceI%a+y㝢ݲ=92 ƠU'u^}crq1TN౾'v?]~91N>_׳8@{l)>~gFOrR.w\ܹlRt)G׮LMҥ(*KK+T !ܩvbߩB!N{MW__Z\'_ɍ%q l.J۷5G6ҳ'f+TJ !,]ס2._v巧\^i~w"B)eh<|s]xMgս`RJ9Jɹ]6DdAK['^it#D$8X ɔWFݥBIfJP@ TBQJEq+ ܸ˺ePhjժeYR)u,w(g`-{RDQB)emfVNG pDT8ᕏ*nTEX-B6җIyeMI"V}D<11X{VK?Ç##Q)Axk2CJ!hF͕Uӣ,U!Mn/9"PHMuHC1_(v._l NO)B}rgt9?LTnPCbKuX`@8$^ ;0fvW5HDish=بB:DHךlԟmDǏ ɇ=^ |k'LJ$=XnzܼJ ] wlŷ0[ۜTJulDEł别㭻oB9{q9< g3hmx軌cWYXQ"1w׿]x1\0\ q3 :GÕ'+giN͢|{Ǔ^o*pJe3I}ݺ*D\/>"Z&ESt>ֿo?g$D=:rCktOq/P&ˠ ן\=;IM 97#8{'~Ju.\ Վ0\C$kWÊZƵ_]ߌk jVs33 s/ĨS71\k(Q۔*­%'c rMj4Hp#Q#GÐ|0 $nj;5p(}/?3ԺTInǎj?;tf&\rq#}=d\y}ݏ"+ apY X(E=,RLvd8n2&p߮S%EթXF. .yFtg\Gj$rMÏf._~eY͠(wa(Oݺ]+4i{s5t- V&]]-=W FbwnQY|}laV(nFgXYͯpa @ d$?e89ܦ䥥vOvt(+E.и'W7(/FX_dJp@TY(nf$~UU qnKɠ!Lg*LSYz_9Qk\⋫>/"(̱`> Gt\]B%iּpĹlo]T i#XnlEJ_J4YLz@ ʂP3Rʢpc 2 B$kM;RppH+15J2<<<7v޴gV F8v%>M>goo׿I.`)E"NE9mؙ7% Ө߭ӓcs6Bhjw`BoH6jf4C)=a{Rq,ڝ hEgP`@"Kzh/fW!f/Nl`:IfyQ k^TkѸ/ !EּX…kwN B")ZOKB lP+A V`iu k+KJt!tb( =1f^lx } م+7zfms̕gи P_*}/ݸL4ZB*n9A+ 0*prg XȁJr]Gr 1_'71?_pbOvO~o?O@2dGe7x?|NkmwX)3G',doC^Giz+u(8yg݃"'ǃ H@`g<-˃]nD2CZ+dRY4+beQqOf{2L\x޹`&xOz,J҈ȘQ,f_g) L"ZbQ)=5<FH-ʍܾV%Ӊ.O\W:46g1/g9-d.3O'fЂUՊit4HhEZ#E SAy8B1 #50@b$!G&tos9:oɲi8$ #iZӂ b,K'PXgrn;ײ2RJlٲ٩'yb[&w:K YjUaaxs9UUﺵf9+ͻyfGCu)KX|@ )&+|ZVVVSRR2n3!tnUeehF̆2Bh{3"Yxq͛FQCUU/H]]t:-⺮sY(0 9T񙧌JwVH$b_O?]Xhɶ[6k]dɒj~?c`0(VVVWTT(s4G DF~ B_7CL29lXܺޞMnz_MM3f̸1˲`&@$^aCQBp8PPP]?A}}=  n6lX8TUFE1weEE$80 aEA(B,!e!\,+s*'a+PTXS"g&vR|^ziߔ)S|@ F8|0 1uTB@(At]Rr9O=UV]xr]ՠ:N'^/l6ɤnݺ+VEQꊎ*D]QH۝l+f\-Ih>Ղ';3IZ@Qk 5j0M$vsBd2gd彨4f+_R g;,x*z"JWր2W50w ~ΦJ⍺{xaٽ77S#>ł/|?Ǘ^X;`etE8E0uRRL<'LfgA"> tEfqT;Xz: K3A  HR0 #p82|O?g:y4"vDX@4">T?u]pD@ÙY98, t]RA)fnGsss˖-1d͸gbTPr7E}6ў/.)M#x<_D:%edR \$Ic H-]oll|oϞ=q߉Gߐwٕ"ڎ[l!&Ub|gϙ6(J  @~s-ݭW\qeϟH$>hE^!H䁰/t.%IlV:hǟ񞾅:(ɔUQH$76Yf/it4HhEZ#E SAy8B1 #50@b$!G&tos9:oɲi8$ #iZӂ b,K'PXgrn;ײ2RJlٲ٩'yb[&w:K YjUaaxs9UUﺵf9+ͻyfGCu)KX|@ )&+|ZVVVSRR2n3!tnUeehF̆2Bh{3"Yxq͛FQCUU/H]]t:-⺮sY(0 9T񙧌JwVH$b_O?]Xhɶ[6k]dɒj~?c`0(VVVWTT(s4G DF~ B_7CL29lXܺޞMnz_MM3f̸1˲`&@$^aCQBp8PPP]?A}}=  n6lX8TUFE1weEE$80 aEA(B,!e!\,+s*'a+PTXS"g&vR|^ziߔ)S|@ F8|0 1uTB@(At]Rr9O=UV]xr]ՠ:N'^/l6ɤnݺ+VEQꊎ*D]QH۝l+f\-Ih>Ղ';3IZ@Qk 5j0M$vsBd2gd彨4f+_R g;,x*z"JWր2W50w ~ΦJ⍺{xaٽ77S#>ł/|?Ǘ^X;`etE8E0uRRL<'LfgA"> tEfqT;Xz: K3A  HR0 #p82|O?g:y4"vDX@4">T?u]pD@ÙY98, t]RA)fnGsss˖-1d͸gbTPr7E}6ў/.)M#x<_D:%edR \$Ic H-]oll|oϞ=q߉Gߐwٕ"ڎ[l!&Ub|gϙ6(J  @~s-ݭW\qeϟH$>hE^!H䁰/t.%IlV:hǟ񞾅:(ɔUQH$76Yf/>tbccҊ387 !lvvsfL.ql&I2P>ollnn@h4ZTTΎ kލ;I"QV  VJ( 1@ P64֤vm``0::ddN.//~}P $q'7BB1P( {p&& IENDB`fotoxx-20.08/data/favorites/menu-config000066400000000000000000000021551362435004500201240ustar00rootroot00000000000000popup 238 91 509 333 posn 100 10 menu trim\nrotate func Trim/Rotate icon 000.png size 40 posn 10 10 menu recent func Recent icon 008.png size 32 posn 100 108 menu retouch func Retouch size 32 posn 200 10 menu edit\nbrightness func Brite Dist icon 011.png size 32 posn 100 150 menu gradients func Gradients posn 410 10 menu sharpen func Sharpen icon 001.png size 32 posn 300 100 menu denoise func Denoise icon 003.png size 32 posn 10 70 menu newest func Newest posn 10 100 menu source\nfolder func Source Folder posn 100 130 menu flatten func Flatten posn 200 100 menu warp\ncurved func Warp curved icon 004.png size 32 posn 310 10 menu red eyes func Red Eyes icon 005.png size 32 posn 410 80 menu meta func Edit Meta icon 010.png size 32 posn 10 160 menu prefs func preferences icon 006.png size 32 posn 410 160 menu favorites\nhelp func help icon 007.png size 32 posn 100 190 menu zonal\nretinex func Zonal Retx posn 200 190 menu Gebogen\nkrümmen func Gebogen krümmen posn 10 260 menu Example menu\narrange your own favorites here bold fotoxx-20.08/data/metadata_short_list000066400000000000000000000006101362435004500177370ustar00rootroot00000000000000Make Model Creator Copyright Comment Description Caption-Abstract User Comment Orientation Flash Flash Mode Focal Length in 35mm Format Shooting Mode Exposure Time F Number Metering Mode Exposure Compensation ISO White Balance Focus Mode Light Source Color Space ICCProfileName Image History Date Time Original Location Country GPS Latitude GPS Longitude Keywords Rating File Size Image Size fotoxx-20.08/data/netmap_locations000066400000000000000000000003331362435004500172460ustar00rootroot00000000000000Brazil|-13.7100|-55.3052|4 China|34.3434|100.9863|4 Europe|48.0634|12.6562|4 France|46.6834|2.6477|6 Germany|50.7260|10.7336|6 Italy|42.8115|12.6068|6 Spain|40.7556|-4.2188|6 UK|53.7942|-4.2352|6 USA|40.2628|-97.9980|4 fotoxx-20.08/data/palettes/000077500000000000000000000000001362435004500156065ustar00rootroot00000000000000fotoxx-20.08/data/palettes/Golden Heavy Body Acrylics.gpl000066400000000000000000000020341362435004500231660ustar00rootroot00000000000000GIMP Palette Name: Golden Heavy Body Acrylics Columns: 10 # From http://sensuallogic.com/paintmaker/artist-colours-to-rgb.html 35 34 36 Bone Black 112 62 53 Burnt Sienna 70 56 54 Burnt Umber 30 29 31 Carbon Black 0 85 152 Cerulean Blue, Chromium 76 106 67 Chromium Oxide Green 255 99 0 C.P. Cadmium Orange 227 40 16 C.P. Cadmium Red Light 185 5 32 C.P. Cadmium Red Medium 255 200 0 C.P. Cadmium Yellow Med 249 231 20 C.P. Cadmium Yellow Primrose 8 50 160 Cobalt Blue 255 162 0 Diarylide Yellow 25 17 19 Dioxazine Purple 250 226 0 Hansa Yellow Light 126 93 9 Nickel Azo Yellow 16 3 62 Phthalo Blue / G.S. 0 31 41 Phthalo Green / B.S. 0 31 27 Phthalo Green / Y.S. 239 61 0 Pyrrole Orange 187 0 0 Pyrrole Red 90 0 37 Quinacridone Magenta 150 0 37 Quinacridone Red 157 103 52 Raw Sienna 64 56 54 Raw Umber 132 53 41 Red Oxide 225 205 186 Titan Buff 255 247 255 Titanium White 50 47 75 Ultramarine Blue 54 47 75 Ultramarine Violet 186 124 51 Yellow Ochre 120 116 122 Natural Gray N5 0 63 130 Primary Cyan 169 26 49 Primary Magenta 255 215 0 Primary Yellow fotoxx-20.08/data/palettes/Liquitex Heavy Body Artist colors.gpl000066400000000000000000000013361362435004500245450ustar00rootroot00000000000000GIMP Palette Name: Liquitex Heavy Body Artist colors Columns: 10 # From http://sensuallogic.com/paintmaker/artist-colours-to-rgb.html 46 18 30 Quinacridone Blue Violet 115 64 57 Burnt Sienna 255 121 0 Cadmium Orange 226 52 28 Cadmium Red Light 255 171 0 Cadmium Yellow Deep Hue 0 96 161 Cerulean Blue 0 169 177 Cobalt Teal 44 43 47 Ivory Black 171 0 32 Naphthol 96 17 59 Deep Magenta 0 142 49 Light Green Permanent 33 25 64 Phthalocyanine Blue GS 0 45 42 Phthalocyanine Green YS 150 92 57 Raw Sienna 49 58 151 Cobalt Blue Hue 255 249 255 Titanium White 190 67 152 Medium Magenta 121 119 125 Neutral Gray Val.5 68 169 74 Light Emerald Green 131 189 58 Vivid Lime Green 61 186 221 Light Blue Permanent 255 190 0 Cadmium Yellow Med. Hue fotoxx-20.08/data/palettes/Rembrandt Extra Fine.gpl000066400000000000000000000007111362435004500221350ustar00rootroot00000000000000GIMP Palette Name: Rembrandt Extra Fine Columns: 10 # From http://sensuallogic.com/paintmaker/artist-colours-to-rgb.html 251 241 244 Titanium White 238 217 3 Cadmium Yellow Lemon 255 147 0 Cadmium Yellow Deep 173 115 34 Yellow Ochre 200 27 9 Cadmium Red Medium 108 1 24 Quinacridone Rose 117 0 10 Permanent Madder Medium 31 28 50 Ultramarine Deep 56 16 65 Permanent Blue Violet 18 47 81 Phthalo Blue Green 0 27 34 Phthalo Green Blue 40 39 42 Ivory Black fotoxx-20.08/data/patterns/000077500000000000000000000000001362435004500156255ustar00rootroot00000000000000fotoxx-20.08/data/patterns/aqua.jpg000066400000000000000000000075671362435004500172750ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?u#Ny,cl~@@NKd䎸xw1RO5?]$rd7d^u ۅԫi{VIʎ3S"Gҵdӄkoz%HA[];VХ+'5Vpāx+E2?4+sVKb5巊 Fc9ϿP: iDpMFěV6y]MޛEou{$F$0?y&-ΠciVUWw=WvWrgd@LH+f4N-;}I9 I:ozz$t9_u{,W[c1| &L5;+.B*ۨ^*}\R D۴J0:zNѵ ҸX YuW7^.@I 8Oi:j=7osR{Hu ,c>9} FH ;]dF}?ٱŃLs_NQb(ݍųXnd }-,lw9 SKi_k?n}_Q|:m  g$懠\V\&H e<i/us`,B@83vJ 24L2G=1^A{m0]E=?k::&֕'(/q.^?̦y \^_{v vKCxwZ.<~Lw#׌=̷r$K[5P1ǠZVzݷ~p )79t Rv35٠h 8* };kt$6;_C+Dmd£Gh~צ協n]b(B03 "nQCtf75hc`kpz񞯥a}lт{1j濬xMD+s TIsj##U K·,P؂^G \vkKLRcR_X'g%ol uĖJlc)1>᧴c{1Z탔̭b4u"mO챓@\(=A^ ̗666ww d?#T<#i10đFz[zfCv% rt)6gZx)ΧcUWt^qlɂrZ+KaL?UmnSIN̻Y*Z}q6 8#5yi\ϩ~\ǗmGivnsy5J&)-PJ0#nI^9 XZȒ"p]qӞ7FǨ5!>˥}t)ɺ^\}v5h",o-laRV8fʕ',zָIK,ŏ&J0zַ5SI:{J9?,uUx&jÊ60goiɩݹ<Ʒ5\iL`|L1,gvGՂ+=X$o$P&%_~jl45>grz|ҥҭ,3y>@vOSY:ȵ|mgjfғd>^vą=1+R[oGk7[H3(q5B +7Kh LUsU=Ofotoxx-20.08/data/patterns/bathroom.jpg000066400000000000000000000057671362435004500201610ustar00rootroot00000000000000JFIFC     C   ]^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?3,u"Jy}upv})7+ķYdMrn+ЧVU59%' W n ֯|k0-i HY}pWv=y HBjŸ_:wW+whe\gnK"ыDXcr#xŪ6_QmOB1K}KUɾVcЖ37.T7c h\Z+]iBe,j|bqXTjQSW5BjŸeg8 i$(=EtS-=H?hqf#c.J_y_Q"5|`"Ԕv_W6< ⣀!|5'𮱢k-b{u?|=yE*iF3MXJ5cY7__Zx8o]FgݸZ௉Nr3XN+8:QvJ&K^CǃAxK[xLJ\5 Ğ;BD,biEPFU ւT3W>Kt` d%N w,k&;tFsĞ+3vkwמ)%wXI>]L ͻw)iW6.U 4ӓ}M/Epu?o^RZkRsަ?k|kM^&RyU[U!I ac;8z W#HNns6K9J\k|_}{T׮fՁn@cvq@6mGB.smDZ_ZI䍳sl]wYcޅox'xM8A ɑ$N.N1Lz8G)s")0$Ws?֩>4r{4>$J$=:e =XUq8]fo.lwCp3]Nwf?Ҳ]1쓑ֻLvE,Mv$Q!YĎ4 ?/ZEd"W;FPt0aUJH ^]k"VlAwi 5ėDc$q|c5QQtcuue/i/Rƭ~O&3[^o'ޓHlmXl_5ύӣ֍_\ǫܨ ǀNJyH4)l?ҫ$vmEs?{{s'YgwOX:W)lq]b[:oXc MaXݙۧ"MWMbs}S-p]1JCMu{!"\gƻ73=K͙ UhWnF )>W¸3}ZJ~:$K €kdWk\էmnxLaR(E u奚aN1۱LyM%I< #ƺH'f6| ֟~j-t+UuL5JR^>KZHK>$G cX$ ]G` /Nj';}k9E+/)6yI6޺o\g]k|Hƴ<%v? lOw$nU k FgFPкX*)IWzk90_~4 RI 5Ugb$W<[6¤C˱ ߉yk>+A~(k?5utM$(Q>Ḭ́hJ1zuUGfotoxx-20.08/data/patterns/blocks.jpg000066400000000000000000000065161362435004500176140ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?";StK A2dv8g4y!Ԡ{fZ%m,GIjJQcJӧ%7uWť^B~_Q\ɓ3Vs ΝpȬ k/иܨVNZhIS|ڑ_2zETUn%aI4Rj:}U NqkSg 7ouQn46waF ןZ 8'^ xm%++)]I)m.:è{7znº宩l(*kgaIԓRv۾[I5TQm[vI<AdM ?xu^_Zikq*qwŵ)=6_҄Mz9=Q^Ir'nǩLנ8~+sTmҺ [$Ԟ5p4lҴO6.b={UJvM9vҼ]c^~GUCb<tZ@im+Cg{p?ƸU' v⏔*}ypn7Օc{p\f,#$Pxxt 26B_?xgu}L0@#6+4kk¹PJ--_~Q?8Eˈ!lkGڧz T3Ee[OZVw8MF*zu+kr16~`2#_ϥs^(`FC/nL呔pr4 #W^[cd~[=OePIrEyliK|1YF ߆kfg{qvd|:_w#n88Zs F `NiN{^U+Ԕ)d~F7خ%K 8tGPxzSk$YrEM޶,vr;tN5Kv%EV.1,^ee&":P&(*MáAn3y$G4*sҴNM񞭧xr-1 d)'4nc)RJ Ko}\ (^@n{Kqm4s6s)8U'})KESp{GuklzrqRAq9I`,3+ ^*3S&ch(vN./pw z ҽqpzk-j]Fic}8cv ! ܐ[XqQZiOFe:uoEy^4v.-"B P׸\? .QeX) q}}*X_t)JM%E%\22{NPvk)NSR?1YҮYcgKѐ~x㚮 e; )qiH7#F]֫4pk6VWsGy*#3TwZS׷&~H̹]Fx}7PY_ v֟[\Vg*-V6:}+W$qs~%_3À&z}r:Հ>HG3AkY8@GCZRu_**OU,*p~5L =ѥKRi->ǵi>Q-=mAU*r<#P2Ks!`H=jH?>kSg0@2 Q[ӭu[R$rƪ0u' Y"KXt,-l㕡kxdc+d"au< R8`c5,UiF9jڛ2:,̶mRmϴwj#YK9%Cݘ mDr~ [ RkMlߢGkHÖB4]Kr5Q]6h Ԯqd\R83I#w$V0,Tec/٤ǖB0tѿ_16"@? z柟{үF+fotoxx-20.08/data/patterns/brick_wall.jpg000066400000000000000000000571611362435004500204520ustar00rootroot00000000000000JFIFHH$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-20.08/data/patterns/bulging.jpg000066400000000000000000000071531362435004500177640ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?W%QA)NOq:6[tړUq;9ޥ3]Wyz4) m.9Wbug F}JIj7%[N钥\zgjԟUe; 9?4lo܁n୾AG<󥽵my%W$Է: n}HvAOl*֏CfRRg啈#*FA6Hh|^k{,Kig+ =Iǯz͸[Dzm[i5Wa2 Uu B/ Ϛ.1d^jX;q֧,g _~2;: W{ր/InRXEd^_NZ呮WhF@+sWti-PjV[,>JxwjvewC0sr{Pl:KuH8UuRZ5{}N=ٷ?9jK}nas,Wd!l4<=94ұjw-6#.3 CMsϔg8 U( |0vbZ'WR(xX3ǥEuEn\/ |3(xjEI2͵N\@wuBגʑoix-ƛ$QXHMG41) y|Aqw`t?3f :IY0`Lϸ9t⤻QrB}j뾅-edLrz`V_ \*.]$1DϷn 2otcVU.?!Z[K|hmۊ*[k4r5D|>sK&Zu.#{b3%.9:圪Ţ*ʓ4&,2V^V5ڬ*m.{1xgı9ǡ{ՙ3^"N@nsZMi; #-.dG;{UK ]Cso!ϙmϮ(Y^!PlkL#Q ^tL3 &q#8}ǭ\mN&-Tw@ivd wd׭:Xt K 0z-BQ6ztwoq-ē! ˴qi_:@p4rJHi&8^jy<=,18%Zi#w <3R܉ ,h֭6B#&4Jq'V++1heUL\{3iV8d^@Ms>DH ?IbLjqnQ ~-E붋QjV> +Xg5˻隝ʇsENԖѮ]Kt'nqj ]6fi v#9s@ c귲$[E # ޯ[zQ!4-JG#ZVgDGSXx7,1'@ =Pt 9.ɂi9gg<ր[]F4'傧;: |tʹ?2'LȢ?*<4eR2׌zry?ʀ5oDml^}9z}yW9*K56[N,d??CקjedV >TUY>4M:TeKm9{֬iz-?Q @1ǭ[rV\!*8dc}@W(0 / =s}{z}C kOoc3u, ş `oQu#KE2tv ] 5T ɷ\``x}2`QqrHY|=jɨ eCBl;Է~ 7r2@CV!!UA>X= ҶLOyQ~q cUO ̚j\.N/ (xcfG Rc'oA@5`8gUkhA{$r` 8>Z ծ#x&,.g Mn&I#nf]Bai2U }:ˏY*>"~'V.|?ogoelI @>[P𾝣*=S真~rhfotoxx-20.08/data/patterns/burlap.jpg000066400000000000000000000611251362435004500176210ustar00rootroot00000000000000JFIFExifMM*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-20.08/data/patterns/canvas2.jpg000066400000000000000000000210551362435004500176670ustar00rootroot00000000000000JFIF``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-20.08/data/patterns/canvas_blue.jpg000066400000000000000000000103231362435004500206100ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?CLbdvS؏n*^Y˧}*|٩ J|Dށm hǩi(R=I>SHO@JϒZp/<=\J~5W"ZO?a2-̅^* ē[ChY? UOw0zAi&'ץWPJOx}M泴H}1y+א~)v1hYm[*=#=^.Ңn̶Mi }q}Ɲ-x|kވ Ycؾn:zU ]. XѶ{#W?Xuh >G5xrf%48dM!,y*X%ƞ'Z9"\9?Zϖ1 έCo}'N.qެ6Ṗ6DOR]-(5»CI F^MRvxdcT}{ֽa0H[fۂ یυWPe\LC{;g_iW2FF 8BT5O ]-\jWq29>yuoH-qnaŬ-6%dbsT7RӦ{+=[fXf1ç^]j!]v`hi{w=j~1/lb]{X]#ݮ3桮[JI{O]!4[\yΕk{ZގF=Gt7NԑF$jpUWN_i6tw7zbN9F#Zojei/}N:_X5ZF({\Jek;PKM,T_$'ō >0~y_p-g~s(.ҝ#\vO|Oi6{ai&Rt2H>⋤Z a;l9F^n"yĒ.QKZ^@[bi qGW {|pˣq}p +"ZT;t߈c}ڗIYLp>l#kP;ww_p-_jS[^)V3O6_ 4Dvic+ xZ[e94m6b/&Sޝ?2F5Vn["Iy]0e؈$42DUͅgur^{zU$7?&2#_~;VmmG~T (kj^%Em=X?}^raw'e{Tu/t%碩< #?>M01Kj~o\}jӇ]YPڝ{Oj1H,kjDӷ=ٝ*Ig{UmKHLZ} \B}OPi׺EռŲJ}i;++_pŏǷIienem1:s֩OOmz0xM~,#g;/z̓UHD!Ϯ9NUMԢ"{ o-{K8>pj׃n5> u?S`v8'9M Ԟn]F1 c2}IP5eN0V6z${ Z_JW 8[ ddgjEЮ!znk׹7(;m6Iu$<$b&n::M%/5-bb>r6H99t0R_\jm&J+"D(AZ6zr#J-Kވʗ+u42붷Bc%sFZe"K&&r@ ,qUWEGXq\ᇺ Jc;dk{' TB,jgOti8<~u1 h a2fTuFXê˃"0v$uVt_|yt\⋵z[mXg8Z [m{7WRƩf_ -NL StXe%֟9gV|4]#R- l>xin0d{ڦM)m9m|Y4ΎPr0>ԍeexGN01vkfotoxx-20.08/data/patterns/cheese.jpg000066400000000000000000000041541362435004500175670ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?74?<~u'$L{OJpR8 Zl,*X`ICzEtPtI+Jdb~'ZWWLmY\%}y5_iZ<)3(8)G([-xj޼~/ $擕F7՞x[Ϲ[VJy漗MѦ-iqת[5RciuFjn̙Eb3SQ> ;&KhF@UNf{Pg1x/>'뺥M+.7' 5[P`cYH&{z:U[AqFąlL$Tp#lCN5 w<-}\W]ekmZmk*/O ◐@WV+Wdc\ǷmW 6Ȩ"F3ֺ Ib *z ,h>UE2KˋO@1'yQ[ tVg,H vqZ%ُMAH 溟B +;rI*j{?=9P5nڙ:5Zi6#=^穥Vk2 8H@?^eew"pHҚAjvl@ N9HƩ4@iO\Ӕ4@4nтN#Si09QyN}j\)č(R$Q/'V̋@R+)$`՛a`桢: Dx=)Q#$uHdB1Pۼ1G;\-RNG8\fW#R\I(dNdSQvc*0;-[xNK8i%|S9]keaӊ?3j>)t0MS$n8ÞV2eϭb3ˆ>v .:[_ƨpF03;}Eq>@c=+Z r;VR˭5jZXثzgXjKG@cZ (ӳGIW{*KhFEH\O,sӧjvJV?fotoxx-20.08/data/patterns/confetti.jpg000066400000000000000000000151501362435004500201440ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? <>kY4SSu$ɖb?9)`#$k7XnEi綌^IUW#'Jg Q]۠KGp#\k:cic Bє*Xs+\oؚ\I}?uZ4}<FkV]O7,?uP)QPhrHz>%(;ʋ5[G<[뎶Ƶr@Ĭ[",aW9/5jo{s%y+|8;TlwVn_ 9ȰF-G^i:^jZΣ!Pr0cp?!^G64fv?(aǸ>¾~eĖ~ԮCIQ_E|/.4{EN@;!2y9⸷/ BUh>h6[OKh} )\]ҫ_Tm۪>*K#O.9.aPʰ83yjG4X]F'`RXdh]pT+.?ޝ{es;=> kP`Gvċf~+yဝ/Yt3ϪaG8ؿqc24W'Ҵ{[봂d0y;km|1?p *%ƱXqקx/|SKmF{i=҂z##z =y3,#$H?kυ|1}QdP H*3ѳaFW5Wt]dfxcc=QZ|rZnzi}ϓ~)]_Wۻ]Rwަ)8⍘( ,rI'.C44mbXOE}x ~,'XȻr;`c&ֻt]bJ9GAi{dA_G}~WS8cs^Q|${9/p hA$'V`I}.&Vvp{i|316e.{$-$i"fj1O Z7|wNw\3trQF-+ƺw"\p@o ;(5KInYRG `z GH}-PIoqnH2T PX⿉XIk89OΣZb'3gFk+yw*m\{+]OY:4rsկo*|? ޚf3:OwFIg=j&s8J]1oԅl> *ZEεh+k?SQ{k`TA(G8#'8',~,M{cM.mb<^a@$VY[q֝xJ>j6tdP5-K/ž!REbna I 6{bjsϖ6t3 9{h]7{uOˮX7ͳz갫ZrmVIZ^xW]. M}ixF%G`Q)^ :ljuxvogHjq%Qn#xKIGJ:{hƬFv㷽~y_ CW}մk4 Ni4m,3̍|aن#8&ri=]M}?fVV^ɫ^)9$O—~$fkJ=d`G,̻gWK|KLW L8b1Nק|F|C,C:*> 7ҭ9I˖ZF:F]W4EX 47mY 2lG JulI r1< gq"M'+x;]k]Uݤ>g|˼Vudk/^>kIim3;>ݜkv/S/SW2--ݭ}.gOׂU9M{G̭fgmm xk34w}& WMMj/Yk".ZĮb'=KP,6[mm\q6F #1jªq{4۳|VLrYJrmk{td MAfF cϋk[jַ0[Z XO Y!w b$rZuߊG4?xf=ޡjV[,h*n e9% <9.R=j$\z߂a9d14,\no/k?^>ؙdy[)!xü#3nx#oi<9cmbsz֘@8"q?P]KOonmO+eJa_0nwjkֺn~ùNWTqWknS xVʙ>8xЊOڿ↗k<d[UݭťvN X G-_MkM6vl{T-$ >k |.&i/ .~s`5tȴ2]G[5Frw9$ӜoW4i1Ƒjez)]A?e$ێ~g<-Wza`uܴk} ӢH(q&@~&T)ׇuV+zYOe,.[I̤oklx-Οw-pIG}7SpD*m9_ Mjt0q^:KyƌLJkoFƈ[Or6is%ő!= rq`kpah:mO\wVB5*9%7[// GwawmuG0{Y؂2(s֬j ~u%>ԯrkg&pq>M}^hVk'''q}Xpgvu8$f_Vic7WJ.P-vow}w# AUmG-Zk>%k?F$X%!еrmRi<Ipe ~:76q{,~W2"\wfbs_Ṉ]?m"ڭonV#!%6}q޽LF/jʧK_Wmv6'i%E%fK_|J _mKKKE=ssy(cٰOq_.k/7^7䫐gG}$3k7"8NcLOl{8}(ⵎ>aWHIqN.SA&Ԯ҈T@ѸLsǡ.#LEOD9cOI>SدB$q=yESMtb̙`S1 ՒL[#'=+nPl,)~-/mdwjHG2#dtkJlxU5ǎCG9sᶅt7onӀJPzNz Gu (|>_|880}V4;a{tv?GO~9}9tU$ٗH N~ꌞ99{Zvpu Z pJp3߃׭hA; $n? |9YDY%[6MPᱎ?Vo\ΐ56l9#zS4fܒwj0#]sNkH7zfYx:a"J>19<$~yǯ}gM[>lϓ989t)^%gtҮ>8>,[^'}UM8Yo fDUIt7 HVP=~2˦ؾߗkRIvI\?Slx\WjD!?xN3V" d%nD(<.>=2O>u,aSG -A 4l??zZ}8.bI$nI99ҫ bd R`Tnc UNWi^^dԯ-P$ntEgj.IQ:GjڅVSxAin8 >J֛Q.HP 19ϷcM|ciiS{tkdsgzRt܋HGu +^3'ҥUf6uxԗTx~\$ rے7*(z{\w4.|E,6PVFFH՛qֱo"kw&3Oԯ< 0Z vS<}zCd%nl;yo2y}ճ0CߛIBct"=:WsywvCہe,YdMMNT u9$) q4'R6/fاˌqߞji׎d\qB3jƥEHl͉0b}ңm-XTm\\8 v^C$ N?L7kj]D6+ rA%C44- ^Bn1I =xJ4+XD<ȱskި.UY=Ixx@کm'*)FIxvyn.]bJD]RБ}7Zf><:v}NӮ\C$*۝|;i0 mJ zNzs"4ֈ׽-=c=FxاIn.>?vCBÏG#4;pNj;[p3s=j./*zۋ(\G4-S?{ޫtЛ%: FkW>/RpH:&翼|@Fc=?^?o,shnZ36C|wp1k9bDNpLu`OOFkN 㶷I.6=z8Ԓj ,.}i¦8FovOb+ܷ6НG 9 W'noMOGU8PF1:ZzO3Us~=E%ܓiV7%VDRIpdc$ q/yyi`fv~jK; k\M@VB;=Gi 6ڕ7N}rGYF%Mč;LfmnfIpGj>tEWP%Ԯl" ;?NZ뒱L.W$* 9UiRlH:yCbd*݂)U -h?AԭwR{!ZA;y`m]vdxmFI`cT;YVF@ -8j:TZuΛCYM$&vgvB1FP1S Ep.l}2)(mч71edPR۲K{:hn.5"ºHFe"H$91l2ĬyV%R/+8v>(#ctkRtppn{+ N}4#MZ4Hclq˸+vlZ[;rWg=pH_"?0 3w1b*}^ׯf[u#o{El(QRU%[l67vs$qp"M$/u؜ɷqb6dG#}^;[ hf3Bb(ۣ "(QQAGåumэc%Wʻ[w)\`K2ǵFrU@Q!bC]I,$XI,w!2mdTx5&4ci6!㸑Q^4b+K)ʆ xYD.e4m2QUcn]T^1ۛ^t.<6:iYmI#R[nN 5=NM2f-)dg ,4'  {-lbWW;&F !Pv$t+q^](U,i!kғ1;$, ʒ9' 4۲khJ-p:M.G eLmR1 Vt6Ϫ[EIbKP$Wcpq"n[DKJk $,ލ,8*։yxJmlX^Yȳ^+G#,HϹʅG\I ݿt ooNMKk*79DrJ,s ۱:hZ[u[(m΍HU;i,'U`Kk[ M $r[n#ec`*.21E9bF9!&I-]$!2"|8- sڋV+tEJYgIQo!I0nRSK4K46JH]V3NC!*w3( o2g5VDd@w[Y[٥ƣ\,mjvr02O0cj1a96s=ơzGB$;B;ٕFH˭]}KC%oZ``}"v`5ݴ#ہ$گ֢ݣ 'y'<|uĊK "oK'U{p-.aR!`Wq 凇mr'B\* vXrHF]HcTգkyxnmwsyӬO+BGd6I^ 'kdAXuikSCoTUwe BΓb89&[[O_#@F;q j6h5ӭ?,>I]yChfb)^ɿ_,{D[o카;y.m@f%UIBFkCx)MN<-ѓD14pi;$+~Rۘt ʛڭ|mP>k+Ny \ydY%,0)E>נk2F(YҢv|aYU (v/jWWWϧYOe!dtZE/4fRۛfQtBmD٭͜IȚO2|>TGXecua{wcpc۷?29.Hid1m gJ[M9!#n;q gTinB.t'fd%%H4e{wkwocdeKU, dylLUSb5ꑀ%#ڥpQ9*$[5s;>\Q̚_^X\\VRSd~[1ffaë2a/֚t-!3ݬysrFs[-z%Ү %CYxc)47y(~aIS CqQN/,}' ^8.%u۔N3 ֙I.ZlL%-wg:I5/r3dp2Qu7`It mR*;-Xҩ 3X{{)dki#,Cp$6G+WoY6Ū)[\q(ʤP7i_;4lܷ:߸H'c]Y'v`ׂcZ46cF;X2*fuP[sgv!'lҕ;6,`׺ K<S,TP.cJU8 .k+/YG4k<3j$)RҥBN"[Zr4mH%I:pRM+}p\YEiim.㍕Ycē! e،ٔ+Tf40\Jm$I*E||40Vi/{wz%֗' hϔ70n9f.|56oEtۙ.lM Kl]o|C.qݲ]1߼u"bs 71:#<͋];̀]HN|ϺX9Jɥo>kXuƵ}BKKYgg@Hġ+ 3աj7Rl\VG ˆwFG B[RKS/UzzĈCi-im̊Kpn"$)0^)~._q|i1jMc#O{>ƖVC4ČT:jt7SBjH".Qlel+2 +X$ӫL؀Ϫ'U!cFU!I?d6Lک#)XC4rl;)U)_5Iق|`քt[\$$WhXQTR^tgZ4/jrӛGI~Pa);UHEPK%qd e{g!kҴ_;Jb FT Ow2U74wrZiдF ]ᄊ/έ͌bNO87}i>Fb"aal;B g 6`UY#7@eM2B2H]Wmfin;$Xw#p;F{'FP)m6ؕ03HobcHg(Nqm:2Lf-Q I洌lSy*Wi0eh]YaԞe3yp UN$8nrNh{%pfI  mmYv:}fM ʋ|\ a12I J3_ fotoxx-20.08/data/patterns/fuzzy-darkgrey.jpg000066400000000000000000000136221362435004500213300ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?D-5= 6]DJ]!g/D ~e;ju1*jdY;E,Qhm93O.As, 3EVܖ'XsP'f.04nnnko<mܮYeO l Q:dM%ޕtX٘#L#`˴yQZҴY|avкYO-""0 ֱ-Y{$SZڿ'_m3[(F&Cd"(,"h!u& 2y# 6doCj\GJLWV5I̮A(fDCs [S^д6=J-*fnm \4ACVRTuqc$E{/'2۽BJryŷ# Z=-Fq$V:Zqae&ܜ hd8p$\&I1-I,:(HJbweno_i2kzlX!⸴kXH[~bV\ !hV.-5;͐eh<ŶYf|Ə| >PSQ IJ,֐E S(. |Ofboek0)Eٶ$, yݷ WTqitWP"iVBXשmˍ0@|ӺF٦6! ErC qnkF v \ZkL.iAuon2űXAr]r-Z]62D( mYNwg;TA&aX*N//2IʼFSp<(,ѭc"i"m/ Z:Lba.v܆ 2 5}*։03ZZFLű*dG\ ѺQ{{>+ЬrEF!@+ho;})j\3Y2o-˕r΄[hpFcc&pp\2P`[ ; 5)/gNMZj!Crz {-J-֡ciBH3Y6ĪoQ J\f c:dF-! ͍Ags"M9VDIVYv*4n;XY#[b*8M(iXeڝm༒DA0ʢC\;)@w $({M$ƫ I'ڥbܻ0n#)rXh+eK>k÷dY|YABf ,.~M]Z@[NUf % +@ߐPfv[.YeeRdmȘڭ5QKeoz@sQYc%D)RT B_ir :eq4H#_Ȩ"E(jUg`Y3WX3{$QTc*YͫKyvֺrH5\[Ք^]Ov/Y1#QEfa@zb\%RD?:4b1J2@%2Qu{k.,ۘ7 t݁ U!*n(XQF[4cܝq(ŕE ;G0*(I$ ƌrbm,U\a- h/Z@T8i"}6 #cjj׳]P֯qk$64Cq:C); ݋3=Cu3i-`gL3!j|27RV?m[?ȭJ_pڣ H9-[^Aq=q\k=_bGPv]ʌ ֒ ʫyRmUA/ćoo]J!FdKYR#DA+ ,Vb qae, z$aGF$`H @6o@?bVgI"am?w (l6J[&Q ޏ-KR/${ryJq,TeS@K Hco/on ũcl$,{IC\HLhg<9E ue@#T?se-ދ{%7qG 6% '#*J!$num鸻8~ŵyv4qsu`( U (Pjv=6-e֩id(@09w:!u?. TqQs.[e+=KK)AK8K n ݬ*mkU. ̡v%Ԯ# 2mAvGaD$3o%TDY ؍$& 2&(J"b* 2׼+{E]\\mo |vbr€ -* F)K@t2#ck(p+j6r[HoW]B;s|>wv`@9 ܨCEKۜjL3-,AFH,P엗w6OnO&RRyq)R@!Bx|8OsMo-d&acB1|bRBJ".Yqp~sV`MX!es}s=ך=B]G2Bh%PVU8C&Y-嬟y-!MConr$~a2vl#i/d$B+$y\. ,77.]\YAwv6YYghRBA!# !d" KK)u{Ԟ[) s ,1UYJ"sLJu6|Eב+Lb$6b\QKuw$ De/EyA5dn\,Gh̞ɮv_*˕ge2q`n4>҃7%$2G_ۯQ"p7q+b22 `Ѳ_EbŎ+m#.Ғ39]s ARZzE-`Elټ(9*.4W;e`VUQyp b7`,OovejL/a_0F~ĬNH`i|^ӯnla/u$\r;DO1ҥuf|*Q%!%>v,Ja[7@RF\%U4F{+Xu kVL7n\cH~T;H> 'ѯeNy1 1eq ,+f` uAnYlwZ<6ދr)yZNT1ݕy$N2Xcky<7ZXO rX6 9iBAܸ#ozcK8'H֮Kuc<ul(*G{n @: zBԵY延Erx`PD2T (&C͐ {Ϧػ6V(䉙,ITbs6h+}>n@dP|вl)nY|Pqsk['hmk VޭcX>Hnγi6R m<Ǩk*G$#1}ΥUJ'UZ^P#Z=LWnUR $y܋9$I85K\Yb[vVi0 JQV\>֍.[gv .Ky "A,v,@R:}ig<$}gd(I"VՔqs۽J{610vݛ JN[SNun\oK4G r f#/n!m@y1^ᷕWm$Em',ekBr\(5{70b%NX4buM*ė7SI-0Ė(iSyp[ moӮ;ڵԺZ_@%bU*]Io"!Ӵ+;;Uأ aVP Lmm]:L1 K+CdTx%bƒ l[ !Ϋ}+ukYm䍆Юw|˹~77,zU͆l}SO $+ fE |21sŧLt_ͪ>k<֩G.[1V>m;0`fotoxx-20.08/data/patterns/geometry2.jpg000066400000000000000000000376641362435004500202640ustar00rootroot00000000000000JFIFExifMM*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-20.08/data/patterns/geometry3.jpg000066400000000000000000000521751362435004500202570ustar00rootroot00000000000000JFIF !!    ' ## 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-20.08/data/patterns/gregre.gif000066400000000000000000000056061362435004500175760ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? [.5#Kg4)f}˄!88$ct+S6"#w2sI#Et3*U.ZFQ xHXA֧&9Z!I1ve.J8fFemHV?JjJ'2ZsriG 7;IoQä ~g0ޒ,6{!9d޴@DcJ񭿗֑%_Ƒ`"xeTxgCEOޤm|$y>h_%E#[ ܍+Fw<#NTѳ^qҀDJFb36_*+ߙd,a9`u4m%Ą$o=ƂRH1@7V;čO2 E9S|ZJ>80ΰb'v?1>޿zj0݋pY8*HUb'Uܸ?a*2Ito_M!<\)S:8H)lS֜e@mG*`AN]Qˬ *[9ǸJr$CmlHzuY`pT[@>csCg =Į˵}y~q{bZY>_ON8Ȃ>i`;TDA`cx%YqAa`ŝO~b}wcs k#@ eXf-Y]ģ:°FDdM;͘ \%;/jYtlðkZHg7ditt"(ScM_UME>0\ c TgsADu=BME'Nx亸xmuC(?Z֋ϗbikah@PO@T~[zu&M RI=* l^LM8@TQڊ)F%B2 :uMiυr"-́6^*3_0߰E=gj7em%ĭ:xBҺjJ|4`wZȳӤ^j(oS[1HTv (֩2MK-HM!#H P}EjwvV-p%#hު"o Uje2yef?4ܓ 2((Uݍ6}=@k?PvRTX8QI`Pj[QE[;evc!ʒj 2XSI@Q@Q@6HĨUuŷf^9#NiNAN(Y9{21#9eQZ(fotoxx-20.08/data/patterns/imitation_leather.jpg000066400000000000000000000064351362435004500220400ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?4CwRXg{qb^dY XևLW)" `5\\βUd`z8L v0󇁞 * 럧jȹv-P ''9ӶjI-G! Zַ7Vݴc{@tLAsjLAI}#88?lNilgXN:zqKKI&QmRpu:r6aG;{P;i D#iO|=M2Ip'>}HY'.`^84˫xXqkX2W =>aX#n*D?`ҡ[ٺ#IFK.TV.o娎H\0̋ ]$Fu"FWPGjP.tڦ4`n`sc4*č9 0OπYܫae~1Y %Ƌz`v 3n gWq$,@G9#jK -$8-#FvBZw98)@HG- @ 4v6׺te)nX%OI[iONqNkԱ0lL`K2cwHnᴖ[6k-rGKst{fP[<N\Cm,@ S I= =Q͵øՋwcT p6_| y\I yRec$*ǞU;N)q%텹ʂ\#ۯo[j 4 *7.:m!p?NM:;the:v(+,QdYp UtViEie{;Rí-H c֣4.~mW=\qϽJ51.s*"y :p qH- fF9Ѝ]HlH⑾8>~4mE,rFq!|Hڬ%l \A?j5d%FHdLm%΁>fHBHE7'ƒgYU8?(#:$`U7"eF?Cۼ-"upH>kR[[Y|bQ"Go^M$qlVBzր$q3x%qlcjhf3 !Z }'J 6}^ 5 |ր!D!vPN9)yjkchnU'3+ed9}Q2~=*c%Și9FB/ӁI_HGqs =}9 tG   `ո4V#"eOd׌{ΒSWw<b[IoW^j^Y_FbIa{Ü|=jż~TK;®w zbi#D`|_9P$ $Ũ5w2+گ, Ju܌QU3B6Uq(Yb7q%.8}NyO/[ʪޝdз_gx' ulTNH=?#Kwy>| mYc۹%tD;Zmz\`\Fw(vN4ǜCs DӞ{`⇶m xw +m>9-yH'8kaV ^R9({g>Q"%zu$`*;NK&,]p 9ǹeTQ\Ro{y?0k~{;4mc4L&UNJf\yioZܴb9 HQYrI\o\'ПNjfv3F@něXǐŌƳ~%sw77>ceBH720~TK_ؙ9*s󶞧M&EvzuvqŲ;[wI,>s#'ˤD5ƛc(IHo734EFR{K }?RIO#6|e.@&HۚN߇Zigu%͆I7l^Ps<F O%Keq^սmn~\rxve]2 q.XYMKeSoq $q$7̭J)1?!-#oVGIa,7 Bdڥ)qdE7:og;l՞9o! EWs w$]Y -Tu&t-Fy,-[)|&34TJ9-xGOmcwYαM>B"2*l9u1,AcOYƣ>R#Ia>kn;mY$1Őy7mP[qKO u#}MkM?Nk-tYmY{I]PmY[$եoVAu_h:-Gi㼺HPng}bP@bA$R5!.2I<6Kpųt$h,J/$y∴{KOO=)?W)#)]Iu%5}WkGeÉ'i?t!HTb`gTNY_bk+?[E̫i'tPBzqqZŲC)B4SfRFX|`{/]xZ5X.md^)lӴ`F*dFV<,3ΓMR5)n.Es5irv]w*fr*[3ʰ[ؕa8DQ"3bUapT()r3W?j3NZUl,-0^%7b"d8BTnY! íOšMƱc;gc7ub$me.{tSi֛zÍ68- EYk&scM:$f.r|EK/jZCFt[&B7y 9$/ET' I[}>3մ=_ͧ5@<fSae$)Ú4:hSEɶ,=\i BY,'ľbt6{5#yH]Gn6,6nmZ]QXzQy'a}dYc۵sk[5W֬[LM6#hIo%;V1L6W|5VS2 X]hƯ5QY$،6јuYx77=u6ș|r2ʲA{RU nyhG(rgFe2tW2q286+)[zO &]Sl 9ie2ܴkEC+K$h(bۯ ׷ axnYoM:`uY$` )=5ƳXx3b薏%Yqf-`qt+w\BMȣдo ޤk w)kMb+ X30Tbe?^?عeNK[k7KpSʄ;>B]gD#l,|^h=ӂWuXH `St=n('eC,?2\HB$㛯 ;NOx#C U9U,8sroGhQppZ-߭U<ht[RJh5ź܉!hr/FQj7:WOZr2DˁF/x[%JU]~}#E-unuI ^'ѭ1 233y"1O @4OGլc tlr:"2 \18Zzoxq][j>^=N=LTk^OY!ʉrE%0b$RM}#wZnjo46R8]і)L ll]̪UZ׈5ɷCZ,.c(PC2ΝW\=258~}u5̖sP>K|(*s<_e|J?tbt'T&lO!gHfvэzd~\K IxFrؽiT7[\4$1DV("o26UB 5(r(QQjp-zXvci.y:4+yjѩfV[eB{%,L:͎gs5Imh',q_`n- R+KU&?dĈz:4)*mDuύ>"[QƞG7hY.mr,c˨FwIGײ俯U>[^Eqߊ<@d:iv2_KڱXh8@FY }eM{&S[ii'h;oyfJ{TW6 .z.4A4SrLxjn|XLuW2\Kxc0(S7JVu*F+O=?6fV?VVZU/ W]=u{ȍ̂gQ&hvFsmEj6 onQbSr;]?-9-ψzV)-uS>"+:oܴcĤU%ʪhx|7jtZ#{ EٹG!ۊvm_OL^׶zcvxoZFv!~o͖< +42 J{.u{(WHKTI$ubpnf;K!K'4o k=%ODˏ+#3)ym 0i^l;AsOR*XRFꔹEJWJujկ5kTewnVf滛-|MeWB9ii`@6# L dq6XuA>R{5tX%eF\ *J3B6l 5߈A{ yh|i!)!H<cxY7/䊋;A'm/ݗt:~KHHjzk$V#tm"&+C s\#ڥ}xm [3ռm"$pJ<#$UV{: k) xR[+=;(GP26N]?HգKLp3HmTI%%{Ej7~_m~<^ZjP]ɋc2%̗Pv\ޠc<5xT̈]K,,H w*+9##zO|)h|#%{w n7Hۥ(f$ u;Mյ$:~s&r4qL$eK(%\H ӷE??y--=%qWroi<^ZK"9hF@$!BKM[4m BkC$FeQ!D)=a^XdxJRկG,n|t,ds_(Hު p')F[ NS9lmk]>jEX׵;IJ,~sLCYI`]:-ܗqW7e)Q#Fсl2j维F;w>G5ƚfxB$B5 ڟȳ6xN+nh."e))mۗh~ƥ>ed޿费Kz֬V6 čbHвe&UQ+ܬAt7> КWQowLmO)6r*ǻcd0;g2^wz=Zın(3I 2rA| 6ω/-6^I<#HdX>c $1Qm6_gCuiKsۤw%X/ R2HnUMی ٨R4 `6PգI+k-*YFSvܜ12wJRc8)EOؗL-BW&ޡ!Ah712n;6\uW?kYčp3u%J*%ćxx΅ZO{|:tP;h bD̶`\ \x^4-l4V7\kSsCcg\VZ^lq_mYgEDU>o,1Ǘz>Y,vFi7wXm,4 tR}3 IOw^ K#Z.j(_iԪhSFUZX|W)(_MwC*7"%-*~grHMuo-lne淝fFHVh@ZZ^;x|YCɵ^7͸}r:K{NzHjs5o[|u?fotoxx-20.08/data/patterns/lawn.jpg000066400000000000000000000156711362435004500173020ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?wIkNJsY`֋t/eM+R}FXŪYLY.$U]^]*I)m3_j ]NC)AЂxq/^Z^lϣӮ⸚8;'00{kIy? uy+u%k#hRΝEG^o7)Jʦ1-mnS#:{x%ضÂ<ۯkYj2W7l-ذBX !oX-KNDlp]B#b1[P8\Tp^"-tk]hm2Ɩ,&nI4S9GKk|sO Թ}Y{NYu] EԴMZy(5909 (9ekF_6uy3m,GA3_[t_fy퍄7N3mG$0G#qZ}_Uv>-h퍬i1{,YW#MmF.'m7zNRN7ާg}Ss\iٯD3l0d Ur2ErmUxX.VOi3#s=+<,56(]B{ڞ컌h>vRNCpyҡޯ5Z@pR͎QkZ5W|ʗV'k{QMG9)O2=Bb2(ye\Q)L '뺭߇-?Zo3jw{ RߔC9-e9 i>͗REK-Ŵ65R]9 Uk^㨥x^uKdKM-``sokD$#ڨ!<~I/|/ʔTWwww גX;YFՎJfve8 O+ V\yʶh tNqֱZ^ج/hַ)6Nqmje- w~z 0dpwָD'NGo@Z\!W|O_uOi(mt&.p Fh !i[$]/L.7ʄ=8bx5M MNOQn6n.UXdFw!IP Ou\ܣӹMo[g=3Ik׈Ys] I6wH k<^;ҮVIb`HX`r eH$5\#:w.cgFG՚L^&|um;is١\hl il>No߻j%mݷKߴIel VPVg4, eF95kR:u;3λ$Ripi(2dU/ +4񆅧 k Q 󝱹ffU$ gx@>C}ykKx\dA;>RTA址9j!s5gkקQJw~CO zf{Ț|ii 9,JrI+|5BIWt %Ա<>ynN]۾+KMsVgeclǩ}:GF>ґv*vp9Z55 ЭuP5kNP{p+~4ߖHW*m4,isݨzk"e=i',IWg> L F BahZӼ'mƣ6H9Ui"wS msʎzO^6|Z"V~%e{P ҮFP"wΟhҼqXv2 e*pi1UN6}펝.d~uWE>܉!c>fAb͕x?_c,N,h,`;pgxONj)nU^~XC@6aڐO/@A$FUkuK|>esiwr\EYj"DYw0@Eq 4Y|MrڄvщCg|OTOlq]ޕ-ۮgt3XPꬪ7rc˸&N8?P'ӖEynu aEZ"o\mFI1Nrp=[/᤹IS~>f[m_ (Z<żM++`*| !%݁ h>t6{JKTqEh:?{VSr,auL̛p)MWg C^)=/zsrD>i"rzըe94h}:Nd[^[zU·kuo-awtrz9{@~Y6w?KO.YT徟G^vkv66ZZ[t7p;]Im؂RvI"$a U[hϰW–;_ Z;m/)MѾA#sW5/Ou,#ĺe]FDlT y&iZmBAwݨmtX'*wEvRK nQOk+hڶ7rVMGGשYT%U,kHa,+  n*-5k;h]夓la/;vyrbw`p+ԼW,+e&=GS{TZdY]m>^GNѴm_ú`vao4'8=pI:P\_mBqgM%%߯!-އh:ݦx9#סIaa}yFSx88{ⵧ>xeƄweRU3`$Dhmǖ;Zo2/.f  x; noemXaiʙb:,^p3@/AR\#-"Jyp3d**>0|8ӯ5ory_eQ VPX= Vx9dֶZ׻4TSjǩxkO^iwB[l)up"qcY<[]JeMKOSe*S B8q1! ~$EAc Mg+nх*cm##?->NҬn`{'R]AyդAtɒ3ZppiԒc􏉺 EI.7ͦi᭧u?0Ơ(`TA\gk56K]6;Q%6IKԂ\u#8S%݄bÖ֮vi0ʙo! zǂG!S@fu򙂩@f<{x:^+ GK&]̋ Ulfr9|@kxoWԡMf+\a"d(C;Fn If{Egqj1$jzz;۹ݼ+GLi1]o{bη?fotoxx-20.08/data/patterns/lightblue-wet.jpg000066400000000000000000000073001362435004500211030ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?u&^_?PEC,n8:O4dUO֣ؒ0$1î{`Ud s1Ӝ %cl$$vw߷c>ԬFJ F:c#4`(!F_x>}mr9tIlCCut c_\ddt44B#p*Iwae3$m@GQ hP F~iY@>Jrl 6l``r3ۥ{{,iĬk*! $rI8Omy }sp?O1M!V݂P@OφKsuj%QDY+Gy c?\ǧ,Tq{Vơ.v0XP/p7sǧ9 0 'yy`oqi`pp>zT9nrapN~zFgDIثӰxJ'<Ap8gWO'aӜ}ՙа(n@{ulq&/*ꧯ_ջ9 3ϻ*[NrWi,jow'*NN”bYI`_օm# m ϧ?N\*r4fHOedVڶiw\My5Ⱥ; 8hAr;]ۿBպ{#kO>ڔ-zIC;F`NyjmGb#G`}q6EɑI8I?4Ȫ#s~<硦3A*I]8<FiX[@7D1v3ǿnإuq㼵ǁpFO™gfm#z$ׯ^ԯ1;'21>OjF7 mS N \ވAIb`˯뜚kߟ C!p'>uF;thB2z>*}ӏn8(d9I!(#=OhGͲ3u'}~-_FO_O\4yKnr?QcZDB8ʩ=9R#K~gMgiv&MO"`8>uLe{ oL@' Ozv^xcJI]I1BTUX &#k$pc}Ηo!cs05BC~RNI'8Ayfٳ&"l#5u]F ['f МsN:(|0 }1ד1n$%j޴aBվbO۟^Ku",F#%xӭ;Y+HmpN:6:Zl"3F]f':)mRXnU^`6ɂt8')4=ns9.=1Q ʜ `tCo40r[G$Ӡ>ml%DB߹~E:>eT)kGMfotoxx-20.08/data/patterns/linen-fine.jpg000066400000000000000000000107371362435004500203630ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?nl5;xUK ZepCgS1ILEqz-#I5:-ߑ$2,SD,ix9$px5rKV.dGk v\>VkygBdn%YmIaEMd`Qt$| 2qM&uZWeXẊ%19y\{S\٬xR6qO,3D+ߑx)v,L|-C0ݵw$du˸tP62k+z"16y#9,]hdcw@%@Ϋ9Ld|WS#&Ay%HFcX/ t kWJ4M:U$TL奊{C+D1K&r sKy9jwc,Rְ\nYHO}"IHREr&bѺ{m-^uin =@fb3qTlb$sw" de*Ux8*C}y4Rw|)с8U5iL˧+H; %6,wuq 4$t$>3VMvwspm_ Xb)KVYL!"(q54 .p m2ۃ{m9BK{|`Ʊ4kxKͦݯy /FaB5ILl17̮>OsҠY|@\J fº@%~SAR6/(&HUDa!K~ *-ܐIq(@݇S I-?,JC Ӭ$b۬[ѤR~\px60OMI:/ r Mk!Մf $; $0,q}Q$E%10A;[Qc6ko[/ mm-Jz `i-x/dU</^#Ăd^dYv*{m#U9ܜ#5f6nJAo M}_O15u0URXP;"d%1) ]"/ⳭlcpEo$z=hca:ի va*ҢV.A^G 4EXGw #t%` =hPfkK9 Hb۪289l:m4P% &]Ĝr2QG߷జhnq2Cmuo,qZ̬cX$La'ހ3 Y䶈8 9J[FU皚<0fNHig(d[QԌ:O҅GhQHH's*'QId/2!Fv&sӨZJLsXHͽ nAӊvF{)aݖG6 d t'x$Ƞ+f<%@Mwq%Ճ@ɍTЮ\g=x,i.VQ8420E,1S$Z8.y5/ō9c/ ER廑#S)SACJι-y@-90Ik-ĦTBzg֬y%HdL pAȪMޝq;K,Q5XǕ|A֮}UXdX 8]9f4-7j#[+0ɹHӣ9bhb,nWpiuݕַ֠r:Oei[v"{U+k1 ծĶW>1(ƟhF;8vyW/0T쎀s Z:^oxluI [rn8$shj&SnQy U9AiXiPG".BU'EWnH'fM,Z\"czm3w<c!*͕#ҧkw4rfEmf2)!TP_j&kmV{w* v?uߵUleUF1[,"`0WC GQik{؍kٷEjM˺EמJʀʢ)f"N@H\[^!BAC7NyS46[}JMլD KfЍџi @K =0Ej+9la\CG䒹qހ(ZGsmt _9GU[# 3ڠ4$aP/ʗ~ 9=*Ο-1H K>qJw!8y_`GҀ3.t=4E ^;p"vɸ |{QuEI4N[Ѣ+| S.dż{Pda @m<Kq]JE1NR fotoxx-20.08/data/patterns/liquid-blue.jpg000066400000000000000000000050631362435004500205470ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Iؒ2{z}s]R[bvxnIvxkjN-+ܯzXT&0sużP@e* ]V?avTUx WTiѰOIAKtTi4Zؤ'=Ei?jMVD2ϽANXduo' ٞ$ .O֓twKZ/qka#L ڵuzg}C'JI=r~.4wz9ʰ9_ZUD}Ue'dnKscJ\@jE$j "4Y7 ߚI5|NH}s>cyl풀{U|DH9 sX1kT  |M<҇Yy}Yu/u:;i*OFqJƿ+9$і,q+Hv.Œ=it)-^YLϜPj'b;FBBKk Rql24|CyveDYo NVzի]B4y9wq[8Q1zh̅h>F@1溝'ZiQ\꒔`6OzT O߯J!G2$Fٌn=܁d< c>abeʛ7$ϦFJhq$EZ{}bwj g%R d^iIf]opqZdžVC .A&tD@+bAp pk_eY$#杣xmW9'W?50yTLsQ^$Dm YqM]ϚlGLDQ}Y|NŜn-Cܬn, WRX C HnrHdq.dNFRҸ F6 -d%@oCH,PYi96Ӥ2QGm=$ia)FK֭Ma6ђísK';!#')r#l$Zl« Wf>0ni/ A>.46F7#: :\hZhe\:5V3t_>h-.YC+Mb0<*{eY$!r'}x`1j~v?fotoxx-20.08/data/patterns/marble.jpg000066400000000000000000000064631362435004500176020ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?2nB B8GeT,70%9Vޘ.Mc1;vgG99OZ@DOHYw%HppO@9vw׎jEKXd1m*9? {c<O BFƌW=s~DDbع9ӟnv '(zCn+>[rRعmmqd Y0qԐ3z=*?uۜ~ SjeM*+(>iߜ`Ҁ"M:XXkPej@0W<:g1q^muVn}+s/ 03ڥ2pHv0;A;>\{cҀ(| 2H: . Ïֳ#jʓx#ԂGL⬵1 :\@"EWrv:p9V{|W R'fYI(té]>9u0Fa!|͟Q0k/ubL׸ $I̋?\>Y;E8 F:\sRv~EH2¦=u}xM,H(9tI@!7n]cǵQ:BMI hDI)%$~W7E61f p8cg9 xPɒ 0zO94Ř0PNAc<.QpwPtm :=G+wF,x=sQPy=>44"ˑAIRq235{`fȔ].J½`** :0pA {Vҭd[A_r{r9 Sr C@F0q>jwb srytNuMVpŀ# 6Fk`"FFHӭjKw1iE>GKBx (J-vj 2NӃ=:tSW]}!#~ t3@d9^GpC%PTdzqxۙ03@mk2 %Sp?/dGKx fShA t#("-L(cOu`3zbdS;cnIIT3Oh^ դa"(E-Ս,FSv|g1PçY#+?pF߽}i!rWշ;\K4#xfQ둌t@76!2̨YaE&1M1,  N3#<hǕ/2y_r:l Ƿ@j7f{"m5 H8 }8Hj6(*3n׌}Yy2G>^8jHW; AcQqڀ '*8V ^j9@T3c<Ҹ s0GJč3ʼvzlhܠ- %7)L+^z`.kȜ :)/lJĬ̝A =VX>pz }'iKn@%\-NA$ӟ\أGGptGNqZZLjb$n #T]Nt\d̪\0~ GҴ%R|+g;AGs=? {?ی9Rݱ~8RiBDw#A`Azquk=> $SQ9a~:}1W5tKҁcjq[LUdPI\<㷽kS|!*A\vqj3 <Ӝz*vf,/c{"OnҐn\pH?tf|IQe,YG_~fX*K ҁ[5d|qpO~DC}hfotoxx-20.08/data/patterns/mazes.jpg000066400000000000000000000162311362435004500174510ustar00rootroot00000000000000JFIFC     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?ڳMzjEu>XV_.~vcޤZzMj('y Z>4UtLQа)o:Iy9 odt7x|FҀ~.jx-ӯ"I uJ$C tBu &V[#vV꒴Q$~nV+>ޜmON)3*QYk *Ǚ,?uj^]WM8SrOm]xgSV[fVbCFwn[E/X-ǒqXy*Sx^)+6{FF}r&tY|Adle.-5j7S0u޺ |ݰ'jƑ5#@ '$?]# 8֩*Em7wIR;3,QP7jm:ImoӃˑV\4[?5"{Ƥ_hiuwpPa^[4yK?Bu2c-1z7!OÚlm,m"nVt;R\Z_IzySR2GIEvZ8q拍;gvAk Ԯ%o3$sȧfJ7Jvz NB6>M^ek{.,tyY'#/kMUEV*;]I?f5ɧ>YZ-ߑlԓ57)rlQĪjD 0A^ ڤR|{6KzxmR(Cg>OV$zݯ_h#Mqt|q lVq;*UQO̬v\µ(-[zE$>|ɶnc~oW6 j ҕ~gʊ>DZmU{5xbVWn-mBN8I䓾megk kE:lvџp=TQx~M/yK"xEX6}V )Y` \w[mԭW"e; ؍T(5ky+8Mhm$n5Λˏ䈁IT]ZyE2 'i$4/Rr 0Gqn|Srm)i{v)9. Yyg] 5=?NTa":Dc<;t{i%Oc?5UUhQ=븚}nJ3ג^{mnY[~a 1G . QQi5:]>}I-,e7 12q4Mo\+6ݦD;U%^Q*9;DSot[=ᰗX7So.ӽ&eN;kdy,V4[},vWL³$:`~Bj3T:i/v˳^zb_~}^Ҙͯ? I}`]u(a `VX>cgV:‹'6gחIaܥֲI趞>k}qlHc oq _t.%լs3d󼝿rkخ*4kkԓnJӷtgII"@oi`wn}kwZ_FkHd+sa[GKR]Ʃc硋Jq~{5n8~amZ7z?5f#E.?¯hś7ZmkTݎ?)J 0U馞ohxCScd&ؤ0/ӡFI-m YIhЂ=ֹ-o컄c*h3\iM e#ocor^ 䢹l~=̜3^c'I~ ̟o*$ mi{kj_L𽘁--ypO cSn$m[] .lCT)FY}˹ 4'䴎\TrN{6zIj4+|M(?XsMuj<">4 ?yʅY6g}-+\`nxRy/z1'*[MZ0"n?U4įs$ŷrO*{c(G(}^>욽+w뫳M2h]qa& yʟM-rWh8!#N23ZՏGt.%Hûc;k3e9(FNO]{1{owMuw C4+GkvKFo_'~tjH..,mN [nA V|#>hm}+%=}|'z gcTKͼ}A_ Ѯ14N;yؼ|ҫ 5+ŧZi\j_FM]. zR {K[E-#Y;Ukqh8okaQ.~{>c.3{ JLg-nm.Vmޘck, Uw~[j>> 4b7,yAXV,),̱8ٮ:͞"iphwncsy ݬ ʿ>mzjpsi^(E"sf4=qGu.{}Ke ,ySJ$u"y,AXŬzv{.gtuRA_3iܫJ.E7>s3C?1HIȯ*xoe/V}<}[vGkX:Z:^kofiYjGtp,Lg(i'f/l"Mkx~nG]*+5u~Hfܬ~Vp}+1>ʔ6Imuh{3ݮVoM<冖Gu,=u-%Ƶct_ !`.~ρ޼#kx0Y͞z"xf8}> ,51ӽT]z1l^Qt ͍jVT7P/Eh%k=b6F_޲CI6;[v$&w0WEW][.]+FOF/'~YK,&d R9Y\U4G'?J11J;SSDﵿ22(n=jW9܍ yp~.j{{;EzS`牌}*YZD׽nŭZE.X!tF8Fb ?հ@oTY.Eғ/&lXW-Ҩie ge'$ u]cZZKǡ2ZݘG>UII6p>o^y,f;mg&ua&1;rITFڎq Lʐ\J1N}N1ӫ+IM+=5v{*dH\>U UvbB.5cr*]lf-( '$=#FZe}1btD F?SǶ^ZKCt`2jt\}xOV&eЭo| gCg,D#z-c6.qיV3j4\٫/w>ZSyeFǘp9Ɔ8?yhUn`Ol"[O'uyn([I3ԣ[ֶnm#WäiwP,fI|eeߑ$:WPNt"(h/^N+k͍ۉdL㕶FjWi7ѹ-QQ#y6ڨrj 4n~W5qg0\Kw,@XV(`cd[zCߕsoJM Э'$x꺮"ն K귳5oq!8c,O%Z֮:ml_ylfKx5k1_c=9n6bK5a9RKjRߓA-u6O3Vp&ݴƎ_McI%1>hޭ7[PC}ܙa ֳZMoZѣ#Mҥ^\z.Wo~[zy2.CMk a5K­샎7UE+3:1JI\C-\O}{ouuhCh';Ұ&&n/e`ӥ%VnI޽npe}7v:\J.`oiچhZM5n|ij7o8ҵʹq-Kó0ޠ 3P;+/P|&禶at]u-ŠFcE">s3xv%|];PU|A2($4 !tAFd~XG,}gZmfIZqno Xy}ltQPY^G#,?M}Dϑi}87/sXǿ=kkI[M~=@B3SmǾ WwCpxI[ԙb'n၆F:EZ9FG$#;`C}8PT0EgYQңiXzZ5O>qrtc?>öz=KPae*TgrEKR7wGMT@Ze،. E`,|e ]TwlE,rDc}QET3BJET ߉nOYcHgԵ{D`;* ,z`WAiFy$$ګnGSQZJI :Ef2 + ]:#6.RǩSE0 +ͬ|URYirYuw H6 #}&Qщ;QP0+÷T;75RII7$ ݉zzi06 =9VTؽET (&Ht؍ëeK89=k;֥}.u5Xa++J#Xۖal_/q_[MQP3/Kmu]Zt`vͣc~'VwL"N3֮^+p#'js.{|:KR4"(aÅecI.x "=JPw|~O7,tد⹾HatՋo=6쑿I"8eeE Eqi6&iEl^`$ qjRgd/I񽝾u>)D:^Ya")Jc1TZ'ź~զtHMj+6M%,Sk'Uυ"ntA<WFX.movG(QKXö/Ԗ V|MjIfs*5ͺ 9*mrj.}'g+ۧ"gM(ۛ* X-ۯo"VYV 7̌>TP<|9VwWzX]OP.48"T`̓\tRҴHBPC_4Bݑv+) KM4,⫭7K66-DKx Jܪ"D mޥpj}ZMT-^{hGvia ܍%l23 'i?ؚAc!kK{1:έ>h7[-«C'ChjQiֺ;V4LMc 1)iI xfG$'{AO H&h^8^C f۵4*n*]t%si)ǧɾ3DҴCS״/$6aV24C,Ti6\᜜a@+1]2-̘&IUB*Lc8mWyJhWwVw2K*Gz*a bUTgqm`Αgw/cI4f KI71eS]6\VV{IJ\˧IhVwz𷌴+B[E"F(I#%w]> iy.s|sj0Bh\`XRXmcemɵ*O. Qp@Vq 25FdH%F%F+Y-9"VUԖ21gPBU_¶De;K!u Ż5%Eo>?<9;!g:WA*EWmU%WiRx/x>:MXj)QD'K7>ц]pNmk ;9u@jӕlrqZ3: iZ7k6w2C.Kc|A@ʠP1&~iNOdV'cxZ5uۙ3UK ȲD2(^rI=t+>כTMnXzf<#F)a1M)w,>%x5m{{ɁyOs DJbrFy/'σ(.cW(3k$r\eIVr`2hg.wz~+pM_٬5?x~;Oj xTRDN \?6z=wM/X׵7k,Ʊ}mrĒmHH*A&Ư/ӵmu ]>t(^K*I伈wby XlRE4]wEyN,jIଚ'OBBmUdkHBc)KInltKOE"ijPHB8l\:AJCEKKKN "Y]Ѯ3J83q 6T t.nn[%innO FVSSwʹ߼m*/ ^5A=j d9.R(|GT6T1# U{{F֭5 Nڜ:cE-]i) BT&Q$m5Fo?o4 ;OŽ"%I""mk^j|mr3ml f|]94=>^.${M'5tB q)j_ؒjwfHmm TH$\mX8+В>&xJHWX?DqL]MgGXT 0)Ep@c7%_ s\{t*PO4MB]N^Ӥ㿍%Y%aW$*;iݪ[^t˱c)w"2#YX[#uνOz0i14\G*RDP̥xT]F\[nXR8Ġ`ulgcq3vsW]:mAhzFOjMJcɧڤbv&d9 G] Ien![n.ciuKy6;fE!XDj*GtԞ{Ԗ+ }ޯ ܷQL`̠6Bi~"~%Jk~SޜbV_7hļrI`.^TsNWk濤Am4]gs=z]X1}5MP5&UrzSƞ <}uM%[:O'XGqYO,V[S?xKӮ oV Qmtr, 1! ;k]joH𖂖L#[$[>u""G*UEe1O)SgrӶb^xAZڍޒMyŪg&w,~HARGM^=-#G(4') >X㍙k1V]~Pԟ>"hv}ꚅ3%՞gb$F%i.BƋ O!>7M̷P+XᕖUUXyh-ݵ8J3)%4|_%[O:\7q^r$Blo2+8J;ɪ?5}{zzm\~-RBfK91BÆ$.Nj)x-k|LөuǻB H@ӅEF%eMbޟJ309($FY[y./WRw'oBנu 5OEo W[%̒h]Ƣ7ڪHK]i7]-{'V}8%q9h$f&&5,8͟k qh,tgQ2$v:Hgo91-*w9|oqgB,n'~{X f8fma9qcOanNX~s|MtQnjnɢ&wZe {ۛj)b>bby.IRr#}P gMv@\3HccGKs"|T'-,R|Aqsv.Eq}<%>͕fFrřV`,~5 kSi;\R<3[G0kKxč"d2:J**Po{_U&V鱗#/v>#֖m#e"\DmIvR27Hۚ^'վxzuޑkKa0ŵ ZE<AH=A,>-in5 NFwdx.S _y4hW~vWS Z$)5_"vpXriE?urpI'm[Ğ𭮗jzgk3b4мlѻ%#A ߎ|]94K."MTD-$K1?t[i/~ H8D9H/$RFQ.,xp9c̦$ۗU˩-<=x3Mtvh^if)b5Sqr#srL%wX,MVYnyG\┴hedV ]{ h6{ }WWmHfgFxacŸ}hF].?1nyb4Hv, )yvv;~ ÙRQkYmz_|\vv1C8 8h8VQ{BR6 ?|2ծtyOy-Ksr?Y Wm6F[qe[x6Mz]͓>2[CҲWBmQmPGB$dFi_ql1>P'&FB)8{o~(M++v⿎ isi _x[d=k ̍ J;!yxwN1^ܽ[]$LLK>J(+K0Y a"hd_,a nT2Lh\%+( =.Ycon]Щ 9$տ|KQ,Sj2[Zjr]H7Mdm&hO,'4?^bslw:M Be9R0tjJo_'CmFD3IX0+^K|H,)2P|߯M+mk۹[7VzŚ5Ɵjzם-XFb0c0K6>!hw~׬gI>(ie oo(XK`>Kse >%bL|^\NO5ƂȰ:-ISݳVz.}|JEX_B"mB[&b#3:1IUd r~rRnfotoxx-20.08/data/patterns/pattern_114.jpg000066400000000000000000000271111362435004500203730ustar00rootroot00000000000000JFIF 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-20.08/data/patterns/pattern_117.jpg000066400000000000000000000316551362435004500204060ustar00rootroot00000000000000JFIF 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-20.08/data/patterns/pattern_131.jpg000066400000000000000000000504441362435004500203770ustar00rootroot00000000000000JFIF 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-20.08/data/patterns/pebble-light.jpg000066400000000000000000000106251362435004500206710ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?4MMA"' e~z".*"c\GC,ֲ(Ge=Gzu?-ͩea \pU;sy߇ylDX.~Rb+!R1|{ןj}mm*ȁd,k=)ErúfkWvvR[ۑ*l\5K[m!KɁx`$G:xP_x+cr:e&y+KגN=+=_κtinlbslv6/^InE2i.Oh9`Z> t 5ظ2!8z/1 |!KFM3l¶v6= ͷ$XИ4]D@'oRqǥGZkT8%RQK}wc3].:%Ч?ƫytaniy<[^ o`JDۍvM[4MK6+6DxpzNR&&n%EǹSImxVN;5;p9+Kix{Xu}BA*H†6=(|fu[ޓ!E[!,i#QӥcxMW]ni w"m?N1zנ$)B|zt wc6Zٛ9R}nkrˬhzwلr[H$ 89wA6>sE1H SpNMC}KXo-ZE Rm[d<9r9 u}{wZb'iryR,k>9_]7K 0Rܒ$']`f~YiZڽΫ}w1-5>Ü A^s$P-||=A}OSдM"[;2X=gI$%$.@ݷI'\j֞JhwEfR2LbǬj]Zd @PNԥmJnGE6ʲJ3ׁ"tm"[xncs,P~DNjXnd{y#D.|A{Id )eN01U.4Oj&ot4v.唩'#ix3GMeMJ瑁A[𽥵-PA+d#&g O\HǦNg!b2 8Y v)4pnX[n;^(r$3|ym`0%Ln]ʓz5ỿ#_[hz#~@'ºL/:|6ZDr{T>:X2iZz׷4r} )uH wqgiQZqFϡ9mnI.>'үtQ2, Mv6, z}97'Qu}AɁY-AV>EqnN `@M6\ܶ|]k!P,4|۝ c=vs+L pP۟J崝6Úm!L_l;̯,I'=TSz~EDщf3ޘ'bޟ\ME$n!d*Q۹ȮbMBw pDF>n~G4%`&ҾsA\GfxRT񶼺u쑛 :plrvǾ1ޛJusg_\ΥPT9\eͥJi+/ğ1]V @c^ez|a= E2\ą31qFi+bԿmw{wE8}$'"7#GռC>}V:eV1,ehӿ^gSֵ=sIt)T6ԟc]^EW%D+1G֓fd%^ swfǭgO3MiIj(z/5s[ˀHi|s`-Zz}Al#RH =|]RK!!ʔqv(hZTK+HE귕tA Z "Zjn~[2AW$quZVoq\C*"1%rpx#޴|_ZxcWœy Jɭ{qjv+Oiiv[ot e,!GEKS]#;^SPEQ#^FI<b{mY}DK9E̋p Ԛ~.;{Y-;P![!=Ҹ7Ŗ &Z9HicA]{6 @23.. qXfΗE>OCK4 bs6{c$1Ayj:AL6q9P'nru/ ^ؐF ;W\u^J s}|Āq 1jVKDf[+1,Q0^8ֵ[LtiV.^ D2k7^Y"qp_9^ݖ#eFF$kXPE4RH7ya9Z贿 ]JŅw7]E\psj)S>ʖ#Lf+W'dYTTt~ d̻q:zWxszֵaqAldw|YG|*'&v2=N2-+БQaKw.Ѧ.Ȋ>efONEiq,L )~S_l L@yl⠴}(y=I=y4F?fotoxx-20.08/data/patterns/pool.jpg000066400000000000000000000076301362435004500173060ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?k WN_Sx 'ǷVt/ *Yܓ7s>Aej˸֧Ҵ Rrz{uIb"FY_r)URTVhsE.u10Eqj,׎]![xVTFHS5ieviNw}}xҔ}ձnlP6vdW.}URE gDUIHzrxZ\x.i-x5 `N-X$d'XqN4I=.swФ7dOնJ$a8M`\\T{vxXKo=}Ir]f.`%GaA֗0Q}?C]|1"Dۈpwg*<՟YhZӭ"Bdlsa\굤E'eR[ <3`ti5-6)*Hvq8j: @}֭ 41haɍ OUv{MoE xX%%Y%dÚTrSXwc#`FyVm.p*w?^}+nVմtļVvJ]#V{x1iT˩: RP#=jQvl(I6c suB]d'^Mb7Ӷ6@9ik(*kqެjD0%ղkDަ|?֥YT0=Q3I$0O{cṮ25&m&I_K)m.7\mӞ9=*KM]BXI@8?Q\(Ԕtߚ^ۏ7P2ĀQxm{E*8/.Jɷ9ͿM,ؓxA9Oz9a[i?i(jѽxi}ݴ62Z[ $H5c[2ϛ !,^k.4xGvc9Q13zJP7}Qqrz.+>smje;p² mFteC%@1״]dT *Oy?ixQQ[}2K%iZ`O\?j[Z*h7ڵōӬ"W"V'Z测^YZy^Fr=? qF&I0ON+cT Bxe)s2mjьg@GٴC>{:/.o \[Fuij:-r$8ą @'ùgRNd‘EX^_L`[D;S3̺vPQ}N o@~ Mz}sTPd F=+׵Iw+K[{jM=ee19A!Fq%R/܌t:-1c˔&#c*NsP_ǧiZD)o}`HC.@n'b(d`g#Yi$h%hRaN}iz-uYYNKi5T%e@~+$N/na'A G=}kDW!x:h[LD۷sNlsio\\2͑>{bOvq!&VAҠUӠ3yqY6'DkD\jhmi>7)MK#1ebɴu8R6Wz4#HEHeOΨGmB[¤6-tUcYNe8ـyzw}䨿Z/ ̎QlNpREc.xեyaW{'>|Rwf802ݕ8sDv" ٟauRO=G8ՍFJ:hf<0jN%E\ᕹ_ʻ RBY4FaPg׎9յ;Cz!\+ly@n´WXUK]N=3Q99).O~k=ʤ$g# zߔ>(qŒ[+jr7OOӭR״˯ "-Iy#޹T&8I{"ajCMI$#>fGC2sU+e6)v JZcBF˃Ųs^E9N"O2Nj%C/%NV6(tty(L0|{qϰ 1;0ˎTmf/K٠#eA+W4;VLFű1Tevs÷w%F$}im5%d<6W 'ԓTY,.e$4G.?E.e{ªK cTӼЎ[ p{TaAbЈu5)0GO>Z\Cs;,1_ZJ<ڞꚴg>Dj*cp8uڹu6vX z~ Fj^ʄ%’q07=:*Cujq54IM2a:JΗ*V hirr3؏Mҟ@\f~jQOq6rQiu/zDI8{*_iRon~AewƅZ+GFv>4C\'Jm'}#Kӡ/$kL**Tg#Ozi$ay6th"P]|\BhkO$__9fawhi$'`ƫmM{mPrNl]C[tmxjcKGTHw91ڬ\[XiH#ӕJʿ#9m48o⸕w0H<^[~\o BLԥNqUNRqVeC4$*{O_SL!LJ\@|26Rѱ I?* +JReұaSYZ]ߙsλm폚cߦ+BXhM|#^kW7r\H`L>R9*i9Ң,As4:Sqq8*jZ:ެCTdt-Llr@lt Jx#Mu Su Hc%1=Julg h"wH,9g>snBC[h4t5݂*rI2IMYz:vZk-I[q-t{y5gͳyyT6qtfotoxx-20.08/data/patterns/purple.jpg000066400000000000000000000065771362435004500176550ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?$L"71Rt3:O֦mt-COU;ZԂ@sޞVsgpv;yNJ,Bkb>[>99Aqߜڀ'gKT|,X}?S,:VJV~Tēf!5iR cy'@ dq߭3U/qr2v<@ q[2@$sOnmsj'P bx9L_A!'s6(~+X Iw`2編\lajp1FAVN?Wj t C+3`8,m$wsI2b!J JI "DZ #zDdn6vO?J@jn#8n epE⽴bgF`IC㊂m6++Wle[\v+I"-^ -nr) R:GvRio,ɑ߸Oެ[$@BS^j{7X01@a8Fl#sSY\G#ȓ:B+Z6z}:c0s\wF Uȱ7APx먠xMͭ1F 9TUgf(Aܧ8l}h53 Iw,rI8[p@ʀ.j0 OO{%ݷ8f1뚆qk3Ey*0ʸ~M\Q, I'⣷/\$E2dn8z'6w9ҕmݺ՛IWOԭrvPlw$}XR^fn\^`VyP7nMQOِZ#+ZHmTlıd#ǯ]Gw qN6RE#le rsځ n"k|g&Mt{֍ډ'` w,`3̫Cx"$.>D-Rv20bV"ϿZkowmAzz7#:%XnO%p9ӡo8rq(ܠqFXAu Ms0J`d9i/tc #}돔kIi4V+~SFNxv Oo Hwb(HׯS~^Eiڛ%̍b)0?^J b3ܨiQ|Oƛ=M{[J'~Gtfb79&BK3=hlYId | x'.cB@AV$D?<+FUyKy<='7(p sYͅC6 $`?2u#<N-HSʐJ۴rA<ʜqAh;fxZ1D.8& N3 Fr&!č-wVn$X)@P1F:{uIHeG 5$9Sɍ`9۞߃0]@?ٮ.ڟfr%ޝqT_ڏlb-age'?zP䲋WAbky' _`|W@mޡCA߾)eg[$ 5[u[K6uH 28Tl<3(RTCiWtqR&NhK<[FRD·ߎC7n ٞEv(0).{XgKIf6W,HLjm?I|4q[ɀ9h;ѮB<<U{)qϽIucIc,fSGkQ[ҡi-f3)|)ެ==WV[yȮ€"tdU-`dR=:N=47+ q р9'C-Z.pK$j~Gs*iWn 19H - 7akWi8YgߐF+"9!S941n#`7HZ[5x-Mܢt=q9tI}gN`1䁎xAK $sDVDcV$al GUDS2c@fҷik֔$,@niEZYi&h `=E9 )Tm݌~ULDz*%1Gހ Y\b9چQqNzgoJZho)/:Ž8(dDKP@p\cڀ?fotoxx-20.08/data/patterns/rings-green.jpg000066400000000000000000000063741362435004500205610ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?,g gi|mb%}iSVMSTǥOM^ /#W6Enj1tDsxyst9>X^:V ?t?·M,? ?fK42c] lΩS{LR0&\uU⹥a,ڰ՛Ow(^Bwggzr,*ҝ 3]I+8͸=9Sn[@tKf3ZU&%ًyUץ6#_Sh|;i;t+#qn6Ƹ(R&i<l$:#R7A*/آQ ? &M7LFAF 1+VIXrdWHڔhʜF5η]$W˃R'.VѱSG<'%< p$?.]c=X?ZjFuUJ$v=TX@2h3Jm |ֺVRJ>GoJқXwg`c^\RiB ^c/WnDp1ӐzqX^Lx[tf' } ٲaUѿM_G%Fb2Q:ZT4,L [V J:j"By1 Y:3V XЪk\5\YP:d ROПkV֥YmX|$mvDϔbXcwW>zQOGhЪ80'5iyVDL7'+%$='jzGحc Gַ7xLLK rr<}* I㹻5XPM/㱳\,}j*o3Υi;-_k="T8`Г\ׄ4i.Fv~?*嬲j ,y\0O>0͸mt1Ej ;]YBvi1ȹ[9KdhpW^+3Jp\ߊVL|+na[cxW>lg8g7aiQwat~? ?/1? "𴚅ڠeY=jK}der$XԂ1}9]nm0(<ǚj::,*2s_{|y~pfOYV{²:@>ֺ(&=)vX'ZΞЮIDRky3XpKF:b)y[J5^@"iqixQUF zZ6$8%B<봗,RoP7`L?$gۡFz5E L6/U2" rk y;I9isODx ڍ5Kqrҡ.ǁַxUR詇KEY-VT>ZZk~TLC]}yIɗpqإiVgVָ'PGu`H?wꑫx2+e+H;Imx3$ԓ]lՃ7$aHqXZʹL Yg')˕-L"U54%0ÓE rK[洎yAc=k[R"|? cuISQ^gfotoxx-20.08/data/patterns/sand.jpg000066400000000000000000000104101362435004500172500ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?`GU+]Nn ؁@*I#lrU7g40ʆWlO8P뒧wA$cYLV R;WIr n9g</b""HV)P! :}֥!!v2ۉA\#y#<5˲D*ƨX$ `rwgxc@J7 1# "VYyBBT,A gG1,fI&H-)!7%*#G%vXs"y眇-%Rp܃I8a2pJ˸v~cIɨg *wU#{gh ] }%G}tp[;+%69\( @EhD&ʴ6K/0x`RG4p cb't' xВOxARc J%8:FNzOnԟ]@* GN kiq)%}Jf1A*= 6L\lÂČNv jh MW\D[%\fId;q F%Ko/T@QAG+Ӵ~H;mer[hW98;--*#ʜ0Crr29k>- cmlGT(88C {kav4B@QGB:mqФʺL'oң#\g@]Y]`@B9vpzw_;UCF F{c&dxV: 0_q+ʀspA[2CjF~8JS!$أ|lpBQygG)T1͕\0c2<'!{@Xnses9ɫFd_56 py0y"N 16Tz- 1EUnF\ rO| 9,I1=<u9lc;b.$&tڊ$i@F~\t0x猓 c!$yJ1,0qOթw3Hnx'EN91tmDRB2J\bx#'#E;֥٤0n p-'зNkKpbfnz\AnmQ1%"FAu'qbVΒd!a* '9H n5åmhw6I܍ 8'5T !iQ\T 9peާk`u.@ȳ$fIe$+8!8x'` pD1!P 瀧rGlqbIHF˨Ɍpy0z֮-3]#e6s׾s$C$b2U7p "0Й!d w: @ #$-=BI CI;X6z cfe8L| Lٕ|pOgYK\,][$2#>pH;Ko-$Lr8T;Vƚlj2D3dOPӔ[imdvl䚜/eqo< Yf,Vbd_Ԟ3 H`a7RiKq+[gmүVړv:wԱeqҩVNЮo6}t8l'5-k|/n;FmxNzz׵ #k% ާu+JQ|); ue:Z[я,y'?ZκOHbӽ>֐vҋ&w]=m}Fr8DɌ$W7h'@t;5A2ܴѸʦ OZK[Ƥ]( =ZM"Y?02v9=zYcןċ%=}T_6Em~r~Hc ߠIH |H@$xLǧ^Ն`2%¹yAt:uh^ +{X2 gZ8ng_fyf`P-{m|W3*C|O7|fNJ7vM|c/"ȡQO +{j/{<ђ]X nqK5ăJ"^nqP?\Up&"̋ՋHW$6Z1/1n`F=f[,r41,jvm-qGP/p'zampCq2\w Ŷ~_U0-SqH VBy4].ig{K,x9= gnAw }1F@֙\jzwpnP2) 'LnY'a"[K1OaNKJ4].Y4]AFy.):QLyFs*oQX%yb:؛j̩47Yg @VdXJc\(Np#4è csm*B>D٨A=K+%у̖Z=$f"94$6~w-jCh7K(8AmuisI +JwqZGg]j:I7l_Pݞ-},d<Ҁ,n"?hȬ|&<$w=tQiJAingPJm.ZdKXae% aOV$pYsmkz0~i6'hlfӬQ[XZ@ll{{aY@ZYd 8 d=xu}TSvP6)z3U bZh֑0$Ow($dulsCZ|"k9ocYXpxotaԥ8 ,T>;QWӖ RF:35WYT~c\6KH#|#_2Id=1i:%$16Y6;YŽ0H3gҴ-y`I󂖚`:qzPN 0N֬K{v0I*m5\O~5PBƃIk\e M2h.1RS.$^f81GQ_ʖVrV `\zze~mbt$a";s{:o {B$$t^Z;4Y^9lXi~0no'g 7ڤEzOetɭ tM@[GuyrnaG>~TjOqXP•dC]$3[O9(J; 3F2>4JriaSPYmɩȥU *8kpt鼹f*e‚bVfGbu-L#4ƫ-Ԛ{ʲIJ_NZԟ6q.Osmomq[M:GkPGB0yZiV-6Yf_( gz/zuW:Xe`FP*ƔTG#>o J⤚H]9K3c<j}skoӥ6xau8K .GVm^R]&-"B>fk+NVHNuVb O֟ 4+ ԛY {ziPV:H$'qPi6omwHF>_lsԮeM'M[|P`m[:uƥ⋏661BMdzU+m 5‚МL?>fotoxx-20.08/data/patterns/space.jpg000066400000000000000000000043451362435004500174300ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?좊ִKجQS݋"Dc%cpl5 vm΋Y m2OIfV "Hr ERyok\H6i6~RX|_?y6:T8Z./1(C'x(~4\s[\4InifP a 9Scy*<׼cx7c/tG o%Lܜz R4e>rooa{,bREeȱ $##LΣөI)#{T5҉aE?6%]pN!psPKX-l( đQWbծ+,NGkCsr~QE4ES(p&&gaa!E2(UwʫUl%Vw$1֣p}=3cĶ:{ldDX.חp9vM)>U{\I\*H"3J:Q>C+I(+61ȋ$Nc*z2H oxVmQm!<A"gn+axQ,aX]bB°5K no]<(<  @- /fڪ %Yȓ SHvfl1fURmg+ŬDʷ2|r&FFa qL"'2ki#TPbԡ3 ܿ#Ƽ)Zާs\֮9gE I TII4+DPu_So].iSjA˓(QY?Wg: SVV$,ԭQ{=fFaΩ!Gd(Z)@';uQEYMBh-3fPT,oUčV )`J.ߠ|?|WvئxA\I`W_ߊ%6vZ^EID8>~W"䳒y$Yϙmн .MAe()c5N?fotoxx-20.08/data/patterns/stone.jpg000066400000000000000000000131171362435004500174620ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?k>kgov Uyv܈"uu 1U9p|3f4.nlW u1i7' JtmZi %搄Yw9ʆTa 8 {;jV+=<10[Ƞdf):Iф],ڄ gp߼Nj' ) [njq,|˽W# oڬ$W%īR覨e-J !x&o8*ʎX0a=Ei59G )Hr+~N8 (-_j,8.bDAm@D%ն6 U MB+&TUUدIª{+ ԼB&D,nm F8GTn pWE/ CR;ȄI&ʅjc9II@KU[IQBWM]A d pTдC62% )e+ ,fYIXn'x`Ndsu$VD. 0Q)%N8ݑެtVKb'nf|aIrn 'iF p* ;_d$#XYd؄q(A)E9tTYko4۴o2H՜ K2QC0nF(#FwsKw4P4sC6wm VRc\tZVؕbni;wè$e  /h:}/t"$"y! *ʥ} pTY 2xvm:;#:[;|'ˑU,] +Q;Y2\ÄY]9`T&h̑iW6is#)8*Z]wi# 6bP1SN$ M#үbDIu [$e#*ea#h݅&4} 3JőFR\$vf,X39ReCK+(cx K 8B馳7).-.Uı012?d!r#ࣗϲ+Y{wIQ_ɽ&a3H.q ju35sڛs36h&c!ԡff8#k(dKCc, ^vi8nHh[_4@ݕqTe|=-_?I-dO%Y}9V-nۡ# ,bE\,F\ZrS Q Y-jw{iO06 nb27N'r #J4튾E\YJLLd\[Dl70JnBNή҂X`Y^<]N^x0\Y^.#X)BM/6g,w[d 43D0)N͹>dg٬L cˑ#1HO  &;J}Fሸ[y "iK0p[oDIvZ-ϺMb7ˆ0X|1=6m#{hn"[EF\ . F KjwyZ#F,Fw/ Oږx'h..9`[pN =rY[Z.-d ;.'@*CBDmn!mB UnKȭ>C\!#,o*|PUKkbm๾Crn$#i@\lJ A{Y[} +ZǪVB3XrwnZRW@-'}RK۸5{#YJH\$1cIJ"A04piJ X bG'HӥK`wIa+$#XRцK,,E2èk}*TP m=͕ad{h"IBπI/`9v$@ho}29n, @cT|d]1VẐF>r=@bKF#X#T`g5==.M4&heX`02H2V!XnNE:u!f V@3`%eu H`.@u 4 .$mly,ٸcmRѐ Io3O /%xjwXЫò !+o^j"S`gQ:K9vɃ!P 8-;Y&xk-GXڙs $dsonZ 6pu(dHg aU2e(]գ Ow\ kF|Zdos+$88&WO֓wFH`Y+9+B;k S$ ,)<Te$We?.oDWb#,m2Iey$ Οoy,r~W1JTlBVB1{um e@ ^DRv# M!3KR% n$J5呱.O@I'9wO gƶ .x!($er nł{9}:GS `%7y" g Zt J(Dwj[̳FeV)c#e%L>e~8xf&H 5RBJ~n\)#99'ޑ-APc B/0w)ml>l@Y*[#P^Rh.< ڰH爮T3n9 H'YfX٥Xoe,H- E&2Z/#9.dL[Gbc}N.m݃$s9`xmo-bNŠڅq[,4ҥuU$n!K dwKqa j2Ki=Omm޽I+bAj-tsql#1IKpȱo^B3 H2ՒT[w'W H\d|sFB3ȯݭ .$VXn|Ĉ!X1 *ӾU(+d'qS"P"A<-sl2e]1 )'ߜdPFBvtfcTQ(rW6ڽX]l%|J/flHu-{QFYu/6^N]R9Cg`JrYCwv221n6a$O ;/BSoMj$D[(UI2wT_y>V"V 2i98 %I$Su(ln!BٸH; 1 aHizvrdM3,.I,ĝCd/b ՎxѬIsLel]%c$*H!wӫG]v+,ZCwY 1E,j)5YIPk28wIz6| JsCi7i"\F, (ɇP)x^ *xJ쁂`Ȳ`xdB51tmgLkZM.(]5HGͷbt jiڥ4 ֍la3gs1 |_ZҵӃmC,Hdۀ,*:sa-G[6E1#(p1W&%{yBI欄ܱڥHITP݌D,IqɕwT6 0ӊ rXuR;F!42G[ yJ [+LXFBɋ0&WRw#[;i*(̲evMwH,1r돣wv.B\1>W r6`CVIx5[i#Del;rhA$#mKwya0yBm Gvvjk\4aP(# g(ZV=]^:j >/.nW+4/ &@FU8`Åfotoxx-20.08/data/patterns/structure_darkgreen.gif000066400000000000000000000130031362435004500223730ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?#S7oOo:Ld+D۷aW!qOt,m;k#C7roFrHe2yl{-7[o61H R:K*6u ֖X\麶gO,# iQ#e >9C9՟)k W_adS[[[bxVLq'EVR9"H/"уϦ,a?uAvnxsm&kh4PMg18v9"z z&Gd>KܲZ&'=`$K^]KI\Y.KmF9]Dd-)Mps8$DH ` ,Pbnoxg*C0!Q3%PN4FD6n,ͥf̣-QO-Οqi43."$pncYnQ[^EQnm$Wo,y;H*rxֺ(H*ʒDI0ۺ\}$qҪ61EţE,wXzG C( IFw-[\.$ۺeA)5"ޑ=_>p.c-P҆(vʑlwŻ=!&ేGURwbm]Y0(D7RȰ>& Is 'PeZkv6!@ FR/:y #:\WrHmsX֤ӂ ,fBB c:͚m2}q{s ȈHM?vN"EUv;,3Un(ilVdoIgGg hl'r|+} ,(Mk4o$ u6EM_^ӼI%ΞQO$Mऱ: ךIm# jnuO2q*D69^ӟ=Gg]D";a@R91;*5[iPx+RޑKXͪklӥt}|N q]ZicX-a0ȲE"4FJz%ޑi."=I'99*ÎGzsz~[]I}qcuۤq淒Ll]~*piV݉-gvb".9ͤrHU#ry` Oǩڝ-&K[%hFŎuaXHl9黑RKN a;isy!IV\d[ϘHbAEm̀ʅẌfXjUw1JO11qjSwwHwd`aHin/' }alp9تo> {H3=BBO/8VMmT]Ye}u9Ho4^l'O栽`-V.ŦGFp$@OKd S%_r ZPvr_6Kj#28"E*EXӵ |@[AqkCI<.0KmFjDEX{#t&ԙT'2QqA1x`4Vb!ۨO!Y-ɑx4)_f&uaX5yyȐ͌Rn-g42ϩ<Z?+#|ʹr iQU7[1z"Y%ʸib3BYp ʲ榶h4+gkMA#oے};1R@%Jj/ԒI}ZZ*+Gi_Y2jj{ &+EP!2Z,l!%RJz6aayi"HOȒ9D=JG3[跓YjG 66U9>n7rGBW}GU{[9Fk|"XJ_@ǦḌfM5ՕӵuDc d-}*-Ᏸj6 K6 dWP<U Ω5݅ޣ:|34 VfJ}JKkf/-"kwcLFX0 "ƿ6sIln~ciAIL`98i. [H- v2k`B0AR2[o5K 2JK{610PN\x4vy--G%$, A;H>Wn sJoB 6%naݸI#Duec}>KTG* ~d}܃E0q0Mfkǚ~y]g݀zgkx+u7_ aak,ђ۠bećSJkU9X-Log,:E#>FȈ܄7m-eo"@,ܖl Ff4] Qh4*cʜ#+4kx$$ J 9B'y\In-a 4#km[XHqX/U[&;𳪆y;kgSay}"HԉP\IXX|;*b=č,re~le/-x^$|kǎxC+O $w{[ r K t:`,O$Sa X5wrޓ6%j:j~ '̺z;&˩%hts6m&Xxo&gq[|P~#hXf> m3=xkT2|9UK#SHӭ.t@PoXz>|AԧV}O¡CuGq_Sr+4{"K,&NN:v$zCu M }N++* {nF һZhv oj/5i &]'#{h7wztÖKdf:#YYjv7Nmcln&(+(zx·:n{ j(̋ݷnVBc{<c˞k5gx֦6|#m'0 [2,[k7z}r1z/4vkŻky$*Akv,h65 5BUyu:Y|'۽Cx $>4 F<[wH"fKW=W9a=&2GLQbBfT `xMoh,^Q.{k eRq;WmR $2D-;t5ROxkA}U5].̫33^q {lпv +GbԎ}Ꟊ_CZR\}4|oPmmK[Yg>D;8]H+_V h$VqjgGh/`g-w z~9ֵ Y{U񷅮=F;g`6 r0{Vd |Ik5|DL𜎿Z,(Х̆H-8*FV^|M_^˨[Ki1!`"|Kb[_Ad1&%A"Wnߋ|. g#z?;ڿhc{ڰ,~$fK/v%*6gGz|dwehv[PE}Ŗž2 Uf`=zq>-oD0500 q;1t[fp-.n>i T`89Mu:?|C/\O\Om9Ro:CZ-{Y3w^98:VXk%;\p>'*.>/lXoAZѳ֑p-7&}2EwڗᇊdU[voq]p[qU5O x~">xAdqXvɵuc_BiYjDZYU u9SnE=,ufdIcxcW;7kCÖ>8Q5M$L "`Oץ-7/V$#Ztz7,MPDdr[}3Ti2sXxvh3FF=+ 4V_p#FGC0yg;\M#-Vieq%N=;Pxk_1ѵiw)ee,$v0<;7&b+#؉73=:V ':⍽͝Eh1ev{kJ)/\-._E)@ΞoL&,+pw E(LgלcLc= %ݤ_L=| *O$aGR мl5ţkzeC%s1ph:?[֒hdЇ+ Rxggghv/4q}5 `fA kVPR}9O+Oڜ7գXE0Fh?5 L q0'@OCSx|IǦ:Fͤ$r E\w muF/Arڝک +LgƑy۠a)h|u,u8B'[& J;dR88J KPm!Y\f;钾T fKC(|o knLx!D᪭׎5-Ể+, 1Cցnj4*Ax] #Ilɧܭ-I nA`yV8{?]x٧.dkk"2.8ϯ|6G#м*\Bŗ`SZm-nhkWG nyF l{-)gzSbys :<[RD#k+g6>2f7!W{SE{cS☵aZz"Y[ḑQV>?-1m,zc:Z#+x[OLp~b|Pޛ[]Oogo>c8"SYT@z!HumW&X]+7 dt?); |t%E* w$IK[xQgSUzKn6cš%&=WW0\( z >x?U=Nj܄+2`WZ>ayui^6$}T=J|Zҵt[kYTf _u K 8>'kObi-Un_~jko~{.Gu[Q";04+ǶV:d׶t 3\G9^e3)e1s@xB]x]On@e+ӿZl>xcHL[hPƏ>C[jpʁI;J/Fs5uվop^ 'Mܩ&Կ9}5xWe3 @AeK:>af lc\1d<7|JsEu=s$WaOa5/>!4AytTI#@ƅEzU5`bFx}a<s__jC{i h9T!>q^fVOlH564JxN="BIP}|У>1 ڨm돘sK-bKim`k;Aio}"Yn*~$sK_<GcA(@М(fotoxx-20.08/data/patterns/wet-turquoise.jpg000066400000000000000000000047051362435004500211720ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?ʨp zi 1=4+pL gWVyrm%ۂz}kʮW;4Y2$LĐXz*"VUCr,n'rw yր7Bmz JjniK*?BH@@)֌7($ {~4 VFbUA#J#B9L&YPGA ~PVA!屜lv ӡ;HV$(!As?FQ>q=y;-%Fn?gj c4aMwJ"Y7JFluoCLC A4 Aez).;HH$AOiD!N 21 = 0Xc҄Hz?>ZjI5Ñ:zP"4LlqϽJdዲ|=}i]~Ge8[ \fEprX~floC @bUDW;Ta(oJvW$ǖpUC3{}hӡdyF,<ՃIbVwDc7 m`}1@ vw7 'QH\گe!p7{U=!!_v8-H\|u 1Cȩnq`A險yq#lv@CtumAb=鐺K!r>\P̐cRe18YPKpHQ`_2ABˀ>nކ=>J",DN Q\⪾J,ql㞔FJ`sHiJl18 V<"ԯП~5sM |*2[vr==-'.\\l,\c}^otf* T BM1>Qs1ZF0ĒNv<^6u皖D4J dʌ?oZ@.O o&) a-H zPn2~v?wF;;P#@_%uF[ĕ,z4 ( Pnf.K9RUw( SD#"IxS'h@2Xa"pJ7 dq@{~=)"˜ey6.+ %.%m^VD/\ϯ*H;sQ_ZLg'->ݹ R6E'Gjm[i(Eh#aI#׊R$P$BU`P Pr Sm#$J )95G qfb b#Ir?/΀A`1d~u6[p ۀWGZF`wb!,Pqׯ@2p `0Jq NFz"a:9M 8>S֣y;e O$h;NNݵf`iH# [󥹒 [TMGB,b(oUJIJ@B1-qWK3л=3ڭ1G@6I QqCc,U2Gl|5G!enӫw 繑tik*H8 IH]Iv9|PąJ?'gFxP6g=)@eQ"*##,aT !Iޮwn2e<5@G7Z,QIfS4bKԦ%W$|͐G*FX#8;z #D*}h['W9fo=-Aw|Wu!$TbkF/EodNXEfmd1(b2yYdqb棊 mUfV9Jy f ի?巌fotoxx-20.08/data/patterns/wood.jpg000066400000000000000000000133051362435004500173010ustar00rootroot00000000000000JFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?K&cm$``P[ QV)3!ӭq ^34[9ʒQNAvDtk/3߅Igm!ۉ.he`־pi%*l{p@QNx_$؎R pՕn#%[ݷ$qHŹ0æOgN]jpX Ģܰ B)3XxZ/#Sm0p]N# $` 9m h9ج1Ñ >uku'K SWcIwak3i }0Bb4Wyc8KO+>Nh™ ִ':2vNARqq/6;S_^Q7קiP:]sv;A$ MM5*κ]( v0z䲞TAt3&t}F)8 31fEpM6cJ:tk?0B@=1铪>kF._mbU֣KF:]x !Ж 1rI9 o5yXX:vyP TǮvMGH,rIak1H>ܜ={n91)e(]<3p urm< n+7S^1k⓪پٯMjEu($o`FTlg&ǸCc1k8l ˓GdWXW1ݥR o"0|c*6ǑOJʓiRK,b;8;zP⺓oHʾI'H%i&3GUS~%M%0< &AaAF`=U[KK#%lhK* 2@;.Nwӂ#k6ϴʻ:[nw 9Zk2w=d-wF eX7Zv?l ăD>4 w++@Ԙ1`#~?)9 @=fUڿ47R7r +G:w;i MxwiNNR8G.. wmD˂#xֳiw6mhd]1O]Bl{Oktm :~'R 6v,@ ؒ3щ+xb `F2*6z\.R&1AA9JR*S4ta FUIze}Ŋ  @oQCl(IQ:4hGx`F䅤s:p'V<~@Y,b͡ޱmTqhquZ+ͯQ˵fI\r J Rc&֭.R4C+GԺ |J:s$eIRr^DYh֌c,l >U[] FԼDcv!Q"pD >۔RgyGg?-ŽF0{퓴I^;Kt$@2.C;eXrsytWwk! II{/d>\C Ec` MAh8VKn|') .hbYl<9;ee,XD’sryn ate `4 36p9-jUqekh55}g\MficPXDi$2Ik2?\K&I vDg?6cVQ!О, t260rQxcI95h&~>z"R;]˽s\R#X땜i̴!ͫaPFS#/PXk_2&gy* DgَЁЮm)mFdGRU9H A3*zYQK4v^u]hi)(O (,da2W*VrZYM.Y]KPꁚ0KP2J8>X*;U=Atf+6"j)K+|YlO\Mqo`4["3Uv$rjQ"/QWsbEHF.VLi#-hRA+20dtz~x,6ci ٨^^B8od`:̏ő sB8c$0&.qО{MNku[@3 шl98$qj-G_rv?šm+ wё68>Rin4|VR]H[?9 Kn0f Z?* Gn*4voUgG:ұ!ZfsN1T⣲{km{5Z%Vn#![Alnnў?7L};r4`ˌSIHM2#%Åp"0Xܞ{ľ$ɧ@G1UZV-J'9-xZ]N,~a֤e9UEc)x I$(ogH+M*#f/AUqm{X]rW%\yTg/7${XʬK!GS:MfuG4$U-uU M%YVf@ H/bj@#v:ȉn +w-8V4KB8/=c]eqXd4Gpwss^gBVB 6r˷{#6_ p8<φ_Q;nM'H7F *H (;(`\w1і/-+hZ3xH&Pwm>=x2awxr)\mmۻPxO sobш<1km mdq(: 8y;XN&ehCM$0a`?ʌ}5 3T{f3rF3, .k] #纖Yʡ1Gծ<ȣdvAK`HV+%BiUH8]+ 1I zc䊷 E@>(T2y.@m9m%>H%4 N%IН#9qȮd 2槪O dErVm+R"˨tD~ avS`5IGBHZ6?moH{#jyaɀ'%?xr.ЍW}Q]mB8O#'2L]$- >jNw:`{9{H̵8I[s6>_jAmR @ 2]0#n00z`u-\ג]HW:A*6HmP09tbbıyEԱh<sۜ)sGEi6?fotoxx-20.08/data/slideshow-tone.oga000066400000000000000000000415721362435004500174320ustar00rootroot00000000000000OggSk\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) |CALIBRATE PRINTER| - calibrate printer color - compensate for color shifts |GRID LINES| - show or remove grid lines, set line count or spacing |LINE COLOR| - set color for select area outlines |DARK/BRIGHT PIXELS| - tool to highlight clipping |MAP DEAD PIXELS| - map dead pixels from camera sensor defects or dust specks |MAP PIXEL BIAS| - map pixel RGB level variances from camera sensor variances |MONITOR COLOR| - tool to adjust monitor brightness and contrast |MONITOR GAMMA| - tool to adjust monitor gamma |CHANGE LANGUAGE| - change the language of the menus and dialogs |MISSING TRANSLATIONS| - list missing translations for the current language |SHOW RESOURCES| - show CPU time and current memory usage |APPIMAGE FILES| - list the files included in the Appimage container |ZAPPCRASH TEST| - test crash report with source code line numbers |HELP MENU| - show user guide, recent changes, log file, web site |OTHER TOPICS| - |VIDEO FILES| - show and play video clips included in image collection |WEB UPLOAD| - how to select and upload a batch of images to Flickr, etc. |ORGANIZING IMAGES| - options for image organization and efficient searching |TRANSLATIONS| - adding or updating a translation |RECENT CHANGES| - list of recent user guide changes |TECHNICAL NOTES| - some technical information about Fotoxx |ALL TOPICS INDEX| alphabetical index of all topics in this document FOTOXX OVERVIEW \_Description Fotoxx is a free open source 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 has a rich set of editing, enhancement, repair, and special effects functions. Image modifications are displayed instantly in a full-size image, allowing quick interactive optimization. Finding desired images can be made fast and easy, even for a collection over 100K images. Albums can be created and images arranged with drag and drop. Images can be viewed by clicking markers on an interactive world map, scalable from street to continent. A detailed list of Fotoxx capabilities is available below. \_Hardware Requirements Fotoxx works best on a strong computer, e.g. a 2+ GHz multi-core CPU with 8+ GB RAM. A weaker computer will generally work, but may be slow for some functions and unable to edit large images. A monitor smaller than HD (1920x1080) will feel too small for some functions using multiple windows. The monitor should have accurate color (most do not). \_Software Requirements Most recent Linux distributions can be used to host Fotoxx (64-bit architecture only). \_License and Warranty Fotoxx is licensed under the GNU General Public License v3. Fotoxx source code is free to use, modify, and share with others. Fotoxx is not warranted for any purpose, but if you find a bug, I will try to fix it. mkornelix@gmail.com \_Origin and Contact Fotoxx originates from the author's web site: kornelix.net. If you have a question, suggestion, or problem, you may contact me. mkornelix@gmail.com \_GitLab link GitLab is used for general information, team members, issues tracking, and a source code repository where development history is kept. \_Privacy Policy Fotoxx does not collect any personal data. Usage statistics may be collected, but this data cannot be associated with a person or location. Details: \_https://gitlab.com/fotoxx/fotoxx/wikis/privacy \_Downloads The latest source code (tarball) is available at kornelix.net. Building Fotoxx from source is not difficult and instructions are provided. An Appimage package is also provided. Appimage is a package format that works on most flavors of Linux. \_Packages Installable packages for many popular Linux distributions can be found here: \_https://repology.org/metapackage/fotoxx/packages Some of these are quite old and should be avoided. \_Updates and Bug Fixes Fotoxx is updated periodically with new or improved capabilities and bug fixes. Serious bugs are generally corrected within a few days of being known, if possible, and the web site is updated with a new release. Other web sites and Linux package repositories may or may not be updated for bug fixes (usually not). \_Optional Package\_ - fotoxx-maps This is a set of geographic maps covering the world. They show image locations as markers (red dots) that can be clicked to display a corresponding image gallery. Modern cameras with GPS receivers automatically add geotags (earth coordinates and location names) to photos, and these are used by Fotoxx to mark image locations on a map. Geotags can also be added to images individually or with a Fotoxx batch utility. You can add maps of your own at any scale, and markers for your images will be added automatically. A source tarball and Appimage package are available as described above. If you have a good internet connection, you may not need this package, because Fotoxx can also use interactive and scalable maps obtained as needed from an internet map service. \_User Guide Fotoxx is easy to use but unconventional. To avoid getting stuck, please read the introductory sections of this user guide. Help for individual functions can be referenced as needed during use, by using the F1 key. \_Phone Home Fotoxx will occasionally send a message to its web host for usage statistics. Nothing is kept that could be associated with a person or location. If you still object, this feature can be turned off in Tools > Preferences. Fotoxx privacy policy: \_https://gitlab.com/fotoxx/fotoxx/wikis/privacy \_Translations Translations of the user interface are available for French, German, Spanish, Catalan, Italian, and Portuguese. If you can help with translations, review the topic |TRANSLATIONS|. The user guide (this document) is only available in English. FOTOXX CAPABILITIES • Organize and manage a very large photo/image collection. • Thumbnail browser/navigator with adjustable thumb size and list view. • Click thumbnail for full-size view, image zoom in/out and pan/scroll. • RAW file conversion, single or batch, save with 8 or 16 bits/color. • Edit RAW files directly using RawTherapee or the Fotoxx edit functions. • Large set of functions to edit, repair, enhance, and transform images. • Internal processing in 24 bits per color (float), output in 8 or 16 bits. • Edited files have a version number, originals are retained by default. • Fast edit visual feedback using the full image or selected zoom-in area. • Undo/Redo edits, go back and forth to compare original and edited versions. • Conventional edit functions include: brightness, contrast, color balance, saturation, trim/crop, rotate, upright, resize, sharpen, denoise, blur, paint, draw text/line/arrow/box, red eyes, smart erase (remove spoilers), fix perspective, warp/unwarp, HDR, panorama, stack/edit, photo montage ... • Advanced edit functions to bring out details and add flair to an image: Edit brightness distribution, Global/zonal Retinex, Gradients ... • A large set of creative special effects and arty transforms is available: sketch, cartoon, drawing, emboss, tiles, dither, painting, mosaic, sphere, tiny planet, Escher spiral, custom convolution kernels ... • Most edit functions can be 'painted' locally and gradually with the mouse. • Select image objects or areas to edit separately from the background: outline by hand, follow feature edges, 'flood' into matching colors ... • Special selection tool for complex image features (e.g. hair, foliage). • Copy areas within and across images by mouse painting and blending. • Create or maintain transparent image areas while editing. • Mashup: arrange images and text in an arbitrary layout using the mouse. • Custom scripts: record a series of edits and use as a new edit function. • Custom favorites menu: select and arrange icons and text in popup window. • Plugins: use other edit apps (e.g. Gimp) as Fotoxx plug-in edit function. • Batch tools for renaming, resizing, converting, processing RAW files, adding/revising/reporting metadata, and executing custom edit scripts. • Metadata edit and report (tags, dates, captions, geotags ... any metadata). • Search images using any metadata and folder/file names or partial names: dates, tags, locations, ratings, captions, comments, exposure data ... • Show a table of image locations and date groups, click for image gallery. • Show an image calendar, click on year or month for a gallery of images. • Click markers on a scalable internet map for gallery of images at location. • Use locally stored maps: world, continents, nations, cities, custom maps. • View 360 degree panorama image (Google Street View format). • Show video files (first frame) in thumbnails, view full size, play video. • Show animated GIF files (first frame) in thumbnails, play animation. • Bookmarks: assign names to file locations, list names, goto name (gallery). • Create albums linking selected images. Arrange sequence by drag and drop. • Slide show: show album images with animated transitions and pan/zoom. • Print an image at any scale. Printer color calibration tool is available. • Custom keyboard shortcuts can be assigned to most functions. • Comprehensive user guide and context help popups via F1 key. FOTOXX USAGE INSTALLATION There are three variations of Fotoxx installation: • Use the package from your Linux distribution (Debian, RPM, others). Install Fotoxx like any other package. • Install the Appimage package from the kornelix.net download page. Follow the instructions on the download page to install the package. • Source code package (tarball) from the kornelix.net download page. Follow the instructions on the download page to build Fotoxx. Download web page: \_https://kornelix.net/downloads/downloads.html \_Fotoxx outboard programs These are separate programs used to extend the functionality of Fotoxx. Some are mandatory and some are optional. The optional ones need to be installed only if you want to use the corresponding functionality. This can be decided later. If Fotoxx was installed using a package from your Linux distribution, some of these additional programs are installed already. To install any others, use your package manager. When Fotoxx is started from a terminal, all missing outboard programs are listed. If any mandatory programs are missing, Fotoxx exits with a message asking you to install them. • dcraw mandatory create RAW file thumbnails, open RAW files • exiftool mandatory read and write metadata in image files • addr2line important add source code line numbers to crash reports • heif-convert optional open Apple iPhone HEIF photo files (.heic) • opj_decompress optional open jpeg2000 files image files (.jp2) • ffmpeg optional include video thumbnails in galleries, click to play • hugin optional alternative panorama image tool • rawtherapee optional edit RAW images using this tool within Fotoxx • rawtherapee-cli optional load RAW files for viewing or editing in Fotoxx • growisofs optional copy selected image files to DVD or BlueRay disc Note: package naming and contents vary by Linux distro (the usual chaos). The above names, or something similar, should be findable in your Linux flavor. Happy hunting. Debian (Ubuntu) packages (where different from above): libimage-exiftool-perl, binutils (addr2line), heif-examples, libopenjp2-tools Fedora packages: • Packages: openjpeg2-tools libheif libheif-devel (heif is non-free) • Fotoxx build: https://apps.fedoraproject.org/packages/fotoxx/sources/spec/ • How to use non-free repository: https://rpmfusion.org/Configuration/ \_Apple iPhone HEIF files (.heic) Recent iPhones can produce image files with the extension .heic as an option to .jpg. If the converter program heif-convert is installed, Fotoxx will recognize these files. When opened, a .jpg temporary file is created and viewed. If the image is modified and saved, a versioned .jpg file is created, e.g. filename.heic --> filename.v01.jpg. \_JPEG-2000 files (.jp2) If the converter program opj_decompress is installed, Fotoxx will recognize these files. When opened, a .tif temporary file is created and viewed. If the image is modified and saved, a versioned .tif file is created, e.g. filename.jp2 --> filename.v01.tif. If the input .jp2 file has greater than 8-bit color depth, the .tif file has 16-bit color. \_Fotoxx home This is where user preferences, settings and data are kept (image files remain at their original locations). The default is /home//.fotoxx (like most Linux apps). If you have multiple Linux installs and want to share the same Fotoxx data, you can move it elsewhere. This location is called "fotoxx home". See |MOVE FOTOXX HOME|. FIRST STARTUP +image: first-startup.jpg When Fotoxx is first started, it will ask to index your image files. This makes the gallery pages (thumbnails) work fast, enables fast search based on indexed metadata (tags, photo date, location, comments ...), and enables clickable map markers to view images by location. You can defer this step if you wish, but search, batch, and map functions will be disabled and galleries will be slow. The index function will ask you for one or more 'top' folders (e.g. /home//Photos). Subfolders are automatically included. Other non-image files can be mixed-in and will be ignored. Indexing speed can range from a few hundred to a few thousand images per minute, depending on image types/sizes and computer speed. Fotoxx does not modify or copy your image files - it only reads them to make the file index and thumbnails. The index file is typically 2% of your total image collection size. INDEX FUNCTION +image: index-files1.jpg \_Top image Folders These are the topmost folders containing your image files, e.g. /home//Pictures. Subfolders underneath your top folders are automatically included, to any depth. Use the [select] button to locate and add your top image folders, one or more. Other files may be mixed with your image files. It may be useful to select the subfolders instead of the parent (as shown above), which will give you a simpler access to the individual folders. A practical limit is about 30 Top folders. Each may have hundreds of subfolders. \_Thumbnails This is the folder where thumbnail files will be placed. These are typically 2% as large as your image files (30 KB compared to megabytes). You can use the supplied default or choose another location. Indexing will run faster if this is on separate physical storage from the image files, especially if rotating discs are used. \_Metadata Items The following metadata is included in the index by default: folder and file names, photo date/time, file creation or modification date/time, caption, comments, rating (1-5 stars), tags (keywords for image content), geotags (country and city/location names and latitude/longitude). Searching a large image collection for one or more of these items is almost instantaneous. You can choose up to 20 extra metadata items to include in the index. These will also be almost instantaneous to search. You may not need this. You can skip this initially and add items later if needed. For more details, see |INDEX FILES|. \_Index Time Required If you have many thousands of image files, the initial index function may need considerable time. The speed can range from 500 to 10K files per minute, depending on processor speed, disk speed, and average image file size: slow case: old laptop, 5400 rpm disk, large image files fast case: 4 GHz desktop, 4 processor cores, SSD disk, small image files For an external 5400 rpm disk connected with a USB cable, I have measured 550/min. for USB-2, and 1167/min. for USB-3, using large (avg. 2.3 MB) JPEG files. When you add new image files to your collection, the next Fotoxx startup will index only the new files. If there are only a few new files, startup will be quite fast. If there are hundreds or thousands of new files, startup will need time to index them, as described above. \_Removable Storage If your top image folders include folders on a removable drive, and you run Fotoxx without the drive mounted, the index data and thumbnails are still preserved, even though the image files have temporarily disappeared. When the removable drive is mounted again, and Fotoxx is started, the last status is restored. If any image files were modified in the interim, this is detected and the index data and thumbnails are updated. For more details, see |INDEX FILES|. USER GUIDE Review the first few pages of the User Guide (Help menu) before trying to use Fotoxx. The rest can be used for reference as needed. When using Fotoxx, press F1 at any time to view information for the current or last used menu function. Fotoxx is easy to use but unconventional. If you try to use Fotoxx while ignorant, you will become frustrated and may conclude that Fotoxx is hard to use. You would be wrong. Fotoxx has deep functionality. Do not expect to master Fotoxx in a few minutes. The [home] key jumps to the top (table of contents). The [end] key jumps to the end (alphabetical index). To scroll the text, use arrow-up/down and page-up/down keys. The User Guide dialog has 2 buttons: [hide] and [find]. [hide] moves the dialog off screen without losing its data. This makes it return instantly when F1 is pressed. The [find] button opens a dialog for a text search. Enter the desired text and press the [find] button in this search dialog. Press again for the next occurrence. Text enclosed by vertical bars is a clickable link to a topic, e.g. |INDEX FILES|. Use the keyboard left/right arrow keys or the buttons [<] and [>] to go back and forth between pages accessed by clicking on such links. |INDEX FILES| <-- [click this text and press [<] to see what happens] Web links (starting with 'http') can be clicked to view the page in your web browser. WINDOW VIEWS AND MENUS +image: fotoxx-views.jpg \_Menu Buttons The icons in the left side panel are menus. When one is clicked, a popup menu appears. These are functions for image viewing and editing, searching, reports, and utilities. The top three icons/menus (File View, Gallery View, Map View) can be right clicked to set the corresponding view mode. The keyboard keys F, G, M can also be used for this. FILE VIEW File View is for viewing or editing an image. To zoom an image, left-click a position on the image. The image will grow with each click and the clicked position will move to center. A right-click will restore the image to fit within the window. The Z-key can also be used to alternate between fit-to-window and 1x (100%) image size (1 image pixel is 1 screen pixel). 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 preferences setting. The movement may be 1:1 with the mouse, or may be magnified for faster movement. The mouse wheel can 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. Left/right click the Prev/Next menu button, or press the left/right arrow key, to move to the previous or next image in the current gallery. Use the menu buttons (Edit, Enhance, Warp, etc.) to modify the image. Use the File Save button to save a modified file (replace the file, create a new version, or create a new file). GALLERY VIEW All image files in the current folder are shown as thumbnails. This may be hundreds or thousands of images. You can scroll through the thumbnails and navigate to other folders. Use the vertical scroll bar (right side) to scroll up and down within the gallery. The keyboard keys can also be used: the up and down arrow keys scroll rows, the page up and down keys scroll pages, and the home and end keys jump to the beginning (top) and end (bottom) of the gallery. You can also scroll up/down with the mouse wheel. Use the |[+-]| menu button or the keyboard [+] and [–] keys to change the thumbnail size, or to show the gallery in |LIST VIEW| and |META VIEW| formats. Clicking on a thumbnail will change to File View and display the image full size. This image is now the "current image". Pressing the G key will return to Gallery View, with the current image scrolled to the top row. The folder path is shown at the top of the gallery window, with one button per folder level. Click one of the buttons to go to that folder. Its subfolders will be shown as folder thumbnails. Click one of them to go to that folder. Click the [TOP] button to choose another top image folder (if more than one), the root folder ( / ) your home folder, your Desktop folder, a gallery of the most recently viewed images, or a gallery of the most recent images added or modified. There are several types of galleries: • Folder: all the image files in a single folder • Search results: images found by a search function, from various folders • Images in an Album: an arbitrary collection from various folders • Recent Files: the most recently viewed or edited image files • Newest Files: image files most recently added or modified • Report: thumbnail images with associated text (metadata) The gallery window title bar will show the folder name (path), the album name, or "Search Results" or "Recent Files" or "Newest Files". If the gallery corresponds to a folder, buttons for navigating to parent folders are shown in the top panel. The other gallery types have only the buttons for Album (select an album) and TOP (go to a top image folder). A gallery thumbnail has a right-click menu with some commonly used functions. One of these is Popup Image, which creates a popup window with a larger image that can be rapidly zoomed to any size with the mouse wheel. Many popup windows can be open at once. This is useful for comparing multiple photos or multiple edited versions. Popup Image Manipulation: • Click thumbnail with middle mouse button: popup image appears • Click thumbnail with shift + left mouse button: popup image appears • Mouse scroll wheel: zoom the popup image bigger or smaller • Key F11: make popup image full screen, or return to prior size • Left mouse button: close the popup image • Escape key: close the popup image MAP VIEW View maps containing markers corresponding to image locations. Click on a marker for a Gallery View of the corresponding images. This depends on your image files having embedded geotag data (latitude, longitude). Modern cameras insert this automatically, and you can also add this data yourself. Two types of maps are available. The better option is Net Map, a scalable internet map that can be zoomed from street level to continental level. If your internet connection is poor, you can use the File Map option. These are downloaded map files of countries and continents having limited zoom range. These are contained in the optional package fotoxx-maps. You can also add your own maps at any scale (city, park, campus, region, country ...). The Net Map option uses a world map from an internet map service. The functionality is similar and superior to File Map, but depends on having a fast and reliable internet connection. Initially a world map is displayed. Use the mouse wheel (or double-click) to zoom-in to any location. Shift the map center using mouse drag. The entire world is represented and can be viewed at any scale down to street-level. You can choose from two net sources, one completely open and one requiring a key (free for normal usage). If your internet connection is slow or unreliable, it may be best to use the File Map method. This is also the only way to make a custom map. File maps are locally stored data files containing map images. Use the Choose Map menu to select a map file. A left click on any map area will expand that area to a much larger size, and a right click will return to the whole map view. The mouse wheel also works. Markers are shown where there are images with geotags. The markers can be clicked to show a Gallery View of the corresponding images. Initial maps are provided by the optional package fotoxx-maps (about 100 MB). You can also add your own maps. MENU SUMMARY +image: menu-summary.jpg The image above is an overview of the functions available in each menu group. A left-click on a menu icon will show a popup menu with all of the functions for this menu group. The top three icons also respond to a right-click by changing to the view mode appropriate for the menu. Many menu functions will work independently of the current view mode, or will change the view mode as needed. DRAG AND DROP Fotoxx accepts drag-drop of image files to File View or Gallery View mode. In File View, the file is opened. In Gallery View, the result depends on the type of gallery. If the gallery is a folder, the file is added to the folder and the gallery will show the new file in its sorted position. If the gallery is an album, the file is added to the album in the position where it is dropped. This works also if a text string that is a valid file name is dropped. EDIT PROCEDURES The image in File View mode (the current image) can be modified with the edit functions in the menus Edit, Enhance, Warp, Effects, Combine. These functions modify the current image in memory and as seen in the window. You can use these functions in any order, and the edits are accumulated. When finished editing, use [Save] to save the modified image back to the same file, save to a new file version (e.g. filename.v01.jpg), or input a new file name and/or folder. Image edit dialogs have sliders, spin buttons, or editable curves that immediately update the image. The reaction time depends on the size of the image, the complexity of the function and the speed of your PC. This is typically less than second for most edit functions on a strong PC. Left or right click the Undo/Redo button to undo or redo the currently active edit function. After an image has had one or more edits applied, the undo/redo button can be used to go back to prior edit steps or forward to the last edit step. The middle mouse button shows a popup list of all edits done to the current image, and you can select any step to go back to. The image can then be re-edited from this step. EDIT WORKFLOW You can speed the editing of multiple photos/images if you understand the following: • Choosing a new edit function automatically completes an active edit. The new edit starts with the 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, and an undo/redo position is created. • Some frequently used functions have a [Prev] button to recall previous settings, making it easier to process multiple photos needing similar adjustments. Here is an 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. Go to the folder with the new photos to process. 2. Open the first file. 3. Menu: Edit > Trim/Rotate. Repeat steps 4-7 for each image. The Trim/Rotate dialog remains open. 4. Level the image if needed (drag the right window edge). 5. Trim the image by dragging the trim borders to suit. 6. Press [Save] and choose "new version". 7. Press [Next] to edit the next file. Press [Next] again to skip over. \_Simple Workflow Most of the time you can just edit the JPEG file that comes out of the camera. Use the following more complex procedure only if you see "color bands" or "posterization" after editing the image, an indication that JPEG 8-bit color is limiting the image quality. \_Complex Workflow To edit with deep color (more than 8 bits), you can start with RAW files from your camera. There are three options for processing RAW files: • Click the RAW file gallery thumbnail. The RAW file may now be edited like any other image file. Fotoxx edit functions work with 24 bits per RGB color, so whatever bit depth the RAW file has will be preserved. Save the edited 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 computer. Saving an edited image as TIFF or PNG is also slower than JPEG. • Use the popup menu 'RAW Therapee' which is available for RAW files in File View or Gallery View. This opens the specialized RAW file editor. Save the file as a TIFF-16 file, which will pass back to Fotoxx for further editing if wanted. • Use the menu Process > Batch RAW. 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 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 if your edits radically shift the brightness distribution. When finished editing, convert the final file to JPEG (quality level 70 or greater) to reduce the final file size to 10% or less. Note that editing in deep color is far more important than having deep color in the final image. It is nearly impossible to see any difference between a 16-bit TIFF or PNG file and a high quality JPEG made from that same file. To preserve the possibility of re-editing the image later, keep the RAW file, which is usually much smaller than the TIFF or PNG file. There are two programs available for loading RAW files for viewing and editing in Fotoxx: Dcraw and RawTherapee. Dcraw is much faster, which saves time if you are viewing many RAW images. RawTherapee may sometimes provide a better initial image, which may not matter, since the difference is easily fixed. In User Preferences, you can set which of these programs is used. Both programs are installed separately from Fotoxx. MOUSE OWNERSHIP Some edit dialogs use the mouse to reference or alter the image in the main window. There may be more than one such dialog active at the same time. The mouse is also used to zoom and scroll the image, and you may need to do this while using a dialog. Therefore it is important to understand who owns the mouse (which dialog, or main window) and how to change the ownership: • The mouse is owned by the dialog that was last clicked or used. Mouse actions 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 on the image, the image will zoom or pan/scroll. Active dialogs are not affected. • The mouse wheel can always be used to zoom-in/out any part of the image. DIALOG BUTTONS These mostly work as follows: [Reset] undo changes, return to initial status [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. [Proceed] proceed with lengthy task based on dialog inputs. KEYBOARD ENTER KEY (or Return key) Many applications handle this key as 'done', meaning that input is complete and the function can be executed. Fotoxx does NOT follow this convention. The Enter key action depends on which dialog widget or control has the input focus: • text entry (single line) - input is finished. • text entry (multi-line) - a new text input line is started. • number entry - input is finished. • check box - toggle status between 'checked' and 'not checked'. • button - the button is pressed, equivalent to a mouse click. KEYBOARD ESCAPE KEY This key has multiple uses. It affects only the window that has current keyboard focus. This can be the main Fotoxx window or a dialog window. Before using the escape key, note which window has focus and is affected. • main window has focus (in priority order): • if a long-running function is active, terminate the function • if the Fotoxx window is maximized, restore former size and position • exit from Fotoxx • dialog window has focus: exit the dialog • others: cancel popup image, exit from 360 panorama viewer, interrupt or exit Slide Show (details are explained in the respective topics) CUSTOM DIALOG WIDGETS +image: custom-widgets.png A few non-standard smaller widgets are used to reduce the size of dialogs (and the amount of image area they cover). They work almost like the standard Gnome widgets for numeric data entry, text data entry, and buttons. A few standard GTK widgets have also been downsized by reducing unnecessary "padding". This is controlled by the file widgets.css in the [fotoxx-home] folder. You can modify this file if desired. You can also delete or comment-out the contents, but do not delete the file, since it would be automatically replaced. CURVE EDITING +image: retouch.jpg 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 PROCESSING There are several batch functions in the Process menu to speed some common tasks. You can select many image files and execute the batch function for all of them. Batch functions can be used for the following tasks: • Rename files using any mix of old name, new name, sequence number, photo date • Convert file types (e.g. .png to .jpeg) • Find and upright photos made with the camera turned 90 degrees • Resize files (e.g. reduce for web upload or e-mail) • Copy or move files to another location • Convert RAW files to tiff, png, or jpeg • Add/change/remove tags or geotags • Report or revise metadata items • Set image dates/times or shift times to change the time zone • Delete files or move them to Trash • Script Files: perform multiple edits on one image, save as a script file which can then be applied to any number of images in batch mode. • Burn selected image files to a DVD or BlueRay disc. • Find duplicate image files. SELECT IMAGE FILES +image: gallery-select.jpg This procedure is used for all functions operating on multiple image files: • batch functions - resize, rename, add tags, convert ... • scripts - perform a predefined set of edits • albums - create and edit image collections The procedure is explained here, and this topic is linked from each of the functions using the procedure. Selected files are added to a list by clicking on gallery thumbnails. The example shows a gallery and a list of files selected from the gallery. • add new files to the list: left-click gallery thumbnails • add a group of files: click the first thumbnail, shift-click the last thumbnail • a file may be added multiple times to the list • you can navigate to other galleries to select more files as needed • files are added initially at the end of the list • click a list position: thumbnail is shown and a new list position is set • new files are added after the current (last clicked) list position • delete a file from the list, 2 methods: • right-click the file's gallery thumbnail • click a list position and press [delete] button • move one or more files to a new position: • click each list position and press [remove] - the files are saved internally • click the new position and press [insert] - the next saved file is inserted (repeat to insert each saved file at a new position) • add all images in the gallery to the list: press [add all] button • clear the list: press [clear] button • use KB up/down arrow keys to step through the list and view thumbnails • the KB page up/down and home/end keys also work POPUP REPORTS Text reports are shown in a popup text window. Some of these have clickable report lines that trigger actions, e.g. a list of location names that can be clicked to show a gallery of images for the selected location. These reports can be navigated with the keyboard arrow keys, page keys, and home/end keys. 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 below. You can change the shortcut keys and add shortcuts for functions you choose. • Favorites menu - a graphic popup menu with your custom contents. Add text and/or icons that link to any menu functions you choose. Arrange these as you wish in a layout window. You can leave this window open and access any of these functions with a single mouse click. See |FAVORITES|. COLLECTION MANAGEMENT You can use Fotoxx to manage a huge image collection and still be able to quickly find the images you want. Some effort to organize the images is required. Search methods include folder and file names (or partial names), image (photo) dates, file dates, image ratings, tags (keywords: labels for persons, places, objects, events ...), captions and comments, and geotags (location names and latitude/longitude). This is done in a standards-compliant manner so that this data can be shared with other image processing applications. Options for how to organize a large image collection can be found in the topic |ORGANIZING IMAGES|. RIGHT-CLICK MENUS Popup menus appear when an image or gallery thumbnail is right-clicked. Most of these functions are also contained in the main menus for File View and Gallery View. The popup menus are shorter and faster to use for the functions they contain. \_File View popup menu Right click on current image in File View mode to get the following menu: • View Meta - Show short form metadata report • View All Meta - Show all available metadata • Edit Meta - Edit key metadata items: photo date/time, rating, tags ... • Edit Any Meta - Edit any metadata item • Rename - Change the file name • Permissions - Change file permissions • Copy/Move - Copy or move the image file to another location (folder) • Copy to Desktop - Copy the image file to the desktop window • Copy to Clipboard - Copy the image file to the clipboard • Upright - Auto upright a rotated image based on EXIF orientation data • Trim/Rotate - Trim (crop) the image, level the image or turn 90 degrees • Resize - Resize (rescale) the image pixel width and height • Voodoo 1 / Voodoo 2 - Limited automatic image enhancement methods • Retouch - Adjust brightness, color, contrast, saturation, white balance • Brite Dist - Adjust the brightness distribution (histogram shape) • Flatten - Enhance contrast and brighten shadows • Gradients - Enhance contrast and perceived brightness range • Select Area - Select an image object or area for separate editing • Show on Map - Go to Net Map view, zoom-in to image location • Delete/Trash - Delete the image file or move it to the wastebasket \_Gallery View popup menu Right click on thumbnail image to get the following menu: • Popup Image - Show image in a new window - zoomable, movable, persistent Zoom in/out using the mouse wheel. When zoomed small it disappears. Shift + left mouse click on a thumbnail: shortcut for Popup Image. • View Meta - Show short form metadata report • View All Meta - Show all available metadata • Edit Meta - Edit key metadata items: photo date/time, rating, tags ... • Edit Any Meta - Edit any metadata item • Rename - Change the file name • Permissions - Change file permissions • Copy/Move - Copy or move the image file to another location (folder) • Copy to Desktop - Copy the image file to the desktop window • Copy to Clipboard - Copy the image file to the clipboard • Upright - Auto upright a rotated image based on EXIF data • Show on Map - Go to Net Map view, zoom-in to image location • Delete/Trash - Delete the image file or move it to the wastebasket Album galleries have the following additions: • Add Selected Files Here - insert previously selected files at the clicked thumbnail position • Add Current File Here - insert current (last viewed) file at the clicked thumbnail position • Remove from Album - remove this image file from the album KEYBOARD SHORTCUTS Keyboard shortcuts are available for most functions. The notation "Ctrl+H" means press and hold the Ctrl key, then press the H key. The following table shows the fixed shortcuts that cannot be changed. Press the 'K' key to view all shortcuts and make custom additions or revisions. • F/G/M keys Change view mode: File, Gallery, Map • F1 key Display user guide for current or prior function • F10 key Toggle main window full-screen and back • F11 key Same as F10, without menu and top panel (for image viewing) • Escape key Kill running function, Exit active dialog, Exit Fotoxx • Arrow keys Previous/next image, report line up/down • [+] [=] key Zoom image or thumbnails larger • [-] key Zoom image or thumbnails smaller • Z key Toggle image between 1x (full size) and fit to window • Home/End Go to gallery first/last page, report go to top/end • Page Up/Down Go to gallery previous/next page, report page up/down MOUSE FUNCTIONS File View • left click - Zoom-in: magnify image, center at click position • right click - If image is zoomed: restore to window size If not, popup menu with common functions • mouse wheel - Zoom image in or out depending on wheel direction • left drag on image - Pan/scroll zoomed image, same direction or magnified opposite direction (like scroll bars) • mouse + Ctrl key - Mouse acts on main window instead of active dialog Gallery View • left click thumbnail - Change to File View, show full-size image • right click thumbnail - Show popup menu with common functions • middle click thumbnail - Pop up image in new zoomable window • scroll mouse wheel - Scroll the gallery page up or down Map View • left click - Zoom map larger at clicked location • right click - Zoom map smaller • left drag - Drag map, following the mouse • mouse wheel - Zoom map larger or smaller • left click on marker - Show gallery of all images at marker location COMMAND PARAMETERS The following parameters can be used if Fotoxx is started with the command 'fotoxx': \_long\_ \_short\_ \_param\_ \_description -help -h show command line parameters (popup window) -album -a "album name" initial album (gallery) to open -ver -v output release version and exit -recent -r show a gallery of recently seen image files -new -n show a gallery of the newest image files -prev -p show the last file viewed in the previous session -blank -b show a blank window -lang -l xx language code to use for GUI (es, de, fr ...) -index -x N disable image indexing for faster startup (see below) -menu -m "menu" Fotoxx will start with this function active -home /folder alternate Fotoxx home location (see below) -uninstall uninstall appimage package \_-index N\_ parameter This command line parameter can be used at Fotoxx startup to partly or completely disable image indexing to obtain a faster startup time. N is one of 0/1/2, as explained in the topic |PREFERENCES|. Note: the first Fotoxx startup after a reboot may be slow if your image collection is very large. Subsequent startups will be much faster since folders are now cached in memory. For very fast startup, you can bypass indexing entirely with the command "fotoxx -x 0", or by setting the image index level to 0 in |PREFERENCES|. Search and map functions will be disabled until you allow the indexing to complete normally. \_-home\_ parameter The default location for 'fotoxx home' is: /home//.fotoxx. This is where user preferences, settings and data are kept (tag names, bookmarks, defaults ...). Image index data is also kept here. The command "fotoxx -home /.../foldername" expects 'fotoxx home' to be in the designated folder. This enables multiple image collections to be managed separately. If Fotoxx is started for the first time with a new -home parameter, the initial file indexing process will begin. You can avoid this by copying the files in /home//.fotoxx to the new home folder before starting fotoxx with the new -home parameter. See also |MOVE FOTOXX HOME|. TOP PANEL DATA +image: top-panel.png The top panel has status information about Fotoxx and the current image file. The following strings can be present, in approximately the following order: CPU: 34% current CPU load (max. for 4 processor cores is 400%) 2866x1917x8 current image width x height x depth (bits per color) 0.87MB image file size (updated when a modified image is saved) 41% zoom status, image % size (window pixels / image pixels) edits: 2 current image edits, reversible with the [undo/redo] button blocked some functions are blocked until current function is done area active edits are confined within the active selected area dialog open a dialog is waiting for user input BUSY 45% some long-running functions show this progress indicator FILE MENU +image: menuF.png NEW SESSION Start a new Fotoxx session in a new window. 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 session will initially have an unmodified version of the current image file. If the same image file is edited in both windows, neither instance will see the changes made by the other, and the final result is the last file saved. SOURCE FOLDER Replace the current gallery (recent files, search results ...) with the folder of the current image file shown in the File View window. The default shortcut key is [S]. CYCLE 2 Open the previously opened image file. Repeating this menu will alternate between the two most recently opened files. This differs from the button [Prev/Next] which goes to the previous or next image file in the current gallery (folder, 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 the area to compare, and use this function to alternate between the two images. The keyboard shortcut key [2] is assigned by default. CYCLE 3 This works the same way as Cycle 2, but alternating the last 3 opened files. The default keyboard shortcut is key [3]. VIEW 360° PANO The current file is opened with a special viewer for 360° panorama files. The image width is assumed to be 360°. Use this function to view Google 'photosphere' images. Use the mouse and keyboard to control the view: • Mouse-drag the image - it will turn through 360° and wrap-around at the ends • Left and right mouse-click will zoom-in and zoom-out • Use the left and right arrow keys to pan quickly • Keys [1] and [2] select two alternative projection types (keypad does not work) • Use the [Q] key or Escape to exit the special viewer Panoramas with less than 360° can also be viewed, but the wrap-around will show a false joining of the two ends. RENAME +image: rename.jpg Rename the current file (File View), or the file of a clicked thumbnail (Gallery View). Enter a new name and press [apply]. This function can also automate the process of renaming a series of image files using a root name (e.g. an event or place name) and a sequence number. Open the first image file in the series, input a new name, and press the [apply] button. Use the [next] button to move to the next file if wanted. You can use the same name again by pressing the [previous name] button and then add a suffix or sequence number. Press the [add 1] button to increment the sequence number. File version numbers (.vNN) are not copied and are otherwise not affected. PERMISSIONS +image: permissions.jpg The permissions of the current file (File View), or the file of a clicked thumbnail (Gallery View), are shown in the dialog. Use the [Change] button to revise the permissions data in the dialog, and the [Apply] button to change the permissions of the named file. You can use the arrow keys to navigate to new files in the same neighborhood, and the data in the dialog will be updated. The three permissions shown are for the owner of the file, members of the owner's group, and others. An explanation of Linux permissions can be found in the man page for the "chmod" command. BLANK IMAGE +image: blank-image.jpg 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 Markup > draw text). Input a file name, choose a background color, and set the desired pixel dimensions. See also the |MASHUP| function. BLANK WINDOW Blank the File View window. If already blanked, restore the current image file. Assign a keyboard shortcut to have a "one button" blank and restore function (boss button). COPY/MOVE +image: copy-move.jpg The current file (File View), or the file of a clicked thumbnail (Gallery View), is copied to the given location. This location can be entered directly or chosen by a file open dialog if the [browse] button is used. Select the copy option to copy the file and leave the source file in place, creating a duplicate. Select the move option to move the file from the original location to the new location. A group of files can be rapidly processed by clicking each thumbnail followed by [apply]. COPY TO DESKTOP The current file (File View), or the file of a clicked thumbnail (Gallery View), is copied to the desktop folder (/home//Desktop). COPY TO CLIPBOARD The current file (File View), or the file of a clicked thumbnail (Gallery View), is copied to the clipboard, from which other applications can paste. SET WALLPAPER The desktop wallpaper is set from the current file. NOTE: this is Gnome specific and likely does not work for other windowing systems. SHOW ON MAP Change to Net Map View and show the location of the current image. The map may need time show up, depending on internet response time. DELETE/TRASH +image: delete-trash.jpg The current file (File View), or the file of a clicked thumbnail (Gallery View), is deleted or moved to trash, depending on the option selected. Fotoxx uses the Linux desktop standard for trash. If this works, trashed image files go into the standard trash location and can be recovered later if wanted. Otherwise, Fotoxx puts trashed images into a desktop folder named "fotoxx-trash". You can delete it or move it to your Linux-specific trash. If both standard and desktop trash do not work, you must trash the files outside of Fotoxx. If you have multiple files to remove, you can leave the dialog open to speed the process. If another file is opened, the file name in the dialog updates itself. In Gallery View, if another thumbnail is clicked, the dialog updates itself. PRINT +image: print.jpg The print menu brings up a standard Page Setup dialog where you can select a printer, a paper size, and orientation. After using the [apply] button, another dialog starts for entering paper margins and an image scale. The margins can be used to shrink the image or shift it on the page. Image scale can be set in the range 5-100%, where 100% means print the maximum size image that fits within the margins. Smaller values will shrink the image proportionally. The actual print size (image width and height) is updated in the dialog as margins and scale are changed, and this can be used to reach a desired printed image size. After the margins dialog, a Print dialog starts for the actual printing. This includes paper type and quality inputs, and a preview of the printed layout which can be accepted or rejected. PRINT CALIBRATED 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. GALLERY MENU +image: menuG.png +image: gallery-view.jpg GALLERY OVERVIEW +image: image-management.png A gallery of thumbnail images can represent a file folder, the output of an image search function, one of the built-in galleries (recent files, newest files), or an album (an ordered list of image files with a given name). THUMB VIEW +image: gallery-view.jpg Image files in the current gallery are shown as an array of thumbnails. The text is limited to file name, date, and size. Size is in pixel width and height if the current thumbnail size is small, and the MB size is added if the thumbnails are larger and more space is available. Use the zoom button |[+-]| to change the thumbnail size. META VIEW +image: meta-view.jpg Image files in the current gallery are shown together with the basic metadata used by Fotoxx. LIST VIEW +image: list-view.jpg Image files in the current gallery are shown in a compact list form that is better for visual searching to find a desired file or folder name. RECENT The 1000 most recently seen image files (viewed or edited) are shown in a gallery, from which you can select files to view or edit. To replace the recent files gallery with the folder of a chosen file, use the function |SOURCE FOLDER|. NEWEST 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. To replace the newest files gallery with the folder of a chosen file, use the function |SOURCE FOLDER|. GOTO FIRST GOTO LAST Jump to the first or last thumbnail in the current gallery. SOURCE FOLDER (2) This is a duplicate of the |SOURCE FOLDER| function in File View. SORT GALLERY +image: gallery-sort.jpg Choose the sort key and ascending or descending order. The Reset checkbox resets all galleries to file name ascending. If an Album gallery is sorted, the output does not replace the album but instead creates a new album "galleryname-sorted". This is done because albums have an arbitrary order controlled by the user. If sorted, there would be no automatic way to put it back in the original order. Folder galleries retain their sort order and scroll position when viewed later (i.e. when other galleries are viewed in-between). \_Scrolling the Gallery The gallery can be scrolled up and down using the keyboard up and down arrow keys or the page-up and page-down keys. [home] and [end] keys may also be used. Use the right-side scroll bar to move rapidly to any position. Left-click and hold on the scroll bar, over or under the scroll button, and the gallery will scroll rapidly to the click position. Right click and hold on the scroll bar to make the gallery smooth-scroll to the click position. Continue holding the mouse button and move to the side to increase the scroll speed. ALL FOLDERS +image: all-folders.jpg All top image folders are shown initially. Click on [+] to unfold subfolders, or on [-] to fold them back in. You can click on any folder to get a gallery of the images in that folder. Keep the window open to navigate anywhere in your image collection. This function is also available using the [TOP] button in the gallery top panel. SELECT FILES +image: select-files.jpg Pre-select image files for use as inputs for creating albums or for batch functions or script functions. These functions will use the pre-selected files by default, but they also allow the selection to be modified or done over. The procedure for selecting files is described in the topic |SELECT IMAGE FILES|. BOOKMARKS +image: bookmarks1.jpg View existing bookmarks. Bookmarks are chosen file locations (folder/file names) with user-assigned names. Click on a bookmark to show a gallery of thumbnails starting with that location. Select [Edit Bookmarks] to add, delete, or rename a bookmark. EDIT BOOKMARKS +image: bookmarks2.jpg After starting Edit Bookmarks, click on a gallery thumbnail to add this location to the bookmark list. The assigned bookmark name will initially be the file name corresponding to the thumbnail. This name appears in an edit field where you can assign another 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 after the last bookmark location selected. MANAGE ALBUMS +image: manage-albums.jpg An album is an arbitrary sequence of images that is manually assembled from existing images. An album is a list of its member image files. The image files themselves are not copied or changed. A given image file can be a member of multiple albums, or may be present more than once within an album. Albums can be used to group images with some shared attributes, such as photos of a given person taken at different times or events, a "best photos" collection, etc. You can add and remove images in an album or rearrange the order of the images. Once an album is made, you can call it up by name and it shows as a gallery. This gallery works like any other: you can scroll through the gallery, step through the images with the [Prev/Next] button, or edit the images. Keep in mind that editing an image in an album will edit the underlying image file, so any other way to view this image will show the same changes. \_Create Enter a name for a new album. You can make an empty album, an album with pre-selected image files from |SELECT FILES|, an album with images from the current gallery, or you can select the initial images for the album. The gallery could also be the result of a Search operation. \_Rename Choose an existing album and enter a new name. \_Delete Choose an existing album and delete it permanently. \_Select The standard file selection dialog is opened (|SELECT IMAGE FILES|). When done, right-click on an album thumbnail and select "add selected files here" to insert the new files. To edit an album, right-click on an album thumbnail and select from the popup menu: • Add selected files here - previously selected files are added at the thumbnail position. Click thumbnail left side to add before, or right side to add after. • Add current file here - the current file (File View) is added at this position. • Remove from album - the clicked image file is removed from the album. To add files to an empty album: • Album button -> select the empty album (a blank window results). • Manage Albums > Select Files: select initial files for the album. When you exit Select Files with the [done] button, the selected files are added to the album. \_Drag and Drop You can rearrange images within an album by dragging thumbnails with the mouse. Drag the thumbnail until the mouse cursor changes to a small thumbnail image. Continue dragging this image to the position where it should be inserted, and release the mouse button. Position the mouse roughly between the thumbnails where the dragged image is to be inserted. If the drag approaches the top or bottom edge of the window, the gallery will scroll to bring more images into view. You can use two Fotoxx sessions to make the selection or movement of many images faster. Drag and drop images from any source gallery on to a target album gallery. The source gallery can also be an album or any other gallery. TIP: If image files are renamed or moved using Batch Convert, and if deletion of the original image files was also specified, then all albums containing any of the files are updated to the new file names and locations. Therefore, don't just rename folders if they contain images in albums, or manually move image files among folders. Instead, use Batch Convert to move the image files to the new folder, then delete the old folder if empty. To delete image files, use Batch Delete/Trash. This will also purge deleted files from all albums containing them. UPDATE ALBUMS +image: update-albums.jpg Replace specified album files with specified replacements, or add specified new files after specified existing files. Select one or more albums to be processed. In gallery view, click on two thumbnails to specify 'old file' and 'new file' in the above dialog. Select the option: 'new file' replaces 'old file' wherever found, or 'new file' is added to the albums after 'old file'. Press [clear] to erase the files and start over. Press [proceed] to process the albums. After processing, the dialog is repeated with the same albums selected and the same 'replace or add' option. You can select two more files and press [proceed]. ALBUM MASS UPDATE +image: album-mass-update.jpg This is a utility for some of the mass updates that you may need after creating new versions for many image files and needing to update one or more albums to the new versions. This does not cover every need, which is likely impossible, but my hope is that it will help reduce the need to change one file at a time. Select one or more albums and then select one of the processing options: • Replace all with newest version only All albums are updated to have only the newest file versions. • Replace all versions with newest version All file versions are replaced with only the newest version. The original file, if present, is NOT replaced. • Add newest versions to existing versions The newest version is added to existing versions, if not already present. If only the original file is present, the newest version is added. • Replace all with original and all versions All album files are replaced with the original file and all existing versions. • Replace all with original version and newest version All album files are replaced with the original file and the newest version. • Replace all with selected versions (*) All album files are replaced with the selected versions only. This includes the original (unversioned) file. • Replace all versions with selected versions (*) All album file versions are replaced with the selected versions only. The original file, if present, is NOT replaced. • Add selected versions to existing versions (*) The selected version is added to existing versions, if not already present. If only the original file is present, the selected version is added. • Replace all with original and selected versions (*) All album files are replaced with the original files and selected versions. (*) The four options using selected files: the list of selected files is from the function |SELECT FILES|. Album files that do not match any selected files are left in the album. Only files that match a selected file are changed according to the above rules. The matching is based on the file name without version or extension: 'filename.png' and 'filename.v01.jpg' are considered matches. Example: Replace all versions with selected versions: If the album has filename.jpg and filename.v02.png, and the selected files list contains filename.v04.jpg, the result in the updated album will be: filename.jpg and filename.v04.jpg GALLERY TO ALBUM Save the current thumbnail gallery as an album. Select an existing album name, or input a new name, press [Done]. SLIDE SHOW +image: slide-show.jpg Use the Manage Albums function to assemble the images for a slide show as an album with an assigned name. This allows you to select images from anywhere in your collection and order them as desired. In the slide show dialog (top left), press [Select Album] and choose an album from the list provided. Change the other settings in the dialog as desired, then 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 to exit the slide show and return to the dialog. The animated transitions between images, and the display times and zoom behavior of individual images, can be customized using the dialogs explained below. Main Dialog Controls • Caption Time The default time to display the image with captions and comments, in seconds. • Image Time The default time to show the image alone, seconds. • Clip Limit % Images are scaled to fit the window, leaving black margins for differences in aspect ratio. Images with difference < clip limit are clipped to have no margins. • Music File The given music file or playlist that will start when the slide show is started. • Full Screen If checked, images are shown full-screen without menu, title bar, etc. • Auto-replay if checked, the slide show will start over after reaching the end. Keyboard keys can be used to control the slide show. Press the button [KB controls] to show the current assignments or to change them (Keyboard Preferences, bottom right). The following actions can be assigned: • Blank the current image and pause the slide show, or restore the image (toggle). Default 'B'. • Show the next image, including the transition animation, in a paused slide show. Default 'N'. • Pause or resume the slide show (toggle). Default 'P'. • Magnify the image - simulated loupe (see the |MAGNIFY IMAGE| function). Default 'X'. The following KB keys are assigned to actions that cannot be changed: • Escape: if a transition or zoom is in-progress, jump immediately to final status. • Escape: otherwise, Interrupt the slide show and return to the main dialog. • Left / right arrow keys: You can go back and forward at any time and the slide show will continue from there. You can configure keys other than P/B/N/X if desired, and the space bar (blank) can also be used. Do this by replacing the key name in the dialog with another key. Press [transitions] to start a dialog (Transition Preferences, bottom left) 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 time parameters sets the duration of the transition. Times less than 2 seconds may cause problems with slow computers or large monitors. If you see fleeting gaps in the image, you likely need to increase the time. The preference parameters specify a relative preference which will influence how frequently the transition is used when random sequence is selected. The buttons [load] and [save] allow you to save transition settings in a file and load them later. These files are independent of the slide show files. A slide show will default to the transition settings last used for that slide show (album name), but you can always replace these by using the [load] button to load some other settings, or changing them in the dialog. Press [image files] to start a dialog for Image Preferences (top right). These are optional. An image is selected for customizing by clicking its thumbnail in the album/gallery display. The dialog is filled-in with default settings or the previous settings for this image in this slide show. Enter revisions and press [done], or click on the next image to be customized. The dialog shows the customizable image events in time order: • play an optional tone when the image appears • wait for a specified time (may be zero) • show the image file name (without folders) for a specified time (overlaps with caption, comments) • show the image caption, if any, for a specified time (overlaps with file name, comments) • show the image comments, if any, for a specified time (overlaps with file name, caption) • wait a specified time before starting an image zoom (may be zero) • zoom the image from normal to magnified or from magnified to normal over the specified time. If neither option is selected, there is no zoom. • zoom-in: start at normal size, slowly magnify the image while moving the center to a specified position • zoom-out: start at magnified size at the specified center, slowly shrink the image back to normal size • [zoom center] - press this button, then click on the image thumbnail to specify the zoom center for the image • wait for a specified time after zoom (may be zero) (the default value comes from the main dialog 'image time' setting) • transition to the next image, using the specified transition or 'next' to indicate no preference (will be randomly selected). MAP MENU +image: menuM.png +image: map-click.jpg All maps will show markers (red dots) where there are geotagged images. Click on a marker to get a Gallery View of all the images at this location. The range of images selected corresponds to the marker size: images located within the marker area on the map are selected. The effective marker area scales with the map scale. FILE MAP Use local file maps for map functions. The last chosen file map is loaded, or a world map by default. Local file maps are from the optional package fotoxx-maps. File Map Navigation • left click: zoom to full size, right click: reduce size to fit window • mouse drag: the zoomed map image moves with the mouse • if map is full size and a marker is clicked, show corresponding gallery CHOOSE MAP Choose a file map from the available maps. An initial set of world and continental maps can be installed with the package fotoxx-maps. \_Custom Map Files Obtain the map from any suitable source, e.g. Google Maps or Open Street Map. You need to get the map as a JPEG or PNG file. You can use a screen capture function to create the map file. You can use the Mashup tool to stitch many maps together to make a huge map. These work well with Fotoxx pan and zoom. You can also use a scanner to capture one or more paper maps and accurately stitch them together with Mashup. To install a map, put the map file into the folder [fotoxx-home]/user_maps. In this folder you will also need a text file: maps_index. Each map requires a line in this file which specifies the map file name and the latitude-longitude range of the map. Here is an example maps_index file: Hamburg.jpg, 53.455, 53.553, 9.906, 10.067 Houston.jpg, 29.479, 30.053, -95.786, -94.905 The entries are map file name, low and high latitude, low and high longitude. All values are degrees, separated by commas. Spacing does not matter. Fotoxx assumes a Mercator projection, but this is not significant for maps covering less than 100 km. If you make a custom map, getting accurate latitude and longitude values for the map edges can be tricky. Use the Open Street Map "export" option to view maps with an overlay rectangle labeled with latitude and longitude. Adjust the rectangle to match a corner of a map you have created and record the latitude and longitude values. You need accurate latitude and longitude data for the upper left and lower right corners of the completed map. The precision should be suitable for the scale of the map. 1 degree of latitude corresponds to about 110 km on the earth's surface. 0.001 degree corresponds to 110 meters. NET MAP The Net Map functions use an internet service to provide maps from any location at any scale. The Net Map offers superior functionality, but depend on having a fast and reliable internet connection. Markers (red dots) will show the locations for any of your images with geotags. Use the mouse wheel or double-clicks to zoom-in on any location. Drag the map to change the center. Click on a marker to get a Gallery View of the images at this location. The range of images selected corresponds to the marker size: images located within the marker area on the map are selected. The marker area scales with the map scale. Nearby markers on a zoomed-in map will consolidate when the map is zoomed out, and clicking the consolidated marker will get all the images of the contained markers. Net Map Navigation • mouse wheel is used to zoom-in or out in small steps • left click on a marker: show gallery of images located within that marker area • mouse drag: the map image moves with the mouse NET SOURCE At this time, two internet map sources are supported: Mapnik and Mapbox. The default is Mapnik. It is entirely open and free and works quite well if your internet connection is good. The one drawback is that location names are in the language script of the location (e.g. Arabic, Chinese ...), so for Westerners these maps are only useful for Western countries using Latin script. Mapbox uses Latin script for all major location names. Mapbox, however, requires an access key, which you must request. This is free for modest usage: up to 50,000 "map tiles" per month, which is normally adequate. To use Mapbox, go to the Mapbox web site, developers page, and follow the instructions to obtain a key. In Fotoxx, use the Net Source menu function and select mapbox. You must input your access key the first time only. NET LOCS +image: netmap-locs2.jpg You can save a map location (map center and zoom level) with a given name, and recall the map location later with one click. To save a map location, navigate the map to a location and zoom-in to include what you want within the window. Enter the map location name and press the [Add] button. The new location is added to the list of available locations in the window. To recall a saved map location, simply click on the location name in the list. To delete a map location, click on the name and press the [Delete] button. A few net map locations are included by default. You can keep or delete them. MARKERS Here you may choose to show markers for all images, or only images in the current gallery. You can use the latter capability to show markers for a chosen subset of images. Use an album, or use the Search function to produce the desired set in a gallery. This works for both file map and net map. FAVORITES +image: favorites-edit.jpg 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 - /.../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 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 [fotoxx-home]/favorites. You can use either the English menu names or the 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 start a new line (see "source folder" in the example). Icon library: Icons for many image edit functions can be found here: /usr/share/fotoxx/icons/edit-funcs PREV/NEXT +image: prev-next.png This button is enabled only in File View. Click with the left or right mouse button to view 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. If you proceed, edits are discarded. The keyboard left and right arrow keys can also be used. [+-] +image: zoom_menu.png Use a left or right click to increase or decrease the image size in |FILE VIEW| or thumbnail size in |GALLERY VIEW|. If the thumb size is minimum, a further right click will show thumbnails together with the main metadata items used by Fotoxx, and another right click will show a compact gallery list with only file names. These last two views can also be reached using the menus |META VIEW| AND |LIST VIEW|. \_Folder Jump If you attempt to go beyond the first or last image in the current gallery (folder), a popup message informs you. If you try again to move in the same direction, the last image file in the next preceding folder, or the first image file in the next following folder, will be opened. If there is no preceding or following folder, or if it has no image files, then nothing is done other than a popup notification. Note that only the next preceding or following folder in the parent of the current folder (gallery) is searched. There is no extended search into additional folders or subfolders. If the current gallery is not a folder (e.g. Newest Images gallery), then there is no preceding or following folder. FILE SAVE +image: save.png +image: file-save.jpg +image: file-save-as.jpg In the first dialog, select 'new version', 'new file', 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 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 can be set in |PREFERENCES|). A keyboard shortcut can be assigned to this function if desired. \_New File The 2nd dialog shown above will open to save the current image 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 greater bits per color has more latitude if an edit function radically alters the brightness distribution. 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 transparent areas (e.g. a Warp function creates a non-rectangular image shape with transparent margins), you should save the image as a PNG file if you want the transparent areas to be preserved. If you use JPEG, these areas will be black and opaque. JPEG does not support transparency. \_Replace File Save the current image file back to itself. If a JPEG file, the default quality is used (this value can be set in |PREFERENCES|). 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). File sizes for a 15-megapixel image are roughly as follows (depending on image detail). The jpeg numbers are the quality value given when the file is saved to disk. tiff-16 tiff-8 png-16 png-8 jpeg-100 jpeg-90 jpeg-80 jpeg-70 112 MB 38 MB 78 MB 21 MB 8 MB 2 MB 1 MB 0.7 MB The default JPEG quality is used unless you change the value in the New File dialog. The default value can be set in |PREFERENCES| (initially 90). You will not be able to see a difference between a file saved with quality 90 and one with 100, but the difference in file space is huge. The |TECHNICAL NOTES| section describes potential loss of image quality from repeated open, edit and save of JPEG images. At the default quality of 90 this issue can be generally ignored. The permissions used for the new file are shown. These are copied from the source file that was copied or edited to make the current file. If you want different permissions, press the [Change] button to edit the permissions. METADATA MENU +image: meta.png Metadata is text data that is stored inside an image file. Digital cameras create some metadata 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). Fotoxx can search your image collection using any metadata, along with folder and file names or partial names, producing a gallery of matching images. In most cases, the search is almost instantaneous. 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 for file and folder names and metadata tags, captions, ratings, etc. See |ORGANIZING IMAGES|. VIEW META VIEW ALL META +image: view-metadata.jpg The View Meta functions 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. View Meta shows the most commonly needed data, including the photo date and time, user-assigned tags and star rating, comments, caption, and a history of Fotoxx edits that have been applied to the image. View All Meta reports all available metadata. The [Extras] button on the View Meta window opens the dialog shown on the right, where you can add extra items to the report. Any item you see in the long report can be added to the short report. Click an item in the left column to add it to the right column and to the report. Click an item in the right column to remove it. Click the entry "Other Item ..." to type-in any item name not contained in the default list, which is limited to the most likely needed items. Fotoxx uses the following EXIF/IPTC data items (keys) for image editing and searching: \_Data Name\_ \_Fotoxx function to add or change the data Date/Time Original Edit Meta - date/time (normally from camera date/time) Keywords Edit Meta - tags Rating Edit Meta - stars User Comments Edit Meta - comments Caption-Abstract Edit Meta - caption Geotags Edit Meta - location - (may come from camera GPS) (any key name) Edit Any Meta Image History Fotoxx edit functions append this data automatically There are also several batch functions for adding data into many image files at once. These are found mostly in the Process menu. 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 embedded blanks or other delimiters can be used. Commas and semicolons are used as delimiters between tags, and therefore cannot be used within a tag (a standard, external to Fotoxx). 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 (folder 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 a gallery window, where you can further review the images and choose those for viewing, editing, or changing their tags. If you have used folder 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 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, art, scenery ...). 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 when first assigned to an image. The number of tags within a category should ideally be less than about 200, so that visual location from a pick-list is fast enough. The pick-list is sorted alphabetically. managed tag system advantages • prevent inconsistent tag names (e.g. 'landscape' and '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 more reliable because tags do not have the above errors • when tagging images, you have a reasonable overview of available tags (as opposed to thousands of tags in an unmanaged system) A large tag list 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 mitigated in 3 ways: (1) Up to about 10 most recently used tags are shown 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. (2) When adding tags to an image, you may simply start typing a desired tag name. A popup list of matching tags appears as soon as there are only a few possible matches. You can then click on the desired tag to add it to the image. If there are no matching tags, you have the option of adding the new tag to the list of available tags. (3) You can limit the list of displayed tags to a chosen category. 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 folders 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 new tags are present will be automatically added under the category "nocatg". If you notice unwanted tags in your tag list, use |SEARCH IMAGES| by tag to find the images needing tag deletion or renaming. \_Random Tag System You may prefer to invent tags as needed with no particular system in mind. Or you may already have thousands of tags, making a conversion to a managed tag system difficult (but not impossible: Fotoxx has a function to mass convert tag names). In this case, you can simply type tags into your images, creating new tags as needed. There is still a limited capability to keep tags organized: existing tags matching the characters you input are shown as soon as there are only a few possible matches. Example: you type "lan" and a list of existing matching tags is shown: 'landscape', 'landscapes', 'Langley' ... If one of these is your intention, you click on it to select the tag. If not, you keep on typing and eventually press Enter to create a new tag. When searching images for tags, you can type desired tag names or pick them from the list of available tags. Available tags matching the first few letters you type are shown, and you can pick from this list. You cannot enter a search tag that does not exist somewhere in your images. \_Geotags Modern cameras can record the location of each photo, using an internal GPS receiver. Latitude, longitude, city or location, and country are recorded in the EXIF metadata of the image JPEG or RAW file. The Edit Meta function also allows location data to be entered or revised. Locations may also be specified by clicking on a map. There are three functions that can find images for a specified location or region: |SEARCH IMAGES| Find images by location name (also multiple names and partial matches). |PLACES/DATES| Find all images for a country, country/location, or country/location/date range. Images by Map Location Click on a map marker to show all images at the marker location. Location names (city or other place name, and country) from the camera GPS are not standardized and may be chaotic (e.g. Munich, München, MONACO). How best to deal with this is discussed below. EDIT META +image: edit-metadata.jpg The Edit Meta function is used to add basic metadata to an image. Please read Tags Overview and Geotags Overview (above) before using Edit Meta. Edit Meta is used to edit the most frequently used metadata: image date and time, rating, caption, comments, location data, and tags. The dialog initially shows existing data for the current image. After making additions or changes, press [Apply] to update the image file. There is no automatic file versioning for metadata changes, but if you want a new version, use File Save > New Version. The dialog shows the metadata for the current image in File View, or for a clicked thumbnail in Gallery View. The dialog updates itself when a new image is opened or new thumbnail is clicked. The date of the image, if available, is shown as Image Date. This may be entered if missing, or changed. You can enter a full date in the format yyyy-mm-dd or a shorter format yyyy or yyyy-mm. A missing month/day is logically equivalent to 01/01 when used as a low limit for searching, or 12/31 when used as a high limit. The [Prev] button fills-in the date from the previous data entered. This is to allow easy dating of a series of images. If time is important, you can include a time using the format hh:mm[.ss]. You may enter a caption, a comment, and a stars rating for the image. If the location data is available from any previous image file, enter the first few characters and press [Find] to get a list of locations to choose from. For the first entry of a location, or to add location data to many files at once, see the topic Adding Geotags below. Existing tags are shown in Image Tags. Available tags are shown in the Defined Tags window below. One of these tags can be added to the image by clicking it. A tag can be deleted from the image by clicking it in Image Tags. Tags recently added are shown in Recent Tags. This is a convenience to make adding tags to a new batch of images easier, since many of the same tags will be used again. 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 click on one of these to add the tag to the image. If the input tag is new (no matching tag is shown), press [Add] when the tag is complete. It will be added to the image and to the list of defined tags with category "nocatg". If you are using tag categories, you can select a category, and only those tags will be shown in the list of defined tags. If your tags list is huge, this can reduce the list to a manageable size for pointing and clicking. The [Apply] button writes the data to the image file and to the metadata index file used for searching images. The [Prev] button can be used to load all available data from the previous image viewed or edited. This can be used to speed-up the processing of a group of images sharing much of the same data. MANAGE TAGS +image: manage-tags.jpg Create tags (keywords) and optionally group them into categories. You can assign categories to tags to help organize them and locate them more quickly when adding tags to images. They are optional and they play no role in tag searching: only the tag is stored in an image, not its category. Typical categories are people, places, things, events, scenery, buildings, art, etc. To add a new tag with a new category, enter the category and the tag, then click [create]. If the category is blank, the tag will be assigned to "nocatg". To assign a tag to a different category, click a category (bold text) or enter a new one, click the tag, and press [create]. The tag will move from the old to the new category. To delete a tag, click the tag and press [delete]. Tags used in images but not assigned to a category will appear under "nocatg". Note: a newly created tag is appended to the end of the tag list for its category. The next time Fotoxx is started, all categories and their tag lists are sorted alphabetically, except that "nocatg" is always last. Use the [orphan tags] button to list tags that are defined but not assigned to any images. These may be deleted if no use is planned. \_Adding Geotags The Edit Meta dialog shows the location data for the current image, if any. For an image with missing or incorrect location data, enter a location name (city, park, museum ...) and use the [Find] button to either complete the data in the dialog, or get a list of matching locations to choose from (e.g. London, United Kingdom and London, Canada). The list of locations comes from your image files, so a location will not be known until it is assigned to an image for the first time. Partial matches are found, so you can usually enter a leading substring, e.g. "hono" for Honolulu. Use the [Apply] button to enter the data into the EXIF metadata for the current image, and also into the metadata index file for later searching by location. Use the [Prev] button to fill the dialog data with the last location used. If the [Find] button does not find a location (it is not present in any other image), you can use the [Web] button to find the location data from an internet web service (MapQuest Open for now, but this could change). The location data is completed and returned into the dialog. The web service names are not standardized in format or language, so check the returned data for reasonableness and change the spelling and capitalization if needed. The [Apply] button will add the location data to the image, and this location will be available for future use by the [Find] button. If the [Web] button fails, you can find the location using an internet service, and enter the location data into the dialog. Here is a web site to look-up a location/country (there are many others): \_https://www.worldatlas.com/aatlas/findlatlong.htm Note for non-English locales: If a comma is used for a decimal point in latitude/longitude, this is accepted but converted to a period internally. The web service always returns periods. Blank the latitude and longitude if you want to save only the location and/or country name in the image file. If the latitude/longitude data is changed from the values returned by [Find], the new values are saved for this image file. If a location is saved without latitude/longitude, there will be no location marker on a map, and finding photos for this location by clicking on a map will not work. The two other methods to find photos by location, Search Images and Places/Dates, will still work. Note: The EXIF keyword "city" is used to mean any location name: city, park, museum, lake, etc. Fotoxx uses the term "location" to avoid stupidities like "city = Yellowstone Park". Internally, "location" is converted into "city" for metadata storage or retrieval. If you change to one of the map views and click on a location, this location will be inserted into the Edit Meta dialog. If you click within a marker (red dot), the location name and latitude/longitude for that marker will be used. If you click outside a marker, the clicked latitude/longitude will be used, and the location name will not be changed. If you use the [Find] button to set the dialog location data from a known location, there are two possible outcomes: if there is only one geocoordinate (latitude and longitude) associated with the location, this is returned into the dialog. If there are multiple geocoordinates, the Net Map is shown and zoomed-in to a scale where all markers for all geocoordinates associated with this location are shown. Click within one of the markers to choose the corresponding geocoordinate, or click outside any marker to set a new geocoordinate for this image file. The location name now has a newly associated geocoordinate. You can use Batch Add Geotags (see below) to quickly add location data to many image files at once. Summary • Enter (or change) a location name (possibly abbreviated) in the dialog. • Use [Find] to find the location and auto-fill country, latitude, longitude. • If there are multiple matches, choose from the list. • If there are too many matches, add more letters or supply country and try again. • If there are zero matches (not found), try the [Web] button (country required). • If still not found, add more letters to the location. • If still not found, use the above web service and input the data manually. • Use [apply] to update the image and make the location available for future use. • If there are multiple geocoordinates for a given location, click on a map marker to select one, or click elsewhere to add a new geocoordinate for this location. EDIT ANY META +image: edit-any-metadata.jpg This is a dialog for editing any metadata for the current image file. The most likely metadata key names are listed. Click one of these to retrieve the current key value. Change the value if wanted and press [Save]. The metadata is updated. To edit a key name not in the list, enter the key name and press keyboard "enter" to retrieve the present value, if any. Then enter or change the value and press [Save]. You may enter the key name in lower case and with or without spaces between the words, e.g. "Bits per Sample" and "bitspersample" will both work. To see all present keys and data, use |VIEW ALL META|. DELETE META 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 present. Some keys are not deletable, e.g. File Name. CAPTIONS +image: caption.jpg Show the file name (without folders), IPTC Caption, and EXIF User Comments at the top of each image displayed in File View. You may choose any or all of the three options. Subsequent image views will include the selected items where available. The lengths are truncated at 200 characters. To see up to 1000 characters, use |VIEW META|. PLACES/DATES +image: image-locations.jpg This is a fast way to find all photos made at a given location or location and date range. In the dialog, select the desired level of grouping: by country, by country and location, by country and location and date, or by date and country and location. In the last two cases, you can select a date range for grouping of images having nearby dates. A number N will group images together with dates that are N days or less apart from other images in the group. A popup report shows all locations found and the dates of photos taken in those locations. The count of photos taken is also shown. In the above example, 29 photos are from Grindelwald in March 2008. Click on a line in the report to get a thumbnail gallery of those images, and from there you can click on any image to view or edit. Note that this method uses only location and country to find the images. Earth coordinates are not used. If you have images with missing or inconsistent earth coordinates for a given location, use this function to get all of them, and then use |BATCH GEOTAGS| to make the earth coordinates consistent. You can also use the keyboard up/down arrow keys to rapidly step through the report lines and view the gallery for each line. The page up/down keys and the home/end keys also work. Use the [find] button to enter a name and jump directly to that name in the report. TIMELINE +image: timeline-report.jpg This report produces a timeline of image counts by month. Click on a year and month in the report to get a thumbnail gallery of all images with a photo date (EXIF) in the selected month. The example here shows 377 images for May 2016. You can also use the keyboard arrow keys (up/down/left/right) to step quickly through the months or years and view the corresponding galleries. Year 'null' is for images without a photo date. AUTOSEARCH This function can be used by shell script files to extract image files meeting any of the selection criteria available in the Search Images function (see below). First, use the Search Images function to set up the desired selection criteria (e.g. folder/file names (wildcards allowed), dates, tags, or other metadata). Use the [save] button to save the criteria as a file with a given name. Use the following command to start Fotoxx and perform the search: $ fotoxx -m autosearch where is the name you assigned to your search criteria. The output is to the file /fotoxx-home/search_results, where /fotoxx-home/ is the location for Fotoxx data files (default: /home//.fotoxx/). The settings file is an ordinary text file which can be edited or created/modified using a shell script. You can use the shell text manipulation utilities to modify fields in this file. SEARCH IMAGES Use the Search Images function to find images having any desired metadata. A metadata index file is used for searching, which makes it possible to search thousands of images per second. The index contains a subset of the EXIF and IPTC metadata in the image files. Some of this data is automatic, created by the camera. Other data, such as tags and star ratings, can be added by the user. There are two report formats: a gallery of thumbnails showing all images that match the search criteria, or a metadata report which combines thumbnails with metadata text. Here is an example of the metadata report format: +image: search-images-metadata.jpg \_Search Dialogs +image: search-images1.jpg +image: search-images2.jpg The main dialog is on the left. If the button "search other metadata" is used, the dialog on the right appears. This can be used to select metadata that is not available in the main dialog. Details are given below. In the main dialog, select which images to search, either all (the entire image database) or current set, meaning the images in the current gallery list, which can be a folder, album, or the results of a prior image search. Next, choose what to do with the matching images found: 'new set' means replace the current set with the images found, 'add' means add them to the current set (gallery), and 'remove' means remove them from the current set. To remove images, you must search the current set. For files matching the selection criteria, you may additionally specify which versions are selected: last version only, original + last version, and original + all versions. These options depend 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. When one of these 'version' options is selected, it becomes effective for any original file or file version that is selected. It is possible, for example, that if only version 2 matches the selection criteria, the resulting output includes only version 3, if that is the last version and 'last version only' was selected. 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 include the standard items (date, rating, tags, geotags, captions, comments) and any items you added in the optional search metadata dialog. Enter your search criteria. Select desired tags, dates, star ratings, text (comments, captions), file or folder names, and location names. See more details below. Press the [proceed] button to perform the search. The output is a standard gallery of thumbnails, or the metadata report format shown above. Choose images to view or edit by clicking the thumbnails. Navigate this set of searched images like any other gallery. You can save the searched images as a permanent album, which can be further edited to add or remove images (see |MANAGE ALBUMS|). Available tags are shown in Defined Tags and can be chosen with point and click. If the list of defined tags is long, it may be easier to type the desired tag into Enter Search Tag. Existing tags matching what you have typed so far will appear in Matching Tags, and you can click one of these to add the tag to the search list. If you type a tag with no match (it does not exist in any image), the list of matching tags will be empty. If you are using tag categories, you can select a category, and only those tags will be shown in the list of defined tags. If your tags list is huge, this can reduce the list to a manageable size for pointing and clicking. A date range may be entered to restrict the search to images within the date range. Choose photo date (EXIF, from camera) or file date (creation or last modification date). The format is yyyy-mm-dd. Images are selected which have a date on or after the first date, if present, and on or before the second date, if present. Missing month/day default to 01/01 for the low date limit and to 12/31 for the high limit. Times may optionally be specified using the format yyyy-mm-dd hh:mm. Missing times default to 00:00 and 23:59. To search for images having no EXIF photo date (e.g. scanned or downloaded images), use "null" in the first date input. 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. Folder and file names may be searched. In the field search files, enter any number of names used for your image folders and file names, separated by blanks. An input of "egypt cairo" would match all image folder or file names containing either of these strings. Substrings will also match. Image comments and captions may be searched. Enter the words to search for in the dialog search text field, separated by blanks. These will be matched to every word in the comments and captions of all images, and matching images are selected. Substrings will also match. To search locations, enter one or more location or country names in the search locations field. Only image files with geotags matching one of the entered locations will be selected. A location may be a city, park or other geographical name. The radio buttons 'all' and 'any' apply to tags, text, file names, and locations. You can select images having ALL the entered strings, or ANY of the entered strings. Example: if the location search field is "new york" and "any" is selected, images from New Zealand and York, England would be included. If "all" is selected, only images located in New York would be included. If you wish to find images with missing data, you can enter "null" as a match value. \_Search Metadata Dialog You may use this dialog to search for "extra" metadata items not present in the main dialog. The items available for any given image file can be seen with |VIEW ALL META|. These include camera make and model, exposure time, F-number, ISO, metering mode, focal length, shooting mode, etc. You can enter shortcut names like "exposuretime" instead of "Exposure Time". You may also enter match criteria, if wanted, so that only the images with matching metadata are reported. For example, if you enter "model" with the match value "DMC-FZ28" (a Panasonic camera) then only the images taken with this camera will be reported. The matching logic can be selected for each metadata key. \_match method\_ \_match value(s) report select and report all key values - no matching is done matches one or more metadata text values to select, separated by commas contains as above, but any metadata containing this text is selected number = single numeric value - equal metadata values are selected number => single numeric value - equal or greater values are selected number <= single numeric value - less or equal values are selected \_Wildcard matching The 'matches' method allows the use of wildcards in the match criteria. An asterisk (*) can be used to match any sequence of characters, including zero characters. A question mark (?) can be used to match any single character. Example: "mar?*ony" would match "mark anthony" but would not match "marony". "mar*ony" would match both. Performance (SSD, 3 GHz computer) \_Report Type\_ \_metadata search items\_ \_search speed\_ \_report speed gallery indexed items only >1000/sec >1000/sec gallery some non-indexed items 90/sec >1000/sec metadata indexed items only >1000/sec 130/sec metadata some non-indexed items 90/sec 130/sec Search speed is the speed to screen the entire image collection to find matches. Report speed is the speed at which matching images are output to the report window (thumbnail and reported metadata). There is a separate topic for image organization options, which explains the options for optimizing image searching: |ORGANIZING IMAGES|. \_Search Results\_ (album) The output of Search Images is automatically saved in the album "Search Results". This is for convenience. You may perform a search and then perform other functions which change the current gallery. If you need to refer to the previous search, this is instantly available in the album "Search Results". Rename this album if you want to keep later searches from replacing it. Limitation: The search function is limited to 40,000 results (images found that meet the search criteria). If this limit is exceeded, you are notified and the search is truncated. This limit exists because the space for a scrolled page in GTK is limited, and an attempt to create a larger page may result in a crash or lock-up. AREA MENU +image: areas.png AREA OVERIVEW 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 (e.g. Resize) 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. Fotoxx uses 'areas' instead of 'layers' as in Photoshop. Instead of selecting something from the image, making a separate layer from the selection, performing edit functions on the layer and finally merging the layers, you select something in the image and perform edit functions on the selection, with WYSIWYG feedback during the edit. Areas can also be saved to a file, loaded and pasted into other images, and edited there. SELECT AREA +image: select-area.jpg 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 hides the area outline, giving you better visibility of edits and possible area edge effects. Use the [Show] button to show the area outline. The [Clear] button removes all area selections. The select area dialog can be exited and re-started later to modify an existing area or start a new one. \_Line Color The color used for the mouse selection circle and the area outline can be changed at any time by clicking one of the color buttons. This allows good visibility against various background colors. \_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 to outline a rectangular area • Ellipse - drag to outline a circular or elliptical area • Freehand Draw - outline area by drawing (dragging) with the mouse • Follow Edge - outline by following image feature edges • Draw/Replace - adjust a previous outline by drawing over it • Select area in mouse - select pixels within a circle centered at mouse pointer • Select color in mouse - as above, but only colors (nearly) matching a set color • Select all colors in mouse - as above, spread into adjacent areas matching the colors within the mouse circle (this is called 'flood' in some editors) 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 a circle or ellipse. The drag start location is the center of the resulting ellipse. The angle of the drag determines the shape of the 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 line segments: right-drag closely along a line to erase it, then left-drag to re-draw the line. At the end, an area must be fully enclosed, with no gaps. Lines that overlap a little at the ends are OK. Gaps can be difficult to find and correct, so work at 100% image size or greater and be careful. A series of lines automatically connected with left clicks will not leave gaps, but deviation from this sequence is likely to create gaps. To reduce the possibility of gaps, use deliberate overlaps when manually connecting lines. There is a gap detection utility described below - |FIND AREA GAP|. If an area edge is also an image edge, you do not have to draw this portion of the area. Draw the line off the image edge, and draw it back onto the image at another position along the image edge. \_Follow Edge High-contrast pixels (likely image feature edges) between the last point drawn and a newly clicked position are found and connected. This is effective for clear edges that are not too irregular. Fuzzy and ragged edges may not work well and freehand draw will be needed if high precision is necessary. The rules for connecting lines are the same as explained above. Dragging the mouse instead of clicking works like freehand draw, but with a tendency to follow feature edges from slightly behind the mouse position. There is a special tool for very irregular edges - |SELECT HAIRY| (described below). \_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 fast way to make a small adjustment to an existing line. \_Mouse Radius and Match Level These two controls apply only to the 'select in mouse' methods described below. 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. Match level defines a required match level (0-100%) to select pixels based on their color and brightness. 0 means anything matches, and 100 means a perfect match is required. \_Select area in 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 color in 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 the match level. Adjust the match level down/up to match a greater/lesser range of colors. Click repeatedly on the image to change the color to be matched. \_Select all colors in mouse\_ (flood) This is called 'flood' in some editors. Left/right drag to select/unselect pixels inside the mouse circle. Pixels beyond the mouse circle are also included if they meet these two conditions: 1) their color matches any color inside the mouse circle, within the current match level. 2) they are within the 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 image feature 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, or change to the select area in mouse method described above. 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 easier to correct this by unselecting from outside the selected area instead of selecting from inside the selected area. You may need some practice to get a feeling for this and be able to work efficiently. \_Select all colors in mouse - 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 unmodified image over a distance called 'blend width'. The 'strength' of the edit function will ramp from zero at the edge of the selected area to 100% at a distance of 'blend width' from the area edge. 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 blend width 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 (i.e. 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 runs along an image edge, it is not considered an edge for blending. \_Edge Creep An area that has been finished (see below) can be expanded or contracted in 1-pixel steps. 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 feature color transitions to background. Expanding or contracting the area 1-2 pixels can produce a cleaner looking edge. The area remains finished after creep, but blend width is no longer valid and is discarded. \_Show/Hide Use [Hide] to hide the area outlines. This is useful to better see the effects of the area edits without interference from the area outline. Use [Show] to show the outlines and resume editing the area. \_Finish +image: select-area-finish.jpg An area is not effective for edits until it is successfully finished. Finish validates drawn outlines and colors the enclosed pixels for visual verification. When you are finished outlining areas, use the [Finish] button to complete the process. The popup dialog shown above summarizes two methods you can use to finish Method 1: Left-click the mouse inside areas that were outlined by hand. The enclosed areas are temporarily colored so you can verify that the final result is what you intended. 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. Press [keep] when all areas have been clicked and correctly colored. If the outline of a hand-drawn area has a gap, the coloring process will "leak out" and areas outside the enclosure will be colored. In this case, press [Undo] in the Finish dialog, which restores the original area outline. Find the gap in the outline and close it, then try [Finish] again. A 1-pixel gap in a large area can be hard to find. Zoom-in to see better. See the function |FIND AREA GAP| below for a fast method to find tiny gaps. Method2: Right-click somewhere outside all the enclosed areas. All enclosed areas will be colored for visual verification. If there is a gap, the results and the fix are the same as described in method 1. This method makes sense if you have many hand-outlined areas and you do not want to check them one at a time using method 1. Areas selected using one of the 'select in mouse' methods are automatically finished. These areas are mapped during the selection process, whereas areas selected with one of the line-drawing methods are mapped only when you click inside them. If you draw a line from one image side to another, you can click on either side of the line to make an area of all pixels on that side of the line. Example: draw a horizontal line on the boundary between sky and land. Click above the line to select sky, below the line to select land. The drawn lines must reach the image edges completely. \_Clear Discard the current area permanently. SELECT HAIRY +image: select-hairy.jpg This is an area selection function for complex boundary cases like hair or foliage, where selection by color may not work well, and manual selection would be very tedious. The objective is to deselect background pixels, making them transparent, while keeping foreground pixels opaque. The resulting "cutout" can be copied and pasted where needed, without the background. This function is not very intuitive, and you will likely need practice to become fast and effective. Follow these steps: • Choose a window background color (|PREFERENCES|) that contrasts with the complex area to be selected. In the example above, a white background was chosen to contrast with the dark hair. The background color can be changed during the selection process when different colors are being selected. • Use Select Area to select the entire area to be copied, including the complex boundary areas. • Start this function, Select Hairy. • Set a suitable mouse radius for painting over the boundary areas. • Left-click on an area representative of the colors to be selected, but away from the irregular edge. About 100 pixels will be sampled from the clicked position. The selected colors are shown in the corresponding dialog color box. • Right-click on a background area to choose colors to be deselected. These are shown in the other color box. • The colors to select or deselect can be changed at any time by left or right clicks on the image. • Choose a mode: select, deselect, or parallel (both options checked). Most of the time you will likely use both. • Left drag over the boundary to select/deselect pixels. Deselected pixels become transparent and the background color will show through. Selected pixels will remain opaque, or become opaque if previously deselected. • Right drag to restore the original pixels, to correct errors. • The select and deselect sliders determine the selection and deselection sensitivity. Move right for a more narrow match to the colors shown in the box, move left to select similar colors more widely. If the foreground and background colors are close, you will need to use narrow color matching to separate them. • When done, save the completed area with the Copy button or the Save button. • To paste the area into another position or image, use the menu Paste or Load (see |COPY/PASTE AREA| and |LOAD/SAVE AREA|). When the area is pasted into the target image, the paste dialog can be used to further blend the edges if needed. The pasted area remains active and can be edited with almost any edit function. FIND AREA GAP If an area outline was created by freehand drawing using multiple strokes, it is easy to leave small gaps in the outline that are not visible below 4x zoom. An attempt to finish such an area will fail because the mapping of the interior pixels will leak out through the gap and cover areas outside the intended area. If the Finish function fails, use this function to find the gap. Click somewhere on the outline of the failed area. The outline will be slowly re-drawn in one direction from the clicked position, until an "end pixel" is found. This is where the gap is. The outline is then drawn in the opposite direction until the gap is encountered. You can now see the gap position clearly. Zoom-in on the gap and close it using freehand draw, then test again to see if the full outline can be drawn without stopping. This function can be used in parallel with the Select Area function (both dialogs active). SHOW/HIDE AREA Show or hide the outline of the current area. Hiding the area is useful when the area is being modified with an edit functions. This makes it easier to judge the effects of the edit. These are also available as buttons in the Select Area dialog. ENABLE/DISABLE AREA Disable the current area and keep the data so that it can be re-activated later (Enable menu). This allows you to alternate edits within a selected area and edits for the entire image. INVERT AREA Invert an existing area: the entire image is selected except for the existing area. Using the function two times returns the original selected area. Inverting a selected area invalidates the edge calculation which must be repeated if edge blending is desired. CLEAR AREA Permanently discard the current area. This is also available as a button in the Select Area dialog. COPY/PASTE AREA A selected area can be saved to a cache file using the menu Areas > Copy. This area can be pasted into the same or another image using Areas > Paste. Click and drag to position the pasted area. The dialog controls can be used to resize, rotate, and change the brightness of the pasted area. The edge blend control allows you to blend the area edges into the background image if wanted. LOAD/SAVE AREA A selected area can be saved to a file using the menu Areas > Save. You are asked to supply a file name. A PNG file is created. The PNG file has an alpha channel for transparency information. The image is a rectangle enclosing the selected area. Selected pixels are opaque, and others are transparent. These files reside in [fotoxx-home]/saved_areas by default, but you can save them anywhere. Use the menu Areas > Load to load a saved area from a file and paste into the current image file. The background image will show through the transparent parts of the pasted area. Click and drag to position the area. The dialog controls can be used to resize, rotate, and change the brightness of the pasted area. The edge blend control allows you to blend the area edges into the background image if wanted. UNDO/REDO BUTTON +image: undo_redo.png 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 some edits were made to the current image: • left mouse click will undo one edit step per click • right mouse click will redo one edit step per click • if combined with the A-key, undo/redo ALL edits (see original and final image) • middle mouse click pops-up a list of all edit steps - go back to any step This works only if the current image file has not changed since being edited. If you open another file, the saved edit steps for the current file are discarded. Of course any saved file versions (created during the edits) are not discarded. EDIT MENU +image: edit.png TRIM/ROTATE +image: trim-rotate.jpg +image: trim-rotate2.jpg Remove unwanted image margins (crop) and/or rotate to upright or level the image. When the dialog opens, a selection rectangle is placed over the image. The areas outside this rectangle are darkened and represent the parts of the image that will be removed. Drag any side or corner of the rectangle to move that side or corner. The dialog box shows the current width/height ratio of the selection rectangle. If the box lock ratio is checked, then moving a side or corner will also cause another side or corner to move, so that the ratio is maintained. You can also drag from the middle of the rectangle to shift the whole rectangle without changing its dimensions. You can use the width and height spin buttons to adjust the pixel dimensions (or type-in values), and the selection rectangle will adjust to these. You can use the keyboard arrow keys to move a side or corner of the selection rectangle in 1-pixel steps. The last side or corner moved with the mouse is the one that is moved with the keyboard. The [Max] button resets the trim rectangle to the full image size. The [Prev] button retrieves a list of previously used width and height values. You can click on any of these to put values into the dialog width and height fields. The [Auto] button will automatically set the trim rectangle to omit transparent margins left over from combine and warp functions. These functions leave transparent margins where images did not overlay or were bent away from the edge. [Auto] tries to find a maximum rectangle within these margin areas. This may or may not be the desired margins, so you can keep them or move them with the mouse before committing with the [Done] button. The five 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). The [invert] button inverts the current width/height ratio. To level a slanted image, use the mouse to drag the right edge up or down until the image looks level. Position the mouse close to the middle of the window right edge. If the mouse is closer to the trim rectangle, only the trim rectangle will be moved. 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 Level] button can automatically level the image if the camera recorded EXIF 'roll angle'. The [Upright] button will automatically upright an image rotated 90 degrees if the camera recorded EXIF 'orientation'. 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 The popup menus for File View and Gallery View include this function for uprighting an image that is turned 90 degrees. If the camera was rotated 90 degrees (roughly) when the photo was made, the image EXIF data will indicate 90 degree rotation clockwise or counterclockwise. Fotoxx uses this data to upright the image file. The EXIF data is erased to prevent another rotation. If there is no EXIF rotation data, a popup dialog is shown with buttons for [-90°] [+90°] and [180°]. RETOUCH +image: retouch.jpg Adjust image brightness and color. • auto black level the image black point is set from a percentage of the darkest RGB values found • auto white balance the image white point is set from a percentage of the brightest RGB values found • click gray spot for white balance press the button, then click on a gray spot to set the white balance • click dark spot for black level press the button, then click on an image dark spot to set the black level • click for RGB distribution press the button for a popup window showing a live RGB brightness distribution • brightness moves the entire 'all' curve up or down • contrast moves the 'all' curve lower and upper parts in opposite directions • color saturation increases or decreases color saturation (black & white <--> intense colors) • color temperature change the RGB balance to match an illumination temperature (warm <--> cool) • 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. The ALL curve adjusts all colors, and the 3 RGB curves adjust individual colors. Use ALL first, then make revisions using RGB. \_Settings File Load button - load all dialog settings (including the curves) from a file Save button - save all dialog settings to a file chosen by the user This can help when the same or similar settings can be used for other photos. \_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 Use the [Prev] button when processing multiple images needing similar adjustments. RESIZE +image: resize.jpg 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 [Previous] 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. Resizing an image requires enough memory to hold both the original and the new image for a short time. Use of [2/1] or [3/1] with an image that is already very large may result in failure for lack of memory. A 50 megapixel image resized to 2x needs 1.6 GB memory, in addition to the 0.8 GB for the original image. ADJUST RGB +image: adjust-RGB.jpg This function is used to change overall brightness and contrast, or that of selected colors. The settings are retained within and across Fotoxx sessions, so this function can be used to process multiple photos made under the same lighting conditions and needing the same (or nearly same) adjustments. Use the [reset] button to restore all inputs to neutral values. • Brightness Increase or decrease overall image brightness • +Red -Cyan etc. Increase or decrease the brightness of one color and change complimentary color in the opposite direction • Contrast All Increase or decrease the overall image contrast • Contrast Red, Green, Blue Increase or decrease the contrast of individual RGB colors ADJUST HSL +image: adjust-HSL.jpg Change a selected range of colors in an image using HSL (Hue, Saturation, Lightness). This function can be used to fix a color caste or change one color to another (e.g. an overexposed sky - too white). If you are not familiar with the HSL color model, I suggest you read the Wikipedia article about this. Begin by selecting a target image color to match and adjust, using shift + left-click on the image. This color will be the center of a range of colors that will be selected for adjustment. Select what color attributes will be matched using the checkboxes for hue, saturation, and lightness. Each of these will narrow the range of selected colors. If none are checked, all colors are selected. Match Level can be used to further widen the range of selected colors. 100% means only closely matching colors are selected. The Output Color controls (hue, saturation, lightness) are used to set the new output color which will replace or blend with the selected input colors. You can also set this color from the image, using shift + right-click. The resulting color will be a mix of the original color and the new color. The new color fraction is set by the Adjustment slider, which can be set from 0 to 100%. Use the HSL checkboxes to determine which HSL components of input color are replaced with the corresponding HSL components of the output color. Use the sliders for output color HSL to adjust the new color. The output color is shown in the smaller box on the left, which is updated as you move the sliders. Move the sliders and watch the live image updates to optimize the result. This is not very intuitive, and practice will help. Hint: begin by matching on hue and saturation, and replacing only hue - this means that the output color saturation and lightness will be copied from the original image colors, and only the hue will be replaced. This function (like most others) can be used with |SELECT AREA| to restrict the change to selected image areas. MARKUP +image: markup.jpg This is a menu containing the image markup functions: draw text, draw line/arrow, draw box, draw oval These are described immediately below. All of these functions have an [apply] button which commits the current edit and lets you begin a new edit (new text, line, box, oval) within the same edit step. Press [done] to commit the last edit and close the edit function. The logical next step would be to save as new version or new file, or begin a new edit. DRAW TEXT +image: draw-text.jpg This function draws text directly on the image. Enter the text into the dialog. Multiple lines can be used. After entering the text, left-click the mouse where you want the text on the image. Click or drag to move the text elsewhere. Right click to remove the text. Use the [Font] button to select a different font. Use the [Size] control to increase or decrease the text size. Use the [Angle] control to change the slant angle of the text. The other controls allow you to provide a background color around the text, a text outline color, and a shadow effect. You can select the color and transparency for all of these. The width control adjusts the width of outlines and shadows. The shadow angle control sets the slant angle of the shadow. You can initialize the text from any available metadata in the image file. Enter the metadata key (e.g. "user comments") and press [Fetch]. You can abbreviate key names, e.g. "usercomments" instead of "User Comments". The [Open] and [Save] buttons start a file chooser dialog with which you can load or save all text data from or to a file. All the items in the dialog are loaded or saved, so you can keep a collection of often-used text strings and settings. The buttons at the bottom work as follows: • Clear - clear the text and metadata fields to blank • Replace - image with added text replaces the current image file • +Version - image with added text is saved as a new file version • Next - open next image file, place the same text at the same position • Apply - commit the current edit and start over: enter new text • Done - complete the current edit - image is ready to save • Cancel - abandon the current edit 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 |EMBOSS| to give the text an appearance of depth. DRAW LINE +image: draw-line.jpg This function draws 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. To keep a drawn line and begin another one, press [apply]. DRAW BOX +image: draw-box.jpg Choose a color and width for the box outline. Place the cursor at the position where you want a box corner, and drag to open the box in the wanted direction. To replace the box, drag again. To move the box, hold the shift key and drag the box from the center. To move one of the edges, hold the shift key and drag the edge. To keep a drawn box and begin another one, press [apply]. DRAW OVAL +image: draw-oval.jpg Choose either the oval (ellipse) or circle checkbox. Choose a color and width for the outline. Place the cursor at the desired center. Drag to draw the oval/circle. The drag angle determines the shape of the oval, from circle to extreme ellipse, wide or tall. To replace the oval/circle, drag again. To move the oval/circle, hold the shift key and drag from the center. To change the size or shape, hold the shift key and drag from the lower right edge. To keep a drawn oval/circle and begin another one, press [apply]. PAINT IMAGE +image: paint-image.jpg This function paints over the image with selected colors, using the mouse. The Paint Color button shows the current color. Click this button to choose a color using the standard Gnome color chooser dialog. You can also shift + left-click on the image to choose a color from the image. The palette button opens the dialog shown in the middle. Click on the image to select the color at that position. The chosen color is reflected in the color button of the main dialog. The image shown above is the default. You can use any image as a color source. Press the [browse] button and select an image where you have saved colors for recall. This choice remains in effect until changed. The HSL button opens an HSL color chooser shown on the right. This dialog can represent any possible color. Move the sliders to get the color you want. The chosen color is reflected in the color button of the main dialog. The brush size control sets a circle around the mouse pointer which shows the area being painted or erased. Left drag on the image to paint with the current color. Right drag over a previously painted area to erase (undo the painting). The opacity controls determine how rapidly the color is applied (or erased) at the center and edges of the mouse circle. 100% opacity applies the full color immediately, and a low value allows you to gradually change the color using multiple drags (analogous to spray painting from a distance). Erase also works this way: use 100% opacity to erase rapidly, and a low value to erase gradually. NOTE: zoom the image to 100% or more when using a small brush. If the mouse steps are larger than the image pixels and a small brush is being used, some pixels may be skipped by the mouse and cannot be painted. If paint over transparent areas is selected, painting over transparent areas will reduce or eliminate the transparency, depending on the opacity controls. If this option is not selected, only opaque areas of the image are painted. The [undo-last] button reverses the last paint or erase operation, and this can be repeated to remove many recent edits. Each new mouse drag operation is a unit of work that can be separately reversed. The memory for undo is limited, so only the most recent paint and erase steps are kept. [undo all] will put the image back in its initial condition. You can use the zoom image buttons to zoom the image larger or smaller as needed. If drag image is selected, you can pan or scroll a zoomed image by dragging it with the mouse. This stops the drag from painting or erasing. If a |SELECT AREA| is active, the painting is confined within the area. \_Wacom Tablet Operation With Paint Image, you can use a Wacom tablet instead of a mouse. Dragging the stylus in contact with the tablet surface will paint as described above. If one of the stylus buttons is held down while dragging, the operation will be erase instead of paint. You can also set paint or erase mode using the radio buttons in the dialog. Holding down a stylus button is cumbersome, which is why the radio buttons are there. Adding pressure to the stylus will increase the opacity, so you can make lighter and darker strokes without adjusting the opacity controls. COPY PIXELS 1 +image: copy-pixels-1.jpg +image: copy-pixels-1b.jpg This function paints image areas by copying from elsewhere in the image. This method can be used to duplicate objects within an image, or erase an unwanted object by replacing it with background taken from elsewhere. In the above example, the right boat was copied from the left boat, and one person was copied into a ghost image. Shift + left click on the image to select a source, then drag over the image area to be painted. The source area is painted over the dragged area, immediately or gradually, depending on the opacity settings. The brush size control sets a circle around the mouse pointer which shows the area being painted or erased. Left drag on the image to paint, or right drag over a previously painted area to erase (undo the painting). The opacity controls determine how quickly the image is modified (or erased) at the center and edges of the circle. 100% opacity paints fully and immediately, whereas a low value allows you to gradually paint using multiple drags. Erase also works this way: use 100% to immediately erase, and a low value to erase gradually. The [undo-last] button reverses the last paint operation, and this can be repeated to remove many recent edits. Each new mouse drag operation is a unit of work that can be separately reversed. The memory for undo is limited, so only the most recent paint steps are kept. [undo all] will put the image back in its initial condition. If paint over transparent areas is selected, painting over transparent areas will reduce or eliminate the transparency, depending on the mouse opacity controls and the gradual paint setting. If this option is not selected, only opaque areas of the image are painted. If a |SELECT AREA| is active, the painting is confined within the area. COPY PIXELS 2 +image: copy-pixels-2.jpg +image: copy-pixels-2b.jpg This function copies an area from one image to another image (they could be the same image). The target image is 'painted' by dragging the mouse over the area to be modified. A previously selected area from the source image is painted onto the target image. The area being copied and the area being painted are both enclosed in red circles which move with the mouse, representing the extent of the 'paint brush'. In the above example, the rightmost boat in the right image was copied to the left image. Start by opening the target image (the one that will be painted). Select the menu 'Copy Pixels 2'. A 2nd Fotoxx session is started automatically to show the source image. Open the source image in the 2nd session and arrange the two windows so you can keep both of them in view. Left click the source image near the center of the area to be copied. Left click the target image at the target position that corresponds to the source position clicked on the source image. Left-drag the mouse over the target image area. Pixels from the source image are copied to the target image. Red circles are visible on both images and represent the areas being copied from and to. Right-drag to erase (restore the original target image). To change the source or target copy positions, repeat the two left-clicks as needed. The brush size control sets the size of the circle around the mouse pointer, which shows the area being painted or erased. Left drag on the image to paint, or right drag over a previously painted area to erase (undo the painting). The opacity controls determine how quickly the image is modified (or erased) at the center and edges of the circle. 100% opacity paints fully and immediately, whereas a low value allows you to gradually paint using multiple drags. Erase also works this way: use 100% to erase immediately, and a low value to erase gradually. The [undo-last] button reverses the last paint operation, and this can be repeated to remove many recent paints. Each new mouse drag operation is a unit of work that can be separately reversed. The memory for undo is limited, so only the most recent paint steps are kept. [undo all] will put the target image back in its initial condition. The source image scale value is used to scale the source image to the target image. Start by making a trial copy to check the scale. If too big or too small, use [undo all] to erase the trial, change the scale value, and try again. If paint over transparent areas is selected, painting over transparent areas will reduce or eliminate the transparency, depending on the mouse opacity controls. If this option is not selected, only opaque areas of the image are painted. If a |SELECT AREA| Is active, the painting is confined within the area. PAINT EDITS +image: paint-edits.jpg 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 left-dragged over an area of the image, the current edit function is applied within the circle. The strength of the function is regulated by the center and edge 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, and the brightness curve is moved upward (brighten), then the image will slowly brighten in the area where the mouse is dragged. This is traditionally called 'dodge and burn'. 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 right-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 works only on the previously painted areas (but this could also be a feature). A suggested approach is: (1) start an edit function and make the initial settings, (2) start the Paint Edits dialog, (3) drag the mouse over the desired areas and watch the effect, (4) adjust the edit settings, (5) alternate between the previous two steps. (6) Exit from the edit function, then from Paint Edits. This method to "paint" edits incrementally can improve selected areas of an image quickly and easily. It works with the following edit functions: Retouch, Flatten, Gradients, Sharpen, Denoise, Color Mode, Shift Colors, Adjust RGB, Adjust HSL, Color Depth. Others may be added in the future. See the following functions for alternative methods to perform localized edits: |SELECT AREA| - perform edits confined within a pre-selected image area. |STACK/LAYER| - make multiple image versions and 'paint' where each version shows. |COPY PIXELS 2| - copy areas from one image to another by 'painting' with the mouse. UNDO EDITS +image: undo-edits.jpg A previously used edit function can be removed, locally and gradually, by "painting" with the mouse. The mouse pointer will have a circle around it with the given radius. As the mouse is dragged, areas within this circle are gradually restored to the status before the previous edit. Left drag to 'undo' or remove the edit, right drag to 'redo' or restore the edit. The rate of change is regulated by the center and edge power factors. Typically you will use a high value at the center and zero at the edge, meaning that the rate of change will be maximum at the center, changing gradually to zero at the edge of the circle. If an edit looks 'overdone' in some image areas only, you can use this function to moderate or remove the edit for selected areas. You can also use |SELECT AREA| to restrict edits to selected areas of an image, rather than 'undo' unwanted areas afterward. Select Area can blend the edges of the edited area to make the transition invisible, but this is sometimes not adequate. You may want a hard edge where there is a hard image feature edge, and a blended edge elsewhere. Select Area cannot do this. To fix the problem, you can omit edge blending in Select Area, and then use this function to blend edges where wanted. PLUGINS +image: plugins.jpg An example 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 "Gimp" which starts the command "gimp %s". 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 (Image Magick program) Gthumb gthumb %s The plugin menu is saved in the file [fotoxx-home]/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. RAW THERAPEE This function is available in the popup menu when a RAW file type is right-clicked, in either F-view or G-view. This is a program specialized for editing RAW image files. The program starts with the RAW file loaded. When you are finished editing with Raw Therapee, save the edited image as a 16-bit TIFF file with the same base name as the RAW file, then exit Raw Therapee. Fotoxx resumes and opens the TIFF file. Fotoxx looks for the TIFF file in the RAW file folder. If it is not found there, it searches all declared top image folders and their subfolders for a TIFF file with the same base name as the RAW file. ENHANCE MENU +image: enhance.png VOODOO 1 This is a fast automatic image enhancement with limited capability. This is sometimes effective and "good enough" for rapidly processing many photos. There is no dialog - the modification is simply done when the menu is selected. Reject the change with the [undo] button if desired. The modification consists of a slight flattening of the brightness distribution, an expansion of the brightness range if less than the full range is used, and a slight increase in the color saturation, more for darker areas of the image than brighter areas. The effect is sometimes minimal or even negative. VOODOO 2 This is an alternative automatic enhancement, using the flatten method described below. BRITE DIST +image: adjust-britedist.jpg +image: adjust-britedist2.jpg 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 and details are lost in image areas having nearly the the same brightness. Pixel brightness is redistributed so that each brightness level is more equally represented. Technically, the brightness distribution is made more uniform (flatter). The selected low/mid/high brightness region is flattened. 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 brighter areas. GRADIENTS +image: gradients.jpg +image: gradients2.jpg Gradients increases the apparent brightness range of an image by increasing local contrast. It is especially useful to improve HDR images, but can also be applied to any image. Gradients increases the contrast between nearby pixels without increasing the overall contrast. It relies on a characteristic of human vision: contrast within a small angle is perceived more strongly than contrast over a large angle. Gradients can bring out subtle details (low contrast) that would otherwise be hard to notice. Other methods can also be used: |RETOUCH| can increase the contrast for a selected brightness range (at the expense of others). |FLATTEN| can spread available contrast more evenly. These methods operate globally: all pixels of a given brightness are processed the same. Gradients processes pixels relative to nearby 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 corresponds to low-contrast pixels and the right end corresponds to 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 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 may also need 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. conservative for sky, more aggressive for textured surfaces like stone walls and vegetation. FLATTEN +image: flatten2.jpg +image: flatten1.jpg Flatten enhances visible detail in areas having poor contrast. The revised brightness for a pixel is based on the brightness distribution for nearby areas. A larger zone count corresponds to smaller/closer areas. Flatten controls the strength of the effect, and deband moderates darker or brighter image areas. This function can amplify noise in uniform areas like sky. If the deband control is insufficient, use |SELECT AREA| and |DENOISE|. Alternatively, use Select Area beforehand to select sky (or other areas) to omit, and then invert the selection prior to using Flatten. \_Technical Explanation\_ (optional) The image is divided into area zones according to the input zone count. Each pixel is adjusted based on the 9 closest zones, the 3x3 zones surrounding the zone of the pixel. More zones means smaller and closer zones. The brightness of a pixel is compared to the brightness distributions of the nearby zones, and the brightness is adjusted up or down in the direction that would flatten the distribution of these zones. The influence of the 9 zones are weighted based on their distance from the pixel being calculated. The influence of the leftmost zones goes to zero for a pixel on the right edge of its zone. The same is true for the topmost zones, etc. This prevents abrupt transitions that could be visible. The nature of human vision hides the radical alterations in pixel brightness, since the eye 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. GLOBAL RETX +image: global retinex.jpg +image: global retinex2.jpg Global Retinex can improve color and contrast for images with extreme fog/haze or color cast (e.g. a 100 year-old photo). Try the [Auto] button first. The image is searched to find the brightest and darkest RGB colors present. The darkest RGB values are subtracted from all pixels and the resulting RGB values are scaled up so that the maximum RGB values are near 255. Of course this may not be initially optimum. The spin buttons are set to the dark and white RGB limits found, and you can changes these to optimize the resulting image. You can choose your own dark and bright points by selecting the corresponding option in the dialog and clicking on the image. The multiplier spin buttons are set to 1.0 by default, and you can change these values to adjust the relative brightness of each RGB color. In the above example, the 1st image is the original, the 2nd image was made using the [Auto] button, and the 3rd image was made by selecting a bright spot on the turtle's shell as the bright point and the shadow under the front left foot as the dark point. The rightmost column of spin buttons will adjust all buttons in the same row together - use the mouse wheel or the keyboard up/down arrow keys. The blend slider is used to mix the input image and the retinex image in any ratio. The reduce bright slider is used to attenuate the effect for brighter image areas, especially sky, where Retinex can produce strange looking results. The algorithm was derived from concepts first published by Edwin Land in the 1970s. Search the web for 'retinex' to find more information. ZONAL RETX +image: zonal retinex.jpg +image: zonal retinex2.jpg Zonal Retinex can increase visible details in dark image areas and other areas having low contrast. The brightness range of a zone around each pixel is used to rescale the pixel brightness as though the entire zone were rescaled to cover the entire 0-255 brightness range. Zonal Retinex can produce dramatic results where Global Retinex is weak. The blend slider is used to mix the input image and the retinex image in any ratio. The reduce bright slider is used to attenuate the effect for brighter image areas, especially sky, where Retinex can produce strange looking results. The example above is about 60% original image + 40% retinex. Zonal Retinex needs lots of memory: a 30 megapixel image uses about 4.7 GB. SHARPEN This function has four methods to sharpen a blurry image. +image: sharpen.jpg +image: sharp-comp.jpg Unsharp mask: a fast and effective method also found in other image editors. A technical description can be found with a web search. This method can make visible 'halos' around high-contrast edges. Gradient: steepens brightness transition areas directly, somewhat like the Gradients function. Effectiveness is comparable to unsharp mask, but 'halos' are reduced. Kuwahara: small neighborhoods of pixels above, below, left and right of each 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. Image may appear 'blocky' if a large radius is used. Subtle details can be lost. Mean diff: pixel brightness is compared to the mean of pixels within radius. The brightness is increased or decreased for pixels respectively brighter or darker than the neighborhood mean. Effectiveness is comparable to unsharp mask, but 'halos' are greatly reduced. This is the only method that works on each RGB color separately. It can sharpen a color transition even if there is little brightness change. 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: blur.jpg +image: blur2.jpg Choose a method with the corresponding check-box, input the required parameters, press [apply]. If |SELECT AREA| is active, only the selected areas are affected. • Normal Mix each pixel with surrounding pixels within radius. Closer pixels have a higher weight. • Radial Mix each pixel with pixels along a line from a chosen center. Length sets the size of the lines. Choose the center by clicking on the image. • Directed Pull a position on the image using the mouse. The area around the mouse will be blurred in this direction. Span determines the size of the area blurred. Intensity determines the strength of the blur. • Graduated Only pixels with less contrast than the given contrast 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 to smooth skin tones without blurring hair or reducing the sparkle in the eyes. Taken to extremes, it produces a "cartoon" effect. • Paint Blend image pixels together by painting with the mouse. Radius sets a circle around the mouse pointer which shows the image area being blended. Use left drag to blend, right drag to restore. Power determines how fast the image is blended or restored, at the center and edges of the circle. • Background A new dialog is started. Blur the background image while leaving the foreground sharp. Use Select Area to select one or more areas that are to remain sharp. After selecting the foreground areas, invert the selection so that the background is now selected (menu Areas > Invert) - this area is the one that will be blurred. There are two blur methods available: Constant blur: a constant blur radius is used for all areas blurred. Increase blur with distance: use the minimum blur radius for pixels adjacent to the foreground and the maximum blur radius for pixels at the maximum distance away from the foreground. This requires that the edge distance for all background pixels be calculated, which can take significant time for a large image. This is done automatically if required. Blur radius can be varied without recalculating the edge distances. Tilt-Shift Effect: You can do this by selecting a horizontal rectangular area to remain sharp, and increasing the blur with increasing distance from this area. DENOISE +image: denoise.jpg This function reduces the noise in photos taken under poor lighting conditions (high ISO), making uniform surfaces appear speckled. Multiple methods are provided because the best method varies with noise characteristics. Mixing methods (using one and then another) is often helpful. Choose the method, set the radius and threshold parameters, 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 but important area and experiment with the different methods and settings until you make a decision, then apply the chosen method to the entire image. The threshold value is the pixel brightness difference above which no change is made. The initial value is set from a preliminary noise analysis of the image. Threshold regulates a tradeoff between noise removal and loss of detail. The threshold value for the Wavelets method is different. A value of 1 or less is appropriate for images with low noise. Use a higher value if the noise is greater, but some loss of detail may be noticeable. The dark areas slider can be used to restrict the process to darker image areas, which are most prone to noise. If left at the right end, all areas are processed. Here is a short technical description of each method: • Voodoo This is a fixed combination of the other methods to serve as a "one button" method. Try this first. It should be adequate for most cases. • Chroma This works like Anneal, but uses the YCbCr color space instead of RGB. • Anneal Pixels are blended with surrounding pixels that have similar color and brightness. This reduces noise with minimal blurring of feature edges. • Flatten Pixels are compared to the mean and sigma of pixels within a radius. Those outside one sigma are moved slightly back toward the mean. • Median Pixels are set to the median value of their neighbors within a radius. Best way to remove 'salt and pepper' noise - set threshold to maximum. • Top Hat Detect outliers by comparison with surrounding pixels at a distance. The distance is increased in steps from 1 pixel to the radius limit. Outliers are flattened slightly. • Wavelets RGB brightness (with noise) is converted into a series of wave functions that nearly sum to brightness and represent an approximation with less noise. The wavelets algorithm was adapted from code originally written by Dave Coffin. The [measure] button starts the lower dialog to measure the actual noise level. Move the mouse over the image to show the RGB noise levels within the mouse circle (radius 10, about 300 pixels). This must be a featureless area so that noise is the only variation present. A gray sky is a good source, or an image area that is badly out of focus. To measure camera sensor noise, use a RAW image, since JPEG images are processed inside the camera to reduce noise. The graph shows pixel deviations from the regional mean. The solid line corresponds to the mean, and the dotted lines are at brightness levels +5 and -5 from the mean, on a scale of 0-255. The center to edge axis is logarithmic from 0 to 10, with 7 at the midpoint. The numbers at the bottom show the mean RGB brightness and noise levels for the area within the mouse. More information about camera noise in RAW images can be found in the |TECHNICAL NOTES|. RED EYES +image: red_eyes1.jpg +image: red_eyes2.jpg 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 more 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. MATCH COLORS +image: match-color.jpg 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. A common usage is to remove a color cast from an image by marking a spot on the image that should have a color taken from another image. Procedure: The dialog lists 5 steps to take in sequence. 1 Set a radius for the spot sample. The resulting mouse circle 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 first image. The image colors will change within a second or two. You can change the radius and click on another spot - the colors will change accordingly. Click the dialog [done] or [cancel] button to finish. SMART ERASE +image: smart-erase.jpg +image: smart-erase2.jpg 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 unselects. 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. 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. For long thin objects (e.g. power lines), you can click on two ends and everything in-between will be selected. The selection works in a straight line, so for curving objects you will need to click at intervals to follow the curve. CHROMATIC 1 +image: chromatic1A.jpg +image: chromatic1.jpg Fix lateral chromatic aberration - the type where color fringes appear mainly in outer image areas. See below for axial chromatic aberration. If you are not sure which function to use, try this one first since it is fast and easy compared to the one below. The left image above is a magnified cutout of the top left corner of a photo with lateral chromatic aberration. This was mostly eliminated in the processed image to the right. 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. Press the [search] button. The red and blue color planes are shifted to better align with the green plane, and the factors used are stuffed into the dialog box. You may be able to improve the result by revising the factors directly in the dialog: use the up/down arrow keys or mouse wheel to change the factors while watching the color fringes on the image. Modify the largest values (2.4 and 3 in the above example). Important: this function will not work well if the image has been trimmed so that the image center is no longer the same as the center of the original photograph. \_Method\_ (optional, for geeks): The red and blue components of each pixel are shifted using the formula R2 = F1 * R1 + F2 * R1 * R1 + F3 * R1 * R1 * R1 R2 is the shifted distance from the image center, R1 the original distance, F1 - F3 are the factors to be discovered for each color red and blue. For a perfect image, F1 = 1 and F2 = F3 = 0. The algorithm tests a range of values for F1 - F3, and retains the best values found. In each test, the image red or blue plane is warped using the formula for R2, and the warped plane is subtracted from the green plane. The best values for F1 - F3 are those where the sum of the absolute pixel differences is minimum: the warped red and blue planes overlap the green plane most accurately. CHROMATIC 2 +image: chromatic2A.jpg +image: chromatic2.jpg Fix axial chromatic aberration - the type where a narrow color band runs along dark/bright feature edges, or where narrow dark features are discolored by a bright background. The cause can be camera lens aberration, light scattering, or the camera sensor and its data processing. Select each of the three check buttons in turn, and click on the image to select the corresponding color. Set the proximity value. Press [Apply]. Pixels at or near the chromatic color will be replaced (hue only) with the replacement color, but only where the background color (or near match) is closer than the given proximity in pixels. The default 10 is normally OK. The point here is to avoid changing unrelated pixels in other image areas. You will normally have to select a new chromatic color and press [Apply] multiple times, since only a narrow band of matching colors will be changed. Click the chromatic color at any position that remains unchanged, and press [Apply] again, until all the colored pixels have been replaced. In the above example, with multiple shades of pink and green, 11 different selections were made over a time of about 2 minutes. The [Undo] button will reverse the previous [Apply], and can be repeated to reverse multiple changes. Color match level sets the sensitivity of the color match. If unwanted pixels get changed, undo the change and set a more exact match level, e.g. 80 instead of the default 70, and repeat the [Apply] step. This match level is also used for matching the background color. Tip: Use |SELECT AREA| to confine changes to an intended area. After completing the edit, you can also use the Undo Edit function to remove changes from unwanted areas. VIGNETTE +image: vignette.jpg This function is used to correct the darkening sometimes seen in the corners of a photo. It can also be used to highlight or colorize an object or area within an image. Click or drag the mouse on the image to change the vignette center, which is initially at the center of the image. Select Brightness to change the brightness of the image in a radial pattern: Adjust the left or right end of the curve to change the brightness of the center and edges of the image respectively. You can give a dark surround to a portrait face, or you can fix an image with darkened corners. The curve middle level corresponds to no change. Use lower values to darken and higher values to brighten. The example above gradually darkens the periphery of the image while leaving a broad central area unchanged. Select Color to add a chosen color to the image in a radial pattern. Curve values of zero represent no change, and higher values add the chosen color to the image. The highest value corresponds to 100% color. Use this function to add a color surround to an image, e.g. surround a face with a gradually increasing color. \_Irregular Vignette +image: vignette2.jpg You can make a vignette with arbitrary shape as follows: • Select Area - select the image area to remain visible • Invert the area to select the areas outside the image • Set a blend width value for the edge fade-out width • Keep the Select Area dialog active • Paint Image - paint the outside areas with the desired color (the image edges will fade-out over blend width pixels) • Adjust the blend width value and paint again until satisfied REMOVE DUST +image: remove-dust.jpg +image: remove-dust2.jpg 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 automatically removed, but Smart Erase can be effective here. EFFECTS MENU +image: effects.png SKETCH +image: sketch.jpg This function transforms a photo into something like a sketch. Dark pixels are aggregated into fewer pixels, leaving vacated areas brighter. Contrast can also be used as a proxy for dark pixels. Threshold can be used to filter the input image by brightness. Clip Level is used to filter the output to further reduce isolated or marginally dark pixels. Choose colors for foreground and background. Two algorithms are provided. Results may look more interesting with one or the other. Reducing the size of the input image may also give more interesting results. CARTOON +image: cartoon.jpg Transform a photo into a cartoon-like drawing. Black lines are drawn over feature edges in the image, wherever the brightness or color changes abruptly. Line Threshold sets the sensitivity for edge detection and line drawing, causing fewer or more lines to be drawn. Line Width makes the lines thicker or thinner. Blur Radius controls a blur function which can make the lines look more curvy or less jagged. Kuwahara Depth controls a sharpen function which can strengthen feature edges within the image. This function can be quite slow to respond (10+ seconds) if the image is large and if the blur and kuwahara controls are set to high values. It works faster and better on smaller images, around 2 megapixels or less. I suggest you start with small values (2-3) for kuwahara and blur, then adjust line threshold to optimize the density of the drawn lines. Now change kuwahara and blur in small steps and re-adjust line threshold. If you are working with a large image, I suggest you select a small but important area within the image so that optimization can proceed faster. Then delete the area and do the entire image. You may be close to optimum already. Other edit functions, applied before or after Cartoon, may enhance the effect. These include color saturation, gradients, color depth, texture, warp curved, graduated blur (3rd image above). Paint and Copy Pixels are useful to remove minor flaws manually. Shadows in the image are a particular problem, since they cause lines to be drawn where normally not wanted. LINE DRAWING +image: line-drawing.jpg This function transforms a photo into a line drawing showing outlines of objects within the image. Feature 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 a feature edge must be in order to get enhanced, from "no edges" at the low end to "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 outlines) to full brightness. The black/white checkbox converts the image from color to black and white, and the negative checkbox makes a color negative image. EMBOSS +image: emboss2.jpg +image: emboss.jpg 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. DITHER +image: dither1.png +image: dither2.png +image: dither3.jpg This function provides several ways to dither an image - convert into dots. • dither 0 - colored round dots in the style of Roy Lichtenstein • dither 1 - pure bright RGB dots with optimized distribution • dither 1 - black/white dots with optimized distribution • dither 2 - classic dither using all RGB colors within a limited bit depth • dither 3 - classic dither using a custom palette of colors The classic dithers use the Floyd-Steinberg method of compensating for color errors: mismatch between an input image color and the closest color from the palette. Dots with nearby colors are mixed-in as needed so that the average over an area more closely matches the input color. The error compensation can be turned off if wanted, to get distinct color bands or contours. PAINTING +image: painting1.jpg +image: painting2.jpg +image: painting3.jpg 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 Set the number of colors to use: 1 = 8 colors, 2 = 64 colors ... 5 = 32768 colors \_patch area goal Set 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. \_required color match Set 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 (minimum 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 |EMBOSS| can add interesting texture to the image. TEXTURE +image: texture.jpg +image: texture2.jpg 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 +image: pattern2.jpg +image: pattern1.jpg 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 web 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 [fotoxx-home]/patterns. Use the [Browse] button to select a pattern file. The selected pattern will be tiled to cover the base image. The pattern is partly transparent so that the base image shows through. Use Zoom to grow or shrink the pattern size. There are two methods to mix the pattern with the base image. The pattern opacity can be set 0-100%. The base image is used to fill the unused opacity - e.g. if the opacity is set to 30%, then the final image will be 30% pattern and 70% base image. Contrast is used to modify the base image brightness using the pattern brightness as a template. The two parameters can both be used and mixed in any ratio. If the pattern file contains an image that repeats at fixed intervals both horizontally and vertically, the [Calculate] button can be used to set width and height to match. The result will be a continuous pattern without any edge effects. If the pattern is irregular and edge effects appear where the duplicated patterns are joined, you may be able to improve this. The two Overlap values determine how much the duplicated patterns overlap at the edges, horizontally and vertically. Add some overlap to mask edge effects. Select Area can be used to apply a pattern to part of an image, or different patterns to different parts. After applying a pattern to an image, it might be interesting to use Emboss or Gradients to add a 3-D effect to the pattern. The checkbox "Grayscale" will convert a color pattern to grayscale before using it. Useful pattern files in LibreOffice: /usr/lib/libreoffice/share/gallery/www-back MOSAIC +image: mosaic.jpg 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 needs time (100K/min. on a strong computer). If you change the tile dimensions, press [Tiles] again to regenerate the tiles. After the tiles are created, press [Image] to convert the current image into a mosaic using these tiles. This takes only a few seconds. Tiles are chosen by matching the average tile color to the average image color at the tile position. If thousands of images are available and if the range of colors is good enough, the mosaic will turn out quite good. Zoom-in on any part of the image to see the tile images. The Tile blending slider will cause the image to be blended with the tiles, making it look better if the tiles are a bad color match. If the blending is less than about 50%, the tiles remain almost as clear as before (the eye compensates the false tint). After a mosaic is created, you can click on any tile to get a larger popup image. This is the full image for the tile, so you can drag the window as large as you like and it will remain sharp. A mosaic image can be saved like any other edited image, but if a saved mosaic is opened, clicking the tiles for a bigger image will not work. Regenerating the mosaic is quite fast, so do this if you want the popups to work. COLOR MODE +image: color-mode.jpg +image: color-mode2.jpg Use this function to make a black and white or color negative, or convert a negative image into a positive image, or convert to sepia coloring (for an aged photo effect). Select one of the buttons: • reset - return the image to the original state • black/white positive - convert a color image to black and white • black/white negative - convert to black and white and invert brightness • color negative - replace each RGB color with its compliment • RGB -> GBR - red/green/blue colors are replaced with green/blue/red colors • RGB -> BRG - red/green/blue colors are replaced with blue/red/green colors • sepia - convert to a modified black and white for an aged photo effect The slider can be used to apply the effect incrementally, from 0% (no change) to 100%. Color negative: Each RGB color is replaced with the maximum value minus the RGB color value. For example, if the RGB colors (% of maximum) are 20/40/60, then the negative color is 80/60/40. For pure RGB colors, red becomes cyan, green becomes magenta, and blue becomes yellow. COLOR DEPTH +image: color-depth2.jpg +image: color-depth1.jpg This function changes the normal 16 bits per RGB color to any value between 1 and 16 bits per color. At 8 bits per color, there are 16.8 million colors (256 x 256 x 256). SHIFT COLORS +image: shift_colors.jpg 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. ALIEN COLORS +image: alien-colors.jpg +image: alien-colors2.jpg Repaint an image or selected area with random alien colors. You can control the rough pattern size and the intensity (from barely visible to dominant). BRITE RAMP +image: bright-ramp1.png +image: bright-ramp2.png This function varies brightness or color across the image. You can use this to compensate for uneven lighting or a color cast that varies across the image. The direction of change is determined by drawing a line on the image. Create the line by clicking on the image, then drag either end to set the direction wanted. In the example above, green is increased in the direction of the line. Edit the All curve first to adjust overall brightness (all colors), then adjust the individual RGB color curves if needed. The image reacts quickly to both line changes and curve edits. PAINT TRANSP +image: paint-transparency.jpg +image: paint-transparency2.jpg 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 can also use transparent areas. The paintbrush radius control sets a circle around the mouse pointer which shows the area being transformed. Left drag on the image to increase the transparency, right drag to decrease. If gradual paint is checked, the strength controls determine how rapidly the transparency changes at the center and edges of the circle. If gradual paint is not checked, transparency is set to 100% (left drag) or 0% (right drag) for the entire area covered by the mouse circle. If a |SELECT AREA| is active, only the selected areas are affected. An image file having transparency information must be saved as a TIF or PNG file. JPEG files do not support transparency. MIRROR +image: mirror1.jpg +image: mirror2.jpg Choose either horizontal or vertical mirror from the dialog. The image is reversed (mirrored) vertically or horizontally. Repeating the mirror restores the original image. Doing both a horizontal and vertical mirror is the same as 180 degree rotation. CUSTOM KERNEL +image: custom-kernel.jpg Apply a custom convolution kernel to an image. The underlying technology is explained in Wikipedia. Input a kernel size, a multiply factor, an add factor, and fill-in the table values. The values can be saved into a file and retrieved later by using the [Save] and [Load] buttons. [Apply] applies the kernel to the image. [Reset] restores the original image status. Several kernel example files are available initially. You can modify these and save with a custom name. Math: the brightness values for each N-by-N block of pixels in the image are multiplied by the corresponding values in the kernel matrix and added together. This number is multiplied by the multiply factor, and the add factor is added to it. This final value becomes the new brightness level for the image pixel at the center of the N x N block. WARP MENU +image: warp.png UNBEND +image: unbend2.jpg +image: unbend3.jpg +image: unbend1.jpg Panoramas of nearby subjects (typically buildings or interior rooms) may show straight lines that are curved, or buildings that are slanted. Warping the images was necessary in the panorama process in order for the images to fit together. The Unbend function can be used afterwards to straighten the panorama image if needed. Vertical and horizontal dotted lines are drawn over the image, showing the unbend axes. Click or drag the mouse near the end of a line to move it. If values in the four input controls are changed, the image is warped in the manner indicated by the corresponding four images. Increase or decrease the values and repeat until satisfied. Move the axes to change the centers of warping. See also |WARP IMAGE| for another method of correcting image curving and perspective. PERSPECTIVE +image: fix-perspective.jpg This function can be used to straighten a photo made from an offset angle. The image on the left is the original photo, taken from right of center to avoid reflections. The image on the right is the straightened version. This function can also be used to straighten a high building photographed from street-level or from the side. Click on the four corners of the tetrahedron that you want to make into a rectangle (in the above case, the four corners of the painting or frame), then select [Apply]. Use [Reset] to go back and try again if needed. The clicked corners are marked with small white boxes containing "A, B, C, D". The upper left corner of each box precisely marks the image position. Clicking near a box will move it to the new position. The [Trim] button will automatically trim the image at the selected corners. The trim and straighten can be undone in sequence with the undo button. You can use the keyboard arrow keys to move the corner markers in 1-pixel steps. The arrow keys work on the last corner clicked or moved. WARP IMAGE +image: warps.jpg 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. 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, drag only from 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". Details can be found with a web search. UNWARP CLOSEUP +image: unwarp-closeup.jpg Closeup face photos are often distorted, because areas closer to the camera are larger in the photo than areas farther away. This function can be used to reverse the distortion. Use Select Area to select the face first (does not need to be accurate). Click the image near the center of distortion (above example: between the eyes). Move the slider to optimize. If the face is turned away from the camera, experiment to find the best center. FLATTEN BOOK +image: flatbook.jpg This function can flatten a photographed page from a book. If the book is thick, the pages bend downward at the binding, and the photographed text is squeezed together. This function straightens the the page and stretches the squeezed 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 not level. Use Fix Perspective to make the page more rectangular if needed. Start the Flatten function. Click the mouse along the top edge, creating visible marker dots at the clicked points. After 4+ points are available, a curved line is drawn through the points. Add more points and drag the points as needed to make the line conform closely to the page edge. Repeat for the bottom edge. Press [flatten] to flatten the page. The edges should now be straight (or straight enough). The text near the binding is still squeezed together. Expand the text by pulling the top and bottom sliders. The text is spread out in a way that is proportional the the slope of the top and bottom page edges, so the area near the binding is stretched the most. The [undo] button restores the unmodified image and the marker dots, which can be adjusted for a revised attempt. AREA RESCALE +image: area-rescale2.jpg +image: area-rescale.jpg This function rescales an image to a smaller size, leaving selected areas unchanged. The goal is to increase the relative size of the area of interest. The image above was reduced, but the boat in the foreground was left unchanged. First, select the areas that are to be preserved using |SELECT AREA|. Press the [proceed] button. The selected area is erased and the mouse cursor changes into a drag cursor. Pull the image inward from the upper left corner. This operation may be repeated as needed until you are satisfied. Press the [done] button. This function works best when the selected area(s) are small in comparison to the entire image. The pixel rows and columns that intersect the selected areas are not changed. Other areas are compressed as the image is pulled inward. MAKE WAVES +image: make-waves.jpg This function distorts an image into a wave pattern, as if 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 edit functions, this function also works within a selected area. The flood above is fake. TWIST +image: twist-image.jpg This function twists the image around a point chosen with a mouse click or drag. Click the desired position and move the Twist slider the amount wanted. You can also drag the mouse around the image to change the center of twisting. The Center slider untwists the central area relative to the rest. The Angle slider rotates the image. SPHERE +image: sphere2.jpg +image: sphere.jpg 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 (right) to a flat image (left). The magnify slider 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). STRETCH +image: stretch.jpg Stretch an image from the center outwards. Drag the mouse to choose a central point where the scale will be 1x. 'Flatten' is used to reduce the effect. 'Magnify' is used to adjust the image size. INSIDE-OUT +image: inside-out.jpg This function inverts the center-edge distances of each pixel. You can click or drag the mouse to vary the center point. TINY PLANET +image: tiny_planet.jpg Wrap an image around a circle. If the image is a 360 degree panorama, there will be no visible seam. • Center Hole: the image is wrapped around a central hole. The size may be zero. • Cut Top: remove a top margin from the input image before conversion. • Cut Bottom: remove a bottom margin from the input image before conversion. • Reverse R: mirror the input image vertically. • Reverse Theta: mirror the input image horizontally. • Rotate: turn the output image around the center. ESCHER SPIRAL +image: escher_spiral.png +image: escher_spiralA.jpg Transform an image into an endless inward spiral like the famous clock of M.C.Escher. Width determines the starting width of the outermost layer. Rim determines the starting width of the border separating layers, and Color determines its color. The default center is the center of the image. Click on the image to set a new center. COMBINE MENU +image: combine.png HDR \_High Dynamic Range +image: HDR.jpg 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 (|SELECT IMAGE FILES|), which must all have nearly the same pixel dimensions. The images are aligned and combined automatically. This needs 10-30 seconds per image, depending on image size and CPU speed. When done, the combined image is shown, along with a dialog for 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 remaining images in-between. The curves can be edited by dragging them with the mouse. The corresponding image contributions are changed accordingly, and you can see the results in real-time in the output image. In general, the brightest image should have a higher contribution to the darker pixels, and the darkest image a higher contribution to the brighter pixels. The above example shows two input images and their output image. The curves were adjusted to optimize the brightness for both the foreground and background buildings. It is often useful to apply the gradient function after HDR, to increase the perceived brightness range. HDF \_High Depth of Field 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 if 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. +image: HDF-paint.jpg Processing the photos: in Fotoxx, choose the HDF menu function and select up to 9 images (|SELECT IMAGE FILES|). The images will now be aligned as well as possible. This needs 10-30 seconds 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" it 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 +image: stack-paint.jpg +image: stack-paint2.jpg Stack (overlay) multiple photos of the same subject taken at different times. Remove tourists and cars that come and go between shots. Making the photos: Aim the camera at the same distant point and take multiple photos as tourists or cars move in front of the subject. Try to get two or more photos for each area of the subject not obstructed by the transient objects. Hold the camera steady and aim at the same point, so that the images will align accurately. Processing the photos: In Fotoxx, choose the Stack/Paint menu function and select up to 9 images (|SELECT IMAGE FILES|). The images will now be aligned as well as possible. This may need 10-30 seconds per image, depending on image size and CPU speed. The output image (lower left) is an even mix of the aligned input images (top row). When the alignment is complete, the dialog shown above starts. You can select any input image and "paint" with the mouse on any area of the output image. The single selected image is used for area being painted. For each area in the image, choose an input image that is free from the transient objects. The radius of the paintbrush can set larger or smaller, so you can paint large areas quickly and control fine detail when needed. Automatic operation: press the [Hide] button to see if transient objects can be removed automatically. This will work for areas in the image where multiple photos of the unobstructed background are available (lower middle image). To show all of the transient objects, press the [Show] button (lower right image). This generally works, but not perfectly. Slight image misalignments or lighting differences (esp. moving shadows) may cause the algorithm to select background pixels instead of foreground pixels. You can use the paint method described above to repair the errors manually. STACK/NOISE +image:stack-noise2.jpg +image: stack-noise.jpg 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. Processing the photos: In Fotoxx, chose the Stack/Noise function and select up to 9 images (|SELECT IMAGE FILES|). They will be combined automatically and shown, and a dialog will open. The initial output image is a combination of all the selected input images, averaged together. This means that the RGB values for each output pixel are the average of the RGB values for the corresponding input pixels. The following alternatives can be used to possibly reduce the remaining noise a little more. The use median button will change the output pixels from an average of the input pixels to the median of the input pixels (1-3 middle RGB values are averaged, depending on the number of images). This may or may not be better, so switch back and forth to compare (the screen update may need several seconds). The checkboxes for omit low pixel and omit high pixel will cause the lowest and highest RGB input values to be discarded before averaging. This may get noise spikes removed from the mix. This has no effect if the median method is selected. STACK/LAYER +image: stack-layer.jpg Use this function to combine different edited versions of an image, where you can determine which version is shown in selected image areas. Any images can be combined that have the same pixel dimensions. Select up to 9 images of identical size. The initial output image is a blend of all input images. Select an image and use the [Fill] button to fill the entire output with the chosen image. Select an image and use the mouse to 'paint' that image locally in areas dragged by the mouse. The paint area is a circle with Paint Radius around the pointer. The paint speed is controlled by the Opacity settings for the center and edge of the mouse. Use low numbers to paint gradually. STACK/SLIDER +image: stack-slider.jpg Two images may be selected. The two images are overlapped and displayed with a movable boundary. The first image is left of the boundary, the 2nd image is right. Move the boundary line by dragging with the mouse. This function is one way to show differences between an original and edited image. IMAGE DIFFS +image: image-diffs.jpg This tool shows the differences between two images by subtracting the RGB values. The pixels in one image are subtracted from the pixels in the other image, and negative values are inverted. Where the pixels exactly match, the result is black. The two images can be fine-aligned with the X- and Y-align values. If one image was made by editing the other one, without trimming, rotating or resizing, then align values of zero will show the image differences. The align values can be deliberately offset to produce an arty effect, as in the above example. See also Duplicate Images. PANORAMA +image: panorama.jpg +image: panorama1.jpg This function stitches 2-4 images together to make a wide-angle image or panorama. The images must overlap by 15% or more, so that the program can find where they match and join them together. Start by selecting 2-4 image files (|SELECT IMAGE FILES|). The images are initially joined and shown with a small transparent overlap. A pre-align dialog (above) asks you to drag the images into rough alignment. Drag the images into the correct left to right order. The image to drag may overlap other images. To be clear about which image is being dragged, drag from near the center of the image. After the images are in the correct order, align each image to its left neighbor. It works best to proceed from left to right. Move an image horizontally and vertically into rough alignment with its neighbor to the left, then rotate the image if needed by dragging the bottom edge left or right - the image pivots around the mid-point of its overlap with the image to the left. The fastest method is to align the overlap middle region first, then rotate the right image if needed to bring the upper and lower overlap regions into alignment. Extreme accuracy is not needed. Use the [resize] button to get a bigger combined image after moving them closer together. The images should be correctly curved and fit together well. If they do not, then the lens mm parameter (focal length, 35mm equivalent) needs adjustment. The curvature of the images changes as lens mm is adjusted. The initial value is obtained from the EXIF data if available, and this is normally good enough. You can measure and set lens mm manually using the [search] button described below. If the images have no curvature (e.g. scanned images), use the no curve checkbox to set the lens mm effectively to infinity. If an image was trimmed so that the greater dimension (width or height) was reduced, then the EXIF focal length is no longer valid, and the EXIF initial value may not work well. A section of an image taken from the middle has an effective focal length greater than the original. Increase the lens mm parameter until the images fit together reasonably well, or use the [search] button to make a more precise determination (described below). The 'no auto warp' checkbox is normally unchecked. Its purpose is described below. The 'manual align' checkbox will use the manual pre-alignment as the final alignment. This is for images that have no clear features to match and align the images automatically. Press [proceed] when pre-alignment is finished, and the program will do fine alignment and join the images. Internally, the images are shifted and rotated and the degree of match is evaluated. This is done with increasing image sizes until the best alignment is found. This may need a minute or more, depending on CPU speed and image size. You can speed up the process greatly if you reduce the input images to 1/2 size. Do this also if the process fails for lack of memory. Panoramas with 4 large images can require 2+ GB of memory during processing. +image: panorama2.jpg When fine alignment is complete, the combined image is displayed. The above dialog 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. Use the [auto color] button to perform an automatic color match, which is often adequate 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 RGB and brightness color controls allow you to make additional changes to better match the images. Select one of the images with the radio buttons, change the brightness and color values, 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 overlaps, the color balance is gradually shifted over this many pixels, to mask imbalances that cannot be fully corrected. The default is 1 pixel, which makes any brightness or color differences look obvious. If the images do not align perfectly, you may be able to improve the alignment by using the mouse to push the images into alignment. In the final dialog, select mouse warp, select which one of the images with the radio buttons, and then drag the mouse along the edge of the image where it should align with its neighbor, moving the image into alignment. The image is moved locally around the mouse while more distant parts stay fixed. Parallax shifting will require a compromise, since it is generally not possible to keep both foreground and background in alignment. The flatten image control can be used to "unbend" the image, which will straighten curved lines (e.g. curved buildings). A value of zero has no effect and a value of 1.0 will fully flatten the image. When done, use |UNBEND| , |WARP IMAGE| , |TRIM/ROTATE| etc. for final adjustments. \_Scanned images Scanned images can be combined if there is enough overlap. Check "no curve" since there is no curvature. \_Auto Warping The images are slightly warped in various directions during alignment to find the best match. This is to compensate for shifts in camera horizon or rotation, causing image distortions that reduce the quality of fit. If the overlap area of two images includes a large object that moved in or out between the two photos, the alignment process may go crazy trying to match the images, resulting in alignment that is very poor. By selecting no auto warp you may be able to get the alignment to succeed or have a smaller error. Another option is to select manual align. \_Setting lens mm Automatically The [search] button in the panorama pre-alignment dialog initiates an automated search for optimum lens mm. Use a suitable image pair: the subject is 50+ meters away, the images have a low horizon difference and little relative rotation, and there is plenty of high-contrast detail in the overlap area. Input your nominal lens focal length for lens_mm. After doing a decent pre-align, press the [search] button and wait a while for the results. Do this a second time and compare. If lens mm remains consistent, you can use it for your panoramas. The search function steps through a range of values for lens_mm and the image alignment offsets for x, y, and theta. It searches for the lens value that give the best alignment results for the given images. The process needs a minute or more, but you only need to do this once to characterize a given camera lens and focal length (zoom setting). Use the [save] button in the 1st dialog to put the focal length back into the image EXIF data. \_Setting lens mm Manually Make a panorama image of a brick wall (or any wall with lots of detail). The wall should be 5+ meters away. Take two photos with about 40% overlap. Within the panorama pre-align process, adjust lens_mm until overlapping bricks coincide. When making the two images, be sure to turn the camera on a vertical axis through the lens, minimizing lateral movement and rotation in other axes - otherwise your lens mm may not be optimal. The result should roughly correspond to the nominal focal length of your lens (35mm equivalent). It may be off somewhat (my 27mm lens works best with a lens_mm setting of 29-30mm). I speculate that this is because wide-angle camera lenses are not ideal lenses (pinhole equivalent). Most panoramas will still work well even if the lens_mm setting is off by 10%. \_Color Matching Problems If the images in a panorama have a large brightness difference, the automatic color matching may not work well enough. The most common problem is false sky colors. You may be able to improve this by brightening or darkening an input image to more closely match its neighbors. Sky can be easy to fix by selecting the false areas and copying sky from elsewhere in the image (|COPY PIXELS 1|). 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 making the photos, 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. VERT. 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. PT PANORAMA The Panorama Tools utilities (included in the Hugin package) have been integrated into Fotoxx under the menu PT Panorama. The user interface is very simple: select the input images in any order (|SELECT IMAGE FILES|) 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 -PT.tif. This is an 8-bit TIFF file and is very large. To save space, save the file as JPEG and delete the original TIFF. 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 +image: mashup.jpg Arrange multiple images and text in a layout. Images can be added, resized, rotated and moved around by dragging with the mouse. Images can be made entirely or partly transparent in selected areas. Text can be added, moved, resized and rotated. Text attributes can be specified: font, size, color, outline, shadow, transparency. Lines and arrows can be added. The example here shows some of the possibilities. +image: mashup1.jpg 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. +image: mashup2.jpg 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. +image: mashup3.jpg 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 (|SELECT IMAGE FILES|). Selected images are placed on the layout. After the images are added, click on any image to select for modifications. Drag the image from the middle to position the image in the layout. Drag the lower right corner to resize the image. Use the dialog controls to adjust the image. The dialog controls operate on the last image added, clicked or dragged. The 'Prev' and 'Next' buttons 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 overlapping image will be on top. Base Transparency is used to make an entire image partly transparent. The [paint] button is used to make any part of an image partly or fully transparent, using the Paint dialog below. The [warp] button is used to bend or warp an image, using the Warp dialog below. The black margins checkbox can be used to remove black margins left by other edit functions, e.g. warp. These will be made transparent. The Margins controls can be used to make image edge areas transparent. The Hard margins cut off image edges. The Blend margins make the edges partly transparent to blend them into the background or other overlapped images. +image: mashup4.jpg "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 regulate the change rate. +image: mashup5.jpg 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]. +image: mashup6.jpg 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. +image: mashup7.jpg 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 set the position of that end, while leaving the other end fixed. You can use the mouse to move and resize objects in the layout: • click the object to select it - it flashes to confirm the selection. • drag from the approximate center to position the object. • drag the image or text lower right corner to resize. • for a line/arrow, drag either end to move that end only. The keyboard arrow keys can also be used to move the currently selected object in 1-pixel steps. You can also add images saved by |SELECT AREA|. In the Edit Images dialog, press [Add] to start the file selection dialog. Use the [Top] button in the gallery window, select 'saved_areas'. Open any of the .png images found there. The original area outline is used, and edge blending works from these edges. \_Mashup project files You can save a project and open it later to continue editing. However, the project file will open successfully only if the layout image and all the overlay images are still available in their original locations. The Mashup project is rebuilt using these images and some saved metadata in the project file: image and text locations, scales, transparency, etc. \_Mashup Rescale Working with a very large layout (over 20 megapixels) can be quite slow, especially if the PC is not very strong. Dragging a large overlay image can be slow and jerky. You can work much faster if you use a smaller layout to build the project, and then make it larger after you are finished. There is no loss of resolution in the final image, since the overlay images and text are regenerated from the inputs (e.g. an image that was scaled to 0.2x in the initial layout is rescaled to 0.6x if the layout is magnified by 3x). The original full size image file is used for this rescale. The Rescale button allows you to magnify the project by 2x, 3x or 4x (4x, 9x or 16x by area). There is also a reset option to restore the original smaller project size. With this method, you can work with a 10 megapixel layout and resize it up to 160 megapixels when done. The larger layout can still be worked on directly afterwards, but it will be much slower. \_PT Panorama Fix Mashup can be used to fix minor alignment offsets from PT Panorama. After running PT Panorama, start Mashup (in the same Fotoxx session), and create a flat layout image with a good size for editing (e.g. 3000 pixels wide). Start the dialog for adding images to the layout. Navigate to the folder /tmp/fotoxx-xxx/ where you will find the images left by PT Panorama. They have been color matched and warped to fit together. Add these images to the layout. Check the box to make the black margins transparent. Resize them to fill the layout (all must have the same scale) and move them around to fit together. Zoom the window larger for precise alignment and align the images as well as possible. Small misalignments will persist if the images do not fit perfectly. There may also be brightness or color mismatches at the image edges, but these can be fixed later. Start the Warp Images dialog. Drag the mouse in small steps over a misaligned area to move an image into alignment with its neighbor. Use a small span (e.g. 0.1) to insure that correcting one misalignment does not create others elsewhere. Several cycles of dragging the images in several positions 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. MONTAGE +image: montage.jpg Select many images and automatically join them into a compact format. This is a fast and easy alternative to Mashup, which is harder to use but offers more flexibility to vary the size and position of each image. The first dialog is shown at upper left. Frame Width is the width of the frame image that will be created. The height is determined by the number of selected image files and the space required. Frame Margin is the margin size (pixels) around the frame image. Image Columns specifies the number of columns in the frame image (6 in the above example). The number of rows is determined by the number of image files selected. Image Margin is the space between images (pixels). Select the image files to combine (|SELECT IMAGE FILES|). Press Proceed when done. The resulting image will be made in a few seconds (or up to a minute for hundreds of images). This image will typically have a very uneven bottom margin, as shown by the top right image above. The Optimize dialog begins automatically. It can rearrange the images so that the bottom margin is more even. Press Start and wait for the image to optimize. You can watch the progress. The function will exit if an optimum is found that makes the bottom margin even within 2 pixels. It may continue to search indefinitely if this goal is not reached. Press Stop to force the optimize function to exit with the best solution found so far. If optimization is successful, you will get even columns as shown in the lower right image. If the bottom edge is still uneven after optimization, you will be asked if you want to add margins to some of the images to make the bottom edge even. This will result in an even bottom edge, but the inside margins will no longer be equal. TIP: Optimization works best if the number of images is divisible by the column count, i,e, the same number of images in all columns. It also works better if the number of images per column is at least four (better more). This gives the optimizer enough flexibility to find a solution with an even bottom edge. The last dialog appears, shown at lower left. You must now assign a unique name to your montage, one that does not conflict with other montages you have made before. A file save dialog is started, and you can save the new montage file anywhere you wish. If the map option is selected, a map of the included images is also created. This map allows you to click on any image in the montage to get a larger popup image which can be zoomed up to the full size of the original image, using the mouse wheel or F11. This will work now and at any time later, even if the montage file is moved, presuming the map file and the original images are still available. The saved montage file is given a special suffix to make it recognizable as a Fotoxx montage file. The full montage file name is: your-assigned-name (fotoxx montage).jpg. If the map option is not selected, "(fotoxx montage)" is not included in the name. The map files are stored in [fotoxx home]/montage_maps/. Note that the popup image feature is not standard. It will not work outside of Fotoxx. PROCESS MENU +image: process.png BATCH CONVERT +image: batch-convert.jpg This function is used to rename, convert, resize, upright and move multiple image files at once. An overlay image can also be added at a selected position (e.g. a title, credit line or copyright notice). Dialog Inputs • Select Files - Select files to convert from gallery windows (|SELECT IMAGE FILES|). • New Name - Optional new name with optional inserted text (see below). • Sequence Numbers - Starting sequence number and adder for each output file. • New Location - Optional new folder location for the converted files. • new file type - File type for converted images, or "no change". • color depth - specify 8 or 16 bits for tif or png output. • max. width, height - Output images will fit within these limits. • delete originals - Delete input files after successful conversion. • copy metadata - Copy all EXIF and IPTC metadata to the output files. • upright - If an image is rotated 90 degrees, upright it (based on EXIF data). • sharpen - Sharpen output images using the two supplied parameters. • Overlay Image - Add an overlay image at a selected position (see below). • Make constant size - Overlay size is independent of image dimensions (see below). \_Plugins The new file name may have inserted text: a photo date (from EXIF metadata), a sequence number, or the original file name. Specify what and where to insert as follows: • $oldname - The original file name is inserted here • $sss - A running sequence number is inserted here. See below. • $yyyy $mm $dd - The photo date (year, month, day) is inserted at these positions. Example: San Francisco $yyyy-$mm-$dd $oldname $sss The input file "Golden Gate Bridge.jpg", with a photo date of May 12, 2014, would convert to the output file name "San Francisco 2014-05-12 Golden Gate Bridge 123.jpg". For "$sss", specify the length of the field with the number of 's' characters, e.g. "$ss" will be adequate for sequence numbers up to 99. Leading zeros are added for sequence numbers with fewer digits. Sequence numbers not fitting in the specified length will be as long as needed. Keep in mind that the file name sort order in the galleries is strictly ascii order, so the sequence of numbers 1, 2 ... 9, 10 will sort as 1, 10, 2 ... 9. You can keep the numeric order by specifying a field length of 2 ($ss), resulting in the sequence 01, 02 ... 09, 10. Either "$oldname" or "$s..." must be present to insure output file names are unique. If New Location is missing or unchanged, delete originals is ignored. If an output file already exists, the input file is not converted. For an explanation of the sharpen parameters, see the Sharpen function. \_Overlay Image A small overlay image can be added at a selected position in all the output images. Use the Open button to select the image file. The overlay image will be scaled to 'Width' percent of the output image width. The position is selected by clicking one of the 'Position' buttons. If 'Make constant size' is checked, the visual size of the overlay image will be made constant for the selected screen or window dimensions. Without this, a "tall" image that displays with left and right margins would have a visually smaller overlay. An overlay image can be used to add a title, credit line or copyright to the converted output images. Prepare the overlay image as follows: Use New Blank Image to create a base image to hold the text. The scale does not matter, so make it larger than needed, say 1000 pixels wide. Use Paint Transparency to paint the base image transparent. Use Draw Text to put the desired text on the base image, using the font, color, and other attributes as desired. Scale the text to fill most of the base image. The text is visible and the base image (background) is transparent. Use Trim/Rotate to remove excess margins if needed. Save the final image. A convenient location is [fotoxx home]/saved_areas, but any folder can be used. This image is now ready to use as an overlay image for Batch Convert. \_Albums If image files are renamed or moved using Batch Convert, and if deletion of the original image files was also specified, then all albums containing any of the input files are updated to reflect the new file names and folder locations. \_Preserving File Sequence If you specify a leading sequence number ($s...), the input file selection sequence will be preserved at the output location. Thus you can select image files in a desired order, or use an album with ordered images, and preserve this sequence in the output. BATCH UPRIGHT +image: batch-upright.jpg This function 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. 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. The [Select Files] button leads to a gallery file selection dialog (|SELECT IMAGE FILES|). If 'Survey all files' is checked, all image files in your database will be searched. BATCH DELETE/TRASH +image: batch-delete-trash.jpg Select the files (|SELECT IMAGE FILES|). Select the option to delete or move to trash. [Proceed]. Files are listed in a popup report as they are deleted or moved to trash. After all files are deleted/trashed, albums are scanned and deleted files removed. BATCH RAW +image: batch-raw.jpg This function converts selected RAW image files to JPG, PNG, or TIF format, using either Dcraw or RawTherapee (|PREFERENCES|). The PNG and TIFF formats have either 8 or 16 bits per color. RAW files generally have 8-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 edit 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 gallery windows (|SELECT IMAGE FILES|). 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. Use the Fix dead pixels [Load] button to select a file that maps dead pixels. These pixels will be fixed in all processed RAW files. See the function |MAP DEAD PIXELS| for more details. Use the Fix pixel bias [Load] button to select a file that maps pixel-level RGB bias. All pixels will be processed to reduce this bias. See the function |MAP PIXEL BIAS| for more details. 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 30 files per minute using Dcraw and TIFF-16 output). PNG-16 produces much smaller files than TIFF-16 because the files are compressed (with no quality loss). This also needs more time to do the compression work. There are two programs available to convert RAW files, Dcraw and RawTherapee. Dcraw is faster and RawTherapee may sometimes produce better results. This may not matter if you intend to edit the images manually. See |PREFERENCES| and |EDIT WORKFLOW|. BURN DVD/BRD +image: burn_DVD.jpg Select any number of image files and copy them to a DVD or BlueRay optical disc. • Insert a blank disc and wait for the initial clatter to finish. A message may appear asking you what to do with the blank disc, or a program may start which the window manager thinks you want to run. Dismiss this window or program. • Start the Fotoxx Burn function. • The [Select Files] button starts a dialog for selecting image files to be copied (|SELECT IMAGE FILES|). Keep within disc capacity (DVD 4.7 GB, BlueRay 25 GB). • Select a DVD/BlueRay disc drive to use from the drop-down list (even if only one). • Press the Start button. The list of selected image files is sent to growisofs. (an optical disc recording utility). • Progress is shown in a popup window, along with any growisofs error messages. The job will fail right away if the selected files do not fit on the disc. The resulting disc is frozen. Leftover space cannot be used later to add more images. EXPORT FILE LIST +image: export-file-list.jpg This function is used to create a file containing a list of image files. These are fully qualified file names, one per line in the text output file. This has no use inside Fotoxx. If you need to select images to feed into a shell script or another program, this is the way. The [Select Files] button starts a dialog for selecting files (|SELECT IMAGE FILES|). Use the Browse button to select or define the output file. EXPORT FILES +image: export-files.jpg This function copies selected image files to another folder. This could be used for copying files to a thumb drive, uploading files to a photo web service, etc. Select image files by clicking gallery thumbnails (|SELECT IMAGE FILES|). Select a folder where the image files will be copied. If 'export metadata' is selected, a limited set of metadata is also copied (relevant for a photo web services like Flickr). These are: photo date, keywords (tags), copyright, caption, comments, and geotag data (location). Privacy notice: others would be able to find your location by date if this metadata is included in uploaded files. BATCH TAGS +image: batch-tags.jpg Add, remove, or change the tags for a large number of image files at once. Use the [select files] button to select the image files (|SELECT IMAGE FILES|). The dialog shows two lists of tags: those to add to the selected files, and those to remove. Available tags are shown in the Defined Tags window below. One of these can be added to the currently selected tag list (add or remove) by clicking it. A tag can be deleted from either tag list by clicking it. To replace tags, specify the new tags in the add tags list and the old tags in the remove tags list. If the list of defined tags is long, it may be easier to type the desired tag into Enter New Tag. Existing tags matching what you have typed so far will appear in Matching Tags, and you can point and click one of these to add the tag to the image. If the input tag is new (no matching tag is shown), press [Add] when the tag is complete. It will be added to the selected tag list and to the list of defined tags under the category "nocatg". If you are using tag categories, you can select a category, and only those tags will be shown in the list of defined tags. If your tags list is huge, this can reduce the list to a manageable size for pointing and clicking. The [manage tags] button can be used to define tags with categories, as described in |EDIT META|. To get a list of images having specific tags which you want to remove or replace, use |SEARCH IMAGES| to find the images, then start this function and use [select files] to add all the image files that you just found, or select any subset. BATCH RENAME TAGS +image: batch-rename-tags.jpg With this function, you can rename any number of tags and apply these changes to your entire image collection. Click on a tag to rename from the list of defined tags, enter a new name in the Rename to box, then click on the arrow button to add this pair of names to the list on the right side. Repeat for each tag to rename. If you make a mistake or change your mind, click on the pair of names in the list to remove them. Press [Proceed] when done. A popup window shows the number of image files containing the affected tag names. Press [Yes] to proceed. The update rate is around 500/minute on a strong computer. The next time Fotoxx is started, the index file function will update all thumbnails for the same images, because the file modification date has changed. BATCH PHOTO DATE +image: batch-photo-date.jpg This function lets you quickly fix a batch of photos that have no photo date/time or an incorrect one. Use the [select files] button to open a gallery window where you can select the image files to process (|SELECT IMAGE FILES|). There are two ways to change photo date/times: • Set a new date/time (right image above) enter a new year only (yyyy) - only the year will be replaced in the image files. enter a date only (yyyy-mm-dd) - the date will be replaced but not the time. enter a full date/time (yyyy-mm-dd hh:mm:ss) - the date/times are fully replaced. • Shift existing date/time (left image) Enter + or - values into any of the 6 fields: years, months, days, hours, minutes, seconds. Image file date/times will be shifted by the entered values. Shifted date/time values propagate to the next higher value when necessary: Time 08:30 shifted by -50 minutes results in 07:40 Date 2016-10-20 shifted by +6 months and -30 days results in 2017-03-21 (March has 31 days). If the checkbox 'test' is selected, a report of the old and new date/times for the selected files is produced for your inspection. No changes to the files are made. If you are satisfied that the results are correct, remove the checkbox and run again to update the files. The same set of files is already selected. BATCH CHANGE META +image: batch-add-change-metadata.jpg 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 from which you select files to process (|SELECT IMAGE FILES|). Enter metadata key names and values to be assigned, or leave the value blank to delete the key. Press [apply]. You will get a confirmation message and you can proceed or cancel at this time. A list of commonly used key names is provided on the left. Click one of these to insert it into the first empty key name field in the dialog. This list comes from the file [fotoxx-home]/metadata_short_list, which you can edit to add other keys to the list. The [Full List] button provides information on how to get a list of all available key names. BATCH REPORT META +image: batch-report-metadata.jpg +image: add-metadata-items.jpg This function can report any metadata for any number of images. The output is a text file which is displayed in the default text editor for your system (e.g. Gedit for a Gnome desktop). You can use the search features of the text editor to find images matching desired metadata, or you can use the text file as input to other programs. Use the [select files] button to open a gallery window to select the files to be processed (|SELECT IMAGE FILES|). You can navigate through any number of galleries and select any number of image files in each. The [edit] button opens a new dialog (right) where you can select the metadata items to be reported. Click an item in the left column to add it to the right column and to the report. Click an item in the right column to remove it. Click the last item in the left column, "Other Item ...", to type-in any item name not appearing in the list, which contains only the most likely needed items. BATCH GEOTAGS +image: batch-add-geotags.jpg 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 (|SELECT IMAGE FILES|). Then get the location data as described in |EDIT META|, using the buttons [Find] and [Web], or one of the map views. Press [proceed] to start the update process. Use this function also to fix inconsistencies in location spelling or latitude/longitude data. If latitude and longitude are left missing, only the location names are updated in the image files. This gives you a way to add missing names, or replace screwy names inserted by a camera GPS receiver. SCRIPT FILES Menu Functions: • Edit Script - Perform a set of edits on one image file, recording all functions and settings in a named script file. • Run Script - Run a script file using the current image file, like an edit function. • Batch Script - Select any number of image files and run a script file on all of them, in batch mode. Script files allow you to define a set of edits once, and repeat them on single or multiple image files at a later time. 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. 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. \_Scriptable Edit Functions Some edit functions use mouse drags on the image, e.g. all warping functions. These are not scriptable because mouse drags are not recorded. 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: Edit Retouch brightness/contrast/color adjustments Resize change the pixel dimensions of an image Adjust RGB adjust colors using RGB or CMY sliders Enhance Voodoo 1 automatic enhancement tool Voodoo 2 automatic enhancement tool Brite Dist reshape the brightness distribution Gradients enhance local contrast where weak Flatten enhance dark areas with low contrast Global Retx enhance color and contrast globally Zonal Retx enhance color and contrast locally Sharpen sharpen fuzzy edges Denoise reduce image noise Effects Sketch convert image into a simulated sketch Cartoon convert image into a cartoon Line Drawing convert image into a color line drawing Emboss add a 3D relief effect to an image Dither convert image into dots - four styles Tiles convert image into tiles Painting convert image into a simulated painting Texture add texture to an image (e.g. image on tapestry) Pattern add pattern to an image (e.g. image on brick wall) Color Mode convert to black/white, color negative, sepia Color Depth reduce color depth (bits/color) for poster effect Shift Colors tune colors using a slider Alien Colors add strange coloring to an image Brite Ramp ramp brightness or color up/down across an image Mirror mirror image horizontally or vertically Custom Kernel apply a custom convolution kernel to the image Tools Color Profile change the color profile, e.g. Adobe RGB <-> sRGB EDIT SCRIPT In the dialog, press [start]. Choose a file name for the script. Begin editing the current image file. Use all desired edit functions in any sequence. Each edit is recorded in the script file along with all the dialog settings used. When finished, press [close] to close the script file and save it with the given file name. RUN SCRIPT Choose one of your saved script files from the popup menu. The script is executed using the current image file, like a normal edit function. BATCH SCRIPT In the dialog, press [Select Files]. Select the files to edit using the standard file selection dialog (|SELECT IMAGE FILES|). When done, press [Select Script]. Choose a script file from the popup menu. The script is now executed on all of the chosen files. All modified images are saved as new versions, so the original files are not lost. Those you do not want can be quickly removed using |BATCH DELETE/TRASH|. If RAW files are processed, the corresponding outputs are TIFF-16 files. TOOLS MENU +image: tools.png INDEX FILES +image: index-files1.jpg Fotoxx needs to know where all your image files are located (folder and file names) and their embedded metadata (dates, tags (keywords), geotags, captions, comments, ratings). This data is indexed for fast searching. Fotoxx also creates thumbnail images so that the gallery windows (thumbnail pages) will work fast. Fotoxx does not modify or copy your image files - it only reads them to make the index and thumbnails. These are typically 2% as large as the JPEG files from a modern camera. The Index 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 files. The speed can range from 500 to 10000 files per minute, depending on computer speed, disk speed, and average file size. If there are few or no new files, indexing completes quickly. Indexing can also be started manually from the Tools menu. NOTE about indexing speed: The rates stated above are for JPEG files around 3 MB. Larger JPEGs and TIFFs are slower. RAW files and large PNG files may be very slow, 200 per minute or less. You can find some benchmark data for indexing speed in the |TECHNICAL NOTES| topic. Image files modified or moved within Fotoxx are taken care of automatically. The Index function is used only for new image files created from outside Fotoxx (e.g. a new batch of photos was added), and for files modified, moved or renamed from outside Fotoxx. Enter your top image folders (e.g. /home//Pictures). Use the [browse] button to locate and add folders. These folders and any subfolders containing images will be processed. It does not matter if other files are mixed with the images. The simplest way is to use /home/ as the only top folder, but it is better to separate the image files from the 100,000 other files that may be under /home/. Delete an entry by clicking the corresponding X. Enter the thumbnails folder where thumbnail files will be stored. Use the supplied default /home//.fotoxx/thumbnails or set your own location. The folder name must end with /thumbnails, and this will be added to your selection if needed. The folder is created if not already present. Indexing will run faster if this is on a separate physical disk from the image files. If you have used folder or file names to classify your images, you can make immediate use of these in the |SEARCH IMAGES| function. If you have saved dates, captions, tags, geotags, titles, or ratings in your image metadata (using other applications), these will also be searchable. After the images have been indexed, searching them by any of these criteria is almost instantaneous. Other items in the image metadata can also be searched, but at a slower speed. See |SEARCH IMAGES| for details. \_Index Metadata Items +image: index-files2.jpg You may add up to 20 other metadata items to the image file index. These are in addition to the standard items mentioned above. Metadata in this list can be searched as fast as the standard items. You may not need this, but if you sometimes need to search non-standard metadata items, you can add them here and the search speed will be almost instantaneous. Press the [select] button for "extra metadata" to get the above dialog. Click on items in the left list to add them to the right list - the items to be added to the index. Click on an item in the right list to remove it. Press [done] to commit the changes. If you make any changes to this list, your entire image collection will need to be indexed again. This will proceed much faster than the initial indexing, because the thumbnails are already there and do not need to be created. Only the image file index will be refreshed. \_Fotoxx Startup Time If you have a huge image collection, the first startup after a reboot may need some time, even if there are no new image files. Subsequent startups are faster because file folders are now cached in memory. If startup time is still a problem, you can bypass the indexing. This may be especially useful if you use a file manager (like Nautilus) to start Fotoxx with a selected image file, and you want the image to open instantly. See the User Settings topic immediately below for more information. \_Accessing non-indexed Files and Folders A non-indexed image file or folder is one that is not included within your declared top image folders (subfolders of your declared image folders are always included). You can navigate to such a folder and view the thumbnails normally, and you can view and edit such files normally. There are limitations that you should understand. • These files will be missing from any search results and map views. • Thumbnails are created at the time of viewing - gallery paging may be slow. If you use Fotoxx to access a portable USB storage device (e.g. a camera SD card), you will have this situation and you will notice the balky performance. Thumbnails will be created as needed, and they will remain after the device is removed. I recommend that you first copy the files to a declared Fotoxx folder or subfolder, and do whatever Fotoxx processing you need from this location. The performance will be much better. \_Removable Disks Run the Index function with the removable drive mounted, add the desired top image folders from this drive, and let the indexing process run to completion. This is no different from the procedure with permanent, fixed storage. After this, Fotoxx can be used with the removable drive mounted or not mounted, and nothing will be lost. If you operate Fotoxx without the drive mounted, the image files on this drive will disappear from Fotoxx view. When the removable drive is mounted again, and Fotoxx is started (or the index function is run manually), the last status is restored. If any image files were modified in the interim, this is detected and the index data and thumbnails are updated in the usual manner. QUICK INDEX Perform a quick incremental index in the current Fotoxx session with no user inputs. If image files are updated while Fotoxx is running, the Fotoxx index is no longer up to date. The new or modified files may not appear in Gallery View, and are excluded from reports and maps. If they appear in Gallery View, they are marked "not indexed". Use Tools > Quick Index to update the Fotoxx index. The time needed will depend on the number of new or modified files present within or underneath your top folders. For 100 files or less, it should be a matter of a few seconds. MOVE FOTOXX HOME The default location for 'fotoxx home' is /home//.fotoxx (like most Linux apps). This folder is where Fotoxx data is kept, including index data and user preferences and settings. If you have multiple Linux systems on your computer, and you want them to share the same Fotoxx data, or if you want your Fotoxx data to be retained when you make a "clean install" of Linux, you can use this function. See |COMMAND PARAMETERS| -home parameter if you want multiple image collections that are managed separately). Use the dialog to select or create a new folder for Fotoxx home. I suggest you use the name /fotoxx or /.fotoxx as the lowest folder level. This folder would typically be on a mounted volume to keep it isolated from Linux installs and shareable with multiple Linux systems. Putting /home on a separate volume is a common method to do this, but there are risks if multiple Linux systems share this same /home folder: applications with different release versions may have data file and format differences, which can lead to application failures and even crashes. All user data will be moved automatically. No new indexing of the image files is required. A file is created in the user home folder: /home//.fotoxx-home. This is a one-line text file which contains the file name of the new fotoxx home folder. When fotoxx starts, fotoxx will look here first for its home folder. If there are multiple Linux systems, this file needs to be duplicated in each of the /home/ folders. PREFERENCES +image: preferences.jpg Various user preferences and settings are collected in this dialog. They are also saved in the file [fotoxx-home]/parameters. • Startup Display - initial display when Fotoxx is started: • Recent Files Gallery: the most recently seen image files • Newest Files Gallery: image files most recently added or modified • Specific Gallery: the specified folder gallery • Album Gallery: the specified album gallery • Previous Gallery: the gallery last used in the prior Fotoxx session • Previous Image File: the file last viewed in the prior Fotoxx session • Specific Image File: the specified image file • Blank Window: start with a blank window • Browse: dialog to browse for the starting folder, album, or image file • Background Colors - background colors for File and Gallery View windows • Menu Style - menu display style: icons, text, or both • Menu Colors - menu font color and background color • Dialog Font - font to use in menus and dialogs (Bold, Italic, etc. are ignored) • Image Zoom Speed - Choose 1-8 zooms (clicks or [+] keys) per 2x size increase • Image Pan Mode • Drag: image moves with the dragged mouse • Scroll: image moves against the dragged mouse (like invisible scroll bars) • Fast: movement is magnified - entire image can be scrolled with one drag • JPEG file save quality - quality/size tradeoff for image saved as a jpeg file • TIFF file compression method - select a libtiff compression method to use • PNG file compression level - 1-9 = no compression - max. compression • Curve Node Separation - minimum node separation for edit curves, % of scale The default is 5% of scale, allowing up to 20 nodes in a curve. This is also the mouse capture threshold when a node is clicked or dragged. If you use a touchpad instead of a mouse, set this value higher. If you use a high DPI monitor, set this value higher. • Map Marker Size - pixel size of the red dots marking map image locations • Show Images - show all images or only the last version of each image • Image Position in Window - move to the right to maximize left margin for dialogs • Image Index Level - set the index level depending on how Fotoxx was started. The values in parentheses are suggestions. See below for a full explanation. • RAW File Loader - loader to use for RAW files. See |EDIT WORKFLOW|. • RAW Conversion Options: • extend dynamic range: auto enhance dark images - good starting point • use embedded image: match embedded image color space - good starting point • RAW File Types - customize this list to match the RAW files you actually have • Video File Types - customize to match the video files you actually have • Video File Play Command - choose or enter a command for playing video files • Anonymous Statistics - message to the Fotoxx web host for counting users. Infrequent. Contains no data related to a person. \_File Quality and Compression JPEG file quality level: should be 70 or higher for good image quality TIFF/PNG file compression: reduce file size at cost of longer time to save file \_Image Index Level These two parameters govern a tradeoff between fast Fotoxx startup time and the completeness of the image index. A complete image index is required for accurate results from image search and map functions. Building a complete image index when Fotoxx starts can take from 0.1 seconds to 10+ hours (e.g. initial indexing of a million image files on a slow computer). Fotoxx startup time depends on the following factors: • If there are (new) image files that have never been indexed, they must be found and added to the image index. New files are processed at 500-10000 per minute, depending on computer and disk speed and average image file size. • The first startup after a reboot needs more time than subsequent startups. Subsequent startups are faster because the index file and the image folders are now cached in memory. First startup time with a 5400 rpm disk is roughly 1 second per 10K image files. Subsequent times are roughly 5x faster. First startup with an SSD disk is roughly 1 second per 40K image files (assuming a strong PC). These numbers assume no new (not yet indexed) image files. If you start Fotoxx by selecting a file from your file manager (e.g. click on a file name in Nautilus), you may want to see the image immediately and not wait for indexing. You can control this with the two parameters named 'Image Index Level'. • The first parameter controls how Fotoxx indexes when started from a menu, launcher or command. The 2nd parameter controls how Fotoxx indexes when started by clicking on a file name displayed in Nautilus or other file manager. • A value of '0' means no indexing at all. Startup is very fast. Image search and map functions are disabled. Viewing and editing images and metadata is normal. • A value of '1' means load the old index file but do not search for new image files to update the index. Startup time is moderately fast. Image search and map functions do work, but any new (not yet indexed) image files are excluded from the outputs. • A value of '2' means full indexing: load the old index file, search for new files (not yet indexed) and update the index file. Startup time depends on how many new image files must be indexed and the speed of your computer. Image search and map functions produce complete results. You can also start the index process manually (Tools > Index Files) if you want to use search and map functions with complete results. You do not have to go back to |PREFERENCES| and change the parameters. KB SHORTCUTS +image: KB-shortcuts.jpg +image: KB-shortcuts2.jpg This function is used to view or change custom keyboard shortcuts. The currently assigned shortcuts are shown in the first window. If you press the [Edit] button, the second dialog is shown, where you can add and change keyboard shortcuts. Enter a new shortcut using the keyboard. You can use the keys A-Z, 0-9, F2-F9, and most of the symbols (% $ & ^ < etc.). You can combine a key with one of Ctrl/Alt/Shift: Press and hold ONE of Ctrl/Alt/Shift, then press the key, then release both. Select one of the available menu assignments from the list on the right. Remove a shortcut by selecting it and pressing [Delete]. Press [Done] when you are finished. [Cancel] will discard all changes. At the end of the list are shortcuts for the most common dialog completion buttons (Done, Cancel, etc.). Always use Ctrl+ or Alt+ for these keys, since single keys are reserved for text inputs into dialog widgets. RGB DISTRIBUTION +image: RGB-dist.jpg This function opens a small window that shows a brightness distribution graph of the current image in File view, or the currently selected area of the image. This graph updates immediately for new images or as edit functions change the image. There are four graphs in four colors: red, green, blue graphs are for the respective colors. The black graph is for overall brightness. Use the buttons [All] [Red] [Green] [Blue] to select the colors to show. MAGNIFY IMAGE +image: magnify-image.jpg 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. FIND DUPLICATES +image: find-dups.jpg 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 in size is likely be classified as a duplicate. Thumbnail size can be set from 32 to 512 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 64 needs about 880 MB, whereas size 256 needs about 14 GB. memory required, roughly: size x size x 0.75 x 3 x (image count) Press the calculate button to calculate the largest thumbnail size (for the shown file count) that can fit within the free memory available. 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 = 2 and pixel count = 10, then images are classified as duplicates if fewer than 10 pixels are different by 2 or less. The output is a Gallery View, showing each set of duplicate images. If two images are duplicates, the two images will appear in the output. The output gallery is saved as album "Duplicate Images". You can use Batch Delete/Trash together with this album to choose which duplicates to delete. \_Current Gallery option You can confine the search to image files in the current gallery. This can also be the output of the |SEARCH IMAGES| function. Use this method to select some subset of your image collection, using folder/file names, a photo date range, etc. Performance (measured with a 2.8 GHz computer) Find 114 duplicates from 13K images, SSD disk: 17 seconds Find 762 duplicates from 56K images, SSD disk: 1.1 minutes Find 12K duplicates from 215K images, 5400 rpm disk: 13 minutes SHOW RGB +image: show-RGB.jpg 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. The outputs are updated immediately if the image is being edited. If "labels" is checked, the points are labeled on the image with letters A-I, corresponding to the letters A-I in the dialog window. If "delta" is checked and the image is being edited, then RGB changes are shown instead of the absolute values. If you open another image (e.g. to compare with the prior image), the positions are kept and updated if they are within the new image boundaries. COLOR PROFILE Use this function to change the ICC color profile. If you have images with Adobe RGB color, you can change to sRGB for better colors on a monitor, or for uploading to a web site. Any EXIF embedded color profile is removed, and the EXIF key "ICCProfileName" is set to the new profile. Because the relevant metadata is changed, a new file version is created automatically, and the input file is left unchanged with the old color profile. To view the color changes, use the KB arrow keys to switch back and forth between the old and new images. You may need to install ICC color profiles. In Ubuntu, the package names are icc-profiles and icc-profiles-free. CALIBRATE PRINTER +image: printer-calibrate-chart.jpg This utility may be able to improve the color accuracy of printed images. \_How It Works A chart of colors is printed on the target printer. Some of the printed colors will be slightly wrong due to printer imperfections. This printed page is scanned into a file, and the colors in this file are compared to the original colors that were printed. 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. My own result was a modest improvement, detailed below. Hopeful Assumptions • Your scanner produces accurate colors. Scanners are generally more accurate than printers. • The printer color errors are small enough that negating them before printing will cancel most of the error. This is less likely if the errors are large. • The limited set of colors in the chart (1728) can be used to calculate all other colors (each color adjustment is interpolated from 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 folder [fotoxx-home]/printer_color/. 1. Generate and print the color chart file (printchart.png) on the printer to be calibrated. Use a large paper size, vertical orientation, no margins. 2. Scan the printed chart into a PNG file. Use 300 dpi or more to make a large file. 3. Edit the PNG image to trim off margins left by the scanner. Save the edited image as .png. The name should contain printer settings and paper type. 4. Process the edited chart file to create a color map file .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 from step 4. Once you have made the color map file, you can print any image using step 5 only. There is also menu function to do step 5 only: |PRINT CALIBRATED|. Precautions For Best Results • Use a large paper size for printing the chart (A4 or US Letter) to make the color tiles as big as possible. • Clean the scanner glass before scanning the color chart. Dust spots or smudges will falsify the colors. • Scan with a high DPI setting (300+) to make a large chart file. This will make the next step more accurate. • Scanners can skew a scanned image into a slightly non-rectangular form. If this happens, you will notice it when trying to trim off the margins: the fat green margin lines will not align perfectly with the trim rectangle, even after slightly rotating the image for best fit. If this happens, use |PERSPECTIVE| to square the image first, then trim the margins. • When editing the scanned chart image, be sure the darkest row is at the top. Trim off the margins surrounding the color tiles. Use the fat green margin line surrounding the color tiles as a guide: cut off this line exactly, leaving only the tiles. Work with 2x magnification while adjusting the trim rectangle. Accurate trimming is critical. The tile positions are calculated from the final image dimensions, assuming 35 equal columns and 50 equal rows. If some edge tiles are cut short, or if margins are left, then the calculated tile positions will be offset, and the measured colors will be wrong. A small error of 1-2 pixels is tolerated, because the outer 20% of each tile is not used to read the tile color. You can use the keyboard arrow keys to make 1-pixel movements in the trim rectangle. The last corner pulled with the mouse is the corner that the arrow keys will move. • If you notice any black or white spots in the scanned chart image (from dust), fix them with |COPY PIXELS 1|. • The output color map file can be used only for the paper, ink, and printer settings used for the calibration. Any other combination needs its own calibration and color map file. Use the name of the color map file to include this information, e.g. cmyk-glossypaper.dat. \_Results (my Canon printer) +image: printer-calibrate.jpg +image: printer-cal-study.jpg The chart above is a photo of three images: upper left: a printout of a photo of a standard color chart upper right: a calibrated printout of the same photo bottom: the color chart itself Look for tiles in the upper left image that least match those in the bottom image. Look at these same tiles in the upper right image. The match is mostly better. 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 the manufacturer's web site. There are also professional services to generate an ICC color profile. The procedure is similar to the one described above: you print a color chart supplied by the service, send the printed chart back to them (along with some money), and receive an ICC profile, a file which you can install. Try a web search for "icc profile service". GRID LINES +image: grid-lines.jpg 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. A keyboard shortcut (default 'L') can be assigned to toggle the grid lines on and off. 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 Some functions draw lines over the image (Trim/Rotate, Select Area, others). You can change the color of these lines to maximize contrast against the background image. The small dialog can be left open while editing, to conveniently switch among the available colors (black, white, red, green). 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 check for brightness clipping. MAP DEAD PIXELS +image: dead-pixels.jpg Camera sensors may have defects causing isolated pixels to be 'stuck' at some color, or dark. Dust specks on the camera sensor can also cause pixels to be darkened. This function can find such pixels in a RAW image and save their locations in a file that can be used by |BATCH RAW| to repair the dead pixels in converted output files. Cameras normally repair their own dead pixels. These are mapped during manufacture and saved in the camera's firmware. This Fotoxx function can be used to fix pixels that have failed later, or pixels darkened by dust specks. The following describes how to make a map of defective pixels for a given camera. Make a photograph of dark gray uniform surface that is badly out of focus. Use a normal ISO level, e.g. 100. This image should be uniform in brightness and have low noise. One way to do this: create a gray image using the Blank Image function. Show this image on your monitor. Set your camera ISO. Focus on some far-away object and press the shutter 1/2 way. Place the camera within 1 cm of the gray image and press the shutter fully. A uniform dark gray image should result. Move this image to your computer. The far-focus trick is to stop the camera from trying to focus on the screen, and to minimize vignette that can occur with very short focus. Vignette does not affect how this function works, since each pixel is compared with its immediate surroundings. Select the gray RAW file and start the Map Dead Pixels function. Press [find] to search for dead pixels. If found, these will have small circles drawn around them, and they are listed in a popup report. Reduce threshold and press [find] to detect more dead pixels. At some point many false dead pixels will appear, which are noise outliers. You can zoom-in on a circled area to check if it is really a dead pixel or only noise. A magnifying glass is convenient for this. The example above shows a dead pixel and random noise. The mapping between camera sensors and pixels causes the "+" pattern. Adjust threshold so that only real dead pixels are included. The popup list shows the pixel with the greatest contrast, followed by adjacent pixels that seem to be related. Each pixel is represented by two values - the x and y positions in the image. Upper left is 0 0. The dead pixel list can be saved to a file by using the [save] button. The default file name is the camera make and model, but you can change this. MAP PIXEL BIAS +image: pixel-bias.jpg Camera sensors have a small variance in sensitivity which is reflected in image pixel RGB values. This is typically much less than random photon and thermal noise. It can still be measured by averaging many photos to minimize the influence of random noise. This function measures pixel-level bias and saves the data in a file that can be used in |BATCH RAW| to eliminate this bias. This benefit is small and likely not visible. My camera shows an average bias of about 0.3 on a scale of 0-255. To compare, random noise is generally more than 1.0. Procedure: photograph a gray image 20 or more times. You can use the procedure described above in Map Dead Pixels. The exposures should be as identical as possible: same image brightness, ISO, aperture and shutter time. This is to insure that the only differences in the photos come from random noise. Save all the RAW files in your computer and start this function. Use the [Select Files] button to select the RAW files (|SELECT IMAGE FILES|). Press the [Measure] button and wait. All the files are read and averaged using the RAW loader specified in |PREFERENCES|. Remaining pixel differences are taken as pixel-level bias. Each pixel is compared to a large sample of neighbor pixels. The difference between a sample of 20 and a sample of 40 should be small compared to the magnitude. MONITOR COLOR +image: moncolor-small.png 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 +image: mongamma-small.jpg Gamma determines how RGB brightness values (0-255) are converted into brightness on the monitor. The standard value is 2.2 and this should normally be used for image editing. Adjust the dialog slider until the middle band has the same brightness as the left and right bands at the scale position 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. His web site has more information about this chart. The chart here in the User Guide has been reduced. CHANGE LANGUAGE This function allows you to change the GUI to one of the available languages. The new setting is preserved across Fotoxx sessions, and this setting overrides the user locale setting. If your language is not available, how about some help? 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". The translation file for the current locale can optionally be exported into the Fotoxx home folder. Here it can be easily accessed for adding or revising translations. SHOW RESOURCES The following data is output to the log file (or terminal window): • process time: CPU time used, since the last time shown • current RAM memory in use by Fotoxx APPIMAGE FILES If Fotoxx is an Appimage build, the files included in the Appimage container are listed. Fotoxx files (e.g. /usr/share/fotoxx/* ) are excluded from the list. ZAPPCRASH TEST This is a diagnostic tool to produce a crash report. A crash report is produced in a text file on the desktop, and Fotoxx terminates. In the case of a real crash, this report should be sent to me (mkornelix@gmail.com). If the utility program 'addr2line' is installed, the crash report should include source code line numbers. Without this, the report may be useless, but send it anyway. HELP MENU +image: help.png • User Guide - The user guide (this document) is displayed. • Recent Changes - This is a summary of recent changes in the User Guide. The intent is to enable reading the changes without reading the whole document. • Edit Functions Overview - Short descriptions of all edit functions in one place. • Change Log - Displays the change log file distributed with Fotoxx, containing details about functional changes, additions, and bug fixes. • Log File - Display the current session log file, where some error messages may appear. This is the same data visible if Fotoxx is started from a terminal. • Man Page - The Linux Man Page for Fotoxx (short description of functionality). • Command Params - Show Fotoxx command line parameters • Translations - Displays the Translations topic from the User Guide: how to make or revise a translation. • Home Page - Shows the Fotoxx home page from the author's web site. • License - Shows the text of the Fotoxx usage and copy license (Gnu GPL3). • Privacy - Shows the Fotoxx privacy policy (Fotoxx collects no personal data). • About - Displays the Fotoxx version number, license, credits, and contact e-mail. OTHER TOPICS VIDEO FILES Video files (.mpeg, .mp4, etc.) may be mixed-in with your image files. They will show as image files in both file and Gallery View. The image shown is the first frame of the video. In File View, the keyboard is used to control the video. The 'P' key will start the video, pause the video, resume the video, or replay a video that has ended. The 'Q' key will quit the video and close the window. These keys cannot be customized. WEB UPLOAD Most photo websites have the ability to upload multiple image files from a single folder, using only a web browser. Use one of the Fotoxx functions listed below to select and copy image files to the desktop or any other folder. From there, use the photo website's native browser interface to upload the image files. |EXPORT FILES| - copy selected files from many locations to a single output folder |BATCH CONVERT| - same functionality plus many more options (see below) The first function is simple to use and can only downsize and copy image files. The second function can also downsize and copy, but it has many more features: • rename the files (combine old and new names, sequence numbers, photo dates) • convert to another file type • upright images turned 90 degrees • remove metadata • sharpen • add an overlay image (e.g. copyright text) at designated position \_Privacy Warning Photos contain metadata you may not want to make available to others. Please review the information at \_https://gitlab.com/fotoxx/fotoxx/wikis/privacy\_ ORGANIZING IMAGES The goal is to find all images for given criteria, e.g. photos of a given person at a given place and/or time range, all photos of a given person, photos from a specified location or event, etc. There are several ways to organize an image collection to accomplish this, with advantages and disadvantages you need to understand. The methods listed here (except for albums) are standards compliant and will work with other programs that support the same methods. These are (1) searching based on metadata (dates, ratings, tags (keywords), geotags (locations), captions and comments, and (2) searching based on folder and file names or partial names. \_Relevant links |EDIT META| |BATCH TAGS| |BATCH RENAME TAGS| |BATCH GEOTAGS| |BATCH CONVERT| |SEARCH IMAGES| |PLACES/DATES| |TIMELINE| |INDEX FILES| |MANAGE ALBUMS| Fotoxx can search using the following image metadata: photo date, file date, rating (stars), tags (keywords), geotags (location names and earth coordinates), and text appearing in captions or comments. Searching based on file and folder names can also be combined with metadata searching. Any other metadata can also be searched, although not nearly as fast as the items listed, which are duplicated in a special index file for fast searching. A strong computer can search for the listed metadata items and file folders/names at a speed exceeding 50,000 images per second. All of the search methods described below can be used in combination. The output of a search function can be the input of another search function (to further narrow the search), and search outputs can be added to prior searches. The following is an overview of the different ways images can be made searchable. \_Folder and File Names These can be used as a basic organization that will enable you to find images even if more effective organizations (tags, captions) are not used. The highest physical organization should be by time, because this will naturally group photos together that are related. I suggest using one subfolder per year named 2001, 2002, etc. This will also prevent any one subfolder from getting too big. Optionally, image files may be further organized in time sequence by using MM.DD as the start of the file name. The rest of the name can be a topic or event, and a sequence number. Example: /images/2011/08.20 Spitzbergen 23.jpg This very basic organization allows Fotoxx to find files by searching for years and topics (file names). In the above example, a search for "spitzbergen" or even "spitz" will produce all the images of Spitzbergen. Years can be also be searched and combined with topic searches, e.g. "2012 Paris". The Batch Convert function lets you rename a batch of photos taken at one location or event by specifying a template name like this: "$mm.$dd Spitzbergen $ss" Month and day (from the EXIF photo date) replace $mm and $dd. "Spitzbergen" replaces the camera file name (P00123456), and a sequence number replaces $ss. \_Captions and Comments A simple method of organization is to use captions and comments. These are arbitrary text strings that can be added to a series of images in rapid sequence: Start Edit Meta, open an image, input text, press [apply], press [next], input text ... Captions and comments are two separate inputs but treated logically the same. They are searchable: words appearing in captions and comments can be searched. You can specify persons, location, topic, etc. for each image and then find them again quickly. \_Tags (EXIF keywords) The most powerful tool is tags, but this is also the most demanding of organizational care. You can go through your images sequentially and add tags by clicking on a list of defined tags. New tags can be defined as needed. Images can have many tags, and can be searched using AND/OR combinations of tags, also in combination with other criteria. Tagging is generally fast, needing a few seconds per image. Fotoxx has two methods of adding tags, a "managed" system and a "random" system. In the managed system, you define tag category names and the tags within each category. When adding tags to images, you can point and click from a list of tags organized by category and alphabetically within category. In the random system, you simply create tags as needed while you tag your images, following no particular system and without categories. When you enter the first few characters of a tag, existing tags that match these characters are shown in a pick-list which you can click to complete adding the tag. If there is no match, a new tag is created. Recently used tags are also shown in a list that can be clicked. Photos made at the same time will normally be tagged in sequence, and will also share many of the same tags. The recent tags list helps to speed the tagging process. Use Batch Add/Remove Tags to add the same tags to many images. Batch Rename Tags can be used to rename tags in selected (or all) images. Tags can be searched in any combination, combined with other search criteria such as a date range or location(s). Note that images downloaded from the Internet may have many tags adhering to no system. You will need to clean these out or redo them to stop them from cluttering your list of defined tags. If you see undesired tags in your list of defined tags, it is easy to find the offending image files and purge or change their tags: use the Search Images function to find the images, using the unwanted tags as search criteria, and feed this list to Batch Add/Remove Tags or Batch Rename Tags. Tags from downloaded files will have no category, which keeps them separate from tags you define (if you are using the managed tags method). Still, you should avoid accumulating thousands of random tags, and it is easy enough to get rid of them whenever they appear. \_Geotags Use geotags to assign a city or location and country to your images, and optionally latitude/longitude. This enables all images for a location to be quickly found. If you use a camera with a GPS receiver, geotags are added to the image EXIF data, and location searching is available automatically. Since image dates are also automatic (in EXIF), images can be searched by date range and location without you having to enter any data for each image. You can leave it at this, or add some of the above extras if you accept the extra effort required. My experience so far with in-camera GPS is that the location names are chaotic and you may want to sanitize them (mixed upper/lower case, with/without states or other subdivisions, mixed languages, etc.). You can fix the mess with a little effort: search for the location name you want to change (e.g. MUENCHEN, BAYERN, DEUTSCHLAND), then process the resulting images with the Batch Geotags function to change the location name (e.g. Munich, Germany). Location names can be searched in any combination. When you add geotags to an image manually, it is usually sufficient to enter just the city or location name and then press [Find]. If the location has been entered sometime in the past, it will be recalled and all geotag data will be filled-in automatically (city/location name, country, latitude, longitude). This will also work if only a few characters of the name is entered, e.g. "hono" will recall the data for Honolulu, if available. When a location is entered for the first time, enter the city or location name and the country, and press the [Web] button. A web service will usually find the latitude and longitude automatically. If not, you can use Wikipedia or other web services to find the location coordinates and enter them manually. Images with geotags are also searchable by clicking markers on a world map. The markers are automatically added to the map for all images containing geotag coordinates. The map can be zoomed to any scale from street-level to planet level. \_Albums Another method of organization is to use Albums. Choose a name for each album and assign any desired images to the album by clicking thumbnails in gallery pages. The images are not duplicated: the album is simply an ordered list of file names. Albums can be selected by name and viewed as a gallery of thumbnails. These can be rearranged via thumbnail drag and drop. The images can then be viewed sequentially using keyboard arrow keys, randomly by clicking thumbnails, or as a slide show with animated transitions between images. Albums are also implemented in some other photo management apps, but each one is different and incompatible. The image search function can be used to find images to start a new album, and then images can be added, removed, and rearranged as needed. Images can be added simply by clicking gallery thumbnails as you browse your image collection. \_Summary The following is a summary of some ways to organize a large image collection, with factors to consider when choosing which methods you want to use. In the list below, "search by" specifies which search criteria can be used with each option. Many of the methods below can be combined, and the possible search criteria increases accordingly. Searching by folder and file name and by photo date (EXIF) is available with any organization. Images Organized by Topic • images are in a topic-named folder (a very common practice) • e.g. /.../Susan 8th birthday/P0123456.jpg (file names from camera) • e.g. /.../Paris 2014/Notre Dame 12.jpg (meaningful file names) • fast implementation: no work if this is your starting point • Gallery View is by topic only (poor overview if very many topics) • search by: topic (using file names) • no overview of available search topics Images Organized by Year and Topic • e.g. /.../2014/Italy/Rome-12.jpg • e.g. /.../2014/Susan Wedding/P123456.jpg • fast implementation: move your topic folders into year folders • Gallery View by year and by topic within year • search by: topic (using file names) • no overview of available search topics Images Organized by Year, Month, Day and Topic • e.g. /.../2014/08.22 Rome-12.jpg • use Batch Convert to format new names from existing groups of image files • natural grouping of related images • gallery overview by year, in time order, with topics visible • search by: topic (using file names) • no overview of available search topics Image Folder and File Names Contain Topics • e.g. /.../travel/2014/Italy/Rome-Susan-Coliseum.jpg • many folks have done this, to search for images using a file search utility • you may end up with hundreds of galleries with a few images each • search by: topic (using folder and file names) • no overview of available search topics Captions and Comments • e.g. "Susan 2014, Coliseum in background" • simple and easy to use • moderately fast implementation: 10+ seconds per image • risk inconsistent names, unreliable search (e.g. search "Rome" would miss the example above) • search by: captions and comments (any contained word or words) • no overview of available search topics Managed Tags • e.g. "Rome, Italy, Susan, birthday" • point and click in a tag list to add tags to images • tags have categories for faster visual location of tags to click • typing a few characters is usually enough to retrieve a defined tag • recent tags are also available for easy re-use while tagging images • no inconsistent or redundant tags, no typos to make searches unreliable • requires careful planning of tags - later revisions are cumbersome • batch add/delete/rename is available for tag revisions • search by: tags in any combination • good overview of available search topics (tags organized by category) • tag categories are not standard and will not be visible in other applications Random Tags • no tag organizing system, tags created as needed while tagging images • legacy tags can be kept unchanged • typing a few characters can retrieve a tag that has been used before • recent tags are also available for easy re-use while tagging images • risk inconsistent tags (scenery/landscape, Susan/Susy) and typos (scnery) • search by: tags in any combination • overview of available search topics: one large pile of tags in alphabetic order Geotags • e.g. Rome, Italy, 41.89 N, 12.48 E • cameras with GPS store this data automatically in images • you can manually add locations (5-10 seconds per image) • you can batch add geotags to many images at once • inconsistent location names from cameras (fixable using Search and Batch Tags) • map markers are generated automatically from image geotag data • search by: location (city, park, monument ...) and country • good overview of available search topics: clickable list of locations and dates • click on a map marker to show a gallery of images at location Albums • album names like this: favorites, best scenery, Italy 2014, Susan childhood • albums are a list of contained image files, which are not duplicated • make albums using any criteria, containing any images • images can be in multiple albums or multiple times within an album • images can be arranged in an album by dragging thumbnails to position • add images by selecting / dragging gallery thumbnails to album gallery • albums are the basis for the slide show function • overview of available albums - click on album name to view gallery • albums are only usable within Fotoxx (there is no standard for albums) TRANSLATIONS Translations are contained in text files. There is one file per language, named "translate-xx.po", where 'xx' is a locale code like 'fr' for French. These ".po" files contain a list of English text phrases and their corresponding translations. The English phrases are the ones used in the Fotoxx user interface: the menus, dialog boxes, and popup messages. These files can be edited with any text editor, or with a special program for this purpose named "poedit". |MISSING TRANSLATIONS| can be used to view a list of missing (untranslated) phrases for any language having a .po file. There is also an option to export the .po file to a place where it can be edited: the Fotoxx home folder. Once this is done, the exported .po file will be used instead of the original file. Any edits made to this file are immediately visible in the next Fotoxx session. For a language having no existing .po file, you can use the English .po file as a template. This file translates English to English, i.e. all translations are duplicates of the English text. Rename the file from "translate-en.po" to "translate-xx.po" where 'xx' is your locale code. A translation in a .po file has a 'msgid' line with the English text and a corresponding 'msgstr' line with the translation: msgid "The file name is: \n %s" msgstr "Der Dateiname ist: \n %s" The special codes "%s" and "\n" are for inserted text and formatting. A file name will be inserted at "%s", and "\n" starts a new line. In the example above, the displayed file name will start on a new line. In the translation (msgstr line), these codes must match the English codes (msgid line) in both type and sequence. A missing translation looks like this: msgid "The file name is: %s \n" msgstr "" Revising an existing translation: 1. Edit file "translate-xx.po" to add or update translations for language "xx". 2. Option: compile the .po file to check for errors: $ msgfmt -v --check-format -o /dev/null translate-xx.po 3. 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 4. Send the modified .po file to me (mkornelix@gmail.com) so it can be included in the next Fotoxx release. Step 2 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, and changes made to a .po file are effective the next time Fotoxx is started. Step 2 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 an ugly or 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 (this document) is available with the menu Help > User Guide. The file is a text file which may be edited with any editor. This is a large document, over 100 pages. RECENT CHANGES v.20.0 Jan 2020 This section is provided to help review changes in the user guide. |USER GUIDE| - text file with navigation and search functions. |INSTALLATION| - Support for iPhone .heic files and JPEG-2000 .jp2 files. |FIRST STARTUP| - initial user options about image indexing. |INDEX FILES| - support for removable drives (retain index and thumbnails). |QUICK INDEX| - fast incremental index with no user interaction. |FILE SAVE| - view and change permissions for created files. |PERMISSIONS| - view and change file permissions. |META VIEW| and |LIST VIEW| - new Gallery View options. |SELECT FILES| - pre-select files for album/batch/script function. |MANAGE ALBUMS| - extended functionality, easier to use. |GALLERY TO ALBUM| - save current gallery with assigned album name. |SEARCH IMAGES| - option to select/include original + last version. |AUTOSEARCH| - shell scripts can use the Search function to select files. |CAPTIONS| - choose to display any/all of filename/caption/comment. |RETOUCH| - more tools in one place: added saturation, white balance ... |UPRIGHT| - automatic upright, but also manual [-90] [+90] buttons. |MARKUP| - draw on image: text, line/arrow, box, oval. |DENOISE| - new effective methods of noise reduction were added. |UNDO EDITS| - Undo previous edits gradually with mouse painting. |CHROMATIC 1| |CHROMATIC 2| - fix lateral/axial chromatic aberration. |ESCHER SPIRAL| - transform an image into an endless inward spiral. |STACK/SLIDER| - show two overlapped images with movable boundary. |MAP DEAD PIXELS| - find and compensate stuck sensor RGB channels. |MAP PIXEL BIAS| - find and compensate biased pixels (sensor variance). |PREFERENCES| - new options for RAW file conversion and edit. |PREFERENCES| - TIFF and PNG file compression settings. |PREFERENCES| - choose program for playing videos: totem, vlc ... |TECHNICAL NOTES|- how symbolic links are handled in Fotoxx. TECHNICAL NOTES Additional technical topics can be found on the Fotoxx web site: kornelix.net \_Fotoxx Limitations • image files - Fotoxx has been tested with 500K image files and the performance was good. See Benchmarks below. • image size = The max. supported image width or height is 30,000 pixels The maximum image size is 500 megapixels (8 GB unpacked in memory). Images edited in memory use 4 floats (16 bytes) per pixel (RGB + alpha). • image edits - 99 edit versions for undo/redo and file version naming. • image tags - 10K tags, 200K chars. for all tags, 1000 chars. for one image. These are compile time constants and could be easily increased. \_Running out of memory Fotoxx can require a huge amount of main memory to edit a large image. A 20 megapixel image requires 640 megabytes for the simplest edits, and more for complex edits. Images in memory are not compressed and each RGB pixel is represented by three floating-point numbers and a transparency value (16 bytes total). HDR, Panorama and other combine functions hold all images in memory during alignment and post-process tuning. If you push the memory limits on a small PC, the Linux kernel may kill the Fotoxx process without warning and with no message (this is to keep the operating system itself from crashing). What you see is that the Fotoxx window vanishes. Fotoxx memory footprint • View 30 megapixel image 324 MB • Retouch 30 megapixel image 670 MB • Trim/Rotate 30 megapixel image 980 MB • Resize (down) 30 megapixel image 1.4 GB • Zonal Retinex 30 megapixel image 4.0 GB • Panorama, 50 megapixel output 4.8 GB Bottom line: If you edit large images, you need a minimum of 8 GB of main memory, and 16 GB is recommended. Extending memory with a swap partition or swap file makes the performance unbearably slow. \_Running out of disk space During a series of image edits, each edit step is saved on disk, and the before/after results can be viewed with the Undo/Redo button. These images use 32-bit floating point numbers for color values and are not compressed. A 30 megapixel image makes a 480 MB file. If there are 10 edit steps in the undo/redo stack, the required disk space is 4.8 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 Fotoxx home folder resides. When you open a new image or quit Fotoxx, the disk space is recovered. If Fotoxx crashes, the space is recovered the next time Fotoxx is started or when the computer is rebooted. \_zappcrash\_ - backtrace dump after a fatal error If Fotoxx has a fatal error, it attempts to intercept the error and produce a backtrace dump which is copied to the desktop. Please send this file to mkornelix@gmail.com so I can fix the error. A description of what you did immediately before the crash would also be helpful, since I may not be able to fix the error unless I can reproduce it. zappcrash uses the utility program 'addr2line' to add source code line numbers to the report. Please insure this program is installed. Otherwise the crash report is useless. \_Symbolic Links in Fotoxx Symbolic links are supported in a limited manner. Take the time to understand these limits if you are using symbolic links. This is complicated, and it is easy to make mistakes if you are not aware of the pitfalls. The image index and thumbnail files are only created and maintained for the real path. For a given image file, only one index entry and thumbnail can exist. If a symlink path is used to access and modify an image file, the symlink is converted into a real path before use. If a new file version is created, it will exist in the same real path as the original file. If there is a symlink to a parent folder of the original file, the new file version will also exist in that same path, just like the original file. If the symlink is direct to the file that was modified, and a new version is created, the new file version will be only in the real path of the original file. No new parallel symlink is created like the one that links to the original file. On that path, only the original file is visible. The Image Search function finds real paths only. Symlinks direct to an image file may have \_no extension or a misleading extension (e.g. /.../filename.png links to /.../filename.jpg). Technically, this works because the real path is used to choose the library function to read and write the file. Good advice: never use symlinks direct to image files - use folder links only. \_Hard Links Fotoxx does not know about these and processes them like normal folders or files. The result is that duplicate index entries and thumbnails are created. If an image file having multiple hard links is modified in Fotoxx, only the index entry and thumbnail for the access path that was used is updated. Others become invalid. If only one access path for a file is within the scope of your top image folders, and you use Fotoxx only on this path, this should work OK. \_Preview Mode Some edit functions use a reduced image size for a faster interactive response time. This is shown on the top panel as "(reduced)". When [done] is pressed, the full-size image is then processed. This is why [done] sometimes takes noticeable time. An image sized for the monitor (2-4 megapixels) is much faster to process than a 20+ megapixel image (the original camera file). This method is used whenever the preview edits can be applied to the full-size image without visible impact (e.g. Trim/Rotate, Warp functions, brightness and color related functions). It cannot be used for some other functions (e.g. sharpen, Gradients) because the results cannot be scaled-up for a larger image. \_File Size The file size shown on the top panel while an image is being edited is the original (unedited) file size. The file size for an edited image is not known until the image is saved on disk. In memory the size is (pixels x 16). A 40 megapixel image uses 640 MB in memory and typically less than 4 megabytes on disk (high quality JPEG). When the edited image is saved, the correct file size is updated on the top panel. \_File Types Supported Fotoxx uses Linux library functions to support reading and writing of image files: the GDK pixbuf library, libjpeg, libpng, libtiff. The file types that can be read and written include JPG/JPEG, PNG, and TIF/TIFF. Three RGB colors with 8 bits per color are supported for all types. PNG and TIFF also support 16 bits per color and an alpha channel (transparency information). 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 folder [fotoxx-home]/image_index/ The file "index20" contains image file pathnames and metadata items that are indexed for fast searching (dates, ratings, tags, caption, comments, geotags). The file "top_folders" contains a list of the top image folders. These are searched for new image files whenever Fotoxx starts up. The last entry is the thumbnails top folder. \_Color Depth 8-bit color (256 brightness levels), as supported by JPEG files, is the norm for image files and is generally adequate. A difference of one brightness step (0.4% of the entire range) cannot be seen. A greater color depth than 8 bits can be useful if some part of the brightness range within an image has been greatly expanded by editing. This expansion can lead to visible "banding" or "posterization". If the RAW image is edited instead of the JPEG (and if the RAW image really has more than 8 bits of low-noise color), this problem can be reduced, even if the edited image is converted back to JPEG for final storage. Currently (2019), only expensive cameras with large sensors produce RAW files with more than 8 bits of low-noise color at normal exposure levels (ISO 100). The image below changes gradually from black to white. The color depth is 6 bits (64 brightness levels). Note that the brightness steps are barely visible. +image: brightness-steps.png \_Denoise Function - noise measurement An area of radius 10 pixels around the clicked position is sampled. The RGB brightness levels reported (0-255) are the averages for this area. Within this area, every pixel is measured and compared with the mean of the surrounding 5x5 block of pixels. The RGB noise levels reported are the average differences. If the sampled area has a small gradient in brightness, the measurement is not sensitive to this. If a RAW image is measured, a 16-bit TIFF made from the RAW is used. \_RAW Image Noise You may be surprised at the typically high noise levels in RAW images. Cameras filter out this noise when making a JPEG image. Expensive cameras with large sensors may have more than 8 bits of low noise color, especially if the image is well exposed at low ISO levels. The bit-depth claims are exaggerations, since noise will predominate at the deeper bit levels. When you use Fotoxx or any other tool to filter noise from a RAW image, you are competing with the camera maker's electronics and software used to produce the camera JPEG. It might be a challenge to do better. \_Trim/Rotate Function Interactive rotations (via mouse drag or the [degrees] spin button) update the image 20 times per second or more. Two special methods are used to make the image move faster and smoother: (1) a smaller image is used, which is scaled up to full size when the [done] button is pressed. (2) a fast rotate method is used having lower image quality, followed by a high-quality method applied one second after all rotation has ceased. If you zoom the image to 2x or more and use the spin button to rotate the image, you can see pixelation on sharp feature edges which disappears after one second. \_Alignment Algorithm (HDR, HDF, Stack, Panorama) Relatively few high-contrast or "feature edge" pixels are used to control alignment. The actual pixels used are shown in red during the alignment process, which is also more entertaining. Each image in succession is systematically warped various small amounts and the fit with other images is tested. This is done because two photos made with slightly different horizons or rotations will not fit perfectly with simple translation and rotation. Also camera lenses are not geometrically perfect. \_Alpha Channels Images having alpha channels (transparency information) can be edited, and the alpha channel is preserved if the image is saved as a PNG or TIFF file. JPEG files do not support alpha channels. \_Image Deterioration From Repeated Editing If you save an edited image file and then use this file later to perform additional edits, pixel resolution may be lost. It is better if you do all edits when the image file is first processed, to minimize image deterioration (or go back to the original if you still have this). Any function that changes image size or shape will reduce resolution about 1/2 pixel. These are the resize, rotate, warp, and all of the combine functions. Rotating 90 degrees does not affect resolution. When downsizing an image, using the ratios 1/2, 1/3, 1/4 will give the best resolution. Functions that change brightness and color do not affect resolution. \_JPEG Compression If a JPEG file from a digital camera is saved with Fotoxx, you will likely notice a large reduction in file size, even if a high JPEG quality level is used. This is because Fotoxx can afford to invest more processing power in the compression. A camera CPU is not very fast and must save an image quickly to be ready for the next photo, so the processing time available for compression is limited. The CPU of a typical PC is 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 file. Differences can be found if you look hard enough. The images are 2x size and the insets are 5x. +image: jpeg-quality.jpg \_Metadata Errors Cameras (esp. older ones) do not always produce structurally correct metadata, and the program exiftool (used by Fotoxx to manipulate metadata) may produce error messages. I have been able to fix these cases by saving the image file on top of itself, which replaces the metadata with whatever exiftool was able to read correctly. If desired metadata gets lost, you can restore it using the Edit Any Meta function. \_Newline characters in 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 metadata, since newline characters are not allowed (exiftool converts them to 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. \_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 used. This works, mostly. Sometimes the window manager decides some other position. \_Uninstall Fotoxx Tarball: use the command: $ sudo make uninstall Appimage package: navigate to the location of the executable file: fotoxx-N.N-appimage and use the command: $ ./fotoxx-N.N-appimage -uninstall Debian package: $ sudo apt remove fotoxx \_Installed Files Fotoxx (Debian package) installs files in the following folders. /usr/bin/fotoxx the executable program file /usr/share/fotoxx/* user guide, translation files, icons, default data, etc. /usr/share/doc/fotoxx/* change log, man page, README, license, copyright The appimage package is self-contained and does not install the above files. The only installed file is the appimage executable itself: fotoxx-N.N-appimage. Installed Files - optional fotoxx-maps (Debian package) /usr/share/fotoxx-maps/* geographic maps data files (112 MB) /usr/share/doc/fotoxx-maps/* change log, man page, README, license, copyright The fotoxx-maps appimage package puts these files at /home//.local/share/fotoxx-maps/ Fotoxx can use the files at either location. Fotoxx Folders and Files • Fotoxx Home: folder for user preferences, settings and general Fotoxx memory • Default Fotoxx home location: /home//.fotoxx/ • If Fotoxx home was moved elsewhere by the Move Fotoxx Home function, the file /home//.fotoxx-home contains the location of Fotoxx home. • The files in Fotoxx home are preserved when a new release of Fotoxx is installed. The folder 'image_index' (within Fotoxx home) contains the following files: • index_config: top image folders, thumbnails folder, custom metadata items • index20: a huge text file containing image file locations and indexed metadata Files in Fotoxx home • draw_line/ image lines and arrows saved from Draw Line/Arrow function • draw_text/ image text overlays saved from Draw Text function • albums/ image albums from Manage Albums function • custom_kernel/ saved custom kernel data files • custom scripts/ saved script files for custom edit functions or batch editing • favorites/ saved data for user-configuration of favorites menu • image_index/ top folders, thumbnail folder, image index file • mashup/ saved mashup project files • montage_maps/ saved image montage maps • palettes color palettes used by the Dither function • patterns/ saved background patterns • printer_color/ saved printer color calibration files • pixel_maps dead pixel maps and pixel bias maps • retouch/ saved settings for the Retouch function • saved_areas/ "cutout" files saved from the Select Area > Save dialog • saved_curves/ curve data saved from Retouch curve edit dialogs • search_images/ saved settings for the Search Images function • slideshows/ user preferences from Slide Show function • slideshow_trans/ saved slide show transition parameters • tempfiles-12345/ temporary files for current session - number is session PID • thumbnails/ thumbnail files (default location, user can change this) • user_maps/ custom map files made by the user • bookmarks bookmark names and image file locations • colorwheel.jpg color selection palette for Paint Image function • gallery_memory saved sort order and position for gallery windows • KB-shortcuts20 user-defined or modified keyboard shortcuts • logfile Fotoxx outputs that may be relevant for diagnosing problems • metadata_report last metadata report - tabular text file • metadata_report_items list of metadata items reported by Batch Report Meta • metadata_short_list metadata key names for Batch Add/Change Metadata • metadata_view_extra extra custom items for the metadata short report • netmap_locations saved locations in net map view • pagesetup saves page setup data for print function • parameters setup parameters that are saved across Fotoxx sessions • plugins saves the plugins menu contents • printsettings saves print settings data for print function • README text file - how to install Fotoxx from the source tarball • recent_files list of the last 1000 files opened by Fotoxx • search_results list of the last image files found with Search Images • slideshow-tone.oga a short music tone for slide show • tags_defined list of all categories and tags currently used in all images • widgets.css styling changes to standard GTK widgets • zdialog_geometry saved dialog window positions (relative to main window) • zdialog_inputs saved dialog data for dialogs that recall prior inputs \_Metadata used by Fotoxx The following metadata items (stored inside the image files) are used by Fotoxx. These items (and any other metadata) can be viewed or edited using Fotoxx. Images can be searched using these items (or any other metadata) as selection criteria. Those marked "indexed" can be searched very fast, others more slowly (see benchmarks below). Metadata section and name Usage Source Indexed IPTC Keywords tags (keywords) user yes IPTC Rating "star" rating user yes EXIF ImageSize pixel dimensions, 12345x6789 auto yes EXIF DateTimeOriginal date/time photo was made camera yes EXIF ImageHistory history of edits using Fotoxx Fotoxx no EXIF UserComment comment text user yes IPTC Caption-Abstract caption or abstract text user yes EXIF FocalLengthIn35mmFormat* focal length, 35mm equivalent camera no EXIF City, Country city/location and country camera* yes EXIF GPSLatitude/Longitude earth coordinates camera* yes EXIF RollAngle camera tilt angle camera no (*) camera - camera GPS, or entered by user (*) FocalLengthIn35mmFormat - some cameras use 'FocalLength' for the same thing \_Fotoxx Benchmarks Conditions: Fotoxx 20.0, Intel Core i5, 3 GHz x 4 processor cores Image Index speed for large JPEG files (average 2.34 MB). Create both the metadata index file and thumbnail files. • 5400 rpm disk: 631 files / minute • 7200 rpm disk: 915 files / minute • SSD disk: 6849 files / minute Fotoxx Startup Time by Image Index Level (see |PREFERENCES|), measured with 109K old image files and zero new image files. Disk Type Index initial startup subsequent Level* after reboot startups 5400 rpm 2 10.4 secs 2.8 secs 5400 rpm 1 0.7 0.6 5400 rpm 0 0.3 0.3 7200 rpm 2 8.5 2.5 7200 rpm 1 0.6 0.6 7200 rpm 0 0.3 0.3 SSD 2 2.9 1.8 SSD 1 0.6 0.6 SSD 0 0.3 0.3 (*) Index level: 0 = indexing disabled (search functions disabled) 1 = use current index without updates for new image files 2 = use current index + search and update for new image files Search speed for indexed metadata: >10K files/sec. Non-indexed metadata, 7200 rpm disk: 3170/min. Non-indexed metadata, SSD disk: 4980/min. Benchmarks run on a laptop computer, Intel Core i5, 1.6 GHz x 4 cores: Image indexing speed is about 1/3 as fast as the desktop system shown above. \_Fotoxx Source Code The C++ source code is available at the web site \_https://kornelix.net. The code is heavily commented in the hope that others can understand and use the code for their own projects. If you have a technical question about how something works, or a better idea to pass along, you may contact me (mkornelix@gmail.com). \_Questions, Problems, Bugs If you have a question or a problem, you may contact me. If there is a traceback dump (zappcrash file), please send this to me. Please explain how to produce the error, if you can. \_Acknowledgements Algorithms for the following functions were adapted from "Introduction to Image Processing and Analysis" by Russ and Russ: flatten brightness distribution, noise reduction (median smoothing, top hat), sharpen (unsharp mask, kuwahara method), emboss, RGB/HSL conversion. The wavelets method for noise reduction was adapted from the program dcraw by Dave Coffin. Other libraries and utilities used by Fotoxx include: libgtk, libjpeg, libtiff, libpng, liblcms, dcraw, RawTherapee, libchamplain, exiftool, and many other programs included in Gnu/Linux. Fotoxx coding was started in 2008. Many users have contributed ideas and helped with testing and debugging. Translation credits are in Help > About. \_Fotoxx for Microsoft Windows 10 This can be done with some effort. See the following web page: \_https://kornelix.net/fotoxx_topics/Fotoxx_for_Windows.html ALL TOPICS INDEX |ADJUST HSL| - select and change colors using HSL colors |ADJUST RGB| - adjust image colors using RGB or CMY colors |ALBUM MASS UPDATE| utilities for mass update of multiple album files |ALIEN COLORS| - add random strange colors to an image |ALL FOLDERS| - show all image file folders in a tree structure |APPIMAGE FILES| - list the files included in the Appimage container |AREA OVERIVEW| - select area - concepts and usage |AREA RESCALE| - rescale an image down, leaving selected area at full scale |AUTOSEARCH| - script files can use the Search function to screen files |BATCH CHANGE META| - add or revise metadata for selected or all images |BATCH CONVERT| - copy, move, resize, rename, convert, add text ... |BATCH DELETE/TRASH| - delete or trash selected images |BATCH GEOTAGS| - add or revise geotags for selected or all images |BATCH PHOTO DATE| - change photo dates or times, or shift time zone |BATCH PROCESSING| - overview of batch processing functions |BATCH RAW| - convert RAW files to JPEG/PNG/TIFF, 8 or 16 bit color |BATCH RENAME TAGS| - rename tags for selected or all images |BATCH REPORT META| - report metadata for selected or all images |BATCH SCRIPT| - execute script on all selected image files |BATCH TAGS| - add or remove tags for selected images |BATCH UPRIGHT| - scan for rotated images and upright them |BLANK IMAGE| - create a new blank image file with specified size and color |BLANK WINDOW| - blank or restore (toggle) the current image window |BLUR| - blur an image or image area, multiple methods |BOOKMARKS| - add bookmarks, view list of bookmarks, go to bookmark |BRITE DIST| - edit brightness distribution, rebalance dark and bright areas |BRITE RAMP| - add a brightness/color ramp across an image |BURN DVD/BRD| - create a DVD or BlueRay disc with selected image files |CALIBRATE PRINTER| - calibrate printer color - compensate for color shifts |CAPTIONS| - show file name, captions, comments in image corner |CARTOON| - convert an image to a cartoon-like drawing |CHANGE LANGUAGE| - change the language of the menus and dialogs |CHOOSE MAP| - choose from available maps (USA, France, NYC, etc.) |CHROMATIC 1| - fix chromatic aberration (color fringes, mainly image periphery) |CHROMATIC 2| - fix chromatic aberration (color bands along bright/dark edges) |CLEAR AREA| - remove an area |COLLECTION MANAGEMENT| - collection management and image searching |COLOR DEPTH| - reduce the number of colors (posterize) |COLOR MODE| - convert to black & white, color, negative, positive, sepia |COLOR PROFILE| - convert color profile (e.g. sRGB <--> Adobe RGB) |COMMAND PARAMETERS| - Fotoxx startup options using command line parameters |COPY/MOVE| - copy or move an image file to another folder |COPY/PASTE AREA| - copy an area and insert somewhere else later |COPY PIXELS 1| - mouse-paint area with pixels copied from the same image |COPY PIXELS 2| - mouse-paint area with pixels copied from another image |COPY TO CLIPBOARD| - copy an image file to the clipboard |COPY TO DESKTOP| - copy an image file to the desktop |CURVE EDITING| - how to manipulate curves used in edit functions |CUSTOM DIALOG WIDGETS| - how to use custom dialog widgets |CUSTOM KERNEL| - edit and apply a custom convolution kernel to an image |CYCLE 2| - cycle through the two most recently seen image files |CYCLE 3| - cycle through the three most recently seen image files |DARK/BRIGHT PIXELS| - tool to highlight clipping |DELETE META| - delete specified metadata or all metadata |DELETE/TRASH| - delete or trash an image files |DENOISE| - reduce image noise (small random specks, esp. in dark areas) |DIALOG BUTTONS| - dialog completion options |DITHER| - different ways to pixelize or posterize an image |DRAG AND DROP| - file drag and drop methods |DRAW BOX| - draw box on image (enclose emphasized area) |DRAW LINE| - draw line or arrow on image |DRAW OVAL| - draw oval on image (enclose emphasized area) |DRAW TEXT| - draw text on image with arty attributes |EDIT ANY META| - edit any metadata (add, change) |EDIT META| - edit commonly used metadata |EDIT PROCEDURES| - how to edit image files |EDIT SCRIPT| - record a series of edits in a script file, execute on many files |EDIT WORKFLOW| - alternative edit methods - simple and complex |EMBOSS| - convert an image to a simulated embossing (3D effect) |ENABLE/DISABLE AREA| - enable or disable an area for editing |ESCHER SPIRAL| - transform an image into an endless inward spiral |EXPORT FILE LIST| - create a list of selected files (e.g. for use in a shell script) |EXPORT FILES| - export selected files to a folder (e.g. for web upload) |FILE MAP| - use local map files |FILE SAVE| - save an image file (replace, new version, new file name) |FILE VIEW| - File View window (view single image file) |FIND AREA GAP| - find gap in hand-drawn area outline |FIND DUPLICATES| - find duplicate images in a gallery or the entire image collection |FIRST STARTUP| - information for when Fotoxx is started for the first time |FLATTEN BOOK| - flatten and straighten a photographed book page |FLATTEN| - flatten brightness within local zones to enhance details |FOTOXX CAPABILITIES| - summary of Fotoxx functions and tools |FOTOXX OVERVIEW| - description, prerequisites, license, downloads |GALLERY TO ALBUM| - save the current gallery as an album with assigned name |GALLERY VIEW| - Gallery View window (view thumbnails) |GLOBAL RETX| - rescale RGB brightness values to remove color cast and fog/haze |GOTO FIRST| - jump to gallery start |GOTO LAST| - jump to gallery end |GRADIENTS| - increase local contrast to enhance details |GRID LINES| - show or remove grid lines, set line count or spacing |HDF| - combine images with varying focus depth for extended depth of field |HDR| - combine images with varying ISO for an extended brightness range |IMAGE DIFFS| - subtract two images (with offsets) for artistic effect |INDEX FILES| - 1-time job to find and index all image files |INDEX FUNCTION| - locating and indexing image files for fast searching |INSIDE-OUT| - invert the center-edge distance of each pixel |INSTALLATION| - Fotoxx software installation, dependencies |INVERT AREA| - invert an area (exchange outside/inside areas) |KB SHORTCUTS| - show and revise keyboard shortcuts for menu functions |KEYBOARD ENTER KEY| - functions of Enter key |KEYBOARD ESCAPE KEY| - functions of the Escape key |KEYBOARD SHORTCUTS| - keyboard shortcuts for file/gallery/map views |LINE COLOR| - set color for select area outlines |LINE DRAWING| - convert an image to a simulated line drawing |LIST VIEW| - compact Gallery View with tiny thumbnails and file names only |LOAD/SAVE AREA| - load an area from a file or save an area to a file |MAGNIFY IMAGE| - turn mouse into a magnifying glass over image |MAKE WAVES| - warp an image with a wave pattern |MANAGE ALBUMS| - create and arrange user-defined image collections |MANAGE TAGS| - manage user-defined tags |MAP DEAD PIXELS| - map dead pixels from camera sensor defects or dust specks |MAP PIXEL BIAS| - map pixel RGB level variances from camera sensor variances |MAP VIEW| - map view window (view map with location markers) |MARKERS| - show map markers for all images or only current gallery |MARKUP| - draw on image: text, line/arrow, box, oval |MASHUP| - arrange multiple images and formatted text in a custom layout |MATCH COLORS| - match the colors in one image to those in another image |MENU SHORTCUTS| - popup menus, keyboard shortcuts, favorites menu |MENU SUMMARY| - outline of menu groups and contents |META VIEW| - Gallery View with thumbnail and basic metadata used by Fotoxx |MIRROR| - mirror (or flip) an image horizontally or vertically |MISSING TRANSLATIONS| - list missing translations for the current language |MONITOR COLOR| - tool to adjust monitor brightness and contrast |MONITOR GAMMA| - tool to adjust monitor gamma |MONTAGE| - join many images into a compact tablular format |MOSAIC| - convert an image into a mosaic using tiles made from all images |MOUSE FUNCTIONS| - mouse functions for file/gallery/map views |MOUSE OWNERSHIP| - dialog-mouse control and interaction |MOVE FOTOXX HOME| - move fotoxx home folder to a new location |NET LOCS| - define or go to named map location (position and scale) |NET MAP| - use internet map source |NET SOURCE| - set internet source for map |NEWEST| - open Gallery View of newest images in the collection |NEW SESSION| - start new Fotoxx session in a separate window |ORGANIZING IMAGES| - options for image organization and efficient searching |PAINT EDITS| - mouse-paint an edit function locally and gradually |PAINT IMAGE| - mouse-paint with a color, brush size, and opacity |PAINTING| - convert an image into a simulated painting |PAINT TRANSP| - paint increasing transparency using the mouse |PANORAMA| - stitch together a series of images horizontally |PATTERN| - add a background pattern to an image |PERMISSIONS| - view and set permissions for an image file |PERSPECTIVE| - straighten an object photographed from an obtuse angle |PLACES/DATES| - list image locations and date groups, click for gallery |PLUGINS| - use another image edit application as a Fotoxx edit function |POPUP REPORTS| - keyboard navigation for text-based popup reports |PREFERENCES| - user preferences and settings |PREV/NEXT| - show the previous or next image file in the current gallery |PRINT CALIBRATED| - print an image file using printer color calibration data |PRINT| - print an image file |PT PANORAMA| - stitch together a series of images using Panorama Tools |QUICK INDEX| - quick incremental index with no user interaction |RAW THERAPEE| - (RAW files only) start special RAW edit program |RECENT CHANGES| - list of recent user guide changes |RECENT CHANGES| - recent changes to this document |RECENT| - open Gallery View of most recently seen images |RED EYES| - remove red eyes from flash photos |REMOVE DUST| - remove dust spots on images scanned from old photos |RENAME| - rename an image file |RESIZE| - change the image pixel dimensions |RETOUCH| - edit brightness, contrast, color, saturation |RGB DISTRIBUTION| - show a brightness distribution graph for the current image file |RIGHT-CLICK MENUS| - popup menu functions for File View and Gallery View |RUN SCRIPT| - execute script on current image file (a custom edit function) |SCRIPT FILES| - script files (macros) overview |SEARCH IMAGES| - search images using any metadata or file name data |SELECT AREA| - selecting an image feature or area for separate editing |SELECT FILES| - select files for an album, or input to batch or script function |SELECT HAIRY| - select a complex area, such as hair or foliage |SELECT IMAGE FILES| - image file selection procedure used for many functions |SET WALLPAPER| - set the desktop wallpaper from the current image file (Gnome) |SHARPEN| - sharpen a blurred image, multiple methods |SHIFT COLORS| - gradually shift RGB colors to GBR or BRG |SHOW/HIDE AREA| - show or hide area outlines |SHOW ON MAP| - show an image file position in Net Map View |SHOW RESOURCES| - show CPU time and current memory usage |SHOW RGB| - show RGB values at mouse position or for selected points |SKETCH| - convert an image into a simulated sketch |SLIDE SHOW| - create a slide show with animated transitions and zooms |SMART ERASE| - remove power lines, trash, signs, other small image spoilers |SORT GALLERY| - change the sort order of a gallery |SOURCE FOLDER (2)| - open corresponding Gallery View from current File View image |SOURCE FOLDER| - open corresponding Gallery View from current File View image |SPHERE| - curve an image into a spheroid shape |STACK/LAYER| - combine images, select/paint which image to show in each area |STACK/NOISE| - combine images to reduce noise by averaging |STACK/PAINT| - combine images to remove transient cars, tourists, etc |STACK/SLIDER| - show two images overlapped with a movable boundary line |STRETCH| - increase the image scale from the middle outwards |TAGS OVERVIEW| - image tags (keywords) used for image searching |TECHNICAL NOTES| - some technical information about Fotoxx |TEXTURE| - add texture to an image or selected area |THUMB VIEW| - Gallery View with large thumbnails and file names only |TILES| - convert an image into simulated square tiles |TIMELINE| - table of image counts by year and month, click for gallery |TINY PLANET| - wrap a panorama image around a circle |TOP PANEL DATA| - details of information in the File View top panel |TRANSLATIONS| - adding or updating a translation |TRIM/ROTATE| - trim margins, rotate or level an image |TWIST| - twist an image around a chosen center point |UNBEND| - straighten images having curved lines, esp. panoramas |UNDO EDITS| - mouse-paint to undo previous edits locally and gradually |UNDO/REDO BUTTON| - sequential undo or redo edits to current image |UNWARP CLOSEUP| - remove distortions of a close-up face photo (e.g. big nose) |UPDATE ALBUMS| - update album files for updated image files (new versions) |UPRIGHT| - upright a rotated image (automatic or ±90 degrees) |USER GUIDE| - comprehensive Fotoxx usage guide (this document) |VERT. PANORAMA| - stitch together a series of images vertically |VIDEO FILES| - show and play video clips included in image collection |VIEW 360° PANO| - view a 360° panorama with a rotating viewpoint and wrap-around |VIEW ALL META| - list all image metadata |VIEW META| - list main image metadata |VIGNETTE| - change brightness or color radially around a chosen center |VOODOO 1| - automatic 1-click enhancement that may work |VOODOO 2| - automatic 1-click enhancement that may work |WARP AFFINE| - warp image by pulling with the mouse - parallel lines preserved |WARP AREA| - warp a selected image area by pulling with the mouse |WARP CURVED| - warp entire image or area using the mouse - curvy warp |WARP LINEAR| - warp image by pulling with the mouse - straight lines preserved |WEB UPLOAD| - how to select and upload a batch of images to Flickr, etc. |WINDOW VIEWS AND MENUS| - top-level windows and associated menus |ZAPPCRASH TEST| - test crash report with source code line numbers |ZONAL RETX| - rescale RGB brightness values to remove color cast and fog/haze |[+-]| - increase/decrease image/thumbnail size, |FILE VIEW|, |GALLERY VIEW| EOF (nothing shows beyond this point) ========================================================================================== following are tests for validate_docfile() function run test as follows: $ fotoxx -m "validate user guide" |BAD TOPIC 1| xxxxxx |BAD TOPIC 2| deliberate bad topic links +image: badfile1.png +image: badfile2.jpg deliberate bad image links F1 topics from Fotoxx source files ---------------------------------- file: /home2/mico/programs/fotoxx-20.0-test/f.file.cc topic: |CYCLE 2| topic: |CYCLE 3| topic: |VIEW 360° PANO| topic: |RENAME| topic: |PERMISSIONS| topic: |PREV/NEXT| topic: |PREV/NEXT| topic: |PREV/NEXT| topic: |[+-]| topic: |BLANK IMAGE| topic: |BLANK WINDOW| topic: |COPY/MOVE| topic: |COPY TO DESKTOP| topic: |COPY TO CLIPBOARD| topic: |SET WALLPAPER| topic: |DELETE/TRASH| topic: |PRINT| topic: |FILE SAVE| file: /home2/mico/programs/fotoxx-20.0-test/f.widgets.cc topic: |FAVORITES| file: /home2/mico/programs/fotoxx-20.0-test/fotoxx.cc topic: |FIRST STARTUP| topic: |UNDO/REDO BUTTON| file: /home2/mico/programs/fotoxx-20.0-test/f.tools.cc topic: |INDEX FILES| topic: |QUICK INDEX| topic: |MOVE FOTOXX HOME| topic: |PREFERENCES| topic: |KB SHORTCUTS| topic: |RGB DISTRIBUTION| topic: |MAGNIFY IMAGE| topic: |FIND DUPLICATES| topic: |SHOW RGB| topic: |COLOR PROFILE| topic: |CALIBRATE PRINTER| topic: |CALIBRATE PRINTER| topic: |PRINT CALIBRATED| topic: |GRID LINES| topic: |LINE COLOR| topic: |DARK/BRIGHT PIXELS| topic: |MAP PIXEL BIAS| topic: |MAP DEAD PIXELS| topic: |MONITOR COLOR| topic: |MONITOR GAMMA| topic: |CHANGE LANGUAGE| topic: |MISSING TRANSLATIONS| topic: |SHOW RESOURCES| topic: |APPIMAGE FILES| file: /home2/mico/programs/fotoxx-20.0-test/f.effects.cc topic: |SKETCH| topic: |CARTOON| topic: |LINE DRAWING| topic: |EMBOSS| topic: |TILES| topic: |DITHER| topic: |DITHER| topic: |DITHER| topic: |DITHER| topic: |DITHER| topic: |PAINTING| topic: |TEXTURE| topic: |PATTERN| topic: |MOSAIC| topic: |COLOR MODE| topic: |COLOR DEPTH| topic: |SHIFT COLORS| topic: |ALIEN COLORS| topic: |BRITE RAMP| topic: |PAINT TRANSP| topic: |MIRROR| topic: |CUSTOM KERNEL| file: /home2/mico/programs/fotoxx-20.0-test/f.gallery.cc topic: |BOOKMARKS| topic: |SORT GALLERY| topic: |THUMB VIEW| topic: |META VIEW| topic: |LIST VIEW| topic: |RECENT| topic: |NEWEST| topic: |SELECT IMAGE FILES| topic: |BOOKMARKS| topic: |BOOKMARKS| topic: |ALL FOLDERS| topic: |SOURCE FOLDER| file: /home2/mico/programs/fotoxx-20.0-test/f.edit.cc topic: |TRIM/ROTATE| topic: |UPRIGHT| topic: |RETOUCH| topic: |RESIZE| topic: |ADJUST RGB| topic: |ADJUST HSL| topic: |MARKUP| topic: |DRAW TEXT| topic: |DRAW LINE| topic: |DRAW BOX| topic: |DRAW OVAL| topic: |PAINT IMAGE| topic: |COPY PIXELS 1| topic: |COPY PIXELS 2| topic: |COPY PIXELS 2| topic: |PAINT EDITS| topic: |UNDO EDITS| topic: |PLUGINS| topic: |PLUGINS| topic: |PLUGINS| topic: |RAW THERAPEE| file: /home2/mico/programs/fotoxx-20.0-test/f.area.cc topic: |SELECT AREA| topic: |SELECT HAIRY| topic: |FIND AREA GAP| topic: |SHOW/HIDE AREA| topic: |SHOW/HIDE AREA| topic: |ENABLE/DISABLE AREA| topic: |ENABLE/DISABLE AREA| topic: |INVERT AREA| topic: |CLEAR AREA| topic: |COPY/PASTE AREA| topic: |LOAD/SAVE AREA| topic: |LOAD/SAVE AREA| topic: |COPY/PASTE AREA| file: /home2/mico/programs/fotoxx-20.0-test/f.pixmap.cc file: /home2/mico/programs/fotoxx-20.0-test/f.combine.cc topic: |HDR| topic: |HDF| topic: |STACK/PAINT| topic: |STACK/NOISE| topic: |STACK/LAYER| topic: |STACK/SLIDER| topic: |IMAGE DIFFS| topic: |PANORAMA| topic: |VERT. PANORAMA| topic: |PT PANORAMA| file: /home2/mico/programs/fotoxx-20.0-test/f.albums.cc topic: |MANAGE ALBUMS| topic: |UPDATE ALBUMS| topic: |ALBUM MASS UPDATE| topic: |GALLERY TO ALBUM| topic: |SLIDE SHOW| file: /home2/mico/programs/fotoxx-20.0-test/f.warp.cc topic: |UNBEND| topic: |PERSPECTIVE| topic: |WARP AREA| topic: |WARP CURVED| topic: |WARP LINEAR| topic: |WARP AFFINE| topic: |UNWARP CLOSEUP| topic: |FLATTEN BOOK| topic: |AREA RESCALE| topic: |MAKE WAVES| topic: |TWIST| topic: |SPHERE| topic: |STRETCH| topic: |INSIDE-OUT| topic: |TINY PLANET| topic: |ESCHER SPIRAL| file: /home2/mico/programs/fotoxx-20.0-test/f.process.cc topic: |BATCH CONVERT| topic: |BATCH UPRIGHT| topic: |BATCH DELETE/TRASH| topic: |BATCH RAW| topic: |BURN DVD/BRD| topic: |EXPORT FILE LIST| topic: |EXPORT FILES| topic: |SCRIPT FILES| topic: |SCRIPT FILES| topic: |SCRIPT FILES| topic: |SCRIPT FILES| file: /home2/mico/programs/fotoxx-20.0-test/f.meta.cc topic: |VIEW META| topic: |VIEW META| topic: |EDIT META| topic: |MANAGE TAGS| topic: |EDIT ANY META| topic: |DELETE META| topic: |CAPTIONS| topic: |BATCH TAGS| topic: |BATCH RENAME TAGS| topic: |BATCH PHOTO DATE| topic: |BATCH CHANGE META| topic: |BATCH REPORT META| topic: |BATCH GEOTAGS| topic: |PLACES/DATES| topic: |TIMELINE| topic: |SEARCH IMAGES| topic: |MARKERS| topic: |FILE MAP| topic: |CHOOSE MAP| topic: |NET SOURCE| topic: |NET MAP| topic: |SHOW ON MAP| topic: |NET LOCS| file: /home2/mico/programs/fotoxx-20.0-test/f.enhance.cc topic: |VOODOO 1| topic: |VOODOO 2| topic: |BRITE DIST| topic: |GRADIENTS| topic: |FLATTEN| topic: |GLOBAL RETX| topic: |ZONAL RETX| topic: |SHARPEN| topic: |BLUR| topic: |DENOISE| topic: |RED EYES| topic: |MATCH COLORS| topic: |SMART ERASE| topic: |CHROMATIC 1| topic: |CHROMATIC 2| topic: |VIGNETTE| topic: |REMOVE DUST| file: /home2/mico/programs/fotoxx-20.0-test/f.mashup.cc topic: |MASHUP| topic: |MONTAGE| fotoxx-20.08/data/widgets.css000066400000000000000000000010141362435004500161410ustar00rootroot00000000000000/*** This file reduces the size of some GTK widgets by reducing unnecessary padding. You may make changes or comment-out text to restore the standard GTK widgets. Do not delete the file - it would be automatically restored. ***/ /*** disabled modifications window *, menu, box * { color: #000000; background-color: #DDDDDD; } ***/ button, .button { padding: 0px 2px 0px 2px; /* top right bottom left */ } scale { /* slider control */ padding: 6px 6px; } fotoxx-20.08/debian-control000066400000000000000000000030331362435004500156760ustar00rootroot00000000000000Package: Fotoxx Version: 20.08 Architecture: amd64 Section: graphics Installed-Size: 15244 Keywords: image, photo, edit, retouch Maintainer: Mike Cornelison Priority: extra Homepage: https://kornelix.net Depends: libc6, xdg-utils, binutils, dcraw, libimage-exiftool-perl, libchamplain-gtk-0.12-0, libclutter-gtk-1.0-0 Conflicts: fotoxx-common Suggests: rawtherapee, growisofs, hugin, vlc, heif-examples, libopenjp2-tools Description: Edit photos and manage a large collection. Navigate a large image collection with a thumbnail browser. View and edit image files (RAW, jpeg, png, tiff ...). Editing is done in 24 bits/color, output is 8 or 16 bits/color. Fotoxx has extensive tools for edit, repair, and special effects. Fotoxx is fast and interactive: view full image as it changes. Undo and redo within and across edit functions. File versioning: save and recall multiple edit stages. Select image features or areas to edit separately from background. Copy and paste area selections within and across images. Composite functions: HDR, HDF, panorama, stack, image/text montage. Metadata edit and report: tags, captions, dates, locations ... Search images based on any metadata and folder/file names or parts. Albums: select images and arranged order with drag and drop. Slide Show with animated transitions and pan/zoom. Scalable world map with image markers - click marker for gallery. Batch functions: convert, resize, upright, move, revise metatata. User Guide (English) describes all functions in great detail. fotoxx-20.08/doc/000077500000000000000000000000001362435004500136215ustar00rootroot00000000000000fotoxx-20.08/doc/README000066400000000000000000000040601362435004500145010ustar00rootroot00000000000000Installation of fotoxx from source tarball Building fotoxx requires the following packages: g++ or clang++ C++ compiler and linker libgtk-3-dev GTK3 graphics library (GUI base) libtiff-*-dev tiff image files libpng-*-dev png image files libjpeg-*-dev jpeg image files liblcms2-*-dev color spaces (sRGB/Adobe-RGB) libchamplain-gtk-*-dev geomapping library libclutter-gtk-*-dev graphics library At run time the following packages are also needed: dcraw RAW file conversion program exiftool read/write metadata (EXIF etc.) binutils GNU binary utilities web browser firefox or chromium The above are recent Debian names. Package naming is chaotic, so you may have to to hunt the names for other Linux flavors. Build and install fotoxx as follows: 1. Download the tar file (fotoxx-N.N.tar.gz) 2. Open a terminal window 3. $ cd Downloads # go to download file 4. $ tar -xzf fotoxx-N.N.tar.gz # unpack to ./fotoxx 5. $ cd fotoxx # go there 6. $ make # build program 7. $ sudo make install # install program Missing dependencies will cause error messages in step 6. Install these from your repository and repeat step 6. Step 7 moves all files to the following locations: /usr/bin/fotoxx binary executable /usr/share/fotoxx/ icons, translations ... /usr/share/doc/fotoxx/ README, man page ... Fotoxx is easy to use but unconventional, so please review the user guide (menu Help > User Guide) before trying fotoxx. The optional fotoxx-maps package may be installed afterwards. This is only useful if you have a poor internet connection. FOR PACKAGE BUILDERS: If $PREFIX is defined, files go there instead of /usr. If $DESTDIR is also defined, files go to $DESTDIR$PREFIX. Please, DO NOT make a separate package for the all-arch parts. fotoxx-20.08/doc/changelog000066400000000000000000000141441362435004500154770ustar00rootroot00000000000000Fotoxx Change Log ================= Version 20.08 Feb 19 2020 • Add Portuguese translation Version 20.07 Feb 17 2020 • Add libraries to appimage container possibly missing if KDE desktop. Version 20.06 Feb 10 2020 • minor change in image zoom from mouse wheel • Trim/Rotate: better logic to decide if mouse drag trims or levels image. • Raw Therapee: search for the .tif output file in all top image folders. • changed contact address to mkornelix@gmail.com Version 20.05 Jan 29 2020 • appimage container: add later release of libjpeg.so.8, remove libjasper. • bugfix: crash in processing of compressed translation file. • bugfix: exit unconditionally if exiftool not installed. Version 20.04 Jan 08 2020 • appimage desktop file: enclose Exec= path in quotes to allow embedded blanks. • improve popup tip for [+-] button, better clarity for new users. • correct user guide zoom button references from 'ZOOM±' to '[+-]'. • add 'Thumb View' to gallery menu - set gallery view mode to thumbnails. • remember position and size of popup windows for user guide and most reports. Version 20.03 Jan 06 2020 When a new release is started for the first time: • initialize 'metadata_short_list' and append user additions from old list • copy the updated 'userguide' file unconditionally Version 20.02 Jan 05 2020 • bugfix: crash if Area Find Gap is restarted (new click) while still running. • bugfix: wrong jpeg library in appimage build caused .heic file errors. • disallow adding custom indexed metadata which is indexed by default. Version 20.01 Jan 03 2020 Remove diagnostic log accidentally left in source code Version 20.0 Jan 01 2020 ------------ • Image Index: • First Startup: dialog for initial decision about image indexing. • Cancel and restart will resume in place instead of starting over. • Track progress for thumbnail updates along with metadata index updates. • Files on removable media: index data and thumbnails are no longer lost if Fotoxx is sometimes operated without the removable media mounted. • Tools > Quick Index: quick incremental index with no user interaction (capture files created/changed by outside agent during Fotoxx session). • Denoise: • median method: over 10x faster for large radius and large image. • anneal and chroma: new methods, very effective with parameter fiddling. • voodoo: new method, no fiddling, fast and usually good enough. • RAW file editing, new options: • Use dcraw or RawTherapee, interactive or batch. • Expand brightness distribution if an image is abnormally dark. • Convert RAW color space using the camera embedded image as a guide. • Save-as new version produces a 16-bits/color TIFF file by default. • Batch RAW processing additions: • map and compensate dead pixels (constant dark or colored pixels). • measure and compensate pixel RGB bias (camera sensor small variations). • File permissions: • File > Permissions: view and set permissions for current file. • File Save > new version: copy permissions from original/prior version. • File Save > new file: dialog shows permissions and allows revision. • Edit > Retouch: now includes color balance and saturation adjustments. • Edit > Auto Upright: usually automatic, but also has [-90] [+90] buttons. • Edit > Trim/Rotate: includes auto-upright and [-90] [+90] buttons. • Edit > Trim/Rotate: faster/smoother response to leveling by mouse drag. • Enhance > Chromatic: fix lateral and axial chromatic aberration. • Edit > Undo Edits: locally/gradually undo prior edit via mouse painting. • Combine > Stack/Slider: view two overlaid images with sliding separator. • File > GIF Animation: play an animated GIF file in a popup window. • Warp > Escher Spiral: Transform image into recursive inward spiral. • Edit > Markup: includes draw text, line/arrow, box, oval/circle. • Gallery > List View: a dense file name list, similar to Nautilus list view. • Gallery > Meta View: list files with thumbnails and key metadata items. • Gallery > Select Files: select files for subsequent batch/script function. • Edit > Resize: buttons were added for resize to 1.5x, 2x, and 3x. • Tools > Preferences: choose program for playing videos: totem, vlc, others. • Zonal Retinex memory reduced by 40% (30 megapixel image needs about 4 GB). • Symbolic links: these can now be used with Fotoxx if you know the rules. • Apple .heic files and jpeg2000 .jp2 files can be viewed and edited. • Maximum gallery and album size was increased from 25K to 40K image files. • Edit menus were reorganized and some names were shortened. • Fotoxx startup: if previous file not found, fall back to previous gallery. • Tools > Magnify: UI improvement, more responsive mouse tracking. • Manage Albums: user interface is simpler and faster for common tasks. • Search Images: option to select only original + newest version. • Shell script: saved Fotoxx Search criteria can be used to select files. • Slide Show: display of image file name is optional, time is adjustable. • Invalid UTF-8 text in image metadata is detected and suppressed. • Metadata text on image: show any combination of file name/caption/comment. • Show RGB: keep pixel positions when a new image is opened (compare images). • User Guide was simplified and updated. Web browser is no longer used. • Warn immanent "out of memory" failure before vanishing without a trace. • Mashup UI: more reliable capture and drag to resize overlay image or text. • Reduced slide show jitter by using kernel function sched_setaffinity(). • Scripting enabled: Resize, Cartoon, Emboss, Pattern, Dither, Brite Ramp. • Maps: show markers for images with geocoordinates but no location names. • Tools > Missing Translations: export the translation .po file for edit. • Tools > Appimage Files: list files included in the Appimage container. • Tools > Preferences: set compression methods/levels for TIFF/PNG files. • Select Area by color: smoother mouse tracking and image update. fotoxx-20.08/doc/copyright000066400000000000000000000014631362435004500155600ustar00rootroot00000000000000Upstream-Name: fotoxx Upstream-Contact: Michael Cornelison Source: https://kornelix.net Files: * Copyright: Copyright 2007-2020 Michael Cornelison License: GPL-3+ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . The complete text of the GNU General Public License can be found at: https://www.gnu.org/licenses/gpl-3.0.en.html fotoxx-20.08/doc/fotoxx.man000066400000000000000000000102621362435004500156460ustar00rootroot00000000000000.TH FOTOXX 1 2020-01-01 "Linux" "Fotoxx man page" Fotoxx - photo/image editor and collection manager DESCRIPTION Organize and manage a large image collection. Edit and optimize photos and other images, edit metadata, search images, and perform batch operations. Fotoxx is a GTK GUI application. The included user manual explains Fotoxx operation in great detail. The following is a summary of capabilities. CAPABILITIES • Organize and manage a very large photo/image collection. • Thumbnail browser/navigator with adjustable thumb size and list view. • Click thumbnail for full-size view, image zoom in/out and pan/scroll. • RAW file conversion, single or batch, save with 8 or 16 bits/color. • Edit RAW files directly using RawTherapee or the Fotoxx edit functions. • Large set of functions to edit, repair, enhance, and transform images. • Internal processing in 24 bits per color (float), output in 8 or 16 bits. • Edited files have a version number, originals are retained by default. • Fast edit visual feedback using the full image or selected zoom-in area. • Undo/Redo edits, go back and forth to compare original and edited versions. • Conventional edit functions include: brightness, contrast, color balance, saturation, trim/crop, rotate, upright, resize, sharpen, denoise, blur, paint, draw text/line/arrow/box, red eyes, smart erase (remove spoilers), fix perspective, warp/unwarp, HDR, panorama, stack/edit, photo montage ... • Advanced edit functions to bring out details and add flair to an image: Edit brightness distribution, Global/zonal Retinex, Gradients ... • A large set of creative special effects and arty transforms is available: sketch, cartoon, drawing, emboss, tiles, dither, painting, mosaic, sphere, tiny planet, Escher spiral, custom convolution kernels ... • Most edit functions can be 'painted' locally and gradually with the mouse. • Select image objects or areas to edit separately from the background: outline by hand, follow feature edges, 'flood' into matching colors ... • Special selection tool for complex image features (e.g. hair, foliage). • Copy areas within and across images by mouse painting and blending. • Create or maintain transparent image areas while editing. • Mashup: arrange images and text in an arbitrary layout using the mouse. • Custom scripts: record a series of edits and use as a new edit function. • Custom favorites menu: select and arrange icons and text in popup window. • Plugins: use other edit apps (e.g. Gimp) as Fotoxx plug-in edit function. • Batch tools for renaming, resizing, converting, processing RAW files, adding/revising/reporting metadata, and executing custom edit scripts. • Metadata edit and report (tags, dates, captions, geotags ... any metadata). • Search images using any metadata and folder/file names or partial names: dates, tags, locations, ratings, captions, comments, exposure data ... • Show a table of image locations and date groups, click for image gallery. • Show an image calendar, click on year or month for a gallery of images. • Click markers on a scalable internet map for gallery of images at location. • Use locally stored maps: world, continents, nations, cities, custom maps. • View 360 degree panorama image (Google Street View format). • Show video files (first frame) in thumbnails, view full size, play video. • Show animated GIF files (first frame) in thumbnails, play animation. • Bookmarks: assign names to file locations, list names, goto name (gallery). • Create albums linking selected images. Arrange sequence by drag and drop. • Slide show: show album images with animated transitions and pan/zoom. • Print an image at any scale. Printer color calibration tool is available. • Custom keyboard shortcuts can be assigned to most functions. • Comprehensive user guide and context help popups via F1 key. SEE ALSO The User Guide is available from the menu Help > User Guide The home page for Fotoxx is at https://kornelix.net AUTHORS Written by Mike Cornelison fotoxx-20.08/doc/license000066400000000000000000001044111362435004500151670ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Fotoxx photo editor Copyright (C) 2018 Fotoxx 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Fotoxx photo editor Copyright (C) 2018 Fotoxx This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . fotoxx-20.08/f.albums.cc000066400000000000000000007514231362435004500151060ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* Fotoxx image edit - album and slide show functions m_manage_albums manage albums album_create create a new album album_rename rename an album album_delete delete an album album_show [TOP] button selection of album to view/edit album_purge_replace purge missing files, replace with later versions album thumbnail functions: album_addselfiles add 'selected files' at position album_addfile add single file at position (drag/drop from gallery) album_removefile remove file at position from album album_movefile move album file position (drag/drop within album) m_update_albums replace old with new version or append new to old m_album_mass_update album mass update utilities album_batch_rename mass album file rename (for m_batch_convert) m_gallery2album save current gallery as album m_slideshow display album with zoom and arty transitions *********************************************************************************/ #define EX extern // disable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // Manage Albums - create, view, edit named albums of image files #define ANCC 100 // max. album name #define AFCC 300 // max. album file name (path) #define maxalbums 200 // max. total albums #define maxalbumfiles maxgallery // max. files in one album 20.0 #define Balbumtoobig E2X("max. album size exceeded: %d") /********************************************************************************/ // menu function void m_manage_albums(GtkWidget *, cchar *) // overhauled 20.0 { int manage_albums_dialog_event(zdialog *zd, cchar *event); // manage albumd dialog event func cchar *helptext1 = E2X("Right-click album thumbnail for edits: \n" " - add selected files at this position \n" " - remove this file from the album"); cchar *helptext2 = E2X("Arrange files with thumbnail drag and drop"); F1_help_topic = "manage albums"; if (FGWM != 'F' && FGWM != 'G') return; if (checkpend("all")) return; // check nothing pending Fblock = 1; /*** __________________________________________________ | | | Manage Albums | | | | [Create] Create or replace an album | | [Rename] Rename an album | | [Delete] Delete an album | | [Select] Files to add to an album | | | | Right-click album thumbnail for edits: | | - add selected files at this position | | - remove this file from album | | | | Arrange files with thumbnail drag and drop | | | | [done] | |__________________________________________________| ***/ zdialog *zd = zdialog_new(E2X("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","create","vb1",Bcreate); zdialog_add_widget(zd,"button","rename","vb1",Brename); zdialog_add_widget(zd,"button","delete","vb1",Bdelete); zdialog_add_widget(zd,"button","select","vb1",Bselect); zdialog_add_widget(zd,"hbox","hbcreate","vb2"); zdialog_add_widget(zd,"label","labcreate","hbcreate",E2X("Create or replace an album")); zdialog_add_widget(zd,"hbox","hbrename","vb2"); zdialog_add_widget(zd,"label","labrename","hbrename",E2X("Rename an album")); zdialog_add_widget(zd,"hbox","hbdelete","vb2"); zdialog_add_widget(zd,"label","labdelete","hbdelete",E2X("Delete an album")); zdialog_add_widget(zd,"hbox","hbselect","vb2"); zdialog_add_widget(zd,"label","labselect","hbselect",E2X("Files to add to an album")); zdialog_add_widget(zd,"hbox","hbhelp1","dialog",0,"space=8"); zdialog_add_widget(zd,"label","labhelp1","hbhelp1",helptext1,"space=5"); zdialog_add_widget(zd,"hbox","hbhelp2","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labhelp2","hbhelp2",helptext2,"space=5"); zdialog_run(zd,manage_albums_dialog_event,"save"); // run dialog return; } // manage albums dialog event and completion function int manage_albums_dialog_event(zdialog *zd, cchar *event) { void album_create(); void album_rename(); void album_delete(); char mess[200], albumname[ANCC], *pp; if (zd->zstat) // [done] or [x] { zdialog_free(zd); Fblock = 0; return 1; } if (strmatch(event,"create")) // start a new album for editing { zdialog_show(zd,0); // hide manage albums dialog album_create(); // create new album zdialog_show(zd,1); // restore manage albums dialog } if (strmatch(event,"rename")) // rename an album { zdialog_show(zd,0); // hide manage albums dialog album_rename(); // rename album zdialog_show(zd,1); // restore manage albums dialog } if (strmatch(event,"delete")) // delete an album { zdialog_show(zd,0); // hide manage albums dialog album_delete(); // delete album zdialog_show(zd,1); // restore manage albums dialog } if (strmatch(event,"select")) // select files to add { *albumname = 0; if (curr_album) strncpy0(albumname,curr_album,ANCC); zdialog_show(zd,0); // hide manage albums dialog m_select_files(0,0); // creates album "selected files" zdialog_show(zd,1); // restore manage albums dialog if (*albumname) { album_show(albumname); // restore org. album if (navi::Nfiles == 0) { album_addselfiles(1); // if empty album, auto add 20.0 pp = strrchr(albumname,'/'); // selected files if (pp) pp++; else pp = albumname; snprintf(mess,200,E2X("%d files added to album %s"),GScount,pp); poptext_window(MWIN,mess,100,100,0,5); } } } return 1; } /********************************************************************************/ // create a new album void album_create() { int album_create_dialog_event(zdialog *zd, cchar *event); char **flist; int NF; char patt[200]; snprintf(patt,200,"%s/*",albums_folder); // get current album count zfind(patt,flist,NF); for (int ii = 0; ii < NF; ii++) zfree(flist[ii]); zfree(flist); if (NF >= maxalbums) { // cannot create more zmessageACK(Mwin,E2X("max. album count exceeded: %d"),maxalbums); return; } /*** _______________________________________________ | Create or replace an album | | | | Album Name [____________________] [Browse] | | (o) make an initially empty album | | (o) fill from pre-selected files | | (o) fill from the current gallery | | (o) select initial files | | | | [done] [cancel] | |_______________________________________________| ***/ zdialog *zd = zdialog_new(E2X("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",E2X("Album Name"),"space=3"); zdialog_add_widget(zd,"zentry","albumname","hbname",0,"space=3|size=20"); zdialog_add_widget(zd,"button","browse","hbname",Bbrowse,"space=3"); zdialog_add_widget(zd,"hbox","hbopt","dialog",0,"space=5"); zdialog_add_widget(zd,"label","space","hbopt",0,"space=10"); zdialog_add_widget(zd,"vbox","vbopt","hbopt"); zdialog_add_widget(zd,"radio","empty","vbopt",E2X("make an initially empty album")); zdialog_add_widget(zd,"radio","selected","vbopt",E2X("fill from pre-selected files")); zdialog_add_widget(zd,"radio","gallery","vbopt",E2X("fill from current gallery")); zdialog_add_widget(zd,"radio","select","vbopt",E2X("select initial files")); zdialog_stuff(zd,"empty",1); zdialog_stuff(zd,"selected",0); zdialog_stuff(zd,"gallery",0); zdialog_stuff(zd,"select",0); zdialog_run(zd,album_create_dialog_event,"parent"); zdialog_wait(zd); zdialog_free(zd); return; } // dialog event and completion function int album_create_dialog_event(zdialog *zd, cchar *event) { int Fempty, Fselected, Fgallery, Fselect; int yn, err; char albumname[ANCC], newalbumfile[AFCC]; char *cfile, *pp; FILE *fid; STATB statb; if (strmatch(event,"browse")) { cfile = zgetfile(E2X("Album Name"),MWIN,"file",albums_folder); // choose file name if (! cfile) return 1; pp = strrchr(cfile,'/'); if (! pp) return 1; zdialog_stuff(zd,"albumname",pp+1); zfree(cfile); return 1; } if (! zd->zstat) return 1; // wait for completion if (zd->zstat != 1) return 1; // cancel or [x] zdialog_fetch(zd,"albumname",albumname,ANCC); // get album name if (*albumname <= ' ') { zmessageACK(Mwin,E2X("enter an album name")); zd->zstat = 0; // keep dialog active return 1; } snprintf(newalbumfile,AFCC,"%s/%s",albums_folder,albumname); // make filespec err = stat(newalbumfile,&statb); if (! err) { // already exists yn = zmessageYN(Mwin,E2X("replace album %s ?"),albumname); if (! yn) return 1; } if (curr_album) zfree(curr_album); // set current album for editing curr_album = zstrdup(newalbumfile); fid = fopen(curr_album,"w"); // open/write empty album file if (! fid) { zmessageACK(Mwin,strerror(errno)); return 1; } fclose(fid); zdialog_fetch(zd,"empty",Fempty); // get option zdialog_fetch(zd,"selected",Fselected); zdialog_fetch(zd,"gallery",Fgallery); zdialog_fetch(zd,"select",Fselect); if (Fempty) zmessageACK(Mwin,E2X("Use [Select] to add files to an empty album")); if (Fselected) album_addselfiles(1); // fill album from selected files if (Fgallery) // fill album from gallery album_create_from_gallery(curr_album); if (Fselect) { // select initial album files m_select_files(0,0); album_addselfiles(1); } album_show(); zmessage_post(Mwin,"20/20",3,E2X("new album created")); return 1; } // make an album from the current gallery // return 0 = OK, +N = error int album_create_from_gallery(cchar *newalbumfile) { char *pp; int Nth; FILE *fid; FTYPE ftype; if (navi::Nimages == 0) { zmessageACK(Mwin,E2X("gallery is empty")); return 1; } if (navi::Nfiles > maxalbumfiles) { zmessageACK(Mwin,Balbumtoobig,maxalbumfiles); return 1; } fid = fopen(newalbumfile,"w"); // open/write album file if (! fid) { zmessageACK(Mwin,strerror(errno)); return 1; } for (Nth = 0; Nth < navi::Nfiles; Nth++) // add gallery images to album file { pp = gallery(0,"get",Nth); if (! pp) break; ftype = image_file_type(pp); // must be image type file if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) { zfree(pp); continue; } fprintf(fid,"%s\n",pp); zfree(pp); } fclose(fid); return 0; } /********************************************************************************/ // rename an album void album_rename() { cchar *renalbum = E2X("Rename an album"); char *cfile, *dfile, *cpp, *dpp; int err; STATB statb; cfile = zgetfile(renalbum,MWIN,"file",albums_folder); // choose album file if (! cfile) return; cpp = strrchr(cfile,'/'); if (! cpp) { // should not happen printz("invalid file: %s \n",cfile); zfree(cfile); return; } dpp = zdialog_text(Mwin,E2X("enter new album name"),cpp+1); if (! dpp) { zfree(cfile); return; } if (strchr(dpp,'/')) { zmessageACK(Mwin,E2X("invalid file name: %s"),dpp); zfree(cfile); zfree(dpp); return; } if (curr_album && strmatch(cfile,curr_album)) { // current album will be renamed zfree(navi::galleryname); navi::galleryname = 0; navi::gallerytype = TNONE; m_viewmode(0,"F"); zfree(curr_album); curr_album = 0; } dfile = zstrdup(cfile,strlen(dpp)); // construct pathname cpp = strrchr(dfile,'/'); strcpy(cpp+1,dpp); err = stat(dfile,&statb); if (! err) { zmessageACK(Mwin,E2X("album already exists: %s"),dpp); zfree(cfile); zfree(dfile); zfree(dpp); return; } err = rename(cfile,dfile); if (err) zmessageACK(Mwin,strerror(errno)); cpp = strrchr(cfile,'/'); zmessageACK(Mwin,E2X("%s \n renamed: %s"),cpp+1,dpp); zfree(cfile); zfree(dfile); zfree(dpp); return; } /********************************************************************************/ // delete an album void album_delete() { cchar *delalbum = E2X("Delete an album"); char *cfile, *cpp; cfile = zgetfile(delalbum,MWIN,"file",albums_folder); // choose album file if (! cfile) return; cpp = strrchr(cfile,'/'); if (! cpp) { // should not happen printz("invalid file: %s \n",cfile); zfree(cfile); return; } int yn = zmessageYN(Mwin,E2X("delete %s ?"),cpp+1); if (! yn) { zfree(cfile); return; } if (curr_album && strmatch(cfile,curr_album)) { // current album will be deleted zfree(navi::galleryname); navi::galleryname = 0; navi::gallerytype = TNONE; m_viewmode(0,"F"); zfree(curr_album); curr_album = 0; } remove(cfile); zfree(cfile); return; } /********************************************************************************/ // initz. gallery from current album, show gallery // if albumfile is null, show current album // if not null, set current album from albumfile void album_show(cchar *albumfile) { if (albumfile && albumfile != curr_album) { if (curr_album) zfree(curr_album); curr_album = zstrdup(albumfile); } if (! curr_album) return; album_purge_replace(curr_album); // purge/replace missing, notify navi::gallerytype = ALBUM; gallery(curr_album,"initF",0); // gallery = album gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // paint m_viewmode(0,"G"); if (navi::Nfiles > maxalbumfiles) zmessageACK(Mwin,Balbumtoobig,maxalbumfiles); return; } /********************************************************************************/ // Purge missing files from an album, replace with later versions if available. // Show popup list of files deleted or replaced. void album_purge_replace(cchar *albumfile) { FTYPE ftype; char albumbuff[XFCC]; cchar *albumname; int ii, Nmissing = 0, Nfiles = 0; int jj, jj1, jj2; char *pp, *pp2, *fnewest, poptitle[100]; char *files[maxalbumfiles]; zdialog *zd; FILE *fidr; fidr = fopen(albumfile,"r"); // open/read album file if (! fidr) { zmessageACK(Mwin,strerror(errno)); return; } while (true) // read album member files { pp = fgets_trim(albumbuff,XFCC,fidr); if (! pp) break; ftype = image_file_type(pp); // count missing files if (ftype == FNF) Nmissing++; files[Nfiles] = zstrdup(pp); // save all files Nfiles++; if (Nfiles == maxalbumfiles) break; } fclose(fidr); if (Nmissing == 0) goto cleanup; // no missing files albumname = strrchr(albumfile,'/'); if (albumname) albumname++; snprintf(poptitle,100,"album %s - missing files",albumname); zd = popup_report_open(poptitle,Mwin,600,300,0,0,0); linedit_open(albumfile); // open album file for editing for (ii = 0; ii < Nfiles; ii++) // get all files { pp = linedit_get(); if (! pp) break; ftype = image_file_type(pp); if (ftype == FNF) // remove deleted files { fnewest = file_newest_version(pp); if (! fnewest) { // no newer version found popup_report_write(zd,0,E2X("%s removed with no replacement \n"),pp); continue; } jj1 = ii-5; // album neighborhood of deleted file jj2 = ii+5; if (jj1 < 0) jj1 = 0; if (jj2 > Nfiles) jj2 = Nfiles; for (jj = jj1; jj < jj2; jj++) // newer version already in neighborhood? if (strmatch(fnewest,files[jj])) break; if (jj < jj2) { // yes, remove file popup_report_write(zd,0,E2X("%s removed with no replacement \n"),pp); continue; } pp2 = strrchr(fnewest,'/'); popup_report_write(zd,0,E2X("%s replaced by ...%s \n"),pp,pp2); // replace with newest version linedit_put(fnewest); zfree(fnewest); continue; } else linedit_put(pp); } linedit_close(); cleanup: for (int ii = 0; ii < Nfiles; ii++) zfree(files[ii]); return; } // Purge missing files from ALL albums, replace with later versions if available. // Called by m_batch_deltrash(). void album_purge_replace_all() { char **flist, *pp; int ii, NF; char findcomm[200]; snprintf(findcomm,200,"%s/*",albums_folder); // find all album files 20.0 zfind(findcomm,flist,NF); for (ii = 0; ii < NF; ii++) { pp = strrchr(flist[ii],'/'); if (! pp) continue; pp++; if (*pp == '.') continue; album_purge_replace(flist[ii]); } for (ii = 0; ii < NF; ii++) zfree(flist[ii]); zfree(flist); return; } /********************************************************************************/ // insert "selected files" into album at designated position // mode: 1 = add at position 0 // 2 = use clicked_posn and clicked_width void album_addselfiles(int mode) { char selfiles[AFCC]; char albumbuff[XFCC]; FILE *fid; int ii, jj, posn; char *pp; if (! curr_album) return; if (mode == 1) posn = 0; // insert at album start else if (mode == 2) { posn = clicked_posn; // insert at clicked thumb position if (clicked_width > 50) posn++; // right side of thumbnail clicked } else return; snprintf(selfiles,AFCC,"%s/%s",albums_folder,"selected files"); fid = fopen(selfiles,"r"); if (! fid) { zmessageACK(Mwin,E2X("no selected files")); return; } linedit_open(curr_album); for (ii = jj = 0; ; ii++) // loop album member files { if (ii == posn) { while ((pp = fgets_trim(albumbuff,XFCC,fid))) { // insert selected files if (++jj == maxalbumfiles) break; linedit_put(pp); } } pp = linedit_get(); // copy rest if (! pp) break; // EOF if (++jj == maxalbumfiles) break; linedit_put(pp); } fclose(fid); linedit_close(); album_show(); // (will alarm maxalbumfiles) return; } /********************************************************************************/ // insert 'curr_file' into album at designated position // mode: 1 = add at position 0 // 2 = use clicked_posn and clicked_width void album_addcurrfile(int mode) // 20.0 { int ii, jj, posn; char *pp; if (! curr_album) return; if (mode == 1) posn = 0; // insert at album start else if (mode == 2) { posn = clicked_posn; // insert at clicked thumb position if (clicked_width > 50) posn++; // right side of thumbnail clicked } else return; linedit_open(curr_album); // open album file for (ii = jj = 0; ; ii++) // loop album member files { if (ii == posn) linedit_put(curr_file); // insert curr_file at position pp = linedit_get(); // copy rest if (! pp) break; // EOF if (++jj == maxalbumfiles) break; linedit_put(pp); } linedit_close(); album_show(); // (will alarm maxalbumfiles) return; } /********************************************************************************/ // insert single file into album at position (drag/drop from gallery window) void album_addfile(cchar *file, int posn) { int ii, jj; char *pp; if (! curr_album) return; linedit_open(curr_album); for (ii = jj = 0; ; ii++) // loop album member files { if (ii == posn) { if (++jj == maxalbumfiles) break; linedit_put(file); // insert current file here } pp = linedit_get(); // copy album member file if (! pp) break; // EOF if (++jj == maxalbumfiles) break; linedit_put(pp); } linedit_close(); album_show(); return; } /********************************************************************************/ // remove file at position from album void album_removefile(int posn) { int ii; char *pp; if (! curr_album) return; linedit_open(curr_album); for (ii = 0; ; ii++) // loop album member files { pp = linedit_get(); if (! pp) break; if (ii == posn) continue; // position to remove linedit_put(pp); } linedit_close(); album_show(); return; } /********************************************************************************/ // move an album file from pos1 to pos2 (for drag and drop) void album_movefile(int pos1, int pos2) { char *filez; int ii; char *pp; if (! curr_album) return; if (pos1 == pos2) return; filez = gallery(0,"get",pos1); // file to move if (! filez) return; linedit_open(curr_album); for (ii = 0; ; ii++) // loop album member files { pp = linedit_get(); // album member file if (! pp) break; if (ii == pos1) continue; // skip file position to move from if (ii == pos2) { // file position to move to linedit_put(filez); // insert the moved file here pos2 = -1; } linedit_put(pp); // copy to output file } if (pos2 >= 0) linedit_put(filez); // add at the end linedit_close(); return; } /********************************************************************************/ // Replace an old file with a designated new file in selected albums, // or add the new file after the old file. namespace update_albums_names { char *albumfiles[maxalbums]; char oldfile[XFCC], newfile[XFCC]; // albums and replace option kept int Nalbums = 0; // to initialize next run int Freplace = 1; char countmess[40]; cchar *countformat = "%d albums chosen"; }; // menu function void m_update_albums(GtkWidget *, cchar *) { using namespace update_albums_names; int update_albums_dialog_event(zdialog *zd, cchar *event); zdialog *zd; STATB statb; char *pp; int ii, jj, zstat; F1_help_topic = "update albums"; if (FGWM != 'F' && FGWM != 'G') return; if (checkpend("all")) return; // check nothing pending Fblock = 1; /*** __________________________________________________ | Replace Album File | | | | [Choose Albums] N albums chosen | | | | old file [____________________________________] | | new file [____________________________________] | | | | (o) replace old (o) add after old | | | | [Clear] [Proceed] [Cancel] | |__________________________________________________| ***/ zd = zdialog_new(E2X("Replace Album File"),Mwin,Bclear,Bproceed,Bcancel,0); zdialog_add_widget(zd,"hbox","hbchoose","dialog",0,"space=5"); zdialog_add_widget(zd,"button","choose","hbchoose",E2X("Choose Albums"),"space=5"); zdialog_add_widget(zd,"label","labcount","hbchoose","0 albums chosen"); zdialog_add_widget(zd,"vbox","space","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbold","dialog"); zdialog_add_widget(zd,"label","labold","hbold",E2X("old file"),"space=5"); zdialog_add_widget(zd,"zentry","oldfile","hbold",0,"expand"); zdialog_add_widget(zd,"hbox","hbnew","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labnew","hbnew",E2X("new file"),"space=5"); zdialog_add_widget(zd,"zentry","newfile","hbnew",0,"expand"); zdialog_add_widget(zd,"hbox","hbrep","dialog",0,"space=10"); zdialog_add_widget(zd,"radio","replace","hbrep",E2X("replace old"),"space=5"); zdialog_add_widget(zd,"radio","addafter","hbrep",E2X("add after old"),"space=10"); snprintf(countmess,40,countformat,Nalbums); zdialog_stuff(zd,"labcount",countmess); if (Freplace) { zdialog_stuff(zd,"replace",1); zdialog_stuff(zd,"addafter",0); } else { zdialog_stuff(zd,"replace",0); zdialog_stuff(zd,"addafter",1); } zdialog_resize(zd,500,0); m_viewmode(0,"G"); // gallery view zd_album_update = zd; // set up mouse intercepts zdialog_run(zd,update_albums_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion zd_album_update = 0; // cancel mouse intercepts if (zstat != 2) { zdialog_free(zd); // [cancel] or [x] goto returnx; } zdialog_fetch(zd,"oldfile",oldfile,XFCC); // [proceed] zdialog_fetch(zd,"newfile",newfile,XFCC); zdialog_fetch(zd,"replace",Freplace); zdialog_free(zd); if (stat(oldfile,&statb) || ! S_ISREG(statb.st_mode)) { zmessageACK(Mwin,Bfilenotfound2,oldfile); goto returnx; } if (stat(newfile,&statb) || ! S_ISREG(statb.st_mode)) { zmessageACK(Mwin,Bfilenotfound2,newfile); goto returnx; } if (! Nalbums) { zmessageACK(Mwin,E2X("no albums chosen")); goto returnx; } for (ii = jj = 0; ii < Nalbums; ii++) { pp = strrchr(albumfiles[ii],'/'); // album: album-name if (pp) pp++; else pp = albumfiles[ii]; linedit_open(albumfiles[ii]); while ((pp = linedit_get())) // read all album recs = image files { if (strmatch(pp,oldfile)) // found old file { if (Freplace) { if (++jj == maxalbumfiles) break; linedit_put(newfile); // replace: write new file only } else { if (++jj == maxalbumfiles) break; linedit_put(oldfile); // add after: write both files if (++jj == maxalbumfiles) break; linedit_put(newfile); } } else { if (++jj == maxalbumfiles) break; linedit_put(pp); } } linedit_close(); album_show(albumfiles[ii]); // show updated album } m_update_albums(0,0); // repeat until canceled returnx: Fblock = 0; return; } // dialog event and completion callback function int update_albums_dialog_event(zdialog *zd, cchar *event) { using namespace update_albums_names; char **pp; int ii; if (zd->zstat == 1) { // [clear] zd->zstat = 0; // keep dialog active zdialog_stuff(zd,"oldfile",""); // clear file name inputs zdialog_stuff(zd,"newfile",""); return 1; } if (strmatch(event,"choose")) // [choose] button { for (ii = 0; ii < Nalbums; ii++) // free prior album names, if any zfree(albumfiles[ii]); Nalbums = 0; pp = zgetfiles(E2X("Choose Albums"),MWIN,"files",albums_folder); // choose album files if (pp) { for (ii = 0; ii < 1000 && pp[ii]; ii++) albumfiles[ii] = pp[ii]; Nalbums = ii; // album count selected zfree(pp); } snprintf(countmess,40,countformat,Nalbums); // update dialog album count zdialog_stuff(zd,"labcount",countmess); } return 1; } // mouse click function void update_albums_Lclick_func(int Nth) { using namespace update_albums_names; char *imagefile = 0; char filename[100]; int ftype; zdialog *zd = zd_album_update; if (! zd) return; // should not happen if (Nth < 0) return; imagefile = gallery(0,"get",Nth); // get file at clicked position if (! imagefile) return; ftype = image_file_type(imagefile); // must be image or RAW file if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) { zfree(imagefile); return; } zdialog_fetch(zd,"oldfile",filename,100); // stuff oldfile or newfile if (filename[0] <= ' ') zdialog_stuff(zd,"oldfile",imagefile); // if currently blank else { zdialog_fetch(zd,"newfile",filename,100); if (filename[0] <= ' ') zdialog_stuff(zd,"newfile",imagefile); } zfree(imagefile); return; } /********************************************************************************/ // mass update of album files namespace album_mass_update_names { char massoption = 0; char *albumfiles[maxalbums]; // albums to process char *oldfiles[maxalbumfiles]; // initial album files char *newfiles[maxalbumfiles]; // album files after processing char albumbuff[XFCC]; int Nalbums, Nold, Nnew; // corresp. counts } // menu function void m_album_mass_update(GtkWidget *, cchar *) { using namespace album_mass_update_names; int album_mass_update_dialog_event(zdialog *zd, cchar *event); void album_mass_update_process(); int ii, jj; char *pp, *albumname; FILE *fid; F1_help_topic = "album mass update"; if (FGWM != 'F' && FGWM != 'G') return; if (checkpend("all")) return; // check nothing pending Fblock = 1; /*** ______________________________________________________ | Album Mass Update | | | | [select] N albums selected | | | | Process all album files: | | (o) Replace all with newest version only | | (o) Replace all versions with newest version | | (o) Add newest version to existing versions | | (o) Replace all with original and all versions | | (o) Replace all with original + newest version | // 20.0 | | | Process album files matching selected files: | | (o) Replace all with selected versions | | (o) Replace all versions with selected versions | | (o) Add selected versions to existing versions | | (o) Replace all with original + selected versions | | | | [proceed] [cancel] | |______________________________________________________| ***/ massoption = 0; Nalbums = 0; zdialog *zd = zdialog_new(E2X("Album Mass Update"),Mwin,Bproceed,Bcancel,0); zdialog_add_widget(zd,"hbox","hbsel","dialog"); zdialog_add_widget(zd,"button","select","hbsel",Bselect,"space=3"); zdialog_add_widget(zd,"label","labcount","hbsel","0 albums selected","space=10"); zdialog_add_widget(zd,"vbox","space","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hball","dialog"); zdialog_add_widget(zd,"label","laball","hball",E2X("Process all album files:"),"space=3"); zdialog_add_widget(zd,"vbox","vb1","dialog"); zdialog_add_widget(zd,"hbox","hbA","vb1"); zdialog_add_widget(zd,"check","optA","hbA",E2X("Replace all with newest version only"),"space=12"); zdialog_add_widget(zd,"hbox","hbB","vb1"); zdialog_add_widget(zd,"check","optB","hbB",E2X("Replace all versions with newest version"),"space=12"); zdialog_add_widget(zd,"hbox","hbC","vb1"); zdialog_add_widget(zd,"check","optC","hbC",E2X("Add newest version to existing versions"),"space=12"); zdialog_add_widget(zd,"hbox","hbD","vb1"); zdialog_add_widget(zd,"check","optD","hbD",E2X("Replace all with original and all versions"),"space=12"); zdialog_add_widget(zd,"hbox","hbE","vb1"); zdialog_add_widget(zd,"check","optE","hbE",E2X("Replace all with original + newest version"),"space=12"); zdialog_add_widget(zd,"vbox","space","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbselc","dialog"); zdialog_add_widget(zd,"label","labselc","hbselc",E2X("Process album files matching selected files:"),"space=3"); zdialog_add_widget(zd,"vbox","vb2","dialog"); zdialog_add_widget(zd,"hbox","hbM","vb2"); zdialog_add_widget(zd,"check","optM","hbM",E2X("Replace all with selected versions"),"space=12"); zdialog_add_widget(zd,"hbox","hbN","vb2"); zdialog_add_widget(zd,"check","optN","hbN",E2X("Replace all versions with selected versions"),"space=12"); zdialog_add_widget(zd,"hbox","hbO","vb2"); zdialog_add_widget(zd,"check","optO","hbO",E2X("Add selected versions to existing versions"),"space=12"); zdialog_add_widget(zd,"hbox","hbP","vb2"); zdialog_add_widget(zd,"check","optP","hbP",E2X("Replace all with original + selected versions"),"space=12"); zdialog_run(zd,album_mass_update_dialog_event,"parent"); // run dialog zdialog_wait(zd); // wait for completion if (massoption == 0) goto returnx; // canceled for (ii = 0; ii < Nalbums; ii++) { pp = strrchr(albumfiles[ii],'/'); // album file if (pp) pp++; else pp = albumfiles[ii]; // album name albumname = pp; fid = fopen(albumfiles[ii],"r"); // open/read album file if (! fid) { zmessageACK(Mwin,"%s %s",albumname,strerror(errno)); continue; } for (jj = 0; jj < maxalbumfiles; jj++) // read all contained image files { pp = fgets_trim(albumbuff,XFCC,fid); if (! pp) break; oldfiles[jj] = zstrdup(pp); // save list of files } fclose(fid); if (jj == maxalbumfiles) { zmessageACK(Mwin,Balbumtoobig,maxalbumfiles); continue; } Nold = jj; // initial file count album_mass_update_process(); // oldfiles[Nold] --> newfiles[Nnew] fid = fopen(albumfiles[ii],"w"); // open/write album file if (! fid) { zmessageACK(Mwin,"%s %s",albumname,strerror(errno)); continue; } for (jj = 0; jj < Nnew; jj++) // write all contained image files fprintf(fid,"%s\n",newfiles[jj]); fclose(fid); for (jj = 0; jj < Nold; jj++) // free memory zfree(oldfiles[jj]); for (jj = 0; jj < Nnew; jj++) zfree(newfiles[jj]); } album_show(albumfiles[0]); // show first album processed for (ii = 0; ii < Nalbums; ii++) // free memory zfree(albumfiles[ii]); returnx: Fblock = 0; return; } // dialog event and completion function int album_mass_update_dialog_event(zdialog *zd, cchar *event) { using namespace album_mass_update_names; int ii; char **pp; char countmess[40]; if (zd->zstat) { if (zd->zstat != 1) massoption = 0; zdialog_destroy(zd); return 1; // 19.0 } if strmatch(event,"select") { for (ii = 0; ii < Nalbums; ii++) zfree(albumfiles[ii]); pp = zgetfiles(E2X("Select Albums"),MWIN,"files",albums_folder); // select album files if (pp) { for (ii = 0; ii < maxalbums && pp[ii]; ii++) albumfiles[ii] = pp[ii]; Nalbums = ii; // album count selected zfree(pp); } snprintf(countmess,40,"%d albums selected",Nalbums); // update dialog album count zdialog_stuff(zd,"labcount",countmess); } if (zstrstr("optA optB optC optD optE optM optN optO optP",event)) { zdialog_stuff(zd,"optA",0); zdialog_stuff(zd,"optB",0); zdialog_stuff(zd,"optC",0); zdialog_stuff(zd,"optD",0); zdialog_stuff(zd,"optE",0); zdialog_stuff(zd,"optM",0); zdialog_stuff(zd,"optN",0); zdialog_stuff(zd,"optO",0); zdialog_stuff(zd,"optP",0); zdialog_stuff(zd,event,1); massoption = event[3]; } return 1; } // process the list of album files in oldfiles[Nold] // output list of new album files in newfiles[Nnew] void album_mass_update_process() { using namespace album_mass_update_names; int ii, ii1, ii2, jj, kk, ll, nf, nc; int Nselect = 0; FILE *fid; char selcomm[AFCC], albumbuff[XFCC]; char *rootname, *basename, *pp; char *selectfiles[maxalbumfiles]; char **fileversions, *ppselect[100]; if (massoption >= 'M') // if 'selected files' option { snprintf(selcomm,AFCC,"%s/%s",albums_folder,"selected files"); // get selected files in memory 20.0 fid = fopen(selcomm,"r"); if (fid) { while ((pp = fgets_trim(albumbuff,XFCC,fid))) { ii = Nselect++; selectfiles[ii] = zstrdup(pp); if (Nselect == maxalbumfiles) break; } fclose(fid); if (Nselect == maxalbumfiles) { zmessageACK(Mwin,E2X("too many selected files: %d"),Nselect); for (ii = 0; ii < Nselect; ii++) zfree(selectfiles[ii]); return; } } } ii = 0; // input oldfiles index kk = 0; // output newfiles index while (ii < Nold) // loop all old files { if (kk > maxalbumfiles-100) { zmessageACK(Mwin,Balbumtoobig,maxalbumfiles); // stop with sufficient margin break; } rootname = file_rootname(oldfiles[ii]); // get root name for group /.../filename if (! rootname) continue; // no files found for group basename = file_basename(oldfiles[ii]); // get base name for group /.../filename.ext // (null if only versions are in group) ii1 = ii; for (ii2 = ii+1; ii2 < Nold; ii2++) // range of oldfiles group, ii1 to ii2 if (! strmatch(rootname,file_rootname(oldfiles[ii2]))) break; ii = ii2--; // leave ii at start of next group if (massoption == 'A') // Replace all files with newest version only { pp = file_newest_version(oldfiles[ii1]); if (pp) newfiles[kk++] = zstrdup(pp); // add newest version only to newfiles continue; } if (massoption == 'B') // Replace all versions with newest version { if (basename) { for (jj = ii1; jj <= ii2; jj++) // oldfiles includes basename? if (strmatch(basename,oldfiles[jj])) break; if (jj <= ii2) newfiles[kk++] = zstrdup(basename); // yes, add basename to newfiles } pp = file_newest_version(oldfiles[ii1]); // newest version if (! pp) continue; // none if (basename && strmatch(pp,basename)) continue; // if same as basename, skip newfiles[kk++] = zstrdup(pp); // add to newfiles continue; } if (massoption == 'C') // Add newest version to existing versions { for (jj = ii1; jj <= ii2; jj++) // add oldfiles to newfiles newfiles[kk++] = zstrdup(oldfiles[jj]); pp = file_newest_version(oldfiles[ii1]); // newest version if (! pp) continue; // none for (jj = ii1; jj <= ii2; jj++) // already included in oldfiles? if (strmatch(pp,oldfiles[jj])) break; if (jj > ii2) newfiles[kk++] = zstrdup(pp); // no, add to newfiles continue; } if (massoption == 'D') // Replace all with original + all versions { fileversions = file_all_versions(oldfiles[ii1],nf); // get basename and all versions that exist if (! nf) continue; for (jj = 0; jj < nf; jj++) // add all file versions to newfiles newfiles[kk++] = fileversions[jj]; zfree(fileversions); // free memory continue; } if (massoption == 'E') // Replace all with original + newest version { // 20.0 if (basename) newfiles[kk++] = zstrdup(basename); // add basename to newfiles pp = file_newest_version(oldfiles[ii1]); if (pp && basename && strmatch(pp,basename)) continue; // add newest version to newfiles newfiles[kk++] = zstrdup(pp); // if not same as basename continue; } if (massoption < 'M') continue; // not a selected files option, stop here for (nc = ll = 0; ll < Nselect && nc < 100; ll++) // find matching select files if (strmatch(rootname,file_rootname(selectfiles[ll]))) ppselect[nc++] = selectfiles[ll]; if (nc == 0) { for (jj = ii1; jj <= ii2; jj++) // none, add all oldfiles to newfiles newfiles[kk++] = zstrdup(oldfiles[jj]); continue; } if (massoption == 'M') // Replace all with selected versions { for (ll = 0; ll < nc; ll++) // loop selected files newfiles[kk++] = zstrdup(ppselect[ll]); // add selected files to newfiles continue; } if (massoption == 'N') // Replace all versions with selected versions { if (basename) { for (jj = ii1; jj <= ii2; jj++) // oldfiles includes basename? if (strmatch(basename,oldfiles[jj])) break; if (jj <= ii2) newfiles[kk++] = zstrdup(basename); // yes, add basename to newfiles } for (ll = 0; ll < nc; ll++) { // loop selected files if (! basename || ! strmatch(basename,ppselect[ll])) // if selected file not basename newfiles[kk++] = zstrdup(ppselect[ll]); // add selected file to newfiles } continue; } if (massoption == 'O') // Add selected versions to existing versions { for (jj = ii1; jj <= ii2; jj++) // add all oldfiles to newfiles newfiles[kk++] = zstrdup(oldfiles[jj]); for (ll = 0; ll < nc; ll++) { // loop selected files for (jj = ii1; jj <= ii2; jj++) // loop oldfiles if (strmatch(oldfiles[jj],ppselect[ll])) break; // is selected file included in oldfiles? if (jj > ii2) newfiles[kk++] = zstrdup(ppselect[ll]); // no, add selected file to newfiles } continue; } if (massoption == 'P') // Replace all with original + selected vers. { if (basename) newfiles[kk++] = zstrdup(basename); // add basename to newfiles for (ll = 0; ll < nc; ll++) { // loop selected files if (! basename || ! strmatch(basename,ppselect[ll])) // if selected file not basename newfiles[kk++] = zstrdup(ppselect[ll]); // add selected file to newfiles } continue; } } Nnew = kk; for (ii = 0; ii < Nselect; ii++) zfree(selectfiles[ii]); return; } /********************************************************************************/ // Fix albums when image files have been renamed or moved. // inputs: a list of old filenames and corresponding new filenames. // used by m_batch_convert(). void album_batch_rename(char **oldfiles, char **newfiles, int nfiles) { char **flist, *pp; char *albumfiles[maxalbums]; char findcomm[200]; int ii, jj, err; int NF; snprintf(findcomm,200,"%s/*",albums_folder); // find all album files zfind(findcomm,flist,NF); for (ii = jj = 0; ii < NF && jj < maxalbums; ii++) { pp = strrchr(flist[ii],'/'); if (! pp) continue; pp++; if (*pp == '.') continue; albumfiles[jj] = zstrdup(flist[ii]); jj++; } for (ii = 0; ii < NF; ii++) zfree(flist[ii]); zfree(flist); NF = jj; if (! NF) return; if (NF == maxalbums) zmessageACK(Mwin,"%d albums limit reached",maxalbums); for (ii = 0; ii < NF; ii++) // loop all albums { linedit_open(albumfiles[ii]); while ((pp = linedit_get())) // read all album recs = image files { for (jj = 0; jj < nfiles; jj++) // list of renamed/moved files if (strmatch(pp,oldfiles[jj])) break; // includes this file? if (jj == nfiles) linedit_put(pp); // no, copy old filespec else linedit_put(newfiles[jj]); // yes, copy corresp. new filespec } err = linedit_close(); if (err) goto error; } goto cleanup; error: zmessageACK(Mwin,"%s \n %s",albumfiles[ii],strerror(errno)); cleanup: for (ii = 0; ii < NF; ii++) zfree(albumfiles[ii]); return; } /********************************************************************************/ // make current gallery into an album with chosen or given name void m_gallery2album(GtkWidget *, cchar *) // 20.0 { char **flist, *pp; int yn, ii, NF, zstat, err; char findcomm[200], albumname[100], albumfile[200]; STATB statb; /*** ________________________________ | Save Gallery as Album | | | | Album Name [______________|v| | | | | [Done] | |________________________________| ***/ F1_help_topic = "gallery to album"; zdialog *zd = zdialog_new(E2X("Save Gallery as Album"),Mwin,Bdone,null); zdialog_add_widget(zd,"hbox","hbname","dialog"); zdialog_add_widget(zd,"label","labname","hbname",E2X("Album Name"),"space=5"); zdialog_add_widget(zd,"comboE","albumname","hbname",0,"space=5"); snprintf(findcomm,200,"%s/*",albums_folder); // find all album files 20.0 zfind(findcomm,flist,NF); for (ii = 0; ii < NF && ii < 100; ii++) // add to combo box { pp = strrchr(flist[ii],'/'); if (! pp) continue; pp++; if (*pp == '.') continue; zdialog_cb_app(zd,"albumname",pp); } for (int ii = 0; ii < NF; ii++) zfree(flist[ii]); zfree(flist); zdialog_stuff(zd,"albumname","gallery"); // default name "gallery" 20.0 zdialog_run(zd,0,"save"); // run dialog zstat = zdialog_wait(zd); zdialog_fetch(zd,"albumname",albumname,100); // get album name zdialog_free(zd); if (zstat != 1) return; // user cancel if (*albumname <= ' ') return; snprintf(albumfile,200,"%s/%s",albums_folder,albumname); // make album file name err = stat(albumfile,&statb); if (! err) { // already exists yn = zmessageYN(Mwin,E2X("replace album %s ?"),albumname); if (! yn) return; } album_create_from_gallery(albumfile); // create album album_show(albumfile); return; } /********************************************************************************/ // slide show function int ss_dialog_event(zdialog *zd, cchar *event); // main dialog void ss_KBprefs_dialog(); // edit KB control key preferences void ss_transprefs_dialog(); // edit transition preferences void ss_imageprefs_dialog(); // edit image preferences void ss_loadprefs(); // load preferences from file void ss_saveprefs(); // write preferences to file int ss_timerfunc(void *); // timer function int ss_nextrans(); // select next transition to use void ss_blankwindow(); // blank the window int ss_showcapcom(int, int, int); // show filename/captions/comments 20.0 PXB *ss_loadpxb(char *file); // load image as PXB pixmap PXB *ss_zoom_posn(char *file, int mode, float zoom); // position zoomed image in PXB 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_spiral(); void ss_ellipse(); void ss_raindrops(); void ss_doubledoor(); void ss_rotate(); void ss_fallover(); void ss_spheroid(); void ss_turnpage(); void ss_frenchdoor(); void ss_turncube(); void ss_windmill(); void ss_pixelize(); void ss_twist(); void ss_Xopen(); void ss_squish(); void ss_disintegrate(); void ss_interleave(); void ss_zoom_setup(char *file); void ss_zoom_start(float zoom); PXB * ss_zoom_wait(); void ss_zoomin(); void ss_zoomout(); char *ss_albumfile = 0; // slide show album file char *ss_albumname = 0; // album name (ss_albumfile tail) int ss_Nfiles = 0; // album file count int ss_imagetime = 0; // image display default time int ss_cctime = 0; // filename/caption/comments show time int ss_cliplimit = 10; // image clipping limit from user char ss_musicfile[500] = "none"; // /folder.../musicfile.ogg int ss_random = 0; // use random transitions option from user int ss_fullscreen = 0; // flag, full screen mode int ss_replay = 0; // flag, start over when done float ss_trantime = 2.0; // transition time, seconds float ss_zoomsize = 1.0; // zoom size, 1-3.0 = 3x int ss_zoomtype = 0; // 0/1/2 = none/zoomin/zoomout int ss_zoomtime = 2; // zoom time, seconds int ss_zoomlocx, ss_zoomlocy; // zoom target (50/50 = image midpoint) int ss_setzloc; // 1-shot flag for image prefs dialog int ss_ww, ss_hh, ss_rs; // slide show image size, rowstride char *ss_oldfile, *ss_newfile; // image files for transition PXB *ss_pxbold, *ss_pxbnew; // PXB pixmap images: old, new double ss_timer = 0; // slide show timer cchar *ss_event; // slide show event int ss_paused; // slide show paused status int ss_Fcurrent = 0; // current image file int ss_isblank = 0; // window is blank int ss_nwt; // threads for transition funcs char ss_KBkeyB; // KB key: blank screen char ss_KBkeyN; // KB key: transition to next image char ss_KBkeyP; // KB key: pause /resume char ss_KBkeyX; // KB key: magnify image #define SSNT 28 // slide show transition types #define SSMAXI 10000 // max. slide show images struct ss_trantab_t { // transition table char tranname[32]; // transition name int enabled; // enabled or not float trantime; // duration, seconds int preference; // relative preference, 0-99 void (*func)(); // function to perform transition }; ss_trantab_t ss_trantab[SSNT]; // specific slide show transition prefs int ss_Tused[SSNT]; // list of transition types enabled int ss_Tlast[SSNT]; // last transitions used, in order int ss_Nused; // count of enabled transitions, 0-SSNT int ss_Tnext; // next transition to use >> last one used ss_trantab_t ss_trantab_default[SSNT] = // transition defaults { // name enab time pref function // (enabled, trantime, preference) { "instant", 1, 0.0, 10, ss_instant }, { "fade-in", 1, 2.0, 10, ss_fadein }, { "roll-right", 1, 2.0, 10, ss_rollright }, // NO BLANKS IN TRANSITION NAMES { "roll-down", 1, 2.0, 10, ss_rolldown }, { "venetian", 1, 2.0, 10, ss_venetian }, { "grate", 1, 2.0, 10, ss_grate }, { "rectangle", 1, 2.0, 10, ss_rectangle }, { "implode", 1, 2.0, 10, ss_implode }, { "explode", 1, 2.0, 10, ss_explode }, { "radar", 1, 2.0, 10, ss_radar }, { "Japan-fan", 1, 2.0, 10, ss_japfan }, { "Spiral", 1, 2.0, 10, ss_spiral }, { "ellipse", 1, 2.0, 10, ss_ellipse }, { "raindrops", 1, 2.0, 10, ss_raindrops }, { "doubledoor", 1, 2.0, 10, ss_doubledoor }, { "rotate", 1, 2.0, 10, ss_rotate }, { "fallover", 1, 2.0, 10, ss_fallover }, { "spheroid", 1, 2.0, 10, ss_spheroid }, { "turn-page", 1, 2.0, 10, ss_turnpage }, { "french-door", 1, 2.0, 10, ss_frenchdoor }, { "turn-cube", 1, 2.0, 10, ss_turncube }, { "windmill", 1, 2.0, 10, ss_windmill }, { "pixelize", 1, 2.0, 10, ss_pixelize }, { "twist", 1, 2.0, 10, ss_twist }, { "Xopen", 1, 2.0, 10, ss_Xopen }, { "squish", 1, 2.0, 10, ss_squish }, { "disintegrate", 1, 2.0, 10, ss_disintegrate }, { "interleave", 1, 2.0, 10, ss_interleave } }; struct ss_imagetab_t { // image table char *imagefile; // image file int tone; // flag, play tone when shown int wait0; // seconds to wait int filesecs; // seconds to show file name 20.0 int capsecs; // seconds to show caption int commsecs; // seconds to show comments int wait1; // seconds to wait int zoomtype; // 0/1/2 = none/zoomin/zoomout float zoomsize; // zoom size, 1-3.0 = 3x int zoomtime; // zoom time, seconds int zoomlocx, zoomlocy; // zoom target (50/50 = image midpoint) int wait2; // seconds to wait char tranname[32]; // transition type to use }; ss_imagetab_t ss_imagetab[SSMAXI]; // specific slide show image table // menu function - start or stop a slide show void m_slideshow(GtkWidget *, cchar *) // overhauled { zdialog *zd; int zstat, ii; char *pp; E2X("instant"); // add translations to .po file E2X("fade-in"); E2X("roll-right"); E2X("roll-down"); E2X("venetian"); E2X("grate"); E2X("rectangle"); E2X("implode"); E2X("explode"); E2X("radar"); E2X("Japan-fan"); E2X("spiral"); E2X("ellipse"); E2X("raindrops"); E2X("doubledoor"); E2X("rotate"); E2X("fallover"); E2X("spheroid"); E2X("turn-page"); E2X("french-door"); E2X("turn-cube"); E2X("windmill"); E2X("pixelize"); E2X("twist"); E2X("Xopen"); E2X("squish"); E2X("disintegrate"); E2X("interleave"); F1_help_topic = "slide show"; if (checkpend("all")) return; // check nothing pending if (FGWM != 'F' && FGWM != 'G') return; // 19.0 ss_nwt = NWT - 2; // leave 2 SMPs free 20.0 if (ss_nwt < 1) ss_nwt = 1; if (ss_nwt > 4) ss_nwt = 4; // more than 4 not helpful ss_KBkeyB = ss_KBkeys[0]; // unpack KB control keys ss_KBkeyN = ss_KBkeys[1]; ss_KBkeyP = ss_KBkeys[2]; ss_KBkeyX = ss_KBkeys[3]; /*** ___________________________________________________________ | Slide Show | | | | [Select Album] album-name 123 images | | Caption Time [___] Image Time [___] Clip Limit % [___] | | Music File: [__________________________________] [Browse] | | [x] Full Screen [x] Auto-replay | | Customize: [transitions] [image files] [KB controls] | // KB functions | | | [Proceed] [Cancel] | |___________________________________________________________| ***/ zd = zdialog_new(E2X("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",E2X("Select Album"),"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","labcctime","hbprefs",E2X("Caption Time"),"space=5"); zdialog_add_widget(zd,"zspin","cctime","hbprefs","0|9999|1|0"); zdialog_add_widget(zd,"label","space","hbprefs",0,"space=5"); zdialog_add_widget(zd,"label","labimagetime","hbprefs",E2X("Image Time"),"space=5"); zdialog_add_widget(zd,"zspin","imagetime","hbprefs","1|9999|1|3"); zdialog_add_widget(zd,"label","space","hbprefs",0,"space=5"); zdialog_add_widget(zd,"label","labclip","hbprefs",E2X("Clip Limit %"),"space=5"); zdialog_add_widget(zd,"zspin","cliplim","hbprefs","0|50|1|0"); zdialog_add_widget(zd,"hbox","hbmuf","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labmf","hbmuf",E2X("Music File"),"space=3"); zdialog_add_widget(zd,"zentry","musicfile","hbmuf","none","size=30|space=5"); zdialog_add_widget(zd,"button","browse","hbmuf",Bbrowse,"space=2"); zdialog_add_widget(zd,"hbox","hbscreen","dialog",0,"space=2"); zdialog_add_widget(zd,"check","fullscreen","hbscreen",E2X("Full Screen"),"space=3"); zdialog_add_widget(zd,"check","replay","hbscreen",E2X("Auto-replay"),"space=5"); zdialog_add_widget(zd,"hbox","hbcust","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labprefs","hbcust",E2X("Customize:"),"space=5"); zdialog_add_widget(zd,"button","transprefs","hbcust",E2X("transitions"),"space=5"); zdialog_add_widget(zd,"button","imageprefs","hbcust",E2X("image files"),"space=5"); zdialog_add_widget(zd,"button","KBprefs","hbcust",E2X("KB controls"),"space=5"); zdialog_run(zd,ss_dialog_event,"save"); // run dialog zdialog_send_event(zd,"initz_album"); // initial album 20.0 zstat = zdialog_wait(zd); // wait for completion if (zstat != 1) { // cancel zdialog_free(zd); Fblock = 0; return; } if (! ss_Nfiles) { // no selection zmessageACK(Mwin,E2X("invalid album")); zdialog_free(zd); Fblock = 0; return; } zdialog_fetch(zd,"imagetime",ss_imagetime); // image show time seconds if (ss_Nused == 0) ss_imagetime = 9999; // if only arrow-keys used, huge interval zdialog_fetch(zd,"cctime",ss_cctime); // filename/caption/comments show time zdialog_fetch(zd,"cliplim",ss_cliplimit); // image clipping limit zdialog_fetch(zd,"musicfile",ss_musicfile,500); // music file zdialog_fetch(zd,"fullscreen",ss_fullscreen); // full screen option zdialog_fetch(zd,"replay",ss_replay); // replay option (last --> first image) zdialog_free(zd); // kill dialog ss_saveprefs(); // save preference changes if (curr_file) { // start at curr. file for (ii = 0; ii < ss_Nfiles; ii++) // if member of file list if (strmatch(curr_file,ss_imagetab[ii].imagefile)) break; if (ii == ss_Nfiles) ii = 0; } else ii = 0; // else first image ss_Fcurrent = ii; // next file in list to show f_open(ss_imagetab[ii].imagefile); m_viewmode(0,"F"); // insure tab F Fblock = 1; // stop edits etc. Fslideshow = 1; // slideshow active for KB events ss_isblank = 0; // not blank window ss_event = "first"; Fzoom = 0; // fit window if (*ss_musicfile == '/') { pp = zescape_quotes(ss_musicfile); shell_ack("paplay \"%s\" &",pp); zfree(pp); } ss_newfile = 0; // no new image ss_pxbnew = 0; ss_oldfile = 0; // no old (prior) image ss_pxbold = 0; if (ss_fullscreen) win_fullscreen(1); // full screen, hide menu and panel zmainsleep(0.5); ss_ww = gdk_window_get_width(gdkwin); // window size ss_hh = gdk_window_get_height(gdkwin); g_timeout_add(100,ss_timerfunc,0); // start timer for image changes return; } // dialog event function - file chooser for images to show and music file int ss_dialog_event(zdialog *zd, cchar *event) { char *file, *pp; char countmess[50]; int err; if (zd->zstat == 1) { // [proceed] if (ss_Nfiles) return 1; zmessageACK(Mwin,E2X("invalid album")); // diagnose and keep dialog open zd->zstat = 0; return 1; } if (zd->zstat) return 1; // cancel or [x] if (strmatch(event,"initz_album")) { if (! ss_albumfile && curr_album) ss_albumfile = zstrdup(curr_album); if (! ss_albumfile) return 1; pp = strrchr(ss_albumfile,'/'); if (! pp) return 1; ss_albumname = pp + 1; if (strmatch(ss_albumname,"gallery")) { err = album_create_from_gallery(ss_albumfile); if (err) return 1; } goto initz_album; } if (strmatch(event,"selectalbum")) // select a slide show album { ss_Nfiles = 0; // reset album data ss_albumfile = ss_albumname = 0; zdialog_stuff(zd,"albumname",Bnoselection); zdialog_stuff(zd,"nfiles",Bnoimages); file = zgetfile(E2X("open album"),MWIN,"file",albums_folder); // user file selection if (! file) return 1; ss_albumfile = file; pp = strrchr(ss_albumfile,'/'); // get album name if (! pp) return 1; ss_albumname = pp + 1; goto initz_album; } if (strmatch(event,"browse")) { // browse for music file pp = ss_musicfile; pp = zgetfile(E2X("Select music file"),MWIN,"file",pp); if (! pp) pp = zstrdup("none"); zdialog_stuff(zd,"musicfile",pp); zfree(pp); } if (strmatch(event,"KBprefs")) ss_KBprefs_dialog(); if (! ss_Nfiles) return 1; if (strmatch(event,"transprefs")) { // edit transition preferences zdialog_show(zd,0); ss_transprefs_dialog(); zdialog_show(zd,1); } if (strmatch(event,"imageprefs")) { // edit image preferences zdialog_show(zd,0); ss_imageprefs_dialog(); zdialog_show(zd,1); } return 1; initz_album: pp = strrchr(ss_albumfile,'/'); if (! pp) return 1; ss_albumname = pp + 1; 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,E2X("%d images"),ss_Nfiles); zdialog_stuff(zd,"nfiles",countmess); zdialog_stuff(zd,"imagetime",ss_imagetime); zdialog_stuff(zd,"cctime",ss_cctime); zdialog_stuff(zd,"cliplim",ss_cliplimit); zdialog_stuff(zd,"musicfile",ss_musicfile); zdialog_stuff(zd,"fullscreen",ss_fullscreen); zdialog_stuff(zd,"replay",ss_replay); album_show(ss_albumfile); // open slide show album 19.0 return 1; } // ------------------------------------------------------------------------------ // set preferences for keyboard control keys (blank screen, next image, pause/resume, magnify) void ss_KBprefs_dialog() { int KBprefs_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int zstat; char keyx[4] = "X"; cchar *tip = E2X("arrow keys show previous or next image instantly \n" "space bar (blank) is allowed and shows as '-'"); /*** ___________________________________________________ | Keyboard Preferences | | | | [ B ] blank or unblank window | | [ N ] show next image, with transition | | [ P ] pause or resume slide show | | [ X ] magnify image (loupe tool) | | | | arrow keys show previous or next image instantly | | | | [done] [camcel] | |___________________________________________________| ***/ zd = zdialog_new(E2X("Keyboard Preferences"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbB","dialog"); zdialog_add_widget(zd,"zentry","B","hbB","B","space=5|size=2"); zdialog_add_widget(zd,"label","labB","hbB",E2X("blank or unblank window")); zdialog_add_widget(zd,"hbox","hbN","dialog"); zdialog_add_widget(zd,"zentry","N","hbN","N","space=5|size=2"); zdialog_add_widget(zd,"label","labN","hbN",E2X("show next image, with transition")); zdialog_add_widget(zd,"hbox","hbP","dialog"); zdialog_add_widget(zd,"zentry","P","hbP","P","space=5|size=2"); zdialog_add_widget(zd,"label","labP","hbP",E2X("pause or resume slide show")); zdialog_add_widget(zd,"hbox","hbX","dialog"); zdialog_add_widget(zd,"zentry","X","hbX","X","space=5|size=2"); zdialog_add_widget(zd,"label","labX","hbX",E2X("magnify image (loupe tool)")); zdialog_add_widget(zd,"label","labak","dialog",tip,"space=5"); keyx[0] = ss_KBkeyB; if (*keyx == ' ') *keyx = '-'; // space bar >> '-' zdialog_stuff(zd,"B",keyx); keyx[0] = ss_KBkeyN; if (*keyx == ' ') *keyx = '-'; zdialog_stuff(zd,"N",keyx); keyx[0] = ss_KBkeyP; if (*keyx == ' ') *keyx = '-'; zdialog_stuff(zd,"P",keyx); keyx[0] = ss_KBkeyX; if (*keyx == ' ') *keyx = '-'; zdialog_stuff(zd,"X",keyx); zdialog_run(zd,KBprefs_dialog_event,"parent"); // run, wait for completion zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); return; } zdialog_fetch(zd,"B",keyx,4); if (*keyx == '-') *keyx = ' '; // '-' >> space ss_KBkeyB = ss_KBkeys[0] = keyx[0]; zdialog_fetch(zd,"N",keyx,4); if (*keyx == '-') *keyx = ' '; ss_KBkeyN = ss_KBkeys[1] = keyx[0]; zdialog_fetch(zd,"P",keyx,4); if (*keyx == '-') *keyx = ' '; ss_KBkeyP = ss_KBkeys[2] = keyx[0]; zdialog_fetch(zd,"X",keyx,4); if (*keyx == '-') *keyx = ' '; ss_KBkeyX = ss_KBkeys[3] = keyx[0]; save_params(); return; } // dialog event and completion function int KBprefs_dialog_event(zdialog *zd, cchar *event) { char keyx[4]; if (zd->zstat) { zdialog_destroy(zd); return 1; } if (zstrstr("B N P X",event)) { zdialog_fetch(zd,event,keyx,2); if (*keyx == ' ') *keyx = '-'; // convert space to '-' else keyx[0] = toupper(keyx[0]); keyx[1] = 0; zdialog_stuff(zd,event,keyx); } return 1; } // ------------------------------------------------------------------------------ // set transitions preferences for specific slide show void ss_transprefs_dialog() { int transprefs_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int ii, jj, zstat; char nameii[32], enabii[8], timeii[8], prefii[8]; /*** __________________________________________________________________________ | | | Transitions File [load] [save] transition filename | | | | Select [all] [none] [x] random time [___] [set all] | | | | transition use time pref | transition use time pref | | instant [ ] [ 1.0 ] [ 10 ] | raindrops [x] [ 1.3 ] [ 10 ] | | fade-in [x] [ 1.5 ] [ 20 ] | doubledoor [x] [ 0.8 ] [ 20 ] | | roll-right [x] [ 2.0 ] [ 0 ] | rotate [ ] [ 1.4 ] [ 50 ] | | .... ... ..... .... | .... ... ..... .... | | | | [done] [cancel] | |__________________________________________________________________________| ***/ if (! ss_Nfiles) { zmessageACK(Mwin,E2X("invalid album")); return; } zd = zdialog_new(E2X("Transition Preferences"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbfile","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labfile","hbfile",E2X("Transitions File"),"space=3"); zdialog_add_widget(zd,"button","load","hbfile",Bload,"space=3"); zdialog_add_widget(zd,"button","save","hbfile",Bsave,"space=3"); zdialog_add_widget(zd,"label","tranfile","hbfile",0,"space=5"); zdialog_add_widget(zd,"hbox","hbopts","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labtran","hbopts",Bselect,"space=3"); zdialog_add_widget(zd,"button","all","hbopts",Ball,"space=3"); zdialog_add_widget(zd,"button","none","hbopts",Bnone,"space=3"); zdialog_add_widget(zd,"check","rand","hbopts",E2X("random"),"space=3"); zdialog_add_widget(zd,"label","space","hbopts","","space=10"); zdialog_add_widget(zd,"label","labtime","hbopts",E2X("time"),"space=3"); zdialog_add_widget(zd,"zspin","alltime","hbopts","0.1|10|0.1|2","size=4"); zdialog_add_widget(zd,"button","setalltime","hbopts",E2X("set all")); zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb3","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb4","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vsep","vs1","hb1",0,"space=5"); // two columns of transition poop zdialog_add_widget(zd,"vbox","vb5","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb6","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb7","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb8","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"label","labname","vb1",E2X("transition")); zdialog_add_widget(zd,"label","labenab","vb2",E2X("use")); zdialog_add_widget(zd,"label","labtime","vb3",E2X("time")); zdialog_add_widget(zd,"label","labpref","vb4",E2X("pref")); zdialog_add_widget(zd,"label","labname","vb5",E2X("transition")); zdialog_add_widget(zd,"label","labenab","vb6",E2X("use")); zdialog_add_widget(zd,"label","labtime","vb7",E2X("time")); zdialog_add_widget(zd,"label","labpref","vb8",E2X("pref")); zdialog_stuff(zd,"rand",ss_random); // stuff random checkbox for (ii = 0; ii < SSNT; ii++) { // build input dialog for transition prefs snprintf(nameii,32,"name_%d",ii); snprintf(enabii,8,"enab_%d",ii); snprintf(timeii,8,"time_%d",ii); snprintf(prefii,8,"pref_%d",ii); if (ii < SSNT / 2) { zdialog_add_widget(zd,"label",nameii,"vb1","transition"); zdialog_add_widget(zd,"check",enabii,"vb2"); zdialog_add_widget(zd,"zspin",timeii,"vb3","0.1|10|0.1|2","size=4"); zdialog_add_widget(zd,"zspin",prefii,"vb4","0|99|1|10","size=3"); } else { zdialog_add_widget(zd,"label",nameii,"vb5","transition"); zdialog_add_widget(zd,"check",enabii,"vb6"); zdialog_add_widget(zd,"zspin",timeii,"vb7","0.1|10|0.1|2","size=4"); zdialog_add_widget(zd,"zspin",prefii,"vb8","0|99|1|10","size=3"); } zdialog_stuff(zd,nameii,E2X(ss_trantab[ii].tranname)); // stuff current transition prefs zdialog_stuff(zd,enabii,ss_trantab[ii].enabled); zdialog_stuff(zd,timeii,ss_trantab[ii].trantime); zdialog_stuff(zd,prefii,ss_trantab[ii].preference); } zdialog_run(zd,transprefs_dialog_event,"parent"); // run dialog, wait for completion zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); return; } zdialog_fetch(zd,"rand",ss_random); // get mode, 0/1 = sequential/random for (ii = 0; ii < SSNT; ii++) { // get all preferences snprintf(enabii,8,"enab_%d",ii); snprintf(timeii,8,"time_%d",ii); snprintf(prefii,8,"pref_%d",ii); zdialog_fetch(zd,enabii,ss_trantab[ii].enabled); zdialog_fetch(zd,timeii,ss_trantab[ii].trantime); zdialog_fetch(zd,prefii,ss_trantab[ii].preference); } zdialog_free(zd); ss_saveprefs(); // update preferences file for (ii = jj = 0; ii < SSNT; ii++) { // initialize list of enabled if (ss_trantab[ii].enabled) { // and last used transition types ss_Tused[jj] = ii; jj++; } ss_Tlast[ii] = 0; } ss_Nused = jj; // no. enabled transition types ss_Tnext = 0; // next one to use (first) return; } // transition prefs dialog event and completion function int transprefs_dialog_event(zdialog *zd, cchar *event) { int transprefs_load(FILE *fid); int transprefs_save(FILE *fid); FILE *fid; int err, ii; float time; char *file, *pp; char nameii[32], enabii[8], timeii[8], prefii[8]; if (strmatch(event,"all")) // enable all transitions { for (ii = 0; ii < SSNT; ii++) { snprintf(enabii,8,"enab_%d",ii); zdialog_stuff(zd,enabii,1); } } if (strmatch(event,"none")) // disable all transitions { for (ii = 0; ii < SSNT; ii++) { snprintf(enabii,8,"enab_%d",ii); zdialog_stuff(zd,enabii,0); } } if (strmatch(event,"load")) // load trans prefs from a file { file = zgetfile("load",MWIN,"file",slideshow_trans_folder,0); // open trans prefs file if (! file) return 1; fid = fopen(file,"r"); if (! fid) { zmessageACK(Mwin,E2X("invalid file")); return 1; } err = transprefs_load(fid); // load file into dialog fclose(fid); if (err) return 1; pp = strrchr(file,'/'); // show file name in dialog zdialog_stuff(zd,"tranfile",pp+1); zdialog_stuff(zd,"rand",ss_random); // stuff random/sequential mode for (ii = 0; ii < SSNT; ii++) { // build input dialog for transition prefs snprintf(nameii,32,"name_%d",ii); snprintf(enabii,8,"enab_%d",ii); snprintf(timeii,8,"time_%d",ii); snprintf(prefii,8,"pref_%d",ii); zdialog_stuff(zd,nameii,E2X(ss_trantab[ii].tranname)); // stuff current transition prefs zdialog_stuff(zd,enabii,ss_trantab[ii].enabled); zdialog_stuff(zd,timeii,ss_trantab[ii].trantime); zdialog_stuff(zd,prefii,ss_trantab[ii].preference); } } if (strmatch(event,"save")) // save trans prefs to a file { zdialog_fetch(zd,"rand",ss_random); // get random/sequential mode for (ii = 0; ii < SSNT; ii++) { // get all preferences snprintf(enabii,8,"enab_%d",ii); snprintf(timeii,8,"time_%d",ii); snprintf(prefii,8,"pref_%d",ii); zdialog_fetch(zd,enabii,ss_trantab[ii].enabled); zdialog_fetch(zd,timeii,ss_trantab[ii].trantime); zdialog_fetch(zd,prefii,ss_trantab[ii].preference); } file = zgetfile("save",MWIN,"save",slideshow_trans_folder,0); // save trans prefs file if (! file) return 1; fid = fopen(file,"w"); if (! fid) { zmessageACK(Mwin,E2X("invalid file")); return 1; } transprefs_save(fid); // save dialog to file fclose(fid); pp = strrchr(file,'/'); // show file name in dialog zdialog_stuff(zd,"tranfile",pp+1); } if (strmatch(event,"setalltime")) // set all transition times { zdialog_fetch(zd,"alltime",time); for (ii = 0; ii < SSNT; ii++) { snprintf(timeii,8,"time_%d",ii); zdialog_stuff(zd,timeii,time); } } return 1; } // load transition prefs from a file // returns 0 = OK, +N = error int transprefs_load(FILE *fid) { char *pp, buff[XFCC]; int ii, jj, nn; char tranname[32]; int n1, n2; float ff; pp = fgets_trim(buff,XFCC,fid,1); if (! pp) goto format_error; nn = sscanf(buff,"random %d ",&ss_random); if (nn != 1) goto format_error; while (true) { pp = fgets_trim(buff,XFCC,fid,1); if (! pp) break; nn = sscanf(buff,"%s %d %f %d ",tranname,&n1,&ff,&n2); // tranname N N.N NN if (nn != 4) { // (enabled 0-1 duration N.N pref. 0-99) printz("bad record: %s \n",buff); continue; } for (ii = 0; ii < SSNT; ii++) if (strmatch(tranname,ss_trantab[ii].tranname)) break; if (ii == SSNT) { printz("bad record: %s \n",buff); continue; } ss_trantab[ii].enabled = n1; ss_trantab[ii].trantime = ff; ss_trantab[ii].preference = n2; } for (ii = jj = 0; ii < SSNT; ii++) { // initialize list of enabled if (ss_trantab[ii].enabled) { // transition types ss_Tused[jj] = ii; jj++; } } ss_Nused = jj; // no. enabled transition types return 0; format_error: zmessageACK(Mwin,E2X("file format error: \n %s"),buff); return 1; } // save transition prefs to a file int transprefs_save(FILE *fid) { fprintf(fid,"random %d \n",ss_random); for (int ii = 0; ii < SSNT; ii++) fprintf(fid,"%s %d %.1f %d \n", ss_trantab[ii].tranname, ss_trantab[ii].enabled, ss_trantab[ii].trantime, ss_trantab[ii].preference); return 0; } // ------------------------------------------------------------------------------ // set image preferences for specific slide show void ss_imageprefs_dialog() { int ss_imageprefs_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int ii, kk; char *pp, zoomloc[40]; /*** __________________________________________________ | Image Preferences | | | | Image File: [________________________________] | | | | Action seconds | | --------------------------------------------- | | Play tone when image shows [x] | | Wait before filename/caption/comments [1.5] | | Show image filename (overlap) [2.0] | // 20.0 | Show image caption (overlap) [2.0] | | Show image comments (overlap) [3.0] | | Wait before zoom [1.0] | | Zoom [2.5] X [_] in [x] out [2.0] | | [Zoom Center] position: x=40 y=50 | | Wait after zoom [1.5] | | Transition to next image [_________|v] | | | | [Done] | |__________________________________________________| ***/ if (! ss_Nfiles) { zmessageACK(Mwin,E2X("invalid album")); return; } m_viewmode(0,"G"); // gallery view zd = zdialog_new(E2X("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",E2X("Image File:"),"space=3"); zdialog_add_widget(zd,"label","imagefile","hbimf",0,"space=3"); zdialog_add_widget(zd,"hbox","hbact","dialog"); zdialog_add_widget(zd,"label","labact","hbact",E2X("Action"),"space=3"); zdialog_add_widget(zd,"label","space","hbact",0,"expand"); zdialog_add_widget(zd,"label","labsecs","hbact",Bseconds,"space=3"); zdialog_add_widget(zd,"hsep","sepact","dialog",0,"space=3"); zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"homog|space=3"); zdialog_add_widget(zd,"label","space","hb1",0,"space=5"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"homog|space=3"); zdialog_add_widget(zd,"hbox","hbtone","vb1"); zdialog_add_widget(zd,"label","labtone","hbtone",E2X("Play tone when image shows")); zdialog_add_widget(zd,"check","tone","hbtone","","space=3"); zdialog_add_widget(zd,"label","space","vb2",0); zdialog_add_widget(zd,"hbox","hbw0","vb1"); zdialog_add_widget(zd,"label","labwait","hbw0",E2X("Wait before filename/caption/comments")); zdialog_add_widget(zd,"zspin","wait0","vb2","0|99|1|0","size=3"); zdialog_add_widget(zd,"hbox","hbfile","vb1"); // 20.0 zdialog_add_widget(zd,"label","labfile","hbfile",E2X("Show image file name (overlap)")); zdialog_add_widget(zd,"zspin","filesecs","vb2","0|99|1|0","size=3"); zdialog_add_widget(zd,"hbox","hbcap","vb1"); zdialog_add_widget(zd,"label","labcap","hbcap",E2X("Show image caption (overlap)")); zdialog_add_widget(zd,"zspin","capsecs","vb2","0|99|1|0","size=3"); zdialog_add_widget(zd,"hbox","hbcom","vb1"); zdialog_add_widget(zd,"label","labcom","hbcom",E2X("Show image comments (overlap)")); zdialog_add_widget(zd,"zspin","commsecs","vb2","0|99|1|0","size=3"); zdialog_add_widget(zd,"hbox","hbw1","vb1"); zdialog_add_widget(zd,"label","labwait","hbw1",E2X("Wait before zoom")); zdialog_add_widget(zd,"zspin","wait1","vb2","0|99|1|0","size=3"); zdialog_add_widget(zd,"hbox","hbzoom","vb1"); zdialog_add_widget(zd,"label","labzoom","hbzoom",E2X("Zoom")); zdialog_add_widget(zd,"zspin","zoomsize","hbzoom","1.0|3.0|0.1|1.0","space=3|size=3"); zdialog_add_widget(zd,"label","labX","hbzoom","X","space=3"); zdialog_add_widget(zd,"label","space","hbzoom",0,"space=3"); zdialog_add_widget(zd,"check","zoomin","hbzoom",E2X("zoom-in"),"space=3"); zdialog_add_widget(zd,"check","zoomout","hbzoom",E2X("zoom-out"),"space=3"); zdialog_add_widget(zd,"zspin","zoomtime","vb2","1|99|1|2","size=3"); zdialog_add_widget(zd,"hbox","hbzloc","vb1"); zdialog_add_widget(zd,"button","zloc","hbzloc",E2X("Zoom Center")); zdialog_add_widget(zd,"label","labzloc","hbzloc","position: 50 50","space=5"); zdialog_add_widget(zd,"label","space","vb2",0); zdialog_add_widget(zd,"hbox","hbw2","vb1"); zdialog_add_widget(zd,"label","labwait","hbw2",E2X("Wait after zoom")); zdialog_add_widget(zd,"zspin","wait2","vb2","0|99|1|0","size=3"); zdialog_add_widget(zd,"hsep","septrn","dialog",0,"space=4"); zdialog_add_widget(zd,"hbox","hbtrn","dialog"); zdialog_add_widget(zd,"label","labtr","hbtrn",E2X("Transition to next image"),"space=3"); zdialog_add_widget(zd,"combo","tranname","hbtrn"); zdialog_cb_app(zd,"tranname",E2X("next")); // default transition for (ii = 0; ii < SSNT; ii++) // add all transitions to dropdown list zdialog_cb_app(zd,"tranname",E2X(ss_trantab[ii].tranname)); if (curr_file) { // set to curr. file for (ii = 0; ii < ss_Nfiles; ii++) // if member of album if (strmatch(curr_file,ss_imagetab[ii].imagefile)) break; if (ii == ss_Nfiles) ii = 0; } else ii = 0; // else first file in album ss_Fcurrent = ii; ss_setzloc = 0; // 1-shot flag is off pp = strrchr(ss_imagetab[ii].imagefile,'/'); // stuff curr. image file data if (pp) zdialog_stuff(zd,"imagefile",pp+1); // into dialog zdialog_stuff(zd,"tone",ss_imagetab[ii].tone); zdialog_stuff(zd,"wait0",ss_imagetab[ii].wait0); zdialog_stuff(zd,"filesecs",ss_imagetab[ii].filesecs); // 20.0 zdialog_stuff(zd,"capsecs",ss_imagetab[ii].capsecs); zdialog_stuff(zd,"commsecs",ss_imagetab[ii].commsecs); zdialog_stuff(zd,"wait1",ss_imagetab[ii].wait1); zdialog_stuff(zd,"zoomin",0); // both zoom checks off zdialog_stuff(zd,"zoomout",0); kk = ss_imagetab[ii].zoomtype; // zoom type 0/1/2 if (kk == 1) zdialog_stuff(zd,"zoomin",1); // set corresp. check on if (kk == 2) zdialog_stuff(zd,"zoomout",1); zdialog_stuff(zd,"zoomsize",ss_imagetab[ii].zoomsize); zdialog_stuff(zd,"zoomtime",ss_imagetab[ii].zoomtime); snprintf(zoomloc,40,"position: x=%02d y=%02d", // stuff zoom location if defined ss_imagetab[ii].zoomlocx,ss_imagetab[ii].zoomlocy); // (else x=00 y=00) zdialog_stuff(zd,"labzloc",zoomloc); zdialog_stuff(zd,"wait2",ss_imagetab[ii].wait2); zdialog_stuff(zd,"tranname",E2X(ss_imagetab[ii].tranname)); zdialog_run(zd,ss_imageprefs_dialog_event,"parent"); // run dialog zdialog_wait(zd); // wait for completion zdialog_free(zd); zd_ss_imageprefs = 0; ss_saveprefs(); // save updated preferences file return; } // image prefs dialog event and completion function int ss_imageprefs_dialog_event(zdialog *zd, cchar *event) { int ii, jj; float ff; char tranname[32]; GdkWindow *gdkwin; ii = ss_Fcurrent; // from mouse click on thumbnail if (ii >= ss_Nfiles) return 1; if (strmatch(event,"tone")) { zdialog_fetch(zd,"tone",jj); ss_imagetab[ii].tone = jj; } if (strmatch(event,"wait0")) { zdialog_fetch(zd,"wait0",jj); ss_imagetab[ii].wait0 = jj; } if (strmatch(event,"filesecs")) { // 20.0 zdialog_fetch(zd,"filesecs",jj); ss_imagetab[ii].filesecs = jj; } if (strmatch(event,"capsecs")) { zdialog_fetch(zd,"capsecs",jj); ss_imagetab[ii].capsecs = jj; } if (strmatch(event,"commsecs")) { zdialog_fetch(zd,"commsecs",jj); ss_imagetab[ii].commsecs = jj; } if (strmatch(event,"wait1")) { zdialog_fetch(zd,"wait1",jj); ss_imagetab[ii].wait1 = jj; } if (strmatch(event,"zoomin")) { // zoom-in checkbox zdialog_fetch(zd,"zoomin",jj); if (jj) { zdialog_stuff(zd,"zoomout",0); ss_imagetab[ii].zoomtype = 1; } else ss_imagetab[ii].zoomtype = 0; } if (strmatch(event,"zoomout")) { // zoom-out checkbox zdialog_fetch(zd,"zoomout",jj); if (jj) { zdialog_stuff(zd,"zoomin",0); ss_imagetab[ii].zoomtype = 2; } else ss_imagetab[ii].zoomtype = 0; } if (strmatch(event,"zoomsize")) { zdialog_fetch(zd,"zoomsize",ff); ss_imagetab[ii].zoomsize = ff; } if (strmatch(event,"zoomtime")) { zdialog_fetch(zd,"zoomtime",jj); ss_imagetab[ii].zoomtime = jj; } if (strmatch(event,"zloc")) { ss_setzloc = 1; // set 1-shot flag gdkwin = gtk_widget_get_window(Gdrawin); gdk_window_set_cursor(gdkwin,dragcursor); poptext_mouse(E2X("click on thumbnail to set zoom center"),20,20,0,2); } if (strmatch(event,"wait2")) { zdialog_fetch(zd,"wait2",jj); ss_imagetab[ii].wait2 = jj; } if (strmatch(event,"tranname")) { zdialog_fetch(zd,"tranname",tranname,32); if (! strmatch(tranname,E2X("next"))) { for (jj = 0; jj < SSNT; jj++) if (strmatch(tranname,E2X(ss_trantab[jj].tranname))) break; if (jj == SSNT) 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, kk; char *pp, zoomloc[40]; GdkWindow *gdkwin; if (! clicked_file) return; zd = zd_ss_imageprefs; // should not happen if (! zd) { zfree(clicked_file); clicked_file = 0; return; } for (ii = 0; ii < ss_Nfiles; ii++) // find clicked file in image prefs if (strmatch(clicked_file,ss_imagetab[ii].imagefile)) break; zfree(clicked_file); clicked_file = 0; if (ii == ss_Nfiles) return; // not found, album file removed ss_Fcurrent = ii; if (ss_setzloc) { // 1-shot flag is set ss_setzloc = 0; ss_imagetab[ii].zoomlocx = clicked_width; // set zoom-in location from ss_imagetab[ii].zoomlocy = clicked_height; // thumbnail click position gdkwin = gtk_widget_get_window(Gdrawin); gdk_window_set_cursor(gdkwin,0); } pp = strrchr(ss_imagetab[ii].imagefile,'/'); // stuff image data into dialog if (pp) zdialog_stuff(zd,"imagefile",pp+1); zdialog_stuff(zd,"tone",ss_imagetab[ii].tone); zdialog_stuff(zd,"wait0",ss_imagetab[ii].wait0); zdialog_stuff(zd,"filesecs",ss_imagetab[ii].filesecs); // 20.0 zdialog_stuff(zd,"capsecs",ss_imagetab[ii].capsecs); zdialog_stuff(zd,"commsecs",ss_imagetab[ii].commsecs); zdialog_stuff(zd,"wait1",ss_imagetab[ii].wait1); zdialog_stuff(zd,"zoomin",0); // set both zoom checks off zdialog_stuff(zd,"zoomout",0); kk = ss_imagetab[ii].zoomtype; // 0/1/2 = none/zoomin/zoomout if (kk == 1) zdialog_stuff(zd,"zoomin",1); // set corresp. check on if (kk == 2) zdialog_stuff(zd,"zoomout",1); zdialog_stuff(zd,"zoomsize",ss_imagetab[ii].zoomsize); zdialog_stuff(zd,"zoomtime",ss_imagetab[ii].zoomtime); snprintf(zoomloc,40,"position: x=%02d y=%02d", // stuff zoom location ss_imagetab[ii].zoomlocx,ss_imagetab[ii].zoomlocy); zdialog_stuff(zd,"labzloc",zoomloc); zdialog_stuff(zd,"wait2",ss_imagetab[ii].wait2); zdialog_stuff(zd,"tranname",E2X(ss_imagetab[ii].tranname)); return; } /*********************** preferences file format ****************************** global data: imagetime: NN 0-99 image show time cctime: NN 0-99 filename/caption/comments show time // 20.0 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 duration preference tranname N NN NN 0-1 0.1-10 0-99 ... images data: imagefile: /path.../file.jpg show this image tone: N 0-1 play tone for this image wait0: NN seconds to wait 0-99 filename: NN seconds to show file name // 20.0 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/zoom-in/zoom-out zoomsize: N.N 1.0 - 3.0 // 1.0 = no zoom zoomtime: NN seconds for zoom 0-99 zoomloc: NN NN 20-80 20-80 wait2: NN seconds to wait 0-99 tranname: aaaaaa transition name or "next" ... *********************************************************************************/ // Load all data for a specific slide show from a slide show preferences file. // Set defaults if no data previously defined. void ss_loadprefs() { FILE *fid; char buff[XFCC]; char prefsfile[200], *pp; int ii, jj, nn, format; FTYPE ftype; char tranname[32]; int n1, n2; float ff; for (ii = 0; ii < ss_Nfiles; ii++) { // free prior image data if any pp = ss_imagetab[ii].imagefile; if (pp) zfree(pp); ss_imagetab[ii].imagefile = 0; } ss_Nfiles = 0; fid = fopen(ss_albumfile,"r"); // open album file if (! fid) { zmessageACK(Mwin,E2X("invalid album")); return; } for (ii = 0; ii < SSMAXI; ) { // read all image file names pp = fgets_trim(buff,XFCC,fid); if (! pp) break; ftype = image_file_type(pp); // screen out deleted image files if (ftype != IMAGE && ftype != VIDEO) continue; ss_imagetab[ii].imagefile = zstrdup(pp); // add to image table ii++; } fclose(fid); ss_Nfiles = ii; if (! ss_Nfiles) { zmessageACK(Mwin,E2X("invalid album")); return; } album_show(ss_albumfile); // open slide show album 19.0 ss_imagetime = 3; // default image display time ss_cctime = 0; // default filename/caption/comment time ss_cliplimit = 0; // image clip limit = no clipping strcpy(ss_musicfile,"none"); // no music file for (ii = 0; ii < SSNT; ii++) // initialize transitions table ss_trantab[ii] = ss_trantab_default[ii]; // with default preferences for (ii = 0; ii < SSNT; 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 = SSNT; // 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].wait0 = 0; ss_imagetab[ii].filesecs = 0; // 20.0 ss_imagetab[ii].capsecs = 0; ss_imagetab[ii].commsecs = 0; ss_imagetab[ii].wait1 = 0; ss_imagetab[ii].zoomtype = 0; // no zoom-in ss_imagetab[ii].zoomsize = 1.0; ss_imagetab[ii].zoomtime = 2; ss_imagetab[ii].zoomlocx = 50; ss_imagetab[ii].zoomlocy = 50; ss_imagetab[ii].wait2 = 0; strcpy(ss_imagetab[ii].tranname,"next"); } snprintf(prefsfile,200,"%s/%s",slideshow_folder,ss_albumname); fid = fopen(prefsfile,"r"); // open slide show prefs file if (! fid) return; format = 0; while (true) { pp = fgets_trim(buff,XFCC,fid,1); if (! pp) break; if (strmatchN(pp,"global data:",12)) { format = 1; continue; } if (strmatchN(pp,"transitions data:",17)) { format = 2; continue; } if (strmatchN(pp,"images data:",12)) { format = 3; continue; } if (format == 1) // overall preferences { if (strmatchN(pp,"imagetime: ",11)) { // imagetime: NN image show time, secs. ss_imagetime = atoi(pp+11); continue; } if (strmatchN(pp,"cctime: ",8)) { // cctime: NN filename/caption/comments ss_cctime = atoi(pp+8); 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: /folder/.../musicfile.ogg strncpy0(ss_musicfile,pp+11,500); continue; } if (strmatchN(pp,"fullscreen: ",12)) { // fullscreen: N 0-1 = no / full screen ss_fullscreen = atoi(pp+12); continue; } if (strmatchN(pp,"replay: ",8)) { // random: N 0-1 = no / replay after end ss_replay = atoi(pp+8); continue; } } if (format == 2) // transition preferences { nn = sscanf(buff,"%s %d %f %d ",tranname,&n1,&ff,&n2); // tranname N N.N NN if (nn != 4) { // (enabled 0-1 duration N.N pref. 0-99) printz("bad record: %s \n",buff); continue; } for (ii = 0; ii < SSNT; ii++) if (strmatch(tranname,ss_trantab[ii].tranname)) break; if (ii == SSNT) { printz("unknown transition: %s \n",tranname); // ignore and continue continue; } ss_trantab[ii].enabled = n1; ss_trantab[ii].trantime = ff; ss_trantab[ii].preference = n2; } if (format == 3) // image file preferences { if (strmatchN(pp,"imagefile: ",11)) { // set image file for subsequent recs pp += 11; if (*pp != '/') { printz("bad image file: %s \n",buff); continue; } for (ii = 0; ii < ss_Nfiles; ii++) // search album for matching image if (strmatch(pp,ss_imagetab[ii].imagefile)) break; if (ii == ss_Nfiles) ii = -1; // if not found, set no curr. image continue; } if (ii < 0) continue; // ignore recs following invalid image if (strmatchN(pp,"tone: ",6)) { // tone: N 0 or 1 = play tone nn = atoi(pp+6); ss_imagetab[ii].tone = nn; continue; } if (strmatchN(pp,"wait0: ",7)) { // wait0: NN secs before cap/comms nn = atoi(pp+7); ss_imagetab[ii].wait0 = nn; continue; } if (strmatchN(pp,"filename: ",10)) { // filename: NN secs for file name 20.0 nn = atoi(pp+10); ss_imagetab[ii].filesecs = nn; continue; } if (strmatchN(pp,"caption: ",9)) { // caption: NN secs to show caption nn = atoi(pp+9); ss_imagetab[ii].capsecs = nn; continue; } if (strmatchN(pp,"comments: ",10)) { // comments: NN secs to show comments nn = atoi(pp+10); ss_imagetab[ii].commsecs = nn; continue; } if (strmatchN(pp,"wait1: ",7)) { // wait1: NN secs before zoom-in nn = atoi(pp+7); ss_imagetab[ii].wait1 = nn; continue; } if (strmatchN(pp,"zoomtype: ",10)) { // zoomtype: N zoom type nn = atoi(pp+10); // 0/1/2 = none/zoom-in/zoom-out ss_imagetab[ii].zoomtype = nn; continue; } if (strmatchN(pp,"zoomsize: ",10)) { // zoomsize: N.N 1.0 - 3.0 = 3x ff = atof(pp+10); // ff float ss_imagetab[ii].zoomsize = ff; continue; } if (strmatchN(pp,"zoomtime: ",10)) { // zoomtime: NN secs nn = atoi(pp+10); if (nn < 1) nn = 1; // insure >= 1 ss_imagetab[ii].zoomtime = 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 secs 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 < SSNT; 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; } // Save all data for a specific slide show to a slide show preferences file. void ss_saveprefs() { FILE *fid; char prefsfile[200]; int ii; if (! ss_Nfiles) { zmessageACK(Mwin,E2X("invalid album")); return; } snprintf(prefsfile,200,"%s/%s",slideshow_folder,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,"imagetime: %d \n",ss_imagetime); fprintf(fid,"cctime: %d \n",ss_cctime); 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 < SSNT; ii++) fprintf(fid,"%s %d %.1f %d \n", ss_trantab[ii].tranname, ss_trantab[ii].enabled, ss_trantab[ii].trantime, 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,"wait0: %d \n",ss_imagetab[ii].wait0); fprintf(fid,"filename: %d \n",ss_imagetab[ii].filesecs); // 20.0 fprintf(fid,"caption: %d \n",ss_imagetab[ii].capsecs); fprintf(fid,"comments: %d \n",ss_imagetab[ii].commsecs); fprintf(fid,"wait1: %d \n",ss_imagetab[ii].wait1); fprintf(fid,"zoomtype: %d \n",ss_imagetab[ii].zoomtype); fprintf(fid,"zoomsize: %.1f \n",ss_imagetab[ii].zoomsize); fprintf(fid,"zoomtime: %d \n",ss_imagetab[ii].zoomtime); fprintf(fid,"zoomloc: %d %d \n",ss_imagetab[ii].zoomlocx,ss_imagetab[ii].zoomlocy); fprintf(fid,"wait2: %d \n",ss_imagetab[ii].wait2); fprintf(fid,"tranname: %s \n",ss_imagetab[ii].tranname); } fclose(fid); ss_loadprefs(); // reload to sync poss. album edits return; } /********************************************************************************/ // Show next slide when time is up or user navigates with arrow keys. // Cycles every 0.1 seconds when slide show is active. int ss_timerfunc(void *) // rewritten { int ss_timerfunc_sleep(int sleepsecs); int img, jj; int filesecs, capsecs, commsecs, sleepsecs; cchar *keynames[2] = { iptc_caption_key, exif_comment_key }; char *keyvals[2], *pp; if (zd_magnify) return 1; // exit magnify before event processing if (Fescape) ss_event = "escape"; // from main() if (FGWM != 'F') ss_event = "escape"; // must be F-view mode 19.0 if (strmatch(ss_event,"")) return 1; // no event, loop and wait if (strmatch(ss_event,"escape")) goto escape; if (strmatch(ss_event,"blank")) goto blank; if (strmatch(ss_event,"first")) goto first; if (strmatch(ss_event,"EOL")) goto EOL; if (strmatch(ss_event,"show")) goto show; if (strmatch(ss_event,"next")) goto next; if (strmatch(ss_event,"tran next")) goto tran_next; if (strmatch(ss_event,"magnify")) goto magnify; if (strmatch(ss_event,"prior")) goto prior; goto escape; // unknown event --> escape 19.0 first: // start slide show ss_paused = 0; // at ss_Fcurrent image ss_event = "show"; return 1; EOL: // last image reached img = ss_Nfiles - 1; f_open(ss_imagetab[img].imagefile); // last file --> current file zmessage_post_bold(Mwin,"20/20",5,"END (Escape to exit)"); ss_event = ""; // wait for escape return 1; escape: // terminate slide show Fescape = 0; // reset escape key if (ss_pxbold) PXB_free(ss_pxbold); // free memory if (ss_pxbnew) PXB_free(ss_pxbnew); ss_pxbold = ss_pxbnew = 0; if (*ss_musicfile == '/') shell_quiet("pulseaudio --kill"); // kill music if any if (ss_fullscreen) win_unfullscreen(); // restore old window size, menu etc. ss_fullscreen = 0; Fslideshow = 0; // reset flags Fblock = 0; img = ss_Fcurrent; f_open(ss_imagetab[img].imagefile); // open last image shown m_slideshow(0,0); // return to slide show dialog return 0; // stop the timer blank: // blank window and wait ss_isblank = 1 - ss_isblank; if (ss_isblank) { ss_event = ""; ss_blankwindow(); } else ss_event = "show"; return 1; magnify: // magnify image ss_event = ""; img = ss_Fcurrent; f_open(ss_imagetab[img].imagefile); m_magnify(0,0); ss_event = "show"; return 1; prior: // back to prior image ss_Fcurrent--; if (ss_Fcurrent < 0) { if (ss_replay) ss_Fcurrent = ss_Nfiles-1; // wrap to last image else ss_Fcurrent = 0; } ss_event = "show"; return 1; show: // current image instantly next: // next image instantly tran_next: // transition to next image if (zstrstr(ss_event,"next")) { // next or tran next if (ss_Fcurrent < ss_Nfiles-1) ss_Fcurrent++; // next image file else if (! ss_replay) { ss_event = "EOL"; // last image file was shown return 1; } else ss_Fcurrent = 0; // start over with first file } img = ss_Fcurrent; // new current image ss_oldfile = ss_newfile; // old file = new if (ss_pxbold) PXB_free(ss_pxbold); ss_pxbold = ss_pxbnew; // new pixbuf --> old ss_newfile = ss_imagetab[img].imagefile; // new current file ss_pxbnew = ss_loadpxb(ss_newfile); // new pixbuf if (! ss_pxbnew) { ss_event = "escape"; // failure, quit slide show return 1; } ss_zoomsize = ss_imagetab[img].zoomsize; // zoom size ss_zoomtype = ss_imagetab[img].zoomtype; // 0/1/2 = none/zoomin/zoomout ss_zoomlocx = ss_imagetab[img].zoomlocx; // target location for final center ss_zoomlocy = ss_imagetab[img].zoomlocy; if (ss_zoomsize > 1 && ss_zoomtype == 2) { // next image will be zoomed out PXB_free(ss_pxbnew); ss_zoom_setup(ss_newfile); // initial image is zoomed image 19.0 ss_zoom_start(ss_zoomsize); ss_pxbnew = ss_zoom_wait(); } if (strmatch(ss_event,"tran next")) { // do transition to new image ss_event = ""; // erase event jj = ss_nextrans(); // select next transition type ss_trantime = ss_trantab[jj].trantime; // set transition duration ss_trantab[jj].func(); // call transition function if (*ss_event) return 1; // new event during transition } else ss_instant(); // show new image immediately if (! ss_fullscreen) // if not full screen mode, gtk_window_set_title(MWIN,ss_newfile); // put filename in title bar // process image events: // play tone, wait before file/cap/com, show file/cap/com, // wait before zoom, zoom, wait after zoom if (ss_imagetab[img].tone) shell_quiet("paplay %s/slideshow-tone.oga &",get_zhomedir()); // play tone if specified sleepsecs = ss_imagetab[img].wait0; // show image specified time jj = ss_timerfunc_sleep(sleepsecs); // before filename/captions/comments if (jj) return 1; filesecs = ss_imagetab[img].filesecs; // time to show file name 20.0 capsecs = ss_imagetab[img].capsecs; // time to show caption commsecs = ss_imagetab[img].commsecs; // time to show comments if (filesecs + capsecs + commsecs == 0) // if not specified, use default 20.0 filesecs = capsecs = commsecs = ss_cctime; exif_get(ss_newfile,keynames,keyvals,2); // get image captions and comments if (! keyvals[0]) capsecs = 0; // zero time if no caption else zfree(keyvals[0]); if (! keyvals[1]) commsecs = 0; // zero time if no comments else zfree(keyvals[1]); while (filesecs + capsecs + commsecs) { ss_showcapcom(filesecs,capsecs,commsecs); // show items with remaining time 20.0 jj = ss_timerfunc_sleep(1.0); // wait 1 second if (jj) return 1; // new event filesecs -= 1; if (filesecs < 0) filesecs = 0; // decrement show times capsecs -= 1; if (capsecs < 0) capsecs = 0; commsecs -= 1; if (commsecs < 0) commsecs = 0; } ss_showcapcom(0,0,0); // remove all if (image_file_type(ss_newfile) == VIDEO) { // if VIDEO file, play now jj = ss_timerfunc_sleep(1.0); // show image one second if (jj) return 1; // new event f_open(ss_newfile); // reset current image pp = zescape_quotes(ss_newfile); shell_ack("ffplay -loglevel -8 -autoexit \"%s\" ",pp); // play video, wait for user quit zfree(pp); jj = ss_timerfunc_sleep(1.0); // wait one second after play if (jj) return 1; // new event if (ss_paused) ss_event = ""; else ss_event = "tran next"; return 1; } sleepsecs = ss_imagetab[img].wait1; // show image specified time jj = ss_timerfunc_sleep(sleepsecs); // before zoom if (jj) return 1; ss_zoomsize = ss_imagetab[img].zoomsize; // 1-3x = no zoom to 3x zoom ss_zoomtype = ss_imagetab[img].zoomtype; // 0/1/2 = none/zoomin/zoomout ss_zoomtime = ss_imagetab[img].zoomtime; // zoom time ss_zoomlocx = ss_imagetab[img].zoomlocx; // target location for final center ss_zoomlocy = ss_imagetab[img].zoomlocy; // (0-100% of image, 50/50 = middle) if (ss_zoomsize > 1.0) { ss_event = ""; // erase event if (ss_zoomtype == 1) ss_zoomin(); // zoom in or zoom out if (ss_zoomtype == 2) ss_zoomout(); if (*ss_event) return 1; // new event during zoom } sleepsecs = ss_imagetab[img].wait2; // show zoomed image specified time if (sleepsecs == 0) sleepsecs = ss_imagetime; // not set, use overall default jj = ss_timerfunc_sleep(sleepsecs); if (jj) return 1; // new event if (ss_paused) ss_event = ""; else ss_event = "tran next"; // do next image return 1; } // ------------------------------------------------------------------------------ // sleep designated seconds and return 0 if no ss_event happens // if ss_event happens during sleep, immediately return 1 int ss_timerfunc_sleep(int sleepsecs) { float Fsecs = sleepsecs; ss_event = ""; while (Fsecs > 0) { zmainsleep(0.1); Fsecs -= 0.1; if (*ss_event) return 1; if (Fescape) return 1; // from main() 19.0 } return 0; } // ------------------------------------------------------------------------------ // process keyboard input key void ss_KBfunc(int kbkey) { kbkey = toupper(kbkey); if (kbkey == ss_KBkeyB) ss_event = "blank"; // blank window if (kbkey == ss_KBkeyN) ss_event = "tran next"; // transition to next image if (kbkey == ss_KBkeyX) ss_event = "magnify"; // magnify image tool if (kbkey == GDK_KEY_Left) ss_event = "prior"; // prior image if (kbkey == GDK_KEY_Right) ss_event = "next"; // next image if (kbkey == ss_KBkeyP) { ss_paused = 1 - ss_paused; // toggle paused / running status if (! ss_paused) ss_event = "tran next"; } return; } // ------------------------------------------------------------------------------ // 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_Fcurrent - 1; // transition type from prior image if (ii < 0) ii = ss_Nfiles - 1; if (! strmatch(ss_imagetab[ii].tranname,"next")) { // image transition not "next" for (jj = 0; jj < SSNT; jj++) if (strmatch(ss_trantab[jj].tranname,ss_imagetab[ii].tranname)) break; if (jj < SSNT) { next = jj; // assigned transition type for (ii = ss_Nused - 1; ii > 0; ii--) // >> most recently used ss_Tlast[ii] = ss_Tlast[ii-1]; ss_Tlast[0] = next; return next; } } if (ss_Nused < 2 || ss_random == 0) // few enabled transition types { // or sequential mode ss_Tnext++; if (ss_Tnext == ss_Nused) ss_Tnext = 0; // select transition types sequentially next = ss_Tused[ss_Tnext]; } else // select transition types randomly { maxrank = 0; maxii = 0; maxjj = ss_Nused / 2; // most recently used to exclude if (maxjj > 4) maxjj = 4; // max. 4 for (ii = 0; ii < ss_Nused; ii++) // search enabled transitions { for (jj = 0; jj < maxjj; jj++) // exclude most recently used 50% if (ss_Tused[ii] == ss_Tlast[jj]) break; if (jj < maxjj) continue; jj = ss_Tused[ii]; rank = ss_trantab[jj].preference * drandz(); // rank = preference * random value if (rank > maxrank) { maxrank = rank; // remember highest rank maxii = ii; } } next = ss_Tused[maxii]; // transition to use for (ii = ss_Nused - 1; ii > 0; ii--) // make it most recent ss_Tlast[ii] = ss_Tlast[ii-1]; ss_Tlast[0] = next; } return next; } // ------------------------------------------------------------------------------ // write file name, caption and comments at the top of the image // show only those items with time > 0 int ss_showcapcom(int filesecs, int capsecs, int commsecs) // 20.0 { cchar *keynames[2] = { iptc_caption_key, exif_comment_key }; char *keyvals[2], *pp; char filename[200], caption[200], comments[200]; static char text[600]; PIXBUF *pixbuf; static PangoFontDescription *pangofont = null; static PangoLayout *pangolayout = null; static int plww, plhh, pline; if (plww) { // clear previous text pixbuf = gdk_pixbuf_new_subpixbuf(ss_pxbnew->pixbuf,0,0,plww+10,plhh+10); cairo_t *cr = draw_context_create(gdkwin,draw_context); gdk_cairo_set_source_pixbuf(cr,pixbuf,0,0); cairo_paint(cr); draw_context_destroy(draw_context); g_object_unref(pixbuf); plww = 0; } if (filesecs + capsecs + commsecs == 0) return 0; // 20.0 *text = 0; pp = strrchr(ss_newfile,'/'); // get base file name if (pp) pp++; else pp = ss_newfile; strncpy0(filename,pp,200); *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; pline = 0; if (filesecs) { strncpy0(text,filename,200); // get items with time remaining 20.0 pline = 1; } if (capsecs && *caption) { if (pline) strncatv(text,600,"\n",0); strncatv(text,600,caption,0); pline = 1; } if (commsecs && *comments) { if (pline) strncatv(text,600,"\n",0); strncatv(text,600,comments,0); } for (int ii = 0; text[ii]; ii++) // replace "\\n" with real newline char. if (text[ii] == '\\' && text[ii+1] == 'n') memcpy(text+ii,"\n ",2); pangofont = pango_font_description_from_string("Sans 12"); // make pango layout for font pangolayout = gtk_widget_create_pango_layout(Fdrawin,0); // Fdrawin instead of Cdrawin pango_layout_set_font_description(pangolayout,pangofont); pango_layout_set_text(pangolayout,text,-1); // add text to layout pango_layout_get_pixel_size(pangolayout,&plww,&plhh); cairo_t *cr = draw_context_create(gdkwin,draw_context); cairo_set_line_width(cr,1); cairo_set_source_rgb(cr,1,1,1); // draw white background cairo_rectangle(cr,10,10,plww,plhh); cairo_fill(cr); cairo_move_to(cr,10,10); // draw layout with text cairo_set_source_rgb(cr,0,0,0); pango_cairo_show_layout(cr,pangolayout); draw_context_destroy(draw_context); return 1; } // ------------------------------------------------------------------------------ // 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. PXB * ss_loadpxb(char *file) { PXB *pxbin, *pxbtemp, *pxbout; 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); pxbin = PXB_load(file,1); // load image if (! pxbin) return 0; PXB_subalpha(pxbin); // remove alpha channel if any ww1 = pxbin->ww; // image dimensions hh1 = pxbin->hh; 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 = PXB_rescale_fast(pxbin,ww1,hh1); PXB_free(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 = PXB_make(ww2,hh2,3); // output PXB PXB_copy_area(pxbtemp,Iorgx,Iorgy,ww1,hh1,pxbout,Worgx,Worgy); PXB_free(pxbtemp); ss_rs = pxbout->rs; // set image row stride return pxbout; } /********************************************************************************/ // write black to entire window void ss_blankwindow() { GdkRGBA GDKdark; cairo_t *cr = draw_context_create(gdkwin,draw_context); GDKdark.red = GDKdark.green = GDKdark.blue = 0.2; GDKdark.alpha = 1; gdk_cairo_set_source_rgba(cr,&GDKdark); cairo_paint(cr); draw_context_destroy(draw_context); zmainloop(); return; } // ------------------------------------------------------------------------------ // instant transition (also used for keyboard arrow keys) void ss_instant() { cairo_t *cr = draw_context_create(gdkwin,draw_context); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); cairo_paint(cr); draw_context_destroy(draw_context); zmainloop(); return; } // ------------------------------------------------------------------------------ // fade-out / fade-in transition void ss_fadein() { PXB *pxbmix; int jj, kk, px, py, rs; float newpart, oldpart; uint8 *pixels1, *pixels2, *pixels3; uint8 *pix1, *pix2, *pix3; double T0, Te, Tz; pxbmix = PXB_copy(ss_pxbold); rs = pxbmix->rs; pixels1 = ss_pxbold->pixels; pixels2 = ss_pxbnew->pixels; pixels3 = pxbmix->pixels; cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done newpart = Te / Tz; oldpart = 1.0 - newpart; for (jj = 0; jj < 2; jj++) // four passes, each modifies 25% for (kk = 0; kk < 2; kk++) // of the pixels (visually smoother) { for (py = jj; py < ss_hh; py += 2) for (px = kk; px < ss_ww; px += 2) { pix1 = pixels1 + py * ss_rs + px * 3; pix2 = pixels2 + py * ss_rs + px * 3; pix3 = pixels3 + py * rs + px * 3; pix3[0] = newpart * pix2[0] + oldpart * pix1[0]; pix3[1] = newpart * pix2[1] + oldpart * pix1[1]; pix3[2] = newpart * pix2[2] + oldpart * pix1[2]; } gdk_cairo_set_source_pixbuf(cr,pxbmix->pixbuf,0,0); cairo_paint(cr); zmainloop(); } if (Fescape) break; } PXB_free(pxbmix); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image rolls over prior image from left to right void ss_rollright() { PIXBUF *pixbuf; int px, ppx, ww; double T0, Te, Tz; cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) px = ppx = 0; while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te >= Tz) break; // done px = Te / Tz * ss_ww; // ending column to paint if (px > ss_ww - 1) break; ww = px - ppx; // width to paint if (ww < 1) ww = 1; pixbuf = gdk_pixbuf_new_subpixbuf(ss_pxbnew->pixbuf,ppx,0,ww,ss_hh); gdk_cairo_set_source_pixbuf(cr,pixbuf,ppx,0); cairo_paint(cr); g_object_unref(pixbuf); ppx = px; zmainloop(); if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image rolls over prior image from top down void ss_rolldown() { PIXBUF *pixbuf; int py, ppy, hh; double T0, Te, Tz; cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) py = ppy = 0; while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te >= Tz) break; // done py = Te / Tz * ss_hh; // last row to paint hh = py - ppy; if (hh < 1) hh = 1; pixbuf = gdk_pixbuf_new_subpixbuf(ss_pxbnew->pixbuf,0,ppy,ss_ww,hh); gdk_cairo_set_source_pixbuf(cr,pixbuf,0,ppy); cairo_paint(cr); g_object_unref(pixbuf); ppy = py; zmainloop(); if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image opens up in horizontal rows like venetian blinds void ss_venetian() { PIXBUF *pixbuf; int py1, py2; uint8 *pixels, *pix3; int louver, Nlouvers = 20; int louversize = ss_hh / Nlouvers; double T0, Te, Tz; pixels = ss_pxbnew->pixels; cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done py1 = Te / Tz * louversize; // 0 .. louversize for (louver = 0; louver < Nlouvers; louver++) // louver, first to last { py2 = py1 + louver * louversize; if (py2 >= ss_hh) break; pix3 = pixels + py2 * ss_rs; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ss_ww,4,ss_rs,0,0); gdk_cairo_set_source_pixbuf(cr,pixbuf,0,py2); cairo_paint(cr); g_object_unref(pixbuf); } zmainloop(); if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // a grate opens up to show new image void ss_grate() { PIXBUF *pixbuf; int px1, px2, py1, py2; uint8 *pixels, *pix3; int row, col, Nrow, Ncol; // rows and columns int boxww, boxhh; double T0, Te, Tz; pixels = ss_pxbnew->pixels; Ncol = 20; // 20 columns boxww = boxhh = ss_ww / Ncol; // square boxes Nrow = ss_hh / boxhh; // corresp. rows Ncol++; // round up Nrow++; cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done py1 = Te / Tz * boxhh; // 0 .. boxhh for (row = 0; row < Nrow; row++) { py2 = py1 + row * boxhh; if (py2 >= ss_hh) break; pix3 = pixels + py2 * ss_rs; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ss_ww,1,ss_rs,0,0); gdk_cairo_set_source_pixbuf(cr,pixbuf,0,py2); cairo_paint(cr); g_object_unref(pixbuf); } px1 = py1; for (col = 0; col < Ncol; col++) { px2 = px1 + col * boxww; if (px2 >= ss_ww) break; pix3 = pixels + px2 * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,1,ss_hh,ss_rs,0,0); gdk_cairo_set_source_pixbuf(cr,pixbuf,px2,0); cairo_paint(cr); g_object_unref(pixbuf); } zmainloop(); if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // A rectangular hole opens up from the center and expands outward to reveal new image. void ss_rectangle() { PIXBUF *pixbuf; int ww, hh, sww, shh; int px, py, ppx, ppy; double T0, Te, Tz; cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) ppx = ss_ww / 2; ppy = ss_hh / 2; while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te >= Tz) break; // done ww = Te / Tz * ss_ww; // 0 ... ss_ww central rectangle hh = Te / Tz * ss_hh; // 0 ... ss_hh width and height px = ss_ww / 2 - ww / 2; // NW corner, moving NW py = ss_hh / 2 - hh / 2; if (px == ppx || py == ppy) { zsleep(0.001); continue; } if (px + ww > ss_ww - 2) break; if (py + hh > ss_hh - 2) break; sww = ppx - px; // left/right stripe width shh = ppy - py; // top/bottom stripe height pixbuf = gdk_pixbuf_new_subpixbuf(ss_pxbnew->pixbuf,px,py,ww+2,shh+2); // paint top edge stripe gdk_cairo_set_source_pixbuf(cr,pixbuf,px,py); cairo_paint(cr); g_object_unref(pixbuf); pixbuf = gdk_pixbuf_new_subpixbuf(ss_pxbnew->pixbuf,px,py+hh-shh,ww+2,shh+2); // paint bottom edge stripe gdk_cairo_set_source_pixbuf(cr,pixbuf,px,py+hh-shh); cairo_paint(cr); g_object_unref(pixbuf); pixbuf = gdk_pixbuf_new_subpixbuf(ss_pxbnew->pixbuf,px,py,sww+2,hh+2); // paint left edge stripe gdk_cairo_set_source_pixbuf(cr,pixbuf,px,py); cairo_paint(cr); g_object_unref(pixbuf); pixbuf = gdk_pixbuf_new_subpixbuf(ss_pxbnew->pixbuf,px+ww-sww,py,sww+2,hh+2); // paint right edge stripe gdk_cairo_set_source_pixbuf(cr,pixbuf,px+ww-sww,py); cairo_paint(cr); g_object_unref(pixbuf); ppx = px; ppy = py; zmainloop(); if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // old image shrinks to the center, revealing new image void ss_implode() { float size; int ww, hh, px, py; PXB *pxbnew, *pxbold; double T0, Te, Tz; cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done size = 1.0 - Te / Tz; if (size < 0.01) size = 0.01; pxbnew = PXB_copy(ss_pxbnew); // new image at full size ww = ss_ww * size; hh = ss_hh * size; pxbold = PXB_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); PXB_copy_area(pxbold,0,0,ww,hh,pxbnew,px,py); // copy shrinking old image into new image gdk_cairo_set_source_pixbuf(cr,pxbnew->pixbuf,0,0); // paint new image cairo_paint(cr); PXB_free(pxbnew); PXB_free(pxbold); zmainloop(); if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image grows from the center, covering old image void ss_explode() { float size; int ww, hh, px, py; PXB *pxbnew, *pxbold; double T0, Te, Tz; cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done size = Te / Tz; if (size < 0.01) size = 0.01; if (size > 1.0) break; pxbold = PXB_copy(ss_pxbold); // old image at full size ww = ss_ww * size; hh = ss_hh * size; pxbnew = PXB_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); PXB_copy_area(pxbnew,0,0,ww,hh,pxbold,px,py); // copy shrinking old image into new image gdk_cairo_set_source_pixbuf(cr,pxbold->pixbuf,0,0); // paint new image cairo_paint(cr); PXB_free(pxbnew); PXB_free(pxbold); zmainloop(); if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // New image sweeps into view like a circular radar image void ss_radar() { int px, py, ww, hh; int px1, py1, px2, py2; int ww2 = ss_ww/2, hh2 = ss_hh/2; float R, Rmax, dR, T, pT, dTmax; float r, r1, r2; float cosT, sinT; uint8 *pixels1, *pixels3, *pix1, *pix3; PIXBUF *pixbuf; double T0, Te, Tz; px = py = 0; // suppress compiler warnings pixels1 = ss_pxbold->pixels; pixels3 = ss_pxbnew->pixels; Rmax = sqrt(ww2*ww2 + hh2*hh2); // max. line length, center to corner dR = 200; // line segment length T = pT = 0.0; dTmax = 1.3 / Rmax; cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 T = 2.0 * PI * Te / Tz; // 0 ... 2 * PI if (T - pT > dTmax) T = pT + dTmax; pT = T; // enforce max. dT to avoid gaps 19.0 if (T > 1.99 * PI) break; cosT = cosf(T); sinT = sinf(T); for (R = 0; R < Rmax; R += dR) // R from center to edge { r1 = R; // R segment r2 = R + dR; px1 = ww2 + r1 * cosT; // R segment px2 = ww2 + r2 * cosT; py1 = hh2 - r1 * sinT; py2 = hh2 - r2 * sinT; for (r = r1; r <= r2; r++) // loop R segment pixels { px = ww2 + r * cosT; py = hh2 - r * sinT; if (px < 0) px = 0; if (px > ss_ww-3) px = ss_ww-3; if (py < 0) py = 0; if (py > ss_hh-3) py = ss_hh-3; pix1 = pixels1 + py * ss_rs + px * 3; // copy new image pixels to old image pix3 = pixels3 + py * ss_rs + px * 3; memcpy(pix1,pix3,9); memcpy(pix1 + ss_rs, pix3 + ss_rs,9); memcpy(pix1 + 2 * ss_rs, pix3 + 2 * ss_rs,9); if (px == 0 || px == ss_ww-3) break; // reached edge of image if (py == 0 || py == ss_hh-3) 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 (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->pixbuf,px,py,ww,hh); // paint window rectangle gdk_cairo_set_source_pixbuf(cr,pixbuf,px,py); cairo_paint(cr); g_object_unref(pixbuf); } zmainloop(); if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image opens up like a Japanese fan void ss_japfan() { int px, py, pxL, ww, hh; int px1, py1, px2, py2; int ww2 = ss_ww/2, hh2 = ss_hh/2; float R, Rmax, dR, T, pT, dTmax; float r, r1, r2; float cosT, sinT; uint8 *pixels1, *pixels3, *pix1, *pix3; PIXBUF *pixbuf; double T0, Te, Tz; px = py = 0; // suppress compiler warnings pixels1 = ss_pxbold->pixels; pixels3 = ss_pxbnew->pixels; Rmax = sqrt(ww2*ww2 + hh2*hh2); // max. line length, center to corner dR = 200; pT = PI / 2; dTmax = 1.3 / Rmax; cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 T = 0.51 * PI - PI * Te / Tz; // 0.5 * PI ... -0.5 * PI if (pT - T > dTmax) T = pT - dTmax; // enforce max. dT to avoid gaps 19.0 pT = T; if (T < -0.499 * PI) break; cosT = cosf(T); sinT = sinf(T); for (R = 0; R < Rmax; R += dR) // R from center to edge { r1 = R; // R segment r2 = R + dR; px1 = ww2 + r1 * cosT; // R segment px2 = ww2 + r2 * cosT; py1 = hh2 - r1 * sinT; py2 = hh2 - r2 * sinT; for (r = r1; r <= r2; r++) // loop R segment pixels { px = ww2 + r * cosT; py = hh2 - r * sinT; if (px < 0) px = 0; if (px > ss_ww-3) px = ss_ww-3; if (py < 0) py = 0; if (py > ss_hh-3) py = ss_hh-3; pix1 = pixels1 + py * ss_rs + px * 3; // copy new image pixels to old image pix3 = pixels3 + py * ss_rs + px * 3; // right side memcpy(pix1, pix3, 9); memcpy(pix1 + ss_rs, pix3 + ss_rs, 9); memcpy(pix1 + 2 * ss_rs, pix3 + 2 * ss_rs, 9); pxL = ss_ww - px - 3; pix1 = pixels1 + py * ss_rs + pxL * 3; // left side pixels pix3 = pixels3 + py * ss_rs + pxL * 3; memcpy(pix1, pix3, 9); memcpy(pix1 + ss_rs, pix3 + ss_rs, 9); memcpy(pix1 + 2 * ss_rs, pix3 + 2 * ss_rs, 9); if (px == 0 || px == ss_ww-3) break; // reached edge of image if (py == 0 || py == ss_hh-3) 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 (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->pixbuf,px,py,ww,hh); // paint window rectangle gdk_cairo_set_source_pixbuf(cr,pixbuf,px,py); cairo_paint(cr); g_object_unref(pixbuf); pxL = ss_ww-3 - px2; pixbuf = gdk_pixbuf_new_subpixbuf(ss_pxbold->pixbuf,pxL,py,ww,hh); // left side rectangle gdk_cairo_set_source_pixbuf(cr,pixbuf,pxL,py); cairo_paint(cr); g_object_unref(pixbuf); } zmainloop(); if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // New image spirals outwards from the center void ss_spiral() { int px, py, ww, hh; int px1, py1, px2, py2; int ww2 = ss_ww/2, hh2 = ss_hh/2; float R, Rmax, dR, T; float r, r1, r2; float cosT, sinT; uint8 *pixels1, *pixels3, *pix1, *pix3; PIXBUF *pixbuf; double T0, Te, Tz; px = py = 0; // suppress compiler warnings pixels1 = ss_pxbold->pixels; pixels3 = ss_pxbnew->pixels; cairo_t *cr = draw_context_create(gdkwin,draw_context); Rmax = sqrt(ww2*ww2 + hh2*hh2); // max. line length, center to corner dR = Rmax / 8; // radius step T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te >= Tz) break; T = 16 * PI * Te / Tz; // 0 ... 8 revolutions R = Rmax * T / (16.0 * PI); // 0 ... Rmax r1 = R; // R segment if (T < 2 * PI) r1 = 0; r2 = R + dR + 4; cosT = cosf(T); sinT = sinf(T); 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-6) px = ss_ww-6; if (py < 0) py = 0; if (py > ss_hh-6) py = ss_hh-6; pix1 = pixels1 + py * ss_rs + px * 3; // copy new image pixels to old image pix3 = pixels3 + py * ss_rs + px * 3; memcpy(pix1, pix3, 18); memcpy(pix1 + ss_rs, pix3 + ss_rs, 18); memcpy(pix1 + 2 * ss_rs, pix3 + 2 * ss_rs, 18); memcpy(pix1 + 3 * ss_rs, pix3 + 3 * ss_rs, 18); memcpy(pix1 + 4 * ss_rs, pix3 + 4 * ss_rs, 18); memcpy(pix1 + 5 * ss_rs, pix3 + 5 * ss_rs, 18); if (px == 0 || px >= ss_ww-6) break; // reached edge of image if (py == 0 || py >= ss_hh-6) break; } 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) + 20; // 19.0 hh = abs(py2-py1) + 20; 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->pixbuf,px,py,ww,hh); // paint window rectangle gdk_cairo_set_source_pixbuf(cr,pixbuf,px,py); cairo_paint(cr); g_object_unref(pixbuf); zmainloop(); if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // An ellipse opens up from the center and expands outward void ss_ellipse() { PIXBUF *pixbuf; uint8 *pixels, *pix3; int Np, px1, py1, ww; float a, b, a2, b2, px, py, px2, py2; float ww2 = ss_ww / 2, hh2 = ss_hh / 2; double T0, Te, Tz; Np = 3; pixels = ss_pxbnew->pixels; cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done a = ww2 * 1.4 * Te / Tz; // ellipse a and b constants b = a * ss_hh / ss_ww; // from tiny to >> image size a2 = a * a; b2 = b * b; for (py = -b; py <= +b; py += Np) // py from top of ellipse to bottom { while (py < -(hh2-Np+1)) py += Np; if (py > hh2-Np+1) 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 + Np > ss_hh) py1 = ss_hh - Np; pix3 = pixels + py1 * ss_rs + px1 * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ww,Np,ss_rs,0,0); gdk_cairo_set_source_pixbuf(cr,pixbuf,px1,py1); cairo_paint(cr); g_object_unref(pixbuf); zmainloop(); } if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image splats onto old image one drop at a time void ss_raindrops() { PXB *pxbmix; PIXBUF *pixbuf; int rsmix; int px, py, px1, py1, px2, py2, cx, cy; int Rmin, Rmax, R, R2, dist2; int Ndrops = 3000; uint8 *pixels2, *pixels3; uint8 *pix2, *pix3 = 0; double T0, Te, Tz; pixels2 = ss_pxbnew->pixels; // source image cairo_t *cr = draw_context_create(gdkwin,draw_context); pxbmix = PXB_copy(ss_pxbold); // destination image pixels3 = pxbmix->pixels; rsmix = pxbmix->rs; Rmin = ss_ww * 0.01; // drop size range Rmax = ss_ww * 0.02; Ndrops = 3000; T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done cx = drandz() * ss_ww; // drop location on image cy = drandz() * ss_hh; R = drandz() * Rmax + Rmin; // drop size R2 = R * R; px1 = cx - R; if (px1 < 0) px1 = 0; py1 = cy - R; if (py1 < 0) py1 = 0; px2 = cx + R; if (px2 >= ss_ww) px2 = ss_ww; py2 = cy + R; if (py2 > ss_hh) py2 = ss_hh; for (py = py1; py < py2; py++) // copy drop area from new image for (px = px1; px < px2; px++) // to old image { dist2 = (px-cx) * (px-cx) + (py-cy) * (py-cy); if (dist2 > R2) continue; pix2 = pixels2 + py * ss_rs + px * 3; pix3 = pixels3 + py * rsmix + px * 3; memcpy(pix3,pix2,3); } pixbuf = gdk_pixbuf_new_subpixbuf(pxbmix->pixbuf,px1,py1,px2-px1,py2-py1); gdk_cairo_set_source_pixbuf(cr,pixbuf,px1,py1); cairo_paint(cr); g_object_unref(pixbuf); zmainloop(); zloop(ss_trantime / Ndrops); if (Fescape) break; } PXB_free(pxbmix); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image spreads from the middle to left and right edges // like a double-door swinging open void ss_doubledoor() { #define GPNFD(pix,ww,hh) gdk_pixbuf_new_from_data(pix,GDKRGB,0,8,ww,hh,ss_rs,0,0) PIXBUF *pixbuf; int bx, px; uint8 *pixels, *pix3; double T0, Te, Tz; pixels = ss_pxbnew->pixels; cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done bx = 1.0 * Te / Tz * ss_ww / 2; // bx = 0 ... ww/2 px = ss_ww / 2 - bx; if (px < 0) break; pix3 = pixels + 3 * px; // line from (ww/2-bx,0) to (ww/2-bx,hh-1) pixbuf = GPNFD(pix3,4,ss_hh); gdk_cairo_set_source_pixbuf(cr,pixbuf,px,0); cairo_paint(cr); g_object_unref(pixbuf); px = ss_ww / 2 + bx; if (px > ss_ww-4) break; pix3 = pixels + 3 * px; // line from (ww/2+bx,0) to (ww/2+bx,hh-1) pixbuf = GPNFD(pix3,4,ss_hh); gdk_cairo_set_source_pixbuf(cr,pixbuf,px,0); cairo_paint(cr); g_object_unref(pixbuf); zmainloop(); if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // Rotate from old image to new image. Horizontal rotate image 'page'. namespace ss_rotate_names { int cx1, cy1, cx2, cy2, cy3, cy4; uint8 *pixels1, *pixels2, *pixels3; int rsmix; } void ss_rotate() { using namespace ss_rotate_names; void * ss_rotate_thread1(void *arg); void * ss_rotate_thread2(void *arg); PIXBUF *pixbuf; float R; double T0, Te, Tz; pixels1 = ss_pxbold->pixels; pixels2 = ss_pxbnew->pixels; pixbuf = gdk_pixbuf_new(GDKRGB,0,8,ss_ww,ss_hh); // destination image rsmix = gdk_pixbuf_get_rowstride(pixbuf); pixels3 = gdk_pixbuf_get_pixels(pixbuf); cairo_t *cr = draw_context_create(gdkwin,draw_context); ss_trantime = 0.5 * ss_trantime; // 2 loops, each 1/2 time T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te >= Tz) break; // done R = Te / Tz; 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); do_wthreads(ss_rotate_thread1,ss_nwt); gdk_cairo_set_source_pixbuf(cr,pixbuf,0,0); cairo_paint(cr); zmainloop(); if (Fescape) break; // 20.0 } T0 = get_seconds(); // transition start time while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done R = Te / Tz; 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); do_wthreads(ss_rotate_thread2,ss_nwt); gdk_cairo_set_source_pixbuf(cr,pixbuf,0,0); cairo_paint(cr); zmainloop(); if (Fescape) break; } g_object_unref(pixbuf); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } void * ss_rotate_thread1(void *arg) { using namespace ss_rotate_names; int index = *((int *) (arg)); int px, py, ylo, yhi, vpx, vpy; uint8 *pix1, *pix3; float Rx, Ry; set_cpu_affinity(index); // stay on same cpu if possible 20.0 for (px = cx1 + 2 * index; px < cx2-1; px += 2 * ss_nwt) // speedup { Rx = 1.0 * (px - cx1) / (cx2 - cx1); ylo = cy1 + Rx * (cy2 - cy1); yhi = cy4 + Rx * (cy3 - cy4); for (py = ylo; py < yhi; py++) { Ry = 1.0 * (py - ylo) / (yhi - ylo); vpx = Rx * (ss_ww - 1); vpy = Ry * (ss_hh - 1); pix1 = pixels1 + vpy * ss_rs + vpx * 3; pix3 = pixels3 + py * rsmix + px * 3; memcpy(pix3,pix1,6); } } 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; set_cpu_affinity(index); // stay on same cpu if possible 20.0 for (px = cx2 + 2 * index; px < cx1-1; px += 2 * ss_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_rs + vpx * 3; pix3 = pixels3 + py * rsmix + px * 3; memcpy(pix3,pix2,6); } } 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; } void ss_fallover() { using namespace ss_fallover_names; void * ss_fallover_thread(void *arg); PIXBUF *pixbuf; float R; double T0, Te, Tz; pixels1 = ss_pxbold->pixels; // old image pixels2 = ss_pxbnew->pixels; // new image pixbuf = gdk_pixbuf_new(GDKRGB,0,8,ss_ww,ss_hh); // output image - mixture rsmix = gdk_pixbuf_get_rowstride(pixbuf); pixels3 = gdk_pixbuf_get_pixels(pixbuf); cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done R = 1.0 * Te / Tz; // 0 ... 1 cx1 = 0.2 * ss_ww * R; // top corners of falling old image cy1 = ss_hh * R; cx2 = ss_ww - cx1; cy2 = cy1; do_wthreads(ss_fallover_thread,ss_nwt); gdk_cairo_set_source_pixbuf(cr,pixbuf,0,0); cairo_paint(cr); zmainloop(); if (Fescape) break; } g_object_unref(pixbuf); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } void * ss_fallover_thread(void *arg) // construct combined image { using namespace ss_fallover_names; int index = *((int *) (arg)); int px, py, npix, vpx, vpy; int cx1a, cx2a; float R; uint8 *pix1, *pix3; set_cpu_affinity(index); // stay on same cpu if possible 20.0 for (py = index; py < ss_hh; py += ss_nwt) // py = 0 ... ss_hh { px = 0; // px = 0 ... ss_ww, new image npix = ss_ww; pix1 = pixels2 + py * ss_rs + px * 3; pix3 = pixels3 + py * rsmix + px * 3; memcpy(pix3,pix1,npix*3); if (py < cy1) continue; R = 1.0 * (py - cy1) / (ss_hh - cy1); // 0 ... 1 cx1a = cx1 * (1.0 - R); // cx1a = cx1 ... 0 cx2a = cx2 + R * (ss_ww - cx2); // cx2a = cx2 ... ss_ww for (px = cx1a; px < cx2a; px++) // px = cx1a ... cx2a { vpx = ss_ww * (px - cx1a) / (cx2a - cx1a); // vpx = 0 ... ss_ww vpy = ss_hh * R; // vpy = 0 ... ss_hh pix1 = pixels1 + vpy * ss_rs + vpx * 3; pix3 = pixels3 + py * rsmix + px * 3; memcpy(pix3,pix1,3); } } pthread_exit(0); return 0; } // ------------------------------------------------------------------------------ // old image deforms from flat to sphere and then shrinks to reveal new image namespace ss_spheroid_names { float Cx, Cy, D; PXB *pxbold, *pxbnew; uint8 *pixels1, *pixels3; float *s1mem, *s2mem, Rmax; } void ss_spheroid() { using namespace ss_spheroid_names; void * ss_spheroid_thread(void *arg); int ii, cc, px, py, dx, dy; double T0, Te, Tz; float F; cc = ss_ww * ss_hh * sizeof(float); s1mem = (float *) zmalloc(cc); Rmax = 1.0 + 0.5 * sqrtf(ss_ww * ss_ww + ss_hh * ss_hh); cc = 10.1 * Rmax * sizeof(float); // float rounding s2mem = (float *) zmalloc(cc); pxbold = PXB_copy(ss_pxbold); // old image at full size pixels1 = pxbold->pixels; Cx = ss_ww / 2; // center of image Cy = ss_hh / 2; for (py = 0; py < ss_hh; py++) // pre-calculate for (px = 0; px < ss_ww; px++) { dx = px - Cx; dy = py - Cy; ii = py * ss_ww + px; s1mem[ii] = sqrtf(dx*dx + dy*dy); // dist. from center to pixel } cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done F = 1.0 - Te / Tz; // 1 ... 0 float D1 = pow(F,10) * 8 * ss_ww; float D2 = pow(F,6) * 3 * ss_ww; float D3 = F * 2 * ss_ww; D = D1 + D2 + D3 + 10; pxbnew = PXB_copy(ss_pxbnew); // new image at full size pixels3 = pxbnew->pixels; do_wthreads(ss_spheroid_thread,ss_nwt); gdk_cairo_set_source_pixbuf(cr,pxbnew->pixbuf,0,0); // paint new image cairo_paint(cr); PXB_free(pxbnew); zmainloop(); if (Fescape) break; } zfree(s1mem); zfree(s2mem); PXB_free(pxbold); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } void * ss_spheroid_thread(void *arg) { using namespace ss_spheroid_names; int index = *((int *) arg); int ii, px0, py0, px3, py3, dx, dy; float px1, py1, s1, s2, T; uint8 *pix0, *pix1, *pix2, *pix3, *pixx, *pixx2; float f0, f1, f2, f3; set_cpu_affinity(index); // stay on same cpu if possible 20.0 for (ii = 0; ii < 10 * Rmax; ii++) // pre-calculate { s1 = 0.1 * ii; T = s1 * PI / D; if (s1 == 0) s2mem[ii] = 0; else if (T > 1.0) s2mem[ii] = -1; else s2mem[ii] = D / PI * asinf(T) / s1; } for (py3 = 2 * index; py3 < ss_hh-1; py3 += 2 * ss_nwt) // loop all output pixels for (px3 = 0; px3 < ss_ww-1; px3 += 2) { /*** dx = px3 - Cx; // code without pre-calculations dy = py3 - Cy; s1 = sqrtf(dx*dx + dy*dy); // dist. from center to output pixel if (s1 == 0) continue; T = s1 * PI / D; // sine of subtended angle if (T > 1.0) continue; s2 = D / PI * asinf(T); // corresp. dist. on sphere px1 = Cx + dx * s2 / s1; // input v.pixel py1 = Cy + dy * s2 / s1; ***/ dx = px3 - Cx; dy = py3 - Cy; ii = py3 * ss_ww + px3; s1 = s1mem[ii]; // dist. from center to output pixel ii = 10 * s1; s2 = s2mem[ii]; // corresp. dist. on sphere / s1 if (s2 < 0) continue; px1 = Cx + dx * s2; // input v.pixel py1 = Cy + dy * s2; // inline vpixel() for speed px0 = px1; // px0/py0: integer px1/py1 py0 = py1; if (px0 < 0 || py0 < 0) continue; if (px0 > ss_ww-3 || py0 > ss_hh-3) continue; f0 = (px0+1 - px1) * (py0+1 - py1); // overlap of (px,py) f1 = (px0+1 - px1) * (py1 - py0); // in each of the 4 pixels f2 = (px1 - px0) * (py0+1 - py1); f3 = (px1 - px0) * (py1 - py0); pix0 = pixels1 + py0 * ss_rs + px0 * 3; // pixel (px0,py0) pix1 = pix0 + ss_rs; // (px0,py0+1) pix2 = pix0 + 3; // (px0+1,py0) pix3 = pix1 + 3; // (px0+1,py0+1) pixx = pixels3 + py3 * ss_rs + px3 * 3; // input v.pixel >> output pixel pixx[0] = f0 * pix0[0] + f1 * pix1[0] + f2 * pix2[0] + f3 * pix3[0]; pixx[1] = f0 * pix0[1] + f1 * pix1[1] + f2 * pix2[1] + f3 * pix3[1]; pixx[2] = f0 * pix0[2] + f1 * pix1[2] + f2 * pix2[2] + f3 * pix3[2]; pixx[3] = f0 * pix0[3] + f1 * pix1[3] + f2 * pix2[3] + f3 * pix3[3]; pixx[4] = f0 * pix0[4] + f1 * pix1[4] + f2 * pix2[4] + f3 * pix3[4]; pixx[5] = f0 * pix0[5] + f1 * pix1[5] + f2 * pix2[5] + f3 * pix3[5]; pixx2 = pixx + ss_rs; memcpy(pixx2,pixx,6); } pthread_exit(0); return 0; } // ------------------------------------------------------------------------------ // new image turns up from lower right corner, like a book page void ss_turnpage() { PXB *pixbuf3; uint8 *pixels1, *pixels2, *pixels3; uint8 *pix1, *pix2, *pix3; int pxA, ppxA, pxS; int px1, py1, px3, py3; int cc, np1, np2; float C; double T0, Te, Tz, Prange; pixels1 = ss_pxbold->pixels; // old image pixels2 = ss_pxbnew->pixels; // new image pixbuf3 = PXB_copy(ss_pxbold); // output image - mixture pixels3 = pixbuf3->pixels; // (initially = old image) cairo_t *cr = draw_context_create(gdkwin,draw_context); Prange = 1.31 * (ss_ww + ss_hh); // pxA loop range np1 = Prange / 300; // step size np2 = 5; pxA = ppxA = ss_ww; T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done pxA = ss_ww - Prange * Te / Tz; np1 = ppxA - pxA; if (np1 < 1) np1 = 1; for (px1 = pxA, py1 = ss_hh-1; px1 < ss_ww && py1 >= 0; px1++, py1--) { pix2 = pixels2 + py1 * ss_rs + px1 * 3; // new image row pix3 = pixels3 + py1 * ss_rs + px1 * 3; // output image row cc = 3 * (ss_ww - px1); if (px1 < 0) { pix2 -= 3 * px1; pix3 -= 3* px1; cc += 3 * px1; } if (cc < 1) continue; if (cc > 3 * ss_ww) cc = cc / 2; memcpy(pix3,pix2,cc); // paint new image from px1 to right edge } for (pxS = pxA; pxS < ss_ww-np2; pxS += np2) // point S moves from point A to the right { C = 0.53 * (pxS - pxA) / (ss_ww - pxA); C = C * (pxS - pxA); for (px1 = pxS, py1 = ss_hh-1; px1 < ss_ww && py1 >= 0; px1++, py1--) { px3 = px1 - C; // dest pixel = source pixel py3 = py1 - C; // offset in NW direction if (px3 < 0 || px3 > ss_ww-1) continue; if (py3 < 0 || py3 > ss_hh-1) continue; pix1 = pixels1 + py1 * ss_rs + px1 * 3; // source pixel --> dest pixel pix3 = pixels3 + py3 * ss_rs + px3 * 3; memcpy(pix3,pix1,3*np2); } } gdk_cairo_set_source_pixbuf(cr,pixbuf3->pixbuf,0,0); // paint image cairo_paint(cr); zmainloop(); if (Fescape) break; } PXB_free(pixbuf3); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // French door: old image swings away left and right to reveal new image. namespace ss_frenchdoor_names { PXB *pxbmix; PIXBUF *pixbuf; // output image float R, ww2; uint8 *pixels1, *pixels3; int ww3, ww4, sww; } void ss_frenchdoor() { using namespace ss_frenchdoor_names; #define GPNFD(pix,ww,hh) \ gdk_pixbuf_new_from_data(pix,GDKRGB,0,8,ww,hh,ss_rs,0,0) void * ss_frenchdoor_thread(void *arg); double T0, Te, Tz; ww2 = 0.5 * ss_ww; cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te >= Tz) break; // done R = Te / Tz; // 0 ... 1 pixels1 = ss_pxbold->pixels; // old image pxbmix = PXB_copy(ss_pxbnew); // mixed image pixels3 = pxbmix->pixels; do_wthreads(ss_frenchdoor_thread,ss_nwt); sww = ww2 / 30 + 6; // left side ww3 = (1 - R) * ww2 + sww; pixbuf = GPNFD(pixels3,ww3,ss_hh); gdk_cairo_set_source_pixbuf(cr,pixbuf,0,0); cairo_paint(cr); g_object_unref(pixbuf); ww4 = (1 + R) * ww2 - sww; // right side pixbuf = GPNFD(pixels3+ww4*3,ww3+sww,ss_hh); gdk_cairo_set_source_pixbuf(cr,pixbuf,ww4,0); cairo_paint(cr); g_object_unref(pixbuf); PXB_free(pxbmix); zmainloop(); if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } void * ss_frenchdoor_thread(void *arg) { using namespace ss_frenchdoor_names; int index = *((int *) arg); uint8 *pix1, *pix3; int px1, py1, px3, py3, px3L = -1; float F; set_cpu_affinity(index); // stay on same cpu if possible 20.0 for (px1 = index; px1 < ww2; px1 += ss_nwt) // 0 >> ww2 { px3 = (1 - R) * px1; // 0 >> ww2 - X if (px3 == px3L) continue; px3L = px3; for (py1 = 0; py1 < ss_hh-1; py1 += 2) // 0 >> ss_hh { F = 0.2 * ss_hh * R * px1 / ww2; // 0 >> max py3 = F + (1.0 * py1 / ss_hh) * (ss_hh - 2 * F); // F >> ss_hh - F pix1 = pixels1 + py1 * ss_rs + px1 * 3; pix3 = pixels3 + py3 * ss_rs + px3 * 3; memcpy(pix3,pix1,3); memcpy(pix3 + ss_rs, pix1 + ss_rs, 3); } } for (px1 = ww2 + index; px1 < ss_ww; px1 += ss_nwt) // ww2 >> ss_ww { px3 = R * ss_ww + (1 - R) * px1; // ww2 + X >> ss_ww if (px3 == px3L) continue; px3L = px3; for (py1 = 0; py1 < ss_hh-1; py1 += 2) // 0 >> ss_hh { F = 0.2 * ss_hh * R * (ss_ww - px1) / ww2; // max >> 0 py3 = F + (1.0 * py1 / ss_hh) * (ss_hh - 2 * F); // F >> ss_hh - F pix1 = pixels1 + py1 * ss_rs + px1 * 3; pix3 = pixels3 + py3 * ss_rs + px3 * 3; memcpy(pix3,pix1,3); memcpy(pix3 + ss_rs, pix1 + ss_rs, 3); } } pthread_exit(0); return 0; } // ------------------------------------------------------------------------------ // turn a cube to a new face with new image namespace ss_turncube_names { float To, Te, Tz; // start, elapsed, goal times PXB *pxbmix1, *pxbmix2, *pxbtemp; float F, edge; uint8 *pixels1, *pixels2, *pixels3; pthread_t tid; } void ss_turncube() // speedup 19.0 { using namespace ss_turncube_names; void ss_turncube_start_step(); void ss_turncube_wait_step(); pixels1 = ss_pxbold->pixels; pixels2 = ss_pxbnew->pixels; pxbmix1 = PXB_copy(ss_pxbold); pxbmix2 = PXB_copy(ss_pxbold); cairo_t *cr = draw_context_create(gdkwin,draw_context); Tz = ss_trantime; // transition goal time To = get_seconds(); // transition start time zsleep(0.001); pixels3 = pxbmix1->pixels; ss_turncube_start_step(); // make first image while (true) { ss_turncube_wait_step(); // wait for image done if (F >= 1) break; if (Fescape) break; // 20.0 pxbtemp = pxbmix1; pxbmix1 = pxbmix2; pxbmix2 = pxbtemp; pixels3 = pxbmix1->pixels; ss_turncube_start_step(); // start next image gdk_cairo_set_source_pixbuf(cr,pxbmix2->pixbuf,0,0); // output image cairo_paint(cr); zmainsleep(0.002); // mysteriously smoother 20.0 } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // output final image cairo_paint(cr); draw_context_destroy(draw_context); PXB_free(pxbmix1); PXB_free(pxbmix2); return; } void ss_turncube_start_step() { using namespace ss_turncube_names; void * ss_turncube_thread(void *); tid = start_Jthread(ss_turncube_thread,null); return; } void ss_turncube_wait_step() { using namespace ss_turncube_names; wait_Jthread(tid); return; } void * ss_turncube_thread(void *) { using namespace ss_turncube_names; void * ss_turncube_wthread(void *); Te = get_seconds() - To; // elapsed time F = Te / Tz; // transition status 0 >> 1 if (F >= 1) pthread_exit(0); edge = F * ss_ww; // 0 ... ss_ww int cc = gdk_pixbuf_get_byte_length(pxbmix1->pixbuf); memset(pixels3,0,cc); do_wthreads(ss_turncube_wthread,ss_nwt); // 20.0 pthread_exit(0); } void * ss_turncube_wthread(void *arg) { using namespace ss_turncube_names; int index = *((int *) arg); int px1, py1, px2, py2; uint8 *pix1, *pix2; float c1, c2, c3, c4, c33, c44; set_cpu_affinity(index); // stay on same cpu if possible 20.0 c1 = edge / ss_ww; // 0 ... 1 c2 = 1.0 - c1; // 1 ... 0 c3 = 0.2 * c2; // 0.2 ... 0 c4 = 0.2 * c1; // 0 ... 0.2 for (py1 = index * 3; py1 < ss_hh-3; py1 += ss_nwt * 3) // loop old image pixels 20.0 for (px1 = 0; px1 < ss_ww-3; px1 += 3) { c44 = c4 * px1 / ss_ww; // 0 ... c4 px2 = edge + 1.0 * px1 * (ss_ww - edge) / ss_ww; // image on right cube face if (px2 > ss_ww - 2) break; py2 = c44 * ss_hh + (1.0 - c44 - c44) * py1; // c44 * ss_hh ... (1 - c44) * ss_hh pix1 = pixels1 + py1 * ss_rs + px1 * 3; // move pixel image to cube face pix2 = pixels3 + py2 * ss_rs + px2 * 3; memcpy(pix2,pix1,9); pix1 += ss_rs; pix2 += ss_rs; memcpy(pix2,pix1,9); pix1 += ss_rs; pix2 += ss_rs; memcpy(pix2,pix1,9); } for (py1 = index * 3; py1 < ss_hh-3; py1 += ss_nwt * 3) // loop new image pixels 20.0 for (px1 = 0; px1 < ss_ww-3; px1 += 3) { c33 = c3 * (1.0 - 1.0 * px1 / ss_ww); // c3 ... 0 px2 = c1 * px1; // image on left cube face py2 = c33 * ss_hh + (1.0 - c33 - c33) * py1; // c33 * ss_hh ... (1 - c33) * ss_hh pix1 = pixels2 + py1 * ss_rs + px1 * 3; // move pixel image to cube face pix2 = pixels3 + py2 * ss_rs + px2 * 3; memcpy(pix2,pix1,9); pix1 += ss_rs; pix2 += ss_rs; memcpy(pix2,pix1,9); pix1 += ss_rs; pix2 += ss_rs; memcpy(pix2,pix1,9); } pthread_exit(0); return 0; } // ------------------------------------------------------------------------------ // new image rotates over old image in many radial segments void ss_windmill() { int px, py; int ww2 = ss_ww/2, hh2 = ss_hh/2; float R, Rmax, T1, T2, T; float cosT, sinT; uint8 *pixels1, *pixels3, *pix1, *pix3; double T0, Te, Tz; pixels1 = ss_pxbold->pixels; pixels3 = ss_pxbnew->pixels; Rmax = sqrt(ww2*ww2 + hh2*hh2); // max. line length, center to corner cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done T1 = Te / Tz * PI / 9; for (T2 = 0; T2 < 2*PI; T2 += PI/9) // segments { T = T1 + T2; // segment and step cosT = cosf(T); sinT = sinf(T); for (R = 0; R < Rmax; R++) // radial line from center to edge { px = ww2 + R * cosT; py = hh2 + R * sinT; if (px < 0 || px > ss_ww-3) break; if (py < 0 || py > ss_hh-3) break; pix1 = pixels1 + py * ss_rs + px * 3; // copy new image pixel to old image pix3 = pixels3 + py * ss_rs + px * 3; memcpy(pix1,pix3,9); memcpy(pix1 + ss_rs, pix3 + ss_rs, 9); memcpy(pix1 + 2 * ss_rs, pix3 + 2 * ss_rs, 9); } } gdk_cairo_set_source_pixbuf(cr,ss_pxbold->pixbuf,0,0); cairo_paint(cr); zmainloop(); if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // Old image is increasingly / decreasingly pixelated to reveal new image void ss_pixelize() { PXB *pxbmix; int px, py, px1, py1, px2, py2; int ii, cc, Npix, blocksize; int Rsum, Gsum, Bsum, Ravg, Gavg, Bavg; uint8 *pixels1, *pixels2, *pixels3, *pix3; float F1, F2; double T0, Te, Tz; pxbmix = PXB_copy(ss_pxbnew); pixels1 = ss_pxbold->pixels; pixels2 = ss_pxbnew->pixels; pixels3 = pxbmix->pixels; cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done F1 = Te / Tz; // new image part, 0 ... 1 F2 = 1 - F1; // old image part, 1 ... 0 cc = gdk_pixbuf_get_byte_length(pxbmix->pixbuf); // pxbmix = mix of old + new images for (ii = 0; ii < cc; ii++) pixels3[ii] = F1 * pixels2[ii] + F2 * pixels1[ii]; // pxbold + pxbnew >> pxbmix blocksize = 0.05 * ss_ww; if (F1 < 0.5) blocksize = 2 + blocksize * F1; // pixel block size, 2 ... max ... 2 else blocksize = 2 + blocksize * F2; for (py1 = 0; py1 < ss_hh; py1 += blocksize) // loop pixel blocks for (px1 = 0; px1 < ss_ww; px1 += blocksize) { py2 = py1 + blocksize; // block region in image if (py2 > ss_hh) py2 = ss_hh; px2 = px1 + blocksize; if (px2 > ss_ww) px2 = ss_ww; Rsum = Gsum = Bsum = Npix = 0; for (py = py1; py < py2; py++) // loop pixels in pixel block for (px = px1; px < px2; px++) { pix3 = pixels3 + py * ss_rs + px * 3; Rsum += pix3[0]; // sum pixel RGB values Gsum += pix3[1]; Bsum += pix3[2]; Npix++; } Ravg = Rsum / Npix; // mean pixel RGB values for block Gavg = Gsum / Npix; Bavg = Bsum / Npix; for (py = py1; py < py2; py++) // loop pixels in pixel block for (px = px1; px < px2; px++) { pix3 = pixels3 + py * ss_rs + px * 3; pix3[0] = Ravg; // set all pixel RGB values to mean pix3[1] = Gavg; pix3[2] = Bavg; } } gdk_cairo_set_source_pixbuf(cr,pxbmix->pixbuf,0,0); cairo_paint(cr); zmainloop(); if (Fescape) break; } PXB_free(pxbmix); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // Old image twists 0 >> 360 deg. as new image untwists -360 >> 0 deg. // The old image fades-out as the new image fades-in. namespace twist2_names { PXB *PXBmix; int cx, cy; float Told, Tnew; float F1, F2; float *D, *Tp; float Dmax; } void ss_twist() { using namespace twist2_names; void * ss_twist_thread(void *arg); int ii, px, py, Dx, Dy; double T0, Te, Tz; cx = ss_ww / 2; // image center cy = ss_hh / 2; Dmax = sqrtf(cx * cx + cy * cy); // distance to corner ii = ss_ww * ss_hh; D = (float *) zmalloc(ii * sizeof(float)); // precalculated factors Tp = (float *) zmalloc(ii * sizeof(float)); // depending only on px, py for (py = 0; py < ss_hh; py++) // loop all pixels for (px = 0; px < ss_ww; px++) { ii = py * ss_ww + px; Dx = px - cx; // px/py relative to cx/cy Dy = py - cy; D[ii] = Dx * Dx + Dy * Dy; // distance pixel to center if (D[ii] == 0) continue; D[ii] = sqrtf(D[ii]); Tp[ii] = asinf(Dy/D[ii]); // angle of pixel line to center if (Dx < 0) { if (Dy > 0) Tp[ii] = PI - Tp[ii]; else Tp[ii] = - PI - Tp[ii]; } } PXBmix = PXB_copy(ss_pxbold); cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done F1 = Te / Tz; // new image part, 0 ... 1 F2 = 1 - F1; // old image part, 1 ... 0 Told = 2 * PI * F1; // old image twist 0 ... 360 deg. Tnew = - 2 * PI + Told; // new image twist -360 ... deg. /* Tnew = -Tnew; */ // opposite directions do_wthreads(ss_twist_thread,ss_nwt); gdk_cairo_set_source_pixbuf(cr,PXBmix->pixbuf,0,0); cairo_paint(cr); zmainloop(); if (Fescape) break; } PXB_free(PXBmix); zfree(D); zfree(Tp); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } void * ss_twist_thread(void *arg) { using namespace twist2_names; int index = *((int *) arg); int ii, px, py, qx, qy; float DN, Tq, T; uint8 *pixmix, *vpix, pix1[3], pix2[3], pix3[3]; set_cpu_affinity(index); // stay on same cpu if possible 20.0 for (py = 2 * index; py < ss_hh-1; py += 2 * ss_nwt) // loop all pixels for (px = 0; px < ss_ww-1; px += 2) { ii = py * ss_ww + px; DN = D[ii]/Dmax; // distance from center, 0.0 ... 1.0 T = Told * DN; // old image rotation at distance Tq = Tp[ii] + T; // rotated pixel angle qx = D[ii] * cosf(Tq) + cx; // rotated pixel position qy = D[ii] * sinf(Tq) + cy; if (qx > 0 && qx < ss_ww && qy > 0 && qy < ss_hh) { vpix = PXBpix(ss_pxbold,qx,qy); memcpy(pix1,vpix,3); } else memset(pix1,0,3); T = Tnew * DN; // same for new image Tq = Tp[ii] + T; qx = D[ii] * cosf(Tq) + cx; qy = D[ii] * sinf(Tq) + cy; if (qx > 0 && qx < ss_ww && qy > 0 && qy < ss_hh) { vpix = PXBpix(ss_pxbnew,qx,qy); memcpy(pix2,vpix,3); } else memset(pix2,0,3); pixmix = PXBpix(PXBmix,px,py); // blend the twisted image pix3[0] = F2 * pix1[0] + F1 * pix2[0]; pix3[1] = F2 * pix1[1] + F1 * pix2[1]; pix3[2] = F2 * pix1[2] + F1 * pix2[2]; pixmix[0] = pix3[0]; pixmix[1] = pix3[1]; pixmix[2] = pix3[2]; pixmix[3] = pix3[0]; pixmix[4] = pix3[1]; pixmix[5] = pix3[2]; pixmix += ss_rs; pixmix[0] = pix3[0]; pixmix[1] = pix3[1]; pixmix[2] = pix3[2]; pixmix[3] = pix3[0]; pixmix[4] = pix3[1]; pixmix[5] = pix3[2]; } pthread_exit(0); return 0; } // ------------------------------------------------------------------------------ // X-shaped region opens up from the center to reveal new image void ss_Xopen() { PXB *pxbmix; int xdisp, ydisp; int px1, py1, px2, py2; int pxL, pxH; int ww = ss_ww, hh = ss_hh, rs = ss_rs; int ww2 = ww/2, hh2 = hh/2; uint8 *pixels1, *pixels2; uint8 *pix1, *pix2; float M = 1.0 * hh / ww; double T0, Te, Tz; cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done pxbmix = PXB_copy(ss_pxbnew); pixels1 = ss_pxbold->pixels; // old image pixels2 = pxbmix->pixels; // new image + old image overlay ydisp = Te / Tz * hh2; xdisp = ydisp / M; for (py1 = ydisp; py1 < hh2; py1++) // top triangle { pxL = py1/M; pxH = ww - pxL; for (px1 = pxL; px1 < pxH; px1++) { py2 = py1 - ydisp; px2 = px1; pix1 = pixels1 + py1 * rs + px1 * 3; pix2 = pixels2 + py2 * rs + px2 * 3; memcpy(pix2,pix1,3); } } for (py1 = ydisp; py1 < hh - ydisp; py1++) // right triangle { pxL = ww2 + abs(py1 - hh2)/M; pxH = ww - xdisp; for (px1 = pxL; px1 < pxH; px1++) { py2 = py1; px2 = px1 + xdisp; pix1 = pixels1 + py1 * rs + px1 * 3; pix2 = pixels2 + py2 * rs + px2 * 3; memcpy(pix2,pix1,3); } } for (py1 = hh2; py1 < hh - ydisp; py1++) // bottom triangle { pxL = ww2 - (py1 - hh2)/M; pxH = ww - pxL; for (px1 = pxL; px1 < pxH; px1++) { py2 = py1 + ydisp; px2 = px1; pix1 = pixels1 + py1 * rs + px1 * 3; pix2 = pixels2 + py2 * rs + px2 * 3; memcpy(pix2,pix1,3); } } for (py1 = ydisp; py1 < hh - ydisp; py1++) // left triangle { pxL = xdisp; pxH = ww2 - abs(py1 - hh2)/M; for (px1 = pxL; px1 < pxH; px1++) { py2 = py1; px2 = px1 - xdisp; pix1 = pixels1 + py1 * rs + px1 * 3; pix2 = pixels2 + py2 * rs + px2 * 3; memcpy(pix2,pix1,3); } } gdk_cairo_set_source_pixbuf(cr,pxbmix->pixbuf,0,0); cairo_paint(cr); PXB_free(pxbmix); zmainloop(); if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image presses out from center and squishes old image to the edges void ss_squish() { PXB *pxbout; uint8 *pixold, *pixnew, *pixout; uint8 *pix1, *pix2; int ww, hh, rs, cx, cy, cmin; int cc, ii, Q; int px, py, qx, qy, vx, vy, ex, ey; float R, Rmax; float *De, *Dp; // edge and pixel distance from center float *Ex, *Ey; // edge coordinates float M, V, Ra; double T0, Te, Tz; ww = ss_ww; hh = ss_hh; rs = ss_rs; cx = ww/2; cy = hh/2; Ra = 1.0 * hh/ww; Rmax = sqrtf(cx*cx + cy*cy); // distance (0,0) to corner cmin = cy; if (cx < cy) cmin = cx; pxbout = PXB_copy(ss_pxbold); // output image pixold = ss_pxbold->pixels; // old image pixnew = ss_pxbnew->pixels; // new image pixout = pxbout->pixels; cc = ww * hh * sizeof(float); // allocate distance arrays De = (float *) zmalloc(cc); // distance (0,0) to edge Dp = (float *) zmalloc(cc); // distance (0,0) to (px,py) Ex = (float *) zmalloc(cc); // line (0,0) to (px,py) Ey = (float *) zmalloc(cc); // extended to edge (Ex,Ey) for (py = 0; py < hh; py++) // compute distances to center for (px = 0; px < ww; px++) // and to edge for each pixel { qx = px - cx; // convert to center origin qy = py - cy; if (qx == 0) { M = 1000; // qx = 0 >> slope = huge if (qy > 0) Q = 4; else Q = 2; } else M = 1.0 * qy / qx; // slope of line (0,0) to (px,py) Q = 0; ex = ey = 0; if (qx > 0) { // extended line (0,0) to (px,py) if (qy > 0) { // intersects which edge? if (M > Ra) Q = 4; // bottom edge else Q = 1; // right edge } else if (-M > Ra) Q = 2; // top edge else Q = 1; // right edge } else if (qx < 0) { if (qy > 0) { if (-M > Ra) Q = 4; // bottom edge else Q = 3; // left edge } else { if (M > Ra) Q = 2; // top edge else Q = 3; // left edge } } if (Q == 1) { // get edge intersect coordinates ex = ww; ey = cy + M * (ex-cx); } else if (Q == 2) { ey = 0; ex = cx - cy / M; } else if (Q == 3) { ex = 0; ey = cy - cx * M; } else if (Q == 4) { ey = hh; ex = cx + (ey-cy) / M; } ii = ww * py + px; De[ii] = sqrtf((ex-cx)*(ex-cx) + (ey-cy)*(ey-cy)); // distance (0,0) to edge (ex,ey) Dp[ii] = sqrtf(qx*qx + qy*qy); // distance (0,0) to (px,py) Ex[ii] = ex - cx; // line (0.0) to (px,py) Ey[ii] = ey - cy; // extended to edge (ex,ey) } cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done R = Te / Tz * Rmax; for (py = 0; py < hh-1; py += 2) // loop all image pixels for (px = 0; px < ww-1; px += 2) { ii = ww * py + px; qx = px - cx; // convert to center origin qy = py - cy; if (Dp[ii] < R) // new image from center to R { V = (1.0 - R/Rmax) * (cmin/R) + (R/Rmax); vx = cx + V * qx; vy = cy + V * qy; if (vx < 0 || vx > ww-1) continue; if (vy < 0 || vy > hh-1) continue; pix1 = pixnew + vy * rs + vx * 3; // new image pixel >> output image pix2 = pixout + py * rs + px * 3; memcpy(pix2,pix1,6); memcpy(pix2+rs,pix1+rs,6); } else if (R < De[ii]) // old image from R to edge { V = (Dp[ii] - R) / (De[ii] - R); // source pixel position vx = cx + V * Ex[ii]; vy = cy + V * Ey[ii]; if (vx < 0 || vx > ww-1) continue; if (vy < 0 || vy > hh-1) continue; pix1 = pixold + vy * rs + vx * 3; // old image pixel >> output image pix2 = pixout + py * rs + px * 3; memcpy(pix2,pix1,6); memcpy(pix2+rs,pix1+rs,6); } } gdk_cairo_set_source_pixbuf(cr,pxbout->pixbuf,0,0); cairo_paint(cr); zmainloop(); if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); zfree(De); zfree(Dp); zfree(Ex); zfree(Ey); PXB_free(pxbout); return; } // ------------------------------------------------------------------------------ // old image disintegrates to reveal new image // pieces detach randomly and fall to the bottom void ss_disintegrate() { PXB *pxbmix; uint8 *pixels1, *pixels2, *pixels3; uint8 *pix1, *pix3; double T0, Te, Tz; int cc, row, col, posn; int tww = ss_ww / 32, thh = ss_hh / 32; // tile width, height float time0[32][32], time, ftime; cairo_t *cr = draw_context_create(gdkwin,draw_context); pixels1 = ss_pxbold->pixels; // old image pixels2 = ss_pxbnew->pixels; // new image pxbmix = PXB_copy(ss_pxbold); // mixed image pixels3 = pxbmix->pixels; for (row = 0; row < 32; row++) // initialize tiles for (col = 0; col < 32; col++) time0[row][col] = 0.6 * drandz(); // fall start, time 0 .. 0.6 T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done time = Te / Tz; // 0 .. 1 cc = gdk_pixbuf_get_byte_length(pxbmix->pixbuf); memcpy(pixels3,pixels2,cc); // mixed image = new image for (row = 0; row < 32; row++) // loop all tiles for (col = 0; col < 32; col++) { posn = row * thh; // initial vertical position if (time >= time0[row][col]) { // tile is falling ftime = time - time0[row][col]; // fall time posn += ss_hh * (ftime * ftime / 0.16); // new fall position } if (posn >= ss_hh - thh) continue; // finished falling, no paint pix1 = pixels1 + row * thh * ss_rs + col * tww * 3; // tile position in old image pix3 = pixels3 + posn * ss_rs + col * tww * 3; // tile position in mixed image for (int ii = 0; ii < thh; ii++) { // copy tile from old image memcpy(pix3,pix1,tww*3); // to mixed image pix1 += ss_rs; pix3 += ss_rs; } } gdk_cairo_set_source_pixbuf(cr,pxbmix->pixbuf,0,0); // paint mixed image to window cairo_paint(cr); zmainloop(); if (Fescape) break; } PXB_free(pxbmix); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image moves in as bars from the left and right, interleaved /*** ________________________ |______| --> ______| |______ <-- |______| new image bars move in from left and right |______| --> ______| to cover the old image | <-- |______| | etc... | |________________________| ***/ void ss_interleave() { PIXBUF *pixbuf; int Nbars = 20, bar, barww, barhh; int px, py; double T0, Te, Tz; cairo_t *cr = draw_context_create(gdkwin,draw_context); T0 = get_seconds(); // transition start time Tz = ss_trantime; // transition time (goal) while (true) { Te = get_seconds() - T0; // elapsed transition time 19.0 if (Te > Tz) break; // done barww = ss_ww * Te / Tz; // bar length, 0 ... ss_ww if (barww < 2) barww = 2; barhh = ss_hh / Nbars; // bar height, fixed for (bar = 0; bar < Nbars; bar += 2) // even bars from left side { px = ss_ww - barww; py = bar * barhh; pixbuf = gdk_pixbuf_new_subpixbuf(ss_pxbnew->pixbuf,px,py,barww,barhh); gdk_cairo_set_source_pixbuf(cr,pixbuf,0,py); cairo_paint(cr); g_object_unref(pixbuf); } for (bar = 1; bar < Nbars; bar += 2) // odd bars from right side { px = 0; py = bar * barhh; pixbuf = gdk_pixbuf_new_subpixbuf(ss_pxbnew->pixbuf,px,py,barww,barhh); gdk_cairo_set_source_pixbuf(cr,pixbuf,ss_ww - barww, py); cairo_paint(cr); g_object_unref(pixbuf); } zmainloop(); if (Fescape) break; } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew->pixbuf,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ namespace ss_zoom_names { PXB *pxb1 = 0, *pxb2 = 0, *pxb3 = 0; int ww1, hh1, ww1a, hh1a, ww2, hh2; int cropw, croph, margw, margh, dm; float R1, R2, zoomnow; int orgx1, orgy1, orgx2, orgy2; int zcx1, zcy1, zcx2, zcy2; pthread_t tid; } // initialize for image zoom out or zoom in // prepare 1x image, cropped or with added margins for window width/height ratio void ss_zoom_setup(char *file) // overhauled for speed 19.0 { using namespace ss_zoom_names; if (pxb1) PXB_free(pxb1); pxb1 = PXB_load(file,1); // load 1x image file if (! pxb1) return; PXB_subalpha(pxb1); // strip alpha channel if present ww1 = pxb1->ww; // image dimensions, 1x hh1 = pxb1->hh; zcx1 = ww1 * 0.01 * ss_zoomlocx; // zoom center location zcy1 = hh1 * 0.01 * ss_zoomlocy; cropw = croph = 0; // image crop sizes margw = margh = 0; // added margin sizes R1 = 1.0 * ww1 / hh1; // image width/height ratio R2 = 1.0 * ss_ww / ss_hh; // window width/height ratio if (fabsf(R1-R2) / R2 < 0.01 * ss_cliplimit) // difference < clip limits { if (R1 > R2) { hh1a = hh1; // crop image width ww1a = R2 * hh1; } else { ww1a = ww1; // crop image height hh1a = ww1 / R2; } cropw = (ww1-ww1a)/2; // width reduction, left and right croph = (hh1-hh1a)/2; // height reduction, top and bottom pxb2 = PXB_subpxb(pxb1,cropw,croph,ww1a,hh1a); // final 1x image, cropped PXB_free(pxb1); // to have ratio ss_ww/ss_hh pxb1 = pxb2; pxb2 = 0; zcx1 -= cropw; // adjust zoom center for crop zcy1 -= croph; } else // difference > clip limits { if (R1 > R2) { ww1a = ww1; // add height margins hh1a = ww1 / R2; } else { hh1a = hh1; // add width margins ww1a = R2 * hh1; } margw = (ww1a - ww1) / 2; // margin adds, left and right margh = (hh1a - hh1) / 2; // margin adds, top and bottom pxb2 = PXB_make(ww1a,hh1a,3); // final 1x image, with margins PXB_copy_area(pxb1,0,0,ww1,hh1,pxb2,margw,margh); // to have ratio ss_ww/ss_hh PXB_free(pxb1); pxb1 = pxb2; pxb2 = 0; zcx1 += margw; // adjust zoom center for margins zcy1 += margh; } ww1 = ww1a; // new 1x image size hh1 = hh1a; // image area to show for zoom = 1x ww2 = ww1 / ss_zoomsize; // image area to show for zoom = max. hh2 = hh1 / ss_zoomsize; orgx2 = zcx1 - ww2 / 2; // image origin at max. zoom orgy2 = zcy1 - hh2 / 2; // and zoom center at image center if (ww2 <= ww1 - 2 * margw) { // adjust to avoid margin overlap if (orgx2 < margw) orgx2 = margw; if (orgx2 + ww2 > ww1 - margw) orgx2 = ww1 - margw - ww2; } else { // too big, show even margins dm = ww2 - (ww1 - 2 * margw); orgx2 = margw - dm / 2; } if (hh2 <= hh1 - 2 * margh) { // adjust to avoid margin overlap if (orgy2 < margh) orgy2 = margh; if (orgy2 + hh2 > hh1 - margh) orgy2 = hh1 - margh - hh2; } else { // too big, show even margins dm = hh2 - (hh1 - 2 * margh); orgy2 = margh - dm / 2; } zcx1 = orgx2 + ww2 / 2; // adjusted zoom center zcy1 = orgy2 + hh2 / 2; return; } // start thread to perform image resize to target zoom size void ss_zoom_start(float zoom) { using namespace ss_zoom_names; void * ss_zoom_thread(void *); zoomnow = zoom; tid = start_Jthread(ss_zoom_thread,null); return; } void * ss_zoom_thread(void *) { using namespace ss_zoom_names; float F; ww2 = ww1 / zoomnow; // image size to show at zoom size hh2 = hh1 / zoomnow; F = 0; if (ss_zoomsize > 1) // F = 0 >> 1 F = (zoomnow - 1) / (ss_zoomsize - 1); // for zoom = 1x >> zoomsize zcx2 = ww1/2 + F * (zcx1 - ww1/2); // image center moves from zcy2 = hh1/2 + F * (zcy1 - hh1/2); // image midpoint >> zoom center orgx2 = zcx2 - ww2/2; // compute image area around if (orgx2 < 0) orgx2 = 0; // image center if (orgx2 + ww2 > ww1) orgx2 = ww1 - ww2; orgy2 = zcy2 - hh2/2; if (orgy2 < 0) orgy2 = 0; if (orgy2 + hh2 > hh1) orgy2 = hh1 - hh2; pxb2 = PXB_subpxb(pxb1,orgx2,orgy2,ww2,hh2); // resize to window pxb3 = PXB_rescale_fast(pxb2,ss_ww,ss_hh); PXB_free(pxb2); pxb2 = pxb3; pxb3 = 0; pthread_exit(0); } // wait for thread completion and return zoomed image PXB * ss_zoom_wait() { using namespace ss_zoom_names; wait_Jthread(tid); return pxb2; } // slowly zoom-in on the image (Ken Burns effect) void ss_zoomin() { PXB *Zpxb; double T0, Te, Tz, zoom; cairo_t *cr = draw_context_create(gdkwin,draw_context); ss_zoom_setup(ss_newfile); // initialize for new image ss_zoom_start(1.0); // start making initial zoom image T0 = get_seconds(); // zoom start time Tz = ss_zoomtime; // zoom time (goal) while (true) { Zpxb = ss_zoom_wait(); // wait for zoom image ready Te = get_seconds() - T0; // elapsed zoom time if (Te > Tz) break; // done if (Fescape) break; zoom = 1 + (ss_zoomsize - 1) * Te / Tz; // 1.0 ... zoom size ss_zoom_start(zoom); // start making next zoom image gdk_cairo_set_source_pixbuf(cr,Zpxb->pixbuf,0,0); // paint image cairo_paint(cr); zmainsleep(0.002); PXB_free(Zpxb); } PXB_free(Zpxb); // discard last ss_zoom_start(ss_zoomsize); // last image, full zoom size Zpxb = ss_zoom_wait(); gdk_cairo_set_source_pixbuf(cr,Zpxb->pixbuf,0,0); // paint cairo_paint(cr); draw_context_destroy(draw_context); PXB_free(ss_pxbnew); // retain final zoomed image ss_pxbnew = Zpxb; // for next transition return; } // slowly zoom-out from initial image center point void ss_zoomout() { PXB *Zpxb; double T0, Te, Tz, zoom; cairo_t *cr = draw_context_create(gdkwin,draw_context); ss_zoom_setup(ss_newfile); // initialize for new image ss_zoom_start(ss_zoomsize); // start making initial zoom image T0 = get_seconds(); // zoom start time Tz = ss_zoomtime; // zoom time (goal) while (true) { Zpxb = ss_zoom_wait(); // wait for zoomed image ready Te = get_seconds() - T0; // elapsed zoom time if (Te > Tz) break; // done if (Fescape) break; zoom = ss_zoomsize + (1 - ss_zoomsize) * Te / Tz; // zoom size ... 1.0 ss_zoom_start(zoom); // start meking next zoom image gdk_cairo_set_source_pixbuf(cr,Zpxb->pixbuf,0,0); // paint cairo_paint(cr); zmainsleep(0.002); PXB_free(Zpxb); } PXB_free(Zpxb); // discard last ss_zoom_start(1.0); // last image, size = 1 Zpxb = ss_zoom_wait(); gdk_cairo_set_source_pixbuf(cr,Zpxb->pixbuf,0,0); // paint cairo_paint(cr); draw_context_destroy(draw_context); PXB_free(ss_pxbnew); // retain final zoomed image ss_pxbnew = Zpxb; // for next transition return; } fotoxx-20.08/f.area.cc000066400000000000000000004706171362435004500145360ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* Fotoxx image editor - select area functions Select an area within the current image. Subsequent edit functions are carried out within the area. Otherwise, edit functions apply to the entire image. sa_stat 0/1/2/3/4 = none/edit/unused/complete/disabled sa_pixmap[*] 0/1/2+ = outside/edge/inside (edge distance) sa_pixselc[*] tracks pixels in current mouse selection 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_geom_mousefunc select rectangle or ellipse sa_draw_mousefunc draw area outline manually sa_mouse_select select area mouse functions sa_nextseq sequence number for selected pixels sa_unselect_pixels unselect recently selected pixels sa_pixmap_create setup area pixel maps sa_finish finish selected areas sa_finish_auto auto finish areas where possible sa_map_pixels map area edge and interior pixels sa_unfinish unfinish selected areas m_select_hairy hairy edge select function m_select_find_gap find a gap in an area outline 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_clear 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_clear delete area callable function sa_edgecalc calculate area edge distances sa_edgecreep adjust area edges +-1 pixel sa_blendfunc compute edge blend coefficient m_select_copy save area to default PNG file with alpha channel m_select_save save area to PNG file with alpha channel m_select_load open PNG file and make a select area m_select_paste paste area into image select_paste paste area in memory onto image *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // user select area dialog // line drawing and selection by color range are combined void m_select(GtkWidget *, cchar *menu) // menu function { int select_dialog_event(zdialog *, cchar *event); // dialog event and completion funcs cchar *title = E2X("Select Area for Edits"); cchar *helptext = E2X("Press F1 for help"); zdialog *zd; if (menu) F1_help_topic = "select area"; if (FGWM != 'F') return; // 19.0 if (! curr_file) return; // no image if (zd_sela) return; // already active if (CEF && CEF->Farea != 2) { // active edit function zmessageACK(Mwin,E2X("Select Area not supported \n" "by this edit function")); return; } /*** _________________________________________ | Select Area for Edits | | Press F1 for help | // 19.0 | [x] select rectangle [x] ellipse | | [x] freehand draw [x] follow edge | | [x] draw/replace | | Line Color: (0) (0) (0) (0) (0) | | - - - - - - - - - - - - - - - - - - - - | | mouse radius [___] | | match level % [___] | | search range [___] | | [x] select area in mouse | | [x] select color in mouse: [####] | | [x] select all colors in mouse (flood) | | - - - - - - - - - - - - - - - - - - - - | | Area Edge Blend Width [___] | | Edge Creep [+] [-] | | | | [Show] [Hide] [Finish] [Clear] [Done] | |_________________________________________| ***/ zd_sela = zdialog_new(title,Mwin,null); zd = zd_sela; zdialog_add_widget(zd,"label","labhelp","dialog",helptext,"space=3"); zdialog_add_widget(zd,"hbox","hbshape","dialog"); zdialog_add_widget(zd,"check","ckrect","hbshape",E2X("select rectangle"),"space=3"); zdialog_add_widget(zd,"check","ckelips","hbshape",E2X("ellipse"),"space=10"); zdialog_add_widget(zd,"hbox","hbdraw","dialog"); zdialog_add_widget(zd,"check","ckdraw","hbdraw",E2X("freehand draw"),"space=3"); zdialog_add_widget(zd,"check","ckfollow","hbdraw",E2X("follow edge"),"space=10"); zdialog_add_widget(zd,"hbox","hbrepl","dialog"); zdialog_add_widget(zd,"check","ckrepl","hbrepl",E2X("draw/replace"),"space=3"); zdialog_add_widget(zd,"hbox","hbm5","dialog"); zdialog_add_widget(zd,"label","labcolor","hbm5",E2X("Line Color:"),"space=3"); zdialog_add_widget(zd,"imagebutt","red","hbm5","redball.png","size=15|space=3"); zdialog_add_widget(zd,"imagebutt","green","hbm5","greenball.png","size=15|space=3"); zdialog_add_widget(zd,"imagebutt","blue","hbm5","blueball.png","size=15|space=3"); zdialog_add_widget(zd,"imagebutt","black","hbm5","blackball.png","size=15|space=3"); zdialog_add_widget(zd,"imagebutt","white","hbm5","whiteball.png","size=15|space=3"); zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=4"); zdialog_add_widget(zd,"hbox","hbmm","dialog"); zdialog_add_widget(zd,"label","labmr","hbmm",Bmouseradius,"space=3"); zdialog_add_widget(zd,"zspin","mouserad","hbmm","1|300|1|20","space=5|size=3"); zdialog_add_widget(zd,"hbox","hbml","dialog"); zdialog_add_widget(zd,"label","labmatch","hbml",E2X("match level %"),"space=3"); zdialog_add_widget(zd,"zspin","colormatch","hbml","0|100|1|90","space=5|size=3"); zdialog_add_widget(zd,"hbox","hbsr","dialog"); zdialog_add_widget(zd,"label","labrange","hbsr",E2X("search range"),"space=3"); zdialog_add_widget(zd,"zspin","searchrange","hbsr","1|20|1|5","space=5|size=3"); zdialog_add_widget(zd,"hbox","hbm1","dialog"); zdialog_add_widget(zd,"check","ckmouse","hbm1",E2X("select area in mouse"),"space=3"); zdialog_add_widget(zd,"hbox","hbm2","dialog"); zdialog_add_widget(zd,"check","ckonecolor","hbm2",E2X("select one color in mouse:"),"space=3"); 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",E2X("select all colors in mouse (flood)"),"space=3"); zdialog_add_widget(zd,"hsep","sep2","dialog",0,"space=3"); zdialog_add_widget(zd,"hbox","hbbw","dialog"); zdialog_add_widget(zd,"label","labblend","hbbw",E2X("Area Edge Blend Width"),"space=3"); zdialog_add_widget(zd,"zspin","blendwidth","hbbw","0|9999|1|0","size=4"); zdialog_add_widget(zd,"hbox","hbec","dialog"); zdialog_add_widget(zd,"label","labcreep","hbec",E2X("Edge Creep"),"space=3"); zdialog_add_widget(zd,"zbutton","creep+","hbec","+"); zdialog_add_widget(zd,"zbutton","creep-","hbec","‒"); zdialog_add_widget(zd,"hbox","hbb2","dialog",0,"space=5"); zdialog_add_widget(zd,"button","finish","hbb2",Bfinish,"space=2"); zdialog_add_widget(zd,"button","invert","hbb2",Binvert,"space=2"); 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","clear","hbb2",Bclear,"space=2"); zdialog_add_widget(zd,"button","done","hbb2",Bdone,"space=2"); zdialog_add_ttip(zd,"ckrect",E2X("drag mouse to select rectangular area")); zdialog_add_ttip(zd,"ckelips",E2X("drag mouse to select circular or elliptical area")); zdialog_add_ttip(zd,"ckdraw",E2X("drag mouse to outline an area")); zdialog_add_ttip(zd,"ckfollow",E2X("drag mouse along an edge to follow the edge")); zdialog_add_ttip(zd,"ckrepl",E2X("drag mouse near a line to move the line")); zdialog_add_ttip(zd,"labcolor",E2X("select line color")); zdialog_add_ttip(zd,"labmr",E2X("size of mouse selection circle")); zdialog_add_ttip(zd,"labmatch",E2X("required match level to select by color")); zdialog_add_ttip(zd,"labrange",E2X("select by color search range")); zdialog_add_ttip(zd,"ckmouse",E2X("select area within mouse circle")); zdialog_add_ttip(zd,"ckonecolor",E2X("first select the checkbox, then \n" "shift+click on image to set the color")); zdialog_add_ttip(zd,"ckallcolors",E2X("select surrounding areas matching colors in mouse")); zdialog_add_ttip(zd,"labblend",E2X("area edits fade away within edge distance")); zdialog_add_ttip(zd,"blendwidth",E2X("area edits fade away within edge distance")); zdialog_add_ttip(zd,"labcreep",E2X("move area boundary in/out in 1-pixel steps")); zdialog_add_ttip(zd,"finish",E2X("map selected areas and verify")); zdialog_add_ttip(zd,"invert",E2X("invert area")); zdialog_add_ttip(zd,"show",E2X("show area outlines")); zdialog_add_ttip(zd,"hide",E2X("hide area outlines")); zdialog_add_ttip(zd,"clear",E2X("clear area selections")); sa_mouseradius = 30; // initial values matching dialog sa_colormatch = 90; sa_searchrange = 5; if (sa_stat == 0) sa_blendwidth = 0; // preserve blend width zdialog_stuff(zd,"blendwidth",sa_blendwidth); sa_mode = 0; zdialog_stuff(zd,"ckrect",0); zdialog_stuff(zd,"ckelips",0); zdialog_stuff(zd,"ckdraw",0); zdialog_stuff(zd,"ckfollow",0); zdialog_stuff(zd,"ckrepl",0); zdialog_stuff(zd,"ckmouse",0); zdialog_stuff(zd,"ckonecolor",0); zdialog_stuff(zd,"ckallcolors",0); zdialog_run(zd,select_dialog_event,"save"); // run dialog - parallel if (sa_stat) sa_show(1,0); // show existing area return; } // dialog event and completion callback function int select_dialog_event(zdialog *zd, cchar *event) { int Nckevents = 8, ii, kk, cc; cchar *ckevents[8] = { "ckrect", "ckelips", "ckdraw", "ckfollow", "ckrepl", "ckmouse", "ckonecolor", "ckallcolors" }; if (! curr_file) event = "done"; // image went away if (FGWM != 'F') event = "done"; if (strmatch(event,"done") || zd->zstat) { // done or cancel freeMouse(); // disconnect mouse function zdialog_free(zd); // kill dialog zd_sela = 0; // bugfix 19.0 if (sa_stat) { cc = Fpxb->ww * Fpxb->hh; // check if any pixels mapped for (ii = 0; ii < cc; ii++) if (sa_pixmap[ii]) break; if (ii == cc) sa_clear(); // no, delete area } return 1; } 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 1; } if (! sa_stat) { // no area, create one sa_pixmap_create(); // allocate pixel maps 19.0 sa_currseq = sa_Ncurrseq = 0; // reset selection sequence sa_Npixel = sa_blendwidth = sa_calced = 0; sa_fww = Fpxb->ww; // valid image size for area sa_fhh = Fpxb->hh; sa_stat = 1; // status = edit sa_mode = 0; zdialog_stuff(zd,"blendwidth",0); // init. blend width = 0 } for (ii = 0; ii < Nckevents; ii++) // look for checkbox event { if (strmatch(event,ckevents[ii])) { // checkbox was changed zdialog_fetch(zd,event,kk); // checkbox status if (kk) { // ON sa_mode = ii+1; // edit mode 1-8 if (sa_mode == mode_replace) sa_unfinish(); // unmap internal pixels sa_stat = 1; // active edit status sa_Npixel = sa_blendwidth = sa_calced = 0; // make area unfinished zdialog_stuff(zd,"blendwidth",0); sa_show(1,0); // show area } else sa_mode = 0; // OFF, no edit mode, edit paused for (kk = 0; kk < Nckevents; kk++) // other checkboxes >> off if (kk != ii) zdialog_stuff(zd,ckevents[kk],0); } } if (strmatch(event,"mouserad")) // mouse selection radius zdialog_fetch(zd,"mouserad",sa_mouseradius); if (strmatch(event,"colormatch")) // mouse color match limit, 0 - 99.9 zdialog_fetch(zd_sela,"colormatch",sa_colormatch); if (strmatch(event,"searchrange")) // mouse color match search range, x radius zdialog_fetch(zd_sela,"searchrange",sa_searchrange); if (strmatch(event,"finish")) sa_finish(); // finish (finalize) area if (strmatch(event,"invert")) sa_invert(); // invert area (inside <--> outside) 20.0 if (strmatch(event,"show")) sa_show(1,0); // show area if (strmatch(event,"hide")) sa_show(0,0); // hide area if (strmatch(event,"clear")) sa_clear(); // clear area if (strmatch(event,"blendwidth") && sa_stat == 3) { // blend width changed and area finished zdialog_fetch(zd,"blendwidth",sa_blendwidth); // update sa_blendwidth if (sa_blendwidth > 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 new blendwidth if (! zd_sela) return 1; // dialog canceled 19.0 if (zd->zstat) { // dialog completed 19.0 zdialog_send_event(zd,"zstat"); return 1; } } 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_blendwidth = 0; zdialog_stuff(zd,"blendwidth",0); } if (strmatch(event,"red")) memcpy(LINE_COLOR,RED,3*sizeof(int)); if (strmatch(event,"green")) memcpy(LINE_COLOR,GREEN,3*sizeof(int)); if (strmatch(event,"blue")) memcpy(LINE_COLOR,BLUE,3*sizeof(int)); if (strmatch(event,"black")) memcpy(LINE_COLOR,BLACK,3*sizeof(int)); if (strmatch(event,"white")) memcpy(LINE_COLOR,WHITE,3*sizeof(int)); if (zstrstr("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) { // mouse radius select sa_lastx = sa_lasty = 0; takeMouse(sa_mouse_select,0); } if (sa_mode == mode_onecolor) { // mouse radius, one color select sa_lastx = sa_lasty = 0; takeMouse(sa_mouse_select,0); } if (sa_mode == mode_allcolors) { // mouse radius, all colors select sa_lastx = sa_lasty = 0; takeMouse(sa_mouse_select,0); } } 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 1; } // select area mouse function - select a rectangle or ellipse void sa_geom_mousefunc() { static int mx1, my1, mx2, my2; static int mdx0, mdy0, drag; float a, b, a2, b2; float x, y, x2, y2, cx, cy; int px, py; cairo_t *cr; if (sa_stat != 1) return; // area gone? if (sa_currseq > sa_maxseq-2) { zmessageACK(Mwin,E2X("exceed %d edits"),sa_maxseq); // cannot continue return; } cr = draw_context_create(gdkwin,draw_context); if (RMclick) // right mouse click { RMclick = 0; sa_unselect_pixels(cr); // remove latest selection drag = 0; Fpaint2(); draw_context_destroy(draw_context); return; } if (! Mxdrag && ! Mydrag) { // no drag underway draw_context_destroy(draw_context); return; } if (Mxdown != mdx0 || Mydown != mdy0) { // new drag initiated mdx0 = Mxdown; mdy0 = Mydown; mx1 = mdx0; // drag start, one corner my1 = mdy0; drag = 0; Mxdrag = Mydrag = 0; draw_context_destroy(draw_context); return; } mx2 = Mxdrag; // drag continues, 2nd corner my2 = Mydrag; Mxdrag = Mydrag = 0; if (drag) sa_unselect_pixels(cr); // remove prior drag result sa_nextseq(); // next sequence number drag = 1; if (sa_mode == mode_rect) // draw rectangle { sa_draw_line(mx1,my1,mx2,my1,cr); // draw 4 lines sa_draw_line(mx2,my1,mx2,my2,cr); sa_draw_line(mx2,my2,mx1,my2,cr); sa_draw_line(mx1,my2,mx1,my1,cr); } if (sa_mode == mode_ellipse) // draw ellipse { a = abs(mx2 - mx1); // ellipse constants from b = abs(my2 - my1); // enclosing rectangle a2 = a * a; b2 = b * b; cx = mx1; // center at drag origin cy = my1; for (y = -b; y < b; y++) // step through y values { y2 = y * y; x2 = a2 * (1 - y2 / b2); x = sqrtf(x2); // corresp. x values, + and - py = y + cy; px = cx - x + 0.5; sa_draw1pix(px,py,cr); // draw 2 points on ellipse px = cx + x + 0.5; sa_draw1pix(px,py,cr); } for (x = -a; x < a; x++) // step through x values { x2 = x * x; y2 = b2 * (1 - x2 / a2); y = sqrtf(y2); // corresp. y values, + and - px = cx + x; py = cy - y + 0.5; sa_draw1pix(px,py,cr); // draw 2 points on ellipse py = cy + y + 0.5; sa_draw1pix(px,py,cr); } } draw_context_destroy(draw_context); return; } // select area mouse function - freehand draw, follow edge, replace nearby void sa_draw_mousefunc() { void sa_follow_edge(int mx1, int my1, int &mx2, int &my2, cairo_t *cr); void sa_redraw(int mx1, int my1, int mx2, int my2, cairo_t *cr); int mx1, my1, mx2, my2; int ii, npdist, npx, npy; int click, newseq, thresh; static int drag = 0, openend = 0; static int mdx0, mdy0, mdx1, mdy1; cairo_t *cr; if (sa_stat != 1) return; // area gone? cr = draw_context_create(gdkwin,draw_context); sa_thresh = 4.0 / Mscale + 1; // mouse pixel distance threshold if (! (LMclick || RMclick || Mxdrag || Mydrag)) { // no mouse action if (openend) { openend = 0; // close pending gap after mx1 = sa_endpx[sa_currseq]; // prior draw/replace my1 = sa_endpy[sa_currseq]; thresh = 3 * sa_thresh; npdist = sa_nearpix(mx1,my1,thresh,mx2,my2,1); if (npdist) sa_draw_line(mx1,my1,mx2,my2,cr); } goto draw_exit; } click = newseq = 0; if (LMclick || Mxdrag || Mydrag) // left mouse click or mouse drag { if (LMclick) // left mouse click { mx1 = mx2 = Mxclick; // click position my1 = my2 = Myclick; newseq++; click++; drag = 0; } else // drag motion { if (Mxdown != mdx0 || Mydown != mdy0) { // new drag initiated mdx0 = mdx1 = Mxdown; mdy0 = mdy1 = Mydown; newseq++; } mx1 = mdx1; // drag start my1 = mdy1; mx2 = Mxdrag; // drag position my2 = Mydrag; mdx1 = mx2; // next drag start mdy1 = my2; drag++; click = 0; } if (Mbutton == 3) // right mouse >> erase { while (true) { thresh = sa_thresh; npdist = sa_nearpix(mx2,my2,thresh,npx,npy,0); // find nearest pixel if (! npdist) break; ii = npy * Fpxb->ww + npx; if (sa_pixmap[ii]) { sa_pixmap[ii] = 0; // unmap pixel erase_pixel(npx,npy,cr); // erase pixel } } goto draw_exit; } if (sa_currseq > sa_maxseq-2) { zmessageACK(Mwin,E2X("exceed %d edits"),sa_maxseq); // cannot continue goto draw_exit; } if (sa_currseq == 0 && newseq) // 1st pixel(s) of 1st sequence { sa_nextseq(); // set next (1st) sequence no. sa_draw_line(mx1,my1,mx2,my2,cr); // draw initial pixel or line sa_endpx[sa_currseq] = mx2; sa_endpy[sa_currseq] = my2; goto draw_exit; } if (click) { mx1 = sa_endpx[sa_currseq]; // prior sequence end pixel my1 = sa_endpy[sa_currseq]; // (before this click) } if (drag) { if (newseq) thresh = 2 * sa_thresh; // new drag threshold else thresh = 5 * sa_thresh; // continuation drag threshold npx = sa_endpx[sa_currseq]; // distance from prior end pixel npy = sa_endpy[sa_currseq]; // (before this drag) if (abs(mx1-npx) < thresh && abs(my1-npy) < thresh) { mx1 = sa_endpx[sa_currseq]; // if < threshold, connect this my1 = sa_endpy[sa_currseq]; // drag to prior drag or click } } if (drag > 50 && sa_mode != mode_replace) newseq = 1; // incr. sequence each 50 pixels if (newseq) { sa_nextseq(); // set next sequence no. drag = 1; // drag length within sequence } if (sa_mode == mode_draw) sa_draw_line(mx1,my1,mx2,my2,cr); // draw line from end pixel to mouse if (sa_mode == mode_follow) sa_follow_edge(mx1,my1,mx2,my2,cr); // follow edge from end pixel to mouse if (sa_mode == mode_replace) sa_redraw(mx1,my1,mx2,my2,cr); // tweak end pixel to mouse sa_endpx[sa_currseq] = mx2; // set end pixel for this sequence sa_endpy[sa_currseq] = my2; if (sa_mode == mode_replace) openend = 1; else openend = 0; } else if (RMclick) // right mouse click sa_unselect_pixels(cr); // remove latest selection draw_exit: draw_context_destroy(draw_context); LMclick = RMclick = 0; // stop further mouse action Mxdrag = Mydrag = 0; return; } // Find the nearest drawn pixel within a radius of a given pixel. // Returns distance to pixel, or zero if nothing found. // Returns 1 for adjacent pixel. // fx flag: exclude current selection (sequence no.) from search. int sa_nearpix(int mx, int my, int rad2, int &npx, int &npy, int fx) { int ii, rad, qx, qy, dx, dy; int mindist, dist; npx = npy = 0; mindist = (rad2+1) * (rad2+1); for (rad = 1; rad <= rad2; rad++) // seek neighbors within range { if (rad * rad > mindist) break; // can stop searching now for (qx = mx-rad; qx <= mx+rad; qx++) // search within rad for (qy = my-rad; qy <= my+rad; qy++) { if (qx != mx-rad && qx != mx+rad && // exclude within rad-1 qy != my-rad && qy != my+rad) continue; // (already searched) if (qx < 0 || qx > Fpxb->ww-1) continue; if (qy < 0 || qy > Fpxb->hh-1) continue; ii = qy * Fpxb->ww + qx; if (! sa_pixmap[ii]) continue; if (fx && sa_pixmap[ii] == sa_currseq) continue; // exclude curr. selection dx = (mx - qx) * (mx - qx); // found pixel dy = (my - qy) * (my - qy); dist = dx + dy; // distance**2 if (dist < mindist) { mindist = dist; npx = qx; // save nearest pixel found npy = qy; } } } if (npx + npy) return sqrt(mindist) + 0.5; return 0; } // draw a line between two given pixels // add all in-line pixels to sa_pixmap[] void sa_draw_line(int px1, int py1, int px2, int py2, cairo_t *cr) { int pxm, pym; float slope; int crflag = 0; if (sa_stat != 1) return; // area gone? if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } if (px1 == px2 && py1 == py2) { // only one pixel sa_draw1pix(px1,py1,cr); if (crflag) draw_context_destroy(draw_context); return; } if (abs(py2 - py1) > abs(px2 - px1)) { slope = 1.0 * (px2 - px1) / (py2 - py1); if (py2 > py1) { for (pym = py1; pym <= py2; pym++) { pxm = round(px1 + slope * (pym - py1)); sa_draw1pix(pxm,pym,cr); } } else { for (pym = py1; pym >= py2; pym--) { pxm = round(px1 + slope * (pym - py1)); sa_draw1pix(pxm,pym,cr); } } } else { slope = 1.0 * (py2 - py1) / (px2 - px1); if (px2 > px1) { for (pxm = px1; pxm <= px2; pxm++) { pym = round(py1 + slope * (pxm - px1)); sa_draw1pix(pxm,pym,cr); } } else { for (pxm = px1; pxm >= px2; pxm--) { pym = round(py1 + slope * (pxm - px1)); sa_draw1pix(pxm,pym,cr); } } } if (crflag) draw_context_destroy(draw_context); return; } // add to select area and draw one pixel if not already // mwcr must be set by caller void sa_draw1pix(int px, int py, cairo_t *cr) { int ii; if (px < 0 || px > Fpxb->ww-1) return; if (py < 0 || py > Fpxb->hh-1) return; ii = Fpxb->ww * py + px; if (! sa_pixmap[ii]) { // if not already selected, sa_pixmap[ii] = sa_currseq; // map pixel to curr. selection sa_Ncurrseq++; } draw_pixel(px,py,cr); // draw pixel return; } // Find series of edge pixels from px1/py1 to px2/py2 and connect them together. void sa_follow_edge(int px1, int py1, int &px2, int &py2, cairo_t *cr) { float sa_get_contrast(int px, int py); float px3, py3, px4, py4, px5, py5, px6, py6; float dx, dy, dist, contrast, maxcontrast; int crflag = 0; if (sa_stat != 1) return; // area gone? if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } px3 = px1; // p3 progresses from p1 to p2 py3 = py1; while (true) { dx = px2 - px3; dy = py2 - py3; dist = sqrt(dx * dx + dy * dy); // last segment if (dist < 5) break; // line laggs mouse 5 pixels px4 = px3 + dx / dist; // p4 = p3 moved toward p2 py4 = py3 + dy / dist; maxcontrast = 0; px6 = px4; py6 = py4; for (int ii = -2; ii <= +2; ii++) // p5 points are in a line through p4 { // and perpendicular to p4 - p2 px5 = px4 + ii * dy / dist; py5 = py4 - ii * dx / dist; contrast = sa_get_contrast(px5,py5); contrast *= (7 - abs(ii)); // favor points closer together if (contrast > maxcontrast) { px6 = px5; // p6 = highest contrast point in p5 py6 = py5; maxcontrast = contrast; } } sa_draw_line(px3,py3,px6,py6,cr); // draw p3 to p6 px3 = px6; // next p3 py3 = py6; } px2 = px3; // return lagging end point py2 = py3; if (crflag) draw_context_destroy(draw_context); return; } // freehand draw while erasing nearby pixels, effectively replacing them void sa_redraw(int mx1, int my1, int mx2, int my2, cairo_t *cr) { int ii, npx, npy; int thresh, npdist, d1, d2; int crflag = 0; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } thresh = 2 * sa_thresh; npdist = sa_nearpix(mx1,my1,thresh,npx,npy,0); // nearest pixel to (mx1,my1) if (npdist) { ii = npy * Fpxb->ww + npx; if (sa_pixmap[ii] != sa_currseq) { // if not in current line, sa_pixmap[ii] = sa_currseq; // add to current line sa_draw_line(npx,npy,mx1,my1,cr); } } sa_draw_line(mx1,my1,mx2,my2,cr); // draw line from (mx1,my1) to (mx2,my2) while (true) { npdist = sa_nearpix(mx2,my2,thresh,npx,npy,1); // nearest pixel to (mx2,my2) not in line if (! npdist) break; d1 = (npx-mx1)*(npx-mx1) + (npy-my1)*(npy-my1); d2 = (npx-mx2)*(npx-mx2) + (npy-my2)*(npy-my2); if (d2 >= d1) break; ii = npy * Fpxb->ww + npx; // motion toward pixel sa_pixmap[ii] = 0; // unmap pixel erase_pixel(npx,npy,cr); // erase pixel } if (crflag) draw_context_destroy(draw_context); return; } // Find max. contrast between neighbors on opposite sides of given pixel float sa_get_contrast(int px, int py) { int map[4][2] = { {1, 0}, {1, 1}, {0, 1}, {-1, 1} }; int ii, qx, qy; uint8 *pix1, *pix2; float match, contrast, maxcontrast = 0; if (px < 1 || px > Fpxb->ww-2) return 0; // avoid edge pixels if (py < 1 || py > Fpxb->hh-2) return 0; for (ii = 0; ii < 4; ii++) // compare pixels around target { // e.g. (px-1,py) to (px+1,py) qx = map[ii][0]; qy = map[ii][1]; pix1 = PXBpix(Fpxb,px+qx,py+qy); pix2 = PXBpix(Fpxb,px-qx,py-qy); match = PIXMATCH(pix1,pix2); // 0..1 = zero..perfect match contrast = 1.0 - match; // max. contrast = 1.0 if (contrast > maxcontrast) maxcontrast = contrast; } return maxcontrast; } // mouse function for edit modes with mouse / color selection // sa_mode = mouse = select area within mouse radius // sa_mode = onecolor = select one matching color within mouse radius // sa_mode = allcolors = select all matching colors within mouse radius // and extend to contiguous matching colors // if left click or drag, find and select matching pixels // if right click, unselect last selection // if right drag, find and unselect matching pixels void sa_mouse_select() { void sa_mouse_select1(int mode, int select); void sa_mouse_select2(int select); int newdrag; static int pxcc, mxdown, mydown, dragseq; int startx, starty, endx, endy; float slope; if (sa_stat != 1) return; // area gone? if (sa_mode == mode_allcolors && ! sa_pixselc) { // allocate memory for this mode pxcc = Fpxb->ww * Fpxb->hh; sa_stackdirec = (char *) zmalloc(pxcc); sa_stackii = (int *) zmalloc(4*pxcc); sa_maxstack = pxcc; sa_Nstack = 0; sa_pixselc = (char *) zmalloc(pxcc); } if (sa_mode != mode_allcolors && sa_pixselc) { // free memory otherwise zfree(sa_stackdirec); zfree(sa_stackii); zfree(sa_pixselc); sa_stackdirec = 0; sa_stackii = 0; sa_pixselc = 0; } if (sa_pixselc) memset(sa_pixselc,0,pxcc); // do always if (LMclick) // left mouse click { // select along line from prior LMclick = 0; sa_nextseq(); // set next sequence no. dragseq = 0; // reset drag counter if (sa_lastx == 0 && sa_lasty == 0) { // no prior selection sa_lastx = Mxclick; sa_lasty = Myclick; } sa_mousex = Mxclick; sa_mousey = Myclick; if (sa_mode == mode_onecolor) sa_mouse_select1(1,1); // set color for matching within mouse if (sa_mode == mode_allcolors) sa_mouse_select2(1); // select matching colors in/beyond mouse if (sa_mode == mode_mouse) // select along line from last { // selection to current mouse startx = sa_lastx; starty = sa_lasty; endx = Mxclick; endy = Myclick; if (abs(endy - starty) > abs(endx - startx)) { slope = 1.0 * (endx - startx) / (endy - starty); if (endy > starty) { for (sa_mousey = starty; sa_mousey <= endy; sa_mousey++) { sa_mousex = round(startx + slope * (sa_mousey - starty)); if (sa_mode == mode_onecolor) sa_mouse_select1(1,1); else sa_mouse_select2(1); // select pixels within mouse } } else { for (sa_mousey = starty; sa_mousey >= endy; sa_mousey--) { sa_mousex = round(startx + slope * (sa_mousey - starty)); if (sa_mode == mode_onecolor) sa_mouse_select1(1,1); else sa_mouse_select2(1); } } } else { slope = 1.0 * (endy - starty) / (endx - startx); if (endx > startx) { for (sa_mousex = startx; sa_mousex <= endx; sa_mousex++) { sa_mousey = round(starty + slope * (sa_mousex - startx)); if (sa_mode == mode_onecolor) sa_mouse_select1(1,1); else sa_mouse_select2(1); } } else { for (sa_mousex = startx; sa_mousex >= endx; sa_mousex--) { sa_mousey = round(starty + slope * (sa_mousex - startx)); if (sa_mode == mode_onecolor) sa_mouse_select1(1,1); else sa_mouse_select2(1); } } } } Fpaintnow(); } if (RMclick) { // right mouse click RMclick = 0; sa_unselect_pixels(0); // remove latest selection sa_show(1,0); // show area } if (Mxdrag || Mydrag) // drag is underway { sa_mousex = Mxdrag; // new mouse position sa_mousey = Mydrag; Mxdrag = Mydrag = 0; newdrag = 0; if (Mxdown != mxdown || Mydown != mydown) { // detect if new drag started newdrag = 1; // yes mxdown = Mxdown; // save drag start position mydown = Mydown; } if (Mbutton == 1) { // left drag, select matching colors if (sa_mode == mode_onecolor) sa_mouse_select1(2,1); // select matching colors within mouse else sa_mouse_select2(1); // select in mouse + matching colors beyond if (newdrag || ++dragseq > 30) { // limit work per sequence no. sa_nextseq(); // set next sequence no. dragseq = 0; // reset drag counter } } if (Mbutton == 3) { // right drag, find and unselect pixels if (sa_mode == mode_onecolor) sa_mouse_select1(2,0); else sa_mouse_select2(0); } } draw_mousecircle(Mxposn,Myposn,sa_mouseradius,0,0); // move mouse circle with mouse return; } // Left click - set one color to match from pixel at mouse position. // Left/right drag - select/unselect matching pixels within mouse radius. void sa_mouse_select1(int mode, int select) { int mrad, mrad2; int rad2, ii, jj; int px, py, rx, ry; int xlo, xhi, ylo, yhi, newpix; uint8 *pix1; float match1, match2; static float red, green, blue; static char colorbutt[16]; px = sa_mousex; py = sa_mousey; if (px < 0 || px > Fpxb->ww-2) return; // mouse outside image if (py < 0 || py > Fpxb->hh-2) return; if (mode == 1) // left click { red = green = blue = 0; for (ii = -1; ii <= +1; ii++) // get mean color for 3x3 pixels for (jj = -1; jj <= +1; jj++) // centered at px, py { pix1 = PXBpix(Fpxb,px+ii,py+jj); red += pix1[0]; green += pix1[1]; blue += pix1[2]; } red = red / 9.0; // color to match green = green / 9.0; blue = blue / 9.0; snprintf(colorbutt,16,"%.0f|%.0f|%.0f",red,green,blue); // set button to new match color zdialog_stuff(zd_sela,"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(zd_sela,"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; sa_pixmap2[ii] = 1; // pixel is mouse selected 19.0 } } else if (sa_pixmap[ii]) { // unselect mode sa_pixmap[ii] = 0; newpix = 1; sa_pixmap2[ii] = 0; } 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; } } if (select) { // remember last select location sa_lastx = sa_mousex; sa_lasty = sa_mousey; } return; } // Find all pixels within mouse radius and optionally extend selection // to all contiguous pixels matching colors within mouse and within range. // Select or unselect the matching pixels. void sa_mouse_select2(int select) { int mrad, mrad2, srange2; int rad1, rad2, ii; int kk, px, py, rx, ry; int ppx, ppy, npx, npy; int xlo, xhi, ylo, yhi, newpix; uint8 *pix1; float red, green, blue, ff1, ff2; float match1, match2, match3; int thresh, Npixels; char direc; struct Ctab_t { // table of pixel colors in mouse circle int count; // count of pixels with this color float rgb[3]; // RGB color }; Ctab_t Ctab[1000]; // table int Ntab; // table count px = sa_mousex; py = sa_mousey; if (px < 0 || px > Fpxb->ww-1) return; // mouse outside image if (py < 0 || py > Fpxb->hh-1) return; sa_Ncurrseq = 0; // count newly selected pixels mrad = sa_mouseradius; mrad2 = mrad * mrad; for (rx = -mrad; rx <= mrad; rx++) // loop every pixel in radius of mouse for (ry = -mrad; ry <= mrad; ry++) { rad2 = rx * rx + ry * ry; if (rad2 > mrad2) continue; // outside radius px = sa_mousex + rx; py = sa_mousey + ry; if (px < 0 || px >= Fpxb->ww) continue; // off the image edge if (py < 0 || py >= Fpxb->hh) continue; ii = Fpxb->ww * py + px; if (select) { // select pixel if (sa_pixmap[ii]) continue; // already selected sa_pixmap[ii] = sa_currseq; // map pixel to current sequence sa_Ncurrseq++; // current sequence pixel count sa_pixmap2[ii] = 1; // pixel is mouse selected 19.0 } else sa_pixmap[ii] = sa_pixmap2[ii] = 0; // unselect } if (select) { // remember last select location sa_lastx = sa_mousex; sa_lasty = sa_mousey; } if (sa_mode == mode_mouse) { // no color matching, done Fpaint4(px-mrad,py-mrad,2*mrad,2*mrad,0); // repaint changed area return; } // find all colors within mouse radius and build table of mouse colors // and counts of pixels (nearly) matching these colors match1 = 0.01 * sa_colormatch; // user color match level, 0.01 to 1.0 match3 = match1 + 0.6 * (1.0 - match1); // level for combining colors Npixels = Ntab = 0; // match color counts rad1 = mrad - 1; // mouse radius - 1 if (rad1 < 1) rad1 = 1; rad2 = rad1 * rad1; for (rx = -rad1; rx <= rad1; rx++) // find every pixel within mouse for (ry = -rad1; ry <= rad1; ry++) { if (rx * rx + ry * ry > rad2) continue; px = sa_mousex + rx; py = sa_mousey + ry; if (px < 1 || px > Fpxb->ww-2) continue; // off the image edge if (py < 1 || py > Fpxb->hh-2) continue; Npixels++; // count pixels inside mouse circle pix1 = PXBpix(Fpxb,px,py); red = pix1[0]; // average of 3x3 block removed green = pix1[1]; blue = pix1[2]; for (ii = 0; ii < Ntab; ii++) { // see if color is already included match2 = RGBMATCH(red,green,blue, // 0..1 = zero..perfect match Ctab[ii].rgb[0],Ctab[ii].rgb[1],Ctab[ii].rgb[2]); if (match2 >= match3) break; // matches table color within margin } if (ii < Ntab) { // average aggregated pixel color ff1 = Ctab[ii].count; ff2 = 1.0 / (ff1 + 1.0); Ctab[ii].rgb[0] = (Ctab[ii].rgb[0] * ff1 + red) * ff2; Ctab[ii].rgb[1] = (Ctab[ii].rgb[1] * ff1 + green) * ff2; Ctab[ii].rgb[2] = (Ctab[ii].rgb[2] * ff1 + blue) * ff2; Ctab[ii].count += 1; // count of pixels matching color } else { Ctab[ii].rgb[0] = red; // add unmatched pixel color to table Ctab[ii].rgb[1] = green; Ctab[ii].rgb[2] = blue; Ctab[ii].count = 1; Ntab++; if (Ntab == 1000) goto endmatch; // exit two nested loops } } endmatch: int keys[1][3] = { { 0, sizeof(int), 4 } }; // sort position, length, descending MemSort((char *) Ctab, sizeof(Ctab_t), Ntab, keys, 1); // sort descending count of matching pixels thresh = 0.03 * Ntab; // exclude minority pixels if (Ntab < 100) thresh = 3; if (Ntab < 40) thresh = 2; for (ii = 0; ii < Ntab; ii++) if (Ctab[ii].count < thresh) break; Ntab = ii; // search pixels outside mouse radius but within range for matching colors srange2 = mrad * sa_searchrange; // search range (* mouse radius) srange2 = srange2 * srange2; // squared px = sa_mousex; // pixel at mouse py = sa_mousey; ii = Fpxb->ww * py + px; sa_pixselc[ii] = 1; // pixel is in current selection xlo = px - mrad; // track limits of changed area xhi = px + mrad; ylo = py - mrad; yhi = py + mrad; sa_stackii[0] = ii; // put 1st pixel into stack sa_stackdirec[0] = 'a'; // direction = ahead sa_Nstack = 1; // stack count while (sa_Nstack) { kk = sa_Nstack - 1; // get last pixel in stack ii = sa_stackii[kk]; direc = sa_stackdirec[kk]; py = ii / Fpxb->ww; // reconstruct px, py px = ii - Fpxb->ww * py; if (direc == 'x') { // no neighbors left to check sa_Nstack--; continue; } if (sa_Nstack > 1) { ii = sa_Nstack - 2; // get prior pixel in stack ii = sa_stackii[ii]; ppy = ii / Fpxb->ww; ppx = ii - ppy * Fpxb->ww; } else { ppx = px - 1; // if only one, assume prior = left ppy = py; } if (direc == 'a') { // next ahead pixel npx = px + px - ppx; npy = py + py - ppy; sa_stackdirec[kk] = 'r'; // next search direction } else if (direc == 'r') { // next right pixel npx = px + py - ppy; npy = py + px - ppx; sa_stackdirec[kk] = 'l'; } else { /* direc = 'l' */ // next left pixel npx = px + ppy - py; npy = py + ppx - px; sa_stackdirec[kk] = 'x'; } if (npx < 0 || npx > Fpxb->ww-1) continue; // pixel off the edge if (npy < 0 || npy > Fpxb->hh-1) continue; ii = npy * Fpxb->ww + npx; if (sa_pixselc[ii]) continue; // already in current selection rx = npx - Mxposn; // limit search to ry = npy - Myposn; // mouse radius * search range rad2 = rx * rx + ry * ry; if (rad2 > srange2) continue; pix1 = PXBpix(Fpxb,npx,npy); // pixel at mouse for (kk = 0; kk < Ntab; kk++) { // compare pixel RGB to mouse colors match2 = PIXMATCH(pix1,Ctab[kk].rgb); // 0..1 = zero..perfect match if (match2 >= match1) break; // good match, accept pixel } if (kk == Ntab) continue; // not within range of any color sa_pixselc[ii] = 1; // map pixel to current selection newpix = 0; if (select) { // select mode if (! sa_pixmap[ii]) { // if selected for the first time, sa_pixmap[ii] = sa_currseq; // map pixel to current sequence sa_Ncurrseq++; // current sequence pixel count newpix = 1; sa_pixmap2[ii] = 1; // pixel is mouse selected 19.0 } } else if (sa_pixmap[ii]) { // unselect mode sa_pixmap[ii] = sa_pixmap2[ii] = 0; newpix = 1; } if (newpix) { if (npx < xlo) xlo = npx; // range of changed pixels if (npx > xhi) xhi = npx; if (npy < ylo) ylo = npy; if (npy > yhi) yhi = npy; } if (sa_Nstack == sa_maxstack) continue; // stack is full kk = sa_Nstack++; // push pixel into stack sa_stackii[kk] = ii; sa_stackdirec[kk] = 'a'; // direction = ahead } Fpaint4(xlo,ylo,xhi-xlo+1,yhi-ylo+1,0); // repaint changed area return; } // set next sequence number for pixels about to be selected void sa_nextseq() { if (sa_Ncurrseq > 0) sa_currseq++; // increase only if some pixels mapped if (sa_currseq < sa_initseq) sa_currseq = sa_initseq; // start at initial value sa_Ncurrseq = 0; return; } // un-select all pixels mapped to current sequence number // reduce sequence number and set pixel count = 1 void sa_unselect_pixels(cairo_t *cr) { int px, py, xlo, xhi, ylo, yhi; int crflag = 0; xlo = Fpxb->ww; xhi = 0; ylo = Fpxb->hh; yhi = 0; if (sa_stat != 1) return; // area gone? if (! sa_currseq) return; // no pixels mapped if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } for (int ii = 0; ii < Fpxb->ww * Fpxb->hh; ii++) { if (sa_pixmap[ii] == sa_currseq) { sa_pixmap[ii] = sa_pixmap2[ii] = 0; // unmap current selection py = ii / Fpxb->ww; px = ii - Fpxb->ww * py; if (px < xlo) xlo = px; // range of changed pixels if (px > xhi) xhi = px; if (py < ylo) ylo = py; if (py > yhi) yhi = py; } } if (xhi >= xlo) Fpaint4(xlo,ylo,xhi-xlo+1,yhi-ylo+1,cr); // repaint changed area if (sa_currseq > sa_initseq) { // reduce sequence no. sa_currseq--; sa_Ncurrseq = 1; // unknown but > 0 } else sa_Ncurrseq = 0; // initial sequence no. reached if (crflag) draw_context_destroy(draw_context); return; } // allocate pixel maps for new select area void sa_pixmap_create() // 19.0 { int cc; cc = Fpxb->ww * Fpxb->hh * sizeof(uint16); sa_pixmap = (uint16 *) zmalloc(cc); // maps outside/edge/inside pixels memset(sa_pixmap,0,cc); cc = Fpxb->ww * Fpxb->hh * sizeof(uint8); sa_pixmap2 = (uint8 *) zmalloc(cc); // maps mouse-selected pixels memset(sa_pixmap2,0,cc); return; } // Finish select area - map pixels enclosed by edge pixels // into sa_pixmap[ii]: 0/1/2 = outside/edge/inside (ii = py * Fww + px) // total count = sa_Npixel zdialog *sa_finzd = 0; // finish area zdialog int sa_fincancel; // finish area - user cancel int sa_finish_dialog_event(zdialog *zd, cchar *event); // private functions void sa_finish_mousefunc(); void sa_finish_mappix(); void sa_finish_fill(); void sa_finish() { cchar *fmess = E2X( "Fill selected areas with color for visual verification. \n" "Method 1: left-click in each selected area not already filled. \n" "Method 2: right-click OUTSIDE all selected areas. \n" "Press [help] button for clarification"); int zstat, cc; if (! sa_stat) return; // no area? if (! sa_validate()) return; // invalid for current image if (sa_mode == mode_image) return; // a whole image area sa_map_pixels(); // find edge and interior pixels if (! sa_Npixel) return; sa_show(1,0); // show area sa_finish_auto(); // auto-finish mouse-selected areas cc = Fpxb->ww * Fpxb->hh; if (sa_stackdirec) zfree(sa_stackdirec); sa_stackdirec = (char *) zmalloc(cc); if (sa_stackii) zfree(sa_stackii); sa_stackii = (int *) zmalloc(cc * sizeof(int)); sa_maxstack = cc; sa_fincancel = 0; sa_Nstack = 0; sa_finzd = zdialog_new(E2X("finish area"),Mwin,Bhelp,Bkeep,Bundo,null); // dialog for user to click inside zdialog_add_widget(sa_finzd,"hbox","hbmess","dialog",0,"space=3"); // each enclosed area zdialog_add_widget(sa_finzd,"label","fmess","hbmess",fmess,"space=5"); takeMouse(sa_finish_mousefunc,dragcursor); // connect mouse function zdialog_run(sa_finzd,sa_finish_dialog_event,"save"); // run dialog, wait for completion zstat = zdialog_wait(sa_finzd); zdialog_free(sa_finzd); sa_finzd = 0; freeMouse(); // disconnect mouse if (! sa_stat) return; // area gone? if (zstat != 2) sa_fincancel = 1; // user cancel while (sa_Nstack) { // wait for pixel search to complete zmainloop(); zsleep(0.01); } if (sa_fincancel) { // user cancel sa_unfinish(); // unmap interior pixels, set edit mode return; } sa_map_pixels(); // count pixels, map interior pixels if (sa_Npixel < 10) { // ignore tiny area zmessageACK(Mwin,E2X("found %d pixels"),sa_Npixel); return; } sa_stat = 3; // area is finished areanumber++; // next sequential number sa_calced = sa_blendwidth = 0; // edge calculation is missing if (zd_sela) zdialog_stuff(zd_sela,"blendwidth",0); Fpaint2(); return; } // dialog event and completion function int sa_finish_dialog_event(zdialog *zd, cchar *event) // 19.0 { cchar *fmess = E2X( "Method 1: \n" " Left-click inside an outlined area that is not already filled. \n" " Area will be filled with color for visible verification. \n" " Gaps in the outline will cause overflow outside the area. \n" " Repeat for each outlined area that is not already filled. \n" "Method 2: \n" " Right-click outside ALL outlined areas. \n" " All areas will be filled with color for visible verification. \n" " Gaps in an area outline will cause that area not to be filled. \n" "Gaps in an area outline: \n" " Gaps must be closed before proceeding with edits. \n" " The Find Gap function can be used for this."); if (zd->zstat == 1) { // [help] zd->zstat = 0; // keep dialog active zmessageACK(Mwin,fmess); return 1; } if (zd->zstat) return 1; // [keep] or [undo] sa_finish_fill(); return 1; } // mouse function - get user clicks and perform pixel searches void sa_finish_mousefunc() { int ii, jj, px, py, npix; if (! sa_stat) return; // area gone? if (sa_mode == mode_image) return; // a whole image area if (! LMclick && ! RMclick) return; 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 // click inside one outlined area, select all pixels within outline if (LMclick) { LMclick = 0; sa_finish_mappix(); // do pixel search sa_finish_fill(); // fill with color for user verify } // click outside all areas, select all pixels outside all outlines, then invert 19.0 if (RMclick) { RMclick = 0; sa_finish_mappix(); // do pixel search npix = 0; for (py = 0; py < Fpxb->hh; py++) // loop all pixels for (px = 0; px < Fpxb->ww; px++) { ii = py * Fpxb->ww + px; if (sa_pixmap2[ii]) { // if mouse-selected pixel npix++; // no change continue; } 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 } sa_finish_fill(); // fill with color for user verify sa_stat = 3; // area now finished sa_Npixel = npix; // new select area pixel count sa_calced = sa_blendwidth = 0; // edge calculation missing if (zd_sela) zdialog_stuff(zd_sela,"blendwidth",0); // reset blend width } return; } // function to map all pixels found within enclusure of starting pixel void sa_finish_mappix() { int px, py, ii, jj, kk; int ppx, ppy, npx, npy; char direc; while (sa_Nstack) // find all pixels within enclosed area(s) { kk = sa_Nstack - 1; // get last pixel in stack ii = sa_stackii[kk]; direc = sa_stackdirec[kk]; py = ii / Fpxb->ww; // reconstruct px, py px = ii - Fpxb->ww * py; if (direc == 'x') { // no neighbors left to check sa_Nstack--; continue; } if (sa_Nstack > 1) { jj = sa_Nstack - 2; // get prior pixel in stack jj = sa_stackii[jj]; ppy = jj / Fpxb->ww; ppx = jj - ppy * Fpxb->ww; } else { ppx = px - 1; // if only one, assume prior = left ppy = py; } if (direc == 'a') { // next ahead pixel npx = px + px - ppx; npy = py + py - ppy; sa_stackdirec[kk] = 'r'; // next search direction } else if (direc == 'r') { // next right pixel npx = px + py - ppy; npy = py + px - ppx; sa_stackdirec[kk] = 'l'; } else { /* direc = 'l' */ // next left pixel npx = px + ppy - py; npy = py + ppx - px; sa_stackdirec[kk] = 'x'; } if (npx < 0 || npx >= Fpxb->ww || npy < 0 || npy >= Fpxb->hh) { // next pixel off image edge sa_pixmap[ii] = 1; // this pixel is edge pixel continue; } jj = npy * Fpxb->ww + npx; if (Fpxb->nc > 3 && PXBpix(Fpxb,npx,npy)[3] < 254) { // next pixel in transparent area sa_pixmap[ii] = 1; // this pixel is edge pixel sa_pixmap[jj] = 1; // next pixel is edge pixel continue; } if (sa_pixmap[jj]) continue; // next pixel already mapped sa_pixmap[jj] = 2; // this pixel is interior pixel kk = sa_Nstack++; // put pixel into stack sa_stackii[kk] = jj; sa_stackdirec[kk] = 'a'; // direction = ahead } sa_Nstack = 0; // done return; } // fill selected areas with color for user verification void sa_finish_fill() { int ii, px, py; int npaint; cairo_t *cr; cr = draw_context_create(gdkwin,draw_context); npaint = 2.0 / Mscale + 1; // area fill, sparse for (py = 0; py < Fpxb->hh; py += npaint) // mark pixels inside area for (px = 0; px < Fpxb->ww; px += npaint) { ii = py * Fpxb->ww + px; if (sa_pixmap[ii]) draw_pixel(px,py,cr); } draw_context_destroy(draw_context); return; } // Finish select area automatically when the // interior selected pixels are already known. void sa_finish_auto() { if (! sa_stat) return; // no area? if (! sa_validate()) return; // invalid for current image sa_stat = 1; // area is unfinished sa_Npixel = 0; sa_map_pixels(); // map interior pixels if (sa_Npixel < 10) { zmessageACK(Mwin,E2X("found %d pixels"),sa_Npixel); sa_clear(); return; } sa_stat = 3; // area is finished areanumber++; // next sequential number sa_calced = sa_blendwidth = 0; // edge calculation is missing Fpaint2(); return; } // private function // map edge and interior pixels (sa_pixmap[*] = 1 or 2) // set sa_Npixel = total pixel count void sa_map_pixels() { int npix, px, py, ii, kk; if (! sa_stat) return; // no area? sa_minx = Fpxb->ww; sa_maxx = 0; sa_miny = Fpxb->hh; sa_maxy = 0; npix = 0; for (ii = 0; ii < Fpxb->ww * Fpxb->hh; ii++) { if (! sa_pixmap[ii]) continue; // outside area npix++; // count pixels inside area py = ii / Fpxb->ww; // simplify px = ii - Fpxb->ww * py; if (px >= sa_maxx) sa_maxx = px+1; // get enclosing rectangle if (px < sa_minx) sa_minx = px; // (sa_maxx = last pixel + 1) if (py >= sa_maxy) sa_maxy = py+1; if (py < sa_miny) sa_miny = py; if (px == 0 || px == Fpxb->ww-1 || py == 0 || py == Fpxb->hh-1) // image edge goto edgepix; if (Fpxb->nc > 3 && PXBpix(Fpxb,px,py)[3] < 254) // transparency edge goto edgepix; if (! sa_pixmap[ii-1] || ! sa_pixmap[ii+1]) goto edgepix; // check 8 neighbor pixels kk = ii - Fpxb->ww; if (! sa_pixmap[kk-1] || ! sa_pixmap[kk] || ! sa_pixmap[kk+1]) goto edgepix; kk = ii + Fpxb->ww; if (! sa_pixmap[kk-1] || ! sa_pixmap[kk] || ! sa_pixmap[kk+1]) goto edgepix; sa_pixmap[ii] = 2; // interior pixel continue; edgepix: sa_pixmap[ii] = 1; // edge pixel } sa_Npixel = npix; // total pixel count sa_minx -= 10; // add margins where possible if (sa_minx < 0) sa_minx = 0; sa_maxx += 10; if (sa_maxx > Fpxb->ww) sa_maxx = Fpxb->ww; sa_miny -= 10; if (sa_miny < 0) sa_miny = 0; sa_maxy += 10; if (sa_maxy > Fpxb->hh) sa_maxy = Fpxb->hh; return; } // 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_pixmap2[ii]) continue; // no unmap mouse selected pixels 19.0 if (sa_pixmap[ii] == 2) sa_pixmap[ii] = 0; // by finish search function } sa_stat = 1; // resume edit mode sa_calced = sa_blendwidth = 0; if (zd_sela) zdialog_stuff(zd_sela,"blendwidth",0); Fpaint2(); return; } /********************************************************************************/ // Hairy Edge selection function. // Set transparencies for hairy edge pixels based on color matching // with a set of selected pixels well within the hairy edge. namespace select_hairy { int ww, hh, pcc; int mradius = 30; // mouse radius int Fmode; // 1/2/3 = select/deselect/both int selrad = 6; // radius for match pixel selection (select) int dselrad = 4; // radius for match pixel selection (deselect) float smatch = 80; // pixel select match level 0-100 float dmatch = 80; // pixel deselect match level 0-100 float selRGB[200][3]; // pixels to match & select float dselRGB[200][3]; // pixels to match & deselect int Nsel, Ndsel; // pixel counts zdialog *zdselhairy; GdkPixbuf *pxbsel, *pxbdsel; // pixbufs to show match colors int pxbsize, pxbmid, rs; // size, middle pixel, row stride uint8 *pixels; } // menu function void m_select_hairy(GtkWidget *, cchar *) // overhauled { using namespace select_hairy; int select_hairy_dialog_event(zdialog *zd, cchar *event); void select_hairy_mousefunc(); cchar *title = E2X("Select Hairy"); cchar *helptext = E2X("Press F1 for help"); F1_help_topic = "select hairy"; if (FGWM != 'F') return; // 19.0 if (CEF) edit_done(0); // if edit active, complete it if (! sa_stat) { // no selected area zmessageACK(Mwin,E2X("select the area first")); return; } if (sa_stat < 3) { zmessageACK(Mwin,Bareanotfinished); return; } if (! E0pxm) { // get poss. 16-bit image E0pxm = PXM_load(curr_file,1); if (! E0pxm) return; } PXM_addalpha(E0pxm); // add alpha channel if not ww = E0pxm->ww; hh = E0pxm->hh; pcc = 4 * sizeof(float); if (E1pxm) PXM_free(E1pxm); // make reference copy of original E1pxm = PXM_copy(E0pxm); Fpaint2(); for (int ii = 0; ii < 200; ii++) { // initial match pixels selRGB[ii][0] = 0; // default = black selRGB[ii][1] = 0; selRGB[ii][2] = 0; dselRGB[ii][0] = 0; dselRGB[ii][1] = 0; dselRGB[ii][2] = 0; } Nsel = Ndsel = 100; // arbitrary at this point pxbsel = gdk_pixbuf_new(GDKRGB,0,8,40,40); // pixbuf showing select match colors rs = gdk_pixbuf_get_rowstride(pxbsel); pixels = gdk_pixbuf_get_pixels(pxbsel); memset(pixels,0,40*rs); // initially all black pxbdsel = gdk_pixbuf_new(GDKRGB,0,8,40,40); // pixbuf showing deselect match colors rs = gdk_pixbuf_get_rowstride(pxbdsel); pixels = gdk_pixbuf_get_pixels(pxbdsel); memset(pixels,0,40*rs); // initially all black /*** ____________________________________________ | Select Hairy | | | | Press F1 for help | | | | mouse radius ========[]============= [18] | | | | ##### | | [X] Select ===========[]===== [92] ##### | | ##### | | | | ##### | | [X] Deselect =======[]======= [83] ##### | | ##### | | | | [Copy] [Save] [Cancel] | |____________________________________________| ***/ zdselhairy = zdialog_new(title,Mwin,Bcopy,Bsave,Bcancel,null); zdialog *zd = zdselhairy; zdialog_add_widget(zd,"label","labhelp","dialog",helptext,"space=3"); zdialog_add_widget(zd,"hbox","hbmrad","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labmrad","hbmrad",Bmouseradius,"space=3"); zdialog_add_widget(zd,"hscale","mradius","hbmrad","3|300|1|40","expand"); zdialog_add_widget(zd,"label","mradius2","hbmrad","20","space=3"); zdialog_add_widget(zd,"hbox","hbsel","dialog",0,"space=3"); zdialog_add_widget(zd,"check","select","hbsel",E2X("select")); zdialog_add_widget(zd,"hscale","smatch","hbsel","10|99|0.1|80","expand"); zdialog_add_widget(zd,"label","smatch2","hbsel","80","space=3"); zdialog_add_widget(zd,"image","scolors","hbsel",(cchar *) pxbsel); zdialog_add_widget(zd,"hbox","hbdes","dialog",0,"space=3"); zdialog_add_widget(zd,"check","deselect","hbdes",E2X("deselect")); zdialog_add_widget(zd,"hscale","dmatch","hbdes","10|99|0.1|80","expand"); zdialog_add_widget(zd,"label","dmatch2","hbdes","80","space=3"); zdialog_add_widget(zd,"image","dcolors","hbdes",(cchar *) pxbdsel); zdialog_rescale(zd,"mradius",3,3,300); zdialog_rescale(zd,"smatch",10,99,99); zdialog_rescale(zd,"dmatch",10,99,99); smatch = dmatch = 80; // defaults mradius = 20; zdialog_stuff(zd,"smatch",smatch); zdialog_stuff(zd,"dmatch",dmatch); zdialog_stuff(zd,"mradius",mradius); zdialog_stuff(zd,"select",0); zdialog_stuff(zd,"deselect",0); Fmode = 0; zdialog_resize(zd,400,0); // run dialog zdialog_run(zd,select_hairy_dialog_event,"save"); takeMouse(select_hairy_mousefunc,dragcursor); // capture mouse return; } // dialog event and completion function int select_hairy_dialog_event(zdialog *zd, cchar *event) { using namespace select_hairy; int ii; char text[8]; void select_hairy_mousefunc(); if (! curr_file) zd->zstat = 3; // image went away if (sa_stat < 3) zd->zstat = 3; // area gone if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"focus")) takeMouse(select_hairy_mousefunc,dragcursor); if (zd->zstat) // done or cancel { if (zd->zstat == 1) { // copy area m_select_copy(0,0); zd->zstat = 0; return 1; } if (zd->zstat == 2) { // save area to file m_select_save(0,0); zd->zstat = 0; return 1; } PXM_free(E0pxm); E0pxm = E1pxm; // refresh image, remove transparency E1pxm = 0; Fpaint2(); zdialog_free(zd); // kill dialog freeMouse(); // disconnect mouse g_object_unref(pxbsel); // free pixbuf memory g_object_unref(pxbdsel); return 1; } Fmode = 0; // mode: 1/2/3 = select/deselect/both zdialog_fetch(zd,"select",ii); if (ii) Fmode = 1; zdialog_fetch(zd,"deselect",ii); if (ii) Fmode += 2; zdialog_fetch(zd,"mradius",mradius); // mouse radius zdialog_fetch(zd,"smatch",smatch); // select match level 0 - 100 zdialog_fetch(zd,"dmatch",dmatch); // deselect match level 0 - 100 snprintf(text,8,"%d",mradius); // stuff number values for sliders zdialog_stuff(zd,"mradius2",text); snprintf(text,8,"%.1f",smatch); zdialog_stuff(zd,"smatch2",text); snprintf(text,8,"%.1f",dmatch); zdialog_stuff(zd,"dmatch2",text); return 1; } // mouse function void select_hairy_mousefunc() { using namespace select_hairy; int select_hairy_ucomp(cchar *, cchar*); int ii, rx, ry, px, py; int mrad, rrad; int pcc3 = 3 * sizeof(float); uint8 *pix; float *pix0, *pix1, *pixM; float pixmatch, bestmatch; float smlev, dmlev, psml, pdml; float f1, f2, alpha; GdkPixbuf *pxbtemp = 0; if (LMclick) // left mouse click, { // get colors to select mrad = selrad; // area for pixel color select ii = 0; for (ry = -mrad; ry <= mrad; ry++) // loop pixels within selection radius for (rx = -mrad; rx <= mrad; rx++) { rrad = sqrtf(rx * rx + ry * ry); if (rrad > mrad) continue; // outside radius px = Mxclick + rx; py = Myclick + ry; if (px < 0 || px > ww-1) continue; // off the image edge if (py < 0 || py > hh-1) continue; pix1 = PXMpix(E1pxm,px,py); selRGB[ii][0] = pix1[0]; // pixels to match and select selRGB[ii][1] = pix1[1]; selRGB[ii][2] = pix1[2]; ii++; } Nsel = ii; // pixel count pxbsize = 2 * mrad + 3; // pixbuf size (odd number) pxbmid = pxbsize / 2; // middle pixel pxbtemp = gdk_pixbuf_new(GDKRGB,0,8,pxbsize,pxbsize); rs = gdk_pixbuf_get_rowstride(pxbtemp); pixels = gdk_pixbuf_get_pixels(pxbtemp); // pixbuf to show match colors memset(pixels,0,pxbsize*rs); ii = 0; for (ry = -mrad; ry <= mrad; ry++) // loop pixels within selection radius for (rx = -mrad; rx <= mrad; rx++) { rrad = sqrtf(rx * rx + ry * ry); if (rrad > mrad) continue; // outside radius px = Mxclick + rx; py = Myclick + ry; if (px < 0 || px > ww-1) continue; // off the image edge if (py < 0 || py > hh-1) continue; px = pxbmid + rx; // corresp. pixbuf pixel py = pxbmid + ry; pix = pixels + py * rs + px * 3; pix[0] = selRGB[ii][0]; // fill pixbuf with colors pix[1] = selRGB[ii][1]; pix[2] = selRGB[ii][2]; ii++; } g_object_unref(pxbsel); // show enlarged pxbsel = gdk_pixbuf_scale_simple(pxbtemp,40,40,BILINEAR); zdialog_set_image(zdselhairy,"scolors",pxbsel); } else if (RMclick) // right mouse click, { // get colors to deselect mrad = dselrad; // area for pixel color deselect ii = 0; for (ry = -mrad; ry <= mrad; ry++) // loop pixels within selection radius for (rx = -mrad; rx <= mrad; rx++) { rrad = sqrtf(rx * rx + ry * ry); if (rrad > mrad) continue; // outside radius px = Mxclick + rx; py = Myclick + ry; if (px < 0 || px > ww-1) continue; // off the image edge if (py < 0 || py > hh-1) continue; pix1 = PXMpix(E1pxm,px,py); dselRGB[ii][0] = pix1[0]; // pixels to match and deselect dselRGB[ii][1] = pix1[1]; dselRGB[ii][2] = pix1[2]; ii++; } Ndsel = ii; // pixel count pxbsize = 2 * mrad + 3; // pixbuf size (odd number) pxbmid = pxbsize / 2; // middle pixel pxbtemp = gdk_pixbuf_new(GDKRGB,0,8,pxbsize,pxbsize); rs = gdk_pixbuf_get_rowstride(pxbtemp); pixels = gdk_pixbuf_get_pixels(pxbtemp); // pixbuf to show match colors memset(pixels,0,pxbsize*rs); ii = 0; for (ry = -mrad; ry <= mrad; ry++) // loop pixels within selection radius for (rx = -mrad; rx <= mrad; rx++) { rrad = sqrtf(rx * rx + ry * ry); if (rrad > mrad) continue; // outside radius px = Mxclick + rx; py = Myclick + ry; if (px < 0 || px > ww-1) continue; // off the image edge if (py < 0 || py > hh-1) continue; px = pxbmid + rx; // corresp. pixbuf pixel py = pxbmid + ry; pix = pixels + py * rs + px * 3; pix[0] = dselRGB[ii][0]; // fill pixbuf with colors pix[1] = dselRGB[ii][1]; pix[2] = dselRGB[ii][2]; ii++; } g_object_unref(pxbdsel); // show enlarged pxbdsel = gdk_pixbuf_scale_simple(pxbtemp,40,40,BILINEAR); zdialog_set_image(zdselhairy,"dcolors",pxbdsel); } cairo_t *cr = draw_context_create(gdkwin,draw_context); if ((Mxdrag || Mydrag) && Mbutton == 1) // left drag underway, { // select or deselect pixels mrad = mradius; smlev = 0.01 * smatch; // select match level scaled 0-1 dmlev = 0.01 * dmatch; // deselect match level scaled 0-1 psml = pdml = 0; // stop GCC warnings for (ry = -mrad; ry <= mrad; ry++) // loop pixels within mouse radius for (rx = -mrad; rx <= mrad; rx++) { rrad = sqrtf(rx * rx + ry * ry); if (rrad > mrad) continue; // outside radius px = Mxdrag + rx; py = Mydrag + ry; if (px < 0 || px > ww-1) continue; // outside image edge if (py < 0 || py > hh-1) continue; ii = py * ww + px; if (! sa_pixmap[ii]) continue; // outside area pix1 = PXMpix(E1pxm,px,py); // input pixel pix0 = PXMpix(E0pxm,px,py); // output pixel bestmatch = -1; pixM = selRGB[0]; for (ii = 0; ii < Nsel; ii++) { // compare to selected colors pixmatch = RGBMATCH(pix1[0], pix1[1], pix1[2], selRGB[ii][0], selRGB[ii][1], selRGB[ii][2]); if (pixmatch > bestmatch) { bestmatch = pixmatch; // save best match level pixM = selRGB[ii]; // save best match color } } psml = bestmatch; // select colors best match level bestmatch = -1; for (ii = 0; ii < Ndsel; ii++) { // compare to deselect colors pixmatch = RGBMATCH(pix1[0], pix1[1], pix1[2], dselRGB[ii][0], dselRGB[ii][1], dselRGB[ii][2]); if (pixmatch > bestmatch) bestmatch = pixmatch; // save best match level } pdml = bestmatch; // deselect colors best match level if (Fmode == 1) // select only - increase opacity { if (psml > smlev) { alpha = (psml - smlev) / (1.0 - smlev); alpha = 255.0 * pow(alpha,0.3); if (pix0[3] < alpha) { pix0[3] = alpha; memcpy(pix0,pixM,pcc3); // color = match color } } } else if (Fmode == 2) // deselect only - increase transparency { if (pdml >= dmlev) alpha = 0; else { alpha = 1.0 - pdml / dmlev; alpha = 255.0 * pow(alpha,0.3); } alpha = 255.0 * pow(alpha,0.3); if (alpha < 20) alpha = 0; if (pix0[3] > alpha) pix0[3] = alpha; } else /* Fmode == 3 */ // parallel select and deselect { if (psml > pdml) // pixel better matches select colors alpha = 1 - 0.5 * (1 - psml) / (1 - pdml); // scale 0.5 to 1.0 else if (pdml > psml) alpha = 0.5 * (1 - pdml) / (1 - psml); // pixel better matches deselect colors else alpha = 0.5; // scale 0 to 0.5 alpha = 255 * alpha; if (alpha < 20) alpha = 0; pix0[3] = alpha; alpha = 255.0 * pow(alpha,0.3); memcpy(pix0,pixM,pcc3); // color = match color } } Fpaint0(Mxdrag-mrad,Mydrag-mrad,2*mrad+1,2*mrad+1,cr); // update drawing window } if ((Mxdrag || Mydrag) && Mbutton == 3) // right drag underway, { // restore original pixels mrad = mradius; for (ry = -mrad; ry <= mrad; ry++) // loop pixels within mouse radius for (rx = -mrad; rx <= mrad; rx++) { rrad = sqrtf(rx * rx + ry * ry); if (rrad > mrad) continue; // outside radius px = Mxdrag + rx; py = Mydrag + ry; if (px < 0 || px > ww-1) continue; // outside image edge if (py < 0 || py > hh-1) continue; ii = py * ww + px; if (! sa_pixmap[ii]) continue; // outside area f1 = 1.0 - 1.0 * rrad / mrad; // center ... edge >> 1 ... 0 f1 = 0.3 * f1 * f1 + 0.03; // 0.33 ... 0.03 f2 = 1.0 - f1; // restore slowly at edge pix0 = PXMpix(E0pxm,px,py); pix1 = PXMpix(E1pxm,px,py); pix0[0] = f2 * pix0[0] + f1 * pix1[0]; pix0[1] = f2 * pix0[1] + f1 * pix1[1]; pix0[2] = f2 * pix0[2] + f1 * pix1[2]; pix0[3] = f2 * pix0[3] + f1 * pix1[3]; } Fpaint0(Mxdrag-mrad,Mydrag-mrad,2*mrad+1,2*mrad+1,cr); // update drawing window } draw_mousecircle(Mxposn,Myposn,mradius,0,cr); // refresh mouse circle draw_context_destroy(draw_context); LMclick = RMclick = Mxdrag = Mydrag = 0; return; } /********************************************************************************/ // Find the edge pixels surrounding the clicked pixel location. // Trace around the edge outline to see if there is a gap. void m_select_find_gap(GtkWidget *, cchar *menu) { int sa_find_gap_dialog_event(zdialog *zd, cchar *event); void sa_find_gap_mousefunc(); cchar *fmess = E2X("Click near any position on the area outline. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help."); zdialog *zd; F1_help_topic = "find area gap"; if (FGWM != 'F') return; // 19.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 sa_unfinish(); if (sa_finzd) zdialog_destroy(sa_finzd); // terminate finish dialog sa_map_pixels(); // find edge pixels if (! sa_Npixel) return; sa_show(1,0); // show area zd = zdialog_new(E2X("find outline gap"),Mwin,Bdone,null); // dialog for user to click inside zdialog_add_widget(zd,"hbox","hbmess","dialog",0,"space=3"); // each enclosed area zdialog_add_widget(zd,"label","fmess","hbmess",fmess,"space=5"); zdialog_run(zd,sa_find_gap_dialog_event,"save"); // run dialog takeMouse(sa_find_gap_mousefunc,dragcursor); // connect mouse function return; } // dialog event and completion function int sa_find_gap_dialog_event(zdialog *zd, cchar *event) { void sa_find_gap_mousefunc(); if (strmatch(event,"focus")) takeMouse(sa_find_gap_mousefunc,dragcursor); // connect mouse function if (! zd->zstat) return 1; // wait for completion zdialog_free(zd); freeMouse(); // disconnect mouse if (sa_stackii) zfree(sa_stackii); // free memory sa_stackii = 0; return 1; } // mouse function - search area outline surrounding mouse click position void sa_find_gap_mousefunc() { int rad, ii, jj, kk, nn, ff, np1, np2, npx; float angle; int ww, hh, cc; int mx, my, px, py, rx, ry; char *pixmark = 0; cairo_t *cr = 0; if (! sa_stat) return; if (! LMclick) return; // no left mouse click data LMclick = 0; sa_map_pixels(); // find edge pixels if (! sa_Npixel) return; nn = np1 = np2 = -1; ww = Fpxb->ww; // image size hh = Fpxb->hh; cc = ww * hh; mx = Mxclick; // mouse click position my = Myclick; if (mx == 0 || mx >= ww) return; // reject if image edge if (my == 0 || my >= hh) return; if (Ffuncbusy++) return; // prevent re-entry f.20.02 if (sa_stackii) zfree(sa_stackii); cc = Fpxb->ww * Fpxb->hh; sa_stackii = (int *) zmalloc(cc * sizeof(int)); sa_Nstack = 0; sa_maxstack = cc; for (rad = 1; rad < 100; rad++) // loop radius = 1 - 100 pixels for (angle = 0; angle < 2*PI; angle += 0.7/rad) // loop angle = 0 - 360 degrees { zmainloop(1000); if (! sa_stackii) goto cleanup; // user cancel 19.0 px = mx + rad * cosf(angle); // search for nearest edge pixel py = my + rad * sinf(angle); ii = py * ww + px; if (sa_pixmap[ii] != 1) continue; nn = np1 = np2 = 0; // test if edge pixel has exactly for (ry = -1; ry <= +1; ry++) // two neighbor edge pixels for (rx = -1; rx <= +1; rx++) { if (ry == 0 && rx == 0) continue; // skip self jj = ii + ww * ry + rx; if (sa_pixmap[jj] == 1) { // neighbor is an edge pixel if (++nn > 2) goto break2; // > 2 edge neighbors, reject if (! np1) np1 = jj; // edge neighbor 1 else if (! np2) np2 = jj; // edge neighbor 2 } } break2: if (nn == 2) break; // found suitable edge pixel } if (nn != 2 || np1 == 0 || np2 == 0) { // no edge pixel with 2 neighbor zmessage_post(Mwin,"20/20",3,E2X("cannot find area outline")); // edge pixels was found goto cleanup; } sa_show(0,0); // hide area Fpaintnow(); cr = draw_context_create(gdkwin,draw_context); pixmark = (char *) zmalloc(cc); // create pixel mark map for (ff = 0; ff < 2; ff++) { memset(pixmark,0,cc); // clear all pixel marks pixmark[ii] = 1; // mark edge pixel sa_stackii[0] = np1; // put neighbor 1 pixel into stack sa_Nstack = 1; // stack count while (sa_Nstack) { kk = --sa_Nstack; // pull pixel from stack ii = sa_stackii[kk]; if (ii == np2) break; // = neighbor 2 pixel, no outline gap pixmark[ii] = 1; // mark pixel py = ii / ww; px = ii - ww * py; draw_pixel(px,py,cr,1); // draw fat pixel zsleep(0.001); zmainloop(); if (! sa_stackii) goto cleanup; // killed by user for (ry = -1; ry <= +1; ry++) // find unmarked edge neighbor pixels for (rx = -1; rx <= +1; rx++) { if (py+ry < 0 || py+ry > hh-1) continue; // off the image edge if (px+rx < 0 || px+rx > ww-1) continue; jj = ii + ww * ry + rx; if (pixmark[jj]) continue; // pixel already marked if (sa_pixmap[jj] == 1) { // neighbor is an unmarked edge pixel kk = sa_Nstack++; // add to stack sa_stackii[kk] = jj; } } } if (ii == np2) break; // no gap npx = np1; // np1 <--> np2 np1 = np2; np2 = npx; zsleep(1); // pause and loop other direction } cleanup: Ffuncbusy = 0; if (pixmark) zfree(pixmark); // free memory if (cr) draw_context_destroy(draw_context); sa_show(1,0); // show area return; } /********************************************************************************/ // menu function for show, hide, enable, disable, invert, clear // (also implemented as buttons in select area dialog) void m_select_show(GtkWidget *, cchar *menu) { F1_help_topic = "show/hide area"; if (FGWM != 'F') return; // 19.0 sa_show(1,0); // show area return; } void m_select_hide(GtkWidget *, cchar *menu) { F1_help_topic = "show/hide area"; if (FGWM != 'F') return; // 19.0 sa_show(0,0); return; } void m_select_enable(GtkWidget *, cchar *menu) { F1_help_topic = "enable/disable area"; if (FGWM != 'F') return; // 19.0 sa_enable(); return; } void m_select_disable(GtkWidget *, cchar *menu) { F1_help_topic = "enable/disable area"; if (FGWM != 'F') return; // 19.0 sa_disable(); return; } void m_select_invert(GtkWidget *, cchar *menu) { F1_help_topic = "invert area"; if (FGWM != 'F') return; // 19.0 sa_invert(); return; } void m_select_clear(GtkWidget *, cchar *menu) // delete the area { F1_help_topic = "clear area"; if (FGWM != 'F') return; // 19.0 sa_clear(); return; } /********************************************************************************/ // show or hide outline of select area // also called from Fpaint() if Fshowarea = 1 // edges are detected independently of sa_pixmap[] void sa_show(int flag, cairo_t *cr) { int px, py, ii, kk; int crflag = 0; if (! sa_stat) return; // no area if (! sa_validate()) return; // invalid for current image if (sa_mode == mode_image) return; // a whole image area Fshowarea = flag; // flag for Fpaint*() if (! flag) { Fpaint2(); // erase area outline return; } if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } for (ii = 0; ii < Fpxb->ww * Fpxb->hh; ii++) { if (! sa_pixmap[ii]) continue; py = ii / Fpxb->ww; px = ii - Fpxb->ww * py; if (px == 0 || px == Fpxb->ww-1 || py == 0 || py == Fpxb->hh-1) // edge of image continue; if (Fpxb->nc > 3 && PXBpix(Fpxb,px,py)[3] < 254) // transparency edge continue; if (! sa_pixmap[ii-1] || ! sa_pixmap[ii+1]) goto edgepix; // check 8 neighbor pixels kk = ii - Fpxb->ww; if (! sa_pixmap[kk] || ! sa_pixmap[kk-1] || ! sa_pixmap[kk+1]) goto edgepix; kk = ii + Fpxb->ww; if (! sa_pixmap[kk] || ! sa_pixmap[kk-1] || ! sa_pixmap[kk+1]) goto edgepix; continue; edgepix: draw_pixel(px,py,cr); } if (crflag) draw_context_destroy(draw_context); return; } // Show the area outline only within a rectangular section. // Improve responsiveness during user mouse-driven updates. // Also called by Fpaint4() after a sectional edit is applied. void sa_show_rect(int px1, int py1, int ww, int hh, cairo_t *cr) { int px, py, px2, py2, ii, kk; int crflag = 0; if (! Fshowarea) return; if (! sa_stat) return; // no area if (! sa_validate()) return; // invalid for current image if (sa_mode == mode_image) return; // a whole image area px2 = px1 + ww; py2 = py1 + hh; if (px1 < 0) px1 = 0; if (py1 < 0) py1 = 0; if (px2 > Fpxb->ww) px2 = Fpxb->ww; if (py2 > Fpxb->hh) py2 = Fpxb->hh; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } for (py = py1; py < py2; py++) // loop pixels in rectangle for (px = px1; px < px2; px++) { ii = Fpxb->ww * py + px; if (! sa_pixmap[ii]) continue; if (px == 0 || px == Fpxb->ww-1 || py == 0 || py == Fpxb->hh-1) // edge of image continue; if (Fpxb->nc > 3 && PXBpix(Fpxb,px,py)[3] < 254) // transparency edge continue; if (! sa_pixmap[ii-1] || ! sa_pixmap[ii+1]) goto edgepix; // check 8 neighbor pixels kk = ii - Fpxb->ww; if (! sa_pixmap[kk] || ! sa_pixmap[kk-1] || ! sa_pixmap[kk+1]) goto edgepix; kk = ii + Fpxb->ww; if (! sa_pixmap[kk] || ! sa_pixmap[kk-1] || ! sa_pixmap[kk+1]) goto edgepix; continue; edgepix: draw_pixel(px,py,cr); } if (crflag) draw_context_destroy(draw_context); return; } // validate an area for use in the current image. // returns 1 if OK, 0 if not (area will have been deleted). int sa_validate() { int ww = 0, hh = 0; if (! sa_stat) return 0; // no area if (! curr_file || (CEF && CEF->Fpreview)) { // no image file or edit preview image sa_clear(); 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_clear(); 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,Bareanotfinished); return; } sa_stat = 3; // finished/enabled areanumber++; // next sequential number sa_show(1,0); // show area return; } // disable select area void sa_disable() { if (! sa_stat) return; // no area if (sa_stat < 3) { // finished or */disabled zmessageACK(Mwin,Bareanotfinished); return; } sa_stat = 4; // finished/disabled sa_show(0,0); // hide area return; } // invert a selected area void sa_invert() { int ii, jj, px, py, npix; if (! sa_stat) return; // no area if (! sa_validate()) return; // invalid for current image if (sa_mode == mode_image) return; // a whole image area if (sa_stat < 3) { zmessageACK(Mwin,Bareanotfinished); return; } sa_minx = Fpxb->ww; // get new enclosing rectangle sa_maxx = 0; sa_miny = Fpxb->hh; sa_maxy = 0; npix = 0; for (py = 0; py < Fpxb->hh; py++) // loop all pixels for (px = 0; px < Fpxb->ww; px++) { ii = py * Fpxb->ww + px; jj = sa_pixmap[ii]; // 0/1/2+ = outside/edge/inside if (jj > 1) { // inside pixel (2+) sa_pixmap[ii] = 0; // is now outside (0) continue; } sa_pixmap[ii] = 2 - jj; // edge/outside (1/0) >> edge/inside (1/2) npix++; // count if (px >= sa_maxx) sa_maxx = px + 1; if (px < sa_minx) sa_minx = px; if (py >= sa_maxy) sa_maxy = py + 1; if (py < sa_miny) sa_miny = py; } sa_minx -= 10; // add margins where possible if (sa_minx < 0) sa_minx = 0; sa_maxx += 10; if (sa_maxx > Fpxb->ww) sa_maxx = Fpxb->ww; sa_miny -= 10; if (sa_miny < 0) sa_miny = 0; sa_maxy += 10; if (sa_maxy > Fpxb->hh) sa_maxy = Fpxb->hh; sa_stat = 3; // if disabled, now finished sa_Npixel = npix; // new select area pixel count sa_calced = sa_blendwidth = 0; // edge calculation missing if (zd_sela) zdialog_stuff(zd_sela,"blendwidth",0); // reset blend width sa_show(1,0); // show area return; } // clear current area (delete the area) void sa_clear() { sa_stat = sa_Npixel = sa_blendwidth = sa_calced = 0; sa_currseq = sa_Ncurrseq = 0; sa_fww = sa_fhh = 0; if (sa_pixmap) zfree(sa_pixmap); if (sa_pixmap2) zfree(sa_pixmap2); if (sa_stackii) zfree(sa_stackii); if (sa_stackdirec) zfree(sa_stackdirec); if (sa_pixselc) zfree(sa_pixselc); sa_pixmap = 0; sa_pixmap2 = 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 edgecalc_done; int sa_Nedge; } void sa_edgecalc() { using namespace sa_edgecalc_names; int edgecalc_dialog_event(zdialog*, cchar *event); void * edgecalc_thread(void *arg); int ii, nn, cc, px, py; 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 cc = Fpxb->ww * Fpxb->hh * sizeof(uint16); // allocate memory for calculations sa_edgedist = (uint16 *) zmalloc(cc); memset(sa_edgedist,0,cc); for (ii = nn = 0; ii < Fpxb->ww * Fpxb->hh; ii++) // count edge pixels in select area if (sa_pixmap[ii] == 1) nn++; cc = nn * sizeof(uint16); sa_edgepx = (uint16 *) zmalloc(cc); // allocate memory sa_edgepy = (uint16 *) zmalloc(cc); for (ii = nn = 0; ii < Fpxb->ww * Fpxb->hh; ii++) // build list of edge pixels { if (sa_pixmap[ii] != 1) continue; py = ii / Fpxb->ww; px = ii - py * Fpxb->ww; if (px == 0 || px == Fpxb->ww-1) continue; // omit edge pixels if (py == 0 || py == Fpxb->hh-1) continue; if (Fpxb->nc > 3) { // omit pixels at transparency edge if (PXBpix(Fpxb,px-1,py)[3] < 254) continue; if (PXBpix(Fpxb,px, py)[3] < 254) continue; if (PXBpix(Fpxb,px+1,py)[3] < 254) continue; if (PXBpix(Fpxb,px-1,py-1)[3] < 254) continue; if (PXBpix(Fpxb,px, py-1)[3] < 254) continue; if (PXBpix(Fpxb,px+1,py-1)[3] < 254) continue; if (PXBpix(Fpxb,px-1,py+1)[3] < 254) continue; if (PXBpix(Fpxb,px, py+1)[3] < 254) continue; if (PXBpix(Fpxb,px+1,py+1)[3] < 254) continue; } sa_edgepx[nn] = px; sa_edgepy[nn] = py; nn++; } sa_Nedge = nn; Fbusy_goal = sa_Npixel; Fbusy_done = 0; edgecalc_done = 0; start_detached_thread(edgecalc_thread,0); // use thread so that progress while (! edgecalc_done) zmainsleep(0.1); // indicator works 19.0 Fbusy_goal = 0; if (Fescape) { // user killed edge calc 19.0 Fescape = 0; sa_calced = 0; sa_blendwidth = 0; // 19.0 if (zd_sela) zdialog_stuff(zd_sela,"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; } // thread function to calculate pixel edge distances void * edgecalc_thread(void *arg) // 19.0 { using namespace sa_edgecalc_names; void * edgecalc_wthread(void *arg); do_wthreads(edgecalc_wthread,NWT); // start worker threads edgecalc_done = 1; pthread_exit(0); } void * edgecalc_wthread(void *arg) // worker thread function { using namespace sa_edgecalc_names; void edgecalc_func(int px, int py); int index = *((int *) (arg)); int midx, midy, radx, rady, rad; int ii, px, py; midx = (sa_maxx + sa_minx) / 2; midy = (sa_maxy + sa_miny) / 2; radx = (sa_maxx - sa_minx) / 2 + 1; rady = (sa_maxy - sa_miny) / 2 + 1; px = midx; // center of enclosing rectangle py = midy; ii = py * Fpxb->ww + px; if (sa_pixmap[ii]) edgecalc_func(px,py); // do center pixel first for (rad = 1; rad < radx || rad < rady; rad++) // expanding square from the center { for (px = midx-rad; px <= midx+rad; px += 2 * rad) // edges only, interior already done for (py = midy-rad+index; py <= midy+rad; py += NWT) { if (px < 0 || px > Fpxb->ww-1) continue; if (py < 0 || py > Fpxb->hh-1) continue; ii = py * Fpxb->ww + px; if (! sa_pixmap[ii]) continue; if (sa_edgedist[ii]) continue; edgecalc_func(px,py); if (Fescape) pthread_exit(0); // user cancel 19.0 } 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 (Fescape) pthread_exit(0); // user cancel 19.0 } } pthread_exit(0); } // Find the nearest edge pixel for a given pixel. // For all pixels in a line from the given pixel to the edge pixel, // the same edge pixel is used to compute edge distance. void edgecalc_func(int px1, int py1) { using namespace sa_edgecalc_names; int ii, px2, py2, mindist; uint dist2, mindist2; int epx, epy, pxm, pym, dx, dy, inc; int ww = Fpxb->ww, hh = Fpxb->hh; int cc = ww * hh; float slope; mindist = 9999; mindist2 = mindist * mindist; epx = epy = 0; for (ii = 0; ii < sa_Nedge; ii++) // loop all edge pixels { // (ii += 2 tried, buggy) px2 = sa_edgepx[ii]; py2 = sa_edgepy[ii]; dx = px2 - px1; dy = py2 - py1; dist2 = dx*dx + dy*dy; // avoid sqrt() if (dist2 < mindist2) { mindist2 = dist2; // remember minimum epx = px2; // remember nearest edge pixel epy = py2; } } if (abs(epy - py1) > abs(epx - px1)) { // find all pixels along a line slope = 1.0 * (epx - px1) / (epy - py1); // to the edge pixel if (epy > py1) inc = 1; else inc = -1; for (pym = py1; pym != epy; pym += inc) { pxm = px1 + slope * (pym - py1); ii = pym * ww + pxm; if (ii < 0 || ii >= cc) { printz("edgecalc() bug %d \n",ii); return; } if (sa_edgedist[ii]) return; dx = epx - pxm; // calculate distance to edge dy = epy - pym; dist2 = sqrt(dx*dx + dy*dy) + 1; // minor bug fix sa_edgedist[ii] = dist2; // save Fbusy_done++; // track progress } } else { slope = 1.0 * (epy - py1) / (epx - px1); if (epx > px1) inc = 1; else inc = -1; for (pxm = px1; pxm != epx; pxm += inc) { pym = py1 + slope * (pxm - px1); ii = pym * ww + pxm; if (ii < 0 || ii >= cc) { printz("edgecalc() bug %d \n",ii); return; } if (sa_edgedist[ii]) return; dx = epx - pxm; dy = epy - pym; dist2 = sqrt(dx*dx + dy*dy) + 1; sa_edgedist[ii] = dist2; Fbusy_done++; } } return; } // adjust area edge 1 pixel out (+) or in (-) void sa_edgecreep(int kk) { int px, py, ii, jj; if (sa_stat != 3) { zmessageACK(Mwin,Bareanotfinished); return; } for (py = sa_miny; py < sa_maxy; py++) // find all area edge pixels for (px = sa_minx; px < sa_maxx; px++) { ii = Fpxb->ww * py + px; if (sa_pixmap[ii] != 1) continue; if (px == 0 || px == Fpxb->ww-1 || py == 0 || py == Fpxb->hh-1) // edge of image, no change continue; if (kk < 0) { // shrink area sa_pixmap[ii] = 0; continue; } sa_pixmap[ii-1] = sa_pixmap[ii+1] = 2; // expand area jj = ii - Fpxb->ww; sa_pixmap[jj] = sa_pixmap[jj-1] = sa_pixmap[jj+1] = 2; // mark 8 neighbor pixels in area jj = ii + Fpxb->ww; sa_pixmap[jj] = sa_pixmap[jj-1] = sa_pixmap[jj+1] = 2; } sa_map_pixels(); // remap edge and interior pixels sa_finish_auto(); // finish area again sa_show(1,0); // show area sa_calced = 0; // invalidate prior edge calc. Fpaint2(); return; } // Compute edge blend coefficient for given edge distance. // Returned coefficient: 0.0 to 1.0 for edge distance from 0 to sa_blendwidth. float sa_blendfunc(int edgedist) { static float coeff[5000]; // edge distance limit static int Pblend = -1; float ff; int ii; if (sa_mode == mode_image) { // whole image area (see m_paint_edits()) ff = 1.0 * edgedist / sa_blendwidth; return ff; } if (edgedist >= sa_blendwidth) return 1.0; if (sa_blendwidth == Pblend) return coeff[edgedist]; if (sa_blendwidth < 1 || sa_blendwidth > 4999) return 1.0; Pblend = sa_blendwidth; for (ii = 0; ii <= sa_blendwidth; ii++) { ff = ii; ff = PI * (ff / sa_blendwidth - 0.5); // -PI/2 ... +PI/2 coeff[ii] = 0.5 * (sinf(ff) + 1.0); // 0.0 ... 1.0 } return coeff[edgedist]; } /******************************************************************************** select area copy/paste and load/save - select area <--> disk file *********************************************************************************/ namespace sa_diskfile { PXM *sacp_pxm = 0; // select area pixmap image int sacp_ww, sacp_hh; // original dimensions PXM *sacpR_pxm = 0; // resized/rotated image int sacpR_ww, sacpR_hh; // resized/rotated dimensions float sacp_scale; // scale, 1.0 = original size float sacp_angle; // angle of rotation, -180 to +180 int sacp_orgx, sacp_orgy; // origin in target image float sacp_blend; // pasted area edge blend with image float sacp_brite; // pasted area brightness adjustment int sacp_porg = 0; // pasted area is present int sacp_porgx, sacp_porgy; // pasted area origin in image int sacp_pww, sacp_phh; // pasted area dimensions editfunc EFpaste; } // Save a select area in memory to a disk PNG file. // For menu "Copy" use default file: ~/.fotoxx/saved_areas/copied_area.png // For menu "Save Area" use file name from user input. void m_select_copysave(GtkWidget *, cchar *menu); void m_select_copy(GtkWidget *, cchar *) { m_select_copysave(0,"copy"); return; } void m_select_save(GtkWidget *, cchar *) { m_select_copysave(0,"save"); return; } void m_select_copysave(GtkWidget *, cchar *menu) { using namespace sa_diskfile; int ii, px1, py1, px2, py2, dist; int ww, nc, pcc, alpha; float *pix1, *pix2; char *pp, *file; char filename[100]; if (strmatch(menu,"copy")) F1_help_topic = "copy/paste area"; else if (strmatch(menu,"save")) F1_help_topic = "load/save area"; else { zmessageACK(Mwin,"menu name bug: %s",menu); return; } if (FGWM != 'F') return; // 19.0 if (! sa_stat) return; // no selected area if (sa_mode == mode_image) return; // a whole image area if (sa_stat < 3) { zmessageACK(Mwin,Bareanotfinished); return; } if (! E0pxm) { // get poss. 16-bit image E0pxm = PXM_load(curr_file,1); if (! E0pxm) return; } ww = E0pxm->ww; nc = E0pxm->nc; pcc = nc * sizeof(float); PXM_free(sacp_pxm); // free prior if any PXM_free(sacpR_pxm); sacp_ww = sa_maxx - sa_minx; // new area image PXM sacp_hh = sa_maxy - sa_miny; sacp_pxm = PXM_make(sacp_ww,sacp_hh,4); // alpha channel for (py2 = 0; py2 < sacp_hh; py2++) // loop area pixels for (px2 = 0; px2 < sacp_ww; px2++) { px1 = px2 + sa_minx; py1 = py2 + sa_miny; pix1 = PXMpix(E0pxm,px1,py1); // copy to area PXM pix2 = PXMpix(sacp_pxm,px2,py2); memcpy(pix2,pix1,pcc); // copy RGB(A) data ii = py1 * ww + px1; dist = sa_pixmap[ii]; // 0/1/2+ = outside/edge/inside if (dist == 0) pix2[3] = 0; // outside pixel, transparent else if (nc < 4) pix2[3] = 255; // inside, opaque if alpha added } for (py2 = 1; py2 < sacp_hh-1; py2++) // loop area edge pixels for (px2 = 1; px2 < sacp_ww-1; px2++) { px1 = px2 + sa_minx; py1 = py2 + sa_miny; ii = py1 * ww + px1; if (sa_pixmap[ii] != 1) continue; // not an edge pixel alpha = 0; if (sa_pixmap[ii-1] == 1 && sa_pixmap[ii+1] == 1) alpha += 128; // add transparency to edge pixels else if (sa_pixmap[ii-1] == 1 || sa_pixmap[ii+1] == 1) alpha += 32; // depending on neighboring if (sa_pixmap[ii-ww] == 1 && sa_pixmap[ii+ww] == 1) alpha += 128; // edge pixels else if (sa_pixmap[ii-ww] == 1 || sa_pixmap[ii+ww] == 1) alpha += 32; if (sa_pixmap[ii-ww-1] == 1 && sa_pixmap[ii+ww+1] == 1) alpha += 128; else if (sa_pixmap[ii-ww-1] == 1 || sa_pixmap[ii+ww+1] == 1) alpha += 32; if (sa_pixmap[ii-ww-1] == 1 && sa_pixmap[ii+ww+1] == 1) alpha += 128; else if (sa_pixmap[ii-ww-1] == 1 || sa_pixmap[ii+ww+1] == 1) alpha += 32; if (alpha > 255) alpha = 255; pix2 = PXMpix(sacp_pxm,px2,py2); if (pix2[3] > alpha) pix2[3] = alpha; // only reduce opacity } if (strmatch(menu,"copy")) { snprintf(filename,100,"%s/copied_area.png",saved_areas_folder); // save to default PNG file PXM_PNG_save(sacp_pxm,filename,16); return; } pp = zgetfile(E2X("save area as a PNG file"),MWIN,"save",saved_areas_folder); // get file name from user if (! pp) return; file = zstrdup(pp,8); zfree(pp); pp = strrchr(file,'/'); pp = strcasestr(pp,".png"); if (! pp) strcat(file,".png"); PXM_PNG_save(sacp_pxm,file,16); // use PNG-16 file zfree(file); return; } // Read a select area from a disk PNG file. void m_select_load(GtkWidget *, cchar *) { using namespace sa_diskfile; void select_paste(GtkWidget *, cchar *); PXM *pxmtemp; char *file; float *pix1, *pix2; int px, py, nc, pcc; F1_help_topic = "load/save area"; if (FGWM != 'F') return; // 19.0 PXM_free(sacp_pxm); // free prior if any PXM_free(sacpR_pxm); file = gallery_select1(saved_areas_folder); // use thumbnail selection if (! file) return; pxmtemp = PXM_load(file,1); // load image file zfree(file); if (! pxmtemp) return; nc = pxmtemp->nc; pcc = nc * sizeof(float); // 3 or 4 channels, RGB or RGBA sacp_ww = pxmtemp->ww; // image dimensiona sacp_hh = pxmtemp->hh; sacp_pxm = PXM_make(sacp_ww,sacp_hh,4); // alpha channel for (py = 0; py < sacp_hh; py++) for (px = 0; px < sacp_ww; px++) { pix1 = PXMpix(pxmtemp,px,py); pix2 = PXMpix(sacp_pxm,px,py); memcpy(pix2,pix1,pcc); // copy all channels if (nc < 4) pix2[3] = 255; // no alpha, all pixels opaque } PXM_free(pxmtemp); select_paste(0,0); // interactive move/resize area image return; } // paste the area last copied on to the current image // uses the default file from "copy area" menu (above) void m_select_paste(GtkWidget *, cchar *) { using namespace sa_diskfile; void select_paste(GtkWidget *, cchar *); PXM *pxmtemp; char filename[100]; float *pix1, *pix2; int px, py, nc, pcc; F1_help_topic = "copy/paste area"; if (FGWM != 'F') return; // 19.0 PXM_free(sacp_pxm); // free prior if any PXM_free(sacpR_pxm); snprintf(filename,100,"%s/copied_area.png",saved_areas_folder); pxmtemp = PXM_load(filename,1); // load image file if (! pxmtemp) return; nc = pxmtemp->nc; pcc = nc * sizeof(float); sacp_ww = pxmtemp->ww; // image dimensiona sacp_hh = pxmtemp->hh; sacp_pxm = PXM_make(sacp_ww,sacp_hh,4); // alpha channel for (py = 0; py < sacp_hh; py++) for (px = 0; px < sacp_ww; px++) { pix1 = PXMpix(pxmtemp,px,py); pix2 = PXMpix(sacp_pxm,px,py); memcpy(pix2,pix1,pcc); // copy all channels if (nc < 4) pix2[3] = 255; // no alpha, all pixels opaque } PXM_free(pxmtemp); select_paste(0,0); // interactive move/resize area image return; } // paste select area in memory into current image // this is an edit function - select area image is copied into main image void select_paste(GtkWidget *, cchar *) { using namespace sa_diskfile; void select_paste_image(); int select_paste_dialog_event(zdialog *, cchar *event); void select_paste_mousefunc(); cchar *dragmess = E2X("position with mouse click/drag"); if (! sacp_pxm) return; // nothing to paste sa_clear(); // clear area if present EFpaste.menufunc = select_paste; EFpaste.funcname = "paste area"; EFpaste.Frestart = 1; // make restartable if (! edit_setup(EFpaste)) return; // setup edit for paste sacp_scale = 1.0; // size = 1x sacp_blend = 0; // edge blend = 0 sacp_angle = 0; // angle = 0 sacp_brite = 1.0; // no brightness adjustment sacp_porg = 0; // no image paste location yet sa_calced = 0; // no edge distance calculation PXM_free(sacpR_pxm); // free prior if any if (sacp_ww > E0pxm->ww) // if paste image > current image sacp_scale = 1.0 * E0pxm->ww / sacp_ww; // scale down to fit if (sacp_hh * sacp_scale > E0pxm->hh) sacp_scale = 1.0 * E0pxm->hh / sacp_hh; if (sacp_scale < 1.0) { sacp_ww *= sacp_scale; sacp_hh *= sacp_scale; sacpR_pxm = PXM_rescale(sacp_pxm,sacp_ww,sacp_hh); PXM_free(sacp_pxm); sacp_pxm = sacpR_pxm; sacp_scale = 1.0; } sacpR_pxm = PXM_copy(sacp_pxm); // initial paste image sacpR_ww = sacp_ww; // size = 1.0, no rotation sacpR_hh = sacp_hh; sacp_orgx = sacp_orgy = 0; // initial position select_paste_image(); // paste area image if (! sa_Npixel) { // failed edit_cancel(0); // cancel edit, restore image PXM_free(sacpR_pxm); // free memory return; } /*** __________________________________________________ | position with mouse click/drag | | | | resize [-10%] [-1%] [-.1%] [+.1%] [+1%] [+10%] | | angle [-10°] [-1°] [-.1°] [+.1°] [+1°] [+10°] | | brightness ==============[]==================== | | edge blend ===[]=============================== | | | | [Done] [Cancel] | |__________________________________________________| ***/ CEF->zd = zdialog_new(E2X("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",E2X("resize"),"space=3"); zdialog_add_widget(CEF->zd,"button","-10%","hbres","-10%"); zdialog_add_widget(CEF->zd,"button","-1%","hbres","-1%"); zdialog_add_widget(CEF->zd,"button","-.1%","hbres","-.1%"); zdialog_add_widget(CEF->zd,"button","+.1%","hbres","+.1%"); zdialog_add_widget(CEF->zd,"button","+1%","hbres","+1%"); zdialog_add_widget(CEF->zd,"button","+10%","hbres","+10%"); zdialog_add_widget(CEF->zd,"hbox","hbang","dialog",0,"space=3"); zdialog_add_widget(CEF->zd,"label","labang","hbang",Bangle,"space=3"); zdialog_add_widget(CEF->zd,"button","-10°","hbang","-10°"); zdialog_add_widget(CEF->zd,"button","-1°","hbang","-1°"); zdialog_add_widget(CEF->zd,"button","-.1°","hbang","-.1°"); zdialog_add_widget(CEF->zd,"button","+.1°","hbang","+.1°"); zdialog_add_widget(CEF->zd,"button","+1°","hbang","+1°"); zdialog_add_widget(CEF->zd,"button","+10°","hbang","+10°"); zdialog_add_widget(CEF->zd,"hbox","hbbr","dialog",0,"space=3"); zdialog_add_widget(CEF->zd,"label","labbr","hbbr","brightness","space=3"); zdialog_add_widget(CEF->zd,"hscale","brite","hbbr","0.3|3.0|0.001|1.0","expand|space=3"); zdialog_add_widget(CEF->zd,"hbox","hbbl","dialog",0,"space=3"); zdialog_add_widget(CEF->zd,"label","labbl","hbbl","edge blend","space=3"); zdialog_add_widget(CEF->zd,"hscale","blend","hbbl","0|50|0.1|0","expand|space=3"); zdialog_run(CEF->zd,select_paste_dialog_event,"save"); // start dialog takeMouse(select_paste_mousefunc,0); // connect mouse function return; } // Dialog event and completion callback function. // Get dialog values and convert image. When done, commit edited image // (with pasted area) and set up a new select area for the pasted area, // allowing further editing of the area. int select_paste_dialog_event(zdialog *zd, cchar *event) { using namespace sa_diskfile; void select_paste_mousefunc(); void select_paste_image(); void select_paste_adjust(); int ww, hh; PXM *pxm_temp; if (strmatch(event,"done")) zd->zstat = 1; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 2; // cancel if (zd->zstat) // dialog completed { freeMouse(); // disconnect mouse if (zd->zstat != 1 || ! sacp_porg) { // cancel paste edit_cancel(0); // cancel edit, restore image sa_clear(); PXM_free(sacpR_pxm); // free memory return 1; } if (! sa_calced) sa_edgecalc(); // update edge distance data sa_show(1,0); // show area edit_done(0); // commit the edit (pasted image) PXM_free(sacpR_pxm); // free memory return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(select_paste_mousefunc,0); if (zstrstr(event,"%") || zstrstr(event,"°")) // new size or angle { if (strmatch(event,"+.1%")) sacp_scale *= 1.001; if (strmatch(event,"+1%")) sacp_scale *= 1.01; if (strmatch(event,"+10%")) sacp_scale *= 1.10; if (strmatch(event,"-.1%")) sacp_scale *= 0.999001; if (strmatch(event,"-1%")) sacp_scale *= 0.990099; if (strmatch(event,"-10%")) sacp_scale *= 0.909091; // -10% is really 1.0/1.10 if (strmatch(event,"+.1°")) sacp_angle += 0.1; // rotation if (strmatch(event,"+1°")) sacp_angle += 1.0; if (strmatch(event,"+10°")) sacp_angle += 10.0; if (strmatch(event,"-.1°")) sacp_angle -= 0.1; if (strmatch(event,"-1°")) sacp_angle -= 1.0; if (strmatch(event,"-10°")) sacp_angle -= 10.0; PXM_free(sacpR_pxm); // free prior if any ww = sacp_scale * sacp_ww; // new size hh = sacp_scale * sacp_hh; pxm_temp = PXM_rescale(sacp_pxm,ww,hh); // resized area image sacpR_pxm = PXM_rotate(pxm_temp,sacp_angle); // rotated area image PXM_free(pxm_temp); sacpR_ww = sacpR_pxm->ww; // size after resize/rotate sacpR_hh = sacpR_pxm->hh; select_paste_image(); // copy onto target image sa_calced = 0; // edge distance calculation needed } if (strmatch(event,"blend") && sacp_porg) { // edge blend width adjustment zdialog_fetch(zd,"blend",sacp_blend); select_paste_adjust(); } if (strmatch(event,"brite") && sacp_porg) { // area brightness adjustment zdialog_fetch(zd,"brite",sacp_brite); select_paste_adjust(); } Fpaint2(); return 1; } // mouse function - follow mouse drags and move pasted area accordingly void select_paste_mousefunc() { using namespace sa_diskfile; void select_paste_image(); int mx1, my1, mx2, my2; static int mdx0, mdy0, mdx1, mdy1; if (LMclick) { // left mouse click LMclick = 0; sacp_orgx = Mxclick - sacpR_ww / 2; // position image at mouse sacp_orgy = Myclick - sacpR_hh / 2; select_paste_image(); } if (! sacp_porg) return; // no select area paste yet if (Mxposn < sacp_orgx || Mxposn > sacp_orgx + sacpR_ww || // mouse outside select area Myposn < sacp_orgy || Myposn > sacp_orgy + sacpR_hh) gdk_window_set_cursor(gdkwin,0); // set normal cursor else gdk_window_set_cursor(gdkwin,dragcursor); // set drag cursor if (! Mxdrag && ! Mydrag) return; // no drag underway if (Mxdown != mdx0 || Mydown != mdy0) { // new drag initiated mdx0 = mdx1 = Mxdown; mdy0 = mdy1 = Mydown; } mx1 = mdx1; // drag start my1 = mdy1; mx2 = Mxdrag; // drag position my2 = Mydrag; mdx1 = mx2; // next drag start mdy1 = my2; Mxdrag = Mydrag = 0; sacp_orgx += (mx2 - mx1); // move position of select area sacp_orgy += (my2 - my1); // by mouse drag amount select_paste_image(); // re-copy area to new position sa_calced = 0; // edge distance calculation needed return; } // copy select area into edit image, starting at sacp_orgx/y // called when area moved, resized, rotated void select_paste_image() { using namespace sa_diskfile; void select_paste_makearea(); int px1, py1, px2, py2, nc, pcc; float *pix1, *pix2, *pix3; float f1, f2; float red, green, blue; if (sacp_porg) // prior area overlap rectangle { nc = E1pxm->nc; pcc = nc * sizeof(float); for (py2 = 0; py2 < sacp_phh; py2++) // loop area pixels for (px2 = 0; px2 < sacp_pww; px2++) { px1 = px2 + sacp_porgx; // coresp. E1/E3 image pixel py1 = py2 + sacp_porgy; if (px1 < 0 || px1 >= Fpxb->ww) continue; // parts may be beyond edges if (py1 < 0 || py1 >= Fpxb->hh) continue; pix1 = PXMpix(E1pxm,px1,py1); // restore E1 pixels to E3 pix3 = PXMpix(E3pxm,px1,py1); memcpy(pix3,pix1,pcc); } Fpaint3(sacp_porgx,sacp_porgy,sacp_pww,sacp_phh,0); // update window } for (py2 = 0; py2 < sacpR_hh; py2++) // copy paste area pixels to new for (px2 = 0; px2 < sacpR_ww; px2++) // image overlap rectangle { px1 = px2 + sacp_orgx; py1 = py2 + sacp_orgy; if (px1 < 0 || px1 >= E3pxm->ww) continue; // parts may be beyond edges if (py1 < 0 || py1 >= E3pxm->hh) continue; pix2 = PXMpix(sacpR_pxm,px2,py2); // area pixel red = pix2[0]; green = pix2[1]; blue = pix2[2]; pix3 = PXMpix(E3pxm,px1,py1); // corresp. image pixel if (pix2[3] < 255) { // opacity of area pixel f1 = pix2[3] / 255.0; f2 = 1.0 - f1; red = f1 * red + f2 * pix3[0]; // blend area and image pixels green = f1 * green + f2 * pix3[1]; blue = f1 * blue + f2 * pix3[2]; } pix3[0] = red; pix3[1] = green; pix3[2] = blue; } Fpaint3(sacp_orgx,sacp_orgy,sacpR_ww,sacpR_hh,0); // update window for new overlap area sacp_porgx = sacp_orgx; // remember location for next call sacp_porgy = sacp_orgy; sacp_pww = sacpR_ww; sacp_phh = sacpR_hh; sacp_porg = 1; select_paste_makearea(); if (! sa_Npixel) return; CEF->Fmods++; // image is modified CEF->Fsaved = 0; return; } // convert the pasted image area into an equivalent select area by mouse void select_paste_makearea() { using namespace sa_diskfile; int ii, px1, py1, px2, py2; float *pix2, opac; sa_clear(); // clear old area sa_pixmap_create(); // allocate pixel maps 19.0 for (py2 = 0; py2 < sacpR_hh; py2++) // loop area pixels for (px2 = 0; px2 < sacpR_ww; px2++) { px1 = px2 + sacp_orgx; // corresp. E0 image pixel py1 = py2 + sacp_orgy; if (px1 < 0 || px1 >= Fpxb->ww) continue; // parts may be beyond edges if (py1 < 0 || py1 >= Fpxb->hh) continue; pix2 = PXMpix(sacpR_pxm,px2,py2); // get opacity opac = pix2[3]; ii = py1 * Fpxb->ww + px1; // set sa_pixmap[] from opacity if (opac== 0) sa_pixmap[ii] = 0; // pixel outside area else sa_pixmap[ii] = 2; // interior pixel } sa_stat = 1; sa_mode = mode_mouse; // equivalent select by mouse area sa_fww = Fpxb->ww; sa_fhh = Fpxb->hh; sa_finish_auto(); // will find new edge pixels sa_show(0,0); // hide area return; } // make area brightness and edge blend adjustments. void select_paste_adjust() { using namespace sa_diskfile; int px1, py1, px2, py2; float *pix1, *pix2, *pix3; float red, green, blue; float maxrgb, f1, f2; int ii, dist; if (! sa_calced) sa_edgecalc(); // update edge distance data for (py2 = 0; py2 < sacpR_hh; py2++) // copy paste area pixels to new for (px2 = 0; px2 < sacpR_ww; px2++) // image overlap rectangle { px1 = px2 + sacp_orgx; py1 = py2 + sacp_orgy; if (px1 < 0 || px1 >= E3pxm->ww) continue; // parts may be beyond edges if (py1 < 0 || py1 >= E3pxm->hh) continue; pix2 = PXMpix(sacpR_pxm,px2,py2); // area pixel pix1 = PXMpix(E1pxm,px1,py1); // corresp. E1 image pixel pix3 = PXMpix(E3pxm,px1,py1); // corresp. E3 image pixel red = pix2[0]; // area pixel green = pix2[1]; blue = pix2[2]; red *= sacp_brite; // adjust brightness green *= sacp_brite; blue *= sacp_brite; maxrgb = red; // prevent RGB limit overflow if (green > maxrgb) maxrgb = green; if (blue > maxrgb) maxrgb = blue; if (maxrgb > 255.9) { f1 = 255.9 / maxrgb; red *= f1; green *= f1; blue *= f1; } f1 = pix2[3] / 255.0; // area pixel opacity if (f1 < 1.0) { f2 = 1.0 - f1; red = f1 * red + f2 * pix1[0]; // blend area and image pixels green = f1 * green + f2 * pix1[1]; blue = f1 * blue + f2 * pix1[2]; } ii = py1 * Fpxb->ww + px1; dist = sa_pixmap[ii]; // edge distance if (dist == 0) f1 = 0; else if (dist < sacp_blend) f1 = f1 * dist / sacp_blend; // ramp opacity within blend distance f2 = 1.0 - f1; red = f1 * red + f2 * pix1[0]; green = f1 * green + f2 * pix1[1]; blue = f1 * blue + f2 * pix1[2]; pix3[0] = red; pix3[1] = green; pix3[2] = blue; } return; } fotoxx-20.08/f.combine.cc000066400000000000000000011444351362435004500152370ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* Fotoxx image edit functions - Combine menu functions m_HDR combine and adjust images with different exposures m_HDF combine and adjust images with different focus depth m_stack_paint combine multiple photos to eliminate transient objects m_stack_noise combine multiple photos to reduce noise m_stack_layer combine multiple image versions, select areas by mouse m_stack_slider split two images with movable vertical separator m_image_diffs show differences between 2 images m_pano_horz combine overlapped images horizontally m_pano_vert combine overlapped images vertically m_pano_PT combine overlapped images using Panorama Tools (Hugin) *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // File scope variables and functions for composite images // used by HDR, HDF, STP, STN, Panorama. int cimNF; // image count, <= 10 int cimLF; // index of alphabetically last input file char *cimFile[10]; // input image files PXM *cimPXMf[10]; // original image pixmaps PXM *cimPXMs[10]; // alignment images, scaled and curved (pano) PXM *cimPXMw[10]; // alignment images, warped struct cimoffs { // image alignment offsets float xf, yf, tf; // x, y, theta offsets float wx[4], wy[4]; // x/y corner warps, 0=NW, 1=NE, 2=SE, 3=SW }; cimoffs cimOffs[10]; // image alignment data in E3 output image int pixcc = 4 * sizeof(float); // RGB pixel cc with alpha channel float cimScale; // alignment image size relative to full image float cimBlend; // image blend width at overlap, pixels (pano) float cimSearchRange; // alignment search range, pixels float cimSearchStep; // alignment search step, vpixels int cim_manualwarp; // alignment manual warping instead of auto int cim_manualalign; // alignment is manual instead of auto float cimWarpRange; // alignment corner warp range, pixels float cimWarpStep; // alignment corner warp step, vpixels float cimSampSize; // pixel sample size int cimOv1xlo, cimOv1xhi, cimOv1ylo, cimOv1yhi; // rectangle enclosing overlap area, int cimOv2xlo, cimOv2xhi, cimOv2ylo, cimOv2yhi; // image 1 and image2 coordinates float cimRGBmf1[3][256]; // RGB matching factors for image 1/2 compare: float cimRGBmf2[3][256]; // cimRGBmf1[*][pix1[*]] == cimRGBmf2[*][pix2[*]] char *cimRedpix = 0; // maps high-contrast pixels for alignment int cimRedImage; // which image has red pixels int cimNsearch; // alignment search counter int cimShowIm1, cimShowIm2; // two images for cim_show_images() int cimShowAll; // if > 0, show all images int cimPano; // pano mode flag for cim_align_image() int cimPanoV; // vertical pano flag int cimPanoNC; // pano no-curve flag (scanned image) float cimPanoFL; // pano lens focal length at image scale int cim_load_files(); // load and check selected files void cim_scale_image(int im, PXM **); // scale image, 1.0 to cimScale (normally < 1) float cim_get_overlap(int im1, int im2, PXM **); // get overlap area for images (horiz or vert) void cim_match_colors(int im1, int im2, PXM **); // match image RGB levels >> match data void cim_adjust_colors(PXM *pxm, int fwhich); // adjust RGB levels from match data void cim_adjust_colors_pano(PXM *pxm, int fwhich); // adjust RGB levels from match data void cim_adjust_colors_vpano(PXM *pxm, int fwhich); // adjust RGB levels from match data void cim_get_redpix(int im1); // find high-contrast pixels in overlap area void cim_curve_image(int im); // curve cimPXMs[im] using lens parameters void cim_curve_Vimage(int im); // vertical pano version void cim_warp_image(int im); // warp image corners: cimPXMs[im] >> cimPXMw[im] void cim_warp_image_pano(int im, int fblend); // pano version, all / left side / blend stripe void cim_warp_image_Vpano(int im, int fblend); // vertical pans version: bottom side corners int cim_sigdiff(float d1, float d2, float signf); // test 2 values for significant difference 20.0 void cim_align_image(int im1, int im2); // align image im2 to im1, modify im2 offsets float cim_match_images(int im1, int im2); // compute match for overlapped images void cim_show_images(int fnew, int fblend); // combine images >> E3pxm >> main window void cim_show_Vimages(int fnew, int fblend); // vertical pano version void cim_trim(); // cut-off edges where all images do not overlap void cim_trim_margins(); // cut-off excess margins (panorama) void cim_dump_offsets(cchar *label); // diagnostic tool void cim_flatten_image(float F); // flatten panorama image void cim_flatten_Vimage(float F); // flatten vert. panorama image /******************************************************************************** Make an HDR (high dynamic range) image from several images of the same subject with different exposure levels. The composite image has better visibility of detail in both the brightest and darkest areas. *********************************************************************************/ int HDR_stat; // 1 = OK, 0 = failed or canceled float HDR_initAlignSize = 160; // initial align image size float HDR_imageIncrease = 1.6; // image size increase per align cycle float HDR_sampSize = 10000; // pixel sample size float HDR_initSearchRange = 8.0; // initial search range, +/- pixels float HDR_initSearchStep = 1.0; // initial search step, pixels float HDR_searchRange = 3.0; // normal search range, +/- pixels float HDR_searchStep = 1.0; // normal search step, pixels float HDR_initWarpRange = 5.0; // initial corner warp range, +/- pixels float HDR_initWarpStep = 1.0; // initial corner warp step, pixels float HDR_warpRange = 3.0; // normal corner warp range, +/- pixels float HDR_warpStep = 1.0; // normal corner warp step, pixels float *HDR_bright = 0; // maps brightness per pixel zdialog *HDR_adjustzd = 0; // adjust dialog float HDR_respfac[10][1000]; // contribution / image / pixel brightness void * HDR_align_thread(void *); // align 2 images void HDR_brightness(); // compute pixel brightness levels void HDR_adjust(); // adjust image contribution curves void * HDR_adjust_thread(void *); // combine images per contribution curves editfunc EFhdr; // edit function data // menu function void m_HDR(GtkWidget *, cchar *) { char *ftemp; int imx, jj, err, px, py, ww, hh; float diffw, diffh; float fbright[10], btemp; float pixsum, fnorm = 1.0 / 256.0; float *pixel; PXM *pxmtemp; F1_help_topic = "HDR"; // help topic if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (checkpend("all")) return; HDR_bright = 0; cim_manualwarp = 0; cimNF = 0; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } gallery_select_clear(); // clear selected file list gallery_select(); // get new list if (GScount == 0) goto cleanup; if (GScount < 2 || GScount > 9) { zmessageACK(Mwin,E2X("Select 2 to 9 files")); goto cleanup; } cimNF = GScount; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(GSfiles[imx]); // set up file list if (! cim_load_files()) goto cleanup; // load and check all files ww = cimPXMf[0]->ww; hh = cimPXMf[0]->hh; for (imx = 1; imx < cimNF; imx++) // check image compatibility { diffw = abs(ww - cimPXMf[imx]->ww); diffw = diffw / ww; diffh = abs(hh - cimPXMf[imx]->hh); diffh = diffh / hh; if (diffw > 0.02 || diffh > 0.02) { zmessageACK(Mwin,E2X("Images are not all the same size")); goto cleanup; } } free_resources(); // ready to commit err = f_open(cimFile[cimLF]); // curr_file = alphabetically last if (err) goto cleanup; EFhdr.menufunc = m_HDR; EFhdr.funcname = "HDR"; if (! edit_setup(EFhdr)) goto cleanup; // setup edit (will lock) for (imx = 0; imx < cimNF; imx++) // compute image brightness levels { ww = cimPXMf[imx]->ww; hh = cimPXMf[imx]->hh; // image sizes can vary a little pixsum = 0; for (py = 0; py < hh; py++) for (px = 0; px < ww; px++) { pixel = PXMpix(cimPXMf[imx],px,py); pixsum += fnorm * (pixel[0] + pixel[1] + pixel[2]); } fbright[imx] = pixsum / (ww * hh); } for (imx = 0; imx < cimNF; imx++) // sort file and pixmap lists for (jj = imx+1; jj < cimNF; jj++) // by decreasing brightness { if (fbright[jj] > fbright[imx]) { // bubble sort btemp = fbright[jj]; fbright[jj] = fbright[imx]; fbright[imx] = btemp; ftemp = cimFile[jj]; cimFile[jj] = cimFile[imx]; cimFile[imx] = ftemp; pxmtemp = cimPXMf[jj]; cimPXMf[jj] = cimPXMf[imx]; cimPXMf[imx] = pxmtemp; } } start_thread(HDR_align_thread,0); // align each pair of images wrapup_thread(0); // wait for completion if (HDR_stat != 1) goto cancel; if (Fescape) { // user cancel 19.0 Fescape = 0; zmessage_post_bold(Mwin,"parent",3,Bcancel); goto cancel; } HDR_brightness(); // compute pixel brightness levels if (HDR_stat != 1) goto cancel; HDR_adjust(); // combine images based on user inputs if (HDR_stat != 1) goto cancel; CEF->Fmods++; // done CEF->Fsaved = 0; edit_done(0); goto cleanup; cancel: edit_cancel(0); cleanup: for (imx = 0; imx < cimNF; imx++) { // free cim file and PXM data if (cimFile[imx]) zfree(cimFile[imx]); if (cimPXMf[imx]) PXM_free(cimPXMf[imx]); if (cimPXMs[imx]) PXM_free(cimPXMs[imx]); if (cimPXMw[imx]) PXM_free(cimPXMw[imx]); } if (HDR_bright) zfree(HDR_bright); *paneltext = 0; return; } // HDR align each pair of input images, output combined image to E3pxm // cimPXMf[*] original image // cimPXMs[*] scaled and color adjusted for pixel comparisons // cimPXMw[*] warped for display void * HDR_align_thread(void *) { int imx, im1, im2, ww, hh, ii, nn; float R, maxtf, mintf, midtf; float xoff, yoff, toff, dxoff, dyoff; cimoffs offsets[10]; // x/y/t offsets after alignment Ffuncbusy = 1; cimPano = cimPanoV = 0; // no pano mode for (imx = 0; imx < cimNF; imx++) memset(&offsets[imx],0,sizeof(cimoffs)); for (im1 = 0; im1 < cimNF-1; im1++) // loop each pair of images { im2 = im1 + 1; memset(&cimOffs[im1],0,sizeof(cimoffs)); // initial image offsets = 0 memset(&cimOffs[im2],0,sizeof(cimoffs)); ww = cimPXMf[im1]->ww; // image dimensions hh = cimPXMf[im1]->hh; nn = ww; // use larger of ww, hh if (hh > ww) nn = hh; cimScale = HDR_initAlignSize / nn; // initial align image size if (cimScale > 1.0) cimScale = 1.0; cimBlend = 0; // no blend width (use all) cim_get_overlap(im1,im2,cimPXMf); // get overlap area cim_match_colors(im1,im2,cimPXMf); // get color matching factors cimSearchRange = HDR_initSearchRange; // initial align search range cimSearchStep = HDR_initSearchStep; // initial align search step cimWarpRange = HDR_initWarpRange; // initial align corner warp range cimWarpStep = HDR_initWarpStep; // initial align corner warp step cimSampSize = HDR_sampSize; // pixel sample size for align/compare cimNsearch = 0; // reset align search counter while (true) // loop, increasing image size { cim_scale_image(im1,cimPXMs); // scale images to cimScale cim_scale_image(im2,cimPXMs); cim_adjust_colors(cimPXMs[im1],1); // apply color adjustments cim_adjust_colors(cimPXMs[im2],2); cim_warp_image(im1); // make warped images to show cim_warp_image(im2); cim_get_overlap(im1,im2,cimPXMs); // get overlap area cim_get_redpix(im1); // get high-contrast pixels cimShowIm1 = im1; // show two images with 50/50 blend cimShowIm2 = im2; cimShowAll = 0; cim_show_images(1,0); // (x/y offsets can change) cim_align_image(im1,im2); // align im2 to im1 if (Fescape) goto finish; // user kill 19.0 zfree(cimRedpix); // clear red pixels cimRedpix = 0; if (cimScale == 1.0) break; // done R = HDR_imageIncrease; // next larger image size cimScale = cimScale * R; if (cimScale > 0.85) { // if close to end, jump to end R = R / cimScale; cimScale = 1.0; } cimOffs[im1].xf *= R; // scale offsets for larger image cimOffs[im1].yf *= R; cimOffs[im2].xf *= R; cimOffs[im2].yf *= R; for (ii = 0; ii < 4; ii++) { cimOffs[im1].wx[ii] *= R; cimOffs[im1].wy[ii] *= R; cimOffs[im2].wx[ii] *= R; cimOffs[im2].wy[ii] *= R; } cimSearchRange = HDR_searchRange; // align search range cimSearchStep = HDR_searchStep; // align search step size cimWarpRange = HDR_warpRange; // align corner warp range cimWarpStep = HDR_warpStep; // align corner warp step size } offsets[im2].xf = cimOffs[im2].xf - cimOffs[im1].xf; // save im2 offsets from im1 offsets[im2].yf = cimOffs[im2].yf - cimOffs[im1].yf; offsets[im2].tf = cimOffs[im2].tf - cimOffs[im1].tf; for (ii = 0; ii < 4; ii++) { offsets[im2].wx[ii] = cimOffs[im2].wx[ii] - cimOffs[im1].wx[ii]; offsets[im2].wy[ii] = cimOffs[im2].wy[ii] - cimOffs[im1].wy[ii]; } } for (imx = 0; imx < cimNF; imx++) // offsets[*] >> cimOffs[*] cimOffs[imx] = offsets[imx]; cimOffs[0].xf = cimOffs[0].yf = cimOffs[0].tf = 0; // image 0 at (0,0,0) for (im1 = 0; im1 < cimNF-1; im1++) // absolute offsets for image 1 to last { im2 = im1 + 1; cimOffs[im2].xf += cimOffs[im1].xf; // x/y/t offsets are additive cimOffs[im2].yf += cimOffs[im1].yf; cimOffs[im2].tf += cimOffs[im1].tf; for (ii = 0; ii < 4; ii++) { // corner warps are additive cimOffs[im2].wx[ii] += cimOffs[im1].wx[ii]; cimOffs[im2].wy[ii] += cimOffs[im1].wy[ii]; } } for (imx = 1; imx < cimNF; imx++) // re-warp to absolute cim_warp_image(imx); toff = cimOffs[0].tf; // balance +/- thetas maxtf = mintf = toff; for (imx = 1; imx < cimNF; imx++) { toff = cimOffs[imx].tf; if (toff > maxtf) maxtf = toff; if (toff < mintf) mintf = toff; } midtf = 0.5 * (maxtf + mintf); for (imx = 0; imx < cimNF; imx++) cimOffs[imx].tf -= midtf; for (im1 = 0; im1 < cimNF-1; im1++) // adjust x/y offsets for images after im1 for (im2 = im1+1; im2 < cimNF; im2++) // due to im1 theta offset { toff = cimOffs[im1].tf; xoff = cimOffs[im2].xf - cimOffs[im1].xf; yoff = cimOffs[im2].yf - cimOffs[im1].yf; dxoff = yoff * sinf(toff); dyoff = xoff * sinf(toff); cimOffs[im2].xf -= dxoff; cimOffs[im2].yf += dyoff; } finish: Ffuncbusy = 0; HDR_stat = 1; thread_exit(); return 0; } // Compute mean image pixel brightness levels. // (basis for setting image contributions per brightness level) void HDR_brightness() { int px3, py3, ww, hh, imx, kk, vstat; float px, py, red, green, blue; float bright, maxbright, minbright; float xoff, yoff, sintf[10], costf[10]; float norm, fnorm = 1.0 / 256.0; float vpix[4], *pix3; Ffuncbusy = 1; cimScale = 1.0; for (imx = 0; imx < cimNF; imx++) // replace alignment images { // (color adjusted for pixel matching) PXM_free(cimPXMs[imx]); // with the original images cimPXMs[imx] = PXM_copy(cimPXMf[imx]); cim_warp_image(imx); // re-apply warps } for (imx = 0; imx < cimNF; imx++) // pre-calculate trig functions { sintf[imx] = sinf(cimOffs[imx].tf); costf[imx] = cosf(cimOffs[imx].tf); } ww = E3pxm->ww; hh = E3pxm->hh; HDR_bright = (float *) zmalloc(ww*hh*sizeof(int)); // get memory for brightness array minbright = 1.0; maxbright = 0.0; for (py3 = 0; py3 < hh; py3++) // step through all output pixels for (px3 = 0; px3 < ww; px3++) { red = green = blue = 0; vstat = 0; for (imx = 0; imx < cimNF; imx++) // step through all input images { xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); // image N pixel, after offsets py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat = vpixel(cimPXMw[imx],px,py,vpix); if (! vstat) break; red += fnorm * vpix[0]; // sum input pixels green += fnorm * vpix[1]; blue += fnorm * vpix[2]; } if (! vstat) { // pixel outside some image pix3 = PXMpix(E3pxm,px3,py3); // output pixel = black pix3[0] = pix3[1] = pix3[2] = pix3[3] = 0; // alpha = 0 kk = py3 * ww + px3; HDR_bright[kk] = 0; continue; } bright = (red + green + blue) / (3 * cimNF); // mean pixel brightness, 0.0 to 1.0 kk = py3 * ww + px3; HDR_bright[kk] = bright; if (bright > maxbright) maxbright = bright; if (bright < minbright) minbright = bright; pix3 = PXMpix(E3pxm,px3,py3); // output pixel pix3[0] = red * 255.9 / cimNF; pix3[1] = green * 255.9 / cimNF; pix3[2] = blue * 255.9 / cimNF; } norm = 0.999 / (maxbright - minbright); // normalize to range 0.0 to 0.999 for (int ii = 0; ii < ww * hh; ii++) HDR_bright[ii] = (HDR_bright[ii] - minbright) * norm; Ffuncbusy = 0; Fpaint2(); // update window return; } // Dialog for user to control the contributions of each input image // while watching the output image which is updated in real time. void HDR_adjust() { int HDR_adjust_dialog_event(zdialog *zd, cchar *event); void HDR_curvedit(int); int imx; float cww = 1.0 / (cimNF-1); /*** _____________________________________________________ | Adjust Image Contributions | | _________________________________________________ | | | | | | | | | | | | | | | curve drawing area | | | | | | | | | | | | | | | |_________________________________________________| | | dark pixels light pixels | | file: xxxxxx.jpg | | | | Curve File: [Open] [Save] | | [Done] [Cancel] | |_____________________________________________________| ***/ HDR_adjustzd = zdialog_new(E2X("Adjust Image Contributions"),Mwin,Bdone,Bcancel,null); zdialog *zd = HDR_adjustzd; zdialog_add_widget(zd,"frame","brframe","dialog",0,"expand|space=2"); zdialog_add_widget(zd,"hbox","hb1","dialog",0); zdialog_add_widget(zd,"label","lab11","hb1",E2X("dark pixels"),"space=3"); zdialog_add_widget(zd,"label","lab12","hb1",0,"expand"); zdialog_add_widget(zd,"label","lab13","hb1",E2X("light pixels"),"space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labf1","hb2",E2X("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.sd = 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,300,250); zdialog_run(zd,HDR_adjust_dialog_event,"save"); // run dialog zdialog_wait(zd); // wait for completion zdialog_free(zd); return; } // dialog event and completion callback function int HDR_adjust_dialog_event(zdialog *zd, cchar *event) { spldat *sd = EFhdr.sd; 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 { wrapup_thread(8); if (zd->zstat == 1) HDR_stat = 1; else HDR_stat = 0; if (HDR_stat == 1) cim_trim(); // cut-off edges } return 1; } // this function is called when a curve is edited void HDR_curvedit(int spc) { cchar *pp; pp = strrchr(cimFile[spc],'/'); zdialog_stuff(HDR_adjustzd,"labf2",pp+1); signal_thread(); return; } // Combine all input images >> E3pxm based on image response curves. void * HDR_adjust_thread(void *) { void * HDR_adjust_wthread(void *arg); int imx, ii, kk; float xlo, xhi, xval, yval, sumrf; spldat *sd = EFhdr.sd; 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; } while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 do_wthreads(HDR_adjust_wthread,NWT); // worker threads paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed } void * HDR_adjust_wthread(void *arg) // working thread { int index = *((int *) (arg)); int imx, ww, hh, ii, px3, py3, vstat; float sintf[10], costf[10], xoff, yoff; float px, py, red, green, blue, bright, factor; float vpix[4], *pix3; for (imx = 0; imx < cimNF; imx++) // pre-calculate trig functions { sintf[imx] = sinf(cimOffs[imx].tf); costf[imx] = cosf(cimOffs[imx].tf); } ww = E3pxm->ww; hh = E3pxm->hh; for (py3 = index; py3 < hh; py3 += NWT) // step through all output pixels for (px3 = 0; px3 < ww; px3++) { ii = py3 * ww + px3; bright = HDR_bright[ii]; // mean brightness, 0.0 to 1.0 ii = 1000 * bright; if (ii < 0) ii = 0; if (ii > 999) ii = 999; 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; } pthread_exit(0); } /******************************************************************************** Make an HDF (high depth of field) image from several images of the same subject with different focus settings. Combine the images and allow the user to "paint" the output composite image using the mouse and choosing the sharpest input image for each area of the output image. The result is an image with a depth of field that exceeds the camera capability. The images are aligned at the center, but small differences in camera position (hand-held photos) will cause parallax errors that prevent perfect alignment of the images. Also, the images with nearer focus will be slightly larger than those with farther focus. These problems can be compensated by dragging and warping the images using the mouse. ********************************************************************************/ int HDF_stat; // 1 = OK, 0 = failed or canceled float HDF_initAlignSize = 160; // initial align image size float HDF_imageIncrease = 1.6; // image size increase per align cycle float HDF_sampSize = 10000; // pixel sample size float HDF_initSearchRange = 8.0; // initial search range, +/- pixels float HDF_initSearchStep = 2.0; // initial search step, pixels float HDF_searchRange = 4.0; // normal search range float HDF_searchStep = 1.0; // normal search step float HDF_initWarpRange = 6.0; // initial corner warp range float HDF_initWarpStep = 2.0; // initial corner warp step float HDF_warpRange = 3.0; // normal corner warp range float HDF_warpStep = 1.0; // normal corner warp step void * HDF_align_thread(void *); void HDF_adjust(); void HDF_mousefunc(); void * HDF_adjust_thread(void *); editfunc EFhdf; // edit function data // menu function void m_HDF(GtkWidget *, cchar *) { int imx, err, ww, hh; float diffw, diffh; F1_help_topic = "HDF"; // help topic if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (checkpend("all")) return; cim_manualwarp = 0; cimNF = 0; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } gallery_select_clear(); // clear selected file list gallery_select(); // get new list if (GScount == 0) goto cleanup; if (GScount < 2 || GScount > 9) { zmessageACK(Mwin,E2X("Select 2 to 9 files")); goto cleanup; } cimNF = GScount; for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(GSfiles[imx]); // set up file list if (! cim_load_files()) goto cleanup; // load and check all files ww = cimPXMf[0]->ww; hh = cimPXMf[0]->hh; for (imx = 1; imx < cimNF; imx++) // check image compatibility { diffw = abs(ww - cimPXMf[imx]->ww); diffw = diffw / ww; diffh = abs(hh - cimPXMf[imx]->hh); diffh = diffh / hh; if (diffw > 0.02 || diffh > 0.02) { zmessageACK(Mwin,E2X("Images are not all the same size")); goto cleanup; } } free_resources(); // ready to commit err = f_open(cimFile[cimLF]); // curr_file = alphabetically last if (err) goto cleanup; EFhdf.menufunc = m_HDF; EFhdf.funcname = "HDF"; EFhdf.mousefunc = HDF_mousefunc; if (! edit_setup(EFhdf)) goto cleanup; // setup edit (will lock) start_thread(HDF_align_thread,0); // align each pair of images wrapup_thread(0); // wait for completion if (HDF_stat != 1) goto cancel; if (Fescape) { // user cancel 19.0 Fescape = 0; zmessage_post_bold(Mwin,"parent",3,Bcancel); goto cancel; } HDF_adjust(); // combine images based on user inputs if (HDF_stat != 1) goto cancel; CEF->Fmods++; // done CEF->Fsaved = 0; edit_done(0); goto cleanup; cancel: edit_cancel(0); cleanup: for (imx = 0; imx < cimNF; imx++) { // free cim file and PXM data if (cimFile[imx]) zfree(cimFile[imx]); if (cimPXMf[imx]) PXM_free(cimPXMf[imx]); if (cimPXMs[imx]) PXM_free(cimPXMs[imx]); if (cimPXMw[imx]) PXM_free(cimPXMw[imx]); } *paneltext = 0; return; } // align each image 2nd-last to 1st image // cimPXMf[*] original image // cimPXMs[*] scaled and color adjusted for pixel comparisons // cimPXMw[*] warped for display void * HDF_align_thread(void *) { int imx, im1, im2, ww, hh, ii, nn; float R; Ffuncbusy = 1; cimPano = cimPanoV = 0; // no pano mode for (imx = 1; imx < cimNF; imx++) // loop 2nd to last image { im1 = 0; // images to align im2 = imx; memset(&cimOffs[im1],0,sizeof(cimoffs)); // initial image offsets = 0 memset(&cimOffs[im2],0,sizeof(cimoffs)); ww = cimPXMf[im1]->ww; // image dimensions hh = cimPXMf[im1]->hh; nn = ww; // use larger of ww, hh if (hh > ww) nn = hh; cimScale = HDF_initAlignSize / nn; // initial align image size if (cimScale > 1.0) cimScale = 1.0; cimBlend = 0; // no blend width (use all) cim_get_overlap(im1,im2,cimPXMf); // get overlap area cim_match_colors(im1,im2,cimPXMf); // get color matching factors cimSearchRange = HDF_initSearchRange; // initial align search range cimSearchStep = HDF_initSearchStep; // initial align search step cimWarpRange = HDF_initWarpRange; // initial align corner warp range cimWarpStep = HDF_initWarpStep; // initial align corner warp step cimSampSize = HDF_sampSize; // pixel sample size for align/compare cimNsearch = 0; // reset align search counter while (true) // loop, increasing image size { cim_scale_image(im1,cimPXMs); // scale images to cimScale cim_scale_image(im2,cimPXMs); cim_adjust_colors(cimPXMs[im1],1); // apply color adjustments cim_adjust_colors(cimPXMs[im2],2); cim_warp_image(im1); // warp images for show cim_warp_image(im2); cim_get_overlap(im1,im2,cimPXMs); // get overlap area cim_get_redpix(im1); // get high-contrast pixels cimShowIm1 = im1; // show these two images cimShowIm2 = im2; // with 50/50 blend cimShowAll = 0; cim_show_images(1,0); // (x/y offsets can change) cim_align_image(im1,im2); // align im2 to im1 if (Fescape) goto finish; // user kill 19.0 zfree(cimRedpix); // clear red pixels cimRedpix = 0; if (cimScale == 1.0) break; // done R = HDF_imageIncrease; // next larger image size cimScale = cimScale * R; if (cimScale > 0.85) { // if close to end, jump to end R = R / cimScale; cimScale = 1.0; } cimOffs[im1].xf *= R; // scale offsets for larger image cimOffs[im1].yf *= R; cimOffs[im2].xf *= R; cimOffs[im2].yf *= R; for (ii = 0; ii < 4; ii++) { cimOffs[im1].wx[ii] *= R; cimOffs[im1].wy[ii] *= R; cimOffs[im2].wx[ii] *= R; cimOffs[im2].wy[ii] *= R; } cimSearchRange = HDF_searchRange; // align search range cimSearchStep = HDF_searchStep; // align search step size cimWarpRange = HDF_warpRange; // align corner warp range cimWarpStep = HDF_warpStep; // align corner warp step size } cimOffs[im2].xf = cimOffs[im2].xf - cimOffs[im1].xf; // save im2 offsets from im1 cimOffs[im2].yf = cimOffs[im2].yf - cimOffs[im1].yf; cimOffs[im2].tf = cimOffs[im2].tf - cimOffs[im1].tf; } memset(&cimOffs[0],0,sizeof(cimoffs)); finish: Ffuncbusy = 0; HDF_stat = 1; thread_exit(); return 0; // not executed } // paint and warp output image zdialog *HDF_adjustzd = 0; // paint/warp dialog int HDF_mode; // mode: paint or warp int HDF_image; // current image (0 based) int HDF_radius; // paint mode radius char *HDF_pixmap = 0; // map input image per output pixel float *HDF_warpx[10], *HDF_warpy[10]; // warp memory, pixel displacements void HDF_adjust() { char imageN[8] = "imageN", labN[4] = "0"; int cc, imx, ww, hh; int HDF_adjust_dialog_event(zdialog *zd, cchar *event); /*** ______________________________________ | Paint and Warp Image | | | | Image (o) 1 (o) 2 (o) 3 ... | | (o) paint Radius [____] | | (o) warp | | [Done] [Cancel] | |______________________________________| ***/ HDF_adjustzd = zdialog_new(E2X("Paint and Warp Image"),Mwin,Bdone,Bcancel,null); zdialog *zd = HDF_adjustzd; zdialog_add_widget(zd,"hbox","hbim","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labim","hbim",Bimage,"space=5"); zdialog_add_widget(zd,"hbox","hbpw","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vbpw1","hbpw",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vbpw2","hbpw",0,"homog|space=5"); zdialog_add_widget(zd,"radio","paint","vbpw1",E2X("paint")); zdialog_add_widget(zd,"radio","warp","vbpw1",E2X("warp")); zdialog_add_widget(zd,"hbox","hbp","vbpw2"); zdialog_add_widget(zd,"label","labpr","hbp",Bradius,"space=5"); zdialog_add_widget(zd,"zspin","radius","hbp","1|400|1|100"); zdialog_add_widget(zd,"label","space","vbpw2"); for (imx = 0; imx < cimNF; imx++) { // add radio button for each image imageN[5] = '1' + imx; labN[0] = '1' + imx; zdialog_add_widget(zd,"radio",imageN,"hbim",labN); } zdialog_stuff(zd,"paint",1); // paint button on zdialog_stuff(zd,"warp",0); // warp button off zdialog_stuff(zd,"image1",1); // initial image = 1st HDF_mode = 0; // start in paint mode HDF_image = 0; // initial image HDF_radius = 100; // paint radius takeMouse(HDF_mousefunc,0); // connect mouse function cc = E3pxm->ww * E3pxm->hh; // allocate pixel map HDF_pixmap = (char *) zmalloc(cc); memset(HDF_pixmap,cimNF,cc); // initial state, blend all images for (imx = 0; imx < cimNF; imx++) { // allocate warp memory ww = cimPXMw[imx]->ww; hh = cimPXMw[imx]->hh; HDF_warpx[imx] = (float *) zmalloc(ww * hh * sizeof(float)); HDF_warpy[imx] = (float *) zmalloc(ww * hh * sizeof(float)); } start_thread(HDF_adjust_thread,0); // start working thread signal_thread(); zdialog_resize(zd,250,0); // stretch a bit zdialog_run(zd,HDF_adjust_dialog_event,"save"); // run dialog, parallel zdialog_wait(zd); // wait for completion zdialog_free(zd); return; } // dialog event and completion callback function int HDF_adjust_dialog_event(zdialog *zd, cchar *event) { int imx, nn; if (zd->zstat) // dialog finish { freeMouse(); // disconnect mouse function signal_thread(); wrapup_thread(8); if (zd->zstat == 1) HDF_stat = 1; else HDF_stat = 0; if (HDF_stat == 1) cim_trim(); // cut-off edges HDF_mode = 0; zfree(HDF_pixmap); // free pixel map for (imx = 0; imx < cimNF; imx++) { zfree(HDF_warpx[imx]); // free warp memory zfree(HDF_warpy[imx]); } return 1; // 19.0 } if (strmatch(event,"paint")) { // set paint mode zdialog_fetch(zd,"paint",nn); if (! nn) return 1; HDF_mode = 0; gdk_window_set_cursor(gdkwin,0); // no drag cursor } if (strmatch(event,"warp")) { // set warp mode zdialog_fetch(zd,"warp",nn); if (! nn) return 1; HDF_mode = 1; draw_mousecircle(0,0,0,1,0); // stop mouse circle gdk_window_set_cursor(gdkwin,dragcursor); // set drag cursor } if (strmatchN(event,"image",5)) { // image radio button nn = event[5] - '0'; // 1 to cimNF if (nn > 0 && nn <= cimNF) HDF_image = nn - 1; // 0 to cimNF-1 signal_thread(); } if (strmatch(event,"radius")) // change paint radius zdialog_fetch(zd,"radius",HDF_radius); if (strmatch(event,"focus")) { // toggle mouse capture takeMouse(HDF_mousefunc,0); // connect mouse function if (HDF_mode == 1) gdk_window_set_cursor(gdkwin,dragcursor); // warp mode, drag cursor signal_thread(); } return 1; } // HDF dialog mouse function // paint: during drag, selected image >> HDF_pixmap (within paint radius) >> E3 // warp: for selected image, cimPXMs >> warp >> cimPXMw >> E3 void HDF_mousefunc() { float vpix1[4], *pix2, *pix3; int imx, radius, rect, radius2, vstat1; int mx, my, dx, dy, px3, py3; char imageN[8] = "imageN"; float px1, py1; float xoff, yoff, sintf[10], costf[10]; int ii, px, py, ww, hh; float mag, dispx, dispy, d1, d2; PXM *pxm1, *pxm2; if (LMclick || RMclick) // mouse click return; // process zooms normally if (HDF_mode == 0) goto paint; else if (HDF_mode == 1) goto warp; else return; paint: radius = HDF_radius; // paintbrush radius radius2 = radius * radius; draw_mousecircle(Mxposn,Myposn,radius,0,0); // draw mouse selection circle if (! Mxdrag && ! Mydrag) return; mx = Mxdrag; // mouse drag my = Mydrag; Mxdrag = Mydrag = 0; if (mx < 0 || mx > E3pxm->ww-1 || my < 0 || my > E3pxm->hh-1) // mouse outside image area return; for (imx = 0; imx < cimNF; imx++) // pre-calculate trig funcs { sintf[imx] = sinf(cimOffs[imx].tf); costf[imx] = cosf(cimOffs[imx].tf); } for (dy = -radius; dy <= radius; dy++) // loop pixels around mouse for (dx = -radius; dx <= radius; dx++) { if (dx*dx + dy*dy > radius2) continue; // outside radius px3 = mx + dx; // output pixel py3 = my + dy; if (px3 < 0 || px3 > E3pxm->ww-1) continue; // outside image if (py3 < 0 || py3 > E3pxm->hh-1) continue; pix3 = PXMpix(E3pxm,px3,py3); // output pixel imx = py3 * E3pxm->ww + px3; // update pixmap to selected image HDF_pixmap[imx] = HDF_image; imx = HDF_image; xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px1 = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); // input virtual pixel py1 = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat1 = vpixel(cimPXMw[imx],px1,py1,vpix1); if (vstat1) memcpy(pix3,vpix1,pixcc); else memset(pix3,0,pixcc); // voided pixel } rect = 2 * radius; Fpaint3(mx-radius,my-radius,rect,rect,0); // update window draw_mousecircle(mx,my,radius,0,0); // draw mouse selection circle return; warp: if (! Mxdrag && ! Mydrag) return; mx = Mxdrag; // mouse drag my = Mydrag; if (mx < 0 || mx > E3pxm->ww-1 || my < 0 || my > E3pxm->hh-1) // mouse outside image area return; imx = my * E3pxm->ww + mx; // if pixel has been painted, imx = HDF_pixmap[imx]; // select corresp. image to warp if (imx == cimNF) return; // else no action if (imx != HDF_image) { HDF_image = imx; // update selected image and imageN[5] = '1' + imx; // dialog radio button zdialog_stuff(HDF_adjustzd,imageN,1); } pxm1 = cimPXMs[imx]; // input image pxm2 = cimPXMw[imx]; // output image ww = pxm2->ww; hh = pxm2->hh; mx = Mxdown; // drag origin, image coordinates my = Mydown; dx = Mxdrag - Mxdown; // drag increment dy = Mydrag - Mydown; Mxdown = Mxdrag; // next drag origin Mydown = Mydrag; Mxdrag = Mydrag = 0; d1 = ww * ww + hh * hh; for (py = 0; py < hh; py++) // process all output pixels for (px = 0; px < ww; px++) { d2 = (px-mx)*(px-mx) + (py-my)*(py-my); mag = (1.0 - d2 / d1); mag = mag * mag * mag * mag; mag = mag * mag * mag * mag; mag = mag * mag * mag * mag; dispx = -dx * mag; // displacement = drag * mag dispy = -dy * mag; ii = py * ww + px; HDF_warpx[imx][ii] += dispx; // add this drag to prior sum HDF_warpy[imx][ii] += dispy; dispx = HDF_warpx[imx][ii]; dispy = HDF_warpy[imx][ii]; vstat1 = vpixel(pxm1,px+dispx,py+dispy,vpix1); // input virtual pixel pix2 = PXMpix(pxm2,px,py); // output pixel if (vstat1) memcpy(pix2,vpix1,pixcc); else memset(pix2,0,pixcc); // voided pixel } signal_thread(); // combine images >> E3 >> main window return; } // Combine images in E3pxm (not reallocated). Update main window. void * HDF_adjust_thread(void *) { void * HDF_combine_wthread(void *); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 do_wthreads(HDF_combine_wthread,NWT); // worker threads paintlock(0); // unblock window paint 19.0 Fpaint2(); } return 0; // not executed } void * HDF_combine_wthread(void *arg) // worker thread { int index = *((int *) (arg)); // no more paint and warp modes int px3, py3, vstat1, imx; float red, green, blue; // float float px, py; float xoff, yoff, sintf[10], costf[10]; float vpix1[4], *pix3; for (imx = 0; imx < cimNF; imx++) // pre-calculate trig funcs { sintf[imx] = sinf(cimOffs[imx].tf); costf[imx] = cosf(cimOffs[imx].tf); } for (py3 = index+1; py3 < E3pxm->hh-1; py3 += NWT) // step through output pixels for (px3 = 1; px3 < E3pxm->ww-1; px3++) { pix3 = PXMpix(E3pxm,px3,py3); imx = py3 * E3pxm->ww + px3; imx = HDF_pixmap[imx]; if (imx < cimNF) // specific image maps to pixel { xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat1 = vpixel(cimPXMw[imx],px,py,vpix1); // corresp. input vpixel if (vstat1) memcpy(pix3,vpix1,pixcc); else memset(pix3,0,pixcc); // voided pixel } else // use blend of all images { red = green = blue = 0; for (imx = 0; imx < cimNF; imx++) { xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat1 = vpixel(cimPXMw[imx],px,py,vpix1); if (vstat1) { red += vpix1[0]; green += vpix1[1]; blue += vpix1[2]; } } pix3[0] = red / cimNF; pix3[1] = green / cimNF; pix3[2] = blue / cimNF; } } pthread_exit(0); } /******************************************************************************** Stack/Paint function Combine multiple images of one subject taken at different times from (almost) the same camera position. Align the images and allow the user to choose which input image to use for each area of the output image, by "painting" with the mouse. Use this to remove tourists and cars that move in and out of a scene being photographed. ********************************************************************************/ int STP_stat; // 1 = OK, 0 = failed or canceled float STP_initAlignSize = 160; // initial align image size float STP_imageIncrease = 1.6; // image size increase per align cycle float STP_sampSize = 10000; // pixel sample size float STP_initSearchRange = 8.0; // initial search range, +/- pixels float STP_initSearchStep = 2.0; // initial search step, pixels float STP_searchRange = 4.0; // normal search range float STP_searchStep = 1.0; // normal search step float STP_initWarpRange = 6.0; // initial corner warp range float STP_initWarpStep = 2.0; // initial corner warp step float STP_warpRange = 3.0; // normal corner warp range float STP_warpStep = 0.5; // normal corner warp step void * STP_align_thread(void *); void STP_adjust(); void STP_mousefunc(); void * STP_adjust_thread(void *); void STP_setpixel(int px, int py, int source); editfunc EFstp; // edit function data // menu function void m_stack_paint(GtkWidget *, cchar *) { int imx, err, ww, hh; float diffw, diffh; F1_help_topic = "stack/paint"; // help topic if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (checkpend("all")) return; cim_manualwarp = 0; cimNF = 0; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } gallery_select_clear(); // clear selected file list gallery_select(); // get new list if (GScount == 0) goto cleanup; if (GScount < 2 || GScount > 9) { zmessageACK(Mwin,E2X("Select 2 to 9 files")); goto cleanup; } cimNF = GScount; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(GSfiles[imx]); // set up file list if (! cim_load_files()) goto cleanup; // load and check all files ww = cimPXMf[0]->ww; hh = cimPXMf[0]->hh; for (imx = 1; imx < cimNF; imx++) // check image compatibility { diffw = abs(ww - cimPXMf[imx]->ww); diffw = diffw / ww; diffh = abs(hh - cimPXMf[imx]->hh); diffh = diffh / hh; if (diffw > 0.02 || diffh > 0.02) { zmessageACK(Mwin,E2X("Images are not all the same size")); goto cleanup; } } free_resources(); // ready to commit err = f_open(cimFile[cimLF]); // curr_file = alphabetically last if (err) goto cleanup; EFstp.menufunc = m_stack_paint; EFstp.funcname = "stack_paint"; EFstp.mousefunc = STP_mousefunc; 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; if (Fescape) { // user cancel 19.0 Fescape = 0; zmessage_post_bold(Mwin,"parent",3,Bcancel); goto cancel; } STP_adjust(); // combine images based on user inputs if (STP_stat != 1) goto cancel; CEF->Fmods++; // done CEF->Fsaved = 0; edit_done(0); goto cleanup; cancel: edit_cancel(0); cleanup: for (imx = 0; imx < cimNF; imx++) { // free cim file and PXM data if (cimFile[imx]) zfree(cimFile[imx]); if (cimPXMf[imx]) PXM_free(cimPXMf[imx]); if (cimPXMs[imx]) PXM_free(cimPXMs[imx]); if (cimPXMw[imx]) PXM_free(cimPXMw[imx]); } *paneltext = 0; return; } // align each image 2nd-last to 1st image // cimPXMf[*] original image // cimPXMs[*] scaled and color adjusted for pixel comparisons // cimPXMw[*] warped for display void * STP_align_thread(void *) { int imx, im1, im2, ww, hh, ii, nn; float R; Ffuncbusy = 1; cimPano = cimPanoV = 0; // no pano mode for (imx = 1; imx < cimNF; imx++) // loop 2nd to last image { im1 = 0; // images to align im2 = imx; memset(&cimOffs[im1],0,sizeof(cimoffs)); // initial image offsets = 0 memset(&cimOffs[im2],0,sizeof(cimoffs)); ww = cimPXMf[im1]->ww; // image dimensions hh = cimPXMf[im1]->hh; nn = ww; // use larger of ww, hh if (hh > ww) nn = hh; cimScale = STP_initAlignSize / nn; // initial align image size if (cimScale > 1.0) cimScale = 1.0; cimBlend = 0; // no blend width (use all) cim_get_overlap(im1,im2,cimPXMf); // get overlap area cim_match_colors(im1,im2,cimPXMf); // get color matching factors cimSearchRange = STP_initSearchRange; // initial align search range cimSearchStep = STP_initSearchStep; // initial align search step cimWarpRange = STP_initWarpRange; // initial align corner warp range cimWarpStep = STP_initWarpStep; // initial align corner warp step cimSampSize = STP_sampSize; // pixel sample size for align/compare cimNsearch = 0; // reset align search counter while (true) // loop, increasing image size { cim_scale_image(im1,cimPXMs); // scale images to cimScale cim_scale_image(im2,cimPXMs); cim_adjust_colors(cimPXMs[im1],1); // apply color adjustments cim_adjust_colors(cimPXMs[im2],2); cim_warp_image(im1); // warp images for show cim_warp_image(im2); cim_get_overlap(im1,im2,cimPXMs); // get overlap area cim_get_redpix(im1); // get high-contrast pixels cimShowIm1 = im1; // show these two images cimShowIm2 = im2; // with 50/50 blend cimShowAll = 0; cim_show_images(1,0); // (x/y offsets can change) cim_align_image(im1,im2); // align im2 to im1 if (Fescape) goto finish; // user kill 19.0 zfree(cimRedpix); // clear red pixels cimRedpix = 0; if (cimScale == 1.0) break; // done R = STP_imageIncrease; // next larger image size cimScale = cimScale * R; if (cimScale > 0.85) { // if close to end, jump to end R = R / cimScale; cimScale = 1.0; } cimOffs[im1].xf *= R; // scale offsets for larger image cimOffs[im1].yf *= R; cimOffs[im2].xf *= R; cimOffs[im2].yf *= R; for (ii = 0; ii < 4; ii++) { cimOffs[im1].wx[ii] *= R; cimOffs[im1].wy[ii] *= R; cimOffs[im2].wx[ii] *= R; cimOffs[im2].wy[ii] *= R; } cimSearchRange = STP_searchRange; // align search range cimSearchStep = STP_searchStep; // align search step size cimWarpRange = STP_warpRange; // align corner warp range cimWarpStep = STP_warpStep; // align corner warp step size } cimOffs[im2].xf = cimOffs[im2].xf - cimOffs[im1].xf; // save im2 offsets from im1 cimOffs[im2].yf = cimOffs[im2].yf - cimOffs[im1].yf; cimOffs[im2].tf = cimOffs[im2].tf - cimOffs[im1].tf; } memset(&cimOffs[0],0,sizeof(cimoffs)); // image 0, no offsets finish: Ffuncbusy = 0; STP_stat = 1; thread_exit(); return 0; // not executed } // dialog to selectively paint output image int STP_image; // current image (0 based) int STP_radius; // paint mode radius int STP_mode; // 1/2 = show/hide float STP_show_adjust; // contrast adjustment float STP_hide_adjust; // contrast adjustment void STP_adjust() { zdialog *zd; char imageN[8] = "imageN", labN[4] = "0"; int imx; int STP_adjust_dialog_event(zdialog *zd, cchar *event); /*** _________________________________________ | Select and Paint Image | | | | image (o) 1 (o) 2 (o) 3 ... | | paint radius [___] | | | | transient objects | | show (o) =========[]================= | | hide (o) ==============[]============ | | | | [Done] [Cancel] | |_________________________________________| ***/ zd = zdialog_new(E2X("Select and Paint Image"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbim","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labim","hbim",Bimage,"space=5"); zdialog_add_widget(zd,"hbox","hbmr","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labr","hbmr",Bpaintradius,"space=5"); zdialog_add_widget(zd,"zspin","radius","hbmr","1|400|1|100"); zdialog_add_widget(zd,"hbox","hbtrob","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labtob","hbtrob",E2X("Transient Objects"),"space=5"); zdialog_add_widget(zd,"hbox","hbshow","dialog"); zdialog_add_widget(zd,"vbox","vbshow1","hbshow",0,"space=5"); zdialog_add_widget(zd,"vbox","vbshow2","hbshow",0,"expand"); zdialog_add_widget(zd,"radio","show","vbshow1",Bshow); zdialog_add_widget(zd,"radio","hide","vbshow1",Bhide); zdialog_add_widget(zd,"hscale","show-adjust","vbshow2","0.0|1.0|0.001|0.9","expand"); zdialog_add_widget(zd,"hscale","hide-adjust","vbshow2","0.0|1.0|0.001|0.9","expand"); for (imx = 0; imx < cimNF; imx++) { // add radio button for each image imageN[5] = '1' + imx; labN[0] = '1' + imx; zdialog_add_widget(zd,"radio",imageN,"hbim",labN); } zdialog_stuff(zd,"image1",1); // initial image = 1st STP_image = 0; // initial image STP_radius = 100; // paint radius zdialog_stuff(zd,"show",1); // initial mode, show zdialog_stuff(zd,"hide",0); STP_mode = 1; STP_show_adjust = 0.9; // contrast adjustments STP_hide_adjust = 0.9; takeMouse(STP_mousefunc,0); // connect mouse function start_thread(STP_adjust_thread,0); // start working thread signal_thread(); zdialog_resize(zd,250,0); zdialog_run(zd,STP_adjust_dialog_event,"save"); // run dialog, parallel zdialog_wait(zd); // wait for completion zdialog_free(zd); return; } // dialog event and completion callback function int STP_adjust_dialog_event(zdialog *zd, cchar *event) { int nn; if (zd->zstat) // dialog finish { freeMouse(); wrapup_thread(8); if (zd->zstat == 1) STP_stat = 1; else STP_stat = 0; if (STP_stat == 1) cim_trim(); // cut-off edges return 1; // 19.0 } if (strmatchN(event,"image",5)) { // image radio button nn = event[5] - '0'; // 1 to cimNF if (nn > 0 && nn <= cimNF) STP_image = nn - 1; // 0 to cimNF-1 } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(STP_mousefunc,0); // connect mouse function if (strmatch(event,"radius")) // change paint radius zdialog_fetch(zd,"radius",STP_radius); if (strmatch(event,"show-adjust")) { // contrast adjustment zdialog_fetch(zd,"show-adjust",STP_show_adjust); signal_thread(); } if (strmatch(event,"hide-adjust")) { // contrast adjustment zdialog_fetch(zd,"hide-adjust",STP_hide_adjust); signal_thread(); } if (zstrstr("show hide",event)) { // set show/hide mode zdialog_fetch(zd,"show",nn); if (nn) STP_mode = 1; zdialog_fetch(zd,"hide",nn); if (nn) STP_mode = 2; signal_thread(); } return 1; } // STP dialog mouse function void STP_mousefunc() { int radius, rect, radius2; int mx, my, dx, dy, px3, py3; radius = STP_radius; // paintbrush radius radius2 = radius * radius; rect = 2 * radius; // draw mouse selection circle draw_mousecircle(Mxposn,Myposn,radius,0,0); if (LMclick || RMclick) // mouse click return; // handle zoom normally if (! Mxdrag && ! Mydrag) return; mx = Mxdrag; // mouse drag my = Mydrag; Mxdrag = Mydrag = 0; if (mx < 0 || mx > E3pxm->ww-1 || my < 0 || my > E3pxm->hh-1) // mouse outside image area return; for (dy = -radius; dy <= radius; dy++) // loop pixels around mouse for (dx = -radius; dx <= radius; dx++) { if (dx*dx + dy*dy > radius2) continue; // outside radius px3 = mx + dx; // output pixel py3 = my + dy; if (px3 < 0 || px3 > E3pxm->ww-1) continue; // outside image if (py3 < 0 || py3 > E3pxm->hh-1) continue; if (Mbutton == 1) // left button STP_setpixel(px3,py3,STP_image); // paint chosen input image if (Mbutton == 3) // right button, set background STP_setpixel(px3,py3,-3); } Fpaint3(mx-radius,my-radius,rect,rect,0); // update window draw_mousecircle(mx,my,radius,0,0); // draw mouse selection circle return; } // show all transient objects on top of background void * STP_adjust_thread(void *) { void * STP_adjust_wthread(void *); STP_setpixel(0,0,-1); // initialization while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 do_wthreads(STP_adjust_wthread,NWT); // worker threads paintlock(0); // unblock window paint 19.0 Fpaint2(); } return 0; } void * STP_adjust_wthread(void *arg) // worker thread { int index = *((int *) arg); int px3, py3; for (py3 = index+1; py3 < E3pxm->hh-1; py3 += NWT) // step through output pixels for (px3 = 1; px3 < E3pxm->ww-1; px3++) { if (STP_mode == 0) STP_setpixel(px3,py3,-2); // blend, all images equal if (STP_mode == 1) STP_setpixel(px3,py3,-4); // show, foreground + background if (STP_mode == 2) STP_setpixel(px3,py3,-3); // hide, background only } pthread_exit(0); } // Set E3 image pixel for given pixel location and input source // source: -1 initialize: pre-calculate trig functions // -2 mix of all input images (blend) // -3 background only (median pixels) // -4 foreground (outlier pixels) + background // 0 - cimNF-1 specific input image (paint) void STP_setpixel(int px3, int py3, int source) { int imx, vstat; int ii, ns, ns1, ns2; int Rlist[10], Glist[10], Blist[10]; float px, py, xoff, yoff; float *pix3, pix1[4], vpix[4]; float R, G, B, R2, G2, B2; float match, worstmatch; // sorted colors [N] --> low-hi range for central group // for 9 colors [xxxxxxxxx], the central group is ---xxx--- or range 3-5 from 0-8 // 0 1 2 3 4 5 6 7 8 9 10 int nsx[11][2] = { {0,0}, {0,0}, {0,0}, {1,1}, {1,2}, {2,2}, {2,3}, {3,3}, {3,4}, {4,4}, {4,5} }; static float sintf[10], costf[10]; pix3 = PXMpix(E3pxm,px3,py3); // output pixel if (source >= 0 && source < cimNF) // specific input image { imx = source; xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); // input virtual pixel py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat = vpixel(cimPXMw[imx],px,py,vpix); if (vstat) memcpy(pix3,vpix,pixcc); return; } if (source == -1) { // pre-calculate trig funcs for (imx = 0; imx < cimNF; imx++) { sintf[imx] = sinf(cimOffs[imx].tf); costf[imx] = cosf(cimOffs[imx].tf); } return; } if (source == -2) // blend all input images { R = G = B = 0; ns = 0; for (imx = 0; imx < cimNF; imx++) { xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat = vpixel(cimPXMw[imx],px,py,vpix); if (vstat) { R += vpix[0]; G += vpix[1]; B += vpix[2]; ns++; } } pix3[0] = R / ns; pix3[1] = G / ns; pix3[2] = B / ns; return; } if (source == -3) // background only { if (cimNF < 3) return; for (imx = ns = 0; imx < cimNF; imx++) // get aligned input pixels { xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat = vpixel(cimPXMw[imx],px,py,vpix); if (vstat) { Rlist[ns] = vpix[0]; // add pixel RGB values to list Glist[ns] = vpix[1]; Blist[ns] = vpix[2]; ns++; } } if (ns < 3) return; HeapSort(Rlist,ns); // sort RGB values HeapSort(Glist,ns); HeapSort(Blist,ns); R = G = B = 0; ns1 = nsx[ns][0]; // middle group of pixels ns2 = nsx[ns][1]; for (ii = ns1; ii <= ns2; ii++) { R += Rlist[ii]; G += Glist[ii]; B += Blist[ii]; } ns = ns2 - ns1 + 1; // sample count R = R / ns; // middle group average G = G / ns; B = B / ns; R2 = G2 = B2 = 0; ns2 = 0; for (ii = 0; ii < ns; ii++) // get pixels matching middle group { match = RGBMATCH(R,G,B,Rlist[ii],Glist[ii],Blist[ii]); if (match > STP_hide_adjust) { // match threshold R2 += Rlist[ii]; G2 += Glist[ii]; B2 += Blist[ii]; ns2++; } } pix3[0] = (R * ns + R2) / (ns + ns2); // mean background pixel pix3[1] = (G * ns + G2) / (ns + ns2); pix3[2] = (B * ns + B2) / (ns + ns2); return; } if (source == -4) // show foreground pixel { STP_setpixel(px3,py3,-3); // set pix3 to background worstmatch = 1.0; for (imx = 0; imx < cimNF; imx++) // loop aligned input pixels { xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat = vpixel(cimPXMw[imx],px,py,vpix); if (! vstat) continue; match = PIXMATCH(vpix,pix3); // find worst match to background if (match < worstmatch) { worstmatch = match; memcpy(pix1,vpix,pixcc); } } if (worstmatch < STP_show_adjust) memcpy(pix3,pix1,pixcc); return; } } /******************************************************************************** Stack/Noise function Combine multiple photos of the same subject and average the pixels for noise reduction. ********************************************************************************/ float STN_initAlignSize = 160; // initial align image size float STN_imageIncrease = 1.6; // image size increase per align cycle float STN_sampSize = 10000; // pixel sample size float STN_initSearchRange = 5.0; // initial search range, +/- pixels float STN_initSearchStep = 1.0; // initial search step, pixels float STN_searchRange = 2.0; // normal search range float STN_searchStep = 1.0; // normal search step float STN_initWarpRange = 2.0; // initial corner warp range float STN_initWarpStep = 1.0; // initial corner warp step float STN_warpRange = 1.0; // normal corner warp range float STN_warpStep = 0.67; // normal corner warp step int STN_stat; // 1 = OK, 0 = failed or canceled int STN_average = 1, STN_median = 0; // use average/median of input pixels int STN_exlow = 0, STN_exhigh = 0; // exclude low/high pixel void * STN_align_thread(void *); void STN_adjust(); void * STN_adjust_thread(void *); editfunc EFstn; // edit function data // menu function void m_stack_noise(GtkWidget *, cchar *) { int imx, err, ww, hh; float diffw, diffh; F1_help_topic = "stack/noise"; // help topic if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (checkpend("all")) return; cim_manualwarp = 0; cimNF = 0; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } gallery_select_clear(); // clear selected file list gallery_select(); // get new list if (GScount == 0) goto cleanup; if (GScount < 2 || GScount > 9) { zmessageACK(Mwin,E2X("Select 2 to 9 files")); goto cleanup; } cimNF = GScount; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(GSfiles[imx]); // set up file list if (! cim_load_files()) goto cleanup; // load and check all files ww = cimPXMf[0]->ww; hh = cimPXMf[0]->hh; for (imx = 1; imx < cimNF; imx++) // check image compatibility { diffw = abs(ww - cimPXMf[imx]->ww); diffw = diffw / ww; diffh = abs(hh - cimPXMf[imx]->hh); diffh = diffh / hh; if (diffw > 0.02 || diffh > 0.02) { zmessageACK(Mwin,E2X("Images are not all the same size")); goto cleanup; } } free_resources(); // ready to commit err = f_open(cimFile[cimLF]); // curr_file = alphabetically last if (err) goto cleanup; EFstn.menufunc = m_stack_noise; EFstn.funcname = "stack_noise"; if (! edit_setup(EFstn)) goto cleanup; // setup edit (will lock) start_thread(STN_align_thread,0); // align each pair of images wrapup_thread(0); // wait for completion if (STN_stat != 1) goto cancel; if (Fescape) { // user cancel 19.0 Fescape = 0; zmessage_post_bold(Mwin,"parent",3,Bcancel); goto cancel; } STN_adjust(); // combine images based on user inputs if (STN_stat != 1) goto cancel; CEF->Fmods++; // done CEF->Fsaved = 0; edit_done(0); goto cleanup; cancel: edit_cancel(0); cleanup: for (imx = 0; imx < cimNF; imx++) { // free cim file and PXM data if (cimFile[imx]) zfree(cimFile[imx]); if (cimPXMf[imx]) PXM_free(cimPXMf[imx]); if (cimPXMs[imx]) PXM_free(cimPXMs[imx]); if (cimPXMw[imx]) PXM_free(cimPXMw[imx]); } *paneltext = 0; return; } // align each image 2nd-last to 1st image // cimPXMf[*] original image // cimPXMs[*] scaled and color adjusted for pixel comparisons // cimPXMw[*] warped for display void * STN_align_thread(void *) { int imx, im1, im2, ww, hh, ii, nn; float R; 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 if (Fescape) goto finish; // user kill 19.0 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)); finish: Ffuncbusy = 0; STN_stat = 1; thread_exit(); return 0; // not executed } // change pixel combination according to user input void STN_adjust() { zdialog *zd; int STN_adjust_dialog_event(zdialog *zd, cchar *event); /*** _________________________________________ | Adjust Pixel Composition | | | | (o) use average (o) use median | | [x] omit low pixel [x] omit high pixel | | | | [Done] [Cancel] | |_________________________________________| ***/ zd = zdialog_new(E2X("Adjust Pixel Composition"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"radio","average","hb1",E2X("use average"),"space=3"); zdialog_add_widget(zd,"radio","median","hb1",E2X("use median"),"space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=3"); zdialog_add_widget(zd,"check","exlow","hb2",E2X("omit low pixel"),"space=3"); zdialog_add_widget(zd,"check","exhigh","hb2",E2X("omit high pixel"),"space=3"); zdialog_stuff(zd,"average",1); // default = average zdialog_stuff(zd,"median",0); zdialog_stuff(zd,"exlow",0); zdialog_stuff(zd,"exhigh",0); STN_average = 1; STN_median = 0; STN_exlow = 0; STN_exhigh = 0; start_thread(STN_adjust_thread,0); // start working thread signal_thread(); zdialog_resize(zd,250,0); zdialog_run(zd,STN_adjust_dialog_event,"save"); // run dialog, parallel zdialog_wait(zd); // wait for completion zdialog_free(zd); return; } // dialog event and completion callback function int STN_adjust_dialog_event(zdialog *zd, cchar *event) { if (zd->zstat) { // dialog finish if (zd->zstat == 1) STN_stat = 1; else STN_stat = 0; wrapup_thread(8); if (STN_stat == 1) cim_trim(); // trim edges return 1; // 19.0 } if (strmatch(event,"average")) { zdialog_fetch(zd,"average",STN_average); signal_thread(); } if (strmatch(event,"median")) { zdialog_fetch(zd,"median",STN_median); signal_thread(); } if (strmatch(event,"exlow")) { zdialog_fetch(zd,"exlow",STN_exlow); signal_thread(); } if (strmatch(event,"exhigh")) { zdialog_fetch(zd,"exhigh",STN_exhigh); signal_thread(); } return 1; } // compute mean/median mix for each output pixel and update E3 image void * STN_adjust_thread(void *) { void * STN_adjust_wthread(void *arg); // worker thread while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 do_wthreads(STN_adjust_wthread,NWT); // worker threads paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed } // worker thread void * STN_adjust_wthread(void *arg) { int index = *((int *) arg); int imx, vstat, px3, py3; float red, green, blue; // float int ii, ns, ns1, ns2; int Rlist[10], Glist[10], Blist[10]; float px, py; float xoff, yoff, sintf[10], costf[10]; float *pix3, vpix[4]; // sorted colors [N] --> low-hi range for median group // 0 1 2 3 4 5 6 7 8 9 10 int nsx[11][2] = { {0,0}, {0,0}, {0,1}, {1,1}, {1,2}, {2,2}, {2,3}, {3,3}, {3,4}, {4,4}, {4,5} }; for (imx = 0; imx < cimNF; imx++) // pre-calculate trig funcs { sintf[imx] = sinf(cimOffs[imx].tf); costf[imx] = cosf(cimOffs[imx].tf); } for (py3 = index+1; py3 < E3pxm->hh-1; py3 += NWT) // step through output pixels for (px3 = 1; px3 < E3pxm->ww-1; px3++) { for (imx = ns = 0; imx < cimNF; imx++) // get aligned input pixels { xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat = vpixel(cimPXMw[imx],px,py,vpix); if (vstat) { Rlist[ns] = vpix[0]; // add pixel RGB values to list Glist[ns] = vpix[1]; Blist[ns] = vpix[2]; ns++; } } if (! ns) continue; if (STN_exlow || STN_exhigh || STN_median) { // RGB values must be sorted HeapSort(Rlist,ns); HeapSort(Glist,ns); HeapSort(Blist,ns); } red = green = blue = 0; if (STN_average) // average the input pixels { ns1 = 0; // low and high RGB values ns2 = ns - 1; if (STN_exlow) { // exclude low ns1++; if (ns1 > ns2) ns1--; } if (STN_exhigh) { // exclude high ns2--; if (ns1 > ns2) ns2++; } for (ii = ns1; ii <= ns2; ii++) // sum remaining RGB levels { red += Rlist[ii]; green += Glist[ii]; blue += Blist[ii]; } ns = ns2 - ns1 + 1; // sample count red = red / ns; // output RGB = average green = green / ns; blue = blue / ns; } if (STN_median) // use median input pixels { ns1 = nsx[ns][0]; // middle group of pixels ns2 = nsx[ns][1]; for (ii = ns1; ii <= ns2; ii++) { red += Rlist[ii]; green += Glist[ii]; blue += Blist[ii]; } ns = ns2 - ns1 + 1; // sample count red = red / ns; // output RGB = average green = green / ns; blue = blue / ns; } pix3 = PXMpix(E3pxm,px3,py3); // output pixel pix3[0] = red; pix3[1] = green; pix3[2] = blue; } pthread_exit(0); } /******************************************************************************** Stack/Layer function Stack images of exactly the same size with no automatic alignment. Choose which input image to use for each output area by mouse painting. ********************************************************************************/ void STL_paint(); void STL_mousefunc(); void STL_setpixel(int px, int py, float opacity); int STL_mode; int STL_image; // current image (0 based) int STL_radius; // mouse paint radius int STL_center, STL_edge; // mouse center and edge opacity int STL_stat; editfunc EFstl; // edit function data // menu function void m_stack_layer(GtkWidget *, cchar *) // 19.0 { int imx, err, ww, hh; float diffw, diffh; F1_help_topic = "stack/layer"; // help topic if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (checkpend("all")) return; cim_manualwarp = 0; cimNF = 0; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = 0; } gallery_select_clear(); // clear selected file list gallery_select(); // get new list if (GScount == 0) goto cleanup; if (GScount < 2 || GScount > 9) { zmessageACK(Mwin,E2X("Select 2 to 9 files")); goto cleanup; } cimNF = GScount; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(GSfiles[imx]); // set up file list if (! cim_load_files()) goto cleanup; // load and check all files ww = cimPXMf[0]->ww; hh = cimPXMf[0]->hh; for (imx = 1; imx < cimNF; imx++) // check image compatibility { diffw = abs(ww - cimPXMf[imx]->ww); diffw = diffw / ww; diffh = abs(hh - cimPXMf[imx]->hh); diffh = diffh / hh; if (diffw > 0 || diffh > 0) { zmessageACK(Mwin,E2X("Images must be exactly the same size")); goto cleanup; } } free_resources(); // ready to commit err = f_open(cimFile[cimLF]); // curr_file = alphabetically last if (err) goto cleanup; EFstl.menufunc = m_stack_layer; EFstl.funcname = "stack_layer"; EFstl.mousefunc = STL_mousefunc; if (! edit_setup(EFstl)) goto cleanup; // setup edit (will lock) STL_paint(); // combine images based on user inputs if (STL_stat != 1) goto cancel; CEF->Fmods++; // done CEF->Fsaved = 0; edit_done(0); goto cleanup; cancel: edit_cancel(0); cleanup: for (imx = 0; imx < cimNF; imx++) { // free cim file and PXM data if (cimFile[imx]) zfree(cimFile[imx]); if (cimPXMf[imx]) PXM_free(cimPXMf[imx]); } return; } // dialog to paint output image areas with selected input image void STL_paint() { zdialog *zd; char imageN[8] = "imageN", labN[4] = "0"; int imx, px, py; int STL_paint_dialog_event(zdialog *zd, cchar *event); /*** ______________________________________ | Select and Paint Image | | | | Image (o) 1 (o) 2 (o) 3 ... | | | | [ fill ] using selected image | | Paint Radius [___] | | opacity center [___] edge [___] | | | | [Done] [Cancel] | |______________________________________| ***/ zd = zdialog_new(E2X("Select and Paint Image"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbim","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labim","hbim",Bimage,"space=5"); for (imx = 0; imx < cimNF; imx++) { // radio button for each image imageN[5] = '1' + imx; labN[0] = '1' + imx; zdialog_add_widget(zd,"radio",imageN,"hbim",labN); } zdialog_add_widget(zd,"hbox","hbfill","dialog",0,"space=3"); zdialog_add_widget(zd,"button","fill","hbfill",E2X(" Fill "),"space=3"); zdialog_add_widget(zd,"label","labuse","hbfill",E2X("using selected image"),"space=3"); zdialog_add_widget(zd,"hbox","hbradius","dialog"); zdialog_add_widget(zd,"label","labradius","hbradius",Bpaintradius,"space=3"); zdialog_add_widget(zd,"zspin","radius","hbradius","1|500|1|100"); zdialog_add_widget(zd,"hbox","hbopc","dialog"); zdialog_add_widget(zd,"label","labcen","hbopc",Bopacitycenter,"space=3"); zdialog_add_widget(zd,"zspin","center","hbopc","0|100|1|20","space=3|size=3"); zdialog_add_widget(zd,"label","space","hbopc",0,"space=10"); zdialog_add_widget(zd,"label","labedge","hbopc",Bedge,"space=3"); zdialog_add_widget(zd,"zspin","edge","hbopc","0|100|1|0","space=3|size=3"); zdialog_stuff(zd,"image1",1); // initial image = 1st STL_image = 0; // initial image STL_mode = 1; // initially blend all input images STL_radius = 100; // paint radius STL_center = 20; STL_edge = 0; for (py = 0; py < E3pxm->hh; py++) // initialize output image for (px = 0; px < E3pxm->ww; px++) STL_setpixel(px,py,1.0); Fpaint2(); takeMouse(STL_mousefunc,0); // connect mouse function zdialog_resize(zd,250,0); zdialog_run(zd,STL_paint_dialog_event,"save"); // run dialog, parallel zdialog_wait(zd); // wait for completion zdialog_free(zd); return; } // dialog event and completion callback function int STL_paint_dialog_event(zdialog *zd, cchar *event) { int nn, px, py; if (zd->zstat) // dialog finish { freeMouse(); if (zd->zstat == 1) STL_stat = 1; // user OK or cancel else STL_stat = 0; return 1; // 19.0 } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(STL_mousefunc,0); // connect mouse function if (strmatchN(event,"image",5)) { // image radio button nn = event[5] - '0'; // 1 to cimNF STL_image = nn - 1; // 0 to cimNF-1 } if (strmatch(event,"radius")) // mouse paint radius zdialog_fetch(zd,"radius",STL_radius); if (strmatch(event,"center")) // center opacity zdialog_fetch(zd,"center",STL_center); if (strmatch(event,"edge")) // edge opactiy zdialog_fetch(zd,"edge",STL_edge); if (strmatch(event,"fill")) // fill all with selected image { STL_mode = 2; for (py = 0; py < E3pxm->hh; py++) for (px = 0; px < E3pxm->ww; px++) STL_setpixel(px,py,1.0); Fpaint2(); } return 1; } // STL dialog mouse function void STL_mousefunc() { int rect; int mx, my, dx, dy, px, py; float radius, pixrad; float opacity, maxopacity, slope; if (LMclick || RMclick) return; // mouse click, handle zoom normally radius = STL_radius; // paintbrush radius draw_mousecircle(Mxposn,Myposn,radius,0,0); if (! Mxdrag && ! Mydrag) return; // motion event maxopacity = 1.0 - radius / 550.0; // 1.0 - 0.09 for radius 0 - 500 maxopacity = 0.1; slope = 0.01 * (STL_edge - STL_center) / radius; mx = Mxdrag; // mouse drag my = Mydrag; Mxdrag = Mydrag = 0; STL_mode = 2; for (dy = -radius; dy <= radius; dy++) // loop pixels around mouse for (dx = -radius; dx <= radius; dx++) { pixrad = sqrtf(dx*dx + dy*dy); if (pixrad > radius) continue; // outside radius px = mx + dx; // output pixel py = my + dy; if (px < 0 || px > E3pxm->ww-1) continue; // outside image if (py < 0 || py > E3pxm->hh-1) continue; opacity = maxopacity * (0.01 * STL_center + slope * pixrad); // reduce opacity for large radius STL_setpixel(px, py, opacity); // paint chosen input image } rect = 2 * radius; // draw mouse selection circle Fpaint3(mx-radius,my-radius,rect,rect,0); // update window draw_mousecircle(mx,my,radius,0,0); // draw mouse selection circle return; } // Set E3 image pixel for given pixel location and input source // // STL_mode: 1 = mix of all input images (blend) // 2 = specific input image (paint) // STL_image: 0 - cimNF-1 = specific input image void STL_setpixel(int px, int py, float opacity) { int imx; float *pix3, *pix1; float R, G, B; pix3 = PXMpix(E3pxm,px,py); // output pixel if (STL_mode == 1) // blend all input images { R = G = B = 0; for (imx = 0; imx < cimNF; imx++) { pix1 = PXMpix(cimPXMf[imx],px,py); R += pix1[0]; G += pix1[1]; B += pix1[2]; } pix3[0] = R / cimNF; pix3[1] = G / cimNF; pix3[2] = B / cimNF; } if (STL_mode == 2) // specific input image { imx = STL_image; pix1 = PXMpix(cimPXMf[imx],px,py); pix3[0] = pix1[0] * opacity + pix3[0] * (1.0 - opacity); pix3[1] = pix1[1] * opacity + pix3[1] * (1.0 - opacity); pix3[2] = pix1[2] * opacity + pix3[2] * (1.0 - opacity); } return; } /******************************************************************************** Stack/slider function Stack images of exactly the same size with no automatic alignment. The two images are split left and right by a movable vertical boundary. ********************************************************************************/ void STS_start(); void STS_mousefunc(); void STS_slider(); int STS_stat; int Eww, Ehh, P, PP; int Finit; editfunc EFsts; // edit function data // menu function void m_stack_slider(GtkWidget *, cchar *) // 20.0 { int imx, err; int diffw, diffh; F1_help_topic = "stack/slider"; // help topic if (FGWM != 'F' && FGWM != 'G') return; if (checkpend("all")) return; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = 0; } gallery_select_clear(); // clear selected file list gallery_select(); // get new list if (GScount == 0) goto cleanup; if (GScount != 2) { zmessageACK(Mwin,E2X("Select 2 files")); goto cleanup; } cimNF = GScount; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(GSfiles[imx]); // set up file list if (! cim_load_files()) goto cleanup; // load and check all files Eww = cimPXMf[0]->ww; Ehh = cimPXMf[0]->hh; for (imx = 1; imx < cimNF; imx++) // check image compatibility { diffw = Eww - cimPXMf[imx]->ww; diffh = Ehh - cimPXMf[imx]->hh; if (diffw != 0 || diffh != 0) { zmessageACK(Mwin,E2X("Images must be exactly the same size")); goto cleanup; } } free_resources(); // ready to commit err = f_open(cimFile[0]); // curr_file = 1st chosen if (err) goto cleanup; EFsts.menufunc = m_stack_slider; EFsts.funcname = "stack_slider"; EFsts.mousefunc = STS_mousefunc; if (! edit_setup(EFsts)) goto cleanup; // setup edit (will lock) PXM_addalpha(E3pxm); STS_start(); // combine images based on mouse position if (STS_stat != 1) goto cancel; CEF->Fmods++; // done CEF->Fsaved = 0; edit_done(0); goto cleanup; cancel: edit_cancel(0); cleanup: for (imx = 0; imx < cimNF; imx++) { // free cim file and PXM data if (cimFile[imx]) zfree(cimFile[imx]); if (cimPXMf[imx]) PXM_free(cimPXMf[imx]); } return; } // dialog to paint output image areas with selected input image void STS_start() { int STS_start_dialog_event(zdialog *zd, cchar *event); zdialog *zd; /*** _____________________________ | Split two Images | | | | drag image boundary | | | | [Done] [Cancel] | |_____________________________| ***/ zd = zdialog_new(E2X("Split two Images"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"label","labtip","dialog",E2X("drag image boundary")); P = PP = Eww / 2; // initial 50/50 split Finit = 1; STS_slider(); takeMouse(STS_mousefunc,0); // connect mouse function zdialog_run(zd,STS_start_dialog_event,"save"); // run dialog, parallel zdialog_wait(zd); // wait for completion zdialog_free(zd); return; } // dialog event and completion callback function int STS_start_dialog_event(zdialog *zd, cchar *event) { if (zd->zstat) // dialog finish { freeMouse(); if (zd->zstat == 1) STS_stat = 1; // user OK or cancel else STS_stat = 0; return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(STS_mousefunc,0); // connect mouse function return 1; } // STS dialog mouse function void STS_mousefunc() { if (! Mxdrag && ! Mydrag) return; // wait for drag event P = Mxdrag; // mouse drag x position, 0...Eww-1 STS_slider(); Mxdrag = Mydrag = 0; return; } // output revised E3 image // use input image 0 for pixels left of P and image 1 for pixels right of P void STS_slider() { float *pix3, *pix1; int px, py, rx, cc; int pcc = E3pxm->nc * sizeof(float); float red[4] = { 255, 0, 0, 255 }; int Rww; // 1/2 red line width static int PRww; // prior Rww Rww = 3 / Mscale; if (Rww < 1) Rww = 1; if (Finit) // initial 50/50 image { Finit = 0; PRww = Rww; for (py = 0; py < Ehh; py++) { px = 0; // left image cc = P-Rww; if (cc > 0) { pix1 = PXMpix(cimPXMf[0],px,py); pix3 = PXMpix(E3pxm,px,py); memcpy(pix3,pix1, cc * pcc); } for (px = P-Rww; px <= P+Rww; px++) // red line { if (px >= 0 && px < Eww) { pix3 = PXMpix(E3pxm,px,py); memcpy(pix3,red,pcc); } } px = P+Rww; // right image cc = Eww - px - 1; if (cc > 0) { pix1 = PXMpix(cimPXMf[1],px,py); pix3 = PXMpix(E3pxm,px,py); memcpy(pix3,pix1, cc * pcc); } } Fpaint2(); } if (P == PP) return; // no movement px = cc = 0; for (py = 0; py < Ehh; py++) { if (P > PP) // movement to right { px = PP - 2 * PRww; cc = P - PP + 4 * PRww; if (px < 0) px = 0; if (px + cc > Eww-1) cc = Eww-1 - px; pix1 = PXMpix(cimPXMf[0],px,py); // expose left image pix3 = PXMpix(E3pxm,px,py); memcpy(pix3,pix1, cc * pcc); } if (P < PP) // movement to left { px = P - 2 * PRww; cc = PP - P + 4 * PRww; if (px < 0) px = 0; if (px + cc > Eww-1) cc = Eww-1 - px; pix1 = PXMpix(cimPXMf[1],px,py); // expose right image pix3 = PXMpix(E3pxm,px,py); memcpy(pix3,pix1, cc * pcc); } for (rx = P - Rww; rx <= P + Rww; rx++) // red line { if (rx >= 0 && rx < Eww) { pix3 = PXMpix(E3pxm,rx,py); memcpy(pix3,red,pcc); } } } PP = P; // save prior position PRww = Rww; // save prior 1/2 red line width if (px < 0) px = 0; if (px + cc > Eww-1) cc = Eww-1 - px; Fpaint3(px,0,cc,Ehh,0); // paint new exposed area return; } /********************************************************************************/ // show the differences between two images namespace imagediffs_names { char *imagefile1, *imagefile2; PXB *pxb1, *pxb2, *pxbdiffs; int xalign, yalign; } // menu function void m_image_diffs(GtkWidget *, const char *menu) { using namespace imagediffs_names; int imagediffs_dialog_event(zdialog* zd, const char *event); F1_help_topic = "image diffs"; if (FGWM != 'F' && FGWM != 'G') return; // 19.0 imagefile1 = imagefile2 = 0; pxb1 = pxb2 = pxbdiffs = 0; /*** ________________________________ | Image Differences | | | | Select 2 files [select] | | (o) imagename1.jpg | | (o) imagename2.jpg | | (o) differences | | | | X-align [___] Y-align [___] | | | | [done] [cancel] | |________________________________| ***/ zdialog *zd = zdialog_new(E2X("Image Differences"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbsel","dialog"); zdialog_add_widget(zd,"label","labsel","hbsel",E2X("Select 2 files"),"space=3"); zdialog_add_widget(zd,"button","select","hbsel",Bselect,"space=3"); zdialog_add_widget(zd,"hbox","hbshow","dialog"); zdialog_add_widget(zd,"vbox","vbshow1","hbshow",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vbshow2","hbshow",0,"space=5|homog"); zdialog_add_widget(zd,"hbox","hbshow21","vbshow2"); zdialog_add_widget(zd,"hbox","hbshow22","vbshow2"); zdialog_add_widget(zd,"hbox","hbshow23","vbshow2"); zdialog_add_widget(zd,"radio","image1","vbshow1",0); zdialog_add_widget(zd,"radio","image2","vbshow1",0); zdialog_add_widget(zd,"radio","diffs","vbshow1",0); zdialog_add_widget(zd,"label","labimage1","hbshow21",Bnoselection); zdialog_add_widget(zd,"label","labimage2","hbshow22",Bnoselection); zdialog_add_widget(zd,"label","labdiffs","hbshow23",E2X("differences")); zdialog_add_widget(zd,"hbox","hbalign","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labxalign","hbalign",E2X("X-align"),"space=3"); zdialog_add_widget(zd,"zspin","xalign","hbalign","-999|999|1|0","space=3"); zdialog_add_widget(zd,"label","space","hbalign",0,"space=5"); zdialog_add_widget(zd,"label","labyalign","hbalign",E2X("Y-align"),"space=3"); zdialog_add_widget(zd,"zspin","yalign","hbalign","-999|999|1|0","space=3"); zdialog_run(zd,imagediffs_dialog_event,"save"); // run dialog - parallel return; } // imagediffs dialog event and completion function int imagediffs_dialog_event(zdialog *zd, const char *event) { using namespace imagediffs_names; void imagediffs_thread(); int err; char *pp; GError *gerror = 0; char *outfile; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) // done or cancel { Fpxb = 0; if (zd->zstat == 1) { // done if (pxbdiffs) { pp = file_new_version(imagefile1); outfile = zgetfile(Bsave,MWIN,"save",pp,0); // save differences to PNG file zfree(pp); if (outfile) { pp = strrchr(outfile,'.'); if (pp && strlen(pp) >= 4) strcpy(pp,".jpg"); gdk_pixbuf_save(pxbdiffs->pixbuf,outfile,"jpeg",&gerror,null); if (gerror) zmessageACK(Mwin,gerror->message); else { f_open(outfile,0,0,1,0); // open diffs file update_image_index(outfile); } zfree(outfile); } } } else { // cancel or [x] 20.0 if (imagefile1) f_open(imagefile1); // fall back to previous file else if (curr_file) f_open(curr_file); } if (imagefile1) zfree(imagefile1); // free resources if (imagefile2) zfree(imagefile2); if (pxb1) PXB_free(pxb1); if (pxb2) PXB_free(pxb2); if (pxbdiffs) PXB_free(pxbdiffs); zdialog_free(zd); return 1; } if (strmatch(event,"select")) // select 2 input files { gallery_select_clear(); err = gallery_select(); // choose thumbnails if (err) return 1; if (GScount != 2) { zmessageACK(Mwin,E2X("select exactly 2 files")); return 1; } if (imagefile1) zfree(imagefile1); // get chosen files imagefile1 = zstrdup(GSfiles[0]); if (imagefile2) zfree(imagefile2); imagefile2 = zstrdup(GSfiles[1]); f_open(imagefile1); // open the first file 20.0 pp = strrchr(imagefile1,'/'); // stuff dialog with chosen file names if (! pp) pp = imagefile1; else pp++; zdialog_stuff(zd,"labimage1",pp); pp = strrchr(imagefile2,'/'); if (! pp) pp = imagefile2; else pp++; zdialog_stuff(zd,"labimage2",pp); if (pxb1) PXB_free(pxb1); // free prior PXBs if (pxb2) PXB_free(pxb2); if (pxbdiffs) PXB_free(pxbdiffs); pxb1 = pxb2 = pxbdiffs = 0; pxb1 = PXB_load(imagefile1,1); // make PXB images from files if (! pxb1) return 1; pxb2 = PXB_load(imagefile2,1); if (! pxb2) return 1; Fpxb = pxb1; // show image1 m_viewmode(0,"F"); Fpaint2(); xalign = yalign = 0; // reset alignment return 1; } if (strmatch(event,"image1")) { if (pxb1) Fpxb = pxb1; Fpaint2(); } if (strmatch(event,"image2")) { if (pxb2) Fpxb = pxb2; Fpaint2(); } if (strmatch(event,"diffs")) { imagediffs_thread(); if (pxbdiffs) Fpxb = pxbdiffs; Fpaint2(); } if (strmatch(event,"xalign")) { zdialog_fetch(zd,"xalign",xalign); imagediffs_thread(); zdialog_stuff(zd,"diffs",1); } if (strmatch(event,"yalign")) { zdialog_fetch(zd,"yalign",yalign); imagediffs_thread(); zdialog_stuff(zd,"diffs",1); } return 1; } // thread function - multiple working threads to update image void imagediffs_thread() { using namespace imagediffs_names; void * imagediffs_wthread(void *arg); // worker thread if (! pxb1 || ! pxb2) return; while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 if (pxbdiffs) PXB_free(pxbdiffs); pxbdiffs = PXB_make(pxb1->ww,pxb1->hh,3); do_wthreads(imagediffs_wthread,NWT); // worker threads Fpxb = pxbdiffs; paintlock(0); // unblock window paint 19.0 Fpaint2(); return; } void * imagediffs_wthread(void *arg) // worker thread function { using namespace imagediffs_names; int index = *((int *) arg); int px1, py1, px2, py2; int xlo, xhi, ylo, yhi; uint8 *pix1, *pix2, *pix3; uint8 red, green, blue; if (xalign > 0) { xlo = xalign; xhi = pxb1->ww; } else { xlo = 0; xhi = pxb1->ww + xalign; } if (yalign > 0) { ylo = yalign; yhi = pxb1->hh; } else { ylo = 0; yhi = pxb1->hh + yalign; } if (xhi > pxb2->ww) xhi = pxb2->ww; if (yhi > pxb2->hh) yhi = pxb2->hh; for (py1 = ylo + index; py1 < yhi; py1 += NWT) for (px1 = xlo; px1 < xhi; px1++) { px2 = px1 - xalign; py2 = py1 - yalign; pix1 = pxb1->pixels + py1 * pxb1->rs + px1 * 3; pix2 = pxb2->pixels + py2 * pxb2->rs + px2 * 3; pix3 = pxbdiffs->pixels + py1 * pxbdiffs->rs + px1 * 3; red = pix1[0] - pix2[0]; green = pix1[1] - pix2[1]; blue = pix1[2] - pix2[2]; if (red < 0) red = -red; if (green < 0) green = -green; if (blue < 0) blue = -blue; pix3[0] = red; pix3[1] = green; pix3[2] = blue; } pthread_exit(0); // exit thread } /******************************************************************************** Panorama function: join 2, 3, or 4 images horizontally *********************************************************************************/ int panStat; // 1 = OK zdialog *panozd = 0; // pre-align dialog float panPreAlignSize = 1000; // pre-align image size (ww) float panInitAlignSize = 200; // initial align image size float panImageIncrease = 1.6; // image size increase per align cycle float panSampSize = 50000; // pixel sample size float panPreAlignBlend = 0.20; // pre-align blend width * ww float panInitBlend = 0.20; // initial auto-align blend width float panFinalBlend = 0.10; // final blend width * ww float panBlendDecrease = 0.7; // BW reduction per align cycle float panInitSearchRange = 15.0; // initial search range, +/- pixels float panInitSearchStep = 1.0; // initial search step, pixels float panSearchRange = 4.0; // normal search range, +/- pixels float panSearchStep = 1.0; // normal search step, pixels float panInitWarpRange = 15.0; // initial corner warp range, +/- pixels float panInitWarpStep = 1.0; // initial corner warp step, pixels float panWarpRange = 8.0; // normal corner warp range, +/- pixels float panWarpStep = 1.0; // normal corner warp step, pixels float pano_adjust_RGB[4][3]; // user color adjustments per image int pano_mousewarp = 0; // use mouse to warp/align images int pano_currimage = 0; // curr. image for color/warp adjustments float *panowarpx[4], *panowarpy[4]; // warp memory, pixel displacements PXM *cimPXMwdup[4]; // copies of images before mouse warps PXM *preflatPXM; // copy of E3pxm before flatten void pano_prealign(); // manual pre-align void pano_align(); // auto fine-align void pano_adjust(); // user color adjust void panowarp_mousefunc(); // image warp mouse function void panowarp_dowarps(int imx); editfunc EFpano; // edit function data // menu function void m_pano_horz(GtkWidget *, cchar *) { int imx, err; F1_help_topic = "panorama"; // help topic if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (checkpend("all")) return; cim_manualwarp = 0; pano_mousewarp = 0; cim_manualalign = 0; cimNF = 0; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } gallery_select_clear(); // clear selected file list gallery_select(); // get new list if (GScount == 0) goto cleanup; if (GScount < 2 || GScount > 4) { zmessageACK(Mwin,E2X("Select 2 to 4 files")); goto cleanup; } cimNF = GScount; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(GSfiles[imx]); // set up file list if (! cim_load_files()) goto cleanup; // load and check all files free_resources(); // ready to commit err = f_open(cimFile[cimLF]); // curr_file = alphabetically last if (err) goto cleanup; EFpano.menufunc = m_pano_horz; EFpano.funcname = "pano"; EFpano.mousefunc = panowarp_mousefunc; 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); if (Fescape) { // user cancel 19.0 Fescape = 0; zmessage_post_bold(Mwin,"parent",3,Bcancel); } cleanup: for (imx = 0; imx < cimNF; imx++) { // free cim file and PXM data if (cimFile[imx]) zfree(cimFile[imx]); if (cimPXMf[imx]) PXM_free(cimPXMf[imx]); if (cimPXMs[imx]) PXM_free(cimPXMs[imx]); if (cimPXMw[imx]) PXM_free(cimPXMw[imx]); } *paneltext = 0; return; } // perform manual pre-align of all images // returns alignment data in cimOffs[*] // lens_mm may also be altered void pano_prealign() { int pano_prealign_event(zdialog *zd, cchar *event); // dialog event function void * pano_prealign_thread(void *); // working thread int imx, ww, err = 0; cchar *exifkey[2] = { exif_focal_length_35_key, exif_focal_length_key }; char *pp[2] = { 0, 0 }; cchar *lens_source; float temp; cchar *align_mess = E2X("Drag images into rough alignment.\n" "To rotate, drag from lower edge."); cchar *scan_mess = E2X("no curve (scanned image)"); cchar *search_mess = E2X("Search for lens mm"); cchar *save_mess = E2X("Save lens mm → image EXIF"); err = 1; lens_source = "NO EXIF"; exif_get(curr_file,exifkey,pp,2); // get lens mm from EXIF if available if (pp[0]) err = convSF(pp[0], temp, 20, 1000); // try both keys 19.16 else if (pp[1]) err = convSF(pp[1], 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; } cimBlend = panPreAlignBlend * cimPXMw[1]->ww; // overlap in align window cim_show_images(1,0); // combine and show images in main window /* _________________________________________ | Pre-align Images | | | | Drag images into rough alignment. | | To rotate, drag from lower edge. | | | | [ 35.5 ] lens mm (EXIF) | | [x] no curve (scanned image) | | [x] no auto warp | | [x] manual align | | [Resize] resize window | | [Search] Search for lens mm | | [Save] Save lens mm -> image EXIF | | | | [Proceed] [Cancel] | |_________________________________________| */ panozd = zdialog_new(E2X("Pre-align Images"),Mwin,Bproceed,Bcancel,null); // start pre-align dialog zdialog_add_widget(panozd,"label","lab1","dialog",align_mess,"space=3"); zdialog_add_widget(panozd,"hbox","hb1","dialog",0); zdialog_add_widget(panozd,"zspin","spmm","hb1","20|999|0.1|35","space=5"); zdialog_add_widget(panozd,"label","labmm","hb1",E2X("lens mm")); zdialog_add_widget(panozd,"label","labsorc","hb1","","space=5"); zdialog_add_widget(panozd,"hbox","hbnc","dialog"); zdialog_add_widget(panozd,"check","nocurve","hbnc",scan_mess,"space=5"); zdialog_add_widget(panozd,"hbox","hbmw","dialog"); zdialog_add_widget(panozd,"check","manwarp","hbmw",E2X("no auto warp"),"space=5"); zdialog_add_widget(panozd,"hbox","hbma","dialog"); zdialog_add_widget(panozd,"check","manalign","hbma",E2X("manual align"),"space=5"); zdialog_add_widget(panozd,"hbox","hb5","dialog",0,"space=2"); zdialog_add_widget(panozd,"button","resize","hb5",E2X("Resize"),"space=5"); zdialog_add_widget(panozd,"label","labsiz","hb5",E2X("resize window"),"space=5"); zdialog_add_widget(panozd,"hbox","hb6","dialog",0,"space=2"); zdialog_add_widget(panozd,"button","search","hb6",Bsearch,"space=5"); zdialog_add_widget(panozd,"label","labsearch","hb6",search_mess,"space=5"); zdialog_add_widget(panozd,"hbox","hb7","dialog",0,"space=2"); zdialog_add_widget(panozd,"button","save","hb7",Bsave,"space=5"); zdialog_add_widget(panozd,"label","labsave","hb7",save_mess,"space=5"); zdialog_add_ttip(panozd,"manwarp",E2X("do not warp images during auto-alignment")); zdialog_stuff(panozd,"spmm",lens_mm); // stuff lens data zdialog_stuff(panozd,"labsorc",lens_source); // show source of lens data zdialog_stuff(panozd,"nocurve",cimPanoNC); zdialog_stuff(panozd,"manwarp",0); panStat = -1; // busy status gdk_window_set_cursor(gdkwin,dragcursor); // set drag cursor zdialog_run(panozd,pano_prealign_event,"save"); // start dialog start_thread(pano_prealign_thread,0); // start working thread zdialog_wait(panozd); // wait for dialog completion zdialog_free(panozd); // free dialog gdk_window_set_cursor(gdkwin,0); // restore normal cursor return; } // pre-align dialog event function int pano_prealign_event(zdialog *zd, cchar *event) { int imx; float overlap; cchar *exifkey[1] = { exif_focal_length_35_key }; // 19.16 cchar *exifdata[1]; char lensdata[8]; if (strmatch(event,"spmm")) zdialog_fetch(zd,"spmm",lens_mm); // get revised lens data if (strmatch(event,"manwarp")) // get auto/manual warp option zdialog_fetch(zd,"manwarp",cim_manualwarp); if (strmatch(event,"manalign")) // get auto/manual align option zdialog_fetch(zd,"manalign",cim_manualalign); if (strmatch(event,"nocurve")) zdialog_fetch(zd,"nocurve",cimPanoNC); // get "no-curve" option if (strmatch(event,"resize")) // allocate new E3 image cim_show_images(1,0); if (strmatch(event,"search")) { // search for optimal lens parms if (cimNF != 2) zmessageACK(Mwin,E2X("use two images only")); else panStat = 2; // tell thread to search return 1; } 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 m_zoom(0,"fit"); // reset poss. user zoom-in 20.0 if (! panStat) return 1; // 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,E2X("Too little overlap, cannot align")); panStat = 0; return 1; } } } return 1; } // 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,E2X("Too little overlap, cannot align")); return; } Ffuncbusy = 1; cimSampSize = 5000; cimNsearch = 0; mm_range = 0.1 * lens_mm; // set initial search ranges xf_range = 7; yf_range = 7; tf_range = 0.01; xf_rfinal = 0.3; // final xf range - when to quit cim_match_colors(0,1,cimPXMw); // adjust colors for image matching cim_adjust_colors(cimPXMs[0],1); cim_adjust_colors(cimPXMw[0],1); cim_adjust_colors(cimPXMs[1],2); cim_adjust_colors(cimPXMw[1],2); lens_mmB = lens_mm; // starting point offsetsB = cimOffs[1]; cimSearchRange = 7; matchB = 0; while (true) { srand48(time(0) + randcount++); lens_mm = lens_mmB + mm_range * (drand48() - 0.5); // new random lens factor for (imx = 0; imx <= 1; imx++) { // re-curve images cim_scale_image(imx,cimPXMs); cim_curve_image(imx); PXM_free(cimPXMw[imx]); cimPXMw[imx] = PXM_copy(cimPXMs[imx]); } cim_get_redpix(0); // get high-contrast pixels cim_show_images(0,0); // combine and show images squeeze = 0.97; // search range reduction for (int ii = 0; ii < 1000; ii++) // loop random x/y/t alignments { rnum = drand48(); if (rnum < 0.33) // random change some alignment offset cimOffs[1].xf = offsetsB.xf + xf_range * (drand48() - 0.5); else if (rnum < 0.67) cimOffs[1].yf = offsetsB.yf + yf_range * (drand48() - 0.5); else cimOffs[1].tf = offsetsB.tf + tf_range * (drand48() - 0.5); matchlev = cim_match_images(0,1); // test quality of image alignment snprintf(paneltext,200,"align: %d match: %.5f lens: %.1f", ++cimNsearch, matchB, lens_mmB); if (cim_sigdiff(matchlev,matchB,0.00001) > 0) { matchB = matchlev; // save new best fit lens_mmB = lens_mm; // alignment is better offsetsB = cimOffs[1]; cim_show_images(0,0); squeeze = 1; // keep same search range as long break; // as improvements are found } if (panStat != -1) goto done; // user kill zmainloop(); } if (xf_range < xf_rfinal) goto done; // finished snprintf(paneltext,200,"align: %d match: %.5f lens: %.1f", cimNsearch, matchB, lens_mmB); mm_range = squeeze * mm_range; // reduce search range if no if (mm_range < 0.02 * lens_mmB) mm_range = 0.02 * lens_mmB; // improvements were found xf_range = squeeze * xf_range; yf_range = squeeze * yf_range; tf_range = squeeze * tf_range; zmainloop(); } done: zfree(cimRedpix); cimRedpix = 0; lens_mm = lens_mmB; // save best lens param found cimSampSize = panSampSize; // restore Ffuncbusy = 0; cim_show_images(1,0); // images are left color-matched return; } // fine-alignment // start with very small image size // search around offset values for best match // increase image size and loop until full-size void pano_align() { int imx, im1, im2, ww; float R, dx, dy, dt; float overlap; cimoffs offsets0; Ffuncbusy = 1; for (imx = 0; imx < cimNF; imx++) { cimOffs[imx].xf = cimOffs[imx].xf / cimScale; // scale x/y offsets for full-size images cimOffs[imx].yf = cimOffs[imx].yf / cimScale; } cimScale = 1.0; // full-size for (imx = 0; imx < cimNF; imx++) { PXM_free(cimPXMs[imx]); cimPXMs[imx] = PXM_copy(cimPXMf[imx]); // copy full-size images cim_curve_image(imx); // curve them } cimBlend = 0.3 * cimPXMs[0]->ww; cim_get_overlap(0,1,cimPXMs); // match images 0 & 1 in overlap area cim_match_colors(0,1,cimPXMs); cim_adjust_colors(cimPXMf[0],1); // image 0 << profile 1 cim_adjust_colors(cimPXMf[1],2); // image 1 << profile 2 if (cimNF > 2) { cimBlend = 0.3 * cimPXMs[1]->ww; cim_get_overlap(1,2,cimPXMs); cim_match_colors(1,2,cimPXMs); cim_adjust_colors(cimPXMf[0],1); cim_adjust_colors(cimPXMf[1],1); cim_adjust_colors(cimPXMf[2],2); } if (cimNF > 3) { cimBlend = 0.3 * cimPXMs[2]->ww; cim_get_overlap(2,3,cimPXMs); cim_match_colors(2,3,cimPXMs); cim_adjust_colors(cimPXMf[0],1); cim_adjust_colors(cimPXMf[1],1); cim_adjust_colors(cimPXMf[2],1); cim_adjust_colors(cimPXMf[3],2); } cimScale = panInitAlignSize / cimPXMf[1]->hh; // initial align image scale if (cimScale > 1.0) cimScale = 1.0; if (cim_manualalign) cimScale = 1.0; // manual alignment, full size for (imx = 0; imx < cimNF; imx++) { // scale offsets for image scale cimOffs[imx].xf = cimOffs[imx].xf * cimScale; cimOffs[imx].yf = cimOffs[imx].yf * cimScale; } cimSearchRange = panInitSearchRange; // initial align search range cimSearchStep = panInitSearchStep; // initial align search step cimWarpRange = panInitWarpRange; // initial align corner warp range cimWarpStep = panInitWarpStep; // initial align corner warp step ww = cimPXMf[0]->ww * cimScale; // initial align image width cimBlend = ww * panInitBlend; // initial align blend width cimSampSize = panSampSize; // pixel sample size for align/compare cimNsearch = 0; // reset align search counter while (true) // loop, increasing image size { for (imx = 0; imx < cimNF; imx++) { // prepare images cim_scale_image(imx,cimPXMs); // scale to new size cim_curve_image(imx); // curve based on lens params cim_warp_image_pano(imx,1); // apply corner warps } cim_show_images(1,0); // show with 50/50 blend in overlaps for (im1 = 0; im1 < cimNF-1; im1++) // fine-align each image with left neighbor { im2 = im1 + 1; offsets0 = cimOffs[im2]; // save initial alignment offsets overlap = cim_get_overlap(im1,im2,cimPXMs); // get overlap area if (overlap < panFinalBlend-2) { zmessageACK(Mwin,E2X("Too little overlap, cannot align")); goto fail; } if (cim_manualalign) cim_show_images(0,0); else { cim_get_redpix(im1); // get high-contrast pixels cim_show_images(0,0); // show with 50/50 blend in overlaps cim_align_image(im1,im2); // search for best offsets and warps zfree(cimRedpix); // clear red pixels cimRedpix = 0; if (Fescape) goto fail; } 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) 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 = E2X("Match Brightness and Color"); char imageN[8] = "imageN"; int ww, hh, cc, imx; for (imx = 0; imx < cimNF; imx++) { // neutral color adjustments pano_adjust_RGB[imx][0] = 100; pano_adjust_RGB[imx][1] = 100; pano_adjust_RGB[imx][2] = 100; } for (imx = 0; imx < cimNF; imx++) { // allocate warp memory ww = cimPXMw[imx]->ww; hh = cimPXMw[imx]->hh; cc = ww * hh * sizeof(float); panowarpx[imx] = (float *) zmalloc(cc); panowarpy[imx] = (float *) zmalloc(cc); memset(panowarpx[imx],0,cc); memset(panowarpy[imx],0,cc); cimPXMwdup[imx] = PXM_copy(cimPXMw[imx]); } cimBlend = 1; // init. blend width /*** ___________________________________ | Match Brightness and Color | | | | Select image (o) (o) (o) (o) | | | | red green blue | | [_____] [_____] [_____] | | brightness [____] [apply] | | [auto color] [file color] | | blend width [____] [apply] | | [x] mouse warp | | [flatten] curved image | | flatten image [___] [apply] | | | | [done] [cancel] | |___________________________________| ***/ panozd = zdialog_new(adjusttitle,Mwin,Bdone,Bcancel,null); zdialog_add_widget(panozd,"hbox","hbim","dialog",0,"space=3"); zdialog_add_widget(panozd,"label","labim","hbim",E2X("Select image"),"space=5"); zdialog_add_widget(panozd,"hbox","hbc1","dialog",0,"homog"); zdialog_add_widget(panozd,"label","labred","hbc1",Bred); zdialog_add_widget(panozd,"label","labgreen","hbc1",Bgreen); zdialog_add_widget(panozd,"label","labblue","hbc1",Bblue); zdialog_add_widget(panozd,"hbox","hbc2","dialog",0,"homog"); zdialog_add_widget(panozd,"zspin","red","hbc2","50|200|0.1|100"); zdialog_add_widget(panozd,"zspin","green","hbc2","50|200|0.1|100"); zdialog_add_widget(panozd,"zspin","blue","hbc2","50|200|0.1|100"); zdialog_add_widget(panozd,"hbox","hbbri","dialog",0,"space=3"); zdialog_add_widget(panozd,"label","labbr","hbbri",Bbrightness,"space=5"); zdialog_add_widget(panozd,"zspin","bright","hbbri","50|200|0.1|100"); zdialog_add_widget(panozd,"button","RGBapply","hbbri",Bapply,"space=10"); zdialog_add_widget(panozd,"hsep","hsep","dialog",0,"space=3"); zdialog_add_widget(panozd,"hbox","hbc3","dialog",0,"space=3"); zdialog_add_widget(panozd,"button","auto","hbc3",E2X("auto color"),"space=5"); zdialog_add_widget(panozd,"button","file","hbc3",E2X("file color"),"space=5"); zdialog_add_widget(panozd,"hbox","hbblen","dialog",0,"space=3"); zdialog_add_widget(panozd,"label","labbl","hbblen",Bblendwidth,"space=5"); zdialog_add_widget(panozd,"zspin","blend","hbblen","1|999|1|1"); zdialog_add_widget(panozd,"button","blendapply","hbblen",Bapply,"space=15"); zdialog_add_widget(panozd,"hbox","hbwarp","dialog",0,"space=3"); zdialog_add_widget(panozd,"check","mousewarp","hbwarp",E2X("mouse warp"),"space=3"); zdialog_add_widget(panozd,"hbox","hbflat","dialog",0,"space=3"); zdialog_add_widget(panozd,"label","labflat","hbflat",E2X("flatten image"),"space=3"); zdialog_add_widget(panozd,"zspin","flatten","hbflat","0|1|0.01|0","space=3"); zdialog_add_widget(panozd,"button","flatapply","hbflat",Bapply,"space=15"); for (imx = 0; imx < cimNF; imx++) { // add radio button per image imageN[5] = '0' + imx; zdialog_add_widget(panozd,"radio",imageN,"hbim",0,"space=3"); } zdialog_stuff(panozd,"image0",1); // pre-select 1st image pano_currimage = 0; zdialog_stuff(panozd,"mousewarp",0); // default mouse warp off panStat = -1; // busy status zdialog_run(panozd,pano_adjust_event,"save"); // run dialog, parallel zdialog_wait(panozd); // wait for dialog completion zdialog_free(panozd); // free dialog return; } // dialog event function int pano_adjust_event(zdialog *zd, cchar *event) { char imageN[8] = "imageN"; float R, G, B, R1, G1, B1, F; int imS, imx, im1, im2; int ww, hh, px, py, nn; float bright, bright2, *pixel; if (zd->zstat) // dialog complete { if (zd->zstat == 1) panStat = 1; // done else panStat = 0; // cancel freeMouse(); for (imx = 0; imx < cimNF; imx++) // free memory PXM_free(cimPXMwdup[imx]); if (preflatPXM) PXM_free(preflatPXM); preflatPXM = 0; return 1; } 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 (zstrstr("red green blue",event)) { // new RGB value bright = (R + G + B) / 3; zdialog_stuff(zd,"bright",bright); // matching brightness } if (strmatch(event,"bright")) { // brightness change bright2 = (R + G + B) / 3; bright2 = bright / bright2; R = R * bright2; // matching RGB G = G * bright2; B = B * bright2; zdialog_stuff(zd,"red",R); zdialog_stuff(zd,"green",G); zdialog_stuff(zd,"blue",B); } if (strmatch(event,"RGBapply")) // apply color & brightness changes { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } R = R / 100; // normalize 0.5 ... 2.0 G = G / 100; B = B / 100; cim_warp_image_pano(imS,0); // refresh cimPXMw from cimPXMs ww = cimPXMw[imS]->ww; hh = cimPXMw[imS]->hh; for (py = 0; py < hh; py++) // loop all image pixels for (px = 0; px < ww; px++) { pixel = PXMpix(cimPXMw[imS],px,py); R1 = R * pixel[0]; // apply color factors G1 = G * pixel[1]; B1 = B * pixel[2]; if (R1 > 255.9 || G1 > 255.9 || B1 > 255.9) { bright = R1; // avoid overflow if (G1 > bright) bright = G1; if (B1 > bright) bright = B1; bright = 255.9 / bright; R1 = R1 * bright; G1 = G1 * bright; B1 = B1 * bright; } pixel[0] = R1; pixel[1] = G1; pixel[2] = B1; } cimBlend = 1; zdialog_stuff(zd,"blend",cimBlend); cim_show_images(0,0); // combine and show with 50/50 blend } if (strmatch(event,"auto")) // auto match color of selected image { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } for (im1 = imS; im1 < cimNF-1; im1++) // from selected image to last image { im2 = im1 + 1; cimBlend = 0.3 * cimPXMw[im2]->ww; cim_get_overlap(im1,im2,cimPXMw); // match images in overlap area cim_match_colors(im1,im2,cimPXMw); cim_adjust_colors_pano(cimPXMw[im1],1); // image im1 << profile 1 cim_adjust_colors_pano(cimPXMw[im2],2); // image im2 << profile 2 for (imx = im1-1; imx >= imS; imx--) cim_adjust_colors_pano(cimPXMw[imx],1); } for (im1 = imS-1; im1 >= 0; im1--) // from selected image to 1st image { im2 = im1 + 1; cimBlend = 0.3 * cimPXMw[im2]->ww; cim_get_overlap(im1,im2,cimPXMw); // match images in overlap area cim_match_colors(im1,im2,cimPXMw); cim_adjust_colors_pano(cimPXMw[im1],1); // image im1 << profile 1 cim_adjust_colors_pano(cimPXMw[im2],2); // image im2 << profile 2 for (imx = im2+1; imx < cimNF; imx++) cim_adjust_colors_pano(cimPXMw[imx],2); } cimBlend = 1; zdialog_stuff(zd,"blend",cimBlend); cim_show_images(0,0); } if (strmatch(event,"file")) // use original file colors { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } if (! cim_load_files()) return 1; for (imx = 0; imx < cimNF; imx++) { PXM_free(cimPXMs[imx]); cimPXMs[imx] = PXM_copy(cimPXMf[imx]); cim_curve_image(imx); // curve and warp cim_warp_image_pano(imx,0); PXM_free(cimPXMwdup[imx]); cimPXMwdup[imx] = PXM_copy(cimPXMw[imx]); panowarp_dowarps(imx); // re-apply mouse warps } cimBlend = 1; zdialog_stuff(zd,"blend",cimBlend); cim_show_images(0,0); } if (strmatch(event,"blendapply")) // apply new blend width { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } zdialog_fetch(zd,"blend",cimBlend); // can be zero cim_show_images(0,1); // show with gradual blend } if (strmatch(event,"mousewarp")) // engage mouse warp/align { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } cimBlend = 1; zdialog_stuff(zd,"blend",cimBlend); cim_show_images(0,0); zdialog_fetch(zd,"mousewarp",pano_mousewarp); if (pano_mousewarp) takeMouse(panowarp_mousefunc,dragcursor); // connect mouse function else freeMouse(); } if (strmatch(event,"flatapply")) { if (preflatPXM) { PXM_free(E3pxm); E3pxm = PXM_copy(preflatPXM); } else preflatPXM = PXM_copy(E3pxm); zdialog_fetch(zd,"flatten",F); cim_flatten_image(F); Fpaint2(); } return 1; } // panorama dialog mouse warp function // for selected image, cimPXMs >> warp >> cimPXMw >> E3 void panowarp_mousefunc() { int imx; int mx, my, dx, dy; int ii, px, py, ww, hh; float mag, dispx, dispy, d1, d2; PXM *pxm; if (LMclick || RMclick) return; // mouse click if (! Mxdrag && ! Mydrag) return; if (! pano_mousewarp) return; mx = Mxdrag; // mouse drag my = Mydrag; if (mx < 0 || mx > E3pxm->ww-1 || my < 0 || my > E3pxm->hh-1) // mouse outside image area return; imx = pano_currimage; pxm = cimPXMw[imx]; // output image ww = pxm->ww; hh = pxm->hh; mx = Mxdown; // drag origin, image coordinates my = Mydown; dx = Mxdrag - Mxdown; // drag increment dy = Mydrag - Mydown; Mxdown = Mxdrag; // next drag origin Mydown = Mydrag; Mxdrag = Mydrag = 0; mx = mx - cimOffs[imx].xf; // mouse position relative my = my - cimOffs[imx].yf; // to image origin d1 = ww * ww + hh * hh; for (py = 0; py < hh; py++) // process all output pixels for (px = 0; px < ww; px++) { d2 = (px-mx)*(px-mx) + (py-my)*(py-my); mag = (1.0 - d2 / d1); mag = pow(mag,50); dispx = -dx * mag; // displacement = drag * mag dispy = -dy * mag; ii = py * ww + px; panowarpx[imx][ii] += dispx; // add this drag to prior sum panowarpy[imx][ii] += dispy; } panowarp_dowarps(imx); // do accumulated warps if (cimPanoV) cim_show_Vimages(0,0); // show images else cim_show_images(0,0); return; } // warp the image using the accumulated mouse drags void panowarp_dowarps(int imx) { float vpix1[4], *pix2; int ii, px, py, ww, hh, vstat1; float dispx, dispy; PXM *pxm1, *pxm2; pxm1 = cimPXMwdup[imx]; // input image pxm2 = cimPXMw[imx]; // output image ww = pxm2->ww; hh = pxm2->hh; for (py = 0; py < hh; py++) // process all output pixels for (px = 0; px < ww; px++) { ii = py * ww + px; dispx = panowarpx[imx][ii]; dispy = panowarpy[imx][ii]; vstat1 = vpixel(pxm1,px+dispx,py+dispy,vpix1); // input virtual pixel pix2 = PXMpix(pxm2,px,py); // output pixel if (vstat1) memcpy(pix2,vpix1,pixcc); else memset(pix2,0,pixcc); // voided pixel } return; } /******************************************************************************** Vertical Panorama function: join 2, 3, or 4 images. *********************************************************************************/ void vpano_prealign(); // manual pre-align void vpano_align(); // auto fine-align void vpano_adjust(); // user color adjust editfunc EFvpano; // edit function data // menu function void m_pano_vert(GtkWidget *, cchar *) { int imx, err; F1_help_topic = "vert. panorama"; // help topic if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (checkpend("all")) return; cim_manualwarp = 0; pano_mousewarp = 0; cim_manualalign = 0; cimNF = 0; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } gallery_select_clear(); // clear selected file list gallery_select(); // get new list if (GScount == 0) goto cleanup; if (GScount < 2 || GScount > 4) { zmessageACK(Mwin,E2X("Select 2 to 4 files")); goto cleanup; } cimNF = GScount; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(GSfiles[imx]); // set up file list if (! cim_load_files()) goto cleanup; // load and check all files free_resources(); // ready to commit err = f_open(cimFile[cimLF]); // curr_file = alphabetically last if (err) goto cleanup; EFvpano.menufunc = m_pano_vert; EFvpano.funcname = "vpano"; EFvpano.mousefunc = panowarp_mousefunc; 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); if (Fescape) { // user cancel 19.0 Fescape = 0; zmessage_post_bold(Mwin,"parent",3,Bcancel); } cleanup: for (imx = 0; imx < cimNF; imx++) { // free cim file and PXM data if (cimFile[imx]) zfree(cimFile[imx]); if (cimPXMf[imx]) PXM_free(cimPXMf[imx]); if (cimPXMs[imx]) PXM_free(cimPXMs[imx]); if (cimPXMw[imx]) PXM_free(cimPXMw[imx]); } *paneltext = 0; return; } // perform manual pre-align of all images // returns alignment data in cimOffs[*] // lens_mm may also be altered void vpano_prealign() { int vpano_prealign_event(zdialog *zd, cchar *event); // dialog event function void * vpano_prealign_thread(void *); // working thread int imx, hh, err = 0; cchar *exifkey[2] = { exif_focal_length_35_key, exif_focal_length_key }; char *pp[2] = { 0, 0 }; cchar *lens_source; float temp; cchar *align_mess = E2X("Drag images into rough alignment.\n" "To rotate, drag from right edge."); cchar *scan_mess = E2X("no curve (scanned image)"); cchar *search_mess = E2X("Search for lens mm"); cchar *save_mess = E2X("Save lens mm → image EXIF"); err = 1; lens_source = "NO EXIF"; exif_get(curr_file,exifkey,pp,2); // get lens mm from EXIF if available if (pp[0]) err = convSF(pp[0], temp, 20, 1000); // try both keys 19.16 else if (pp[1]) err = convSF(pp[1], 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; } cimBlend = panPreAlignBlend * cimPXMw[1]->hh; // overlap in align window cim_show_Vimages(1,0); // combine and show images in main window /* _________________________________________ | Pre-align Images | | | | Drag images into rough alignment. | | To rotate, drag from right edge. | | | | [ 35.5 ] lens mm (EXIF) | | [x] no curve (scanned image) | | [x] no auto warp | | [x] manual align | | [Resize] resize window | | [Search] Search for lens mm | | [Save] Save lens mm -> image EXIF | | | | [Proceed] [Cancel] | |_________________________________________| */ panozd = zdialog_new(E2X("Pre-align Images"),Mwin,Bproceed,Bcancel,null); // start pre-align dialog zdialog_add_widget(panozd,"label","lab1","dialog",align_mess,"space=3"); zdialog_add_widget(panozd,"hbox","hb1","dialog",0); zdialog_add_widget(panozd,"zspin","spmm","hb1","20|999|0.1|35","space=5"); zdialog_add_widget(panozd,"label","labmm","hb1",E2X("lens mm")); zdialog_add_widget(panozd,"label","labsorc","hb1","","space=5"); zdialog_add_widget(panozd,"hbox","hbnc","dialog"); zdialog_add_widget(panozd,"check","nocurve","hbnc",scan_mess,"space=5"); zdialog_add_widget(panozd,"hbox","hbmw","dialog"); zdialog_add_widget(panozd,"check","manwarp","hbmw",E2X("no auto warp"),"space=5"); zdialog_add_widget(panozd,"hbox","hbma","dialog"); zdialog_add_widget(panozd,"check","manalign","hbma",E2X("manual align"),"space=5"); zdialog_add_widget(panozd,"hbox","hb5","dialog",0,"space=2"); zdialog_add_widget(panozd,"button","resize","hb5",E2X("Resize"),"space=5"); zdialog_add_widget(panozd,"label","labsiz","hb5",E2X("resize window"),"space=5"); zdialog_add_widget(panozd,"hbox","hb6","dialog",0,"space=2"); zdialog_add_widget(panozd,"button","search","hb6",Bsearch,"space=5"); zdialog_add_widget(panozd,"label","labsearch","hb6",search_mess,"space=5"); zdialog_add_widget(panozd,"hbox","hb7","dialog",0,"space=2"); zdialog_add_widget(panozd,"button","save","hb7",Bsave,"space=5"); zdialog_add_widget(panozd,"label","labsave","hb7",save_mess,"space=5"); zdialog_add_ttip(panozd,"manwarp",E2X("do not warp images during auto-alignment")); zdialog_stuff(panozd,"spmm",lens_mm); // stuff lens data zdialog_stuff(panozd,"labsorc",lens_source); // show source of lens data zdialog_stuff(panozd,"nocurve",cimPanoNC); zdialog_stuff(panozd,"manwarp",0); panStat = -1; // busy status gdk_window_set_cursor(gdkwin,dragcursor); // set drag cursor zdialog_run(panozd,vpano_prealign_event,"save"); // start dialog start_thread(vpano_prealign_thread,0); // start working thread zdialog_wait(panozd); // wait for dialog completion zdialog_free(panozd); // free dialog gdk_window_set_cursor(gdkwin,0); // restore normal cursor return; } // pre-align dialog event function int vpano_prealign_event(zdialog *zd, cchar *event) { int imx; float overlap; cchar *exifkey[1] = { exif_focal_length_35_key }; // 19.16 cchar *exifdata[1]; char lensdata[8]; if (strmatch(event,"spmm")) zdialog_fetch(zd,"spmm",lens_mm); // get revised lens data if (strmatch(event,"manwarp")) // get auto/manual warp option zdialog_fetch(zd,"manwarp",cim_manualwarp); if (strmatch(event,"manalign")) // get auto/manual align option zdialog_fetch(zd,"manalign",cim_manualalign); if (strmatch(event,"nocurve")) zdialog_fetch(zd,"nocurve",cimPanoNC); // get "no-curve" option if (strmatch(event,"resize")) // allocate new E3 image cim_show_Vimages(1,0); if (strmatch(event,"search")) { // search for optimal lens parms if (cimNF != 2) zmessageACK(Mwin,E2X("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 m_zoom(0,"fit"); // reset poss. user zoom-in 20.0 if (! panStat) return 1; // 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,E2X("Too little overlap, cannot align")); panStat = 0; return 1; } } } return 1; } // pre-align working thread // convert mouse and KB events into image movements // overhauled void * vpano_prealign_thread(void *) { void vpano_autolens(); cimoffs offstemp; PXM *pxmtemp; char *ftemp; int im1, im2, imm, imx; int mx0, my0, mx, my; // mouse drag origin, position int xoff, yoff, loy, hiy; int sepy, minsep; int ww, hh, rotate, midy; int fchange, nc0; float lens_mm0; float dx, dy, t1, t2, dt; imm = ww = hh = rotate = xoff = yoff = 0; // stop compiler warnings lens_mm0 = lens_mm; // to detect changes nc0 = cimPanoNC; mx0 = my0 = 0; // no drag in progress Mcapture = KBcapture = 1; // capture mouse drag and KB keys cimBlend = 0; // full blend during pre-align while (true) // loop and align until done { zsleep(0.02); // logic simplified if (panStat == 2) { // dialog search button panStat = -1; // back to busy status vpano_autolens(); } if (panStat != -1) break; // quit signal from dialog fchange = 0; if (lens_mm != lens_mm0) // change in lens parameter fchange = 1; if (cimPanoNC != nc0) fchange = 1; // change in "no-curve" option if (fchange) { lens_mm0 = lens_mm; nc0 = cimPanoNC; for (imx = 0; imx < cimNF; imx++) { // re-curve images cim_scale_image(imx,cimPXMs); cim_curve_Vimage(imx); PXM_free(cimPXMw[imx]); cimPXMw[imx] = PXM_copy(cimPXMs[imx]); } cim_show_Vimages(1,0); // combine and show images continue; } if (KBkey) { // KB input if (KBkey == GDK_KEY_Left) cimOffs[imm].xf -= 0.5; // adjust alignment offsets if (KBkey == GDK_KEY_Right) cimOffs[imm].xf += 0.5; if (KBkey == GDK_KEY_Up) cimOffs[imm].yf -= 0.5; if (KBkey == GDK_KEY_Down) cimOffs[imm].yf += 0.5; if (KBkey == GDK_KEY_r) cimOffs[imm].tf += 0.0005; if (KBkey == GDK_KEY_l) cimOffs[imm].tf -= 0.0005; KBkey = 0; cim_show_Vimages(0,0); // combine and show images continue; } if (! Mxdrag && ! Mydrag) // no drag underway mx0 = my0 = 0; // reset drag origin if ((Mxdrag || Mydrag) && ! Fmousemain) // mouse drag underway { mx = Mxdrag; // mouse position in image my = Mydrag; if (! mx0 && ! my0) // new drag { mx0 = mx; // set drag origin my0 = my; minsep = 9999; for (imx = 0; imx < cimNF; imx++) // find image with midpoint { // closest to mouse y loy = cimOffs[imx].yf; hiy = loy + cimPXMw[imx]->hh; midy = (loy + hiy) / 2; sepy = abs(midy - my0); if (sepy < minsep) { minsep = sepy; imm = imx; // image to drag or rotate } } xoff = cimOffs[imm].xf; yoff = cimOffs[imm].yf; ww = cimPXMw[imm]->ww; hh = cimPXMw[imm]->hh; rotate = 0; // if drag at right edge, if (mx0 > xoff + 0.85 * ww) rotate = 1; // set rotate flag } if (mx != mx0 || my != my0) // drag is progressing { dx = mx - mx0; // mouse movement dy = my - my0; if (rotate && my0 > yoff && my > yoff) // rotation { if (imm > 0) { loy = cimOffs[imm].yf; // if there is an image above, hiy = cimOffs[imm-1].yf + cimPXMw[imm-1]->hh; // midy = midpoint of overlap midy = (loy + hiy) / 2; } else midy = 0; // this is the topmist image t1 = atan(1.0 * (my0-yoff) / (mx0-xoff)); t2 = atan(1.0 * (my-yoff) / (mx-xoff)); dt = t2 - t1; // angle change dy = - dt * ww / 2; // pivot = middle of overlap above dx = dt * (midy-yoff); } else dt = 0; // x/y drag cimOffs[imm].xf += dx; // update image cimOffs[imm].yf += dy; cimOffs[imm].tf += dt; xoff = cimOffs[imm].xf; yoff = cimOffs[imm].yf; cim_show_Vimages(0,0); // show combined images mx0 = mx; // next drag origin = current mouse my0 = my; } } for (im1 = 0; im1 < cimNF-1; im1++) // track image order changes { im2 = im1 + 1; if (cimOffs[im2].yf < cimOffs[im1].yf) { ftemp = cimFile[im2]; // switch filespecs cimFile[im2] = cimFile[im1]; cimFile[im1] = ftemp; pxmtemp = cimPXMf[im2]; // switch images cimPXMf[im2] = cimPXMf[im1]; cimPXMf[im1] = pxmtemp; pxmtemp = cimPXMs[im2]; // scaled images cimPXMs[im2] = cimPXMs[im1]; cimPXMs[im1] = pxmtemp; pxmtemp = cimPXMw[im2]; // warped images cimPXMw[im2] = cimPXMw[im1]; cimPXMw[im1] = pxmtemp; offstemp = cimOffs[im2]; // offsets cimOffs[im2] = cimOffs[im1]; cimOffs[im1] = offstemp; if (imm == im1) imm = im2; // current drag image else if (imm == im2) imm = im1; break; } } } KBcapture = Mcapture = 0; thread_exit(); return 0; // not executed, stop g++ warning } // optimize lens parameters // inputs and outputs: // pre-aligned images cimPXMw[0] and [1] // offsets in cimOffs[0] and [1] // lens_mm void vpano_autolens() { float mm_range, xf_range, yf_range, tf_range; float squeeze, xf_rfinal, rnum, matchB, matchlev; float overlap, lens_mmB; int imx, randcount = 0; cimoffs offsetsB; overlap = cim_get_overlap(0,1,cimPXMs); if (overlap < 0.1) { zmessageACK(Mwin,E2X("Too little overlap, cannot align")); return; } Ffuncbusy = 1; cimSampSize = 5000; cimNsearch = 0; mm_range = 0.1 * lens_mm; // set initial search ranges xf_range = 7; yf_range = 7; tf_range = 0.01; xf_rfinal = 0.3; // final xf range - when to quit cim_match_colors(0,1,cimPXMw); // adjust colors for image matching cim_adjust_colors(cimPXMs[0],1); cim_adjust_colors(cimPXMw[0],1); cim_adjust_colors(cimPXMs[1],2); cim_adjust_colors(cimPXMw[1],2); lens_mmB = lens_mm; // starting point offsetsB = cimOffs[1]; cimSearchRange = 7; matchB = 0; while (true) { srand48(time(0) + randcount++); lens_mm = lens_mmB + mm_range * (drand48() - 0.5); // new random lens factor for (imx = 0; imx <= 1; imx++) { // re-curve images cim_scale_image(imx,cimPXMs); cim_curve_Vimage(imx); PXM_free(cimPXMw[imx]); cimPXMw[imx] = PXM_copy(cimPXMs[imx]); } cim_get_redpix(0); // get high-contrast pixels cim_show_Vimages(0,0); // combine and show images squeeze = 0.97; // search range reduction for (int ii = 0; ii < 1000; ii++) // loop random x/y/t alignments { rnum = drand48(); if (rnum < 0.33) // random change some alignment offset cimOffs[1].xf = offsetsB.xf + xf_range * (drand48() - 0.5); else if (rnum < 0.67) cimOffs[1].yf = offsetsB.yf + yf_range * (drand48() - 0.5); else cimOffs[1].tf = offsetsB.tf + tf_range * (drand48() - 0.5); matchlev = cim_match_images(0,1); // test quality of image alignment snprintf(paneltext,200,"align: %d match: %.5f lens: %.1f", ++cimNsearch, matchB, lens_mmB); if (cim_sigdiff(matchlev,matchB,0.00001) > 0) { matchB = matchlev; // save new best fit lens_mmB = lens_mm; // alignment is better offsetsB = cimOffs[1]; cim_show_Vimages(0,0); squeeze = 1; // keep same search range as long break; // as improvements are found } if (panStat != -1) goto done; // user kill zmainloop(); } if (xf_range < xf_rfinal) goto done; // finished snprintf(paneltext,200,"align: %d match: %.5f lens: %.1f", cimNsearch, matchB, lens_mmB); mm_range = squeeze * mm_range; // reduce search range if no if (mm_range < 0.02 * lens_mmB) mm_range = 0.02 * lens_mmB; // improvements were found xf_range = squeeze * xf_range; yf_range = squeeze * yf_range; tf_range = squeeze * tf_range; zmainloop(); } done: zfree(cimRedpix); cimRedpix = 0; lens_mm = lens_mmB; // save best lens params found cimSampSize = panSampSize; // restore Ffuncbusy = 0; cim_show_Vimages(1,0); // images are left color-matched return; } // fine-alignment // start with very small image size // search around offset values for best match // increase image size and loop until full-size void vpano_align() { int imx, im1, im2, ww, hh; float R, dx, dy, dt; float overlap; cimoffs offsets0; Ffuncbusy = 1; for (imx = 0; imx < cimNF; imx++) { cimOffs[imx].xf = cimOffs[imx].xf / cimScale; // scale x/y offsets for full-size images cimOffs[imx].yf = cimOffs[imx].yf / cimScale; } cimScale = 1.0; // full-size for (imx = 0; imx < cimNF; imx++) { PXM_free(cimPXMs[imx]); cimPXMs[imx] = PXM_copy(cimPXMf[imx]); // copy full-size images cim_curve_Vimage(imx); // curve them } cimBlend = 0.3 * cimPXMs[0]->hh; cim_get_overlap(0,1,cimPXMs); // match images 0 & 1 in overlap area cim_match_colors(0,1,cimPXMs); cim_adjust_colors(cimPXMf[0],1); // image 0 << profile 1 cim_adjust_colors(cimPXMf[1],2); // image 1 << profile 2 if (cimNF > 2) { cimBlend = 0.3 * cimPXMs[1]->hh; cim_get_overlap(1,2,cimPXMs); cim_match_colors(1,2,cimPXMs); cim_adjust_colors(cimPXMf[0],1); cim_adjust_colors(cimPXMf[1],1); cim_adjust_colors(cimPXMf[2],2); } if (cimNF > 3) { cimBlend = 0.3 * cimPXMs[2]->hh; cim_get_overlap(2,3,cimPXMs); cim_match_colors(2,3,cimPXMs); cim_adjust_colors(cimPXMf[0],1); cim_adjust_colors(cimPXMf[1],1); cim_adjust_colors(cimPXMf[2],1); cim_adjust_colors(cimPXMf[3],2); } cimScale = panInitAlignSize / cimPXMf[1]->hh; // initial align image scale if (cimScale > 1.0) cimScale = 1.0; if (cim_manualalign) cimScale = 1.0; // manual alignment, full size for (imx = 0; imx < cimNF; imx++) { // scale offsets for image scale cimOffs[imx].xf = cimOffs[imx].xf * cimScale; cimOffs[imx].yf = cimOffs[imx].yf * cimScale; } cimSearchRange = panInitSearchRange; // initial align search range cimSearchStep = panInitSearchStep; // initial align search step cimWarpRange = panInitWarpRange; // initial align corner warp range cimWarpStep = panInitWarpStep; // initial align corner warp step hh = cimPXMf[0]->hh * cimScale; // initial align image width cimBlend = hh * panInitBlend; // initial align blend width cimSampSize = panSampSize; // pixel sample size for align/compare cimNsearch = 0; // reset align search counter while (true) // loop, increasing image size { for (imx = 0; imx < cimNF; imx++) { // prepare images cim_scale_image(imx,cimPXMs); // scale to new size cim_curve_Vimage(imx); // curve based on lens params cim_warp_image_Vpano(imx,1); // apply corner warps } cim_show_Vimages(1,0); // show with 50/50 blend in overlaps for (im1 = 0; im1 < cimNF-1; im1++) // fine-align each image with top neighbor { im2 = im1 + 1; offsets0 = cimOffs[im2]; // save initial alignment offsets overlap = cim_get_overlap(im1,im2,cimPXMs); // get overlap area if (overlap < panFinalBlend-2) { zmessageACK(Mwin,E2X("Too little overlap, cannot align")); goto fail; } if (cim_manualalign) cim_show_Vimages(0,0); else { cim_get_redpix(im1); // get high-contrast pixels cim_show_Vimages(0,0); // show with 50/50 blend in overlaps cim_align_image(im1,im2); // search for best offsets and warps zfree(cimRedpix); // clear red pixels cimRedpix = 0; if (Fescape) goto fail; } 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: 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 = E2X("Match Brightness and Color"); char imageN[8] = "imageN"; int ww, hh, cc, imx; for (imx = 0; imx < cimNF; imx++) { // neutral color adjustments pano_adjust_RGB[imx][0] = 100; pano_adjust_RGB[imx][1] = 100; pano_adjust_RGB[imx][2] = 100; } for (imx = 0; imx < cimNF; imx++) { // allocate warp memory ww = cimPXMw[imx]->ww; hh = cimPXMw[imx]->hh; cc = ww * hh * sizeof(float); panowarpx[imx] = (float *) zmalloc(cc); panowarpy[imx] = (float *) zmalloc(cc); memset(panowarpx[imx],0,cc); memset(panowarpy[imx],0,cc); cimPXMwdup[imx] = PXM_copy(cimPXMw[imx]); } cimBlend = 1; // init. blend width /*** ___________________________________ | Match Brightness and Color | | | | Image (o) (o) (o) (o) | | | | red green blue | | [_____] [_____] [_____] | | brightness [____] [apply] | | [auto color] [file color] | | blend width [____] [apply] | | [x] mouse warp | | [flatten] curved image | | flatten image [___] [apply] | | | | [done] [cancel] | |___________________________________| ***/ panozd = zdialog_new(adjusttitle,Mwin,Bdone,Bcancel,null); zdialog_add_widget(panozd,"hbox","hbim","dialog",0,"space=3"); zdialog_add_widget(panozd,"label","labim","hbim",Bimage,"space=5"); zdialog_add_widget(panozd,"hbox","hbc1","dialog",0,"homog"); zdialog_add_widget(panozd,"label","labred","hbc1",Bred); zdialog_add_widget(panozd,"label","labgreen","hbc1",Bgreen); zdialog_add_widget(panozd,"label","labblue","hbc1",Bblue); zdialog_add_widget(panozd,"hbox","hbc2","dialog",0,"homog"); zdialog_add_widget(panozd,"zspin","red","hbc2","50|200|0.1|100","space=5"); zdialog_add_widget(panozd,"zspin","green","hbc2","50|200|0.1|100","space=5"); zdialog_add_widget(panozd,"zspin","blue","hbc2","50|200|0.1|100","space=5"); zdialog_add_widget(panozd,"hbox","hbbri","dialog",0,"space=3"); zdialog_add_widget(panozd,"label","labbr","hbbri",Bbrightness,"space=5"); zdialog_add_widget(panozd,"zspin","bright","hbbri","50|200|0.1|100"); zdialog_add_widget(panozd,"button","RGBapply","hbbri",Bapply,"space=10"); zdialog_add_widget(panozd,"hsep","hsep","dialog",0,"space=3"); zdialog_add_widget(panozd,"hbox","hbc3","dialog",0,"space=3"); zdialog_add_widget(panozd,"button","auto","hbc3",E2X("auto color"),"space=5"); zdialog_add_widget(panozd,"button","file","hbc3",E2X("file color"),"space=5"); zdialog_add_widget(panozd,"hbox","hbblen","dialog",0,"space=3"); zdialog_add_widget(panozd,"label","labbl","hbblen",Bblendwidth,"space=5"); zdialog_add_widget(panozd,"zspin","blend","hbblen","1|999|1|1"); zdialog_add_widget(panozd,"button","blendapply","hbblen",Bapply,"space=15"); zdialog_add_widget(panozd,"hbox","hbwarp","dialog",0,"space=3"); zdialog_add_widget(panozd,"check","mousewarp","hbwarp",E2X("mouse warp"),"space=3"); zdialog_add_widget(panozd,"hbox","hbflat","dialog",0,"space=3"); zdialog_add_widget(panozd,"label","labflat","hbflat",E2X("flatten image"),"space=3"); zdialog_add_widget(panozd,"zspin","flatten","hbflat","0|1|0.01|0","space=3"); zdialog_add_widget(panozd,"button","flatapply","hbflat",Bapply,"space=15"); for (imx = 0; imx < cimNF; imx++) { // add radio button per image imageN[5] = '0' + imx; zdialog_add_widget(panozd,"radio",imageN,"hbim",0,"space=3"); } zdialog_stuff(panozd,"image0",1); // pre-select 1st image pano_currimage = 0; zdialog_stuff(panozd,"mousewarp",0); // default mouse warp off panStat = -1; // busy status zdialog_run(panozd,vpano_adjust_event,"save"); // run dialog, parallel zdialog_wait(panozd); // wait for dialog completion zdialog_free(panozd); // free dialog return; } // dialog event function int vpano_adjust_event(zdialog *zd, cchar *event) { char imageN[8] = "imageN"; float R, G, B, R1, G1, B1, F; int imS, imx, im1, im2; int ww, hh, px, py, nn; float bright, bright2, *pixel; if (zd->zstat) // dialog complete { if (zd->zstat == 1) panStat = 1; // done else panStat = 0; // cancel freeMouse(); for (imx = 0; imx < cimNF; imx++) // free memory PXM_free(cimPXMwdup[imx]); if (preflatPXM) PXM_free(preflatPXM); preflatPXM = 0; return 1; } 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 (zstrstr("red green blue",event)) { // new RGB value bright = (R + G + B) / 3; zdialog_stuff(zd,"bright",bright); // matching brightness } if (strmatch(event,"bright")) { // brightness change bright2 = (R + G + B) / 3; bright2 = bright / bright2; R = R * bright2; // matching RGB G = G * bright2; B = B * bright2; zdialog_stuff(zd,"red",R); zdialog_stuff(zd,"green",G); zdialog_stuff(zd,"blue",B); } if (strmatch(event,"RGBapply")) // apply color & brightness changes { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } R = R / 100; // normalize 0.5 ... 2.0 G = G / 100; B = B / 100; cim_warp_image_Vpano(imS,0); // refresh cimPXMw from cimPXMs ww = cimPXMw[imS]->ww; hh = cimPXMw[imS]->hh; for (py = 0; py < hh; py++) // loop all image pixels for (px = 0; px < ww; px++) { pixel = PXMpix(cimPXMw[imS],px,py); R1 = R * pixel[0]; // apply color factors G1 = G * pixel[1]; B1 = B * pixel[2]; if (R1 > 255.9 || G1 > 255.9 || B1 > 255.9) { bright = R1; // avoid overflow if (G1 > bright) bright = G1; if (B1 > bright) bright = B1; bright = 255.9 / bright; R1 = R1 * bright; G1 = G1 * bright; B1 = B1 * bright; } pixel[0] = R1; pixel[1] = G1; pixel[2] = B1; } cimBlend = 1; zdialog_stuff(zd,"blend",cimBlend); cim_show_Vimages(0,0); // combine and show with 50/50 blend } if (strmatch(event,"auto")) // auto match color of selected image { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } for (im1 = imS; im1 < cimNF-1; im1++) // from selected image to last image { im2 = im1 + 1; cimBlend = 0.3 * cimPXMw[im2]->ww; cim_get_overlap(im1,im2,cimPXMw); // match images in overlap area cim_match_colors(im1,im2,cimPXMw); cim_adjust_colors_vpano(cimPXMw[im1],1); // image im1 << profile 1 cim_adjust_colors_vpano(cimPXMw[im2],2); // image im2 << profile 2 for (imx = im1-1; imx >= imS; imx--) cim_adjust_colors_vpano(cimPXMw[imx],1); } for (im1 = imS-1; im1 >= 0; im1--) // from selected image to 1st image { im2 = im1 + 1; cimBlend = 0.3 * cimPXMw[im2]->ww; cim_get_overlap(im1,im2,cimPXMw); // match images in overlap area cim_match_colors(im1,im2,cimPXMw); cim_adjust_colors_vpano(cimPXMw[im1],1); // image im1 << profile 1 cim_adjust_colors_vpano(cimPXMw[im2],2); // image im2 << profile 2 for (imx = im2+1; imx < cimNF; imx++) cim_adjust_colors_vpano(cimPXMw[imx],2); } cimBlend = 1; zdialog_stuff(zd,"blend",cimBlend); cim_show_Vimages(0,0); } if (strmatch(event,"file")) // use original file colors { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } if (! cim_load_files()) return 1; for (imx = 0; imx < cimNF; imx++) { PXM_free(cimPXMs[imx]); cimPXMs[imx] = PXM_copy(cimPXMf[imx]); cim_curve_Vimage(imx); // curve and warp cim_warp_image_Vpano(imx,0); PXM_free(cimPXMwdup[imx]); cimPXMwdup[imx] = PXM_copy(cimPXMw[imx]); panowarp_dowarps(imx); // re-apply mouse warps } cimBlend = 1; zdialog_stuff(zd,"blend",cimBlend); cim_show_Vimages(0,0); } if (strmatch(event,"blendapply")) // apply new blend width { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } zdialog_fetch(zd,"blend",cimBlend); // can be zero cim_show_Vimages(0,1); // show with gradual blend } if (strmatch(event,"mousewarp")) // engage mouse warp/align { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } cimBlend = 1; zdialog_stuff(zd,"blend",cimBlend); cim_show_Vimages(0,0); zdialog_fetch(zd,"mousewarp",pano_mousewarp); if (pano_mousewarp) takeMouse(panowarp_mousefunc,dragcursor); // connect mouse function else freeMouse(); } if (strmatch(event,"flatapply")) { if (preflatPXM) { PXM_free(E3pxm); E3pxm = PXM_copy(preflatPXM); } else preflatPXM = PXM_copy(E3pxm); zdialog_fetch(zd,"flatten",F); cim_flatten_Vimage(F); Fpaint2(); } return 1; } /********************************************************************************/ // make a multi-row panorama via Panorama Tools package (Hugin version) void m_pano_PT(GtkWidget *, cchar *) { int im, ii, err; char file1[100], *file2, *pp; char cfolder[XFCC]; FILE *fid; struct stat statb; F1_help_topic = "PT panorama"; // help topic if (FGWM != 'F' && FGWM != 'G') return; // 19.0 cimNF = 0; if (! PTtools) { zmessageACK(Mwin,E2X("pano tools (hugin) not installed")); return; } gallery_select_clear(); // clear selected file list gallery_select(); // get new list if (GScount == 0) goto cleanup; if (GScount < 2) { zmessageACK(Mwin,E2X("Select at least 2 files")); goto cleanup; } cimNF = GScount; if (checkpend("all")) return; Fblock = 1; pp = getcwd(cfolder,XFCC); // save curr. folder err = chdir(temp_folder); // use /tmp/fotoxx-xxxxx if (err) { zmessageACK(Mwin,strerror(errno)); goto cleanup; } fid = fopen("PTscript","w"); // build pano tools script if (! fid) { zmessageACK(Mwin,strerror(errno)); goto cleanup; } fprintf(fid,"%s","pto_gen -o PTpano.pto "); // create a project file for (im = 0; im < cimNF; im++) // for member image files fprintf(fid," \"%s\" ",GSfiles[im]); fprintf(fid,"\n"); fprintf(fid,"cpfind --celeste --multirow -o PTpano.pto PTpano.pto \n"); // find control points fprintf(fid,"cpclean -o PTpano.pto PTpano.pto \n"); // remove flakey ones fprintf(fid,"autooptimiser -a -m -l -s -o PTpano.pto PTpano.pto \n"); // optimize colors etc. fprintf(fid,"pano_modify --crop=AUTO -o PTpano.pto PTpano.pto \n"); // cut off black margins fprintf(fid,"hugin_executor --prefix=PTpano --stitching PTpano.pto \n"); // execute script err = fclose(fid); if (err) { zmessageACK(Mwin,strerror(errno)); goto cleanup; } chmod("PTscript",0760); // run the script popup_command("./PTscript",600,400,Mwin,0); snprintf(file1,100,"%s/PTpano.tif",temp_folder); // look for output file err = stat(file1,&statb); if (err) goto cleanup; im = 0; for (ii = 1; ii < cimNF; ii++) // get alphabetically last input file if (strcmp(GSfiles[ii],GSfiles[im]) > 0) im = ii; file2 = zstrdup(GSfiles[im],12); // make fotoxx output file pp = strrchr(file2,'.'); // = < last input file > -PT.tif if (! pp) pp = file2; strcpy(pp,"-PT.tif"); err = copyFile(file1,file2); // copy /tmp/.../PTpano.tif if (err) goto cleanup; // to fotoxx output file f_open(file2,0,0,1,0); // and open it as curr. file m_viewmode(0,"F"); cleanup: err = chdir(cfolder); // restore curr. folder Fblock = 0; return; } /********************************************************************************/ // load image files into pixmaps cimPXMf[*] and check for errors // returns 0 if error int cim_load_files() { PXM *pxm; int imx; for (imx = 0; imx < cimNF; imx++) { PXM_free(cimPXMf[imx]); pxm = PXM_load(cimFile[imx],1); // ACK errors if (! pxm) return 0; PXM_addalpha(pxm); cimPXMf[imx] = pxm; } cimLF = 0; // find alphabetically last file for (imx = 1; imx < cimNF; imx++) if (strcmp(cimFile[imx],cimFile[cimLF]) > 0) cimLF = imx; return 1; } // scale image from full size to cimScale (normally < 1.0) void cim_scale_image(int im, PXM** pxmout) { int ww, hh; ww = cimScale * cimPXMf[im]->ww; hh = cimScale * cimPXMf[im]->hh; PXM_free(pxmout[im]); pxmout[im] = PXM_rescale(cimPXMf[im],ww,hh); return; } // get overlap area for a pair of images im1 and im2 // outputs are coordinates of overlap area in im1 and in im2 // returns overlap width as fraction of image width <= 1.0 float cim_get_overlap(int im1, int im2, PXM **pxmx) { float x1, y1, t1, x2, y2, t2; float xoff, yoff, toff, costf, sintf; int ww1, ww2, hh1, hh2, pxM; PXM *pxm1, *pxm2; x1 = cimOffs[im1].xf; // im1, im2 absolute offsets y1 = cimOffs[im1].yf; t1 = cimOffs[im1].tf; x2 = cimOffs[im2].xf; y2 = cimOffs[im2].yf; t2 = cimOffs[im2].tf; xoff = (x2 - x1) * cosf(t1) + (y2 - y1) * sinf(t1); // offset of im2 relative to im1 yoff = (y2 - y1) * cosf(t1) - (x2 - x1) * sinf(t1); toff = t2 - t1; costf = cosf(toff); sintf = sinf(toff); pxm1 = pxmx[im1]; pxm2 = pxmx[im2]; ww1 = pxm1->ww; hh1 = pxm1->hh; ww2 = pxm2->ww; hh2 = pxm2->hh; cimOv1xlo = 0; // lowest x overlap if (xoff > 0) cimOv1xlo = xoff; cimOv1xhi = ww1-1; // highest x overlap if (cimOv1xhi > xoff + ww2-1) cimOv1xhi = xoff + ww2-1; cimOv1ylo = 0; // lowest y overlap if (yoff > 0) cimOv1ylo = yoff; cimOv1yhi = hh1-1; // highest y overlap if (cimOv1yhi > yoff + hh2-1) cimOv1yhi = yoff + hh2-1; if (toff < 0) cimOv1xlo -= toff * (cimOv1yhi - cimOv1ylo); // reduce for theta offset if (toff < 0) cimOv1yhi += toff * (cimOv1xhi - cimOv1xlo); if (toff > 0) cimOv1xhi -= toff * (cimOv1yhi - cimOv1ylo); if (toff > 0) cimOv1ylo += toff * (cimOv1xhi - cimOv1xlo); if (cimPanoV) { if (cimBlend && cimBlend < (cimOv1yhi - cimOv1ylo)) { // reduce y range to cimBlend pxM = (cimOv1yhi + cimOv1ylo) / 2; cimOv1ylo = pxM - cimBlend / 2; cimOv1yhi = pxM + cimBlend / 2; } } else { if (cimBlend && cimBlend < (cimOv1xhi - cimOv1xlo)) { // reduce x range to cimBlend pxM = (cimOv1xhi + cimOv1xlo) / 2; cimOv1xlo = pxM - cimBlend / 2; cimOv1xhi = pxM + cimBlend / 2; } } cimOv2xlo = costf * (cimOv1xlo - xoff) + sintf * (cimOv1ylo - yoff); // overlap area in im2 coordinates cimOv2xhi = costf * (cimOv1xhi - xoff) + sintf * (cimOv1yhi - yoff); cimOv2ylo = costf * (cimOv1ylo - yoff) + sintf * (cimOv1xlo - xoff); cimOv2yhi = costf * (cimOv1yhi - yoff) + sintf * (cimOv1xhi - xoff); if (cimOv1xlo < 0) cimOv1xlo = 0; // take care of limits if (cimOv1ylo < 0) cimOv1ylo = 0; if (cimOv2xlo < 0) cimOv2xlo = 0; if (cimOv2ylo < 0) cimOv2ylo = 0; if (cimOv1xhi > ww1-1) cimOv1xhi = ww1-1; if (cimOv1yhi > hh1-1) cimOv1yhi = hh1-1; if (cimOv2xhi > ww2-1) cimOv2xhi = ww2-1; if (cimOv2yhi > hh2-1) cimOv2yhi = hh2-1; if (cimPanoV) return 1.0 * (cimOv1yhi - cimOv1ylo) / hh1; // return overlap height <= 1.0 else return 1.0 * (cimOv1xhi - cimOv1xlo) / ww1; // return overlap width <= 1.0 } // Get the RGB brightness distribution in the overlap area for each image. // Compute matching factors to compare pixels within the overlap area. // compare cimRGBmf1[rgb][pix1[rgb]] to cimRGBmf2[rgb][pix2[rgb]]. void cim_match_colors(int im1, int im2, PXM **pxmx) { float Bratios1[3][256]; // image2/image1 brightness ratio per color per level float Bratios2[3][256]; // image1/image2 brightness ratio per color per level float *pix1, vpix2[4]; int vstat2, px1, py1; int ii, jj, rgb; int npix, npix1, npix2, npix3; int brdist1[3][256], brdist2[3][256]; float x1, y1, t1, x2, y2, t2; float xoff, yoff, toff, costf, sintf; float px2, py2; float brlev1[3][256], brlev2[3][256]; float a1, a2, b1, b2, bratio = 1; PXM *pxm1, *pxm2; pxm1 = pxmx[im1]; pxm2 = pxmx[im2]; x1 = cimOffs[im1].xf; // im1, im2 absolute offsets y1 = cimOffs[im1].yf; t1 = cimOffs[im1].tf; x2 = cimOffs[im2].xf; y2 = cimOffs[im2].yf; t2 = cimOffs[im2].tf; xoff = (x2 - x1) * cosf(t1) + (y2 - y1) * sinf(t1); // offset of im2 relative to im1 yoff = (y2 - y1) * cosf(t1) - (x2 - x1) * sinf(t1); toff = t2 - t1; costf = cosf(toff); sintf = sinf(toff); for (rgb = 0; rgb < 3; rgb++) // clear distributions for (ii = 0; ii < 256; ii++) brdist1[rgb][ii] = brdist2[rgb][ii] = 0; npix = 0; for (py1 = cimOv1ylo; py1 < cimOv1yhi; py1++) // loop overlapped rows for (px1 = cimOv1xlo; px1 < cimOv1xhi; px1++) // loop overlapped columns { pix1 = PXMpix(pxm1,px1,py1); // image1 pixel if (pix1[3] < 254) continue; // ignore void pixels px2 = costf * (px1 - xoff) + sintf * (py1 - yoff); // corresponding image2 pixel py2 = costf * (py1 - yoff) - sintf * (px1 - xoff); vstat2 = vpixel(pxm2,px2,py2,vpix2); if (! vstat2) continue; // does not exist if (vpix2[3] < 254) continue; // ignore void pixels ++npix; // count overlapping pixels for (rgb = 0; rgb < 3; rgb++) // accumulate distributions { // by color in 256 bins ++brdist1[rgb][int(pix1[rgb])]; ++brdist2[rgb][int(vpix2[rgb])]; } } npix1 = npix / 256; // 1/256th of total pixels for (rgb = 0; rgb < 3; rgb++) // get brlev1[rgb][N] = mean bright for (ii = jj = 0; jj < 256; jj++) // for Nth group of image1 pixels { // for color rgb brlev1[rgb][jj] = 0; npix2 = npix1; // 1/256th of total pixels while (npix2 > 0 && ii < 256) // next 1/256th group from distr. { npix3 = brdist1[rgb][ii]; if (npix3 == 0) { ++ii; continue; } if (npix3 > npix2) npix3 = npix2; brlev1[rgb][jj] += ii * npix3; // brightness * (pixels with) brdist1[rgb][ii] -= npix3; npix2 -= npix3; } brlev1[rgb][jj] = brlev1[rgb][jj] / npix1; // mean brightness for group, 0-255 } for (rgb = 0; rgb < 3; rgb++) // do same for image2 for (ii = jj = 0; jj < 256; jj++) { brlev2[rgb][jj] = 0; npix2 = npix1; while (npix2 > 0 && ii < 256) { npix3 = brdist2[rgb][ii]; if (npix3 == 0) { ++ii; continue; } if (npix3 > npix2) npix3 = npix2; brlev2[rgb][jj] += ii * npix3; brdist2[rgb][ii] -= npix3; npix2 -= npix3; } brlev2[rgb][jj] = brlev2[rgb][jj] / npix1; } for (rgb = 0; rgb < 3; rgb++) // color for (ii = jj = 0; ii < 256; ii++) // brlev1 brightness, 0 to 255 { if (ii == 0) bratio = 1; while (jj < 256 && ii > brlev2[rgb][jj]) jj++; // find matching brlev2 brightness if (jj == 256) jj = 255; 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 (jj < 256 && ii > brlev1[rgb][jj]) jj++; // find matching brlev1 brightness if (jj == 256) jj = 255; a2 = brlev1[rgb][jj]; // next higher value b2 = brlev2[rgb][jj]; if (a2 > 0 && b2 > 0) { if (jj > 0) { a1 = brlev1[rgb][jj-1]; // next lower value b1 = brlev2[rgb][jj-1]; } else a1 = b1 = 0; if (ii == 0) bratio = b2 / a2; else bratio = (b1 + (ii-a1)/(a2-a1) * (b2-b1)) / ii; // interpolate } if (bratio < 0.2) bratio = 0.2; // contain outliers if (bratio > 5) bratio = 5; Bratios1[rgb][ii] = bratio; } for (ii = 0; ii < 256; ii++) // convert brightness ratios for (rgb = 0; rgb < 3; rgb++) // to conversion factors { cimRGBmf1[rgb][ii] = sqrt(Bratios1[rgb][ii]) * ii; // use sqrt(ratio) so that adjustment cimRGBmf2[rgb][ii] = sqrt(Bratios2[rgb][ii]) * ii; // can be applied to both images } return; } // Use color match data from cim_match_colors() to // modify images so the colors match. void cim_adjust_colors(PXM *pxm, int fwhich) { int ww, hh, px, py; float red, green, blue, max; float *pix; float ff; ww = pxm->ww; hh = pxm->hh; for (py = 0; py < hh; py++) for (px = 0; px < ww; px++) { pix = PXMpix(pxm,px,py); red = pix[0]; green = pix[1]; blue = pix[2]; if (! blue) continue; if (fwhich == 1) { red = cimRGBmf1[0][int(red)]; green = cimRGBmf1[1][int(green)]; blue = cimRGBmf1[2][int(blue)]; } else { red = cimRGBmf2[0][int(red)]; green = cimRGBmf2[1][int(green)]; blue = cimRGBmf2[2][int(blue)]; } if (red > 255.9 || green > 255.9 || blue > 255.9) { max = red; if (green > max) max = green; if (blue > max) max = blue; ff = 255.9 / max; red = red * ff; green = green * ff; blue = blue * ff; } pix[0] = red; pix[1] = green; pix[2] = blue; } return; } // Use color match data from cim_match_colors() to // modify images so the colors match. // fwhich = 1/2 = left/right image void cim_adjust_colors_pano(PXM *pxm, int fwhich) { int ww, hh, ww2, px, py; int pxlo, pxhi; float red1, green1, blue1, max; float red2, green2, blue2; float *pix; float f1, f2; ww = pxm->ww; hh = pxm->hh; ww2 = ww / 2; if (fwhich == 1) { // scan half the image pxlo = ww2; pxhi = ww; } else { pxlo = 0; pxhi = ww2; } for (py = 0; py < hh; py++) for (px = pxlo; px < pxhi; px++) { pix = PXMpix(pxm,px,py); red1 = pix[0]; green1 = pix[1]; blue1 = pix[2]; if (! blue1) continue; if (fwhich == 1) { red2 = cimRGBmf1[0][int(red1)]; green2 = cimRGBmf1[1][int(green1)]; blue2 = cimRGBmf1[2][int(blue1)]; } else { red2 = cimRGBmf2[0][int(red1)]; green2 = cimRGBmf2[1][int(green1)]; blue2 = cimRGBmf2[2][int(blue1)]; } if (fwhich == 1) { // taper color adjustment over distance f2 = 1.0 * (px - pxlo) / ww2; f1 = 1.0 - f2; } else { f1 = 1.0 * px / ww2; f2 = 1.0 - f1; } red2 = f2 * red2 + f1 * red1; green2 = f2 * green2 + f1 * green1; blue2 = f2 * blue2 + f1 * blue1; if (red2 > 255.9 || green2 > 255.9 || blue2 > 255.9) { max = red2; if (green2 > max) max = green2; if (blue2 > max) max = blue2; f1 = 255.9 / max; red2 = red2 * f1; green2 = green2 * f1; blue2 = blue2 * f1; } pix[0] = red2; pix[1] = green2; pix[2] = blue2; } return; } // version for vertical panoramas // fwhich = 1/2 = upper/lower image void cim_adjust_colors_vpano(PXM *pxm, int fwhich) { int ww, hh, hh2, px, py; int pylo, pyhi; float red1, green1, blue1, max; float red2, green2, blue2; float *pix; float f1, f2; ww = pxm->ww; hh = pxm->hh; hh2 = hh / 2; if (fwhich == 1) { // scan half the image pylo = hh2; pyhi = hh; } else { pylo = 0; pyhi = hh2; } for (py = pylo; py < pyhi; py++) for (px = 0; px < ww; px++) { pix = PXMpix(pxm,px,py); red1 = pix[0]; green1 = pix[1]; blue1 = pix[2]; if (! blue1) continue; if (fwhich == 1) { red2 = cimRGBmf1[0][int(red1)]; green2 = cimRGBmf1[1][int(green1)]; blue2 = cimRGBmf1[2][int(blue1)]; } else { red2 = cimRGBmf2[0][int(red1)]; green2 = cimRGBmf2[1][int(green1)]; blue2 = cimRGBmf2[2][int(blue1)]; } if (fwhich == 1) { // taper color adjustment over distance f2 = 1.0 * (py - pylo) / hh2; f1 = 1.0 - f2; } else { f1 = 1.0 * py / hh2; f2 = 1.0 - f1; } red2 = f2 * red2 + f1 * red1; green2 = f2 * green2 + f1 * green1; blue2 = f2 * blue2 + f1 * blue1; if (red2 > 255.9 || green2 > 255.9 || blue2 > 255.9) { max = red2; if (green2 > max) max = green2; if (blue2 > max) max = blue2; f1 = 255.9 / max; red2 = red2 * f1; green2 = green2 * f1; blue2 = blue2 * f1; } pix[0] = red2; pix[1] = green2; pix[2] = blue2; } return; } // find pixels of greatest contrast within overlap area // flag high-contrast pixels to use in each image compare region void cim_get_redpix(int im1) { int ww, hh, samp, xzone, yzone; int pxL, pxH, pyL, pyH; int px, py, ii, jj, npix; int ov1xlo, ov1xhi, ov1ylo, ov1yhi; int Hdist[256], Vdist[256], Hmin, Vmin; float zsamp[16] = { 5,5,5,5, 7,8,8,7, 7,8,8,7, 5,5,5,5 }; // % sample per zone, sum = 100 uchar *Hcon, *Vcon; float *pix1, *pix2; PXM *pxm; pxm = cimPXMs[im1]; ww = pxm->ww; hh = pxm->hh; if (cimRedpix) zfree(cimRedpix); // clear prior cimRedpix = (char *) zmalloc(ww*hh); memset(cimRedpix,0,ww*hh); cimRedImage = im1; // image with red pixels ov1xlo = cimOv1xlo + cimSearchRange; // stay within x/y search range ov1xhi = cimOv1xhi - cimSearchRange; // so that red pixels persist ov1ylo = cimOv1ylo + cimSearchRange; // over offset changes ov1yhi = cimOv1yhi - cimSearchRange; for (yzone = 0; yzone < 4; yzone++) // loop 16 zones for (xzone = 0; xzone < 4; xzone++) { pxL = ov1xlo + 0.25 * xzone * (ov1xhi - ov1xlo); // px and py zone limits pxH = ov1xlo + 0.25 * (xzone+1) * (ov1xhi - ov1xlo); pyL = ov1ylo + 0.25 * yzone * (ov1yhi - ov1ylo); pyH = ov1ylo + 0.25 * (yzone+1) * (ov1yhi - ov1ylo); npix = (pxH - pxL) * (pyH - pyL); // zone pixels if (npix < 1) continue; // bug fix Hcon = (uchar *) zmalloc(npix); // horizontal pixel contrast 0-255 Vcon = (uchar *) zmalloc(npix); // vertical pixel contrast 0-255 ii = 4 * yzone + xzone; samp = cimSampSize * 0.01 * zsamp[ii]; // sample size for zone if (samp > 0.1 * npix) samp = 0.1 * npix; // limit to 10% of zone pixels for (py = pyL; py < pyH; py++) // scan image pixels in zone for (px = pxL; px < pxH; px++) { ii = (py-pyL) * (pxH-pxL) + (px-pxL); Hcon[ii] = Vcon[ii] = 0; // horiz. = vert. contrast = 0 if (py < 8 || py > hh-9) continue; // keep away from image edges if (px < 8 || px > ww-9) continue; pix1 = PXMpix(pxm,px,py-6); // verify not near void areas if (pix1[3] < 254) continue; // avoid void pixels pix1 = PXMpix(pxm,px+6,py); if (pix1[3] < 254) continue; pix1 = PXMpix(pxm,px,py+6); if (pix1[3] < 254) continue; pix1 = PXMpix(pxm,px-6,py); if (pix1[3] < 254) continue; pix1 = PXMpix(pxm,px,py); // candidate red pixel pix2 = PXMpix(pxm,px+2,py); // 2 pixels to right Hcon[ii] = 255 * (1.0 - PIXMATCH(pix1,pix2)); // horz. contrast 0-255 pix2 = PXMpix(pxm,px,py+2); // 2 pixels below Vcon[ii] = 255 * (1.0 - PIXMATCH(pix1,pix2)); // vert. contrast } for (ii = 0; ii < 256; ii++) // clear contrast distributions Hdist[ii] = Vdist[ii] = 0; for (py = pyL; py < pyH; py++) // scan image pixels for (px = pxL; px < pxH; px++) { // build contrast distributions ii = (py-pyL) * (pxH-pxL) + (px-pxL); ++Hdist[Hcon[ii]]; ++Vdist[Vcon[ii]]; } for (npix = 0, ii = 255; ii > 0; ii--) // find minimum contrast needed to get { // enough pixels for sample size npix += Hdist[ii]; // (horizontal contrast pixels) if (npix > samp) break; } Hmin = ii; for (npix = 0, ii = 255; ii > 0; ii--) // (vertical contrast pixels) { npix += Vdist[ii]; if (npix > samp) break; } Vmin = ii; for (py = pyL; py < pyH; py++) // scan zone pixels for (px = pxL; px < pxH; px++) { ii = (py-pyL) * (pxH-pxL) + (px-pxL); jj = py * ww + px; if (Hcon[ii] + Vcon[ii] < 20) continue; // ignore low contrast pixels if (Hcon[ii] > Hmin) cimRedpix[jj] = 1; // flag pixels above min. contrast if (Vcon[ii] > Vmin) cimRedpix[jj] = 1; } zfree(Hcon); zfree(Vcon); for (py = pyL; py < pyH; py++) // scan zone pixels for (px = pxL; px < pxH; px++) { ii = (py-pyL) * (pxH-pxL) + (px-pxL); jj = py * ww + px; if (! cimRedpix[jj]) continue; npix = cimRedpix[jj-1] + cimRedpix[jj+1]; // eliminate flagged pixels with no npix += cimRedpix[jj-ww] + cimRedpix[jj+ww]; // neighboring flagged pixels npix += cimRedpix[jj-ww-1] + cimRedpix[jj+ww-1]; npix += cimRedpix[jj-ww+1] + cimRedpix[jj+ww+1]; if (npix < 2) cimRedpix[jj] = 0; } for (py = pyL; py < pyH; py++) // scan zone pixels for (px = pxL; px < pxH; px++) { ii = (py-pyL) * (pxH-pxL) + (px-pxL); jj = py * ww + px; if (cimRedpix[jj] == 1) { // flag horizontal group of 3 cimRedpix[jj+1] = 2; cimRedpix[jj+2] = 2; cimRedpix[jj+ww] = 2; // and vertical group of 3 cimRedpix[jj+2*ww] = 2; } } } return; } // curve image based on lens mm (pano) // replaces cimPXMs[im] with curved version void cim_curve_image(int im) // overhauled { int px, py, ww, hh, vstat; float ww2, hh2; float dx, dy; float sx, sy; float tx, ty; float F = lens_mm; // lens focal length, 35mm equivalent float S = 35.0; // corresponding image width PXM *pxmin, *pxmout; float vpix[4], *pix; if (cimPanoNC) return; // no-curve flag pxmin = cimPXMs[im]; // input and output image ww = pxmin->ww; hh = pxmin->hh; ww2 = ww / 2; hh2 = hh / 2; if (ww > hh) F = F / S * ww; else F = F / S * hh; pxmout = PXM_make(ww,hh,4); // temp. output PXM for (py = 0; py < hh; py++) // cylindrical projection for (px = 0; px < ww; px++) { sx = px - ww2; sy = py - hh2; tx = sx / F; ty = sy / F; dx = F * tanf(tx); dy = F * tanf(ty) / cosf(tx); dx += ww2; dy += hh2; vstat = vpixel(pxmin,dx,dy,vpix); // input virtual pixel pix = PXMpix(pxmout,px,py); // output real pixel if (vstat) memcpy(pix,vpix,pixcc); else memset(pix,0,pixcc); // voided pixels are (0,0,0,0) } PXM_free(pxmin); // replace input with output PXM cimPXMs[im] = pxmout; cimPanoFL = F; // save focal length globally return; } // version for vertical panorama void cim_curve_Vimage(int im) { int px, py, ww, hh, vstat; float ww2, hh2; float dx, dy; float sx, sy; float tx, ty; float F = lens_mm; float S = 35.0; PXM *pxmin, *pxmout; float vpix[4], *pix; if (cimPanoNC) return; pxmin = cimPXMs[im]; ww = pxmin->ww; hh = pxmin->hh; ww2 = ww / 2; hh2 = hh / 2; if (ww > hh) F = F / S * ww; else F = F / S * hh; pxmout = PXM_make(ww,hh,4); for (py = 0; py < hh; py++) for (px = 0; px < ww; px++) { sx = px - ww2; sy = py - hh2; tx = sx / F; ty = sy / F; dy = F * tanf(ty); // these two lines dx = F * tanf(tx) / cosf(ty); // are different dx += ww2; dy += hh2; vstat = vpixel(pxmin,dx,dy,vpix); pix = PXMpix(pxmout,px,py); if (vstat) memcpy(pix,vpix,pixcc); else memset(pix,0,pixcc); // voided pixel } PXM_free(pxmin); cimPXMs[im] = pxmout; cimPanoFL = F; // save focal length globally return; } // Warp 4 image corners according to cimOffs[im].wx[ii] and .wy[ii] // corner = 0 = NW, 1 = NE, 2 = SE, 3 = SW // 4 corners move by these pixel amounts and center does not move. // input: cimPXMs[im] (flat or curved) output: cimPXMw[im] namespace cim_warp_image_names { PXM *pxmin, *pxmout; float ww, hh, wwi, hhi; float wx0, wy0, wx1, wy1, wx2, wy2, wx3, wy3; } void cim_warp_image(int im) // caller function { using namespace cim_warp_image_names; void * cim_warp_image_wthread(void *arg); pxmin = cimPXMs[im]; // input and output pixmaps pxmout = cimPXMw[im]; PXM_free(pxmout); pxmout = PXM_copy(pxmin); cimPXMw[im] = pxmout; ww = pxmin->ww; hh = pxmin->hh; wwi = 1.0 / ww; hhi = 1.0 / hh; wx0 = cimOffs[im].wx[0]; // corner warps wy0 = cimOffs[im].wy[0]; wx1 = cimOffs[im].wx[1]; wy1 = cimOffs[im].wy[1]; wx2 = cimOffs[im].wx[2]; wy2 = cimOffs[im].wy[2]; wx3 = cimOffs[im].wx[3]; wy3 = cimOffs[im].wy[3]; do_wthreads(cim_warp_image_wthread,NWT); // worker threads 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 } pthread_exit(0); } // 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; } // 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 cim_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; } // 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 (cim_sigdiff(match,matchB,0.00001) > 0) { matchB = match; // save best match offsetsB = cimOffs[im2]; } snprintf(paneltext,200,"align: %d match: %.5f",cimNsearch++,matchB); zmainloop(); if (Fescape) goto finish; // user kill 19.0 } cimOffs[im2] = offsetsB; // restore best match 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 (cim_sigdiff(match,matchB,0.00001) > 0) { matchB = match; // save best match offsetsB = cimOffs[im2]; } snprintf(paneltext,200,"warp: %d match: %.5f",cimNsearch++,matchB); zmainloop(); if (Fescape) goto finish; // user kill 19.0 } 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); } } finish: Ffuncbusy = 0; return; } // Compare two images in overlapping areas. // Use the high-contrast pixels from cim_get_redpix() // return: 1 = perfect match, 0 = total mismatch (black/white) // cimPXMs[im1] is matched to cimPXMs[im2] + virtual corner warps float cim_match_images(int im1, int im2) { float *pix1, vpix2[4]; int ww, hh, ww2, hh2; int px1, py1, ii, vstat; float wwi, hhi, ww2i, hh2i, xdisp, ydisp; float wx0, wy0, wx1, wy1, wx2, wy2, wx3, wy3; float dx, dy, px2, py2; float x1, y1, t1, x2, y2, t2; float xoff, yoff, toff, costf, sintf, coeff; float match, cmatch, maxcmatch; PXM *pxm1, *pxm2; x1 = cimOffs[im1].xf; // im1, im2 absolute offsets y1 = cimOffs[im1].yf; t1 = cimOffs[im1].tf; x2 = cimOffs[im2].xf; y2 = cimOffs[im2].yf; t2 = cimOffs[im2].tf; xoff = (x2 - x1) * cosf(t1) + (y2 - y1) * sinf(t1); // offset of im2 relative to im1 yoff = (y2 - y1) * cosf(t1) - (x2 - x1) * sinf(t1); toff = t2 - t1; costf = cosf(toff); sintf = sinf(toff); wx0 = cimOffs[im2].wx[0]; // im2 corner warps wy0 = cimOffs[im2].wy[0]; wx1 = cimOffs[im2].wx[1]; wy1 = cimOffs[im2].wy[1]; wx2 = cimOffs[im2].wx[2]; wy2 = cimOffs[im2].wy[2]; wx3 = cimOffs[im2].wx[3]; wy3 = cimOffs[im2].wy[3]; pxm1 = cimPXMs[im1]; // base image pxm2 = cimPXMs[im2]; // comparison image (virtual warps) ww = pxm1->ww; hh = pxm1->hh; ww2 = ww / 2; hh2 = hh / 2; wwi = 1.0 / ww; hhi = 1.0 / hh; ww2i = 1.0 / ww2; hh2i = 1.0 / hh2; cmatch = 0; maxcmatch = 1; if (cimPano) { for (py1 = cimOv1ylo; py1 < cimOv1yhi; py1++) // loop overlapping pixels for (px1 = cimOv1xlo; px1 < cimOv1xhi; px1++) { ii = py1 * ww + px1; // skip low-contrast pixels if (! cimRedpix[ii]) continue; pix1 = PXMpix(pxm1,px1,py1); // image1 pixel if (pix1[3] < 254) continue; // ignore void pixels px2 = costf * (px1 - xoff) + sintf * (py1 - yoff); // corresponding image2 pixel py2 = costf * (py1 - yoff) - sintf * (px1 - xoff); dx = dy = 0.0; // corner warp xdisp = (px2 - ww2) * ww2i; // -1 ... 0 ... +1 ydisp = (py2 - hh2) * hh2i; if (xdisp > 0) // right half, no warp dx = dy = 0; else if (ydisp < 0) { // use NW corner warp dx = wx0 * xdisp * ydisp; dy = wy0 * xdisp * ydisp; } else { // use SW corner warp dx = wx3 * xdisp * ydisp; dy = wy3 * xdisp * ydisp; } px2 += dx; // source pixel location py2 += dy; // after corner warps vstat = vpixel(pxm2,px2,py2,vpix2); // get virtual pixel if (! vstat) continue; match = PIXMATCH(pix1,vpix2); // compare, brightness adjusted cmatch += match * match; // accumulate total match maxcmatch += 1.0; } } else if (cimPanoV) { for (py1 = cimOv1ylo; py1 < cimOv1yhi; py1++) // loop overlapping pixels for (px1 = cimOv1xlo; px1 < cimOv1xhi; px1++) { ii = py1 * ww + px1; // skip low-contrast pixels if (! cimRedpix[ii]) continue; pix1 = PXMpix(pxm1,px1,py1); // image1 pixel if (pix1[3] < 254) continue; // ignore void pixels px2 = costf * (px1 - xoff) + sintf * (py1 - yoff); // corresponding image2 pixel py2 = costf * (py1 - yoff) - sintf * (px1 - xoff); dx = dy = 0.0; // corner warp xdisp = (px2 - ww2) * ww2i; // -1 ... 0 ... +1 ydisp = (py2 - hh2) * hh2i; if (ydisp > 0) // bottom half, no warp dx = dy = 0; else if (xdisp < 0) { // use NW corner warp dx = wx0 * xdisp * ydisp; dy = wy0 * xdisp * ydisp; } else { // use NE corner warp dx = wx1 * xdisp * ydisp; dy = wy1 * xdisp * ydisp; } px2 += dx; // source pixel location py2 += dy; // after corner warps vstat = vpixel(pxm2,px2,py2,vpix2); if (! vstat) continue; match = PIXMATCH(pix1,vpix2); // compare brightness adjusted cmatch += match * match; // accumulate total match maxcmatch += 1.0; } } else { for (py1 = cimOv1ylo; py1 < cimOv1yhi; py1++) // loop overlapping pixels for (px1 = cimOv1xlo; px1 < cimOv1xhi; px1++) { ii = py1 * ww + px1; // skip low-contrast pixels if (! cimRedpix[ii]) continue; pix1 = PXMpix(pxm1,px1,py1); // image1 pixel if (pix1[3] < 254) continue; // ignore void pixels px2 = costf * (px1 - xoff) + sintf * (py1 - yoff); // corresponding image2 pixel py2 = costf * (py1 - yoff) - sintf * (px1 - xoff); dx = dy = 0.0; // corner warp coeff = (1.0 - py2 * hhi - px2 * wwi); // corner 0 NW if (coeff > 0) { dx += coeff * wx0; dy += coeff * wy0; } coeff = (1.0 - py2 * hhi - (ww - px2) * wwi); // corner 1 NE if (coeff > 0) { dx += coeff * wx1; dy += coeff * wy1; } coeff = (1.0 - (hh - py2) * hhi - (ww - px2) * wwi); // corner 2 SE if (coeff > 0) { dx += coeff * wx2; dy += coeff * wy2; } coeff = (1.0 - (hh - py2) * hhi - px2 * wwi); // corner 3 SW if (coeff > 0) { dx += coeff * wx3; dy += coeff * wy3; } px2 += dx; // source pixel location py2 += dy; // after corner warps vstat = vpixel(pxm2,px2,py2,vpix2); if (! vstat) continue; match = PIXMATCH(pix1,vpix2); // compare brightness adjusted cmatch += match * match; // accumulate total match maxcmatch += 1.0; } } return cmatch / maxcmatch; } // combine and show all images // used for all composite image functions except Vpano. // fnew >> make new E3 output image and adjust x and y offsets // cimPXMw[*] >> E3pxm >> main window // fblend: 0 > 50/50 blend, 1 > gradual blend // CALLED FROM THREADS as well as from main() namespace cim_show_images_names { int im1, im2, iminc, fblendd; int wwlo[10], wwhi[10]; int hhlo[10], hhhi[10]; float costf[10], sintf[10]; } void cim_show_images(int fnew, int fblend) { using namespace cim_show_images_names; void * cim_show_images_wthread(void *arg); int imx, pxr, pyr, ii, px3, py3; int ww, hh, wwmin, wwmax, hhmin, hhmax, bmid; float xf, yf, tf; float *pix3; paintlock(1); // block window paint 19.0 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; } } } do_wthreads(cim_show_images_wthread,NWT); // worker threads if (cimRedpix) { imx = cimRedImage; // paint red pixels for current image ww = cimPXMw[imx]->ww; // being aligned hh = cimPXMw[imx]->hh; for (ii = 0; ii < ww * hh; ii++) { if (cimRedpix[ii]) { pyr = ii / ww; // red pixel pxr = ii - pyr * ww; px3 = cimOffs[imx].xf + pxr * costf[imx] - pyr * sintf[imx] + 0.5; py3 = cimOffs[imx].yf + pyr * costf[imx] + pxr * sintf[imx] + 0.5; if (px3 < 0 || px3 > E3pxm->ww-1) continue; // off top/bottom edge if (py3 < 0 || py3 > E3pxm->hh-1) continue; pix3 = PXMpix(E3pxm,px3,py3); pix3[0] = 255; pix3[1] = pix3[2] = 0; } } } paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window return; } void * cim_show_images_wthread(void *arg) // working thread { using namespace cim_show_images_names; int index = *((int *) (arg)); int imx, imy; int px3, py3; int vstat, vstat1, vstat2; float red1, green1, blue1; float red2, green2, blue2; float red3, green3, blue3, alpha3; float f1, f2, px, py; float vpix[4], *pix3; red1 = green1 = blue1 = 0; f1 = f2 = 0.5; // to use if no fblend flag for (py3 = index; py3 < E3pxm->hh; py3 += NWT) // loop E3 rows for (px3 = 0; px3 < E3pxm->ww; px3++) // loop E3 columns { vstat1 = vstat2 = 0; for (imx = imy = im1; imx <= im2; imx += iminc) // find which images overlap this pixel { if (px3 < wwlo[imx] || px3 > wwhi[imx]) continue; px = costf[imx] * (px3 - cimOffs[imx].xf) + sintf[imx] * (py3 - cimOffs[imx].yf); py = costf[imx] * (py3 - cimOffs[imx].yf) - sintf[imx] * (px3 - cimOffs[imx].xf); vstat = vpixel(cimPXMw[imx],px,py,vpix); if (! vstat) continue; if (vpix[3] < 250) continue; // voided pixel 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; } pthread_exit(0); } // version for vertical panorama void cim_show_Vimages(int fnew, int fblend) { using namespace cim_show_images_names; void * cim_show_Vimages_wthread(void *arg); int imx, pxr, pyr, ii, px3, py3; int ww, hh, wwmin, wwmax, hhmin, hhmax, bmid; float xf, yf, tf; float *pix3; paintlock(1); // block window paint 19.0 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; } } } do_wthreads(cim_show_Vimages_wthread,NWT); // worker threads if (cimRedpix) { imx = cimRedImage; // paint red pixels for current image ww = cimPXMw[imx]->ww; // being aligned hh = cimPXMw[imx]->hh; for (ii = 0; ii < ww * hh; ii++) { if (cimRedpix[ii]) { pyr = ii / ww; // red pixel pxr = ii - pyr * ww; px3 = cimOffs[imx].xf + pxr * costf[imx] - pyr * sintf[imx] + 0.5; py3 = cimOffs[imx].yf + pyr * costf[imx] + pxr * sintf[imx] + 0.5; if (px3 < 0 || px3 > E3pxm->ww-1) continue; // off left/right edge pix3 = PXMpix(E3pxm,px3,py3); pix3[0] = 255; pix3[1] = pix3[2] = 0; } } } paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window return; } void * cim_show_Vimages_wthread(void *arg) // working thread { using namespace cim_show_images_names; int index = *((int *) (arg)); int imx, imy; int px3, py3; int vstat, vstat1, vstat2; float red1, green1, blue1, alpha1; float red2, green2, blue2, alpha2; float red3, green3, blue3, alpha3; float f1, f2, px, py; float vpix[4], *pix3; red1 = green1 = blue1 = 0; alpha1 = 255; // alpha f1 = f2 = 0.5; // to use if no fblend flag for (py3 = index; py3 < E3pxm->hh; py3 += NWT) // loop E3 rows for (px3 = 0; px3 < E3pxm->ww; px3++) // loop E3 columns { vstat1 = vstat2 = 0; for (imx = imy = im1; imx <= im2; imx++) // find which images overlap this pixel { if (py3 < hhlo[imx] || py3 > hhhi[imx]) continue; px = costf[imx] * (px3 - cimOffs[imx].xf) + sintf[imx] * (py3 - cimOffs[imx].yf); py = costf[imx] * (py3 - cimOffs[imx].yf) - sintf[imx] * (px3 - cimOffs[imx].xf); vstat = vpixel(cimPXMw[imx],px,py,vpix); if (! vstat) continue; if (vpix[3] < 250) continue; // voided pixel 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; } pthread_exit(0); } // cut-off edges of output image where all input images do not overlap // (HDR HDF Stack) void cim_trim() { int edgex[8] = { 0, 1, 2, 2, 2, 1, 0, 0 }; // 4 corners and 4 midpoints of rectangle int edgey[8] = { 0, 0, 0, 1, 2, 2, 2, 1 }; // 0 and 2 mark corners, 1 marks midpoints int edgewx[4] = { +1, -1, -1, +1 }; int edgewy[4] = { +1, +1, -1, -1 }; int imx, ii, jj, ww, hh, px3, py3, px9, py9; int wwmin, wwmax, hhmin, hhmax; float xf, yf, tf, sintf, costf, px, py, wx, wy; float *pix3, *pix9; wwmin = hhmin = 0; wwmax = E3pxm->ww; hhmax = E3pxm->hh; for (imx = 0; imx < cimNF; imx++) // loop all images { ww = cimPXMw[imx]->ww; // image size hh = cimPXMw[imx]->hh; xf = cimOffs[imx].xf; // alignment offsets yf = cimOffs[imx].yf; tf = cimOffs[imx].tf; sintf = sinf(tf); costf = cosf(tf); for (ii = 0; ii < 8; ii++) // 8 points around image rectangle { px = ww * edgex[ii] / 2; // coordinates before warping py = hh * edgey[ii] / 2; if (edgex[ii] != 1 && edgey[ii] != 1) { // if a corner jj = ii / 2; wx = cimOffs[imx].wx[jj]; // corner warp wy = cimOffs[imx].wy[jj]; if (edgewx[jj] > 0 && wx < 0) px -= wx; // if warp direction inwards, if (edgewx[jj] < 0 && wx > 0) px -= wx; // reduce px/py by warp if (edgewy[jj] > 0 && wy < 0) py -= wy; if (edgewy[jj] < 0 && wy > 0) py -= wy; } px3 = xf + px * costf - py * sintf; // map px/py to output image px3/py3 py3 = yf + py * costf + px * sintf; if (edgex[ii] != 1) { if (px3 < ww/2 && px3 > wwmin) wwmin = px3; // remember px3/py3 extremes if (px3 > ww/2 && px3 < wwmax) wwmax = px3; } if (edgey[ii] != 1) { if (py3 < hh/2 && py3 > hhmin) hhmin = py3; if (py3 > hh/2 && py3 < hhmax) hhmax = py3; } } } wwmin += 2; // compensate rounding wwmax -= 2; hhmin += 2; hhmax -= 2; ww = wwmax - wwmin; // new image size hh = hhmax - hhmin; if (ww < 0.7 * E3pxm->ww) return; // sanity check if (hh < 0.7 * E3pxm->hh) return; E9pxm = PXM_make(ww,hh,4); for (py3 = hhmin; py3 < hhmax; py3++) // E9 = trimmed E3 for (px3 = wwmin; px3 < wwmax; px3++) { px9 = px3 - wwmin; py9 = py3 - hhmin; pix3 = PXMpix(E3pxm,px3,py3); pix9 = PXMpix(E9pxm,px9,py9); memcpy(pix9,pix3,pixcc); } PXM_free(E3pxm); // E3 = E9 E3pxm = E9pxm; E9pxm = 0; E3pxm->ww = ww; E3pxm->hh = hh; return; } // cut off excess margins (panorama) void cim_trim_margins() { int ww, hh; int xlo, xhi, ylo, yhi; int px, py; float *pix, *pix1, *pix2; PXM *pxmout; ww = E3pxm->ww; hh = E3pxm->hh; xlo = ww; xhi = 0; ylo = hh; yhi = 0; for (py = 0; py < hh; py += 2) // find extent of empty margins for (px = 0; px < ww; px += 2) { pix = PXMpix(E3pxm,px,py); if (pix[3] == 0) continue; if (px < xlo) xlo = px; if (px > xhi) xhi = px; if (py < ylo) ylo = py; if (py > yhi) yhi = py; } ww = xhi - xlo; // make new image just large enough hh = yhi - ylo; pxmout = PXM_make(ww,hh,4); if (! pxmout) return; for (py = 0; py < hh; py++) // copy pixels into new image for (px = 0; px < ww; px++) { pix1 = PXMpix(E3pxm,px+xlo,py+ylo); pix2 = PXMpix(pxmout,px,py); memcpy(pix2,pix1,pixcc); } PXM_free(E3pxm); // replace input with output PXM E3pxm = pxmout; return; } // dump offsets to stdout - diagnostic tool void cim_dump_offsets(cchar *label) { printz("\n offsets: %s \n",label); for (int imx = 0; imx < cimNF; imx++) { printz(" imx %d x/y/t: %.1f %.1f %.4f w0: %.1f %.1f w1: %.1f %.1f w2: %.1f %.1f w3: %.1f %.1f \n", imx, cimOffs[imx].xf, cimOffs[imx].yf, cimOffs[imx].tf, cimOffs[imx].wx[0], cimOffs[imx].wy[0], cimOffs[imx].wx[1], cimOffs[imx].wy[1], cimOffs[imx].wx[2], cimOffs[imx].wy[2], cimOffs[imx].wx[3], cimOffs[imx].wy[3]); } return; } // uncurve the combined panorama image // input F = 0.0 - 1.0 = no unbend - full unbend void cim_flatten_image(float F) // scaled range { PXM *pxmout; int ww, hh, vstat; float ww2, hh2; int px, py, rx, ry; float dx, dy; float sx, sy; float tx, ty; float *pix, vpix[4]; float *pix1, *pix2; // F = 0.0 0.2 0.4 0.6 0.8 1.0 F = 1.0 - F; // 1.0 0.8 0.6 0.4 0.2 0.0 F = F * 3.0; // 3.0 2.4 1.8 1.2 0.6 0.0 F = pow(2,F); // 8.0 5.3 3.5 1.7 1.5 1.0 F = F * cimPanoFL; // 8*FL (min flat) to 1*FL (max flat) ww = E3pxm->ww; hh = E3pxm->hh; pxmout = PXM_make(1.4*ww,1.2*hh,4); // larger image space if (! pxmout) return; // too big, do nothing for (ry = 0; ry < hh; ry++) // copy image to middle for (rx = 0; rx < ww; rx++) { px = rx + 0.2 * ww; py = ry + 0.1 * hh; pix1 = PXMpix(E3pxm,rx,ry); pix2 = PXMpix(pxmout,px,py); memcpy(pix2,pix1,pixcc); } PXM_free(E3pxm); E3pxm = pxmout; // new image with bigger margins ww = E3pxm->ww; hh = E3pxm->hh; pxmout = PXM_make(ww,hh,4); // flattened image space if (! pxmout) return; ww2 = ww/2; hh2 = hh/2; for (py = 0; py < hh; py++) // copy and flatten image for (px = 0; px < ww; px++) { dx = px - ww2; dy = py - hh2; tx = atanf(dx / F); ty = atanf(dy / F * cosf(tx)); sx = F * tx; sy = F * ty; sy += hh2; sx += ww2; vstat = vpixel(E3pxm,sx,sy,vpix); // input virtual pixel pix = PXMpix(pxmout,px,py); // output real pixel if (vstat) memcpy(pix,vpix,pixcc); else memset(pix,0,pixcc); // voided pixel } PXM_free(E3pxm); // replace input with output PXM E3pxm = pxmout; cim_trim_margins(); // trim excess margins return; } // version for vertical panorama void cim_flatten_Vimage(float F) { PXM *pxmout; int ww, hh, vstat; float ww2, hh2; int px, py, rx, ry; float dx, dy; float sx, sy; float tx, ty; float *pix, vpix[4]; float *pix1, *pix2; // F = 0.0 0.2 0.4 0.6 0.8 1.0 F = 1.0 - F; // 1.0 0.8 0.6 0.4 0.2 0.0 F = F * 3.0; // 3.0 2.4 1.8 1.2 0.6 0.0 F = pow(2,F); // 8.0 5.3 3.5 1.7 1.5 1.0 F = F * cimPanoFL; // 8*FL (min flat) to 1*FL (max flat) ww = E3pxm->ww; hh = E3pxm->hh; pxmout = PXM_make(1.2*ww,1.4*hh,4); // larger image space if (! pxmout) return; // too big, do nothing for (ry = 0; ry < hh; ry++) // copy image to middle for (rx = 0; rx < ww; rx++) { px = rx + 0.1 * ww; py = ry + 0.2 * hh; pix1 = PXMpix(E3pxm,rx,ry); pix2 = PXMpix(pxmout,px,py); memcpy(pix2,pix1,pixcc); } PXM_free(E3pxm); E3pxm = pxmout; // new image with bigger margins ww = E3pxm->ww; hh = E3pxm->hh; pxmout = PXM_make(ww,hh,4); // flattened image space if (! pxmout) return; ww2 = ww/2; hh2 = hh/2; for (py = 0; py < hh; py++) // copy and flatten image for (px = 0; px < ww; px++) { dy = py - hh2; dx = px - ww2; ty = atanf(dy / F); tx = atanf(dx / F * cosf(ty)); sy = F * ty; sx = F * tx; sy += hh2; sx += ww2; vstat = vpixel(E3pxm,sx,sy,vpix); // input virtual pixel pix = PXMpix(pxmout,px,py); // output real pixel if (vstat) memcpy(pix,vpix,pixcc); else memset(pix,0,pixcc); // voided pixel } PXM_free(E3pxm); // replace input with output PXM E3pxm = pxmout; cim_trim_margins(); // trim excess margins return; } fotoxx-20.08/f.edit.cc000066400000000000000000013134501362435004500145430ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* Fotoxx image edit - Edit menu functions m_trim_rotate trim/crop and rotate combination m_upright auto upright based on EXIF orientation data m_retouch brightness, contrast, color ... blackbodyRGB convert deg. K to RGB factors m_resize resize (rescale) image m_adjust_RGB adjust brightness/color using RGB or CMY colors m_adjust_HSL adjust color using HSL model m_markup draw on image image: text, line, box, oval m_draw_text draw text on image m_draw_line draw line/arrow on image m_draw_box draw box on image m_draw_oval draw oval on image m_paint_image paint on the image with a color RGB_chooser select RGB color or use color chooser file HSL_chooser select HSL color m_copypixels1 copy pixels within one image m_copypixels2 copy pixels from source image to target image m_copypixels3 copypixels2 source image process m_paint_edits paint edit function gradually with the mouse m_undo_edits undo prior edits gradually with the mouse 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 m_rawtherapee edit a camera RAW file using this program *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // trim (crop) and/or rotate an image // combined crop and rotate function // Fotoxx 20.0: extensive revisions for preview mode and other changes // 20.0 // fotoxx.h // int trimx1, trimy1, trimx2, trimy2; // trim rectangle (calling function) // char *trimsizes[10]; // previous trim sizes "NNNNxNNNN" // char *trimbuttons[5]; // trim dialog button labels // char *trimratios[5]; // corresponding aspect ratios namespace trimrotate { editfunc EFtrimrotate; zdialog *zd; int ptrimx1, ptrimy1, ptrimx2, ptrimy2; // prior trim rectangle int trimww, trimhh; // trim rectangle width and height double trimR; // trim ratio, width/height double Zratio; // E0 size / E3 size >= 1.0 double rotate_goal, rotate_angle; // target and actual rotation int E0ww, E0hh, E3ww, E3hh; // full size image and preview size int Fguidelines, guidelineX, guidelineY; // horz/vert guidelines for rotate int Fcorner, Fside, Frotate; int KBcorner, KBside; int Ftimer1; double Ftimer2; void dialog(); int timerfunc(void *); int dialog_event(zdialog *zd, cchar *event); void trimsize_menu_event(GtkWidget *, cchar *menu); void update_prev_trimsize(); void mousefunc(); void KBfunc(int key); void trim_limits(); void trim_darkmargins(); void trim_final(); void rotate_func(int fast); void drawlines(cairo_t *cr); void autotrim(); void trim_customize(); } // menu function void m_trim_rotate(GtkWidget *, cchar *menu) { using namespace trimrotate; int ii, xmargin, ymargin; cchar *trim_message = E2X("drag middle to move \n" "drag corners to resize \n" "drag right edge to level"); char text[20]; F1_help_topic = "trim/rotate"; EFtrimrotate.menufunc = m_trim_rotate; // menu function EFtrimrotate.funcname = "trim/rotate"; EFtrimrotate.menuname = menu; EFtrimrotate.Frestart = 1; // allow restart EFtrimrotate.mousefunc = mousefunc; if (! zstrstr("keep max auto",menu)) // if not special case, EFtrimrotate.FprevReq = 1; // use preview (small) size if (! edit_setup(EFtrimrotate)) return; // setup edit PXM_addalpha(E0pxm); PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); E0ww = E0pxm->ww; // full-size image dimensions E0hh = E0pxm->hh; E3ww = E3pxm->ww; // preview image dimensions E3hh = E3pxm->hh; Zratio = 1.0 * (E0ww + E0hh) / (E3ww + E3hh); // full / preview size >= 1.0 if (E9pxm) PXM_free(E9pxm); // E9 invalid E9pxm = 0; if (menu && strmatch(menu,"keep")) { // use a preset trim rectangle (1x image)) if (trimx1 < 0) trimx1 = 0; if (trimx2 > E3ww) trimx2 = E3ww; // sanity checks if (trimx2 <= trimx1) { trimx1 = 0.2 * E3ww; trimx2 = 0.8 * E3ww; } if (trimy1 < 0) trimy1 = 0; if (trimy2 > E3hh) trimy2 = E3hh; if (trimy2 <= trimy1) { trimy1 = 0.2 * E3hh; trimy2 = 0.8 * E3hh; } trimww = trimx2 - trimx1; trimhh = trimy2 - trimy1; trimR = 1.0 * trimww / trimhh; // trim ratio = width/height } else if (menu && strmatch(menu,"max")) { trimww = E3ww; // initial trim rectangle, 1x image size trimhh = E3hh; xmargin = ymargin = 0; trimx1 = 0; trimx2 = E3ww; trimy1 = 0; trimy2 = E3hh; trimR = 1.0 * E3ww / E3hh; // trim ratio = width/height } else { trimww = 0.8 * E3ww; // initial trim rectangle, 80% image size trimhh = 0.8 * E3hh; xmargin = 0.5 * (E3ww - trimww); // set balanced margins ymargin = 0.5 * (E3hh - trimhh); trimx1 = xmargin; trimx2 = E3ww - xmargin; trimy1 = ymargin; trimy2 = E3hh - ymargin; trimR = 1.0 * trimww / trimhh; // trim ratio = width/height } ptrimx1 = 0; // set prior trim rectangle ptrimy1 = 0; // = 100% of image ptrimx2 = E3ww; ptrimy2 = E3hh; rotate_goal = rotate_angle = 0; // initially no rotation /*** ______________________________________ | | | drag middle to move | | drag corners to resize | | drag right edge to level | | - - - - - - - - - - - - - - - - - - | | width [____] height [____] | | Ratio 1.5 [x] Lock Ratio | | Trim Size: [Max] [Auto] [Prev] | | - - - - - - - - - - - - - - - - - - | | Set Ratio [Invert] [Customize] | | [1:1] [2:1] [3:2] [4:3] [16:9] | | - - - - - - - - - - - - - - - - - - | | Degrees [___] [Auto Level] | | [-90°] [+90°] [180°] [upright] | | | | [Grid] [Done] [Cancel] | |______________________________________| ***/ zd = zdialog_new(E2X("Trim/Rotate"),Mwin,Bgrid,Bdone,Bcancel,null); EFtrimrotate.zd = zd; zdialog_add_widget(zd,"label","labtrim","dialog",trim_message,"space=3"); zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbwh","dialog"); zdialog_add_widget(zd,"label","labW","hbwh",Bwidth,"space=5"); zdialog_add_widget(zd,"zspin","width","hbwh","20|30000|1|1000"); // fotoxx.h zdialog_add_widget(zd,"label","space","hbwh",0,"space=5"); zdialog_add_widget(zd,"label","labH","hbwh",Bheight,"space=5"); zdialog_add_widget(zd,"zspin","height","hbwh","20|30000|1|600"); zdialog_add_widget(zd,"hbox","hbrat","dialog"); zdialog_add_widget(zd,"label","labR","hbrat",E2X("ratio"),"space=5"); zdialog_add_widget(zd,"label","ratio","hbrat","1.67 "); zdialog_add_widget(zd,"check","lock","hbrat",E2X("Lock Ratio"),"space=15"); zdialog_add_widget(zd,"hbox","hbsz","dialog"); zdialog_add_widget(zd,"label","labsz","hbsz",E2X("Trim Size:"),"space=5"); zdialog_add_widget(zd,"button","max","hbsz",Bmax,"space=5"); zdialog_add_widget(zd,"button","auto","hbsz",Bauto,"space=5"); zdialog_add_widget(zd,"button","prev","hbsz",Bprev,"space=5"); zdialog_add_widget(zd,"hsep","sep2","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbsr1","dialog"); zdialog_add_widget(zd,"label","labsr1","hbsr1",E2X("Set Ratio"),"space=5"); zdialog_add_widget(zd,"button","invert","hbsr1",E2X("Invert"),"space=5"); zdialog_add_widget(zd,"button","custom","hbsr1",E2X("Customize"),"space=5"); zdialog_add_widget(zd,"hbox","hbsr2","dialog",0,"space=2"); for (ii = 0; ii < 5; ii++) zdialog_add_widget(zd,"button",trimbuttons[ii],"hbsr2",trimbuttons[ii],"space=5"); zdialog_add_widget(zd,"hsep","sep3","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbrot","dialog"); zdialog_add_widget(zd,"label","labrotate","hbrot",E2X("Degrees:"),"space=5"); zdialog_add_widget(zd,"zspin","degrees","hbrot","-180|180|0.1|0"); zdialog_add_widget(zd,"button","level","hbrot",E2X("Auto Level"),"space=10"); zdialog_add_widget(zd,"hbox","hb90","dialog",0,"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,"imagebutt","180","hb90","rotate-180.png","size=32|space=8"); zdialog_add_widget(zd,"button","upright","hb90",E2X("Upright"),"space=8"); zdialog_add_ttip(zd,"max",E2X("maximize trim box")); zdialog_add_ttip(zd,"auto",E2X("trim transparent edges")); zdialog_add_ttip(zd,"lock",E2X("lock width/height ratio")); zdialog_add_ttip(zd,"level",E2X("use EXIF data if available")); zdialog_add_ttip(zd,"prev",Bprevtip); zd = EFtrimrotate.zd; zdialog_restore_inputs(zd); // restore all prior inputs zdialog_stuff(zd,"width",trimww*Zratio); // stuff width, height, ratio as zdialog_stuff(zd,"height",trimhh*Zratio); // pre-calculated for this image snprintf(text,20,"%.3f ",trimR); zdialog_stuff(zd,"ratio",text); zdialog_stuff(zd,"degrees",0); if (zstrstr("keep max auto",menu)) // no ratio lock in these cases zdialog_stuff(zd,"lock",0); takeMouse(mousefunc,dragcursor); // connect mouse function currgrid = 1; // use trim/rotate grid Fzoom = 0; trim_darkmargins(); zdialog_run(zd,dialog_event,"save"); // run dialog - parallel if (menu && strmatch(menu,"auto")) zdialog_send_event(zd,"auto"); g_timeout_add(100,timerfunc,0); // start rotate watchdog Ftimer1 = 1; // trim_rotate() active Ftimer2 = 0; // rotate fix pending return; } // watchdog timer function - watch for fast low-quality rotates // and fix them when rotation has apparently ended. int trimrotate::timerfunc(void *) { if (! Ftimer1) return 0; // trim_rotate() ended if (! Ftimer2) return 1; // nothing pending if (get_seconds() - Ftimer2 < 1.0) return 1; // < 1 sec. since last rotate Ftimer2 = 0; // reset pending flag rotate_angle = 0; rotate_func(0); // do high quality rotate return 1; } // dialog event and completion callback function int trimrotate::dialog_event(zdialog *zd, cchar *event) { using namespace trimrotate; static GtkWidget *popmenu = 0; static int flip = 0; int width, height, delta; int ii, Rlock; double r1, r2, Fratio = 0; int angle = 0; char orientation = 0; char text[20]; cchar *pp; cchar *exifkey[1]; char *ppv[1]; if (strmatch(event,"done")) zd->zstat = 2; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 3; // cancel if (strmatch(event,"undo")) { // 'undo' button was used edit_cancel(0); m_trim_rotate(0,0); return 1; } Fcorner = Fside = KBcorner = KBside = Frotate = 0; // nothing underway if (strmatch(event,"focus")) { takeMouse(mousefunc,dragcursor); // re-connect mouse function return 1; } if (zd->zstat) // dialog complete { if (zd->zstat == 1) { // [grid] zd->zstat = 0; // keep dialog active if (! gridsettings[1][GON]) m_gridlines(0,"grid 1"); else toggle_grid(2); return 1; } if (zd->zstat == 2) // [done] { Ftimer1 = Ftimer2 = 0; // stop watchdog timer draw_toplines(2,0); // erase trim rectangle erase_topcircles(); // erase center circle currgrid = 0; // restore normal grid settings edit_fullsize(); // get full size image E3ww = E3pxm->ww; // rescale all settings E3hh = E3pxm->hh; trimx1 *= Zratio; trimy1 *= Zratio; trimx2 *= Zratio; trimy2 *= Zratio; Zratio = 1.0; zdialog_fetch(zd,"width",trimww); // width/height set by user zdialog_fetch(zd,"height",trimhh); trim_final(); // rotate/trim final image CEF->Fmods++; // finish edit edit_done(0); update_prev_trimsize(); // update prev. trim size memory return 1; } Ftimer1 = Ftimer2 = 0; // stop watchdog timer draw_toplines(2,0); // [cancel] - erase trim rectangle erase_topcircles(); // erase center circle currgrid = 0; // restore normal grid settings edit_cancel(0); return 1; } if (strmatch(event,"auto")) // auto trim margins { autotrim(); // do auto-trim zdialog_stuff(zd,"width",trimww*Zratio); // update dialog values zdialog_stuff(zd,"height",trimhh*Zratio); zdialog_stuff(zd,"lock",0); // set no ratio lock trim_darkmargins(); // show trim area in image Fpaintnow(); return 1; } if (strmatch(event,"max")) // maximize trim rectangle { trimx1 = trimy1 = 0; trimx2 = trimww = E3ww; trimy2 = trimhh = E3hh; zdialog_stuff(zd,"width",E3ww*Zratio); // stuff width, height zdialog_stuff(zd,"height",E3hh*Zratio); zdialog_stuff(zd,"lock",0); // set no ratio lock trim_darkmargins(); // show trim area in image Fpaintnow(); return 1; } if (zstrstr("width height",event)) // width or height input { zdialog_fetch(zd,"width",width); // full image scale, E0 zdialog_fetch(zd,"height",height); if (width < 20) width = 20; if (width > E3ww*Zratio) width = E3ww*Zratio; if (height < 20) height = 20; if (height > E3hh*Zratio) height = E3hh*Zratio; zdialog_fetch(zd,"lock",Rlock); // get ratio lock, on/off if (Rlock) { if (strmatch(event,"width")) height = width / trimR + 0.5; if (strmatch(event,"height")) width = height * trimR + 0.5; if (width > E3ww*Zratio) { width = E3ww*Zratio; height = width / trimR + 0.5; } if (height > E3hh*Zratio) { height = E3hh*Zratio; width = height * trimR + 0.5; } if (width > E3ww*Zratio) width = E3ww*Zratio; // rounding could put it over limit if (height > E3hh*Zratio) height = E3hh*Zratio; } zdialog_stuff(zd,"width",width); zdialog_stuff(zd,"height",height); flip = 1 - flip; // alternates 0, 1, 0, 1 ... delta = width / Zratio - trimww; // change in E3 width if (delta > 0) { // increased width trimx1 = trimx1 - delta / 2; // left and right sides equally trimx2 = trimx2 + delta / 2; if (delta % 2) { // if increase is odd trimx1 = trimx1 - flip; // add 1 alternatively to each side trimx2 = trimx2 + 1 - flip; } if (trimx1 < 0) { // add balance to upper limit trimx2 = trimx2 - trimx1; trimx1 = 0; } if (trimx2 > E3ww) { // add balance to lower limit trimx1 = trimx1 - trimx2 + E3ww; trimx2 = E3ww; } } if (delta < 0) { // decreased width trimx1 = trimx1 - delta / 2; trimx2 = trimx2 + delta / 2; if (delta % 2) { trimx1 = trimx1 + flip; trimx2 = trimx2 - 1 + flip; } } if (trimx1 < 0) trimx1 = 0; // keep within limits if (trimx2 > E3ww) trimx2 = E3ww; // use ww not ww-1 trimww = trimx2 - trimx1; delta = height / Zratio - trimhh; // change in E3 height if (delta > 0) { // increased height trimy1 = trimy1 - delta / 2; // top and bottom sides equally trimy2 = trimy2 + delta / 2; if (delta % 2) { // if increase is odd trimy1 = trimy1 - flip; // add 1 alternatively to each side trimy2 = trimy2 + 1 - flip; } if (trimy1 < 0) { trimy2 = trimy2 - trimy1; trimy1 = 0; } if (trimy2 > E3hh) { trimy1 = trimy1 - trimy2 + E3hh; trimy2 = E3hh; } } if (delta < 0) { // decreased height trimy1 = trimy1 - delta / 2; trimy2 = trimy2 + delta / 2; if (delta % 2) { trimy1 = trimy1 + flip; trimy2 = trimy2 - 1 + flip; } } if (trimy1 < 0) trimy1 = 0; // keep within limits if (trimy2 > E3hh) trimy2 = E3hh; // use ww not ww-1 trimhh = trimy2 - trimy1; trim_darkmargins(); // show trim area in image return 1; } zdialog_fetch(zd,"lock",Rlock); // get ratio lock, on/off if (! Rlock) trimR = 1.0 * trimww / trimhh; snprintf(text,20,"%.3f ",trimR); // update ratio zdialog_stuff(zd,"ratio",text); for (ii = 0; ii < 5; ii++) // trim ratio buttons if (strmatch(event,trimbuttons[ii])) break; if (ii < 5) { r1 = r2 = Fratio = 0; // ratio button pressed 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) Fratio = r1/r2; if (Fratio < 0.1 || Fratio > 10) Fratio = 1.0; if (! Fratio) return 1; zdialog_stuff(zd,"lock",1); // assume lock is wanted } if (strmatch(event,"invert")) // [invert] ratio button Fratio = 1.0 * trimhh / trimww; if (Fratio) // ratio was changed { trimR = Fratio; if (trimx2 - trimx1 > trimy2 - trimy1) // adjust smaller dimension trimy2 = trimy1 + (trimx2 - trimx1) / trimR + 0.5; // round else trimx2 = trimx1 + (trimy2 - trimy1) * trimR + 0.5; if (trimx2 > E3ww) { // if off the right edge, trimx2 = E3ww; // adjust height trimy2 = trimy1 + (trimx2 - trimx1) / trimR + 0.5; } if (trimy2 > E3hh) { // if off the bottom edge, trimy2 = E3hh; // adjust width trimx2 = trimx1 + (trimy2 - trimy1) * trimR + 0.5; } trimww = trimx2 - trimx1; // new rectangle dimensions trimhh = trimy2 - trimy1; zdialog_stuff(zd,"width",trimww*Zratio); // stuff width, height, ratio zdialog_stuff(zd,"height",trimhh*Zratio); snprintf(text,20,"%.3f ",trimR); zdialog_stuff(zd,"ratio",text); trim_darkmargins(); // update trim area in image return 1; } if (zstrstr("+90 -90 180",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 (rotate_goal > 180) rotate_goal -= 360; // keep within -180 to +180 deg. if (rotate_goal < -180) rotate_goal += 360; if (fabs(rotate_goal) < 0.01) rotate_goal = 0; // = 0 within my precision zdialog_stuff(zd,"degrees",rotate_goal); rotate_func(0); // E3 is rotated E1 if (zstrstr("+90 -90",event)) // adjust margins 20.0 dialog_event(zd,"auto"); } if (strmatch(event,"upright")) // auto upright button { exifkey[0] = exif_orientation_key; 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,E2X("rotation unknown")); return 1; } rotate_goal += angle; rotate_func(0); } if (strmatch(event,"degrees")) // rotate action { 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 (fabs(rotate_goal) < 0.01) rotate_goal = 0; // = 0 within my precision zdialog_stuff(zd,"degrees",rotate_goal); rotate_func(1); // fast low quality rotate } if (strmatch(event,"level")) // auto level using EXIF RollAngle { exifkey[0] = exif_rollangle_key; exif_get(curr_file,exifkey,ppv,1); if (ppv[0]) { rotate_goal = atof(ppv[0]); zfree(ppv[0]); rotate_func(0); // E3 is rotated E1 } } if (strmatch(event,"prev")) // [Prev] button - get prev. trim size { zdialog_stuff(zd,"lock",0); if (popmenu) gtk_widget_destroy(popmenu); popmenu = create_popmenu(); // make popup menu with trim sizes for (int ii = 0; ii < 10; ii++) if (trimsizes[ii]) add_popmenu_item(popmenu,trimsizes[ii],trimsize_menu_event,0,0); popup_menu(Mwin,popmenu); return 1; } if (strmatch(event,"custom")) { // [customize] button draw_toplines(2,0); // erase trim rectangle erase_topcircles(); // erase center circle edit_cancel(0); // cancel edit trim_customize(); // customize dialog m_trim_rotate(0,0); // restart edit return 1; } return 1; } // trim size popup menu event function void trimrotate::trimsize_menu_event(GtkWidget *, cchar *menu) { using namespace trimrotate; int nn, width, height; zdialog *zd; nn = sscanf(menu,"%dx%d",&width,&height); // chosen trim size, "NNNNxNNNN" if (nn != 2) return; zd = EFtrimrotate.zd; zdialog_stuff(zd,"width",width); // update dialog zdialog_stuff(zd,"height",height); zdialog_send_event(zd,"width"); return; } // update memory of previously used trim sizes, for future use void trimrotate::update_prev_trimsize() { using namespace trimrotate; int ii; char mysize[20]; snprintf(mysize,20,"%dx%d",trimww,trimhh); // this trim size used, "NNNNxNNNN" for (ii = 0; ii < 10; ii++) { if (! trimsizes[ii]) break; // check past sizes list for same size if (strmatch(trimsizes[ii],mysize)) break; } if (ii < 10 && trimsizes[ii]) { // found in list zfree(trimsizes[ii]); // remove from list for (ii++; ii < 10; ii++) // pack down remaining entries trimsizes[ii-1] = trimsizes[ii]; trimsizes[9] = 0; // last = null } if (trimsizes[9]) zfree(trimsizes[9]); // if entry 9 exists, discard for (ii = 9; ii > 0; ii--) // move entries 0-8 to 1-9 trimsizes[ii] = trimsizes[ii-1]; trimsizes[0] = zstrdup(mysize); // entry 0 = this size return; } // trim/rotate mouse function void trimrotate::mousefunc() { using namespace trimrotate; int mpx, mpy, xdrag, ydrag; int moveall = 0; int dx, dy, dd, dc, ds; int d1, d2, d3, d4; int edgedist, linedist; zdialog *zd = EFtrimrotate.zd; if (! LMclick && ! RMclick && ! Mxdrag && ! Mydrag) { // no click or drag Fcorner = Fside = Frotate = 0; return; } mpx = mpy = xdrag = ydrag = 0; // avoid gcc warnings if (LMclick) // add vertical and horizontal { // lines at click position LMclick = 0; Fcorner = Fside = KBcorner = KBside = Frotate = 0; Fguidelines = 1; guidelineX = Mxclick; guidelineY = Myclick; trim_darkmargins(); Fpaint2(); return; } if (RMclick) // remove the lines { RMclick = 0; Fcorner = Fside = KBcorner = KBside = Frotate = 0; Fguidelines = 0; trim_darkmargins(); Fpaint2(); return; } if (Mxdrag || Mydrag) // drag { mpx = Mxdrag; mpy = Mydrag; xdrag = Mxdrag - Mxdown; ydrag = Mydrag - Mydown; Mxdown = Mxdrag; // reset drag origin Mydown = Mydrag; Mxdrag = Mydrag = 0; } if (Frotate) goto mouse_rotate; // continue rotate in progress else if (Fcorner) { if (Fcorner == 1) { trimx1 = mpx; trimy1 = mpy; } // continue dragging corner with mouse if (Fcorner == 2) { trimx2 = mpx; trimy1 = mpy; } if (Fcorner == 3) { trimx2 = mpx; trimy2 = mpy; } if (Fcorner == 4) { trimx1 = mpx; trimy2 = mpy; } } else if (Fside) { if (Fside == 1) trimx1 = mpx; // continue dragging side with mouse if (Fside == 2) trimy1 = mpy; if (Fside == 3) trimx2 = mpx; if (Fside == 4) trimy2 = mpy; } else { moveall = 1; dd = 0.2 * (trimx2 - trimx1); // test if mouse is in the broad if (mpx < trimx1 + dd) moveall = 0; // middle of the rectangle if (mpx > trimx2 - dd) moveall = 0; dd = 0.2 * (trimy2 - trimy1); if (mpy < trimy1 + dd) moveall = 0; if (mpy > trimy2 - dd) moveall = 0; } if (moveall) { // yes, move the whole rectangle trimx1 += xdrag; trimx2 += xdrag; trimy1 += ydrag; trimy2 += ydrag; Fcorner = Fside = KBcorner = KBside = Frotate = 0; } else if (! Fcorner && ! Fside) // no, find closest corner/side to mouse { dx = mpx - trimx1; dy = mpy - trimy1; d1 = sqrt(dx*dx + dy*dy); // distance from NW corner dx = mpx - trimx2; dy = mpy - trimy1; d2 = sqrt(dx*dx + dy*dy); // NE dx = mpx - trimx2; dy = mpy - trimy2; d3 = sqrt(dx*dx + dy*dy); // SE dx = mpx - trimx1; dy = mpy - trimy2; d4 = sqrt(dx*dx + dy*dy); // SW Fcorner = 1; // find closest corner dc = d1; // NW if (d2 < dc) { Fcorner = 2; dc = d2; } // NE if (d3 < dc) { Fcorner = 3; dc = d3; } // SE if (d4 < dc) { Fcorner = 4; dc = d4; } // SW dx = mpx - trimx1; dy = mpy - (trimy1 + trimy2) / 2; d1 = sqrt(dx*dx + dy*dy); // distance from left side middle dx = mpx - (trimx1 + trimx2) / 2; dy = mpy - trimy1; d2 = sqrt(dx*dx + dy*dy); // top middle dx = mpx - trimx2; dy = mpy - (trimy1 + trimy2) / 2; d3 = sqrt(dx*dx + dy*dy); // right side middle dx = mpx - (trimx1 + trimx2) / 2; dy = mpy - trimy2; d4 = sqrt(dx*dx + dy*dy); // bottom middle Fside = 1; // find closest side ds = d1; // left if (d2 < ds) { Fside = 2; ds = d2; } // top if (d3 < ds) { Fside = 3; ds = d3; } // right if (d4 < ds) { Fside = 4; ds = d4; } // bottom if (dc < ds) Fside = 0; // closer to corner else Fcorner = 0; // closer to side if (Fside == 3) { // closest to right side linedist = Dorgx + Mscale * trimx2 - Mwxposn; // dist. to trim rect. right side edgedist = Dorgx + dww - Mwxposn; // dist. to image right edge 20.06 if (abs(linedist) > abs(edgedist)) goto mouse_rotate; // closer to image edge - rotate } // closer to trim rectangle - move it if (Fcorner == 1) { trimx1 = mpx; trimy1 = mpy; } // move this corner to mouse if (Fcorner == 2) { trimx2 = mpx; trimy1 = mpy; } if (Fcorner == 3) { trimx2 = mpx; trimy2 = mpy; } if (Fcorner == 4) { trimx1 = mpx; trimy2 = mpy; } if (Fside == 1) trimx1 = mpx; // move this side to mouse if (Fside == 2) trimy1 = mpy; if (Fside == 3) trimx2 = mpx; if (Fside == 4) trimy2 = mpy; KBcorner = Fcorner; // save last corner/side moved KBside = Fside; } trim_limits(); // check margin limits and adjust if req. trim_darkmargins(); // show trim area in image return; // ------------------------- mouse_rotate: Frotate = 1; Fcorner = Fside = KBcorner = KBside = 0; rotate_goal += 40.0 * ydrag / E3ww; // convert radians to degrees (reduced) if (rotate_goal > 180) rotate_goal -= 360; // keep within -180 to +180 deg. if (rotate_goal < -180) rotate_goal += 360; if (fabs(rotate_goal) < 0.01) rotate_goal = 0; // = 0 within my precision zdialog_stuff(zd,"degrees",rotate_goal); // update dialog rotate_func(1); // fast low quality rotate return; } // Keyboard function // KB arrow keys tweak the last selected corner or side void trimrotate::KBfunc(int key) { using namespace trimrotate; int xstep, ystep; xstep = ystep = 0; if (key == GDK_KEY_Left) xstep = -1; if (key == GDK_KEY_Right) xstep = +1; if (key == GDK_KEY_Up) ystep = -1; if (key == GDK_KEY_Down) ystep = +1; if (KBcorner == 1) { // NW trimx1 += xstep; trimy1 += ystep; } if (KBcorner == 2) { // NE trimx2 += xstep; trimy1 += ystep; } if (KBcorner == 3) { // SE trimx2 += xstep; trimy2 += ystep; } if (KBcorner == 4) { // SW trimx1 += xstep; trimy2 += ystep; } if (KBside == 1) trimx1 += xstep; // left if (KBside == 2) trimy1 += ystep; // top if (KBside == 3) trimx2 += xstep; // right if (KBside == 4) trimy2 += ystep; // bottom trim_limits(); // check margin limits and adjust if req. trim_darkmargins(); // show trim area in image return; } // check new margins for sanity and enforce locked aspect ratio void trimrotate::trim_limits() { using namespace trimrotate; int Rlock, chop; int ww, hh, ww2, hh2; float drr; char text[20]; zdialog *zd = EFtrimrotate.zd; if (trimx1 > trimx2-10) trimx1 = trimx2-10; // sanity limits if (trimy1 > trimy2-10) trimy1 = trimy2-10; zdialog_fetch(zd,"lock",Rlock); // w/h ratio locked if (Rlock && Fcorner) { if (Fcorner < 3) trimy1 = trimy2 - 1.0 * (trimx2 - trimx1) / trimR + 0.5; // round else trimy2 = trimy1 + 1.0 * (trimx2 - trimx1) / trimR + 0.5; } if (Rlock && Fside) { ww = trimx2 - trimx1; hh = trimy2 - trimy1; ww2 = ww; hh2 = hh; if (Fside == 1 || Fside == 3) hh2 = ww / trimR + 0.5; else ww2 = hh * trimR + 0.5; ww2 = (ww2 - ww) / 2; hh2 = (hh2 - hh) / 2; trimx1 -= ww2; trimx2 += ww2; trimy1 -= hh2; trimy2 += hh2; } chop = 0; if (trimx1 < 0) { // look for off the edge trimx1 = 0; // after corner move chop = 1; } if (trimx2 > E3ww) { trimx2 = E3ww; chop = 2; } if (trimy1 < 0) { trimy1 = 0; chop = 3; } if (trimy2 > E3hh) { trimy2 = E3hh; chop = 4; } if (Rlock && chop) { // keep ratio if off edge if (chop < 3) trimy2 = trimy1 + 1.0 * (trimx2 - trimx1) / trimR + 0.5; // round else trimx2 = trimx1 + 1.0 * (trimy2 - trimy1) * trimR + 0.5; } if (trimx1 > trimx2-10) trimx1 = trimx2-10; // sanity limits if (trimy1 > trimy2-10) trimy1 = trimy2-10; if (trimx1 < 0) trimx1 = 0; // keep within visible area if (trimx2 > E3ww) trimx2 = E3ww; if (trimy1 < 0) trimy1 = 0; if (trimy2 > E3hh) trimy2 = E3hh; trimww = trimx2 - trimx1; // new rectangle dimensions trimhh = trimy2 - trimy1; drr = 1.0 * trimww / trimhh; // new w/h ratio if (! Rlock) trimR = drr; zdialog_stuff(zd,"width",trimww*Zratio); // stuff width, height, ratio zdialog_stuff(zd,"height",trimhh*Zratio); snprintf(text,20,"%.3f ",trimR); zdialog_stuff(zd,"ratio",text); return; } // Darken image pixels outside of current trim margins. // messy logic: update pixmaps only for changed pixels to increase speed void trimrotate::trim_darkmargins() { using namespace trimrotate; int ox1, oy1, ox2, oy2; // outer trim rectangle int nx1, ny1, nx2, ny2; // inner trim rectangle int px, py; float *pix1, *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); int Rflag = 0; int cx, cy, rad, rad2; if (trimx1 < 0) trimx1 = 0; // keep within visible area if (trimx2 > E3ww) trimx2 = E3ww; if (trimy1 < 0) trimy1 = 0; if (trimy2 > E3hh) trimy2 = E3hh; if (ptrimx1 < trimx1) ox1 = ptrimx1; // outer rectangle else ox1 = trimx1; if (ptrimx2 > trimx2) ox2 = ptrimx2; else ox2 = trimx2; if (ptrimy1 < trimy1) oy1 = ptrimy1; else oy1 = trimy1; if (ptrimy2 > trimy2) oy2 = ptrimy2; else oy2 = trimy2; if (ptrimx1 > trimx1) nx1 = ptrimx1; // inner rectangle else nx1 = trimx1; if (ptrimx2 < trimx2) nx2 = ptrimx2; else nx2 = trimx2; if (ptrimy1 > trimy1) ny1 = ptrimy1; else ny1 = trimy1; if (ptrimy2 < trimy2) ny2 = ptrimy2; else ny2 = trimy2; ox1 -= 2; // expand outer rectangle oy1 -= 2; ox2 += 2; oy2 += 2; nx1 += 2; // reduce inner rectangle ny1 += 2; nx2 -= 2; ny2 -= 2; if (ox1 < 0) ox1 = 0; if (oy1 < 0) oy1 = 0; if (ox2 > E3ww) ox2 = E3ww; if (oy2 > E3hh) oy2 = E3hh; if (nx1 < ox1) nx1 = ox1; if (ny1 < oy1) ny1 = oy1; if (nx2 > ox2) nx2 = ox2; if (ny2 > oy2) ny2 = oy2; if (nx2 < nx1) nx2 = nx1; if (ny2 < ny1) ny2 = ny1; if (! E9pxm) E9pxm = PXM_copy(E3pxm); for (py = oy1; py < ny1; py++) // top band of pixels for (px = ox1; px < ox2; px++) { pix1 = PXMpix(E9pxm,px,py); pix3 = PXMpix(E3pxm,px,py); if (px < trimx1 || px >= trimx2 || py < trimy1 || py >= trimy2) { pix3[0] = pix1[0] / 2; // outside trim margins pix3[1] = pix1[1] / 2; // 50% brightness pix3[2] = pix1[2] / 2; } else memcpy(pix3,pix1,pcc); // inside, full brightness } for (py = oy1; py < oy2; py++) // right band for (px = nx2; px < ox2; px++) { pix1 = PXMpix(E9pxm,px,py); pix3 = PXMpix(E3pxm,px,py); if (px < trimx1 || px >= trimx2 || py < trimy1 || py >= trimy2) { pix3[0] = pix1[0] / 2; pix3[1] = pix1[1] / 2; pix3[2] = pix1[2] / 2; } else memcpy(pix3,pix1,pcc); } for (py = ny2; py < oy2; py++) // bottom band for (px = ox1; px < ox2; px++) { pix1 = PXMpix(E9pxm,px,py); pix3 = PXMpix(E3pxm,px,py); if (px < trimx1 || px >= trimx2 || py < trimy1 || py >= trimy2) { pix3[0] = pix1[0] / 2; pix3[1] = pix1[1] / 2; pix3[2] = pix1[2] / 2; } else memcpy(pix3,pix1,pcc); } for (py = oy1; py < oy2; py++) // left band for (px = ox1; px < nx1; px++) { pix1 = PXMpix(E9pxm,px,py); pix3 = PXMpix(E3pxm,px,py); if (px < trimx1 || px >= trimx2 || py < trimy1 || py >= trimy2) { pix3[0] = pix1[0] / 2; pix3[1] = pix1[1] / 2; pix3[2] = pix1[2] / 2; } else memcpy(pix3,pix1,pcc); } cairo_t *cr = draw_context_create(gdkwin,draw_context); Fpaint3(ox1,oy1,ox2-ox1,ny1-oy1,cr); // 4 updated rectangles Fpaint3(nx2,oy1,ox2-nx2,oy2-oy1,cr); Fpaint3(ox1,ny2,ox2-ox1,oy2-ny2,cr); Fpaint3(ox1,oy1,nx1-ox1,oy2-oy1,cr); drawlines(cr); // draw trim margin lines erase_topcircles(); // erase center circle rad = 15 / Mscale; // repaint center area rad2 = 2 * rad + 1; cx = (ptrimx1 + ptrimx2) / 2; cy = (ptrimy1 + ptrimy2) / 2; Fpaint3(cx-rad,cy-rad,rad2,rad2,cr); cx = (trimx1 + trimx2) / 2; // draw center circle cy = (trimy1 + trimy2) / 2; add_topcircle(cx,cy,10); draw_topcircles(cr); draw_context_destroy(draw_context); ptrimx1 = trimx1; // set prior trim rectangle ptrimx2 = trimx2; // from current trim rectangle ptrimy1 = trimy1; ptrimy2 = trimy2; if (gridsettings[currgrid][GON]) Rflag++; // refresh gridlines if (ox1 == 0 || oy1 == 0 || ox2 == E3ww || oy2 == E3hh) Rflag++; // insure edges are refreshed if (Rflag) Fpaint2(); // refresh entire image return; } // final trim - cut margins off // E3 is the input image, rotated // E3 is the output image from user trim rectangle void trimrotate::trim_final() { using namespace trimrotate; int px1, py1, px2, py2; float *pix3, *pix9; int nc = E3pxm->nc, pcc = nc * sizeof(float); int ww, hh, nn; ww = trimww; // save final width/height in user dialog hh = trimhh; if (rotate_angle != 0) { // final rotate rotate_angle = 0; // insure goal difference rotate_func(0); // slow high quality rotate sharp_GR_callable(50,10); // E3 = sharpened E3 } nn = ww - (trimx2 - trimx1); // adjust trim rectangle to hit trimx1 -= nn/2; // target width and height if (trimx1 < 0) trimx1 = 0; trimx2 = trimx1 + ww; if (trimx2 > E3ww) trimx2 = E3ww; nn = hh - (trimy2 - trimy1); trimy1 -= nn/2; if (trimy1 < 0) trimy1 = 0; trimy2 = trimy1 + hh; if (trimy2 > E3hh) trimy2 = E3hh; if (E9pxm) PXM_free(E9pxm); E9pxm = PXM_make(ww,hh,nc); // new pixmap with requested size for (py1 = trimy1; py1 < trimy2; py1++) // copy E3 (rotated) to new size for (px1 = trimx1; px1 < trimx2; px1++) { px2 = px1 - trimx1; py2 = py1 - trimy1; pix3 = PXMpix(E3pxm,px1,py1); pix9 = PXMpix(E9pxm,px2,py2); memcpy(pix9,pix3,pcc); } PXM_free(E3pxm); E3pxm = E9pxm; E9pxm = 0; E3ww = E3pxm->ww; // update E3 dimensions E3hh = E3pxm->hh; CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); return; } // rotate function // E3 = E1 with rotation // fast: 0 = slow / high quality // 1 = fast / low quality void trimrotate::rotate_func(int fast) { using namespace trimrotate; zdialog *zd = EFtrimrotate.zd; if (rotate_goal == rotate_angle) return; 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(E3pxm); E3pxm = PXM_rotate(E1pxm,rotate_goal,fast); // fast low quality rotate rotate_angle = rotate_goal; if (fast) Ftimer2 = get_seconds(); // flag watchdog timer 20.0 else Ftimer2 = 0; if (E9pxm) PXM_free(E9pxm); // E9 invalid E9pxm = 0; E3ww = E3pxm->ww; // update E3 dimensions E3hh = E3pxm->hh; if (trimx2 > E3ww) trimx2 = E3ww; // contract trim rectangle if needed if (trimy2 > E3hh) trimy2 = E3hh; trimww = trimx2 - trimx1; // new rectangle dimensions trimhh = trimy2 - trimy1; zdialog_stuff(zd,"width",trimww*Zratio); // stuff width, height zdialog_stuff(zd,"height",trimhh*Zratio); ptrimx1 = 0; // set prior trim rectangle ptrimy1 = 0; ptrimx2 = E3ww; // = 100% of image ptrimy2 = E3hh; CEF->Fmods++; Fpaintnow(); return; } // draw lines on image: trim rectangle, guidelines, gridlines void trimrotate::drawlines(cairo_t *cr) { using namespace trimrotate; Ntoplines = 4; // outline trim rectangle toplines[0].x1 = trimx1; toplines[0].y1 = trimy1; toplines[0].x2 = trimx2; toplines[0].y2 = trimy1; toplines[0].type = 1; toplines[1].x1 = trimx2; toplines[1].y1 = trimy1; toplines[1].x2 = trimx2; toplines[1].y2 = trimy2; toplines[1].type = 1; toplines[2].x1 = trimx2; toplines[2].y1 = trimy2; toplines[2].x2 = trimx1; toplines[2].y2 = trimy2; toplines[2].type = 1; toplines[3].x1 = trimx1; toplines[3].y1 = trimy2; toplines[3].x2 = trimx1; toplines[3].y2 = trimy1; toplines[3].type = 1; if (Fguidelines) { // vert/horz rotate guidelines Ntoplines = 6; toplines[4].x1 = guidelineX; toplines[4].y1 = 0; toplines[4].x2 = guidelineX; toplines[4].y2 = E3hh-1; toplines[4].type = 2; toplines[5].x1 = 0; toplines[5].y1 = guidelineY; toplines[5].x2 = E3ww-1; toplines[5].y2 = guidelineY; toplines[5].type = 2; } draw_toplines(1,cr); // draw trim rectangle and guidelines return; } // auto-trim image - set trim rectangle to exclude transparent regions void trimrotate::autotrim() { using namespace trimrotate; PXM *pxm = 0; int px1, py1, px2, py2; int qx1, qy1, qx2, qy2; int qx, qy, step1, step2; int area1, area2, Fgrow = 0; float *ppix; if (! E3pxm) return; // no edit image pxm = E3pxm; 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] < 250) continue; // in transparent zones ppix = PXMpix(pxm,qx2,qy1); if (ppix[3] < 250) continue; ppix = PXMpix(pxm,qx2,qy2); if (ppix[3] < 250) continue; ppix = PXMpix(pxm,qx1,qy2); if (ppix[3] < 250) continue; area2 = (qx2 - qx1) * (qy2 - qy1); // look for larger enclosed area if (area2 <= area1) continue; for (qx = qx1; qx < qx2; qx++) { // check top/bottom sides not intersect ppix = PXMpix(pxm,qx,qy1); // transparent zones if (ppix[3] < 250) break; ppix = PXMpix(pxm,qx,qy2); if (ppix[3] < 250) break; } if (qx < qx2) continue; for (qy = qy1; qy < qy2; qy++) { // also left/right sides ppix = PXMpix(pxm,qx1,qy); if (ppix[3] < 250) break; ppix = PXMpix(pxm,qx2,qy); if (ppix[3] < 250) break; } if (qy < qy2) continue; Fgrow = 1; // successfully grew the rectangle px1 = qx1; // new bigger rectangle coordinates py1 = qy1; // for the next search iteration px2 = qx2; py2 = qy2; goto breakout; // leave all 4 loops } breakout: if (! Fgrow) break; } if (step2 == 1) break; // done step1 = 0.6 * step1; // reduce search step size if (step1 < 2) step1 = 2; step2 = 0.2 * step1; if (step2 < 1) step2 = 1; } trimx1 = px1; // set parameters for trim function trimx2 = px2; trimy1 = py1; trimy2 = py2; trimww = px2 - px1; trimhh = py2 - py1; return; } // dialog to set custom trim button names and corresponding ratios void trimrotate::trim_customize() { using namespace trimrotate; char text[20], blab[8], rlab[8]; float r1, r2, ratio; cchar *pp; int ii, zstat; /*** ___________________________________________________________ | Trim Buttons | | | | label [ 5:4 ] [ 4:3 ] [ 8:5 ] [ 16:9 ] [ 2:1 ] [ gold ] | | ratio [ 5:4 ] [ 4:3 ] [ 8:5 ] [ 16:9 ] [ 2:1 ] [1.62:1] | | | | [Done] [Cancel] | |___________________________________________________________| ***/ zdialog *zd = zdialog_new(E2X("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",E2X("label"),"space=3"); zdialog_add_widget(zd,"label","ratio","hbr",E2X("ratio"),"space=3"); strcpy(blab,"butt-0"); strcpy(rlab,"ratio-0"); for (ii = 0; ii < 5; ii++) // add current trimbuttons to dialog { blab[5] = '0' + ii; rlab[6] = '0' + ii; zdialog_add_widget(zd,"zentry",blab,"hbb",trimbuttons[ii],"size=4"); zdialog_add_widget(zd,"zentry",rlab,"hbr",trimratios[ii],"size=4"); } zdialog_set_modal(zd); zdialog_run(zd,0,"mouse"); // run dialog zstat = zdialog_wait(zd); // wait for complete if (zstat == 1) // done { for (ii = 0; ii < 5; 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 < 5; ii++) // get new ratios { rlab[6] = '0' + ii; zdialog_fetch(zd,rlab,text,12); strTrim2(text); r1 = r2 = ratio = 0; pp = strField(text,':',1); if (! pp) continue; r1 = atof(pp); pp = strField(text,':',2); if (! pp) continue; r2 = atof(pp); if (r1 > 0 && r2 > 0) ratio = r1/r2; if (ratio < 0.1 || ratio > 10) continue; zfree(trimratios[ii]); trimratios[ii] = zstrdup(text); } } zdialog_free(zd); // kill dialog save_params(); // save parameters return; } /********************************************************************************/ // Upright a rotated image: -90, +90, 180 degrees. // This is not an edit transaction: file is replaced and re-opened. // No loss of resolution. void m_upright(GtkWidget *, cchar *menu) // 20.0 auto-upright only { int angle = 0, nn; char orientation = 0, *ppv[1]; cchar *exifkey[1] = { exif_orientation_key }; cchar *exifdata[1]; cchar *message = E2X("rotation unknown"); F1_help_topic = "upright"; if (checkpend("all")) return; if (clicked_file) { // use clicked file if present if (! curr_file || ! strmatch(clicked_file,curr_file)) // bugfix 19.5 f_open(clicked_file,0,0,1,0); // avoid f_open() re-entry clicked_file = 0; } if (! curr_file) return; // use current file 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) { nn = zdialog_choose(Mwin,"mouse",message,"-90°","+90°","180°","quit",null); // 20.0 if (nn == 1) angle = -90; else if (nn == 2) angle = +90; else if (nn == 3) angle = 180; else return; } Fblock = 1; E0pxm = PXM_load(curr_file,1); // load poss. 16-bit image if (E0pxm) { E3pxm = PXM_rotate(E0pxm,angle); // rotate (threaded) PXM_free(E0pxm); E0pxm = E3pxm; E3pxm = 0; } f_save(curr_file,curr_file_type,curr_file_bpc,0,1); // replace file exifdata[0] = (char *) ""; // remove EXIF orientation data exif_put(curr_file,exifkey,exifdata,1); // (f_save() omits if no edit made) update_image_index(curr_file); // update index data Fblock = 0; f_open(curr_file); gallery(curr_file,"paint",0); // repaint gallery return; } /********************************************************************************/ // Retouch // Adjust all aspects of brightness, contrast, color: // Black level, white balance, brightness, contrast, color temperature, color saturation // overhauled v.20.0 namespace retouch_names { editfunc EFretouch; float blackR, blackG, blackB; // black level RGB values float Rbscale, Gbscale, Bbscale; // scaled RGB factors - black clip float wbalR, wbalG, wbalB; // white balance RGB factors int Fblack; // flag, black level was set int sampWB, sampBL; // sample size, % pixels int spotWB, spotBL; // spot white balance or black level active float tempR, tempG, tempB; // illumination temperature RGB factors float combR, combG, combB; // combined RGB factors float brightness; // brightness input, -1 ... +1 float contrast; // contrast input, -1 ... +1 float colorsat; // saturation input, -1 ... +1 = saturated int colortemp; // illumination temp. input, 1K ... 8K int Fapply; // flag, apply dialog controls to image float whitebalance[4] = { 1.0, 1.0, 1.0, 5000 }; // white balance RGB factors, deg. K } // menu function void m_retouch(GtkWidget *, cchar *menu) { using namespace retouch_names; void retouch_mousefunc(); int retouch_dialog_event(zdialog* zd, cchar *event); void retouch_curvedit(int spc); void * retouch_thread(void *); GtkWidget *drawwin_scale; F1_help_topic = "retouch"; EFretouch.menuname = menu; EFretouch.menufunc = m_retouch; EFretouch.funcname = "retouch"; EFretouch.FprevReq = 1; // use preview EFretouch.Farea = 2; // select area usable EFretouch.Frestart = 1; // restart allowed EFretouch.FusePL = 1; // use with paint/lever edits OK EFretouch.Fscript = 1; // scripting supported EFretouch.threadfunc = retouch_thread; EFretouch.mousefunc = retouch_mousefunc; if (! edit_setup(EFretouch)) return; // setup edit /*** _______________________________________________ | Retouch | | ___________________________________________ | | | | | | | | | // 5 curves are maintained: | | | | // 0: current display curve | | | | // 1: curve for overall brightness | | 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 | | | [_] Auto black level [__] sample % | // auto black level | [_] Auto white balance [__] sample % | // auto white balance | [x] Click dark spot for black level | // click dark spot | [x] Click gray spot for white balance | // click gray spot | [_] Click for RGB distribution | // show RGB distribution in popup window | | | Brightness =============[]================= | // brightness | Contrast ===============[]=============== | // contrast | Saturation =================[]============ | // saturation B/W ... saturated | Temperature ===============[]============== | // illumination temperature | | | Settings File [Load] [Save] | | | | [Reset] [Prev] [Done] [Cancel] | |_______________________________________________| ***/ zdialog *zd = zdialog_new(E2X("Retouch"),Mwin,Breset,Bprev,Bdone,Bcancel,null); EFretouch.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"); // (o) all (o) red (o) green (o) blue zdialog_add_widget(zd,"radio","all","hbrgb",Ball,"space=5"); zdialog_add_widget(zd,"radio","red","hbrgb",Bred,"space=3"); zdialog_add_widget(zd,"radio","green","hbrgb",Bgreen,"space=3"); zdialog_add_widget(zd,"radio","blue","hbrgb",Bblue,"space=3"); zdialog_add_widget(zd,"hbox","hbabl","dialog"); // black level zdialog_add_widget(zd,"zbutton","autoBL","hbabl",E2X("Auto black level"),"space=3"); zdialog_add_widget(zd,"label","space","hbabl",0,"space=5"); zdialog_add_widget(zd,"zspin","sampBL","hbabl","1|50|1|1","space=3|size=3"); zdialog_add_widget(zd,"label","labsamp","hbabl",E2X("sample %")); zdialog_add_widget(zd,"hbox","hbawb","dialog"); // white balance zdialog_add_widget(zd,"zbutton","autoWB","hbawb",E2X("Auto white balance"),"space=3"); zdialog_add_widget(zd,"label","space","hbawb",0,"space=5"); zdialog_add_widget(zd,"zspin","sampWB","hbawb","1|50|1|1","space=3|size=3"); zdialog_add_widget(zd,"label","labsamp","hbawb",E2X("sample %")); zdialog_add_widget(zd,"hbox","hbwb","dialog"); // click dark spot zdialog_add_widget(zd,"check","spotWB","hbwb",E2X("Click gray spot for white balance"),"space=3"); zdialog_add_widget(zd,"hbox","hbbl","dialog"); // click gray spot zdialog_add_widget(zd,"check","spotBL","hbbl",E2X("Click dark spot for black level"),"space=3"); zdialog_add_widget(zd,"hbox","hbdist","dialog"); // click RGB distribution zdialog_add_widget(zd,"zbutton","dist","hbdist",E2X("Click for RGB distribution"),"space=3"); zdialog_add_widget(zd,"hbox","hbcolor","dialog",0,"space=8"); zdialog_add_widget(zd,"label","space","hbcolor",0,"space=1"); zdialog_add_widget(zd,"vbox","vbcolor1","hbcolor",0,"homog"); zdialog_add_widget(zd,"label","space","hbcolor",0,"space=1"); zdialog_add_widget(zd,"vbox","vbcolor2","hbcolor",0,"homog|expand"); zdialog_add_widget(zd,"label","labrite","vbcolor1",E2X("Brightness")); zdialog_add_widget(zd,"label","labcont","vbcolor1",E2X("Contrast")); zdialog_add_widget(zd,"label","labsat1","vbcolor1",E2X("Saturation")); zdialog_add_widget(zd,"label","labtemp1","vbcolor1",E2X("Temperature")); 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","colorsat","vbcolor2","-1.0|1.0|0.01|0"); zdialog_add_widget(zd,"hscale","colortemp","vbcolor2","2000|8000|1|5000"); zdialog_add_widget(zd,"hbox","hbset","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labset","hbset",E2X("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,Bprevtip); zdialog_rescale(zd,"brightness",-1,0,1); // expand scale around neutral values zdialog_rescale(zd,"contrast",-1,0,1); zdialog_rescale(zd,"colorsat",-1,0,1); GtkWidget *frameH = zdialog_widget(zd,"frameH"); // setup edit curves spldat *sd = splcurve_init(frameH,retouch_curvedit); EFretouch.sd = sd; sd->Nscale = 1; // horizontal line, neutral value sd->xscale[0][0] = 0.00; sd->yscale[0][0] = 0.50; sd->xscale[1][0] = 1.00; sd->yscale[1][0] = 0.50; for (int ii = 0; ii < 4; ii++) // loop curves 0-3 { sd->nap[ii] = 3; // initial curves are neutral sd->vert[ii] = 0; sd->fact[ii] = 0; sd->apx[ii][0] = 0.01; // horizontal lines sd->apy[ii][0] = 0.50; sd->apx[ii][1] = 0.50; sd->apy[ii][1] = 0.50; // curve 0 = overall brightness sd->apx[ii][2] = 0.99; sd->apy[ii][2] = 0.50; // curve 1/2/3 = R/G/B adjustment splcurve_generate(sd,ii); sd->mod[ii] = 0; // mark curve unmodified } sd->Nspc = 4; // 4 curves sd->fact[0] = 1; // curve 0 active zdialog_stuff(zd,"all",1); // stuff default selection, all 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,100,12); G_SIGNAL(drawwin_scale,"draw",brightness_scale,0); wbalR = wbalG = wbalB = 1.0; // neutral white balance colortemp = 5000; // neutral illumination temp. blackbodyRGB(colortemp,tempR,tempG,tempB); // neutral temp. RGB factors combR = combG = combB = 1.0; // neutral combined RGB factors blackR = blackG = blackB = 0.0; // zero black point Fblack = 0; zdialog_stuff(zd,"spotWB",0); // reset mouse click status zdialog_stuff(zd,"spotBL",0); spotWB = spotBL = 0; brightness = 0; // neutral brightness contrast = 0; // neutral contrast colorsat = 0; // neutral saturation Fapply = 0; zdialog_resize(zd,350,450); zdialog_run(zd,retouch_dialog_event,"save"); // run dialog - parallel return; } // dialog event and completion callback function int retouch_dialog_event(zdialog *zd, cchar *event) { using namespace retouch_names; void retouch_mousefunc(); spldat *sd = EFretouch.sd; float bright0, dbrite, cont0, dcont; float dx, dy; int ii; void retouch_autoWB(); void retouch_autoBL(); void retouch_mousefunc(); Fapply = 0; if (strmatch(event,"done")) zd->zstat = 3; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 5; if (strmatch(event,"apply")) Fapply = 1; // from script if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); // get full size image signal_thread(); return 1; } if (zd->zstat == 1) // [reset] { zd->zstat = 0; // keep dialog active zdialog_stuff(zd,"brightness",0); // neutral brightness zdialog_stuff(zd,"contrast",0); // neutral contrast brightness = contrast = 0; zdialog_stuff(zd,"colorsat",0); // neutral saturation colorsat = 0; 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->fact[ii] = 0; sd->apx[ii][0] = 0.01; sd->apy[ii][0] = 0.50; sd->apx[ii][1] = 0.50; sd->apy[ii][1] = 0.50; sd->apx[ii][2] = 0.99; sd->apy[ii][2] = 0.50; splcurve_generate(sd,ii); sd->mod[ii] = 0; // mark curve unmodified } sd->fact[0] = 1; // active curve is 'all' gtk_widget_queue_draw(sd->drawarea); // redraw curves zdialog_stuff(zd,"all",1); zdialog_stuff(zd,"red",0); zdialog_stuff(zd,"green",0); zdialog_stuff(zd,"blue",0); zdialog_stuff(zd,"spotWB",0); zdialog_stuff(zd,"spotBL",0); wbalR = wbalG = wbalB = 1.0; // neutral white balance colortemp = 5000; // neutral illumination temp. zdialog_stuff(zd,"colortemp",colortemp); blackbodyRGB(colortemp,tempR,tempG,tempB); // neutral temp. RGB factors combR = combG = combB = 1.0; // neutral combined RGB factors blackR = blackG = blackB = 0.0; // zero black point Fblack = 0; edit_reset(); return 1; // 19.0 } if (zd->zstat == 2) // [prev] restore previous settings { zd->zstat = 0; // keep dialog active func_load_prev_widgets(zd,sd,"retouch"); wbalR = whitebalance[0]; // use previous white balance settings wbalG = whitebalance[1]; wbalB = whitebalance[2]; colortemp = whitebalance[3]; zdialog_stuff(zd,"colortemp",colortemp); Fapply = 1; // trigger apply event } if (zd->zstat == 3) // [done] { freeMouse(); if (CEF->Fmods) { edit_fullsize(); // get full size image signal_thread(); // apply changes func_save_last_widgets(zd,sd,"retouch"); // save settings edit_done(0); // complete edit whitebalance[0] = wbalR; // save settings for next time whitebalance[1] = wbalG; whitebalance[2] = wbalB; whitebalance[3] = colortemp; } else edit_cancel(0); return 1; } if (zd->zstat < 0 || zd->zstat > 3) { // [cancel] or [x] edit_cancel(0); return 1; } if (strmatch(event,"load")) // load all settings from a file func_load_widgets(zd,sd,"retouch",null); if (strmatch(event,"save")) // save all settings to a file func_save_widgets(zd,sd,"retouch",null); if (strmatch(event,"autoWB")) { // auto white balance zdialog_fetch(zd,"sampWB",sampWB); // % brightest pixels to sample zdialog_stuff(zd,"spotWB",0); zdialog_stuff(zd,"spotBL",0); spotWB = spotBL = 0; retouch_autoWB(); } if (strmatch(event,"autoBL")) { // auto black level zdialog_fetch(zd,"sampBL",sampBL); // % darkest pixels to sample zdialog_stuff(zd,"spotWB",0); zdialog_stuff(zd,"spotBL",0); spotWB = spotBL = 0; retouch_autoBL(); } if (strmatch(event,"spotWB")) { // spot white balance checkbox zdialog_fetch(zd,"spotWB",spotWB); if (spotWB) { zdialog_stuff(zd,"spotBL",0); spotBL = 0; } } if (strmatch(event,"spotBL")) { // spot black level checkbox zdialog_fetch(zd,"spotBL",spotBL); if (spotBL) { zdialog_stuff(zd,"spotWB",0); spotWB = 0; } } if (zstrstr("spotWB spotBL",event)) { // spot white balance or black level if (spotWB || spotBL) // if either is on, takeMouse(retouch_mousefunc,dragcursor); // connect mouse function else freeMouse(); // both off: free mouse return 1; } if (strmatch(event,"dist")) m_RGB_dist(0,0); // popup RGB distribution window if (zstrstr("all red green blue",event)) // new choice of curve { zdialog_fetch(zd,event,ii); if (! ii) return 0; // button OFF event, wait for ON event for (ii = 0; ii < 4; ii++) sd->fact[ii] = 0; ii = strmatchV(event,"all","red","green","blue",null); ii = ii-1; // new active curve: 0, 1, 2, 3 sd->fact[ii] = 1; splcurve_generate(sd,ii); // regenerate curve gtk_widget_queue_draw(sd->drawarea); // draw curve } if (strmatch(event,"brightness")) // brightness slider, 0 ... 1 { bright0 = brightness; zdialog_fetch(zd,"brightness",brightness); dbrite = brightness - bright0; // change in brightness, + - zdialog_stuff(zd,"all",1); // active curve is "all" sd->fact[0] = 1; for (ii = 1; ii < 4; ii++) sd->fact[ii] = 0; for (ii = 0; ii < sd->nap[0]; ii++) // update curve 0 nodes from slider { dy = sd->apy[0][ii] + 0.5 * dbrite; // increment brightness if (dy < 0) dy = 0; if (dy > 1) dy = 1; sd->apy[0][ii] = dy; } splcurve_generate(sd,0); // regenerate curve 0 gtk_widget_queue_draw(sd->drawarea); // draw curve 0 } if (strmatch(event,"contrast")) // contrast slider, 0 ... 1 { cont0 = contrast; zdialog_fetch(zd,"contrast",contrast); dcont = contrast - cont0; // change in contrast, + - zdialog_stuff(zd,"all",1); // active curve is "all" sd->fact[0] = 1; for (ii = 1; ii < 4; ii++) sd->fact[ii] = 0; for (ii = 0; ii < sd->nap[0]; ii++) // update curve 0 nodes from slider { dx = sd->apx[0][ii]; // 0 ... 0.5 ... 1 if (dx < 0.0 || dx >= 1.0) continue; dy = dcont * (dx - 0.5); // -0.5 ... 0 ... +0.5 * dcont dy += sd->apy[0][ii]; if (dy < 0) dy = 0; if (dy > 1) dy = 1; sd->apy[0][ii] = dy; } splcurve_generate(sd,0); // regenerate curve 0 gtk_widget_queue_draw(sd->drawarea); // draw curve 0 } if (zstrstr("brightness contrast colortemp colorsat " // detect change "autoWB autoBL blendwidth load",event)) Fapply = 1; if (! Fapply) return 1; // wait for change zdialog_fetch(zd,"colortemp",colortemp); // get illumination temp. setting blackbodyRGB(colortemp,tempR,tempG,tempB); // generate temp. adjustments zdialog_fetch(zd,"brightness",brightness); // get brightness setting zdialog_fetch(zd,"contrast",contrast); // get contrast setting zdialog_fetch(zd,"colorsat",colorsat); // get saturation setting signal_thread(); // update the image return 1; } // this function is called when a curve is edited void retouch_curvedit(int spc) { using namespace retouch_names; signal_thread(); return; } // use brightest pixels to estimate RGB values of illumination // and use these to adjust image RGB values for neutral illumination void retouch_autoWB() { using namespace retouch_names; int pixR[256], pixG[256], pixB[256]; // pixel counts per RGB value 0-255 int samplesize; // sample size, pixel count int E1ww, E1hh; // image dimensions int px, py, ii, dist; int sampR, sampG, sampB; double sumR, sumG, sumB; float meanR, meanG, meanB; float *pix; for (ii = 0; ii < 256; ii++) pixR[ii] = pixG[ii] = pixB[ii] = 0; E1ww = E1pxm->ww; // get image dimensions E1hh = E1pxm->hh; for (py = 0; py < E1hh; py++) // scan image file, get pixel counts for (px = 0; px < E1ww; px++) // per RGB value 0-255 { 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 } pix = PXMpix(E1pxm,px,py); ii = pix[0]; // pixel R value 0-255 pixR[ii]++; // accumulate pixel count for R value ii = pix[1]; pixG[ii]++; ii = pix[2]; pixB[ii]++; } samplesize = E1ww * E1hh; // image pixel count if (sa_stat == 3) samplesize = sa_Npixel; // use area count samplesize = samplesize * 0.01 * sampWB; // % sample size >> sample pixel count sampR = sampG = sampB = 0; sumR = sumG = sumB = 0; for (ii = 254; ii > 0; ii--) // sample brightest % pixels { // (avoid blowout value 255) if (sampR < samplesize) { sampR += pixR[ii]; // pixels with R value ii sumR += ii * pixR[ii]; // sum product } if (sampG < samplesize) { sampG += pixG[ii]; sumG += ii * pixG[ii]; } if (sampB < samplesize) { sampB += pixB[ii]; sumB += ii * pixB[ii]; } } meanR = sumR / sampR; // mean RGB for brightest % pixels meanG = sumG / sampG; meanB = sumB / sampB; wbalR = meanG / meanR; // = 1.0 if all RGB are equal wbalG = meanG / meanG; // <1/>1 if RGB should be less/more wbalB = meanG / meanB; return; } // use darkest pixels to estimate black point and reduce image RGB values void retouch_autoBL() { using namespace retouch_names; int pixR[256], pixG[256], pixB[256]; // pixel counts per RGB value 0-255 int samplesize; // sample size, pixel count int E1ww, E1hh; // image dimensions int px, py, ii, dist; int sampR, sampG, sampB; double sumR, sumG, sumB; float meanR, meanG, meanB; float *pix; for (ii = 0; ii < 256; ii++) pixR[ii] = pixG[ii] = pixB[ii] = 0; E1ww = E1pxm->ww; // get image dimensions E1hh = E1pxm->hh; for (py = 0; py < E1hh; py++) // scan image file, get pixel counts for (px = 0; px < E1ww; px++) // per RGB value 0-255 { 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 } pix = PXMpix(E1pxm,px,py); ii = pix[0]; // pixel R value 0-255 pixR[ii]++; // accumulate pixel count for R value ii = pix[1]; pixG[ii]++; ii = pix[2]; pixB[ii]++; } samplesize = E1ww * E1hh; // image pixel count if (sa_stat == 3) samplesize = sa_Npixel; // use area count samplesize = samplesize * 0.01 * sampBL; // % sample size >> sample pixel count sampR = sampG = sampB = 0; sumR = sumG = sumB = 0; for (ii = 0; ii < 100; ii++) // sample darkest % pixels { if (sampR < samplesize) { sampR += pixR[ii]; // pixels with R value ii sumR += ii * pixR[ii]; // sum product } if (sampG < samplesize) { sampG += pixG[ii]; sumG += ii * pixG[ii]; } if (sampB < samplesize) { sampB += pixB[ii]; sumB += ii * pixB[ii]; } } meanR = sumR / sampR; // mean RGB for darkest % pixels meanG = sumG / sampG; meanB = sumB / sampB; blackR = meanR; // set black RGB levels blackG = meanG; blackB = meanB; Fblack = 1; return; } // get nominal white color or black point from mouse click position void retouch_mousefunc() // mouse function { using namespace retouch_names; int px, py, dx, dy; float red, green, blue; float *ppix; char mousetext[60]; if (! LMclick) return; LMclick = 0; px = Mxclick; // mouse click position py = Myclick; if (px < 2) px = 2; // pull back from edge if (px > E3pxm->ww-3) px = E3pxm->ww-3; if (py < 2) py = 2; if (py > E3pxm->hh-3) py = E3pxm->hh-3; red = green = blue = 0; for (dy = -1; dy <= 1; dy++) // 3x3 block around mouse position for (dx = -1; dx <= 1; dx++) { ppix = PXMpix(E1pxm,px+dx,py+dy); // input image red += ppix[0]; green += ppix[1]; blue += ppix[2]; } red = red / 9.0; // mean RGB levels green = green / 9.0; blue = blue / 9.0; snprintf(mousetext,60,"3x3 pixels RGB: %.0f %.0f %.0f \n",red,green,blue); poptext_mouse(mousetext,10,10,0,3); if (spotWB && (red<5 || red>250 || green<5 || green>250 || blue<5 || blue>250)) { // refuse unscalable spot zmessageACK(Mwin,E2X("choose a better spot")); return; } if (spotWB) { // click pixel is new gray/white wbalR = green / red; // = 1.0 if all RGB are equal wbalG = green / green; // <1/>1 if RGB should be less/more wbalB = green / blue; signal_thread(); // trigger image update } if (spotBL) { // click pixel is new black point blackR = red; blackG = green; blackB = blue; Fblack = 1; signal_thread(); // update image } return; } // thread function void * retouch_thread(void *arg) { using namespace retouch_names; void * retouch_wthread(void *); while (true) { thread_idle_loop(); // wait for work or exit request combR = wbalR * tempR / 256.0; // combine white balance colors combG = wbalG * tempG / 256.0; // and illumination temperature. combB = wbalB * tempB / 256.0; // <1/>1 if RGB should be less/more Rbscale = Gbscale = Bbscale = 1.0; if (Fblack) { // if RGB black points defined, Rbscale = 256.0 / (256.0 - blackR); // rescale for full brightness Gbscale = 256.0 / (256.0 - blackG); Bbscale = 256.0 / (256.0 - blackB); } while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 do_wthreads(retouch_wthread,NWT); paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window CEF->Fmods++; // image3 modified CEF->Fsaved = 0; } return 0; // not executed, stop g++ warning } void * retouch_wthread(void *arg) // worker thread function { using namespace retouch_names; int index = *((int *) arg); int ii, dist = 0, px, py; float *pix1, *pix3; float R1, G1, B1, maxRGB; float R3, G3, B3; float pixbrite, ff, F1, F2; float coeff = 1000.0 / 256.0; spldat *sd = EFretouch.sd; 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 R1 = R3 = pix1[0]; // input RGB values G1 = G3 = pix1[1]; B1 = B3 = pix1[2]; // apply RGB black points to input RGB values if (Fblack) { R3 = R3 - blackR; // clip black value at low end R3 = R3 * Rbscale; // rescale so that high end = 256 if (R3 < 0) R3 = 0; G3 = G3 - blackG; G3 = G3 * Gbscale; if (G3 < 0) G3 = 0; B3 = B3 - blackB; B3 = B3 * Bbscale; if (B3 < 0) B3 = 0; } // apply white balance and temperature color shifts // dampen as RGB values approach 256 if (combR > 1) { F1 = (256 - R3) / 256; // R1 = 0...256 >> F1 = 1...0 F2 = F1 * combR + 1 - F1; // F2 = combR ... 1 R3 = F2 * R3; // R3 is adjusted R1 } else R3 = combR * R3; if (combG > 1) { // same for G3 and B3 F1 = (256 - G3) / 256; F2 = F1 * combG + 1 - F1; G3 = F2 * G3; } else G3 = combG * G3; if (combB > 1) { F1 = (256 - B3) / 256; F2 = F1 * combB + 1 - F1; B3 = F2 * B3; } else B3 = combB * B3; // apply saturation color shift if (colorsat != 0) { pixbrite = 0.333 * (R3 + G3 + B3); // pixel brightness, 0 to 255.9 R3 = R3 + colorsat * (R3 - pixbrite); // colorsat is -1 ... +1 G3 = G3 + colorsat * (G3 - pixbrite); B3 = B3 + colorsat * (B3 - pixbrite); } // underflow/overflow test removed v.20.0 // apply brightness/contrast curves if (sd->mod[0]) // curve was modified { pixbrite = R3; // use max. RGB value if (G3 > pixbrite) pixbrite = G3; if (B3 > pixbrite) pixbrite = B3; ii = coeff * pixbrite; // "all" curve index 0-999 if (ii < 0) ii = 0; if (ii > 999) ii = 999; ff = 2.0 * sd->yval[0][ii]; // 0.0 - 2.0 R3 = ff * R3; G3 = ff * G3; B3 = ff * B3; } if (sd->mod[1]) { ii = coeff * R3; // additional RGB curve adjustments if (ii < 0) ii = 0; if (ii > 999) ii = 999; R3 = R3 * (0.5 + sd->yval[1][ii]); // 0.5 - 1.5 } if (sd->mod[2]) { ii = coeff * G3; if (ii < 0) ii = 0; if (ii > 999) ii = 999; G3 = G3 * (0.5 + sd->yval[2][ii]); } if (sd->mod[3]) { ii = coeff * B3; if (ii < 0) ii = 0; if (ii > 999) ii = 999; B3 = B3 * (0.5 + sd->yval[3][ii]); } 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 maxRGB = R3; if (G3 > maxRGB) maxRGB = G3; if (B3 > maxRGB) maxRGB = B3; R3 = R3 * 255.9 / maxRGB; G3 = G3 * 255.9 / maxRGB; B3 = B3 * 255.9 / maxRGB; } // select area edge blending if (sa_stat == 3 && dist < sa_blendwidth) { F2 = sa_blendfunc(dist); F1 = 1.0 - F2; R3 = F2 * R3 + F1 * R1; G3 = F2 * G3 + F1 * G1; B3 = F2 * B3 + F1 * B1; } pix3[0] = R3; pix3[1] = G3; pix3[2] = B3; } pthread_exit(0); } /********************************************************************************/ // Return relative RGB illumination values for a light source // having a given input temperature of 1000-10000 deg. K // 5000 K is neutral: all returned factors relative to 1.0 void blackbodyRGB(int K, float &R, float &G, float &B) { float kk[19] = { 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, 10.0 }; float r1[19] = { 255, 255, 255, 255, 255, 255, 255, 255, 254, 250, 242, 231, 220, 211, 204, 197, 192, 188, 184 }; float g1[19] = { 060, 148, 193, 216, 232, 242, 249, 252, 254, 254, 251, 245, 239, 233, 228, 224, 220, 217, 215 }; float b1[19] = { 000, 010, 056, 112, 158, 192, 219, 241, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; static int ftf = 1; static float r2[10000], g2[10000], b2[10000]; if (ftf) { // initialize spline curves 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; } /********************************************************************************/ // Resize (rescale) image namespace resize_names { editfunc EFresize; int width, height; // new image size int orgwidth, orgheight; // original image size int pctwidth, pctheight; // percent of original size int Fnewwidth, Fnewheight; // flags, width or height was changed int Flockratio; // flag, W/H ratio is locked float WHratio; } void m_resize(GtkWidget *, cchar *menu) { using namespace resize_names; int resize_dialog_event(zdialog *zd, cchar *event); void * resize_thread(void *); F1_help_topic = "resize"; char rtext[12]; EFresize.menuname = menu; EFresize.menufunc = m_resize; EFresize.funcname = "resize"; EFresize.Frestart = 1; // allow restart EFresize.Fscript = 1; // script supported 20.0 EFresize.threadfunc = resize_thread; // thread function if (! edit_setup(EFresize)) return; // setup edit /**** __________________________________________________ | | | Pixels Percent | | Width [_____] [_____] [Previous] | | Height [_____] [_____] | | | | [_] 3/1 [_] 2/1 [_] 3/2 [_] 1/1 | | [_] 3/4 [_] 2/3 [_] 1/2 [_] 1/3 [_] 1/4 | | | | W/H Ratio: 1.333 Lock [_] | | [done] [cancel] | |__________________________________________________| ****/ zdialog *zd = zdialog_new(E2X("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,"vbox","vb14","hb1",0,"homog|space=10"); zdialog_add_widget(zd,"label","placeholder","vb11",0); zdialog_add_widget(zd,"label","labw","vb11",Bwidth); zdialog_add_widget(zd,"label","labh","vb11",Bheight); zdialog_add_widget(zd,"label","labpix","vb12","Pixels"); zdialog_add_widget(zd,"zspin","width","vb12","20|30000|1|20"); // fotoxx.h limits zdialog_add_widget(zd,"zspin","height","vb12","20|30000|1|20"); zdialog_add_widget(zd,"label","labpct","vb13",Bpercent); zdialog_add_widget(zd,"zspin","pctwidth","vb13","1|400|1|100"); zdialog_add_widget(zd,"zspin","pctheight","vb13","1|400|1|100"); zdialog_add_widget(zd,"button","prev","vb14",E2X("Previous")); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); zdialog_add_widget(zd,"label","space","hb2",0); zdialog_add_widget(zd,"check","3/1","hb2","3/1"); zdialog_add_widget(zd,"check","2/1","hb2","2/1"); zdialog_add_widget(zd,"check","3/2","hb2","3/2"); zdialog_add_widget(zd,"check","1/1","hb2","1/1"); zdialog_add_widget(zd,"hbox","hb3","dialog",0,"space=5"); zdialog_add_widget(zd,"label","space","hb3",0); zdialog_add_widget(zd,"check","3/4","hb3","3/4"); zdialog_add_widget(zd,"check","2/3","hb3","2/3"); zdialog_add_widget(zd,"check","1/2","hb3","1/2"); zdialog_add_widget(zd,"check","1/3","hb3","1/3"); zdialog_add_widget(zd,"check","1/4","hb3","1/4"); zdialog_add_widget(zd,"hbox","hb4","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labratio","hb4",E2X("W/H Ratio:"),"space=5"); zdialog_add_widget(zd,"label","WHratio","hb4","1.6667"); zdialog_add_widget(zd,"check","Flockratio","hb4",E2X("Lock"),"space=10"); zdialog_add_ttip(zd,"prev",Bprevtip); orgwidth = E1pxm->ww; // original width, height orgheight = E1pxm->hh; width = orgwidth; // = initial width, height height = orgheight; WHratio = 1.0 * width / height; // initial W/H ratio pctwidth = pctheight = 100; Fnewwidth = Fnewheight = 0; Flockratio = 1; // W/H ratio initially locked zdialog_stuff(zd,"width",width); zdialog_stuff(zd,"height",height); zdialog_stuff(zd,"pctwidth",pctwidth); zdialog_stuff(zd,"pctheight",pctheight); snprintf(rtext,12,"%.3f",WHratio); zdialog_stuff(zd,"WHratio",rtext); zdialog_stuff(zd,"Flockratio",1); zdialog_stuff(zd,"3/1",0); zdialog_stuff(zd,"2/1",0); zdialog_stuff(zd,"3/2",0); 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_run(zd,resize_dialog_event,"save"); // run dialog return; } // dialog event and completion callback function int resize_dialog_event(zdialog *zd, cchar *event) { using namespace resize_names; int nn; double wwxhh, memreq, memavail; char rtext[12]; int pixcc = 4 * sizeof(float); // pixel size, RGBA if (strmatch(event,"done")) zd->zstat = 1; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 2; // cancel if (strmatch(event,"apply")) { // from script 20.0 zdialog_fetch(zd,"width",width); zdialog_fetch(zd,"height",height); signal_thread(); return 1; } if (zd->zstat) // dialog complete { if (zd->zstat == 1) { // done editresize[0] = width; // remember size used editresize[1] = height; signal_thread(); edit_done(0); } else edit_cancel(0); // cancel or kill Fzoom = 0; return 1; } if (strmatch(event,"focus")) return 1; // ignore focus Fnewwidth = Fnewheight = 0; // reset flags if (strmatch(event,"pctwidth")) { // % width input zdialog_fetch(zd,"pctwidth",nn); if (nn != pctwidth) { width = nn / 100.0 * orgwidth + 0.5; Fnewwidth = 1; // note changed } } if (strmatch(event,"pctheight")) { // same for height zdialog_fetch(zd,"pctheight",nn); if (nn != pctheight) { height = nn / 100.0 * orgheight + 0.5; Fnewheight = 1; } } if (strmatch(event,"width")) { // width input zdialog_fetch(zd,"width",nn); if (nn != width) { width = nn; // set new width Fnewwidth = 1; // note change } } if (strmatch(event,"height")) { // same for height zdialog_fetch(zd,"height",nn); if (nn != height) { height = nn; Fnewheight = 1; } } if (Fnewwidth || Fnewheight) { // width or height was set directly zdialog_stuff(zd,"3/1",0); // uncheck all preset sizes zdialog_stuff(zd,"2/1",0); zdialog_stuff(zd,"3/2",0); zdialog_stuff(zd,"1/1",0); 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); } if (zstrstr("3/1 2/1 3/2 1/1 3/4 2/3 1/2 1/3 1/4",event)) { // a ratio button was selected zdialog_stuff(zd,"3/1",0); // uncheck all preset sizes zdialog_stuff(zd,"2/1",0); zdialog_stuff(zd,"3/2",0); zdialog_stuff(zd,"1/1",0); 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,event,1); // set selected button on if (strmatch(event,"3/1")) { width = 3 * orgwidth; height = 3 * orgheight; } if (strmatch(event,"2/1")) { width = 2 * orgwidth; height = 2 * orgheight; } if (strmatch(event,"3/2")) { width = 1.5 * orgwidth; height = 1.5 * orgheight; } if (strmatch(event,"1/1")) { width = orgwidth; height = orgheight; } if (strmatch(event,"3/4")) { width = 0.75 * orgwidth; height = 0.75 * orgheight; } if (strmatch(event,"2/3")) { width = 0.66667 * orgwidth; height = 0.66667 * orgheight; } if (strmatch(event,"1/2")) { width = 0.5 * orgwidth; height = 0.5 * orgheight; } if (strmatch(event,"1/3")) { width = 0.33333 * orgwidth; height = 0.33333 * orgheight; } if (strmatch(event,"1/4")) { width = 0.25 * orgwidth; height = 0.25 * orgheight; } WHratio = 1.0 * orgwidth / orgheight; // new W/H ratio Flockratio = 1; // lock W/H ratio } if (strmatch("prev",event)) { // Previous size button checked zdialog_stuff(zd,"3/1",0); // uncheck all preset sizes zdialog_stuff(zd,"2/1",0); zdialog_stuff(zd,"3/2",0); zdialog_stuff(zd,"1/1",0); 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); width = editresize[0]; // set previous size height = editresize[1]; WHratio = 1.0 * width / height; // new W/H ratio Flockratio = 1; // lock W/H ratio } if (strmatch(event,"Flockratio")) // toggle W/H ratio lock zdialog_fetch(zd,"Flockratio",Flockratio); if (Flockratio) { // if W/H ratio locked, if (Fnewwidth) height = width / WHratio + 0.5; // force same W/H ratio if (Fnewheight) width = height * WHratio + 0.5; // round 19.0 } if (width < 20) { // enforce lower limits width = 20; height = width / WHratio + 0.5; } if (height < 20) { height = 20; width = height * WHratio + 0.5; } if (width > wwhh_limit1) { // enforce upper limits width = wwhh_limit1; height = width / WHratio + 0.5; } if (height > wwhh_limit1) { height = wwhh_limit1; width = height * WHratio + 0.5; } wwxhh = 1.0 * width * height; // enforce image size limit if (wwxhh > wwhh_limit2) { width *= wwhh_limit2 / wwxhh; height *= wwhh_limit2 / wwxhh; } if (! Flockratio) WHratio = 1.0 * width / height; // track actual W/H ratio pctwidth = 100.0 * width / orgwidth + 0.5; // final % size pctheight = 100.0 * height / orgheight + 0.5; zdialog_stuff(zd,"width",width); // update all zdialog data zdialog_stuff(zd,"height",height); zdialog_stuff(zd,"pctwidth",pctwidth); zdialog_stuff(zd,"pctheight",pctheight); zdialog_stuff(zd,"Flockratio",Flockratio); snprintf(rtext,12,"%.3f",WHratio); zdialog_stuff(zd,"WHratio",rtext); memreq = 1.0 * width * height * pixcc; // memory for rescaled image 19.0 memreq = memreq / 1024; // peak required memory, KB parseprocfile("/proc/meminfo","MemAvailable:",&memavail,0); // est. free memory (+ cache), KB memavail -= 200000; // 200 MB margin if (memreq > memavail) { zmessageACK(Mwin,E2X("insufficient memory, cannot proceed")); edit_cancel(0); return 1; } signal_thread(); // update image, no wait for idle return 1; } // do the resize job void * resize_thread(void *) { using namespace resize_names; PXM *pxmtemp; while (true) { thread_idle_loop(); // wait for signal while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 pxmtemp = PXM_rescale(E1pxm,width,height); // rescale the edit image PXM_free(E3pxm); E3pxm = pxmtemp; CEF->Fmods++; CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); } return 0; } /********************************************************************************/ // Adjust RGB menu function // Adjust Brightness, contrast, and color levels using RGB or CMY colors namespace adjust_RGB_names { editfunc EF_RGB; // edit function data float RGB_inputs[8]; int E3ww, E3hh; } // menu function void m_adjust_RGB(GtkWidget *, cchar *menu) { using namespace adjust_RGB_names; int RGB_dialog_event(zdialog *zd, cchar *event); void * RGB_thread(void *); F1_help_topic = "adjust RGB"; EF_RGB.menuname = menu; EF_RGB.menufunc = m_adjust_RGB; EF_RGB.funcname = "adjust_RGB"; // function name EF_RGB.FprevReq = 1; // use preview EF_RGB.Farea = 2; // select area usable EF_RGB.Frestart = 1; // allow restart EF_RGB.Fscript = 1; // scripting supported EF_RGB.FusePL = 1; // paint/lever edits supported EF_RGB.threadfunc = RGB_thread; // thread function if (! edit_setup(EF_RGB)) return; // setup edit E3ww = E3pxm->ww; E3hh = E3pxm->hh; /*** ________________________________ | | | +Brightness =====[]===== | | +Red -Cyan =====[]===== | | +Green -Magenta =====[]===== | | +Blue -Yellow =====[]===== | | | | Contrast =====[]===== | | Red =====[]===== | | Green =====[]===== | | Blue =====[]===== | | | | [reset] [done] [cancel] | |________________________________| ***/ zdialog *zd = zdialog_new(E2X("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"); zdialog_add_widget(zd,"vbox","vb2","hb2",0,"homog|expand"); zdialog_add_widget(zd,"label","labBriteDens","vb1",E2X("+Brightness")); zdialog_add_widget(zd,"label","labRedDens","vb1",E2X("+Red -Cyan")); zdialog_add_widget(zd,"label","labGreenDens","vb1",E2X("+Green -Magenta")); zdialog_add_widget(zd,"label","labBlueDens","vb1",E2X("+Blue -Yellow")); zdialog_add_widget(zd,"hsep","sep1","vb1"); zdialog_add_widget(zd,"label","labContrast","vb1","Contrast All"); zdialog_add_widget(zd,"label","labRedCon","vb1",E2X("Contrast Red")); zdialog_add_widget(zd,"label","labGreenCon","vb1",E2X("Contrast Green")); zdialog_add_widget(zd,"label","labBlueCon","vb1",E2X("Contrast Blue")); 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,"hsep","sep2","vb2"); zdialog_add_widget(zd,"hscale","Contrast","vb2","-1|+1|0.001|0","expand"); zdialog_add_widget(zd,"hscale","RedCon","vb2","-1|+1|0.001|0","expand"); zdialog_add_widget(zd,"hscale","GreenCon","vb2","-1|+1|0.001|0","expand"); zdialog_add_widget(zd,"hscale","BlueCon","vb2","-1|+1|0.001|0","expand"); zdialog_rescale(zd,"BriteDens",-1,0,+1); zdialog_rescale(zd,"RedDens",-1,0,+1); zdialog_rescale(zd,"GreenDens",-1,0,+1); zdialog_rescale(zd,"BlueDens",-1,0,+1); zdialog_rescale(zd,"Contrast",-1,0,+1); zdialog_rescale(zd,"RedCon",-1,0,+1); zdialog_rescale(zd,"GreenCon",-1,0,+1); zdialog_rescale(zd,"BlueCon",-1,0,+1); zdialog_resize(zd,300,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 { using namespace adjust_RGB_names; int mod = 0; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); E3ww = E3pxm->ww; E3hh = E3pxm->hh; 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); edit_reset(); return 1; // 19.0 } else if (zd->zstat == 2) { // done edit_fullsize(); // get full size image E3ww = E3pxm->ww; E3hh = E3pxm->hh; 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 *) { using namespace adjust_RGB_names; void * RGB_wthread(void *arg); // worker thread while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 if (sa_stat == 3) Fbusy_goal = sa_Npixel; // set up progress monitor else Fbusy_goal = E3ww * E3hh; Fbusy_done = 0; do_wthreads(RGB_wthread,NWT); // worker threads Fbusy_goal = Fbusy_done = 0; CEF->Fmods++; // image modified CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed, stop warning } // worker thread function void * RGB_wthread(void *arg) // overhauled { using namespace adjust_RGB_names; float R1, G1, B1, R3, G3, B3; float briA, briR, briG, briB; float conA, conR, conG, conB; float R, G, B; int index = *((int *) (arg)); int px, py, ii, dist = 0; float *pix1, *pix3; float cmax, F, f1, f2; briA = RGB_inputs[0]; // color brightness inputs, -1 to +1 briR = RGB_inputs[1]; briG = RGB_inputs[2]; briB = RGB_inputs[3]; R = briR - 0.5 * briG - 0.5 * briB + briA; // red = red - green - blue + all G = briG - 0.5 * briR - 0.5 * briB + briA; // etc. B = briB - 0.5 * briR - 0.5 * briG + briA; R += 1; // -1 ... 0 ... +1 >> 0 ... 1 ... 2 G += 1; // increase the range B += 1; briR = R; // final color brightness factors briG = G; briB = B; conA = RGB_inputs[4]; // contrast inputs, -1 to +1 conR = RGB_inputs[5]; conG = RGB_inputs[6]; conB = RGB_inputs[7]; if (conA < 0) conA = 0.5 * conA + 1; // -1 ... 0 >> 0.5 ... 1.0 else conA = conA + 1; // 0 ... 1 >> 1.0 ... 2.0 if (conR < 0) conR = 0.5 * conR + 1; else conR = conR + 1; if (conG < 0) conG = 0.5 * conG + 1; else conG = conG + 1; if (conB < 0) conB = 0.5 * conB + 1; else conB = conB + 1; conR = conR * conA; // apply overall contrast conG = conG * conA; conB = conB * conA; for (py = index; py < E3hh; py += NWT) // loop all image pixels for (px = 0; px < E3ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside area } pix1 = PXMpix(E1pxm,px,py); // input 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_blendwidth) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth 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; } pthread_exit(0); } /********************************************************************************/ // HSL color menu function // Adjust colors using the HSL (hue/saturation/lightness) color model namespace adjust_HSL_names { GtkWidget *RGBframe, *RGBcolor; GtkWidget *Hframe, *Hscale; editfunc EFHSL; // edit function data int E3ww, E3hh; int Huse, Suse, Luse; // "match using" flags, 0 or 1 int Hout, Sout, Lout; // "output color" flags, 0 or 1 float Rm, Gm, Bm; // RGB image color to match float Hm, Sm, Lm; // corresp. HSL color float Mlev; // match level 0..1 = 100% float Hc, Sc, Lc; // new color to add float Rc, Gc, Bc; // corresp. RGB color float Adj; // color adjustment, 0..1 = 100% } // menu function void m_adjust_HSL(GtkWidget *, cchar *menu) // overhauled { using namespace adjust_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.menuname = menu; 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 E3ww = E3pxm->ww; E3hh = E3pxm->hh; /*** ___________________________________________________ | | | Input color to match and adjust: [#####] | | Match using: [] Hue [] Saturation [] Lightness | | Match Level: ==================[]========== 100% | | - - - - - - - - - - - - - - - - - - - - - - - - | | Output Color | | [########] [#################################] | // new color and hue spectrum | [] Color Hue ================[]============== | | [] Saturation =====================[]========= | | [] Lightness ===========[]=================== | | Adjustment ===================[]========== 100% | | | | [reset] [done] [cancel] | |___________________________________________________| ***/ zdialog *zd = zdialog_new(E2X("Adjust HSL"),Mwin,Breset,Bdone,Bcancel,null); EFHSL.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"label","labmatch","hb1",E2X("Input color to match and adjust:"),"space=5"); zdialog_add_widget(zd,"colorbutt","matchRGB","hb1","0|0|0"); zdialog_add_ttip(zd,"matchRGB",E2X("shift+click on image to select color")); zdialog_add_widget(zd,"hbox","hbmu","dialog"); zdialog_add_widget(zd,"label","labmu","hbmu",E2X("Match using:"),"space=5"); zdialog_add_widget(zd,"check","Huse","hbmu",E2X("Hue"),"space=3"); zdialog_add_widget(zd,"check","Suse","hbmu",E2X("Saturation"),"space=3"); zdialog_add_widget(zd,"check","Luse","hbmu",E2X("Lightness"),"space=3"); zdialog_add_widget(zd,"hbox","hbmatch","dialog"); zdialog_add_widget(zd,"label","labmatch","hbmatch",Bmatchlevel,"space=5"); zdialog_add_widget(zd,"hscale","Mlev","hbmatch","0|1|0.001|1.0","expand"); zdialog_add_widget(zd,"label","lab100%","hbmatch","100%","space=4"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"label","laboutput","hb1",E2X("Output Color")); zdialog_add_widget(zd,"hbox","hb2","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb2",0,"homog"); zdialog_add_widget(zd,"vbox","vb2","hb2",0,"homog|expand"); zdialog_add_widget(zd,"frame","RGBframe","vb1",0,"space=1"); // drawing area for RGB color RGBframe = zdialog_widget(zd,"RGBframe"); RGBcolor = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(RGBframe),RGBcolor); gtk_widget_set_size_request(RGBcolor,0,16); G_SIGNAL(RGBcolor,"draw",HSL_RGBcolor,0); zdialog_add_widget(zd,"frame","Hframe","vb2",0,"space=1"); // drawing area for hue scale Hframe = zdialog_widget(zd,"Hframe"); Hscale = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(Hframe),Hscale); gtk_widget_set_size_request(Hscale,200,16); G_SIGNAL(Hscale,"draw",HSL_Hscale,0); zdialog_add_widget(zd,"check","Hout","vb1",E2X("Color Hue")); zdialog_add_widget(zd,"check","Sout","vb1",E2X("Saturation")); zdialog_add_widget(zd,"check","Lout","vb1",E2X("Lightness")); zdialog_add_widget(zd,"label","labadjust","vb1",E2X("Adjustment")); zdialog_add_widget(zd,"hscale","Hc","vb2","0|359.9|0.1|180","expand"); zdialog_add_widget(zd,"hscale","Sc","vb2","0|1|0.001|0.5","expand"); zdialog_add_widget(zd,"hscale","Lc","vb2","0|1|0.001|0.5","expand"); zdialog_add_widget(zd,"hbox","vb2hb","vb2"); zdialog_add_widget(zd,"hscale","Adj","vb2hb","0|1|0.001|0.0","expand"); zdialog_add_widget(zd,"label","lab100%","vb2hb","100%","space=4"); zdialog_stuff(zd,"Huse",1); // default: match on hue and saturation zdialog_stuff(zd,"Suse",1); zdialog_stuff(zd,"Luse",0); zdialog_stuff(zd,"Hout",1); // default: replace only hue zdialog_stuff(zd,"Sout",0); zdialog_stuff(zd,"Lout",0); Rm = Gm = Bm = 0; // color to match = black = not set Hm = Sm = Lm = 0; Huse = Suse = 1; Luse = 0; Hout = 1; Sout = Lout = 0; Mlev = 1.0; Hc = 180; // new HSL color to set / mix Sc = 0.5; Lc = 0.5; Adj = 0.0; zdialog_run(zd,HSL_dialog_event,"save"); // run dialog - parallel takeMouse(HSL_mousefunc,arrowcursor); // connect mouse function return; } // Paint RGBcolor drawing area with RGB color from new HSL color void HSL_RGBcolor(GtkWidget *drawarea, cairo_t *cr, int *) { using namespace adjust_HSL_names; int ww, hh; ww = gtk_widget_get_allocated_width(drawarea); // drawing area size hh = gtk_widget_get_allocated_height(drawarea); HSLtoRGB(Hc,Sc,Lc,Rc,Gc,Bc); // new RGB color cairo_set_source_rgb(cr,Rc,Gc,Bc); cairo_rectangle(cr,0,0,ww-1,hh-1); cairo_fill(cr); return; } // Paint Hscale drawing area with all hue values in a horizontal scale void HSL_Hscale(GtkWidget *drawarea, cairo_t *cr, int *) { using namespace adjust_HSL_names; int px, ww, hh; float H, S, L, R, G, B; ww = gtk_widget_get_allocated_width(drawarea); // drawing area size hh = gtk_widget_get_allocated_height(drawarea); S = L = 0.5; 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 adjust_HSL_names; void HSL_mousefunc(); int mod = 0; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); E3ww = E3pxm->ww; E3hh = E3pxm->hh; signal_thread(); return 1; } if (zd->zstat) { if (zd->zstat == 1) { // reset zd->zstat = 0; // keep dialog active Mlev = 1.0; Hc = 180; // set defaults Sc = 0.5; Lc = 0.5; Adj = 0.0; zdialog_stuff(zd,"Mlev",Mlev); zdialog_stuff(zd,"Hc",Hc); zdialog_stuff(zd,"Sc",Sc); zdialog_stuff(zd,"Lc",Lc); zdialog_stuff(zd,"Adj",Adj); edit_reset(); return 1; // 19.0 } else if (zd->zstat == 2) { // done edit_fullsize(); // get full size image E3ww = E3pxm->ww; E3hh = E3pxm->hh; signal_thread(); edit_done(0); // commit edit return 1; } else { edit_cancel(0); // discard edit return 1; } } if (strmatch("focus",event)) { takeMouse(HSL_mousefunc,arrowcursor); return 1; } if (strmatch(event,"Huse")) { // match on Hue, 0/1 zdialog_fetch(zd,"Huse",Huse); mod = 1; } if (strmatch(event,"Suse")) { // match on Saturation, 0/1 zdialog_fetch(zd,"Suse",Suse); mod = 1; } if (strmatch(event,"Luse")) { // match on Lightness, 0/1 zdialog_fetch(zd,"Luse",Luse); mod = 1; } if (strmatch(event,"Hout")) { // replace Hue, 0/1 zdialog_fetch(zd,"Hout",Hout); mod = 1; } if (strmatch(event,"Sout")) { // replace Saturation, 0/1 zdialog_fetch(zd,"Sout",Sout); mod = 1; } if (strmatch(event,"Lout")) { // replace Lightness, 0/1 zdialog_fetch(zd,"Lout",Lout); mod = 1; } if (strmatch("Mlev",event)) { // color match 0..1 = 100% zdialog_fetch(zd,"Mlev",Mlev); mod = 1; } if (strmatch("Hc",event)) { // new color hue 0-360 zdialog_fetch(zd,"Hc",Hc); mod = 1; } if (strmatch("Sc",event)) { // saturation 0-1 zdialog_fetch(zd,"Sc",Sc); mod = 1; } if (strmatch("Lc",event)) { // lightness 0-1 zdialog_fetch(zd,"Lc",Lc); mod = 1; } if (strmatch("Adj",event)) { // adjustment 0..1 = 100% zdialog_fetch(zd,"Adj",Adj); mod = 1; } if (strmatch("blendwidth",event)) mod = 1; // area blend width changed if (mod) { gtk_widget_queue_draw(RGBcolor); // draw current RGB color signal_thread(); // trigger update thread } return 1; } // mouse function // click on image to set the color to match and change void HSL_mousefunc() { using namespace adjust_HSL_names; int mx, my, px, py; char color[20]; float *pix1, R, G, B; float f256 = 1.0 / 256.0; zdialog *zd = EFHSL.zd; if (! KBshiftkey) return; // check shift + left or right click if (! LMclick && ! RMclick) return; mx = Mxclick; // clicked pixel on image my = Myclick; if (mx < 1) mx = 1; // pull back from image edge if (mx > E3ww - 2) mx = E3ww - 2; if (my < 1) my = 1; if (my > E3hh - 2) my = E3hh - 2; R = G = B = 0; for (py = my-1; py <= my+1; py++) // compute average RGB for 3x3 for (px = mx-1; px <= mx+1; px++) // block of pixels { pix1 = PXMpix(E1pxm,px,py); R += pix1[0]; G += pix1[1]; B += pix1[2]; } R = R / 9; G = G / 9; B = B / 9; if (LMclick) // left mouse click { // pick MATCH color from image LMclick = 0; snprintf(color,19,"%.0f|%.0f|%.0f",R,G,B); // draw new match color button if (zd) zdialog_stuff(zd,"matchRGB",color); Rm = R * f256; Gm = G * f256; Bm = B * f256; RGBtoHSL(Rm,Gm,Bm,Hm,Sm,Lm); // set HSL color to match signal_thread(); // trigger update thread 20.0 } if (RMclick) // right mouse click { // pick OUTPUT color from image RMclick = 0; R = R * f256; G = G * f256; B = B * f256; RGBtoHSL(R,G,B,Hc,Sc,Lc); // output HSL zdialog_stuff(zd,"Hc",Hc); zdialog_stuff(zd,"Sc",Sc); zdialog_stuff(zd,"Lc",Lc); gtk_widget_queue_draw(RGBcolor); // draw current RGB color signal_thread(); // trigger update thread } return; } // thread function - multiple working threads to update image void * HSL_thread(void *) { using namespace adjust_HSL_names; void * HSL_wthread(void *arg); // worker thread while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 do_wthreads(HSL_wthread,NWT); // worker threads CEF->Fmods++; // image modified CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed, stop warning } // worker thread function void * HSL_wthread(void *arg) { using namespace adjust_HSL_names; int index = *((int *) (arg)); int px, py, ii, dist = 0; float *pix1, *pix3; float R1, G1, B1, R3, G3, B3; float H1, S1, L1, H3, S3, L3; float dH, dS, dL, match; float a1, a2, f1, f2; float f256 = 1.0 / 256.0; for (py = index; py < E3hh; py += NWT) // loop all image pixels for (px = 0; px < E3ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside area } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel R1 = f256 * pix1[0]; // input pixel RGB G1 = f256 * pix1[1]; B1 = f256 * pix1[2]; RGBtoHSL(R1,G1,B1,H1,S1,L1); // convert to HSL match = 1.0; // compare image pixel to match HSL if (Huse) { dH = fabsf(Hm - H1); if (360 - dH < dH) dH = 360 - dH; dH *= 0.002778; // H difference, normalized 0..1 match = 1.0 - dH; } if (Suse) { dS = fabsf(Sm - S1); // S difference, 0..1 match *= (1.0 - dS); } if (Luse) { dL = fabsf(Lm - L1); // L difference, 0..1 match *= (1.0 - dL); } a1 = pow(match, 10.0 * Mlev); // color selectivity, 0..1 = max a1 = Adj * a1; a2 = 1.0 - a1; if (Hout) H3 = a1 * Hc + a2 * H1; // output HSL = a1 * new HSL else H3 = H1; // + a2 * old HSL if (Sout) S3 = a1 * Sc + a2 * S1; else S3 = S1; if (Lout) L3 = a1 * Lc + a2 * L1; else L3 = L1; HSLtoRGB(H3,S3,L3,R3,G3,B3); if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth f2 = 1.0 - f1; R3 = f1 * R3 + f2 * R1; G3 = f1 * G3 + f2 * G1; B3 = f1 * B3 + f2 * B1; } pix3[0] = 255.0 * R3; pix3[1] = 255.0 * G3; pix3[2] = 255.0 * B3; } pthread_exit(0); } // HSL to RGB converter (Wikipedia) // H = 0-360 deg. S = 0-1 L = 0-1 // output RGB values = 0-1 void HSLtoRGB(float H, float S, float L, float &R, float &G, float &B) { float C, X, M; float h1, h2; h1 = H / 60; h2 = h1 - 2 * int(h1/2); C = (1 - fabsf(2*L-1)) * S; X = C * (1 - fabsf(h2-1)); M = L - C/2; if (H < 60) { R = C; G = X; B = 0; } else if (H < 120) { R = X; G = C; B = 0; } else if (H < 180) { R = 0; G = C; B = X; } else if (H < 240) { R = 0; G = X; B = C; } else if (H < 300) { R = X; G = 0; B = C; } else { R = C; G = 0; B = X; } R = R + M; G = G + M; B = B + M; return; } // RGB to HSL converter // input RGB values 0-1 // outputs: H = 0-360 deg. S = 0-1 L = 0-1 void RGBtoHSL(float R, float G, float B, float &H, float &S, float &L) { float max, min, D; max = R; if (G > max) max = G; if (B > max) max = B; min = R; if (G < min) min = G; if (B < min) min = B; D = max - min; L = 0.5 * (max + min); if (D < 0.004) { H = S = 0; return; } if (L > 0.5) S = D / (2 - max - min); else S = D / (max + min); if (max == R) H = (G - B) / D; else if (max == G) H = 2 + (B - R) / D; else H = 4 + (R - G) / D; H = H * 60; if (H < 0) H += 360; return; } /********************************************************************************/ // draw on image: text, line/arrow, box, oval void m_markup(GtkWidget *widget, cchar *menu) // 20.0 { int markup_dialog_event(zdialog *zd, cchar *event); zdialog *zd; F1_help_topic = "markup"; /*** ___________________________________ | Image Markup | | | | [_] Draw text on image | | [_] Draw line or arrow on image | | [_] Draw box on image | | [_] Draw oval on image | | | | [cancel] | |___________________________________| ***/ zd = zdialog_new(E2X("Image Markup"),Mwin,Bcancel,0); zdialog_add_widget(zd,"check","text","dialog",E2X("Draw text on image")); zdialog_add_widget(zd,"check","line","dialog",E2X("Draw line or arrow on image")); zdialog_add_widget(zd,"check","box","dialog",E2X("Draw box on image")); zdialog_add_widget(zd,"check","oval","dialog",E2X("Draw oval on image")); zdialog_stuff(zd,"text",0); zdialog_stuff(zd,"line",0); zdialog_stuff(zd,"box",0); zdialog_stuff(zd,"oval",0); zdialog_run(zd,markup_dialog_event,"save"); return; } // dialog event and completion function int markup_dialog_event(zdialog *zd, cchar *event) { if (zd->zstat) { zdialog_free(zd); return 1; } if (! zstrstr("text line box oval",event)) return 1; zdialog_free(zd); if (strmatch(event,"text")) m_draw_text(0,0); if (strmatch(event,"line")) m_draw_line(0,0); if (strmatch(event,"box")) m_draw_box(0,0); if (strmatch(event,"oval")) m_draw_oval(0,0); return 1; } /********************************************************************************/ // add text on top of the image namespace drawtext_names { #define Bversion E2X("+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 EFdrawtext; } // menu function void m_draw_text(GtkWidget *, cchar *menu) { using namespace drawtext_names; cchar *title = E2X("Draw text on image"); cchar *tip = E2X("Enter text, click/drag on image, right click to remove"); F1_help_topic = "draw text"; // user guide topic EFdrawtext.menufunc = m_draw_text; EFdrawtext.funcname = "draw_text"; EFdrawtext.Farea = 1; // select area ignored EFdrawtext.Frestart = 1; // allow restart EFdrawtext.mousefunc = mousefunc; if (! edit_setup(EFdrawtext)) return; // setup edit /*** ____________________________________________________________________ | Draw text on image | | | | Enter text, click/drag on image, right click to remove. | | | | Use settings file [Open] [Save] | Bopen Bsave | Text [____________________________________________________] | text | Use metadata key [________________________________] [Fetch] | metakey Bfetch | [Font] [FreeSans_________] Size [ 44|v] | Bfont fontname fontsize | | | color transp. width angle | | text [#####] [_______] [______] | txcolor txtransp txangle | backing [#####] [_______] | bgcolor bgtransp | outline [#####] [_______] [_______] | tocolor totransp towidth | shadow [#####] [_______] [_______] [______] | shcolor shtransp shwidth shangle | | | [Clear] [Replace] [+Version] [Next] [Apply] [Done] [Cancel] | |____________________________________________________________________| [Clear] clear text and metadata key [Replace] edit_done(), replace current file, restart dialog [+Version] edit_done(), create new file version, restart dialog [Next] edit_done(), replace current file, move to next file, restart dialog, write same text [Apply] edit_done, restart dialog [Done] edit_done() [Cancel] edit_cancel() ***/ zdialog *zd = zdialog_new(title,Mwin,Bclear,Breplace,Bversion,Bnext,Bapply,Bdone,Bcancel,null); EFdrawtext.zd = zd; EFdrawtext.mousefunc = mousefunc; EFdrawtext.menufunc = m_draw_text; // allow restart zdialog_add_widget(zd,"label","tip","dialog",tip,"space=5"); zdialog_add_widget(zd,"hbox","hbfile","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labfile","hbfile",E2X("Use settings file"),"space=3"); zdialog_add_widget(zd,"button",Bopen,"hbfile",Bopen); zdialog_add_widget(zd,"button",Bsave,"hbfile",Bsave); zdialog_add_widget(zd,"hbox","hbtext","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labtext","hbtext",E2X("Text"),"space=5"); zdialog_add_widget(zd,"frame","frtext","hbtext",0,"expand"); zdialog_add_widget(zd,"edit","text","frtext","text","expand|wrap"); zdialog_add_widget(zd,"hbox","hbmeta","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labmeta","hbmeta",E2X("Use metadata key"),"space=5"); zdialog_add_widget(zd,"zentry","metakey","hbmeta",0,"space=2|expand"); zdialog_add_widget(zd,"button",Bfetch,"hbmeta",Bfetch); zdialog_add_widget(zd,"hbox","hbfont","dialog",0,"space=2"); zdialog_add_widget(zd,"button",Bfont,"hbfont",Bfont); zdialog_add_widget(zd,"zentry","fontname","hbfont","FreeSans","space=2|size=20"); zdialog_add_widget(zd,"label","space","hbfont",0,"space=10"); zdialog_add_widget(zd,"label","labfsize","hbfont",Bsize); zdialog_add_widget(zd,"zspin","fontsize","hbfont","8|500|1|40","space=3"); zdialog_add_widget(zd,"hbox","hbattr","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vbattr1","hbattr",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vbattr2","hbattr",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbattr3","hbattr",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbattr4","hbattr",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbattr5","hbattr",0,"homog|space=2"); zdialog_add_widget(zd,"label","space","vbattr1"); zdialog_add_widget(zd,"label","labtext","vbattr1",E2X("text")); zdialog_add_widget(zd,"label","labback","vbattr1",E2X("backing")); zdialog_add_widget(zd,"label","laboutln","vbattr1",E2X("outline")); zdialog_add_widget(zd,"label","labshadow","vbattr1",E2X("shadow")); zdialog_add_widget(zd,"label","labcol","vbattr2",Bcolor); zdialog_add_widget(zd,"colorbutt","txcolor","vbattr2","0|0|0"); zdialog_add_widget(zd,"colorbutt","bgcolor","vbattr2","255|255|255"); zdialog_add_widget(zd,"colorbutt","tocolor","vbattr2","255|0|0"); zdialog_add_widget(zd,"colorbutt","shcolor","vbattr2","255|0|0"); zdialog_add_widget(zd,"label","labtran","vbattr3","transp."); zdialog_add_widget(zd,"zspin","txtransp","vbattr3","0|100|1|0"); zdialog_add_widget(zd,"zspin","bgtransp","vbattr3","0|100|1|0"); zdialog_add_widget(zd,"zspin","totransp","vbattr3","0|100|1|0"); zdialog_add_widget(zd,"zspin","shtransp","vbattr3","0|100|1|0"); zdialog_add_widget(zd,"label","labw","vbattr4",Bwidth); zdialog_add_widget(zd,"label","space","vbattr4"); zdialog_add_widget(zd,"label","space","vbattr4"); zdialog_add_widget(zd,"zspin","towidth","vbattr4","0|30|1|0"); zdialog_add_widget(zd,"zspin","shwidth","vbattr4","0|50|1|0"); zdialog_add_widget(zd,"label","labw","vbattr5",Bangle); zdialog_add_widget(zd,"zspin","txangle","vbattr5","-360|360|0.5|0"); zdialog_add_widget(zd,"label","space","vbattr5"); zdialog_add_widget(zd,"label","space","vbattr5"); zdialog_add_widget(zd,"zspin","shangle","vbattr5","-360|360|1|0"); zdialog_add_ttip(zd,Breplace,E2X("save to current file")); zdialog_add_ttip(zd,Bversion,E2X("save as new file version")); zdialog_add_ttip(zd,Bnext,E2X("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,"txcolor",attr.color[0],20); zdialog_fetch(zd,"txtransp",attr.transp[0]); zdialog_fetch(zd,"txangle",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 drawtext_names::dialog_event(zdialog *zd, cchar *event) { using namespace drawtext_names; GtkWidget *font_dialog; char font[100]; // font name and size int size, err; char *newfilename, *pp; cchar *keyname[1]; char *keyvals[1]; if (strmatch(event,"done")) zd->zstat = 6; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 7; // cancel if (zd->zstat) { if (zd->zstat < 0 || zd->zstat > 7) zd->zstat = 7; // cancel if (zd->zstat == 1) { // clear all inputs *attr.text = 0; *metakey = 0; zdialog_stuff(zd,"text",""); zdialog_stuff(zd,"metakey",""); zd->zstat = 0; // keep dialog active write(2); // erase text on image bugfix 19.0 return 1; } 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,0,1); // replace curr. file curr_file_size = f_save_size; m_draw_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,0,1); // save file if (! err) f_open_saved(); // open saved file with edit hist zfree(newfilename); m_draw_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,0,1); // replace curr. file curr_file_size = f_save_size; m_next(0,0); // open next file m_draw_text(0,0); // start again write(1); // put same text etc. onto image return 1; } if (zd->zstat == 5) { // apply 20.0 zd->zstat = 0; if (! textpresent) return 1; edit_apply(); px = py = -1; return 1; } if (zd->zstat == 6) { // done if (textpresent) edit_done(0); // save mods else edit_cancel(0); return 1; } if (zd->zstat == 7) { edit_cancel(0); // cancel or [x] return 1; } } if (strmatch(event,"focus")) { // toggle mouse capture takeMouse(mousefunc,dragcursor); // connect mouse function return 1; } if (strmatch(event,Bopen)) // load zdialog fields from a file { load_text(zd); zdialog_fetch(zd,"text",attr.text,1000); // get all zdialog fields zdialog_fetch(zd,"fontname",attr.font,80); zdialog_fetch(zd,"fontsize",attr.size); zdialog_fetch(zd,"txcolor",attr.color[0],20); zdialog_fetch(zd,"txtransp",attr.transp[0]); zdialog_fetch(zd,"txangle",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(E2X("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,"txangle")) zdialog_fetch(zd,"txangle",attr.angle); if (strmatch(event,"txcolor")) // foreground (text) color zdialog_fetch(zd,"txcolor",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,"txtransp")) // foreground transparency zdialog_fetch(zd,"txtransp",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 zmainloop(); if (textpresent) write(1); // update text on image return 1; } // mouse function, set position for text on image void drawtext_names::mousefunc() { using namespace drawtext_names; 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 drawtext_names::write(int mode) { using namespace drawtext_names; if (! E1pxm) return; // can happen with wild key bashing 20.0 float *pix1, *pix3; uint8 *pixT; int px1, py1, px3, py3, done; float e3part, Ot, Om, Ob; static int orgx1, orgy1, ww1, hh1; // old text image overlap rectangle int orgx2, orgy2, ww2, hh2; // new overlap rectangle int nc = E1pxm->nc, pcc = nc * sizeof(float); cairo_t *cr = draw_context_create(gdkwin,draw_context); if (textpresent) { for (py3 = orgy1; py3 < orgy1 + hh1; py3++) // erase prior text image for (px3 = orgx1; px3 < orgx1 + ww1; px3++) // replace E3 pixels with E1 pixels { // in prior overlap rectangle if (px3 < 0 || px3 >= E3pxm->ww) continue; if (py3 < 0 || py3 >= E3pxm->hh) continue; pix1 = PXMpix(E1pxm,px3,py3); pix3 = PXMpix(E3pxm,px3,py3); memcpy(pix3,pix1,pcc); } } done = 0; if (mode == 2) done = 1; // erase only if (! *attr.text) done = 2; // no text defined if (px < 0 && py < 0) done = 3; // no position defined if (done) { if (textpresent) { Fpaint3(orgx1,orgy1,ww1,hh1,cr); // update window to erase old text textpresent = 0; // mark no text present CEF->Fmods--; } draw_context_destroy(draw_context); return; } ww2 = attr.pxb_text->ww; // text image size hh2 = attr.pxb_text->hh; if (px > E3pxm->ww) px = E3pxm->ww; // if off screen, pull back in sight if (py > E3pxm->hh) py = E3pxm->hh; orgx2 = px - ww2/2; // copy-to image3 location orgy2 = py - hh2/2; for (py1 = 0; py1 < hh2; py1++) // loop all pixels in text image for (px1 = 0; px1 < ww2; px1++) { px3 = orgx2 + px1; // copy-to image3 pixel py3 = orgy2 + py1; if (px3 < 0 || px3 >= E3pxm->ww) continue; // omit parts beyond edges if (py3 < 0 || py3 >= E3pxm->hh) continue; pixT = PXBpix(attr.pxb_text,px1,py1); // copy-from text pixel pix3 = PXMpix(E3pxm,px3,py3); // copy-to image pixel e3part = pixT[3] / 256.0; // text image transparency pix3[0] = pixT[0] + e3part * pix3[0]; // combine text part + image part pix3[1] = pixT[1] + e3part * pix3[1]; pix3[2] = pixT[2] + e3part * pix3[2]; if (nc > 3) { Ot = (1.0 - e3part); // text opacity Om = pix3[3] / 256.0; // image opacity Ob = 1.0 - (1.0 - Ot) * (1.0 - Om); // combined opacity pix3[3] = 255.0 * Ob; } } if (textpresent) { Fpaint3(orgx1,orgy1,ww1,hh1,cr); // update window to erase old text textpresent = 0; CEF->Fmods--; } // (updates together to reduce flicker) Fpaint3(orgx2,orgy2,ww2,hh2,cr); // update window for new text draw_context_destroy(draw_context); textpresent = 1; // mark text is present CEF->Fmods++; // increase mod count CEF->Fsaved = 0; orgx1 = orgx2; // remember overlap rectangle orgy1 = orgy2; // for next call ww1 = ww2; hh1 = hh2; return; } // load text and attributes from a file void load_text(zdialog *zd) { FILE *fid; int err, nn; char *pp, *pp2, *file, buff[1200]; cchar *dialogtitle = "load text data from a file"; textattr_t attr; file = zgetfile(dialogtitle,MWIN,"file",drawtext_folder); // 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 = (char *) zstrstr(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,"txangle",attr.angle); zdialog_stuff(zd,"txcolor",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,"txtransp",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,E2X("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",drawtext_folder); // 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,"txangle",attr.angle); zdialog_fetch(zd,"txcolor",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,"txtransp",attr.transp[0]); zdialog_fetch(zd,"bgtransp",attr.transp[1]); zdialog_fetch(zd,"totransp",attr.transp[2]); zdialog_fetch(zd,"shtransp",attr.transp[3]); zdialog_fetch(zd,"towidth",attr.towidth); zdialog_fetch(zd,"shwidth",attr.shwidth); zdialog_fetch(zd,"shangle",attr.shangle); repl_Nstrs(attr.text,text2,"\n","\\n",null); // replace newlines with "\n" fprintf(fid,"text string: %s\n",text2); fprintf(fid,"font: %s size: %d \n",attr.font, attr.size); fprintf(fid,"attributes: %.4f %s %s %s %s %d %d %d %d %d %d %d \n", attr.angle, attr.color[0], attr.color[1], attr.color[2], attr.color[3], attr.transp[0], attr.transp[1], attr.transp[2], attr.transp[3], attr.towidth, attr.shwidth, attr.shangle); fclose(fid); return; } /********************************************************************************/ /*** Create a graphic image with text, any color, any font, any angle. Add outline and shadow colors. Apply transparencies. struct textattr_t // attributes for gentext() function char text[1000]; // text to generate image from char font[80]; // font name int size; // font size int tww, thh; // generated image size, unrotated float angle; // text angle, degrees float sinT, cosT; // trig funcs for text angle char color[4][20]; // text, backing, outline, shadow "R|G|B" int transp[4]; // corresponding transparencies 0-255 int towidth; // outline width, pixels int shwidth; // shadow width int shangle; // shadow angle -180...+180 PXB *pxb_text; // image with text/outline/shadow ***/ int gentext(textattr_t *attr) { PXB * gentext_outline(textattr_t *, PXB *); PXB * gentext_shadow(textattr_t *, PXB *); PangoFontDescription *pfont; PangoLayout *playout; cairo_surface_t *surface; cairo_t *cr; float angle; int txred, txgreen, txblue; int bgred, bggreen, bgblue; int tored, togreen, toblue; int shred, shgreen, shblue; float txtransp, 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 txpart, topart, shpart, bgpart; if (! *attr->text) strcpy(attr->text," "); // no text 20.0 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); txred = atoi(strField(attr->color[0],'|',1)); // get text foreground color txgreen = atoi(strField(attr->color[0],'|',2)); txblue = 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)); txtransp = 0.01 * attr->transp[0]; // get transparencies bgtransp = 0.01 * attr->transp[1]; // text, background, outline, shadow totransp = 0.01 * attr->transp[2]; shtransp = 0.01 * attr->transp[3]; pfont = pango_font_description_from_string(font); // make layout with text playout = gtk_widget_create_pango_layout(Fdrawin,null); // Fdrawin instead of Cdrawin if (! playout) zappcrash("gentext(): cannot create pango layout"); pango_layout_set_font_description(playout,pfont); pango_layout_set_text(playout,attr->text,-1); pango_layout_get_pixel_size(playout,&ww,&hh); ww += 2 + 0.2 * attr->size; // compensate bad font metrics hh += 2 + 0.1 * attr->size; attr->tww = ww; // save text image size before rotate attr->thh = hh; surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,ww,hh); // cairo output image cr = cairo_create(surface); pango_cairo_show_layout(cr,playout); // write text layout to image cairo_data = cairo_image_surface_get_data(surface); // get text image pixels pxb_temp1 = PXB_make(ww+5,hh,3); // 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+4,py); // small pad before text 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,4); // 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 txpart = pix1[0] / 256.0; // text opacity 0-1 topart = pix1[1] / 256.0; // outline shpart = pix1[2] / 256.0; // shadow bgpart = (1.0 - txpart - topart - shpart); // background txpart = txpart * (1.0 - txtransp); // reduce for transparencies topart = topart * (1.0 - totransp); shpart = shpart * (1.0 - shtransp); bgpart = bgpart * (1.0 - bgtransp); red = txpart * txred + topart * tored + shpart * shred + bgpart * bgred; green = txpart * txgreen + topart * togreen + shpart * shgreen + bgpart * bggreen; blue = txpart * txblue + 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 - txpart - 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,3); // 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,3); // 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 /********************************************************************************/ // draw a line or arrow on top of the image namespace drawline_names { 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 EFdrawline; } void m_draw_line(GtkWidget *, cchar *menu) { using namespace drawline_names; cchar *intro = E2X("Enter line or arrow properties in dialog, \n" "click/drag on image, right click to remove"); F1_help_topic = "draw line"; // user guide topic EFdrawline.menufunc = m_draw_line; EFdrawline.funcname = "draw_line"; EFdrawline.Farea = 1; // select area ignored EFdrawline.mousefunc = mousefunc; if (! edit_setup(EFdrawline)) return; // setup edit /*** ___________________________________________________ | Draw 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 transp. width angle | | line [#####] [_______] [______] | lncolor lntransp lnangle | backing [#####] [_______] | bgcolor bgtransp | outline [#####] [_______] [_______] | tocolor totransp towidth | shadow [#####] [_______] [_______] [______] | shcolor shtransp shwidth shangle | | | [Apply] [Done] [Cancel] | |___________________________________________________| ***/ zd = zdialog_new(E2X("Draw line or arrow on image"),Mwin,Bapply,Bdone,Bcancel,null); EFdrawline.zd = zd; EFdrawline.mousefunc = mousefunc; EFdrawline.menufunc = m_draw_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",E2X("Line length"),"space=5"); zdialog_add_widget(zd,"zspin","length","hbline","2|9999|1|20"); zdialog_add_widget(zd,"label","space","hbline",0,"space=10"); zdialog_add_widget(zd,"label","labwidth","hbline",Bwidth,"space=5"); zdialog_add_widget(zd,"zspin","width","hbline","1|99|1|2"); zdialog_add_widget(zd,"hbox","hbarrow","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labarrow","hbarrow",E2X("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",E2X("line")); zdialog_add_widget(zd,"label","labback","vbcol1",E2X("backing")); zdialog_add_widget(zd,"label","laboutln","vbcol1",E2X("outline")); zdialog_add_widget(zd,"label","labshadow","vbcol1",E2X("shadow")); zdialog_add_widget(zd,"label","labcol","vbcol2",Bcolor); zdialog_add_widget(zd,"colorbutt","lncolor","vbcol2","0|0|0"); zdialog_add_widget(zd,"colorbutt","bgcolor","vbcol2","255|255|255"); zdialog_add_widget(zd,"colorbutt","tocolor","vbcol2","255|0|0"); zdialog_add_widget(zd,"colorbutt","shcolor","vbcol2","255|0|0"); zdialog_add_widget(zd,"label","labcol","vbcol3","Transp."); zdialog_add_widget(zd,"zspin","lntransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","bgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","totransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","shtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"label","labw","vbcol4",Bwidth); zdialog_add_widget(zd,"label","space","vbcol4"); zdialog_add_widget(zd,"label","space","vbcol4"); zdialog_add_widget(zd,"zspin","towidth","vbcol4","0|30|1|0"); zdialog_add_widget(zd,"zspin","shwidth","vbcol4","0|50|1|0"); zdialog_add_widget(zd,"label","labw","vbcol5",Bangle); zdialog_add_widget(zd,"zspin","lnangle","vbcol5","-360|360|0.1|0"); zdialog_add_widget(zd,"label","space","vbcol5"); zdialog_add_widget(zd,"label","space","vbcol5"); zdialog_add_widget(zd,"zspin","shangle","vbcol5","-360|360|1|0"); zdialog_add_ttip(zd,Bapply,E2X("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,"lnangle",attr.angle); zdialog_fetch(zd,"lncolor",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,"lntransp",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 drawline_names::dialog_event(zdialog *zd, cchar *event) { using namespace drawline_names; if (strmatch(event,"done")) zd->zstat = 2; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 3; // cancel if (zd->zstat) { if (zd->zstat == 1) { // Apply, commit present line to image zd->zstat = 0; write(1); linepresent = 0; // (no old line to erase) mpx = mpy = -1; edit_apply(); 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,"lnangle")) // line angle zdialog_fetch(zd,"lnangle",attr.angle); if (strmatch(event,"lncolor")) // foreground (line) color zdialog_fetch(zd,"lncolor",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,"lntransp")) // foreground transparency zdialog_fetch(zd,"lntransp",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 drawline_names::mousefunc() { using namespace drawline_names; float ax1, ay1, ax2, ay2; // line/arrow end points float angle, rad, l2; float amx, amy; float d1, d2, sinv; if (RMclick) { // right mouse click write(2); // erase old line, if any mpx = mpy = -1; LMclick = RMclick = Mxdrag = Mydrag = 0; return; } if (LMclick + Mxdrag + Mydrag == 0) return; if (LMclick) { mpx = Mxclick; // new line position on image mpy = Myclick; } else { mpx = Mxdrag; mpy = Mydrag; } LMclick = RMclick = Mxdrag = Mydrag = 0; if (! linepresent) { orgx2 = mpx; orgy2 = mpy; write(1); return; } // move the closest line endpoint to the mouse position and leave the other endpoint fixed angle = attr.angle; rad = -angle / 57.296; l2 = attr.length / 2.0; ww2 = attr.pxb_line->ww; // line image buffer hh2 = attr.pxb_line->hh; amx = ww2 / 2.0; // line midpoint within line image amy = hh2 / 2.0; ax1 = amx - l2 * cosf(rad) + 0.5; // line end points ay1 = amy + l2 * sinf(rad) + 0.5; ax2 = amx + l2 * cosf(rad) + 0.5; ay2 = amy - l2 * sinf(rad) + 0.5; d1 = (mpx-ax1-orgx1) * (mpx-ax1-orgx1) + (mpy-ay1-orgy1) * (mpy-ay1-orgy1); d2 = (mpx-ax2-orgx1) * (mpx-ax2-orgx1) + (mpy-ay2-orgy1) * (mpy-ay2-orgy1); d1 = sqrtf(d1); // mouse - end point distance d2 = sqrtf(d2); if (d1 < d2) { // move ax1/ay1 end to mouse ax2 += orgx1; ay2 += orgy1; ax1 = mpx; ay1 = mpy; attr.length = d2 + 0.5; sinv = (ay1-ay2) / d2; if (sinv > 1.0) sinv = 1.0; if (sinv < -1.0) sinv = -1.0; rad = asinf(sinv); angle = -57.296 * rad; if (mpx > ax2) angle = -180 - angle; } else { // move ax2/ay2 end to mouse ax1 += orgx1; ay1 += orgy1; ax2 = mpx; ay2 = mpy; attr.length = d1 + 0.5; sinv = (ay1-ay2) / d1; if (sinv > 1.0) sinv = 1.0; if (sinv < -1.0) sinv = -1.0; rad = asinf(sinv); angle = -57.296 * rad; if (mpx < ax1) angle = -180 - angle; } if (angle < -180) angle += 360; if (angle > 180) angle -= 360; attr.angle = angle; genline(&attr); ww2 = attr.pxb_line->ww; hh2 = attr.pxb_line->hh; amx = (ax1 + ax2) / 2.0; amy = (ay1 + ay2) / 2.0; orgx2 = amx - ww2 / 2.0; orgy2 = amy - hh2 / 2.0; write(1); zdialog_stuff(zd,"lnangle",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 drawline_names::write(int mode) { using namespace drawline_names; float *pix1, *pix3; uint8 *pixL; int px1, py1, px3, py3, done; float e3part, Ot, Om, Ob; int nc = E1pxm->nc, pcc = nc * sizeof(float); cairo_t *cr = draw_context_create(gdkwin,draw_context); if (linepresent) { for (py3 = orgy1; py3 < orgy1 + hh1; py3++) // erase prior line image for (px3 = orgx1; px3 < orgx1 + ww1; px3++) // replace E3 pixels with E1 pixels { // in prior overlap rectangle if (px3 < 0 || px3 >= E3pxm->ww) continue; if (py3 < 0 || py3 >= E3pxm->hh) continue; pix1 = PXMpix(E1pxm,px3,py3); pix3 = PXMpix(E3pxm,px3,py3); memcpy(pix3,pix1,pcc); } } done = 0; if (mode == 2) done = 1; // erase only if (mpx < 0 && mpy < 0) done = 1; // no position defined if (done) { if (linepresent) { Fpaint3(orgx1,orgy1,ww1,hh1,cr); // update window to erase old line linepresent = 0; // mark no line present } draw_context_destroy(draw_context); return; } ww2 = attr.pxb_line->ww; // line image buffer hh2 = attr.pxb_line->hh; for (py1 = 0; py1 < hh2; py1++) // loop all pixels in line image for (px1 = 0; px1 < ww2; px1++) { px3 = orgx2 + px1; // copy-to image3 pixel py3 = orgy2 + py1; if (px3 < 0 || px3 >= E3pxm->ww) continue; // omit parts beyond edges if (py3 < 0 || py3 >= E3pxm->hh) continue; pixL = PXBpix(attr.pxb_line,px1,py1); // copy-from line pixel pix3 = PXMpix(E3pxm,px3,py3); // copy-to image pixel e3part = pixL[3] / 256.0; // line image transparency pix3[0] = pixL[0] + e3part * pix3[0]; // combine line part + image part pix3[1] = pixL[1] + e3part * pix3[1]; pix3[2] = pixL[2] + e3part * pix3[2]; if (nc > 3) { Ot = (1.0 - e3part); // line opacity Om = pix3[3] / 256.0; // image opacity Ob = 1.0 - (1.0 - Ot) * (1.0 - Om); // combined opacity pix3[3] = 255.0 * Ob; } } if (linepresent) { Fpaint3(orgx1,orgy1,ww1,hh1,cr); // update window to erase old line linepresent = 0; } // (updates together to reduce flicker) Fpaint3(orgx2,orgy2,ww2,hh2,cr); // update window for new line draw_context_destroy(draw_context); 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 lnred, lngreen, lnblue; int bgred, bggreen, bgblue; int tored, togreen, toblue; int shred, shgreen, shblue; float lntransp, 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 lnpart, topart, shpart, bgpart; if (attr->pxb_line) PXB_free(attr->pxb_line); angle = attr->angle; // line angle, degrees attr->sinT = sin(angle/57.296); // trig funcs for line angle attr->cosT = cos(angle/57.296); lnred = atoi(strField(attr->color[0],'|',1)); // get line foreground color lngreen = atoi(strField(attr->color[0],'|',2)); lnblue = 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)); lntransp = 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,3); // 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,4); // 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 lnpart = pix1[0] / 256.0; topart = pix1[1] / 256.0; shpart = pix1[2] / 256.0; bgpart = (1.0 - lnpart - topart - shpart); lnpart = lnpart * (1.0 - lntransp); topart = topart * (1.0 - totransp); shpart = shpart * (1.0 - shtransp); bgpart = bgpart * (1.0 - bgtransp); red = lnpart * lnred + topart * tored + shpart * shred + bgpart * bgred; green = lnpart * lngreen + topart * togreen + shpart * shgreen + bgpart * bggreen; blue = lnpart * lnblue + 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 - lnpart - 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,3); // 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,3); // 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 /********************************************************************************/ // draw a box around an area drag-selected with the mouse namespace drawbox_names { editfunc EFdrawbox; int RGB[3]; // box line color int width; // box line width int bx1, by1, bx2, by2; // box NW and SE corners } // menu function void m_draw_box(GtkWidget *, cchar *) // 20.0 { using namespace drawbox_names; int drawbox_dialog_event(zdialog *zd, cchar *event); void drawbox_mousefunc(); zdialog *zd; cchar *tip = E2X("drag mouse to draw box \n" "shift + drag center to move box \n" "shift + drag edge to move edge"); F1_help_topic = "draw box"; EFdrawbox.menufunc = m_draw_box; EFdrawbox.funcname = "draw_box"; EFdrawbox.mousefunc = drawbox_mousefunc; if (! edit_setup(EFdrawbox)) return; /*** ___________________________________ | Draw box on image | | drag mouse to draw box | | shift + drag center to move box | | shift + drag edge to move edge | | | | line color [###] line width [__] | | | | [apply] [done] [cancel] | |___________________________________| ***/ zd = zdialog_new(E2X("Draw box on image"),Mwin,Bapply,Bdone,Bcancel,null); EFdrawbox.zd = zd; zdialog_add_widget(zd,"label","labtip","dialog",tip,"space=3"); zdialog_add_widget(zd,"hbox","space","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbline","dialog"); zdialog_add_widget(zd,"label","labrgb","hbline",E2X("line color"),"space=3"); zdialog_add_widget(zd,"colorbutt","RGB","hbline","255|0|0","space=3"); zdialog_add_widget(zd,"label","space","hbline",0,"space=5"); zdialog_add_widget(zd,"label","labwidth","hbline",E2X("line width"),"space=3"); zdialog_add_widget(zd,"zspin","width","hbline","1|10|1|1","space=3"); zdialog_restore_inputs(zd); zdialog_run(zd,drawbox_dialog_event,"save"); zdialog_send_event(zd,"init"); takeMouse(drawbox_mousefunc,dragcursor); bx1 = by1 = bx2 = by2 = 0; // no box yet return; } // dialog event and completion function int drawbox_dialog_event(zdialog *zd, cchar *event) { using namespace drawbox_names; char color[20]; cchar *pp; if (strmatch(event,"done")) zd->zstat = 2; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 3; // cancel if (zd->zstat) { if (zd->zstat == 1) { // apply zd->zstat = 0; edit_apply(); return 1; } if (zd->zstat == 2) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } if (zstrstr("init RGB width",event)) // set new line color and width { zdialog_fetch(zd,"RGB",color,19); 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_fetch(zd,"width",width); } return 1; } // mouse function // record drag start and current mouse position // draw box with opposing corners at these positions void drawbox_mousefunc() { using namespace drawbox_names; void drawline(int x1, int y1, int x2, int y2, int RGB[3], int width); int mx, my, dx, dy, bump; int cx, cy, rx, ry, rr; int min, dt, db, dl, dr; if (Mxdrag || Mydrag) // drag underway { PXM_free(E3pxm); // erase prior E3pxm = PXM_copy(E1pxm); if (KBshiftkey && bx1) // shift key, move prior box { dx = Mxdrag - Mxdown; // new drag displacement dy = Mydrag - Mydown; Mxdown = Mxdrag; // reset drag Mydown = Mydrag; Mxdrag = Mydrag = 0; mx = Mxdown; // mouse position my = Mydown; cx = (bx1 + bx2) / 2; // box center cy = (by1 + by2) / 2; rx = mx - cx; // get mouse distance from center ry = my - cy; rr = sqrt(rx*rx + ry*ry); dt = abs(my - by1); // get mouse distance from each edge db = abs(my - by2); // top, bottom, left, right dl = abs(mx - bx1); dr = abs(mx - bx2); min = dt; // find minimum edge distance if (db < min) min = db; if (dl < min) min = dl; if (dr < min) min = dr; if (rr < min) { // mouse is closer to center bx1 += dx; // move unchanged box by1 += dy; bx2 += dx; by2 += dy; } else { if (min == dt) by1 = my; // move line closest to mouse if (min == db) by2 = my; if (min == dl) bx1 = mx; if (min == dr) bx2 = mx; } } else { // no shift key: create new box bx1 = Mxdown; by1 = Mydown; bx2 = Mxdrag; by2 = Mydrag; Mxdrag = Mydrag = 0; } bump = 1; // fill last pixel if (by2 < by1) bump = -1; // (bx2,by2) at NW corner of pixel drawline(bx1,by1,bx2,by1,RGB,width); drawline(bx1,by2,bx2,by2,RGB,width); drawline(bx1,by1,bx1,by2,RGB,width); drawline(bx2,by1,bx2,by2+bump,RGB,width); CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); } return; } /********************************************************************************/ // draw an oval around an area drag-selected with the mouse namespace drawoval_names { editfunc EFdrawoval; int RGB[3]; // oval line color int width; // oval line width int type = 1; // 1/2 = oval/circle int mx1, my1, mx2, my2; // mouse drag coordinates float exa, exb; // ellipse axes } // menu function void m_draw_oval(GtkWidget *, cchar *) // 20.0 { using namespace drawoval_names; int drawoval_dialog_event(zdialog *zd, cchar *event); void drawoval_mousefunc(); zdialog *zd; cchar *tip = E2X("drag mouse down/right to draw oval \n" "shift + drag center to move oval \n" "shift + drag lower right edge to change"); F1_help_topic = "draw oval"; EFdrawoval.menufunc = m_draw_oval; EFdrawoval.funcname = "draw_oval"; EFdrawoval.mousefunc = drawoval_mousefunc; if (! edit_setup(EFdrawoval)) return; /*** _________________________________________ | Draw oval on image | | drag mouse down/right to draw oval | | shift + drag center to move oval | | shift + drag lower right edge to change | | | | line color [###] line width [__] | | [x] oval [x] circle | | | | [apply] [done] [cancel] | |_________________________________________| ***/ zd = zdialog_new(E2X("Draw oval on image"),Mwin,Bapply,Bdone,Bcancel,null); EFdrawoval.zd = zd; zdialog_add_widget(zd,"label","labtip","dialog",tip,"space=3"); zdialog_add_widget(zd,"hbox","space","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbline","dialog"); zdialog_add_widget(zd,"label","labline","hbline",E2X("line color"),"space=3"); zdialog_add_widget(zd,"colorbutt","RGB","hbline","255|0|0","space=3"); zdialog_add_widget(zd,"label","space","hbline",0,"space=5"); zdialog_add_widget(zd,"label","labwidth","hbline",E2X("line width"),"space=3"); zdialog_add_widget(zd,"zspin","width","hbline","1|10|1|1","space=3"); zdialog_add_widget(zd,"hbox","hbcircle","dialog"); zdialog_add_widget(zd,"check","oval","hbcircle",E2X("oval"),"space=3"); zdialog_add_widget(zd,"check","circle","hbcircle",E2X("circle"),"space=8"); zdialog_restore_inputs(zd); zdialog_run(zd,drawoval_dialog_event,"save"); zdialog_send_event(zd,"init"); takeMouse(drawoval_mousefunc,dragcursor); mx1 = my1 = 0; // no oval yet return; } // dialog event and completion function int drawoval_dialog_event(zdialog *zd, cchar *event) { using namespace drawoval_names; char color[20]; cchar *pp; if (strmatch(event,"done")) zd->zstat = 2; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 3; // cancel if (zd->zstat) { if (zd->zstat == 1) { // apply zd->zstat = 0; edit_apply(); return 1; } if (zd->zstat == 2) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } if (zstrstr("init RGB width",event)) // set line color { zdialog_fetch(zd,"RGB",color,19); 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_fetch(zd,"width",width); // line width zdialog_stuff(zd,"oval",0); zdialog_stuff(zd,"circle",0); if (type == 1) zdialog_stuff(zd,"oval",1); if (type == 2) zdialog_stuff(zd,"circle",1); } if (zstrstr("oval circle",event)) { zdialog_stuff(zd,"oval",0); zdialog_stuff(zd,"circle",0); zdialog_stuff(zd,event,1); if (strmatch(event,"oval")) type = 1; else type = 2; } return 1; } // mouse function // record drag start and current mouse position // draw oval (ellipse) with major/minor axes from drag X/Y distances. void drawoval_mousefunc() { using namespace drawoval_names; void drawpoint(int px, int py, int RGB[3], int W); int px, py, dx, dy, rx, ry, rr; float a2, b2; float x, y, x2, y2, cx, cy; if (Mxdrag || Mydrag) // drag underway { PXM_free(E3pxm); // erase prior E3pxm = PXM_copy(E1pxm); if (KBshiftkey && mx1) // shift key: move prior oval { dx = Mxdrag - Mxdown; // new drag displacement dy = Mydrag - Mydown; Mxdown = Mxdrag; // reset drag Mydown = Mydrag; Mxdrag = Mydrag = 0; rx = mx1 - Mxdown; // get mouse distance from prior center ry = my1 - Mydown; rr = sqrtf(rx*rx + ry*ry); if (rr < 0.6 * exa || rr < 0.6 * exb) { // small, move unchanged oval mx1 += dx; // move center my1 += dy; } mx2 += dx; // move edge (change size) my2 += dy; } else { // no shift key: create new oval mx1 = Mxdown; my1 = Mydown; mx2 = Mxdrag; my2 = Mydrag; Mxdrag = Mydrag = 0; } exa = abs(mx2 - mx1); // ellipse constants from exb = abs(my2 - my1); // enclosing rectangle if (type == 2) { // make a circle if (exa > exb) exb = exa; else exa = exb; } a2 = exa * exa; b2 = exb * exb; cx = mx1; // center at drag origin cy = my1; for (y = -exb; y < exb; 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; drawpoint(px,py,RGB,width); // draw 2 points on ellipse px = cx + x + 0.5; drawpoint(px,py,RGB,width); } for (x = -exa; x < exa; 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; drawpoint(px,py,RGB,width); // draw 2 points on ellipse py = cy + y + 0.5; drawpoint(px,py,RGB,width); } CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); } return; } /********************************************************************************/ // draw a line from pixel (x1,y1) to (x2,y2) with given pixel width. // color is RGB[3], range 0-255. void drawline(int x1, int y1, int x2, int y2, int RGB[3], int W) { void drawpoint(int px, int py, int RGB[3], int W); float M; int px, py, inc; if (abs(x1-x2) > abs(y1-y2)) // line more horizontal { M = 1.0 * (y2-y1) / (x2-x1); inc = 1; if (x2 < x1) inc = -1; for (px = x1; px != x2; px += inc) { // loop points in line center py = y1 + M * (px-x1); drawpoint(px,py,RGB,W); // color center + neighbor points } } else { M = 1.0 * (x2-x1) / (y2-y1); // line more vertical inc = 1; if (y2 < y1) inc = -1; for (py = y1; py != y2; py += inc) { px = x1 + M * (py-y1); drawpoint(px,py,RGB,W); } } return; } // draw one 'point' of line (point size = line width) void drawpoint(int px, int py, int RGB[3], int W) { int qx, qy, w1, w2; int Eww = E3pxm->ww; int Ehh = E3pxm->hh; float *pix3; w1 = W / 2; w2 = w1; if (!(W & 1)) w2 -= 1; for (qy = py-w1; qy <= py+w2; qy++) // loop pixels overlapping point for (qx = px-w1; qx <= px+w2; qx++) { if (qx < 0 || qx > Eww-1) continue; if (qy < 0 || qy > Ehh-1) continue; pix3 = PXMpix(E3pxm,qx,qy); pix3[0] = RGB[0]; pix3[1] = RGB[1]; pix3[2] = RGB[2]; } return; } /********************************************************************************/ // Pixel paint function - paint individual pixels with the mouse. // The mouse circle paints a selected color. namespace paint_image_names { int paint_dialog_event(zdialog* zd, cchar *event); void paint_mousefunc(); void paint_dopixels(int px, int py); // update pixel block void paint_savepixB(int px, int py); // save pixel block for poss. undo void paint_undolastB(); // undo last pixel block, free memory void paint_freefirstB(); // free memory for first pixel block void paint_freeallB(); // free memory for all pixel blocks uint8 RGB[3] = { 0, 0, 0 }; // color to paint int mode; // 1/2 = paint / erase int Mradius; // mouse radius int Fptran = 0; // flag, paint over transparent areas int Fdrag = 0; // flag, mouse drags image int nc, ac; // no. channels, alpha channel float kernel[202][202]; // Mradius <= 100 int64 maxmem = (int64) 4000 * MEGA; // max. pixel block memory (4 GB) int64 totmem; // pixB memory allocated int maxpixB = 10000; // max. pixel blocks int totpixB = 0; // total pixel blocks int pixBseq = 0; // last pixel block sequence no. typedef struct { // pixel block before edit int seq; // block sequence no. uint16 px, py; // center pixel (radius org.) uint16 radius; // radius of pixel block float pixel[][4]; // array of pixel[npix][4] } pixBmem_t; pixBmem_t **pixBmem = 0; // *pixBmem_t[] int pixBmem_cc = 12; // all except pixel array + pad int pcc4 = 4 * sizeof(float); // pixel cc: RGBA = 4 channels editfunc EFpaint; } // menu function void m_paint_image(GtkWidget *, cchar *) // separate paint and copy { using namespace paint_image_names; cchar *mess1 = E2X("shift + left click: pick color from image \n" "left drag: paint color on image \n" // remove click actions "right drag: restore original image"); cchar *dash = " \xcc\xb6 "; F1_help_topic = "paint image"; EFpaint.menufunc = m_paint_image; EFpaint.funcname = "paint_image"; EFpaint.Farea = 2; // select area OK EFpaint.mousefunc = paint_mousefunc; // mouse function if (! edit_setup(EFpaint)) return; // setup edit /**** __________________________________________________ | Paint on Image | | | | shift + left click: pick color from image | | left drag: paint color on image | | right drag: restore original image | | | | paint color [######] [palette] [HSL] | | | | brush size NN ============[]============= | | opacity center NN ==================[]======= | | opacity edge NN ====[]===================== | | | | (o) paint (o) erase [undo last] [undo all] | | [x] include transparent areas | | [x] drag image zoom image [+] [-] | | | | [done] [cancel] | |__________________________________________________| ****/ zdialog *zd = zdialog_new(E2X("Paint on Image"),Mwin,Bdone,Bcancel,null); EFpaint.zd = zd; zdialog_add_widget(zd,"label","labm","dialog",mess1,"space=5"); zdialog_add_widget(zd,"hbox","hbp","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labp","hbp",E2X("paint color"),"space=5"); zdialog_add_widget(zd,"colorbutt","colorbutt","hbp","255|0|0"); zdialog_add_widget(zd,"label","space","hbp",0,"space=10"); zdialog_add_widget(zd,"button","palette","hbp","palette","space=10"); zdialog_add_widget(zd,"button","HSL","hbp","HSL"); zdialog_add_widget(zd,"hbox","hbbru","dialog",0,"space=5"); zdialog_add_widget(zd,"vbox","vbbru1","hbbru",0,"homog|space=1"); zdialog_add_widget(zd,"vbox","vbbru2","hbbru",0,"homog|space=1"); zdialog_add_widget(zd,"vbox","vbbru3","hbbru",0,"homog|expand|space=1"); zdialog_add_widget(zd,"label","labbr","vbbru1",E2X("brush size")); zdialog_add_widget(zd,"label","laboc","vbbru1",Bopacitycenter); zdialog_add_widget(zd,"label","laboe","vbbru1",Bopacityedge); zdialog_add_widget(zd,"label","labbrNN","vbbru2","NN"); zdialog_add_widget(zd,"label","labocNN","vbbru2","NNN"); zdialog_add_widget(zd,"label","laboeNN","vbbru2","NNN"); zdialog_add_widget(zd,"hscale","Mradius","vbbru3","1|100|1|20","expand"); zdialog_add_widget(zd,"hscale","opccent","vbbru3","1|100|1|50","expand"); zdialog_add_widget(zd,"hscale","opcedge","vbbru3","0|100|1|100","expand"); zdialog_add_widget(zd,"hbox","hbp","dialog",0,"space=3"); zdialog_add_widget(zd,"radio","paint","hbp",E2X("paint"),"space=3"); zdialog_add_widget(zd,"radio","erase","hbp",E2X("erase")); zdialog_add_widget(zd,"button","undlast","hbp",Bundolast,"space=5"); zdialog_add_widget(zd,"button","undall","hbp",Bundoall); zdialog_add_widget(zd,"hbox","hbt","dialog",0,"space=3"); zdialog_add_widget(zd,"check","Fptran","hbt",E2X("include transparent areas"),"space=3"); zdialog_add_widget(zd,"hbox","hbd","dialog"); zdialog_add_widget(zd,"check","Fdrag","hbd",E2X("drag image"),"space=3"); zdialog_add_widget(zd,"label","space","hbd",0,"space=10"); zdialog_add_widget(zd,"label","labzoom","hbd",E2X("zoom image"),"space=3"); zdialog_add_widget(zd,"button","zoom+","hbd"," + ","space=3"); zdialog_add_widget(zd,"button","zoom-","hbd",dash,"space=3"); zdialog_rescale(zd,"Mradius",1,2,100); // stretch scales at sensitive end zdialog_rescale(zd,"opccent",1,2,100); zdialog_rescale(zd,"opcedge",0,1,100); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_stuff(zd,"Fptran",0); // initialize zdialog_stuff(zd,"paint",1); zdialog_stuff(zd,"erase",0); zdialog_stuff(zd,"Fdrag",0); mode = 1; Fdrag = 0; zdialog_run(zd,paint_dialog_event,"save"); // run dialog, parallel zdialog_send_event(zd,"colorbutt"); // initialize paint color zdialog_send_event(zd,"Mradius"); // get kernel initialized zdialog_fetch(zd,"Fptran",Fptran); // paint over transparent areas totmem = 0; // memory used pixBmem = 0; // pixel block memory totpixB = 0; // pixel blocks pixBseq = 0; ac = 0; nc = E1pxm->nc; // channels, RGBA if (nc > 3) ac = 1; // alpha channel present takeMouse(paint_mousefunc,drawcursor); // connect mouse function return; } // dialog event and completion callback function int paint_image_names::paint_dialog_event(zdialog *zd, cchar *event) { using namespace paint_image_names; cchar *pp; char color[20], text[20]; float opccent, opcedge; // center and edge opacities int paint, radius, dx, dy, err; float rad, kern; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit paint_freeallB(); // free pixel block memory return 1; } draw_mousecircle(0,0,0,1,0); // erase mouse circle if (strmatch(event,"focus")) // toggle mouse capture takeMouse(paint_mousefunc,drawcursor); if (strmatch(event,"colorbutt")) { zdialog_fetch(zd,"colorbutt",color,19); // get paint color from color wheel pp = strField(color,"|",1); if (pp) RGB[0] = atoi(pp); pp = strField(color,"|",2); if (pp) RGB[1] = atoi(pp); pp = strField(color,"|",3); if (pp) RGB[2] = atoi(pp); } if (strmatch(event,"palette")) err = RGB_chooser(zd,"colorbutt",RGB); // select color from palette 19.0 if (strmatch(event,"HSL")) { err = HSL_chooser(zd,"colorbutt",RGB); // select color from palette 19.0 if (err) return 1; snprintf(color,20,"%d|%d|%d",RGB[0],RGB[1],RGB[2]); zdialog_stuff(zd,"colorbutt",color); } if (zstrstr("Mradius opccent opcedge",event)) { zdialog_fetch(zd,"Mradius",Mradius); // get new brush attributes zdialog_fetch(zd,"opccent",opccent); zdialog_fetch(zd,"opcedge",opcedge); sprintf(text,"%3d",Mradius); // stuff corresp. number values zdialog_stuff(zd,"labbrNN",text); sprintf(text,"%3.0f",opccent); zdialog_stuff(zd,"labocNN",text); sprintf(text,"%3.0f",opcedge); zdialog_stuff(zd,"laboeNN",text); opccent = 0.01 * opccent; // opacity 0 ... 1 opcedge = 0.01 * opcedge; opccent = pow(opccent,2.0); // change response curve opcedge = opccent * opcedge; // edge relative to center radius = Mradius; for (dy = -radius; dy <= radius; dy++) // build kernel for (dx = -radius; dx <= radius; dx++) { rad = sqrt(dx*dx + dy*dy); kern = (radius - rad) / radius; // center ... edge >> 1 ... 0 kern = kern * (opccent - opcedge) + opcedge; // opacity center ... edge if (rad > radius) kern = 0; // beyond radius, within square if (kern < 0) kern = 0; if (kern > 1) kern = 1; kernel[dx+radius][dy+radius] = kern; } } if (strmatch(event,"undlast")) // undo last edit (click or drag) paint_undolastB(); if (strmatch(event,"undall")) { // undo all edits edit_reset(); paint_freeallB(); } if (strmatch(event,"Fptran")) // flag, paint over transparency zdialog_fetch(zd,"Fptran",Fptran); if (zstrstr("paint erase",event)) { // set paint or erase mode zdialog_fetch(zd,"paint",paint); if (paint) mode = 1; else mode = 2; } if (strmatch(event,"Fdrag")) // mouse drags image zdialog_fetch(zd,"Fdrag",Fdrag); if (strmatch(event,"zoom+")) m_zoom(0,"in"); // zoom image in or out if (strmatch(event,"zoom-")) m_zoom(0,"out"); return 1; } // mouse function void paint_image_names::paint_mousefunc() // no action on clicks { using namespace paint_image_names; static int pmxdown = 0, pmydown = 0; static int pmxdrag = 0, pmydrag = 0; char color[20]; float *pix3; zdialog *zd = EFpaint.zd; if (Fdrag) return; // pass through to main() if (LMclick && KBshiftkey) // shift + left mouse click { LMclick = 0; pix3 = PXMpix(E3pxm,Mxclick,Myclick); // pick new color from image RGB[0] = pix3[0]; RGB[1] = pix3[1]; RGB[2] = pix3[2]; snprintf(color,19,"%d|%d|%d",RGB[0],RGB[1],RGB[2]); if (zd) zdialog_stuff(zd,"colorbutt",color); return; } if (Mxdrag || Mydrag) // drag in progress { if (Mxdown != pmxdown || Mydown != pmydown) { // new drag pixBseq++; // new undo seq. no. pmxdown = Mxdown; pmydown = Mydown; } if (Mxdrag == pmxdrag && Mydrag == pmydrag) { // no movement Mxdrag = Mydrag = 0; return; } pmxdrag = Mxdrag; pmydrag = Mydrag; paint_dopixels(Mxdrag,Mydrag); // do 1 block of pixels } draw_mousecircle(Mxposn,Myposn,Mradius,0,0); // draw mouse circle Mxdrag = Mydrag = LMclick = 0; return; } // paint or erase 1 block of pixels within mouse radius of px, py void paint_image_names::paint_dopixels(int px, int py) { using namespace paint_image_names; float *pix1, *pix3; int radius, dx, dy, qx, qy; int ii, ww, hh, dist = 0; int pot = ac * Fptran; // paint over transparent areas float red, green, blue; float kern; cairo_t *cr = draw_context_create(gdkwin,draw_context); draw_mousecircle(0,0,0,1,cr); // erase mouse circle ww = E3pxm->ww; hh = E3pxm->hh; paint_savepixB(px,py); // save pixels for poss. undo red = RGB[0]; // paint color green = RGB[1]; blue = RGB[2]; radius = Mradius; if (mode == 1) { // paint CEF->Fmods++; CEF->Fsaved = 0; } for (dy = -radius; dy <= radius; dy++) // loop surrounding block of pixels for (dx = -radius; dx <= radius; dx++) { qx = px + dx; qy = py + dy; if (qx < 0 || qx > ww-1) continue; if (qy < 0 || qy > hh-1) continue; if (sa_stat == 3) { // select area active ii = qy * ww + qx; dist = sa_pixmap[ii]; if (! dist) continue; // pixel is outside area } kern = kernel[dx+radius][dy+radius]; // mouse opacities if (kern == 0) continue; // outside mouse radius if (sa_stat == 3 && dist < sa_blendwidth) // select area edge blend, kern = kern * sa_blendfunc(dist); // reduce opacity pix1 = PXMpix(E1pxm,qx,qy); // source image pixel pix3 = PXMpix(E3pxm,qx,qy); // edited image pixel if (mode == 1 && Mbutton < 2) // paint { kern = kern * (1.0 + 50.0 * wacom_pressure); // wacom if (kern > 1.0) kern = 1.0; pix3[0] = kern * red + (1.0 - kern) * pix3[0]; // overpaints accumulate pix3[1] = kern * green + (1.0 - kern) * pix3[1]; pix3[2] = kern * blue + (1.0 - kern) * pix3[2]; if (pot) pix3[3] = kern * 255.0 + (1.0 - kern) * pix3[3]; // overpaint transparent area } if (mode == 2 || Mbutton >= 2) // erase { kern = kern * (2.0 + 50.0 * wacom_pressure); // wacom if (kern > 1.0) kern = 1.0; if (kern > 1) kern = 1; pix3[0] = kern * pix1[0] + (1.0 - kern) * pix3[0]; // gradual erase pix3[1] = kern * pix1[1] + (1.0 - kern) * pix3[1]; pix3[2] = kern * pix1[2] + (1.0 - kern) * pix3[2]; if (pot) pix3[3] = kern * pix1[3] + (1.0 - kern) * pix3[3]; } } px = px - radius - 1; // repaint modified area py = py - radius - 1; ww = 2 * radius + 3; Fpaint3(px,py,ww,ww,cr); draw_context_destroy(draw_context); return; } // save 1 block of pixels for possible undo void paint_image_names::paint_savepixB(int px, int py) { using namespace paint_image_names; int cc, npix, radius, dx, dy; float *pix3; pixBmem_t *paintsave1; if (! pixBmem) { // first time pixBmem = (pixBmem_t **) zmalloc(maxpixB * sizeof(void *)); totpixB = 0; totmem = 0; } if (totmem > maxmem || totpixB == maxpixB) // free memory for oldest updates while (totmem > 0.7 * maxmem || totpixB > 0.7 * maxpixB) paint_freefirstB(); radius = Mradius; npix = 0; for (dy = -radius; dy <= radius; dy++) // count pixels in block for (dx = -radius; dx <= radius; dx++) { if (px + dx < 0 || px + dx > E3pxm->ww-1) continue; if (py + dy < 0 || py + dy > E3pxm->hh-1) continue; npix++; } cc = npix * pcc4 + pixBmem_cc; paintsave1 = (pixBmem_t *) zmalloc(cc); // allocate memory for block pixBmem[totpixB] = paintsave1; totpixB += 1; totmem += cc; paintsave1->seq = pixBseq; // save pixel block poop paintsave1->px = px; paintsave1->py = py; paintsave1->radius = radius; npix = 0; for (dy = -radius; dy <= radius; dy++) // save pixels in block for (dx = -radius; dx <= radius; dx++) { if (px + dx < 0 || px + dx > E3pxm->ww-1) continue; if (py + dy < 0 || py + dy > E3pxm->hh-1) continue; pix3 = PXMpix(E3pxm,(px+dx),(py+dy)); // edited image pixel paintsave1->pixel[npix][0] = pix3[0]; paintsave1->pixel[npix][1] = pix3[1]; paintsave1->pixel[npix][2] = pix3[2]; if (ac) paintsave1->pixel[npix][3] = pix3[3]; npix++; } return; } // undo last pixel block (newest edit) and free memory void paint_image_names::paint_undolastB() { using namespace paint_image_names; int ii, cc, npix, radius; int ww, px, py, dx, dy; float *pix3; pixBmem_t *paintsave1; for (ii = totpixB-1; ii >= 0; ii--) { paintsave1 = pixBmem[ii]; if (paintsave1->seq != pixBseq) break; px = paintsave1->px; py = paintsave1->py; radius = paintsave1->radius; npix = 0; for (dy = -radius; dy <= radius; dy++) for (dx = -radius; dx <= radius; dx++) { if (px + dx < 0 || px + dx > E3pxm->ww-1) continue; if (py + dy < 0 || py + dy > E3pxm->hh-1) continue; pix3 = PXMpix(E3pxm,(px+dx),(py+dy)); pix3[0] = paintsave1->pixel[npix][0]; pix3[1] = paintsave1->pixel[npix][1]; pix3[2] = paintsave1->pixel[npix][2]; if (ac) pix3[3] = paintsave1->pixel[npix][3]; npix++; } px = px - radius - 1; py = py - radius - 1; ww = 2 * radius + 3; Fpaint3(px,py,ww,ww,0); zfree(paintsave1); pixBmem[ii] = 0; cc = npix * pcc4 + pixBmem_cc; totmem -= cc; totpixB--; } if (pixBseq > 0) --pixBseq; return; } // free memory for first pixel block (oldest edit) void paint_image_names::paint_freefirstB() { using namespace paint_image_names; int firstseq; int ii, jj, cc, npix, radius; int px, py, dx, dy; pixBmem_t *paintsave1; if (! totpixB) return; firstseq = pixBmem[0]->seq; for (ii = 0; ii < totpixB; ii++) { paintsave1 = pixBmem[ii]; if (paintsave1->seq != firstseq) break; px = paintsave1->px; py = paintsave1->py; radius = paintsave1->radius; npix = 0; for (dy = -radius; dy <= radius; dy++) for (dx = -radius; dx <= radius; dx++) { if (px + dx < 0 || px + dx > E3pxm->ww-1) continue; if (py + dy < 0 || py + dy > E3pxm->hh-1) continue; npix++; } zfree(paintsave1); pixBmem[ii] = 0; cc = npix * pcc4 + pixBmem_cc; totmem -= cc; } for (jj = 0; ii < totpixB; jj++, ii++) pixBmem[jj] = pixBmem[ii]; totpixB = jj; return; } // free all pixel block memory void paint_image_names::paint_freeallB() { using namespace paint_image_names; int ii; pixBmem_t *paintsave1; for (ii = totpixB-1; ii >= 0; ii--) { paintsave1 = pixBmem[ii]; zfree(paintsave1); } if (pixBmem) zfree(pixBmem); pixBmem = 0; pixBseq = 0; totpixB = 0; totmem = 0; return; } /********************************************************************************/ // Select a color from a color chooser image file. // Returns color button in callers zdialog. // Returns selected RGB color in prgb[3] argument. namespace RGB_chooser_names { // char *RGB_chooser_file; // defined in fotoxx.h char color[20]; zdialog *pzdialog; cchar *pcolorbutt; uint8 *pRGB; PIXBUF *pixbuf = 0; GtkWidget *frame, *drawarea; STATB statb; } int RGB_chooser(zdialog *pzd, cchar *pbutt, uint8 prgb[3]) // 19.0 { using namespace RGB_chooser_names; int RGB_chooser_dialog_event(zdialog *zd, cchar *event); // dialog events function int RGB_chooser_draw(GtkWidget *window, cairo_t *cr); // window draw function int RGB_chooser_mouse(GtkWidget *window, GdkEventButton *); // window mouse button event pzdialog = pzd; // copy args from caller pcolorbutt = pbutt; pRGB = prgb; if (! RGB_chooser_file || stat(RGB_chooser_file,&statb)) { // default color chooser file 20.0 RGB_chooser_file = (char *) zmalloc(200); snprintf(RGB_chooser_file,200,"%s/colorwheel.jpg",get_zimagedir()); } /*** ____________________________________________ | Color Chooser | | | | click on desired color | | ________________________________________ | | | | | | | | | | | | | | | image | | | | | | | | | | | | | | | | | | | |________________________________________| | | | | [_______________________________] [browse] | | | | [cancel] | |____________________________________________| ***/ zdialog *zd = zdialog_new(E2X("Color Chooser"),Mwin,Bcancel,null); zdialog_add_widget(zd,"label","labclick","dialog",E2X("click on desired color")); zdialog_add_widget(zd,"frame","frame","dialog"); frame = zdialog_widget(zd,"frame"); drawarea = gtk_drawing_area_new(); gtk_widget_set_size_request(drawarea,-1,200); gtk_container_add(GTK_CONTAINER(frame),drawarea); zdialog_add_widget(zd,"hbox","hbfile","dialog",0,"space=3"); zdialog_add_widget(zd,"zentry","file","hbfile",0,"space=3|expand"); zdialog_add_widget(zd,"button","browse","hbfile",Bbrowse,"space=3"); zdialog_stuff(zd,"file",RGB_chooser_file); G_SIGNAL(drawarea,"draw",RGB_chooser_draw,0); G_SIGNAL(drawarea,"button-press-event",RGB_chooser_mouse,0); gtk_widget_add_events(drawarea,GDK_BUTTON_PRESS_MASK); zdialog_resize(zd,300,0); zdialog_run(zd,RGB_chooser_dialog_event,"save"); return 0; } // dialog event and completion function int RGB_chooser_dialog_event(zdialog *zd, cchar *event) { using namespace RGB_chooser_names; char *pp; if (strmatch(event,"browse")) { pp = navi::galleryname; // 20.0 if (! pp) pp = topfolders[0]; pp = gallery_select1(pp); if (! pp) return 1; zdialog_stuff(zd,"file",pp); if (RGB_chooser_file) zfree(RGB_chooser_file); RGB_chooser_file = pp; gtk_widget_queue_draw(drawarea); } if (zd->zstat) zdialog_free(zd); return 1; } // color chooser window draw function int RGB_chooser_draw(GtkWidget *widget, cairo_t *cr) { using namespace RGB_chooser_names; PIXBUF *pixbuf1; GError *gerror = 0; GdkWindow *gdkwin; int ww1, hh1, ww2, hh2; if (*RGB_chooser_file != '/') return 1; // load last color chooser file pixbuf1 = gdk_pixbuf_new_from_file_at_size(RGB_chooser_file,500,500,&gerror); if (! pixbuf1) { printz("pixbuf error: %s \n",gerror->message); // popup message >> draw event loop return 1; // GTK 3.22.11 } ww1 = gdk_pixbuf_get_width(pixbuf1); // image dimensions hh1 = gdk_pixbuf_get_height(pixbuf1); gdkwin = gtk_widget_get_window(widget); // set drawing area to match ww2 = gdk_window_get_width(gdkwin); // aspect ratio hh2 = ww2 * hh1 / ww1; gtk_widget_set_size_request(widget,-1,hh2); if (pixbuf) g_object_unref(pixbuf); pixbuf = gdk_pixbuf_scale_simple(pixbuf1,ww2,hh2,BILINEAR); g_object_unref(pixbuf1); if (! pixbuf) return 1; gdk_cairo_set_source_pixbuf(cr,pixbuf,0,0); // draw image cairo_paint(cr); return 1; } // color chooser mouse click function int RGB_chooser_mouse(GtkWidget *widget, GdkEventButton *event) { using namespace RGB_chooser_names; int mx, my, rs, nc; uint8 *pixels, *pix1; if (! pixbuf) return 1; rs = gdk_pixbuf_get_rowstride(pixbuf); nc = gdk_pixbuf_get_n_channels(pixbuf); pixels = gdk_pixbuf_get_pixels(pixbuf); mx = event->x; my = event->y; pix1 = pixels + my * rs + mx * nc; snprintf(color,20,"%d|%d|%d",pix1[0],pix1[1],pix1[2]); // update caller's color button 19.0 zdialog_stuff(pzdialog,pcolorbutt,color); pRGB[0] = pix1[0]; // update caller's paint color 19.0 pRGB[1] = pix1[1]; pRGB[2] = pix1[2]; return 1; } /********************************************************************************/ // HSL color chooser function // Returns color button in callers zdialog. // Returns selected RGB color in prgb[3] argument. namespace HSL_chooser_names { zdialog *HSLzdialog; GtkWidget *RGBframe, *RGBcolor; GtkWidget *Hframe, *Hscale; zdialog *pzdialog; cchar *pcolorbutt; uint8 *pRGB; float H, S, L; // chosen HSL color float R, G, B; // corresp. RGB color } int HSL_chooser(zdialog *pzd, cchar *pbutt, uint8 prgb[3]) // 19.0 { using namespace HSL_chooser_names; void HSL_chooser_RGB(GtkWidget *drawarea, cairo_t *cr, int *); void HSL_chooser_Hscale(GtkWidget *drawarea, cairo_t *cr, int *); int HSL_chooser_dialog_event(zdialog *zd, cchar *event); pzdialog = pzd; // copy args from caller pcolorbutt = pbutt; pRGB = prgb; /*** ________________________________________________ | | | [#######] [##############################] | | Color Hue ================[]============== | | Saturation =====================[]========= | | Lightness ===========[]=================== | | | | [cancel] | |________________________________________________| ***/ zdialog *zd = zdialog_new("Adjust HSL",Mwin,Bcancel,null); HSLzdialog = zd; zdialog_add_widget(zd,"hbox","hb2","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb2",0,"homog|space=0"); zdialog_add_widget(zd,"vbox","vb2","hb2",0,"homog|expand|space=0"); zdialog_add_widget(zd,"frame","RGBframe","vb1",0,"space=1"); // drawing area for RGB color RGBframe = zdialog_widget(zd,"RGBframe"); RGBcolor = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(RGBframe),RGBcolor); gtk_widget_set_size_request(RGBcolor,0,16); G_SIGNAL(RGBcolor,"draw",HSL_chooser_RGB,0); zdialog_add_widget(zd,"frame","Hframe","vb2",0,"space=1"); // drawing area for hue scale Hframe = zdialog_widget(zd,"Hframe"); Hscale = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(Hframe),Hscale); gtk_widget_set_size_request(Hscale,200,16); G_SIGNAL(Hscale,"draw",HSL_chooser_Hscale,0); zdialog_add_widget(zd,"label","labhue","vb1",E2X("Color Hue")); zdialog_add_widget(zd,"label","labsat","vb1",E2X("Saturation")); zdialog_add_widget(zd,"label","lablgt","vb1",E2X("Lightness")); zdialog_add_widget(zd,"hscale","H","vb2","0|359.9|0.1|180","expand"); zdialog_add_widget(zd,"hscale","S","vb2","0|1|0.001|0.5","expand"); zdialog_add_widget(zd,"hscale","L","vb2","0|1|0.001|0.5","expand"); H = 180; // chosen HSL color = not set S = 0.5; L = 0.5; zdialog_run(zd,HSL_chooser_dialog_event,"save"); // run dialog - parallel return 1; } // Paint RGBcolor drawing area with RGB color from chosen HSL color void HSL_chooser_RGB(GtkWidget *drawarea, cairo_t *cr, int *) { using namespace HSL_chooser_names; int ww, hh; int r, g, b; char color[20]; ww = gtk_widget_get_allocated_width(drawarea); // drawing area size hh = gtk_widget_get_allocated_height(drawarea); HSLtoRGB(H,S,L,R,G,B); // RGB color from chosen HSL cairo_set_source_rgb(cr,R,G,B); cairo_rectangle(cr,0,0,ww-1,hh-1); cairo_fill(cr); r = 255 * R; g = 255 * G; b = 255 * B; snprintf(color,20,"%d|%d|%d",r,g,b); // update caller's color button 19.0 zdialog_stuff(pzdialog,pcolorbutt,color); pRGB[0] = r; // update caller's paint color 19.0 pRGB[1] = g; pRGB[2] = b; return; } // Paint Hscale drawing area with all hue values in a horizontal scale void HSL_chooser_Hscale(GtkWidget *drawarea, cairo_t *cr, int *) { using namespace HSL_chooser_names; int px, ww, hh; float H, S, L, R, G, B; ww = gtk_widget_get_allocated_width(drawarea); // drawing area size hh = gtk_widget_get_allocated_height(drawarea); S = L = 0.5; for (px = 0; px < ww; px++) // paint hue color scale { H = 360 * px / ww; HSLtoRGB(H,S,L,R,G,B); cairo_set_source_rgb(cr,R,G,B); cairo_move_to(cr,px,0); cairo_line_to(cr,px,hh-1); cairo_stroke(cr); } return; } // HSL dialog event and completion function int HSL_chooser_dialog_event(zdialog *zd, cchar *event) // HSL dialog event function { using namespace HSL_chooser_names; if (zd->zstat) { // zdialog complete zdialog_free(zd); freeMouse(); HSLzdialog = 0; return 1; } if (zstrstr("H S L",event)) { // HSL inputs changed zdialog_fetch(zd,"H",H); zdialog_fetch(zd,"S",S); zdialog_fetch(zd,"L",L); gtk_widget_queue_draw(RGBcolor); // draw corresp. RGB color } return 1; } /********************************************************************************/ // Copy Pixels function. // Copy from one image area to another with variable opacity. namespace copypixels1 { int dialog_event(zdialog* zd, cchar *event); void mousefunc(); void dopixels(int px, int py); // update pixel block void savepixB(int px, int py); // save pixel block for poss. undo void undolastB(); // undo last pixel block, free memory void freefirstB(); // free memory for first pixel block void freeallB(); // free memory for all pixel blocks int mode; // 1/2 = paint / erase int Mradius; // mouse radius int imagex, imagey; // source image location float kernel[402][402]; // radius <= 200 int Fptran = 0; // flag, paint over transparent areas int nc, ac; // no. channels, alpha channel int64 maxmem = (int64) 4000 * MEGA; // max. pixel block memory int64 totmem; // pixB memory allocated int maxpixB = 10000; // max. pixel blocks int totpixB = 0; // total pixel blocks int pixBseq = 0; // last pixel block sequence no. typedef struct { // pixel block before edit int seq; // block sequence no. uint16 px, py; // center pixel (radius org.) uint16 radius; // radius of pixel block float pixel[][4]; // array of pixel[npix][4] } pixBmem_t; pixBmem_t **pixBmem = 0; // *pixBmem_t[] int pixBmem_cc = 12; // all except pixel array + pad int pcc4 = 4 * sizeof(float); // pixel cc: RGBA = 4 channels editfunc EFcopypix1; } // menu function void m_copypixels1(GtkWidget *, cchar *) // separate paint and copy { using namespace copypixels1; cchar *mess1 = E2X("shift + left click: pick position to copy \n" "left click or drag: copy image to mouse \n" "right click or drag: restore original image"); F1_help_topic = "copy pixels 1"; EFcopypix1.menufunc = m_copypixels1; EFcopypix1.funcname = "copy_pixels_1"; EFcopypix1.Farea = 2; // select area OK EFcopypix1.mousefunc = mousefunc; // mouse function if (! edit_setup(EFcopypix1)) return; // setup edit /*** ____________________________________________________ | Copy Pixels (1 image) | | | | shift + left click: pick image position to copy | | left click or drag: copy image to mouse position | | right click or drag: restore original image | | | | brush size [____] [undo last] | | opacity center [____] [undo all] | | opacity edge [____] | | | | [x] paint transparent areas | | | | [done] [cancel] | |____________________________________________________| ***/ zdialog *zd = zdialog_new(E2X("Copy Pixels (1 image)"),Mwin,Bdone,Bcancel,null); EFcopypix1.zd = zd; zdialog_add_widget(zd,"hbox","hbr","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labm","dialog",mess1,"space=5"); zdialog_add_widget(zd,"hbox","hbbri","dialog",0,"space=5"); zdialog_add_widget(zd,"vbox","vbbr1","hbbri",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vbbr2","hbbri",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","space","hbbri",0,"space=10"); zdialog_add_widget(zd,"vbox","vbbr3","hbbri",0,"space=10"); zdialog_add_widget(zd,"label","labbr","vbbr1",E2X("brush size")); zdialog_add_widget(zd,"label","labtc","vbbr1",Bopacitycenter); zdialog_add_widget(zd,"label","labte","vbbr1",Bopacityedge); zdialog_add_widget(zd,"zspin","Mradius","vbbr2","1|200|1|30"); zdialog_add_widget(zd,"zspin","opccent","vbbr2","1|100|1|30"); zdialog_add_widget(zd,"zspin","opcedge","vbbr2","0|100|1|0"); zdialog_add_widget(zd,"button","undlast","vbbr3",Bundolast); zdialog_add_widget(zd,"button","undall","vbbr3",Bundoall); zdialog_add_widget(zd,"hbox","hb4","dialog",0,"space=3"); zdialog_add_widget(zd,"check","Fptran","hb4",E2X("paint over transparent areas"),"space=5"); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,dialog_event,"save"); // run dialog, parallel zdialog_fetch(zd,"Fptran",Fptran); // paint over transparent areas zdialog_send_event(zd,"Mradius"); // get kernel initialized totmem = 0; // memory used pixBmem = 0; // pixel block memory totpixB = 0; // pixel blocks pixBseq = 0; imagex = imagey = 0; // no source pixels ac = 0; nc = E1pxm->nc; // channels, RGBA if (nc > 3) ac = 1; // alpha channel present takeMouse(mousefunc,drawcursor); // connect mouse function return; } // dialog event and completion callback function int copypixels1::dialog_event(zdialog *zd, cchar *event) { using namespace copypixels1; int radius, dx, dy; float rad, kern, opccent, opcedge; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit freeallB(); // free pixel block memory return 1; } draw_mousecircle(0,0,0,1,0); // erase mouse circle draw_mousecircle2(0,0,0,1,0); // erase source tracking circle if (strmatch(event,"focus")) // toggle mouse capture takeMouse(mousefunc,drawcursor); if (zstrstr("Mradius opccent opcedge",event)) // get new brush attributes { zdialog_fetch(zd,"Mradius",Mradius); zdialog_fetch(zd,"opccent",opccent); zdialog_fetch(zd,"opcedge",opcedge); opccent = 0.01 * opccent; // scale 0 ... 1 opcedge = 0.01 * opcedge; opccent = pow(opccent,2); // change response curve opcedge = opccent * opcedge; // edge relative to center radius = Mradius; for (dy = -radius; dy <= radius; dy++) // build kernel for (dx = -radius; dx <= radius; dx++) { rad = sqrt(dx*dx + dy*dy); kern = (radius - rad) / radius; // center ... edge >> 1 ... 0 kern = kern * (opccent - opcedge) + opcedge; // opacity center ... edge if (rad > radius) kern = 0; // beyond radius, within square if (kern < 0) kern = 0; if (kern > 1) kern = 1; kernel[dx+radius][dy+radius] = kern; } } if (strmatch(event,"undlast")) // undo last edit (click or drag) undolastB(); if (strmatch(event,"undall")) { // undo all edits edit_reset(); freeallB(); } if (strmatch(event,"Fptran")) // flag, paint over transparency zdialog_fetch(zd,"Fptran",Fptran); return 1; } // pixel paint mouse function void copypixels1::mousefunc() { using namespace copypixels1; static int pmxdown = 0, pmydown = 0; int px, py; if (LMclick && KBshiftkey) // shift + left mouse click { imagex = Mxclick; // new source image location imagey = Myclick; } else if (LMclick || RMclick) { if (LMclick) mode = 1; // left click, paint if (RMclick) mode = 2; // right click, erase px = Mxdown = Mxclick; py = Mydown = Myclick; pixBseq++; // new undo seq. no. dopixels(px,py); // do 1 block of pixels } else if (Mxdrag || Mydrag) // drag in progress { if (Mbutton == 1) mode = 1; // left drag, paint if (Mbutton == 3) mode = 2; // right drag, erase px = Mxdrag; py = Mydrag; if (Mxdown != pmxdown || Mydown != pmydown) { // new drag pixBseq++; // new undo seq. no. pmxdown = Mxdown; pmydown = Mydown; } dopixels(px,py); // do 1 block of pixels } draw_mousecircle(Mxposn,Myposn,Mradius,0,0); // draw mouse circle if (mode == 1 && (Mxdown || Mydown)) { // 2nd circle tracks source pixels px = imagex + Mxposn - Mxdown; py = imagey + Myposn - Mydown; if (px > 0 && px < E3pxm->ww-1 && py > 0 && py < E3pxm->hh-1) draw_mousecircle2(px,py,Mradius,0,0); } else draw_mousecircle2(0,0,0,1,0); // no 2nd circle LMclick = RMclick = Mxdrag = Mydrag = 0; return; } // paint or erase 1 block of pixels within mouse radius of px, py void copypixels1::dopixels(int px, int py) { using namespace copypixels1; float *pix1, *pix3; int radius, dx, dy, qx, qy, sx, sy; int ii, ww, hh, dist = 0; int pot = ac * Fptran; // paint over transparent areas float kern; if (! imagex && ! imagey) return; // no source area defined cairo_t *cr = draw_context_create(gdkwin,draw_context); draw_mousecircle(0,0,0,1,cr); // erase mouse circle draw_mousecircle2(0,0,0,1,cr); // erase source tracking circle ww = E3pxm->ww; hh = E3pxm->hh; savepixB(px,py); // save pixels for poss. undo radius = Mradius; if (mode == 1) { CEF->Fmods++; CEF->Fsaved = 0; } for (dy = -radius; dy <= radius; dy++) // loop surrounding block of pixels for (dx = -radius; dx <= radius; dx++) { qx = px + dx; qy = py + dy; if (qx < 0 || qx > ww-1) continue; if (qy < 0 || qy > hh-1) continue; if (sa_stat == 3) { // select area active ii = qy * ww + qx; dist = sa_pixmap[ii]; if (! dist) continue; // pixel is outside area } kern = kernel[dx+radius][dy+radius]; // mouse opacities if (kern == 0) continue; // outside mouse radius if (sa_stat == 3 && dist < sa_blendwidth) // select area edge blend, kern = kern * sa_blendfunc(dist); // reduce opacity pix1 = PXMpix(E1pxm,qx,qy); // source image pixel pix3 = PXMpix(E3pxm,qx,qy); // edited image pixel if (mode == 1) // paint { 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; pix1 = PXMpix(E1pxm,sx,sy); // source image pixel at location kern = 0.3 * kern; pix3[0] = kern * pix1[0] + (1.0 - kern) * pix3[0]; // overpaints accumulate pix3[1] = kern * pix1[1] + (1.0 - kern) * pix3[1]; pix3[2] = kern * pix1[2] + (1.0 - kern) * pix3[2]; if (pot) pix3[3] = kern * pix1[3] + (1.0 - kern) * pix3[3]; // overpaint transparent area } if (mode == 2) // erase { pix3[0] = kern * pix1[0] + (1.0 - kern) * pix3[0]; // gradual erase pix3[1] = kern * pix1[1] + (1.0 - kern) * pix3[1]; pix3[2] = kern * pix1[2] + (1.0 - kern) * pix3[2]; if (pot) pix3[3] = kern * pix1[3] + (1.0 - kern) * pix3[3]; } } px = px - radius - 1; // repaint modified area py = py - radius - 1; ww = 2 * radius + 3; Fpaint3(px,py,ww,ww,cr); draw_context_destroy(draw_context); return; } // save 1 block of pixels for possible undo void copypixels1::savepixB(int px, int py) { using namespace copypixels1; int cc, npix, radius, dx, dy; float *pix3; pixBmem_t *save1B; if (! pixBmem) { // first time pixBmem = (pixBmem_t **) zmalloc(maxpixB * sizeof(void *)); totpixB = 0; totmem = 0; } if (totmem > maxmem || totpixB == maxpixB) // free memory for oldest updates while (totmem > 0.7 * maxmem || totpixB > 0.7 * maxpixB) freefirstB(); radius = Mradius; npix = 0; for (dy = -radius; dy <= radius; dy++) // count pixels in block for (dx = -radius; dx <= radius; dx++) { if (px + dx < 0 || px + dx > E3pxm->ww-1) continue; if (py + dy < 0 || py + dy > E3pxm->hh-1) continue; npix++; } cc = npix * pcc4 + pixBmem_cc; save1B = (pixBmem_t *) zmalloc(cc); // allocate memory for block pixBmem[totpixB] = save1B; totpixB += 1; totmem += cc; save1B->seq = pixBseq; // save pixel block poop save1B->px = px; save1B->py = py; save1B->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 save1B->pixel[npix][0] = pix3[0]; save1B->pixel[npix][1] = pix3[1]; save1B->pixel[npix][2] = pix3[2]; if (ac) save1B->pixel[npix][3] = pix3[3]; npix++; } return; } // undo last pixel block (newest edit) and free memory void copypixels1::undolastB() { using namespace copypixels1; int ii, cc, npix, radius; int ww, px, py, dx, dy; float *pix3; pixBmem_t *save1B; for (ii = totpixB-1; ii >= 0; ii--) { save1B = pixBmem[ii]; if (save1B->seq != pixBseq) break; px = save1B->px; py = save1B->py; radius = save1B->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] = save1B->pixel[npix][0]; pix3[1] = save1B->pixel[npix][1]; pix3[2] = save1B->pixel[npix][2]; if (ac) pix3[3] = save1B->pixel[npix][3]; npix++; } px = px - radius - 1; py = py - radius - 1; ww = 2 * radius + 3; Fpaint3(px,py,ww,ww,0); zfree(save1B); pixBmem[ii] = 0; cc = npix * pcc4 + pixBmem_cc; totmem -= cc; totpixB--; } if (pixBseq > 0) --pixBseq; return; } // free memory for first pixel block (oldest edit) void copypixels1::freefirstB() { using namespace copypixels1; int firstseq; int ii, jj, cc, npix, radius; int px, py, dx, dy; pixBmem_t *save1B; if (! totpixB) return; firstseq = pixBmem[0]->seq; for (ii = 0; ii < totpixB; ii++) { save1B = pixBmem[ii]; if (save1B->seq != firstseq) break; px = save1B->px; py = save1B->py; radius = save1B->radius; npix = 0; for (dy = -radius; dy <= radius; dy++) for (dx = -radius; dx <= radius; dx++) { if (px + dx < 0 || px + dx > E3pxm->ww-1) continue; if (py + dy < 0 || py + dy > E3pxm->hh-1) continue; npix++; } zfree(save1B); pixBmem[ii] = 0; cc = npix * pcc4 + pixBmem_cc; totmem -= cc; } for (jj = 0; ii < totpixB; jj++, ii++) pixBmem[jj] = pixBmem[ii]; totpixB = jj; return; } // free all pixel block memory void copypixels1::freeallB() { using namespace copypixels1; int ii; pixBmem_t *save1B; for (ii = totpixB-1; ii >= 0; ii--) { save1B = pixBmem[ii]; zfree(save1B); } if (pixBmem) zfree(pixBmem); pixBmem = 0; pixBseq = 0; totpixB = 0; totmem = 0; return; } /********************************************************************************/ // Copy Pixels function. // 'Paint' a target image with pixels from a different source image. namespace copypixels2 { int dialog_event(zdialog* zd, cchar *event); void mousefunc(); // generate mouse circle void dopixels(); // copy pixel block to target void save_pixblock(); // save pixel block for poss. undo void undo_lastblock(); // undo last pixel block, free memory void free_firstblock(); // free memory for first pixel block void free_allblocks(); // free memory for all pixel blocks editfunc EFcopypix2; int mode; // 1/2 = paint / erase int Fptran; // flag, paint over transparent areas int mpxC, mpyC; // center of pixel copy area int mpx, mpy; // center of moving mouse circle int mrad; // radius of pixel copy area typedef struct { // shared memory data int killsource; // source image process should exit int mpxC, mpyC; // center of pixel copy area int mpx, mpy; // mouse drag position int mrad; // mouse radius int Fvalid; // mouse data valid flag float Fscale; // source image scale factor int Freq; // source/target coord. flag float pixels[402*402*4]; // pixel block, max. mrad = 200 } mmap_data_t; mmap_data_t *mmap_data; // shared memory pointer float kernel[402][402]; // mrad <= 200 int64 maxmem = (int64) 4000 * MEGA; // max. pixel block memory int64 totmem; // pixblock memory allocated int maxpixblock = 10000; // max. pixel blocks int totpixblock = 0; // total pixel blocks int pixblockseq = 0; // last pixel block sequence no. typedef struct { // saved pixel block before edit int seq; // block sequence no. uint16 mpx, mpy; // center pixel (mrad org.) uint16 mrad; // radius of pixel block float pixels[][4]; // array of pixels, rows x cols } pixblockmem_t; pixblockmem_t **pixblockmem; int pixblockmem_cc = 12; // all except pixel array + pad } // menu function void m_copypixels2(GtkWidget *, cchar *) // split source/target processes { using namespace copypixels2; int err, fd; size_t cc; fd = shm_open("/fotoxx_copy_pixels2",O_RDWR+O_CREAT,0600); // identify memory mapped region if (fd < 0) { zmessageACK(Mwin,"shm_open() failure: %s",strerror(errno)); return; } cc = sizeof(mmap_data_t); err = ftruncate(fd,cc); if (err) { zmessageACK(Mwin,"ftruncate() failure: %s",strerror(errno)); return; } mmap_data = (mmap_data_t *) mmap(0,cc,PROT_WRITE,MAP_SHARED,fd,0); // create memory mapped region if (mmap_data == (void *) -1) { zmessageACK(Mwin,"mmap() failure: %s",strerror(errno)); return; } memset(mmap_data,0,cc); mpxC = mpyC = -1; // no pixel copy area selected mrad = -1; mmap_data->Fvalid = 0; // no valid target mouse data yet mmap_data->Fscale = 1.0; // defaule source image scale factor EFcopypix2.menufunc = m_copypixels2; // start edit function for target image EFcopypix2.funcname = "copy_pixels_2"; EFcopypix2.Farea = 2; // select area OK EFcopypix2.mousefunc = mousefunc; // mouse function if (! edit_setup(EFcopypix2)) return; F1_help_topic = "copy pixels 2"; PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); pixblockmem = (pixblockmem_t **) zmalloc(maxpixblock * sizeof(void *)); // saved pixel blocks list totmem = 0; // memory used totpixblock = 0; // pixel blocks pixblockseq = 0; /**** __________________________________________________ | Copy Pixels (2 images) | | | | left click: synchronize copy position | | left click or drag: copy source image to mouse | | right click or drag: restore original image | | | | source image scale [____] | | | | brush size [____] [undo last] | | opacity center [____] [undo all] | | opacity edge [____] | | | | [x] paint over transparent areas | | | | [done] [cancel] | |__________________________________________________| ****/ cchar *mess1 = E2X("left click: synchronize copy position \n" "left click or drag: copy source image to mouse \n" "right click or drag: restore original image"); zdialog *zd = zdialog_new(E2X("Copy Pixels (2 images)"),Mwin,Bdone,Bcancel,null); EFcopypix2.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","hbsc","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labsc","hbsc",E2X("source image scale"),"space=3"); zdialog_add_widget(zd,"zspin","scale","hbsc","0.2|5.0|0.01|1.0","space=3"); zdialog_add_widget(zd,"hbox","hbbri","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vbbr1","hbbri",0,"homog|space=3"); zdialog_add_widget(zd,"vbox","vbbr2","hbbri",0,"homog|space=3"); 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",E2X("brush size")); zdialog_add_widget(zd,"label","labtc","vbbr1",Bopacitycenter); zdialog_add_widget(zd,"label","labte","vbbr1",Bopacityedge); zdialog_add_widget(zd,"zspin","mrad","vbbr2","1|200|1|30"); zdialog_add_widget(zd,"zspin","opccent","vbbr2","1|100|1|10"); zdialog_add_widget(zd,"zspin","opcedge","vbbr2","0|100|1|0"); zdialog_add_widget(zd,"button","undlast","vbbr3",Bundolast); zdialog_add_widget(zd,"button","undall","vbbr3",Bundoall); zdialog_add_widget(zd,"hbox","hb4","dialog",0,"space=3"); zdialog_add_widget(zd,"check","Fptran","hb4",E2X("paint over transparent areas"),"space=5"); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,dialog_event,"save"); // run dialog, parallel zdialog_fetch(zd,"Fptran",Fptran); // paint over transparent areas zdialog_send_event(zd,"mrad"); // get kernel initialized takeMouse(mousefunc,drawcursor); // connect mouse function save_params(); // communicate current file bugfix 19.19 new_session("-x 1 -m \"Copy Pixels 3\" "); // start source image fotoxx process return; } // dialog event and completion callback function int copypixels2::dialog_event(zdialog *zd, cchar *event) { using namespace copypixels2; int dx, dy; float rad, kern, opccent, opcedge; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit free_allblocks(); // free pixel block memory zfree(pixblockmem); mmap_data->killsource = 1; // make source image process exit return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(mousefunc,drawcursor); if (strmatch(event,"scale")) // source image scale change zdialog_fetch(zd,"scale",mmap_data->Fscale); if (zstrstr("mrad opccent opcedge",event)) // get new brush attributes { zdialog_fetch(zd,"mrad",mrad); zdialog_fetch(zd,"opccent",opccent); zdialog_fetch(zd,"opcedge",opcedge); opccent = 0.01 * opccent; // scale 0 ... 1 opcedge = 0.01 * opcedge; opccent = pow(opccent,2); // change response curve opcedge = opccent * opcedge; // edge relative to center for (dy = -mrad; dy <= mrad; dy++) // rebuild kernel for (dx = -mrad; dx <= mrad; dx++) { rad = sqrt(dx*dx + dy*dy); kern = (mrad - rad) / mrad; // center ... edge >> 1 ... 0 kern = kern * (opccent - opcedge) + opcedge; // opacity center ... edge if (rad > mrad) kern = 0; // beyond mrad, within square if (kern < 0) kern = 0; if (kern > 1) kern = 1; kernel[dx+mrad][dy+mrad] = kern; } } if (strmatch(event,"undlast")) // undo last edit (click or drag) undo_lastblock(); if (strmatch(event,"undall")) { // undo all edits edit_reset(); free_allblocks(); } if (strmatch(event,"Fptran")) // flag, paint over transparency zdialog_fetch(zd,"Fptran",Fptran); return 1; } // mouse function void copypixels2::mousefunc() { using namespace copypixels2; static int pmxdown = 0, pmydown = 0; static int pmpx, pmpy; mode = 0; mpx = Mxposn; mpy = Myposn; mmap_data->mpx = mpx; // inform source image, mmap_data->mpy = mpy; // mouse position, mmap_data->mrad = mrad; // mouse radius if (LMclick) // left mouse click { Mxdown = Mxclick; // click position Mydown = Myclick; mmap_data->mpxC = Mxdown; // inform source image, mmap_data->mpyC = Mydown; // new mouse drag start mmap_data->Fvalid = 1; pixblockseq++; // new undo seq. no. mode = 1; if (! mmap_data->Freq) { // request pixel block from source mmap_data->Freq = 1; pmpx = mpx; // save mouse position pmpy = mpy; } } else if (Mxdrag || Mydrag) // drag in progress { if (Mbutton == 1) mode = 1; // left drag, paint if (Mbutton == 3) mode = 2; // right drag, erase if (Mxdown != pmxdown || Mydown != pmydown) { // new drag pmxdown = Mxdown; pmydown = Mydown; if (mode == 1) pixblockseq++; // new undo seq. no. } if (mode == 1 && ! mmap_data->Freq) { // request pixel block from source mmap_data->Freq = 1; pmpx = mpx; // save mouse position pmpy = mpy; } } if (mode == 1 && mmap_data->Freq == 2) { // new pixel block from source avail. mpx = pmpx; // synch mouse position mpy = pmpy; dopixels(); // paint pixel block on target image mmap_data->Freq = 0; // ready for next } if (mode == 2) dopixels(); // erase target image draw_mousecircle(0,0,0,1,0); // erase mouse circle draw_mousecircle(mpx,mpy,mrad,0,0); // draw mouse circle LMclick = RMclick = Mxdrag = Mydrag = 0; return; } // paint or erase 1 block of pixels within mouse radius of mpx, mpy void copypixels2::dopixels() { using namespace copypixels2; float *pix1, *pix2, *pix3, pixF[4]; int dx, dy, qx, qy, px, py; int ii, ww, hh, rs, dist = 0; int pot = Fptran; // paint over transparent areas float kern, alpha1, alpha2; ww = E3pxm->ww; hh = E3pxm->hh; rs = 2 * mrad + 1; // pixel block row stride save_pixblock(); // save pixels for poss. undo for (dy = -mrad; dy <= mrad; dy++) // loop surrounding block of pixels for (dx = -mrad; dx <= mrad; dx++) { qx = mpx + dx; qy = mpy + 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+mrad][dy+mrad]; // mouse opacities if (kern == 0) continue; // outside mouse radius if (sa_stat == 3 && dist < sa_blendwidth) // select area edge blend, kern = kern * sa_blendfunc(dist); // reduce opacity pix1 = PXMpix(E1pxm,qx,qy); // input target image pixel ii = (dy + mrad) * rs + (dx + mrad); // source image pixel >> target pix2 = mmap_data->pixels + ii * 4; pix3 = PXMpix(E3pxm,qx,qy); // output target image pixel if (mode == 1) // paint { if (pot) alpha1 = 1; // paint over transparent areas else alpha1 = 0.00392 * pix1[3]; // input target image opacity alpha2 = 0.00392 * pix2[3]; // source image opacity if (alpha2 < 0.99) { pixF[0] = pix2[0] * alpha2 + pix1[0] * (1 - alpha2); // construct output target pixel pixF[1] = pix2[1] * alpha2 + pix1[1] * (1 - alpha2); // = source image pixel pixF[2] = pix2[2] * alpha2 + pix1[2] * (1 - alpha2); // + target image pixel pixF[3] = pix2[3] * alpha2 + pix1[3] * (1 - alpha2); } else { pixF[0] = pix2[0]; // (no transparency case) pixF[1] = pix2[1]; pixF[2] = pix2[2]; pixF[3] = pix2[3]; } if (alpha1 < 0.99) { pixF[0] = pixF[0] * alpha1; // apply target image opacity pixF[1] = pixF[1] * alpha1; pixF[2] = pixF[2] * alpha1; pixF[3] = pixF[3] * alpha1; } kern = 0.2 * kern; // output target image changes pix3[0] = kern * pixF[0] + (1 - kern) * pix3[0]; // gradually to source image pix3[1] = kern * pixF[1] + (1 - kern) * pix3[1]; pix3[2] = kern * pixF[2] + (1 - kern) * pix3[2]; pix3[3] = kern * pixF[3] + (1 - kern) * pix3[3]; } if (mode == 2) // erase { kern = 0.4 * kern; pix3[0] = kern * pix1[0] + (1.0 - kern) * pix3[0]; // gradual erase pix3[1] = kern * pix1[1] + (1.0 - kern) * pix3[1]; pix3[2] = kern * pix1[2] + (1.0 - kern) * pix3[2]; pix3[3] = kern * pix1[3] + (1.0 - kern) * pix3[3]; } } if (mode == 1) { CEF->Fmods++; CEF->Fsaved = 0; } px = mpx - mrad - 1; // repaint modified area py = mpy - mrad - 1; ww = 2 * mrad + 3; Fpaint3(px,py,ww,ww,0); return; } // save 1 block of pixels for possible undo void copypixels2::save_pixblock() { using namespace copypixels2; int cc, npix, dx, dy; int pcc = 4 * sizeof(float); float *pix3; pixblockmem_t *save1B; if (totmem > maxmem || totpixblock == maxpixblock) // free memory for oldest updates while (totmem > 0.7 * maxmem || totpixblock > 0.7 * maxpixblock) free_firstblock(); npix = 0; for (dy = -mrad; dy <= mrad; dy++) // count pixels in block for (dx = -mrad; dx <= mrad; dx++) { if (mpx + dx < 0 || mpx + dx > E3pxm->ww-1) continue; if (mpy + dy < 0 || mpy + dy > E3pxm->hh-1) continue; npix++; } cc = npix * pcc + pixblockmem_cc; save1B = (pixblockmem_t *) zmalloc(cc); // allocate memory for block pixblockmem[totpixblock] = save1B; totpixblock += 1; totmem += cc; save1B->seq = pixblockseq; // save pixel block poop save1B->mpx = mpx; save1B->mpy = mpy; save1B->mrad = mrad; npix = 0; for (dy = -mrad; dy <= mrad; dy++) // save pixels in block for (dx = -mrad; dx <= mrad; dx++) { if (mpx + dx < 0 || mpx + dx > E3pxm->ww-1) continue; if (mpy + dy < 0 || mpy + dy > E3pxm->hh-1) continue; pix3 = PXMpix(E3pxm,(mpx+dx),(mpy+dy)); // edited image pixel memcpy(save1B->pixels[npix],pix3,pcc); npix++; } return; } // undo last pixel block (newest edit) and free memory void copypixels2::undo_lastblock() { using namespace copypixels2; int ii, cc, npix; int ww, px, py, dx, dy; int pcc = 4 * sizeof(float); float *pix3; pixblockmem_t *save1B; for (ii = totpixblock-1; ii >= 0; ii--) { save1B = pixblockmem[ii]; if (save1B->seq != pixblockseq) break; px = save1B->mpx; py = save1B->mpy; mrad = save1B->mrad; npix = 0; for (dy = -mrad; dy <= mrad; dy++) for (dx = -mrad; dx <= mrad; 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)); memcpy(pix3,save1B->pixels[npix],pcc); npix++; } px = px - mrad - 1; py = py - mrad - 1; ww = 2 * mrad + 3; Fpaint3(px,py,ww,ww,0); zfree(save1B); pixblockmem[ii] = 0; cc = npix * pcc + pixblockmem_cc; totmem -= cc; totpixblock--; } if (pixblockseq > 0) --pixblockseq; return; } // free memory for first pixel block (oldest edit) void copypixels2::free_firstblock() { using namespace copypixels2; int firstseq; int ii, jj, cc, npix; int px, py, dx, dy; int pcc = 4 * sizeof(float); pixblockmem_t *save1B; if (! totpixblock) return; firstseq = pixblockmem[0]->seq; for (ii = 0; ii < totpixblock; ii++) { save1B = pixblockmem[ii]; if (save1B->seq != firstseq) break; px = save1B->mpx; py = save1B->mpy; mrad = save1B->mrad; npix = 0; for (dy = -mrad; dy <= mrad; dy++) for (dx = -mrad; dx <= mrad; dx++) { if (px + dx < 0 || px + dx > E3pxm->ww-1) continue; if (py + dy < 0 || py + dy > E3pxm->hh-1) continue; npix++; } zfree(save1B); pixblockmem[ii] = 0; cc = npix * pcc + pixblockmem_cc; totmem -= cc; } for (jj = 0; ii < totpixblock; jj++, ii++) pixblockmem[jj] = pixblockmem[ii]; totpixblock = jj; return; } // free all pixel block memory void copypixels2::free_allblocks() { using namespace copypixels2; int ii; pixblockmem_t *save1B; for (ii = totpixblock-1; ii >= 0; ii--) { save1B = pixblockmem[ii]; zfree(save1B); } pixblockseq = 0; totpixblock = 0; totmem = 0; return; } /********************************************************************************/ // Copy Pixels source image function. // Started by m_copypixels2() as a separate session to view the source image. namespace copypixels3 { void mousefunc(); void dopixels(); // update pixel block PXM *pxm_1x = 0, *pxm_scaled = 0; int mpxC, mpyC; // center of pixel copy area int mpx, mpy; // center of moving mouse circle int mrad; // radius of pixel copy area float Fscale = -1; typedef struct { // shared memory data int killsource; // source image process should exit int mpxC, mpyC; // center of pixel copy area int mpx, mpy; // mouse drag position int mrad; // mouse radius int Fvalid; // mouse data valid flag float Fscale; // source image scale factor int Freq; // source/target coord. flag float pixels[402*402*4]; // pixel block, max. mrad = 200 } mmap_data_t; mmap_data_t *mmap_data; // shared memory pointer } // menu function void m_copypixels3(GtkWidget *, cchar *) // split source/target processes { using namespace copypixels3; int err, fd; int pmpx = 0, pmpy = 0, pmrad = 0; size_t cc; F1_help_topic = "copy pixels 2"; fd = shm_open("/fotoxx_copy_pixels2",O_RDWR+O_CREAT,0600); // identify memory mapped region if (fd < 0) { zmessageACK(Mwin,"shm_open() failure: %s",strerror(errno)); return; } cc = sizeof(mmap_data_t); err = ftruncate(fd,cc); if (err) { zmessageACK(Mwin,"ftruncate() failure: %s",strerror(errno)); return; } mmap_data = (mmap_data_t *) mmap(0,cc,PROT_WRITE,MAP_SHARED,fd,0); // create memory mapped region if (mmap_data == (void *) -1) { zmessageACK(Mwin,"mmap() failure: %s",strerror(errno)); return; } char *Pcurr_file = 0; int Frefresh = 1; int ww, hh; int mpx2, mpy2, mrad2; mpxC = mpyC = -1; // no pixel copy area selected mrad = -1; while (true) { zsleep(0.01); zmainloop(3); if (! curr_file) continue; if (mmap_data->killsource) goto done; // target image process is done if (! Pcurr_file || ! strmatch(curr_file,Pcurr_file)) { // detect file change if (Pcurr_file) zfree(Pcurr_file); Pcurr_file = zstrdup(curr_file); Frefresh = 1; } if (CEF) { // source image edit, wait until done Frefresh = 1; continue; } if (mmap_data->Fscale != Fscale) Frefresh = 1; if (Frefresh) { printz("m_copypixels3() source image: %s \n",curr_file); // 19.19 if (pxm_1x) PXM_free(pxm_1x); // refresh edited image if (E0pxm) pxm_1x = PXM_copy(E0pxm); else pxm_1x = PXM_load(curr_file,1); // load new image if (! pxm_1x) goto fail; Fscale = mmap_data->Fscale; ww = pxm_1x->ww * Fscale; hh = pxm_1x->hh * Fscale; if (pxm_scaled) PXM_free(pxm_scaled); pxm_scaled = PXM_rescale(pxm_1x,ww,hh); if (! pxm_scaled) goto fail; PXM_addalpha(pxm_scaled); mpxC = mpyC = -1; // no copy area selected takeMouse(mousefunc,dragcursor); // connect mouse function Frefresh = 0; } if (mpxC < 0) continue; // no source pixel copy area if (! mmap_data->Fvalid) continue; // no valid target mouse data mpx = mpxC + mmap_data->mpx - mmap_data->mpxC; // convert target copy area mpy = mpyC + mmap_data->mpy - mmap_data->mpyC; // to source copy area mrad = mmap_data->mrad; if (mpx != pmpx || mpy != pmpy || mrad != pmrad) { // mouse circle changed pmpx = mpx; pmpy = mpy; pmrad = mrad; mpx2 = mpx / Fscale; mpy2 = mpy / Fscale; mrad2 = mrad / Fscale; gdk_window_freeze_updates(gdkwin); draw_mousecircle(0,0,0,1,0); // erase mouse circle draw_mousecircle(mpx2,mpy2,mrad2,0,0); // draw mouse circle gdk_window_thaw_updates(gdkwin); zmainloop(); } if (mmap_data->Freq == 1) { // pixels wanted from target image dopixels(); // copy pixel block to shared memory mmap_data->Freq = 2; // pixel block available } } fail: zmessageACK(Mwin,E2X("source image failure (scale too big?)")); // 20.0 done: if (CEF) edit_cancel(0); printz("m_copypixels3() exit \n"); quitxx(); // unconditional exit 20.05 } // mouse function void copypixels3::mousefunc() { using namespace copypixels3; if (LMclick) { // left mouse click mpxC = Mxclick * Fscale; // new source pixel copy area mpyC = Myclick * Fscale; LMclick = 0; } return; } // copy block of pixels within mouse circle to shared memory void copypixels3::dopixels() { using namespace copypixels3; float *pix0, *pix1; float pixnull[4] = { 0, 0, 0, 0 }; // black, alpha = 0 int dx, dy, qx, qy; int ii, ww, hh, rs; int pcc = 4 * sizeof(float); ww = pxm_scaled->ww; hh = pxm_scaled->hh; rs = 2 * mrad + 1; // pixel block row stride for (dy = -mrad; dy <= mrad; dy++) // loop surrounding block of pixels for (dx = -mrad; dx <= mrad; dx++) { qx = mpx + dx; qy = mpy + dy; if (qx < 0 || qx > ww-1 || qy < 0 || qy > hh-1) pix0 = pixnull; // pixel outside source image else pix0 = PXMpix(pxm_scaled,qx,qy); // source image pixel ii = (dy + mrad) * rs + (dx + mrad); pix1 = mmap_data->pixels + ii * 4; // to shared memory memcpy(pix1,pix0,pcc); } return; } /********************************************************************************/ // 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 = E2X("Paint Edits"); cchar *helptext = E2X("Press F1 for help"); F1_help_topic = "paint edits"; if (sa_stat || zd_sela) { // warn select area will be lost int yn = zmessageYN(Mwin,E2X("Select area cannot be kept.\n" "Continue?")); if (! yn) return; sa_clear(); // clear area if (zd_sela) zdialog_free(zd_sela); } if (! CEF) { // edit func must be active zmessageACK(Mwin,E2X("Edit function must be active")); return; } if (! CEF->FusePL) { zmessageACK(Mwin,E2X("Cannot use Paint Edits")); return; } if (CEF->Fpreview) zdialog_send_event(CEF->zd,"fullsize"); // use full-size image /*** ______________________________________ | Press F1 for help | | Edit Function must be active | | | | mouse radius [____] | | power: center [____] edge [____] | | [reset area] | | [done] | |______________________________________| ***/ zd_sela = zdialog_new(title,Mwin,Bdone,null); zdialog_add_widget(zd_sela,"label","labhelp1","dialog",helptext,"space=5"); zdialog_add_widget(zd_sela,"label","labspace","dialog"); zdialog_add_widget(zd_sela,"hbox","hbr","dialog",0,"space=3"); zdialog_add_widget(zd_sela,"label","labr","hbr",Bmouseradius,"space=5"); zdialog_add_widget(zd_sela,"zspin","radius","hbr","2|500|1|50"); zdialog_add_widget(zd_sela,"hbox","hbt","dialog",0,"space=3"); zdialog_add_widget(zd_sela,"label","labtc","hbt",E2X("power: center"),"space=5"); zdialog_add_widget(zd_sela,"zspin","center","hbt","0|100|1|50"); zdialog_add_widget(zd_sela,"label","labte","hbt",Bedge,"space=5"); zdialog_add_widget(zd_sela,"zspin","edge","hbt","0|100|1|0"); zdialog_add_widget(zd_sela,"hbox","hbra","dialog",0,"space=5"); zdialog_add_widget(zd_sela,"button","reset","hbra",E2X("reset area"),"space=5"); paint_edits_radius = 50; paint_edits_cpower = 50; paint_edits_epower = 0; sa_pixmap_create(); // allocate select area pixel maps 19.0 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_blendwidth = 10000; // "blend width" sa_fww = Fpxb->ww; // valid image dimensions sa_fhh = Fpxb->hh; areanumber++; // next sequential number zdialog_run(zd_sela,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_blendwidth = maximum edit power (edit fully applied) int paint_edits_dialog_event(zdialog *zd, cchar *event) { void paint_edits_mousefunc(); // mouse function if (zd->zstat) // done or cancel { freeMouse(); // disconnect mouse function if (CEF) zdialog_send_event(CEF->zd,"done"); // complete edit zdialog_free(zd_sela); // kill dialog sa_clear(); // clear area return 1; } 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_clear(); // clear current area if any sa_pixmap_create(); // allocate select area pixel maps 19.0 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_blendwidth = 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() { static int busy = 0; int ii, px, py, rx, ry; int radius, radius2, cpower, epower; float rad, rad2, power; if (! CEF) return; // no active edit if (sa_stat != 3) return; // area gone? radius = paint_edits_radius; // pixel selection radius radius2 = radius * radius; cpower = paint_edits_cpower; epower = paint_edits_epower; draw_mousecircle(Mxposn,Myposn,radius,0,0); // show mouse selection circle if (! Mxdrag && ! Mydrag) return; // wait for drag event Mxdrag = Mydrag = 0; // neutralize drag if (busy++) return; // avoid re-entrance 20.0 for (rx = -radius; rx <= radius; rx++) // loop every pixel in radius for (ry = -radius; ry <= radius; ry++) { rad2 = rx * rx + ry * ry; if (rad2 > radius2) continue; // outside radius px = Mxposn + rx; py = Myposn + ry; if (px < 0 || px > Fpxb->ww-1) continue; // off the image edge if (py < 0 || py > Fpxb->hh-1) continue; ii = Fpxb->ww * py + px; rad = sqrt(rad2); power = cpower + rad / radius * (epower - cpower); // power at pixel radius if (Mbutton == 1) { // left mouse button sa_pixmap[ii] += 5.0 * power; // increase edit power if (sa_pixmap[ii] > sa_blendwidth) sa_pixmap[ii] = sa_blendwidth; } if (Mbutton == 3) { // right mouse button if (sa_pixmap[ii] <= 5.0 * power) sa_pixmap[ii] = 0; // weaken edit power else sa_pixmap[ii] -= 5.0 * power; } } zdialog_send_event(CEF->zd,"blendwidth"); // notify edit dialog draw_mousecircle(Mxposn,Myposn,radius,0,0); // show mouse selection circle busy = 0; return; } /********************************************************************************/ // Undo effects of current edit function in areas painted with the mouse. // Mouse can be weak or strong, and edits are removed incrementally. // // method: // E3 image (edited) is incrementally replaced from E1 image (original); namespace undo_edits_names { editfunc EFundo_edits; PXM *E0source, *E0edited; int Eww, Ehh; int radius; int cpower; int epower; } // menu function void m_undo_edits(GtkWidget *, cchar *) // menu function 20.0 { using namespace undo_edits_names; int undo_edits_dialog_event(zdialog *, cchar *event); // dialog event function void undo_edits_mousefunc(); // mouse function zdialog *zd; F1_help_topic = "undo edits"; if (CEF) { // edit func must be active zmessageACK(Mwin,E2X("finish current edit first")); return; } if (URS_pos < 1) { zmessageACK(Mwin,E2X("no previous edit")); return; } if (! E0pxm) { zmessageACK(Mwin,E2X("no current image")); return; } m_undo(0,0); // load previous edit version E0source = PXM_copy(E0pxm); // >> unedited image m_redo(0,0); // load current image E0edited = PXM_copy(E0pxm); // >> edited image if (E0source->ww != E0edited->ww || E0source->hh != E0edited->hh) { zmessageACK(Mwin,E2X("This edit cannot be incrementally undone")); PXM_free(E0source); // free memory PXM_free(E0edited); return; } Eww = E0source->ww; Ehh = E0source->hh; EFundo_edits.menufunc = m_undo_edits; // start new edit EFundo_edits.funcname = "undo edits"; EFundo_edits.mousefunc = undo_edits_mousefunc; if (! edit_setup(EFundo_edits)) { PXM_free(E0source); PXM_free(E0edited); return; } /*** ______________________________________ | Undo Edits | | | | mouse radius [____] | | power: center [____] edge [____] | | | | [done] [cancel] | |______________________________________| ***/ zd = zdialog_new(E2X("Undo Edits"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbr","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labr","hbr",Bmouseradius,"space=5"); zdialog_add_widget(zd,"zspin","radius","hbr","2|500|1|200"); zdialog_add_widget(zd,"hbox","hbt","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labtc","hbt",E2X("power: center"),"space=5"); zdialog_add_widget(zd,"zspin","center","hbt","0|100|1|50"); zdialog_add_widget(zd,"label","labte","hbt",Bedge,"space=5"); zdialog_add_widget(zd,"zspin","edge","hbt","0|100|1|0"); EFundo_edits.zd = zd; radius = 200; cpower = 50; epower = 0; zdialog_run(zd,undo_edits_dialog_event,"save"); // run dialog - parallel return; } // dialog event and completion function int undo_edits_dialog_event(zdialog *zd, cchar *event) { using namespace undo_edits_names; void undo_edits_mousefunc(); // mouse function if (strmatch(event,"done")) zd->zstat = 1; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 2; // cancel if (zd->zstat) // done or cancel { freeMouse(); // disconnect mouse function edit_done(0); // complete edit PXM_free(E0source); // free memory PXM_free(E0edited); return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(undo_edits_mousefunc,0); if (strmatch(event,"radius")) zdialog_fetch(zd,"radius",radius); // mouse radius if (strmatch(event,"center")) zdialog_fetch(zd,"center",cpower); // mouse center power if (strmatch(event,"edge")) zdialog_fetch(zd,"edge",epower); // mouse edge power return 1; } // mouse function void undo_edits_mousefunc() { using namespace undo_edits_names; int px, py, ww, rx, ry, radius2; float rad, rad2, power, F1; float R0, G0, B0, R1, G1, B1, R3, G3, B3; float *pix0, *pix1, *pix3; radius2 = radius * radius; draw_mousecircle(Mxposn,Myposn,radius,0,0); // show mouse selection circle if (! Mxdrag && ! Mydrag) return; // wait for drag event 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 > Eww-1) continue; // off the image edge if (py < 0 || py > Ehh-1) continue; rad = sqrt(rad2); power = cpower + rad / radius * (epower - cpower); // power at pixel radius, 0 - 100 F1 = 0.0001 * power; // scale 0 - 0.01 pix0 = PXMpix(E3pxm,px,py); // current pixel pix1 = PXMpix(E0source,px,py); // original image pixel pix3 = PXMpix(E0edited,px,py); // edited image pixel R0 = pix0[0]; // current pixel RGB G0 = pix0[1]; B0 = pix0[2]; R1 = pix1[0]; // original pixel RGB G1 = pix1[1]; B1 = pix1[2]; R3 = pix3[0]; // edited pixel RGB G3 = pix3[1]; B3 = pix3[2]; if (Mbutton == 1) { // left mouse, move to E1 R0 += F1 * (R1 - R3); G0 += F1 * (G1 - G3); B0 += F1 * (B1 - B3); } else if (Mbutton == 3) { // right mouse, move to E3 R0 += F1 * (R3 - R1); G0 += F1 * (G3 - G1); B0 += F1 * (B3 - B1); } if (R3 > R1) { // limit R range to R1 ... R3 if (R0 < R1) R0 = R1; else if (R0 > R3) R0 = R3; } else { if (R0 < R3) R0 = R3; else if (R0 > R1) R0 = R1; } if (G3 > G1) { // G range if (G0 < G1) G0 = G1; else if (G0 > G3) G0 = G3; } else { if (G0 < G3) G0 = G3; else if (G0 > G1) G0 = G1; } if (B3 > B1) { // B range if (B0 < B1) B0 = B1; else if (B0 > B3) B0 = B3; } else { if (B0 < B3) B0 = B3; else if (B0 > B1) B0 = B1; } pix0[0] = R0; pix0[1] = G0; pix0[2] = B0; } px = Mxposn - radius - 1; // repaint modified area py = Myposn - radius - 1; ww = 2 * radius + 3; Fpaint3(px,py,ww,ww,0); draw_mousecircle(Mxposn,Myposn,radius,0,0); // show mouse selection circle CEF->Fmods++; CEF->Fsaved = 0; return; } /********************************************************************************/ // Plugin menu functions namespace plugins_names { #define maxplugins 100 int Nplugins; // plugin menu items char *plugins[100]; GtkWidget *popup_plugmenu = 0; editfunc EFplugin; } // edit plugins menu or choose and run a plugin function void m_plugins(GtkWidget *, cchar *) { using namespace plugins_names; void m_edit_plugins(GtkWidget *, cchar *); void m_run_plugin(GtkWidget *, cchar *); char plugfile[200], buff[200], *pp; FILE *fid; STATB stbuff; int ii, err; F1_help_topic = "plugins"; if (popup_plugmenu) { popup_menu(Mwin,popup_plugmenu); // popup the plugins menu return; } snprintf(plugfile,200,"%s/plugins",get_zhomedir()); // plugins file err = stat(plugfile,&stbuff); // exists? if (err) { fid = fopen(plugfile,"w"); // no, create default fprintf(fid,"Gimp = gimp %%s \n"); fprintf(fid,"auto-gamma = mogrify -auto-gamma %%s \n"); fprintf(fid,"whiteboard cleanup = mogrify " // ImageMagick white board cleanup "-morphology Convolve DoG:15,100,0 " "-negate -normalize -blur 0x1 -channel RBG " "-level 60%%,91%%,0.1 %%s \n"); fclose(fid); } fid = fopen(plugfile,"r"); // open plugins file if (! fid) { zmessageACK(Mwin,"plugins file: %s",strerror(errno)); return; } for (ii = 0; ii < 99; ii++) // read list of plugins { pp = fgets_trim(buff,200,fid,1); if (! pp) break; plugins[ii] = zstrdup(buff); } fclose(fid); Nplugins = ii; popup_plugmenu = create_popmenu(); // create popup menu for plugins add_popmenu_item(popup_plugmenu, E2X("Edit Plugins"), // 1st entry is Edit Plugins m_edit_plugins, 0, E2X("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, E2X("Run as Fotoxx edit function")); *pp = ' '; } popup_menu(Mwin,popup_plugmenu); // popup the menu return; } /********************************************************************************/ // edit plugins menu void m_edit_plugins(GtkWidget *, cchar *) // overhauled { using namespace plugins_names; int edit_plugins_event(zdialog *zd, cchar *event); int ii; char *pp; zdialog *zd; F1_help_topic = "plugins"; if (FGWM != 'F' && FGWM != 'G') return; // 19.0 /*** ___________________________________ | Edit Plugins | | | | menu name [_____________|v] | e.g. edit with gimp | command [___________________] | e.g. gimp %s | | | [Add] [Remove] [Done] | |___________________________________| ***/ zd = zdialog_new(E2X("Edit Plugins"),Mwin,Badd,Bremove,Bdone,null); zdialog_add_widget(zd,"hbox","hbm","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labm","hbm",E2X("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",E2X("command"),"space=5"); zdialog_add_widget(zd,"zentry","command","hbc",0,"space=5|expand"); for (ii = 0; ii < Nplugins; ii++) // stuff combo box with available menus { pp = strstr(plugins[ii]," = "); // menu name = command line if (! pp) continue; *pp = 0; zdialog_cb_app(zd,"menuname",plugins[ii]); *pp = ' '; } zdialog_set_modal(zd); zdialog_run(zd,edit_plugins_event,"mouse"); return; } // dialog event function int edit_plugins_event(zdialog *zd, cchar *event) { using namespace plugins_names; int ii, jj, cc, zstat; char *pp, menuname[100], command[200]; char buff[200]; FILE *fid; if (strmatch(event,"menuname")) { zdialog_fetch(zd,"menuname",menuname,100); for (ii = 0; ii < Nplugins; ii++) // find selected menu name { pp = strstr(plugins[ii]," = "); if (! pp) continue; *pp = 0; jj = strmatch(menuname,plugins[ii]); *pp = ' '; if (jj) { zdialog_stuff(zd,"command",pp+3); // stuff corresp. command in dialog break; } } return 1; } zstat = zd->zstat; // wait for dialog completion if (! zstat) return 1; if (zstat < 1 || zstat > 3) { // cancel zdialog_free(zd); return 1; } if (zstat == 1) // add plugin or replace same menu name { zd->zstat = 0; // keep dialog active if (Nplugins == maxplugins) { zmessageACK(Mwin,"too many plugins"); return 1; } zdialog_fetch(zd,"menuname",menuname,100); zdialog_fetch(zd,"command",command,200); pp = strstr(command," %s"); if (! pp) zmessageACK(Mwin,"Warning: command without \"%%s\" "); for (ii = 0; ii < Nplugins; ii++) // find existing plugin record { pp = strstr(plugins[ii]," = "); if (! pp) continue; *pp = 0; jj = strmatch(menuname,plugins[ii]); *pp = ' '; if (jj) break; } if (ii == Nplugins) { // new plugin record plugins[ii] = 0; Nplugins++; zdialog_cb_app(zd,"menuname",menuname); pp = zdialog_cb_get(zd,"menuname",ii); } if (plugins[ii]) zfree(plugins[ii]); cc = strlen(menuname) + strlen(command) + 4; plugins[ii] = (char *) zmalloc(cc); *plugins[ii] = 0; strncatv(plugins[ii],cc,menuname," = ",command,0); return 1; // 19.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",""); return 1; // 19.0 } if (zstat == 3) // done { snprintf(buff,199,"%s/plugins",get_zhomedir()); // open file for plugins fid = fopen(buff,"w"); if (! fid) { zmessageACK(Mwin,strerror(errno)); return 1; } for (int ii = 0; ii < Nplugins; ii++) // save plugins if (plugins[ii]) fprintf(fid,"%s \n",plugins[ii]); fclose(fid); zdialog_free(zd); popup_plugmenu = 0; // rebuild popup menu return 1; // 19.0 } return 1; } /********************************************************************************/ // process plugin menu selection // execute corresponding command using current image file void m_run_plugin(GtkWidget *, cchar *menu) { using namespace plugins_names; int ii, jj, err; char *pp = 0, plugincommand[200], pluginfile[100]; PXM *pxmtemp; zdialog *zd = 0; F1_help_topic = "plugins"; for (ii = 0; ii < Nplugins; ii++) // search plugins for menu name { pp = strchr(plugins[ii],'='); // match menu name to plugin command if (! pp) continue; // menu name = ... *pp = 0; jj = strmatch(plugins[ii],menu); *pp = '='; if (jj) break; } if (ii == Nplugins) { zmessageACK(Mwin,"plugin menu not found %s",menu); return; } strncpy0(plugincommand,pp+1,200); // corresp. command strTrim2(plugincommand); pp = strstr(plugincommand,"%s"); // no file placeholder in command if (! pp) { err = shell_ack(plugincommand); // execute non-edit plugin command goto RETURN; } EFplugin.menufunc = m_run_plugin; EFplugin.funcname = menu; if (! edit_setup(EFplugin)) return; // start edit function snprintf(pluginfile,100,"%s/plugfile.tif",temp_folder); // .../tempfiles-nnnnnn/plugfile.tif err = PXM_save(E1pxm,pluginfile,8,100,1); // E1 >> plugin_file if (err) goto FAIL; zd = zmessage_post(Mwin,"20/20",0,E2X("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,E2X("plugin failed")); goto FAIL; } PXM_free(E3pxm); // plugin_file >> E3 E3pxm = pxmtemp; CEF->Fmods++; // assume image was modified CEF->Fsaved = 0; edit_done(0); goto RETURN; FAIL: edit_cancel(0); RETURN: if (zd) zdialog_free(zd); return; } /********************************************************************************/ // menu function: open a camera RAW file and edit with the Raw Therapee GUI // opens 'rawfile' if not null, else 'clicked_file' if not null void m_rawtherapee(GtkWidget *, cchar *) { char * rawtherapee_findtiff(cchar *pattern); char *pp; char *rawfile, *tiffile = 0; int err; STATB statb; F1_help_topic = "raw therapee"; if (! Frawtherapee) { zmessageACK(Mwin,E2X("Raw Therapee not installed")); return; } if (checkpend("busy block mods")) return; rawfile = 0; if (clicked_file) { rawfile = clicked_file; clicked_file = 0; } else if (curr_file) rawfile = zstrdup(curr_file); if (! rawfile) return; if (image_file_type(rawfile) != RAW) { zmessageACK(Mwin,E2X("RAW type not registered in User Preferences")); zfree(rawfile); return; } pp = zescape_quotes(rawfile); shell_ack("rawtherapee \"%s\"",pp); zfree(pp); tiffile = raw_to_tiff(rawfile); // tif file with base name = RAW zfree(rawfile); err = stat(tiffile,&statb); // look first in RAW file folder if (! err) goto found; pp = strrchr(tiffile,'/'); // not found, search top folders 20.06 pp = rawtherapee_findtiff(pp+1); if (! pp) goto retx; // not found zfree(tiffile); // found tiffile = zstrdup(pp); found: err = f_open(tiffile,0,0,1); // open tiff file if (err) goto retx; update_image_index(tiffile); // update index rec. retx: if (tiffile) zfree(tiffile); else zmessage_post(Mwin,"20/20",3,E2X("Raw Therapee produced no tif file")); m_viewmode(0,"F"); return; } /********************************************************************************/ // Find first image file matching a base name pattern. // All top image folders and subfolders are searched. // The base file name pattern may have * and ? wildcards. char * rawtherapee_findtiff(cchar *pattern) // 20.06 { int ii; char command[300], buff[500]; FILE *fid; char *pp; for (ii = 0; ii < Ntopfolders; ii++) { snprintf(command,300,"find \"%s\" -name \"%s\" -print -quit", topfolders[ii], pattern); fid = popen(command,"r"); if (! fid) continue; pp = fgets_trim(buff,500,fid); pclose(fid); if (pp) return pp; } return 0; } fotoxx-20.08/f.effects.cc000066400000000000000000010502461362435004500152360ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* Fotoxx image edit - Effects menu functions - paint, sketch ... and 'filters' m_sketch convert photo to simulated sketch m_cartoon convert image to cartoon drawing m_line_drawing combine image with highlighted edge pixels m_emboss convert image to simulated embossing (3D relief) m_tiles convert image to square tiles with 3D edges m_dither parent menu for four dither functions m_dither0 Roy Lichtenstein effect m_dither1 pure RGB or black/white dots m_dither2 RGB mix with given bit-depth m_dither3 custom palette colors m_painting convert image to simulated painting 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_color_mode convert between color, B&W, sepia, positive, negative m_color_depth reduce color depth from 16 ... 1 bits m_shift_colors gradually shift selected RGB colors into other colors m_alien_colors change color hues with an algorithm m_brite_ramp adjust brightness graduaally across the image m_paint_transp paint more or less transparency on an image m_mirror horizontal or vertical mirror image m_anykernel transform an image using any custom kernel *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // sketch menu function - convert photo to simulated sketch namespace sketch_names { editfunc EFsketch; // edit function data float Bweight = 0.5; // brightness weight 0-1 int Brthresh = 255; // brightness threshold 0-255 float Cweight = 0.5; // contrast weight 0-1 int cliplev = 255; // clipping level 0-255 int algorithm = 1; // which algorithm to use uint8 fgrgb[3] = { 0, 0, 0 }; // foreground color (black) uint8 bgrgb[3] = { 255, 255, 255 }; // background color (white) int ww, hh, cc; // image dimensions uint8 *britness; // maps pixel brightness 0-255 uint8 *CLcont; // maps pixel color contrast 0-255 uint8 *monopix; // output pixel map 0-255 uint8 *fclip; // output clipping flag 0/1 } void m_sketch(GtkWidget *, const char *menu) { using namespace sketch_names; int sketch_dialog_event(zdialog* zd, const char *event); void * sketch_thread(void *); int ii, px, py; float *pix0, *pix1, *pix2, *pix3, *pix4; float f1, f2; cchar *title = E2X("Convert to Sketch"); F1_help_topic = "sketch"; EFsketch.menuname = menu; EFsketch.menufunc = m_sketch; EFsketch.funcname = "sketch"; // function name EFsketch.Farea = 2; // select area usable EFsketch.Fscript = 1; // scripting supported 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",E2X("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",E2X("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",E2X("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",E2X("Background"),"space=2"); zdialog_add_widget(zd,"colorbutt","bgcolor","hb6","255|255|255","space=2"); Bweight = 0.5; // initial values Brthresh = 255; Cweight = 0.5; cliplev = 255; algorithm = 1; zdialog_stuff(zd,"algorithm1",1); zdialog_resize(zd,250,0); zdialog_run(zd,sketch_dialog_event,"save"); // run dialog - parallel signal_thread(); return; } // sketch dialog event and completion function int sketch_dialog_event(zdialog *zd, const char *event) { using namespace sketch_names; int ii; cchar *pp; char color[20]; if (strmatch(event,"focus")) return 1; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit zfree(britness); zfree(CLcont); zfree(monopix); zfree(fclip); return 1; } zdialog_fetch(zd,"Bweight",Bweight); // revised brightness weight zdialog_fetch(zd,"Brthresh",Brthresh); // brightness threshold zdialog_fetch(zd,"Cweight",Cweight); // contrast weight zdialog_fetch(zd,"cliplev",cliplev); cliplev = 255 - cliplev; // scale is reversed zdialog_fetch(zd,"algorithm1",ii); if (ii == 1) algorithm = 1; else algorithm = 2; zdialog_fetch(zd,"fgcolor",color,19); pp = strField(color,"|",1); if (pp) fgrgb[0] = atoi(pp); pp = strField(color,"|",2); if (pp) fgrgb[1] = atoi(pp); pp = strField(color,"|",3); if (pp) fgrgb[2] = atoi(pp); zdialog_fetch(zd,"bgcolor",color,19); pp = strField(color,"|",1); if (pp) bgrgb[0] = atoi(pp); pp = strField(color,"|",2); if (pp) bgrgb[1] = atoi(pp); pp = strField(color,"|",3); if (pp) bgrgb[2] = atoi(pp); signal_thread(); return 1; } // thread function - multiple working threads to update image void * sketch_thread(void *) { using namespace sketch_names; void sketch_algorithm1(); void sketch_algorithm2(); void sketch_finish(); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 if (algorithm == 1) sketch_algorithm1(); if (algorithm == 2) sketch_algorithm2(); sketch_finish(); CEF->Fmods++; // image modified CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.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_blendwidth) { pix1 = PXMpix(E1pxm,px,py); // input pixel (blend area) f1 = sa_blendfunc(dist); f2 = 1.0 - f1; pix3[0] = f1 * R + f2 * pix1[0]; // blend monocolor brightness pix3[1] = f1 * G + f2 * pix1[1]; // and original image pix3[2] = f1 * B + f2 * pix1[2]; continue; } } pix3[0] = R; pix3[1] = G; pix3[2] = B; } return; } /********************************************************************************/ // convert an image into a cartoon drawing namespace cartoon { editfunc EFcartoon; int line_threshold = 1000; int line_width = 1; int blur_radius = 1; int kuwahara_depth = 1; int priorKD, priorBR; int ww, hh; float *pixcon; float Fthreshold; float Wrad[21][21]; // radius <= 10 int dialog_event(zdialog* zd, cchar *event); void * thread(void *); void blur(); void * blur_wthread(void *); void kuwahara(); void * kuwahara_wthread(void *); void contrast_map(); void drawlines(); void * drawlines_wthread(void *); } // menu function void m_cartoon(GtkWidget *, cchar *menu) { using namespace cartoon; F1_help_topic = "cartoon"; EFcartoon.menuname = menu; EFcartoon.menufunc = m_cartoon; EFcartoon.funcname = "cartoon"; EFcartoon.Farea = 2; // select area usable EFcartoon.Fscript = 1; // scripting supported EFcartoon.threadfunc = thread; // thread function if (! edit_setup(EFcartoon)) return; // setup edit ww = E1pxm->ww; hh = E1pxm->hh; pixcon = (float *) zmalloc(ww * hh * sizeof(float)); E8pxm = PXM_copy(E1pxm); // blurred image E9pxm = PXM_copy(E1pxm); // kuwahara image /*** _____________________________ | Cartoon | | | | Line Threshold [ 20 ] | | Line Width [ 3 ] | | Blur Radius [ 10 ] | | Kuwahara Depth [ 4 ] | | | | [Apply] [Done] [Cancel] | |_____________________________| ***/ zdialog *zd = zdialog_new("Cartoon",Mwin,Bapply,Bdone,Bcancel,null); EFcartoon.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"vbox","space","hb1",0,"space=3"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"homog"); zdialog_add_widget(zd,"vbox","space","hb1",0,"space=3"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"homog"); zdialog_add_widget(zd,"label","lab1","vb1",E2X("Line Threshold")); zdialog_add_widget(zd,"label","lab1","vb1",E2X("Line Width")); zdialog_add_widget(zd,"label","lab2","vb1",E2X("Blur Radius")); zdialog_add_widget(zd,"label","lab1","vb1",E2X("Kuwahara Depth")); zdialog_add_widget(zd,"zspin","line_threshold","vb2","0|1000|1|1000"); zdialog_add_widget(zd,"zspin","line_width","vb2","0|10|1|1"); zdialog_add_widget(zd,"zspin","blur_radius","vb2","0|10|1|1"); zdialog_add_widget(zd,"zspin","kuwahara_depth","vb2","0|10|1|1"); zdialog_resize(zd,200,0); zdialog_restore_inputs(zd); zdialog_run(zd,dialog_event,"save"); // run dialog, parallel zdialog_fetch(zd,"line_threshold",line_threshold); zdialog_fetch(zd,"line_width",line_width); zdialog_fetch(zd,"blur_radius",blur_radius); zdialog_fetch(zd,"kuwahara_depth",kuwahara_depth); priorKD = priorBR = -1; // initial edit return; } // dialog event and completion callback function int cartoon::dialog_event(zdialog *zd, cchar *event) // dialog event function { using namespace cartoon; if (strmatch(event,"focus")) return 1; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"apply")) zd->zstat = 1; // from script 20.0 zdialog_fetch(zd,"line_threshold",line_threshold); // get outline threshold 0-1000 zdialog_fetch(zd,"line_width",line_width); // get line width 0-10 zdialog_fetch(zd,"blur_radius",blur_radius); // get blur radius 0-10 zdialog_fetch(zd,"kuwahara_depth",kuwahara_depth); // get kuwahara depth 0-10 if (zd->zstat) { if (zd->zstat == 1) { // [apply] 19.0 zd->zstat = 0; // keep dialog active signal_thread(); return 1; } else if (zd->zstat == 2) edit_done(0); // [done] else edit_cancel(0); // discard edit zfree(pixcon); } return 1; } // thread function to update image void * cartoon::thread(void *) { using namespace cartoon; int Fblur, Fkuwa, Fcon; while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 Fblur = Fkuwa = Fcon = 0; if (blur_radius != priorBR) Fblur = Fkuwa = Fcon = 1; if (kuwahara_depth != priorKD) Fkuwa = Fcon = 1; priorBR = blur_radius; // capture now, can change during process priorKD = kuwahara_depth; if (Fblur) blur(); // E1 + blur >> E8 if (Fkuwa) kuwahara(); // E8 + kuwahara >> E9 if (Fcon) contrast_map(); // compute E9 pixel contrast drawlines(); // draw lines where contrast > threshold CEF->Fmods++; CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed, stop g++ warning } // Blur the image using blur_radius. E1 + blur >> E8 void cartoon::blur() { using namespace cartoon; int rad, px, py, qx, qy; float R, W; PXM_free(E8pxm); E8pxm = PXM_copy(E1pxm); if (blur_radius == 0) return; rad = blur_radius; for (qy = -rad; qy <= rad; qy++) // compute blur weights for pixels for (qx = -rad; qx <= rad; qx++) // within rad of target pixel { R = sqrtf(qx * qx + qy * qy); if (R > rad) W = 0.0; else if (R == 0) W = 1.0; else W = 1.0 / R; py = qy + rad; px = qx + rad; Wrad[py][px] = W; } do_wthreads(blur_wthread,NWT); // worker threads return; } void * cartoon::blur_wthread(void *arg) // worker thread function { using namespace cartoon; int index = *((int *) arg); int rad = blur_radius; int ii, dist = 0, px, py, qx, qy; float W, Wsum, f1, f2; float R, G, B, *pix1, *pixq, *pix8; for (py = index + rad; py < hh-rad; py += NWT) // loop all image pixels for (px = rad; px < ww-rad; px++) { if (sa_stat == 3) { // select area active ii = py * ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel is outside area } R = G = B = Wsum = 0; for (qy = -rad; qy <= rad; qy++) // compute weighted sum RGB for pixels for (qx = -rad; qx <= rad; qx++) // within rad of target pixel { pix1 = PXMpix(E1pxm,px,py); pixq = PXMpix(E1pxm,px+qx,py+qy); if (PIXMATCH(pix1,pixq) < 0.7) continue; // use only pixels matching target pixel W = Wrad[qy+rad][qx+rad]; R += W * pixq[0]; G += W * pixq[1]; B += W * pixq[2]; Wsum += W; } R = R / Wsum; // divide by sum of weights G = G / Wsum; B = B / Wsum; if (sa_stat == 3 && dist < sa_blendwidth) { // if select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth f2 = 1.0 - f1; pix1 = PXMpix(E1pxm,px,py); // input pixel R = f1 * R + f2 * pix1[0]; G = f1 * G + f2 * pix1[1]; B = f1 * B + f2 * pix1[2]; } pix8 = PXMpix(E8pxm,px,py); // output pixel pix8[0] = R; pix8[1] = G; pix8[2] = B; } pthread_exit(0); } // Perform a kuwahara sharpen of the image. E8 + kuwahara >> E9. void cartoon::kuwahara() { using namespace cartoon; PXM_free(E9pxm); E9pxm = PXM_copy(E8pxm); if (kuwahara_depth == 0) return; do_wthreads(kuwahara_wthread,NWT); // worker threads return; } void * cartoon::kuwahara_wthread(void *arg) // worker thread function { using namespace cartoon; int index = *((int *) arg); int px, py, qx, qy, rx, ry; int ii, rad, N, dist = 0; float *pix1, *pix8, *pix9; float red, green, blue, red2, green2, blue2; float vmin, vall, vred, vgreen, vblue; float red9, green9, blue9; float f1, f2; rad = kuwahara_depth; // user input radius N = (rad + 1) * (rad + 1); for (py = index + rad; py < hh-rad; py += NWT) // loop all image pixels for (px = rad; px < ww-rad; px++) { if (sa_stat == 3) { // select area active ii = py * ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel is outside area } vmin = 99999; red9 = green9 = blue9 = 0; for (qy = py - rad; qy <= py; qy++) // loop all surrounding neighborhoods for (qx = px - rad; qx <= px; qx++) { red = green = blue = 0; red2 = green2 = blue2 = 0; for (ry = qy; ry <= qy + rad; ry++) // loop all pixels in neighborhood for (rx = qx; rx <= qx + rad; rx++) { pix8 = PXMpix(E8pxm,rx,ry); red += pix8[0]; // compute mean RGB and mean RGB**2 red2 += pix8[0] * pix8[0]; green += pix8[1]; green2 += pix8[1] * pix8[1]; blue += pix8[2]; blue2 += pix8[2] * pix8[2]; } red = red / N; // mean RGB of neighborhood green = green / N; blue = blue / N; vred = red2 / N - red * red; // variance RGB vgreen = green2 / N - green * green; vblue = blue2 / N - blue * blue; vall = vred + vgreen + vblue; // save RGB values with least variance if (vall < vmin) { vmin = vall; red9 = red; green9 = green; blue9 = blue; } } if (sa_stat == 3 && dist < sa_blendwidth) { // if select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth f2 = 1.0 - f1; pix1 = PXMpix(E1pxm,px,py); // input pixel red9 = f1 * red9 + f2 * pix1[0]; green9 = f1 * green9 + f2 * pix1[1]; blue9 = f1 * blue9 + f2 * pix1[2]; } pix9 = PXMpix(E9pxm,px,py); // output pixel pix9[0] = red9; pix9[1] = green9; pix9[2] = blue9; } pthread_exit(0); } // Map contrast for E9 image pixels and set threshold scale. void cartoon::contrast_map() { using namespace cartoon; int ii, px, py, qx, qy; double sumcon, pix1con, maxcon; float *pixx, *pixq; maxcon = 0; for (py = 1; py < hh-1; py++) // make image pixel contrast map for (px = 1; px < ww-1; px++) { sumcon = 0; for (qy = py-1; qy <= py+1; qy++) for (qx = px-1; qx <= px+1; qx++) { pixx = PXMpix(E9pxm,px,py); // pixel contrast is mean contrast pixq = PXMpix(E9pxm,qx,qy); // with 8 neighboring pixels sumcon += fabsf(pixx[0] - pixq[0]); sumcon += fabsf(pixx[1] - pixq[1]); sumcon += fabsf(pixx[2] - pixq[2]); } ii = py * ww + px; pixcon[ii] = pix1con = 0.0417 * sumcon; // / (8 * 3) if (pix1con > maxcon) maxcon = pix1con; } Fthreshold = maxcon; // threshold scale factor return; } // Draw lines on the image where edges are detected. // E9 >> E3, add lines to E3, use E1 for area blend. void cartoon::drawlines() { PXM_free(E3pxm); E3pxm = PXM_copy(E9pxm); do_wthreads(drawlines_wthread,NWT); // worker threads } void * cartoon::drawlines_wthread(void *arg) // worker thread function { using namespace cartoon; int index = *((int *) arg); int px, py, qx, qy, ii, dist = 0; float Fthresh, paint, f1, f2; float *pix1, *pix3, *pix3q; Fthresh = 0.001 * line_threshold * Fthreshold; // 0 .. 1000 >> 0 .. Fthreshold for (py = index+1; py < hh-1; py += NWT) for (px = 1; px < ww-1; px++) { ii = py * ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } if (pixcon[ii] < Fthresh) continue; pix1 = PXMpix(E1pxm,px,py); // input image pixel (for area blend) pix3 = PXMpix(E3pxm,px,py); // output image pixel paint = line_width * (pixcon[ii] - Fthresh) / Fthresh; if (paint <= 1) { f1 = paint; f2 = 1.0 - f1; pix3[0] = f2 * pix3[0]; pix3[1] = f2 * pix3[1]; pix3[2] = f2 * pix3[2]; } else { f1 = (paint - 1.0) / 8.0; if (f1 > 1.0) f1 = 1.0; f2 = 1.0 - f1; for (qy = py-1; qy <= py+1; qy++) for (qx = px-1; qx <= px+1; qx++) { if (qx == px && qy == py) pix3[0] = pix3[1] = pix3[2] = 0; else { pix3q = PXMpix(E3pxm,qx,qy); pix3q[0] = f2 * pix3q[0]; pix3q[1] = f2 * pix3q[1]; pix3q[2] = f2 * pix3q[2]; } } } if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth f2 = 1.0 - f1; pix3[0] = f1 * pix3[0] + f2 * pix1[0]; pix3[1] = f1 * pix3[1] + f2 * pix1[1]; pix3[2] = f1 * pix3[2] + f2 * pix1[2]; continue; } } pthread_exit(0); } /********************************************************************************/ // make an image outline drawing from edge pixels // superimpose drawing and image and vary brightness of each namespace linedraw_names { editfunc EFlinedraw; float outline_thresh; // contrast threshold to make a line float outline_width; // drawn line width float image_briteness; // background image brightness int blackwhite, negative; // flags } void m_line_drawing(GtkWidget *, cchar *menu) { using namespace linedraw_names; int linedraw_dialog_event(zdialog* zd, cchar *event); void * linedraw_thread(void *); F1_help_topic = "line drawing"; EFlinedraw.menuname = menu; EFlinedraw.menufunc = m_line_drawing; EFlinedraw.funcname = "linedraw"; EFlinedraw.Farea = 2; // select area usable EFlinedraw.Fscript = 1; // scripting supported EFlinedraw.threadfunc = linedraw_thread; // thread function if (! edit_setup(EFlinedraw)) return; // setup edit /*** ___________________________________ | Line Drawing | | | | Threshold =======[]============== | | Width =================[]======== | | Brightness =========[]=========== | | [_] black/white [_] negative | | | | [Done] [Cancel] | |___________________________________| ***/ zdialog *zd = zdialog_new(E2X("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",E2X("black/white"),"space=5"); zdialog_add_widget(zd,"label","space","hb4",0,"space=10"); zdialog_add_widget(zd,"check","negative","hb4",Bnegative); outline_thresh = 90; outline_width = 50; image_briteness = 10; blackwhite = negative = 0; zdialog_stuff(zd,"blackwhite",0); zdialog_stuff(zd,"negative",0); zdialog_resize(zd,300,0); zdialog_run(zd,linedraw_dialog_event,"save"); // run dialog, parallel signal_thread(); return; } // dialog event and completion callback function int linedraw_dialog_event(zdialog *zd, cchar *event) // dialog event function { using namespace linedraw_names; if (strmatch(event,"focus")) return 1; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit PXM_free(E8pxm); PXM_free(E9pxm); return 1; } zdialog_fetch(zd,"olth",outline_thresh); // get outline threshold 0-100 zdialog_fetch(zd,"olww",outline_width); // get outline width 0-100 zdialog_fetch(zd,"imbr",image_briteness); // get image brightness 0-100 zdialog_fetch(zd,"blackwhite",blackwhite); zdialog_fetch(zd,"negative",negative); signal_thread(); return 1; } // thread function to update image void * linedraw_thread(void *) { using namespace linedraw_names; void * linedraw_wthread(void *arg); int px, py, ww, hh; float olww, red3, green3, blue3; float red3h, red3v, green3h, green3v, blue3h, blue3v; float *pix8, *pix9; float *pixa, *pixb, *pixc, *pixd, *pixe, *pixf, *pixg, *pixh; int nc = E1pxm->nc; while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 olww = 0.01 * outline_width; // outline width, 0 - 1.0 olww = 1.0 - olww; // 1.0 - 0 olww = 0.8 * olww + 0.2; // 1.0 - 0.2 ww = E3pxm->ww * olww + 0.5; // create smaller outline image hh = E3pxm->hh * olww + 0.5; if (! E9pxm || ww != E9pxm->ww) // initial or changed outline brightness { PXM_free(E8pxm); PXM_free(E9pxm); E8pxm = PXM_rescale(E1pxm,ww,hh); E9pxm = PXM_copy(E8pxm); for (py = 1; py < hh-1; py++) for (px = 1; px < ww-1; px++) { pix8 = PXMpix(E8pxm,px,py); // input pixel pix9 = PXMpix(E9pxm,px,py); // output pixel pixa = pix8-nc*ww-nc; // 8 neighboring pixels are used pixb = pix8-nc*ww; // to get edge brightness using pixc = pix8-nc*ww+nc; // a Sobel filter pixd = pix8-nc; pixe = pix8+nc; pixf = pix8+nc*ww-nc; pixg = pix8+nc*ww; pixh = pix8+nc*ww+nc; red3h = -pixa[0] - 2 * pixb[0] - pixc[0] + pixf[0] + 2 * pixg[0] + pixh[0]; red3v = -pixa[0] - 2 * pixd[0] - pixf[0] + pixc[0] + 2 * pixe[0] + pixh[0]; green3h = -pixa[1] - 2 * pixb[1] - pixc[1] + pixf[1] + 2 * pixg[1] + pixh[1]; green3v = -pixa[1] - 2 * pixd[1] - pixf[1] + pixc[1] + 2 * pixe[1] + pixh[1]; blue3h = -pixa[2] - 2 * pixb[2] - pixc[2] + pixf[2] + 2 * pixg[2] + pixh[2]; blue3v = -pixa[2] - 2 * pixd[2] - pixf[2] + pixc[2] + 2 * pixe[2] + pixh[2]; red3 = (fabsf(red3h) + fabsf(red3v)) / 2; // average vert. and horz. brightness green3 = (fabsf(green3h) + fabsf(green3v)) / 2; blue3 = (fabsf(blue3h) + fabsf(blue3v)) / 2; if (red3 > 255.9) red3 = 255.9; if (green3 > 255.9) green3 = 255.9; if (blue3 > 255.9) blue3 = 255.9; pix9[0] = red3; pix9[1] = green3; pix9[2] = blue3; } } PXM_free(E8pxm); // scale back to full-size E8pxm = PXM_rescale(E9pxm,E3pxm->ww,E3pxm->hh); do_wthreads(linedraw_wthread,NWT); // worker threads CEF->Fmods++; CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.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_blendwidth) { // select area is active, dnew = sa_blendfunc(dist); // blend changes over sa_blendwidth 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; } pthread_exit(0); } /********************************************************************************/ // convert image to simulate an embossing int emboss_radius, emboss_color; float emboss_depth; float emboss_kernel[20][20]; // support radius <= 9 editfunc EFemboss; // menu function void m_emboss(GtkWidget *, cchar *menu) { int emboss_dialog_event(zdialog* zd, cchar *event); void * emboss_thread(void *); F1_help_topic = "emboss"; EFemboss.menuname = menu; EFemboss.menufunc = m_emboss; EFemboss.funcname = "emboss"; EFemboss.Fscript = 1; // scripting supported EFemboss.Farea = 2; // select area usable EFemboss.threadfunc = emboss_thread; // thread function if (! edit_setup(EFemboss)) return; // setup edit /*** ____________________________________ | Emboss | | | | Radius [__] Depth [__] [x] Color | | | | [apply] [done] [cancel] | |____________________________________| ***/ zdialog *zd = zdialog_new(E2X("Emboss"),Mwin,Bapply,Bdone,Bcancel,null); EFemboss.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lab1","hb1",Bradius,"space=5"); zdialog_add_widget(zd,"zspin","radius","hb1","0|9|1|0"); zdialog_add_widget(zd,"label","lab2","hb1",E2X("Depth"),"space=5"); zdialog_add_widget(zd,"zspin","depth","hb1","0|99|1|0"); zdialog_add_widget(zd,"check","color","hb1",Bcolor,"space=8"); zdialog_run(zd,emboss_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int emboss_dialog_event(zdialog *zd, cchar *event) // emboss dialog event function { if (strmatch(event,"focus")) return 1; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"apply")) zd->zstat = 1; // from script 20.0 if (zd->zstat) { if (zd->zstat == 1) { // [apply] zd->zstat = 0; // keep dialog active 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 } else if (zd->zstat == 2) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } return 1; } // thread function - use multiple working threads void * emboss_thread(void *) { void * emboss_wthread(void *arg); int dx, dy, rad; float depth, kern, coeff; while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 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 do_wthreads(emboss_wthread,NWT); // worker threads CEF->Fmods++; CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.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); pthread_exit(0); } void emboss_1pix(int px, int py) // process one pixel { int ii, dist = 0; float bright1, bright3; int rgb, dx, dy, rad; float *pix1, *pix3, *pixN; float sumpix, kern, dold, dnew; int nc = E1pxm->nc; if (sa_stat == 3) { // select area active ii = py * E3pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) return; // outside pixel } rad = emboss_radius; if (px < rad || py < rad) return; if (px > E3pxm->ww-rad-1 || py > E3pxm->hh-rad-1) return; pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel if (emboss_color) { for (rgb = 0; rgb < 3; rgb++) { sumpix = 0; for (dy = -rad; dy <= rad; dy++) // loop surrounding block of pixels for (dx = -rad; dx <= rad; dx++) { pixN = pix1 + (dy * E1pxm->ww + dx) * nc; kern = emboss_kernel[dx+rad][dy+rad]; sumpix += kern * pixN[rgb]; bright1 = pix1[rgb]; bright3 = sumpix; if (bright3 < 0) bright3 = 0; if (bright3 > 255) bright3 = 255; if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, dnew = sa_blendfunc(dist); // blend changes over sa_blendwidth dold = 1.0 - dnew; bright3 = dnew * bright3 + dold * bright1; } pix3[rgb] = bright3; } } } else // use gray scale { sumpix = 0; for (dy = -rad; dy <= rad; dy++) // loop surrounding block of pixels for (dx = -rad; dx <= rad; dx++) { pixN = pix1 + (dy * E1pxm->ww + dx) * nc; kern = emboss_kernel[dx+rad][dy+rad]; sumpix += kern * (pixN[0] + pixN[1] + pixN[2]); } bright1 = 0.3333 * (pix1[0] + pix1[1] + pix1[2]); bright3 = 0.3333 * sumpix; if (bright3 < 0) bright3 = 0; if (bright3 > 255) bright3 = 255; if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, dnew = sa_blendfunc(dist); // blend changes over sa_blendwidth dold = 1.0 - dnew; bright3 = dnew * bright3 + dold * bright1; } pix3[0] = pix3[1] = pix3[2] = bright3; } return; } /********************************************************************************/ // convert image to simulate square tiles int tile_size, tile_gap, tile_3D; float *tile_pixmap = 0; editfunc EFtiles; void m_tiles(GtkWidget *, cchar *menu) { int tiles_dialog_event(zdialog *zd, cchar *event); void * tiles_thread(void *); F1_help_topic = "tiles"; EFtiles.menuname = menu; EFtiles.menufunc = m_tiles; EFtiles.funcname = "tiles"; EFtiles.Farea = 2; // select area usable EFtiles.Fscript = 1; // scripting supported EFtiles.threadfunc = tiles_thread; // thread function if (! edit_setup(EFtiles)) return; // setup edit /*** _____________________________ | Simulate Tiles | | | | tile size [____] | | tile gap [____] | | 3D depth [____] | | | | [Apply] [Done] [Cancel] | |_____________________________| ***/ zdialog *zd = zdialog_new(E2X("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",E2X("tile size"),"space=5"); zdialog_add_widget(zd,"zspin","size","hb1","1|99|1|10","space=5"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labg","hb2",E2X("tile gap"),"space=5"); zdialog_add_widget(zd,"zspin","gap","hb2","0|9|1|1","space=5"); zdialog_add_widget(zd,"hbox","hb3","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labd","hb3",E2X("3D depth"),"space=5"); zdialog_add_widget(zd,"zspin","3D","hb3","0|9|1|1","space=5"); tile_size = 10; // defaults tile_gap = 1; tile_3D = 0; int cc = E1pxm->ww * E1pxm->hh * 3 * sizeof(float); tile_pixmap = (float *) zmalloc(cc); // set up pixel color map memset(tile_pixmap,0,cc); zdialog_run(zd,tiles_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int tiles_dialog_event(zdialog * zd, cchar *event) { if (strmatch(event,"focus")) return 1; if (strmatch(event,"apply")) zd->zstat = 1; // from script if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) // dialog complete { if (zd->zstat == 1) { // apply zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"size",tile_size); // get tile size zdialog_fetch(zd,"gap",tile_gap); // get tile gap zdialog_fetch(zd,"3D",tile_3D); // get 3D yes/no if (tile_size < 2) edit_reset(); // restore original image else signal_thread(); // trigger working thread return 1; } else if (zd->zstat == 2) edit_done(0); // commit edit else edit_cancel(0); // discard edit zfree(tile_pixmap); } return 1; } // image tiles thread function void * tiles_thread(void *) { int sg, gg, eg, sumpix; int ii, jj, px, py, qx, qy, dist; int td, bd, ld, rd; float red = 0, green = 0, blue = 0; float hired = 0, higreen = 0, hiblue = 0; float lored = 0, logreen = 0, loblue = 0; float dnew, dold; float *pix1, *pix3; while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 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_blendwidth) { // blend changes over sa_blendwidth pix3[0] = tile_pixmap[jj]; pix3[1] = tile_pixmap[jj+1]; // apply block color to member pixels pix3[2] = tile_pixmap[jj+2]; } else { dnew = sa_blendfunc(dist); dold = 1.0 - dnew; pix1 = PXMpix(E1pxm,px,py); pix3[0] = dnew * tile_pixmap[jj] + dold * pix1[0]; pix3[1] = dnew * tile_pixmap[jj+1] + dold * pix1[1]; pix3[2] = dnew * tile_pixmap[jj+2] + dold * pix1[2]; } } } else // process entire image { for (py = 0; py < E3pxm->hh-1; py++) // loop all image pixels for (px = 0; px < E3pxm->ww-1; px++) { pix3 = PXMpix(E3pxm,px,py); // target pixel jj = (py * E3pxm->ww + px) * 3; // color map for (px,py) pix3[0] = tile_pixmap[jj]; pix3[1] = tile_pixmap[jj+1]; pix3[2] = tile_pixmap[jj+2]; } } CEF->Fmods++; CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed, stop g++ warning } /********************************************************************************/ // Dither menu, incorporating dither0, dither1, dither2, dither3 void m_dither(GtkWidget *, cchar *menu) { int dither_dialog_event(zdialog *zd, cchar *event); // 19.0 zdialog *zd; F1_help_topic = "dither"; if (FGWM != 'F') return; if (! curr_file) return; /*** ____________________________________________ | Dither Image | | | | [Dither0] Roy Lichtenstein effect | | [Dither1] pure RGB or black/white dots | | [Dither2] RGB mix with given bit-depth | | [Dither3] custom palette colors | | | | [done] | |____________________________________________| ***/ zd = zdialog_new(E2X("Dither Image"),Mwin,Bdone,null); zdialog_add_widget(zd,"hbox","hb0","dialog"); zdialog_add_widget(zd,"button","dither0","hb0",E2X("Dither0"),"space=3"); zdialog_add_widget(zd,"label","lab0","hb0",E2X("Roy Lichtenstein effect"),"space=3"); zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"button","dither1","hb1",E2X("Dither1"),"space=3"); zdialog_add_widget(zd,"label","lab1","hb1",E2X("pure RGB or black/white dots"),"space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog"); zdialog_add_widget(zd,"button","dither2","hb2",E2X("Dither2"),"space=3"); zdialog_add_widget(zd,"label","lab2","hb2",E2X("RGB mix with given bit-depth"),"space=3"); zdialog_add_widget(zd,"hbox","hb3","dialog"); zdialog_add_widget(zd,"button","dither3","hb3",E2X("Dither3"),"space=3"); zdialog_add_widget(zd,"label","lab3","hb3",E2X("custom palette colors"),"space=3"); zdialog_run(zd,dither_dialog_event,"mouse"); return; } // dialog event and completion function int dither_dialog_event(zdialog *zd, cchar *event) { if (zd->zstat) { // done or [x] zdialog_free(zd); // kill dialog return 1; } if (! zstrstr(event,"dither")) return 1; // ignore other events if (strmatch(event,"dither0")) m_dither0(0,0); if (strmatch(event,"dither1")) m_dither1(0,0); if (strmatch(event,"dither2")) m_dither2(0,0); if (strmatch(event,"dither3")) m_dither3(0,0); zdialog_free(zd); return 1; } /********************************************************************************/ // convert image to dot grid (like Roy Lichtenstein) int dot_size; // tile size enclosing dots editfunc EFdither0; void m_dither0(GtkWidget *, cchar *menu) { int dither0_dialog_event(zdialog *zd, cchar *event); void * dither0_thread(void *); F1_help_topic = "dither"; EFdither0.menuname = "dither0"; EFdither0.menufunc = m_dither0; EFdither0.funcname = "dither0"; EFdither0.Farea = 2; // select area usable EFdither0.Fscript = 1; // scripting supported 20.0 EFdither0.threadfunc = dither0_thread; // thread function if (! edit_setup(EFdither0)) return; // setup edit zdialog *zd = zdialog_new(E2X("Roy Lichtenstein effect"),Mwin,Bapply,Bdone,Bcancel,null); EFdither0.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labt","hb1",E2X("dot size"),"space=5"); zdialog_add_widget(zd,"zspin","size","hb1","4|99|1|9","space=5"); dot_size = 9; zdialog_run(zd,dither0_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int dither0_dialog_event(zdialog * zd, cchar *event) { if (strmatch(event,"focus")) return 1; if (strmatch(event,"apply")) zd->zstat = 1; // from script if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { // apply zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"size",dot_size); // get dot size signal_thread(); // trigger working thread } else if (zd->zstat == 2) edit_done(0); // commit edit else edit_cancel(0); // discard edit } return 1; } // image dither0 thread function void * dither0_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, qrad2; int nc = E3pxm->nc; while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paints 19.0 ds = dot_size; // dot size and tile size for (py = 0; py < E3pxm->hh; py++) // set image to black for (px = 0; px < E3pxm->ww; px++) { // not alpha channel pix3 = PXMpix(E3pxm,px,py); pix3[0] = pix3[1] = pix3[2] = 0; } px1 = 0; // limits for tiles px2 = E3pxm->ww - ds; py1 = 0; py2 = E3pxm->hh - ds; if (sa_stat == 3) { // reduce for active area px1 = sa_minx; px2 = sa_maxx; py1 = sa_miny; py2 = sa_maxy; if (px2 > E3pxm->ww - ds) px2 = E3pxm->ww - ds; if (py2 > E3pxm->hh - ds) py2 = E3pxm->hh - ds; } shift1 = 0; for (py = py1; py < py2; py += ds) // loop all tiles in input image { shift1 = 1 - shift1; // offset alternate rows shift2 = 0.5 * shift1 * ds; for (px = px1 + shift2; px < px2; px += ds) { sumpix = red = green = blue = 0; for (qy = py; qy < py + ds; qy++) // loop all pixels in tile for (qx = px; qx < px + ds; qx++) // get mean RGB levels for tile { pix1 = PXMpix(E1pxm,qx,qy); red += pix1[0]; green += pix1[1]; blue += pix1[2]; sumpix++; } red = red / sumpix; // mean RGB levels, 0 to 255.9 green = green / sumpix; blue = blue / sumpix; maxrgb = red; // max. mean RGB level, 0 to 255.9 if (green > maxrgb) maxrgb = green; if (blue > maxrgb) maxrgb = blue; relmax = maxrgb / 256.0; // max. RGB as 0 to 0.999 relmax = pow(relmax,1.6); // improve contrast 19.0 radius = ds * sqrt(fpi * relmax); // radius of dot with maximized color radius = radius * 0.9; // deliberate reduction if (radius < 0.5) continue; // tiny - leave tile black 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; qrad2 = (qcx-dcx)*(qcx-dcx) + (qcy-dcy)*(qcy-dcy); // pixel distance**2 from center of dot if (qrad2 > radius2a) f1 = 0.0; // pixel outside dot, no color else if (qrad2 < radius2b) f1 = 1.0; // pixel inside dot, full color else f1 = radius + 0.5 - sqrtf(qrad2); // 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_blendwidth) { f1 = sa_blendfunc(dist); // pixel in blend region f2 = 1.0 - f1; pix3[0] = f1 * pix3[0] + f2 * pix1[0]; // output pixel is mix of input and output pix3[1] = f1 * pix3[1] + f2 * pix1[1]; pix3[2] = f1 * pix3[2] + f2 * pix1[2]; } pix1 += nc; pix3 += nc; } } CEF->Fmods++; CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed, stop g++ warning } /********************************************************************************/ // Convert image into pure RGB or black/white dots, dithered to show other tones namespace dither1_names { editfunc EFdither1; int E3ww, E3hh; int RES, RGB, BW; int RAND; } // menu function void m_dither1(GtkWidget *, const char *menu) // 19.0 { using namespace dither1_names; int dither1_dialog_event(zdialog* zd, const char *event); void * dither1_thread(void *); F1_help_topic = "dither"; EFdither1.menuname = "dither1"; EFdither1.menufunc = m_dither1; EFdither1.funcname = "dither1"; // function name EFdither1.threadfunc = dither1_thread; // thread function EFdither1.Farea = 2; // select area usable EFdither1.Frestart = 1; // allow restart EFdither1.FusePL = 1; // can use with paint/lever edits EFdither1.Fscript = 1; // scripting supported 20.0 if (! edit_setup(EFdither1)) return; // setup edit E3ww = E3pxm->ww; E3hh = E3pxm->hh; /*** __________________________ | Dither1 | | | | resolution [ N ] | 2x2 to 30x30 // see Pflag dependency below | (o) RGB color | | (o) black/white | | [x] random position | | | | [apply] [done] [cancel] | |__________________________| ***/ zdialog *zd = zdialog_new(E2X("Dither1"),Mwin,Bapply,Bdone,Bcancel,null); // dither1 dialog CEF->zd = zd; zdialog_add_widget(zd,"hbox","hbres","dialog"); zdialog_add_widget(zd,"label","labres","hbres",E2X("resolution"),"space=5"); zdialog_add_widget(zd,"zspin","RES","hbres","2|30|1|4","space=5"); zdialog_add_widget(zd,"radio","RGB","dialog",E2X("RGB color")); zdialog_add_widget(zd,"radio","B&W","dialog",E2X("black/white")); zdialog_add_widget(zd,"check","RAND","dialog",E2X("random position")); zdialog_restore_inputs(zd); // restore previous inputs zdialog_run(zd,dither1_dialog_event,"save"); // run dialog - parallel return; } // dither1 dialog event and completion function int dither1_dialog_event(zdialog *zd, const char *event) { using namespace dither1_names; if (strmatch(event,"apply")) zd->zstat = 1; // from script 20.0 if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() zdialog_fetch(zd,"RES",RES); zdialog_fetch(zd,"RGB",RGB); zdialog_fetch(zd,"B&W",BW); zdialog_fetch(zd,"RAND",RAND); if (zd->zstat) { if (zd->zstat == 1) { // apply zd->zstat = 0; signal_thread(); } else if (zd->zstat == 2) edit_done(0); // done else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"blendwidth")) signal_thread(); return 1; } // thread function - multiple working threads to update image void * dither1_thread(void *) { using namespace dither1_names; void * dither1_wthread_RGB(void *arg); void * dither1_wthread_BW(void *arg); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint if (sa_stat == 3) Fbusy_goal = sa_Npixel; // set up progress monitor else Fbusy_goal = E3pxm->ww * E3pxm->hh; Fbusy_done = 0; if (RGB) do_wthreads(dither1_wthread_RGB,NWT); // do RGB worker threads if (BW) do_wthreads(dither1_wthread_BW,NWT); // do B/W worker threads Fbusy_goal = Fbusy_done = 0; CEF->Fmods++; // image modified CEF->Fsaved = 0; // not saved paintlock(0); // unblock window paint Fpaint2(); // update window } return 0; // not executed, stop warning } // worker thread function for RGB dither1 void * dither1_wthread_RGB(void *arg) { using namespace dither1_names; int index = *((int *) arg); int px, py, qx, qy; int ii, jj, kk, dist = 1; int pixcc = 3 * sizeof(float); int RES2 = RES * RES; char Pflag[1000]; // max. RES = 31 float *pix1, *pix3; float Rs, Gs, Bs, Rp, Gp, Bp; float maxrgb; float f1, f2; pix3 = 0; // stop compiler 'uninitialized' kk = 0; // warnings for (py = index * RES; py < E3hh - RES; py += NWT * RES) // loop each pixel block for (px = 0; px < E3ww - RES; px += RES) { if (sa_stat == 3) { // select area active ii = py * E3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } for (qy = py; qy < py + RES; qy++) // set all output pixels in block for (qx = px; qx < px + RES; qx++) // to RGB values 0/0/0 { pix3 = PXMpix(E3pxm,qx,qy); memset(pix3,0,pixcc); } Rs = Gs = Bs = 0; for (qy = py; qy < py + RES; qy++) // get RGB sums for pixel block for (qx = px; qx < px + RES; qx++) { pix1 = PXMpix(E1pxm,qx,qy); Rs += pix1[0]; Gs += pix1[1]; Bs += pix1[2]; } Rp = Rs / 256 / 3; // count of dithered pixels for block Gp = Gs / 256 / 3; // (single RGB value of 255) Bp = Bs / 256 / 3; memset(Pflag,0,RES2); // reset pixel flags while (true) // distribute dithered pixels { if (Rp < 0.5 && Gp < 0.5 && Bp < 0.5) break; // nothing left to distribute ii = 0; // get greatest remaining if (Gp >= Rp && Gp >= Bp) ii = 1; // dithered pixel count if (Bp >= Rp && Bp >= Gp) ii = 2; if (ii == 0) Rp -= 1; // decrement dithered pixel count if (ii == 1) Gp -= 1; if (ii == 2) Bp -= 1; if (RAND) // use random position in block { kk = RES2 * drandz(); // random initial search posn. for (jj = 0; jj < RES2; jj++) // loop pixels in block { kk++; // next pixel if (kk == RES2) kk = 0; if (! Pflag[kk]) break; // available } if (jj < RES2) // found { Pflag[kk] = 1; // mark pixel as used qy = kk / RES; // pixel within block qx = kk - RES * qy; pix3 = PXMpix(E3pxm, px + qx, py + qy); // make into dithered pixel pix3[ii] = 255; } } else // use best match position in block { maxrgb = 0; for (jj = 0; jj < RES2; jj++) // loop pixels in block { if (Pflag[jj]) continue; // pixel used already qy = jj / RES; // pixel within block qx = jj - qy * RES; pix1 = PXMpix(E1pxm, px + qx, py + qy); // find pixel with greatest if (pix1[ii] > maxrgb) { // matching RGB value maxrgb = pix1[ii]; kk = jj; } } if (maxrgb > 0) // found pixel { Pflag[kk] = 1; // mark pixel as used qy = kk / RES; // matching pixel within block qx = kk - RES * qy; pix3 = PXMpix(E3pxm, px + qx, py + qy); // make into dithered pixel pix3[ii] = 255; } } } if (sa_stat == 3 && dist < sa_blendwidth) { f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; for (qy = py; qy < py + RES; qy++) for (qx = px; qx < px + RES; qx++) { pix1 = PXMpix(E1pxm,qx,qy); pix3 = PXMpix(E3pxm,qx,qy); 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 += RES2; } pthread_exit(0); // exit thread } // worker thread function for black/white dither void * dither1_wthread_BW(void *arg) { using namespace dither1_names; int index = *((int *) arg); int px, py, qx, qy; int ii, jj, kk, dist = 1; int pixcc = 3 * sizeof(float); int RES2 = RES * RES; char Pflag[1000]; // max. RES = 31 float *pix1, *pix3; float white[3] = { 255, 255, 255 }; float Nw, Nb, minrgb, pixrgb; float f1, f2; pix3 = 0; // stop compiler 'uninitialized' kk = 0; // warnings for (py = index * RES; py < E3hh - RES; py += NWT * RES) // loop each pixel block for (px = 0; px < E3ww - RES; px += RES) { if (sa_stat == 3) { // select area active ii = py * E3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } for (qy = py; qy < py + RES; qy++) // set all output pixels in block for (qx = px; qx < px + RES; qx++) // to RGB white { pix3 = PXMpix(E3pxm,qx,qy); memcpy(pix3,white,pixcc); } Nw = 0; for (qy = py; qy < py + RES; qy++) // get RGB sum for pixel block for (qx = px; qx < px + RES; qx++) { pix1 = PXMpix(E1pxm,qx,qy); Nw += pix1[0] + pix1[1] + pix1[2]; } Nw = Nw / 255 / 3; // white pixel count Nb = RES2 - Nw; // black pixel count memset(Pflag,0,RES2); // reset pixel flags while (true) // distribute black pixels { if (Nb < 0.5) break; // nothing left to distribute Nb -= 1; minrgb = 9999; if (RAND) // use random position in block { kk = RES2 * drandz(); // random initial search posn. for (jj = 0; jj < RES2; jj++) // loop pixels in block { kk++; // next pixel if (kk == RES2) kk = 0; if (! Pflag[kk]) break; // available } if (jj < RES2) // found { Pflag[kk] = 1; // mark pixel as used qy = kk / RES; // pixel within block qx = kk - RES * qy; pix3 = PXMpix(E3pxm, px + qx, py + qy); pix3[0] = pix3[1] = pix3[2] = 0; // make pixel black } } else // use best match position in block { for (jj = 0; jj < RES2; jj++) // loop pixels in block { if (Pflag[jj]) continue; // pixel used already qy = jj / RES; // pixel within block qx = jj - qy * RES; pix1 = PXMpix(E1pxm, px + qx, py + qy); pixrgb = pix1[0] + pix1[1] + pix1[2]; // get pixel RGB sum if (pixrgb < minrgb) { minrgb = pixrgb; // remember pixel with lowest sum kk = jj; } } if (minrgb < 9999) // found pixel { Pflag[kk] = 1; // mark pixel as used qy = kk / RES; // matching pixel within block qx = kk - RES * qy; pix3 = PXMpix(E3pxm, px + qx, py + qy); pix3[0] = pix3[1] = pix3[2] = 0; // make pixel black } } } if (sa_stat == 3 && dist < sa_blendwidth) { f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; for (qy = py; qy < py + RES; qy++) for (qx = px; qx < px + RES; qx++) { pix1 = PXMpix(E1pxm,qx,qy); pix3 = PXMpix(E3pxm,qx,qy); 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 += RES2; } pthread_exit(0); // exit thread } /********************************************************************************/ // Dither image using RGB colors and Floyd-Steinberg error distribution algorithm. namespace dither2_names { editfunc EFdither2; int E3ww, E3hh; int RES, RES2, DEPTH, COMP; } // menu function void m_dither2(GtkWidget *, const char *menu) // 19.0 { using namespace dither2_names; int dither2_dialog_event(zdialog* zd, const char *event); void * dither2_thread(void *); F1_help_topic = "dither"; EFdither2.menuname = "dither2"; EFdither2.menufunc = m_dither2; EFdither2.funcname = "dither2"; // function name EFdither2.threadfunc = dither2_thread; // thread function EFdither2.Farea = 2; // select area usable EFdither2.Frestart = 1; // allow restart EFdither2.FusePL = 1; // can use with paint/lever edits EFdither2.Fscript = 1; // scripting supported 20.0 if (! edit_setup(EFdither2)) return; // setup edit E3ww = E3pxm->ww; E3hh = E3pxm->hh; /*** __________________________ | Dither2 | | | | resolution [ N ] | 2x2 to 30x30 | color depth [ N ] | 1 to 8 bits | [x] error compensation | | | | [apply] [done] [cancel] | |__________________________| ***/ zdialog *zd = zdialog_new(E2X("Dither2"),Mwin,Bapply,Bdone,Bcancel,null); // dither dialog CEF->zd = zd; zdialog_add_widget(zd,"hbox","hbres","dialog"); zdialog_add_widget(zd,"label","labres","hbres",E2X("resolution"),"space=5"); zdialog_add_widget(zd,"zspin","RES","hbres","2|30|1|4","space=5|size=2"); zdialog_add_widget(zd,"hbox","hbdep","dialog"); zdialog_add_widget(zd,"label","labdep","hbdep",E2X("color depth"),"space=5"); zdialog_add_widget(zd,"zspin","DEPTH","hbdep","1|8|1|2","space=5|size=2"); zdialog_add_widget(zd,"hbox","hbcomp","dialog"); zdialog_add_widget(zd,"check","COMP","hbcomp",E2X("error compensation"),"space=5"); zdialog_restore_inputs(zd); // restore previous inputs zdialog_run(zd,dither2_dialog_event,"save"); // run dialog - parallel return; } // dither2 dialog event and completion function int dither2_dialog_event(zdialog *zd, const char *event) { using namespace dither2_names; if (strmatch(event,"apply")) zd->zstat = 1; // from script 20.0 if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() zdialog_fetch(zd,"RES",RES); zdialog_fetch(zd,"DEPTH",DEPTH); zdialog_fetch(zd,"COMP",COMP); if (zd->zstat) { if (zd->zstat == 1) { // apply zd->zstat = 0; signal_thread(); } else if (zd->zstat == 2) edit_done(0); // done else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"blendwidth")) signal_thread(); return 1; } // thread function - multiple working threads to update image void * dither2_thread(void *) { using namespace dither2_names; int bits[9] = { 1, 2, 4, 8, 16, 32, 64, 128, 256 }; int Brows, Bcols, Brow, Bcol, block, Nblocks; int pxlo, pxhi, pylo, pyhi, px, py; float *blockR, *blockG, *blockB; float R, G, B, Rp, Gp, Bp, Re, Ge, Be; int n1, n2, n3, n4; int bb, ii, dist = 0; float *pix1, *pix3; float f1, f2; while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint bb = bits[9-DEPTH]; // DEPTH: 1 .. 8 bb: 256 128 64 .. 2 RES2 = RES * RES; // pixel block size Bcols = E3ww / RES; // block rows and cols Brows = E3hh / RES; Nblocks = Brows * Bcols; // allocate block color memory blockR = (float *) zmalloc(Nblocks * sizeof(float)); blockG = (float *) zmalloc(Nblocks * sizeof(float)); blockB = (float *) zmalloc(Nblocks * sizeof(float)); Fbusy_goal = Nblocks; Fbusy_done = 0; for (Brow = 0; Brow < Brows; Brow++) // loop blocks for (Bcol = 0; Bcol < Bcols; Bcol++) { block = Brow * Bcols + Bcol; // block sequence number pylo = Brow * RES; // range of pixels in block pyhi = pylo + RES; pxlo = Bcol * RES; pxhi = pxlo + RES; R = G = B = 0; for (py = pylo; py < pyhi; py++) // loop pixels in block for (px = pxlo; px < pxhi; px++) { pix1 = PXMpix(E1pxm,px,py); // sum RGB for all pixels R += pix1[0]; G += pix1[1]; B += pix1[2]; } blockR[block] = R / RES2; // save mean RGB values for block blockG[block] = G / RES2; blockB[block] = B / RES2; } if (COMP) { for (Brow = 1; Brow < Brows-1; Brow++) // loop blocks, omitting for (Bcol = 1; Bcol < Bcols-1; Bcol++) // first/last rows/cols { block = Brow * Bcols + Bcol; // block sequence number R = blockR[block]; // block mean RGB G = blockG[block]; B = blockB[block]; Rp = int(R + bb/2) / bb * bb; // nearest palette RGB Gp = int(G + bb/2) / bb * bb; Bp = int(B + bb/2) / bb * bb; if (Rp > 255) Rp = 255; if (Gp > 255) Gp = 255; if (Bp > 255) Bp = 255; Re = R - Rp; // errors Ge = G - Gp; Be = B - Bp; n1 = block + 1; // distribute errors downstream n2 = block + Bcols - 1; n3 = n2 + 1; n4 = n3 + 1; blockR[n1] += 0.4375 * Re; blockG[n1] += 0.4375 * Ge; blockB[n1] += 0.4375 * Be; blockR[n2] += 0.1875 * Re; blockG[n2] += 0.1875 * Ge; blockB[n2] += 0.1875 * Be; blockR[n3] += 0.3125 * Re; blockG[n3] += 0.3125 * Ge; blockB[n3] += 0.3125 * Be; blockR[n4] += 0.0625 * Re; blockG[n4] += 0.0625 * Ge; blockB[n4] += 0.0625 * Be; } } for (Brow = 0; Brow < Brows; Brow++) // loop blocks for (Bcol = 0; Bcol < Bcols; Bcol++) { block = Brow * Bcols + Bcol; // block sequence number R = blockR[block]; // block mean RGB + errors G = blockG[block]; B = blockB[block]; Rp = int(R + bb/2) / bb * bb; // nearest palette RGB Gp = int(G + bb/2) / bb * bb; Bp = int(B + bb/2) / bb * bb; if (Rp > 255) Rp = 255; if (Gp > 255) Gp = 255; if (Bp > 255) Bp = 255; pylo = Brow * RES; // range of pixels in block pyhi = pylo + RES; pxlo = Bcol * RES; pxhi = pxlo + RES; for (py = pylo; py < pyhi; py++) // loop pixels in block for (px = pxlo; px < pxhi; 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 } pix3 = PXMpix(E3pxm,px,py); // set pixels to pix3[0] = Rp; // block palette color pix3[1] = Gp; pix3[2] = Bp; if (sa_stat == 3 && dist < sa_blendwidth) // within area edge blend width { f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; pix1 = PXMpix(E1pxm,px,py); 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++; } zfree(blockR); zfree(blockG); zfree(blockB); Fbusy_goal = Fbusy_done = 0; CEF->Fmods++; // image modified CEF->Fsaved = 0; // not saved paintlock(0); // unblock window paint Fpaint2(); // update window } return 0; // not executed, stop warning } /********************************************************************************/ // Dither image using custom colors and Floyd-Steinberg error distribution algorithm. namespace dither3_names { editfunc EFdither3; int E3ww, E3hh; int RES, RES2, COMP; char palettename[100] = "none"; // palette file name int Ncolors = 0; // palette color count } // menu function void m_dither3(GtkWidget *, const char *menu) // 19.0 { using namespace dither3_names; int dither3_dialog_event(zdialog* zd, const char *event); void * dither3_thread(void *); F1_help_topic = "dither"; EFdither3.menuname = "dither3"; EFdither3.menufunc = m_dither3; EFdither3.funcname = "dither3"; // function name EFdither3.threadfunc = dither3_thread; // thread function EFdither3.Farea = 2; // select area usable EFdither3.Frestart = 1; // allow restart EFdither3.FusePL = 1; // can use with paint/lever edits EFdither3.Fscript = 1; // script file supported 20.0 if (! edit_setup(EFdither3)) return; // setup edit E3ww = E3pxm->ww; E3hh = E3pxm->hh; if (Fscriptrun) Ncolors = 0; // 20.0 /*** _________________________________ | Dither3 | | | | resolution [ N ] | // 2x2 to 30x30 | palette: [palette name] [load] | // 20.0 | [x] error compensation | | | | [apply] [done] [cancel] | |_________________________________| ***/ zdialog *zd = zdialog_new(E2X("Dither3"),Mwin,Bapply,Bdone,Bcancel,null); // dither dialog CEF->zd = zd; zdialog_add_widget(zd,"hbox","hbres","dialog"); zdialog_add_widget(zd,"label","labres","hbres",E2X("resolution"),"space=5"); zdialog_add_widget(zd,"zspin","RES","hbres","2|30|1|4","space=5|size=2"); zdialog_add_widget(zd,"hbox","hbpal","dialog"); zdialog_add_widget(zd,"hbox","hbpal","dialog"); zdialog_add_widget(zd,"label","labpal","hbpal",E2X("palette:"),"space=5"); zdialog_add_widget(zd,"text","palname","hbpal","none","space=5"); zdialog_add_widget(zd,"button","LOAD","hbpal",Bload,"space=5"); zdialog_add_widget(zd,"hbox","hbcomp","dialog"); zdialog_add_widget(zd,"check","COMP","hbcomp",E2X("error compensation"),"space=5"); if (! Fscriptrun) zdialog_restore_inputs(zd); // restore previous inputs zdialog_run(zd,dither3_dialog_event,"save"); // run dialog - parallel return; } // dither3 dialog event and completion function int dither3_dialog_event(zdialog *zd, const char *event) { using namespace dither3_names; int load_palette_map(cchar *file); char palettefile[200], *pp; if (strmatch(event,"apply")) zd->zstat = 1; // from script 20.0 if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) { if (zd->zstat == 1) // [apply] { zd->zstat = 0; zdialog_fetch(zd,"RES",RES); zdialog_fetch(zd,"COMP",COMP); zdialog_fetch(zd,"palname",palettename,100); if (! Ncolors) { // no palette loaded 20.0 snprintf(palettefile,200,"%s/%s",palettes_folder,palettename); Ncolors = load_palette_map(palettefile); // CPU loops for a long time } if (Ncolors) signal_thread(); // no palette loaded } else if (zd->zstat == 2) edit_done(0); // [done] else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"blendwidth")) signal_thread(); if (strmatch(event,"LOAD")) // load a palette file { pp = zgetfile(E2X("palette file"),MWIN,"file",palettes_folder,0); if (pp) { zdialog_show(zd,0); poptext_window(MWIN,"mapping palette ...",200,200,0,1000); zmainsleep(0.1); Ncolors = load_palette_map(pp); // CPU loops for a long time poptext_window(0,0,0,0,0,0); if (Ncolors) { zmessage_post(Mwin,"parent",3,"palette loaded, %d colors",Ncolors); pp = strrchr(pp,'/'); zdialog_stuff(zd,"palname",pp+1); } zdialog_show(zd,1); } } return 1; } // thread function - multiple working threads to update image void * dither3_thread(void *) { using namespace dither3_names; void match_palette_map(int r, int g, int b, int &R, int &G, int&B); int Brows, Bcols, Brow, Bcol, block, Nblocks; int pxlo, pxhi, pylo, pyhi, px, py; float *blockR, *blockG, *blockB; float R, G, B, Re, Ge, Be; int Rp, Gp, Bp; int n1, n2, n3, n4; int ii, dist = 0; float *pix1, *pix3; float f1, f2; while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint RES2 = RES * RES; // pixel block size Bcols = E3ww / RES; // block rows and cols Brows = E3hh / RES; Nblocks = Brows * Bcols; // allocate block color memory blockR = (float *) zmalloc(Nblocks * sizeof(float)); blockG = (float *) zmalloc(Nblocks * sizeof(float)); blockB = (float *) zmalloc(Nblocks * sizeof(float)); Fbusy_goal = Nblocks; Fbusy_done = 0; for (Brow = 0; Brow < Brows; Brow++) // loop blocks for (Bcol = 0; Bcol < Bcols; Bcol++) { block = Brow * Bcols + Bcol; // block sequence number pylo = Brow * RES; // range of pixels in block pyhi = pylo + RES; pxlo = Bcol * RES; pxhi = pxlo + RES; R = G = B = 0; for (py = pylo; py < pyhi; py++) // loop pixels in block for (px = pxlo; px < pxhi; px++) { pix1 = PXMpix(E1pxm,px,py); // sum RGB for all pixels R += pix1[0]; G += pix1[1]; B += pix1[2]; } blockR[block] = R / RES2; // save mean RGB values for block blockG[block] = G / RES2; blockB[block] = B / RES2; } if (COMP) { for (Brow = 1; Brow < Brows-1; Brow++) // loop blocks, omitting for (Bcol = 1; Bcol < Bcols-1; Bcol++) // first/last rows/cols { block = Brow * Bcols + Bcol; // block sequence number R = blockR[block]; // block mean RGB G = blockG[block]; B = blockB[block]; if (R < 0) R = 0; if (R > 255) R = 255; if (G < 0) G = 0; if (G > 255) G = 255; if (B < 0) B = 0; if (B > 255) B = 255; match_palette_map(R,G,B,Rp,Gp,Bp); // get nearest palette RGB Re = R - Rp; // errors Ge = G - Gp; Be = B - Bp; n1 = block + 1; // distribute errors downstream n2 = block + Bcols - 1; n3 = n2 + 1; n4 = n3 + 1; blockR[n1] += 0.4375 * Re; blockG[n1] += 0.4375 * Ge; blockB[n1] += 0.4375 * Be; blockR[n2] += 0.1875 * Re; blockG[n2] += 0.1875 * Ge; blockB[n2] += 0.1875 * Be; blockR[n3] += 0.3125 * Re; blockG[n3] += 0.3125 * Ge; blockB[n3] += 0.3125 * Be; blockR[n4] += 0.0625 * Re; blockG[n4] += 0.0625 * Ge; blockB[n4] += 0.0625 * Be; } } for (Brow = 0; Brow < Brows; Brow++) // loop blocks for (Bcol = 0; Bcol < Bcols; Bcol++) { block = Brow * Bcols + Bcol; // block sequence number R = blockR[block]; // block mean RGB + errors G = blockG[block]; B = blockB[block]; if (R < 0) R = 0; if (R > 255) R = 255; if (G < 0) G = 0; if (G > 255) G = 255; if (B < 0) B = 0; if (B > 255) B = 255; match_palette_map(R,G,B,Rp,Gp,Bp); // get nearest palette RGB pylo = Brow * RES; // range of pixels in block pyhi = pylo + RES; pxlo = Bcol * RES; pxhi = pxlo + RES; for (py = pylo; py < pyhi; py++) // loop pixels in block for (px = pxlo; px < pxhi; 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 } pix3 = PXMpix(E3pxm,px,py); // set pixels to pix3[0] = Rp; // block palette color pix3[1] = Gp; pix3[2] = Bp; if (sa_stat == 3 && dist < sa_blendwidth) // within area edge blend width { f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; pix1 = PXMpix(E1pxm,px,py); 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++; } zfree(blockR); zfree(blockG); zfree(blockB); Fbusy_goal = Fbusy_done = 0; CEF->Fmods++; // image modified CEF->Fsaved = 0; // not saved paintlock(0); // unblock window paint Fpaint2(); // update window } return 0; // not executed, stop warning } /********************************************************************************/ namespace palette_map_names { int Np = 0, maxNp = 2000; int palette[2000][3]; int16 RGBmap[256][256][256]; } // Load a palette file and map all RGB colors to best palette color. // Returns palette color count, or 0 if error. int load_palette_map(cchar *file) { using namespace palette_map_names; void * load_palette_map_thread(void *arg); FILE *fid; char buffer[200]; int nn, R, G, B; Np = 0; printz("read palette file: %s \n",file); fid = fopen(file,"r"); if (! fid) { zmessageACK(Mwin,strerror(errno)); return 0; } while (true) { if (! fgets_trim(buffer,200,fid)) break; nn = sscanf(buffer,"%d %d %d",&R,&G,&B); if (nn != 3) continue; if (R < 0 || R > 255) continue; if (G < 0 || G > 255) continue; if (B < 0 || B > 255) continue; palette[Np][0] = R; palette[Np][1] = G; palette[Np][2] = B; Np++; if (Np == maxNp) break; } fclose(fid); if (Np < 6) { zmessageACK(Mwin,"palette has %d colors",Np); return 0; } if (Np == maxNp) { zmessageACK(Mwin,"palette exceeds %d color limit",maxNp); return 0; } do_wthreads(load_palette_map_thread,NWT); return Np; } // thread function for mapping all RGB values to best-fit palette color void * load_palette_map_thread(void *arg) { using namespace palette_map_names; int index = * ((int *) arg); int Dr, Dg, Db, Dmin, D; int R, G, B, p, Mp; for (R = index; R < 256; R += NWT) for (G = 0; G < 256; G++) for (B = 0; B < 256; B++) { Dmin = 99999999; Mp = 0; for (p = 0; p < Np; p++) { Dr = R - palette[p][0]; Dg = G - palette[p][1]; Db = B - palette[p][2]; D = Dr * Dr + Dg * Dg + Db * Db; if (D > Dmin) continue; Dmin = D; Mp = p; } RGBmap[R][G][B] = Mp; } pthread_exit(0); } // return the best matching palette RGB for a given input RGB void match_palette_map(int r, int g, int b, int &R, int &G, int&B) { using namespace palette_map_names; int p; R = G = B = 0; if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) return; if (! Np) return; p = RGBmap[r][g][b]; R = palette[p][0]; G = palette[p][1]; B = palette[p][2]; } /********************************************************************************/ // convert image to simulate a painting namespace painting_names { editfunc EFpainting; int color_depth; int group_area; float color_match; int borders; typedef struct { int16 px, py; char direc; } spixstack; int Nstack; spixstack *pixstack; // pixel group search memory int *pixgroup; // maps (px,py) to pixel group no. int *groupcount; // count of pixels in each group int group; char direc; float gcolor[3]; } void m_painting(GtkWidget *, cchar *menu) { using namespace painting_names; int painting_dialog_event(zdialog *zd, cchar *event); void * painting_thread(void *); F1_help_topic = "painting"; EFpainting.menuname = menu; EFpainting.menufunc = m_painting; EFpainting.funcname = "painting"; EFpainting.Farea = 2; // select area usable EFpainting.Fscript = 1; // scripting supported EFpainting.threadfunc = painting_thread; // thread function if (! edit_setup(EFpainting)) return; // setup edit zdialog *zd = zdialog_new(E2X("Painting"),Mwin,Bapply,Bdone,Bcancel,null); EFpainting.zd = zd; /*** _____________________________ | Simulate Painting | | | | color depth [____] | | patch area goal [____] | | req. color match [____] | | borders [_] | | | | [Apply] [Done] [Cancel] | |_____________________________| ***/ zdialog_add_widget(zd,"hbox","hbcd","dialog",0,"space=1"); zdialog_add_widget(zd,"label","lab1","hbcd",E2X("color depth"),"space=5"); zdialog_add_widget(zd,"zspin","colordepth","hbcd","1|5|1|3","space=5"); zdialog_add_widget(zd,"hbox","hbts","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labts","hbts",E2X("patch area goal"),"space=5"); zdialog_add_widget(zd,"zspin","grouparea","hbts","0|9999|10|1000","space=5"); zdialog_add_widget(zd,"hbox","hbcm","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labcm","hbcm",E2X("req. color match"),"space=5"); zdialog_add_widget(zd,"zspin","colormatch","hbcm","0|99|1|50","space=5"); zdialog_add_widget(zd,"hbox","hbbd","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labbd","hbbd",E2X("borders"),"space=5"); zdialog_add_widget(zd,"check","borders","hbbd",0,"space=2"); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,painting_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int painting_dialog_event(zdialog *zd, cchar *event) { using namespace painting_names; if (strmatch(event,"focus")) return 1; if (strmatch(event,"apply")) zd->zstat = 1; // from script if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { // apply zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"colordepth",color_depth); // color depth zdialog_fetch(zd,"grouparea",group_area); // target group area (pixels) zdialog_fetch(zd,"colormatch",color_match); // req. color match to combine groups zdialog_fetch(zd,"borders",borders); // borders wanted color_match = 0.01 * color_match; // scale 0 to 1 signal_thread(); // do the work } else if (zd->zstat == 2) edit_done(0); // done, commit edit else edit_cancel(0); // cancel, discard edit return 1; } return 0; } // painting thread function void * painting_thread(void *) { void painting_colordepth(); void painting_pixgroups(); void painting_mergegroups(); void painting_paintborders(); void painting_blend(); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 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; paintlock(0); // unblock window paint 19.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_blendwidth > 0) { for (ii = 0; ii < E3pxm->ww * E3pxm->hh; ii++) // find pixels in select area { dist = sa_pixmap[ii]; if (! dist || dist >= sa_blendwidth) continue; py = ii / E3pxm->ww; px = ii - py * E3pxm->ww; pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel f2 = sa_blendfunc(dist); // changes over distance sa_blendwidth 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; } /********************************************************************************/ // texture menu function // apply a random texture to the image namespace texturenames { editfunc EFtexture; // edit function data int radius = 4; int power = 40; int e3ww, e3hh; } void m_texture(GtkWidget *, cchar *menu) { using namespace texturenames; int texture_dialog_event(zdialog* zd, const char *event); void * texture_thread(void *); zdialog *zd; F1_help_topic = "texture"; EFtexture.menuname = menu; EFtexture.menufunc = m_texture; EFtexture.funcname = "texture"; // function name EFtexture.Farea = 2; // select area OK EFtexture.Fscript = 1; // scripting supported 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(E2X("Add Texture"),Mwin,Bapply,Bdone,Bcancel,null); // texture dialog CEF->zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=10"); zdialog_add_widget(zd,"label","labrad","hb1",Bradius,"space=3"); zdialog_add_widget(zd,"zspin","radius","hb1","1|40|1|4"); zdialog_add_widget(zd,"label","space","hb1",0,"space=1"); zdialog_add_widget(zd,"label","labpow","hb1",Bpower); zdialog_add_widget(zd,"zspin","power","hb1","1|100|1|40","space=3"); zdialog_stuff(zd,"radius",radius); zdialog_stuff(zd,"power",power); zdialog_run(zd,texture_dialog_event,"save"); // run dialog - parallel return; } // texture dialog event and completion function int texture_dialog_event(zdialog *zd, const char *event) { using namespace texturenames; if (strmatch(event,"focus")) return 1; if (strmatch(event,"apply")) zd->zstat = 1; // from script if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { // apply zd->zstat = 0; // keep dialog active edit_reset(); zdialog_fetch(zd,"radius",radius); zdialog_fetch(zd,"power",power); signal_thread(); // process return 1; } if (zd->zstat == 2) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } return 1; } // thread function - update image void * texture_thread(void *) { using namespace texturenames; void * texture_wthread(void *arg); // worker thread while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 if (sa_stat == 3) Fbusy_goal = sa_Npixel; // set up progress monitor else Fbusy_goal = e3ww * e3hh; Fbusy_done = 0; do_wthreads(texture_wthread,NWT); // worker threads Fbusy_goal = Fbusy_done = 0; CEF->Fmods = 1; // image modified CEF->Fsaved = 0; // not saved paintlock(0); // unblock window paint 19.0 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_blendwidth) { // select area is active, f3 = sa_blendfunc(dist); // blend changes over sa_blendwidth 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++; } pthread_exit(0); // exit thread } /********************************************************************************/ // 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 Fgray = 0; // grayscale = no } // menu function void m_pattern(GtkWidget *, cchar *menu) { using namespace patternnames; int pattern_dialog_event(zdialog* zd, const char *event); zdialog *zd; F1_help_topic = "pattern"; EFpattern.menuname = menu; EFpattern.menufunc = m_pattern; EFpattern.funcname = "pattern"; // function name EFpattern.Farea = 2; // select area OK EFpattern.Fscript = 1; // scripting supported if (! edit_setup(EFpattern)) return; // setup edit e3ww = E3pxm->ww; // image dimensions e3hh = E3pxm->hh; /*** _______________________________________________ | [x][-][_] Background Pattern | | | | Pattern File [____________________] [Browse] | | Zoom [___] Geometry [Calculate] | | Pattern Width NNN Height NNN | // labels not spin buttons | Overlap Width [___] Height [___] | | Opacity [___] Contrast [___] [x] Grayscale | | | | [apply] [done] [cancel] | |_______________________________________________| ***/ zd = zdialog_new(E2X("Background Pattern"),Mwin,Bapply,Bdone,Bcancel,null); CEF->zd = zd; zdialog_add_widget(zd,"hbox","hbfile","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labfile","hbfile",E2X("Pattern File:"),"space=5"); zdialog_add_widget(zd,"zentry","pattfile","hbfile",0,"expand"); zdialog_add_widget(zd,"button","browse","hbfile",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hbcalc","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labzoom","hbcalc",E2X("Zoom"),"space=5"); zdialog_add_widget(zd,"zspin","zoom","hbcalc","1.0|5.0|0.01|1.0"); zdialog_add_widget(zd,"label","space","hbcalc",0,"space=10"); zdialog_add_widget(zd,"label","labcalc","hbcalc",E2X("Geometry"),"space=5"); zdialog_add_widget(zd,"button","calc","hbcalc",Bcalculate); zdialog_add_widget(zd,"hbox","hbs11","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labpatt","hbs11",E2X("Pattern"),"space=3"); zdialog_add_widget(zd,"label","labwidth","hbs11",Bwidth,"space=3"); zdialog_add_widget(zd,"label","pattww","hbs11","tbd","space=5"); zdialog_add_widget(zd,"label","space","hbs11",0,"space=10"); zdialog_add_widget(zd,"label","labheight","hbs11",Bheight,"space=3"); zdialog_add_widget(zd,"label","patthh","hbs11","tbd","space=5"); zdialog_add_widget(zd,"hbox","hbs12","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labover","hbs12",E2X("Overlap"),"space=3"); zdialog_add_widget(zd,"label","labwidth","hbs12",Bwidth,"space=3"); zdialog_add_widget(zd,"zspin","olapww","hbs12","0|1000|1|0"); zdialog_add_widget(zd,"label","space","hbs12",0,"space=10"); zdialog_add_widget(zd,"label","labheight","hbs12",Bheight,"space=3"); zdialog_add_widget(zd,"zspin","olaphh","hbs12","0|1000|1|0"); zdialog_add_widget(zd,"hbox","hbopac","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labopac","hbopac",E2X("Opacity"),"space=5"); zdialog_add_widget(zd,"zspin","opacity","hbopac","0|100|1|0"); zdialog_add_widget(zd,"label","space","hbopac",0,"space=10"); zdialog_add_widget(zd,"label","labcont","hbopac",E2X("Contrast"),"space=5"); zdialog_add_widget(zd,"zspin","contrast","hbopac","0|200|1|100"); zdialog_add_widget(zd,"label","space","hbopac",0,"space=10"); zdialog_add_widget(zd,"check","gray","hbopac",E2X("Grayscale")); zdialog_restore_inputs(zd); // restore previous inputs zdialog_run(zd,pattern_dialog_event,"save"); // run dialog - parallel zdialog_send_event(zd,"init"); // initialize return; } // pattern dialog event and completion function int pattern_dialog_event(zdialog *zd, const char *event) // overhauled to retain prior data { using namespace patternnames; void pattern_match(); void pattern_image(); char temp1[150], temp2[200]; char *file, *pp; PIXBUF *pixbuf2; GError *gerror = 0; if (strmatch(event,"focus")) return 1; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"apply")) zd->zstat = 1; // from script 20.0 if (zd->zstat) { if (zd->zstat == 1) { // apply 20.0 zd->zstat = 0; // keep dialog alive pattern_image(); // calculate image + pattern CEF->Fmods = 1; // image modified CEF->Fsaved = 0; // not saved Fpaint2(); // update window return 1; } else { if (zd->zstat == 2) edit_done(0); // commit edit else edit_cancel(0); // discard edit if (pixbuf) g_object_unref(pixbuf); // free memory pixbuf = 0; if (pattfile) zfree(pattfile); pattfile = 0; return 1; } } zdialog_fetch(zd,"olapww",olapww); zdialog_fetch(zd,"olaphh",olaphh); zdialog_fetch(zd,"zoom",zoom); zdialog_fetch(zd,"opacity",opacity); zdialog_fetch(zd,"contrast",contrast); if (strmatch(event,"init")) { if (pattfile) zfree(pattfile); pattfile = 0; if (pixbuf) g_object_unref(pixbuf); pixbuf = 0; zdialog_fetch(zd,"pattfile",temp1,150); // get prior pattern file if (*temp1 > ' ') { snprintf(temp2,200,"%s/%s",pattern_folder,temp1); pattfile = zstrdup(temp2); } zdialog_stuff(zd,"gray",0); // grayscale off Fgray = 0; } if (strmatch(event,"gray")) // 20.0 zdialog_fetch(zd,"gray",Fgray); if (strmatch(event,"browse")) // choose new pattern file { if (pattfile) pp = pattfile; else pp = pattern_folder; file = gallery_select1(pp); // use thumbnail selection 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 (zstrstr("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_folder)) // add pattern to folder if needed copyFile(pattfile,pattern_folder); pixbuf2 = gdk_pixbuf_stripalpha(pixbuf); // strip alpha channel if present if (pixbuf2) { g_object_unref(pixbuf); pixbuf = pixbuf2; } pattrs = gdk_pixbuf_get_rowstride(pixbuf); // row stride pixels = gdk_pixbuf_get_pixels(pixbuf); // image data (pixels) } if (! pixbuf) return 1; // continuation pointless pattww = gdk_pixbuf_get_width(pixbuf); // pixbuf dimensions patthh = gdk_pixbuf_get_height(pixbuf); if (strmatch(event,"calc")) { // find pattern dimensions automatically pattern_match(); olapww = olaphh = 0; // reset overlaps } if (zstrstr("zoom init",event)) // set pattern zoom level { g_object_unref(pixbuf); pixbuf = gdk_pixbuf_new_from_file(pattfile,&gerror); // refresh pixbuf at scale 1x if (! pixbuf) { zmessageACK(Mwin,"%s \n %s",pattfile,gerror->message); return 1; } pixbuf2 = gdk_pixbuf_stripalpha(pixbuf); // strip alpha channel if present if (pixbuf2) { g_object_unref(pixbuf); pixbuf = pixbuf2; } pattww = gdk_pixbuf_get_width(pixbuf); // pixbuf dimensions patthh = gdk_pixbuf_get_height(pixbuf); pattww = pattww * zoom; // new dimensions patthh = patthh * zoom; pixbuf2 = gdk_pixbuf_scale_simple(pixbuf,pattww,patthh,BILINEAR); g_object_unref(pixbuf); // replace original pixbuf pixbuf = pixbuf2; if (! pixbuf) return 1; pattww = gdk_pixbuf_get_width(pixbuf); // new pixbuf dimensions patthh = gdk_pixbuf_get_height(pixbuf); pattrs = gdk_pixbuf_get_rowstride(pixbuf); // row stride pixels = gdk_pixbuf_get_pixels(pixbuf); // image data (pixels) } if (olapww > pattww/3) olapww = pattww/3; // prevent nonsense if (olaphh > patthh/3) olaphh = patthh/3; zdialog_stuff(zd,"pattww",pattww); // stuff all dialog parameters zdialog_stuff(zd,"patthh",patthh); zdialog_stuff(zd,"olapww",olapww); zdialog_stuff(zd,"olaphh",olaphh); zdialog_stuff(zd,"zoom",zoom); zdialog_stuff(zd,"opacity",opacity); zdialog_stuff(zd,"contrast",contrast); 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; } // combine image and pattern void pattern_image() // 19.0 { 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; int R, G, B; uint8 *pixt; float *pix1, *pix3; float pbrite, f1, f2; double mbrite = 0; float *tbmap; pww = pattww; // capture and freeze volatile params phh = patthh; prs = pattrs; cc = e3ww * e3hh; // tile pixels mapping to image pixel tmap = (char *) zmalloc(cc); memset(tmap,0,cc); cc = pww * phh * sizeof(float); // tile brightness map tbmap = (float *) zmalloc(cc); for (mpy = 0; mpy < e3hh; mpy++) // clear output image to black for (mpx = 0; mpx < e3ww; mpx++) { pix3 = PXMpix(E3pxm,mpx,mpy); pix3[0] = pix3[1] = pix3[2] = 0; } Ntrows = e3hh / (phh-2*olaphh) + 1; // tile rows and columns including Ntcols = e3ww / (pww-2*olapww) + 1; // top/bottom and left/right overlaps for (trow = 0; trow < Ntrows; trow++) // loop tile rows, columns for (tcol = 0; tcol < Ntcols; tcol++) { for (tpy = 0; tpy < phh; tpy++) // loop tile pixels for (tpx = 0; tpx < pww; tpx++) { mpy = trow * (phh-2*olaphh) + tpy; // corresponding image pixel mpx = tcol * (pww-2*olapww) + tpx; if (mpy >= e3hh || mpx >= e3ww) continue; ii = mpy * e3ww + mpx; // count tile pixels overlapping ++tmap[ii]; // this image pixel } } for (trow = 0; trow < Ntrows; trow++) // loop tile rows, columns for (tcol = 0; tcol < Ntcols; tcol++) { for (tpy = 0; tpy < phh; tpy++) // loop tile pixels for (tpx = 0; tpx < pww; tpx++) { mpy = trow * (phh-2*olaphh) + tpy; // corresponding image pixel mpx = tcol * (pww-2*olapww) + tpx; if (mpy >= e3hh || mpx >= e3ww) continue; ii = mpy * e3ww + mpx; pixt = pixels + tpy * prs + tpx * 3; // input tile pixel R = pixt[0]; G = pixt[1]; B = pixt[2]; if (Fgray) R = G = B = 0.333 * (R + G + B); // convert to grayscale 20.0 pix3 = PXMpix(E3pxm,mpx,mpy); // output image pixel pix3[0] += (0.01 * opacity / tmap[ii]) * R; // image = tile * opacity 0-100% pix3[1] += (0.01 * opacity / tmap[ii]) * G; // reduce for overlapping tiles pix3[2] += (0.01 * opacity / tmap[ii]) * B; } } for (mpy = 0; mpy < e3hh; mpy++) // loop image pixels for (mpx = 0; mpx < e3ww; mpx++) { pix1 = PXMpix(E1pxm,mpx,mpy); // input image pixel pix3 = PXMpix(E3pxm,mpx,mpy); // output image pixel pix3[0] += (1.0 - 0.01 * opacity) * pix1[0]; // add input pixel to output, pix3[1] += (1.0 - 0.01 * opacity) * pix1[1]; // part not taken by tiles pix3[2] += (1.0 - 0.01 * opacity) * pix1[2]; } for (tpy = 0; tpy < phh; tpy++) // loop tile pixels for (tpx = 0; tpx < pww; tpx++) { pixt = pixels + tpy * prs + tpx * 3; mbrite += pixt[0] + pixt[1] + pixt[2]; // sum of all pixel RGB values } mbrite = mbrite / (phh * pww); // mean RGB sum for all tile pixels for (tpy = 0; tpy < phh; tpy++) // loop tile pixels for (tpx = 0; tpx < pww; tpx++) { pixt = pixels + tpy * prs + tpx * 3; pbrite = pixt[0] + pixt[1] + pixt[2]; pbrite = pbrite / mbrite; // pixel relative brightness 0...2 ii = tpy * pww + tpx; tbmap[ii] = pbrite; } for (trow = 0; trow < Ntrows; trow++) // loop tile rows, columns for (tcol = 0; tcol < Ntcols; tcol++) { for (tpy = 0; tpy < phh; tpy++) // loop tile pixels for (tpx = 0; tpx < pww; tpx++) { mpy = trow * (phh-2*olaphh) + tpy; // corresponding image pixel mpx = tcol * (pww-2*olapww) + tpx; if (mpy >= e3hh || mpx >= e3ww) continue; pix3 = PXMpix(E3pxm,mpx,mpy); ii = tpy * pww + tpx; pbrite = tbmap[ii]; // tile pixel rel. brightness 0...2 ii = mpy * e3ww + mpx; if (tmap[ii] > 1) // if 2+ tile pixels map to image pixel pbrite = (pbrite - 1.0) / tmap[ii] + 1.0; // reduce impact of each pbrite = 0.01 * contrast * (pbrite - 1.0) + 1.0; // apply contrast factor 0-100% pix3[0] *= pbrite; pix3[1] *= pbrite; pix3[2] *= pbrite; } } for (mpy = 0; mpy < e3hh; mpy++) // stop RGB overflows for (mpx = 0; mpx < e3ww; mpx++) { pix3 = PXMpix(E3pxm,mpx,mpy); if (pix3[0] > 255) pix3[0] = 255; if (pix3[1] > 255) pix3[1] = 255; if (pix3[2] > 255) pix3[2] = 255; } if (sa_stat == 3) // select area active { for (mpy = 0; mpy < e3hh; mpy++) // loop image pixels for (mpx = 0; mpx < e3ww; mpx++) { ii = mpy * e3ww + mpx; dist = sa_pixmap[ii]; // distance from edge if (dist) { // pixel inside area if (dist < sa_blendwidth) f1 = sa_blendfunc(dist); // blend edges else f1 = 1.0; } else f1 = 0.0; // pixel outside area f2 = 1.0 - f1; pix1 = PXMpix(E1pxm,mpx,mpy); // input image pixel pix3 = PXMpix(E3pxm,mpx,mpy); // output image pixel pix3[0] = f1 * pix3[0] + f2 * pix1[0]; pix3[1] = f1 * pix3[1] + f2 * pix1[1]; pix3[2] = f1 * pix3[2] + f2 * pix1[2]; } } return; } /********************************************************************************/ // Create a mosaic image using tiny thumbnails as tiles. // tiles can be clicked to pop-up a larger zoomable image. namespace mosaic_names { editfunc EFmosaic; // edit function data #define maxtiles maximages int iww, ihh; // base image dimensions int tww, thh; // tile size (default 32x24) int mww, mhh; // image size char *tilefile[maxtiles]; // tile files list (100K x 100 --> 10M) uint8 *tileimage[maxtiles]; // tile images (tww x thh x 3) (100K x 32 x 24 x 3 --> 230M) int Ntiles, Ndone; // tile image count float tileRGB1[maxtiles][3]; // tile mean RGB values for each tile quadrant float tileRGB2[maxtiles][3]; // (100K x 3 x 4B --> 1.2M x 4Q --> 4.8M) float tileRGB3[maxtiles][3]; float tileRGB4[maxtiles][3]; int *tilemap; // maps image [px,py] to tile used at that position int tbusy[max_threads]; } // menu function void m_mosaic(GtkWidget *, const char *) { using namespace mosaic_names; int mosaic_dialog_event(zdialog*, cchar *); void mosaic_mousefunc(); zdialog *zd; cchar *title = E2X("Create Mosaic"); char label[12]; int cc; F1_help_topic = "mosaic"; EFmosaic.menufunc = m_mosaic; EFmosaic.funcname = "mosaic"; // function name EFmosaic.mousefunc = mosaic_mousefunc; // mouse function if (! edit_setup(EFmosaic)) return; // setup edit tww = 32; // default tile size thh = 24; Ntiles = 0; // tile file and image counts iww = E3pxm->ww; // base image dimensions ihh = E3pxm->hh; cc = maxtiles * sizeof(char *); // clear tile file list memset(tilefile,0,cc); cc = maxtiles * sizeof(uint8 *); // clear tile image list memset(tileimage,0,cc); cc = iww * ihh * sizeof(int); // allocate image/tile map tilemap = (int *) zmalloc(cc); memset(tilemap,-1,cc); /*** ____________________________________________ | Create Mosaic | | | | Tile Width [___] Height [___] | | [Tiles] nnnnn [Image] | | Tile blending =========[]============= | | | | [done] [cancel] | |____________________________________________| ***/ zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); // mosaic dialog CEF->zd = zd; zdialog_add_widget(zd,"hbox","hbsize","dialog"); zdialog_add_widget(zd,"label","labsize","hbsize",E2X("Tile"),"space=3"); zdialog_add_widget(zd,"label","labwidth","hbsize",Bwidth,"space=3"); zdialog_add_widget(zd,"zspin","width","hbsize","16|48|2|32"); zdialog_add_widget(zd,"label","space","hbsize",0,"space=5"); zdialog_add_widget(zd,"label","labheight","hbsize",Bheight,"space=3"); zdialog_add_widget(zd,"zspin","height","hbsize","16|48|2|24"); zdialog_add_widget(zd,"hbox","hbcreate","dialog"); zdialog_add_widget(zd,"button","tiles","hbcreate",E2X("Tiles"),"space=3"); zdialog_add_widget(zd,"label","labNtiles","hbcreate","0","space=5"); zdialog_add_widget(zd,"button","image","hbcreate",Bimage,"space=10"); zdialog_add_widget(zd,"vbox","space","dialog",0,"space=3"); zdialog_add_widget(zd,"hbox","hbblend","dialog"); zdialog_add_widget(zd,"label","labblend","hbblend",E2X("Tile blending"),"space=3"); zdialog_add_widget(zd,"hscale","blend","hbblend","0|100|1|0","space=5|expand"); zdialog_stuff(zd,"width",tww); zdialog_stuff(zd,"height",thh); snprintf(label,12,"%d",Ntiles); zdialog_stuff(zd,"labNtiles",label); zdialog_run(zd,mosaic_dialog_event,"save"); // run dialog - parallel return; } // mosaic dialog event and completion function int mosaic_dialog_event(zdialog *zd, const char *event) { using namespace mosaic_names; void * mosaic_thread1(void *arg); void * mosaic_thread2(void *arg); void mosaic_mousefunc(); static int block = 0; char **flist = 0; char label[12]; int ii, err, NF; int trow, tcol, tpx, tpy, ipx, ipy; uint8 *timage, *tpix; float blend, f1, f3; float *pix1, *pix3; if (block) return 1; block = 1; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit for (ii = 0; ii < Ntiles; ii++) { // free memory if (tilefile[ii]) zfree(tilefile[ii]); if (tileimage[ii]) zfree(tileimage[ii]); } if (tilemap) zfree(tilemap); block = 0; return 1; } if (strmatch(event,"focus")) { takeMouse(mosaic_mousefunc,dragcursor); // connect mouse function block = 0; return 1; } if (strmatch(event,"tiles")) // read thumbnails, create tile images { for (ii = 0; ii < Ntiles; ii++) { // free memory if (tilefile[ii]) zfree(tilefile[ii]); if (tileimage[ii]) zfree(tileimage[ii]); tilefile[ii] = 0; tileimage[ii] = 0; } Ntiles = 0; Ndone = 0; // progress counter zdialog_stuff(zd,"labNtiles","0"); zdialog_fetch(zd,"width",tww); // get tile size from dialog zdialog_fetch(zd,"height",thh); err = find_imagefiles(thumbfolder,2+16,flist,NF); // all thumbnails, all levels if (err) { zmessageACK(Mwin,strerror(errno)); block = 0; return 1; } if (NF > maxtiles) { // too many zmessageACK(Mwin,E2X("exceeded max. tiles: %d"),maxtiles); zfree(flist); block = 0; return 1; } if (NF < 100) { // too few zmessageACK(Mwin,E2X("only %d tile images found"),NF); zfree(flist); block = 0; return 1; } for (ii = 0; ii < NF; ii++) { // save thumbnail filespecs tilefile[ii] = flist[ii]; tileimage[ii] = 0; } zfree(flist); Ntiles = NF; for (ii = 0; ii < NWT; ii++) { // create tile images and tbusy[ii] = 1; // RGB color data per tile start_detached_thread(mosaic_thread1,&Nval[ii]); } for (ii = 0; ii < NWT; ii++) { // wait for completion while(tbusy[ii]) { zsleep(0.01); snprintf(label,12,"%d",Ndone); // show count in dialog zdialog_stuff(zd,"labNtiles",label); zmainloop(); } } block = 0; return 1; } if (strmatch(event,"image")) // apply tiles to mosaic image { if (! Ntiles) { block = 0; return 1; } edit_undo(); // reset to original image for (ii = 0; ii < NWT; ii++) { // apply tiles to image tbusy[ii] = 1; start_detached_thread(mosaic_thread2,&Nval[ii]); } for (int ii = 0; ii < NWT; ii++) { // wait for completion while(tbusy[ii]) { zsleep(0.01); Fpaint2(); // update image continuously zmainloop(); } } zdialog_stuff(zd,"blend",0); // reset blend control CEF->Fmods++; // image is modified CEF->Fsaved = 0; block = 0; return 1; } if (strmatch(event,"blend")) { if (! Ntiles) { block = 0; return 1; } zdialog_fetch(zd,"blend",blend); // get blend value 0-100 f1 = blend / 100.0; // base image part, 0.0 --> 1.0 f3 = 1.0 - f1; // tile image part, 1.0 --> 0.0 for (trow = 0; trow < ihh/thh; trow++) // loop tile positions in base image for (tcol = 0; tcol < iww/tww; tcol++) { for (tpy = 0; tpy < thh; tpy++) // loop tile pixels for (tpx = 0; tpx < tww; tpx++) { ipy = trow * thh + tpy; // corresponding image pixels ipx = tcol * tww + tpx; ii = iww * ipy + ipx; // get tile used at image pixel ii = tilemap[ii]; timage = tileimage[ii]; tpix = timage + tpy * tww * 3 + tpx * 3; // input tile pixel pix1 = PXMpix(E1pxm,ipx,ipy); // input E1 image pixel pix3 = PXMpix(E3pxm,ipx,ipy); // output E3 image pixel pix3[0] = f1 * pix1[0] + f3 * tpix[0]; // replace image pixels with tile pixels pix3[1] = f1 * pix1[1] + f3 * tpix[1]; pix3[2] = f1 * pix1[2] + f3 * tpix[2]; } } Fpaint2(); CEF->Fmods++; // image is modified CEF->Fsaved = 0; block = 0; return 1; } block = 0; return 1; } // thread to make tile images from thumbnails and tile RGB color data void * mosaic_thread1(void *arg) { using namespace mosaic_names; int index = *((int *) arg); PIXBUF *pxb1, *pxb2; GError *gerror = 0; int tpx, tpy; int tww1, thh1, tww2, thh2; int ii, row, rs; uint8 *tpixels, *tpix, *timage; uint8 *row1, *row2; float fred1, fgreen1, fblue1; float fred2, fgreen2, fblue2; float fred3, fgreen3, fblue3; float fred4, fgreen4, fblue4; float coeff; for (ii = index; ii < Ntiles; ii += NWT) // loop tile image files { if (tileimage[ii]) zfree(tileimage[ii]); tileimage[ii] = 0; pxb1 = gdk_pixbuf_new_from_file_at_size(tilefile[ii],64,64,&gerror); // create pixbuf within 64x64 if (! pxb1) { printz("file: %s \n %s",tilefile[ii],gerror->message); gerror = 0; continue; } tww1 = gdk_pixbuf_get_width(pxb1); // actual width, height (max. 64) thh1 = gdk_pixbuf_get_height(pxb1); g_object_unref(pxb1); if (tww1 < tww || thh1 < thh) continue; // unusable 19.0 if (tww1 * thh < tww * thh1) { // tww1/thh1 < tww/thh tww2 = tww; // (too high for tww x thh) thh2 = tww * thh1 / tww1; } else { // tww1/thh1 > tww/thh thh2 = thh; // (too wide for tww x thh) tww2 = thh * tww1 / thh1; } pxb2 = gdk_pixbuf_new_from_file_at_size(tilefile[ii],tww2+1,thh2,&gerror); // tww2+1 required (see below) 19.0 if (! pxb2) { printz("file: %s \n %s",tilefile[ii],gerror->message); gerror = 0; continue; } tpx = (tww2 - tww) / 2; // tww2 or thh2 offset for pixels tpy = (thh2 - thh) / 2; // copied to tww x thh tile image rs = gdk_pixbuf_get_rowstride(pxb2); tpixels = gdk_pixbuf_get_pixels(pxb2); timage = (uint8 *) zmalloc(tww * thh * 3); // allocate memory for tww x thh pixels for (row = 0; row < thh; row++) { // copy pixbuf tww x thh section row1 = tpixels + rs * (row + tpy) + tpx * 3; row2 = timage + row * tww * 3; memcpy(row2,row1,tww * 3); // buffer-overflow unless (see above) } g_object_unref(pxb2); tileimage[ii] = timage; // save final tile image fred1 = fgreen1 = fblue1 = 0; fred2 = fgreen2 = fblue2 = 0; fred3 = fgreen3 = fblue3 = 0; fred4 = fgreen4 = fblue4 = 0; for (tpy = 0; tpy < thh/2; tpy++) // loop pixels in top left tile quadrant for (tpx = 0; tpx < tww/2; tpx++) { tpix = timage + tpy * tww * 3 + tpx * 3; fred1 += tpix[0]; // sum RGB values fgreen1 += tpix[1]; fblue1 += tpix[2]; } for (tpy = 0; tpy < thh/2; tpy++) // top right for (tpx = tww/2; tpx < tww; tpx++) { tpix = timage + tpy * tww * 3 + tpx * 3; fred2 += tpix[0]; fgreen2 += tpix[1]; fblue2 += tpix[2]; } for (tpy = thh/2; tpy < thh; tpy++) // bottom left for (tpx = 0; tpx < tww/2; tpx++) { tpix = timage + tpy * tww * 3 + tpx * 3; fred3 += tpix[0]; fgreen3 += tpix[1]; fblue3 += tpix[2]; } for (tpy = thh/2; tpy < thh; tpy++) // bottom right for (tpx = tww/2; tpx < tww; tpx++) { tpix = timage + tpy * tww * 3 + tpx * 3; fred4 += tpix[0]; fgreen4 += tpix[1]; fblue4 += tpix[2]; } coeff = 0.25 / (tww * thh); tileRGB1[ii][0] = coeff * fred1; // save tile mean RGB values tileRGB1[ii][1] = coeff * fgreen1; // for each quadrant tileRGB1[ii][2] = coeff * fblue1; tileRGB2[ii][0] = coeff * fred2; tileRGB2[ii][1] = coeff * fgreen2; tileRGB2[ii][2] = coeff * fblue2; tileRGB3[ii][0] = coeff * fred3; tileRGB3[ii][1] = coeff * fgreen3; tileRGB3[ii][2] = coeff * fblue3; tileRGB4[ii][0] = coeff * fred4; tileRGB4[ii][1] = coeff * fgreen4; tileRGB4[ii][2] = coeff * fblue4; Ndone++; // progress counter } tbusy[index] = 0; pthread_exit(0); } // thread to apply best color-matched tile to each image position void * mosaic_thread2(void *arg) { using namespace mosaic_names; int index = *((int *) arg); int trow, tcol, tpx, tpy, ipx, ipy; int ii, bestii; uint8 *tpix, *timage; float *pix1, *pix3; float fred1, fgreen1, fblue1; float fred2, fgreen2, fblue2; float fred3, fgreen3, fblue3; float fred4, fgreen4, fblue4; float match, bestmatch; float coeff; for (trow = index; trow < ihh/thh; trow += NWT) // loop tile positions in base image for (tcol = 0; tcol < iww/tww; tcol++) { fred1 = fgreen1 = fblue1 = 0; fred2 = fgreen2 = fblue2 = 0; fred3 = fgreen3 = fblue3 = 0; fred4 = fgreen4 = fblue4 = 0; for (tpy = 0; tpy < thh/2; tpy++) // loop pixels in top left tile quadrant for (tpx = 0; tpx < tww/2; tpx++) { ipy = trow * thh + tpy; // corresponding image pixels ipx = tcol * tww + tpx; pix1 = PXMpix(E1pxm,ipx,ipy); fred1 += pix1[0]; // sum image RGB values fgreen1 += pix1[1]; fblue1 += pix1[2]; } for (tpy = 0; tpy < thh/2; tpy++) // top right quadrant for (tpx = tww/2; tpx < tww; tpx++) { ipy = trow * thh + tpy; ipx = tcol * tww + tpx; pix1 = PXMpix(E1pxm,ipx,ipy); fred2 += pix1[0]; fgreen2 += pix1[1]; fblue2 += pix1[2]; } for (tpy = thh/2; tpy < thh; tpy++) // lower left quadrant for (tpx = 0; tpx < tww/2; tpx++) { ipy = trow * thh + tpy; ipx = tcol * tww + tpx; pix1 = PXMpix(E1pxm,ipx,ipy); fred3 += pix1[0]; fgreen3 += pix1[1]; fblue3 += pix1[2]; } for (tpy = thh/2; tpy < thh; tpy++) // lower right quadrant for (tpx = tww/2; tpx < tww; tpx++) { ipy = trow * thh + tpy; ipx = tcol * tww + tpx; pix1 = PXMpix(E1pxm,ipx,ipy); fred4 += pix1[0]; fgreen4 += pix1[1]; fblue4 += pix1[2]; } coeff = 0.25 / (tww * thh); fred1 = coeff * fred1; // mean image RGB values for fgreen1 = coeff * fgreen1; // each quadrant fblue1 = coeff * fblue1; fred2 = coeff * fred2; fgreen2 = coeff * fgreen2; fblue2 = coeff * fblue2; fred3 = coeff * fred3; fgreen3 = coeff * fgreen3; fblue3 = coeff * fblue3; fred4 = coeff * fred4; fgreen4 = coeff * fgreen4; fblue4 = coeff * fblue4; bestmatch = 0; bestii = 0; for (ii = 0; ii < Ntiles; ii++) // loop tile RGB values { if (! tileimage[ii]) continue; match = RGBMATCH(fred1,fgreen1,fblue1, // 0..1 = zero..perfect match tileRGB1[ii][0],tileRGB1[ii][1],tileRGB1[ii][2]); match += RGBMATCH(fred2,fgreen2,fblue2, tileRGB2[ii][0],tileRGB2[ii][1],tileRGB2[ii][2]); match += RGBMATCH(fred3,fgreen3,fblue3, tileRGB3[ii][0],tileRGB3[ii][1],tileRGB3[ii][2]); match += RGBMATCH(fred4,fgreen4,fblue4, tileRGB4[ii][0],tileRGB4[ii][1],tileRGB4[ii][2]); if (match > bestmatch) { bestmatch = match; // save best matching tile bestii = ii; } } ii = bestii; // best matching tile timage = tileimage[ii]; for (tpy = 0; tpy < thh; tpy++) // loop tile pixels for (tpx = 0; tpx < tww; tpx++) { ipy = trow * thh + tpy; // corresponding image pixels ipx = tcol * tww + tpx; tpix = timage + tpy * tww * 3 + tpx * 3; pix1 = PXMpix(E1pxm,ipx,ipy); pix3 = PXMpix(E3pxm,ipx,ipy); pix3[0] = tpix[0]; // replace image pixels with tile pixels pix3[1] = tpix[1]; pix3[2] = tpix[2]; ii = iww * ipy + ipx; // save map of tile used tilemap[ii] = bestii; // at each image pixel } } tbusy[index] = 0; pthread_exit(0); } // mouse function - called for mouse events inside image void mosaic_mousefunc() { using namespace mosaic_names; int ii; char *tfile, *mfile; if (! LMclick) return; LMclick = 0; ii = Myclick * iww + Mxclick; ii = tilemap[ii]; if (ii < 0) return; tfile = tilefile[ii]; mfile = thumb2imagefile(tfile); if (mfile) popup_image(mfile,MWIN,1,256); else popup_image(tfile,MWIN,1,256); return; } /********************************************************************************/ // make a black & white or color positive or negative, or sepia image namespace colormode_names { editfunc EFcolormode; int mode; float blend; } // menu function void m_color_mode(GtkWidget *, cchar *menu) { using namespace colormode_names; int colormode_dialog_event(zdialog *zd, cchar *event); void * colormode_thread(void *); F1_help_topic = "color mode"; EFcolormode.menuname = menu; EFcolormode.menufunc = m_color_mode; EFcolormode.funcname = "color_mode"; EFcolormode.threadfunc = colormode_thread; EFcolormode.FprevReq = 1; // use preview 19.5 EFcolormode.Farea = 2; // select area usable EFcolormode.Frestart = 1; // allow restart EFcolormode.Fscript = 1; // scripting supported if (! edit_setup(EFcolormode)) return; // setup edit: no preview /*** _____________________________ | Color Mode | | | | [_] reset | // check boxes | [_] black/white positive | | [_] black/white negative | | [_] color negative | | [_] RGB -> GBR | | [_] RGB -> BRG | | [_] sepia | | | | 0% ===========[]==== 100% | | | | [Done] [Cancel] | |_____________________________| ***/ zdialog *zd = zdialog_new(E2X("Color Mode"),Mwin,Bdone,Bcancel,null); EFcolormode.zd = zd; zdialog_add_widget(zd,"check","reset","dialog",E2X("reset")); zdialog_add_widget(zd,"check","b&wpos","dialog",E2X("black/white positive")); zdialog_add_widget(zd,"check","b&wneg","dialog",E2X("black/white negative")); zdialog_add_widget(zd,"check","colneg","dialog",E2X("color negative")); zdialog_add_widget(zd,"check","rgb-gbr","dialog",E2X("RGB -> GBR")); // 19.5 zdialog_add_widget(zd,"check","rgb-brg","dialog",E2X("RGB -> BRG")); zdialog_add_widget(zd,"check","sepia","dialog",E2X("sepia")); zdialog_add_widget(zd,"hbox","hbblend","dialog"); zdialog_add_widget(zd,"label","lab0","hbblend","0%","space=5"); zdialog_add_widget(zd,"hscale","blend","hbblend","0.0|1.0|0.01|1.0","expand"); zdialog_add_widget(zd,"label","lab100","hbblend","100%","space=5"); zdialog_resize(zd,200,0); zdialog_run(zd,colormode_dialog_event,"save"); // run dialog - parallel return; } // dialog event and completion callback function int colormode_dialog_event(zdialog *zd, cchar *event) { using namespace colormode_names; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"fullsize")) { // from select area 19.5 edit_fullsize(); // get full size image signal_thread(); return 1; } if (zd->zstat) { if (zd->zstat == 1) { edit_fullsize(); // get full size image 19.5 signal_thread(); edit_done(0); // commit edit } else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"focus")) return 1; if (zstrstr("reset b&wpos b&wneg colneg rgb-gbr rgb-brg sepia",event)) { zdialog_stuff(zd,"reset",0); zdialog_stuff(zd,"b&wpos",0); zdialog_stuff(zd,"b&wneg",0); zdialog_stuff(zd,"colneg",0); zdialog_stuff(zd,"rgb-gbr",0); // 19.5 zdialog_stuff(zd,"rgb-brg",0); zdialog_stuff(zd,"sepia",0); zdialog_stuff(zd,event,1); if (strmatch(event,"reset")) mode = 0; if (strmatch(event,"b&wpos")) mode = 1; if (strmatch(event,"b&wneg")) mode = 2; if (strmatch(event,"colneg")) mode = 3; if (strmatch(event,"rgb-gbr")) mode = 4; if (strmatch(event,"rgb-brg")) mode = 5; if (strmatch(event,"sepia")) mode = 6; } zdialog_fetch(zd,"blend",blend); if (mode == 0) { edit_reset(); return 1; } paintlock(1); // block window paint 19.5 signal_thread(); wait_thread_idle(); // wait for thread idle paintlock(0); // unblock window paint 19.5 Fpaint2(); // update window return 1; } // thread function void * colormode_thread(void *) { using namespace colormode_names; void * colormode_wthread(void *arg); while (true) { thread_idle_loop(); // wait for work or exit request do_wthreads(colormode_wthread,NWT); // worker threads CEF->Fmods++; // image modified CEF->Fsaved = 0; // not saved } return 0; // not executed, stop warning } // worker thread functions void * colormode_wthread(void *arg) { using namespace colormode_names; int index = *((int *) (arg)); int E3ww, E3hh; int ii, dist, px, py; float red1, green1, blue1; float red3, green3, blue3; float *pix1, *pix3; float brite, ff1, ff2; E3ww = E3pxm->ww; // 19.5 E3hh = E3pxm->hh; dist = 0; // stop compiler warnings for (py = index; py < E3hh; py += NWT) for (px = 0; px < E3ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel red1 = pix1[0]; green1 = pix1[1]; blue1 = pix1[2]; red3 = pix3[0]; green3 = pix3[1]; blue3 = pix3[2]; switch (mode) { case 1: { // black and white positive red3 = green3 = blue3 = 0.333 * (red1 + green1 + blue1); break; } case 2: { // black and white negative red3 = green3 = blue3 = 255.9 - 0.333 * (red1 + green1 + blue1); break; } case 3: { // color negative red3 = 255.9 - red1; green3 = 255.9 - green1; blue3 = 255.9 - blue1; break; } case 4: { // RGB - GBR red3 = green1; green3 = blue1; blue3 = red1; break; } case 5: { // RGB - BRG 19.5 red3 = blue1; green3 = red1; blue3 = green1; break; } case 6: { // sepia brite = red1; if (green1 > brite) brite = green1; // max. color level if (blue1 > brite) brite = blue1; brite = 0.2 * brite + 0.2666 * (red1 + green1 + blue1); // brightness, 0.0 ... 255.9 brite = brite * 0.003906; // 0.0 ... 1.0 ff1 = 1.0 - 0.7 * brite; // sepia part, 1.0 ... 0.3 19.0 ff2 = 1.0 - ff1; // B & W part, 0.0 ... 0.7 red3 = ff1 * 255.0 + ff2 * 256; // combine max. sepia with white green3 = ff1 * 150.0 + ff2 * 256; blue3 = ff1 * 46.0 + ff2 * 256; brite = 0.8333 * (brite + 0.2); // add brightness at low end red3 = red3 * brite; // output = combined color * brightness green3 = green3 * brite; blue3 = blue3 * brite; break; } } if (red3 < 0) red3 = 0; // prevent underflow/overlfow if (red3 > 255.9) red3 = 255.9; if (green3 < 0) green3 = 0; if (green3 > 255.9) green3 = 255.9; if (blue3 < 0) blue3 = 0; if (blue3 > 255.9) blue3 = 255.9; if (sa_stat == 3 && dist < sa_blendwidth) { // blend changes over blendwidth ff1 = sa_blendfunc(dist); ff2 = 1.0 - ff1; red3 = ff1 * red3 + ff2 * red1; green3 = ff1 * green3 + ff2 * green1; blue3 = ff1 * blue3 + ff2 * blue1; } if (blend < 1.0) { // blend slider ff1 = blend; ff2 = 1.0 - ff1; red3 = ff1 * red3 + ff2 * red1; green3 = ff1 * green3 + ff2 * green1; blue3 = ff1 * blue3 + ff2 * blue1; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } CEF->Fmods++; CEF->Fsaved = 0; pthread_exit(0); } /********************************************************************************/ // image color-depth reduction namespace color_depth_names { int color_depth_depth = 16; // bits per RGB color editfunc EFcolor_depth; int E3ww, E3hh; } // menu function void m_color_depth(GtkWidget *, cchar *menu) { using namespace color_depth_names; int color_depth_dialog_event(zdialog *zd, cchar *event); void * color_depth_thread(void *); cchar *colmess = E2X("Set color depth to 1-16 bits"); F1_help_topic = "color depth"; EFcolor_depth.menuname = menu; EFcolor_depth.menufunc = m_color_depth; EFcolor_depth.funcname = "color_depth"; EFcolor_depth.FprevReq = 1; // use preview EFcolor_depth.Farea = 2; // select area usable EFcolor_depth.FusePL = 1; // use with paint/lever edits OK EFcolor_depth.Fscript = 1; // scripting supported EFcolor_depth.threadfunc = color_depth_thread; // thread function if (! edit_setup(EFcolor_depth)) return; // setup edit E3ww = E3pxm->ww; E3hh = E3pxm->hh; /*** _____________________________ | Set Color Depth | | | | Set color depth 1-16 bits | | [___] | | | | [Done] [Cancel] | |_____________________________| ***/ zdialog *zd = zdialog_new(E2X("Set Color Depth"),Mwin,Bdone,Bcancel,null); EFcolor_depth.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lab1","hb1",colmess,"space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); zdialog_add_widget(zd,"zspin","colors","hb2","1|16|1|16","space=5"); color_depth_depth = 16; zdialog_resize(zd,200,0); zdialog_run(zd,color_depth_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int color_depth_dialog_event(zdialog *zd, cchar *event) { using namespace color_depth_names; if (strmatch(event,"focus")) return 1; if (strmatch(event,"apply")) event = "colors"; // from script if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); E3ww = E3pxm->ww; E3hh = E3pxm->hh; signal_thread(); return 1; } if (zd->zstat) { if (zd->zstat == 1) { // done edit_fullsize(); // get full size image E3ww = E3pxm->ww; E3hh = E3pxm->hh; 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",color_depth_depth); signal_thread(); } return 0; } // image color depth thread function void * color_depth_thread(void *) { using namespace color_depth_names; int ii, px, py, rgb, dist = 0; uint16 m1, m2, val1, val3; float *pix1, *pix3; float f1, f2; while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 m1 = 0xFFFF << (16 - color_depth_depth); // 5 > 1111100000000000 m2 = 0x8000 >> color_depth_depth; // 5 > 0000010000000000 for (py = 0; py < E3hh; py++) for (px = 0; px < E3ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside 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 val3 = (val1 + m2) & m1; // round: nearest value within bits if (val3 > m1) val3 = m1; if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, f2 = sa_blendfunc(dist); // blend changes over sa_blendwidth f1 = 1.0 - f2; val3 = int(f1 * val1 + f2 * val3); } pix3[rgb] = val3 / 256; } } CEF->Fmods++; CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed, stop g++ warning } /********************************************************************************/ // Shift Colors menu function // Gradually shift selected RGB colors into other colors. namespace shiftcolors_names { editfunc EFshiftcolors; // edit function data float shiftred = 0.5, shiftgreen = 0.5, shiftblue = 0.5; int E3ww, E3hh; } // menu function void m_shift_colors(GtkWidget *, cchar *menu) { using namespace shiftcolors_names; int shiftcolors_dialog_event(zdialog* zd, const char *event); void * shiftcolors_thread(void *); F1_help_topic = "shift colors"; EFshiftcolors.menuname = menu; EFshiftcolors.menufunc = m_shift_colors; EFshiftcolors.funcname = "shift_colors"; // function name EFshiftcolors.FprevReq = 1; // use preview EFshiftcolors.Farea = 2; // select area OK EFshiftcolors.Fscript = 1; // scripting supported EFshiftcolors.FusePL = 1; // paint/lever edits supported EFshiftcolors.threadfunc = shiftcolors_thread; // thread function if (! edit_setup(EFshiftcolors)) return; // setup edit E3ww = E3pxm->ww; E3hh = E3pxm->hh; /*** Shift Colors red: green =======[]======= blue green: blue ==========[]==== red blue: red ======[]======== green all: all ========[]====== all [reset] [done] [cancel] ***/ zdialog *zd = zdialog_new(E2X("Shift Colors"),Mwin,Breset,Bdone,Bcancel,null); EFshiftcolors.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"homog|space=3"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"homog|space=3"); zdialog_add_widget(zd,"vbox","vb3","hb1",0,"homog|space=3|expand"); zdialog_add_widget(zd,"vbox","vb4","hb1",0,"homog|space=3"); zdialog_add_widget(zd,"label","labr","vb1",Bred); zdialog_add_widget(zd,"label","labr","vb1",Bgreen); zdialog_add_widget(zd,"label","labr","vb1",Bblue); zdialog_add_widget(zd,"label","labr","vb1",Ball); zdialog_add_widget(zd,"label","labg","vb2",Bgreen); zdialog_add_widget(zd,"label","labb","vb2",Bblue); zdialog_add_widget(zd,"label","labr","vb2",Bred); zdialog_add_widget(zd,"label","laba","vb2",Ball); zdialog_add_widget(zd,"hscale","red","vb3","0|1|0.001|0.5"); zdialog_add_widget(zd,"hscale","green","vb3","0|1|0.001|0.5"); zdialog_add_widget(zd,"hscale","blue","vb3","0|1|0.001|0.5"); zdialog_add_widget(zd,"hscale","all","vb3","0|1|0.001|0.5"); zdialog_add_widget(zd,"label","labb","vb4",Bblue); zdialog_add_widget(zd,"label","labr","vb4",Bred); zdialog_add_widget(zd,"label","labg","vb4",Bgreen); zdialog_add_widget(zd,"label","laba","vb4",Ball); zdialog_rescale(zd,"red",0,0.5,1); zdialog_rescale(zd,"green",0,0.5,1); zdialog_rescale(zd,"blue",0,0.5,1); zdialog_rescale(zd,"all",0,0.5,1); shiftred = shiftgreen = shiftblue = 0.5; // neutral values zdialog_resize(zd,400,0); zdialog_run(zd,shiftcolors_dialog_event,"save"); // run dialog - parallel return; } // shift color dialog event and completion function int shiftcolors_dialog_event(zdialog *zd, const char *event) { using namespace shiftcolors_names; float shiftall; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"fullsize")) { // from select area if (! CEF->Fpreview) return 1; edit_fullsize(); E3ww = E3pxm->ww; E3hh = E3pxm->hh; 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 E3ww = E3pxm->ww; E3hh = E3pxm->hh; 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 (zstrstr("red green blue all blendwidth apply",event)) // trigger update thread signal_thread(); return 1; } // thread function - launch multiple working threads to update image void * shiftcolors_thread(void *) { using namespace shiftcolors_names; void * shiftcolors_wthread(void *arg); // worker thread while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 do_wthreads(shiftcolors_wthread,NWT); // worker threads CEF->Fmods++; // image modified CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed, stop warning } void * shiftcolors_wthread(void *arg) // worker thread function { using namespace shiftcolors_names; 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 < E3hh; py += NWT) // loop all image pixels for (px = 0; px < E3ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside 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_blendwidth) { // select area is active f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth 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; } pthread_exit(0); } /********************************************************************************/ // alien colors - displace hue by an angle that varies over the image namespace alien_colors_names { editfunc EFaliencolors; float BLsz, Ampl; // block size, amplitude float Frand[200][200]; // random numbers #define maxblocks 200 } // menu function void m_alien_colors(GtkWidget *, cchar *menu) { using namespace alien_colors_names; int alien_colors_dialog_event(zdialog* zd, const char *event); void * alien_colors_thread(void *); F1_help_topic = "alien colors"; EFaliencolors.menuname = menu; EFaliencolors.menufunc = m_alien_colors; EFaliencolors.funcname = "alien_colors"; EFaliencolors.Farea = 2; // select area usable EFaliencolors.FprevReq = 1; // use preview mode EFaliencolors.Fscript = 1; // scripting supported EFaliencolors.threadfunc = alien_colors_thread; if (! edit_setup(EFaliencolors)) return; /*** __________________________ | Alien Colors | | | | blocksize [___] | | amplitude [___] | | | | [done] [cancel] | |__________________________| ***/ zdialog *zd = zdialog_new(E2X("Alien Colors"),Mwin,Bdone,Bcancel,null); CEF->zd = zd; zdialog_add_widget(zd,"hbox","hbbsz","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labbsz","hbbsz",E2X("blocksize"),"space=5"); zdialog_add_widget(zd,"zspin","BLsz","hbbsz","10|1000|1|100"); zdialog_add_widget(zd,"hbox","hbamp","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labamp","hbamp",E2X("amplitude"),"space=5"); zdialog_add_widget(zd,"zspin","Ampl","hbamp","0.0|1.0|0.01|1.0"); zdialog_restore_inputs(zd); // restore previous inputs zdialog_run(zd,alien_colors_dialog_event,"save"); // run dialog - parallel zdialog_send_event(zd,"BLsz"); // initial image return; } // alien dialog event and completion function int alien_colors_dialog_event(zdialog *zd, const char *event) // alien dialog event function { using namespace alien_colors_names; if (strmatch(event,"focus")) return 1; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zstrstr("BLsz Ampl Apply",event)) { zdialog_fetch(zd,"BLsz",BLsz); // get user inputs zdialog_fetch(zd,"Ampl",Ampl); signal_thread(); // calculate } if (zd->zstat) { if (zd->zstat == 1) // [done] { int ww = E3pxm->ww; // preview size edit_fullsize(); if (E3pxm->ww > ww) { BLsz *= 1.0 * E3pxm->ww / ww; // scale up for full size signal_thread(); } edit_done(0); } else edit_cancel(0); // discard edit } return 1; } // thread function - multiple working threads to update image void * alien_colors_thread(void *) { using namespace alien_colors_names; void * alien_colors_wthread(void *); for (int ii = 0; ii < maxblocks; ii++) // populate random values for (int jj = 0; jj < maxblocks; jj++) Frand[ii][jj] = drandz(); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 do_wthreads(alien_colors_wthread,NWT); // worker threads CEF->Fmods++; // image modified CEF->Fsaved = 0; // not saved paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed, stop warning } void * alien_colors_wthread(void *arg) // working threads { using namespace alien_colors_names; int index = *((int *) (arg)); int px, py, ww, hh; int row, col, row1, col1, row2, col2; int maxrow, maxcol, bsize, edist = 0; float d1, d7, dx, dy; float W0, W1, W2, W3, W4, W5, W6, W7, W8, Wsum; float R1, G1, B1, R3, G3, B3, H, S, L; float theta, f1, f2; float *pix1, *pix3; ww = E3pxm->ww; hh = E3pxm->hh; bsize = BLsz; // block size if (ww / bsize >= maxblocks) bsize = ww / (maxblocks - 1); // stop > maxblocks rows or cols if (hh / bsize >= maxblocks) bsize = hh / (maxblocks - 1); maxrow = 1 + hh / bsize; maxcol = 1 + ww / bsize; for (py = index; py < hh; py += NWT) for (px = 0; px < ww; px++) { if (sa_stat == 3) { // select area active int ii = py * ww + px; edist = sa_pixmap[ii]; // distance from edge if (! edist) continue; // pixel outside area } d1 = py - py / bsize * bsize; // y coordinate within block d7 = px - px / bsize * bsize; // x coordinate d1 = d1 / bsize; // rescale, 0 .. 1 d7 = d7 / bsize; dx = d7 - 0.5; // distance from pixel to center dy = d1 - 0.5; // of 9 block group W0 = sqrtf(dx*dx + dy*dy); dx = d7 - 0.5; dy = d1 + 0.5; W1 = sqrtf(dx*dx + dy*dy); dx = 1.5 - d7; dy = d1 + 0.5; W2 = sqrtf(dx*dx + dy*dy); dx = 1.5 - d7; dy = 0.5 - d1; W3 = sqrtf(dx*dx + dy*dy); dx = 1.5 - d7; dy = 1.5 - d1; W4 = sqrtf(dx*dx + dy*dy); dx = 0.5 - d7; dy = 1.5 - d1; W5 = sqrtf(dx*dx + dy*dy); dx = d7 + 0.5; dy = 1.5 - d1; W6 = sqrtf(dx*dx + dy*dy); dx = d7 + 0.5; dy = 0.5 - d1; W7 = sqrtf(dx*dx + dy*dy); dx = d7 + 0.5; dy = d1 + 0.5; W8 = sqrtf(dx*dx + dy*dy); W0 = 1.5 - W0; // block weight: W1 = 1.5 - W1; // max. distance - actual W2 = 1.5 - W2; // closer blocks have more weight W3 = 1.5 - W3; W4 = 1.5 - W4; W5 = 1.5 - W5; W6 = 1.5 - W6; W7 = 1.5 - W7; W8 = 1.5 - W8; if (W2 < 0) W2 = 0; // corners may go negative if (W4 < 0) W4 = 0; if (W6 < 0) W6 = 0; if (W8 < 0) W8 = 0; Wsum = W0 + W1 + W2 + W3 + W4 + W5 + W6 + W7 + W8; // sum of weights W0 = W0 / Wsum; // normalize to make sum = 1 W1 = W1 / Wsum; W2 = W2 / Wsum; W3 = W3 / Wsum; W4 = W4 / Wsum; W5 = W5 / Wsum; W6 = W6 / Wsum; W7 = W7 / Wsum; W8 = W8 / Wsum; row = py / bsize; // block[row][col] for this px/py col = px / bsize; row1 = row2 = row; col1 = col2 = col; if (row > 0) row1 = row - 1; if (row < maxrow) row2 = row + 1; if (col > 0) col1 = col - 1; if (col < maxcol) col2 = col + 1; W0 = W0 * Frand[col][row]; // compute random for pixel from W1 = W1 * Frand[col][row1]; // block randoms * weights W2 = W2 * Frand[col2][row1]; W3 = W3 * Frand[col2][row]; W4 = W4 * Frand[col2][row2]; W5 = W5 * Frand[col][row2]; W6 = W6 * Frand[col1][row2]; W7 = W7 * Frand[col1][row]; W8 = W8 * Frand[col1][row1]; theta = W0 + W1 + W2 + W3 + W4 + W5 + W6 + W7 + W8; // hue angle change theta = theta * 2.0 * Ampl - Ampl; // scale -Ampl ... +Ampl pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel R1 = pix1[0]; G1 = pix1[1]; B1 = pix1[2]; RGBtoHSL(R1,G1,B1,H,S,L); H = H + 180 * theta; // displace H by -90 ... +90 if (H < 0) H += 360.0; else if (H >= 360.0) H -= 360.0; HSLtoRGB(H,S,L,R3,G3,B3); if (sa_stat == 3 && edist < sa_blendwidth) { // blend edges f1 = sa_blendfunc(edist); f2 = 1.0 - f1; R3 = f1 * R3 + f2 * R1; G3 = f1 * G3 + f2 * G1; B3 = f1 * B3 + f2 * B1; } pix3[0] = R3; pix3[1] = G3; pix3[2] = B3; } pthread_exit(0); // exit thread } /********************************************************************************/ // add a brightness/color curved ramp to an image, in a chosen direction namespace brite_ramp_names { editfunc EFbrite_ramp; int Fline, linex1, liney1, linex2, liney2; float A, B, C; float ex1, ey1, ex2, ey2; int E3ww, E3hh; } // menu function void m_brite_ramp(GtkWidget *, cchar *menu) { using namespace brite_ramp_names; void brite_ramp_curvedit(int spc); int brite_ramp_dialog_event(zdialog* zd, cchar *event); void * brite_ramp_thread(void *); void brite_ramp_mousefunc(); cchar *mess = E2X("Draw a line across the image in \n" "direction of brightness change."); F1_help_topic = "brite ramp"; EFbrite_ramp.menuname = menu; EFbrite_ramp.menufunc = m_brite_ramp; EFbrite_ramp.funcname = "brite_ramp"; EFbrite_ramp.FprevReq = 1; // use preview EFbrite_ramp.Fscript = 1; // scripting supported 20.0 EFbrite_ramp.Farea = 2; // select area usable EFbrite_ramp.threadfunc = brite_ramp_thread; EFbrite_ramp.mousefunc = brite_ramp_mousefunc; if (! edit_setup(EFbrite_ramp)) return; // setup edit E3ww = E3pxm->ww; E3hh = E3pxm->hh; Fline = 0; // no drawn line initially /*** _______________________________________________ | Brightness Ramp | | | | Draw a line across the image in | | direction of brightness change. | | ___________________________________________ | | | | | // 5 curves are maintained: | | | | // curve 0: current display curve | | | | // 1: curve for all colors | | curve edit area | | // 2,3,4: red, green, blue | | | | | | | | | | | | | |___________________________________________| | | (o) all (o) red (o) green (o) blue | // select curve to display | | | [reset] [Done] [Cancel] | |_______________________________________________| ***/ zdialog *zd = zdialog_new(E2X("Brightness Ramp"),Mwin,Breset,Bdone,Bcancel,null); EFbrite_ramp.zd = zd; zdialog_add_widget(zd,"label","labmess","dialog",mess); zdialog_add_widget(zd,"frame","frameH","dialog",0,"expand"); // edit-curves zdialog_add_widget(zd,"hbox","hbrgb","dialog"); // radio buttons all/red/green/blue zdialog_add_widget(zd,"radio","all","hbrgb",Ball,"space=5"); zdialog_add_widget(zd,"radio","red","hbrgb",Bred,"space=3"); zdialog_add_widget(zd,"radio","green","hbrgb",Bgreen,"space=3"); zdialog_add_widget(zd,"radio","blue","hbrgb",Bblue,"space=3"); GtkWidget *frameH = zdialog_widget(zd,"frameH"); // setup edit curves spldat *sd = splcurve_init(frameH,brite_ramp_curvedit); EFbrite_ramp.sd = sd; sd->Nscale = 1; // horizontal fixed line, neutral curve sd->xscale[0][0] = 0.01; sd->yscale[0][0] = 0.50; sd->xscale[1][0] = 0.99; sd->yscale[1][0] = 0.50; for (int ii = 0; ii < 4; ii++) // loop curves 0-3 { sd->nap[ii] = 3; // initial curves are neutral sd->vert[ii] = 0; sd->fact[ii] = 0; sd->apx[ii][0] = 0.01; sd->apx[ii][1] = 0.50; // curve 0 = overall brightness sd->apx[ii][2] = 0.99; // curve 1/2/3 = R/G/B adjustment sd->apy[ii][0] = 0.5; sd->apy[ii][1] = 0.5; sd->apy[ii][2] = 0.5; splcurve_generate(sd,ii); } sd->Nspc = 4; // 4 curves sd->fact[0] = 1; // curve 0 active zdialog_stuff(zd,"all",1); // stuff default selection, all zdialog_resize(zd,200,200); zdialog_run(zd,brite_ramp_dialog_event,"save"); // run dialog - parallel takeMouse(brite_ramp_mousefunc,dragcursor); // connect mouse return; } // dialog event and completion callback function int brite_ramp_dialog_event(zdialog *zd, cchar *event) { using namespace brite_ramp_names; void brite_ramp_mousefunc(); int ii, Fupdate = 0; spldat *sd = EFbrite_ramp.sd; if (strmatch(event,"done")) zd->zstat = 2; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 3; // cancel if (strmatch(event,"apply")) Fupdate++; // from script if (strmatch(event,"focus")) takeMouse(brite_ramp_mousefunc,dragcursor); // connect mouse if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); E3ww = E3pxm->ww; E3hh = E3pxm->hh; signal_thread(); return 1; } if (zd->zstat) { if (zd->zstat == 1) // reset { for (int ii = 0; ii < 4; ii++) { // loop curves 0-3 sd->nap[ii] = 3; // all curves are neutral sd->vert[ii] = 0; sd->fact[ii] = 0; sd->apx[ii][0] = 0.01; sd->apx[ii][1] = 0.50; sd->apx[ii][2] = 0.99; sd->apy[ii][0] = 0.5; sd->apy[ii][1] = 0.5; sd->apy[ii][2] = 0.5; splcurve_generate(sd,ii); sd->fact[ii] = 0; } sd->fact[0] = 1; gtk_widget_queue_draw(sd->drawarea); // draw curve 0 zdialog_stuff(zd,"all",1); zdialog_stuff(zd,"red",0); zdialog_stuff(zd,"green",0); zdialog_stuff(zd,"blue",0); edit_reset(); // restore initial image zd->zstat = 0; return 1; } if (zd->zstat == 2) { // done edit_fullsize(); // get full size image E3ww = E3pxm->ww; E3hh = E3pxm->hh; signal_thread(); edit_done(0); // commit edit } else edit_cancel(0); // discard edit Ntoplines = 0; Fpaint2(); return 1; } if (zstrstr("all red green blue",event)) // new choice of curve { zdialog_fetch(zd,event,ii); if (! ii) return 0; // button OFF event, wait for ON event for (ii = 0; ii < 4; ii++) sd->fact[ii] = 0; ii = strmatchV(event,"all","red","green","blue",null); ii = ii-1; // new active curve: 0, 1, 2, 3 sd->fact[ii] = 1; splcurve_generate(sd,ii); // regenerate curve gtk_widget_queue_draw(sd->drawarea); // draw curve Fupdate = 1; } if (strmatch(event,"blendwidth")) Fupdate = 1; if (Fupdate) signal_thread(); // trigger image update return 1; } // brite_ramp mouse function void brite_ramp_mousefunc() { using namespace brite_ramp_names; int mx = 0, my = 0; float d1, d2; if (! (LMclick || RMclick || Mxdrag || Mydrag)) return; // ignore mouse movement if (LMclick || RMclick) { // left or right mouse click mx = Mxclick; my = Myclick; LMclick = RMclick = 0; } if (Mxdrag || Mydrag) { // mouse drag mx = Mxdrag; my = Mydrag; Mxdrag = Mydrag = 0; } if (! Fline && (mx || my)) // 19.0 { Fline = 1; linex1 = mx; // draw arbitrary line to start with liney1 = my; linex2 = mx + 100; liney2 = my + 100; } else // move nearest line end point to mouse { d1 = (linex1 - mx) * (linex1 - mx) + (liney1 - my) * (liney1 - my); d2 = (linex2 - mx) * (linex2 - mx) + (liney2 - my) * (liney2 - my); if (d1 < d2) { linex1 = mx; liney1 = my; } else { linex2 = mx; liney2 = my; } } Ntoplines = 1; // update line data toplines[0].x1 = linex1; toplines[0].y1 = liney1; toplines[0].x2 = linex2; toplines[0].y2 = liney2; toplines[0].type = 1; signal_thread(); // update image return; } // this function is called when a curve is edited void brite_ramp_curvedit(int spc) { signal_thread(); return; } // brite_ramp thread function void * brite_ramp_thread(void *arg) { using namespace brite_ramp_names; void brite_ramp_equation(); void brite_ramp_rampline(); void * brite_ramp_wthread(void *); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 ex1 = linex1; // ramp line end points ey1 = liney1; ex2 = linex2; ey2 = liney2; brite_ramp_equation(); // compute line equation brite_ramp_rampline(); // compute new end points do_wthreads(brite_ramp_wthread,NWT); // worker threads CEF->Fmods++; // image3 modified CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed, stop g++ warning } void * brite_ramp_wthread(void *arg) // worker thread function { using namespace brite_ramp_names; void brite_ramp_posn(int px, int py, float &rx, float &ry); int index = *((int *) arg); int ii, dist = 0, px3, py3; float x3, y3; float d1, d2, rampval; float *pix1, *pix3; float red1, green1, blue1, maxrgb; float red3, green3, blue3; float Fall, Fred, Fgreen, Fblue; float dold, dnew; spldat *sd = EFbrite_ramp.sd; for (py3 = index; py3 < E3hh; py3 += NWT) // loop output pixels for (px3 = 0; px3 < E3ww; px3++) { if (sa_stat == 3) { // select area active ii = py3 * E3ww + px3; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } brite_ramp_posn(px3,py3,x3,y3); // nearest point on ramp line d1 = sqrtf((x3-ex1) * (x3-ex1) + (y3-ey1) * (y3-ey1)); // compute ramp value d2 = sqrtf((x3-ex2) * (x3-ex2) + (y3-ey2) * (y3-ey2)); rampval = d1 / (d1 + d2); // 0.0 ... 1.0 ii = 999.0 * rampval; // corresp. curve index 0-999 Fall = sd->yval[0][ii] * 2.0; // curve values 0.0 - 1.0 Fred = sd->yval[1][ii] * 2.0; // (0.5 is neutral value) Fgreen = sd->yval[2][ii] * 2.0; Fblue = sd->yval[3][ii] * 2.0; pix1 = PXMpix(E1pxm,px3,py3); // input pixel red1 = pix1[0]; green1 = pix1[1]; blue1 = pix1[2]; red3 = red1 * Fall; // curve "all" adjustment green3 = green1 * Fall; // projected on each RGB color blue3 = blue1 * Fall; red3 = red3 * Fred; // add additional RGB adjustments green3 = green3 * Fgreen; blue3 = blue3 * Fblue; maxrgb = red3; // stop overflows if (green3 > maxrgb) maxrgb = green3; if (blue3 > maxrgb) maxrgb = blue3; if (maxrgb > 255.9) { red3 = red3 * 255.9 / maxrgb; green3 = green3 * 255.9 / maxrgb; blue3 = blue3 * 255.9 / maxrgb; } if (sa_stat == 3 && dist < sa_blendwidth) { // blend changes over blendwidth dnew = sa_blendfunc(dist); dold = 1.0 - dnew; red3 = dnew * red3 + dold * red1; green3 = dnew * green3 + dold * green1; blue3 = dnew * blue3 + dold * blue1; } pix3 = PXMpix(E3pxm,px3,py3); // output pixel pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } pthread_exit(0); } // get equation of ramp line in the form Ax + By + C = 0 // end points are (ex1,ey1) and (ex2,ey2) void brite_ramp_equation() { using namespace brite_ramp_names; if (ex1 != ex2) { A = (ey2 - ey1) / (ex2 - ex1); B = -1; C = ey1 - A * ex1; } else { A = 1; B = 0; C = -ex1; } return; } // compute nearest point on ramp line for given image pixel position void brite_ramp_posn(int px, int py, float &rx, float &ry) { using namespace brite_ramp_names; float F1, F2; F1 = B * px - A * py; F2 = A * A + B * B; rx = (B * F1 - A * C) / F2; ry = (-A * F1 - B * C) / F2; return; } // extend ramp line end points long enough for entire image void brite_ramp_rampline() { using namespace brite_ramp_names; void brite_ramp_posn(int px, int py, float &rx, float &ry); float rx, ry, d1, d2; if (B == 0) { // vertical line ey1 = 0; ey2 = E3hh - 1; return; } if (A == 0) { // horizontal line ex1 = 0; ex2 = E3ww - 1; return; } brite_ramp_posn(0,0,rx,ry); if (rx < 0 || ry < 0) { d1 = (rx - ex1) * (rx - ex1) + (ry - ey1) * (ry - ey1); d2 = (rx - ex2) * (rx - ex2) + (ry - ey2) * (ry - ey2); if (d1 < d2) { ex1 = rx; ey1 = ry; } else { ex2 = rx; ey2 = ry; } } brite_ramp_posn(E3ww,0,rx,ry); if (rx > E3ww || ry < 0) { d1 = (rx - ex1) * (rx - ex1) + (ry - ey1) * (ry - ey1); d2 = (rx - ex2) * (rx - ex2) + (ry - ey2) * (ry - ey2); if (d1 < d2) { ex1 = rx; ey1 = ry; } else { ex2 = rx; ey2 = ry; } } brite_ramp_posn(E3ww,E3hh,rx,ry); if (rx > E3ww || ry > E3hh) { d1 = (rx - ex1) * (rx - ex1) + (ry - ey1) * (ry - ey1); d2 = (rx - ex2) * (rx - ex2) + (ry - ey2) * (ry - ey2); if (d1 < d2) { ex1 = rx; ey1 = ry; } else { ex2 = rx; ey2 = ry; } } brite_ramp_posn(0,E3hh,rx,ry); if (rx < 0 || ry > E3hh) { d1 = (rx - ex1) * (rx - ex1) + (ry - ey1) * (ry - ey1); d2 = (rx - ex2) * (rx - ex2) + (ry - ey2) * (ry - ey2); if (d1 < d2) { ex1 = rx; ey1 = ry; } else { ex2 = rx; ey2 = ry; } } return; } /********************************************************************************/ // Paint transparency function - paint pixel alpha channel with the mouse. namespace paint_transp_names { int mode = 1; // 1/2 = more/less transparency int Fgrad = 1; // gradual or instant transparency int Mradius; // mouse radius float kernel[400][400]; // radius <= 199 int E3ww, E3hh; editfunc EFpaintransp; } // menu function void m_paint_transp(GtkWidget *, cchar *) { using namespace paint_transp_names; int paint_transp_dialog_event(zdialog* zd, cchar *event); void paint_transp_mousefunc(); cchar *mess1 = E2X("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 = paint_transp_mousefunc; // mouse function if (! edit_setup(EFpaintransp)) return; // setup edit E3ww = E3pxm->ww; E3hh = E3pxm->hh; 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(E2X("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",E2X("paintbrush radius")); zdialog_add_widget(zd,"label","labsc","vbbr1",E2X("strength center")); zdialog_add_widget(zd,"label","labse","vbbr1",E2X("strength edge")); zdialog_add_widget(zd,"zspin","radius","vbbr2","1|199|1|30"); zdialog_add_widget(zd,"zspin","stcent","vbbr2","0|100|1|95"); zdialog_add_widget(zd,"zspin","stedge","vbbr2","0|100|1|100"); zdialog_add_widget(zd,"hbox","hb4","dialog",0,"space=3"); zdialog_add_widget(zd,"check","Fgrad","hb4",E2X("gradual paint"),"space=5"); zdialog_stuff(zd,"Fgrad",1); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,paint_transp_dialog_event,"save"); // run dialog, parallel zdialog_send_event(zd,"radius"); // get kernel initialized zdialog_fetch(zd,"Fgrad",Fgrad); // instant/gradual paint mode = 1; // start with paint mode takeMouse(paint_transp_mousefunc,drawcursor); // connect mouse function return; } // dialog event and completion callback function int paint_transp_dialog_event(zdialog *zd, cchar *event) { using namespace paint_transp_names; void paint_transp_mousefunc(); int radius, dx, dy; float rad, kern, stcent, stedge; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(paint_transp_mousefunc,drawcursor); if (zstrstr("radius stcent stedge",event)) // get new brush attributes { zdialog_fetch(zd,"radius",Mradius); // mouse radius zdialog_fetch(zd,"stcent",stcent); // center transparency zdialog_fetch(zd,"stedge",stedge); // edge transparency stcent = 0.01 * stcent; // scale 0 ... 1 stedge = 0.01 * stedge; radius = Mradius; for (dy = -radius; dy <= radius; dy++) // build kernel for (dx = -radius; dx <= radius; dx++) { rad = sqrt(dx*dx + dy*dy); kern = (radius - rad) / radius; // center ... edge >> 1 ... 0 kern = kern * (stcent - stedge) + stedge; // strength center ... edge if (kern < 0) kern = 0; if (kern > 1) kern = 1; if (rad > radius) kern = 2; // beyond radius, within square kernel[dx+radius][dy+radius] = kern; } } if (strmatch(event,"Fgrad")) // flag, gradual overpaints zdialog_fetch(zd,"Fgrad",Fgrad); return 1; } // pixel paint mouse function void paint_transp_mousefunc() { using namespace paint_transp_names; void paint_transp_dopixels(int px, int py); 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; paint_transp_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; } paint_transp_dopixels(px,py); // do 1 block of pixels LMclick = RMclick = Mxdrag = Mydrag = 0; return; } LMclick = RMclick = Mxdrag = Mydrag = 0; draw_mousecircle(Mxposn,Myposn,Mradius,0,0); // draw mouse circle return; } // paint or erase 1 block of pixels within mouse radius of px, py void paint_transp_dopixels(int px, int py) { using namespace paint_transp_names; float *pix3; int radius, dx, dy, qx, qy; int ii, rr, dist = 0; float kern; cairo_t *cr = draw_context_create(gdkwin,draw_context); 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 > E3ww-1) continue; if (qy < 0 || qy > E3hh-1) continue; if (sa_stat == 3) { // select area active ii = qy * E3ww + 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_blendwidth) // within blend distance, kern = kern * sa_blendfunc(dist); // kern = kern ... 0 at edge pix3 = PXMpix(E3pxm,qx,qy); // edited image pixel if (mode == 1) { // add transparency if (Fgrad) { pix3[3] -= 2 * kern; // accumulate transparency if (pix3[3] < 0) pix3[3] = 0; } else pix3[3] = 0; // instant } if (mode == 2) { // add opacity if (Fgrad) { pix3[3] += 2 * kern; // accumulate if (pix3[3] > 255.0) pix3[3] = 255.0; } else pix3[3] = 255.0; // instant } } CEF->Fmods++; CEF->Fsaved = 0; px = px - radius - 1; // repaint modified area py = py - radius - 1; rr = 2 * radius + 3; Fpaint3(px,py,rr,rr,cr); draw_mousecircle(Mxposn,Myposn,Mradius,0,cr); // draw mouse circle draw_context_destroy(draw_context); return; } /********************************************************************************/ // mirror an image horizontally or vertically editfunc EFmirror; void m_mirror(GtkWidget *, cchar *menu) { int mirror_dialog_event(zdialog *zd, cchar *event); F1_help_topic = "mirror"; EFmirror.menuname = menu; EFmirror.menufunc = m_mirror; EFmirror.funcname = "mirror"; EFmirror.Fscript = 1; // allow scripting EFmirror.Frestart = 1; // allow restart if (! edit_setup(EFmirror)) return; // setup edit /*** _____________________________ | Mirror Image | | | | [] horizontal [] vertical | | | | [Done] [Cancel] | |_____________________________| ***/ zdialog *zd = zdialog_new(E2X("Mirror Image"),Mwin,Bdone,Bcancel,null); EFmirror.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"check","horz","hb1",E2X("horizontal"),"space=5"); zdialog_add_widget(zd,"check","vert","hb1",E2X("vertical"),"space=5"); zdialog_run(zd,mirror_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int mirror_dialog_event(zdialog *zd, cchar *event) { int mirror_horz(void); int mirror_vert(void); int nn; if (strmatch(event,"focus")) return 1; if (strmatch(event,"done")) zd->zstat = 1; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 2; // cancel if (zd->zstat) // dialog complete { if (zd->zstat == 1) edit_done(0); else edit_cancel(0); return 1; } if (strmatch(event,"apply")) { // from script zdialog_fetch(zd,"horz",nn); if (nn) mirror_horz(); zdialog_fetch(zd,"vert",nn); if (nn) mirror_vert(); } if (strmatch(event,"horz")) mirror_horz(); if (strmatch(event,"vert")) mirror_vert(); return 1; } int mirror_horz() { int px, py; float *pix3, *pix9; int nc = E3pxm->nc, pcc = nc * sizeof(float); E9pxm = PXM_copy(E3pxm); for (py = 0; py < E3pxm->hh; py++) for (px = 0; px < E3pxm->ww; px++) { pix3 = PXMpix(E3pxm,px,py); // image9 = mirrored image3 pix9 = PXMpix(E9pxm,E3pxm->ww-1-px,py); memcpy(pix9,pix3,pcc); } PXM_free(E3pxm); // image9 >> image3 E3pxm = E9pxm; E9pxm = 0; CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); return 0; } int mirror_vert() { int px, py; float *pix3, *pix9; int nc = E3pxm->nc, pcc = nc * sizeof(float); E9pxm = PXM_copy(E3pxm); for (py = 0; py < E3pxm->hh; py++) for (px = 0; px < E3pxm->ww; px++) { pix3 = PXMpix(E3pxm,px,py); // image9 = mirrored image3 pix9 = PXMpix(E9pxm,px,E3pxm->hh-1-py); memcpy(pix9,pix3,pcc); } PXM_free(E3pxm); // image9 >> image3 E3pxm = E9pxm; E9pxm = 0; CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); return 0; } /********************************************************************************/ // process image using a custom kernel namespace anykernel_names { int ww, hh; // image dimensions int kernsize = 5; // kernel size, N x N float fmpyer = 1.0; // multiplier int fadder = 0; // adder int kernel[15][15]; // up to 15 x 15 editfunc EFanykernel; } void m_anykernel(GtkWidget *, cchar *menu) // menu function { using namespace anykernel_names; int anykernel_make_dialog(void); void * anykernel_thread(void *); F1_help_topic = "custom kernel"; EFanykernel.menuname = menu; EFanykernel.menufunc = m_anykernel; EFanykernel.funcname = "custom_kernel"; EFanykernel.Farea = 2; // select area usable EFanykernel.Fscript = 1; // scripting supported EFanykernel.threadfunc = anykernel_thread; // thread function EFanykernel.Frestart = 1; // allow restart if (! edit_setup(EFanykernel)) return; // setup edit ww = E3pxm->ww; // image dimensions hh = E3pxm->hh; anykernel_make_dialog(); return; } // build the input dialog with the requested array size int anykernel_make_dialog() { using namespace anykernel_names; int anykernel_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int row, col; char rowname[16], cellname[30]; /*** __________________________________________ | Custom Kernel | | | | Kernel size [___] | | | | [____] [____] [____] [____] [____] ... | | [____] [____] [____] [____] [____] ... | | [____] [____] [____] [____] [____] ... | | [____] [____] [____] [____] [____] ... | | [____] [____] [____] [____] [____] ... | | [____] [____] [____] [____] [____] ... | | ... ... ... ... ... | | | | multiply [____] add [____] | | | | Data file [Load] [Save] | | | | [Reset] [Apply] [Done] [Cancel] | |__________________________________________| ***/ zd = zdialog_new(E2X("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",E2X("Kernel size"),"space=3"); zdialog_add_widget(zd,"zspin","kernsize","hbkern","3|15|2|5","size=3"); zdialog_stuff(zd,"kernsize",kernsize); for (row = 1; row <= kernsize; row++) // make entry cell matrix { snprintf(rowname,16,"row%02d",row); zdialog_add_widget(zd,"hbox",rowname,"dialog","space=5"); for (col = 1; col <= kernsize; col++) { snprintf(cellname,30,"cell%02d%02d",col,row); zdialog_add_widget(zd,"zspin",cellname,rowname,"-99|+99|1|0","size=4|space=4"); } } zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=5"); // multiplier and adder zdialog_add_widget(zd,"label","labmul","hbf",E2X("multiply"),"space=3"); zdialog_add_widget(zd,"zspin","fmul","hbf","0|99|0.01|1","size=5"); zdialog_add_widget(zd,"label","space","hbf",0,"space=3"); zdialog_add_widget(zd,"label","labadd","hbf",E2X("add"),"space=3"); zdialog_add_widget(zd,"zspin","fadd","hbf","-999|+999|1|0","size=5"); zdialog_add_widget(zd,"hbox","hbfile","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labfile","hbfile",E2X("Data file"),"space=3"); zdialog_add_widget(zd,"button","load","hbfile",Bload,"space=3"); zdialog_add_widget(zd,"button","save","hbfile",Bsave,"space=3"); zdialog_run(zd,anykernel_dialog_event,"save"); // run dialog - parallel return 1; } // dialog event and completion callback function int anykernel_dialog_event(zdialog *zd, cchar *event) { using namespace anykernel_names; int row, col, err, nn, kernsize2; float value; char cellname[20]; char *filename, dirname[200]; FILE *fid; cchar *mess = E2X("Load settings from file"); if (strmatch(event,"focus")) return 1; if (strmatch(event,"apply")) zd->zstat = 2; // from script if (strmatch(event,"done")) zd->zstat = 3; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 4; // from f_open() if (zd->zstat) { if (zd->zstat == 1) // reset { zd->zstat = 0; // keep dialog active edit_reset(); return 1; } if (zd->zstat == 2) // apply { zd->zstat = 0; // keep dialog active for (row = 1; row <= kernsize; row++) // get kernel values from dialog for (col = 1; col <= kernsize; col++) { snprintf(cellname,20,"cell%02d%02d",col,row); zdialog_fetch(zd,cellname,value); kernel[row-1][col-1] = value; } zdialog_fetch(zd,"fmul",fmpyer); zdialog_fetch(zd,"fadd",fadder); signal_thread(); // start thread return 1; } if (zd->zstat == 3) // done - commit edit { func_save_last_widgets(zd,null,"custom_kernel"); edit_done(0); } else edit_cancel(0); // cancel - discard edit return 1; } if (strmatch(event,"kernsize")) // change kernel size { zdialog_fetch(zd,"kernsize",kernsize2); if (kernsize2 == kernsize) return 1; kernsize = kernsize2; zdialog_destroy(zd); anykernel_make_dialog(); return 1; } if (strmatch(event,"save")) // save kernel data to a file func_save_widgets(zd,null,"custom_kernel",null); if (strmatch(event,"load")) // load kernel data from a file { snprintf(dirname,200,"%s/%s",get_zhomedir(),CEF->funcname); // folder for data files filename = zgetfile(mess,MWIN,"file",dirname,0); // open data file if (! filename) return 1; // user cancel fid = fopen(filename,"r"); // open file if (! fid) { zmessageACK(Mwin,"%s \n %s",filename,strerror(errno)); zfree(filename); return 1; } nn = fscanf(fid,"kernsize == %d",&kernsize2); // read 1st record, "kernsize == N" fclose(fid); if (nn != 1) { zmessageACK(Mwin,"file format error: \n %s",filename); zfree(filename); return 1; } if (kernsize2 != kernsize) { // change kernel size to match file zdialog_destroy(zd); kernsize = kernsize2; anykernel_make_dialog(); zd = EFanykernel.zd; // changed 20.0 } fid = fopen(filename,"r"); // re-open file and load kernel data err = func_load_widgets(zd,null,"custom_kernel",fid); if (err) zmessageACK(Mwin,"file format error: \n %s",filename); fclose(fid); 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 while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 E9pxm = PXM_copy(E3pxm); // [apply] accumulates do_wthreads(anykernel_wthread,NWT); // worker threads PXM_free(E9pxm); CEF->Fmods++; CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.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 (fmpyer != 1.0) { // apply multiplier red = fmpyer * red; green = fmpyer * green; blue = fmpyer * blue; } if (fadder != 0) { // apply adder red = red + fadder; green = green + fadder; blue = blue + fadder; } 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_blendwidth > 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_blendwidth) continue; // omit if > blendwidth from edge pix1 = PXMpix(E1pxm,px,py); // source pixel pix3 = PXMpix(E3pxm,px,py); // target pixel f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth 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]; } } pthread_exit(0); } fotoxx-20.08/f.enhance.cc000066400000000000000000013151561362435004500152240ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* Fotoxx image edit - Enhance menu functions m_voodoo1 automatic image enhancement with limited smarts m_voodoo2 automatic image enhancement with limited smarts m_brite_dist edit brightness distribution, rebalance bright/dark areas m_gradients magnify brightness gradients to enhance details m_flatten flatten brightness distribution m_gretinex rescale pixel RGB brightness range - for global image m_zretinex rescale pixel RGB brightness range - for image zones m_sharpen sharpen an image m_blur blur an image m_blur_background blur background (called via m_blur()) m_denoise remove noise from an image m_redeyes remove red-eyes from flash photos m_match_colors adjust image colors to match those in a chosen image m_smart_erase replace pixels inside selected areas with background m_chromatic1 fix lateral CA (color bands increasing with radius) m_chromatic2 fix axial CA (color bands on dark/bright edges) m_vignette highlight selected image region m_remove_dust remove dust specs from an image *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // 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 1"; EFvoodoo1.menuname = menu; EFvoodoo1.menufunc = m_voodoo1; EFvoodoo1.funcname = "voodoo1"; EFvoodoo1.Farea = 1; // select area ignored EFvoodoo1.Fscript = 1; // scripting supported EFvoodoo1.threadfunc = voodoo1_thread; if (! edit_setup(EFvoodoo1)) return; // setup edit voodoo1_distribution(); // compute brightness distribution signal_thread(); // start update thread edit_done(0); // edit done return; } // compute brightness distribution for image void voodoo1_distribution() { int px, py, ii; int ww = E1pxm->ww, hh = E1pxm->hh; float bright1; float *pix1; for (ii = 0; ii < 256; ii++) // clear brightness distribution data voodoo1_brdist[ii] = 0; for (py = 0; py < hh; py++) // compute brightness distribution for (px = 0; px < ww; px++) { pix1 = PXMpix(E1pxm,px,py); bright1 = pixbright(pix1); voodoo1_brdist[int(bright1)]++; } for (ii = 1; ii < 256; ii++) // cumulative brightness distribution voodoo1_brdist[ii] += voodoo1_brdist[ii-1]; // 0 ... (ww * hh) voodoo_01 = 0; voodoo_99 = 255; for (ii = 0; ii < 256; ii++) // compute index values (0 - 255) { // for darkest 1% and brightest 1% if (voodoo1_brdist[ii] < 0.01 * ww * hh) voodoo_01 = ii; if (voodoo1_brdist[ii] < 0.99 * ww * hh) voodoo_99 = ii; } for (ii = 0; ii < 256; ii++) voodoo1_brdist[ii] = voodoo1_brdist[ii] // multiplier per brightness level / (ww * hh) * 256.0 / (ii + 1); // ( = 1 for a flat distribution) return; } // thread function - use multiple working threads void * voodoo1_thread(void *) { void * voodoo1_wthread(void *arg); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 do_wthreads(voodoo1_wthread,NWT); CEF->Fmods++; CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 } 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; } pthread_exit(0); } /********************************************************************************/ // flatten brightness distribution based on the distribution of nearby zones namespace voodoo2_names { float flatten = 30; // flatten value, 30% float deband = 70; // deband value, 70% int Iww, Ihh; // image dimensions int NZ; // no. of image zones int Zsize, Zrows, Zcols; // zone parameters float *Br; // Br[py][px] pixel brightness int *Zxlo, *Zylo, *Zxhi, *Zyhi; // Zxlo[ii] etc. zone ii pixel range int *Zcen; // Zcen[ii][2] zone ii center (py,px) uint8 *Zn; // Zn[py][px][9] 9 nearest zones for pixel: 0-200 (255 = none) uint8 *Zw; // Zw[py][px][9] zone weights for pixel: 0-100 = 1.0 float *Zff; // Zff[ii][1000] zone ii flatten factors editfunc EFvoodoo2; // edit function void * wthread(void *); // working thread } void m_voodoo2(GtkWidget *, cchar *menu) { using namespace voodoo2_names; int gNZ, pNZ; int px, py, cx, cy; int rx, ii, jj, kk; int zww, zhh, row, col; float *pix1, bright; float weight[9], sumweight; float D, Dthresh; F1_help_topic = "voodoo 2"; EFvoodoo2.menuname = menu; EFvoodoo2.menufunc = m_voodoo2; EFvoodoo2.funcname = "voodoo2"; EFvoodoo2.Farea = 1; // select area ignored EFvoodoo2.Fscript = 1; // scripting supported if (! edit_setup(EFvoodoo2)) return; // setup edit Iww = E1pxm->ww; // image dimensions Ihh = E1pxm->hh; if (Iww * Ihh > wwhh_limit2) { zmessageACK(Mwin,"image too big"); edit_cancel(0); return; } Ffuncbusy = 1; NZ = 40; // zone count gNZ = pNZ = NZ; // zone count goal while (true) { Zsize = sqrtf(Iww * Ihh / gNZ); // approx. zone size Zrows = Ihh / Zsize + 0.5; // get appropriate rows and cols Zcols = Iww / Zsize + 0.5; NZ = Zrows * Zcols; // NZ matching rows x cols if (NZ >= pNZ) break; gNZ++; } Br = (float *) zmalloc(Iww * Ihh * sizeof(float)); // allocate memory Zn = (uint8 *) zmalloc(Iww * Ihh * 9 * sizeof(uint8)); // cannot exceed 2048 M Zw = (uint8 *) zmalloc(Iww * Ihh * 9 * sizeof(uint8)); Zxlo = (int *) zmalloc(NZ * sizeof(int)); Zylo = (int *) zmalloc(NZ * sizeof(int)); Zxhi = (int *) zmalloc(NZ * sizeof(int)); Zyhi = (int *) zmalloc(NZ * sizeof(int)); Zcen = (int *) zmalloc(NZ * 2 * sizeof(int)); Zff = (float *) zmalloc(NZ * 1000 * sizeof(float)); for (py = 0; py < Ihh; py++) // get initial pixel brightness levels for (px = 0; px < Iww; px++) { pix1 = PXMpix(E1pxm,px,py); bright = pixbright(pix1); ii = py * Iww + px; Br[ii] = bright; zmainloop(1000); // keep GTK alive } zww = Iww / Zcols; // actual zone size zhh = Ihh / Zrows; for (row = 0; row < Zrows; row++) for (col = 0; col < Zcols; col++) // set low/high bounds per zone { ii = row * Zcols + col; Zxlo[ii] = col * zww; Zylo[ii] = row * zhh; Zxhi[ii] = Zxlo[ii] + zww; Zyhi[ii] = Zylo[ii] + zhh; Zcen[2*ii] = Zylo[ii] + zhh/2; Zcen[2*ii+1] = Zxlo[ii] + zww/2; } for (ii = 0; ii < NZ; ii++) // compute brightness distributiion { // for each zone for (jj = 0; jj < 1000; jj++) Zff[1000*ii+jj] = 0; for (py = Zylo[ii]; py < Zyhi[ii]; py++) // brightness distribution for (px = Zxlo[ii]; px < Zxhi[ii]; px++) { pix1 = PXMpix(E1pxm,px,py); bright = 1000.0 / 256.0 * pixbright(pix1); Zff[1000*ii+int(bright)]++; } for (jj = 1; jj < 1000; jj++) // cumulative brightness distribution Zff[1000*ii+jj] += Zff[1000*ii+jj-1]; for (jj = 0; jj < 1000; jj++) // multiplier per brightness level Zff[1000*ii+jj] = Zff[1000*ii+jj] / (zww*zhh) * 1000 / (jj+1); // to make distribution flat zmainloop(1000); // keep GTK alive } for (py = 0; py < Ihh; py++) // set 9 nearest zones per pixel for (px = 0; px < Iww; px++) { rx = (py * Iww + px) * 9; // index for 9 nearest zones to px/py row = py / zhh; // zone containing pixel col = px / zww; ii = 0; for (jj = row-1; jj <= row+1; jj++) // loop 3x3 surrounding zones for (kk = col-1; kk <= col+1; kk++) { if (jj >= 0 && jj < Zrows && kk >= 0 && kk < Zcols) // zone is not off the edge Zn[rx+ii] = jj * Zcols + kk; else Zn[rx+ii] = 255; // edge row/col: missing neighbor ii++; } } if (zww < zhh) // pixel to zone distance threshold Dthresh = 1.5 * zww; // area influence = 0 beyond this distance else Dthresh = 1.5 * zhh; for (py = 0; py < Ihh; py++) // set zone weights per pixel for (px = 0; px < Iww; px++) { rx = (py * Iww + px) * 9; // index for 9 nearest zones to px/py for (ii = 0; ii < 9; ii++) // distance to each zone center { jj = Zn[rx+ii]; if (jj == 255) { weight[ii] = 0; // missing zone weight = 0 continue; } cy = Zcen[2*jj]; cx = Zcen[2*jj+1]; D = sqrtf((py-cy)*(py-cy) + (px-cx)*(px-cx)); // distance from pixel to zone center D = D / Dthresh; if (D > 1) D = 1; // zone influence reaches zero at Dthresh weight[ii] = 1.0 - D; } sumweight = 0; for (ii = 0; ii < 9; ii++) // zone weights based on distance sumweight += weight[ii]; for (ii = 0; ii < 9; ii++) // normalize weights, sum = 1.0 Zw[rx+ii] = 100 * weight[ii] / sumweight; // 0-100 = 1.0 zmainloop(1000); // keep GTK alive } do_wthreads(wthread,NWT); 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; } pthread_exit(0); } /********************************************************************************/ // edit brightness distribution namespace brite_dist_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_brite_dist(GtkWidget *, cchar *menu) { using namespace brite_dist_names; cchar *title = E2X("Adjust Brightness Distribution"); F1_help_topic = "brite dist"; EFbrightdist.menuname = menu; EFbrightdist.menufunc = m_brite_dist; EFbrightdist.funcname = "brightness"; EFbrightdist.FprevReq = 1; // preview EFbrightdist.Farea = 2; // select area usable EFbrightdist.Frestart = 1; // restart allowed EFbrightdist.Fscript = 1; // scripting supported EFbrightdist.threadfunc = thread; if (! edit_setup(EFbrightdist)) return; // setup edit /*** __________________________________________ | Adjust Brightness Distribution | | | | Low Cutoff ==========[]============== | | High Cutoff ==============[]========== | | Low Flatten =========[]=============== | | Mid Flatten ============[]============ | | High Flatten ==============[]========== | | Low Stretch ================[]======== | | Mid Stretch =============[]=========== | | High Stretch =========[]=============== | | | | [Reset] [Done] [Cancel] | |__________________________________________| ***/ zdialog *zd = zdialog_new(title,Mwin,Breset,Bdone,Bcancel,null); EFbrightdist.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"homog|space=3"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"homog|expand"); zdialog_add_widget(zd,"label","labLC","vb1",E2X("Low Cutoff")); zdialog_add_widget(zd,"label","labHC","vb1",E2X("High Cutoff")); zdialog_add_widget(zd,"label","labLF","vb1",E2X("Low Flatten")); zdialog_add_widget(zd,"label","labMF","vb1",E2X("Mid Flatten")); zdialog_add_widget(zd,"label","labHF","vb1",E2X("High Flatten")); zdialog_add_widget(zd,"label","labLS","vb1",E2X("Low Stretch")); zdialog_add_widget(zd,"label","labMS","vb1",E2X("Mid Stretch")); zdialog_add_widget(zd,"label","labHS","vb1",E2X("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_RGB_dist(0,0); // popup brightness distribution LC = HC = LF = MF = HF = LS = MS = HS = 0.0; compute_BB(); return; } // dialog event and completion function int brite_dist_names::dialog_event(zdialog *zd, cchar *event) { using namespace brite_dist_names; if (strmatch(event,"done")) zd->zstat = 2; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 3; // cancel if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); // get full size image compute_BB(); signal_thread(); return 1; } if (zd->zstat) { if (zd->zstat == 1) { // reset zd->zstat = 0; // keep dialog active LC = HC = LF = MF = HF = LS = MS = HS = 0.0; zdialog_stuff(zd,"LC",LC); zdialog_stuff(zd,"HC",HC); zdialog_stuff(zd,"LF",LF); zdialog_stuff(zd,"MF",MF); zdialog_stuff(zd,"HF",HF); zdialog_stuff(zd,"LS",LS); zdialog_stuff(zd,"MS",MS); zdialog_stuff(zd,"HS",HS); compute_BB(); edit_reset(); // 19.0 } else if (zd->zstat == 2 && CEF->Fmods) { // done edit_fullsize(); // get full size image compute_BB(); signal_thread(); m_RGB_dist(0,"kill"); // kill RGB distribution graph edit_done(0); // commit edit } else { edit_cancel(0); // cancel - discard edit m_RGB_dist(0,"kill"); // kill RGB distribution graph } return 1; // 19.0 } if (strmatch(event,"blendwidth")) // select area blendwidth change signal_thread(); if (zstrstr("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 brite_dist_names::compute_BB() { using namespace brite_dist_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 * brite_dist_names::thread(void *) { using namespace brite_dist_names; while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 do_wthreads(wthread,NWT); paintlock(0); // unblock window paint 19.0 CEF->Fmods++; // image modified, not saved CEF->Fsaved = 0; } return 0; // not executed, stop g++ warning } // worker thread for each CPU processor core void * brite_dist_names::wthread(void *arg) { using namespace brite_dist_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_blendwidth) { // select area is active, dnew = sa_blendfunc(dist); // blend edge dold = 1.0 - dnew; red3 = dnew * red3 + dold * red1; green3 = dnew * green3 + dold * green1; blue3 = dnew * blue3 + dold * blue1; } cmax = red3; // stop overflow, keep color balance if (green3 > cmax) cmax = green3; if (blue3 > cmax) cmax = blue3; if (cmax > 255.9) { cmax = 255.9 / cmax; red3 = red3 * cmax; green3 = green3 * cmax; blue3 = blue3 * cmax; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } pthread_exit(0); } /******************************************************************************** Magnify Gradients function enhance detail by magnifying brightness gradients methodology: get brightness gradients for each pixel in 4 directions: SE SW NE NW amplify gradients using the edit curve: new gradient = F(old gradient) integrate 4 new brightness surfaces from the amplified gradients: - pixel brightness = prior pixel brightness + amplified gradient - the Amplify control varies amplification from zero to maximum new pixel brightness = average from 4 calculated brightness surfaces *********************************************************************************/ namespace gradients_names { float *brmap1, *brmap2; float *brmap4[4]; int contrast99; float amplify; int ww, hh; editfunc EFgradients; void gradients_initz(zdialog *zd); int gradients_dialog_event(zdialog *zd, cchar *event); void gradients_curvedit(int); void * gradients_thread(void *); void * gradients_wthread1(void *arg); void * gradients_wthread2(void *arg); void * gradients_wthread3(void *arg); } // menu function void m_gradients(GtkWidget *, cchar *menu) { using namespace gradients_names; F1_help_topic = "gradients"; cchar *title = E2X("Magnify Gradients"); EFgradients.menuname = menu; EFgradients.menufunc = m_gradients; EFgradients.funcname = "gradients"; EFgradients.Farea = 2; // select area usable EFgradients.Frestart = 1; // restart allowed EFgradients.FusePL = 1; // use with paint/lever edits OK EFgradients.Fscript = 1; // scripting supported EFgradients.threadfunc = gradients_thread; if (! edit_setup(EFgradients)) return; // setup: no preview, select area OK /*** ________________________________ | Magnify Gradients | | ____________________________ | | | | | | | | | | | curve drawing area | | | | | | | |____________________________| | | low contrast high | | | | Amplify ========[]========== | | | | Curve File: [Open] [Save] | | | | [Done] [Cancel] | |________________________________| ***/ zdialog *zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); EFgradients.zd = zd; zdialog_add_widget(zd,"frame","frame","dialog",0,"expand"); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labcL","hb1",E2X("low"),"space=4"); zdialog_add_widget(zd,"label","labcM","hb1",Bcontrast,"expand"); zdialog_add_widget(zd,"label","labcH","hb1",E2X("high"),"space=5"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labcon","hb2",E2X("Amplify"),"space=5"); zdialog_add_widget(zd,"hscale","amplify","hb2","0|1|0.005|0","expand"); zdialog_add_widget(zd,"label","ampval","hb2",0,"space=5"); zdialog_add_widget(zd,"hbox","hbcf","dialog",0,"space=8"); zdialog_add_widget(zd,"label","labcf","hbcf",Bcurvefile,"space=5"); zdialog_add_widget(zd,"button","loadcurve","hbcf",Bopen,"space=5"); zdialog_add_widget(zd,"button","savecurve","hbcf",Bsave,"space=5"); GtkWidget *frame = zdialog_widget(zd,"frame"); // set up curve edit spldat *sd = splcurve_init(frame,gradients_curvedit); EFgradients.sd = sd; sd->Nspc = 1; sd->fact[0] = 1; sd->vert[0] = 0; sd->nap[0] = 2; // initial curve anchor points sd->apx[0][0] = 0.01; sd->apy[0][0] = 0.10; sd->apx[0][1] = 0.99; sd->apy[0][1] = 0.10; splcurve_generate(sd,0); // generate curve data gradients_initz(zd); // initialize return; } // initialization for new image file void gradients_names::gradients_initz(zdialog *zd) { using namespace gradients_names; int ii, cc, px, py; float b1, *pix1; int jj, sum, limit, condist[100]; ww = E1pxm->ww; // image dimensions hh = E1pxm->hh; cc = ww * hh * sizeof(float); // allocate brightness map memory brmap1 = (float *) zmalloc(cc); brmap2 = (float *) zmalloc(cc); for (ii = 0; ii < 4; ii++) brmap4[ii] = (float *) zmalloc(cc); for (py = 0; py < hh; py++) // map initial image brightness for (px = 0; px < ww; px++) { ii = py * ww + px; pix1 = PXMpix(E1pxm,px,py); b1 = 0.333 * (pix1[0] + pix1[1] + pix1[2]); // pixel brightness 0-255.9 if (b1 < 1) b1 = 1; brmap1[ii] = b1; } for (ii = 0; ii < 100; ii++) condist[ii] = 0; for (py = 1; py < hh; py++) // map contrast distribution for (px = 1; px < ww; px++) { ii = py * ww + px; jj = 0.388 * fabsf(brmap1[ii] - brmap1[ii-1]); // horiz. contrast, ranged 0 - 99 if (jj > 99) jj = 99; // trap bad image data condist[jj]++; jj = 0.388 * fabsf(brmap1[ii] - brmap1[ii-ww]); // vertical if (jj > 99) jj = 99; 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,250,300); zdialog_run(zd,gradients_dialog_event,"save"); // run dialog - parallel amplify = 0; return; } // dialog event and completion callback function int gradients_names::gradients_dialog_event(zdialog *zd, cchar *event) { using namespace gradients_names; spldat *sd = EFgradients.sd; char text[8]; if (strmatch(event,"done")) zd->zstat = 1; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 2; // cancel if (zd->zstat) { // dialog complete if (zd->zstat == 1) edit_done(0); else edit_cancel(0); zfree(brmap1); // free memory zfree(brmap2); brmap1 = brmap2 = 0; for (int ii = 0; ii < 4; ii++) { zfree(brmap4[ii]); brmap4[ii] = 0; } return 1; } if (strmatch(event,"apply")) // from script gradients_dialog_event(zd,"amplify"); if (strmatch(event,"blendwidth")) signal_thread(); if (strmatch(event,"amplify")) { // slider value zdialog_fetch(zd,"amplify",amplify); snprintf(text,8,"%.2f",amplify); // numeric feedback zdialog_stuff(zd,"ampval",text); signal_thread(); // trigger update thread } if (strmatch(event,"loadcurve")) { // load saved curve splcurve_load(sd); signal_thread(); return 0; } if (strmatch(event,"savecurve")) { // save curve to file splcurve_save(sd); return 0; } return 0; } // this function is called when the curve is edited void gradients_names::gradients_curvedit(int) { signal_thread(); return; } // thread function void * gradients_names::gradients_thread(void *) { using namespace gradients_names; while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 do_wthreads(gradients_wthread1,4); do_wthreads(gradients_wthread2,4); do_wthreads(gradients_wthread3,NWT); CEF->Fmods++; CEF->Fsaved = 0; if (amplify == 0) CEF->Fmods = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); } return 0; // not executed, stop g++ warning } // working threads void * gradients_names::gradients_wthread1(void *arg) { using namespace gradients_names; int ii, kk, bii, pii, dist = 0; int px, px1, px2, pxinc; int py, py1, py2, pyinc; float b1, b4, xval, yval, grad; float amp, con99; spldat *sd = EFgradients.sd; 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 } pthread_exit(0); } void * gradients_names::gradients_wthread2(void *arg) { using namespace gradients_names; int index, ii, px, py, dist = 0; float b4; index = *((int *) arg); // thread 0...3 for (py = index; py < hh; py += 4) // loop all image pixels for (px = 0; px < ww; px++) { ii = py * ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel is outside area } b4 = brmap4[0][ii] + brmap4[1][ii] // new brightness = average of four + brmap4[2][ii] + brmap4[3][ii]; // calculated brightness surfaces b4 = 0.25 * b4; if (b4 < 1) b4 = 1; brmap2[ii] = b4; } pthread_exit(0); } void * gradients_names::gradients_wthread3(void *arg) { using namespace gradients_names; float *pix1, *pix3; int index, ii, px, py, dist = 0; float b1, b2, bf, f1, f2; float red1, green1, blue1, red3, green3, blue3; index = *((int *) arg); for (py = index; py < hh; py += NWT) // loop all image pixels for (px = 0; px < ww; px++) { ii = py * ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel is outside area } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel b1 = brmap1[ii]; // initial pixel brightness b2 = brmap2[ii]; // calculated new brightness bf = b2 / b1; // brightness ratio red1 = pix1[0]; // input RGB green1 = pix1[1]; blue1 = pix1[2]; red3 = bf * red1; // output RGB if (red3 > 255) red3 = 255; green3 = bf * green1; if (green3 > 255) green3 = 255; blue3 = bf * blue1; if (blue3 > 255) blue3 = 255; if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, f1 = sa_blendfunc(dist); // blend changes f2 = 1.0 - f1; red3 = f1 * red3 + f2 * red1; green3 = f1 * green3 + f2 * green1; blue3 = f1 * blue3 + f2 * blue1; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } pthread_exit(0); } /********************************************************************************/ // flatten brightness distribution based on the distribution of nearby zones namespace flatten_names { int Zinit; // zone initialization needed float flatten; // flatten amount, 0 - 100 float deband1, deband2; // deband dark, bright, 0 - 100 int Iww, Ihh; // image dimensions int NZ; // no. of image zones int pNZ; // prior image zones int Zsize, Zrows, Zcols; // zone parameters float *Br; // Br[py][px] pixel brightness int *Zxlo, *Zylo, *Zxhi, *Zyhi; // Zxlo[ii] etc. zone ii pixel range int *Zcen; // Zcen[ii][2] zone ii center (py,px) int16 *Zn; // Zn[py][px][9] 9 nearest zones for pixel: 0-200 (-1 = none) uint8 *Zw; // Zw[py][px][9] zone weights for pixel: 0-100 = 1.0 float *Zff; // Zff[ii][1000] zone ii flatten factors editfunc EFflatten; int dialog_event(zdialog* zd, cchar *event); void doflatten(); void calczones(); void initzones(); void * thread(void *); void * wthread(void *); } // menu function void m_flatten(GtkWidget *, cchar *menu) { using namespace flatten_names; cchar *title = E2X("Flatten Brightness"); F1_help_topic = "flatten"; Iww = Fpxb->ww; Ihh = Fpxb->hh; if (Iww * Ihh > wwhh_limit2) { zmessageACK(Mwin,"image too big"); edit_cancel(0); return; } EFflatten.menuname = menu; EFflatten.menufunc = m_flatten; EFflatten.funcname = "flatten"; EFflatten.FprevReq = 1; // use preview image EFflatten.Farea = 2; // select area usable EFflatten.Frestart = 1; // restartable EFflatten.FusePL = 1; // use with paint/lever edits OK EFflatten.Fscript = 1; // scripting supported EFflatten.threadfunc = thread; if (! edit_setup(EFflatten)) return; // setup edit Iww = E0pxm->ww; Ihh = E0pxm->hh; if (Iww * Ihh > wwhh_limit2) { zmessageACK(Mwin,"image too big"); edit_cancel(0); return; } /*** ______________________________________ | Flatten Brightness | | | | Zones [ 123 ] [Apply] | | Flatten =========[]============ NN | | Deband Dark =========[]========= NN | | Deband Bright ==========[]====== NN | | | | [Done] [Cancel] | |______________________________________| ***/ zdialog *zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); EFflatten.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labreg","hb1",E2X("Zones"),"space=5"); zdialog_add_widget(zd,"zspin","zones","hb1","1|999|1|100"); // zone range 1-999 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",E2X("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",E2X("Deband Bright"),"space=5"); zdialog_add_widget(zd,"hscale","deband2","hb4","0|100|1|0","expand"); zdialog_resize(zd,300,0); zdialog_run(zd,dialog_event,"save"); // run dialog - parallel NZ = pNZ = 40; // default zone count calczones(); // adjust to fit image flatten = deband1 = deband2 = 0; // dialog controls = neutral Zinit = 1; // zone initialization needed Br = 0; // no memory allocated return; } // dialog event and completion function int flatten_names::dialog_event(zdialog *zd, cchar *event) { using namespace flatten_names; if (strmatch(event,"done")) zd->zstat = 1; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 2; // cancel if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); Zinit = 1; doflatten(); return 1; } if (zd->zstat) { if (zd->zstat == 1) { // done edit_fullsize(); // get full size image Zinit = 1; // recalculate zones doflatten(); // flatten full image edit_done(0); // commit edit } else edit_cancel(0); // discard edit if (Br) { zfree(Br); // free memory zfree(Zn); zfree(Zw); zfree(Zxlo); zfree(Zylo); zfree(Zxhi); zfree(Zyhi); zfree(Zcen); zfree(Zff); Br = 0; } return 1; } if (strmatch(event,"apply")) { // [apply] (also from script) dialog_event(zd,"zones"); dialog_event(zd,"flatten"); dialog_event(zd,"deband1"); dialog_event(zd,"deband2"); doflatten(); } if (strmatch(event,"blendwidth")) // select area blendwidth change doflatten(); if (strmatch(event,"zones")) { // get zone count input zdialog_fetch(zd,"zones",NZ); if (NZ == pNZ) return 1; calczones(); // adjust to fit image zdialog_stuff(zd,"zones",NZ); // update dialog Zinit = 1; // zone initialization needed } if (strmatch(event,"flatten")) { zdialog_fetch(zd,"flatten",flatten); // get slider values doflatten(); } if (strmatch(event,"deband1")) { zdialog_fetch(zd,"deband1",deband1); doflatten(); } if (strmatch(event,"deband2")) { zdialog_fetch(zd,"deband2",deband2); doflatten(); } return 1; } // perform the flatten calculations and update the image void flatten_names::doflatten() { if (flatten > 0) { signal_thread(); wait_thread_idle(); // no overlap window update and threads Fpaintnow(); } else edit_reset(); return; } // recalculate zone count based on what fits the image dimensions // done only when the user zone count input changes // outputs: NZ, Zrows, Zcols void flatten_names::calczones() { int gNZ, dNZ; Iww = E1pxm->ww; // image dimensions Ihh = E1pxm->hh; gNZ = NZ; // new zone count goal dNZ = NZ - pNZ; // direction of change while (true) { Zsize = sqrtf(Iww * Ihh / gNZ); // approx. zone size Zrows = Ihh / Zsize + 0.5; // get appropriate rows and cols Zcols = Iww / Zsize + 0.5; if (Zrows < 1) Zrows = 1; if (Zcols < 1) Zcols = 1; NZ = Zrows * Zcols; // NZ matching rows x cols if (dNZ > 0 && NZ <= pNZ) { // no increase, try again if (NZ >= 999) break; gNZ++; continue; } if (dNZ < 0 && NZ >= pNZ) { // no decrease, try again if (NZ <= 20) break; gNZ--; continue; } if (dNZ == 0) break; if (dNZ > 0 && NZ > pNZ) break; if (dNZ < 0 && NZ < pNZ) break; } pNZ = NZ; // final zone count dNZ = 0; return; } // build up the zones data when NZ or the image size changes // (preview or full size image) void flatten_names::initzones() { int px, py, cx, cy; int rx, ii, jj, kk; int zww, zhh, row, col; float *pix1, bright; float weight[9], sumweight; float D, Dthresh; if (Br) { zfree(Br); // free memory zfree(Zn); zfree(Zw); zfree(Zxlo); zfree(Zylo); zfree(Zxhi); zfree(Zyhi); zfree(Zcen); zfree(Zff); Br = 0; } Iww = E1pxm->ww; // image dimensions Ihh = E1pxm->hh; Br = (float *) zmalloc(Iww * Ihh * sizeof(float)); // allocate memory Zn = (int16 *) zmalloc(Iww * Ihh * 9 * sizeof(int16)); Zw = (uint8 *) zmalloc(Iww * Ihh * 9 * sizeof(uint8)); Zxlo = (int *) zmalloc(NZ * sizeof(int)); Zylo = (int *) zmalloc(NZ * sizeof(int)); Zxhi = (int *) zmalloc(NZ * sizeof(int)); Zyhi = (int *) zmalloc(NZ * sizeof(int)); Zcen = (int *) zmalloc(NZ * 2 * sizeof(int)); Zff = (float *) zmalloc(NZ * 1000 * sizeof(float)); for (py = 0; py < Ihh; py++) // get initial pixel brightness levels for (px = 0; px < Iww; px++) { pix1 = PXMpix(E1pxm,px,py); bright = pixbright(pix1); ii = py * Iww + px; Br[ii] = bright; } zww = Iww / Zcols; // actual zone size zhh = Ihh / Zrows; for (row = 0; row < Zrows; row++) for (col = 0; col < Zcols; col++) // set low/high bounds per zone { ii = row * Zcols + col; Zxlo[ii] = col * zww; Zylo[ii] = row * zhh; Zxhi[ii] = Zxlo[ii] + zww; Zyhi[ii] = Zylo[ii] + zhh; Zcen[2*ii] = Zylo[ii] + zhh/2; Zcen[2*ii+1] = Zxlo[ii] + zww/2; } for (ii = 0; ii < NZ; ii++) // compute brightness distributiion { // for each zone for (jj = 0; jj < 1000; jj++) Zff[1000*ii+jj] = 0; for (py = Zylo[ii]; py < Zyhi[ii]; py++) // brightness distribution for (px = Zxlo[ii]; px < Zxhi[ii]; px++) { pix1 = PXMpix(E1pxm,px,py); kk = pixbright(pix1); if (kk > 255) kk = 255; bright = 3.906 * kk; // 1000 / 256 * kk Zff[1000*ii+int(bright)]++; } for (jj = 1; jj < 1000; jj++) // cumulative brightness distribution Zff[1000*ii+jj] += Zff[1000*ii+jj-1]; for (jj = 0; jj < 1000; jj++) // multiplier per brightness level Zff[1000*ii+jj] = Zff[1000*ii+jj] / (zww*zhh) * 1000 / (jj+1); // to make distribution flat } for (py = 0; py < Ihh; py++) // set 9 nearest zones per pixel for (px = 0; px < Iww; px++) { rx = (py * Iww + px) * 9; // index for 9 nearest zones to px/py row = py / zhh; // zone containing pixel col = px / zww; ii = 0; for (jj = row-1; jj <= row+1; jj++) // loop 3x3 surrounding zones for (kk = col-1; kk <= col+1; kk++) { if (jj >= 0 && jj < Zrows && kk >= 0 && kk < Zcols) // zone is not off the edge Zn[rx+ii] = jj * Zcols + kk; else Zn[rx+ii] = -1; // edge row/col: missing neighbor ii++; } } if (zww < zhh) // pixel to zone distance threshold Dthresh = 1.5 * zww; // area influence = 0 beyond this distance else Dthresh = 1.5 * zhh; for (py = 0; py < Ihh; py++) // set zone weights per pixel for (px = 0; px < Iww; px++) { rx = (py * Iww + px) * 9; // index for 9 nearest zones to px/py for (ii = 0; ii < 9; ii++) // distance to each zone center { jj = Zn[rx+ii]; if (jj == -1) { // missign zone weight[ii] = 0; // weight = 0 continue; } cy = Zcen[2*jj]; cx = Zcen[2*jj+1]; D = sqrtf((py-cy)*(py-cy) + (px-cx)*(px-cx)); // distance from pixel to zone center D = D / Dthresh; if (D > 1) D = 1; // zone influence reaches zero at Dthresh weight[ii] = 1.0 - D; } sumweight = 0; for (ii = 0; ii < 9; ii++) // zone weights based on distance sumweight += weight[ii]; for (ii = 0; ii < 9; ii++) // normalize weights, sum = 1.0 Zw[rx+ii] = 100 * weight[ii] / sumweight; // 0-100 = 1.0 } return; } // adjust brightness distribution thread function void * flatten_names::thread(void *) { using namespace flatten_names; while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 if (Zinit) initzones(); // reinitialize zones Zinit = 0; do_wthreads(wthread,NWT); paintlock(0); // unblock window paint 19.0 if (! flatten) CEF->Fmods = 0; // no modification else { CEF->Fmods++; // image modified, not saved CEF->Fsaved = 0; } } return 0; // not executed, stop g++ warning } // worker thread for each CPU processor core void * flatten_names::wthread(void *arg) { using namespace flatten_names; int index = *((int *) (arg)); int px, py, rx, ii, jj, dist = 0; float *pix1, *pix3; float fnew1, fnew2, fnew, fold; float dold, dnew, cmax; float red1, green1, blue1, red3, green3, blue3; float FF, weight, bright; for (py = index; py < Ihh; py += NWT) // loop all image pixels for (px = 0; px < Iww; px++) { if (sa_stat == 3) { // select area active ii = py * E1pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel red1 = pix1[0]; green1 = pix1[1]; blue1 = pix1[2]; bright = 0.976 * red1 + 2.539 * green1 + 0.39 * blue1; // brightness scaled 0-999.9 rx = (py * Iww + px) * 9; // index for 9 nearest zones to px/py FF = 0; for (ii = 0; ii < 9; ii++) { // loop 9 nearest zones weight = Zw[rx+ii]; if (weight > 0) { // 0-100 = 1.0 jj = Zn[rx+ii]; FF += 0.01 * weight * Zff[1000*jj+int(bright)]; // sum weight * flatten factor } } red3 = FF * red1; // fully flattened brightness green3 = FF * green1; blue3 = FF * blue1; fnew1 = 1 - 0.01 * deband1 * 0.001 * (1000 - bright); // attenuate dark pixels fnew2 = 1 - 0.01 * deband2 * 0.001 * bright; // attenuate bright pixels fnew = fnew1 * fnew2; fnew = fnew * 0.01 * flatten; // how much to flatten, 0 to 1 fold = 1.0 - fnew; // how much to retain, 1 to 0 red3 = fnew * red3 + fold * red1; // blend new and old brightness green3 = fnew * green3 + fold * green1; blue3 = fnew * blue3 + fold * blue1; if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, dnew = sa_blendfunc(dist); // blend changes over sa_blendwidth 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; } pthread_exit(0); } /********************************************************************************/ // Global Retinex function // Rescale RGB values based on entire image - eliminate color caste and reduce fog/haze. namespace gretinex_names { editfunc EFgretinex; // edit function data int e3ww, e3hh; cchar *thread_command; float Rdark, Gdark, Bdark; float Rbrite, Gbrite, Bbrite; float Rmpy, Gmpy, Bmpy; float pRdark, pGdark, pBdark; // prior values float pRbrite, pGbrite, pBbrite; float pRmpy, pGmpy, pBmpy; float blend, reducebright; int Fbrightpoint, Fdarkpoint; } // menu function void m_gretinex(GtkWidget *, cchar *menu) // separated from zonal retinex { using namespace gretinex_names; int gretinex_dialog_event(zdialog *zd, cchar *event); void gretinex_mousefunc(); void * gretinex_thread(void *); F1_help_topic = "global retx"; EFgretinex.menuname = menu; EFgretinex.menufunc = m_gretinex; EFgretinex.funcname = "Gretinex"; // function name EFgretinex.Farea = 2; // select area usable EFgretinex.Frestart = 1; // allow restart EFgretinex.Fscript = 1; // scripting supported EFgretinex.threadfunc = gretinex_thread; // thread function EFgretinex.mousefunc = gretinex_mousefunc; // mouse function if (! edit_setup(EFgretinex)) return; // setup edit e3ww = E3pxm->ww; // image size e3hh = E3pxm->hh; E9pxm = 0; /*** ______________________________________ | Global Retinex | | | | Red Green Blue +/- | | Dark Point: [__] [__] [__] [__] | 0 ... 255 neutral point is 0 | Bright Point: [__] [__] [__] [__] | 0 ... 255 neutral point is 255 | Multiplyer: [__] [__] [__] [__] | 0 ... 5 neutral point is 1 | | | [auto] brightness rescale | | [x] bright point [x] dark point | mouse click spots | blend: =======[]=================== | | reduce bright: ==========[]======== | | | | [reset] [done] [cancel] | |______________________________________| ***/ zdialog *zd = zdialog_new(E2X("Global Retinex"),Mwin,Breset,Bdone,Bcancel,null); EFgretinex.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","space","hb1","","space=2"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"homog"); zdialog_add_widget(zd,"label","space","hb1","","space=2"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"homog"); zdialog_add_widget(zd,"label","space","hb1","","space=2"); zdialog_add_widget(zd,"vbox","vb3","hb1",0,"homog"); zdialog_add_widget(zd,"label","space","hb1","","space=2"); zdialog_add_widget(zd,"vbox","vb4","hb1",0,"homog"); zdialog_add_widget(zd,"label","space","hb1","","space=2"); zdialog_add_widget(zd,"vbox","vb5","hb1",0,"homog"); zdialog_add_widget(zd,"label","space","vb1",""); zdialog_add_widget(zd,"label","labdark","vb1",E2X("Dark Point")); zdialog_add_widget(zd,"label","labbrite","vb1",E2X("Bright Point")); zdialog_add_widget(zd,"label","labmpy","vb1",E2X("Multiplyer")); zdialog_add_widget(zd,"label","labred","vb2",Bred); zdialog_add_widget(zd,"zspin","Rdark","vb2","0|255|1|0","size=3"); zdialog_add_widget(zd,"zspin","Rbrite","vb2","0|255|1|255","size=3"); zdialog_add_widget(zd,"zspin","Rmpy","vb2","0.1|5|0.01|1","size=3"); zdialog_add_widget(zd,"label","labgreen","vb3",Bgreen); zdialog_add_widget(zd,"zspin","Gdark","vb3","0|255|1|0","size=3"); zdialog_add_widget(zd,"zspin","Gbrite","vb3","0|255|1|255","size=3"); zdialog_add_widget(zd,"zspin","Gmpy","vb3","0.1|5|0.01|1","size=3"); zdialog_add_widget(zd,"label","labred","vb4",Bblue); zdialog_add_widget(zd,"zspin","Bdark","vb4","0|255|1|0","size=3"); zdialog_add_widget(zd,"zspin","Bbrite","vb4","0|255|1|255","size=3"); zdialog_add_widget(zd,"zspin","Bmpy","vb4","0.1|5|0.01|1","size=3"); zdialog_add_widget(zd,"label","laball","vb5",Ball); zdialog_add_widget(zd,"zspin","dark+-","vb5","-1|+1|1|0","size=3"); zdialog_add_widget(zd,"zspin","brite+-","vb5","-1|+1|1|0","size=3"); zdialog_add_widget(zd,"zspin","mpy+-","vb5","-1|+1|1|0","size=3"); zdialog_add_widget(zd,"hbox","hbauto","dialog"); zdialog_add_widget(zd,"button","autoscale","hbauto",Bauto,"space=3"); zdialog_add_widget(zd,"label","labauto","hbauto",E2X("brightness rescale"),"space=5"); zdialog_add_widget(zd,"hbox","hbclicks","dialog"); zdialog_add_widget(zd,"check","brightpoint","hbclicks",E2X("click bright point"),"space=3"); zdialog_add_widget(zd,"check","darkpoint","hbclicks",E2X("click dark point"),"space=5"); zdialog_add_widget(zd,"hbox","hbb","dialog"); zdialog_add_widget(zd,"label","labb","hbb",E2X("blend"),"space=5"); zdialog_add_widget(zd,"hscale","blend","hbb","0|1.0|0.01|1.0","expand"); zdialog_add_widget(zd,"hbox","hbrd","dialog"); zdialog_add_widget(zd,"label","labrd","hbrd",E2X("reduce bright"),"space=5"); zdialog_add_widget(zd,"hscale","reduce bright","hbrd","0|1.0|0.01|0.0","expand"); zdialog_run(zd,gretinex_dialog_event,"save"); // run dialog - parallel zdialog_send_event(zd,"reset"); return; } // dialog event and completion function int gretinex_dialog_event(zdialog *zd, cchar *event) { using namespace gretinex_names; void gretinex_mousefunc(); int adddark, addbrite, addmpy; int Fchange = 0; if (strmatch(event,"focus")) return 1; // stop loss of button event (?) 19.0 if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"reset")) zd->zstat = 1; // initz. if (zd->zstat) { if (zd->zstat == 1) { // reset zd->zstat = 0; // keep dialog active Rdark = Gdark = Bdark = 0; // set neutral values Rbrite = Gbrite = Bbrite = 255; pRdark = pGdark = pBdark = 0; // prior values = same pRbrite = pGbrite = pBbrite = 255; // (no change) Rmpy = Gmpy = Bmpy = 1.0; Fbrightpoint = Fdarkpoint = 0; blend = 1.0; reducebright = 0.0; zdialog_stuff(zd,"Rdark",0); // stuff neutral values into dialog zdialog_stuff(zd,"Gdark",0); zdialog_stuff(zd,"Bdark",0); zdialog_stuff(zd,"Rbrite",255); zdialog_stuff(zd,"Gbrite",255); zdialog_stuff(zd,"Bbrite",255); zdialog_stuff(zd,"Rmpy",1.0); zdialog_stuff(zd,"Gmpy",1.0); zdialog_stuff(zd,"Bmpy",1.0); zdialog_stuff(zd,"brightpoint",0); zdialog_stuff(zd,"darkpoint",0); edit_reset(); if (E9pxm) PXM_free(E9pxm); // free memory E9pxm = 0; return 1; } else if (zd->zstat == 2) { // done edit_done(0); // commit edit if (E9pxm) PXM_free(E9pxm); // free memory E9pxm = 0; return 1; } else { edit_cancel(0); // discard edit if (E9pxm) PXM_free(E9pxm); // free memory E9pxm = 0; return 1; } } Fbrightpoint = Fdarkpoint = 0; // disable mouse if (strmatch(event,"autoscale")) // auto set dark and bright points { edit_reset(); thread_command = "autoscale"; // get dark/bright limits from signal_thread(); // darkest/brightest pixels wait_thread_idle(); zdialog_stuff(zd,"Rdark",Rdark); // update dialog widgets zdialog_stuff(zd,"Gdark",Gdark); zdialog_stuff(zd,"Bdark",Bdark); zdialog_stuff(zd,"Rbrite",Rbrite); zdialog_stuff(zd,"Gbrite",Gbrite); zdialog_stuff(zd,"Bbrite",Bbrite); zdialog_stuff(zd,"Rmpy",1.0); zdialog_stuff(zd,"Gmpy",1.0); zdialog_stuff(zd,"Bmpy",1.0); zdialog_stuff(zd,"brightpoint",0); zdialog_stuff(zd,"darkpoint",0); pRdark = Rdark; // prior values = same pGdark = Gdark; pBdark = Bdark; pRbrite = Rbrite; pGbrite = Gbrite; pBbrite = Bbrite; pRmpy = Rmpy; pGmpy = Gmpy; pBmpy = Bmpy; return 1; } if (strmatch(event,"brightpoint")) { zdialog_fetch(zd,"brightpoint",Fbrightpoint); if (Fbrightpoint) { zdialog_stuff(zd,"darkpoint",0); Fdarkpoint = 0; } } if (strmatch(event,"darkpoint")) { zdialog_fetch(zd,"darkpoint",Fdarkpoint); if (Fdarkpoint) { zdialog_stuff(zd,"brightpoint",0); Fbrightpoint = 0; } } if (zstrstr("brightpoint darkpoint",event)) { // brightpoint or darkpoint takeMouse(gretinex_mousefunc,dragcursor); // connect mouse function return 1; } else { zdialog_stuff(zd,"brightpoint",0); // reset zdialog checkboxes zdialog_stuff(zd,"darkpoint",0); } adddark = addbrite = addmpy = 0; if (strmatch(event,"dark+-")) zdialog_fetch(zd,"dark+-",adddark); // [+/-] button if (strmatch(event,"brite+-")) zdialog_fetch(zd,"brite+-",addbrite); if (strmatch(event,"mpy+-")) zdialog_fetch(zd,"mpy+-",addmpy); if (adddark) { zdialog_fetch(zd,"Rdark",Rdark); // increment dark levels zdialog_fetch(zd,"Gdark",Gdark); zdialog_fetch(zd,"Bdark",Bdark); Rdark += adddark; Gdark += adddark; Bdark += adddark; if (Rdark < 0) Rdark = 0; if (Gdark < 0) Gdark = 0; if (Bdark < 0) Bdark = 0; if (Rdark > 255) Rdark = 255; if (Gdark > 255) Gdark = 255; if (Bdark > 255) Bdark = 255; zdialog_stuff(zd,"Rdark",Rdark); zdialog_stuff(zd,"Gdark",Gdark); zdialog_stuff(zd,"Bdark",Bdark); } if (addbrite) { // increment bright levels zdialog_fetch(zd,"Rbrite",Rbrite); zdialog_fetch(zd,"Gbrite",Gbrite); zdialog_fetch(zd,"Bbrite",Bbrite); Rbrite += addbrite; Gbrite += addbrite; Bbrite += addbrite; if (Rbrite < 0) Rbrite = 0; if (Gbrite < 0) Gbrite = 0; if (Bbrite < 0) Bbrite = 0; if (Rbrite > 255) Rbrite = 255; if (Gbrite > 255) Gbrite = 255; if (Bbrite > 255) Bbrite = 255; zdialog_stuff(zd,"Rbrite",Rbrite); zdialog_stuff(zd,"Gbrite",Gbrite); zdialog_stuff(zd,"Bbrite",Bbrite); } if (addmpy) { // increment mpy factors zdialog_fetch(zd,"Rmpy",Rmpy); zdialog_fetch(zd,"Gmpy",Gmpy); zdialog_fetch(zd,"Bmpy",Bmpy); Rmpy += 0.01 * addmpy; Gmpy += 0.01 * addmpy; Bmpy += 0.01 * addmpy; if (Rmpy < 0.1) Rmpy = 0.1; if (Gmpy < 0.1) Gmpy = 0.1; if (Bmpy < 0.1) Bmpy = 0.1; if (Rmpy > 5) Rmpy = 5; if (Gmpy > 5) Gmpy = 5; if (Bmpy > 5) Bmpy = 5; zdialog_stuff(zd,"Rmpy",Rmpy); zdialog_stuff(zd,"Gmpy",Gmpy); zdialog_stuff(zd,"Bmpy",Bmpy); } zdialog_fetch(zd,"Rdark",Rdark); // get all params zdialog_fetch(zd,"Gdark",Gdark); zdialog_fetch(zd,"Bdark",Bdark); zdialog_fetch(zd,"Rbrite",Rbrite); zdialog_fetch(zd,"Gbrite",Gbrite); zdialog_fetch(zd,"Bbrite",Bbrite); zdialog_fetch(zd,"Rmpy",Rmpy); zdialog_fetch(zd,"Gmpy",Gmpy); zdialog_fetch(zd,"Bmpy",Bmpy); Fchange = 0; if (Rdark != pRdark) Fchange = 1; // detect changed params if (Gdark != pGdark) Fchange = 1; if (Bdark != pBdark) Fchange = 1; if (Rbrite != pRbrite) Fchange = 1; if (Gbrite != pGbrite) Fchange = 1; if (Bbrite != pBbrite) Fchange = 1; if (Rmpy != pRmpy) Fchange = 1; if (Gmpy != pGmpy) Fchange = 1; if (Bmpy != pBmpy) Fchange = 1; pRdark = Rdark; // remember values for change detection pGdark = Gdark; pBdark = Bdark; pRbrite = Rbrite; pGbrite = Gbrite; pBbrite = Bbrite; pRmpy = Rmpy; pGmpy = Gmpy; pBmpy = Bmpy; // wait_thread_idle() removed 20.0 if (Fchange) { // global params changed thread_command = "global"; signal_thread(); // update image Fchange = 0; } if (zstrstr("blend, reduce bright",event)) { // blend params changed zdialog_fetch(zd,"blend",blend); zdialog_fetch(zd,"reduce bright",reducebright); thread_command = "blend"; signal_thread(); // update image } return 1; } // get dark point or bright point from mouse click position void gretinex_mousefunc() { using namespace gretinex_names; int px, py, dx, dy; float red, green, blue; float *ppix; char mousetext[60]; zdialog *zd = EFgretinex.zd; if (! zd) { freeMouse(); return; } if (! Fbrightpoint && ! Fdarkpoint) { freeMouse(); return; } if (! LMclick) return; LMclick = 0; px = Mxclick; // mouse click position py = Myclick; if (px < 2) px = 2; // pull back from edge if (px > E3pxm->ww-3) px = E3pxm->ww-3; if (py < 2) py = 2; if (py > E3pxm->hh-3) py = E3pxm->hh-3; red = green = blue = 0; for (dy = -1; dy <= 1; dy++) // 3x3 block around mouse position for (dx = -1; dx <= 1; dx++) { ppix = PXMpix(E1pxm,px+dx,py+dy); // input image red += ppix[0]; green += ppix[1]; blue += ppix[2]; } red = red / 9.0; // mean RGB levels green = green / 9.0; blue = blue / 9.0; snprintf(mousetext,60,"3x3 pixels RGB: %.0f %.0f %.0f \n",red,green,blue); poptext_mouse(mousetext,10,10,0,3); if (Fbrightpoint) { // click pixel is new bright point Rbrite= red; Gbrite = green; Bbrite = blue; zdialog_stuff(zd,"Rbrite",Rbrite); // stuff values into dialog zdialog_stuff(zd,"Gbrite",Gbrite); zdialog_stuff(zd,"Bbrite",Bbrite); } if (Fdarkpoint) { // click pixel is new dark point Rdark = red; Gdark = green; Bdark = blue; zdialog_stuff(zd,"Rdark",Rdark); // stuff values into dialog zdialog_stuff(zd,"Gdark",Gdark); zdialog_stuff(zd,"Bdark",Bdark); } if (Fbrightpoint || Fdarkpoint) { thread_command = "global"; signal_thread(); // trigger image update wait_thread_idle(); } return; } // thread function - multiple working threads to update image void * gretinex_thread(void *) { using namespace gretinex_names; void * gretinex_wthread1(void *arg); // worker threads void * gretinex_wthread2(void *arg); void * gretinex_wthread3(void *arg); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 if (strmatch(thread_command,"autoscale")) { gretinex_wthread1(0); // worker thread for autoscale thread_command = "global"; } if (strmatch(thread_command,"global")) { do_wthreads(gretinex_wthread2,NWT); // worker thread for image RGB rescale if (E9pxm) PXM_free(E9pxm); // E9 image = global retinex output E9pxm = PXM_copy(E3pxm); thread_command = "blend"; // auto blend after global retinex } if (strmatch(thread_command,"blend")) { if (! E9pxm) E9pxm = PXM_copy(E3pxm); // stop thread crash 19.0 do_wthreads(gretinex_wthread3,NWT); // worker thread for image blend } CEF->Fmods++; // image modified CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed, stop warning } // worker thread function - autoscale retinex void * gretinex_wthread1(void *) { using namespace gretinex_names; int ii, dist = 0; int px, py, dx, dy; int red, green, blue; float *pix1; Rdark = Gdark = Bdark = 255; Rbrite = Gbrite = Bbrite = 0; Rmpy = Gmpy = Bmpy = 1.0; Fbrightpoint = Fdarkpoint = 0; for (py = 1; py < E1pxm->hh-1; py++) for (px = 1; px < E1pxm->ww-1; px++) { if (sa_stat == 3) { // select area active ii = py * E1pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside area } red = green = blue = 0; for (dy = -1; dy <= 1; dy++) // 3x3 block around mouse position for (dx = -1; dx <= 1; dx++) { pix1 = PXMpix(E1pxm,px+dx,py+dy); // input image red += pix1[0]; green += pix1[1]; blue += pix1[2]; } red = red / 9.0; // mean RGB levels green = green / 9.0; blue = blue / 9.0; if (red < Rdark) Rdark = red; // update limits if (green < Gdark) Gdark = green; if (blue < Bdark) Bdark = blue; if (red > Rbrite) Rbrite = red; if (green > Gbrite) Gbrite = green; if (blue > Bbrite) Bbrite = blue; } return 0; // not executed, avoid gcc warning } // worker thread function - scale image RGB values void * gretinex_wthread2(void *arg) { using namespace gretinex_names; int ii, index, dist = 0; int px, py; float R1, G1, B1, R3, G3, B3; float f1, f2, F, cmax; float *pix1, *pix3; index = *((int *) arg); for (py = index; py < E3pxm->hh; py += NWT) // loop all image pixels for (px = 0; px < E3pxm->ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside area } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel R1 = pix1[0]; // input RGB values G1 = pix1[1]; B1 = pix1[2]; R1 = 255 * (R1 - Rdark) / (Rbrite - Rdark); // rescale for full 0-255 range G1 = 255 * (G1 - Gdark) / (Gbrite - Gdark); B1 = 255 * (B1 - Bdark) / (Bbrite - Bdark); if (R1 < 0) R1 = 0; if (G1 < 0) G1 = 0; if (B1 < 0) B1 = 0; R3 = R1 * Rmpy; G3 = G1 * Gmpy; B3 = B1 * Bmpy; if (R3 > 255 || G3 > 255 || B3 > 255) { // stop overflow cmax = R3; if (G3 > cmax) cmax = G3; if (B3 > cmax) cmax = B3; F = 255 / cmax; R3 *= F; G3 *= F; B3 *= F; } if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth 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; } pthread_exit(0); } // worker thread function - blend input and output images, attenuate bright pixels void * gretinex_wthread3(void *arg) { using namespace gretinex_names; int index = *((int *) arg); int px, py; int ii, dist = 0; float *pix1, *pix2, *pix3; float R1, G1, B1, R2, G2, B2, R3, G3, B3; float F1, F2, Lmax; for (py = index; py < e3hh; py += NWT) // loop all image pixels for (px = 0; px < e3ww; px++) { if (sa_stat == 3) { // select area active ii = py * e3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside area } pix1 = PXMpix(E1pxm,px,py); // input image pixel pix2 = PXMpix(E9pxm,px,py); // global retinex image pixel pix3 = PXMpix(E3pxm,px,py); // output image pixel R1 = pix1[0]; // input color - original image G1 = pix1[1]; B1 = pix1[2]; R2 = pix2[0]; // input color - retinex image G2 = pix2[1]; B2 = pix2[2]; if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, F1 = sa_blendfunc(dist); // blend changes over sa_blendwidth F2 = 1.0 - F1; R2 = F1 * R2 + F2 * R1; G2 = F1 * G2 + F2 * G1; B2 = F1 * B2 + F2 * B1; } Lmax = R1; // max. RGB input if (G1 > Lmax) Lmax = G1; if (B1 > Lmax) Lmax = B1; F1 = blend; // 0 ... 1 >> E1 ... E3 F2 = reducebright; // 0 ... 1 F1 = F1 * (1.0 - F2 * Lmax / 255.0); // reduce F1 for high F2 * Lmax R3 = F1 * R2 + (1.0 - F1) * R1; // output RGB = blended inputs G3 = F1 * G2 + (1.0 - F1) * G1; B3 = F1 * B2 + (1.0 - F1) * B1; pix3[0] = R3; // output RGB values pix3[1] = G3; pix3[2] = B3; } pthread_exit(0); } /********************************************************************************/ // Zonal Retinex function // Rescale RGB values based on surrounding zones: increase local brightness range. namespace zretinex_names { editfunc EFzretinex; // edit function data int e3ww, e3hh; cchar *thread_command; float blend, reducedark, reducebright; int maxzones = 10000; int Nzones = 100, Pzones = 0; // zone count, 1-10000 int zsize, zrows, zcols, zww, zhh; // zone data typedef struct { // zone structure int cx, cy; // zone center in image float minR, minG, minB; // RGB minimum values in zone float maxR, maxG, maxB; // RGB mazimum values in zone } zone_t; zone_t *zones = 0; // up to 10000 zones int16 *zoneindex = 0; // zoneindex[ii][z] pixel ii, 25 zones int16 *zoneweight = 0; // zoneweight[ii][z] zone weights } // float -> int16 to save memory 20.0 // menu function void m_zretinex(GtkWidget *, cchar *menu) { using namespace zretinex_names; int zretinex_dialog_event(zdialog *zd, cchar *event); void * zretinex_thread(void *); F1_help_topic = "zonal retx"; EFzretinex.menuname = menu; EFzretinex.menufunc = m_zretinex; EFzretinex.funcname = "Zretinex"; // function name EFzretinex.Farea = 2; // select area usable EFzretinex.Frestart = 1; // allow restart EFzretinex.Fscript = 1; // scripting supported EFzretinex.threadfunc = zretinex_thread; // thread function if (! edit_setup(EFzretinex)) return; // setup edit e3ww = E3pxm->ww; // image size e3hh = E3pxm->hh; E9pxm = PXM_copy(E1pxm); /*** ______________________________________ | Zonal Retinex | | | | zone count: [___] [apply] | | blend: =======[]=================== | | reduce dark: ==========[]========== | | reduce bright: ==========[]======== | | | | [reset] [done] [cancel] | |______________________________________| ***/ zdialog *zd = zdialog_new(E2X("Zonal Retinex"),Mwin,Breset,Bdone,Bcancel,null); EFzretinex.zd = zd; zdialog_add_widget(zd,"hbox","hbz","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labzs","hbz",E2X("zone count:"),"space=5"); zdialog_add_widget(zd,"zspin","zone count","hbz","20|10000|10|100"); zdialog_add_widget(zd,"button","apply","hbz",Bapply,"space=3"); zdialog_add_widget(zd,"hbox","hbb","dialog"); zdialog_add_widget(zd,"label","labb","hbb",E2X("blend"),"space=5"); zdialog_add_widget(zd,"hscale","blend","hbb","0|1.0|0.01|1.0","expand"); zdialog_add_widget(zd,"hbox","hbrd","dialog"); zdialog_add_widget(zd,"label","labrd","hbrd",E2X("reduce dark"),"space=5"); zdialog_add_widget(zd,"hscale","reduce dark","hbrd","0|1.0|0.01|0.0","expand"); zdialog_add_widget(zd,"hbox","hbrl","dialog"); zdialog_add_widget(zd,"label","labrd","hbrl",E2X("reduce bright"),"space=5"); zdialog_add_widget(zd,"hscale","reduce bright","hbrl","0|1.0|0.01|0.0","expand"); zdialog_run(zd,zretinex_dialog_event,"save"); // run dialog - parallel zdialog_send_event(zd,"reset"); return; } // dialog event and completion function int zretinex_dialog_event(zdialog *zd, cchar *event) { using namespace zretinex_names; void zretinex_zonesetup(zdialog *zd); if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"reset")) zd->zstat = 1; // initz. if (zd->zstat) { if (zd->zstat == 1) { // reset zd->zstat = 0; // keep dialog active blend = 1.0; reducedark = 0.0; reducebright = 0.0; edit_reset(); PXM_free(E9pxm); E9pxm = PXM_copy(E1pxm); return 1; } else if (zd->zstat == 2) { // done edit_done(0); // commit edit PXM_free(E9pxm); if (zones) zfree(zones); if (zoneindex) zfree(zoneindex); if (zoneweight) zfree(zoneweight); zones = 0; zoneindex = 0; zoneweight = 0; return 1; } else { edit_cancel(0); // discard edit PXM_free(E9pxm); if (zones) zfree(zones); if (zoneindex) zfree(zoneindex); if (zoneweight) zfree(zoneweight); zones = 0; zoneindex = 0; zoneweight = 0; return 1; } } if (strmatch(event,"apply")) { // get new zone count zretinex_zonesetup(zd); // zone setups thread_command = "apply"; signal_thread(); // update image wait_thread_idle(); } if (strmatch(event,"blend")) { // blend param changed zdialog_fetch(zd,"blend",blend); thread_command = "blend"; signal_thread(); // update image wait_thread_idle(); } if (zstrstr(event,"reduce")) { // reduce param changed zdialog_fetch(zd,"reduce dark",reducedark); zdialog_fetch(zd,"reduce bright",reducebright); thread_command = "blend"; signal_thread(); // update image wait_thread_idle(); } return 1; } // setup new zone parameters and allocate memory void zretinex_zonesetup(zdialog *zd) { using namespace zretinex_names; int goal, dirc; int row, col, xlo, ylo; int ii, nn, cx, cy; Pzones = Nzones; // prior count zdialog_fetch(zd,"zone count",Nzones); // new count goal = Nzones; // new zone count goal dirc = Nzones - Pzones; // direction of change if (dirc == 0) dirc = 1; while (true) { zsize = sqrt(e3ww * e3hh / goal); // approx. zone size zrows = e3hh / zsize; // get appropriate rows and cols zcols = e3ww / zsize; if (zrows < 1) zrows = 1; if (zcols < 1) zcols = 1; Nzones = zrows * zcols; // matching rows x cols if (dirc > 0 && Nzones <= Pzones) { // no increase, try again if (Nzones == maxzones) break; goal++; continue; } if (dirc < 0 && Nzones >= Pzones) { // no decrease, try again if (Nzones == 1) break; goal--; continue; } if (dirc > 0 && Nzones > Pzones) break; // done if (dirc < 0 && Nzones < Pzones) break; } zdialog_stuff(zd,"zone count",Nzones); // update zdialog widget if (zones) zfree(zones); // allocate zone memory zones = (zone_t *) zmalloc(Nzones * sizeof(zone_t)); zww = e3ww / zcols; // zone width, height zhh = e3hh / zrows; nn = e3ww * e3hh * 25; // 25 neighbor zones per pixel if (zoneindex) zfree(zoneindex); // allocate pixel zone index zoneindex = (int16 *) zmalloc(nn * sizeof(int)); // 20.0 if (zoneweight) zfree(zoneweight); // allocate pixel zone weight zoneweight = (int16 *) zmalloc(nn * sizeof(float)); // 20.0 for (row = 0; row < zrows; row++) // loop all zones for (col = 0; col < zcols; col++) { xlo = col * zww; // zone low pixel ylo = row * zhh; cx = xlo + zww/2; // zone center pixel cy = ylo + zhh/2; ii = row * zcols + col; // zone index zones[ii].cx = cx; // zone center zones[ii].cy = cy; } return; } // thread function - multiple working threads to update image void * zretinex_thread(void *) { using namespace zretinex_names; void * zretinex_wthread1(void *arg); void * zretinex_wthread2(void *arg); void * zretinex_wthread3(void *arg); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 if (strmatch(thread_command,"apply")) // update zones and image { do_wthreads(zretinex_wthread1,NWT); // compute zone RGB levels and weights do_wthreads(zretinex_wthread2,NWT); // compute new pixel RGB values thread_command = "blend"; // auto blend after } if (strmatch(thread_command,"blend")) do_wthreads(zretinex_wthread3,NWT); // blend input and retinex images CEF->Fmods++; // image modified CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed, stop warning } // worker thread to compute zone RGB levels and weights per image pixel void * zretinex_wthread1(void *arg) { using namespace zretinex_names; int index = *((int *) arg); int px, py, row, col; int xlo, xhi, ylo, yhi, cx, cy; int ii, jj, kk; float minR, minG, minB, maxR, maxG, maxB; float dist, maxdist, weight, sumweight; float *pix; for (row = index; row < zrows; row += NWT) // loop all zones for (col = 0; col < zcols; col++) { xlo = col * zww; // zone low pixel ylo = row * zhh; xhi = xlo + zww; // zone high pixel yhi = ylo + zhh; minR = minG = minB = 256; maxR = maxG = maxB = 0; for (py = ylo; py < yhi; py++) // find min/max RGB in zone for (px = xlo; px < xhi; px++) { pix = PXMpix(E1pxm,px,py); if (pix[0] < minR) minR = pix[0]; if (pix[1] < minG) minG = pix[1]; if (pix[2] < minB) minB = pix[2]; if (pix[0] > maxR) maxR = pix[0]; if (pix[1] > maxG) maxG = pix[1]; if (pix[2] > maxB) maxB = pix[2]; } ii = row * zcols + col; // zone index zones[ii].minR = minR; // zone RGB range zones[ii].minG = minG; zones[ii].minB = minB; zones[ii].maxR = maxR; zones[ii].maxG = maxG; zones[ii].maxB = maxB; } for (py = index; py < e3hh; py += NWT) // loop all image pixels for (px = 0; px < e3ww; px++) { row = py / zhh; // zone containing pixel col = px / zww; ii = (py * e3ww + px) * 25; // base zone index for pixel for (jj = row-2; jj <= row+2; jj++) // loop 25 neighboring zones for (kk = col-2; kk <= col+2; kk++) { if (jj >= 0 && jj < zrows && kk >= 0 && kk < zcols) // neighbor zone number zoneindex[ii] = jj * zcols + kk; // --> pixel zone index else zoneindex[ii] = -1; // pixel zone on edge, missing neighbor ii++; } } if (zww < zhh) maxdist = 2.5 * zww; else maxdist = 2.5 * zhh; for (py = index; py < e3hh; py += NWT) // loop all image pixels for (px = 0; px < e3ww; px++) { row = py / zhh; // zone containing pixel col = px / zww; ii = (py * e3ww + px) * 25; // base zone index for pixel for (jj = 0; jj < 25; jj++) // loop neighbor zones { kk = zoneindex[ii+jj]; if (kk < 0) { // neighbor missing zoneweight[ii+jj] = 0; continue; } cx = zones[kk].cx; // zone center cy = zones[kk].cy; dist = sqrtf((px-cx)*(px-cx) + (py-cy)*(py-cy)); // distance from (px,py) weight = 1.0 - dist / maxdist; // dist 0..max --> weight 1..0 if (weight < 0) weight = 0; zoneweight[ii+jj] = 1000.0 * weight; // scale 1.0 = 1000 20.0 } sumweight = 0; for (jj = 0; jj < 25; jj++) // get sum of zone weights sumweight += zoneweight[ii+jj]; for (jj = 0; jj < 25; jj++) // make weights add up to 1.0 zoneweight[ii+jj] = 1000.0 * zoneweight[ii+jj] / sumweight; // 1000 = 1.0 20.0 } pthread_exit(0); } // worker thread to compute new image RGB values void * zretinex_wthread2(void *arg) { using namespace zretinex_names; int index = *((int *) arg); int px, py, ii, jj, kk, dist = 0; float minR, minG, minB, maxR, maxG, maxB; float R1, G1, B1, R2, G2, B2; float weight, F, f1, f2; float *pix1, *pix2; for (py = index; py < e3hh; py += NWT) // loop all image pixels for (px = 0; px < e3ww; px++) { if (sa_stat == 3) { // select area active ii = py * e3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside area } minR = minG = minB = 0; maxR = maxG = maxB = 0; ii = (py * e3ww + px) * 25; // base zone index for pixel for (jj = 0; jj < 25; jj++) // loop neighbor zones { kk = zoneindex[ii+jj]; // zone number if (kk < 0) continue; weight = 0.001 * zoneweight[ii+jj]; // zone weight (1000 = 1.0) 20.0 minR += weight * zones[kk].minR; // sum weighted RGB range minG += weight * zones[kk].minG; // for neighbor zones minB += weight * zones[kk].minB; maxR += weight * zones[kk].maxR; maxG += weight * zones[kk].maxG; maxB += weight * zones[kk].maxB; } pix1 = PXMpix(E1pxm,px,py); // input pixel pix2 = PXMpix(E9pxm,px,py); // output pixel R1 = pix1[0]; G1 = pix1[1]; B1 = pix1[2]; R2 = 255 * (R1 - minR) / (maxR - minR + 1); // avoid poss. divide by zero G2 = 255 * (G1 - minG) / (maxG - minG + 1); B2 = 255 * (B1 - minB) / (maxB - minB + 1); if (R2 < 0) R2 = 0; // stop underflow if (G2 < 0) G2 = 0; if (B2 < 0) B2 = 0; if (R2 > 255 || G2 > 255 || B2 > 255) { // stop overflow F = R2; if (G2 > F) F = G2; if (B2 > F) F = B2; F = 255 / F; R2 *= F; G2 *= F; B2 *= F; } if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth f2 = 1.0 - f1; R2 = f1 * R2 + f2 * R1; G2 = f1 * G2 + f2 * G1; B2 = f1 * B2 + f2 * B1; } pix2[0] = R2; pix2[1] = G2; pix2[2] = B2; } pthread_exit(0); } // worker thread to blend input image with retinex image // and attenuate bright pixels void * zretinex_wthread3(void *arg) { using namespace zretinex_names; int index = *((int *) arg); int px, py; int ii, dist = 0; float *pix1, *pix2, *pix3; float R1, G1, B1, R2, G2, B2, R3, G3, B3; float F1, F2, Lmax; for (py = index; py < e3hh; py += NWT) // loop all image pixels for (px = 0; px < e3ww; px++) { if (sa_stat == 3) { // select area active ii = py * e3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside area } pix1 = PXMpix(E1pxm,px,py); // input image pixel pix2 = PXMpix(E9pxm,px,py); // retinex image pixel pix3 = PXMpix(E3pxm,px,py); // output image pixel R1 = pix1[0]; // input color - original image G1 = pix1[1]; B1 = pix1[2]; R2 = pix2[0]; // input color - retinex image G2 = pix2[1]; B2 = pix2[2]; if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, F1 = sa_blendfunc(dist); // blend changes over sa_blendwidth F2 = 1.0 - F1; R2 = F1 * R2 + F2 * R1; G2 = F1 * G2 + F2 * G1; B2 = F1 * B2 + F2 * B1; } Lmax = R1; // max. RGB input if (G1 > Lmax) Lmax = G1; if (B1 > Lmax) Lmax = B1; Lmax = Lmax / 255.0; // scale 0 - 1 F1 = blend; // 0 ... 1 >> E1 ... E3 F2 = reducedark; F1 = F1 * (1.0 - F2 * (1.0 - Lmax)); // reduce F1 for high F2 * (1 - Lmax) F2 = reducebright; // 0 ... 1 F1 = F1 * (1.0 - F2 * Lmax); // reduce F1 for high F2 * Lmax R3 = F1 * R2 + (1.0 - F1) * R1; // output RGB = blended inputs G3 = F1 * G2 + (1.0 - F1) * G1; B3 = F1 * B2 + (1.0 - F1) * B1; pix3[0] = R3; // output RGB values pix3[1] = G3; pix3[2] = B3; } pthread_exit(0); } /********************************************************************************/ // image sharpen functions namespace sharpen_names { int E3ww, E3hh; // image dimensions int UM_radius, UM_amount, UM_thresh; int GR_amount, GR_thresh; int KH_radius; int MD_radius, MD_dark, MD_light, *MD_britemap; char sharp_function[4] = ""; int brhood_radius; float brhood_kernel[200][200]; // up to radius = 99 char brhood_method; // g = gaussian, f = flat distribution float *brhood_brightness; // neighborhood brightness per pixel VOL int sharp_cancel, brhood_cancel; // avoid GCC optimizing code away editfunc EFsharp; } // menu function void m_sharpen(GtkWidget *, cchar *menu) { using namespace sharpen_names; int sharp_dialog_event(zdialog *zd, cchar *event); void * sharp_thread(void *); int ii; F1_help_topic = "sharpen"; EFsharp.menuname = menu; EFsharp.menufunc = m_sharpen; EFsharp.funcname = "sharpen"; EFsharp.Farea = 2; // select area usable EFsharp.threadfunc = sharp_thread; // thread function EFsharp.Frestart = 1; // allow restart EFsharp.FusePL = 1; // use with paint/lever edits OK EFsharp.Fscript = 1; // scripting supported if (! edit_setup(EFsharp)) return; // setup edit E3ww = E3pxm->ww; // image dimensions E3hh = E3pxm->hh; /*** _________________________________________ | Sharpen | | | | [_] unsharp mask radius [__] | | amount [__] | | threshold [__] | | | | [_] gradient amount [__] | | threshold [__] | | | | [_] Kuwahara radius [__] | | | | [_] median diff radius [__] | | dark [__] | | light [__] | | | | [reset] [apply] [done] [cancel] | |_________________________________________| ***/ zdialog *zd = zdialog_new(E2X("Sharpen"),Mwin,Breset,Bapply,Bdone,Bcancel,null); EFsharp.zd = zd; zdialog_add_widget(zd,"hbox","hbum","dialog",0,"space=5"); // unsharp mask zdialog_add_widget(zd,"vbox","vb21","hbum",0,"space=2"); zdialog_add_widget(zd,"label","space","hbum",0,"expand"); zdialog_add_widget(zd,"vbox","vb22","hbum",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vb23","hbum",0,"homog|space=2"); zdialog_add_widget(zd,"check","UM","vb21",E2X("unsharp mask"),"space=5"); zdialog_add_widget(zd,"label","lab21","vb22",Bradius); zdialog_add_widget(zd,"label","lab22","vb22",Bamount); zdialog_add_widget(zd,"label","lab23","vb22",Bthresh); zdialog_add_widget(zd,"zspin","radiusUM","vb23","1|20|1|2"); zdialog_add_widget(zd,"zspin","amountUM","vb23","1|200|1|100"); zdialog_add_widget(zd,"zspin","threshUM","vb23","1|100|1|0"); zdialog_add_widget(zd,"hsep","sep3","dialog"); // gradient zdialog_add_widget(zd,"hbox","hbgr","dialog",0,"space=5"); zdialog_add_widget(zd,"vbox","vb31","hbgr",0,"space=2"); zdialog_add_widget(zd,"label","space","hbgr",0,"expand"); zdialog_add_widget(zd,"vbox","vb32","hbgr",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vb33","hbgr",0,"homog|space=2"); zdialog_add_widget(zd,"check","GR","vb31","gradient","space=5"); zdialog_add_widget(zd,"label","lab32","vb32",Bamount); zdialog_add_widget(zd,"label","lab33","vb32",Bthresh); zdialog_add_widget(zd,"zspin","amountGR","vb33","1|400|1|100"); zdialog_add_widget(zd,"zspin","threshGR","vb33","1|100|1|0"); zdialog_add_widget(zd,"hsep","sep4","dialog"); // kuwahara zdialog_add_widget(zd,"hbox","hbku","dialog",0,"space=5"); zdialog_add_widget(zd,"check","KH","hbku","Kuwahara","space=3"); zdialog_add_widget(zd,"label","space","hbku",0,"expand"); zdialog_add_widget(zd,"label","lab42","hbku",Bradius,"space=3"); zdialog_add_widget(zd,"zspin","radiusKH","hbku","1|9|1|1"); zdialog_add_widget(zd,"hsep","sep5","dialog"); // median diff zdialog_add_widget(zd,"hbox","hbmd","dialog",0,"space=5"); zdialog_add_widget(zd,"vbox","vb51","hbmd",0,"space=2"); zdialog_add_widget(zd,"label","space","hbmd",0,"expand"); zdialog_add_widget(zd,"vbox","vb52","hbmd",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vb53","hbmd",0,"homog|space=2"); zdialog_add_widget(zd,"check","MD","vb51",E2X("median diff"),"space=5"); zdialog_add_widget(zd,"label","lab51","vb52",Bradius); zdialog_add_widget(zd,"label","lab52","vb52",E2X("dark")); zdialog_add_widget(zd,"label","lab53","vb52",E2X("light")); zdialog_add_widget(zd,"zspin","radiusMD","vb53","1|20|1|3"); zdialog_add_widget(zd,"zspin","darkMD","vb53","0|50|1|1"); zdialog_add_widget(zd,"zspin","lightMD","vb53","0|50|1|1"); zdialog_restore_inputs(zd); zdialog_fetch(zd,"UM",ii); // set function from checkboxes if (ii) strcpy(sharp_function,"UM"); zdialog_fetch(zd,"GR",ii); if (ii) strcpy(sharp_function,"GR"); zdialog_fetch(zd,"KH",ii); if (ii) strcpy(sharp_function,"KH"); zdialog_fetch(zd,"MD",ii); if (ii) strcpy(sharp_function,"MD"); zdialog_run(zd,sharp_dialog_event,"save"); // run dialog - parallel return; } // dialog event and completion callback function int sharp_dialog_event(zdialog *zd, cchar *event) // reworked for script files { using namespace sharpen_names; if (strmatch(event,"focus")) return 1; zdialog_fetch(zd,"radiusUM",UM_radius); // get all parameters zdialog_fetch(zd,"amountUM",UM_amount); zdialog_fetch(zd,"threshUM",UM_thresh); zdialog_fetch(zd,"amountGR",GR_amount); zdialog_fetch(zd,"threshGR",GR_thresh); zdialog_fetch(zd,"radiusKH",KH_radius); zdialog_fetch(zd,"radiusMD",MD_radius); zdialog_fetch(zd,"darkMD",MD_dark); zdialog_fetch(zd,"lightMD",MD_light); if (strmatch(event,"apply")) zd->zstat = 2; // from script file if (strmatch(event,"done")) zd->zstat = 3; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 4; // from f_open() sharp_cancel = 0; if (zd->zstat) { if (zd->zstat == 1) { // reset zd->zstat = 0; edit_reset(); return 1; } if (zd->zstat == 2) { // apply zd->zstat = 0; if (*sharp_function) signal_thread(); else zmessageACK(Mwin,Bnoselection); // no choice made return 1; } if (zd->zstat == 3) { edit_done(0); // done return 1; } sharp_cancel = 1; // cancel or [x] edit_cancel(0); // discard edit return 1; } if (strmatch(event,"blendwidth")) signal_thread(); if (strmatchV(event,"UM","GR","KH","MD",null)) { zdialog_stuff(zd,"UM",0); // make checkboxes like radio buttons zdialog_stuff(zd,"GR",0); zdialog_stuff(zd,"KH",0); zdialog_stuff(zd,"MD",0); zdialog_stuff(zd,event,1); strcpy(sharp_function,event); // set chosen method } return 1; } // sharpen image thread function void * sharp_thread(void *) { using namespace sharpen_names; int sharp_UM(void); int sharp_GR(void); int sharp_KH(void); int sharp_MD(void); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 if (strmatch(sharp_function,"UM")) sharp_UM(); if (strmatch(sharp_function,"GR")) sharp_GR(); if (strmatch(sharp_function,"KH")) sharp_KH(); if (strmatch(sharp_function,"MD")) sharp_MD(); CEF->Fmods++; CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); } return 0; // not executed, stop g++ warning } // image sharpen function using unsharp mask int sharp_UM() { using namespace sharpen_names; void britehood(int radius, char method); // compute neighborhood brightness void * sharp_UM_wthread(void *arg); int cc; cc = E3ww * E3hh * sizeof(float); brhood_brightness = (float *) zmalloc(cc); britehood(UM_radius,'f'); if (sharp_cancel) return 1; if (sa_stat == 3) Fbusy_goal = sa_Npixel; else Fbusy_goal = E3ww * E3hh; Fbusy_done = 0; do_wthreads(sharp_UM_wthread,NWT); // worker threads Fbusy_goal = 0; zfree(brhood_brightness); return 1; } void * sharp_UM_wthread(void *arg) // worker thread function { using namespace sharpen_names; 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 < E3hh; py += NWT) // loop all image3 pixels for (px = 0; px < E3ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside 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 * E3ww + 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_blendwidth) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth 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; } pthread_exit(0); } // sharpen image by increasing brightness gradient int sharp_GR() { using namespace sharpen_names; void * sharp_GR_wthread(void *arg); if (sa_stat == 3) Fbusy_goal = sa_Npixel; else Fbusy_goal = E3ww * E3hh; Fbusy_done = 0; do_wthreads(sharp_GR_wthread,NWT); // worker threads Fbusy_goal = 0; return 1; } // callable sharp_GR() used by trim/rotate function // returns E3 = sharpened E3 void sharp_GR_callable(int amount, int thresh) { using namespace sharpen_names; PXM *PXMtemp = E1pxm; // save E1 E1pxm = PXM_copy(E3pxm); // copy E3 > E1 E3ww = E3pxm->ww; E3hh = E3pxm->hh; GR_amount = amount; GR_thresh = thresh; sharp_GR(); // E3 = sharpened E1 PXM_free(E1pxm); E1pxm = PXMtemp; // restore org. E1 return; } void * sharp_GR_wthread(void *arg) // worker thread function { using namespace sharpen_names; float *pix1, *pix3; int ii, px, py, dist = 0; int nc = E1pxm->nc; float amount, thresh; float b1, b1x, b1y, b3x, b3y, b3, bf, f1, f2; float red1, green1, blue1, red3, green3, blue3; int index = *((int *) arg); amount = 1 + 0.01 * GR_amount; // 1.0 - 5.0 thresh = GR_thresh; // 0 - 100 for (py = index + 1; py < E3hh; py += NWT) // loop all image pixels for (px = 1; px < E3ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel is outside area } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel b1 = pixbright(pix1); // pixel brightness, 0 - 256 if (b1 == 0) continue; // black, don't change b1x = b1 - pixbright(pix1-nc); // horiz. brightness gradient b1y = b1 - pixbright(pix1-nc * E3ww); // 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 * E3ww) + 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_blendwidth) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth 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; } pthread_exit(0); } // sharpen edges using the Kuwahara algorithm int sharp_KH() { using namespace sharpen_names; void * sharp_KH_wthread(void *arg); if (sa_stat == 3) Fbusy_goal = sa_Npixel; else Fbusy_goal = E3ww * E3hh; Fbusy_done = 0; do_wthreads(sharp_KH_wthread,NWT); // worker threads Fbusy_goal = 0; return 1; } void * sharp_KH_wthread(void *arg) // worker thread function { using namespace sharpen_names; 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 < E3hh-rad; py += NWT) // loop all image pixels for (px = rad; px < E3ww-rad; px++) { if (sa_stat == 3) { // select area active ii = py * E3ww + 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_blendwidth) { // if select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth 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; } pthread_exit(0); } // sharpen edges using the median difference algorithm int sharp_MD() { using namespace sharpen_names; void * sharp_MD_wthread(void *arg); int px, py, ii; float *pix1; MD_britemap = (int *) zmalloc(E3ww * E3hh * sizeof(int)); for (py = 0; py < E3hh; py++) // loop all pixels for (px = 0; px < E3ww; px++) { pix1 = PXMpix(E1pxm,px,py); // initz. pixel brightness map ii = py * E3ww + px; MD_britemap[ii] = pixbright(pix1); } if (sa_stat == 3) Fbusy_goal = sa_Npixel; else Fbusy_goal = E3ww * E3hh; Fbusy_done = 0; do_wthreads(sharp_MD_wthread,NWT); Fbusy_goal = 0; zfree(MD_britemap); return 1; } void * sharp_MD_wthread(void *arg) // worker thread function { using namespace sharpen_names; int index = *((int *) arg); int rad, dark, light, *britemap; int ii, px, py, dist = 0; int dy, dx, ns; float R, G, B, R2, G2, B2; float F, f1, f2; float *pix1, *pix3; int bright, median; int bsortN[1681]; // radius <= 20 (41 x 41 pixels) rad = MD_radius; // parameters from dialog dark = MD_dark; light = MD_light; britemap = MD_britemap; for (py = index+rad; py < E3hh-rad; py += NWT) // loop all image3 pixels for (px = rad; px < E3ww-rad; px++) { if (sa_stat == 3) { // select area active ii = py * E3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix1 = PXMpix(E1pxm,px,py); // source pixel pix3 = PXMpix(E3pxm,px,py); // target pixel ns = 0; for (dy = py-rad; dy <= py+rad; dy++) // loop surrounding pixels for (dx = px-rad; dx <= px+rad; dx++) // get brightness values { ii = dy * E3ww + dx; bsortN[ns] = britemap[ii]; ns++; } HeapSort(bsortN,ns); // sort the pixels median = bsortN[ns/2]; // median brightness R = pix3[0]; G = pix3[1]; B = pix3[2]; bright = pixbright(pix3); if (bright < median) { F = 1.0 - 0.1 * dark * (median - bright) / (median + 50); R2 = R * F; G2 = G * F; B2 = B * F; if (R2 > 0 && G2 > 0 && B2 > 0) { R = R2; G = G2; B = B2; } } if (bright > median) { F = 1.0 + 0.03 * light * (bright - median) / (median + 50); R2 = R * F; G2 = G * F; B2 = B * F; if (R2 < 255 && G2 < 255 && B2 < 255) { R = R2; G = G2; B = B2; } } if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth f2 = 1.0 - f1; R = f1 * R + f2 * pix1[0]; G = f1 * G + f2 * pix1[1]; B = f1 * B + f2 * pix1[2]; } pix3[0] = R; pix3[1] = G; pix3[2] = B; Fbusy_done++; // track progress } pthread_exit(0); } // Compute the mean brightness of all pixel neighborhoods, // using a Gaussian 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 Gaussian method. void britehood(int radius, char method) { using namespace sharpen_names; void * brhood_wthread(void *arg); int rad, radflat2, dx, dy; 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 = E3ww * E3hh; Fbusy_done = 0; do_wthreads(brhood_wthread,NWT); // worker threads Fbusy_goal = 0; return; } // worker thread function void * brhood_wthread(void *arg) { using namespace sharpen_names; 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 < E3hh; py += NWT) for (px = 0; px < E3ww; px++) { if (sa_stat == 3 && sa_mode != mode_image) { // select area, not whole image ii = py * E3ww + 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 > E3hh-1) continue; if (qx < 0 || qx > E3ww-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 * E3ww + 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 < E3hh; py += NWT) for (px = 0; px < E3ww; px++) { if (sa_stat == 3 && sa_mode != mode_image) { // select area, not whole image ii = py * E3ww + 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 > E3hh-1) continue; if (qx < 0 || qx > E3ww-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 > E3hh-1) continue; pixel = PXMpix(E1pxm,qx,qy); bsum -= pixbright(pixel); bsamp -= 1; } } qx = px+rad; // add last column if (qx < E3ww) { for (qy = py-rad; qy <= py+rad; qy++) { if (qy < 0 || qy > E3hh-1) continue; pixel = PXMpix(E1pxm,qx,qy); bsum += pixbright(pixel); bsamp += 1; } } } bmean = bsum / bsamp; // mean brightness ii = py * E3ww + px; brhood_brightness[ii] = bmean; Fbusy_done++; // track progress if (sharp_cancel) break; } } pthread_exit(0); } /********************************************************************************/ // image blur function namespace blur_names { int Fnormblur; // normal blur float Nblur_radius; // blur radius float blur_weight[1415]; // blur radius limit 999 int Fradblur; // radial blur int RBlen; // radial blur length int Cx, Cy; // image center of radial blur int Fdirblur; // directed blur float Dmdx, Dmdy, Dmdw, Dmdh; float DD, Dspan, Dintens; int Fgradblur; // graduated blur float gblur_radius; // blur radius int con_limit; // contrast limit uint8 *pixcon; int pixseq_done[122][122]; // up to gblur_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 int Fpaintblur; // paint blur int pmode = 1; // 1/2 = blend/restore int pblur_radius = 20; // mouse radius float powcent, powedge; // power at center and edge float kernel[402][402]; // radius limit 200 int mousex, mousey; // mouse click/drag position int Fblurbackground; // blur background via select area editfunc EFblur; VOL int Fcancel; PXM *E2pxm; int E3ww, E3hh; // image dimensions } // menu function void m_blur(GtkWidget *, cchar *menu) // consolidate all blur functions { using namespace blur_names; int blur_dialog_event(zdialog *zd, cchar *event); void blur_mousefunc(); void * blur_thread(void *); cchar *radblur_tip = E2X("Click to set center"); cchar *dirblur_tip = E2X("Pull image using the mouse"); cchar *paintblur_tip = E2X("left drag: blend image \n" "right drag: restore image"); F1_help_topic = "blur"; EFblur.menuname = menu; EFblur.menufunc = m_blur; EFblur.funcname = "blur"; EFblur.Farea = 2; // select area usable EFblur.threadfunc = blur_thread; // thread function EFblur.mousefunc = blur_mousefunc; EFblur.Frestart = 1; // allow restart if (! edit_setup(EFblur)) return; // setup edit /*** _________________________________________ | Blur Image | | | | [x] Normal Blur Radius [____] | | - - - - - - - - - - - - - - - - - - | | [x] Radial Blur | | Length [___] Center X [___] Y [___] | | - - - - - - - - - - - - - - - - - - | | [X] Directed Blur | | Blur Span [___] Intensity [___] | | - - - - - - - - - - - - - - - - - - | | [X] Graduated Blur | | Radius [___] Contrast Limit [___] | | - - - - - - - - - - - - - - - - - - | | [X] Paint Blur | | Radius [___] Power [___] Edge [___] | | - - - - - - - - - - - - - - - - - - | | [X] Blur Background | | | | [Reset] [Apply] [Done] [Cancel] | |_________________________________________| ***/ zdialog *zd = zdialog_new(E2X("Blur Radius"),Mwin,Breset,Bapply,Bdone,Bcancel,null); EFblur.zd = zd; zdialog_add_widget(zd,"hbox","hbnb","dialog"); zdialog_add_widget(zd,"check","Fnormblur","hbnb",E2X("Normal Blur"),"space=2"); zdialog_add_widget(zd,"label","space","hbnb",0,"space=5"); zdialog_add_widget(zd,"label","labrad","hbnb",Bradius,"space=5"); zdialog_add_widget(zd,"zspin","Nblur_radius","hbnb","1|999|1|10","space=5|size=3"); zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbrb1","dialog"); zdialog_add_widget(zd,"check","Fradblur","hbrb1",E2X("Radial Blur"),"space=2"); zdialog_add_widget(zd,"hbox","hbrb2","dialog"); zdialog_add_widget(zd,"label","labrbl","hbrb2",Blength,"space=5"); zdialog_add_widget(zd,"zspin","RBlen","hbrb2","1|999|1|100","space=3|size=3"); zdialog_add_widget(zd,"label","space","hbrb2",0,"space=5"); zdialog_add_widget(zd,"label","labc","hbrb2",Bcenter,"space=3"); zdialog_add_widget(zd,"label","labcx","hbrb2","X","space=3"); zdialog_add_widget(zd,"zentry","Cx","hbrb2",0,"space=3|size=3"); zdialog_add_widget(zd,"label","space","hbrb2",0,"space=3"); zdialog_add_widget(zd,"label","labcy","hbrb2","Y","space=3"); zdialog_add_widget(zd,"zentry","Cy","hbrb2",0,"space=3|size=3"); zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbdb1","dialog"); zdialog_add_widget(zd,"check","Fdirblur","hbdb1",E2X("Directed Blur"),"space=2"); zdialog_add_widget(zd,"hbox","hbdb2","dialog"); zdialog_add_widget(zd,"label","labspan","hbdb2",E2X("Blur Span"),"space=5"); zdialog_add_widget(zd,"zspin","span","hbdb2","0.00|1.0|0.01|0.1","space=3|size=3"); zdialog_add_widget(zd,"label","space","hbdb2",0,"space=5"); zdialog_add_widget(zd,"label","labint","hbdb2",E2X("Intensity")); zdialog_add_widget(zd,"zspin","intens","hbdb2","0.00|1.0|0.01|0.2","space=3|size=3"); zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbgb1","dialog"); zdialog_add_widget(zd,"check","Fgradblur","hbgb1",E2X("Graduated Blur"),"space=2"); zdialog_add_widget(zd,"hbox","hbgb2","dialog"); zdialog_add_widget(zd,"label","labgrad","hbgb2",Bradius,"space=5"); zdialog_add_widget(zd,"zspin","gblur_radius","hbgb2","1|50|1|10","space=3|size=3"); zdialog_add_widget(zd,"label","space","hbgb2",0,"space=5"); zdialog_add_widget(zd,"label","lablim","hbgb2",E2X("Contrast Limit")); zdialog_add_widget(zd,"zspin","con_limit","hbgb2","1|255|1|50","space=3|size=3"); zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbpb1","dialog"); zdialog_add_widget(zd,"check","Fpaintblur","hbpb1",E2X("Paint Blur"),"space=2"); zdialog_add_widget(zd,"hbox","hbpb2","dialog"); zdialog_add_widget(zd,"label","labpaint","hbpb2",Bradius,"space=5"); zdialog_add_widget(zd,"zspin","pblur_radius","hbpb2","2|200|1|20","space=3|size=3"); zdialog_add_widget(zd,"label","space","hbpb2",0,"space=5"); zdialog_add_widget(zd,"label","labpow","hbpb2",E2X("Power")); zdialog_add_widget(zd,"zspin","powcent","hbpb2","0|100|1|30","space=3|size=3"); zdialog_add_widget(zd,"label","space","hbpb2",0,"space=5"); zdialog_add_widget(zd,"label","labedge","hbpb2",Bedge); zdialog_add_widget(zd,"zspin","powedge","hbpb2","0|100|1|10","space=3|size=3"); zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbbg","dialog"); zdialog_add_widget(zd,"check","Fblurbackground","hbbg",E2X("Blur Background"),"space=2"); zdialog_add_ttip(zd,"Fradblur",radblur_tip); zdialog_add_ttip(zd,"Fdirblur",dirblur_tip); zdialog_add_ttip(zd,"Fpaintblur",paintblur_tip); E3ww = E3pxm->ww; // image dimensions E3hh = E3pxm->hh; Fcancel = 0; // initial status E2pxm = 0; Fnormblur = 0; // default settings Nblur_radius = 10; Fradblur = 0; RBlen = 100; Cx = E3ww / 2; Cy = E3hh / 2; zdialog_stuff(zd,"Cx",Cx); zdialog_stuff(zd,"Cy",Cy); Fdirblur = 0; Dspan = 0.1; Dintens = 0.2; Fgradblur = 0; con_limit = 1; gblur_radius = 10; Fpaintblur = 0; pmode = 1; pblur_radius = 20; powcent = 30; powedge = 10; zdialog_restore_inputs(zd); zdialog_stuff(zd,"Fblurbackground",0); // no blur background at start Fblurbackground = 0; zdialog_run(zd,blur_dialog_event,"save"); // run dialog - parallel zdialog_send_event(zd,"pblur_radius"); // get kernel initialized return; } // dialog event and completion callback function int blur_dialog_event(zdialog * zd, cchar *event) { using namespace blur_names; void blur_mousefunc(); float frad, kern; int rad, dx, dy; if (strmatch(event,"done")) zd->zstat = 3; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 4; // from f_open() if (zstrstr("Fnormblur Fradblur Fdirblur Fgradblur " // checkboxes work like radio buttons "Fpaintblur Fblurbackground",event)) { zdialog_stuff(zd,"Fnormblur",0); zdialog_stuff(zd,"Fradblur",0); zdialog_stuff(zd,"Fdirblur",0); zdialog_stuff(zd,"Fgradblur",0); zdialog_stuff(zd,"Fpaintblur",0); zdialog_stuff(zd,"Fblurbackground",0); zdialog_stuff(zd,event,1); zdialog_fetch(zd,"Fnormblur",Fnormblur); zdialog_fetch(zd,"Fradblur",Fradblur); zdialog_fetch(zd,"Fdirblur",Fdirblur); zdialog_fetch(zd,"Fgradblur",Fgradblur); zdialog_fetch(zd,"Fpaintblur",Fpaintblur); zdialog_fetch(zd,"Fblurbackground",Fblurbackground); } if (Fradblur || Fdirblur) // connect mouse takeMouse(blur_mousefunc,dragcursor); else if (Fpaintblur) takeMouse(blur_mousefunc,0); else freeMouse(); zdialog_fetch(zd,"Fnormblur",Fnormblur); // get all dialog inputs zdialog_fetch(zd,"Nblur_radius",Nblur_radius); zdialog_fetch(zd,"Fradblur",Fradblur); zdialog_fetch(zd,"RBlen",RBlen); zdialog_fetch(zd,"Cx",Cx); zdialog_fetch(zd,"Cy",Cy); zdialog_fetch(zd,"Fdirblur",Fdirblur); zdialog_fetch(zd,"span",Dspan); zdialog_fetch(zd,"intens",Dintens); zdialog_fetch(zd,"Fgradblur",Fgradblur); zdialog_fetch(zd,"gblur_radius",gblur_radius); zdialog_fetch(zd,"con_limit",con_limit); zdialog_fetch(zd,"Fpaintblur",Fpaintblur); zdialog_fetch(zd,"pblur_radius",pblur_radius); zdialog_fetch(zd,"powcent",powcent); zdialog_fetch(zd,"powedge",powedge); zdialog_fetch(zd,"Fblurbackground",Fblurbackground); if (Fblurbackground) { // cancel and start new function Fcancel = 1; edit_cancel(0); if (E2pxm) PXM_free(E2pxm); E2pxm = 0; m_blur_background(0,0); return 1; // 19.0 } if (zstrstr("pblur_radius powcent powedge",event)) // paint blur parameters { zdialog_fetch(zd,"pblur_radius",pblur_radius); // mouse radius zdialog_fetch(zd,"powcent",powcent); // center transparency zdialog_fetch(zd,"powedge",powedge); // powedge transparency powcent = 0.01 * powcent; // scale 0 ... 1 powedge = 0.01 * powedge; rad = pblur_radius; for (dy = -rad; dy <= rad; dy++) // build kernel for (dx = -rad; dx <= rad; dx++) { frad = sqrt(dx*dx + dy*dy); kern = (rad - frad) / rad; // center ... powedge >> 1 ... 0 kern = kern * (powcent - powedge) + powedge; // strength center ... powedge if (kern < 0) kern = 0; if (kern > 1) kern = 1; if (frad > rad) kern = 2; // beyond radius, within square kernel[dx+rad][dy+rad] = kern; } } if (zd->zstat) { if (zd->zstat == 1) // [reset] { zd->zstat = 0; // keep dialog active edit_reset(); } else if (zd->zstat == 2) // [apply] { zd->zstat = 0; // keep dialog active signal_thread(); // trigger thread return 1; // do not free E2 19.0 } else if (zd->zstat == 3) // [done] edit_done(0); else { // [cancel] Fcancel = 1; edit_cancel(0); // discard edit } if (E2pxm) PXM_free(E2pxm); // free memory E2pxm = 0; } return 1; } // blur mouse function void blur_mousefunc() // mouse function { using namespace blur_names; if (! CEF) return; if (Fnormblur) { freeMouse(); return; } if (Fradblur && LMclick) // radial blur, new center { zdialog *zd = CEF->zd; Cx = Mxposn; Cy = Myposn; zdialog_stuff(zd,"Cx",Cx); zdialog_stuff(zd,"Cy",Cy); LMclick = 0; signal_thread(); } if (Fdirblur && (Mxdrag || Mydrag)) // directed blur, mouse drag { Dmdx = Mxdown; // drag origin Dmdy = Mydown; Dmdw = Mxdrag - Mxdown; // drag increment Dmdh = Mydrag - Mydown; Mxdrag = Mydrag = 0; signal_thread(); } if (Fpaintblur) // paint blur { int px, py, rr; if (LMclick || RMclick) // mouse click { if (LMclick) pmode = 1; // left click, paint if (RMclick) pmode = 2; // right click, erase mousex = Mxclick; mousey = Myclick; signal_thread(); } else if (Mxdrag || Mydrag) // mouse drag in progress { if (Mbutton == 1) pmode = 1; // left drag, paint if (Mbutton == 3) pmode = 2; // right drag, erase mousex = Mxdrag; mousey = Mydrag; signal_thread(); } cairo_t *cr = draw_context_create(gdkwin,draw_context); px = mousex - pblur_radius - 1; // repaint modified area py = mousey - pblur_radius - 1; rr = 2 * pblur_radius + 3; Fpaint3(px,py,rr,rr,cr); draw_mousecircle(Mxposn,Myposn,pblur_radius,0,cr); // redraw mouse circle draw_context_destroy(draw_context); LMclick = RMclick = Mxdrag = Mydrag = 0; // reset mouse } return; } // image blur thread function void * blur_thread(void *) { using namespace blur_names; void * normblur_wthread(void *); void * radblur_wthread(void *); void * dirblur_wthread(void *); void * gradblur_wthread(void *); void * paintblur_wthread(void *); int ii, jj; float rad, w, wsum; float dd, d1, d2, d3, d4; int px, py, dx, dy, adx, ady; float *pix1, *pix2; float contrast, maxcon; float rad1, rad2, angle, astep; while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 if (Fnormblur) // normal blur { if (E2pxm) PXM_free(E2pxm); E2pxm = PXM_copy(E1pxm); // intermediate image rad = Nblur_radius; wsum = 0; for (ii = 0; ii < rad; ii++) // set pixel weight per distance { // example, rad = 10 w = 1.0 - ii / rad; // dist: 0 1 2 3 5 7 9 w = w * w; // weight: 1 .81 .64 .49 .25 .09 .01 blur_weight[ii] = w; wsum += w; } for (ii = 0; ii < rad; ii++) // make weights sum to 1.0 blur_weight[ii] = blur_weight[ii] / wsum; if (sa_stat == 3) Fbusy_goal = sa_Npixel; // setup progress counter else Fbusy_goal = E3ww * E3hh; Fbusy_goal = 2 * Fbusy_goal; Fbusy_done = 0; do_wthreads(normblur_wthread,NWT); // worker threads } if (Fradblur) // radial blur { if (E2pxm) PXM_free(E2pxm); if (Fnormblur) // if normal blur done before, E2pxm = PXM_copy(E3pxm); // use the blur output image else E2pxm = PXM_copy(E1pxm); // else use the original image if (sa_stat == 3) Fbusy_goal = sa_Npixel; // setup progress counter else Fbusy_goal = E3ww * E3hh; Fbusy_done = 0; do_wthreads(radblur_wthread,NWT); // worker threads } if (Fdirblur) // directed blur { d1 = (Dmdx-0) * (Dmdx-0) + (Dmdy-0) * (Dmdy-0); // distance, mouse to 4 corners d2 = (E3ww-Dmdx) * (E3ww-Dmdx) + (Dmdy-0) * (Dmdy-0); d3 = (E3ww-Dmdx) * (E3ww-Dmdx) + (E3hh-Dmdy) * (E3hh-Dmdy); d4 = (Dmdx-0) * (Dmdx-0) + (E3hh-Dmdy) * (E3hh-Dmdy); dd = d1; if (d2 > dd) dd = d2; // find greatest corner distance if (d3 > dd) dd = d3; if (d4 > dd) dd = d4; DD = dd * 0.5 * Dspan; do_wthreads(dirblur_wthread,NWT); // worker threads } if (Fgradblur) // graduated blur { pixcon = (uint8 *) zmalloc(E3ww * E3hh); // pixel contrast map for (py = 1; py < E3hh-1; py++) // loop interior pixels for (px = 1; px < E3ww-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 = 1.0 - PIXMATCH(pix1,pix2); // contrast, 0-1 if (contrast > maxcon) maxcon = contrast; } ii = py * E3ww + px; // ii maps to (px,py) pixcon[ii] = 255 * maxcon; // pixel contrast, 0 to 255 } rad1 = gblur_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 = E3ww * E3hh; Fbusy_done = 0; do_wthreads(gradblur_wthread,NWT); // worker threads zfree(pixcon); } if (Fpaintblur) do_wthreads(paintblur_wthread,NWT); // worker threads Fbusy_goal = 0; CEF->Fmods++; CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); } return 0; // not executed, stop g++ warning } // normal blur worker thread void * normblur_wthread(void *arg) { using namespace blur_names; int index = *((int *) arg); int rad = Nblur_radius; int ii, dist = 0; int px, py, qx, qy; int ylo, yhi, xlo, xhi; float R, G, B, w1, w2, f1, f2; float *pix1, *pix3, *pix2; for (py = index; py < E3hh; py += NWT) // loop all image pixels for (px = 0; px < E3ww; px++) { if (Fcancel) pthread_exit(0); // user cancel ii = py * E3ww + px; 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 pix2 = PXMpix(E2pxm,px,py); // target pixel - intermediate image ylo = py - rad; if (ylo < 0) ylo = 0; yhi = py + rad; if (yhi > E3hh-1) yhi = E3hh - 1; R = G = B = 0; w2 = 0; for (qy = ylo; qy <= yhi; qy++) // loop pixels in same column { if (sa_stat == 3) { ii = qy * E3ww + px; // don't use pixels outside area dist = sa_pixmap[ii]; if (! dist) continue; } w1 = blur_weight[abs(qy-py)]; // weight based on radius pix1 = PXMpix(E1pxm,px,qy); R += w1 * pix1[0]; // accumulate RGB * weight G += w1 * pix1[1]; B += w1 * pix1[2]; w2 += w1; // accumulate weights } R = R / w2; // normalize G = G / w2; B = B / w2; pix2[0] = R; // weighted average pix2[1] = G; pix2[2] = B; Fbusy_done++; // track progress } for (py = index; py < E3hh; py += NWT) // loop all image pixels for (px = 0; px < E3ww; px++) { if (Fcancel) pthread_exit(0); // user cancel ii = py * E3ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix2 = PXMpix(E2pxm,px,py); // source pixel - intermediate image pix3 = PXMpix(E3pxm,px,py); // target pixel - final image xlo = px - rad; if (xlo < 0) xlo = 0; xhi = px + rad; if (xhi > E3ww-1) xhi = E3ww - 1; R = G = B = 0; w2 = 0; for (qx = xlo; qx <= xhi; qx++) // loop pixels in same row { if (sa_stat == 3) { ii = py * E3ww + qx; // don't use pixels outside area dist = sa_pixmap[ii]; if (! dist) continue; } w1 = blur_weight[abs(qx-px)]; // weight based on radius pix2 = PXMpix(E2pxm,qx,py); R += w1 * pix2[0]; // accumulate RGB * weight G += w1 * pix2[1]; B += w1 * pix2[2]; w2 += w1; // accumulate weights } R = R / w2; // normalize G = G / w2; B = B / w2; pix3[0] = R; // weighted average pix3[1] = G; pix3[2] = B; Fbusy_done++; // track progress } if (sa_stat == 3 && sa_blendwidth > 0) // select area has edge blend { for (py = index; py < E3hh; py += NWT) // loop all image pixels for (px = 0; px < E3ww; px++) { ii = py * E3ww + px; dist = sa_pixmap[ii]; if (! dist) continue; // omit pixels outside area if (dist >= sa_blendwidth) continue; // omit if > blendwidth from edge pix1 = PXMpix(E1pxm,px,py); // source pixel pix3 = PXMpix(E3pxm,px,py); // target pixel f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth 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]; } } pthread_exit(0); } // radial blur worker thread void * radblur_wthread(void *arg) { using namespace blur_names; int index = *((int *) arg); int ii, dist = 0; int px, py, qx, qy, qz; float *pix2, *pix3; float R, Rx, Ry, Rz; float f1, f2; int Rsum, Gsum, Bsum, Npix; for (py = index+1; py < E3hh-1; py += NWT) // loop all image pixels for (px = 1; px < E3ww-1; px++) { if (Fcancel) pthread_exit(0); // user cancel ii = py * E3ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } Rsum = Gsum = Bsum = Npix = 0; // reset RGB sums R = sqrtf((px-Cx)*(px-Cx) + (py-Cy)*(py-Cy)); // distance (Cx,Cy) to (px,py) if (R == 0) continue; Rx = (px-Cx)/R; // unit vector along (Cx,Cy) to (px,py) Ry = (py-Cy)/R; if (fabsf(Rx) > fabsf(Ry)) Rz = 1.0 / fabsf(Rx); // Rz is 1.0 .. 1.414 else Rz = 1.0 / fabsf(Ry); Rx = Rx * Rz; // vector with max x/y component = 1 Ry = Ry * Rz; for (qz = 0; qz < RBlen; qz++) // loop (qx,qy) from (px,py) { // in direction to (Cx,Cy) qx = px - qz * Rx; // for distance RBlen qy = py - qz * Ry; if (qx < 0 || qx > E3ww-1) break; if (qy < 0 || qy > E3hh-1) break; if (sa_stat == 3) { ii = qy * E3ww + qx; // don't use pixels outside area dist = sa_pixmap[ii]; if (! dist) continue; } pix2 = PXMpix(E2pxm,qx,qy); // sum RGB values Rsum += pix2[0]; Gsum += pix2[1]; Bsum += pix2[2]; Npix++; // count pixels in sum } pix3 = PXMpix(E3pxm,px,py); // output pixel is average of pix3[0] = Rsum / Npix; // pixels in line of length RBlen pix3[1] = Gsum / Npix; // from (px,py) to (Cx,Cy) pix3[2] = Bsum / Npix; Fbusy_done++; // track progress } if (sa_stat == 3 && sa_blendwidth > 0) // select area has edge blend { for (py = index; py < E3hh; py += NWT) // loop all image pixels for (px = 0; px < E3ww; px++) { ii = py * E3ww + px; dist = sa_pixmap[ii]; if (! dist) continue; // omit pixels outside area if (dist >= sa_blendwidth) continue; // omit if > blendwidth from edge pix2 = PXMpix(E2pxm,px,py); // source pixel pix3 = PXMpix(E3pxm,px,py); // target pixel f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth f2 = 1.0 - f1; pix3[0] = f1 * pix3[0] + f2 * pix2[0]; pix3[1] = f1 * pix3[1] + f2 * pix2[1]; pix3[2] = f1 * pix3[2] + f2 * pix2[2]; } } pthread_exit(0); } // directed blur worker thread void * dirblur_wthread(void *arg) { using namespace blur_names; int index = *((int *) arg); int ii, px, py, dist = 0, vstat; float d, mag, dispx, dispy; float F1, F2, f1, f2; float vpix[4], *pix1, *pix3; F1 = Dintens * Dintens; F2 = 1.0 - F1; for (py = index; py < E3hh; py += NWT) // process all pixels for (px = 0; px < E3ww; px++) { ii = py * E3ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } d = (px-Dmdx)*(px-Dmdx) + (py-Dmdy)*(py-Dmdy); mag = (1.0 - d / DD); if (mag < 0) continue; mag = mag * mag; // faster than pow(mag,4); mag = mag * mag; dispx = -Dmdw * mag; // displacement = drag * mag dispy = -Dmdh * 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]; } if (sa_stat == 3 && dist < sa_blendwidth) { // within edge blend area pix1 = PXMpix(E1pxm,px,py); // input pixel f1 = sa_blendfunc(dist); f2 = 1.0 - f1; pix3[0] = f1 * pix3[0] + f2 * pix1[0]; pix3[1] = f1 * pix3[1] + f2 * pix1[1]; pix3[2] = f1 * pix3[2] + f2 * pix1[2]; } } pthread_exit(0); // exit thread } // graduated blur worker thread void * gradblur_wthread(void *arg) { using namespace blur_names; int index = *((int *) arg); int ii, jj, npix, dist = 0; int px, py, dx, dy; float red, green, blue, f1, f2; float *pix1, *pix3, *pixN; int rad, blurrad, con; for (py = index+1; py < E3hh-1; py += NWT) // loop interior pixels for (px = 1; px < E3ww-1; px++) { if (Fcancel) pthread_exit(0); ii = py * E3ww + 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 + gblur_radius * (con_limit - pixcon[ii]) / con_limit; // blur radius for pixel, 1 - gblur_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 > E3ww-1) break; // stop here if off the edge if (py+dy < 0 || py+dy > E3hh-1) break; pixN = PXMpix(E1pxm,px+dx,py+dy); con = 255 * (1.0 - PIXMATCH(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_blendwidth) { // within edge blend area f1 = sa_blendfunc(dist); f2 = 1.0 - f1; pix3[0] = f1 * pix3[0] + f2 * pix1[0]; pix3[1] = f1 * pix3[1] + f2 * pix1[1]; pix3[2] = f1 * pix3[2] + f2 * pix1[2]; } Fbusy_done++; // track progress } pthread_exit(0); } // paint blur worker thread void * paintblur_wthread(void *arg) { using namespace blur_names; int index = *((int *) arg); float *pix1, *pix3, *pixm; int radius, radius2, npix; int px, py, dx, dy, qx, qy, rx, ry, sx, sy; int ii, dist = 0; float kern, kern2, meanR, meanG, meanB; px = mousex; py = mousey; radius = pblur_radius; for (dy = -radius+index; dy <= radius; dy += NWT) // loop within mouse radius for (dx = -radius; dx <= radius; dx++) { qx = px + dx; qy = py + dy; if (qx < 0 || qx > E3ww-1) continue; // off image if (qy < 0 || qy > E3hh-1) continue; if (sa_stat == 3) { // select area active ii = qy * E3ww + 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_blendwidth) // within blend distance kern = kern * sa_blendfunc(dist); pix1 = PXMpix(E1pxm,qx,qy); // original pixel pix3 = PXMpix(E3pxm,qx,qy); // edited pixel meanR = meanG = meanB = npix = 0; radius2 = sqrtf(radius); // radius = 2..99 >> radius2 = 1..9 for (ry = -radius2; ry <= radius2; ry++) for (rx = -radius2; rx <= radius2; rx++) { sx = qx + rx; sy = qy + ry; if (px - sx < -radius || px - sx > radius) continue; // outside mouse radius if (py - sy < -radius || py - sy > radius) continue; if (sx < 0 || sx > E3ww-1) continue; // off image if (sy < 0 || sy > E3hh-1) continue; pixm = PXMpix(E3pxm,sx,sy); meanR += pixm[0]; meanG += pixm[1]; meanB += pixm[2]; npix++; } if (npix == 0) continue; meanR = meanR / npix; meanG = meanG / npix; meanB = meanB / npix; if (pmode == 1) { // blend kern2 = 0.5 * kern; pix3[0] = kern2 * meanR + (1.0 - kern2) * pix3[0]; // pix3 tends to regional mean pix3[1] = kern2 * meanG + (1.0 - kern2) * pix3[1]; pix3[2] = kern2 * meanB + (1.0 - kern2) * pix3[2]; } if (pmode == 2) { // restore kern2 = 0.1 * kern; pix3[0] = kern2 * pix1[0] + (1.0 - kern2) * pix3[0]; // pix3 tends to pix1 pix3[1] = kern2 * pix1[1] + (1.0 - kern2) * pix3[1]; pix3[2] = kern2 * pix1[2] + (1.0 - kern2) * pix3[2]; } } pthread_exit(0); } /********************************************************************************/ // Blur Background // Blur the image outside of a selected area or areas. // Blur increases with distance from selected area edges. namespace blur_BG_names { int conrad, incrad; // constant or increasing blur int conbrad; // constant blur radius int minbrad; // min. blur radius int maxbrad; // max. blur radius VOL int Fcancel; // GCC inconsistent int E3ww, E3hh; // image dimensions int maxdist; // max. area edge distance editfunc EFblurBG; } // called from main blur function, no separate menu void m_blur_background(GtkWidget *, const char *) { using namespace blur_BG_names; int blur_BG_dialog_event(zdialog* zd, const char *event); void * blur_BG_thread(void *); EFblurBG.menufunc = m_blur_background; EFblurBG.funcname = "blur_background"; // function name EFblurBG.Farea = 2; // select area usable (required) EFblurBG.threadfunc = blur_BG_thread; // thread function if (! edit_setup(EFblurBG)) return; // setup edit minbrad = 10; // defaults maxbrad = 20; conbrad = 10; conrad = 1; incrad = 0; Fcancel = 0; E3ww = E3pxm->ww; E3hh = E3pxm->hh; /*** ____________________________________ | Blur Background | | | | [x] constant blur [ 20 ] | | | | [x] increase blur with distance | | min. blur radius [ 20 ] | | max. blur radius [ 90 ] | | | | [Apply] [Done] [Cancel] | |____________________________________| ***/ zdialog *zd = zdialog_new(E2X("Blur Background"),Mwin,Bapply,Bdone,Bcancel,null); CEF->zd = zd; zdialog_add_widget(zd,"hbox","hbcon","dialog",0,"space=5"); zdialog_add_widget(zd,"check","conrad","hbcon",E2X("constant blur"),"space=3"); zdialog_add_widget(zd,"zspin","conbrad","hbcon","1|100|1|10","space=8"); zdialog_add_widget(zd,"hbox","hbinc","dialog"); zdialog_add_widget(zd,"check","incrad","hbinc",E2X("increase blur with distance"),"space=3"); zdialog_add_widget(zd,"hbox","hbmin","dialog"); zdialog_add_widget(zd,"label","labmin","hbmin",E2X("min. blur radius"),"space=8"); zdialog_add_widget(zd,"zspin","minbrad","hbmin","0|100|1|10","space=3"); zdialog_add_widget(zd,"hbox","hbmax","dialog"); zdialog_add_widget(zd,"label","labmax","hbmax",E2X("max. blur radius"),"space=8"); zdialog_add_widget(zd,"zspin","maxbrad","hbmax","1|100|1|20","space=3"); zdialog_stuff(zd,"conrad",conrad); zdialog_stuff(zd,"incrad",incrad); zdialog_resize(zd,300,0); zdialog_restore_inputs(zd); // restore previous inputs zdialog_run(zd,blur_BG_dialog_event,"save"); // run dialog - parallel if (sa_stat != 3) m_select(0,0); // start select area dialog return; } // blur_BG dialog event and completion function int blur_BG_dialog_event(zdialog *zd, const char *event) // blur_BG dialog event function { using namespace blur_BG_names; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) { if (zd->zstat == 1) // [apply] { zd->zstat = 0; // keep dialog active if (sa_stat != 3) { zmessageACK(Mwin,E2X("no active Select Area")); m_select(0,0); return 1; } if (incrad && ! sa_calced) // if increasing blur radius, sa_edgecalc(); // calc. area edge distances sa_show(0,0); edit_reset(); signal_thread(); return 1; } else if (zd->zstat == 2) { edit_done(0); // [done] if (zd_sela) zdialog_send_event(zd_sela,"done"); // kill select area dialog } else { Fcancel = 1; // kill threads edit_cancel(0); // [cancel] or [x], discard edit if (zd_sela) zdialog_send_event(zd_sela,"done"); } return 1; } if (zstrstr("conrad incrad",event)) { zdialog_stuff(zd,"conrad",0); zdialog_stuff(zd,"incrad",0); zdialog_stuff(zd,event,1); } zdialog_fetch(zd,"conrad",conrad); zdialog_fetch(zd,"incrad",incrad); zdialog_fetch(zd,"conbrad",conbrad); zdialog_fetch(zd,"minbrad",minbrad); zdialog_fetch(zd,"maxbrad",maxbrad); return 1; } // thread function - multiple working threads to update image void * blur_BG_thread(void *) { using namespace blur_BG_names; void * blur_BG_wthread(void *arg); // worker thread int ii, dist; while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 if (incrad && sa_calced) { // if increasing blur radius, maxdist = 0; // get max. area edge distance for (ii = 0; ii < E3ww * E3hh; ii++) { dist = sa_pixmap[ii]; if (dist > maxdist) maxdist = dist; } } Fbusy_goal = sa_Npixel; Fbusy_done = 0; do_wthreads(blur_BG_wthread,NWT); // worker threads Fbusy_goal = Fbusy_done = 0; CEF->Fmods++; // image modified CEF->Fsaved = 0; // not saved paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed, stop warning } void * blur_BG_wthread(void *arg) // worker thread function { using namespace blur_BG_names; int index = *((int *) (arg)); int ii, rad = 0, dist, npix; int px, py, qx, qy; float *pix1, *pix3; float red, green, blue, F; for (py = index; py < E3hh; py += NWT) // loop all image pixels for (px = 0; px < E3ww; px++) { if (Fcancel) break; // cancel edit ii = py * E3ww + px; dist = sa_pixmap[ii]; // area edge distance if (! dist) continue; // pixel outside the area if (conrad) rad = conbrad; // use constant blur radius if (incrad) { // use increasing blur radius if (! sa_calced) pthread_exit(0); // depending on edge distance rad = minbrad + (maxbrad - minbrad) * dist / maxdist; } npix = 0; red = green = blue = 0; for (qy = py-rad; qy <= py+rad; qy++) // average surrounding pixels for (qx = px-rad; qx <= px+rad; qx++) { if (qy < 0 || qy > E3hh-1) continue; if (qx < 0 || qx > E3ww-1) continue; ii = qy * E3ww + qx; if (! sa_pixmap[ii]) continue; pix1 = PXMpix(E1pxm,qx,qy); red += pix1[0]; green += pix1[1]; blue += pix1[2]; npix++; } F = 0.999 / npix; red = F * red; // blurred pixel RGB green = F * green; blue = F * blue; pix3 = PXMpix(E3pxm,px,py); pix3[0] = red; pix3[1] = green; pix3[2] = blue; Fbusy_done++; // count pixels done } pthread_exit(0); // exit thread } /********************************************************************************/ // image noise reduction namespace denoise_names { enum denoise_method { voodoo, chroma, anneal, flatten, median, tophat, wavelets } denoise_method; int noise_histogram[3][256]; int denoise_radius; float denoise_thresh, wavelets_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 E3ww, E3hh; // image dimensions int8 *Fpix, *Gpix; editfunc EFdenoise; GtkWidget *denoise_measure_drawwin; } // menu function void m_denoise(GtkWidget *, cchar *menu) { using namespace denoise_names; void denoise_characterize(); int denoise_dialog_event(zdialog *zd, cchar *event); void * denoise_thread(void *); cchar *tip = E2X("Apply repeatedly while watching the image."); F1_help_topic = "denoise"; EFdenoise.menuname = menu; EFdenoise.menufunc = m_denoise; EFdenoise.funcname = "denoise"; EFdenoise.Farea = 2; // select area usable EFdenoise.threadfunc = denoise_thread; // thread function EFdenoise.Frestart = 1; // allow restart EFdenoise.FusePL = 1; // use with paint/lever edits OK EFdenoise.Fscript = 1; // scripting supported if (! edit_setup(EFdenoise)) return; // setup edit E3ww = E3pxm->ww; // image dimensions E3hh = E3pxm->hh; /*** _____________________________________________ | Noise Reduction | | Apply repeatedly while watching the image. | | - - - - - - - - - - - - - - - - - - - - - - | sep0 | Method | hbm1 | (o) Voodoo (o) Chroma (o) Anneal | hbm2 vbm1 vbm2 vbm3 | (o) Flatten (o) Median (o) Top Hat | | Radius [___] Threshold [___] | hbrt | - - - - - - - - - - - - - - - - - - - - - - | sep1 | (o) Wavelets Threshold [___] | hbwav | - - - - - - - - - - - - - - - - - - - - - - | sep2 | dark areas =========[]=========== all areas | hbar | | | [Measure] [Apply] [Reset] [Done] [Cancel] | |_____________________________________________| ***/ zdialog *zd = zdialog_new(E2X("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,"hsep","sep0","dialog",0,"space=3"); zdialog_add_widget(zd,"hbox","hbm1","dialog",0); zdialog_add_widget(zd,"label","labm","hbm1","Method","space=3"); zdialog_add_widget(zd,"hbox","hbm2","dialog",0); zdialog_add_widget(zd,"label","space","hbm2",0,"space=8"); zdialog_add_widget(zd,"vbox","vbm1","hbm2",0,"space=3"); zdialog_add_widget(zd,"vbox","vbm2","hbm2",0,"space=3"); zdialog_add_widget(zd,"vbox","vbm3","hbm2",0,"space=3"); zdialog_add_widget(zd,"check","voodoo","vbm1","Voodoo"); zdialog_add_widget(zd,"check","chroma","vbm2","Chroma"); zdialog_add_widget(zd,"check","anneal","vbm3","Anneal"); zdialog_add_widget(zd,"check","flatten","vbm1","Flatten"); zdialog_add_widget(zd,"check","median","vbm2","Median"); zdialog_add_widget(zd,"check","tophat","vbm3","Top Hat"); zdialog_add_widget(zd,"hbox","hbrt","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labrad","hbrt",Bradius,"space=3"); zdialog_add_widget(zd,"zspin","denoise_radius","hbrt","1|9|1|3","size=4"); zdialog_add_widget(zd,"label","space","hbrt",0,"space=8"); zdialog_add_widget(zd,"label","labthresh","hbrt",Bthresh,"space=3"); zdialog_add_widget(zd,"zspin","denoise_thresh","hbrt","1|50|1|10","size=4"); zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=3"); zdialog_add_widget(zd,"hbox","hbwav","dialog",0,"space=3"); zdialog_add_widget(zd,"label","space","hbwav",0,"space=8"); zdialog_add_widget(zd,"check","wavelets","hbwav","Wavelets"); zdialog_add_widget(zd,"label","labthresh","hbwav",Bthresh); zdialog_add_widget(zd,"zspin","wavelets_thresh","hbwav","0.1|8.0|0.1|1","size=4|space=12"); zdialog_add_widget(zd,"hsep","sep2","dialog",0,"space=3"); zdialog_add_widget(zd,"hbox","hbar","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labdark","hbar",E2X("dark areas"),"space=8"); zdialog_add_widget(zd,"hscale","darkareas","hbar","0|256|1|256","expand"); zdialog_add_widget(zd,"label","laball","hbar",E2X("all areas"),"space=8"); denoise_characterize(); // characterize image noise 20.0 zdialog_stuff(zd,"denoise_thresh",denoise_thresh); // = default threshold wavelets_thresh = 1.0; // default wavelets threshold zdialog_stuff(zd,"voodoo",1); // initial selected method zdialog_run(zd,denoise_dialog_event,"save"); // run dialog - parallel return; } // characterize noise levels void denoise_characterize() // 20.0 { using namespace denoise_names; void * denoise_characterize_wthread(void *arg); int ii, rgb, Npix, Tpix; double val, sum, sum2, mean, sigma, varnc, thresh, limit; for (rgb = 0; rgb < 3; rgb++) // clear histogram for (ii = 0; ii < 256; ii++) noise_histogram[rgb][ii] = 0; do_wthreads(denoise_characterize_wthread,NWT); // make histogram of pixel-pixel diffs thresh = 0; limit = 100; for (rgb = 0; rgb < 3; rgb++) // get mean and sigma { sum = sum2 = Tpix = 0; for (ii = 0; ii < limit; ii++) { // omit diffs > limit Npix = noise_histogram[rgb][ii]; Tpix += Npix; val = ii * Npix; sum += val; sum2 += val * val; } mean = sum / limit; varnc = (sum2 - 2.0 * mean * mean) / limit + mean * mean; sigma = sqrtf(varnc); mean = mean / Tpix * limit; // mean and sigma sigma = sigma / Tpix * limit; if (sigma > thresh) thresh = sigma; } sigma = thresh; // greatest RGB sigma thresh = 0; limit = 3 * sigma; for (rgb = 0; rgb < 3; rgb++) // make another histogram { // ignoring values > 3 * sigma sum = sum2 = Tpix = 0; for (ii = 0; ii < limit; ii++) { Npix = noise_histogram[rgb][ii]; Tpix += Npix; val = ii * Npix; sum += val; sum2 += val * val; } mean = sum / limit; varnc = (sum2 - 2.0 * mean * mean) / limit + mean * mean; sigma = sqrtf(varnc); mean = mean / Tpix * limit; sigma = sigma / Tpix * limit; if (sigma > thresh) thresh = sigma; } denoise_thresh = thresh; // greatest RGB sigma return; } void * denoise_characterize_wthread(void *arg) { using namespace denoise_names; int index = *((int *) arg); int ii, px, py, diff, rgb; float *pix1, *pix2; for (py = index; py < E3hh; py += NWT) // loop all image pixels for (px = 0; px < E3ww-1; px++) // omit last column { if (sa_stat == 3) { // select area active 20.0 ii = py * E3ww + px; if (sa_pixmap[ii] == 0) continue; // pixel outside area, ignore } pix1 = PXMpix(E1pxm,px,py); pix2 = PXMpix(E1pxm,px+1,py); for (rgb = 0; rgb < 3; rgb++) { diff = pix1[rgb] - pix2[rgb]; // pixel-pixel RGB difference diff = abs(diff); noise_histogram[rgb][diff] += 1; } } pthread_exit(0); return 0; } // dialog event and completion callback function int denoise_dialog_event(zdialog * zd, cchar *event) // reworked for script files { using namespace denoise_names; void denoise_measure(); int ii; wait_thread_idle(); if (strmatch(event,"apply")) zd->zstat = 2; // from script file if (strmatch(event,"done")) zd->zstat = 4; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 5; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { // [measure] zd->zstat = 0; // keep dialog active denoise_measure(); return 1; } if (zd->zstat == 2) { // [apply] zd->zstat = 0; // keep dialog active signal_thread(); return 1; } if (zd->zstat == 3) { // [reset] edit_undo(); // undo edits zd->zstat = 0; // keep dialog active return 1; } if (zd->zstat == 4) edit_done(0); // [done] commit edit else edit_cancel(0); // [cancel] or [x] discard edit if (zd_denoise_measure) { // kill measure dialog freeMouse(); zdialog_free(zd_denoise_measure); zd_denoise_measure = 0; } return 1; } if (strmatch(event,"blendwidth")) signal_thread(); zdialog_fetch(zd,"denoise_radius",denoise_radius); zdialog_fetch(zd,"denoise_thresh",denoise_thresh); zdialog_fetch(zd,"wavelets_thresh",wavelets_thresh); zdialog_fetch(zd,"darkareas",denoise_darkareas); if (zstrstr("voodoo chroma anneal flatten median tophat wavelets",event)) { // capture choice zdialog_stuff(zd,"voodoo",0); zdialog_stuff(zd,"chroma",0); zdialog_stuff(zd,"anneal",0); zdialog_stuff(zd,"flatten",0); zdialog_stuff(zd,"median",0); zdialog_stuff(zd,"tophat",0); zdialog_stuff(zd,"wavelets",0); zdialog_stuff(zd,event,1); } zdialog_fetch(zd,"voodoo",ii); // 20.0 if (ii) denoise_method = voodoo; zdialog_fetch(zd,"chroma",ii); // 20.0 if (ii) denoise_method = chroma; zdialog_fetch(zd,"anneal",ii); // 20.0 if (ii) denoise_method = anneal; zdialog_fetch(zd,"flatten",ii); if (ii) denoise_method = flatten; zdialog_fetch(zd,"median",ii); if (ii) denoise_method = median; zdialog_fetch(zd,"tophat",ii); if (ii) denoise_method = tophat; zdialog_fetch(zd,"wavelets",ii); if (ii) denoise_method = wavelets; return 1; } // image noise reduction thread void * denoise_thread(void *) { using namespace denoise_names; void * denoise_chroma_wthread1(void *arg); void * denoise_chroma_wthread2(void *arg); void * denoise_anneal_wthread(void *arg); void * denoise_flatten_wthread(void *arg); void * denoise_median_wthread(void *arg); void * denoise_tophat_wthread(void *arg); void * denoise_wavelets_wthread(void *arg); int ii, px, py, dist = 0; float *pix1, *pix9; float save_thresh; int nc = E3pxm->nc, pcc = nc * sizeof(float); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 E9pxm = PXM_copy(E3pxm); // E3 is source, E9 is modified if (denoise_method == wavelets) // wavelets method do_wthreads(denoise_wavelets_wthread,3); // worker threads, 1 per RGB color else // other method { if (sa_stat == 3) Fbusy_goal = sa_Npixel; else Fbusy_goal = E3ww * E3hh; Fbusy_done = 0; if (denoise_method == voodoo) // 20.0 { save_thresh = denoise_thresh; Fbusy_goal *= 4; denoise_thresh = 255; // salt and pepper elimination denoise_radius = 1; do_wthreads(denoise_median_wthread,NWT); denoise_thresh = save_thresh; for (ii = 0; ii < 3; ii++) { PXM_free(E3pxm); // accumulate changes E3pxm = E9pxm; E9pxm = PXM_copy(E3pxm); denoise_radius = 2 + 2 * ii; // 2 4 6 if (ii > 0) denoise_thresh *= 0.8; // 1.0 0.8 0.64 do_wthreads(denoise_anneal_wthread,NWT); // general noise reduction } denoise_thresh = save_thresh; } if (denoise_method == chroma) { // 20.0 E8pxm = PXM_make(E3ww,E3hh,3); // allocate HSL image do_wthreads(denoise_chroma_wthread1,NWT); do_wthreads(denoise_chroma_wthread2,NWT); PXM_free(E8pxm); } if (denoise_method == anneal) // 20.0 do_wthreads(denoise_anneal_wthread,NWT); if (denoise_method == flatten) do_wthreads(denoise_flatten_wthread,NWT); if (denoise_method == median) do_wthreads(denoise_median_wthread,NWT); if (denoise_method == tophat) do_wthreads(denoise_tophat_wthread,NWT); Fbusy_goal = 0; } if (denoise_darkareas < 256) // if brightness threshold set, 19.5 { // revert brighter areas for (py = 0; py < E3hh; py++) for (px = 0; px < E3ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix1 = PXMpix(E1pxm,px,py); // source pixel 19.0 pix9 = PXMpix(E9pxm,px,py); // target pixel if (pix1[0] + pix1[1] + pix1[2] > 3 * denoise_darkareas) // revert brighter pixels memcpy(pix9,pix1,pcc); } } PXM_free(E3pxm); // image9 >> image3 E3pxm = E9pxm; E9pxm = 0; CEF->Fmods++; CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed, stop g++ warning } // Chroma: // Exchange color data between neighbor pixels that match closely enough. void RGB_YCbCr(float R, float G, float B, float &Y, float &Cb, float &Cr) { Y = 0.299 * R + 0.587 * G + 0.114 * B; Cb = 128 - 0.168736 * R - 0.331264 * G + 0.5 * B; Cr = 128 + 0.5 * R - 0.418688 * G - 0.081312 * B; return; } void YCbCr_RGB(float Y, float Cb, float Cr, float &R, float &G, float &B) { R = Y + 1.402 * (Cr - 128); G = Y - 0.344136 * (Cb - 128) - 0.714136 * (Cr - 128); B = Y + 1.772 * (Cb - 128); return; } void * denoise_chroma_wthread1(void *arg) // 20.0 { using namespace denoise_names; int index = *((int *) arg); int px, py; float *pix3, *pix8; for (py = index; py < E3hh; py += NWT) // loop all image pixels for (px = 0; px < E3ww; px++) { pix3 = PXMpix(E3pxm,px,py); // E3 RGB image >> E8 YCbCr image pix8 = PXMpix(E8pxm,px,py); RGB_YCbCr(pix3[0],pix3[1],pix3[2],pix8[0],pix8[1],pix8[2]); } pthread_exit(0); } void * denoise_chroma_wthread2(void *arg) // 20.0 { using namespace denoise_names; int index = *((int *) arg); int rad, thresh; int ii, dist = 0; int px, py, dx, dy, rgb; float *pix1, *pix9; float *pix8, *pix8N, pixM[3]; float Frad, f1, f2; float match, reqmatch; rad = denoise_radius; thresh = denoise_thresh; reqmatch = 1.0 - 0.01 * thresh; // 20 >> 0.8 Frad = rad + rad + 1; Frad = 1.0 / (Frad * Frad); for (py = index+rad; py < E3hh-rad; py += NWT) // loop all image pixels for (px = rad; px < E3ww-rad; px++) // skip outermost rows and cols { if (sa_stat == 3) { // select area active ii = py * E3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix8 = PXMpix(E8pxm,px,py); // source YCbCr pixel memset(pixM,0,3*sizeof(float)); // clear totals for (dy = -rad; dy <= +rad; dy++) // loop neighbor pixels for (dx = -rad; dx <= +rad; dx++) { pix8N = pix8 + (dy * E3ww + dx) * 3; // neighbor pixel match = PIXMATCH(pix8,pix8N); // match level, 0..1 = perfect match if (match > reqmatch) f1 = 0.5; // color exchange factor else f1 = 0; f2 = 1.0 - f1; for (rgb = 0; rgb < 3; rgb++) pixM[rgb] += f2 * pix8[rgb] + f1 * pix8N[rgb]; // accumulate exchange colors } for (rgb = 0; rgb < 3; rgb++) // normalize to 0-255 pixM[rgb] = pixM[rgb] * Frad; match = PIXMATCH(pix8,pixM); // final old:new comparison if (match < reqmatch) continue; // reject larger changes pix9 = PXMpix(E9pxm,px,py); // target RGB pixel YCbCr_RGB(pixM[0],pixM[1],pixM[2],pix9[0],pix9[1],pix9[2]); // YCbCr pix8 >> RGB pix9 if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth f2 = 1.0 - f1; pix1 = PXMpix(E1pxm,px,py); // source image pixel pix9[0] = f1 * pix9[0] + f2 * pix1[0]; pix9[1] = f1 * pix9[1] + f2 * pix1[1]; pix9[2] = f1 * pix9[2] + f2 * pix1[2]; } Fbusy_done++; // track progress } pthread_exit(0); } // Anneal: // Exchange RGB data between neighbor pixels that match closely enough. void * denoise_anneal_wthread(void *arg) // 20.0 { using namespace denoise_names; int index = *((int *) arg); int rad, thresh; int ii, rgb, dist = 0; int px, py, dx, dy; int nc = E3pxm->nc; float *pix1, *pix3, *pix9, *pix3N; float f1, f2, Frad, pixM[3]; float match, reqmatch; rad = denoise_radius; thresh = denoise_thresh; reqmatch = 1.0 - 0.01 * thresh; // 20 >> 0.8 Frad = rad + rad + 1; Frad = 1.0 / (Frad * Frad); for (py = index+rad; py < E3hh-rad; py += NWT) // loop all image pixels for (px = rad; px < E3ww-rad; px++) // skip outermost rows and cols { if (sa_stat == 3) { // select area active ii = py * E3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } for (rgb = 0; rgb < 3; rgb++) pixM[rgb] = 0; pix1 = PXMpix(E1pxm,px,py); pix3 = PXMpix(E3pxm,px,py); // source image pixel pix9 = PXMpix(E9pxm,px,py); // target image pixel for (dy = -rad; dy <= +rad; dy++) // loop neighbor pixels for (dx = -rad; dx <= +rad; dx++) { pix3N = pix3 + (dy * E3ww + dx) * nc; // neighbor pixel match = PIXMATCH(pix3,pix3N); // match level, 0..1 = perfect match if (match > reqmatch) f1 = 0.5; // RGB exchange factors else f1 = 0; f2 = 1.0 - f1; for (rgb = 0; rgb < 3; rgb++) // loop all RGB colors pixM[rgb] += f2 * pix3[rgb] + f1 * pix3N[rgb]; // accumulate exchange colors } for (rgb = 0; rgb < 3; rgb++) // normalize to 0-255 pixM[rgb] = Frad * pixM[rgb]; match = PIXMATCH(pix1,pixM); // final old:new comparison if (match < reqmatch) continue; // reject larger changes for (rgb = 0; rgb < 3; rgb++) pix9[rgb] = pixM[rgb]; if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth f2 = 1.0 - f1; pix9[0] = f1 * pix9[0] + f2 * pix1[0]; pix9[1] = f1 * pix9[1] + f2 * pix1[1]; pix9[2] = f1 * pix9[2] + f2 * pix1[2]; } Fbusy_done++; // track progress } pthread_exit(0); } // Flatten: // Flatten outlyer pixels within neighborhood group. // An outlier pixel has an RGB value outside mean +- sigma. // Process groups increasing from radius = 1 to denoise_radius. void * denoise_flatten_wthread(void *arg) { using namespace denoise_names; int index = *((int *) arg); int ii, rgb, dist = 0; int px, py, dx, dy, rad1, rad2, thresh; int nc = E3pxm->nc; float *pix1, *pix3, *pix9, *pixN; float nn, val, sum, sum2, mean, variance, sigma, thresh2; float f1, f2; rad1 = denoise_radius; thresh = denoise_thresh; for (py = index+rad1; py < E3hh-rad1; py += NWT) // loop all image pixels for (px = rad1; px < E3ww-rad1; px++) // skip outermost rows and cols { if (sa_stat == 3) { // select area active ii = py * E3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix3 = PXMpix(E3pxm,px,py); // source image pixel pix9 = PXMpix(E9pxm,px,py); // target image pixel for (rad2 = 1; rad2 <= rad1; rad2++) // radius from 1 to max 20.0 { nn = (rad2 * 2 + 1); // pixel group NxN size nn = nn * nn; // pixels in group for (rgb = 0; rgb < 3; rgb++) // loop RGB color { sum = sum2 = 0; for (dy = -rad2; dy <= rad2; dy++) // loop surrounding pixels for (dx = -rad2; dx <= rad2; dx++) { pixN = pix3 + (dy * E3ww + dx) * nc; val = pixN[rgb]; sum += val; sum2 += val * val; } mean = sum / nn; // compute mean and sigma variance = (sum2 - 2.0 * mean * sum) / nn + mean * mean; sigma = sqrtf(variance); thresh2 = 0.5 * (thresh + sigma); if (pix3[rgb] > mean + sigma && pix3[rgb] - mean < thresh2) // if | pixel - mean | > sigma 20.0 pix9[rgb] = mean; // flatten pixel else if (pix3[rgb] < mean - sigma && mean - pix3[rgb] < thresh2) pix9[rgb] = mean; } } if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth f2 = 1.0 - f1; pix1 = PXMpix(E1pxm,px,py); // source image pixel pix9[0] = f1 * pix9[0] + f2 * pix1[0]; pix9[1] = f1 * pix9[1] + f2 * pix1[1]; pix9[2] = f1 * pix9[2] + f2 * pix1[2]; } Fbusy_done++; // track progress } pthread_exit(0); } // Median: // Use median RGB brightness for pixels within radius void * denoise_median_wthread(void *arg) { using namespace denoise_names; int index = *((int *) arg); int ii, rgb, dist = 0; int px, py, dx, dy, rad, Frad, thresh; int nc = E3pxm->nc; float *pix1, *pix3, *pix9, *pixN; float f1, f2, median; int16 rgbdist[256]; int rgbsum; rad = denoise_radius; thresh = denoise_thresh; Frad = 2 * rad + 1; Frad = Frad * Frad; for (py = index+rad; py < E3hh-rad; py += NWT) // loop all image pixels for (px = rad; px < E3ww-rad; px++) // skip outermost rows and cols { if (sa_stat == 3) { // select area active ii = py * E3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix3 = PXMpix(E3pxm,px,py); // source image pixel pix9 = PXMpix(E9pxm,px,py); // target image pixel for (rgb = 0; rgb < 3; rgb++) // loop all RGB colors { memset(rgbdist,0,256*sizeof(int16)); // clear rgb distribution rgbsum = 0; for (dy = -rad; dy <= rad; dy++) // loop surrounding pixels for (dx = -rad; dx <= rad; dx++) // get RGB values { pixN = pix3 + (dy * E3ww + dx) * nc; // make distribution of RGB values 20.0 ii = pixN[rgb]; rgbdist[ii]++; } for (ii = 0; ii < 256; ii++) // sum distribution from 0 to 255 20.0 { rgbsum += rgbdist[ii]; // break when half of RGB values if (rgbsum > Frad / 2) break; // have been counted } median = ii; // >> median RGB value if (pix3[rgb] > median && pix3[rgb] - median < thresh) // if | rgb - median | < threshold 20.0 pix9[rgb] = median; // moderate rgb else if (pix3[rgb] < median && median - pix3[rgb] < thresh) pix9[rgb] = median; } if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth f2 = 1.0 - f1; pix1 = PXMpix(E1pxm,px,py); // source image pixel pix9[0] = f1 * pix9[0] + f2 * pix1[0]; pix9[1] = f1 * pix9[1] + f2 * pix1[1]; pix9[2] = f1 * pix9[2] + f2 * pix1[2]; } Fbusy_done++; // track progress } pthread_exit(0); } // Top Hat: // Execute with increasing radius from 1 to limit. // Detect outlier by comparing with pixels along outer radius only. void * denoise_tophat_wthread(void *arg) { using namespace denoise_names; int index = *((int *) arg); int ii, dist = 0; int px, py, dx, dy, rad1, rad2; int nc = E3pxm->nc; float *pix1, *pix3, *pix9, *pixN; float minR, minG, minB, maxR, maxG, maxB; float f1, f2; rad1 = denoise_radius; for (py = index+rad1; py < E3hh-rad1; py += NWT) // loop all image pixels for (px = rad1; px < E3ww-rad1; px++) // skip outermost rows and cols { if (sa_stat == 3) { // select area active ii = py * E3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix3 = PXMpix(E3pxm,px,py); // source image pixel pix9 = PXMpix(E9pxm,px,py); // target image pixel for (rad2 = 1; rad2 <= rad1; rad2++) // loop rad2 from 1 to max. for (int loops = 0; loops < 2; loops++) { minR = minG = minB = 255; maxR = maxG = maxB = 0; for (dy = -rad2; dy <= rad2; dy++) // loop all pixels within rad2 for (dx = -rad2; dx <= rad2; dx++) { if (dx > -rad2 && dx < rad2) continue; // skip inner pixels if (dy > -rad2 && dy < rad2) continue; pixN = pix3 + (dy * E3ww + dx) * nc; if (pixN[0] < minR) minR = pixN[0]; // find min and max per color if (pixN[0] > maxR) maxR = pixN[0]; // among outermost pixels if (pixN[1] < minG) minG = pixN[1]; if (pixN[1] > maxG) maxG = pixN[1]; if (pixN[2] < minB) minB = pixN[2]; if (pixN[2] > maxB) maxB = pixN[2]; } if (pix3[0] < minR && pix9[0] < 254) pix9[0] += 2; // if central pixel is outlier, 20.0 if (pix3[0] > maxR && pix9[0] > 1) pix9[0] -= 2; // moderate its values if (pix3[1] < minG && pix9[1] < 254) pix9[1] += 2; if (pix3[1] > maxG && pix9[1] > 1) pix9[1] -= 2; if (pix3[2] < minB && pix9[2] < 254) pix9[2] += 2; if (pix3[2] > maxB && pix9[2] > 1) pix9[2] -= 2; } if (sa_stat == 3 && dist < sa_blendwidth) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth f2 = 1.0 - f1; pix1 = PXMpix(E1pxm,px,py); // source image pixel pix9[0] = f1 * pix9[0] + f2 * pix1[0]; pix9[1] = f1 * pix9[1] + f2 * pix1[1]; pix9[2] = f1 * pix9[2] + f2 * pix1[2]; } Fbusy_done++; // track progress } pthread_exit(0); } // Wavelets: // worker thread for wavelets method // do wavelets denoise for one color in each of 3 threads void * denoise_wavelets_wthread(void *arg) { using namespace denoise_names; void denoise_wavelets(float *fimg[3], uint ww2, uint hh2, float); int rgb = *((int *) arg); // rgb color 0/1/2 int ii, jj; float *fimg[3]; float f256 = 1.0 / 256.0; int nc = E3pxm->nc; if (sa_stat == 3) goto denoise_area; // select area is active fimg[0] = (float *) zmalloc(E3ww * E3hh * sizeof(float)); fimg[1] = (float *) zmalloc(E3ww * E3hh * sizeof(float)); fimg[2] = (float *) zmalloc(E3ww * E3hh * sizeof(float)); for (ii = 0; ii < E3ww * E3hh; ii++) // extract one noisy color from E3 fimg[0][ii] = E3pxm->pixels[nc*ii+rgb] * f256; denoise_wavelets(fimg,E3ww,E3hh,wavelets_thresh); for (ii = 0; ii < E3ww * E3hh; 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]); pthread_exit(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 > E3ww) pxh = E3ww; pyl = sa_miny - 16; if (pyl < 0) pyl = 0; pyh = sa_maxy + 16; if (pyh > E3hh) pyh = E3hh; 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) * E3ww + (px + pxl); fimg[0][ii] = E3pxm->pixels[nc*jj+rgb] * f256; } denoise_wavelets(fimg,ww2,hh2,wavelets_thresh); for (py = 0; py < hh2; py++) for (px = 0; px < ww2; px++) { ii = py * ww2 + px; jj = (py + pyl) * E3ww + (px + pxl); dist = sa_pixmap[jj]; if (! dist) continue; if (dist < sa_blendwidth) { f1 = sa_blendfunc(dist); f2 = 1.0 - f1; E9pxm->pixels[nc*jj+rgb] = f1 * 256.0 * fimg[0][ii] + f2 * E3pxm->pixels[nc*jj+rgb]; } else E9pxm->pixels[nc*jj+rgb] = 256.0 * fimg[0][ii]; } zfree(fimg[0]); zfree(fimg[1]); zfree(fimg[2]); pthread_exit(0); } // wavelets denoise algorithm // Adapted from Gimp wavelets 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_wavelets(float *fimg[3], uint ww2, uint hh2, float thresh) { void denoise_wavelets_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_wavelets_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_wavelets_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_wavelets_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 (NOP; ii < size - sc; ii++) temp[ii] = 0.25*(2*fimg[st*ii] + fimg[st*(ii-sc)] + fimg[st*(ii+sc)]); for (NOP; ii < size; ii++) temp[ii] = 0.25*(2*fimg[st*ii] + fimg[st*(ii-sc)] + fimg[st*(2*size-2-(ii+sc))]); return; } // dialog to measure noise at mouse position void denoise_measure() { using namespace denoise_names; int denoise_measure_dialog_event(zdialog *zd, cchar *event); GtkWidget *frdraw, *drawwin; char text[100]; cchar *title = E2X("Measure Noise"); cchar *mousemess = E2X("Move mouse in a monotone image area."); /*** _______________________________________ | Measure Noise | | | | Move mouse in a monotone image area. | | _____________________________________ | || || || || ||.....................................|| || || || || ||_____________________________________|| drawing area || || || || ||.....................................|| || || ||_____________________________________|| | center edge | | | | mean RGB: 100 150 200 | | mean noise: 1.51 1.23 0.76 | | | | [cancel] | |_______________________________________| ***/ if (zd_denoise_measure) return; zdialog *zd = zdialog_new(title,Mwin,Bcancel,null); // measure noise dialog zd_denoise_measure = zd; zdialog_add_widget(zd,"label","clab","dialog",mousemess,"space=5"); zdialog_add_widget(zd,"frame","frdraw","dialog",0,"expand"); // frame for drawing areas frdraw = zdialog_widget(zd,"frdraw"); drawwin = gtk_drawing_area_new(); // drawing area gtk_container_add(GTK_CONTAINER(frdraw),drawwin); denoise_measure_drawwin = drawwin; zdialog_add_widget(zd,"hbox","hbce","dialog"); zdialog_add_widget(zd,"label","labcen","hbce",Bcenter,"space=3"); zdialog_add_widget(zd,"label","space","hbce",0,"expand"); zdialog_add_widget(zd,"label","labend","hbce",Bedge,"space=5"); snprintf(text,100,mformat,0.0,0.0,0.0); // mean RGB: 0 0 0 zdialog_add_widget(zd,"label","mlab","dialog",text); snprintf(text,100,nformat,0.0,0.0,0.0); // mean noise: 0.00 0.00 0.00 zdialog_add_widget(zd,"label","nlab","dialog",text); zdialog_resize(zd,300,300); zdialog_run(zd,denoise_measure_dialog_event,"save"); // run dialog return; } // dialog event and completion function int denoise_measure_dialog_event(zdialog *zd, cchar *event) { using namespace denoise_names; void denoise_measure_mousefunc(); if (strmatch(event,"focus")) takeMouse(denoise_measure_mousefunc,dragcursor); // connect mouse function if (zd->zstat) { freeMouse(); // free mouse zdialog_free(zd); zd_denoise_measure = 0; } return 1; } // mouse function // sample noise where the mouse is clicked // assumed: mouse is on a monotone image area void denoise_measure_mousefunc() { using namespace denoise_names; GtkWidget *drawwin; GdkWindow *gdkwin; cairo_t *cr; zdialog *zd = zd_denoise_measure; char text[100]; int mx, my, px, py, qx, qy, Npix; float *pix3, R; float Rm, Gm, Bm, Rn, Gn, Bn, Ro, Go, Bo; int dww, dhh; float max, xscale, yscale; float rx, ry; float Noise[400][3]; double dashes[2] = { 1, 3 }; if (! E3pxm) return; if (! zd) return; if (Fthreadbusy) return; mx = Mxposn; // mouse position my = Myposn; if (mx < 13 || mx >= E3ww-13) return; // must be 12+ pixels from image edge if (my < 13 || my >= E3hh-13) return; draw_mousecircle(Mxposn,Myposn,10,0,0); // draw mouse circle, radius 10 Npix = 0; Rm = Gm = Bm = 0; for (py = my-10; py <= my+10; py++) // loop pixels within mouise circle for (px = mx-10; px <= mx+10; px++) // (approx. 314 pixels) { R = sqrtf((px-mx)*(px-mx) + (py-my)*(py-my)); // distance from center if (R > 10) continue; pix3 = PXMpix(E3pxm,px,py); // get pixel RGB values Rm += pix3[0]; // accumulate Gm += pix3[1]; Bm += pix3[2]; Npix++; } Rm = Rm / Npix; // mean RGB values Gm = Gm / Npix; // for pixels within mouse Bm = Bm / Npix; Npix = 0; Rn = Gn = Bn = 0; for (py = my-10; py <= my+10; py++) // loop pixels within mouise circle for (px = mx-10; px <= mx+10; px++) // (approx. 314 pixels) { R = sqrtf((px-mx)*(px-mx) + (py-my)*(py-my)); // distance from center if (R > 10) continue; Ro = Go = Bo = 0; for (qy = py-2; qy <= py+2; qy++) // for each pixel, get mean RGB for (qx = px-2; qx <= px+2; qx++) // for 5x5 surrounding pixels { pix3 = PXMpix(E3pxm,qx,qy); Ro += pix3[0]; Go += pix3[1]; Bo += pix3[2]; } Ro = Ro / 25; // mean RGB for surrounding pixels Go = Go / 25; Bo = Bo / 25; pix3 = PXMpix(E3pxm,px,py); // get pixel RGB noise levels Noise[Npix][0] = pix3[0] - Ro; // noise = pixel value - mean Noise[Npix][1] = pix3[1] - Go; Noise[Npix][2] = pix3[2] - Bo; Rn += fabsf(Noise[Npix][0]); // accumulate absolute values Gn += fabsf(Noise[Npix][1]); Bn += fabsf(Noise[Npix][2]); Npix++; } Rn = Rn / Npix; // mean RGB noise levels Gn = Gn / Npix; // for pixels within mouse Bn = Bn / Npix; snprintf(text,100,mformat,0.0,0.0,0.0); // clear dialog data zdialog_stuff(zd,"mlab",text); snprintf(text,100,nformat,0.0,0.0,0.0); zdialog_stuff(zd,"nlab",text); snprintf(text,100,mformat,Rm,Gm,Bm); // mean RGB: NNN NNN NNN zdialog_stuff(zd,"mlab",text); snprintf(text,100,nformat,Rn,Gn,Bn); // mean noise: N.NN N.NN N.NN zdialog_stuff(zd,"nlab",text); max = Rn; if (Gn > max) max = Gn; if (Bn > max) max = Bn; drawwin = denoise_measure_drawwin; gdkwin = gtk_widget_get_window(drawwin); // GDK drawing window dww = gtk_widget_get_allocated_width(drawwin); // drawing window size dhh = gtk_widget_get_allocated_height(drawwin); xscale = dww / 10.0; // x scale: 0 to max radius yscale = dhh / 20.0; // y scale: -10 to +10 noise level cr = draw_context_create(gdkwin,draw_context); cairo_set_source_rgb(cr,1,1,1); // white background cairo_paint(cr); cairo_set_source_rgb(cr,0,0,0); // paint black cairo_set_line_width(cr,2); // center line cairo_set_dash(cr,dashes,0,0); cairo_move_to(cr,0,0.5*dhh); cairo_line_to(cr,dww,0.5*dhh); cairo_stroke(cr); cairo_set_dash(cr,dashes,2,0); // dash lines at -5 and +5 cairo_move_to(cr,0,0.25*dhh); cairo_line_to(cr,dww,0.25*dhh); cairo_move_to(cr,0,0.75*dhh); cairo_line_to(cr,dww,0.75*dhh); cairo_stroke(cr); cairo_set_source_rgb(cr,1,0,0); Npix = 0; for (py = my-10; py <= my+10; py++) // loop pixels within mouise circle for (px = mx-10; px <= mx+10; px++) // (approx. 314 pixels) { R = sqrtf((px-mx)*(px-mx) + (py-my)*(py-my)); // distance from center if (R > 10) continue; Rn = Noise[Npix][0]; // RED noise rx = R * xscale; // px, 0 to dww ry = 0.5 * dhh - Rn * yscale; // red py, 0 to dhh cairo_move_to(cr,rx,ry); cairo_arc(cr,rx,ry,1,0,2*PI); Npix++; } cairo_stroke(cr); cairo_set_source_rgb(cr,0,1,0); Npix = 0; for (py = my-10; py <= my+10; py++) // same for GREEN noise for (px = mx-10; px <= mx+10; px++) { R = sqrtf((px-mx)*(px-mx) + (py-my)*(py-my)); if (R > 10) continue; Gn = Noise[Npix][1]; rx = R * xscale; ry = 0.5 * dhh - Gn * yscale; cairo_move_to(cr,rx,ry); cairo_arc(cr,rx,ry,1,0,2*PI); Npix++; } cairo_stroke(cr); cairo_set_source_rgb(cr,0,0,1); Npix = 0; for (py = my-10; py <= my+10; py++) // same for BLUE noise for (px = mx-10; px <= mx+10; px++) { R = (px-mx)*(px-mx) + (py-my)*(py-my); if (R > 100) continue; R = 0.1 * R; Bn = Noise[Npix][2]; rx = R * xscale; ry = 0.5 * dhh - Bn * yscale; cairo_move_to(cr,rx,ry); cairo_arc(cr,rx,ry,1,0,2*PI); Npix++; } cairo_stroke(cr); draw_context_destroy(draw_context); return; } /********************************************************************************/ // red eye removal function namespace redeye_names { 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 E3ww, E3hh; int Nredmem = 0, maxredmem = 100; editfunc EFredeye; #define pixred(pix) (25*pix[0]/(pixbright(pix)+1)) // red brightness 0-100% } // menu function void m_redeyes(GtkWidget *, cchar *) { using namespace redeye_names; int redeye_dialog_event(zdialog *zd, cchar *event); void redeye_mousefunc(); cchar *redeye_message = E2X( "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 = "red eyes"; EFredeye.menufunc = m_redeyes; EFredeye.funcname = "redeyes"; EFredeye.Farea = 1; // select area ignored EFredeye.mousefunc = redeye_mousefunc; if (! edit_setup(EFredeye)) return; // setup edit E3ww = E3pxm->ww; E3hh = E3pxm->hh; zdialog *zd = zdialog_new(E2X("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) { using namespace redeye_names; void redeye_mousefunc(); if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (Nredmem > 0) { CEF->Fmods++; CEF->Fsaved = 0; } if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(redeye_mousefunc,dragcursor); // connect mouse function return 1; } 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() { using namespace redeye_names; 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 > E3ww-1 || py < 0 || py > E3hh-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 > E3ww-1) ww = E3ww-1 - px; if (py + hh > E3hh-1) hh = E3hh-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) { using namespace redeye_names; 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) { using namespace redeye_names; int rad, radlim; draw_mousearc(cx,cy,2*ww,2*hh,0,0); // draw ellipse around mouse pointer if (ww > hh) rad = ww; else rad = hh; radlim = redeye_radlim(cx,cy); if (rad > radlim) rad = radlim; int ii = Nredmem++; // add red-eye to memory redmem[ii].type = 'R'; redmem[ii].cx = cx; redmem[ii].cy = cy; redmem[ii].ww = 2 * ww; redmem[ii].hh = 2 * hh; redmem[ii].rad = rad; redmem[ii].clicks = 0; redmem[ii].thresh = 0; return ii; } // darken a red-eye and increase click count void redeye_darken(int ii) { using namespace redeye_names; 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 threshold tstep = redmem[ii].tstep; // redness step size draw_mousearc(0,0,0,0,1,0); // erase mouse ellipse } tstep = (thresh - tstep) / thresh; // convert to reduction factor thresh = thresh * pow(tstep,clicks); // reduce threshold by total clicks for (py = cy-rad; py <= cy+rad; py++) // darken pixels over threshold 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 = threshold 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 threshold and step size for stepwise darkening. void redeye_distr(int ii) { using namespace redeye_names; 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 threshold 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) { using namespace redeye_names; 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) { using namespace redeye_names; int cx, cy, rad, px, py; float *pix1, *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); cx = redmem[ii].cx; cy = redmem[ii].cy; rad = redmem[ii].rad; for (px = cx-rad; px <= cx+rad; px++) for (py = cy-rad; py <= cy+rad; py++) { pix1 = PXMpix(E1pxm,px,py); pix3 = PXMpix(E3pxm,px,py); memcpy(pix3,pix1,pcc); } for (ii++; ii < Nredmem; ii++) redmem[ii-1] = redmem[ii]; Nredmem--; draw_mousearc(0,0,0,0,1,0); // erase mouse ellipse return; } // compute red-eye radius limit: smaller of 100 and nearest image edge int redeye_radlim(int cx, int cy) { using namespace redeye_names; int radlim = 100; if (cx < 100) radlim = cx; if (E3ww-1 - cx < 100) radlim = E3ww-1 - cx; if (cy < 100) radlim = cy; if (E3hh-1 - cy < 100) radlim = E3hh-1 - cy; return radlim; } /********************************************************************************/ // match_colors 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. namespace match_colors_names { float match_colors_RGB1[3]; // image 1 base colors to match float match_colors_RGB2[3]; // image 2 target colors to match int match_colors_radius = 10; // mouse radius int match_colors_mode = 0; int E3ww, E3hh; editfunc EFmatchcolors; } // menu function void m_match_colors(GtkWidget *, const char *) { using namespace match_colors_names; int match_colors_dialog_event(zdialog* zd, const char *event); void * match_colors_thread(void *); void match_colors_mousefunc(); cchar *title = E2X("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_colors dialog zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=2"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"homog|space=3"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"homog|space=3"); zdialog_add_widget(zd,"label","labn1","vb1","1"); zdialog_add_widget(zd,"label","labn2","vb1","2"); zdialog_add_widget(zd,"label","labn3","vb1","3"); zdialog_add_widget(zd,"label","labn4","vb1","4"); zdialog_add_widget(zd,"label","labn5","vb1","5"); zdialog_add_widget(zd,"hbox","hbrad","vb2"); zdialog_add_widget(zd,"zspin","radius","hbrad","1|20|1|10","space=5"); zdialog_add_widget(zd,"label","labrad","hbrad",E2X("mouse radius for color sample")); zdialog_add_widget(zd,"hbox","hbop1","vb2"); zdialog_add_widget(zd,"button","open1","hbop1",E2X("Open"),"space=5"); zdialog_add_widget(zd,"label","labop1","hbop1",E2X("image for source color")); zdialog_add_widget(zd,"hbox","hbclik1","vb2"); zdialog_add_widget(zd,"label","labclik1","hbclik1",E2X("click on image to get source color")); zdialog_add_widget(zd,"hbox","hbop2","vb2"); zdialog_add_widget(zd,"button","open2","hbop2",E2X("Open"),"space=5"); zdialog_add_widget(zd,"label","labop2","hbop2",E2X("image to set matching color")); zdialog_add_widget(zd,"hbox","hbclik2","vb2"); zdialog_add_widget(zd,"label","labclik2","hbclik2",E2X("click on image to set matching color")); zdialog_stuff(zd,"radius",match_colors_radius); // remember last radius EFmatchcolors.funcname = "match_colors"; EFmatchcolors.Farea = 1; // select area ignored EFmatchcolors.zd = zd; EFmatchcolors.threadfunc = match_colors_thread; EFmatchcolors.mousefunc = match_colors_mousefunc; match_colors_mode = 0; if (curr_file) { match_colors_mode = 1; // image 1 ready to click takeMouse(match_colors_mousefunc,0); // connect mouse function } zdialog_run(zd,match_colors_dialog_event,"parent"); // run dialog - parallel return; } // match_colors dialog event and completion function int match_colors_dialog_event(zdialog *zd, const char *event) { using namespace match_colors_names; void match_colors_mousefunc(); int err; char *file; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (match_colors_mode == 4) { // edit was started if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit match_colors_mode = 0; return 1; } freeMouse(); // abandoned zdialog_free(zd); match_colors_mode = 0; return 1; } if (strmatch(event,"radius")) // set new mouse radius zdialog_fetch(zd,"radius",match_colors_radius); if (strmatch(event,"open1")) // get image 1 for color source { if (match_colors_mode == 4) edit_cancel(1); // cancel edit, keep dialog match_colors_mode = 0; file = gallery_select1(null); // open image 1 if (file) { err = f_open(file); if (! err) match_colors_mode = 1; // image 1 ready to click } } if (strmatch(event,"open2")) // get image 2 to set matching color { if (match_colors_mode < 2) { zmessageACK(Mwin,E2X("select source image color first")); // check that RGB1 has been set return 1; } match_colors_mode = 2; file = gallery_select1(null); // open image 2 if (! file) return 1; err = f_open(file); if (err) return 1; match_colors_mode = 3; // image 2 ready to click } takeMouse(match_colors_mousefunc,0); // reconnect mouse function return 1; } // mouse function - click on image and get colors to match void match_colors_mousefunc() { using namespace match_colors_names; void match_colors_getRGB(int px, int py, float rgb[3]); int px, py; if (match_colors_mode < 1) return; // no image available yet draw_mousecircle(Mxposn,Myposn,match_colors_radius,0,0); // draw circle around pointer if (LMclick) { LMclick = 0; px = Mxclick; py = Myclick; if (match_colors_mode == 1 || match_colors_mode == 2) // image 1 ready to click { match_colors_getRGB(px,py,match_colors_RGB1); // get RGB1 color match_colors_mode = 2; return; } if (match_colors_mode == 3 || match_colors_mode == 4) // image 2 ready to click { if (match_colors_mode == 4) edit_reset(); else { if (! edit_setup(EFmatchcolors)) return; // setup edit - thread will launch E3ww = E3pxm->ww; E3hh = E3pxm->hh; match_colors_mode = 4; // edit waiting for cancel or done } match_colors_getRGB(px,py,match_colors_RGB2); // get RGB2 color signal_thread(); // update the target image return; } } return; } // get the RGB averages for pixels within mouse radius void match_colors_getRGB(int px, int py, float rgb[3]) { using namespace match_colors_names; int radflat1 = match_colors_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_colors_thread(void *) { using namespace match_colors_names; void * match_colors_wthread(void *arg); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 do_wthreads(match_colors_wthread,NWT); // worker threads CEF->Fmods++; CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed, avoid warning } void * match_colors_wthread(void *arg) // worker thread function { using namespace match_colors_names; int index = *((int *) (arg)); int px, py; float *pix3; float Rred, Rgreen, Rblue; float red, green, blue, cmax; Rred = match_colors_RGB1[0] / match_colors_RGB2[0]; // color adjustment ratios Rgreen = match_colors_RGB1[1] / match_colors_RGB2[1]; Rblue = match_colors_RGB1[2] / match_colors_RGB2[2]; for (py = index; py < E3hh; py += NWT) // loop all image pixels for (px = 0; px < E3ww; 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; } pthread_exit(0); } /********************************************************************************/ // Smart Erase menu function - Replace pixels inside a select area // with a reflection of pixels outside the area. namespace smarterase_names { editfunc EFsmarterase; int E3ww, E3hh; } // menu function void m_smart_erase(GtkWidget *, const char *) { using namespace smarterase_names; int smart_erase_dialog_event(zdialog* zd, const char *event); cchar *erase_message = E2X("Drag mouse to select. Erase. Repeat. \n" "Click: extend selection to mouse."); F1_help_topic = "smart erase"; EFsmarterase.menufunc = m_smart_erase; EFsmarterase.funcname = "smart_erase"; EFsmarterase.Farea = 0; // select area deleted EFsmarterase.mousefunc = sa_mouse_select; // mouse function (use select area) if (! edit_setup(EFsmarterase)) return; // setup edit E3ww = E3pxm->ww; E3hh = E3pxm->hh; /*** _________________________________________ | | | Drag mouse to select. Erase. Repeat. | | Click: extend selection to mouse. | | | | Radius [ 10 ] Blur [ 1.5 ] | | [New Area] [Show] [Hide] [Erase] [Undo] | | | | [Done] | |_________________________________________| ***/ zdialog *zd = zdialog_new(E2X("Smart Erase"),Mwin,Bdone,null); EFsmarterase.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",E2X("Radius"),"space=5"); zdialog_add_widget(zd,"zspin","radius","hb2","1|30|1|10"); zdialog_add_widget(zd,"label","labb","hb2",E2X("Blur"),"space=10"); zdialog_add_widget(zd,"zspin","blur","hb2","0|9|0.5|1"); zdialog_add_widget(zd,"hbox","hb3","dialog",0,"space=5"); zdialog_add_widget(zd,"button","newarea","hb3",E2X("New Area"),"space=3"); zdialog_add_widget(zd,"button","show","hb3",E2X(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_clear(); // clear area if any sa_pixmap_create(); // allocate select area pixel maps 19.0 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 sa_lastx = sa_lasty = 0; // initz. for sa_mouse_select takeMouse(sa_mouse_select,0); // use select area mouse function sa_show(1,0); zdialog_run(zd,smart_erase_dialog_event,"save"); // run dialog - parallel return; } // dialog event and completion function int smart_erase_dialog_event(zdialog *zd, const char *event) // overhauled { using namespace smarterase_names; void smart_erase_func(int mode); int smart_erase_blur(float radius); float radius; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { sa_clear(); // clear 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_clear(); sa_lastx = sa_lasty = 0; // forget last click 19.0 sa_pixmap_create(); // allocate select area pixel maps 19.0 sa_mode = mode_mouse; // mode = select by mouse sa_stat = 1; // status = active edit sa_fww = E1pxm->ww; sa_fhh = E1pxm->hh; sa_show(1,0); takeMouse(sa_mouse_select,0); } if (strmatch(event,"show")) { sa_show(1,0); takeMouse(sa_mouse_select,0); } if (strmatch(event,"hide")) { sa_show(0,0); freeMouse(); } if (strmatch(event,"erase")) { // do smart erase sa_finish_auto(); // finish the area smart_erase_func(1); zdialog_fetch(zd,"blur",radius); // add optional blur if (radius > 0) smart_erase_blur(radius); sa_show(0,0); } if (strmatch(event,"undo1")) // dialog undo, undo last erase smart_erase_func(2); return 1; } // erase the area or restore last erased area // mode = 1 = erase, mode = 2 = restore void smart_erase_func(int mode) { using namespace smarterase_names; int px, py, npx, npy; int qx, qy, sx, sy, tx, ty; int ww, hh, ii, rad, inc, cc; int dist2, mindist2; float slope; char *pmap; float *pix1, *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); if (sa_stat != 3) return; // nothing selected if (! sa_validate()) return; // area invalid for curr. image file ww = E1pxm->ww; hh = E1pxm->hh; for (py = sa_miny; py < sa_maxy; py++) // undo all pixels in area for (px = sa_minx; px < sa_maxx; px++) { ii = py * ww + px; if (! sa_pixmap[ii]) continue; // pixel not selected pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel memcpy(pix3,pix1,pcc); } Fpaint2(); // update window if (mode == 2) return; // mode = undo, done cc = ww * hh; // allocate pixel done map pmap = (char *) zmalloc(cc); memset(pmap,0,cc); for (py = sa_miny; py < sa_maxy; py++) // loop all pixels in area for (px = sa_minx; px < sa_maxx; px++) { ii = py * ww + px; if (! sa_pixmap[ii]) continue; // pixel not selected if (pmap[ii]) continue; // pixel already done mindist2 = 999999; // find nearest edge npx = npy = 0; for (rad = 1; rad < 50; rad++) // 50 pixel limit { for (qx = px-rad; qx <= px+rad; qx++) // search within rad for (qy = py-rad; qy <= py+rad; qy++) { if (qx < 0 || qx >= ww) continue; // off image edge if (qy < 0 || qy >= hh) continue; ii = qy * ww + qx; if (sa_pixmap[ii]) continue; // within selected area dist2 = (px-qx) * (px-qx) + (py-qy) * (py-qy); // distance**2 to edge pixel if (dist2 < mindist2) { mindist2 = dist2; npx = qx; // save nearest edge pixel found npy = qy; } } if (rad * rad >= mindist2) break; // found edge, 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)) // line px/py to qx/qy is more { // vertical than horizontal slope = 1.0 * (qx - px) / (qy - py); if (qy > py) inc = 1; else inc = -1; for (sy = py; sy != qy; sy += inc) { // sx/sy = line from px/py to qx/qy sx = px + slope * (sy - py); ii = sy * ww + sx; if (pmap[ii]) continue; // skip done pixels pmap[ii] = 1; tx = qx + (qx - sx); // tx/ty = extended line from qx/qy ty = qy + (qy - sy); if (tx < 0) tx = 0; // don't go off edge if (tx > ww-1) tx = ww-1; if (ty < 0) ty = 0; if (ty > hh-1) ty = hh-1; pix1 = PXMpix(E1pxm,tx,ty); // copy pixel from tx/ty to sx/sy pix3 = PXMpix(E3pxm,sx,sy); // simplified memcpy(pix3,pix1,pcc); } } else // more horizontal than vertical { 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; pix1 = PXMpix(E1pxm,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) { using namespace smarterase_names; int ii, px, py, dx, dy, adx, ady; float blur_weight[12][12]; // up to blur radius = 10 float rad, radflat2; float m, d, w, sum, weight; float red, green, blue; float *pix9, *pix3, *pixN; int nc = E3pxm->nc; if (sa_stat != 3) return 0; rad = radius - 0.2; radflat2 = rad * rad; for (dx = 0; dx < 12; dx++) // clear weights array for (dy = 0; dy < 12; dy++) blur_weight[dx][dy] = 0; for (dx = -rad-1; dx <= rad+1; dx++) // blur_weight[dx][dy] = no. of pixels for (dy = -rad-1; dy <= rad+1; dy++) // at distance (dx,dy) from center ++blur_weight[abs(dx)][abs(dy)]; m = sqrt(radflat2 + radflat2); // corner pixel distance from center sum = 0; for (dx = 0; dx <= rad+1; dx++) // compute weight of pixel for (dy = 0; dy <= rad+1; dy++) // at distance dx, dy { d = sqrt(dx*dx + dy*dy); w = (m + 1.2 - d) / m; w = w * w; sum += blur_weight[dx][dy] * w; blur_weight[dx][dy] = w; } for (dx = 0; dx <= rad+1; dx++) // make weights add up to 1.0 for (dy = 0; dy <= rad+1; dy++) blur_weight[dx][dy] = blur_weight[dx][dy] / sum; E9pxm = PXM_copy(E3pxm); // copy edited image for (py = sa_miny; py < sa_maxy; py++) // loop all pixels in area for (px = sa_minx; px < sa_maxx; px++) { ii = py * E1pxm->ww + px; if (! sa_pixmap[ii]) continue; // pixel not in area pix9 = PXMpix(E9pxm,px,py); // source pixel pix3 = PXMpix(E3pxm,px,py); // target pixel rad = radius; red = green = blue = 0; for (dy = -rad-1; dy <= rad+1; dy++) // loop neighbor pixels within radius for (dx = -rad-1; dx <= rad+1; dx++) { if (px+dx < 0 || px+dx >= E3ww) continue; // omit pixels off edge if (py+dy < 0 || py+dy >= E3hh) continue; adx = abs(dx); ady = abs(dy); pixN = pix9 + (dy * E3ww + 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; } /********************************************************************************/ // Shift R/B color planes to maximize overlap with G plane. // Radius of a pixel in R-plane or B-plane is shifted using the formula: // R2 = F1 * R1 + F2 * R1 * R1 + F3 * sqrt(R1) // R1: pixel old radius R2: pixel new radius // F1 F2 F3: computed values for maximum overlap // F1 is near 1.0 // F2 and F3 are near 0.0 namespace chromatic1_names { editfunc EFchromatic1; double Rf1, Rf2, Rf3, Bf1, Bf2, Bf3; int Eww, Ehh; int cx, cy; uint8 *pixcon; int threshcon; int evrgb; float evF1, evF2, evF3; double evRsum1[max_threads]; double evRsum2; } // menu function void m_chromatic1(GtkWidget *, cchar *menu) // 20.0 { using namespace chromatic1_names; void chromatic1_threshcon(); int chromatic1_dialog_event(zdialog* zd, cchar *event); cchar *title = E2X("Chromatic Aberration"); F1_help_topic = "chromatic 1"; EFchromatic1.menuname = menu; // setup edit EFchromatic1.menufunc = m_chromatic1; EFchromatic1.funcname = "chromatic1"; EFchromatic1.Farea = 1; // select area ignored if (! edit_setup(EFchromatic1)) return; Eww = E3pxm->ww; // image dimensions Ehh = E3pxm->hh; cx = Eww / 2; // image center cy = Ehh / 2; chromatic1_threshcon(); // get image threshold contrast /*** ____________________________________________ | Chromatic Aberration | | | | Red Factors: [ 1.0 ] [ 0.0 ] [ 0.0 ] | | Blue Factors: [ 1.0 ] [ 0.0 ] [ 0.0 ] | | | | Find optimum factors: [ Search ] | | | | [Done] [Cancel] | |____________________________________________| ***/ zdialog *zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); CEF->zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=3|homog"); 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","labR","vb1",E2X("Red Factors")); zdialog_add_widget(zd,"zspin","Rf1","vb2","-3|+3|0.2|0.0","size=6"); zdialog_add_widget(zd,"zspin","Rf2","vb3","-3|+3|0.2|0.0","size=6"); zdialog_add_widget(zd,"zspin","Rf3","vb4","-3|+3|0.2|0.0","size=6"); zdialog_add_widget(zd,"label","labB","vb1",E2X("Blue Factors")); zdialog_add_widget(zd,"zspin","Bf1","vb2","-3|+3|0.2|0.0","size=6"); zdialog_add_widget(zd,"zspin","Bf2","vb3","-3|+3|0.2|0.0","size=6"); zdialog_add_widget(zd,"zspin","Bf3","vb4","-3|+3|0.2|0.0","size=6"); zdialog_add_widget(zd,"hbox","hbopt","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labopt","hbopt",E2X("Find optimum factors:")); zdialog_add_widget(zd,"button","search","hbopt",Bsearch,"space=5"); zdialog_run(zd,chromatic1_dialog_event,"save"); // run dialog - parallel return; } // dialog event and completion function int chromatic1_dialog_event(zdialog *zd, cchar *event) { using namespace chromatic1_names; void chromatic1_RBshift(); void chromatic1_RBsearch(); if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // done else edit_cancel(0); // discard edit zfree(pixcon); // free memory return 1; } if (zstrstr("Rf1 Rf2 Rf3 Bf1 Bf2 Bf3",event)) { zdialog_fetch(zd,"Rf1",Rf1); // get manually adjusted factors zdialog_fetch(zd,"Rf2",Rf2); zdialog_fetch(zd,"Rf3",Rf3); zdialog_fetch(zd,"Bf1",Bf1); zdialog_fetch(zd,"Bf2",Bf2); zdialog_fetch(zd,"Bf3",Bf3); zmainloop(); chromatic1_RBshift(); // shift R/B color planes using factors } if (strmatch(event,"search")) { chromatic1_RBsearch(); // search for optimum factors zdialog_stuff(zd,"Rf1",Rf1); // stuff dialog with found factors zdialog_stuff(zd,"Rf2",Rf2); zdialog_stuff(zd,"Rf3",Rf3); zdialog_stuff(zd,"Bf1",Bf1); zdialog_stuff(zd,"Bf2",Bf2); zdialog_stuff(zd,"Bf3",Bf3); chromatic1_RBshift(); // shift R/B color planes using factors } return 1; } // Find contrast threshold for highest-contrast pixels in color green. void chromatic1_threshcon() { using namespace chromatic1_names; int ii, jj; int px, py, qx, qy; int Gcon, Gmax; int condist[256]; float *pix1, *pix2; pixcon = (uint8 *) zmalloc(Eww * Ehh); // map of pixel green contrast for (py = 9; py < Ehh-9; py++) // loop all pixels for (px = 9; px < Eww-9; px++) { Gmax = 0; pix1 = PXMpix(E1pxm,px,py); for (qy = py - 5; qy <= py + 5; qy += 10) // loop 4 neighbors offset by 5 for (qx = px - 5; qx <= px + 5; qx += 10) { pix2 = PXMpix(E1pxm,qx,qy); // find max. green contrast Gcon = pix1[1] - pix2[1]; Gcon = abs(Gcon); if (Gcon > Gmax) Gmax = Gcon; } ii = py * Eww + px; // save pixel green contrast pixcon[ii] = Gmax; } memset(condist, 0, 256 * sizeof(int)); for (ii = 0; ii < Eww * Ehh; ii++) // make histogram of contrast values { jj = pixcon[ii]; // condist[jj] = pixels with ++condist[jj]; // contrast value jj } for (jj = 0, ii = 255; ii > 0; ii--) // find minimum contrast for 200K { // highest green-contrast pixels jj += condist[ii]; if (ii > 100 && jj > 100000) break; if (jj > 200000) break; } threshcon = ii; // threshold for chromatic1_evaluate() return; } // Compute factors Rf1 Rf2 Bf1 Bf2 that minimize // Rplane - Gplane and Bplane - Gplane void chromatic1_RBsearch() { using namespace chromatic1_names; double chromatic1_evaluate(int rgb, float F1, float F2, float F3); int rgb; // 0/1/2 = red/green/blue float F1, F2, F3; float R1, R2, R3; float S1, S2, S3; double Rsum, Rmin; Ffuncbusy = 1; Fbusy_goal = 1372; for (rgb = 0; rgb <= 2; rgb += 2) // loop rgb = 0, 2 (red, blue) { Rmin = 1.0 * Eww * Ehh * 256; // max. possible image difference R1 = R2 = R3 = 0; for (F1 = -3; F1 <= +3; F1 += 1.0) // loop all combinations F1, F2, F3 for (F2 = -3; F2 <= +3; F2 += 1.0) // in 1-pixel steps for (F3 = -3; F3 <= +3; F3 += 1.0) { Rsum = chromatic1_evaluate(rgb, F1, F2, F3); // evaluate each combination if (Rsum < Rmin) { Rmin = Rsum; // remember best combination R1 = F1; R2 = F2; R3 = F3; } Fbusy_done++; zmainloop(); } S1 = R1; S2 = R2; S3 = R3; // loop around best combination for (F1 = R1-1; F1 <= R1+1; F1 += 0.333) // loop all combinations F1, F2, F3 for (F2 = R2-1; F2 <= R2+1; F2 += 0.333) // in 0.333 pixel steps for (F3 = R3-1; F3 <= R3+1; F3 += 0.333) { Rsum = chromatic1_evaluate(rgb, F1, F2, F3); // evaluate each combination if (Rsum < Rmin) { Rmin = Rsum; // remember best combination S1 = F1; S2 = F2; S3 = F3; } Fbusy_done++; zmainloop(); } if (rgb == 0) { Rf1 = S1; // red plane factors Rf2 = S2; Rf3 = S3; } else { Bf1 = S1; // blue plane factors Bf2 = S2; Bf3 = S3; } } Ffuncbusy = 0; Fbusy_goal = Fbusy_done = 0; return; } // evaluate the alignment of a shifted R/B color plane with the G plane // R/B color plane is shifted using factors F1 F2 F3 // rgb is 0/2 for R/B double chromatic1_evaluate(int rgb, float F1, float F2, float F3) { using namespace chromatic1_names; void * chromatic1_evaluate_wthread(void *); evrgb = rgb; // make args avail. for thread evF1 = F1; evF2 = F2; evF3 = F3; do_wthreads(chromatic1_evaluate_wthread,NWT); // do worker threads evRsum2 = 0; for (int ii = 0; ii < NWT; ii++) evRsum2 += evRsum1[ii]; return evRsum2; } void * chromatic1_evaluate_wthread(void *arg) // worker thread { using namespace chromatic1_names; int index = *((int *) arg); int ii, px1, py1; float fx1, fy1, fx2, fy2, fx3, fy3; float *pix1, vpix[4]; double Rsum = 0; for (py1 = index + 9; py1 < Ehh-9; py1 += NWT) // loop all image pixels for (px1 = 9; px1 < Eww-9; px1++) { ii = py1 * Eww + px1; // skip low contrast pixel if (pixcon[ii] < threshcon) continue; fx1 = 1.0 * (px1 - cx) / cx; // -1 to +1 at edges, 0 at center fy1 = 1.0 * (py1 - cy) / cy; fx2 = fx1 * fabsf(fx1); // square, keep sign fy2 = fy1 * fabsf(fy1); fx3 = fx1 * fx2; // cube fy3 = fy1 * fy2; fx1 = fx1 * evF1; // * F1 fy1 = fy1 * evF1; fx2 = fx2 * evF2; // * F2 fy2 = fy2 * evF2; fx3 = fx3 * evF3; // * F3 fy3 = fy3 * evF3; pix1 = PXMpix(E1pxm,px1,py1); // image unshifted pixel (G) vpixel(E1pxm, px1+fx1+fx2+fx3, py1+fy1+fy2+fy3, vpix); // virtual pixel, shifted (R/B) Rsum += fabs(vpix[evrgb] - pix1[1]); // sum shifted R/B - unshifted G } evRsum1[index] = Rsum; pthread_exit(0); } // shift R/B color planes using factors Rf1 Rf2 Bf1 Bf2 void chromatic1_RBshift() { void * chromatic1_RBshift_wthread(void *); do_wthreads(chromatic1_RBshift_wthread,NWT); // do worker threads CEF->Fmods++; // image is modified CEF->Fsaved = 0; // and not saved Fpaint2(); // update window return; } void * chromatic1_RBshift_wthread(void *arg) // worker thread { using namespace chromatic1_names; int index = *((int *) arg); int px3, py3, ok; float px1, py1; float fx1, fy1, fx2, fy2, fx3, fy3; float *pix3, vpix[4]; for (py3 = index; py3 < Ehh; py3 += NWT) // loop all image pixels for (px3 = 0; px3 < Eww; px3++) { pix3 = PXMpix(E3pxm,px3,py3); // output pixel fx1 = 1.0 * (px3 - cx) / cx; // -1 to +1 at edges, 0 at center fy1 = 1.0 * (py3 - cy) / cy; fx2 = fx1 * fabsf(fx1); // square, keep sign fy2 = fy1 * fabsf(fy1); fx3 = fx1 * fx2; // cube fy3 = fy1 * fy2; px1 = px3 + Rf1 * fx1 + Rf2 * fx2 + Rf3 * fx3; // red shift py1 = py3 + Rf1 * fy1 + Rf2 * fy2 + Rf3 * fy3; ok = vpixel(E1pxm,px1,py1,vpix); // red input pixel if (ok) pix3[0] = vpix[0]; px1 = px3 + Bf1 * fx1 + Bf2 * fx2 + Bf3 * fx3; // blue shift py1 = py3 + Bf1 * fy1 + Bf2 * fy2 + Bf3 * fy3; ok = vpixel(E1pxm,px1,py1,vpix); // blue input pixel if (ok) pix3[2] = vpix[2]; } pthread_exit(0); } /********************************************************************************/ // Remove axial chromatic aberration, color bands often seen on // dark image features against a bright background. namespace chromatic2_names { editfunc EFchromatic2; zdialog *zd; int Eww, Ehh; // image dimensions float Crgb[3]; // chromatic color float Rrgb[3]; // replacement color float Brgb[3]; // background color float Rhsl[3]; // replacement color, HSL space float Cmatch; // chromatic color match level int pcc = 3 * sizeof(float); // RGB pixel size uint8 *Pmark; // pixel undo markers int Vmark; // current Pmark value 0-255 int Wcolor; // which color check button int Bprox; // background proximity range int Fusehue; // flag, use hue } // menu function void m_chromatic2(GtkWidget *, cchar *menu) // 20.0 { using namespace chromatic2_names; int chromatic2_dialog_event(zdialog* zd, cchar *event); void chromatic2_mousefunc(); cchar *title = E2X("Chromatic Aberration"); int cc; F1_help_topic = "chromatic 2"; EFchromatic2.menuname = menu; // setup edit EFchromatic2.menufunc = m_chromatic2; EFchromatic2.funcname = "chromatic2"; EFchromatic2.mousefunc = chromatic2_mousefunc; // mouse function EFchromatic2.Farea = 2; // select area OK if (! edit_setup(EFchromatic2)) return; Eww = E3pxm->ww; // image dimensions Ehh = E3pxm->hh; cc = Eww * Ehh; // allocate pixel markers Pmark = (uint8 *) zmalloc(cc); memset(Pmark,0,cc); /*** ______________________________________ | Chromatic Aberration | | | | [x] Chromatic Color [ ### ] | | [_] Replacement Color [ ### ] | | [_] Background Color [ ### ] | | Color match level [ 70 ] | | Background proximity [ 10 ] | | | | [apply] [undo] [done] [cancel] | |______________________________________| ***/ zd = zdialog_new(title,Mwin,Bapply,Bundo,Bdone,Bcancel,null); CEF->zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"homog"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"homog"); zdialog_add_widget(zd,"check","Ccheck","vb1",E2X("Chromatic Color"),"space=3"); zdialog_add_widget(zd,"colorbutt","Crgb","vb2","0|0|0"); zdialog_add_widget(zd,"check","Rcheck","vb1",E2X("Replacement Color"),"space=3"); zdialog_add_widget(zd,"colorbutt","Rrgb","vb2","0|0|0"); zdialog_add_widget(zd,"check","Bcheck","vb1",E2X("Background Color"),"space=3"); zdialog_add_widget(zd,"colorbutt","Brgb","vb2","0|0|0"); zdialog_add_widget(zd,"hbox","hb4","dialog"); zdialog_add_widget(zd,"label","labmatch","hb4",E2X("Color match level"),"space=3"); zdialog_add_widget(zd,"zspin","Cmatch","hb4","50|100|1|70","space=3"); zdialog_add_widget(zd,"hbox","hb5","dialog"); zdialog_add_widget(zd,"label","labp","hb5",E2X("Background Proximity"),"space=3"); zdialog_add_widget(zd,"zspin","Bprox","hb5","1|100|1|10","space=3"); zdialog_run(zd,chromatic2_dialog_event,"save"); // run dialog - parallel zdialog_stuff(zd,"Ccheck",1); zdialog_stuff(zd,"Rcheck",0); zdialog_stuff(zd,"Bcheck",0); Wcolor = 1; // next color = chromatic Cmatch = 70; // match level % Bprox = 10; // background proximity Vmark = 0; // no marks yet takeMouse(chromatic2_mousefunc,dragcursor); return; } // dialog event and completion function int chromatic2_dialog_event(zdialog *zd, cchar *event) { using namespace chromatic2_names; void chromatic2_mousefunc(); void chromatic2_repair(); int ii; int px, py; float *pix1, *pix3; if (strmatch(event,"done")) zd->zstat = 3; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 4; // from f_open() if (zd->zstat) { if (zd->zstat == 1) // [apply] { zd->zstat = 0; // keep dialog active if (Vmark == 255) { // check for max. marker value zmessageACK(Mwin,E2X("255 iterations, cannot continue")); return 1; } chromatic2_repair(); // do repair function return 1; } if (zd->zstat == 2) // [undo] { zd->zstat = 0; // keep dialog active if (Vmark < 1) return 1; // nothing to undo for (py = 0; py < Ehh; py++) for (px = 0; px < Eww; px++) { ii = py * Eww + px; if (Pmark[ii] == Vmark) { pix1 = PXMpix(E1pxm,px,py); pix3 = PXMpix(E3pxm,px,py); memcpy(pix3,pix1,pcc); } } Vmark -= 1; Fpaint2(); return 1; } if (zd->zstat == 3) edit_done(0); // [done] else edit_cancel(0); // [cancel] freeMouse(); zfree(Pmark); return 1; } if (strmatch(event,"focus")) takeMouse(chromatic2_mousefunc,dragcursor); if (zstrstr("Ccheck Rcheck Bcheck",event)) { // mark which color to be selected zdialog_stuff(zd,"Ccheck",0); zdialog_stuff(zd,"Rcheck",0); zdialog_stuff(zd,"Bcheck",0); zdialog_stuff(zd,event,1); if (strmatch(event,"Ccheck")) Wcolor = 1; if (strmatch(event,"Rcheck")) Wcolor = 2; if (strmatch(event,"Bcheck")) Wcolor = 3; } if (strmatch(event,"Bprox")) // background proximity limit zdialog_fetch(zd,"Bprox",Bprox); if (strmatch(event,"Cmatch")) zdialog_fetch(zd,"Cmatch",Cmatch); return 1; } // mouse function void chromatic2_mousefunc() { using namespace chromatic2_names; float *pix; char text[20]; if (! LMclick) return; LMclick = 0; if (! zd) return; pix = PXBpix(E1pxm,Mxclick,Myclick); // pick new color from image snprintf(text,20,"%.0f|%.0f|%.0f",pix[0],pix[1],pix[2]); if (Wcolor == 1) { // chromatic color to fix Crgb[0] = pix[0]; Crgb[1] = pix[1]; Crgb[2] = pix[2]; zdialog_stuff(zd,"Crgb",text); // update dialog color button } if (Wcolor == 2) { // replacement color Rrgb[0] = pix[0]; Rrgb[1] = pix[1]; Rrgb[2] = pix[2]; zdialog_stuff(zd,"Rrgb",text); } if (Wcolor == 3) { // background color Brgb[0] = pix[0]; Brgb[1] = pix[1]; Brgb[2] = pix[2]; zdialog_stuff(zd,"Brgb",text); } return; } // repair the chromatic aberraton void chromatic2_repair() { using namespace chromatic2_names; void * chromatic2_repair_wthread(void *arg); float rmin, rmax; char text[20]; cchar *pp; zdialog_fetch(zd,"Crgb",text,20); // get chromatic color to fix pp = strField(text,'|',1); if (pp) Crgb[0] = atoi(pp); pp = strField(text,'|',2); if (pp) Crgb[1] = atoi(pp); pp = strField(text,'|',3); if (pp) Crgb[2] = atoi(pp); zdialog_fetch(zd,"Rrgb",text,20); // get replacement color pp = strField(text,'|',1); if (pp) Rrgb[0] = atoi(pp); pp = strField(text,'|',2); if (pp) Rrgb[1] = atoi(pp); pp = strField(text,'|',3); if (pp) Rrgb[2] = atoi(pp); zdialog_fetch(zd,"Brgb",text,20); // get background color pp = strField(text,'|',1); if (pp) Brgb[0] = atoi(pp); pp = strField(text,'|',2); if (pp) Brgb[1] = atoi(pp); pp = strField(text,'|',3); if (pp) Brgb[2] = atoi(pp); zdialog_fetch(zd,"Bprox",Bprox); // background proximity range zdialog_fetch(zd,"Cmatch",Cmatch); // color match level RGBtoHSL(Rrgb[0],Rrgb[1],Rrgb[2],Rhsl[0],Rhsl[1],Rhsl[2]); // replacement color in HSL units rmin = rmax = Rrgb[0]; if (Rrgb[1] < rmin) rmin = Rrgb[1]; // get replacement color RGB range if (Rrgb[2] < rmin) rmin = Rrgb[2]; if (Rrgb[1] > rmax) rmax = Rrgb[1]; if (Rrgb[2] > rmax) rmax = Rrgb[2]; Fusehue = 0; // use replacement color hue if (rmax > 0 && (rmax-rmin) / rmax > 0.1) Fusehue = 1; // only if distinct hue Vmark += 1; // next pixel marker value do_wthreads(chromatic2_repair_wthread,NWT); // do worker threads CEF->Fmods++; // image is modified CEF->Fsaved = 0; // and not saved Fpaint2(); // update window return; } void * chromatic2_repair_wthread(void *arg) // worker threads { using namespace chromatic2_names; int index = *((int *) arg); int ii, dist; int px, py, qx, qy; float f1, f2; float *pix1, *pix3; float Prgb[3], Phsl[3]; int dx, dy, R, D; R = Bprox; for (py = R+index; py < Ehh-R; py += NWT) // loop all image pixels for (px = R; px < Eww-R; px++) // find pix1 matching chromatic color { ii = py * Eww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } pix3 = PXMpix(E1pxm,px,py); // input pixel f1 = PIXMATCH(pix3,Crgb); // compare to chromatic color if (f1 < 0.01 * Cmatch) continue; // match level, 0.7 ... 1.0 f1 = 3.333 * f1 - 2.333; // part replacement color, 0.0 ... 1.0 f1 = sqrtf(f1); // use curve f2 = 1.0 - f1; // part original color, 1.0 ... 0.0 D = 999; for (qy = py-R; qy <= py+R; qy++) // loop pixels within Bprox of pix3 for (qx = px-R; qx <= px+R; qx++) { pix1 = PXMpix(E1pxm,qx,qy); if (PIXMATCH(pix1,Brgb) < 0.01 * Cmatch) continue; // not a background pixel dx = qx - px; dy = qy - py; D = sqrtf(dx*dx + dy*dy); // image pixel distance to background if (D <= Bprox) goto break2; // exit both loops } break2: if (D > Bprox) continue; // pix3 too far from background pix3 = PXMpix(E3pxm,px,py); // output pixel if (Fusehue) { // use replacement color hue only RGBtoHSL(pix3[0],pix3[1],pix3[2],Phsl[0],Phsl[1],Phsl[2]); // convert to HSL Phsl[0] = Rhsl[0]; // replace hue only HSLtoRGB(Phsl[0],Phsl[1],Phsl[2],Prgb[0],Prgb[1],Prgb[2]); // back to RGB pix3[0] = f1 * Prgb[0] + f2 * pix3[0]; // new = mix of new + old pix3[1] = f1 * Prgb[1] + f2 * pix3[1]; pix3[2] = f1 * Prgb[2] + f2 * pix3[2]; } else { pix3[0] = f1 * Rrgb[0] + f2 * pix3[0]; // use replacement color RGB pix3[1] = f1 * Rrgb[1] + f2 * pix3[1]; pix3[2] = f1 * Rrgb[2] + f2 * pix3[2]; } Pmark[ii] = Vmark; // mark undo level } pthread_exit(0); } /******************************************************************************** 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 = E2X("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.sd = 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_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.sd; int ii; char color[20]; cchar *pp; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); signal_thread(); return 1; } if (zd->zstat) { if (zd->zstat == 1) { // done wait_thread_idle(); // insure thread done float R = 1.0 * E0pxm->ww / E3pxm->ww; vign_cx = R * vign_cx; // scale geometries to full size vign_cy = R * vign_cy; vign_rad = R * vign_rad; edit_fullsize(); // get full size image signal_thread(); edit_done(0); // commit edit } else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(vign_mousefunc,dragcursor); // connect mouse function if (strmatchN(event,"RB",2)) { // new choice of curve sd->fact[0] = sd->fact[1] = 0; ii = strmatchV(event,"RBbrite","RBcolor",null); vignette_spc = ii = ii - 1; sd->fact[ii] = 1; // active curve splcurve_generate(sd,ii); // regenerate curve gtk_widget_queue_draw(sd->drawarea); // draw curve signal_thread(); } if (strmatch(event,"blendwidth")) signal_thread(); if (strmatch(event,"color")) { // change color zdialog_fetch(zd,"color",color,19); // get color from color wheel pp = strField(color,"|",1); if (pp) vignette_RGB[0] = atoi(pp); pp = strField(color,"|",2); if (pp) vignette_RGB[1] = atoi(pp); pp = strField(color,"|",3); if (pp) vignette_RGB[2] = atoi(pp); signal_thread(); // trigger update thread } if (strmatch(event,"load")) { // load saved curve splcurve_load(sd); Vign_curvedit(0); signal_thread(); return 0; } if (strmatch(event,"savecurve")) { // save curve to file splcurve_save(sd); return 0; } return 0; } // get mouse position and set new center for vignette void vign_mousefunc() // mouse function { if (! LMclick && ! Mdrag) return; LMclick = 0; vign_cx = Mxposn; // new vignette center = mouse position vign_cy = Myposn; Mxdrag = Mydrag = 0; signal_thread(); // trigger image update return; } // this function is called when the curve is edited void Vign_curvedit(int) { signal_thread(); // update image return; } // thread function void * Vign_thread(void *) { void * Vign_wthread(void *arg); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 do_wthreads(Vign_wthread,NWT); // worker threads CEF->Fmods++; CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.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.sd; 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_blendwidth) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth 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; } pthread_exit(0); } /********************************************************************************/ // 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 committed // image 9 committed 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"; m_viewmode(0,"F"); // file view mode 19.0 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(E2X("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",E2X("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",E2X("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",E2X("min. contrast"),"space=5"); zdialog_add_widget(zd,"hscale","contrast","hbmc","1|500|1|40","space=5|expand"); zdialog_add_widget(zd,"hbox","hbbutts","dialog",0,"space=5"); zdialog_add_widget(zd,"button","erase","hbbutts",Berase,"space=5"); zdialog_add_widget(zd,"button","red","hbbutts",Bred,"space=5"); zdialog_add_widget(zd,"button","undo1","hbbutts",Bundolast,"space=5"); zdialog_add_widget(zd,"button","apply","hbbutts",Bapply,"space=5"); zdialog_resize(zd,300,0); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_fetch(zd,"spotspan",spotspan); // max. dustspot span (pixels) spotspan2 = spotspan * spotspan; zdialog_fetch(zd,"brightness",brightness); // max. dustspot brightness brightness = 0.001 * brightness; // scale 0 to 1 = white zdialog_fetch(zd,"contrast",contrast); // min. dustspot contrast contrast = 0.001 * contrast; // scale 0 to 1 = black/white zdialog_run(zd,dust_dialog_event,"save"); // run dialog - parallel signal_thread(); return; } // dialog event and completion callback function int dust_dialog_event(zdialog *zd, cchar *event) { using namespace dust_names; void dust_erase(); if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { // done wrapup_thread(8); // thread finish, exit PXM_free(E3pxm); E3pxm = E9pxm; // image 3 = image 9 E9pxm = 0; edit_done(0); // commit edit } else { // cancel wrapup_thread(8); // thread finish, exit PXM_free(E9pxm); edit_cancel(0); // discard edit } zfree(pixgroup); // free memory zfree(pixstack); return 1; } if (zstrstr("spotspan brightness contrast red",event)) { zdialog_fetch(zd,"spotspan",spotspan); // max. dustspot span (pixels) spotspan2 = spotspan * spotspan; zdialog_fetch(zd,"brightness",brightness); // max. dustspot brightness brightness = 0.001 * brightness; // scale 0 to 1 = white zdialog_fetch(zd,"contrast",contrast); // min. dustspot contrast contrast = 0.001 * contrast; // scale 0 to 1 = black/white signal_thread(); // do the work } if (strmatch(event,"erase")) dust_erase(); if (strmatch(event,"blendwidth")) dust_erase(); if (strmatch(event,"undo1")) { PXM_free(E3pxm); E3pxm = PXM_copy(E9pxm); Fred = 0; Fpaint2(); } if (strmatch(event,"apply")) { // button if (Fred) dust_erase(); PXM_free(E9pxm); // image 9 = copy of image 3 E9pxm = PXM_copy(E3pxm); CEF->Fmods++; CEF->Fsaved = 0; } return 1; } // dust find thread function - find the dust particles and mark them void * dust_thread(void *) { using namespace dust_names; int xspan, yspan, span2; int group, cc, ii, kk, Nremoved; int px, py, dx, dy, ppx, ppy, npx, npy; float gbright, pbright, pcontrast; float ff = 1.0 / 256.0; uint16 direc; float *pix3; while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window updates PXM_free(E3pxm); E3pxm = PXM_copy(E9pxm); paintlock(0); // unblock window updates cc = E1pxm->ww * E1pxm->hh * sizeof(int); // clear group arrays memset(pixgroup,0,cc); cc = maxgroups * sizeof(int); memset(groupcount,0,cc); memset(edgecount,0,cc); cc = maxgroups * sizeof(float ); memset(groupbright,0,cc); memset(edgebright,0,cc); cc = maxgroups * sizeof(sgroupspan); memset(groupspan,0,cc); group = 0; for (py = 0; py < E1pxm->hh; py++) // loop all pixels for (px = 0; px < E1pxm->ww; px++) { ii = py * E1pxm->ww + px; if (sa_stat == 3 && ! sa_pixmap[ii]) continue; // not in active area if (pixgroup[ii]) continue; // already assigned to a group pix3 = PXMpix(E3pxm,px,py); // get pixel brightness gbright = ff * pixbright(pix3); // 0 to 1.0 = white if (gbright > brightness) continue; // ignore bright pixel if (group == maxgroups-1) break; // too many groups, make no more pixgroup[ii] = ++group; // assign next group groupcount[group] = 1; groupbright[group] = gbright; pixstack[0].px = px; // put pixel into stack with pixstack[0].py = py; // direction = ahead pixstack[0].direc = 0; Nstack = 1; while (Nstack) { kk = Nstack - 1; // get last pixel in stack px = pixstack[kk].px; py = pixstack[kk].py; direc = pixstack[kk].direc; // next search direction if (direc == 'x') { Nstack--; // none left continue; } if (Nstack > 1) { ii = Nstack - 2; // get prior pixel in stack ppx = pixstack[ii].px; ppy = pixstack[ii].py; } else { ppx = px - 1; // if only one, assume prior = left ppy = py; } dx = px - ppx; // vector from prior to this pixel dy = py - ppy; switch (direc) { case 0: npx = px + dx; npy = py + dy; pixstack[kk].direc = 1; break; case 1: npx = px + dy; npy = py + dx; pixstack[kk].direc = 3; break; case 2: npx = px - dx; // back to prior pixel npy = py - dy; // (this path never taken) zappcrash("stack search bug"); break; case 3: npx = px - dy; npy = py - dx; pixstack[kk].direc = 4; break; case 4: npx = px - dx; npy = py + dy; pixstack[kk].direc = 5; break; case 5: npx = px - dy; npy = py + dx; pixstack[kk].direc = 6; break; case 6: npx = px + dx; npy = py - dy; pixstack[kk].direc = 7; break; case 7: npx = px + dy; npy = py - dx; pixstack[kk].direc = 'x'; break; default: npx = npy = 0; zappcrash("stack search bug"); } if (npx < 0 || npx > E1pxm->ww-1) continue; // pixel off the edge if (npy < 0 || npy > E1pxm->hh-1) continue; ii = npy * E1pxm->ww + npx; if (pixgroup[ii]) continue; // pixel already assigned if (sa_stat == 3 && ! sa_pixmap[ii]) continue; // pixel outside area pix3 = PXMpix(E3pxm,npx,npy); // pixel brightness pbright = ff * pixbright(pix3); if (pbright > brightness) continue; // brighter than limit pixgroup[ii] = group; // assign pixel to group ++groupcount[group]; // count pixels in group groupbright[group] += pbright; // sum brightness for group kk = Nstack++; // put pixel into stack pixstack[kk].px = npx; pixstack[kk].py = npy; pixstack[kk].direc = 0; // search direction } } Ngroups = group; // group numbers are 1-Ngroups Nremoved = 0; for (py = 0; py < E1pxm->hh; py++) // loop all pixels for (px = 0; px < E1pxm->ww; px++) { ii = py * E1pxm->ww + px; group = pixgroup[ii]; if (! group) continue; if (groupspan[group].px1 == 0) { // first pixel found in this group groupspan[group].px1 = px; // group px1/py1 = this pixel groupspan[group].py1 = py; continue; } xspan = groupspan[group].px1 - px; // span from group px1/py1 to this pixel yspan = groupspan[group].py1 - py; span2 = xspan * xspan + yspan * yspan; if (span2 > groupspan[group].span2) { groupspan[group].span2 = span2; // if greater, group px2/py2 = this pixel groupspan[group].px2 = px; groupspan[group].py2 = py; } } for (py = 0; py < E1pxm->hh; py++) // loop all pixels for (px = 0; px < E1pxm->ww; px++) { ii = py * E1pxm->ww + px; group = pixgroup[ii]; if (! group) continue; if (groupspan[group].span2 > spotspan2) continue; xspan = groupspan[group].px2 - px; // span from this pixel to group px2/py2 yspan = groupspan[group].py2 - py; span2 = xspan * xspan + yspan * yspan; if (span2 > groupspan[group].span2) { groupspan[group].span2 = span2; // if greater, group px1/py1 = this pixel groupspan[group].px1 = px; groupspan[group].py1 = py; } } for (py = 0; py < E1pxm->hh; py++) // loop all pixels for (px = 0; px < E1pxm->ww; px++) { ii = py * E1pxm->ww + px; // eliminate group if span > limit group = pixgroup[ii]; if (! group) continue; if (! groupcount[group]) pixgroup[ii] = 0; else if (groupspan[group].span2 > spotspan2) { pixgroup[ii] = 0; groupcount[group] = 0; Nremoved++; } } for (py = 1; py < E1pxm->hh-1; py++) // loop all pixels except image edges for (px = 1; px < E1pxm->ww-1; px++) { ii = py * E1pxm->ww + px; group = pixgroup[ii]; if (group) continue; // find pixels bordering group pixels pix3 = PXMpix(E3pxm,px,py); pbright = ff * pixbright(pix3); group = pixgroup[ii-E1pxm->ww-1]; if (group) { ++edgecount[group]; // accumulate pixel count and edgebright[group] += pbright; // bordering the groups } group = pixgroup[ii-E1pxm->ww]; if (group) { ++edgecount[group]; edgebright[group] += pbright; } group = pixgroup[ii-E1pxm->ww+1]; if (group) { ++edgecount[group]; edgebright[group] += pbright; } group = pixgroup[ii-1]; if (group) { ++edgecount[group]; edgebright[group] += pbright; } group = pixgroup[ii+1]; if (group) { ++edgecount[group]; edgebright[group] += pbright; } group = pixgroup[ii+E1pxm->ww-1]; if (group) { ++edgecount[group]; edgebright[group] += pbright; } group = pixgroup[ii+E1pxm->ww]; if (group) { ++edgecount[group]; edgebright[group] += pbright; } group = pixgroup[ii+E1pxm->ww+1]; if (group) { ++edgecount[group]; edgebright[group] += pbright; } } for (group = 1; group <= Ngroups; group++) // compute group pixel and edge pixel { // mean brightness if (groupcount[group] && edgecount[group]) { edgebright[group] = edgebright[group] / edgecount[group]; groupbright[group] = groupbright[group] / groupcount[group]; pcontrast = edgebright[group] - groupbright[group]; // edge - group contrast if (pcontrast < contrast) { groupcount[group] = 0; Nremoved++; } } } for (py = 0; py < E1pxm->hh; py++) // loop all pixels for (px = 0; px < E1pxm->ww; px++) { ii = py * E1pxm->ww + px; // eliminate group if low contrast group = pixgroup[ii]; if (! group) continue; if (! groupcount[group]) pixgroup[ii] = 0; } for (py = 0; py < E1pxm->hh; py++) // loop all pixels for (px = 0; px < E1pxm->ww; px++) { ii = py * E1pxm->ww + px; if (! pixgroup[ii]) continue; // not a dust pixel pix3 = PXMpix(E3pxm,px,py); // paint it red pix3[0] = 255; pix3[1] = pix3[2] = 0; } Fred = 1; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } // erase the selected dust areas void dust_erase() { using namespace dust_names; int cc, ii, px, py, inc; int qx, qy, npx, npy; int sx, sy, tx, ty; int rad, dist, dist2, mindist2; float slope, f1, f2; float *pix1, *pix3; char *pmap; int nc = E1pxm->nc, pcc = nc * sizeof(float); Ffuncbusy = 1; PXM_free(E3pxm); // not a thread E3pxm = PXM_copy(E9pxm); cc = E1pxm->ww * E1pxm->hh; // allocate pixel done map pmap = (char *) zmalloc(cc); memset(pmap,0,cc); for (py = 0; py < E1pxm->hh; py++) // loop all pixels for (px = 0; px < E1pxm->ww; px++) { ii = py * E1pxm->ww + px; if (! pixgroup[ii]) continue; // not a dust pixel if (pmap[ii]) continue; // skip pixels already done mindist2 = 999999; npx = npy = 0; for (rad = 1; rad < 10; rad++) // find nearest edge (10 pixel limit) { for (qx = px-rad; qx <= px+rad; qx++) // search within rad for (qy = py-rad; qy <= py+rad; qy++) { if (qx < 0 || qx >= E1pxm->ww) continue; // off image edge if (qy < 0 || qy >= E1pxm->hh) continue; ii = qy * E1pxm->ww + qx; if (pixgroup[ii]) continue; // within dust area dist2 = (px-qx) * (px-qx) + (py-qy) * (py-qy); // distance**2 to edge pixel if (dist2 < mindist2) { mindist2 = dist2; npx = qx; // save nearest pixel found npy = qy; } } if (rad * rad >= mindist2) break; // can quit now } if (! npx && ! npy) continue; // should not happen qx = npx; // nearest edge pixel qy = npy; if (abs(qy - py) > abs(qx - px)) { // qx/qy = near edge from px/py slope = 1.0 * (qx - px) / (qy - py); if (qy > py) inc = 1; else inc = -1; for (sy = py; sy != qy+inc; sy += inc) // line from px/py to qx/qy { sx = px + slope * (sy - py); ii = sy * E1pxm->ww + sx; if (pmap[ii]) continue; pmap[ii] = 1; tx = qx + (qx - sx); // tx/ty = parallel line from qx/qy ty = qy + (qy - sy); if (tx < 0) tx = 0; if (tx > E1pxm->ww-1) tx = E1pxm->ww-1; if (ty < 0) ty = 0; if (ty > E1pxm->hh-1) ty = E1pxm->hh-1; pix1 = PXMpix(E3pxm,tx,ty); // copy pixel from tx/ty to sx/sy pix3 = PXMpix(E3pxm,sx,sy); memcpy(pix3,pix1,pcc); } } else { slope = 1.0 * (qy - py) / (qx - px); if (qx > px) inc = 1; else inc = -1; for (sx = px; sx != qx+inc; sx += inc) { sy = py + slope * (sx - px); ii = sy * E1pxm->ww + sx; if (pmap[ii]) continue; pmap[ii] = 1; tx = qx + (qx - sx); ty = qy + (qy - sy); if (tx < 0) tx = 0; if (tx > E1pxm->ww-1) tx = E1pxm->ww-1; if (ty < 0) ty = 0; if (ty > E1pxm->hh-1) ty = E1pxm->hh-1; pix1 = PXMpix(E3pxm,tx,ty); pix3 = PXMpix(E3pxm,sx,sy); memcpy(pix3,pix1,pcc); } } } zfree(pmap); if (sa_stat == 3) // area edge blending { for (ii = 0; ii < E1pxm->ww * E1pxm->hh; ii++) // find pixels in select area { dist = sa_pixmap[ii]; if (! dist || dist >= sa_blendwidth) continue; py = ii / E1pxm->ww; px = ii - py * E1pxm->ww; pix1 = PXMpix(E1pxm,px,py); // input pixel, unchanged image pix3 = PXMpix(E3pxm,px,py); // output pixel, changed image f2 = sa_blendfunc(dist); f1 = 1.0 - f2; pix3[0] = f1 * pix1[0] + f2 * pix3[0]; // blend the pixels pix3[1] = f1 * pix1[1] + f2 * pix3[1]; pix3[2] = f1 * pix1[2] + f2 * pix3[2]; } } Fred = 0; Ffuncbusy = 0; Fpaint2(); // update window return; } fotoxx-20.08/f.file.cc000066400000000000000000004327541362435004500145450ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* Fotoxx image edit - file menu and file functions m_new_session start a new parallel session of Fotoxx m_cycle2files cycle 2 previous image files m_cycle3files cycle 3 previous image files m_view360 view a 360 degree panorama image m_rename rename current image file or clicked thumbnail m_permissions show and change file permissions 20.0 f_open open and display an image file f_open_saved open the last image file saved f_preload preload image files ahead of need x_prev_next open prev/next file in current or prev/next gallery m_prev menu function - open previous m_next menu function - open next m_prev_next menu button function - open previous or next file m_zoom_menu menu button function - zoom image/thumb (F/G view) m_blank_image create a new monocolor image create_blank_file callable function to create a new monocolor image m_blank_window blank/restore window play_gif play current GIF file animation m_copy_move copy or move an image file to a new location m_copyto_desktop copy an image file to the desktop m_copyto_clip copy clicked file or current file to the clipboard m_wallpaper set current image file as wallpaper m_delete_trash delete or trash an image file m_print print an image file m_print_calibrated print an image file with calibrated colors m_quit menu quit quitxx callable quit m_help help menu m_file_save save a (modified) image file to disk m_file_save_replace save file (replace) for KB shortcut m_file_save_version save file (new version) for KB shortcut file_rootname get root file name /.../filename file_basename get base file name /.../filename.ext file_all_versions get base file and all versions /.../filename.vNN.ext file_new_version get next available version /.../filename.vNN.ext file_newest_version get newest file version or base file name if none file_prior_version get prior version or base file name for given file f_save save an image file to disk (replace, new version, new file) f_save_as dialog to save an image file with a designated file name linedit text file line edit functions find_imagefiles find all image files under a given folder path raw_to_tiff convert a RAW file name to equivalent .tif name set_permissions dialog to show and set file permissions // 20.0 conv_permissions conv. permission formats: external <> mode_t // 20.0 f_realpath get real path for filename having symlinks // 20.0 *********************************************************************************/ #define EX extern // disable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // start a new parallel session of fotoxx // new window is slightly down and right from old window // current language locale is preserved // args = optional command line args // current file is appended if it exists void m_new_session(GtkWidget *, cchar *) { char *pp = curr_file; int cc; if (pp && strchr(pp,' ')) { // if embedded blanks, enclose in quotes cc = strlen(pp); pp = (char *) zmalloc(cc+4); pp[0] = '"'; strncpy(pp+1,curr_file,cc); pp[cc+1] = '"'; pp[cc+2] = 0; new_session(pp); zfree(pp); } else new_session(pp); return; } // callable new session function void new_session(cchar *args) { int cc, xx, yy, ww, hh; char progexe[300], command[1000]; gtk_window_get_position(MWIN,&xx,&yy); // get window position and size gtk_window_get_size(MWIN,&ww,&hh); xx += 100; // shift down and right yy += 100; 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; if (! args) args = ""; snprintf(command,999,"%s -c %d %d %d %d %s", // remove -lang 19.0 progexe, xx, yy, ww, hh, args); printz("\n %s \n",command); shell_asynch(command); return; } /********************************************************************************/ // open the previous file opened (not the same as toolbar [prev] button) // repeated use will alternate the two most recent files void m_cycle2files(GtkWidget *, cchar *) { FILE *fid; char *file, buff[XFCC]; int Nth = 0, err; float gzoom; F1_help_topic = "cycle 2"; if (checkpend("busy block mods")) return; m_viewmode(0,"F"); if (! Cstate) return; gzoom = Cstate->fzoom; fid = fopen(recentfiles_file,"r"); if (! fid) return; file = fgets_trim(buff,XFCC,fid,1); // skip over first most recent file while (true) { file = fgets_trim(buff,XFCC,fid,1); // find next most recent file if (! file) break; err = f_open(file,Nth,0,0,0); if (! err) break; } fclose(fid); Cstate->fzoom = gzoom; // keep same image scale Fpaint2(); return; } /********************************************************************************/ // open the 2nd previous file opened // repeated use will alternate the 3 most recent files void m_cycle3files(GtkWidget *, cchar *) { FILE *fid; char *file, buff[XFCC]; int Nth = 0, err; float gzoom; F1_help_topic = "cycle 3"; if (checkpend("busy block mods")) return; m_viewmode(0,"F"); if (! Cstate) return; gzoom = Cstate->fzoom; fid = fopen(recentfiles_file,"r"); if (! fid) return; file = fgets_trim(buff,XFCC,fid,1); // skip over 2 most recent files file = fgets_trim(buff,XFCC,fid,1); while (true) { file = fgets_trim(buff,XFCC,fid,1); // find next most recent file if (! file) break; err = f_open(file,Nth,0,0,0); if (! err) break; } fclose(fid); Cstate->fzoom = gzoom; // keep same image scale Fpaint2(); return; } /********************************************************************************/ // view a 360 degree panorama image // center of view direction can be any angle 0-360 degrees // image width is assumed to correspond to a 360 degree view namespace view360 { void show(); void * show_thread(void *arg); void mouse(); void KB_func(int key); void quit(); PXB *filepxb, *viewpxb; PIXBUF *viewpixbuf; char *filename = 0; int projection = 1; int Frecalc; int fww, fhh; // file dimensions int dww, dhh; // window dimensions int pdww, pdhh; int cx, cy; float *Fx, *Fy, zoom; zdialog *zd; } // menu function void m_view360(GtkWidget *, const char *) { using namespace view360; F1_help_topic = "view 360° pano"; if (checkpend("busy block mods")) return; if (clicked_file) { // use clicked file if present filename = clicked_file; clicked_file = 0; } else if (curr_file) // else use current file filename = zstrdup(curr_file); else return; filepxb = PXB_load(filename,1); zfree(filename); if (! filepxb) return; zd = zmessage_post_bold(Mwin,"20/20",8, "Mouse drag: pan image 360° \n" "L/R arrow keys: pan image 360° \n" "L/R mouse click: zoom in/out \n" "Key 1 or 2: change projection \n" "Key Q: quit panorama view"); Vmenu_block(1); // block menus Fview360 = 1; // flag for main window KBpress() fww = filepxb->ww; fhh = filepxb->hh; cx = fww/2; // initial view center = middle cy = fhh/2; zoom = 0.5; // initial zoom Frecalc = 1; // initial calculate Fx = Fy = 0; // no allocated constants pdww = pdhh = 0; m_viewmode(0,"F"); gdk_window_freeze_updates(gdkwin); takeMouse(mouse,0); show(); return; } // show current view region on window void view360::show() { using namespace view360; int ii, px, py, dx, dy; float sx, sy, R, C, Tx, Ty; if (! Fview360) return; dww = gdk_window_get_width(gdkwin); // drawing window size dhh = gdk_window_get_height(gdkwin); if (dww < 20 || dhh < 20) return; // too small if (dww != pdww || dhh != pdhh) { pdww = dww; pdhh = dhh; if (Fx) zfree(Fx); if (Fy) zfree(Fy); Fx = (float *) zmalloc(dww * dhh * sizeof(float)); Fy = (float *) zmalloc(dww * dhh * sizeof(float)); Frecalc = 1; } viewpxb = PXB_make(dww,dhh,3); viewpixbuf = viewpxb->pixbuf; gdk_pixbuf_fill(viewpixbuf,0); if (Frecalc && projection == 1) // flat projection { for (py = 0; py < dhh; py++) // loop window pixels for (px = 0; px < dww; px++) { dx = px - dww/2; // distance from center dy = py - dhh/2; dx = dx / zoom; // scale for zoom dy = dy / zoom; Tx = PI * dx / fww; // x view angle Ty = PI * dy / fww; // y view angle if (Tx > PI/3 || Tx < -PI/3 || Ty > PI/4 || Ty < -PI/4) { // limit to +-60 and +-45 deg. view ii = py * dww + px; Fx[ii] = Fy[ii] = 0; continue; } Tx = 0.4 * Tx; sx = dx * cosf(Tx); sy = dy * cosf(Ty) * cosf(Tx); ii = py * dww + px; Fx[ii] = sx; Fy[ii] = sy; } } if (Frecalc && projection == 2) // spherical projection { for (py = 0; py < dhh; py++) // loop window pixels for (px = 0; px < dww; px++) { dx = px - dww/2; // distance from center of window dy = py - dhh/2; dx = dx / zoom; // scale for zoom dy = dy / zoom; sx = 6.28 * dx / fww; // scale (dx,dy) so that fww/4 sy = 6.28 * dy / fww; // (90 deg.) is PI/2 if (sx > 1.0 || sx < -1.0 || sy > 0.7 || sy < -0.7) { // limit +-90 and +-63 deg. view ii = py * dww + px; Fx[ii] = Fy[ii] = 0; continue; } R = sqrtf(sx*sx + sy*sy); // conv. (dx,dy) in rectilinear proj. C = atanf(R); // to spherical coordinates Tx = atanf(sx); Ty = asinf(sy/R * sinf(C)); if (R == 0.0) Tx = Ty = 0; // avoid Ty = nan 19.1 sx = 0.25 * fww * Tx; // conv. spherical coordinate (Ty,Tx) sy = 0.25 * fww * Ty * cosf(Tx); // to flat coordinates (sx,sy) ii = py * dww + px; Fx[ii] = sx; Fy[ii] = sy; } } Frecalc = 0; do_wthreads(show_thread,NWT); cairo_t *cr = draw_context_create(gdkwin,draw_context); gdk_cairo_set_source_pixbuf(cr,viewpxb->pixbuf,0,0); cairo_paint(cr); draw_context_destroy(draw_context); PXB_free(viewpxb); return; } // thread function to generate the image void * view360::show_thread(void *arg) { using namespace view360; int index = *((int *) (arg)); int ii, stat, px, py; float sx, sy; uint8 pixs[4], *pixss, *pixp; for (py = index; py < dhh; py += NWT) // loop window pixels for (px = 0; px < dww; px++) { ii = py * dww + px; sx = Fx[ii]; sy = Fy[ii]; if (sx == 0 && sy == 0) continue; sx += cx; // add back center sy += cy; if (sy < 0) continue; // off the image if (sy >= fhh) continue; if (sx < 0) sx = fww + sx; // handle x wrap-around if (sx >= fww) sx = sx - fww; pixp = PXBpix(viewpxb,px,py); // dest. pixel stat = vpixel(filepxb,sx,sy,pixs); // source pixel interpolated if (stat) memcpy(pixp,pixs,3); // fails at image left/right edge else { pixss = PXBpix(filepxb,int(sx),int(sy)); // use no interpolation memcpy(pixp,pixss,3); } } pthread_exit(0); return 0; } // mouse function - move center of view with mouse drag void view360::mouse() { using namespace view360; int Fshow = 0; if (Mxdrag || Mydrag) { // change center of view cx += 2 * Mwdragx; if (cx < 0) cx = fww + cx; if (cx >= fww) cx = cx - fww; Mxdrag = Mydrag = 0; Fshow = 1; } if (LMclick) { // zoom-in LMclick = 0; zoom = zoom * 1.4142; if (zoom > 1.99) zoom = 2; Fshow = 1; Frecalc = 1; } if (RMclick) { // zoom-out RMclick = 0; zoom = zoom / 1.4142; if (zoom < 0.36) zoom = 0.354; Frecalc = 1; Fshow = 1; } if (Fshow) { if (zd) zdialog_free(zd); zd = 0; show(); } return; } // keyboard function void view360::KB_func(int key) { using namespace view360; static int busy = 0; while (busy) zmainloop(); busy++; if (zd) zdialog_free(zd); zd = 0; if (key == GDK_KEY_Escape || key == GDK_KEY_q) quit(); if (key == GDK_KEY_1) { projection = 1; Frecalc = 1; } if (key == GDK_KEY_2) { projection = 2; Frecalc = 1; } if (key == GDK_KEY_Left) { cx -= 20; if (cx < 0) cx = fww + cx; } if (key == GDK_KEY_Right) { cx += 20; if (cx >= fww) cx = cx - fww; } show(); busy = 0; return; } // quit and free resources void view360::quit() { if (! Fview360) return; freeMouse(); PXB_free(filepxb); PXB_free(viewpxb); if (Fx) zfree(Fx); if (Fy) zfree(Fy); Fview360 = 0; Vmenu_block(0); gdk_window_thaw_updates(gdkwin); Fpaint2(); } /********************************************************************************/ // rename menu function // activate rename dialog, stuff data from current or clicked file // dialog remains active when new file is opened char rename_old[200] = ""; char rename_new[200] = ""; char rename_prev[200] = ""; char *rename_file = 0; void m_rename(GtkWidget *, cchar *menu) { int rename_dialog_event(zdialog *zd, cchar *event); char *pdir, *pfile, *pext; F1_help_topic = "rename"; 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; if (FGWM != 'F' && FGWM != 'G') m_viewmode(0,"F"); // 19.0 /*** ______________________________________ | Rename Image File | | | | Old Name [_______________________] | | New Name [_______________________] | | [previous name] [Add 1] | | | | [x] keep this dialog open | | | | [apply] [cancel] | |______________________________________| ***/ if (! zd_rename) // restart dialog { zd_rename = zdialog_new(E2X("Rename Image File"),Mwin,Bapply,Bcancel,null); zdialog *zd = zd_rename; 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",E2X("Old Name")); zdialog_add_widget(zd,"label","Lnew","vb1",E2X("New Name")); zdialog_add_widget(zd,"label","space","vb1"); zdialog_add_widget(zd,"hbox","hb2","vb2"); zdialog_add_widget(zd,"label","oldname","hb2"); zdialog_add_widget(zd,"label","space","hb2",0,"expand"); zdialog_add_widget(zd,"zentry","newname","vb2",0,"size=20"); zdialog_add_widget(zd,"hbox","hb3","vb2",0,"space=3"); zdialog_add_widget(zd,"button","prev","hb3",E2X("previous name")); zdialog_add_widget(zd,"button","Badd1","hb3",E2X("Add 1"),"space=8"); zdialog_add_widget(zd,"hbox","hb4","dialog",0,"space=3"); zdialog_add_widget(zd,"check","keepopen","hb4",E2X("keep this dialog open"),"space=3"); zdialog_restore_inputs(zd); zdialog_run(zd,rename_dialog_event,"parent"); // run dialog } zdialog *zd = zd_rename; parsefile(rename_file,&pdir,&pfile,&pext); strncpy0(rename_old,pfile,199); strncpy0(rename_new,pfile,199); zdialog_stuff(zd,"oldname",rename_old); // current file name zdialog_stuff(zd,"newname",rename_new); // entered file name (same) return; } // dialog event and completion callback function int rename_dialog_event(zdialog *zd, cchar *event) { char *pp, *pdir, *pfile, *pext, suffix[16]; char *newfile = 0, *nextfile = 0, namever[200]; int nseq, digits, ccp, ccn, ccx, err, Fkeep; STATB statb; if (strmatch(event,"prev")) // previous name >> new name { if (! *rename_prev) return 1; parsefile(rename_prev,&pdir,&pfile,&pext); // get previous rename name pp = strrchr(rename_prev,'.'); if (pp && strlen(pp) == 4 && pp[1] == 'v' // look for file version .vNN && pp[2] >= '0' && pp[2] <= '9' // and remove if found && pp[3] >= '0' && pp[3] <= '9') *pp = 0; zdialog_stuff(zd,"newname",rename_prev); // stuff prev rename name into dialog if (! rename_file) return 1; parsefile(rename_file,&pdir,&pfile,&pext); // curr. file to be renamed pp = strrchr(pfile,'.'); // look for file version .vNN if (pp && strlen(pp) == 4 && pp[1] == 'v' && pp[2] >= '0' && pp[2] <= '9' && pp[3] >= '0' && pp[3] <= '9') { *namever = 0; // prev rename name + curr file version strncatv(namever,200,rename_prev,pp,null); zdialog_stuff(zd,"newname",namever); // stuff into dialog } } if (strmatch(event,"Badd1")) // increment sequence number { zdialog_fetch(zd,"newname",rename_new,188); // get entered filename pp = strrchr(rename_new,'.'); // find .ext if (pp && strlen(pp) < 8) { if (strmatchN(pp-4,".v",2) && // find .vNN.ext (pp[2] >= '0' && pp[2] <= '9') && (pp[3] >= '0' && pp[3] <= '9')) pp -= 4; strncpy0(suffix,pp,16); // save .ext or .vNN.ext } else { pp = rename_new + strlen(rename_new); *suffix = 0; } digits = 0; while (pp[-1] >= '0' && pp[-1] <= '9') { pp--; // look for NNN in filenameNNN digits++; } nseq = 1; if (digits) nseq += atoi(pp); // NNN + 1 if (nseq > 9999) nseq = 1; if (digits < 1) digits = 1; if (nseq > 9 && digits < 2) digits = 2; if (nseq > 99 && digits < 3) digits = 3; if (nseq > 999 && digits < 4) digits = 4; sprintf(pp,"%0*d",digits,nseq); // replace NNN with NNN + 1 strncat(rename_new,suffix,199); // append .ext or .vNN.ext zdialog_stuff(zd,"newname",rename_new); } if (zd->zstat == 0) return 1; // not finished if (zd->zstat != 1) goto KILL; // canceled zd->zstat = 0; // apply - keep dialog active if (! rename_file) return 1; if (checkpend("all")) return 1; parsefile(rename_file,&pdir,&pfile,&pext); // existing /folders/file.ext zdialog_fetch(zd,"newname",rename_new,194); // new file name from user ccp = strlen(pdir); // length of /folders/ 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); // /folders.../newfilename.ext strcpy(newfile+ccp,rename_new); if (ccx) strcpy(newfile+ccp+ccn,pext); err = stat(newfile,&statb); // check if new name exists if (! err) { zmessageACK(Mwin,Bfileexists); zfree(newfile); newfile = 0; return 1; } if (FGWM == 'F') nextfile = gallery(0,"get",curr_file_posn+1); // save next file, before rename else nextfile = 0; err = rename(rename_file,newfile); // rename the file if (err) { zmessageACK(Mwin,"file error: %s",strerror(errno)); goto KILL; } load_filemeta(newfile); // add new file to image index update_image_index(newfile); delete_image_index(rename_file); // delete old file in image index delete_thumbfile(rename_file); // remove thumbnails, disk and cache delete_thumbfile(newfile); // (will auto add when referenced) add_recent_file(newfile); // first in recent files list strncpy0(rename_prev,rename_new,199); // save new name to previous name if (curr_file && strmatch(rename_file,curr_file)) // current file exists no more free_resources(); if (navi::gallerytype == GDIR) { // refresh gallery list gallery(0,"init",0); gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); } zdialog_fetch(zd,"keepopen",Fkeep); if (Fkeep) { // keep dialog open if (FGWM == 'F' && nextfile) { f_open(nextfile); // will call m_rename() gtk_window_present(MWIN); // keep focus on main window } if (nextfile) zfree(nextfile); nextfile = 0; if (newfile) zfree(newfile); newfile = 0; return 1; } else { // close dialog f_open(newfile); // open renamed file goto KILL; } KILL: if (nextfile) zfree(nextfile); nextfile = 0; if (newfile) zfree(newfile); newfile = 0; zdialog_free(zd); // kill dialog zd_rename = 0; return 1; } /********************************************************************************/ // show and change file permissions char *permissions_file = 0; void m_permissions(GtkWidget *, cchar *menu) // 20.0 { int permissions_dialog_event(zdialog *zd, cchar *event); char permissions[100]; cchar *pp; STATB statb; mode_t mode; int ftype; F1_help_topic = "permissions"; if (FGWM != 'F' && FGWM != 'G') return; if (permissions_file) zfree(permissions_file); permissions_file = 0; if (clicked_file) { // use clicked file if present permissions_file = clicked_file; clicked_file = 0; } else if (curr_file) // else current file permissions_file = zstrdup(curr_file); if (permissions_file) { ftype = image_file_type(permissions_file); if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) { // must be image or RAW or VIDEO zfree(permissions_file); permissions_file = 0; } } if (! permissions_file) return; /*** ______________________________________ | File Permissions | | | | File: [____________________________] | file name | | | owner [read+write |v] | | group [read only |v] | combo box entries: read+write, read only, no access | other [no access |v] | | | | [x] keep this dialog open | | | | [apply] [cancel] | |______________________________________| ***/ if (! zd_permissions) // restart dialog { zd_permissions = zdialog_new(E2X("File Permissions"),Mwin,Bapply,Bcancel,null); zdialog *zd = zd_permissions; zdialog_add_widget(zd,"hbox","hbfile","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labfile","hbfile",E2X("File:"),"space=3"); zdialog_add_widget(zd,"label","filename","hbfile",0,"space=3"); zdialog_add_widget(zd,"hbox","hbperm","dialog",0,"space=2"); zdialog_add_widget(zd,"label","space","hbperm",0,"space=3"); zdialog_add_widget(zd,"vbox","vb1","hbperm",0,"homog"); zdialog_add_widget(zd,"label","space","hbperm",0,"space=6"); zdialog_add_widget(zd,"vbox","vb2","hbperm",0,"homog"); zdialog_add_widget(zd,"label","labown","vb1",ownertran,"space=3"); zdialog_add_widget(zd,"combo","permown","vb2",0,"space=3"); zdialog_cb_app(zd,"permown",RWtran); zdialog_cb_app(zd,"permown",ROtran); zdialog_cb_app(zd,"permown",NOtran); zdialog_add_widget(zd,"label","labgrp","vb1",grouptran,"space=3"); zdialog_add_widget(zd,"combo","permgrp","vb2",0,"space=3"); zdialog_cb_app(zd,"permgrp",RWtran); zdialog_cb_app(zd,"permgrp",ROtran); zdialog_cb_app(zd,"permgrp",NOtran); zdialog_add_widget(zd,"label","laboth","vb1",othertran,"space=3"); zdialog_add_widget(zd,"combo","permoth","vb2",0,"space=3"); zdialog_cb_app(zd,"permoth",RWtran); zdialog_cb_app(zd,"permoth",ROtran); zdialog_cb_app(zd,"permoth",NOtran); zdialog_add_widget(zd,"hbox","hbkeep","dialog",0,"space=3"); zdialog_add_widget(zd,"check","keepopen","hbkeep",E2X("keep this dialog open"),"space=3"); zdialog_resize(zd,280,0); zdialog_run(zd,permissions_dialog_event,"parent"); // run dialog } zdialog *zd = zd_permissions; pp = strrchr(permissions_file,'/'); // stuff file name into dialog if (pp) pp++; else pp = permissions_file; zdialog_stuff(zd,"filename",pp); stat(permissions_file,&statb); // stuff curr. permissions mode = statb.st_mode; conv_permissions(mode,permissions); pp = strField(permissions,',',1); if (pp) zdialog_stuff(zd,"permown",pp); pp = strField(permissions,',',2); if (pp) zdialog_stuff(zd,"permgrp",pp); pp = strField(permissions,',',3); if (pp) zdialog_stuff(zd,"permoth",pp); return; } // dialog event and completion callback function int permissions_dialog_event(zdialog *zd, cchar *event) { int Fkeep; mode_t mode; char permown[20], permgrp[20], permoth[20]; char permissions[100]; if (zd->zstat == 0) return 1; // wait for dialog finished if (zd->zstat != 1) goto KILL; // [cancel] or [x] zdialog_fetch(zd,"permown",permown,20); // [apply] zdialog_fetch(zd,"permgrp",permgrp,20); // construct permissions string zdialog_fetch(zd,"permoth",permoth,20); // e.g. "read+write, read only, no access" snprintf(permissions,100,"%s, %s, %s",permown,permgrp,permoth); conv_permissions(permissions,mode); // convert to mode_t chmod(permissions_file,mode); // set file permissions zdialog_fetch(zd,"keepopen",Fkeep); if (! Fkeep) goto KILL; zd->zstat = 0; // keep dialog active gtk_window_present(MWIN); // keep focus on main window return 1; KILL: if (permissions_file) zfree(permissions_file); // free memory permissions_file = 0; zdialog_free(zd); // kill dialog zd_permissions = 0; return 1; } /********************************************************************************/ // 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. // fzoom: 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 fzoom) { PXB *tempxb = 0; int ii, err, fposn, retcode = 0; FTYPE ftype; static int Freent = 0; char *pp, *file; void (*menufunc)(GtkWidget *, cchar *); STATB statb; static char *last_folder = 0; char *curr_folder; if (Freent++) { // stop re-entry printz("f_open() re-entry \n"); goto ret1; } if (CEF && ! CEF->Frestart) goto ret2; // edit function not restartable if (CEF) menufunc = CEF->menufunc; // active edit, save menu function else menufunc = 0; // for possible edit restart if (CEF && CEF->zd) zdialog_send_event(CEF->zd,"cancel"); // cancel if possible if (CEF) goto ret2; // cannot sa_clear(); // clear area if any file = 0; if (filespec) file = zstrdup(filespec); // use passed filespec else { pp = curr_file; // use file open dialog if (! pp && navi::gallerytype == GDIR) pp = navi::galleryname; // 20.0 file = zgetfile(E2X("Open Image File"),MWIN,"file",pp); } if (! file) goto ret3; err = stat(file,&statb); // check file exists if (err) { if (Fack) zmessage_post(Mwin,"20/20",4,"%s \n %s",file,strerror(errno)); zfree(file); goto ret3; } ftype = image_file_type(file); // reject thumbnail if (ftype == THUMB) { if (Fack) zmessageACK(Mwin,"thumbnail file"); goto ret4; } if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) { // must be supported image file type if (Fack) zmessageACK(Mwin,E2X("unknown file type")); // or RAW file or VIDEO file zfree(file); goto ret4; } Ffuncbusy = 1; // may be large or RAW file, slow CPU tempxb = PXB_load(file,1); // load image as PXB pixbuf Ffuncbusy = 0; if (! tempxb) { // PXB_load() messages user zfree(file); goto ret4; } free_resources(Fkeep); // free resources for old image file curr_file = file; // new current file Fpxb = tempxb; // pixmap for current image strcpy(curr_file_type,f_load_type); // set curr_file_xxx from f_load_xxx curr_file_bpc = f_load_bpc; curr_file_size = f_load_size; load_filemeta(curr_file); // load metadata used by fotoxx fposn = file_position(file,Nth); // file position in gallery list if (fposn < 0) { // not there gallery(curr_file,"init",0); // generate new gallery list gallery(0,"sort",-2); // recall sort and position gallery(curr_file,"paint",0); // position at curr. file fposn = file_position(curr_file,0); // position and count in gallery list } curr_file_posn = fposn; // keep track of file position URS_reopen_pos = 0; // not f_open_saved() if (! fzoom) { // 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 (zd_rename) m_rename(0,0); // update active rename dialog if (zd_permissions) m_permissions(0,0); // " permissions dialog if (zd_copymove) m_copy_move(0,0); // " copy/move dialog if (zd_deltrash) m_delete_trash(0,0); // " delete/trash dialog if (zd_metaview) meta_view(0); // " EXIF/IPTC data view window if (zd_editmeta) m_meta_edit_main(0,0); // " edit metadata dialog if (zd_editanymeta) m_meta_edit_any(0,0); // " edit any metadata dialog if (zd_deletemeta) m_meta_delete(0,0); // " delete metadata dialog if (Fcaptions) m_meta_captions(0,0); // show caption/comments at top if (menufunc) menufunc(0,0); // restart edit function for (ii = 0; ii < Ntopfolders; ii++) // check file under top folders if (strstr(file,topfolders[ii])) break; if (ii == Ntopfolders) { curr_folder = zstrdup(file); // not 20.0 pp = strrchr(curr_folder+1,'/'); if (pp) *pp = 0; // if (! last_folder || ! strmatch(curr_folder,last_folder)) // notify, one time this folder // zmessage_post_bold(Mwin,"5/5",1,E2X("outside top folders"),null); // removed 20.0 if (last_folder) zfree(last_folder); last_folder = curr_folder; } if (ftype == VIDEO) // offer to play video file zmessage_post_bold(Mwin,"20/30",3,"VIDEO press P to play"); pp = strrchr(curr_file,'.'); // offer to play animated GIF file 20.0 if (pp && strstr(".gif .GIF",pp)) zmessage_post_bold(Mwin,"20/30",3,"GIF press P to play"); gallery(0,"paint",-1); // paint gallery 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 *_folder *_file_posn *_file_type *_file_bpc *_file_size // Returns: 0 = OK, +N = error. int f_open_saved() { int Nth = -1, fposn; if (! f_save_file) return 1; if (E0pxm) { // edits were made PXB_free(Fpxb); // new window image Fpxb = PXM_PXB_copy(E0pxm); } if (curr_file) zfree(curr_file); curr_file = zstrdup(f_save_file); // new current file fposn = file_position(curr_file,Nth); // file position in gallery list if (fposn < 0) { // not there gallery(curr_file,"init",0); // generate new gallery list gallery(0,"sort",-2); // recall sort and position gallery(curr_file,"paint",-1); // position at current file fposn = file_position(curr_file,0); // position and count in gallery list } curr_file_posn = fposn; // keep track of file position strcpy(curr_file_type,f_save_type); // set curr_file_xxx from f_save_xxx curr_file_bpc = f_save_bpc; curr_file_size = f_save_size; URS_reopen_pos = URS_pos; // track undo/redo stack position zoomx = zoomy = 0; // no zoom center set_mwin_title(); // set win title from curr_file info if (zd_metaview) meta_view(0); // update EXIF/IPTC view window return 0; } /********************************************************************************/ // Function to preload image files hopefully ahead of need. // Usage: f_preload(next) // next = -1 / +1 to read previous / next image file in curr. gallery // preload_file will be non-zero if and when preload_pxb is available. void f_preload(int next) { int fd; char *file; if (! curr_file) return; file = prev_next_file(next,Flastversion); if (! file) return; if (strmatch(file,curr_file)) { zfree(file); return; } fd = open(file,O_RDONLY); if (fd >= 0) { posix_fadvise(fd,0,0,POSIX_FADV_WILLNEED); // preload file in kernel cache close(fd); } zfree(file); return; } /********************************************************************************/ // Open previous or next file in current gallery list, // or jump to previous or next gallery when at the limits. // index is -1 or +1 void x_prev_next(int index) { using namespace navi; char *pp, *newfile = 0, *newgallery = 0; int err, Nth = 0; static int xbusy = 0; static int jumpgallery = 0; static zdialog *zd = 0; if (! curr_file) return; // 19.0 if (FGWM != 'F' && FGWM != 'G') return; cchar *mess1 = E2X("Start of gallery, preceding gallery: %s"); cchar *mess2 = E2X("End of gallery, following gallery: %s"); if (zd && zdialog_valid(zd,"post")) zdialog_free(zd); // clear prior popup message 19.0 zd = 0; if (checkpend("busy block mods")) return; if (xbusy) return; // avoid "function busy" xbusy = 1; newfile = prev_next_file(index,Flastversion); // get prev/next file (last version) if (newfile) { Nth = curr_file_posn + index; // albums can have repeat files err = f_open(newfile,Nth,0,1,1); // open image or RAW file 19.0 if (! err) f_preload(index); // preload next image jumpgallery = 0; // 19.0 goto returnx; } if (jumpgallery) { // jump to prev/next gallery jumpgallery = 0; newgallery = prev_next_gallery(index); if (! newgallery) goto returnx; gallery(newgallery,"init",0); // load gallery gallery(0,"sort",-2); // preserve sort if (Nfiles - Nfolders > 0) { // at least one image file present if (index == +1) Nth = Nfolders; // get first or last image file if (index == -1) Nth = Nfiles - 1; newfile = gallery(0,"get",Nth); err = f_open(newfile,Nth,0,1,0); // open image or RAW file 19.0 if (! err) gallery(newfile,"paint",Nth); if (! err) f_preload(index); // preload next image goto returnx; } } newgallery = prev_next_gallery(index); // check for prev/next gallery if (newgallery) { pp = strrchr(newgallery,'/'); if (pp) pp++; if (index == -1) zd = zmessage_post_bold(Mwin,"5/5",3,mess1,pp,0); // prepare jump to prev/next gallery if (index == +1) zd = zmessage_post_bold(Mwin,"5/5",3,mess2,pp,0); // if user tries prev/next again jumpgallery = 1; } else { if (index == -1) zd = zmessage_post_bold(Mwin,"5/5",3,mess1,Bnone,0); // no prev/next gallery if (index == +1) zd = zmessage_post_bold(Mwin,"5/5",3,mess2,Bnone,0); } returnx: if (newfile) zfree(newfile); if (newgallery) zfree(newgallery); Fpaint2(); zmainloop(); // refresh window (holding arrow key) xbusy = 0; return; } void m_prev(GtkWidget *, cchar *menu) { F1_help_topic = "prev/next"; x_prev_next(-1); // search from curr_file -1 return; // to first file } void m_next(GtkWidget *, cchar *menu) { F1_help_topic = "prev/next"; x_prev_next(+1); // search from curr_file +1 return; // to last file } void m_prev_next(GtkWidget *, cchar *menu) // left/right mouse click >> 20.0 { // previous/next image file F1_help_topic = "prev/next"; if (FGWM != 'F') return; int button = zfuncs::vmenuclickbutton; if (button == 1) m_prev(0,0); else m_next(0,0); return; } void m_zoom_menu(GtkWidget *, cchar *menu) // left/right mouse click 20.0 { F1_help_topic = "[+-]"; // UG button name change 20.04 int button = zfuncs::vmenuclickbutton; if (FGWM == 'G') { // G view, larger/smaller thumbnails if (button == 1) navi::menufuncx(0,"Zoom+"); else navi::menufuncx(0,"Zoom-"); } if (FGWM == 'F') { // F view, zoom image in/out if (button == 1) m_zoom(0,"Zoom+"); else m_zoom(0,"Zoom-"); } return; } /********************************************************************************/ // create a new blank image file with desired background color void m_blank_image(GtkWidget *, cchar *pname) { char color[20], fname[100], fext[8], *filespec; int zstat, err, cc, ww, hh, RGB[3]; zdialog *zd; cchar *pp; char *curr_folder = 0; F1_help_topic = "blank image"; if (checkpend("all")) return; if (navi::gallerytype == GDIR) curr_folder = navi::galleryname; // 20.0 else curr_folder = topfolders[0]; if (! curr_folder) { printz("no top image folders defined \n"); // should not happen return; } Fblock = 1; m_viewmode(0,"F"); // 19.0 /*** __________________________________________________ | Create Blank Image | | | | file name [___________________________] [.jpg|v] | | width [____] height [____] (pixels) | | color [____] | | | | [Done] [Cancel] | |__________________________________________________| ***/ zd = zdialog_new(E2X("Create Blank Image"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labf","hbf",E2X("file name"),"space=3"); zdialog_add_widget(zd,"zentry","file","hbf",0,"space=3|expand"); zdialog_add_widget(zd,"combo","ext","hbf",".jpg"); zdialog_add_widget(zd,"hbox","hbz","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labw","hbz",Bwidth,"space=5"); zdialog_add_widget(zd,"zspin","width","hbz","100|30000|1|1600"); zdialog_add_widget(zd,"label","space","hbz",0,"space=5"); zdialog_add_widget(zd,"label","labh","hbz",Bheight,"space=5"); zdialog_add_widget(zd,"zspin","height","hbz","100|16000|1|1000"); zdialog_add_widget(zd,"label","labp","hbz","(pixels)","space=3"); zdialog_add_widget(zd,"hbox","hbc","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labc","hbc",Bcolor,"space=5"); zdialog_add_widget(zd,"colorbutt","color","hbc","200|200|200"); zdialog_cb_app(zd,"ext",".jpg"); zdialog_cb_app(zd,"ext",".png"); zdialog_cb_app(zd,"ext",".tif"); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_stuff(zd,"file",""); // force input of new name zdialog_set_modal(zd); zdialog_run(zd,0,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion if (zstat != 1) { // cancel zdialog_free(zd); Fblock = 0; return; } zdialog_fetch(zd,"file",fname,92); // get new file name strTrim2(fname); if (*fname <= ' ') { zmessageACK(Mwin,E2X("supply a file name")); zdialog_free(zd); Fblock = 0; m_blank_image(0,0); // retry return; } zdialog_fetch(zd,"ext",fext,8); // add extension strcat(fname,fext); cc = strlen(fname); filespec = zstrdup(curr_folder,cc+4); // make full filespec strcat(filespec,"/"); strcat(filespec,fname); zdialog_fetch(zd,"width",ww); // get image dimensions zdialog_fetch(zd,"height",hh); RGB[0] = RGB[1] = RGB[2] = 255; zdialog_fetch(zd,"color",color,19); // get image color pp = strField(color,"|",1); if (pp) RGB[0] = atoi(pp); pp = strField(color,"|",2); if (pp) RGB[1] = atoi(pp); pp = strField(color,"|",3); if (pp) RGB[2] = atoi(pp); zdialog_free(zd); err = create_blank_file(filespec,ww,hh,RGB); if (! err) f_open(filespec); // make it the current file Fblock = 0; return; } // function to create a new blank image file // file extension must be one of: .jpg .tif .png // RGB args are in the range 0 - 255 // if file exists it is overwritten // returns 0 if OK, 1 if file exists, +N if error int create_blank_file(cchar *file, int ww, int hh, int RGB[3]) { cchar *pp; cchar *fext; int err, cstat; PXB *tempxb; GError *gerror = 0; uint8 *pixel; STATB statb; err = stat(file,&statb); if (! err) { // file already exists zmessageACK(Mwin,Bfileexists); return 1; } pp = strrchr(file,'.'); // get file .ext if (! pp || strlen(pp) > 4) return 1; if (strmatch(pp,".jpg")) fext = "jpeg"; // validate and set pixbuf arg. else if (strmatch(pp,".png")) fext = "png"; else if (strmatch(pp,".tif")) fext = "tif"; else return 2; tempxb = PXB_make(ww,hh,3); // create pixbuf image for (int py = 0; py < hh; py++) // fill with color for (int px = 0; px < ww; px++) { pixel = PXBpix(tempxb,px,py); pixel[0] = RGB[0]; pixel[1] = RGB[1]; pixel[2] = RGB[2]; } cstat = gdk_pixbuf_save(tempxb->pixbuf,file,fext,&gerror,null); if (! cstat) { zmessageACK(Mwin,"error: %s",gerror->message); PXB_free(tempxb); return 3; } PXB_free(tempxb); return 0; } /********************************************************************************/ // blank or restore window void m_blank_window(GtkWidget *, cchar *menu) { static int Fblank = 0; static char *savefile = 0; F1_help_topic = "blank window"; if (checkpend("all")) return; if (FGWM != 'F') m_viewmode(0,"F"); // insure file view mode if (! Fblank) { if (curr_file) { if (savefile) zfree(savefile); savefile = zstrdup(curr_file); } free_resources(); } else if (savefile) { f_open(savefile); zfree(savefile); savefile = 0; } Fblank = 1 - Fblank; return; } /********************************************************************************/ // animate given GIF file if possible void play_gif(cchar *file) // 20.0 { int gif_animations_dialog_event(zdialog *, cchar *event); FILE *fid; char command[300], buff[20] = "", *pp = 0; zdialog *zd; GtkWidget *image; GdkPixbufAnimation *animation; snprintf(command,300,"exiftool -s3 -framecount \"%s\"",file); fid = popen(command,"r"); if (fid) { pp = fgets(buff,20,fid); pclose(fid); } if (! pp || atoi(pp) < 2) return; zd = zdialog_new("GIF animation",Mwin,0); zdialog_add_widget(zd,"image","gif","dialog"); image = zdialog_widget(zd,"gif"); animation = gdk_pixbuf_animation_new_from_file(curr_file,0); gtk_image_set_from_animation(GTK_IMAGE(image),animation); zdialog_run(zd,gif_animations_dialog_event,"desktop"); zdialog_can_focus(zd,0); return; } // dialog event and completion function int gif_animations_dialog_event(zdialog *zd, cchar *event) { if (zd->zstat) zdialog_free(zd); return 1; } /******************************************************************************** right-click popup menu functions *********************************************************************************/ // copy or move an image file to a new location char *copymove_file = 0; void m_copy_move(GtkWidget *, cchar *) // combine copy and move { int copymove_dialog_event(zdialog *zd, cchar *event); cchar *title = E2X("Copy or Move Image File"); char newloc[200]; F1_help_topic = "copy/move"; if (copymove_file) zfree(copymove_file); copymove_file = 0; if (clicked_file) { // use clicked file if present copymove_file = clicked_file; clicked_file = 0; } else if (curr_file) // else use current file copymove_file = zstrdup(curr_file); else return; if (checkpend("all")) return; if (FGWM != 'F' && FGWM != 'G') return; // 19.0 /*** ________________________________________________________ | Copy or Move Image File | | | | Image File: [_______________________________________] | | New Location: [____________________________] [browse] | | New Name: [___________________________________] [prev] | // 19.0 | | | (o) copy (duplicate file) (o) move (remove original) | | [x] keep this dialog open | | | | [apply] [cancel] | |________________________________________________________| ***/ if (! zd_copymove) { zd_copymove = zdialog_new(title,Mwin,Bapply,Bcancel,null); zdialog *zd = zd_copymove; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labf","hb1",E2X("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",0,"space=2"); zdialog_add_widget(zd,"label","labl","hb2",E2X("New Location:"),"space=5"); zdialog_add_widget(zd,"zentry","newloc","hb2",0,"expand"); zdialog_add_widget(zd,"button","browse","hb2",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hb3","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labl","hb3",E2X("New Name:"),"space=5"); zdialog_add_widget(zd,"zentry","newname","hb3",0,"expand"); zdialog_add_widget(zd,"button","prev","hb3",Bprev,"space=5"); zdialog_add_widget(zd,"hbox","hb4","dialog",0,"space=3"); zdialog_add_widget(zd,"radio","copy","hb4",E2X("copy (duplicate file)"),"space=5"); zdialog_add_widget(zd,"radio","move","hb4",E2X("move (remove original)"),"space=5"); zdialog_add_widget(zd,"hbox","hb5","dialog"); zdialog_add_widget(zd,"check","keepopen","hb5",E2X("keep this dialog open"),"space=3"); zdialog_add_ttip(zd,"prev",Bprevtip); zdialog_stuff(zd,"copy",1); zdialog_stuff(zd,"move",0); zdialog_stuff(zd,"keepopen",0); zdialog_restore_inputs(zd); zdialog_fetch(zd,"newloc",newloc,200); if (*newloc != '/') zdialog_stuff(zd,"newloc",topfolders[0]); zdialog_resize(zd,400,0); zdialog_run(zd,copymove_dialog_event,"parent"); // run dialog } zdialog *zd = zd_copymove; char *pp = strrchr(copymove_file,'/'); // initz. newname from copymove_file if (pp) pp++; else pp = copymove_file; zdialog_stuff(zd,"file",pp); zdialog_stuff(zd,"newname",pp); // 19.0 if (copymove_loc && *copymove_loc == '/') zdialog_stuff(zd,"newloc",copymove_loc); // last used return; } // dialog event and completion callback function int copymove_dialog_event(zdialog *zd, cchar *event) { int Fcopy, Fmove; char *newloc, *newfile = 0, *nextfile = 0; char *pp1, *pp2; int cc, err, Nth, Fkeep; STATB statb; static char newname[100], prevname[100]; if (strmatch(event,"browse")) { // browse for new location if (! copymove_loc) { copymove_loc = (char *) zmalloc(XFCC); zdialog_fetch(zd,"newloc",copymove_loc,XFCC); } newloc = zgetfile(E2X("Select folder"),MWIN,"folder",copymove_loc); if (! newloc) goto cleanup; zdialog_stuff(zd,"newloc",newloc); if (copymove_loc) zfree(copymove_loc); copymove_loc = newloc; goto cleanup; } if (strmatch(event,"prev")) // [prev] button pressed 19.0 if (*prevname) zdialog_stuff(zd,"newname",prevname); gtk_window_activate_focus(MWIN); // return focus to main window 19.0 if (zd->zstat == 0) goto cleanup; // wait for dialog finished if (zd->zstat != 1) { // cancel or [x] zdialog_free(zd); // kill dialog zd_copymove = 0; goto cleanup; } zd->zstat = 0; // apply - keep dialog active if (! copymove_file) goto cleanup; if (checkpend("all")) goto cleanup; err = stat(copymove_file,&statb); // source file not found 20.0 if (err) { zmessageACK(Mwin,Bfilenotfound2,copymove_file); goto cleanup; } zdialog_fetch(zd,"copy",Fcopy); // one of these must be set 19.0 zdialog_fetch(zd,"move",Fmove); if (Fcopy + Fmove != 1) goto cleanup; // should not happen 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 folder if (! S_ISDIR(statb.st_mode)) { zmessageACK(Mwin,E2X("new location is not a folder")); goto cleanup; } zdialog_fetch(zd,"newname",newname,100); cc = strlen(copymove_loc) + strlen(newname) + 2; // new file = /new/location/newname.ext newfile = (char *) zmalloc(cc); strcpy(newfile,copymove_loc); strcat(newfile,"/"); strcat(newfile,newname); pp1 = strrchr(copymove_file,'.'); // check if user erased 20.0 pp2 = strrchr(newfile,'.'); // or modified file .ext if (!pp1 || !pp2 || !strmatchcase(pp1,pp2)) { if (pp1) { if (! pp2) pp2 = newfile + strlen(newfile); strcpy(pp2,pp1); } else { zmessageACK(Mwin,E2X("new file extension missing or changed")); goto cleanup; } } err = stat(newfile,&statb); // check if new file exists if (! err) { zmessageACK(Mwin,Bfileexists); goto cleanup; } err = copyFile(copymove_file,newfile); // copy source file to new file if (err) { zmessageACK(Mwin,strerror(err)); zdialog_free(zd); zd_copymove = 0; goto cleanup; } load_filemeta(newfile); // update image index for new file update_image_index(newfile); // (memory metadata now invalid) add_recent_file(newfile); // 20.0 strcpy(prevname,newname); // set new 'previous' name 19.0 if (FGWM == 'F') { // if F-view, get next file Nth = curr_file_posn + 1; nextfile = gallery(0,"get",Nth); } if (Fmove) { // move - delete source file err = remove(copymove_file); if (err) { zmessageACK(Mwin,E2X("delete failed: \n %s"),strerror(errno)); zdialog_free(zd); zd_copymove = 0; goto cleanup; } delete_image_index(copymove_file); // delete in image index delete_thumbfile(copymove_file); // delete thumbnail file and cache if (curr_file && strmatch(curr_file,copymove_file)) // current file gone free_resources(); } if (Fcopy) if (curr_file && strmatch(curr_file,copymove_file)) // current file was copied load_filemeta(curr_file); // restore curr. metadata 19.0 if (FGWM == 'F') { // F-view if (nextfile) f_open(nextfile); // open next file else { // end of gallery if (Fmove) free_resources(); // no curr. file zdialog_free(zd); // kill dialog zd_copymove = 0; } gtk_window_present(MWIN); // keep focus on main window } if (FGWM == 'G' && Fmove) // G-view and file is gone zdialog_stuff(zd,"file",""); if (navi::gallerytype == GDIR) { // refresh gallery gallery(0,"init",0); gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); } zdialog_fetch(zd,"keepopen",Fkeep); if (Fkeep) goto cleanup; // keep dialog open zdialog_free(zd); // kill dialog zd_copymove = 0; goto cleanup; cleanup: if (nextfile) zfree(nextfile); // free memory 20.0 if (newfile) zfree(newfile); return 1; } /********************************************************************************/ // copy selected image file to the desktop void m_copyto_desktop(GtkWidget *, cchar *) { char *file, *pp, newfile[XFCC]; STATB statbuff; int err; F1_help_topic = "copy to desktop"; if (FGWM != 'F' && FGWM != 'G') return; // 19.0 file = clicked_file; if (! file) file = curr_file; if (! file) return; snprintf(newfile,100,"%s/%s",getenv("HOME"),desktopname); // locale specific desktop name pp = strrchr(file,'/'); if (! pp) return; strncatv(newfile,XFCC,pp,0); // new desktop file name err = stat(newfile,&statbuff); // check if file exists if (! err) { int yn = zmessageYN(Mwin,E2X("Overwrite file? \n %s"),newfile); // confirm overwrite if (! yn) goto cleanup; } if (curr_file && strmatch(file,curr_file)) // current file is copied bugfix 19.0 f_save(newfile,curr_file_type,curr_file_bpc,0,1); // preserve edits else { err = copyFile(file,newfile); // copy other file if (err) { zmessageACK(Mwin,strerror(err)); goto cleanup; } load_filemeta(newfile); // update image index bugfix 19.0 update_image_index(newfile); } cleanup: if (clicked_file) zfree(clicked_file); clicked_file = 0; return; } /********************************************************************************/ // copy selected image file to the clipboard void m_copyto_clip(GtkWidget *, cchar *) { int file_copytoclipboard(char *file); char tempfile[200]; int err; F1_help_topic = "copy to clipboard"; if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (clicked_file) { file_copytoclipboard(clicked_file); // get file behind thumbnail zfree(clicked_file); clicked_file = 0; return; } if (! curr_file) return; // no current file snprintf(tempfile,200,"%s/clipboard.jpg",temp_folder); // may be edited and unsaved err = f_save(tempfile,"jpg",8,0,1); if (err) return; file_copytoclipboard(tempfile); remove(tempfile); return; } // copy an image file to the clipboard (as pixbuf) // any prior clipboard image is replaced // supports copy/paste to other apps (not used in fotoxx) // returns 1 if OK, else 0 int file_copytoclipboard(char *file) { GtkClipboard *clipboard; PIXBUF *pixbuf; GError *gerror = 0; clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); if (! clipboard) return 0; gtk_clipboard_clear(clipboard); pixbuf = gdk_pixbuf_new_from_file(file,&gerror); if (! pixbuf) return 0; gtk_clipboard_set_image(clipboard,pixbuf); g_object_unref(pixbuf); return 1; } /********************************************************************************/ // set current file as desktop wallpaper (GNOME only) void m_wallpaper(GtkWidget *, cchar *) // 19.0 { cchar * key = "gsettings set org.gnome.desktop.background"; cchar * id = "picture-uri"; F1_help_topic = "set wallpaper"; if (! curr_file) return; shell_ack("%s %s \"file://%s\" ",key,id,curr_file); return; } /********************************************************************************/ // Delete or Trash an image file. // Use the Linux standard trash function. // If not available, show diagnostic and do nothing. char *delete_trash_file = 0; void m_delete_trash(GtkWidget *, cchar *) // combined delete/trash function { int delete_trash_dialog_event(zdialog *zd, cchar *event); cchar *title = E2X("Delete/Trash Image File"); F1_help_topic = "delete/trash"; if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (delete_trash_file) zfree(delete_trash_file); delete_trash_file = 0; if (clicked_file) { // use clicked file if present delete_trash_file = clicked_file; clicked_file = 0; } else if (curr_file) // else current file delete_trash_file = zstrdup(curr_file); if (checkpend("all")) return; /*** ______________________________________ | Delete/Trash Image File | | | | Image File: [______________________] | | | | [x] keep this dialog open | | | | [delete] [trash] [cancel] | |______________________________________| ***/ if (! zd_deltrash) // start dialog if not already { zd_deltrash = zdialog_new(title,Mwin,Bdelete,Btrash,Bcancel,null); zdialog *zd = zd_deltrash; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labf","hb1",E2X("Image File:"),"space=3"); zdialog_add_widget(zd,"label","file","hb1",0,"space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=3"); zdialog_add_widget(zd,"check","keepopen","hb2",E2X("keep this dialog open"),"space=3"); zdialog_resize(zd,300,0); zdialog_restore_inputs(zd); zdialog_run(zd,delete_trash_dialog_event,"parent"); // run dialog } if (delete_trash_file) { char *pp = strrchr(delete_trash_file,'/'); if (pp) pp++; else pp = delete_trash_file; zdialog_stuff(zd_deltrash,"file",pp); } else zdialog_stuff(zd_deltrash,"file",""); return; } // dialog event and completion callback function int delete_trash_dialog_event(zdialog *zd, cchar *event) { int err = 0, yn = 0; int Nth, gstat, ftype, Fkeep; char *file; GError *gerror = 0; GFile *gfile = 0; STATB statb; cchar *trashmess = E2X("GTK g_file_trash() function failed"); if (zd->zstat == 0) return 1; // wait for dialog finished if (zd->zstat == 1) goto PREP; // [delete] button if (zd->zstat == 2) goto PREP; // [trash] button goto KILL; // [cancel] or [x] PREP: if (! delete_trash_file) goto KILL; // no file to delete or trash err = stat(delete_trash_file,&statb); // check file exists if (err) { zmessageACK(Mwin,strerror(errno)); goto KILL; } ftype = image_file_type(delete_trash_file); if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) { // must be image or RAW or VIDEO zmessageACK(Mwin,E2X("not a known image file")); goto KILL; } if (! (statb.st_mode & S_IWUSR)) { // check permission if (zd->zstat == 1) yn = zmessageYN(Mwin,E2X("Delete read-only file?")); if (zd->zstat == 2) yn = zmessageYN(Mwin,E2X("Trash read-only file?")); if (! yn) goto NEXT; // keep file statb.st_mode |= S_IWUSR; // make writable if needed err = chmod(delete_trash_file,statb.st_mode); if (err) { zmessageACK(Mwin,strerror(errno)); goto KILL; } } if (checkpend("all")) return 1; // check for conflicts if (zd->zstat == 1) goto DELETE; // [delete] button if (zd->zstat == 2) goto TRASH; // [trash] button DELETE: err = remove(delete_trash_file); // delete file if (! err) goto BOTH; zmessageACK(Mwin,E2X("delete failed: \n %s"),strerror(errno)); goto KILL; TRASH: gfile = g_file_new_for_path(delete_trash_file); gstat = g_file_trash(gfile,0,&gerror); // move file to trash g_object_unref(gfile); if (gstat) goto BOTH; zmessageACK(Mwin,trashmess); if (gerror) printz("%s\n",gerror->message); goto KILL; BOTH: delete_image_index(delete_trash_file); // delete file in image index delete_thumbfile(delete_trash_file); // delete thumbnail file and cache Nth = file_position(delete_trash_file,0); // find in gallery list if (Nth >= 0) gallery(0,"delete",Nth); // delete from gallery list if (curr_file && strmatch(delete_trash_file,curr_file)) { // current file was removed last_file_posn = curr_file_posn; // remember for prev/next use free_resources(); } if (FGWM == 'F') { // F window file = gallery(0,"get",curr_file_posn); // new current file = next if (file) { f_open(file); zfree(file); } else m_viewmode(0,"G"); // no next, show gallery 20.0 zdialog_fetch(zd,"keepopen",Fkeep); if (! Fkeep) goto KILL; zd->zstat = 0; // keep dialog active gtk_window_present(MWIN); // keep focus on main window return 1; } NEXT: if (FGWM == 'G') { // gallery view gallery(0,"init",0); // refresh for removed file gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // paint zdialog_stuff(zd,"file",""); // erase file in dialog } if (FGWM == 'F') m_next(0,0); // file view mode, open next image zdialog_fetch(zd,"keepopen",Fkeep); if (Fkeep) { zd->zstat = 0; // keep dialog active return 1; } KILL: if (delete_trash_file) zfree(delete_trash_file); // free memory delete_trash_file = 0; zdialog_free(zd); // kill dialog zd_deltrash = 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"; if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (clicked_file) Ppxb = PXB_load(clicked_file,1); // clicked thumbnail else if (E0pxm) Ppxb = PXM_PXB_copy(E0pxm); // current edited file else if (curr_file) Ppxb = PXB_load(curr_file,1); // current file clicked_file = 0; if (! Ppxb) return; print_addgrid(Ppxb); // add grid lines if wanted printfile = zstrdup(temp_folder,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 *) { if (FGWM != 'F' && FGWM != 'G') return; // 19.0 print_calibrated(); return; } /********************************************************************************/ // normal quit menu function // does not return void m_quit(GtkWidget *, cchar *) { zdialog *zd; int busy; if (checkpend("mods")) return; // allow user bailout if (zfuncs::zdialog_busy) { for (int ii = 0; ii < zfuncs::zdialog_count; ii++) { zd = zfuncs::zdialog_list[ii]; if (strmatch(zd->title,"userguide")) continue; // omit this dialog 20.0 zdialog_show(zd,1); // un-hide minimized dialog 20.0 int yn = zmessageYN(Mwin,E2X("Kill active dialog? %s"),zd->title); // allow user bailout if (! yn) return; zdialog_free(zd); } } 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; zmainsleep(0.1); } if (busy) printz("busy function killed \n"); quitxx(); // does not return } // used also for main window destroy event // does not return void quitxx() { Fshutdown++; gtk_window_get_position(MWIN,&mwgeom[0],&mwgeom[1]); // get last window position gtk_window_get_size(MWIN,&mwgeom[2],&mwgeom[3]); // and size for next session zdialog_inputs("save"); // save dialog inputs zdialog_geometry("save"); // save dialogs position/size gallery_memory("save"); // save recent gallery positions free_resources(); // free memory shell_quiet("rm -R -f %s",temp_folder); // delete temp files save_params(); // save state for next session fflush(null); // flush stdout, stderr /// gtk_main_quit(); // exit GTK zexit("Fotoxx normal exit"); } /********************************************************************************/ // help menu function void m_help(GtkWidget *, cchar *menu) { char *pp; if (strmatch(menu,E2X("User Guide"))) showz_docfile(Mwin,"userguide",0); // 20.0 if (strmatch(menu,E2X("Recent Changes"))) showz_docfile(Mwin,"userguide","recent changes"); // 20.0 if (strmatch(menu,E2X("Edit Functions Overview"))) // 20.0 showz_docfile(Mwin,"userguide","edit overview"); if (strmatch(menu,"Validate User Guide")) // developer function 20.0 showz_docfile(Mwin,"userguide","validate"); if (strmatch(menu,E2X("Change Log"))) showz_textfile("doc","changelog",Mwin); if (strmatch(menu,E2X("Log File"))) showz_logfile(Mwin); // 20.0 if (strmatch(menu,"Man Page")) showz_textfile("doc","fotoxx.man",Mwin); if (strmatch(menu,E2X("Command Params"))) showz_docfile(Mwin,"userguide","command parameters"); // 20.0 if (strmatch(menu,E2X("Translations"))) showz_docfile(Mwin,"userguide","translations"); // 20.0 if (strmatch(menu,E2X("Home Page"))) showz_html(Fhomepage); if (strmatch(menu,E2X("License"))) // 19.0 showz_textfile("doc","license",Mwin); if (strmatch(menu,E2X("Privacy"))) // 19.1 showz_html("https://gitlab.com/fotoxx/fotoxx/wikis/privacy"); if (strmatch(menu,E2X("About"))) { if (zfuncs::appimagexe) pp = zfuncs::appimagexe; else pp = zfuncs::progexe; zmessageACK(Mwin," %s %s \n %s \n\n"" %s %s \n %s \n\n"" %s \n", Frelease, zfuncs::build_date_time, pp, Fhomepage, Fcontact, Fgitlab, Ftranslators); } if (strmatch(menu,E2X("Help"))) // menu button showz_docfile(Mwin,"userguide",F1_help_topic); // show topic if there, or page 1 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 (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (! curr_file) { if (zd_filesave) zdialog_destroy(zd_filesave); zd_filesave = 0; return; } if (strmatch(curr_file_type,"other")) // if unsupported type, use jpg strcpy(curr_file_type,"jpg"); /*** _______________________________________________ | Save Image File | | | | filename.jpg | | | | [new version] save as new file version | | [new file ...] save as new file name or type | | [replace file] replace file (OVERWRITE) | | | | [cancel] | |_______________________________________________| ***/ if (! zd_filesave) { zd = zdialog_new(E2X("Save Image File"),Mwin,Bcancel,null); zd_filesave = 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=5|homog"); zdialog_add_widget(zd,"button","newvers","vb1",E2X("new version")); zdialog_add_widget(zd,"button","newfile","vb1",E2X("new file ...")); zdialog_add_widget(zd,"button","replace","vb1",E2X("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",E2X("save as new file version")); zdialog_add_widget(zd,"label","labfile","hb2",E2X("save as new file name or type")); zdialog_add_widget(zd,"icon","warning","hb3","warning.png","size=30"); zdialog_add_widget(zd,"label","labrepl","hb3",E2X("replace old file (OVERWRITE)"),"space=3"); } zd = zd_filesave; 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 *RP = 0, *newfilename = 0; int err; if (zd->zstat) goto retx; // cancel if (checkpend("busy block")) goto retx; if (! zstrstr("newvers newfile replace",event)) return 1; // ignore other events if (strmatch(event,"newvers")) // new version { RP = f_realpath(curr_file); // use real path 20.0 if (! RP) { zmessageACK(Mwin,Bfilenotfound2,curr_file); goto retx; } newfilename = file_new_version(RP); // get next avail. file version name if (! newfilename) goto retx; if (strmatch(curr_file_type,"RAW")) // if RAW file, save as 16-bit TIFF 20.0 err = f_save(newfilename,"tif",16,0,1); else if (strmatch(curr_file_type,"heic")) // if .heic file, save as JPEG 20.0 err = f_save(newfilename,"jpg",8,0,1); else if (strmatch(curr_file_type,"jp2")) // if .jp2 file, save as PNG 20.0 err = f_save(newfilename,"png",8,0,1); else err = f_save(newfilename,curr_file_type,curr_file_bpc,0,1); // save with same file type if (! err) f_open_saved(); // open saved file with edit hist goto retx; } if (strmatch(event,"replace")) // replace { if (strmatch(curr_file_type,"RAW")) { zmessageACK(Mwin,E2X("cannot replace RAW file")); goto retx; } if (strmatch(curr_file_type,"heic")) { // 20.0 zmessageACK(Mwin,E2X("cannot replace HEIC file")); goto retx; } if (strmatch(curr_file_type,"jp2")) { // 20.0 zmessageACK(Mwin,E2X("cannot replace JP2 file")); goto retx; } RP = f_realpath(curr_file); // use real path 20.0 if (! RP) { zmessageACK(Mwin,Bfilenotfound2,curr_file); goto retx; } err = f_save(RP,curr_file_type,curr_file_bpc,0,1); // save file with curr. edits applied if (err) goto retx; curr_file_size = f_save_size; } if (strmatch(event,"newfile")) err = f_save_as(); // save-as file chooser dialog retx: if (RP) zfree(RP); if (newfilename) zfree(newfilename); zdialog_free(zd); // kill dialog 20.0 zd_filesave = 0; return 1; } // menu entry for KB shortcut Save File (replace) void m_file_save_replace(GtkWidget *, cchar *menu) { int err; char *RP; RP = f_realpath(curr_file); // use real path 20.0 if (! RP) { zmessageACK(Mwin,Bfilenotfound2,curr_file); return; } err = f_save(RP,curr_file_type,curr_file_bpc,0,1); // save file if (! err) f_open_saved(); // open saved file with edit hist zfree(RP); return; } // menu entry for KB shortcut Save File Version void m_file_save_version(GtkWidget *, cchar *menu) { char *newfilename = 0, *RP = 0; char newfiletype[8]; int err; RP = f_realpath(curr_file); // use real path 20.0 if (! RP) { zmessageACK(Mwin,Bfilenotfound2,curr_file); return; } strncpy0(newfiletype,curr_file_type,6); newfilename = file_new_version(RP); // get next avail. file version name if (! newfilename) goto retx; err = f_save(newfilename,newfiletype,curr_file_bpc,0,1); // save file if (! err) f_open_saved(); // open saved file with edit hist retx: if (RP) zfree(RP); if (newfilename) zfree(newfilename); return; } /********************************************************************************/ // get the root name of an image file, without version and extension // /.../filename.V03.png --> /.../filename // returned root name has extra length to add .vNN.exxxxt // returned root name is subject for zfree() char * file_rootname(cchar *file) { char *rootname, *pp1, *pp2; rootname = zstrdup(file,16); pp1 = strrchr(rootname,'/'); // find file .ext if (! pp1) pp1 = rootname; pp2 = strrchr(pp1,'.'); // pp2 --> .ext or ending null if (! pp2) pp2 = pp1 + strlen(pp1); if (pp2[-4] == '.' && pp2[-3] == 'v' && // look for .vNN.ext pp2[-2] >= '0' && pp2[-2] <= '9' && pp2[-1] >= '0' && pp2[-1] <= '9') pp2 -= 4; // pp2 --> .ext or .vNN.ext *pp2 = 0; // pp2 --> new ending null return rootname; } /********************************************************************************/ // return base name for given file = original file name /.../filename.ext // returns null if base file does not exist. // returned base name has extra length to add .vNN.exxxxt // returned base name is subject for zfree() char * file_basename(cchar *file) // 19.0 { char *rootname, **flist, *pp = 0; int ii, cc, nf; flist = file_all_versions(file,nf); // get base file and all versions if (! nf) return 0; rootname = file_rootname(file); cc = strlen(rootname); zfree(rootname); for (ii = 0; ii < nf; ii++) { pp = flist[ii]; if (strmatchN(pp+cc,".v",2)) continue; if (pp[cc] == '.') break; } if (ii < nf) pp = zstrdup(pp,16); else pp = 0; for (ii = 0; ii < nf; ii++) zfree(flist[ii]); zfree(flist); return pp; } /********************************************************************************/ // Get all versions of a given file name in sequence, including base file. // Returned list of file names is subject for zfree() // (each flist[*] member and flist itself) // returns null if nothing found char ** file_all_versions(cchar *file, int &NF) // 19.0 { char **flist1, **flist2, *rootname; int ii, jj, err, cc; rootname = file_rootname(file); // /.../fname.vNN.ext --> /.../fname if (! rootname) return 0; cc = strlen(rootname); strcpy(rootname + cc,".*"); err = zfind(rootname,flist1,NF); // find /.../fname.* zfree(rootname); if (err || NF < 1) return 0; flist2 = (char **) zmalloc(NF * sizeof(char *)); // purge non-image type files 20.0 for (ii = jj = 0; ii < NF; ii++) if (image_file_type(flist1[ii]) <= VIDEO) flist2[jj++] = flist1[ii]; zfree(flist1); NF = jj; if (NF == 0) { zfree(flist2); return 0; } if (NF == 1) return flist2; if (NF > 99) { // screen out problem file zmessageACK(Mwin,E2X("file: %s \n exceed 99 versions"),file); zfree(flist2); return 0; } HeapSort(flist2,NF); // sort in base, version order return flist2; } /********************************************************************************/ // Get next available version /.../filename.vNN.ext for given file. // Returns "/.../filename.v01.ext" if no versions are found. // Returns null if bad file name or 99 versions already exist. // Returned file name is subject for zfree(). char * file_new_version(cchar *file) // 19.0 { char *retname, *pp, pext[8]; char **flist = 0; int ii, cc, NF = 0, vers; pp = (char *) strrchr(file,'/'); // find .ext for file if (! pp) return 0; pp = strrchr(pp,'.'); if (pp) strncpy0(pext,pp,8); else strcpy(pext,".jpg"); flist = file_all_versions(file,NF); // get base file and all versions if (! NF) { // nothing exists retname = file_rootname(file); // get /.../filename retname = zstrdup(retname,12); cc = strlen(retname); strcpy(retname+cc,".v01"); // return /.../filename.v01.ext strcpy(retname+cc+4,pext); return retname; } retname = zstrdup(flist[NF-1],12); // get last version found for (ii = 0; ii < NF; ii++) // free memory zfree(flist[ii]); zfree(flist); pp = strrchr(retname,'/'); // find .ext if (! pp) return 0; pp = strrchr(pp,'.'); if (! pp) { // none strcat(retname,"v.01.jpg"); // return /.../filename.v01.jpg return retname; } if (strmatchN(pp-4,".v",2)) { // find /.../filename.vNN.ext if (pp[-2] >= '0' && pp[-2] <= '9' && // | pp[-1] >= '0' && pp[-1] <= '9') // pp { vers = 10 * (pp[-2] - '0') + pp[-1] - '0'; if (vers >= 99) { zmessageACK(Mwin,E2X("file: %s \n exceed 99 versions"),file); return 0; } vers++; // add 1 to .vNN, leave same .ext pp[-2] = vers/10 + '0'; pp[-1] = vers - 10 * (vers/10) + '0'; return retname; // return /.../filename.vNN.ext } } strncpy0(pext,pp,8); // keep .ext strcpy(pp,".v01"); // return /.../filename.v01.ext strcpy(pp+4,pext); return retname; } /********************************************************************************/ // Get the newest version /.../filename.vNN.ext for the given file. // Returns unversioned file /.../filename.ext if found by itself. // Returns null if bad file name. // Returned file name is subject for zfree(). char * file_newest_version(cchar *file) // 19.0 { char *retname; char **flist; int ii, NF; flist = file_all_versions(file,NF); // get base file and all versions if (! NF) return 0; retname = zstrdup(flist[NF-1],12); // get last version found for (ii = 0; ii < NF; ii++) // free memory zfree(flist[ii]); zfree(flist); return retname; } /********************************************************************************/ // Get the prior version /.../filename.vNN.ext for the given file. // Returns unversioned file /.../filename.ext if no prior .vNN found. // Returns null if bad file name or no prior version. char * file_prior_version(cchar *file) // 19.0 { char *retname; char **flist; int ii, NF; flist = file_all_versions(file,NF); // get base file and all versions if (! NF) return 0; for (ii = 0; ii < NF; ii++) // find input file in list if (strmatch(file,flist[ii])) break; if (ii == 0) return 0; // input is base file (no prior) if (ii == NF) return 0; // should not happen retname = zstrdup(flist[ii-1]); // prior file in list for (ii = 0; ii < NF; ii++) // free memory zfree(flist[ii]); zfree(flist); return retname; } /********************************************************************************/ // save current image to specified disk file (same or new). // update image index file and thumbnail file. // set f_save_type, f_save_bpc, f_save_size // 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 qual, int Fack) { cchar *exifkey[5], *exifdata[5]; char *ppv[1], *tempfile, *pext, *outfile2; char edithist[exif_maxcc]; // edit history trail int nkeys, err, cc1, cc2, ii, ii0; int Fmod, Fcopy, Ftransp, Fnewfile; int px, py; void (*menufunc)(GtkWidget *, cchar *); STATB statb; mode_t mode; cchar *warnalpha = E2X("Transparency map will be lost.\n" "save to PNG file to retain."); if (! curr_file) return 1; err = stat(curr_file,&statb); // get current file permissions 20.0 if (err) return 1; // (default for replace or new version) mode = statb.st_mode; if (strmatch(outype,"RAW")) { // disallow saving as RAW type zmessageACK(Mwin,E2X("cannot save as RAW type")); return 1; } Fmod = 0; menufunc = 0; if (! qual) qual = jpeg_def_quality; // use default jpeg compression 19.0 if (CEF) { // edit function active if (CEF->Fmods) Fmod = 1; // active edits pending menufunc = CEF->menufunc; // save menu function for restart if (CEF->zd) zdialog_send_event(CEF->zd,"done"); // tell it to finish if (CEF) return 1; // failed (HDR etc.) } if (URS_pos > 0 && URS_saved[URS_pos] == 0) Fmod = 1; // completed edits not saved if (strmatch(outfile,curr_file)) Fnewfile = 0; // replace current file else Fnewfile = 1; // new file (name/.ext/location) if (! Fnewfile) { err = access(outfile,W_OK); // test file can be written by me 19.1 if (err) { zmessageACK(Mwin,"%s: %s",Bnowriteperm,outfile); return 1; } } outfile2 = f_realpath(outfile); // resolve symlinks in file path 20.0 if (! outfile2) { zmessageACK(Mwin,Bfilenotfound2,outfile); return 2; } if (Fnewfile) { // if new file, force .ext pext = strrchr(outfile2,'/'); // to one of: .jpg .png .tif if (pext) pext = strrchr(pext,'.'); if (! pext) pext = outfile2 + strlen(outfile2); pext[0] = '.'; strcpy(pext+1,outype); } if (! Fnewfile && ! Fmod) { // no edit changes to file Fcopy = 1; // direct copy to output file? if (E0pxm) Fcopy = 0; // no, non-edit change (upright) if (curr_file_bpc != outbpc) Fcopy = 0; // no, BPC change if (qual < jpeg_def_quality) Fcopy = 0; // no, higher compression wanted if (Fcopy) { err = copyFile(curr_file,outfile2); // copy unchanged file to output if (! err) goto updateindex; zmessageACK(Mwin,strerror(err)); return 1; } } Ffuncbusy = 1; // may be large file, slow CPU if (! E0pxm) { E0pxm = PXM_load(curr_file,1); // no prior edits, load image file if (! E0pxm) { zfree(outfile2); Ffuncbusy = 0; // PXM_load() diagnoses error return 1; } } tempfile = zstrdup(outfile2,20); // temp file in same folder strcat(tempfile,"-temp."); strcat(tempfile,outype); if (E0pxm->nc == 4 && ! strstr("tif png",outype)) // alpha channel will be lost 19.0 { Ftransp = 0; for (py = 2; py < E0pxm->hh-2; py += 2) // ignore extreme edges for (px = 2; px < E0pxm->ww-2; px += 2) if (PXMpix(E0pxm,px,py)[3] < 250) { // 20.0 Ftransp = 1; goto breakout; } breakout: // warn transparency lost if (Ftransp) { ii = zdialog_choose(Mwin,"mouse",warnalpha,E2X("save anyway"),Bcancel,null); if (ii != 1) { remove(tempfile); // user canceled zfree(tempfile); zfree(outfile2); Ffuncbusy = 0; return 0; } } } if (! strstr("tif png",outype)) outbpc = 8; // force 8 if 16 not supported err = PXM_save(E0pxm,tempfile,outbpc,qual,Fack); // 19.0 if (err) { remove(tempfile); // failure, clean up zfree(tempfile); zfree(outfile2); Ffuncbusy = 0; return 1; } exifkey[0] = exif_editlog_key; exif_get(curr_file,&exifkey[0],ppv,1); // get prior edit history in EXIF if (ppv[0]) { strncpy0(edithist,ppv[0],exif_maxcc-2); zfree(ppv[0]); cc1 = strlen(edithist); // edits made before this file was opened } else cc1 = 0; // none if ((CEF && CEF->Fmods) || URS_pos > 0) // active or completed unsaved edits { strcpy(edithist+cc1," "); // update edit history strcpy(edithist+cc1+1,"Fotoxx:"); // append " Fotoxx:" cc1 += 8; if (URS_reopen_pos > 0) ii0 = URS_reopen_pos + 1; // if last file saved was kept open, else ii0 = 1; // these edits are already in history for (ii = ii0; ii <= URS_pos; ii++) // append list of edits from undo/redo stack { // (omit index 0 = initial image) cc2 = strlen(URS_funcs[ii]); strcpy(edithist+cc1,URS_funcs[ii]); strcpy(edithist+cc1+cc2,"|"); cc1 += cc2 + 1; } if (CEF && CEF->Fmods) { // save during active edit function cc2 = strlen(CEF->funcname); // add curr. edit to history list strcpy(edithist+cc1,CEF->funcname); strcpy(edithist+cc1+cc2,"|"); cc1 += cc2 + 1; } } exifkey[0] = exif_orientation_key; // remove EXIF orientation exifdata[0] = ""; // (assume saved file is fixed) nkeys = 1; if (cc1) { // prior and/or curr. edit history exifkey[1] = exif_editlog_key; // will be added to EXIF exifdata[1] = edithist; nkeys = 2; } err = exif_copy(curr_file,tempfile,exifkey,exifdata,nkeys); // copy all EXIF/IPTC data to if (err && Fack) // temp file with above revisions zmessageACK(Mwin,E2X("Unable to copy EXIF/IPTC data")); if (err && ! Fack) printz("%s \n",E2X("Unable to copy EXIF/IPTC data")); // 20.0 err = rename(tempfile,outfile2); // rename temp file to output file if (err) { if (Fack) zmessageACK(Mwin,strerror(err)); remove(tempfile); // delete temp file zfree(tempfile); zfree(outfile2); Ffuncbusy = 0; return 2; // could not save } zfree(tempfile); // free memory for (ii = 0; ii <= URS_pos; ii++) // mark all prior edits as saved URS_saved[ii] = 1; updateindex: update_thumbfile(outfile2); // refresh thumbnail file load_filemeta(outfile2); // load output file metadata if (Fmod) { set_meta_wwhh(E0pxm->ww,E0pxm->hh); // set w/h in metadata add_tag_fotoxx(outfile2); // edited file has "fotoxx" tag save_filemeta(outfile2); // includes update_image_index() } else update_image_index(outfile2); // add to image index 20.0 if (samefolder(outfile2,navi::galleryname)) { // if saving into current gallery gallery(curr_file,"init",0); // update curr. gallery list gallery(0,"sort",-2); // recall sort and position set_mwin_title(); // update posn, count in title } add_recent_file(outfile2); // first in recent files list if (f_save_file) zfree(f_save_file); f_save_file = outfile2; chmod(outfile2,mode); // set permissions from input file 20.0 stat(outfile2,&statb); // set output file size, type, bit depth f_save_size = statb.st_size; strcpy(f_save_type,outype); f_save_bpc = outbpc; 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; char *save_folder = 0; cchar *type; char *newfile, *fname; char *outfile = 0, *outfile2 = 0, *pp, *pext; char permissions[100]; int ii, zstat, err; int bpc, mkcurr = 0; int jpgqual = jpeg_def_quality; STATB statbuf; mode_t mode; /*** _____________________________________________________ | Save as New File Name or Type | | ________________________________________________ | | | | | | | file chooser dialog | | | | | | | | | | | | | | | | | | | | | | | |________________________________________________| | | | | (o) tif (o) png (o) jpg [90] jpg quality | // sep. file type and color depth | color depth: (o) 8-bit (o) 16-bit | | [x] make current (new file becomes current file) | | permissions: [__________________________] [change] | // 20.0 | | | [save] [cancel] | |_____________________________________________________| ***/ zd = zdialog_new(E2X("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","hbft","tif","space=4"); zdialog_add_widget(zd,"radio","png","hbft","png","space=4"); zdialog_add_widget(zd,"radio","jpg","hbft","jpg","space=2"); zdialog_add_widget(zd,"zspin","jpgqual","hbft","10|100|1|90","size=3"); zdialog_add_widget(zd,"label","labqual","hbft",E2X("jpg quality"),"space=6"); zdialog_add_widget(zd,"hbox","hbcd","hbft"); zdialog_add_widget(zd,"label","space","hbcd","","space=8"); zdialog_add_widget(zd,"label","labdepth","hbcd",E2X("color depth:"),"space=3"); zdialog_add_widget(zd,"radio","8-bit","hbcd","8-bit","space=4"); zdialog_add_widget(zd,"radio","16-bit","hbcd","16-bit","space=4"); zdialog_add_widget(zd,"hbox","hbmc","dialog"); zdialog_add_widget(zd,"check","mkcurr","hbmc",0,"space=8"); zdialog_add_widget(zd,"label","labmc","hbmc",E2X("make current")); zdialog_add_widget(zd,"label","labmc2","hbmc",E2X("(new file becomes current file)"),"space=5"); zdialog_add_widget(zd,"hbox","hbperm","dialog"); // 20.0 zdialog_add_widget(zd,"label","labperm","hbperm",E2X("permissions:"),"space=8"); zdialog_add_widget(zd,"label","permissions","hbperm"); zdialog_add_widget(zd,"button","change","hbperm",Bchange,"space=8"); zdialog_labelfont(zd,"labmc","sans bold",E2X("make current")); save_folder = f_realpath(curr_file); // use real path 20.0 if (! save_folder) save_folder = zstrdup(curr_file); pp = strrchr(save_folder,'/'); if (pp) *pp = 0; gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(saveas_fchooser),save_folder); zfree(save_folder); newfile = file_new_version(curr_file); // suggest next version if (! newfile) newfile = zstrdup(curr_file); fname = strrchr(newfile,'/') + 1; gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(saveas_fchooser),fname); zfree(newfile); zdialog_stuff(zd,"tif",0); // no file type selected zdialog_stuff(zd,"png",0); zdialog_stuff(zd,"jpg",0); zdialog_stuff(zd,"8-bit",0); zdialog_stuff(zd,"16-bit",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, zdialog_stuff(zd,"tif",1); // set corresp. type and bit depth if (curr_file_bpc == 16) zdialog_stuff(zd,"16-bit",1); else zdialog_stuff(zd,"8-bit",1); } else if (strmatch(curr_file_type,"png")) { // same for png zdialog_stuff(zd,"png",1); if (curr_file_bpc == 16) zdialog_stuff(zd,"16-bit",1); else zdialog_stuff(zd,"8-bit",1); } else { // same for jpg zdialog_stuff(zd,"jpg",1); zdialog_stuff(zd,"8-bit",1); } zdialog_stuff(zd,"mkcurr",0); // deselect "make current" err = stat(curr_file,&statbuf); // current file permissions 20.0 conv_permissions(statbuf.st_mode,permissions); // >> default new file permissions zdialog_stuff(zd,"permissions",permissions); zdialog_resize(zd,700,500); zdialog_run(zd,f_save_as_dialog_event,"parent"); zdialog_wait: zstat = zdialog_wait(zd); if (zstat != 1) { // user cancel zdialog_free(zd); return 1; } outfile2 = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(saveas_fchooser)); if (! outfile2) { zd->zstat = 0; goto zdialog_wait; } if (outfile) zfree(outfile); outfile = zstrdup(outfile2,12); // add space for possible .vNN and .ext g_free(outfile2); type = ""; bpc = -1; zdialog_fetch(zd,"tif",ii); // get selected file type if (ii) type = "tif"; zdialog_fetch(zd,"png",ii); if (ii) type = "png"; zdialog_fetch(zd,"jpg",ii); if (ii) type = "jpg"; zdialog_fetch(zd,"8-bit",ii); // get selected color depth if (ii) bpc = 8; zdialog_fetch(zd,"16-bit",ii); if (ii) bpc = 16; if (! strstr("tif png jpg",type)) goto zdialog_wait; // should not happen if (bpc != 8 && bpc != 16) goto zdialog_wait; if (strmatch(type,"jpg") && bpc != 8) goto zdialog_wait; zdialog_fetch(zd,"jpgqual",jpgqual); // jpeg compression level 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,&statbuf); // check if file exists if (! err) { int yn = zmessageYN(Mwin,E2X("Overwrite file? \n %s"),outfile); // confirm overwrite if (! yn) { zd->zstat = 0; goto zdialog_wait; } } zdialog_fetch(zd,"permissions",permissions,100); // get permissions from dialog 20.0 conv_permissions(permissions,mode); zdialog_free(zd); // zdialog_free(zd); err = f_save(outfile,type,bpc,jpgqual,1); // save the file if (err) { zfree(outfile); return 1; } chmod(outfile,mode); // set permissions 20.0 if (samefolder(outfile,navi::galleryname)) { // if saving into current gallery gallery(outfile,"init",0); // refresh gallery list gallery(0,"sort",-2); // recall sort and position curr_file_posn = file_position(curr_file,curr_file_posn); // update curr. file position set_mwin_title(); // update window title (file count) } if (mkcurr) f_open_saved(); // open saved file with edit hist zfree(outfile); return 0; } // dialog event and completion function int f_save_as_dialog_event(zdialog *zd, cchar *event) { int ii, err; char *filespec, *filename, *pp; char oldperms[100], newperms[100]; char ext[4]; 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"; } if (zstrstr("tif png jpg",event)) { // file type selection zdialog_stuff(zd,"tif",0); // turn off all types zdialog_stuff(zd,"png",0); zdialog_stuff(zd,"jpg",0); zdialog_stuff(zd,event,1); // turn on selected type } if (zstrstr("8-bit 16-bit",event)) { // color depth selection zdialog_stuff(zd,"8-bit",0); // turn off all depths zdialog_stuff(zd,"16-bit",0); zdialog_stuff(zd,event,1); // turn on selected depth } zdialog_fetch(zd,"jpg",ii); // if jpg, force 8-bit if (ii) { zdialog_stuff(zd,"16-bit",0); zdialog_stuff(zd,"8-bit",1); } zdialog_fetch(zd,"tif",ii); // get chosen file type "tif" ... if (ii) strcpy(ext,"tif"); // set corresp. extension ".tif" ... zdialog_fetch(zd,"png",ii); if (ii) strcpy(ext,"png"); zdialog_fetch(zd,"jpg",ii); if (ii) strcpy(ext,"jpg"); 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); if (strmatch(event,"change")) { // change saved file permissions 20.0 zdialog_fetch(zd,"permissions",oldperms,100); err = set_permissions(zd->dialog,"",oldperms,newperms); if (! err) zdialog_stuff(zd,"permissions",newperms); } return 1; } /******************************************************************************** text file line edit function open a text file: err = linedit_open(filespec) get next record: record = linedit_get() null = EOF put next record: err = linedit_put(record) close file: err = linedit_close() resulting file contains only the 'put' records and replaces input file trailing '\n' is removed from records read and added to records written linedit+get() returns pointer to internal buffer - DO NOT zfree() returns: 0 = OK, +N = error *********************************************************************************/ namespace linedit { FILE *fidr = 0, *fidw = 0; char filespec[202], copyfile[208]; } int linedit_open(cchar *file) { using namespace linedit; if (strlen(file) > 200) { printz("linedit() filespec > 200: %s \n",file); return 1; } strcpy(filespec,file); snprintf(copyfile,206,"%s-copy",filespec); fidr = fopen(filespec,"r"); // open/read input file (may be null) fidw = fopen(copyfile,"w"); // open/write copy file if (! fidw) { printz("linedit(): %s \n %s \n",file,strerror(errno)); if (fidr) fclose(fidr); fidr = 0; return 1; } return 0; } char * linedit_get() { using namespace linedit; static char buff[1000], *prec; if (! fidr) return 0; // new file prec = fgets_trim(buff,1000,fidr); // read next record if (! prec) return 0; if (strlen(prec) > 998) { printz("linedit() record > 998: %s \n",filespec); return 0; } return prec; } int linedit_put(cchar *record) { using namespace linedit; int nn; if (! fidw) return 1; nn = fprintf(fidw,"%s\n",record); // write next record if (nn < 0) { printz("linedit(): %s \n %s \n",filespec,strerror(errno)); return 1; } return 0; } int linedit_close() { using namespace linedit; int err; if (fidr) fclose(fidr); // close input and copy files fidr = 0; if (! fidw) return 1; err = fclose(fidw); fidw = 0; if (err) { printz("linedit(): %s \n %s \n",filespec,strerror(errno)); return 1; } err = rename(copyfile,filespec); // replace input file with copy if (err) { printz("linedit(): %s \n %s \n",filespec,strerror(errno)); return 1; } return 0; } /******************************************************************************** Find all image files within given path. int find_imagefiles(cchar *folder, int flags, char **&flist, int &NF) folder folder path to search flags sum of the following: 1 include image files (+RAW +video) 2 include thumbnails 4 include hidden files 8 include folders 16 recurse folders 32 omit symlinks // 20.0 NF count of files returned flist list of files returned Returns 0 if OK, +N if error (errno is set). flist and flist[*] are subjects for zfree(). *********************************************************************************/ namespace find_imagefiles_names { char **fif_filelist; // list of filespecs returned char *lockfile = 0; // global lock file char lockname[200]; // lock file name int fif_max; // filelist slots allocated int fif_count; // filelist slots filled int fd = -1; // global lock file descriptor } int find_imagefiles(cchar *folder, int flags, char **&flist, int &NF, int Finit) { using namespace find_imagefiles_names; static int ftf = 1; int globflags, Fimages, Fthumbs, Fdirs, Frecurse, Fnolinks; int err1, err2, cc; FTYPE ftype; char *file, *mfolder, **templist; glob_t globdata; STATB statB; if (ftf) { ftf = 0; snprintf(lockname,200,"%s/find_imagefiles_lock",temp_folder); make_global_lockfile(lockname,&lockfile); } if (Finit) { // initial call (OMIT, default 1) while ((fd = global_lock(lockfile)) < 0) zsleep(0.001); // 19.0 fif_max = fif_count = 0; } globflags = GLOB_NOSORT; Fimages = Fthumbs = Fdirs = Frecurse = Fnolinks = 0; if (flags & 1) Fimages = 1; if (flags & 2) Fthumbs = 1; if (flags & 4) globflags += GLOB_PERIOD; if (flags & 8) Fdirs = 1; if (flags & 16) Frecurse = 1; if (flags & 32) Fnolinks = 1; if (Fdirs && ! Fimages && ! Fthumbs) globflags += GLOB_ONLYDIR; globdata.gl_pathc = 0; // glob() setup globdata.gl_offs = 0; globdata.gl_pathc = 0; NF = 0; // empty output flist = 0; mfolder = zstrdup(folder,4); // append /* to input folder strcat(mfolder,"/*"); err1 = glob(mfolder,globflags,null,&globdata); // find all files in folder if (err1) { if (err1 == GLOB_NOMATCH) err1 = 0; else if (err1 == GLOB_ABORTED) err1 = 1; else if (err1 == GLOB_NOSPACE) err1 = 2; else err1 = 3; goto fif_return; } for (uint ii = 0; ii < globdata.gl_pathc; ii++) // loop found files { file = globdata.gl_pathv[ii]; if (Fnolinks) { // detect and omit symlinks 20.0 err1 = lstat(file,&statB); if (err1) continue; if (S_ISLNK(statB.st_mode)) continue; } else err1 = stat(file,&statB); if (err1) continue; // should not happen if (Frecurse && S_ISDIR(statB.st_mode)) { // folder err1 = find_imagefiles(file,flags,flist,NF,0); // process member files if (err1) goto fif_return; } ftype = image_file_type(file); if (ftype == OTHER) continue; // unknown file type if (ftype == FDIR && ! Fdirs) continue; if (ftype == THUMB && ! Fthumbs) continue; if (ftype == IMAGE || ftype == RAW || ftype == VIDEO) if (! Fimages) continue; if (fif_count == fif_max) { // output list is full if (fif_max == 0) { fif_max = 1000; // initial space, 1000 files cc = fif_max * sizeof(char *); fif_filelist = (char **) zmalloc(cc); } else { templist = fif_filelist; // expand by 2x each time needed cc = fif_max * sizeof(char *); fif_filelist = (char **) zmalloc(cc+cc); memcpy(fif_filelist,templist,cc); memset(fif_filelist+fif_max,0,cc); zfree(templist); fif_max *= 2; } } fif_filelist[fif_count] = zstrdup(file); // add file to output list fif_count += 1; } err1 = 0; fif_return: err2 = errno; // preserve Linux errno globfree(&globdata); // free memory zfree(mfolder); if (Finit) { // user call NF = fif_count; if (NF) flist = fif_filelist; global_unlock(fd,lockfile); // 19.0 } errno = err2; // return err1 and preserve errno // zmainloop(); // NO - causes re-entrance bugs return err1; } /********************************************************************************/ // 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; } /********************************************************************************/ // Show a permissions setting, edit and return changed permissions. // 'fname' is a file name or permission category, e.g. 'default' // Permissions are strings: "read+write", "read only", "no access". // 'p1' and 'p2' are input and output permissions strings, which are // any combination of three of the above strings, comma separated, // which define the access permissions for user, group, other. // p1 and p2 are char[100] strings to allow for fat translations. // Returns 1 if user cancel or error, 0 if changes were made OK. int set_permissions(GtkWidget *parent, cchar *fname, cchar *p1, char *p2) // 20.0 { char operm[50], gperm[50], wperm[50]; const char *pp; zdialog *zd; int zstat; /*** ________________________________ | permissions | | | | owner [read+write |v] | | group [read only |v] | | other [no access |v] | | | | [apply] [cancel] | |________________________________| ***/ zd = zdialog_new(E2X("Permissions"),parent,Bapply,Bcancel,null); zdialog_add_widget(zd,"hbox","space","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hb1","dialog"); 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,"label","labowner","vb1",E2X("owner")); zdialog_add_widget(zd,"label","labgroup","vb1",E2X("group")); zdialog_add_widget(zd,"label","labother","vb1",E2X("other")); zdialog_add_widget(zd,"combo","ownerperm","vb2"); zdialog_add_widget(zd,"combo","groupperm","vb2"); zdialog_add_widget(zd,"combo","otherperm","vb2"); zdialog_stuff(zd,"ownerperm",RWtran); // initz. combo boxes zdialog_stuff(zd,"ownerperm",ROtran); zdialog_stuff(zd,"ownerperm",NOtran); zdialog_stuff(zd,"groupperm",RWtran); zdialog_stuff(zd,"groupperm",ROtran); zdialog_stuff(zd,"groupperm",NOtran); zdialog_stuff(zd,"otherperm",RWtran); zdialog_stuff(zd,"otherperm",ROtran); zdialog_stuff(zd,"otherperm",NOtran); pp = strField(p1,',',1); // initz. dialog from input permissions if (pp) zdialog_stuff(zd,"ownerperm",pp); pp = strField(p1,',',2); if (pp) zdialog_stuff(zd,"groupperm",pp); pp = strField(p1,',',3); if (pp) zdialog_stuff(zd,"otherperm",pp); zdialog_run(zd,0,0); // run dialog zstat = zdialog_wait(zd); // wait for completion if (zstat != 1) { zdialog_free(zd); // [cancel] return 1; // error return } *p2 = 0; zdialog_fetch(zd,"ownerperm",operm,50); // get permissions from dialog zdialog_fetch(zd,"groupperm",gperm,50); zdialog_fetch(zd,"otherperm",wperm,50); snprintf(p2,100,"%s, %s, %s",operm,gperm,wperm); // output permissions zdialog_free(zd); return 0; // normal return } // convert between permission formats // user format: "read+write, read only, no access" (also translatable) // internal format: mode_t bitmap of permissions, e.g. 0640 int conv_permissions(cchar *perms, mode_t &mode) // 20.0 { int err = 0; cchar *pp; mode = 0; pp = strField(perms,',',1); if (pp) { if (strmatch(pp,RWtran)) mode += S_IRUSR + S_IWUSR; else if (strmatch(pp,ROtran)) mode += S_IRUSR; else if (strmatch(pp,NOtran)); else err = 1; } else err = 1; pp = strField(perms,',',2); if (pp) { if (strmatch(pp,RWtran)) mode += S_IRGRP + S_IWGRP; else if (strmatch(pp,ROtran)) mode += S_IRGRP; else if (strmatch(pp,NOtran)); else err = 1; } else err = 1; pp = strField(perms,',',3); if (pp) { if (strmatch(pp,RWtran)) mode += S_IROTH + S_IWOTH; else if (strmatch(pp,ROtran)) mode += S_IROTH; else if (strmatch(pp,NOtran)); else err = 1; } else err = 1; return err; } // output perms is char[100] to allow for fat translations int conv_permissions(mode_t mode, char *perms) // 20.0 { int err = 0; *perms = 0; if (mode & S_IRUSR && mode & S_IWUSR) strcat(perms,RWtran); else if (mode & S_IRUSR) strcat(perms,ROtran); else strcat(perms,NOtran); strcat(perms,", "); if (mode & S_IRGRP && mode & S_IWGRP) strcat(perms,RWtran); else if (mode & S_IRGRP) strcat(perms,ROtran); else strcat(perms,NOtran); strcat(perms,", "); if (mode & S_IROTH && mode & S_IWOTH) strcat(perms,RWtran); else if (mode & S_IROTH) strcat(perms,ROtran); else strcat(perms,NOtran); return err; } // get real path for filename possibly having symlinks // all folders in input filename path must exist // final file name need not exist (f_save() caller) // returns real path or null if error // returned path is subject for zfree() char * f_realpath(cchar * infile) // 20.0 { int cc; char *pp, *RP, *outfile; char tempfile[XFCC]; RP = realpath(infile,null); // try full file path if (RP) { outfile = zstrdup(RP); // OK free(RP); return outfile; } strcpy(tempfile,infile); // fail, try folders only pp = strrchr(tempfile + 2,'/'); if (! pp) return 0; *pp = 0; RP = realpath(tempfile,null); *pp = '/'; if (! RP) return 0; // fail cc = strlen(pp); // return folders/file outfile = zstrdup(RP,cc+2); strcat(outfile,pp); free(RP); return outfile; } fotoxx-20.08/f.gallery.cc000066400000000000000000006177601362435004500152670ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* Fotoxx image edit - folder navigation and thumbnail gallery functions gallery create/update/search/paint an image gallery navi:: gallery_comp gallery sort compare function gallery_paint paint thumbnail gallery gallery_paintmeta same, metadata report from search function gallery_listview1 same, small list view, no metadata gallery_listview2 same, big list view with metadata dir_filecount paint - get folder subdir and image file counts draw_text paint - write text over thumbnail images gallery_navibutts add folder navigation buttons menufuncx gallery menu: sort/zoom/scroll ... gallery_scroll scroll gallery in rows and pages navibutt_clicked open new gallery from clicked folder newtop open new gallery from clicked TOP entry newalbum open new gallery from clicked album entry gallery_sort choose gallery sort order and sort gallery mouse_event process gallery thumbnail clicks gallery_dragfile process thumbnail dragged gallery_dropfile process thumbnail dropped (add new file, arrange album) KBaction process gallery KB actions: zoom, page, top, end gallery_memory save/recall gallery sort and position set_gwin_title update the gallery window title bar prev_next_file get previous/next file from curr_file in current gallery prev_next_gallery get gallery's previous or next gallery file_position get gallery file position m_thumbview gallery thumbnail view m_metaview gallery metadata view m_listview gallery list view 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 image_file_type determine file type (folder, image, RAW, thumbnail, other) thumb2imagefile get corresponding image file for given thumbnail file image2thumbfile get corresponding thumbnail file for given image file thumbfile_OK check if thumbnail file missing or stale get_folder_pixbuf get 'folder' pixbuf image get_broken_pixbuf get 'broken file' pixbuf image update_thumbfile refresh thumbnail file if missing or stale delete_thumbfile delete thumbnail disk file get_cache_thumb get thumbnail from cache, create and add if missing check_cache_thumb test if a thumbnail is in the cache already preload_thumbs preload thumbnails beyond gallery into thumbnail cache gallery_popimage popup a larger image from a clicked thumbnail gallery_select1 single gallery file selection function and dialog gallery_select multiple gallery file selection function and dialog m_select_files select files for album and batch functions m_bookmarks goto selected bookmark, edit bookmarks m_edit_bookmarks edit bookmarks m_alldirs folding folder tree, click for gallery view m_source_folder set gallery to folder of current image file m_show_hidden dummy menu for KB shortcut "Show Hidden Files" m_current_album dummy menu for KB shortcut "Current Album" *********************************************************************************/ #define EX extern // disable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ /*** defined in fotoxx.h typedef struct { current gallery file list in memory char *file; /folder.../filename char fdate[16]; file date: yyyymmddhhmmss char pdate[16]; photo date: yyyymmddhhmmss int fsize; file size, bytes 19.0 int psize; image size, pixels 19.0 int mdindex; index to metadata list Gmdlist[*] } GFlist_t; ***/ namespace navi { #define maxgallerylevs 60 // max gallery navigation levels #define TEXTWIN GTK_TEXT_WINDOW_TEXT // GDK window of GTK text view #define NEVER GTK_POLICY_NEVER #define ALWAYS GTK_POLICY_ALWAYS #define thumbxx 5 // thumbx array size int thumbx[5] = { 512, 360, 256, 180, 128 }; // thumbnail sizes int fontx[5] = { 0, -1, -1, -2, -3 }; // font size wrt appfont 20.0 GFlist_t *GFlist = 0; // gallery file list int GFlock = 0; // gallery file list lock int TClock = 0; // thumbnail cache lock int Nfiles = 0; // gallery files (incl. subfolders) int Nfolders = 0; // gallery subfolder count int Nimages = 0; // gallery image file count int preload_trigger = 0; // kick preload_thumb() thread 20.0 int preload_start = 0; // 1st file in gallery to check 20.0 char **Gmdlist = 0; // corresp. metadata list int Gmdrows = 0; // text rows in metadata list char *galleryname = 0; // folder or file list name GTYPE gallerytype; // gallery type: folder, recent, etc. GSORT gallerysort; // gallery sort: f.name, f.date, p.date GSEQ galleryseq; // gallery sort: ascending/descending int galleryposn = 0; // scroll-to file position (Nth) int Flistview = 0; // gallery list view mode 0/1/2 GtkWidget *gallerybutt[60]; // gallery navi buttons [aaa] [bbb] ... char *gallerypath[60]; // corresp. folder names aaa bbb ... GtkWidget *gallerylabel = 0; // gallery label (album name ...) int gallerypainted = 0; // gallery is finished painting int xwinW, xwinH; // gallery window current size int thumbsize = 256; // initial thumbnail image size int thumbW, thumbH; // gallery window thumbnail cell size int fontsize = 10; // font size for gallery text int texthh; // vertical space req. for metadata text int xrows, xcols; // gallery window thumbnail rows, cols int margin = 5; // cell margin from left and top edge int genthumbs = 0; // count newly generated thumbnails int scrollposn = 0; // scroll position int maxscroll; // max. scroll position char *drag_file = 0; // selected thumbnail (file) int drag_posn = -1; // drag from position int gallery_scrollgoal = -1; // gallery scroll goal position int gallery_scrollspeed = 0; // gallery scroll speed pixels/second // private functions int gallery_comp(cchar *rec1, cchar *rec2); // gallery record compare for sort options int gallery_paint(GtkWidget *, cairo_t *); // gallery window paint function int gallery_paintmeta(GtkWidget *Gdrawin, cairo_t *cr); // same, metadata report from search func. int gallery_listview1(GtkWidget *Gdrawin, cairo_t *cr); // same, small list view, no metadata int gallery_listview2(GtkWidget *Gdrawin, cairo_t *cr); // same, big list view with metadata void dir_filecount(char *dirname, int &ndir, int &nfil); // get subdir and image file counts void draw_text(cairo_t *cr, char *text, int x, int y, int ww); // draw text in gallery window void gallery_navibutts(); // create navigation buttons in top panel void menufuncx(GtkWidget *, cchar *menu); // function for gallery window buttons void gallery_scroll(int position, int speed); // start gallery slow scroll to position int gallery_scrollfunc(void *); // gallery scroll timer function void navibutt_clicked(GtkWidget *, int *level); // set gallery via click navigation button void newtop(GtkWidget *widget, GdkEventButton *event); // function for [TOP] button void newtop_menu_event(GtkWidget *, cchar *); // [TOP] menu response function void newalbum(GtkWidget *widget, GdkEventButton *event); // function for [Album] button void newalbum_menu_event(GtkWidget *, cchar *); // [Album] menu response function void gallery_sort(); // choose sort order and sort gallery int mouse_event(GtkWidget *widget, GdkEvent *event, void *); // gallery window mouse event function char * gallery_dragfile(); // start drag-drop, set the file void gallery_dropfile(int mousex, int mousey, char *file); // accept drag-drop file at position int KBpress(GtkWidget *, GdkEventKey *, void *); // gallery window key press event } using namespace zfuncs; using namespace navi; /******************************************************************************** char * gallery(cchar *filez, cchar *action, int Nth) public function to create/update image gallery (thumbnail window) Make a scrolling window of thumbnails for a folder or list of files Handle window buttons (up row, down page, open file, etc.) Call external functions in response to thumbnail mouse-clicks. filez: image file or folder, or file with list of image files filez action Nth ----- ------ --- file init * folder gallery, file name sort (subdirs first, last version option) file initF * file list gallery, no sort * sort -2 sort and position via gallery_memory() * sort -1 sort and position from current params file paint N paint, position = file (use N if valid for file) 0 paint N paint, position = N 0 paint -1 paint, no change in position file insert N add file to gallery at position N (folder or unsorted album) * delete N delete Nth file in gallery list * get N return file N in gallery list or null if N > last file update * update gallery file params (GFlist[*]) from image index in memory * get1st * return 1st image file in gallery or null if none Returned values: get or get1st: filespec others: null The returned file belongs to caller and is subject for zfree(). thumbnail click functions: gallery_Lclick_func() default function (open file) gallery_Rclick_popup() default function (popup menu) gallery_select_Lclick_func() gallery_select active gallery_select_Rclick_func() gallery_select active edit_bookmarks_Lclick_func edit bookmarks active *********************************************************************************/ char * gallery(cchar *filez, cchar *action, int Nth) { char *file, *file2; char *pp, *pp1, *pp2; char buff[XFCC]; char **Flist = 0; // 19.0 xxrec_t *xxrec; static int ftf = 1, Gbusy = 0; int err, ii, jj, kk; int cc, cc1, cc2, fposn; int NF, flags; FTYPE ftype; FILE *fid; STATB statb; if (ftf) { ftf = 0; cc = maxgallery * sizeof(GFlist_t); GFlist = (GFlist_t *) zmalloc(cc); // allocate gallery file list Nfiles = Nfolders = Nimages = 0; // no files yet } if (Gbusy++) { printz("gallery re-enter \n"); // prevent GUI lockup bugfix 19.3 return 0; } while (! resource_lock(GFlock)) zsleep(0.001); // lock gallery file list 20.0 if (strmatchN(action,"init",4)) // init or initF { if (! filez && gallerytype == GDIR) filez = galleryname; // refresh gallery if (! filez) goto return0; NF = Nfiles; // current file count Nfiles = Nfolders = Nimages = 0; // no new files yet if (Gmdlist) { for (ii = 0; ii < NF; ii++) { // free prior metadata list kk = GFlist[ii].mdindex; if (Gmdlist[kk]) zfree(Gmdlist[kk]); } zfree(Gmdlist); Gmdlist = 0; } for (ii = 0; ii < NF; ii++) // free prior gallery list zfree(GFlist[ii].file); galleryname = zstrdup(filez); // may be folder or file } if (strmatch(action,"init")) // initz. from given file or folder { err = stat(galleryname,&statb); if (err) { pp = (char *) strrchr(galleryname,'/'); // bad file, check folder part if (! pp) goto return0; pp[0] = 0; err = stat(galleryname,&statb); if (err) goto return0; // give up, empty file list } if (S_ISREG(statb.st_mode)) { // if a file, get folder part pp = (char *) strrchr(galleryname,'/'); if (! pp) goto return0; pp[0] = 0; } gallerytype = GDIR; // gallery type = folder cc = strlen(galleryname) - 1; // remove trailing '/' if (cc > 1 && galleryname[cc] == '/') galleryname[cc] = 0; // but not if root folder '/' flags = 1 + 8; // images + folders, one level if (Fshowhidden) flags += 4; // include hidden files err = find_imagefiles(galleryname,flags,Flist,NF); // find all image files in folder if (err) { zmessageACK(Mwin,strerror(errno)); goto return0; } for (ii = 0; ii < NF; ii++) { file = Flist[ii]; pp = strrchr(file,'/'); if (pp && (strmatch(pp,"/.") || strmatch(pp,"/.."))) { zfree(file); continue; } err = stat(file,&statb); if (err) { zfree(file); continue; } if (Nfiles == maxgallery) { // too many files zmessageACK(Mwin,Bgallerytruncated,maxgallery); break; } ftype = image_file_type(file); if (ftype == FDIR) { // subfolder GFlist[Nfiles].file = file; // add to file list GFlist[Nfiles].file[0] = '!'; // if folder, make it sort first GFlist[Nfiles].fdate[0] = 0; // no file date GFlist[Nfiles].pdate[0] = 0; // no photo date GFlist[Nfiles].psize = 0; // no image size 19.13 Nfiles++; Nfolders++; } else if (ftype == IMAGE || ftype == RAW || ftype == VIDEO) { // supported image file type xxrec = get_xxrec(file); // 19.0 if (! xxrec) { zfree(file); continue; } GFlist[Nfiles].file = file; // add to file list strcpy(GFlist[Nfiles].fdate,xxrec->fdate); strcpy(GFlist[Nfiles].pdate,xxrec->pdate); GFlist[Nfiles].fsize = xxrec->fsize; // file size, MB GFlist[Nfiles].psize = xxrec->ww * xxrec->hh; // image size, pixels Nfiles++; Nimages++; } else { zfree(file); // thumbnail or other kind of file continue; } } if (Flist) zfree(Flist); gallerysort = FNAME; // sort GFlist by file name ascending galleryseq = ASCEND; galleryposn = 0; if (Nfiles > 1) HeapSort((char *) GFlist, sizeof(GFlist_t), Nfiles, gallery_comp); if (Flastversion) // last versions only (user setting) { ii = Nfolders; // skip folders jj = ii + 1; kk = 0; while (jj < Nfiles) { pp1 = strrchr(GFlist[jj].file,'.'); // /.../filename.vNN.ext if (pp1 && pp1[-4] == '.' && pp1[-3] == 'v') { // | cc1 = pp1 - GFlist[jj].file - 3; pp2 = strrchr(GFlist[ii].file,'.'); if (! pp2) cc2 = 0; else { cc2 = pp2 - GFlist[ii].file + 1; // /.../filename.ext if (pp2[-4] == '.' && pp2[-3] == 'v') cc2 -= 4; // | } if (cc1 == cc2 && strmatchN(GFlist[jj].file,GFlist[ii].file,cc1)) { zfree(GFlist[ii].file); // if match to "/.../filename." GFlist[ii] = GFlist[jj]; // replace with later version jj++; kk++; continue; } } ii++; GFlist[ii] = GFlist[jj]; jj++; } Nfiles -= kk; Nimages -= kk; } curr_file_count = Nimages; // gallery image file count gallerypainted = 0; goto return0; } if (strmatch(action,"initF")) // gallery from given file list { if (gallerytype == TNONE || gallerytype == GDIR) // gallery type from caller: zappcrash("gallery() initF gallerytype %d",gallerytype); // SEARCH META RECENT NEWEST ALBUM fid = fopen(galleryname,"r"); // open file if (! fid) goto return0; while (true) // read list of files { file = fgets_trim(buff,XFCC-1,fid,1); if (! file) break; err = stat(file,&statb); // check file exists if (err) continue; xxrec = get_xxrec(file); // get index record if (! xxrec) continue; GFlist[Nfiles].file = zstrdup(file); // add to file list 19.13 compact_time(statb.st_mtime,GFlist[Nfiles].fdate); // file date, "yyyymmddhhmmss" GFlist[Nfiles].fsize = statb.st_size; // file size, bytes strcpy(GFlist[Nfiles].pdate,xxrec->pdate); // photo date GFlist[Nfiles].psize = xxrec->ww * xxrec->hh; // image size, pixels Nfiles++; if (Nfiles == maxgallery) { // too many files zmessageACK(Mwin,Bgallerytruncated,maxgallery); break; } } fclose(fid); Nimages = Nfiles; // no folders curr_file_count = Nimages; // gallery image file count gallerysort = SNONE; // no initial sort galleryseq = QNONE; galleryposn = 0; gallerypainted = 0; goto return0; } if (strmatch(action,"sort")) // sort the gallery file list { if (Nth == -2) gallery_memory("get"); // recall prior sort and posn. if (Nfiles > 1 && gallerysort != SNONE) // sort using current or prior params HeapSort((char *) GFlist, sizeof(GFlist_t), Nfiles, gallery_comp); gallerypainted = 0; goto return0; } if (strmatch(action,"paint")) // paint gallery window { if (filez) Nth = file_position(filez,Nth); // use or get valid Nth for filez if (Nth >= 0) galleryposn = Nth; // filez position or caller Nth gtk_widget_queue_draw(Gdrawin); // draw gallery window gallerypainted = 0; goto return0; } if (strmatch(action,"insert")) // insert new file into list { fposn = Nth; // file position from caller if (fposn < 0) fposn = 0; // limit to allowed range if (fposn > Nfiles) fposn = Nfiles; if (Nfiles == maxgallery-1) { // no room zmessageACK(Mwin,Bgallerytruncated,maxgallery); goto return0; } xxrec = get_xxrec(filez); // 19.0 if (! xxrec) { zmessageACK(Mwin,"file not indexed: %s",filez); goto return0; } for (ii = Nfiles; ii > fposn; ii--) // create hole in list GFlist[ii] = GFlist[ii-1]; GFlist[fposn].file = zstrdup(filez); // put new file in hole strcpy(GFlist[fposn].fdate,xxrec->fdate); strcpy(GFlist[fposn].pdate,xxrec->pdate); GFlist[fposn].fsize = xxrec->fsize; // file size, MB 19.0 GFlist[fposn].psize = xxrec->ww * xxrec->hh; // image size, pixels Nfiles++; Nimages++; gallerypainted = 0; goto return0; } if (strmatch(action,"delete")) // delete file from list { fposn = Nth; // file position from caller must be OK if (fposn < Nfolders || fposn > Nfiles-1) goto return0; Nfiles--; // decr. total files Nimages--; // decr. image files zfree(GFlist[fposn].file); // remove GFlist record for (ii = fposn; ii < Nfiles; ii++) // close the hole GFlist[ii] = GFlist[ii+1]; // gcc bug workaround removed if (Gmdlist) { kk = GFlist[fposn].mdindex; // remove corresp. Gmdlist data if (Gmdlist[kk]) zfree(Gmdlist[kk]); Gmdlist[kk] = 0; } gallerypainted = 0; goto return0; } if (strmatch(action,"get")) // return Nth file in gallery { fposn = Nth; // file position from caller must be OK if (fposn < 0 || fposn > Nfiles-1) goto return0; file2 = zstrdup(GFlist[fposn].file); // get Nth file file2[0] = '/'; // restore initial '/' err = stat(file2,&statb); if (! err) goto return2; // return file2 zfree(file2); goto return0; } if (strmatch(action,"update")) // update file from image index 19.13 { // (from update_image_index()) for (ii = 0; ii < Nfiles; ii++) if (strmatch(filez,GFlist[ii].file)) break; if (ii == Nfiles) goto return0; // not found xxrec = get_xxrec(filez); // 19.0 if (! xxrec) goto return0; strcpy(GFlist[ii].fdate,xxrec->fdate); strcpy(GFlist[ii].pdate,xxrec->pdate); GFlist[ii].fsize = xxrec->fsize; // file size, MB GFlist[ii].psize = xxrec->ww * xxrec->hh; // image size, pixels gallerypainted = 0; goto return0; } if (strmatch(action,"get1st")) // return 1st image file in gallery { for (ii = Nfolders; ii < Nfiles; ii++) { // loop from 1st image file 19.0 err = stat(GFlist[ii].file,&statb); // file gone? if (err) continue; } if (ii == Nfiles) goto return0; // no files in gallery file2 = zstrdup(GFlist[ii].file); // 1st file goto return2; // return file2 } zappcrash("navigate %s",action); // invalid action return0: resource_unlock(GFlock); // unlock gallery file list Gbusy = 0; return 0; return2: resource_unlock(GFlock); Gbusy = 0; return file2;; } // private function, gallery sort compare // folders sort first and upper/lower case is ignored int navi::gallery_comp(cchar *rec1, cchar *rec2) { int nn; GFlist_t *grec1, *grec2; grec1 = (GFlist_t *) rec1; grec2 = (GFlist_t *) rec2; if (grec1->file[0] == '!') { // folder sort if (grec2->file[0] != '!') return -1; // folder :: image file nn = strcasecmp(grec1->file,grec2->file); // folder :: folder if (nn) return nn; nn = strcmp(grec1->file,grec2->file); // if equal, use utf8 compare return nn; } else if (grec2->file[0] == '!') return +1; // image file :: folder if (galleryseq == DESCEND) { // descending, switch inputs grec1 = (GFlist_t *) rec2; grec2 = (GFlist_t *) rec1; } switch (gallerysort) { case FNAME: { // file name nn = strcasecmp(grec1->file,grec2->file); // ignore case if (nn) return nn; nn = strcmp(grec1->file,grec2->file); // if equal, use utf8 compare return nn; } case FDATE: { // file mod date/time nn = strcmp(grec1->fdate,grec2->fdate); if (nn) return nn; goto tiebreak; } case PDATE: { // photo date/time nn = strcmp(grec1->pdate,grec2->pdate); // (EXIF DateTimeOriginal) if (nn) return nn; goto tiebreak; } case FSIZE: { // file size 19.0 nn = grec1->fsize - grec2->fsize; if (nn) return nn; goto tiebreak; } case PSIZE: { // image pixel size 19.0 nn = grec1->psize - grec2->psize; if (nn) return nn; goto tiebreak; } default: return 0; tiebreak: // tie breaker nn = strcasecmp(grec1->file,grec2->file); // file name without case if (nn) return nn; nn = strcmp(grec1->file,grec2->file); // if equal, use utf8 compare return nn; } } // private function // paint gallery window - draw thumbnail images and limited metadata int navi::gallery_paint(GtkWidget *drwin, cairo_t *cr) { GdkRGBA rgba; PIXBUF *pxbT; FTYPE ftype; xxrec_t *xxrec; double x1, y1, x2, y2; int ii, nrows, row, col; int textlines; int row1, row2, ww, hh, cc, fsize; int drwingW, drwingH; int thumx, thumy; int ndir, nfil, convdate; char *pp, *fspec, *fname, p0; char *ppd, ffolder[60]; char pdt[24], text[200]; gallerypainted = 0; if (! galleryname) return 1; // no gallery set_gwin_title(); // main window title = gallery name gallery_navibutts(); // set navigation buttons on top panel rgba.red = 0.00392 * GBrgb[0]; // window background color rgba.green = 0.00392 * GBrgb[1]; // 0 - 255 --> 0.0 - 1.0 rgba.blue = 0.00392 * GBrgb[2]; rgba.alpha = 1.0; gdk_cairo_set_source_rgba(cr,&rgba); cairo_paint(cr); if (gallerytype == META) { // metadata report (search function) gallery_paintmeta(drwin,cr); return 1; } if (Flistview == 0) {} /////////// if (Flistview == 1) { // small list view report 19.0 gallery_listview1(drwin,cr); return 1; } if (Flistview == 2) { // big list view, with metadata 20.0 gallery_listview2(drwin,cr); return 1; } for (ii = 0; ii < thumbxx; ii++) // largest ... smallest thumb size if (thumbsize >= thumbx[ii]) break; if (ii == thumbxx) ii = thumbxx-1; // (fix poss. bad parameter) thumbsize = thumbx[ii]; // thumbnail size fontsize = appfontsize + fontx[ii]; // corresp. font size 20.0 if (! Findexvalid) textlines = 1; // file name only else textlines = 2; // file name + date + wwhh if (gallerytype != GDIR) textlines++; // add folder name above file name texthh = textlines * 1.6 * fontsize + 4; // vertical space required thumbW = thumbsize + 10; // thumbnail cell size thumbH = thumbsize + texthh + thumbsize/24 + 10; xwinW = gtk_widget_get_allocated_width(Gscroll); // drawing window size xwinH = gtk_widget_get_allocated_height(Gscroll); xrows = int(0.1 + 1.0 * xwinH / thumbH); // get thumbnail rows and cols that xcols = int(0.1 + 1.0 * xwinW / thumbW); // (almost) fit in window if (xrows < 1) xrows = 1; if (xcols < 1) xcols = 1; nrows = (Nfiles+xcols-1) / xcols; // thumbnail rows, 1 or more if (nrows < 1) nrows = 1; drwingW = xcols * thumbW + margin + 10; // layout size for entire gallery drwingH = (nrows + 1) * thumbH; // (+ empty row for visual end) if (drwingH <= xwinH) drwingH = xwinH + 1; // at least window size + 1 gtk_widget_get_size_request(drwin,&ww,&hh); // current size if (ww != drwingW || hh != drwingH) gtk_widget_set_size_request(drwin,-1,drwingH); // needs to change maxscroll = nrows * thumbH; // too far but necessary if (maxscroll < xwinH) maxscroll = xwinH; // compensate GTK bug gtk_adjustment_set_upper(Gadjust,maxscroll); gtk_adjustment_set_step_increment(Gadjust,thumbH); // scrollbar works in row steps gtk_adjustment_set_page_increment(Gadjust,thumbH * xrows); // and in page steps if (galleryposn >= 0) { // new target file position (Nth) scrollposn = galleryposn / xcols * thumbH; // scroll position for target file if (scrollposn > maxscroll) scrollposn = maxscroll; // >> top row of window gtk_adjustment_set_value(Gadjust,scrollposn); } scrollposn = gtk_adjustment_get_value(Gadjust); galleryposn = (scrollposn + thumbH/2) / thumbH * xcols; // remember gallery position gallery_memory("put"); galleryposn = -1; // disable cairo_clip_extents(cr,&x1,&y1,&x2,&y2); // window region to paint row1 = y1 / thumbH; row2 = y2 / thumbH; for (row = row1; row <= row2; row++) // draw file thumbnails for (col = 0; col < xcols; col++) // draw all columns in row { ii = row * xcols + col; // next file if (ii >= Nfiles) goto endloops; // exit 2 nested loops fspec = GFlist[ii].file; // folder/file name p0 = *fspec; // replace possible '!' with '/' *fspec = '/'; pp = strrchr(fspec,'/'); // get file name only if (pp) fname = pp + 1; else fname = fspec; strcpy(ffolder,"/"); if (gallerytype != GDIR) { // files from mixed folders if (pp && pp > fspec) { for (ppd = pp-1; ppd >= fspec && *ppd != '/'; ppd--); // get last folder level cc = pp - ppd + 1; // (include null to be added) if (cc > 60) cc = 60; strncpy0(ffolder,ppd,cc); } } thumx = col * thumbW + margin; // drawing area position thumy = row * thumbH + margin; if (curr_file && strmatch(fspec,curr_file)) { // yellow background for curr. image cairo_set_source_rgb(cr,1,1,0.5); cairo_rectangle(cr,thumx-3,thumy-3,thumbW-3,texthh); cairo_fill(cr); } ftype = image_file_type(fspec); // folder/image/RAW file if (ftype == FDIR) { // folder dir_filecount(fspec,ndir,nfil); // get subdir and file counts snprintf(text,200,"%s\n%d + %d images",fname,ndir,nfil); // dir name, subdirs + image files } else // image/RAW/VIDEO file { xxrec = get_xxrec(fspec); // get index record if (! xxrec) continue; convdate = 1; if (gallerysort == FDATE) strncpy0(pdt,xxrec->fdate,15); // use file or photo date 19.13 else { // based on gallery sort convdate = 0; strncpy0(pdt,xxrec->pdate,15); // photo date if (xxrec->ww == 0) strcpy(pdt,"not indexed"); else if (strmatch(pdt,"null")) strcpy(pdt,"undated"); else convdate = 1; } if (convdate) { memmove(pdt+17,pdt+12,2); // convert yyyymmddhhmmss memmove(pdt+14,pdt+10,2); // to yyyy-mm-dd hh:mm:ss memmove(pdt+11,pdt+8,2); memmove(pdt+8,pdt+6,2); memmove(pdt+5,pdt+4,2); pdt[19] = 0; pdt[16] = pdt[13] = ':'; pdt[10] =' '; pdt[4] = pdt[7] = '-'; if (thumbsize <= 256) pdt[10] = 0; // truncate date/time to date only } ww = xxrec->ww; // width, height, file size 19.0 hh = xxrec->hh; fsize = xxrec->fsize; cc = 0; if (gallerytype != GDIR) { // mixed folders (search results) snprintf(text,200,"%s\n",ffolder); // output folder name + \n cc = strlen(text); } snprintf(text+cc,200-cc,"%s\n",fname); // output file name + \n cc = cc + strlen(fname) + 1; if (Findexvalid) { // valid index, metadata available snprintf(text+cc,200-cc,"%s",pdt); // output date [ time ] cc += strlen(pdt); if (ww == 0) // file not indexed 19.13 snprintf(text+cc,200-cc," %.2fmb",fsize/FMEGA); // output file size only 19.13 else { if (thumbsize > 180) snprintf(text+cc,200-cc," %dx%d %.2fmb",ww,hh,fsize/FMEGA); // output width, height, file size 19.0 else if (thumbsize > 128) snprintf(text+cc,200-cc," %dx%d",ww,hh); // output only width, height 19.0 } } draw_text(cr,text,thumx,thumy,thumbW-5); // paint text first thumy += texthh; // position thumbnail below text } pxbT = get_cache_thumb(fspec,0); // get thumbnail if (pxbT) { ww = gdk_pixbuf_get_width(pxbT); ww = (thumbsize - ww) / 4; // shift margin if smaller width gdk_cairo_set_source_pixbuf(cr,pxbT,thumx+ww,thumy); cairo_paint(cr); // paint } if (ftype == FDIR) { // folder thumy += thumbsize/3 + 10; // overlay thumbnail with text draw_text(cr,text,thumx+ww+thumbW/6,thumy,thumbW-5); } *fspec = p0; // restore '!' } endloops: gallerypainted = 1; // mark gallery display complete preload_start = ii; // start file for preload_thread() 20.0 preload_trigger = 1; // trigger preload_thread() 20.0 return 1; } // private function // paint metadata report - draw thumbnail images + metadata // metadata is in Gmdlist[*] from search_images() metadata report int navi::gallery_paintmeta(GtkWidget *drwin, cairo_t *cr) { PIXBUF *pxbT; double x1, y1, x2, y2; int ii, kk, nrows, row; int row1, row2, ww, hh; int drwingW, drwingH; int thumx, thumy, textww; char *fspec, p0; thumbsize = 256; for (ii = 0; ii < thumbxx; ii++) if (thumbsize == thumbx[ii]) break; // tolerate bad parameter value if (ii >= thumbxx) thumbsize = thumbx[2]; // inherited from prior release fontsize = appfontsize; thumbW = thumbsize + 10; // thumbnail layout size thumbH = thumbsize + 20; texthh = Gmdrows * fontsize * 1.8 + 20; // space for metadata text if (texthh > thumbH) thumbH = texthh; xwinW = gtk_widget_get_allocated_width(Gscroll); // drawing window size xwinH = gtk_widget_get_allocated_height(Gscroll); xrows = int(0.1 + 1.0 * xwinH / thumbH); // get thumbnail rows fitting in window if (xrows < 1) xrows = 1; xcols = 1; // force cols = 1 nrows = Nfiles; // thumbnail rows if (! nrows) nrows = 1; drwingW = xwinW; // layout size for entire file list if (drwingW < 800) drwingW = 800; drwingH = (nrows + 1) * thumbH; // last row if (drwingH < xwinH) drwingH = xwinH; gtk_widget_get_size_request(drwin,&ww,&hh); // current size if (ww != drwingW || hh != drwingH) gtk_widget_set_size_request(drwin,-1,drwingH); // needs to change maxscroll = nrows * thumbH; // too far but necessary if (maxscroll < xwinH) maxscroll = xwinH; // compensate GTK bug gtk_adjustment_set_upper(Gadjust,maxscroll); gtk_adjustment_set_step_increment(Gadjust,thumbH); // scrollbar works in row steps gtk_adjustment_set_page_increment(Gadjust,thumbH * xrows); // and in page steps if (galleryposn >= 0) { // new target file position (Nth) scrollposn = galleryposn / xcols * thumbH; // scroll position for target file if (scrollposn > maxscroll) scrollposn = maxscroll; // >> top row of window gtk_adjustment_set_value(Gadjust,scrollposn); } scrollposn = gtk_adjustment_get_value(Gadjust); // save gallery sort and position galleryposn = (scrollposn + thumbH/2) / thumbH * xcols; gallery_memory("put"); galleryposn = -1; // disable cairo_clip_extents(cr,&x1,&y1,&x2,&y2); // window region to paint row1 = y1 / thumbH; row2 = y2 / thumbH; textww = drwingW - thumbW - 2 * margin; // space for text right of thumbnail for (row = row1; row <= row2; row++) // draw file thumbnails { ii = row; // next file if (ii >= Nfiles) break; fspec = GFlist[ii].file; p0 = *fspec; // replace possible ! with / *fspec = '/'; thumx = margin; // drawing area position thumy = row * thumbH + margin; pxbT = get_cache_thumb(fspec,0); // get thumbnail if (pxbT) { gdk_cairo_set_source_pixbuf(cr,pxbT,thumx,thumy); cairo_paint(cr); } draw_text(cr,fspec,thumbW+margin,thumy,textww); // write filespec to right of thumbnail if (Gmdlist) { // write corresp. metadata if any kk = GFlist[ii].mdindex; draw_text(cr,Gmdlist[kk],thumbW+margin,thumy+20,textww); // bugfix (kk=0 omitted) } *fspec = p0; // restore '!' } gallerypainted = 1; return 1; } // private function // paint gallery window small list view: tiny thumbnail, file name only int navi::gallery_listview1(GtkWidget *drwin, cairo_t *cr) // 20.0 { PIXBUF *pxbT; FTYPE ftype; double x1, y1, x2, y2; int ii, nrows, row, col; int row1, row2, ww, hh; int drwingW, drwingH; int thumx, thumy, textx, textww; int ndir, nfil; char *fspec, *fspec2, p0; xxrec_t *xxrec; char fdate[20], pdate[20], *pp; char text[1000]; thumbsize = 48; // small list view thumbnail size fontsize = appfontsize; textww = fontsize * 60; // text width limit for max. 60 chars. if (gallerytype != GDIR) textww = fontsize * 200; // more if full path is written thumbW = thumbsize + 10 + textww; // thumbnail layout size thumbH = thumbsize + 10; texthh = 2 * fontsize * 1.8 + 20; // space for 2 lines of 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 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; if (gallerytype != GDIR) xcols = 1; // not folder gallery, full file path nrows = (Nfiles+xcols-1) / xcols; // thumbnail rows, 1 or more if (nrows < 1) nrows = 1; drwingW = xcols * thumbW + margin + 10; // layout size for entire gallery drwingH = (nrows + 1) * thumbH; // (+ empty row for visual end) if (drwingH <= xwinH) drwingH = xwinH + 1; // at least window size + 1 gtk_widget_get_size_request(drwin,&ww,&hh); // current size if (ww != drwingW || hh != drwingH) gtk_widget_set_size_request(drwin,-1,drwingH); // needs to change maxscroll = nrows * thumbH; // too far but necessary if (maxscroll < xwinH) maxscroll = xwinH; // compensate GTK bug gtk_adjustment_set_upper(Gadjust,maxscroll); gtk_adjustment_set_step_increment(Gadjust,thumbH); // scrollbar works in row steps gtk_adjustment_set_page_increment(Gadjust,thumbH * xrows); // and in page steps if (galleryposn >= 0) { // new target file position (Nth) scrollposn = galleryposn / xcols * thumbH; // scroll position for target file if (scrollposn > maxscroll) scrollposn = maxscroll; // >> top row of window gtk_adjustment_set_value(Gadjust,scrollposn); } scrollposn = gtk_adjustment_get_value(Gadjust); // save gallery sort and position galleryposn = (scrollposn + thumbH/2) / thumbH * xcols; gallery_memory("put"); galleryposn = -1; // disable cairo_clip_extents(cr,&x1,&y1,&x2,&y2); // window region to paint row1 = y1 / thumbH; row2 = y2 / thumbH; for (row = row1; row <= row2; row++) // draw file thumbnails for (col = 0; col < xcols; col++) // draw all columns in row { ii = row * xcols + col; // next file if (ii >= Nfiles) goto endloops; // exit 2 nested loops fspec = GFlist[ii].file; p0 = *fspec; // replace possible ! with / *fspec = '/'; fspec2 = fspec; if (gallerytype == GDIR) { // folder gallery, write only pp = strrchr(fspec,'/'); // base file names if (pp) fspec2 = pp+1; } thumx = col * thumbW + margin; // drawing area position thumy = row * thumbH + margin; pxbT = get_cache_thumb(fspec,0); // get thumbnail if (pxbT) { gdk_cairo_set_source_pixbuf(cr,pxbT,thumx,thumy); // paint thumbnail cairo_paint(cr); } textx = thumx + margin + thumbsize; if (curr_file && strmatch(fspec,curr_file)) { // yellow background for curr. file name ww = fontsize * strlen(fspec2); if (ww > textww) ww = textww; cairo_set_source_rgb(cr,1,1,0.5); cairo_rectangle(cr,textx,thumy,ww,fontsize*1.6); cairo_fill(cr); } ftype = image_file_type(fspec); // file type if (ftype == FDIR) { // subfolder dir_filecount(fspec,ndir,nfil); // get subfolder and file counts snprintf(text,1000," %s \n %d folders + %d images \n", // paint 2 lines: fspec2, ndir, nfil); // folder name, file counts draw_text(cr,text,textx,thumy,textww); } else if (Findexvalid) // image file { xxrec = get_xxrec(fspec); if (! xxrec) continue; strncpy0(fdate,xxrec->fdate,16); strncpy0(pdate,xxrec->pdate,16); pp = fdate; // file date memmove(pp+17,pp+12,2); // :ss added memmove(pp+14,pp+10,2); // yyyymmddhhmmss to yyyy-mm-dd hh:mm:ss memmove(pp+11,pp+8,2); memmove(pp+8,pp+6,2); memmove(pp+5,pp+4,2); pp[19] = 0; pp[16] = pp[13] = ':'; pp[10] =' '; pp[4] = pp[7] = '-'; pp = pdate; // photo date if (xxrec->ww == 0) strcpy(pp,"not indexed"); else if (strmatch(pp,"null")) strcpy(pp,"undated"); else { memmove(pp+17,pp+12,2); // :ss added memmove(pp+14,pp+10,2); // yyyymmddhhmmss to yyyy-mm-dd hh:mm:ss memmove(pp+11,pp+8,2); memmove(pp+8,pp+6,2); memmove(pp+5,pp+4,2); pp[19] = 0; pp[16] = pp[13] = ':'; pp[10] =' '; pp[4] = pp[7] = '-'; } snprintf(text,1000," %s \n photo date: %s file date: %s \n", // file name, photo date, file date fspec2, pdate, fdate); } else snprintf(text,1000,"\n %s",fspec2); // no index, no metadata, file name only draw_text(cr,text,textx,thumy,textww); // paint text *fspec = p0; // restore '!' } endloops: gallerypainted = 1; return 1; } // private function // paint gallery window big list view: small thumbnail, file name, basic metadata int navi::gallery_listview2(GtkWidget *drwin, cairo_t *cr) { PIXBUF *pxbT; FTYPE ftype; double x1, y1, x2, y2; int ii, nrows, row, col; int row1, row2, ww, hh; int drwingW, drwingH; int thumx, thumy, textx, textww; int ndir, nfil; char *fspec, *fspec2, p0; xxrec_t *xxrec; char fdate[20], pdate[20], *pp; char *rating, *tags, *capt, *comms, *location, *country; char wwhh[16], fsize[16]; char text[1000]; thumbsize = 128; // list view thumbnail size fontsize = appfontsize; textww = fontsize * 60; // text width limit for max. 60 chars. if (gallerytype != GDIR) textww = fontsize * 200; // more if full path is written thumbW = thumbsize + 10 + textww; // thumbnail + text layout size thumbH = thumbsize + 20; texthh = 6 * fontsize * 1.8 + 20; // space for 6 lines of metadata text if (texthh > thumbH) thumbH = texthh; xwinW = gtk_widget_get_allocated_width(Gscroll); // drawing window size xwinH = gtk_widget_get_allocated_height(Gscroll); xrows = int(0.1 + 1.0 * xwinH / thumbH); // get thumbnail rows 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; if (gallerytype != GDIR) xcols = 1; // not folder gallery, full file path nrows = (Nfiles+xcols-1) / xcols; // thumbnail rows, 1 or more if (nrows < 1) nrows = 1; drwingW = xcols * thumbW + margin + 10; // layout size for entire gallery drwingH = (nrows + 1) * thumbH; // (+ empty row for visual end) if (drwingH <= xwinH) drwingH = xwinH + 1; // at least window size + 1 gtk_widget_get_size_request(drwin,&ww,&hh); // current size if (ww != drwingW || hh != drwingH) gtk_widget_set_size_request(drwin,-1,drwingH); // needs to change maxscroll = nrows * thumbH; // too far but necessary if (maxscroll < xwinH) maxscroll = xwinH; // compensate GTK bug gtk_adjustment_set_upper(Gadjust,maxscroll); gtk_adjustment_set_step_increment(Gadjust,thumbH); // scrollbar works in row steps gtk_adjustment_set_page_increment(Gadjust,thumbH * xrows); // and in page steps if (galleryposn >= 0) { // new target file position (Nth) scrollposn = galleryposn / xcols * thumbH; // scroll position for target file if (scrollposn > maxscroll) scrollposn = maxscroll; // >> top row of window gtk_adjustment_set_value(Gadjust,scrollposn); } scrollposn = gtk_adjustment_get_value(Gadjust); // save gallery sort and position galleryposn = (scrollposn + thumbH/2) / thumbH * xcols; gallery_memory("put"); galleryposn = -1; // disable cairo_clip_extents(cr,&x1,&y1,&x2,&y2); // window region to paint row1 = y1 / thumbH; row2 = y2 / thumbH; for (row = row1; row <= row2; row++) // draw file thumbnails for (col = 0; col < xcols; col++) // draw all columns in row { ii = row * xcols + col; // next file if (ii >= Nfiles) goto endloops; // exit 2 nested loops fspec = GFlist[ii].file; p0 = *fspec; // replace possible ! with / *fspec = '/'; fspec2 = fspec; if (gallerytype == GDIR) { // folder gallery, write only pp = strrchr(fspec,'/'); // base file names if (pp) fspec2 = pp+1; } thumx = col * thumbW + margin; // drawing area position thumy = row * thumbH + margin; pxbT = get_cache_thumb(fspec,0); // get thumbnail if (pxbT) { gdk_cairo_set_source_pixbuf(cr,pxbT,thumx,thumy); // paint thumbnail cairo_paint(cr); } textx = thumx + margin + thumbsize; // text position if (curr_file && strmatch(fspec,curr_file)) { // yellow background for curr. file name ww = fontsize * strlen(fspec2); if (ww > textww) ww = textww; cairo_set_source_rgb(cr,1,1,0.5); cairo_rectangle(cr,textx,thumy,ww,fontsize*1.6); cairo_fill(cr); } ftype = image_file_type(fspec); // file type if (ftype == FDIR) { // subfolder dir_filecount(fspec,ndir,nfil); // get subfolder and file counts snprintf(text,1000,"\n %s \n %d folders + %d images \n", // paint 2 lines: fspec2, ndir, nfil); // folder name, file counts draw_text(cr,text,textx,thumy,textww); } else if (Findexvalid) { xxrec = get_xxrec(fspec); if (! xxrec) continue; // 19.13 strncpy0(fdate,xxrec->fdate,16); // 19.13 strncpy0(pdate,xxrec->pdate,16); rating = xxrec->rating; snprintf(wwhh,16,"%dx%d",xxrec->ww,xxrec->hh); // 19.0 snprintf(fsize,16,"%.2fmb",xxrec->fsize/FMEGA); // 19.0 tags = xxrec->tags; capt = xxrec->capt; comms = xxrec->comms; location = xxrec->location; country = xxrec->country; pp = fdate; // file date memmove(pp+17,pp+12,2); // :ss added memmove(pp+14,pp+10,2); // yyyymmddhhmmss to yyyy-mm-dd hh:mm:ss memmove(pp+11,pp+8,2); memmove(pp+8,pp+6,2); memmove(pp+5,pp+4,2); pp[19] = 0; pp[16] = pp[13] = ':'; pp[10] =' '; pp[4] = pp[7] = '-'; pp = pdate; // photo date 19.13 if (xxrec->ww == 0) strcpy(pp,"not indexed"); else if (strmatch(pp,"null")) strcpy(pp,"undated"); else { memmove(pp+17,pp+12,2); // :ss added memmove(pp+14,pp+10,2); // yyyymmddhhmmss to yyyy-mm-dd hh:mm:ss memmove(pp+11,pp+8,2); memmove(pp+8,pp+6,2); memmove(pp+5,pp+4,2); pp[19] = 0; pp[16] = pp[13] = ':'; pp[10] =' '; pp[4] = pp[7] = '-'; } snprintf(text,1000, " %s \n photo date: %s file date: %s \n" " rating: %s size: %s %s location: %s %s \n" " tags: %s \n caption: %s \n comments: %s", fspec2, pdate, fdate, rating, wwhh, fsize, location, country, tags, capt, comms); } else snprintf(text,1000,"\n %s",fspec2); // no index, no metadata, file name only draw_text(cr,text,textx,thumy,textww); // paint text *fspec = p0; // restore '!' } endloops: gallerypainted = 1; return 1; } // private function // find the number of subdirs and image files within a given folder void navi::dir_filecount(char *dirname, int &ndir, int &nfil) { char *file, **flist; int ii, cc, err, NF; int dcount = 0, fcount = 0; FTYPE ftype; #define NC 1000 // cache capacity static int ftf = 1; static char *dirnamecache[NC]; // cache for recent folder 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 *); // first call, clear cache memset(dirnamecache,0,cc); } ndir = nfil = 0; err = stat(dirname,&statb); if (err) return; for (ii = 0; ii < NC; ii++) { // look for folder in cache if (! dirnamecache[ii]) break; if (strmatch(dirname,dirnamecache[ii]) && statb.st_mtime == modtimecache[ii]) { ndir = dcountcache[ii]; // found, not stale nfil = fcountcache[ii]; return; } } err = find_imagefiles(dirname,1+8,flist,NF); // find image files + folders if (err) { zmessageACK(Mwin,strerror(errno)); return; } for (ii = 0; ii < NF; ii++) { file = flist[ii]; ftype = image_file_type(file); zfree(file); if (ftype == FDIR) dcount++; // folder count else if (ftype == IMAGE || ftype == RAW || ftype == VIDEO) fcount++; // image file count } if (NF) zfree(flist); ii = pcache++; // add to cache, replace oldest 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 block at px/py location with width limit ww 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[20] = ""; if (fontsize != pfontsize) { // adjust for curr. font size pfontsize = fontsize; snprintf(thumbfont,20,"sans bold %d",fontsize); if (pfont) pango_font_description_free(pfont); pfont = pango_font_description_from_string(thumbfont); if (playout) g_object_unref(playout); 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); // text = black pango_cairo_show_layout(cr,playout); return; } // private function // create a row of navigation buttons in gallery top panel void navi::gallery_navibutts() { char labtext[100]; int ii, cc, max = maxgallerylevs; char *pp1, *pp2; for (ii = 0; ii < max; ii++) { // clear old navi buttons if any if (gallerypath[ii]) { zfree(gallerypath[ii]); gallerypath[ii] = 0; gtk_widget_destroy(gallerybutt[ii]); } } if (gallerylabel) gtk_widget_destroy(gallerylabel); // clear gallery label if any gallerylabel = 0; sprintf(labtext,"no gallery"); // 20.0 if (gallerytype == SEARCH) sprintf(labtext,"search results"); // search results if (gallerytype == META) sprintf(labtext,"search results"); // search results (metadata report) if (gallerytype == RECENT) sprintf(labtext,"recent images"); // recent images if (gallerytype == NEWEST) sprintf(labtext,"newest images"); // newest images if (gallerytype == ALBUM) { // album gallery pp1 = strrchr(galleryname,'/'); if (pp1) pp1++; else pp1 = galleryname; snprintf(labtext,100,"album: %s",pp1); // album: album-name } if (gallerytype != GDIR) { // not a folder gallery gallerylabel = gtk_label_new(labtext); // show gallery label gtk_box_pack_start(GTK_BOX(Gpanel),gallerylabel,0,0,0); gtk_widget_show_all(Gpanel); return; } ii = 0; pp1 = galleryname; while (pp1 && *pp1) // construct new buttons 20.0 { 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 folder level /cccccc if (! *pp1) break; // null = end if (*pp1 == '/' && ! *(pp1+1)) break; // / + null = end if (++ii == max) break; // limit of folder levels } gtk_widget_show_all(Gpanel); return; } // private function - menu function for gallery window // - scroll window as requested // - jump to new file or folder as requested void navi::menufuncx(GtkWidget *, cchar *menu) { int ii, scroll1, scroll2; if (FGWM != 'G') return; gallery_scroll(-1,0); // stop scrolling if (! gallerypainted) return; // wait for pending paint scrollposn = gtk_adjustment_get_value(Gadjust); // current scroll position if (strmatch(menu,"GoTo")) { F1_help_topic = "bookmarks"; m_bookmarks(0,0); return; } if (strmatch(menu,"Sort")) { // choose sort order and sort gallery F1_help_topic = "sort gallery"; gallery_sort(); return; } if (strmatch(menu,"Zoom+")) // next bigger thumbnail size 20.0 { if (Flistview == 1) Flistview = 2; // small list view --> big list view else if (Flistview == 2) { // big list view --> thumbnail gallery Flistview = 0; thumbsize = thumbx[thumbxx-1]; // set smallest thumbnail size } else { // thumbnail gallery for (ii = 0; ii < thumbxx; ii++) // set next greater thumbnail size if (thumbsize == thumbx[ii]) break; if (ii == 0) return; // already maximum, no change thumbsize = thumbx[ii-1]; } galleryposn = scrollposn / thumbH * xcols; // keep top row position gallery(0,"paint",-1); // paint gallery return; } if (strmatch(menu,"Zoom-")) // next smaller thumb size 20.0 { if (Flistview == 1) return; // small list view, no change else if (Flistview == 2) Flistview = 1; // big list view --> small list view else { // thumbnail gallery for (ii = 0; ii < thumbxx; ii++) // set next smaller thumb size if (thumbsize == thumbx[ii]) break; if (ii >= thumbxx-1) Flistview = 2; // < minimum, --> big list view else thumbsize = thumbx[ii+1]; // next smaller } galleryposn = scrollposn / thumbH * xcols; // keep top row position gallery(0,"paint",-1); // paint gallery return; } if (strmatch(menu,"Row Up")) { // scroll 1 row up scroll1 = scrollposn / thumbH * thumbH; scroll2 = scroll1 - thumbH; if (scroll2 < 0) scroll2 = 0; gallery_scroll(scroll2,1500); return; } if (strmatch(menu,"Row Down")) { // scroll 1 row down scroll1 = scrollposn / thumbH * thumbH; scroll2 = scroll1 + thumbH; if (scroll2 > maxscroll) scroll2 = maxscroll; gallery_scroll(scroll2,1500); return; } if (strmatch(menu,"Page Up")) { // scroll 1 page up scroll1 = scrollposn / thumbH * thumbH; scroll2 = scroll1 - thumbH * xrows; if (scroll2 < 0) scroll2 = 0; gallery_scroll(scroll2,3000); return; } if (strmatch(menu,"Page Down")) { // scroll 1 page down scroll1 = scrollposn / thumbH * thumbH; scroll2 = scroll1 + thumbH * xrows; if (scroll2 > maxscroll) scroll2 = maxscroll; gallery_scroll(scroll2,3000); return; } if (strmatch(menu,"First")) { // jump to top scrollposn = 0; galleryposn = scrollposn / thumbH * xcols; gallery(0,"paint",-1); return; } if (strmatch(menu,"Last")) { // jump to bottom scrollposn = maxscroll; galleryposn = scrollposn / thumbH * xcols; gallery(0,"paint",-1); return; } printz("unknown gallery function: %s \n",menu); return; } // private function // scroll gallery page up or down to goal scroll position // position: N goal scroll position, 0 to maxscroll // -1 stop scrolling immediately // speed: N scroll N pixels/second void navi::gallery_scroll(int position, int speed) { if (position < 0) { // stop scrolling gallery_scrollgoal = -1; gallery_scrollspeed = 0; return; } if (gallery_scrollgoal < 0) { // start scrolling gallery_scrollgoal = position; gallery_scrollspeed = speed; g_timeout_add(4,gallery_scrollfunc,0); // 4 millisec. timer period return; } gallery_scrollgoal = position; // continue scrolling with gallery_scrollspeed = speed; // possible goal/speed change return; } // private function // timer function, runs every 4 milliseconds int navi::gallery_scrollfunc(void *) { float cumscroll = 0; if (gallery_scrollgoal < 0) { // stop scrolling gallery_scrollspeed = 0; cumscroll = 0; return 0; } if (FGWM != 'G') { // not gallery view gallery_scrollgoal = -1; // stop scrolling gallery_scrollspeed = 0; cumscroll = 0; return 0; } if (scrollposn == gallery_scrollgoal) { // goal reached, stop gallery_scrollgoal = -1; gallery_scrollspeed = 0; cumscroll = 0; return 0; } cumscroll = 0.004 * gallery_scrollspeed; // based on 4 millisec. timer scrollposn = gtk_adjustment_get_value(Gadjust); if (scrollposn < gallery_scrollgoal) { // adjust scroll position scrollposn += cumscroll; if (scrollposn > gallery_scrollgoal) scrollposn = gallery_scrollgoal; } if (scrollposn > gallery_scrollgoal) { scrollposn -= cumscroll; if (scrollposn < gallery_scrollgoal) scrollposn = gallery_scrollgoal; } gtk_adjustment_set_value(Gadjust,scrollposn); return 1; } // private function // gallery top panel folder button clicked, open corresponding folder void navi::navibutt_clicked(GtkWidget *widget, int *lev) { char gallerydir[XFCC], *pp; gallery_scroll(-1,0); // stop scrolling pp = gallerydir; for (int ii = 0; ii <= *lev; ii++) { *pp = '/'; strcpy(pp+1,gallerypath[ii]); pp = pp + strlen(pp); } gallery(gallerydir,"init",0); // new gallery gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // paint return; } // private function - [TOP] button: select new top folder void navi::newtop(GtkWidget *widget, GdkEventButton *event) { static GtkWidget *popmenu = 0; gallery_scroll(-1,0); // stop scrolling if (popmenu) gtk_widget_destroy(popmenu); popmenu = create_popmenu(); for (int ii = 0; ii < Ntopfolders; ii++) // insert all top image folders add_popmenu_item(popmenu,topfolders[ii],newtop_menu_event,0,0); add_popmenu_item(popmenu,"ALL",newtop_menu_event,0,0); add_popmenu_item(popmenu,"/",newtop_menu_event,0,0); add_popmenu_item(popmenu,"HOME",newtop_menu_event,0,0); add_popmenu_item(popmenu,"Desktop",newtop_menu_event,0,0); add_popmenu_item(popmenu,"Fotoxx home",newtop_menu_event,0,0); add_popmenu_item(popmenu,"Saved Areas",newtop_menu_event,0,0); add_popmenu_item(popmenu,E2X("recent images"),newtop_menu_event,0,0); add_popmenu_item(popmenu,E2X("newest images"),newtop_menu_event,0,0); popup_menu(Mwin,popmenu); return; } void navi::newtop_menu_event(GtkWidget *, cchar *menu) // menu event function { char folder[200]; if (! menu) return; strncpy0(folder,menu,200); if (strmatch(menu,"ALL")) { m_alldirs(0,0); return; } if (strmatch(menu,E2X("recent images"))) { m_recentfiles(0,0); return; } if (strmatch(menu,E2X("newest images"))) { m_newfiles(0,0); return; } if (strmatch(menu,"HOME")) // user home folder strncpy0(folder,getenv("HOME"),200); if (strmatch(menu,"Desktop")) // user desktop snprintf(folder,200,"%s/%s",getenv("HOME"),desktopname); // locale-specific desktop name if (strmatch(menu,"Fotoxx home")) // fotoxx home folder strncpy0(folder,get_zhomedir(),200); if (strmatch(menu,"Saved Areas")) // saved areas folder snprintf(folder,200,"%s/saved_areas",get_zhomedir()); gallery(folder,"init",0); // new gallery gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // paint return; } // private function - [Album] button: select new album void navi::newalbum(GtkWidget *widget, GdkEventButton *event) { static GtkWidget *popmenu = 0; char **flist, *pp; char findcomm[200]; char *albums[200]; int ii, NF, count = 0; gallery_scroll(-1,0); // stop scrolling snprintf(findcomm,200,"%s/*",albums_folder); // find all album names 20.0 zfind(findcomm,flist,NF); for (ii = 0; ii < NF && count < 200; ii++) { pp = strrchr(flist[ii],'/'); if (! pp) continue; pp++; if (*pp == '.') continue; albums[count] = zstrdup(pp); count++; } for (int ii = 0; ii < NF; ii++) zfree(flist[ii]); zfree(flist); if (! count) { zmessageACK(Mwin,E2X("no albums found")); return; } if (count > 1) // sort album names HeapSort(albums,count); if (popmenu) gtk_widget_destroy(popmenu); popmenu = create_popmenu(); add_popmenu_item(popmenu,E2X("Current Album"),newalbum_menu_event,0,0); // add "current album" for (int ii = 0; ii < count; ii++) // insert all known albums add_popmenu_item(popmenu,albums[ii],newalbum_menu_event,0,0); popup_menu(widget,popmenu); return; } void navi::newalbum_menu_event(GtkWidget *, cchar *menu) // menu event function { char albumfile[200]; if (! menu || strmatch(menu,E2X("Current Album"))) { if (! curr_album || ! *curr_album) { zmessageACK(Mwin,E2X("no current album")); return; } album_show(); return; } snprintf(albumfile,200,"%s/%s",albums_folder,menu); // show the album gallery album_show(albumfile); return; } // private function // dialog to choose gallery sort order and sort the gallery void navi::gallery_sort() { zdialog *zd; int zstat, nn; char albumfile[200], *pp, *pp2; cchar *resetmess = E2X(" Reset all galleries\n to file name ascending"); /*** ________________________________ | Gallery Sort | | | | (o) File Name | | (o) File Mod Date/Time | | (o) Photo Date/Time (EXIF) | | (o) File Size (bytes) | // 19.0 | (o) Image Size (pixels) | | (o) ascending (o) descending | | | | [x] reset all galleries | | to file name ascending | | | | [Apply] | |________________________________| ***/ zd = zdialog_new(E2X("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",E2X("File Name")); zdialog_add_widget(zd,"radio","filedate","vb1",E2X("File Mod Date/Time")); zdialog_add_widget(zd,"radio","photodate","vb1",E2X("Photo Date/Time (EXIF)")); zdialog_add_widget(zd,"radio","filesize","vb1",E2X("File Size (bytes)")); zdialog_add_widget(zd,"radio","pixelsize","vb1",E2X("Image Size (pixels)")); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); zdialog_add_widget(zd,"radio","ascending","hb2",E2X("ascending"),"space=4"); zdialog_add_widget(zd,"radio","descending","hb2",E2X("descending"),"space=2"); zdialog_add_widget(zd,"hbox","hbreset","dialog",0,"space=5"); zdialog_add_widget(zd,"check","reset","hbreset",resetmess,"space=4"); zdialog_stuff(zd,"filename",0); // all buttons off zdialog_stuff(zd,"filedate",0); // GTK radio buttons not reliable zdialog_stuff(zd,"photodate",0); // (vbox works, hbox does not) zdialog_stuff(zd,"filesize",0); zdialog_stuff(zd,"pixelsize",0); zdialog_stuff(zd,"ascending",0); zdialog_stuff(zd,"descending",0); zdialog_stuff(zd,"reset",0); if (gallerysort == FNAME || gallerysort == SNONE) // 19.0 zdialog_stuff(zd,"filename",1); if (gallerysort == FDATE) zdialog_stuff(zd,"filedate",1); if (gallerysort == PDATE) zdialog_stuff(zd,"photodate",1); if (gallerysort == FSIZE) zdialog_stuff(zd,"filesize",1); if (gallerysort == PSIZE) zdialog_stuff(zd,"pixelsize",1); if (galleryseq == ASCEND || galleryseq == QNONE) // 19.0 zdialog_stuff(zd,"ascending",1); if (galleryseq == DESCEND) zdialog_stuff(zd,"descending",1); zdialog_set_modal(zd); zdialog_run(zd,0,"mouse"); // run dialog, wait for completion zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); return; } zdialog_fetch(zd,"filename",nn); // get user sort type if (nn) gallerysort = FNAME; zdialog_fetch(zd,"filedate",nn); if (nn) gallerysort = FDATE; zdialog_fetch(zd,"photodate",nn); if (nn) gallerysort = PDATE; zdialog_fetch(zd,"filesize",nn); if (nn) gallerysort = FSIZE; zdialog_fetch(zd,"pixelsize",nn); if (nn) gallerysort = PSIZE; zdialog_fetch(zd,"ascending",nn); // get ascending/descending if (nn) galleryseq = ASCEND; else galleryseq = DESCEND; zdialog_fetch(zd,"reset",nn); // reset all gallery sort memory if (nn) { // (revert to file name ascending) gallery_memory("reset"); gallerysort = FNAME; galleryseq = ASCEND; } zdialog_free(zd); gallery(0,"sort",-1); // sort the gallery gallery(0,"paint",0); // paint, position = 0 if (gallerytype == ALBUM) // an album was sorted { pp = strrchr(galleryname,'/'); // get album name if (pp) pp++; else pp = galleryname; pp2 = strstr(pp,"-sorted"); // append "-sorted" if (pp2) *pp2 = 0; // avoid "-sorted-sorted" snprintf(albumfile,200,"%s/%s-sorted",albums_folder,pp); album_create_from_gallery(albumfile); // new album, oldname-sorted album_show(albumfile); } return; } // private function // mouse event function for gallery window - get selected thumbnail and file // user function receives clicked file, which is subject for zfree() int navi::mouse_event(GtkWidget *widget, GdkEvent *event, void *) { GdkEventButton *eventB; PIXBUF *pxbT; int evtype, mousex, mousey, mousebutt; int row, col, nrows, tww, thh, marg; int Nth, poswidth, posheight, err, ftype; static int Fmyclick = 0; char *filez = 0; STATB statb; if (! Nfiles) return 1; // empty gallery if (! gallerypainted) return 1; // not initialized eventB = (GdkEventButton *) event; evtype = eventB->type; mousex = int(eventB->x); mousey = int(eventB->y); mousebutt = eventB->button; if (mousex < margin) return 1; if (mousey < margin) return 1; 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; row = (mousey - margin) / thumbH; // find selected row, col col = (mousex - margin) / thumbW; if (thumbsize) { poswidth = (mousex - margin) - thumbW * col; // mouse position within thumbnail poswidth = 100 * poswidth / thumbsize; // 0-100 = left to right edge posheight = (mousey - texthh - margin) - thumbH * row; posheight = 100 * posheight / thumbsize; // 0-100 = top to bottom edge } else poswidth = posheight = 0; if (! xcols) return 1; nrows = 1 + (Nfiles-1) / xcols; // total thumbnail rows, 1 or more if (col < 0 || col >= xcols) return 1; // mouse not on a thumbnail if (row < 0 || row >= nrows) return 1; Nth = xcols * row + col; // mouse at this thumbnail (image file) if (Nth >= Nfiles) return 1; filez = zstrdup(GFlist[Nth].file); // file (thumbnail) at mouse posn. *filez = '/'; if (evtype == GDK_BUTTON_PRESS) { gallery_scroll(-1,0); // stop scrolling Fmyclick = 1; // button press is mine if (drag_file) zfree(drag_file); drag_file = 0; ftype = image_file_type(filez); // save for poss. drag-drop if (ftype == IMAGE || ftype == RAW || ftype == VIDEO) { drag_file = zstrdup(filez); // save file and position in gallery drag_posn = Nth; } goto cleanup; } if (evtype == GDK_BUTTON_RELEASE) { gallery_scroll(-1,0); // stop scrolling if (! Fmyclick) goto cleanup; // ignore unmatched button release Fmyclick = 0; // (from vanished popup window) err = stat(filez,&statb); if (err) goto cleanup; // file is gone? if (S_ISDIR(statb.st_mode)) { // if folder, go there gallery(filez,"init",0); // new gallery gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // paint goto cleanup; } if (clicked_file) zfree(clicked_file); // save clicked file and gallery position clicked_file = zstrdup(filez); clicked_posn = Nth; clicked_width = poswidth; // normalized 0-100 clicked_height = posheight; if (thumbsize) { pxbT = get_cache_thumb(filez,0); // get thumbnail image if (pxbT) { tww = gdk_pixbuf_get_width(pxbT); // thumbnail width and height thh = gdk_pixbuf_get_height(pxbT); marg = (thumbsize - tww) / 4; // allow for margin offset poswidth -= 100 * marg/thumbsize; clicked_width = poswidth * thumbsize / tww; // clicked position is relative clicked_height = posheight * thumbsize / thh; // to thumbnail dimensions if (clicked_width < 0) clicked_width = 0; if (clicked_width > 100) clicked_width = 100; if (clicked_height < 0) clicked_height = 0; if (clicked_height > 100) clicked_height = 100; } } if (mousebutt == 1) { // left click if (zd_gallery_select) gallery_select_Lclick_func(Nth); // send to gallery_select() else if (zd_gallery_select1) gallery_select1_Lclick_func(Nth); // send to gallery_select1() else if (zd_album_update) update_albums_Lclick_func(Nth); // send to album_replacefile() 19.0 else if (zd_edit_bookmarks) edit_bookmarks_Lclick_func(Nth); // send to bookmarks editor else if (zd_ss_imageprefs) ss_imageprefs_Lclick_func(Nth); // send to slide show editor else if (KBshiftkey) popup_image(filez,MWIN,1,0); // popup enlarged image else if (zd_metaview) meta_view(0); // EXIF/IPTC data view window else if (zd_copymove) m_copy_move(0,0); // copy/move dialog else if (zd_rename) m_rename(0,0); // rename dialog else if (zd_permissions) m_permissions(0,0); // permissions dialog 20.0 else if (zd_deltrash) m_delete_trash(0,0); // delete/trash dialog else if (zd_editmeta) m_meta_edit_main(0,0); // edit metadata dialog else if (zd_editanymeta) m_meta_edit_any(0,0); // edit any metadata dialog else if (zd_deletemeta) m_meta_delete(0,0); // delete metadata dialog else gallery_Lclick_func(Nth); // open the file } if (mousebutt == 2) { // middle click ftype = image_file_type(clicked_file); if (ftype == IMAGE) gallery_popimage(); if (ftype == RAW) gallery_popimage(); } if (mousebutt == 3) { // right click if (zd_gallery_select) gallery_select_Rclick_func(Nth); // send to gallery_select() else gallery_Rclick_popup(Nth); // send to gallery thumbnail popup menu } } cleanup: if (filez) zfree(filez); return 0; // must be 0 for drag/drop to work 19.0 } // this function is called if a drag-drop is initiated from the gallery window char * navi::gallery_dragfile() { char dragfile[200]; FILE *fid; snprintf(dragfile,200,"%s/%s",get_zhomedir(),"drag_from_gallery"); // save source gallery name 19.0 fid = fopen(dragfile,"w"); // and position if (! fid) { zmessageACK(Mwin,strerror(errno)); return 0; } fprintf(fid,"%s\n",galleryname); fprintf(fid,"%d\n",drag_posn); fclose(fid); return drag_file; } // this function is called if a drag-drop file is dragged or dropped on the gallery window void navi::gallery_dropfile(int mousex, int mousey, char *file) { int err, top, mpos, speed; int row, col, nrows, Nth, cc; int poswidth, from_posn = -1; char dragfile[200], buff[200]; char *pp1, *pp2, *from_gallery = 0, *newfile; FILE *fid; if (gallerytype != GDIR && gallerytype != ALBUM) return; // reject others (search, recent ...) if (! file) // drag motion underway { if (! mousex) { // drag leave event 19.0 gallery_scroll(-1,0); // stop scroll bugfix return; } top = gtk_adjustment_get_value(Gadjust); // mouse vertical posn in window mpos = 100 * (mousey - top) / xwinH; // 0 - 100 if (mpos < 15 && top > 0) { // mouse position from window top to bottom if (mpos < 0) mpos = 0; // 0 .... 15 .......... 85 .... 100 speed = 200 * (15 - mpos); // corresponding scroll speed gallery_scroll(0,speed); // 4000 ... 200 ... 0 ... 200 ... 4000 } // down down up up if (mpos > 85 && top < maxscroll) { if (mpos >= 100) mpos = 100; speed = 200 * (mpos - 85); gallery_scroll(maxscroll,speed); } if (mpos >= 15 && mpos <= 85) // stop scrolling in mid-window range gallery_scroll(-1,0); return; } gallery_scroll(-1,0); // stop scrolling if (gallerytype == GDIR) // folder gallery, add new file { err = copyFile(file,galleryname); if (err) { zfree(file); zmessageACK(Mwin,strerror(err)); return; } pp1 = strrchr(file+2,'/'); // isolate filename.ext 20.0 if (! pp1) return; cc = strlen(pp1); newfile = zstrdup(galleryname,cc+2); // construct galleryname/filename strcat(newfile,pp1); load_filemeta(file); // update image index save_filemeta(newfile); zfree(file); zfree(newfile); if (curr_file) load_filemeta(curr_file); // restore curr. metadata gallery(0,"init",0); // refresh gallery gallery(0,"sort",-2); // sort, keep position gallery(0,"paint",-1); // paint return; } row = (mousey - margin) / thumbH; // find drop location: row, col col = (mousex - margin) / thumbW; if (col < 0) col = 0; if (col >= xcols) col = xcols-1; if (xcols) nrows = 1 + (Nfiles-1) / xcols; // total thumbnail rows, 1 or more else nrows = 1; if (nrows < 1) nrows = 1; if (row < 0) row = 0; if (row >= nrows) row = nrows-1; if (thumbsize) { poswidth = (mousex - margin) - thumbW * col; // mouse position within thumbnail poswidth = 100 * poswidth / thumbsize; // 0-100 = left to right edge } else poswidth = 0; Nth = xcols * row + col; // mouse at this thumbnail (image file) if (poswidth > 50) Nth++; // drop after this position if (Nth > Nfiles) Nth = Nfiles; // last + 1 snprintf(dragfile,200,"%s/%s",get_zhomedir(),"drag_from_gallery"); // get source gallery name 19.0 fid = fopen(dragfile,"r"); // and position if (! fid) { zmessageACK(Mwin,strerror(errno)); return; } pp1 = fgets_trim(buff,200,fid); // source gallery name if (pp1) from_gallery = zstrdup(pp1); pp2 = fgets_trim(buff,200,fid); // source gallery position if (pp2) from_posn = atoi(pp2); fclose(fid); if (! pp1 || from_posn < 0) { printz("drag_from data not available \n"); return; } if (from_gallery && strmatch(galleryname,from_gallery)) { // drag-drop in same gallery bugfix 19.0 album_movefile(from_posn,Nth); // move file position in gallery 19.0 } else album_addfile(file,Nth); // insert new file at position if (from_gallery) zfree(from_gallery); album_show(); return; } // Private function - respond to keyboard navigation keys. // KBpress() for main window calls this function when G view is active. int navi::KBaction(cchar *action) { int ii; int row1, row2, rowf; gallery_scroll(-1,0); // stop scrolling if (strmatch(action,"Zoom+")) { menufuncx(0,"Zoom+"); return 1; } if (strmatch(action,"Zoom-")) { menufuncx(0,"Zoom-"); return 1; } if (strmatch(action,"Up")) { menufuncx(0,"Row Up"); return 1; } if (strmatch(action,"Down")) { menufuncx(0,"Row Down"); return 1; } if (strmatch(action,"First")) { menufuncx(0,"First"); return 1; } if (strmatch(action,"Last")) { menufuncx(0,"Last"); return 1; } if (strmatch(action,"Page_Up")) { menufuncx(0,"Page Up"); return 1; } if (strmatch(action,"Page_Down")) { menufuncx(0,"Page Down"); return 1; } if (strmatch(action,"Left")) { // left arrow - previous image m_prev(0,0); row1 = scrollposn / thumbH; rowf = curr_file_posn / xcols; if (rowf < row1) menufuncx(0,"Row Up"); return 1; } if (strmatch(action,"Right")) { // right arrow - next image m_next(0,0); row1 = scrollposn / thumbH; row2 = row1 + xwinH / thumbH - 1; rowf = curr_file_posn / xcols; if (rowf == 0) menufuncx(0,"First"); if (rowf > row2) menufuncx(0,"Row Down"); return 1; } if (strmatch(action,"Delete")) { // delete key - delete/trash dialog m_delete_trash(0,0); return 1; } if (strmatch(action,"Show Hidden")) { Fshowhidden = 1 - Fshowhidden; gallery(0,"init",0); gallery(0,"paint",-1); return 1; } // check for menu function KB shortcut for (ii = 0; ii < Nkbsf; ii++) if (strmatchcase(action,kbsftab[ii].menu)) break; if (ii == Nkbsf) { printz("shortcut not found: %s \n",action); return 1; } kbsftab[ii].func(0,kbsftab[ii].arg); // call the menu function return 1; } /********************************************************************************/ // save and restore gallery sort and top file position #define RGmax 100 // max. recent galleries to remember typedef struct { // gallery memory data int galleryposn; // top file position (scroll position) int gallerysort; // sort galleryname/filedate/photodate int galleryseq; // sort ascending/descending char *galleryname; // gallery name /.../filename } gallerymem_t; gallerymem_t gallerymem[RGmax]; // array of gallery memory gallerymem_t Tgallerymem; int NGmem; // current entries <= RGmax void gallery_memory(cchar *action) { FILE *fid; char buff[XFCC], *pp; int ii, nn, err; if (strmatch(action,"reset")) // clear gallery memory data { NGmem = 0; return; } if (strmatch(action,"load")) // load gallery memory from file { // at Fotoxx startup time NGmem = 0; fid = fopen(gallerymem_file,"r"); // open gallery memiry file if (fid) { for (ii = 0; ii < RGmax; ii++) { pp = fgets_trim(buff,XFCC,fid); // read gallery memory record if (! pp) break; // NNNNNN N N /folder/name/... err = convSI(pp,nn,0,999999); // NNNNNN = gallery top file position if (err) break; gallerymem[ii].galleryposn = nn; err = convSI(pp+7,nn,0,3); // N = 0/1/2/3 = sort if (err) break; // none / f.name / f.date / photo-date gallerymem[ii].gallerysort = nn; err = convSI(pp+9,nn,0,2); // N = 0/1/2 = none / ascend / descend if (err) break; gallerymem[ii].galleryseq = nn; pp += 11; // gallery name (folder) if (*pp != '/') break; gallerymem[ii].galleryname = zstrdup(pp); } fclose(fid); NGmem = ii; // memory entry count } return; } if (strmatch(action,"save")) // save gallery memory at shutdown { fid = fopen(gallerymem_file,"w"); if (! fid) return; for (ii = 0; ii < NGmem; ii++) fprintf(fid,"%06d %1d %1d %s\n", gallerymem[ii].galleryposn, gallerymem[ii].gallerysort, gallerymem[ii].galleryseq, gallerymem[ii].galleryname); fclose(fid); return; } if (! galleryname) return; if (strmatch(action,"get")) // get gallery data from memory { for (ii = 0; ii < NGmem; ii++) // search for gallery in memory if (strmatch(galleryname,gallerymem[ii].galleryname)) break; if (ii < NGmem) { galleryposn = gallerymem[ii].galleryposn; // found, restore top file posn and sort gallerysort = (GSORT) gallerymem[ii].gallerysort; galleryseq = (GSEQ) gallerymem[ii].galleryseq; } else { // not found, use defaults if (gallerytype == GDIR) { gallerysort = FNAME; galleryseq = ASCEND; galleryposn = 0; } else { gallerysort = SNONE; galleryseq = QNONE; galleryposn = 0; } } return; } if (strmatch(action,"put")) // save gallery data in memory { if (! galleryname) return; for (ii = 0; ii < NGmem; ii++) // search for gallery in memory if (strmatch(galleryname,gallerymem[ii].galleryname)) break; if (NGmem == 0 || ii == NGmem) // not found { if (NGmem == RGmax) { // gallery memory full zfree(gallerymem[ii-1].galleryname); // remove last entry NGmem--; } for (ii = NGmem; ii > 0; ii--) // push all entries up gallerymem[ii] = gallerymem[ii-1]; // to free entry [0] NGmem++; // one more entry gallerymem[0].galleryname = zstrdup(galleryname); // entry [0] is mine } else if (ii > 0) // found at entry [ii] { Tgallerymem = gallerymem[0]; // exchange entries [0] and [ii] gallerymem[0] = gallerymem[ii]; gallerymem[ii] = Tgallerymem; } gallerymem[0].galleryposn = galleryposn; // update entry [0] gallerymem[0].gallerysort = gallerysort; gallerymem[0].galleryseq = galleryseq; return; } zappcrash("gallery_memory() %s",action); // bad action return; } /********************************************************************************/ // set the window title for the gallery window // window title = gallery name void set_gwin_title() { char *pp, title[200]; if (FGWM != 'G') return; if (gallerytype == GDIR) snprintf(title,200,"Fotoxx FOLDER %s %d folders %d files", galleryname,Nfolders,Nimages); else if (gallerytype == SEARCH || gallerytype == META) snprintf(title,200,"Fotoxx SEARCH RESULTS %d files",Nimages); else if (gallerytype == ALBUM) { pp = strrchr(galleryname,'/'); if (! pp) pp = galleryname; else pp++; snprintf(title,200,"Fotoxx ALBUM %s %d files",pp,Nimages); } else if (gallerytype == RECENT) strcpy(title,"Fotoxx RECENT FILES"); else if (gallerytype == NEWEST) strcpy(title,"Fotoxx NEWEST FILES"); else strcpy(title,"Fotoxx UNKNOWN"); gtk_window_set_title(MWIN,title); return; } /********************************************************************************/ // Return previous or next image file from curr_file in the gallery file list. // If lastver is set, only the last edit version is returned. // (gallery must be sorted by file name (version sequence)). // Returns null if no previous/next file found. // returned file is subject for zfree(). char * prev_next_file(int index, int lastver) // overhauled { char * prev_next_gallery(int index); int Nth; char *rootname1 = 0, *rootname2 = 0; // file names without .vNN and .ext char *file = 0, *filever = 0; Nth = curr_file_posn; if (index == +1) // get next file { while (true) { Nth += 1; if (Nth >= Nfiles) { // no more files this gallery if (filever) break; // return last file version goto retnull; // no more files } if (file) zfree(file); file = gallery(0,"get",Nth); // get next file if (! file) goto retnull; if (! lastver) goto retfile; // return all versions if (! filever) { filever = file; // potential last version file = 0; rootname1 = file_rootname(filever); // save rootname continue; } if (rootname2) zfree(rootname2); rootname2 = file_rootname(file); if (! strmatch(rootname1,rootname2)) break; // new rootname, filever was last version zfree(filever); filever = file; // save last file with same rootname file = 0; } if (file) zfree(file); file = filever; goto retfile; } if (index == -1) // get previous file { if (curr_file) rootname1 = file_rootname(curr_file); // current file rootname while (true) { Nth -= 1; if (Nth < Nfolders) goto retnull; // no more files if (file) zfree(file); file = gallery(0,"get",Nth); // get previous file if (! file) goto retnull; if (! lastver) goto retfile; // return all versions if (! rootname1) goto retfile; // no current file - return previous file if (rootname2) zfree(rootname2); rootname2 = file_rootname(file); if (! strmatch(rootname1,rootname2)) goto retfile; // new rootname, return this file } } retnull: if (file) zfree(file); file = 0; retfile: if (rootname1) zfree(rootname1); if (rootname2) zfree(rootname2); return file; } /********************************************************************************/ // Find the previous or next gallery from the current gallery. // (previous/next defined by subfolder sequence in parent folder) // returned gallery is subject for zfree(). char * prev_next_gallery(int index) // overhauled { int nn, Nth; char *parentdir = 0, *olddir = 0, *newdir = 0, *file = 0; if (gallerytype != GDIR) goto errret; // gallery not a physical folder olddir = zstrdup(galleryname); // olddir = current gallery / folder if (! olddir) goto errret; nn = strlen(olddir) - 1; if (olddir[nn] == '/') olddir[nn] = 0; parentdir = zstrdup(olddir); // get parent folder for (NOP; nn && parentdir[nn] != '/'; nn--) if (! nn) goto errret; parentdir[nn] = 0; gallery(parentdir,"init",0); // gallery = parent for (Nth = 0; Nth < Nfolders; Nth++) { // find olddir in parent if (file) zfree(file); file = gallery(0,"get",Nth); if (! file) goto errret; if (strmatch(file,olddir)) break; } Nth += index; // previous or next folder if (Nth < 0 || Nth >= Nfolders) goto errret; newdir = gallery(0,"get",Nth); if (newdir) goto okret; errret: if (newdir) zfree(newdir); newdir = 0; okret: if (olddir) { gallery(olddir,"init",0); // restore old folder gallery(0,"sort",-2); // recall sort and position zfree(olddir); gallerypainted = 1; // no need to paint } if (parentdir) zfree(parentdir); if (file) zfree(file); return newdir; } /********************************************************************************/ // Get file position in gallery file list. // If Nth position matches file, this is returned. // Otherwise the list is searched from position 0. // Position 0-last is returned if found, or -1 if not. int file_position(cchar *file, int Nth) { int ii; if (! Nimages) return -1; if (! file) return -1; if (Nth >= Nfolders && Nth < Nfiles) if (strmatch(file,GFlist[Nth].file)) return Nth; for (ii = Nfolders; ii < Nfiles; ii++) if (strmatch(file,GFlist[ii].file)) break; if (ii < Nfiles) return ii; return -1; } /********************************************************************************/ // gallery menu, thumbnail view void m_thumbview(GtkWidget *, cchar *menu) // 20.04 { F1_help_topic = "thumb view"; Flistview = 0; if (thumbsize < 256) thumbsize = 256; m_viewmode(0,"G"); gallery(0,"paint",-1); return; } // gallery menu, metadata view void m_metaview(GtkWidget *, cchar *menu) // 20.0 { F1_help_topic = "meta view"; Flistview = 2; m_viewmode(0,"G"); gallery(0,"paint",-1); return; } // gallery menu, list view void m_listview(GtkWidget *, cchar *menu) // 20.0 { F1_help_topic = "list view"; Flistview = 1; m_viewmode(0,"G"); gallery(0,"paint",-1); return; } // Show recently viewed image files. void m_recentfiles(GtkWidget *, cchar *menu) { F1_help_topic = "recent"; if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } navi::gallerytype = RECENT; // gallery type = recent files gallery(recentfiles_file,"initF",0); // generate gallery of recent files gallery(0,"paint",0); m_viewmode(0,"G"); return; } // add a new file to the list of recent files, first position void add_recent_file(cchar *newfile) { int ii; char *pp; linedit_open(recentfiles_file); linedit_put(newfile); // add new file as first in list for (ii = 0; ii < 500; ii++) { // read remaining list, up to 500 pp = linedit_get(); if (! pp) break; if (strmatch(pp,newfile)) continue; // omit new file if elsewhere in list linedit_put(pp); } linedit_close(); return; } /********************************************************************************/ // Report the newest or most recently modified image files, // based on EXIF photo date or file mod date. namespace newfiles { struct newfile_t { // new file record char *file; // image file char fdate[16]; // date-time, yyyymmddhhmmss }; } // menu function void m_newfiles(GtkWidget *, cchar *menu) { using namespace newfiles; int newfile_comp(cchar *rec1, cchar *rec2); cchar *mess = E2X("Use EXIF photo date or \n file modification date?"); F1_help_topic = "newest"; int ii, jj, cc, sort, Nxrec; xxrec_t *xxrec; FILE *fid; newfile_t *newfile = 0; if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } if (Nxxrec == 0) { zmessageACK(Mwin,"no files found"); return; } cc = Nxxrec * sizeof(newfile_t); // allocate memory newfile = (newfile_t *) zmalloc(cc); if (menu && strmatch(menu,"EXIF")) sort = 1; else if (menu && strmatch(menu,"file")) sort = 2; else sort = zdialog_choose(Mwin,"mouse",mess,"EXIF",E2X("File"),null); for (ii = jj = 0; ii < Nxxrec; ii++) // loop image index table { xxrec = xxrec_tab[ii]; newfile[jj].file = xxrec->file; // image file if (sort == 1) { if (strmatch(xxrec->pdate,"null")) continue; // use EXIF photo date 19.13 strncpy0(newfile[jj].fdate,xxrec->pdate,15); // ignore images without photo date } else strncpy0(newfile[jj].fdate,xxrec->fdate,15); // else use file mod date jj++; // selected files } Nxrec = jj; // final count if (Nxrec > 1) // sort index recs. by file date HeapSort((char *) newfile, sizeof(newfile_t), Nxrec, newfile_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",newfile[ii].file); fclose(fid); cleanup: zfree(newfile); // free memory navi::gallerytype = NEWEST; // newest files gallery(searchresults_file,"initF",0); // generate gallery of files gallery(0,"paint",0); m_viewmode(0,"G"); return; } // Compare 2 newfile records by file date-time // return <0 =0 >0 for rec2 < = > rec1 (descending sequence) using namespace newfiles; int newfile_comp(cchar *rec1, cchar *rec2) { char *date1 = ((newfile_t *) rec1)->fdate; char *date2 = ((newfile_t *) rec2)->fdate; return strcmp(date2,date1); } /********************************************************************************/ // popup a new window with a larger image of a clicked thumbnail void gallery_popimage() { static int ftf = 1, ii; static char *popfiles[20]; // last 20 files if (ftf) { ftf = 0; // initz. empty file memory for (ii = 0; ii < 20; ii++) popfiles[ii] = 0; ii = 0; } if (! clicked_file) return; ii++; // use next file memory position if (ii == 20) ii = 0; if (popfiles[ii]) zfree(popfiles[ii]); popfiles[ii] = clicked_file; // save clicked_file persistently clicked_file = 0; // reset clicked_file popup_image(popfiles[ii],MWIN,1,512); // popup window with image return; } /********************************************************************************/ // Determine if a file is a folder or a supported image file type FTYPE image_file_type(cchar *file) { int err, xcc, tcc; static int ftf = 1, tdcc = 0; cchar *ppx; char ppx2[8], *ppt, *RP; STATB statbuf; if (ftf) { if (thumbfolder && *thumbfolder == '/') // thumbnail top folder tdcc = strlen(thumbfolder); myRAWtypes = zstrdup(" "); // clear cache of found file types myVIDEOtypes = zstrdup(" "); ftf = 0; } if (! file) return FNF; RP = f_realpath(file); // use real path 20.0 if (! RP) return FNF; err = stat(RP,&statbuf); // file not found if (err) { zfree(RP); return FNF; } if (S_ISDIR(statbuf.st_mode)) { // folder zfree(RP); return FDIR; } if (! S_ISREG(statbuf.st_mode)) { // not a regular file zfree(RP); return OTHER; } if (tdcc && strmatchN(RP,thumbfolder,tdcc)) { // fotoxx thumbnail zfree(RP); return THUMB; } ppx = strrchr(RP,'.'); if (! ppx) { // no file .ext zfree(RP); return OTHER; } xcc = strlen(ppx); // file .ext > 6 chars. if (xcc > 6) { zfree(RP); return OTHER; } strcpy(ppx2,ppx); // add trailing blank: ".ext " strcpy(ppx2+xcc," "); zfree(RP); if (strcasestr(imagefiletypes,ppx2)) return IMAGE; // supported image type if (strcasestr(myRAWtypes,ppx2))return RAW; // one of my RAW types if (strcasestr(myVIDEOtypes,ppx2)) return VIDEO; // one of my VIDEO types if (strcasestr(RAWfiletypes,ppx2)) { // found in list of known RAW types tcc = strlen(myRAWtypes) + xcc + 2; ppt = (char *) zmalloc(tcc); // add to cache of my RAW types strcpy(ppt,ppx2); strcpy(ppt+xcc+1,myRAWtypes); zfree(myRAWtypes); myRAWtypes = ppt; return RAW; } if (! Ffmpeg) return OTHER; // missing ffmpeg program 20.0 if (strcasestr(VIDEOfiletypes,ppx2)) { // found in known VIDEO types tcc = strlen(myVIDEOtypes) + xcc + 2; ppt = (char *) zmalloc(tcc); // add to cache of my VIDEO types strcpy(ppt,ppx2); strcpy(ppt+xcc+1,myVIDEOtypes); zfree(myVIDEOtypes); myVIDEOtypes = ppt; return VIDEO; } return OTHER; // not a known image file type } /********************************************************************************/ // Given a thumbnail file, get the corresponding image file. // Returns null if no image file found. // Returned file is subject for zfree(). // image file: /image/folder/file.xxx // thumb folder: /thumb/folder // thumb file: /thumb/folder/image/folder/file.xxx.jpeg char * thumb2imagefile(cchar *thumbfile) // simplified { STATB statb; int err; uint cc; char *imagefile; static int Fdone = 0; if (! thumbfolder || *thumbfolder != '/') { if (! Fdone) printz("%s \n","no thumbnail folder"); Fdone++; return 0; } cc = strlen(thumbfolder); if (cc > strlen(thumbfile) - 12) { // /thumbfolder/imagefolder/file.xxx.jpeg printz("invalid thumbfile: %s \n",thumbfile); return 0; } imagefile = zstrdup(thumbfile+cc); // /imagefolder/file.xxx.jpeg cc = strlen(imagefile); imagefile[cc-5] = 0; // /imagefolder/file.xxx err = stat(imagefile,&statb); // check file exists if (! err) return imagefile; // return image file zfree(imagefile); // not found return 0; } // Given an image file, get the corresponding thumbnail file. // The filespec is returned whether or not the file exists. // Returned file is subject for zfree(). char * image2thumbfile(cchar *imagefile) // simplified { int cc1, cc2, err; char *RP, *thumbfile; static int Fdone = 0; STATB statb; if (! thumbfolder || *thumbfolder != '/') { if (! Fdone++) printz("%s \n","no thumbnail folder"); return 0; } RP = f_realpath(imagefile); // use real path 20.0 if (! RP) return 0; err = stat(RP,&statb); if (err) return 0; cc1 = strlen(thumbfolder); cc2 = strlen(RP); thumbfile = (char *) zmalloc(cc1+cc2+6); strcpy(thumbfile,thumbfolder); // /thumb/folder strcpy(thumbfile+cc1,RP); // .../image/folder/file.xxx strcpy(thumbfile+cc1+cc2,".jpeg"); // .../image/folder/file.xxx.jpeg zfree(RP); return thumbfile; } /********************************************************************************/ // check if thumbnail file is missing or stale. // returns 1 new thumbnail not needed // 0 new thumbnail is needed int thumbfile_OK(cchar *imagefile) { int err, ftype; STATB statf, statb; char *thumbfile; err = stat(imagefile,&statf); if (err) return 1; // file does not exist ftype = image_file_type(imagefile); if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) return 1; // not an image file or RAW file thumbfile = image2thumbfile(imagefile); // get thumbnail file for image file if (! thumbfile) return 0; // should not happen err = stat(thumbfile,&statb); // thumbfile exists? if (err) return 0; // no if (statb.st_mtime != statf.st_mtime) return 0; // thumbnail is stale 19.5 return 1; // thumbnail is up to date } /********************************************************************************/ // get "folder" or "broken" pixbuf using cached pixbuf image // caller must avoid gdk_object_unref() of returned pixbuf PIXBUF * get_folder_pixbuf() { static int ftf = 1; static PIXBUF *bigpixbuf = 0; static PIXBUF *pixbuf = 0; static int psize = -1; static char thumbfile[300] = ""; GError *gerror = 0; if (ftf) { ftf = 0; strncatv(thumbfile,300,zfuncs::zimagedir,"/folder.png",0); bigpixbuf = gdk_pixbuf_new_from_file(thumbfile,&gerror); if (! bigpixbuf) { printz("cannot make folder pixbuf: "); if (gerror) printz("%s",gerror->message); printz("\n"); } } if (! bigpixbuf) return 0; if (thumbsize == psize) return pixbuf; pixbuf = gdk_pixbuf_scale_simple(bigpixbuf,thumbsize,thumbsize,BILINEAR); psize = thumbsize; return pixbuf; } PIXBUF * get_broken_pixbuf() { static int ftf = 1; static PIXBUF *bigpixbuf = 0; static PIXBUF *pixbuf = 0; static int psize = -1; static char thumbfile[300] = ""; GError *gerror = 0; if (ftf) { ftf = 0; strncatv(thumbfile,300,zfuncs::zimagedir,"/broken.png",0); bigpixbuf = gdk_pixbuf_new_from_file(thumbfile,&gerror); if (! bigpixbuf) { printz("cannot make broken pixbuf: "); if (gerror) printz("%s",gerror->message); printz("\n"); } } if (! bigpixbuf) return 0; if (thumbsize == psize) return pixbuf; pixbuf = gdk_pixbuf_scale_simple(bigpixbuf,thumbsize,thumbsize,BILINEAR); psize = thumbsize; return pixbuf; } /********************************************************************************/ // create or replace thumbnail file if missing or stale. // returns 0 thumbnail exists already, nothing was done (OK) // 1 new thumbnail file created (OK) // 2 cannot create PXB or make thumbnail file (error) int update_thumbfile(cchar *imagefile) // 19.0 { char *thumbfile = 0, *pp; PXB *imagepxb = 0, *thumbpxb = 0; STATB statb; int err, size, retstat; timeval thumbtimes[2]; if (thumbfile_OK(imagefile)) return 0; // thumbnail file exists thumbfile = image2thumbfile(imagefile); // get thumbnail file for image file if (! thumbfile) { retstat = 2; goto returnx; } pp = strrchr(thumbfile,'/'); // check if thumbnail folder exists *pp = 0; err = stat(thumbfile,&statb); *pp = '/'; if (err) { // no pp = thumbfile; while (true) { pp = strchr(pp+1,'/'); // check each folder level if (! pp) break; *pp = 0; err = stat(thumbfile,&statb); if (err) { err = mkdir(thumbfile,0750); // if missing, try to create if (err && errno != EEXIST) { // this happens (parallel threads) retstat = 2; goto returnx; } } *pp = '/'; } } size = thumbfilesize; pp = (char *) strrchr(imagefile,'.'); // GDK pixbufs no longer used 19.0 if (pp && strcasestr(".jpg .jpeg",pp)) { thumbpxb = JPG_PXB_load(imagefile,size); // load JPEG file to PXB (reduced) if (! thumbpxb) { retstat = 2; goto returnx; } } else { if (image_file_type(imagefile) == RAW) // load RAW image file to PXB imagepxb = RAW_PXB_load_dcraw_half(imagefile); // use dcraw_half for speed 20.0 else imagepxb = PXB_load(imagefile,0); // load other image file to PXB if (! imagepxb) { retstat = 2; goto returnx; } thumbpxb = PXB_resize(imagepxb,size); // resize PXB to thumbnail size if (! thumbpxb) { retstat = 2; goto returnx; } } err = PXB_JPG_save(thumbpxb,thumbfile,80); // save to JPEG thumbnail file if (err) { retstat = 2; goto returnx; } err = stat(imagefile,&statb); // get image file mod time if (err) { retstat = 2; goto returnx; } thumbtimes[0].tv_sec = thumbtimes[1].tv_sec = statb.st_mtim.tv_sec; // thumbnail mod time 19.5 thumbtimes[0].tv_usec = thumbtimes[1].tv_usec = 0; // = image file mod time utimes(thumbfile,thumbtimes); retstat = 1; returnx: if (retstat > 1) printz("update_thumbfile() failure: %s \n",imagefile); if (thumbfile) zfree(thumbfile); if (imagepxb) PXB_free(imagepxb); if (thumbpxb) PXB_free(thumbpxb); return retstat; } /********************************************************************************/ // Remove thumbnail from disk (for deleted or renamed image file). void delete_thumbfile(cchar *imagefile) { int err; STATB statf; char *tpath; tpath = image2thumbfile(imagefile); if (! tpath) return; err = stat(tpath,&statf); // remove from disk if (! err && S_ISREG(statf.st_mode)) remove(tpath); zfree(tpath); return; } /******************************************************************************** Get thumbnail image (pixbuf) for given image file. Use cached thumbnail image if available and not stale. Use thumbnail file if available and not stale. Create or replace thumbnail file if missing or stale. Add thumbnail file to cache if not already there. Return thumbnail pixbuf or null if error. Returned pixbuf is a cache entry (DO NOT g_object_unref()). 3000 x 512 x 400 x 3 = 1.7 GB for 512 pixel thumbnail size. This function is also called in thread preload_thumbs(). thumb file --> get_cache_thumb() <--> thumb cache V --> PIXBUF returned image file --> update_thumbfile() --> thumb file *********************************************************************************/ namespace thumbnail_cache { int ftf = 1; char lockname[200]; int cachesize = 1000; // max thumbnails cached in memory int maxhash = 10 * cachesize; // hash table = 10 * cache size int hashw = 20; // hash search width typedef struct { char *imagefile; PXB *pxb; // PXB with congruent pixbuf time_t mtime; int size; } thumbtab_t; char **filetab; // cached imagefiles int *indextab; // corresp. thumbtab indexes thumbtab_t *thumbtab; // corresp. cached thumbtab int nextcache = 0; } // look ahead of gallery page and preload thumbnails into cache void preload_thumbs_start() // initialize, start the threads 20.0 { void * preload_thumbs_thread(void *); start_detached_thread(preload_thumbs_thread,0); return; } // thread function - add thumbnails to thumbnail cache ahead of need void * preload_thumbs_thread(void *) // thread runs continuously 20.0 { int check_cache_thumb(cchar *imagefile); int ii, jj, err, fnn, size; char *imagefile, *thumbfile; PXB *tempxb, *thumbpxb; static int last_preload_start = 0; while (true) // loop forever { while (preload_trigger == 0) zsleep(0.001); // wait for gallery page change preload_trigger = 0; if (preload_start == last_preload_start) continue; last_preload_start = preload_start; for (ii = 0; ii < 50; ii++) { if (FGWM != 'G') break; // not gallery view fnn = preload_start + ii; // gallery page last file + ii if (fnn > Nfiles-1) break; // beyond last gallery file imagefile = GFlist[fnn].file; if (! imagefile) break; while (! resource_lock(GFlock)) zsleep(0.0001); // gallery is changing jj = check_cache_thumb(imagefile); // thumbnail in cache already? resource_unlock(GFlock); if (jj) continue; // yes err = update_thumbfile(imagefile); // check/create thumbnail file 20.0 if (err > 1) continue; // fail if (thumbsize <= thumbfilesize) { // thumbnail size thumbfile = image2thumbfile(imagefile); // small, use thumbnail file tempxb = JPG_PXB_load(thumbfile); zfree(thumbfile); } else tempxb = JPG_PXB_load(imagefile); // large, use image file if (! tempxb) continue; // fail size = tempxb->ww; // get actual size if (tempxb->hh > size) size = tempxb->hh; if (size == thumbsize) thumbpxb = tempxb; // current thumb size? else { thumbpxb = PXB_resize(tempxb,thumbsize); // no, downsize PXB_free(tempxb); } if (! thumbpxb) continue; while (! resource_lock(GFlock)) zsleep(0.0001); // wait if gallery is locked get_cache_thumb(imagefile,thumbpxb); // add to thumbnail cache resource_unlock(GFlock); } } } // initialize cache at first call void init_cache_thumb() { using namespace thumbnail_cache; int cc; ftf = 0; cc = (maxhash + hashw) * sizeof(char *); // allocate table space and clear filetab = (char **) zmalloc(cc); // (+ hashw to avoid wraparound logic) memset(filetab,0,cc); cc = (maxhash + hashw) * sizeof(int); indextab = (int *) zmalloc(cc); memset(indextab,-1,cc); cc = cachesize * sizeof(thumbtab_t); thumbtab = (thumbtab_t *) zmalloc(cc); memset(thumbtab,0,cc); preload_thumbs_start(); // start preload thumbs threads 20.0 return; } // get thumbnail from cache, or add to cache if missing // use given thumbnail PXB if not null // create new thumbnail file if necessary, create thumbnail PXB PIXBUF * get_cache_thumb(cchar *imagefile, PXB *thumbpxb) { using namespace thumbnail_cache; PXB *tempxb; char *thumbfile, *purgefile; int Fii, Tii, Pii; int ii, err; FTYPE ftype; time_t mtime; STATB statf; if (ftf) init_cache_thumb(); // first call if (thumbsize == 0) return 0; // should not happen err = stat(imagefile,&statf); // check file exists if (err) return 0; mtime = statf.st_mtime; // last modification time ftype = image_file_type(imagefile); if (ftype == FDIR) // 'folder' image return get_folder_pixbuf(); if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) // not an image or RAW or VIDEO file return get_broken_pixbuf(); // 'broken' image while (! resource_lock(TClock)) zsleep(0.001); // lock thumbnail cache Fii = strHash(imagefile,maxhash); // find imagefile in filetab for (ii = 0; ii < hashw; ii++) { if (filetab[Fii+ii] == 0) continue; // empty position if (strmatch(imagefile,filetab[Fii+ii])) break; // found } Tii = -1; // no thumbtab cache position if (ii < hashw) { // found in cache Fii += ii; // filetab entry Tii = indextab[Fii]; // corresp. thumbtab entry if (Tii == -1) goto bug0; // must exist if (! strmatch(imagefile,thumbtab[Tii].imagefile)) goto bug1; // must match if (thumbsize != thumbtab[Tii].size) goto add_to_cache; // not current thumb size if (mtime != thumbtab[Tii].mtime) goto add_to_cache; // thumbtab is stale resource_unlock(TClock); // unlock thumbnail cache return thumbtab[Tii].pxb->pixbuf; // return reference to cache } for (ii = 0; ii < hashw; ii++) // not found in cache if (filetab[Fii+ii] == 0) break; // get first empty position if (ii == hashw) goto bug2; Fii += ii; // use this position for new entry add_to_cache: // not found, not size, or stale if (! thumbpxb) { ii = update_thumbfile(imagefile); // make/refresh thumbnail file if (ii > 1) goto ret0; // fail if (thumbsize <= thumbfilesize) { thumbfile = image2thumbfile(imagefile); // small, use thumbnail file tempxb = JPG_PXB_load(thumbfile); zfree(thumbfile); } else tempxb = JPG_PXB_load(imagefile); // large, use image file if (! tempxb) goto ret0; // fail ii = tempxb->ww; // get actual size if (tempxb->hh > ii) ii = tempxb->hh; if (ii == thumbsize) thumbpxb = tempxb; // current thumbnail size ? else { thumbpxb = PXB_resize(tempxb,thumbsize); // no, resize PXB_free(tempxb); } if (! thumbpxb) goto ret0; // fail } if (Tii == -1) { // add new thumbtab entry nextcache++; // next cache slot (oldest) if (nextcache == cachesize) nextcache = 0; Tii = nextcache; } purgefile = thumbtab[Tii].imagefile; // prior occupant of thumbtab if (purgefile) { PXB_free(thumbtab[Tii].pxb); // free thumbtab entry Pii = strHash(purgefile,maxhash); // find purgefile in filetab for (ii = 0; ii < hashw; ii++) { if (filetab[Pii+ii] == 0) continue; // skip empty position if (strmatch(purgefile,filetab[Pii+ii])) break; // found } if (ii == hashw) goto bug3; // not found Pii += ii; zfree(filetab[Pii]); // free filetab entry filetab[Pii] = 0; indextab[Pii] = -1; // free indextab entry zfree(purgefile); } thumbtab[Tii].imagefile = zstrdup(imagefile); // add thumbnail PXB to cache thumbtab[Tii].pxb = thumbpxb; thumbtab[Tii].size = thumbsize; thumbtab[Tii].mtime = mtime; filetab[Fii] = zstrdup(imagefile); // add filetab and indextab entries indextab[Fii] = Tii; resource_unlock(TClock); // unlock thumbnail cache return thumbtab[Tii].pxb->pixbuf; // return reference to cache ret0: resource_unlock(TClock); // unlock thumbnail cache return 0; bug0: resource_unlock(TClock); // unlock thumbnail cache printz("get_cache_thumb() indextab entry missing \n"); return get_broken_pixbuf(); // return ref. to 'broken' pixbuf bug1: resource_unlock(TClock); printz("get_cache_thumb() indextab thumbtab no match \n"); return get_broken_pixbuf(); bug2: resource_unlock(TClock); printz("get_cache_thumb() hash table failure \n"); return get_broken_pixbuf(); bug3: resource_unlock(TClock); printz("get_cache_thumb() purgefile not in filetab \n"); return get_broken_pixbuf(); } // check if thumbnail and thumbsize is currently in the thumbnail cache // return 0 if not found // return 1 if found, or not needed (not image, not found, folder) // used by preload_thumbs() int check_cache_thumb(cchar *imagefile) { using namespace thumbnail_cache; int Fii, Tii; int ii, err; FTYPE ftype; time_t mtime; STATB statf; if (ftf) init_cache_thumb(); // first call ftype = image_file_type(imagefile); if (ftype == FDIR) return 1; // 'folder' image if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) return 1; // not an image or RAW or VIDEO file err = stat(imagefile,&statf); // check file exists if (err) return 1; mtime = statf.st_mtime; // last modification time Fii = strHash(imagefile,maxhash); // find imagefile in filetab for (ii = 0; ii < hashw; ii++) { if (filetab[Fii+ii] == 0) continue; // skip empty position if (strmatch(imagefile,filetab[Fii+ii])) break; // found } if (ii == hashw) return 0; // not found Fii += ii; // filetab entry Tii = indextab[Fii]; // corresp. thumbtab entry if (Tii == -1) goto bug0; // must exist if (! strmatch(imagefile,thumbtab[Tii].imagefile)) goto bug1; // must match if (thumbsize != thumbtab[Tii].size) return 0; // thumbtab not current size if (mtime != thumbtab[Tii].mtime) return 0; // thumbtab is stale return 1; // thumbtab OK bug0: printz("check_cache_thumb() index tab entry missing \n"); return 0; bug1: printz("check_cache_thumb() indextab thumbtab no match \n"); return 0; } /********************************************************************************/ // select one image file by clicking a gallery thumbnail // returned file is subject for zfree() char * gallery_select1(cchar *gfolder) { zdialog *zd; int zstat; static char filename[XFCC]; char *cfolder; char fgwm[4]; fgwm[0] = FGWM; // save current view mode fgwm[1] = 0; cfolder = navi::galleryname; // and gallery folder 20.0 if (gfolder) { gallery(gfolder,"init",0); // switch to caller's gallery gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // paint } m_viewmode(0,"G"); /*** _________________________________________ | Image File | | | | click on thumbnail to select file | | | | Image File [__________________________] | | | | [Done] [Cancel] | |_________________________________________| ***/ zd = zdialog_new(E2X("Image File"),Mwin,Bdone,Bcancel,null); // dialog to select a thumbnail zd_gallery_select1 = zd; zdialog_add_widget(zd,"label","labtip","dialog",Bclickthumbtoselect); // 20.0 zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labf","hbf",E2X("Image File"),"space=3"); zdialog_add_widget(zd,"zentry","filename","hbf",0,"space=3|expand"); zdialog_resize(zd,400,0); zdialog_run(zd,0,"parent"); // run dialog and wait for completion zstat = zdialog_wait(zd); // via mouse click function below zdialog_fetch(zd,"filename",filename,XFCC); // get selected file zdialog_destroy(zd); zd_gallery_select1 = 0; if (cfolder) { gallery(cfolder,"init",0); // restore view mode gallery(0,"sort",-2); } m_viewmode(0,fgwm); // may paint if (zstat != 1) return 0; // no selection if (*filename != '/') return 0; return f_realpath(filename); // use real path 20.0 } void gallery_select1_Lclick_func(int Nth) // called by gallery mouse function { char *imagefile = 0; int ftype; if (! zd_gallery_select1) return; // should not happen if (Nth < 0) return; imagefile = gallery(0,"get",Nth); // get file at clicked position if (! imagefile) return; ftype = image_file_type(imagefile); // must be image or RAW file if (ftype != IMAGE && ftype != RAW) { zfree(imagefile); return; } zdialog_stuff(zd_gallery_select1,"filename",imagefile); // stuff file name in dialog zfree(imagefile); return; } /********************************************************************************/ // Select multiple image files from thumbnail gallery window. // GSfiles[*]: list of selected image files, GScount entries. // Pre-selected files are passed in the same list, which is updated. // The dialog shows the list of files selected and can be edited. // Returned status: 0 = OK, 1 = user cancel, 2 = internal error namespace galselnames { int dialog_event(zdialog *zd, cchar *event); int find_file(cchar *imagefile); void insert_file(cchar *imagefile); void remove_file(cchar *imagefile); void Xclick_func(int Nth, char LR); void callbackfunc(GtkWidget *textwidget, int line, int posn, int KBkey); void showthumb(); GtkWidget *drawarea = 0; GtkWidget *Ftext = 0; int currline; int Nselect = 0; char *imagefile; }; void gallery_select_clear() { using namespace galselnames; for (int ii = 0; ii < GScount; ii++) zfree(GSfiles[ii]); GScount = 0; return; } int gallery_select() { using namespace galselnames; int ii, kk, dups = 0, yn = 0;; /*** _________________________________________________________________ | Select Image Files | | __________________________________ ________________________ | | | | | | | | | list of selected files | | image of current file | | | | | | selected in file list | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |__________________________________| |________________________| | | | | [Delete] [Remove] [Insert] [Clear] [Add All] | | [Done] [Cancel] | |_________________________________________________________________| ***/ zdialog *zd = zdialog_new(E2X("Select Image Files"),Mwin,Bdone,Bcancel,null); zd_gallery_select = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"expand|space=3"); zdialog_add_widget(zd,"scrwin","scrwin","hb1",0,"expand"); zdialog_add_widget(zd,"text","files","scrwin"); zdialog_add_widget(zd,"frame","fr12","hb1",0,"space=5"); // for thumbnail - added later zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); zdialog_add_widget(zd,"button","delete","hb2",Bdelete,"space=8"); zdialog_add_widget(zd,"button","remove","hb2",Bremove,"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"); zdialog_add_ttip(zd,"delete","delete selected file from list"); zdialog_add_ttip(zd,"remove","remove and save selected file from list"); zdialog_add_ttip(zd,"insert","insert next removed and saved file here"); zdialog_add_ttip(zd,"clear","delete all files in list"); zdialog_add_ttip(zd,"addall","add all gallery files to list"); Ftext = zdialog_widget(zd,"files"); textwidget_set_eventfunc(Ftext,callbackfunc); // set mouse/KB event function GtkWidget *frame = zdialog_widget(zd,"fr12"); // drawing area for thumbnail image drawarea = gtk_drawing_area_new(); gtk_widget_set_size_request(drawarea,256,258); gtk_container_add(GTK_CONTAINER(frame),drawarea); G_SIGNAL(drawarea,"draw",showthumb,0); zdialog_resize(zd,600,0); // start dialog zdialog_run(zd,dialog_event,"save"); // keep relative position textwidget_clear(Ftext); Nselect = 0; currline = -1; imagefile = 0; for (ii = 0; ii < GScount; ii++) // add pre-selected files to dialog { textwidget_append(Ftext,0,"%s\n",GSfiles[ii]); Nselect++; } m_viewmode(0,"G"); // open gallery window if (gallerytype == TNONE) { // if no gallery, start with gallery(topfolders[0],"init",0); // top folder gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // paint } zdialog_wait(zd); // wait for dialog completion if (zd->zstat != 1) { // cancelled zdialog_free(zd); // kill dialog zd_gallery_select = 0; // selected files unchanged return 1; } gallery_select_clear(); // clear gallery_select() file list for (ii = kk = 0; ii < Nselect; ii++) // get list of files from dialog { imagefile = textwidget_line(Ftext,ii,1); if (! imagefile || ! *imagefile) continue; GSfiles[kk] = imagefile; // /.../filename.ext kk++; if (kk == GSmax) { zmessageACK(Mwin,E2X("exceed %d selected files"),GSmax); break; } } GScount = kk; // selected files for (ii = kk = 0; ii < GScount; ii++) // look for duplicates 20.0 for (kk = ii + 1; kk < GScount; kk++) if (strmatch(GSfiles[ii],GSfiles[kk])) dups++; if (dups) yn = zmessageYN(Mwin,E2X("remove %d duplicates?"),dups); if (yn) { for (ii = kk = 0; ii < GScount; ii++) { if (! GSfiles[ii]) continue; for (kk = ii + 1; kk < GScount; kk++) { if (! GSfiles[kk]) continue; if (strmatch(GSfiles[ii],GSfiles[kk])) { zfree(GSfiles[kk]); GSfiles[kk] = 0; } } } for (ii = kk = 0; ii < GScount; ii++) if (GSfiles[ii]) GSfiles[kk++] = GSfiles[ii]; GScount = kk; } zdialog_free(zd); // kill dialog zd_gallery_select = 0; return 0; } // gallery getfiles dialog event function int galselnames::dialog_event(zdialog *zd, cchar *event) { using namespace galselnames; char *file, *ftemp; static char *removedfiles[100]; // last 100 files removed static int Nremoved = 0; int ii, Nth; FTYPE ftype; if (strmatch(event,"delete")) // delete file from list 20.0 { if (! Nselect) return 1; if (currline < 0) return 1; ftemp = textwidget_line(Ftext,currline,0); // file (text line) to remove (keep \n) if (! ftemp) return 1; textwidget_delete(Ftext,currline); // remove from dialog list Nselect--; if (currline > Nselect-1) currline = Nselect - 1; textwidget_highlight_line(Ftext,currline); // next line showthumb(); // show thumbnail } if (strmatch(event,"remove")) // remove file at curr. position { // and save for later insertion if (! Nselect) return 1; if (currline < 0) return 1; if (Nremoved == 100) { // capacity reached zfree(removedfiles[0]); // remove oldest entry for (ii = 0; ii < 99; ii++) removedfiles[ii] = removedfiles[ii+1]; Nremoved = 99; } ftemp = textwidget_line(Ftext,currline,0); // file (text line) to remove (keep \n) if (! ftemp) return 1; removedfiles[Nremoved] = ftemp; // save removed file for poss. insert Nremoved++; textwidget_delete(Ftext,currline); // remove from dialog list Nselect--; if (currline > Nselect-1) currline = Nselect - 1; textwidget_highlight_line(Ftext,currline); // next line showthumb(); // show thumbnail } if (strmatch(event,"insert")) // insert first removed file { // at current position if (! Nremoved) return 1; textwidget_insert(Ftext,0,currline,"%s",removedfiles[0]); Nselect++; currline++; textwidget_highlight_line(Ftext,currline); // next line textwidget_scroll(Ftext,currline); showthumb(); // update thumbnail zfree(removedfiles[0]); // remove file from the removed list for (ii = 0; ii < Nremoved-1; ii++) removedfiles[ii] = removedfiles[ii+1]; Nremoved--; } if (strmatch(event,"clear")) { // clear all files textwidget_clear(Ftext); Nselect = 0; currline = -1; showthumb(); // blank thumbnail } if (strmatch(event,"addall")) // insert all files in image gallery { for (Nth = 0; ; Nth++) { ftemp = gallery(0,"get",Nth); // next file if (! ftemp) break; ftype = image_file_type(ftemp); // must be image or RAW file if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) { zfree(ftemp); continue; } file = f_realpath(ftemp); // use real path 20.0 if (! file) { zfree(ftemp); continue; } textwidget_append(Ftext,0,"%s\n",file); // append - could be insert zfree(ftemp); zfree(file); Nselect++; } textwidget_scroll(Ftext,-1); // scroll to end currline = Nselect - 1; // position at last file showthumb(); } return 1; } // See if image file is in the file list already or not. // Return the last matching line number or -1 if not found. int galselnames::find_file(cchar *imagefile) { using namespace galselnames; int line; char *ftemp; for (line = Nselect-1; line >= 0; line--) { ftemp = textwidget_line(Ftext,line,1); // get file without \n if (! ftemp) continue; if (strmatch(ftemp,imagefile)) { zfree(ftemp); return line; } zfree(ftemp); } return -1; } // add image file to list at current position, set thumbnail = file void galselnames::insert_file(cchar *imagefile) { using namespace galselnames; int ftype; char *RP; ftype = image_file_type(imagefile); // must be image or RAW file if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) return; RP = f_realpath(imagefile); // use real path 20.0 if (! RP) return; textwidget_insert(Ftext,0,currline,"%s\n",RP); zfree(RP); Nselect++; currline++; textwidget_highlight_line(Ftext,currline); textwidget_scroll(Ftext,currline); showthumb(); // update thumbnail return; } // remove image file at last position found, set thumbnail = next // called when gallery thumbnail is right-clicked void galselnames::remove_file(cchar *imagefile) { using namespace galselnames; int line; line = find_file(imagefile); // find last instance if (line < 0) return; currline = line; showthumb(); textwidget_delete(Ftext,currline); Nselect--; if (currline > Nselect-1) currline = Nselect - 1; textwidget_highlight_line(Ftext,currline); showthumb(); return; } // called from image gallery window when a thumbnail is clicked // add image file to list at current position, set thumbnail = file void gallery_select_Lclick_func(int Nth) // left click, add { galselnames::Xclick_func(Nth,'L'); if (clicked_file) zfree(clicked_file); // bugfix 19.2 clicked_file = 0; return; } void gallery_select_Rclick_func(int Nth) // right click, find and remove { galselnames::Xclick_func(Nth,'R'); if (clicked_file) zfree(clicked_file); clicked_file = 0; return; } void galselnames::Xclick_func(int Nth, char LR) { using namespace galselnames; char *imagefile; FTYPE ftype; static int pNth = -1; // previously clicked file int nn, incr; if (! zd_gallery_select) return; // should not happen if (Nth < 0) return; // gallery gone ? imagefile = gallery(0,"get",Nth); // get file at clicked position if (! imagefile) { pNth = -1; return; } ftype = image_file_type(imagefile); // must be image or RAW file if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) { zfree(imagefile); pNth = -1; return; } if (LR == 'R') { // right click, unselect remove_file(imagefile); zfree(imagefile); return; } if (! KBshiftkey) // no shift key { pNth = Nth; // possible start of range insert_file(imagefile); // insert file at current position zfree(imagefile); return; } if (KBshiftkey) // shift key, end of range { if (pNth < 0) return; // no start of range, ignore if (pNth > Nth) incr = -1; // range is descending else incr = +1; // ascending for (nn = pNth+incr; nn != Nth+incr; nn += incr) // add all files from pNth to Nth { // excluding pNth (already added) imagefile = gallery(0,"get",nn); if (! imagefile) continue; ftype = image_file_type(imagefile); // only image and RAW files if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) { zfree(imagefile); continue; } insert_file(imagefile); zfree(imagefile); } pNth = -1; // no prior return; } return; } // process mouse or key click in files window: // set new current position and set thumbnail = clicked file void galselnames::callbackfunc(GtkWidget *widget, int line, int posn, int KBkey) { if (KBkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } if (! Nselect) return; // no lines if (KBkey >= 0xfd00) { if (KBkey == GDK_KEY_Up) currline--; // KB arrow key navigation if (KBkey == GDK_KEY_Down) currline++; if (KBkey == GDK_KEY_Page_Up) currline -= 10; if (KBkey == GDK_KEY_Page_Down) currline += 10; if (KBkey == GDK_KEY_Home) currline = 0; if (KBkey == GDK_KEY_End) currline = Nselect - 1; if (currline < 0) currline = 0; if (currline > Nselect-1) currline = Nselect - 1; textwidget_highlight_line(Ftext,currline); // highlight line textwidget_scroll(Ftext,currline); } if (line >= 0) { // line clicked textwidget_highlight_line(Ftext,line); // highlight currline = line; showthumb(); return; } showthumb(); return; } // show thumbnail for file at current position in select list void galselnames::showthumb() { using namespace galselnames; GdkWindow *gdkwin; char *thumbfile; PIXBUF *thumbpxb = 0; GError *gerror = 0; static draw_context_t draw_context; cairo_t *cr; if (! Nselect) return; if (currline < 0) return; gdkwin = gtk_widget_get_window(drawarea); cr = draw_context_create(gdkwin,draw_context); imagefile = textwidget_line(Ftext,currline,1); // get curr. image without \n if (imagefile) { thumbfile = image2thumbfile(imagefile); // use thumbnail file if (thumbfile) thumbpxb = gdk_pixbuf_new_from_file_at_size(thumbfile,256,256,&gerror); } if (thumbpxb) { cairo_set_source_rgb(cr,1,1,1); // white background cairo_paint(cr); gdk_cairo_set_source_pixbuf(cr,thumbpxb,0,0); // + thumbnail cairo_paint(cr); g_object_unref(thumbpxb); zfree(thumbfile); } else { cairo_set_source_rgb(cr,1,1,1); // white background only cairo_paint(cr); } draw_context_destroy(draw_context); return; } /********************************************************************************/ // menu function to pre-select files for feeding album, batch and script functions void m_select_files(GtkWidget *, cchar *) // 20.0 { char albumfile[200]; FILE *fid; int err, ii; F1_help_topic = "select image files"; err = gallery_select(); if (err) return; if (GScount == 0) return; snprintf(albumfile,200,"%s/selected files",albums_folder); // save search results to album // "selected files" fid = fopen(albumfile,"w"); if (! fid) { zmessageACK(Mwin,strerror(errno)); return; } for (ii = 0; ii < GScount; ii++) fprintf(fid,"%s\n",GSfiles[ii]); fclose(fid); return; } /********************************************************************************/ namespace bookmarknames { #define maxbmks 50 char *bookmarks[maxbmks]; // bookmark names and files int Nbmks; // count of entries int bmkposn; // current entry, 0-last zdialog *zd_bookmark; GtkWidget *textwidget; } void bookmarks_load(); void bookmarks_refresh(); // select a bookmark and jump gallery to selected bookmark thumbnail void m_bookmarks(GtkWidget *, cchar *) { using namespace bookmarknames; int bookmarks_dialog_event(zdialog *zd, cchar *event); void bookmarks_callbackfunc(GtkWidget *, int line, int pos, int kbkey); zdialog *zd; /*** _______________________________________________ | Bookmarks | |-----------------------------------------------| | 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_bookmark) return; zd = zdialog_new(E2X("Bookmarks"),Mwin,E2X("Edit Bookmarks"),null); zd_bookmark = zd; zdialog_add_widget(zd,"frame","frame","dialog",0,"space=5|expand"); zdialog_add_widget(zd,"scrwin","scrwin","frame"); zdialog_add_widget(zd,"text","bmklist","scrwin"); textwidget = zdialog_widget(zd,"bmklist"); // set mouse/KB event function textwidget_set_eventfunc(textwidget,bookmarks_callbackfunc); bookmarks_load(); // get bookmarks from bookmarks file bookmarks_refresh(); // update bookmarks list in dialog zdialog_resize(zd,400,300); zdialog_set_modal(zd); zdialog_run(zd,bookmarks_dialog_event,"mouse"); // run dialog return; } // dialog event and completion function int bookmarks_dialog_event(zdialog *zd, cchar *event) { using namespace bookmarknames; int zstat = zd->zstat; if (! zstat) return 1; // wait for completion zdialog_free(zd); zd_bookmark = 0; if (zstat == 1) m_edit_bookmarks(0,0); // [edit bookmarks] button return 1; } // mouse click function to receive clicked bookmarks void bookmarks_callbackfunc(GtkWidget *, int line, int pos, int kbkey) { using namespace bookmarknames; char *file; int err; STATB sbuff; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } if (checkpend("all")) return; if (! zd_bookmark) return; bmkposn = line; // get clicked line if (bmkposn < 0 || bmkposn > Nbmks-1) return; file = bookmarks[bmkposn] + 32; err = stat(file,&sbuff); if (err) { zmessageACK(Mwin,Bfilenotfound); return; } f_open(file,0); // open bookmarked image file gallery(file,"init",0); // go to gallery and file position gallery(file,"paint",0); m_viewmode(0,"G"); // no close zdialog return; } /********************************************************************************/ // edit bookmarks void m_edit_bookmarks(GtkWidget *, cchar *) { using namespace bookmarknames; int edit_bookmarks_dialog_event(zdialog *zd, cchar *event); void edit_bookmarks_callbackfunc(GtkWidget *, int line, int pos, int kbkey); zdialog *zd; cchar *bmk_add = E2X("Click a list position. Click a gallery thumbnail for the new bookmark.\n" "Bookmark for thumbnail will be added. Change the name and press [Rename]."); /*** _______________________________________________ | 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 (checkpend("all")) return; zd = zdialog_new(E2X("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,"frame","frbmk","dialog",0,"space=5|expand"); zdialog_add_widget(zd,"scrwin","scrwin","frbmk"); zdialog_add_widget(zd,"text","bmklist","scrwin"); zdialog_add_widget(zd,"hbox","hbname","dialog",0,"space=5"); zdialog_add_widget(zd,"zentry","bmkname","hbname",0,"space=5|size=30"); zdialog_add_widget(zd,"button","rename","hbname",Brename,"space=5"); zdialog_add_widget(zd,"button","delete","hbname",Bdelete,"space=5"); textwidget = zdialog_widget(zd,"bmklist"); // set mouse/KB event function textwidget_set_eventfunc(textwidget,edit_bookmarks_callbackfunc); bookmarks_load(); // load bookmarks from bookmarks file bookmarks_refresh(); // update bookmarks list in dialog zdialog_resize(zd,500,400); zdialog_run(zd,edit_bookmarks_dialog_event,"save"); // run dialog, parallel m_viewmode(0,"G"); // show current gallery return; } // load bookmarks list from bookmarks file void bookmarks_load() { using namespace bookmarknames; int err; char buff[XFCC], bmkfile[200]; char *pp, *pp2; FILE *fid; STATB stbuff; Nbmks = 0; err = locale_filespec("user","bookmarks",bmkfile); // read bookmarks file if (! err) { fid = fopen(bmkfile,"r"); if (fid) { while (true) { pp = fgets_trim(buff,XFCC,fid,1); // next bookmark rec. if (! pp) break; if (strlen(pp) < 40) continue; pp2 = strchr(pp+32,'/'); // verify bookmark if (! pp2) continue; err = stat(pp2,&stbuff); if (err) continue; bookmarks[Nbmks] = zstrdup(pp); // fill bookmark list if (++Nbmks == maxbmks) break; } fclose(fid); } } bmkposn = Nbmks; // next free position return; } // mouse click function to select existing bookmark from list void edit_bookmarks_callbackfunc(GtkWidget *, int line, int pos, int kbkey) { using namespace bookmarknames; char bookmarkname[32]; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } if (! zd_edit_bookmarks) return; if (Nbmks < 1) return; if (line < 0) line = 0; if (line > Nbmks-1) line = Nbmks-1; bmkposn = line; strncpy0(bookmarkname,bookmarks[bmkposn],31); strTrim(bookmarkname); zdialog_stuff(zd_edit_bookmarks,"bmkname",bookmarkname); return; } // mouse click function to receive clicked thumbnails for new/revised bookmarks void edit_bookmarks_Lclick_func(int Nth) { using namespace bookmarknames; char *imagefile, *newbookmark; char *pp, bookmarkname[32]; int cc; if (! zd_edit_bookmarks) return; if (Nth < 0) return; // gallery gone ? imagefile = gallery(0,"get",Nth); // get file at clicked position if (! imagefile) return; pp = strrchr(imagefile,'/'); // get file name or last subfolder 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 /folders.../filename snprintf(newbookmark,cc,"%-30s %s",bookmarkname,imagefile); zfree(imagefile); if (Nbmks == maxbmks) { // if list full, remove first zfree(bookmarks[0]); Nbmks--; for (int ii = 0; ii < Nbmks; ii++) bookmarks[ii] = bookmarks[ii+1]; } if (Nbmks == 0) bmkposn = 0; // 1st bookmark --> 0 else bmkposn++; // else clicked position + 1 if (bmkposn < 0) bmkposn = 0; if (bmkposn > Nbmks) bmkposn = Nbmks; for (int ii = Nbmks; ii > bmkposn; ii--) // make hole to insert new bookmark bookmarks[ii] = bookmarks[ii-1]; bookmarks[bmkposn] = newbookmark; // insert Nbmks++; bookmarks_refresh(); // update bookmarks list in dialog zdialog_stuff(zd_edit_bookmarks,"bmkname",bookmarkname); return; } // dialog event and completion function int edit_bookmarks_dialog_event(zdialog *zd, cchar *event) { using namespace bookmarknames; char bmkfile[200]; char bookmarkname[32]; FILE *fid; int cc; if (strmatch(event,"delete")) // delete bookmark at position { if (bmkposn < 0 || bmkposn > Nbmks-1) return 1; for (int ii = bmkposn; ii < Nbmks-1; ii++) bookmarks[ii] = bookmarks[ii+1]; Nbmks--; zdialog_stuff(zd,"bmkname",""); // clear name field bookmarks_refresh(); // update bookmarks list in dialog } if (strmatch(event,"rename")) // apply new name to bookmark { if (bmkposn < 0 || bmkposn > Nbmks-1) return 1; zdialog_fetch(zd,"bmkname",bookmarkname,31); // get name from dialog cc = strlen(bookmarkname); if (cc < 30) memset(bookmarkname+cc,' ',30-cc); // blank pad to 30 chars. bookmarkname[30] = 0; memcpy(bookmarks[bmkposn],bookmarkname,30); // replace name in bookmarks list 19.0 bookmarks_refresh(); // update bookmarks list in dialog } if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) // done { locale_filespec("user","bookmarks",bmkfile); // write bookmarks file fid = fopen(bmkfile,"w"); if (! fid) zmessageACK(Mwin,E2X("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_bookmark) return; textwidget_clear(textwidget); // clear bookmarks list for (int ii = 0; ii < Nbmks; ii++) { // write bookmarks list strncpy0(bookmarkline,bookmarks[ii],31); cc = utf8len(bookmarkline); // compensate multibyte chars. strncat(bookmarkline,blanks,32-cc); strcat(bookmarkline,bookmarks[ii]+32); textwidget_append(textwidget,0,"%s\n",bookmarkline); } return; } /********************************************************************************/ // generate a clickable list of all image folders // show gallery for any folder clicked namespace alldirs { #define maxdirs 10000 int Nlines = 0, Fall = 0; int currline = 0; zdialog *zd; typedef struct { char *name; // /dir1/dir2/.../dirN folder name int Nsubs; // subfolder count 0-N int8 lev; // folder level 0-N, top/sub/sub ... int8 exp; // folder status 0/1 = collapsed/expanded int16 line; // textwidget line -1 = not displayed } dlist_t; dlist_t *dlist; int drecl = sizeof(dlist_t); int compfunc(cchar *rec1, cchar *rec2); void callbackfunc(GtkWidget *, int line, int pos, int kbkey); void writetext(); } // menu function void m_alldirs(GtkWidget *, cchar *) { using namespace alldirs; int ii, jj, cc, pcc, NF, err; char *dir, *pdir, **Flist; F1_help_topic = "all folders"; /*** ________________________________ | All Folders | | ____________________________ | | | | | | | [+] topdir1 | | | | [-] topdir2 | | | | subdir1 | | | | [+] subdir2 | | | | subdir3 | | | | [+] topdir3 | | | | ... | | | |____________________________| | | | | [Done] | |________________________________| ***/ if (dlist) goto report; // already done cc = drecl * maxdirs; dlist = (dlist_t *) zmalloc(cc); // memory for folder list memset(dlist,0,cc); Fall = 0; for (ii = 0; ii < Ntopfolders; ii++) // loop all top image folders { dlist[Fall].name = topfolders[ii]; Fall++; if (Fall == maxdirs) break; err = find_imagefiles(topfolders[ii],8+16,Flist,NF); // folders, all levels if (err) { zmessageACK(Mwin,strerror(errno)); continue; } for (jj = 0; jj < NF; jj++) // add to folders list { dlist[Fall].name = zstrdup(Flist[jj]); Fall++; if (Fall == maxdirs) break; } if (NF) zfree(Flist); if (Fall == maxdirs) break; } if (Fall > 1) HeapSort((char *) dlist,drecl,Fall,compfunc); // sort alphabetically for (ii = 0; ii < Fall; ii++) // loop all folders { dir = dlist[ii].name; // this folder name for (jj = ii-1; jj >= 0; jj--) { // search backwards for parent pdir = dlist[jj].name; // previous folder name pcc = strlen(pdir); if (strmatchN(dir,pdir,pcc) && dir[pcc] == '/') break; // this dir = prev dir + /... } if (jj >= 0) { // parent found dlist[ii].lev = dlist[jj].lev + 1; // level = parent level + 1 dlist[jj].Nsubs++; // add parent subdir count } else dlist[ii].lev = 0; // no parent, level = 0 } for (ii = 0; ii < Fall; ii++) // loop all folders dlist[ii].exp = 0; // expand = no report: zd = popup_report_open("All Folders",Mwin,300,400,0,callbackfunc,0); // open report window writetext(); // write top folders to window currline = 0; // first entry return; } // sort compare function int alldirs::compfunc(cchar *rec1, cchar *rec2) { dlist_t *dir1 = (dlist_t *) rec1; dlist_t *dir2 = (dlist_t *) rec2; int nn; nn = strcasecmp(dir1->name,dir2->name); if (nn) return nn; nn = strcmp(dir1->name,dir2->name); return nn; } // folder list mouse function void alldirs::callbackfunc(GtkWidget *textwidget, int line, int pos, int kbkey) { using namespace alldirs; int ii; char *pline, *pp; static int Fbusy = 0; if (checkpend("all")) return; // check nothing pending if (Fbusy++) return; // stop re-entry if (line < 0) // KB key { if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); goto returnx; } line = currline; if (kbkey == GDK_KEY_Up) line--; // KB arrow key navigation if (kbkey == GDK_KEY_Down) line++; if (line < 0) line = 0; if (line > Nlines-1) line = Nlines - 1; } pline = textwidget_line(textwidget,line,1); // textwidget line: ... [x] dirname if (! pline || ! *pline) goto returnx; textwidget_highlight_line(textwidget,line); textwidget_scroll(textwidget,line); currline = line; for (ii = 0; ii < Fall; ii++) // find dlist[] rec corresponding if (line == dlist[ii].line) break; // to textwidget line clicked if (ii == Fall) goto returnx; if (kbkey == GDK_KEY_Return) { // Enter key, look for [+] or [-] pp = strchr(pline,'['); if (pp) pos = pp - pline; // simulate click on [*] } if (pos > 0) // clicked position { pp = strchr(pline,'['); if (pp) { // [+] or [-] button present if (pos < (pp-pline)) goto returnx; // click position before button if (pos >= (pp-pline) && pos <= (pp+2-pline)) { // on the button if (pp[1] == '+') dlist[ii].exp = 1; // set expand flag if (pp[1] == '-') dlist[ii].exp = 0; writetext(); // refresh textwidget textwidget_highlight_line(textwidget,line); textwidget_scroll(textwidget,line); goto returnx; } } } m_viewmode(0,"G"); // 19.0 gallery(dlist[ii].name,"init",0); // folder name clicked gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // show gallery returnx: Fbusy = 0; return; } // write all visible folders to text window void alldirs::writetext() { using namespace alldirs; int ii = 0, jj, line = 0; char *pp, indent[100]; cchar *expbutt; memset(indent,' ',100); popup_report_clear(zd); while (ii < Fall) // loop all folders { jj = dlist[ii].lev * 4; // indent 4 blanks per folder level if (jj > 99) jj = 99; indent[jj] = 0; if (dlist[ii].Nsubs == 0) expbutt = " "; // no subdirs, no expand button else if (dlist[ii].exp) expbutt = "[-]"; // prepare [+] or [-] else expbutt = "[+]"; pp = strrchr(dlist[ii].name,'/'); if (! pp) continue; popup_report_write(zd,0,"%s %s %s \n",indent,expbutt,pp+1); // ... [x] dirname indent[jj] = ' '; dlist[ii].line = line; // text line for this folder line++; if (dlist[ii].exp) { // if folder expanded, continue ii++; continue; } for (jj = ii + 1; jj < Fall; jj++) { // not expanded, find next folder if (dlist[jj].lev <= dlist[ii].lev) break; // at same or lower level dlist[jj].line = -1; // no text line for skipped folders } ii = jj; } Nlines = line; return; } /********************************************************************************/ // set the gallery from the current image file physical folder void m_source_folder(GtkWidget*, cchar *menu) { F1_help_topic = "source folder"; if (! curr_file) return; gallery(curr_file,"init",0); // new gallery gallery(curr_file,"paint",0); // position at curr. file curr_file_posn = file_position(curr_file,0); // file position in gallery list m_viewmode(0,"G"); return; } /********************************************************************************/ // dummy menu entry as target for "Show Hidden Files" KB shortcut void m_show_hidden(GtkWidget *, cchar *) { KBaction("Show Hidden"); return; } /********************************************************************************/ // dummy menu entry as target for "Current Album" KB shortcut void m_current_album(GtkWidget *, cchar *) // 20.0 { navi::newalbum_menu_event(0,0); return; } fotoxx-20.08/f.mashup.cc000066400000000000000000006104221362435004500151110ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* Fotoxx image edit functions - Mashup and Montage functions m_mashup combine images and text into custom layout m_montage combine images into a matrix of images *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /******************************************************************************** MASHUP Define a layout/background image. Add images, text, and lines as overlays on top of layout. Overlays have adjustable position, angle, size, transparency. *********************************************************************************/ namespace mashup { char projectname[100]; // project name char projectfile[200]; // /.../projectname char layoutfile[XFCC]; // background/layout image file char tranfile[220]; // /.../projectname_trandata char warpfile[220]; // /.../projectname_warpdata int Lww, Lhh; // layout image size int flatRGB[3]; // flat layout RGB values (no file) PXB *Lpxb; // layout image PXB int Nimage; // overlay image count int Ntext; // overlay text count int Nline; // overlay line count int updxlo, updxhi, updylo, updyhi; // update region in layout image float PRscale; // project scale for editing int Fupdall; // flag, update entire layout int Fupdatebusy; // window update busy cchar *focus; // "image" "text" "line" "" int focusii; // image[ii] text[ii] line[ii] or -1 int Fnextimage = 0; // flag, image selected with [next] int Mradius = 50; // paint transparency mouse radius int Mpowcen = 10; // paint transparency mouse center power int Mpowedge = 0; // paint transparency mouse edge power int Mgradual = 1; // paint transparency gradual or instant zdialog *zdimage; // image edit dialog active zdialog *zdtext; // text edit dialog active zdialog *zdaddtext; // add text dialog active zdialog *zdline; // line edit dialog active zdialog *zdaddline; // add line dialog active zdialog *zdtransp; // paint transparencies dialog active zdialog *zdwarp; // warp image dialog active struct image_t { // overlay image data char *file; // filespec PXB *pxb1; // image PXB, 1x size PXB *pxb2; // image PXB, scaled size float scale; // pxb2 scale, 0.0 to 1.0 = 1x float theta; // angle, -PI to +PI radians float sinT, cosT; // trig values for theta int ww1, hh1; // image size at 1x scale int ww2, hh2; // image size at curr. scale int px0, py0; // image position in layout image int pxlo, pxhi, pylo, pyhi; // image extent in layout image space int Lmarg, Rmarg, Tmarg, Bmarg; // left/right/top/bottom hard margins int Lblend, Rblend, Tblend, Bblend; // left/right/top/bottom blended margins float Btransp; // base image transparency 0...1 int vtrancc; // var. transparency map cc uint8 *vtranmap; // var. transparency map int Nwarp; // warpmem count float warpmem[200][5]; // warp undo memory, last 200 warps int warpcc; // warpx/warpy data length float *warpx, *warpy; // aggregate pixel warps }; struct text_t { // overlay text data textattr_t attr; // text, font, angle, outline ... int ww, hh; // image dimensions in layout image int px0, py0; // image position in layout image int pxlo, pxhi, pylo, pyhi; // extent of text image in layout image }; struct line_t { // overlay line/arrow data lineattr_t attr; // attributes (length, width, arrowheads) int ww, hh; // image dimensions in layout image int px0, py0; // image position in layout image int pxlo, pxhi, pylo, pyhi; // extent of line image in layout image }; #define maxmash 50 // max. images, text, lines image_t image[maxmash]; text_t text[maxmash]; line_t line[maxmash]; void project(void); int project_dialog_event(zdialog *zd, cchar *event); int project_open(void); void project_save(void); void project_rescale(float scale); void image_edit(void); int image_dialog_event(zdialog *zd, cchar *event); void image_rescale(int ii, float scale2); void paintransp_dialog(void); int paintransp_dialog_event(zdialog *zd, cchar *event); void paintransp_mousefunc(void); void warp_dialog(void); int warp_dialog_event(zdialog *zd, cchar *event); void warp_mousefunc(void); void warp_warpfunc(void); void * warp_warpfunc_wthread(void *arg); void transparent_margins(void); void add_image(void); void remove_image(int ii); void flashimage(void); void text_edit(void); int text_dialog_event(zdialog *zd, cchar *event); void add_text(void); void remove_text(int ii); void flashtext(void); void line_edit(void); int line_dialog_event(zdialog *zd, cchar *event); void add_line(void); void remove_line(int ii); void flashline(void); void select(cchar *which, int ii); void setlayoutupdatearea(void); void mousefunc_layout(void); void KBfunc(int key); void Lupdate(void); void * Lupdate_thread(void *); void * Lupdate_wthread(void *arg); } /********************************************************************************/ // menu function void m_mashup(GtkWidget *, cchar *) { using namespace mashup; zdialog *zd; int zstat, nn, err, ww, hh; char color[20], *file; cchar *choice = 0, *ppc; int px, py; uint8 *pix; F1_help_topic = "mashup"; if (FGWM != 'F' && FGWM != 'G') return; // 19.0 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 /*** _________________________________________ | | | Project name [_______________________] | // 20.0 | | | 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,"hbox","hbpn","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labpn","hbpn",E2X("Project name"),"space=3"); zdialog_add_widget(zd,"zentry","projectname","hbpn","mashup","space=3|expand"); zdialog_add_widget(zd,"hbox","space","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbl","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labtitle","hbl",E2X("Layout and background image"),"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",E2X("choose an image file")); zdialog_add_widget(zd,"radio","usecurrent","vbopt",E2X("use current image file")); zdialog_add_widget(zd,"radio","makelayout","vbopt",E2X("specify layout size and color")); zdialog_add_widget(zd,"radio","openproject","vbopt",E2X("open a Mashup project file")); zdialog_stuff(zd,"choosefile",1); zdialog_run(zd,0,"save"); // run dialog, wait for completion zstat = zdialog_wait(zd); if (zstat != 1) { // canceled zdialog_free(zd); Fblock = 0; return; } zdialog_fetch(zd,"projectname",projectname,100); // get project name 20.0 strTrim2(projectname); if (*projectname <= ' ') { zmessageACK(Mwin,E2X("enter a project name")); zdialog_free(zd); goto try_again; } zdialog_fetch(zd,"choosefile",nn); if (nn) choice = "choosefile"; zdialog_fetch(zd,"usecurrent",nn); if (nn) choice = "usecurrent"; zdialog_fetch(zd,"makelayout",nn); if (nn) choice = "makelayout"; zdialog_fetch(zd,"openproject",nn); if (nn) choice = "openproject"; zdialog_free(zd); if (strmatch(choice,"choosefile")) // open new curr. file >> layout { file = gallery_select1(curr_file); 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); project(); // start project return; } if (strmatch(choice,"usecurrent")) { // curr. file >> layout if (! curr_file) { zmessageACK(Mwin,E2X("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); project(); // start project return; } if (strmatch(choice,"makelayout")) // create flat layout image { /*** _______________________________________________ | Make Layout Image | | | | width [____] height [____] (pixels) | | color [____] | | [done] [cancel] | |_______________________________________________| ***/ zd = zdialog_new(E2X("Make Layout Image"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbz","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labw","hbz",Bwidth,"space=5"); zdialog_add_widget(zd,"zspin","width","hbz","1000|20000|1|2000"); // limit 20K x 10K = 200 megapixel PXB zdialog_add_widget(zd,"label","space","hbz",0,"space=5"); // (x 12 = 2.4 GB for PXM image) zdialog_add_widget(zd,"label","labh","hbz",Bheight,"space=5"); zdialog_add_widget(zd,"zspin","height","hbz","600|10000|1|1200"); zdialog_add_widget(zd,"label","labp","hbz","(pixels)","space=3"); zdialog_add_widget(zd,"hbox","hbc","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labc","hbc",Bcolor,"space=5"); zdialog_add_widget(zd,"colorbutt","color","hbc","200|200|200"); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,null,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion if (zstat != 1) { // cancel zdialog_free(zd); goto try_again; } zdialog_fetch(zd,"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,3); // 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; // restart mashup m_mashup(0,0); return; } // overall steering dialog - edit dialogs return to this dialog void mashup::project() { using namespace mashup; zdialog *zd; free_resources(); // no E1/E3 etc. Fmashup = 1; // flag for KBpress() func Fpxb = PXB_copy(Lpxb); // initz. window update image m_viewmode(0,"F"); // force file view PRscale = 1.0; select("",-1); // nothing selected Fupdall = 1; Lupdate(); // update layout composite image /*** ____________________________________________ | Mashup | | | | [Edit Images] add or edit images | | [Edit Text] add or edit text | | [Edit Line] add or edit lines/arrows | | [Rescale] change project scale | | [Done] project complete | | [Cancel] cancel project | |____________________________________________| ***/ zd = zdialog_new("Mashup",Mwin,null); zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=3|homog"); zdialog_add_widget(zd,"button","editimage","vb1",E2X("Edit Images"),"space=3"); zdialog_add_widget(zd,"button","edittext","vb1",E2X("Edit Text"),"space=3"); zdialog_add_widget(zd,"button","editline","vb1",E2X("Edit Line"),"space=3"); zdialog_add_widget(zd,"button","rescale","vb1",E2X("Rescale"),"space=3"); zdialog_add_widget(zd,"button","done","vb1",Bdone,"space=3"); zdialog_add_widget(zd,"button","cancel","vb1",Bcancel,"space=3"); zdialog_add_widget(zd,"hbox","hbedim","vb2"); zdialog_add_widget(zd,"label","labedim","hbedim",E2X("add or edit images"),"space=3"); zdialog_add_widget(zd,"hbox","hbedtx","vb2"); zdialog_add_widget(zd,"label","labedtx","hbedtx",E2X("add or edit text"),"space=3"); zdialog_add_widget(zd,"hbox","hbedln","vb2"); zdialog_add_widget(zd,"label","labedln","hbedln",E2X("add or edit lines/arrows"),"space=3"); zdialog_add_widget(zd,"hbox","hbres","vb2"); zdialog_add_widget(zd,"label","labscale","hbres",E2X("change project scale"),"space=3"); zdialog_add_widget(zd,"hbox","hbdone","vb2"); zdialog_add_widget(zd,"label","labdone","hbdone",E2X("project complete"),"space=3"); zdialog_add_widget(zd,"hbox","hbcan","vb2"); zdialog_add_widget(zd,"label","labcancel","hbcan",E2X("cancel project"),"space=3"); zdialog_run(zd,project_dialog_event,"save"); // start dialog takeMouse(mousefunc_layout,0); // capture mouse click/drag return; } // project dialog event and completion function int mashup::project_dialog_event(zdialog *zd, cchar *event) { using namespace mashup; int ii, cc, yn; GError *gerror = 0; char *file = 0, *file2, *pp; cchar *rescalemess = E2X("rescale project"); if (zd->zstat) event = "cancel"; // 20.0 if (strmatch(event,"focus")) { takeMouse(mousefunc_layout,0); return 1; } if (strmatch(event,"editimage")) { zdialog_show(zd,0); image_edit(); // start edit images dialog zdialog_show(zd,1); return 1; } if (strmatch(event,"edittext")) { zdialog_show(zd,0); text_edit(); // start edit text dialog zdialog_show(zd,1); return 1; } if (strmatch(event,"editline")) { zdialog_show(zd,0); line_edit(); // start edit line dialog zdialog_show(zd,1); return 1; } if (strmatch(event,"rescale")) { // rescale project bigger, or reset ii = zdialog_choose(Mwin,"mouse",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 (navi::gallerytype == GDIR) { // 20.0 file2 = zstrdup(navi::galleryname,108); cc = strlen(file2); strncatv(file2,cc+108,"/",projectname,".png",null); // gallery/projectname.png } else { file2 = zstrdup(projectname,6); // projectname.png strcat(file2,".png"); } file = zgetfile(E2X("save Mashup output file"),MWIN,"save",file2); zfree(file2); if (file) { pp = strrchr(file,'.'); // force .png extension if (! pp || strlen(pp) < 4) { pp = zstrdup(file,6); zfree(file); file = pp; pp = strrchr(file,'.'); if (! pp || strlen(pp) > 5) pp = file + strlen(file); } strcpy(pp,".png"); ii = gdk_pixbuf_save(Fpxb->pixbuf,file,"png",&gerror,"compression","1",null); if (! ii) { zmessageACK(Mwin,gerror->message); zfree(file); file = 0; } } yn = zmessageYN(Mwin,E2X("save Mashup project file?")); // offer to save project file if (yn) project_save(); event = "cleanup"; } if (strmatch(event,"cancel")) // project canceled { yn = zmessageYN(Mwin,"cancel project?"); // 20.0 if (! yn) return 1; zdialog_free(zd); // kill dialog freeMouse(); if (*projectfile) { yn = zmessageYN(Mwin,E2X("delete Mashup project file?")); // offer to delete project file if (yn) { remove(projectfile); if (*tranfile) remove(tranfile); if (*warpfile) remove(warpfile); } } m_viewmode(0,"G"); // 20.0 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(E2X("Open Project"),MWIN,"file",mashup_folder); // 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,E2X("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,3); // 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,E2X("overlay image file missing: \n %s"),image[ii].file); goto cleanupErr; } pxbtemp = PXB_load(image[ii].file,1); // make overlay image if (! pxbtemp) goto cleanupErr; PXB_addalpha(pxbtemp); // add alpha channel if missing image[ii].pxb1 = pxbtemp; image[ii].ww1 = pxbtemp->ww; image[ii].hh1 = pxbtemp->hh; continue; } ii = Nimage - 1; // current image if (strmatchN(pp,"position:",9)) { // position: xxx yyy nn = sscanf(pp,"position: %d %d",&image[ii].px0,&image[ii].py0); if (nn != 2) { printz("position rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"scale:",6)) { // scale: n.nnn nn = sscanf(pp,"scale: %f",&image[ii].scale); if (nn != 1) { printz("scale rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"theta:",6)) { // theta: n.nnn nn = sscanf(pp,"theta: %f",&image[ii].theta); if (nn != 1) { printz("theta rec. \n"); goto badproject; } image[ii].sinT = sin(image[ii].theta); image[ii].cosT = cos(image[ii].theta); continue; } if (strmatchN(pp,"Btransp:",8)) { // Btransp: 0.nnn nn = sscanf(pp,"Btransp: %f",&image[ii].Btransp); if (nn != 1) { printz("Btransp rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Lmarg:",6)) { // Lmarg: nnn nn = sscanf(pp,"Lmarg: %d",&image[ii].Lmarg); if (nn != 1) { printz("Lmarg rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Rmarg:",6)) { // Rmarg: nnn nn = sscanf(pp,"Rmarg: %d",&image[ii].Rmarg); if (nn != 1) { printz("Rmarg rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Tmarg:",6)) { // Tmarg: nnn nn = sscanf(pp,"Tmarg: %d",&image[ii].Tmarg); if (nn != 1) { printz("Tmarg rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Bmarg:",6)) { // Bmarg: nnn nn = sscanf(pp,"Bmarg: %d",&image[ii].Bmarg); if (nn != 1) { printz("Bmarg rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Lblend:",7)) { // Lblend: nnn nn = sscanf(pp,"Lblend: %d",&image[ii].Lblend); if (nn != 1) { printz("Lblend rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Rblend:",7)) { // Rblend: nnn nn = sscanf(pp,"Rblend: %d",&image[ii].Rblend); if (nn != 1) { printz("Rblend rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Tblend:",7)) { // Tblend: nnn nn = sscanf(pp,"Tblend: %d",&image[ii].Tblend); if (nn != 1) { printz("Tblend rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Bblend:",7)) { // Bblend: nnn nn = sscanf(pp,"Bblend: %d",&image[ii].Bblend); if (nn != 1) { printz("Bblend rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Nwarp:",6)) { // Nwarp: nn nn = sscanf(pp,"Nwarp: %d",&image[ii].Nwarp); if (nn != 1) { printz("Nwarp rec. \n"); goto badproject; } if (image[ii].Nwarp > 200) { printz("Nwarp rec. \n"); goto badproject; } for (kk = 0; kk < image[ii].Nwarp; kk++) { pp = fgets_trim(buff,XFCC,fid); // n.nnn n.nnn n.nnn n.nnn n.nnn if (! pp) break; // (loop for Nwarp records) nn = sscanf(pp,"%f %f %f %f %f",&image[ii].warpmem[kk][0], &image[ii].warpmem[kk][1],&image[ii].warpmem[kk][2], &image[ii].warpmem[kk][3],&image[ii].warpmem[kk][4]); if (nn != 5) { printz("warpmem rec. \n"); goto badproject; } } } ww1 = image[ii].ww1; hh1 = image[ii].hh1; if (strmatchN(pp,"vtrancc:",8)) { // vtrancc: nnnn nn = sscanf(pp,"vtrancc: %d",&vcc); if (nn != 1) { printz("vtrancc rec. \n"); goto badproject; } if (vcc && vcc != ww1 * hh1) { printz("vtrancc rec. \n"); goto badproject; } image[ii].vtrancc = vcc; continue; } if (strmatchN(pp,"warpcc:",7)) { // warpcc: nnnn nn = sscanf(pp,"warpcc: %d",&wcc); if (nn != 1) { printz("warpcc rec. \n"); goto badproject; } if (wcc && wcc != (int) (ww1 * hh1 * sizeof(float))) { printz("warpcc rec. \n"); goto badproject; } image[ii].warpcc = wcc; continue; } } // read any number of text records while (true) { pp = fgets_trim(buff,XFCC,fid); if (! pp) break; if (strmatchN(pp,"end",3)) break; if (strmatchN(pp,"text:",5)) // text: this is my text ... { ii = Ntext++; txattr = &text[ii].attr; pp += 5; while (*pp == ' ') pp++; repl_Nstrs(pp,txattr->text,"\\n","\n",null); // replace "\n" with newline continue; } ii = Ntext - 1; // current text if (strmatchN(pp,"position:",9)) { // position: xx yy nn = sscanf(pp,"position: %d %d",&text[ii].px0,&text[ii].py0); if (nn != 2) { printz("position rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"font:",5)) { // font: free sans size: nn pp += 5; while (*pp == ' ') pp++; pp2 = strstr(pp,"size:"); if (! pp2) { printz("font rec. \n"); goto badproject; } *pp2 = 0; pp2 += 5; strncpy0(txattr->font,pp,80); txattr->size = atoi(pp2); if (txattr->size < 8) { printz("font rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"attributes:",11)) { // attributes txattr = &text[ii].attr; nn = sscanf(pp,"attributes: %f %s %s %s %s %d %d %d %d %d %d %d", &txattr->angle, txattr->color[0], txattr->color[1], txattr->color[2], txattr->color[3], &txattr->transp[0], &txattr->transp[1], &txattr->transp[2], &txattr->transp[3], &txattr->towidth, &txattr->shwidth, &txattr->shangle); if (nn != 12) { printz("attributes rec. \n"); goto badproject; } } } // read any number of line/arrow records while (true) { pp = fgets_trim(buff,XFCC,fid); if (! pp) break; if (strmatchN(pp,"end",3)) break; if (strmatchN(pp,"line:",5)) { // line: ii = Nline++; continue; } ii = Nline - 1; // current line if (strmatchN(pp,"position:",9)) { // position: xx yy nn = sscanf(pp,"position: %d %d",&line[ii].px0,&line[ii].py0); if (nn != 2) { printz("position rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"attributes:",11)) { // attributes: lnattr = &line[ii].attr; nn = sscanf(pp,"attributes: %d %d %d %d %f %s %s %s %s %d %d %d %d %d %d %d", &lnattr->length, &lnattr->width, &lnattr->larrow, &lnattr->rarrow, &lnattr->angle, lnattr->color[0], lnattr->color[1], lnattr->color[2], lnattr->color[3], &lnattr->transp[0], &lnattr->transp[1], &lnattr->transp[2], &lnattr->transp[3], &lnattr->towidth, &lnattr->shwidth, &lnattr->shangle); if (nn != 16) { printz("attributes rec. \n"); goto badproject; } } } fclose(fid); fid = 0; // read binary transparency file if any for (vcc = ii = 0; ii < Nimage; ii++) // sum transparency data vcc += image[ii].vtrancc; if (vcc) { nfid = open(tranfile,O_RDONLY); if (nfid < 0) goto failure; for (ii = 0; ii < Nimage; ii++) // read transparency data { vcc = image[ii].vtrancc; if (vcc) { image[ii].vtranmap = (uint8 *) zmalloc(vcc); cc = read(nfid,image[ii].vtranmap,vcc); if (cc != vcc) { printz("vtranmap data \n"); goto badproject; } } else image[ii].vtranmap = 0; } close(nfid); nfid = -1; } // read binary warp data file if any for (wcc = ii = 0; ii < Nimage; ii++) // sum warp data wcc += image[ii].warpcc; if (wcc) { nfid = open(warpfile,O_RDONLY); if (nfid < 0) goto failure; for (ii = 0; ii < Nimage; ii++) // read warp data { wcc = image[ii].warpcc; if (wcc) { image[ii].warpx = (float *) zmalloc(wcc); cc = read(nfid,image[ii].warpx,wcc); if (cc != wcc) { printz("warpx data \n"); goto badproject; } image[ii].warpy = (float *) zmalloc(wcc); cc = read(nfid,image[ii].warpy,wcc); if (cc != wcc) { printz("warpy data \n"); goto badproject; } } else image[ii].warpx = image[ii].warpy = 0; } close(nfid); nfid = -1; } // generate overlay images for (ii = 0; ii < Nimage; ii++) { image_rescale(ii,image[ii].scale); // scaled and warped image select("image",ii); setlayoutupdatearea(); // set layout update area } // generate text images for (ii = 0; ii < Ntext; ii++) { gentext(&text[ii].attr); // make image from text and attributes text[ii].ww = text[ii].attr.pxb_text->ww; // text image size in layout text[ii].hh = text[ii].attr.pxb_text->hh; select("text",ii); setlayoutupdatearea(); // set layout update area } // generate line/arrow images for (ii = 0; ii < Nline; ii++) // load overlay line/arrow images { genline(&line[ii].attr); // make image from line attributes line[ii].ww = line[ii].attr.pxb_line->ww; // line image size in layout line[ii].hh = line[ii].attr.pxb_line->hh; select("line",ii); setlayoutupdatearea(); // set layout update area } m_viewmode(0,"F"); 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,E2X("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_folder,projectname); pp = zgetfile(E2X("Save Project"),MWIN,"save",projectfile); // get project name from user if (! pp) return; strncpy0(projectfile,pp,200); // project file strncpy0(tranfile,pp,200); strcat(tranfile,"_trandata"); // corresp. transparency data file strncpy0(warpfile,pp,200); strcat(warpfile,"_warpdata"); // corresp. warp data file zfree(pp); fid = fopen(projectfile,"w"); if (! fid) goto failure; if (*layoutfile) fprintf(fid,"layout: %s\n",layoutfile); // write layout file else fprintf(fid,"flatlayout: %d %d RGB: %d/%d/%d \n", // write flat layout attributes Lww,Lhh,flatRGB[0],flatRGB[1],flatRGB[2]); for (ii = 0; ii < Nimage; ii++) // write image data { fprintf(fid,"image: %s\n",image[ii].file); fprintf(fid,"position: %d %d \n", image[ii].px0, image[ii].py0); fprintf(fid,"scale: %.4f \n",image[ii].scale); fprintf(fid,"theta: %.4f \n",image[ii].theta); fprintf(fid,"Btransp: %.4f \n",image[ii].Btransp); fprintf(fid,"Lmarg: %d \n",image[ii].Lmarg); fprintf(fid,"Rmarg: %d \n",image[ii].Rmarg); fprintf(fid,"Tmarg: %d \n",image[ii].Tmarg); fprintf(fid,"Bmarg: %d \n",image[ii].Bmarg); fprintf(fid,"Lblend: %d \n",image[ii].Lblend); fprintf(fid,"Rblend: %d \n",image[ii].Rblend); fprintf(fid,"Tblend: %d \n",image[ii].Tblend); fprintf(fid,"Bblend: %d \n",image[ii].Bblend); fprintf(fid,"Nwarp: %d \n",image[ii].Nwarp); for (kk = 0; kk < image[ii].Nwarp; kk++) fprintf(fid,"%f %f %f %f %f \n", image[ii].warpmem[kk][0],image[ii].warpmem[kk][1], image[ii].warpmem[kk][2],image[ii].warpmem[kk][3],image[ii].warpmem[kk][4]); fprintf(fid,"vtrancc: %d \n",image[ii].vtrancc); fprintf(fid,"warpcc: %d \n",image[ii].warpcc); } fprintf(fid,"end\n"); for (ii = 0; ii < Ntext; ii++) // write text data { repl_Nstrs(text[ii].attr.text, text2,"\n","\\n",null); fprintf(fid,"text: %s\n",text2); fprintf(fid,"position: %d %d \n", text[ii].px0, text[ii].py0); txattr = text[ii].attr; fprintf(fid,"font: %s size: %d \n", txattr.font, txattr.size); fprintf(fid,"attributes: %.4f %s %s %s %s %d %d %d %d %d %d %d \n", txattr.angle, txattr.color[0], txattr.color[1], txattr.color[2], txattr.color[3], txattr.transp[0], txattr.transp[1], txattr.transp[2], txattr.transp[3], txattr.towidth, txattr.shwidth, txattr.shangle); } fprintf(fid,"end\n"); for (ii = 0; ii < Nline; ii++) // write line/arrow data { fprintf(fid,"line:\n"); fprintf(fid,"position: %d %d \n", line[ii].px0, line[ii].py0); lnattr = line[ii].attr; fprintf(fid,"attributes: %d %d %d %d %.4f %s %s %s %s %d %d %d %d %d %d %d \n", lnattr.length, lnattr.width, lnattr.larrow, lnattr.rarrow, lnattr.angle, lnattr.color[0], lnattr.color[1], lnattr.color[2], lnattr.color[3], lnattr.transp[0], lnattr.transp[1], lnattr.transp[2], lnattr.transp[3], lnattr.towidth, lnattr.shwidth, lnattr.shangle); } fprintf(fid,"end\n"); fclose(fid); for (vcc = ii = 0; ii < Nimage; ii++) // sum transparency data vcc += image[ii].vtrancc; if (vcc == 0) remove(tranfile); // no transparencies, remove poss. file else { nfid = open(tranfile,O_WRONLY|O_CREAT|O_TRUNC,0640); // write transparencies to binary file if (nfid < 0) goto failure; for (ii = 0; ii < Nimage; ii++) { vcc = image[ii].vtrancc; if (! vcc) continue; cc = write(nfid,image[ii].vtranmap,vcc); if (cc != vcc) goto failure; } close(nfid); } for (wcc = ii = 0; ii < Nimage; ii++) // sum warpcc data wcc += image[ii].warpcc; if (wcc == 0) remove(warpfile); // no warp data, remove poss. file else { nfid = open(warpfile,O_WRONLY|O_CREAT|O_TRUNC,0640); // write warp data to binary file if (nfid < 0) goto failure; for (ii = 0; ii < Nimage; ii++) { wcc = image[ii].warpcc; if (! wcc) continue; cc = write(nfid,image[ii].warpx,wcc); cc = write(nfid,image[ii].warpy,wcc); if (cc != wcc) goto failure; } close(nfid); } return; failure: zmessageACK(Mwin,strerror(errno)); return; } // scale project up by 2x/3x/4x or reset to 1x void mashup::project_rescale(float scale) { using namespace mashup; int ii, nn; int64 size; float scaleR; PXB *pxbtemp; if (scale == 1.0) { if (PRscale == 2.0) scale = 0.5; if (PRscale == 3.0) scale = 0.3334; // int 3/6/9... --> 1/2/3... not 0/1/2... if (PRscale == 4.0) scale = 0.25; } size = Lww * Lhh * scale * scale; if (12 * size > (int64) 4000 * MEGA) { // PXM size < 4 GB to remain editable zmessageACK(Mwin,E2X("layout exceeds 2 gigabytes")); return; } if (scale < 1) PRscale = 1.0; // reset to 1x else PRscale = scale; // resize to 2x, 3x or 4x 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(); } Fupdall = 1; Lupdate(); return; } /********************************************************************************/ // mashup image edit function void mashup::image_edit() { using namespace mashup; cchar *tipmess = E2X("Click image to select, drag image to move."); cchar *blackmargmess = E2X("Make black margins transparent"); if (! strmatch(focus,"image")) select("",-1); // nothing selected /*** _______________________________________________ | Edit Images | | | | Click image to select, drag image to move. | | | | Current image: filename.jpg | | Cycle through images: [Prev] [Next] | | | | Scale [0.345 ] [ 1.0 ] | | Angle [12.3 ] | | Stacking Order [raise] [lower] | | Base Transparency [0.5 ] | | Variable Transparency [paint] | | Bend and fine-align [warp] | | [x] Make black margins transparent | | | | Margins hard blend | | Left [ 123 ] [ 0 ] | | Right [ 0 ] [ 123 ] | | Top [ 234 ] [ 0 ] | | Bottom [ 0 ] [ 234 ] | | | | [Add Image] [Delete] [Done] | |_______________________________________________| ***/ zdimage = zdialog_new("Edit Images",Mwin,E2X("Add Image"),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",E2X("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",E2X("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",E2X("Scale"),"space=3"); zdialog_add_widget(zd,"zspin","scale","hbscale","0.02|2.0|0.001|0.3"); zdialog_add_widget(zd,"button","scale1x","hbscale"," 1.0 ","space=10"); zdialog_add_widget(zd,"hbox","hbangle","dialog"); zdialog_add_widget(zd,"label","labangle","hbangle",Bangle,"space=3"); zdialog_add_widget(zd,"zspin","angle","hbangle","-180|180|0.1|0"); zdialog_add_widget(zd,"hbox","hbstack","dialog"); zdialog_add_widget(zd,"label","labstack","hbstack",E2X("Stacking Order"),"space=3"); zdialog_add_widget(zd,"button","raise","hbstack",E2X("Raise"),"space=5"); zdialog_add_widget(zd,"button","lower","hbstack",E2X("Lower"),"space=5"); zdialog_add_widget(zd,"hbox","hbbtransp","dialog"); zdialog_add_widget(zd,"label","labbtr","hbbtransp",E2X("Base Transparency"),"space=3"); zdialog_add_widget(zd,"zspin","Btransp","hbbtransp","0|1.0|0.01|0"); zdialog_add_widget(zd,"hbox","hbvtranmap","dialog"); zdialog_add_widget(zd,"label","labvtr","hbvtranmap",E2X("Var. Transparency"),"space=3"); zdialog_add_widget(zd,"button","vtranmap","hbvtranmap",E2X("Paint"),"space=5"); zdialog_add_widget(zd,"hbox","hbwarp","dialog"); zdialog_add_widget(zd,"label","labwarp","hbwarp",E2X("Bend and fine-align"),"space=3"); zdialog_add_widget(zd,"button","warp","hbwarp",E2X("Warp"),"space=5"); zdialog_add_widget(zd,"hbox","hbmarg","dialog"); zdialog_add_widget(zd,"check","fixmarg","hbmarg",blackmargmess,"space=3"); zdialog_add_widget(zd,"hbox","hbmarg","dialog"); // margins zdialog_add_widget(zd,"vbox","vbmarg1","hbmarg",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vbmarg2","hbmarg",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vbmarg3","hbmarg",0,"space=3|homog"); zdialog_add_widget(zd,"label","labmarg","vbmarg1",E2X("Margins")); zdialog_add_widget(zd,"label","labhard","vbmarg2",E2X("Hard")); zdialog_add_widget(zd,"label","labblend","vbmarg3",E2X("Blend")); zdialog_add_widget(zd,"label","lableft","vbmarg1",Bleft); zdialog_add_widget(zd,"label","labright","vbmarg1",Bright); zdialog_add_widget(zd,"label","labtop","vbmarg1",Btop); zdialog_add_widget(zd,"label","labbott","vbmarg1",Bbottom); zdialog_add_widget(zd,"zspin","Lmarg","vbmarg2","0|999|1|0"); zdialog_add_widget(zd,"zspin","Rmarg","vbmarg2","0|999|1|0"); zdialog_add_widget(zd,"zspin","Tmarg","vbmarg2","0|999|1|0"); zdialog_add_widget(zd,"zspin","Bmarg","vbmarg2","0|999|1|0"); zdialog_add_widget(zd,"zspin","Lblend","vbmarg3","0|999|1|0"); zdialog_add_widget(zd,"zspin","Rblend","vbmarg3","0|999|1|0"); zdialog_add_widget(zd,"zspin","Tblend","vbmarg3","0|999|1|0"); zdialog_add_widget(zd,"zspin","Bblend","vbmarg3","0|999|1|0"); zdialog_add_ttip(zd,E2X("Add Image"),E2X("add images to layout")); zdialog_run(zd,image_dialog_event,"save"); // run dialog - parallel zdialog_wait(zd); zdialog_free(zd); return; } // image dialog event and completion function int mashup::image_dialog_event(zdialog *zd, cchar *event) { using namespace mashup; int ii; float scale, angle, rad = PI / 180.0; image_t temp; if (strmatch(event,"kill")) // kill dialog { zdialog_free(zd); zdimage = 0; if (zdtransp) { zdialog_free(zdtransp); zdtransp = 0; } if (zdwarp) { zdialog_free(zdwarp); zdwarp = 0; } return 1; } if (zd->zstat) { if (zd->zstat == 1) { // add image zd->zstat = 0; add_image(); return 1; } if (zd->zstat == 2) { // remove image zd->zstat = 0; if (! strmatch(focus,"image")) return 1; ii = focusii; remove_image(ii); return 1; } zdimage = 0; // done or [x] kill if (zdtransp) { zdialog_free(zdtransp); zdtransp = 0; } if (zdwarp) { zdialog_free(zdwarp); zdwarp = 0; } return 1; } if (strmatch(event,"focus")) { // get mouse ownership takeMouse(mousefunc_layout,0); return 1; } if (strmatch(event,"next")) { // select and flash next image if (! Nimage) return 1; if (strmatch(focus,"image")) { if (focusii == Nimage-1) select("image",0); else select("image",focusii+1); } else select("image",0); flashimage(); Fnextimage = 1; // flag for layout mouse func. } if (strmatch(event,"prev")) { // select and flash prev. image if (! Nimage) return 1; if (strmatch(focus,"image")) { if (focusii == 0) select("image",Nimage-1); else select("image",focusii-1); } else select("image",0); flashimage(); Fnextimage = 1; } if (! strmatch(focus,"image")) return 1; // get selected image ii = focusii; if (strmatch(event,"scale")) // resize { zdialog_fetch(zd,"scale",scale); // new image scale image_rescale(ii,scale); select("image",ii); } if (strmatch(event,"scale1x")) // resize to 1x { zdialog_stuff(zd,"scale",1.0); image_rescale(ii,1.0); select("image",ii); } if (strmatch(event,"angle")) // rotate { zdialog_fetch(zd,"angle",angle); angle = angle * rad; image[ii].theta = angle; image[ii].cosT = cos(angle); image[ii].sinT = sin(angle); } if (strmatch(event,"raise")) { // raise image in stacking order if (ii == Nimage - 1) return 1; temp = image[ii+1]; image[ii+1] = image[ii]; image[ii] = temp; ii = focusii = ii + 1; } if (strmatch(event,"lower")) { // lower image in stacking order if (ii == 0) return 1; temp = image[ii-1]; image[ii-1] = image[ii]; image[ii] = temp; ii = focusii = ii - 1; } if (strmatch(event,"Btransp")) // base transparency, 0 to 1.0 = invisible zdialog_fetch(zd,"Btransp",image[ii].Btransp); if (strmatch(event,"vtranmap")) { // paint variable transparency dialog paintransp_dialog(); return 1; } if (strmatch(event,"warp")) { warp_dialog(); return 1; } if (strmatch(event,"fixmarg")) transparent_margins(); // fix black margins if (strmatch(event,"Lmarg")) // left edge margin zdialog_fetch(zd,"Lmarg",image[ii].Lmarg); if (strmatch(event,"Rmarg")) // right edge zdialog_fetch(zd,"Rmarg",image[ii].Rmarg); if (strmatch(event,"Tmarg")) // top edge zdialog_fetch(zd,"Tmarg",image[ii].Tmarg); if (strmatch(event,"Bmarg")) // bottom edge zdialog_fetch(zd,"Bmarg",image[ii].Bmarg); if (strmatch(event,"Lblend")) // left edge blend width zdialog_fetch(zd,"Lblend",image[ii].Lblend); if (strmatch(event,"Rblend")) // right edge zdialog_fetch(zd,"Rblend",image[ii].Rblend); if (strmatch(event,"Tblend")) // top edge zdialog_fetch(zd,"Tblend",image[ii].Tblend); if (strmatch(event,"Bblend")) // bottom edge zdialog_fetch(zd,"Bblend",image[ii].Bblend); zmainloop(); setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return 1; } // rescale an overlay image: pxb1 (1x) --> pxb2 (scale) // if warps are present, rescale from 1x and apply to pxb2 void mashup::image_rescale(int ii, float scale) { int ww1, hh1, ww2, hh2; int px1, py1, px2, py2, kk; float dx1, dy1, dx2, dy2, vstat; uint8 vpix[4], *pix2; PXB *pxb2, *pxb2w; ww1 = image[ii].ww1; // 1x image size hh1 = image[ii].hh1; ww2 = scale * ww1; // image size at scale hh2 = scale * hh1; pxb2 = PXB_rescale(image[ii].pxb1,ww2,hh2); // rescale image -> pxb2 PXB_free(image[ii].pxb2); image[ii].pxb2 = pxb2; image[ii].ww2 = ww2; image[ii].hh2 = hh2; image[ii].scale = scale; if (image[ii].warpcc) { // apply image warps to pxb2 pxb2w = PXB_copy(pxb2); for (py2 = 0; py2 < hh2; py2++) for (px2 = 0; px2 < ww2; px2++) { px1 = px2 / scale; // rescale 1x warps py1 = py2 / scale; kk = py1 * ww1 + px1; dx1 = image[ii].warpx[kk]; dy1 = image[ii].warpy[kk]; dx2 = dx1 * scale; dy2 = dy1 * scale; vstat = vpixel(image[ii].pxb2,px2+dx2,py2+dy2,vpix); pix2 = PXBpix(pxb2w,px2,py2); if (vstat) memcpy(pix2,vpix,3); else pix2[0] = pix2[1] = pix2[2] = pix2[3] = 0; } PXB_free(image[ii].pxb2); image[ii].pxb2 = pxb2w; } return; } // dialog to initiate painting image variable transparency void mashup::paintransp_dialog() { using namespace mashup; cchar *ptitle = E2X("Paint Image Transparencies"); /*** _________________________________ | Paint Image Transparencies | | | | Radius [123 ] [x] Gradual | | Power Center [100] Edge [ 10] | | | | [done] | |_________________________________| ***/ zdtransp = zdialog_new(ptitle,Mwin,Bdone,null); zdialog_add_widget(zdtransp,"hbox","hbrad","dialog",0,"space=3"); zdialog_add_widget(zdtransp,"label","labrad","hbrad",Bradius,"space=5"); zdialog_add_widget(zdtransp,"zspin","radius","hbrad","5|300|1|100"); zdialog_add_widget(zdtransp,"check","gradual","hbrad",E2X("Gradual"),"space=10"); zdialog_add_widget(zdtransp,"hbox","hbpow","dialog",0,"space=3"); zdialog_add_widget(zdtransp,"label","labpower","hbpow",E2X("Power"),"space=5"); zdialog_add_widget(zdtransp,"label","labcen","hbpow",Bcenter,"space=5"); zdialog_add_widget(zdtransp,"zspin","center","hbpow","0|100|1|10"); zdialog_add_widget(zdtransp,"label","space","hbpow",0,"space=6"); zdialog_add_widget(zdtransp,"label","labedge","hbpow",Bedge,"space=5"); zdialog_add_widget(zdtransp,"zspin","edge","hbpow","0|100|1|3"); zdialog_stuff(zdtransp,"radius",Mradius); zdialog_stuff(zdtransp,"radius",Mradius); zdialog_stuff(zdtransp,"gradual",Mgradual); zdialog_stuff(zdtransp,"edge",Mpowedge); zdialog_run(zdtransp,paintransp_dialog_event,"save"); takeMouse(paintransp_mousefunc,0); return; } // zdialog event and completion function int mashup::paintransp_dialog_event(zdialog *zd, cchar *event) { using namespace mashup; if (zd->zstat) { // done or cancel zdialog_free(zd); zdtransp = 0; takeMouse(mousefunc_layout,0); return 1; } if (strmatch(event,"focus")) { // take mouse ownership takeMouse(paintransp_mousefunc,0); return 1; } if (strmatch(event,"radius")) // mouse radius value zdialog_fetch(zd,"radius",Mradius); if (strmatch(event,"gradual")) // gradual/instant paint option zdialog_fetch(zd,"gradual",Mgradual); if (strmatch(event,"center")) // mouse center power zdialog_fetch(zd,"center",Mpowcen); if (strmatch(event,"edge")) // mouse edge power zdialog_fetch(zd,"edge",Mpowedge); return 1; } // mouse function for painting image var. transparency void mashup::paintransp_mousefunc() { using namespace mashup; int ii, Mii, jj, vcc, mx, my; int distx, disty, dist, mindist; int pxlo, pylo, pxhi, pyhi; int cenx, ceny, mrad = Mradius; int ww1, hh1, ww2, hh2, transp, mtransp; int dx, dy, qx, qy, rx, ry; float sinT, cosT, px0, py0, px1, py1; px1 = py1 = -1; // no mouse position in image if (! strmatch(focus,"image")) { // if no image focus, draw_mousecircle(0,0,0,1,0); // no mouse circle gdk_window_set_cursor(gdkwin,0); } mx = Mxposn; // mouse position in layout my = Myposn; if (LMclick) // left click { LMclick = 0; // stop main window click after return mx = Mxclick; // click position my = Myclick; Mii = -1; // no selected image mindist = 9999; for (ii = 0; ii < Nimage; ii++) // find closest image center to mouse { px0 = image[ii].px0; // image position and rotation py0 = image[ii].py0; // in layout image space sinT = image[ii].sinT; cosT = image[ii].cosT; ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; px1 = (mx-px0) * cosT + (my-py0) * sinT; // image pixel for layout position py1 = (my-py0) * cosT - (mx-px0) * sinT; if (px1 < 0 || px1 > ww2-1) continue; // not within image if (py1 < 0 || py1 > hh2-1) continue; pxlo = image[ii].pxlo; // image extent pxhi = image[ii].pxhi; pylo = image[ii].pylo; pyhi = image[ii].pyhi; cenx = (pxlo + pxhi) / 2; // image center ceny = (pylo + pyhi) / 2; distx = mx - cenx; disty = my - ceny; dist = sqrtf(distx * distx + disty * disty); // mouse/center distance if (dist < mindist) { mindist = dist; Mii = ii; // save closest image center } } if (Mii < 0) { // no image clicked select("",-1); // no selected image draw_mousecircle(0,0,0,1,0); // no mouse circle gdk_window_set_cursor(gdkwin,0); return; } ii = Mii; select("image",ii); // set selected image flashimage(); // flash selected image return; } if (! strmatch(focus,"image")) { // if no current image, draw_mousecircle(0,0,0,1,0); // no mouse circle gdk_window_set_cursor(gdkwin,0); return; } ii = focusii; // current image px0 = image[ii].px0; // image position and rotation py0 = image[ii].py0; // in layout image space sinT = image[ii].sinT; cosT = image[ii].cosT; ww1 = image[ii].ww1; // image size at 1x hh1 = image[ii].hh1; ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; px1 = (mx-px0) * cosT + (my-py0) * sinT; // mouse position within image py1 = (my-py0) * cosT - (mx-px0) * sinT; if (px1+mrad < 0 || px1-mrad > ww2 || // entire circle off image py1+mrad < 0 || py1-mrad > hh2) { draw_mousecircle(0,0,0,1,0); // no mouse circle gdk_window_set_cursor(gdkwin,0); return; } draw_mousecircle(mx,my,mrad,0,0); // mouse within image, draw mouse circle gdk_window_set_cursor(gdkwin,dragcursor); zmainloop(); // keep mouse circle visible if (! Mxdrag && ! Mydrag) return; // no drag underway Mxdrag = Mydrag = 0; // stop main window drag after return if (! image[ii].vtranmap) { // make transparency map if not already vcc = ww1 * hh1; // transparency map is 1x scale image[ii].vtranmap = (uint8 *) zmalloc(vcc); // and one uint8 per pixel memset(image[ii].vtranmap,0,vcc); // initial values = 0 transparency image[ii].vtrancc = vcc; } qx = px1 / image[ii].scale; // mouse position in 1x image qy = py1 / image[ii].scale; mrad = mrad / image[ii].scale; // mouse radius at 1x scale for (dy = -mrad; dy <= mrad; dy++) // loop pixels within mouse circle for (dx = -mrad; dx <= mrad; dx++) { dist = sqrtf(dx*dx + dy*dy); // distance from mouse center if (dist >= mrad) continue; // outside mouse circle if (Mgradual) { // gradual paint transparency mtransp = Mpowcen * (mrad - dist) / mrad // mouse power at distance from center + Mpowedge * dist / mrad; // Mpowcen ... Mpowedge scale 0-100 if (drandz() < 0.005) continue; // thin active points } else mtransp = 255; // instant paint transparency rx = qx + dx; // 1x pixel position ry = qy + dy; if (rx < 0 || rx > ww1-1) continue; // outside 1x image if (ry < 0 || ry > hh1-1) continue; jj = ry * ww1 + rx; // get corresp. var. transparency transp = image[ii].vtranmap[jj]; if (Mbutton == 1) { // left mouse button transp += mtransp; // increase image transparency if (transp > 255) transp = 255; // by mouse power } else { // right button transp -= mtransp; // decrease if (transp < 0) transp = 0; } image[ii].vtranmap[jj] = transp; // adjusted image transparency } updxlo = mx - mrad; // update area = mouse circle updxhi = mx + mrad; updylo = my - mrad; updyhi = my + mrad; setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return; } // dialog to initiate warping an image by dragging the mouse namespace warp { float dragx, dragy, dragw, dragh; float warpD, span; int Fdrag; PXB *pxb2w; int warpii; }; void mashup::warp_dialog() { using namespace mashup; using namespace warp; cchar *warp_message = E2X("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(E2X("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",E2X("warp span"),"space=8"); zdialog_add_widget(zdwarp,"zspin","span","hb2","0.00|1.0|0.01|0.2","space=1"); span = 0.2; Fdrag = 0; pxb2w = 0; warpii = -1; zdialog_run(zdwarp,warp_dialog_event,"save"); // run dialog, parallel takeMouse(warp_mousefunc,0); // connect mouse function return; } // dialog event and completion callback function int mashup::warp_dialog_event(zdialog * zd, cchar *event) { using namespace mashup; using namespace warp; int ii, kk; if (zd->zstat) // done or cancel { if (pxb2w) PXB_free(pxb2w); pxb2w = 0; zdialog_free(zd); // kill dialog zdwarp = 0; takeMouse(mousefunc_layout,0); // restore mouse ownership return 1; } if (! strmatch(focus,"image")) return 1; // insure image focus ii = focusii; if (strmatch(event,"focus")) // connect mouse function takeMouse(warp_mousefunc,dragcursor); if (strmatch(event,"undolast")) { if (image[ii].Nwarp == 1) event = "undoall"; else if (image[ii].Nwarp) { kk = image[ii].Nwarp - 1; dragx = image[ii].warpmem[kk][0]; // new warp = negative prior warp dragy = image[ii].warpmem[kk][1]; dragw = -image[ii].warpmem[kk][2]; dragh = -image[ii].warpmem[kk][3]; span = image[ii].warpmem[kk][4]; zdialog_stuff(zd,"span",span); Fdrag = 0; warp_warpfunc(); image[ii].Nwarp -= 1; } } if (strmatch(event,"undoall") && image[ii].warpcc) { // undo all warps image[ii].Nwarp = 0; zfree(image[ii].warpx); zfree(image[ii].warpy); image[ii].warpx = image[ii].warpy = 0; image[ii].warpcc = 0; } if (strstr(event,"undo")) { image_rescale(ii,image[ii].scale); // pxb1 + warps -> pxb2 if (pxb2w) PXB_free(pxb2w); pxb2w = PXB_copy(image[ii].pxb2); // base for next drag/warp setlayoutupdatearea(); Lupdate(); // update layout image } if (strmatch(event,"span")) zdialog_fetch(zd,"span",span); return 1; } // warp mouse function // convert mouse drags to image warps void mashup::warp_mousefunc() { using namespace mashup; using namespace warp; int ii, kk, wcc, Mii; int mx, my, dx, dy, dw, dh; int distx, disty, dist, mindist; int pxlo, pylo, pxhi, pyhi; int cenx, ceny, ww1, hh1, ww2, hh2; float sinT, cosT, scale, px0, py0, px1, py1; if (LMclick) // left click { gdk_window_set_cursor(gdkwin,0); // no drag cursor LMclick = 0; // stop main window click after return mx = Mxclick; // click position my = Myclick; Mii = -1; // no selected image mindist = 9999; for (ii = 0; ii < Nimage; ii++) // find closest image center to mouse { px0 = image[ii].px0; // image position and rotation py0 = image[ii].py0; // in layout image space sinT = image[ii].sinT; cosT = image[ii].cosT; ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; px1 = (mx-px0) * cosT + (my-py0) * sinT; // image pixel for layout position py1 = (my-py0) * cosT - (mx-px0) * sinT; if (px1 < 0 || px1 > ww2-1) continue; // not within image if (py1 < 0 || py1 > hh2-1) continue; pxlo = image[ii].pxlo; // image extent pxhi = image[ii].pxhi; pylo = image[ii].pylo; pyhi = image[ii].pyhi; cenx = (pxlo + pxhi) / 2; // image center ceny = (pylo + pyhi) / 2; distx = mx - cenx; disty = my - ceny; dist = sqrtf(distx * distx + disty * disty); // mouse/center distance if (dist < mindist) { mindist = dist; Mii = ii; // save closest image center } } if (Mii < 0) { // no image clicked select("",-1); // no selected image return; } ii = Mii; select("image",ii); // set selected image flashimage(); // flash selected image return; } if (! strmatch(focus,"image")) { // if no image focus, gdk_window_set_cursor(gdkwin,0); // no drag cursor return; } ii = focusii; // focus image mx = Mxposn; // mouse position in layout space my = Myposn; scale = image[ii].scale; px0 = image[ii].px0; // image position and rotation py0 = image[ii].py0; // in layout space sinT = image[ii].sinT; cosT = image[ii].cosT; px1 = (mx-px0) * cosT + (my-py0) * sinT; // mouse position within scaled image py1 = (my-py0) * cosT - (mx-px0) * sinT; ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; if (px1 < 0 || px1 > ww2 || py1 < 0 || py1 > hh2) { // mouse outside image gdk_window_set_cursor(gdkwin,0); // no drag cursor return; } gdk_window_set_cursor(gdkwin,dragcursor); // mouse inside image, drag cursor if (! pxb2w || (ii != warpii)) { // image changed if (pxb2w) PXB_free(pxb2w); pxb2w = PXB_copy(image[ii].pxb2); warpii = ii; } if (Mxdrag || Mydrag) // mouse drag underway { if (! image[ii].warpcc) // first warp for image { ww1 = image[ii].ww1; hh1 = image[ii].hh1; wcc = ww1 * hh1 * sizeof(float); image[ii].warpx = (float *) zmalloc(wcc); // get memory for pixel displacements image[ii].warpy = (float *) zmalloc(wcc); image[ii].warpcc = wcc; memset(image[ii].warpx,0,wcc); memset(image[ii].warpy,0,wcc); image[ii].Nwarp = 0; // no warp memory data } dx = Mxdown; // drag origin, image space dy = Mydown; dw = Mxdrag - Mxdown; // drag increment dh = Mydrag - Mydown; Mxdrag = Mydrag = 0; // reset px0 = image[ii].px0; py0 = image[ii].py0; sinT = image[ii].sinT; cosT = image[ii].cosT; dragx = (dx-px0) * cosT + (dy-py0) * sinT; // drag origin in scaled image dragy = (dy-py0) * cosT - (dx-px0) * sinT; dragw = dw * cosT + dh * sinT; // drag increment in scaled image dragh = dh * cosT - dw * sinT; warpD = ww2 + hh2; // warp span warpD = warpD * span; Fdrag = 1; // drag underway warp_warpfunc(); // drag pxb2 image } else if (Fdrag) // drag complete { dragx = dragx / scale; // conv. drag vector to 1x scale dragy = dragy / scale; dragw = dragw / scale; dragh = dragh / scale; warpD = warpD / scale; Fdrag = 0; warp_warpfunc(); // accumulate pxb1 warp positions image_rescale(ii,image[ii].scale); // pxb1 + warps -> pxb2 PXB_free(pxb2w); // base for next drag/warp pxb2w = PXB_copy(image[ii].pxb2); if (image[ii].Nwarp == 200) // add drag to undo memory { image[ii].Nwarp = 199; // full, remove oldest for (kk = 0; kk < image[ii].Nwarp; kk++) { image[ii].warpmem[kk][0] = image[ii].warpmem[kk+1][0]; image[ii].warpmem[kk][1] = image[ii].warpmem[kk+1][1]; image[ii].warpmem[kk][2] = image[ii].warpmem[kk+1][2]; image[ii].warpmem[kk][3] = image[ii].warpmem[kk+1][3]; image[ii].warpmem[kk][4] = image[ii].warpmem[kk+1][4]; } } kk = image[ii].Nwarp; image[ii].warpmem[kk][0] = dragx; // save drag for undo image[ii].warpmem[kk][1] = dragy; image[ii].warpmem[kk][2] = dragw; image[ii].warpmem[kk][3] = dragh; image[ii].warpmem[kk][4] = span; image[ii].Nwarp++; } setlayoutupdatearea(); // update layout image Lupdate(); return; } // warp image and accumulate warp memory // mouse at (dragx,dragy) is moved (dragw,dragh) pixels void mashup::warp_warpfunc() { using namespace mashup; using namespace warp; do_wthreads(warp_warpfunc_wthread,NWT); // worker threads return; } void * mashup::warp_warpfunc_wthread(void *arg) // worker thread { using namespace mashup; using namespace warp; int index = *((int *) arg); int ii, kk, px, py, vstat; int ww1, hh1, ww2, hh2; float d, mag, dx, dy; float FD = 1.0 / (warpD * warpD); uint8 vpix[4], *pix2; ii = focusii; if (Fdrag) // drag underway { ww2 = image[ii].ww2; hh2 = image[ii].hh2; for (py = index; py < hh2; py += NWT) // process all pxb2 pixels for (px = 0; px < ww2; px++) { d = (px-dragx)*(px-dragx) + (py-dragy)*(py-dragy); // compute warp based on nearness mag = 1.0 - d * FD; // to mouse position = drag center if (mag < 0) continue; mag = mag * mag; // faster than pow(mag,4); mag = mag * mag; dx = -dragw * mag; // displacement = drag * mag dy = -dragh * mag; vstat = vpixel(pxb2w,px+dx,py+dy,vpix); // drag/warp pxb2 image pix2 = PXBpix(image[ii].pxb2,px,py); if (vstat) memcpy(pix2,vpix,3); else pix2[0] = pix2[1] = pix2[2] = pix2[3] = 0; } } else // drag complete { ww1 = image[ii].ww1; hh1 = image[ii].hh1; for (py = index; py < hh1; py += NWT) // process all pxb1 pixels for (px = 0; px < ww1; px++) { d = (px-dragx)*(px-dragx) + (py-dragy)*(py-dragy); // compute warp based on nearness mag = 1.0 - d * FD; // to mouse position = drag center if (mag < 0) continue; mag = mag * mag; // faster than pow(mag,4); mag = mag * mag; dx = -dragw * mag; // displacement = drag * mag dy = -dragh * mag; kk = py * ww1 + px; // accumulate drag/warp image[ii].warpx[kk] += dx; // into pxb1 warp memory image[ii].warpy[kk] += dy; } } pthread_exit(0); // exit thread } // make black image margins transparent void mashup::transparent_margins() { int ii, ww1, hh1, px, py; PXB *pxb1; uint8 *pixel; for (ii = 0; ii < Nimage; ii++) // loop all images { ww1 = image[ii].ww1; // image 1x size hh1 = image[ii].hh1; pxb1 = image[ii].pxb1; for (py = 0; py < hh1; py++) // loop all rows { for (px = 0; px < ww1/2; px++) // from left edge to middle { pixel = PXBpix(pxb1,px,py); if (pixel[0] > 0 || pixel[1] > 0 || pixel[2] > 1) break; // break out at first non-black pixel pixel[3] = 0; // pixel has full transparency } for (px = ww1-1; px > ww1/2; px--) // from right edge to middle { pixel = PXBpix(pxb1,px,py); if (pixel[0] > 0 || pixel[1] > 0 || pixel[2] > 1) break; pixel[3] = 0; } } for (px = 0; px < ww1; px++) // from top to middle { for (py = 0; py < hh1/2; py++) { pixel = PXBpix(pxb1,px,py); if (pixel[0] > 0 || pixel[1] > 0 || pixel[2] > 1) break; pixel[3] = 0; } for (py = hh1-1; py > hh1/2; py--) // from bottom to middle { pixel = PXBpix(pxb1,px,py); if (pixel[0] > 0 || pixel[1] > 0 || pixel[2] > 1) break; pixel[3] = 0; } } image_rescale(ii,image[ii].scale); // update scaled image pxb2 } Fupdall = 1; Lupdate(); // update layout composite image return; } // select and add one or more new overlay image files void mashup::add_image() { using namespace mashup; int ii, jj, nn, cc, Nfiles = 0; int ww1, hh1, ww2, hh2; float scale; char **selfiles = 0, *file; PXB *pxb1; zdialog_show(zdimage,0); // hide parent dialog gallery_select_clear(); // clear gallery_select() file list gallery_select(); // select image files from gallery m_viewmode(0,"F"); // restore view mode F zdialog_show(zdimage,1); if (! GScount) return; // nothing selected selfiles = (char **) zmalloc(GScount * sizeof(char *)); for (ii = 0; ii < GScount; ii++) selfiles[ii] = GSfiles[ii]; Nfiles = GScount; GScount = 0; for (nn = 0; nn < Nfiles; nn++) { if (Nimage == maxmash) { zmessageACK(Mwin,E2X("exceeded %d images"),maxmash); break; } file = selfiles[nn]; pxb1 = PXB_load(file,1); // load image, ACK errors if (! pxb1) { zfree(file); continue; } PXB_addalpha(pxb1); // add alpha channel if missing ii = Nimage++; // new image cc = sizeof(image_t); // clear image data memset(&image[ii],0,cc); image[ii].file = file; // add image file to mashup list image[ii].pxb1 = pxb1; ww1 = pxb1->ww; hh1 = pxb1->hh; scale = 1.0; if (scale * ww1 / Lww > 0.3) scale = 0.3 * Lww / ww1; // if image > 30% layout, reduce to 30% if (scale * hh1 / Lhh > 0.3) scale = 0.3 * Lhh / hh1; ww2 = ww1 * scale; hh2 = hh1 * scale; image[ii].scale = scale; image[ii].ww1 = ww1; image[ii].hh1 = hh1; image[ii].ww2 = ww2; image[ii].hh2 = hh2; image[ii].pxb2 = PXB_rescale(image[ii].pxb1,ww2,hh2); // scaled image for display image[ii].theta = 0.0; image[ii].sinT = 0; image[ii].cosT = 1.0; jj = ii; while (jj >= 25) jj -= 25; image[ii].py0 = (jj / 5) * 0.20 * Lhh; // initial position image[ii].px0 = (jj % 5) * 0.20 * Lww; select("image",ii); setlayoutupdatearea(); // set layout update area } while (nn < Nfiles) zfree(selfiles[nn++]); // free unused files zfree(selfiles); select("",-1); // nothing selected Fupdall = 1; Lupdate(); // update layout composite image return; } // remove an overlay image void mashup::remove_image(int ii) { using namespace mashup; select("image",ii); setlayoutupdatearea(); // set layout update area zfree(image[ii].file); // free memory PXB_free(image[ii].pxb1); PXB_free(image[ii].pxb2); if (image[ii].vtranmap) zfree(image[ii].vtranmap); for (int jj = ii+1; jj < Nimage; jj++) // pack-down image list image[ii] = image[jj]; Nimage--; // image count select("",-1); // no selected image Fupdall = 1; Lupdate(); // update layout composite image return; } // flash a selected image as bright as possible for a short moment void mashup::flashimage() { int ii, px, py, ww2, hh2; int xlo, xhi, ylo, yhi; float px0, py0, px1, py1, px2, py2; float sinT, cosT; uint8 *pix2, vpix[4]; PXB *pxb2; if (! strmatch(focus,"image")) return; ii = focusii; xlo = image[ii].pxlo; // overlay image extent in layout image xhi = image[ii].pxhi; ylo = image[ii].pylo; yhi = image[ii].pyhi; for (py = ylo; py < yhi; py++) for (px = xlo; px < xhi; px++) { px1 = px; py1 = py; px0 = image[ii].px0; // overlay image position and rotation py0 = image[ii].py0; // in layout image space pxb2 = image[ii].pxb2; sinT = image[ii].sinT; cosT = image[ii].cosT; ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; px2 = (px1-px0) * cosT + (py1-py0) * sinT; // overlay image pixel for layout position py2 = (py1-py0) * cosT - (px1-px0) * sinT; if (px2 < 0 || px2 > ww2-1) continue; // not within image if (py2 < 0 || py2 > hh2-1) continue; pix2 = PXBpix(Fpxb,px,py); // output image pixel if (! vpixel(pxb2,px2,py2,vpix)) continue; // overlay image virt. pixel pix2[0] = vpix[1]; // rotate the colors pix2[1] = vpix[2]; pix2[2] = vpix[0]; } ww2 = xhi - xlo; hh2 = yhi - ylo; PXB_PXB_update(Fpxb,Mpxb,xlo,ylo,ww2,hh2); // Fpxb > Mpxb, scaled up or down Fpaint4(xlo,ylo,ww2,hh2,0); // update drawing window from Mpxb zmainloop(); zsleep(0.1); // hold for a moment setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return; } /********************************************************************************/ // add text to image or revise existing text void mashup::text_edit() { using namespace mashup; cchar *intro = E2X("Enter text, [Add] to layout, edit properties."); /*** __________________________________________________ | Edit Text | | | | Enter text, [Add] to layout, edit properties. | | | | Text [______________________________________] | | | | [font] [FreeSans______________] size [ 44 ] | | | | color transp. width angle | | text [_____] [_____] [_____] | | backing [_____] [_____] | | outline [_____] [_____] [_____] | | shadow [_____] [_____] [_____] [_____] | | | | Text File: [Open] [Save] | | | | [clear] [Add] [Delete] [Done] | |__________________________________________________| ***/ if (! strmatch(focus,"text")) select("",-1); // nothing selected zdtext = zdialog_new(E2X("Edit Text"),Mwin,Bclear,Badd,Bdelete,Bdone,null); zdialog *zd = zdtext; zdialog_add_widget(zd,"label","intro","dialog",intro,"space=3"); zdialog_add_widget(zd,"hbox","hbtext","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labtext","hbtext",E2X("Text"),"space=5"); zdialog_add_widget(zd,"frame","frtext","hbtext",0,"expand"); zdialog_add_widget(zd,"edit","text","frtext","","expand"); zdialog_add_widget(zd,"hbox","hbfont","dialog",0,"space=3"); zdialog_add_widget(zd,"button","fontbutt","hbfont",Bfont); zdialog_add_widget(zd,"zentry","fontname","hbfont","FreeSans","space=2|expand"); zdialog_add_widget(zd,"label","labsize","hbfont",Bsize,"space=2"); zdialog_add_widget(zd,"zspin","fontsize","hbfont","8|500|1|40"); zdialog_add_widget(zd,"hbox","hbcol","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vbcol1","hbcol",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vbcol2","hbcol",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbcol3","hbcol",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbcol4","hbcol",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbcol5","hbcol",0,"homog|space=2"); zdialog_add_widget(zd,"label","space","vbcol1"); zdialog_add_widget(zd,"label","labtext","vbcol1",E2X("text")); zdialog_add_widget(zd,"label","labback","vbcol1",E2X("backing")); zdialog_add_widget(zd,"label","laboutln","vbcol1",E2X("outline")); zdialog_add_widget(zd,"label","labshadow","vbcol1",E2X("shadow")); zdialog_add_widget(zd,"label","labcol","vbcol2",Bcolor); zdialog_add_widget(zd,"colorbutt","fgcolor","vbcol2","0|0|0"); zdialog_add_widget(zd,"colorbutt","bgcolor","vbcol2","255|255|255"); zdialog_add_widget(zd,"colorbutt","tocolor","vbcol2","255|0|0"); zdialog_add_widget(zd,"colorbutt","shcolor","vbcol2","0|255|0"); zdialog_add_widget(zd,"label","labcol","vbcol3","Transp."); zdialog_add_widget(zd,"zspin","fgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","bgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","totransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","shtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"label","labw","vbcol4",Bwidth); zdialog_add_widget(zd,"label","space","vbcol4"); zdialog_add_widget(zd,"label","space","vbcol4"); zdialog_add_widget(zd,"zspin","towidth","vbcol4","0|30|1|0"); zdialog_add_widget(zd,"zspin","shwidth","vbcol4","0|50|1|0"); zdialog_add_widget(zd,"label","labw","vbcol5",Bangle); zdialog_add_widget(zd,"zspin","angle","vbcol5","-180|180|0.1|0"); zdialog_add_widget(zd,"label","space","vbcol5"); zdialog_add_widget(zd,"label","space","vbcol5"); zdialog_add_widget(zd,"zspin","shangle","vbcol5","-180|180|1|0"); zdialog_add_widget(zd,"hbox","hbaf","dialog",0,"space=10"); zdialog_add_widget(zd,"label","labbg","hbaf",E2X("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,E2X("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], fname[70]; int ii, size; char *pp; if (strmatch(event,"kill")) { // kill dialog zdialog_free(zd); zdtext = 0; return 1; } if (zd->zstat) { if (zd->zstat == 1) { // clear zd->zstat = 0; zdialog_stuff(zd,"text",""); focus = ""; return 1; } if (zd->zstat == 2) { // add zd->zstat = 0; add_text(); // zdialog inputs >> new text image return 1; } if (zd->zstat == 3) { // delete zd->zstat = 0; if (! strmatch(focus,"text")) return 1; ii = focusii; remove_text(ii); // remove selected text image return 1; } zdtext = 0; // done or [x] kill return 1; } if (strmatch(event,"focus")) { // get mouse ownership takeMouse(mousefunc_layout,0); return 1; } if (strmatch(event,"loadtext")) // load text data from file load_text(zdtext); // and stuff zdialog fields if (strmatch(event,"savetext")) { // get all zdialog fields save_text(zdtext); // and save them to a file return 1; } if (strmatch(event,"fontbutt")) // set new font { zdialog_fetch(zd,"fontname",fname,70); zdialog_fetch(zd,"fontsize",size); snprintf(font,80,"%s %d",fname,size); font_dialog = gtk_font_chooser_dialog_new(E2X("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 = E2X("click position to add text"); if (! zd) return; if (zdaddtext) return; if (Ntext == maxmash) { zmessageACK(Mwin,E2X("exceeded %d text entries"),maxmash); return; } Mxclick = Myclick = 0; zdaddtext = zdialog_new(E2X("Add Text"),Mwin,Bcancel,null); // get mouse click for text position zdialog_set_decorated(zdaddtext,0); zdialog_add_widget(zdaddtext,"label","labtip","dialog",tip,"space=3"); zdialog_run(zdaddtext,0,"mouse"); zdialog_wait(zdaddtext); zdialog_free(zdaddtext); if (! (Mxclick + Myclick)) return; ii = Ntext++; memset(&text[ii],0,sizeof(text_t)); zdialog_fetch(zd,"text",text[ii].attr.text,1000); // initz. new text and attributes zdialog_fetch(zd,"fontname",text[ii].attr.font,80); zdialog_fetch(zd,"fontsize",text[ii].attr.size); zdialog_fetch(zd,"angle",text[ii].attr.angle); zdialog_fetch(zd,"fgcolor",text[ii].attr.color[0],20); zdialog_fetch(zd,"bgcolor",text[ii].attr.color[1],20); zdialog_fetch(zd,"tocolor",text[ii].attr.color[2],20); zdialog_fetch(zd,"shcolor",text[ii].attr.color[3],20); zdialog_fetch(zd,"fgtransp",text[ii].attr.transp[0]); zdialog_fetch(zd,"bgtransp",text[ii].attr.transp[1]); zdialog_fetch(zd,"totransp",text[ii].attr.transp[2]); zdialog_fetch(zd,"shtransp",text[ii].attr.transp[3]); zdialog_fetch(zd,"towidth",text[ii].attr.towidth); zdialog_fetch(zd,"shwidth",text[ii].attr.shwidth); zdialog_fetch(zd,"shangle",text[ii].attr.shangle); gentext(&text[ii].attr); // generate new text image text[ii].ww = text[ii].attr.pxb_text->ww; text[ii].hh = text[ii].attr.pxb_text->hh; text[ii].px0 = Mxclick; // set initial position text[ii].py0 = Myclick; select("text",ii); // set selected text image setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return; } // remove text entry from text[ii] void mashup::remove_text(int ii) { using namespace mashup; select("text",ii); setlayoutupdatearea(); // set layout update area PXB_free(text[ii].attr.pxb_text); for (int jj = ii+1; jj < Ntext; jj++) // pack down text list text[jj-1] = text[jj]; Ntext--; // text count select("",-1); // nothing selected Fupdall = 1; Lupdate(); // update layout composite image return; } // flash a selected text image as bright as possible for a short moment void mashup::flashtext() { int ii, jj, red, green, blue; char savecolor[4][20], flashcolor[20]; if (! strmatch(focus,"text")) return; ii = focusii; for (jj = 0; jj < 4; jj++) // save text colors strcpy(savecolor[jj],text[ii].attr.color[jj]); for (jj = 0; jj < 4; jj++) { // get complimentary colors red = 255 - atoi(strField(text[ii].attr.color[jj],'|',1)); green = 255 - atoi(strField(text[ii].attr.color[jj],'|',2)); blue = 255 - atoi(strField(text[ii].attr.color[jj],'|',3)); snprintf(flashcolor,20,"%d|%d|%d",red,green,blue); strcpy(text[ii].attr.color[jj],flashcolor); } gentext(&text[ii].attr); // generate complimentary text image zmainloop(); setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image zsleep(0.05); // hold for a moment for (jj = 0; jj < 4; jj++) // restore text colors strcpy(text[ii].attr.color[jj],savecolor[jj]); gentext(&text[ii].attr); // gemerate normal text image zmainloop(); setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return; } /********************************************************************************/ // add a line/arrow to image or revise existing line/arrow void mashup::line_edit() { using namespace mashup; cchar *intro = E2X("Set line properties, [Add] to layout, edit."); /*** _______________________________________________ | Edit Line/Arrow | | | | Set line properties, [Add] to layout, edit. | | | | Line length [____] width [____] | | Arrow head [x] left [x] right | | | | color transp. width angle | | line [_____] [_____] [_____] | | backing [_____] [_____] | | outline [_____] [_____] [_____] | | shadow [_____] [_____] [_____] [_____] | | | | [Add] [Delete] [Done] | |_______________________________________________| ***/ if (! strmatch(focus,"line")) select("",-1); // nothing selected zdline = zdialog_new(E2X("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",E2X("Line length"),"space=5"); zdialog_add_widget(zd,"zspin","length","hbline","2|9999|1|20"); zdialog_add_widget(zd,"label","space","hbline",0,"space=10"); zdialog_add_widget(zd,"label","labwidth","hbline",Bwidth,"space=5"); zdialog_add_widget(zd,"zspin","width","hbline","1|99|1|2"); zdialog_add_widget(zd,"hbox","hbarrow","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labarrow","hbarrow",E2X("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",E2X("line")); zdialog_add_widget(zd,"label","labback","vbcol1",E2X("backing")); zdialog_add_widget(zd,"label","laboutln","vbcol1",E2X("outline")); zdialog_add_widget(zd,"label","labshadow","vbcol1",E2X("shadow")); zdialog_add_widget(zd,"label","labcol","vbcol2",Bcolor); zdialog_add_widget(zd,"colorbutt","fgcolor","vbcol2","0|0|0"); zdialog_add_widget(zd,"colorbutt","bgcolor","vbcol2","255|255|255"); zdialog_add_widget(zd,"colorbutt","tocolor","vbcol2","255|0|0"); zdialog_add_widget(zd,"colorbutt","shcolor","vbcol2","0|255|0"); zdialog_add_widget(zd,"label","labcol","vbcol3","Transp."); zdialog_add_widget(zd,"zspin","fgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","bgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","totransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","shtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"label","labw","vbcol4",Bwidth); zdialog_add_widget(zd,"label","space","vbcol4"); zdialog_add_widget(zd,"label","space","vbcol4"); zdialog_add_widget(zd,"zspin","towidth","vbcol4","0|30|1|0"); zdialog_add_widget(zd,"zspin","shwidth","vbcol4","0|50|1|0"); zdialog_add_widget(zd,"label","labw","vbcol5",Bangle); zdialog_add_widget(zd,"zspin","angle","vbcol5","-180|180|0.1|0"); zdialog_add_widget(zd,"label","space","vbcol5"); zdialog_add_widget(zd,"label","space","vbcol5"); zdialog_add_widget(zd,"zspin","shangle","vbcol5","-180|180|1|0"); zdialog_add_ttip(zd,Badd,E2X("add line/arrow to layout")); zdialog_restore_inputs(zd); // restore prior inputs zdialog_run(zd,line_dialog_event,"save"); // run dialog, parallel zdialog_wait(zd); zdialog_free(zd); return; } // dialog event and completion callback function int mashup::line_dialog_event(zdialog *zd, cchar *event) { using namespace mashup; int ii; if (strmatch(event,"kill")) { // kill dialog zdialog_free(zd); zdline = 0; return 1; } if (zd->zstat) { if (zd->zstat == 1) { // add zd->zstat = 0; add_line(); // zdialog inputs >> new line/arrow image return 1; } if (zd->zstat == 2) { // delete zd->zstat = 0; if (! strmatch(focus,"line")) return 1; ii = focusii; remove_line(ii); // remove selected line/arrow image return 1; } zdline = 0; // done or [x] kill return 1; } if (strmatch(event,"focus")) { // get mouse ownership takeMouse(mousefunc_layout,0); return 1; } ii = focusii; if (ii < 0) return 1; // 19.0 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 = E2X("click position to add line"); if (! zd) return; if (zdaddline) return; if (Nline == maxmash) { zmessageACK(Mwin,E2X("exceeded %d line entries"),maxmash); return; } Mxclick = Myclick = 0; zdaddline = zdialog_new(E2X("Add Line"),Mwin,Bdone,Bcancel,null); // get mouse click for line position zdialog_set_decorated(zdaddline,0); zdialog_add_widget(zdaddline,"label","labtip","dialog",tip,"space=3"); zdialog_run(zdaddline,0,"mouse"); zstat = zdialog_wait(zdaddline); zdialog_free(zdaddline); if (zstat != 1) return; if (! (Mxclick + Myclick)) return; ii = Nline++; memset(&line[ii],0,sizeof(line_t)); zdialog_fetch(zd,"length",line[ii].attr.length); zdialog_fetch(zd,"width",line[ii].attr.width); zdialog_fetch(zd,"larrow",line[ii].attr.larrow); zdialog_fetch(zd,"rarrow",line[ii].attr.rarrow); zdialog_fetch(zd,"angle",line[ii].attr.angle); zdialog_fetch(zd,"fgcolor",line[ii].attr.color[0],20); zdialog_fetch(zd,"bgcolor",line[ii].attr.color[1],20); zdialog_fetch(zd,"tocolor",line[ii].attr.color[2],20); zdialog_fetch(zd,"shcolor",line[ii].attr.color[3],20); zdialog_fetch(zd,"fgtransp",line[ii].attr.transp[0]); zdialog_fetch(zd,"bgtransp",line[ii].attr.transp[1]); zdialog_fetch(zd,"totransp",line[ii].attr.transp[2]); zdialog_fetch(zd,"shtransp",line[ii].attr.transp[3]); zdialog_fetch(zd,"towidth",line[ii].attr.towidth); zdialog_fetch(zd,"shwidth",line[ii].attr.shwidth); zdialog_fetch(zd,"shangle",line[ii].attr.shangle); genline(&line[ii].attr); // generate new line/arrow image line[ii].ww = line[ii].attr.pxb_line->ww; line[ii].hh = line[ii].attr.pxb_line->hh; line[ii].px0 = Mxclick; // initial position line[ii].py0 = Myclick; select("line",ii); // set selected line/arrow image setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return; } // remove line/arrow entry from line[ii] void mashup::remove_line(int ii) { using namespace mashup; select("line",ii); setlayoutupdatearea(); // set layout update area PXB_free(line[ii].attr.pxb_line); for (int jj = ii+1; jj < Nline; jj++) // pack down line list line[jj-1] = line[jj]; Nline--; // line/arrow count select("",-1); // nothing selected Fupdall = 1; Lupdate(); // update layout composite image return; } // flash a selected line/arrow image as bright as possible for a short moment void mashup::flashline() { int ii, jj, red, green, blue; char savecolor[4][20], flashcolor[20]; if (! strmatch(focus,"line")) return; ii = focusii; for (jj = 0; jj < 4; jj++) // save line colors strcpy(savecolor[jj],line[ii].attr.color[jj]); for (jj = 0; jj < 4; jj++) { // get complimentary colors red = 255 - atoi(strField(line[ii].attr.color[jj],'|',1)); green = 255 - atoi(strField(line[ii].attr.color[jj],'|',2)); blue = 255 - atoi(strField(line[ii].attr.color[jj],'|',3)); snprintf(flashcolor,20,"%d|%d|%d",red,green,blue); strcpy(line[ii].attr.color[jj],flashcolor); } genline(&line[ii].attr); // generate complimentary image zmainloop(); setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image zsleep(0.05); // hold for a moment for (jj = 0; jj < 4; jj++) // restore line colors strcpy(line[ii].attr.color[jj],savecolor[jj]); genline(&line[ii].attr); // gemerate normal line image zmainloop(); setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return; } /********************************************************************************/ // select an image, text image, or line/arrow image - update active dialog // which = "image" or "text" or "line" or "" to select nothing void mashup::select(cchar *which, int ii) { using namespace mashup; char *pp; float rad = 180 / PI; zdialog *zd; if (strmatch(which,"image") && ii >= 0) { if (ii >= Nimage) zappcrash("bad image index"); focus = "image"; // for KB key drags focusii = ii; zd = zdimage; if (! zd) return; // no active edit image dialog pp = strrchr(image[ii].file,'/'); // stuff zdialog fields zdialog_stuff(zd,"currfile",pp+1); // from selected image zdialog_stuff(zd,"scale",image[ii].scale); zdialog_stuff(zd,"angle",image[ii].theta * rad); zdialog_stuff(zd,"Btransp",image[ii].Btransp); zdialog_stuff(zd,"Lmarg",image[ii].Lmarg); zdialog_stuff(zd,"Rmarg",image[ii].Rmarg); zdialog_stuff(zd,"Tmarg",image[ii].Tmarg); zdialog_stuff(zd,"Bmarg",image[ii].Bmarg); zdialog_stuff(zd,"Lblend",image[ii].Lblend); zdialog_stuff(zd,"Rblend",image[ii].Rblend); zdialog_stuff(zd,"Tblend",image[ii].Tblend); zdialog_stuff(zd,"Bblend",image[ii].Bblend); return; } if (strmatch(which,"text") && ii >= 0) { if (ii >= Ntext) zappcrash("bad text index"); focus = "text"; // for KB key drags focusii = ii; zd = zdtext; if (! zd) return; // no active edit text dialog zdialog_stuff(zd,"text",text[ii].attr.text); // stuff zdialog fields zdialog_stuff(zd,"fontname",text[ii].attr.font); // from selected text image zdialog_stuff(zd,"fontsize",text[ii].attr.size); zdialog_stuff(zd,"angle",text[ii].attr.angle); zdialog_stuff(zd,"fgcolor",text[ii].attr.color[0]); zdialog_stuff(zd,"bgcolor",text[ii].attr.color[1]); zdialog_stuff(zd,"tocolor",text[ii].attr.color[2]); zdialog_stuff(zd,"shcolor",text[ii].attr.color[3]); zdialog_stuff(zd,"fgtransp",text[ii].attr.transp[0]); zdialog_stuff(zd,"bgtransp",text[ii].attr.transp[1]); zdialog_stuff(zd,"totransp",text[ii].attr.transp[2]); zdialog_stuff(zd,"shtransp",text[ii].attr.transp[3]); zdialog_stuff(zd,"towidth",text[ii].attr.towidth); zdialog_stuff(zd,"shwidth",text[ii].attr.shwidth); zdialog_stuff(zd,"shangle",text[ii].attr.shangle); return; } if (strmatch(which,"line") && ii >= 0) { if (ii >= Nline) zappcrash("bad line index"); focus = "line"; // for KB key drags focusii = ii; zd = zdline; if (! zd) return; // no active edit line/arrow dialog zdialog_stuff(zd,"length",line[ii].attr.length); zdialog_stuff(zd,"width",line[ii].attr.width); zdialog_stuff(zd,"larrow",line[ii].attr.larrow); zdialog_stuff(zd,"rarrow",line[ii].attr.rarrow); zdialog_stuff(zd,"angle",line[ii].attr.angle); zdialog_stuff(zd,"fgcolor",line[ii].attr.color[0]); zdialog_stuff(zd,"bgcolor",line[ii].attr.color[1]); zdialog_stuff(zd,"tocolor",line[ii].attr.color[2]); zdialog_stuff(zd,"shcolor",line[ii].attr.color[3]); zdialog_stuff(zd,"fgtransp",line[ii].attr.transp[0]); zdialog_stuff(zd,"bgtransp",line[ii].attr.transp[1]); zdialog_stuff(zd,"totransp",line[ii].attr.transp[2]); zdialog_stuff(zd,"shtransp",line[ii].attr.transp[3]); zdialog_stuff(zd,"towidth",line[ii].attr.towidth); zdialog_stuff(zd,"shwidth",line[ii].attr.shwidth); zdialog_stuff(zd,"shangle",line[ii].attr.shangle); return; } focus = ""; // no selection focusii = -1; zd = zdimage; if (! zd) return; zdialog_stuff(zd,"currfile","*********"); // mark obvious no selection return; } // Set the update area for the layout based on // the current selected image, text, or line/arrow. void mashup::setlayoutupdatearea() { using namespace mashup; int pxlo, pxhi, pylo, pyhi; int ii, px, py, px0, py0; int ww, hh, ww2, hh2; float cosT, sinT; Fupdall = 0; if (strmatch(focus,"image")) { ii = focusii; px0 = image[ii].px0; py0 = image[ii].py0; ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; cosT = image[ii].cosT; sinT = image[ii].sinT; pxlo = pxhi = px0; // NW corner pylo = pyhi = py0; px = px0 + ww2 * cosT; // NE if (px < pxlo) pxlo = px; if (px > pxhi) pxhi = px; py = py0 + ww2 * sinT; if (py < pylo) pylo = py; if (py > pyhi) pyhi = py; px = px0 + ww2 * cosT - hh2 * sinT; // SE if (px < pxlo) pxlo = px; if (px > pxhi) pxhi = px; py = py0 + hh2 * cosT + ww2 * sinT; if (py < pylo) pylo = py; if (py > pyhi) pyhi = py; px = px0 - hh2 * sinT; // SW if (px < pxlo) pxlo = px; if (px > pxhi) pxhi = px; py = py0 + hh2 * cosT; if (py < pylo) pylo = py; if (py > pyhi) pyhi = py; if (pxlo < 0) pxlo = 0; if (pxhi > Lww) pxhi = Lww; if (pylo < 0) pylo = 0; if (pyhi > Lhh) pyhi = Lhh; updxlo = image[ii].pxlo = pxlo; updxhi = image[ii].pxhi = pxhi; updylo = image[ii].pylo = pylo; updyhi = image[ii].pyhi = pyhi; } else if (strmatch(focus,"text")) { ii = focusii; px0 = text[ii].px0; py0 = text[ii].py0; ww = text[ii].ww; hh = text[ii].hh; updxlo = text[ii].pxlo = px0; // set update region updxhi = text[ii].pxhi = px0 + ww; updylo = text[ii].pylo = py0; updyhi = text[ii].pyhi = py0 + hh; } else if (strmatch(focus,"line")) { ii = focusii; px0 = line[ii].px0; py0 = line[ii].py0; ww = line[ii].ww; hh = line[ii].hh; updxlo = line[ii].pxlo = px0; // set update region updxhi = line[ii].pxhi = px0 + ww; updylo = line[ii].pylo = py0; updyhi = line[ii].pyhi = py0 + hh; } return; } // mouse function - called for mouse events inside layout image // move and resize the overlay images, text images, line/arrow images void mashup::mousefunc_layout() { using namespace mashup; static int mdx0 = 0, mdy0 = 0, mdx1, mdy1; static int Mcen, Mcor, Tcen, Tcor, Lcen, Lcor; // mouse at image/text/line center/corner static int iiMcen, iiTcen, iiLcen, iiMcor, iiTcor, iiLcor; // selected image/text/line center/corner static int Tcorx, Tcory; int cenx, ceny; int minMcen, minMcor, minTcen, minTcor, minLcen, minLcor; int ii, Fflash, Fmove, Fnew; int mx, my, mx0, my0; int ww, hh, ww2, hh2; int pxlo, pxhi, pylo, pyhi; int length; float dist, distx, disty, Cdist, Mdist, Mdist0; float expand, contract; float scale, thresh, size; float px0, py0, px1, py1, px2, py2, px3, py3; float tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3; float Vcx, Vcy, Vmx, Vmy; float sinT, cosT; float leng2, amx, amy, d1, d2, angle, rad; if (LMclick) { // left click LMclick = 0; Fnextimage = 0; mx0 = mx = Mxclick; // mouse position my0 = my = Myclick; Fnew = 1; // new image or text selected Fmove = 0; Fflash = 1; select("",-1); // unselect prior } else if (Mxdrag || Mydrag) // drag underway { Fnew = 0; // assume same image/text as before Fmove = 1; // is being moved Fflash = 0; if (Mxdown != mdx0 || Mydown != mdy0) { // new drag was initiated mdx0 = mdx1 = Mxdown; mdy0 = mdy1 = Mydown; Fnew = 1; // new image or text selected } mx0 = mdx1; // drag start my0 = mdy1; mx = Mxdrag; // drag position my = Mydrag; mdx1 = mx; // next drag start mdy1 = my; if (mx == mx0 && my == my0) Fmove = 0; // no mouse movement Mxdrag = Mydrag = 0; // stop main window drag after return } else return; // ignore simple mouse movement if (Fnextimage) { // image set from [next] button Fnextimage = 0; Fnew = 0; Mcen = 1; iiMcen = focusii; } if (zdaddtext && (Mxclick + Myclick)) { // return clicked position zdaddtext->zstat = 1; zdialog_destroy(zdaddtext); return; } if (zdaddline && (Mxclick + Myclick)) { // return clicked position zdaddline->zstat = 1; zdialog_destroy(zdaddline); return; } thresh = 50; // corner match threshold, pixels 20.0 if (Fnew) // search all images and text images { minMcen = 9999; // find closest image center to mouse for (ii = 0; ii < Nimage; ii++) { px0 = image[ii].px0; // image position and rotation py0 = image[ii].py0; // in layout image space sinT = image[ii].sinT; cosT = image[ii].cosT; ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; px2 = (mx-px0) * cosT + (my-py0) * sinT; // image pixel for layout position py2 = (my-py0) * cosT - (mx-px0) * sinT; if (px2 < 0 || px2 > ww2-1) continue; // mouse not within image if (py2 < 0 || py2 > hh2-1) continue; pxlo = image[ii].pxlo; // image extent pxhi = image[ii].pxhi; pylo = image[ii].pylo; pyhi = image[ii].pyhi; cenx = (pxlo + pxhi) / 2; // image center ceny = (pylo + pyhi) / 2; distx = mx - cenx; disty = my - ceny; dist = sqrtf(distx * distx + disty * disty); // mouse/center distance if (dist < minMcen) { minMcen = dist; iiMcen = ii; // save closest image center } } minMcor = 9999; // find closest image corner to mouse for (ii = 0; ii < Nimage; ii++) { px0 = image[ii].px0; // image position and rotation py0 = image[ii].py0; // in layout image space sinT = image[ii].sinT; cosT = image[ii].cosT; ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; px2 = (mx-px0) * cosT + (my-py0) * sinT; // image pixel for layout position py2 = (my-py0) * cosT - (mx-px0) * sinT; if (px2 < 0 || px2 > ww2-1) continue; // mouse not within image if (py2 < 0 || py2 > hh2-1) continue; px0 = image[ii].px0; // NW corner py0 = image[ii].py0; distx = mx - px0; disty = my - py0; dist = sqrtf(distx * distx + disty * disty); // mouse/corner distance if (dist < minMcor && dist < thresh) { minMcor = dist; iiMcor = ii; // save closest image corner } px1 = px0 + ww2 * cosT; // NE py1 = py0 + ww2 * sinT; distx = mx - px1; disty = my - py1; dist = sqrtf(distx * distx + disty * disty); if (dist < minMcor && dist < thresh) { minMcor = dist; iiMcor = ii; } px2 = px0 + ww2 * cosT - hh2 * sinT; // SE py2 = py0 + hh2 * cosT + ww2 * sinT; distx = mx - px2; disty = my - py2; dist = sqrtf(distx * distx + disty * disty); if (dist < minMcor && dist < thresh) { minMcor = dist; iiMcor = ii; } px3 = px0 - hh2 * sinT; // SW py3 = py0 + hh2 * cosT; distx = mx - px3; disty = my - py3; dist = sqrtf(distx * distx + disty * disty); if (dist < minMcor && dist < thresh) { minMcor = dist; iiMcor = ii; } } minTcen = 9999; // find closest text image center to mouse for (ii = 0; ii < Ntext; ii++) { pxlo = text[ii].pxlo; // image extent pxhi = text[ii].pxhi; pylo = text[ii].pylo; pyhi = text[ii].pyhi; if (mx < pxlo || mx > pxhi) continue; // mouse not within text image if (my < pylo || my > pyhi) continue; cenx = (pxlo + pxhi) / 2; // text image center ceny = (pylo + pyhi) / 2; distx = mx - cenx; disty = my - ceny; dist = sqrtf(distx * distx + disty * disty); // mouse/center distance if (dist < minTcen) { minTcen = dist; iiTcen = ii; } } minTcor = 9999; // find closest text image corner to mouse Tcorx = Tcory = 0; for (ii = 0; ii < Ntext; ii++) { pxlo = text[ii].pxlo; // text image extent pxhi = text[ii].pxhi; pylo = text[ii].pylo; pyhi = text[ii].pyhi; if (mx < pxlo || mx > pxhi) continue; // mouse not within text image if (my < pylo || my > pyhi) continue; ww = text[ii].attr.tww; // unrotated text rectangle size hh = text[ii].attr.thh; sinT = text[ii].attr.sinT; // angle of text cosT = text[ii].attr.cosT; px0 = text[ii].px0; // NW corner of enclosing rectangle py0 = text[ii].py0; if (text[ii].attr.angle > 0) { tx0 = px0 + hh * sinT; // NW corner of text rectangle ty0 = py0; tx1 = px0 + ww * cosT + hh * sinT; // NE ty1 = py0 + ww * sinT; tx2 = px0 + ww * cosT; // SE ty2 = py0 + ww * sinT + hh * cosT; tx3 = px0; // SW ty3 = py0 + hh * cosT; } else { sinT = -sinT; tx0 = px0; // NW ty0 = py0 + ww * sinT; tx1 = px0 + ww * cosT; // NE ty1 = py0; tx2 = px0 + ww * cosT + hh * sinT; // SE ty2 = py0 + hh * cosT; tx3 = px0 + hh * sinT; // SW ty3 = py0 + ww * sinT + hh * cosT; } distx = mx - tx0; // NW corner disty = my - ty0; dist = sqrtf(distx * distx + disty * disty); // mouse/corner distance if (dist < minTcor && dist < thresh) { minTcor = dist; iiTcor = ii; Tcorx = tx0; // save closest text image corner Tcory = ty0; } distx = mx - tx1; // NE disty = my - ty1; dist = sqrtf(distx * distx + disty * disty); if (dist < minTcor && dist < thresh) { minTcor = dist; iiTcor = ii; Tcorx = tx1; Tcory = ty1; } distx = mx - tx2; // SE disty = my - ty2; dist = sqrtf(distx * distx + disty * disty); if (dist < minTcor && dist < thresh) { minTcor = dist; iiTcor = ii; Tcorx = tx2; Tcory = ty2; } distx = mx - tx3; // SW disty = my - ty3; dist = sqrtf(distx * distx + disty * disty); if (dist < minTcor && dist < thresh) { minTcor = dist; iiTcor = ii; Tcorx = tx3; Tcory = ty3; } } minLcen = 9999; // find closest line/arrow image // center to mouse position for (ii = 0; ii < Nline; ii++) { pxlo = line[ii].pxlo; // image extent pxhi = line[ii].pxhi; pylo = line[ii].pylo; pyhi = line[ii].pyhi; if (mx < pxlo || mx > pxhi) continue; // mouse not within line image if (my < pylo || my > pyhi) continue; cenx = (pxlo + pxhi) / 2; // line image center ceny = (pylo + pyhi) / 2; distx = mx - cenx; disty = my - ceny; dist = sqrtf(distx * distx + disty * disty); // mouse/center distance if (dist < minLcen) { minLcen = dist; iiLcen = ii; } } minLcor = 9999; // find closest line end point to mouse for (ii = 0; ii < Nline; ii++) { pxlo = line[ii].pxlo; // line image extent pxhi = line[ii].pxhi; pylo = line[ii].pylo; pyhi = line[ii].pyhi; if (mx < pxlo || mx > pxhi) continue; // mouse not within line image if (my < pylo || my > pyhi) continue; angle = line[ii].attr.angle; rad = -angle / 57.296; length = line[ii].attr.length; leng2 = length / 2.0; ww2 = line[ii].attr.pxb_line->ww; // line image hh2 = line[ii].attr.pxb_line->hh; amx = ww2 / 2.0; // line midpoint within line image amy = hh2 / 2.0; px1 = amx - leng2 * cosf(rad) + 0.5; // line end points py1 = amy + leng2 * sinf(rad) + 0.5; px2 = amx + leng2 * cosf(rad) + 0.5; py2 = amy - leng2 * sinf(rad) + 0.5; d1 = (mx-px1-pxlo) * (mx-px1-pxlo) + (my-py1-pylo) * (my-py1-pylo); d2 = (mx-px2-pxlo) * (mx-px2-pxlo) + (my-py2-pylo) * (my-py2-pylo); d1 = sqrtf(d1); // mouse - end point distance d2 = sqrtf(d2); if (d1 < minLcor && d1 < thresh) { minLcor = d1; iiLcor = ii; } if (d2 < minLcor && d2 < thresh) { minLcor = d2; iiLcor = ii; } } Mcen = Mcor = Tcen = Tcor = Lcen = Lcor = 0; // mark where mouse is acting: if (Fmove) // recognize drags from anywhere { if (minMcen < minMcor && minMcen < minTcen && minMcen < minTcor && minMcen < minLcen && minMcen < minLcor) Mcen = 1; if (minMcor < minMcen && minMcor < minTcen && minMcor < minTcor && minMcor < minLcen && minMcor < minLcor) Mcor = 1; if (minTcen < minMcen && minTcen < minMcor && minTcen < minTcor && minTcen < minLcen && minTcen < minLcor) Tcen = 1; if (minTcor < minMcen && minTcor < minMcor && minTcor < minTcen && minTcor < minLcen && minTcor < minLcor) Tcor = 1; if (minLcen < minMcen && minLcen < minMcor && minLcen < minTcen && minLcen < minTcor && minLcen < minLcor) Lcen = 1; if (minLcor < minMcen && minLcor < minMcor && minLcor < minTcen && minLcor < minTcor && minLcor < minLcen) Lcor = 1; } else // recognize clicks near center only { if (minMcen < minTcen && minMcen < minLcen) Mcen = 1; if (minTcen < minMcen && minTcen < minLcen) Tcen = 1; if (minLcen < minMcen && minLcen < minTcen) Lcen = 1; } } if (Mcen) { ii = iiMcen; select("image",ii); } // select the corresp. image, text or line if (Mcor) { ii = iiMcor; select("image",ii); } if (Tcen) { ii = iiTcen; select("text",ii); } if (Tcor) { ii = iiTcor; select("text",ii); } if (Lcen) { ii = iiLcen; select("line",ii); } if (Lcor) { ii = iiLcor; select("line",ii); } if (Fflash) { if (Mcen) flashimage(); // if click on image, flash it if (Tcen) flashtext(); // same for text if (Lcen) flashline(); // same for line return; } if (Fmove) { if (Mcen) // move image { ii = iiMcen; if (mx0 < image[ii].pxlo || mx0 > image[ii].pxhi) return; // mouse outside image bounds if (my0 < image[ii].pylo || my0 > image[ii].pyhi) return; select("image",ii); image[ii].px0 += (mx - mx0); // move selected image image[ii].py0 += (my - my0); // by mouse drag amount } if (Mcor) // resize image by dragging a corner { ii = iiMcor; // if (mx0 < image[ii].pxlo || mx0 > image[ii].pxhi) return; // mouse outside image bounds // if (my0 < image[ii].pylo || my0 > image[ii].pyhi) return; // removed 20.0 select("image",ii); pxlo = image[ii].pxlo; pxhi = image[ii].pxhi; pylo = image[ii].pylo; pyhi = image[ii].pyhi; cenx = (pxlo + pxhi) / 2; // image center ceny = (pylo + pyhi) / 2; distx = mx0 - cenx; disty = my0 - ceny; Mdist0 = sqrtf(distx * distx + disty * disty); // center - mouse drag start distx = mx - cenx; disty = my - ceny; Mdist = sqrtf(distx * distx + disty * disty); // center - mouse drag end scale = image[ii].scale * 0.5 * (Mdist + Mdist0) / Mdist0; // image scale change image_rescale(ii,scale); } if (Tcen) // move text image { ii = iiTcen; if (mx0 < text[ii].pxlo || mx0 > text[ii].pxhi) return; // mouse outside text bounds if (my0 < text[ii].pylo || my0 > text[ii].pyhi) return; select("text",ii); text[ii].px0 += (mx - mx0); // move selected text image text[ii].py0 += (my - my0); // by mouse drag amount } if (Tcor) // resize text image { ii = iiTcor; // if (mx0 < text[ii].pxlo || mx0 > text[ii].pxhi) return; // mouse outside text bounds // if (my0 < text[ii].pylo || my0 > text[ii].pyhi) return; // removed 20.0 select("text",ii); pxlo = text[ii].pxlo; pxhi = text[ii].pxhi; pylo = text[ii].pylo; pyhi = text[ii].pyhi; cenx = (pxlo + pxhi) / 2; // text image center ceny = (pylo + pyhi) / 2; distx = Tcorx - cenx; disty = Tcory - ceny; Cdist = sqrtf(distx * distx + disty * disty); Vcx = distx / Cdist; // unit vector, text image center to corner Vcy = disty / Cdist; distx = mx - mx0; disty = my - my0; Mdist = sqrtf(distx * distx + disty * disty); Vmx = distx / Mdist; // unit vector, mouse movement Vmy = disty / Mdist; expand = fabsf(Vcx + Vmx) + fabsf(Vcy + Vmy); // mouse pull-out amount contract = fabsf(Vcx - Vmx) + fabsf(Vcy - Vmy); // push-in amount size = expand - contract; // change in text size if (size > 0.5) text[ii].attr.size++; if (size < -0.5) text[ii].attr.size--; gentext(&text[ii].attr); // generate new text image text[ii].ww = text[ii].attr.pxb_text->ww; text[ii].hh = text[ii].attr.pxb_text->hh; } if (Lcen) // move line/arrow image { ii = iiLcen; if (mx0 < line[ii].pxlo || mx0 > line[ii].pxhi) return; // mouse outside line bounds if (my0 < line[ii].pylo || my0 > line[ii].pyhi) return; select("line",ii); line[ii].px0 += (mx - mx0); // move selected line image line[ii].py0 += (my - my0); // by mouse drag amount } if (Lcor) // drag line end point { ii = iiLcor; if (mx0 < line[ii].pxlo || mx0 > line[ii].pxhi) return; // mouse outside line bounds if (my0 < line[ii].pylo || my0 > line[ii].pyhi) return; select("line",ii); angle = line[ii].attr.angle; rad = -angle / 57.296; length = line[ii].attr.length; leng2 = length / 2.0; ww2 = line[ii].attr.pxb_line->ww; // line image hh2 = line[ii].attr.pxb_line->hh; amx = ww2 / 2.0; // line midpoint within line image amy = hh2 / 2.0; px1 = amx - leng2 * cosf(rad) + 0.5; // line end points py1 = amy + leng2 * sinf(rad) + 0.5; px2 = amx + leng2 * cosf(rad) + 0.5; py2 = amy - leng2 * sinf(rad) + 0.5; pxlo = line[ii].pxlo; pylo = line[ii].pylo; d1 = (mx-px1-pxlo) * (mx-px1-pxlo) + (my-py1-pylo) * (my-py1-pylo); d2 = (mx-px2-pxlo) * (mx-px2-pxlo) + (my-py2-pylo) * (my-py2-pylo); d1 = sqrtf(d1); // mouse - end point distance d2 = sqrtf(d2); if (d1 < d2) { // move px1/py1 end to mouse px2 += pxlo; py2 += pylo; px1 = mx; py1 = my; length = d2 + 0.5; rad = asinf((py1-py2) / d2); angle = -57.296 * rad; if (mx > px2) angle = -180 - angle; } if (d2 < d1) { // move px2/py2 end to mouse px1 += pxlo; py1 += pylo; px2 = mx; py2 = my; length = d1 + 0.5; rad = asinf((py1-py2) / d1); angle = -57.296 * rad; if (mx < px1) angle = -180 - angle; } if (angle < -180) angle += 360; if (angle > 180) angle -= 360; line[ii].attr.angle = angle; line[ii].attr.length = length; genline(&line[ii].attr); line[ii].ww = line[ii].attr.pxb_line->ww; line[ii].hh = line[ii].attr.pxb_line->hh; ww2 = line[ii].attr.pxb_line->ww; hh2 = line[ii].attr.pxb_line->hh; amx = (px1 + px2) / 2.0; amy = (py1 + py2) / 2.0; pxlo = amx - ww2 / 2.0; pylo = amy - hh2 / 2.0; line[ii].px0 = pxlo; line[ii].py0 = pylo; } } setlayoutupdatearea(); Lupdate(); // update layout composite image return; } // Keyboard function // KB arrow keys act like mouse drag of 1 pixel for current image void mashup::KBfunc(int key) { using namespace mashup; int ii, xstep, ystep; xstep = ystep = 0; if (key == GDK_KEY_Left) xstep = -1; if (key == GDK_KEY_Right) xstep = +1; if (key == GDK_KEY_Up) ystep = -1; if (key == GDK_KEY_Down) ystep = +1; if (strmatch(focus,"image")) // choose last selected image { ii = focusii; image[ii].px0 += xstep; // move image 1 pixel image[ii].py0 += ystep; } else if (strmatch(focus,"text")) // last selected text image { ii = focusii; text[ii].px0 += xstep; // move selected text image 1 pixel text[ii].py0 += ystep; } else if (strmatch(focus,"line")) // last selected line/arrow image { ii = focusii; line[ii].px0 += xstep; // move selected line image 1 pixel line[ii].py0 += ystep; } else return; setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return; } // update layout image Fpxb from layout + overlay images // paint main window void mashup::Lupdate() { using namespace mashup; int nupdxlo, nupdxhi, nupdylo, nupdyhi; static int pupdxlo, pupdxhi, pupdylo, pupdyhi; // prior image update region static cchar *pfocus = "x"; static int pfocusii = -2; // no prior selected image or text int ww, hh; if (! Lpxb || ! Fpxb) return; if (! strmatch(focus,pfocus) || focusii != pfocusii) // focus image/text/line has changed Fupdall = 1; // update entire layout pfocus = focus; // remember focus image/text/line pfocusii = focusii; if (updxlo < 0) updxlo = 0; // keep within layout image if (updxhi > Lww) updxhi = Lww; if (updylo < 0) updylo = 0; if (updyhi > Lhh) updyhi = Lhh; nupdxlo = updxlo; // save update region nupdxhi = updxhi; nupdylo = updylo; nupdyhi = updyhi; if (! Fupdall) { if (pupdxlo < updxlo) updxlo = pupdxlo; // expand update region to if (pupdxhi > updxhi) updxhi = pupdxhi; // include prior update region if (pupdylo < updylo) updylo = pupdylo; if (pupdyhi > updyhi) updyhi = pupdyhi; } pupdxlo = nupdxlo; // save update region pupdxhi = nupdxhi; pupdylo = nupdylo; pupdyhi = nupdyhi; if (Fupdall) { // update entire layout updxlo = updylo = 0; updxhi = Lww; updyhi = Lhh; } Fupdatebusy = 1; start_detached_thread(Lupdate_thread,0); // trigger image update thread while (Fupdatebusy) zsleep(0.01); if (Fupdall) Fpaintnow(); // paint entire layout else { ww = updxhi-updxlo; hh = updyhi-updylo; PXB_PXB_update(Fpxb,Mpxb,updxlo,updylo,ww,hh); // Fpxb > Mpxb, scaled up or down Fpaint4(updxlo,updylo,ww,hh,0); // update drawing window from Mpxb } Fupdall = 0; return; } void * mashup::Lupdate_thread(void *) // main thread { using namespace mashup; if (! Lpxb || ! Fpxb) { Fupdatebusy = 0; pthread_exit(0); } do_wthreads(Lupdate_wthread,NWT); // worker threads Fupdatebusy = 0; pthread_exit(0); } void * mashup::Lupdate_wthread(void *arg) // worker thread for images { using namespace mashup; int index = *((int *) (arg)); int ii, jj, px1, py1, ww, hh, qx, qy; uint8 *pix1, *pix2, *pix3, vpix2[4]; float red, green, blue; float Bopac, Iopac, Vtran; float px0, py0, px2, py2; float ww1, hh1, ww2, hh2; float edgedist, margin, blendist; float scale, sinT, cosT; uint8 *vtranmap; PXB *pxb2; float e3part; for (py1 = updylo+index; py1 < updyhi; py1 += NWT) // loop layout image pixels for (px1 = updxlo; px1 < updxhi; px1++) // within update region { pix1 = PXBpix(Lpxb,px1,py1); // start with layout pixel red = pix1[0]; green = pix1[1]; blue = pix1[2]; for (ii = 0; ii < Nimage; ii++) // loop overlay images from bottom to top { if (image[ii].pxhi < updxlo) continue; // no overlap with update region if (image[ii].pxlo > updxhi) continue; if (image[ii].pyhi < updylo) continue; if (image[ii].pylo > updyhi) continue; px0 = image[ii].px0; // overlay image position and rotation py0 = image[ii].py0; // in layout image space ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; if (image[ii].theta == 0) { px2 = px1 - px0; // overlay image pixel for layout pixel py2 = py1 - py0; } else { sinT = image[ii].sinT; cosT = image[ii].cosT; px2 = (px1-px0) * cosT + (py1-py0) * sinT; // overlay image pixel for layout pixel py2 = (py1-py0) * cosT - (px1-px0) * sinT; } if (px2 < 0 || px2 > ww2-1) continue; // layout pixel not in this image if (py2 < 0 || py2 > hh2-1) continue; pxb2 = image[ii].pxb2; // overlay image virt. pixel if (! vpixel(pxb2,px2,py2,vpix2)) continue; // beyond the edge Bopac = 1.0 - image[ii].Btransp; // image base opacity 0...1 Iopac = vpix2[3] / 255.0; // alpha channel opacity 0...1 Iopac = Iopac * Bopac; // combined vtranmap = image[ii].vtranmap; if (vtranmap) // var. transparency (px2,py2) { ww1 = image[ii].ww1; // image size at 1x scale hh1 = image[ii].hh1; qx = px2 / image[ii].scale; // position in 1x image qy = py2 / image[ii].scale; if (qx < ww1 && qy < hh1) { // position is within image jj = qy * ww1 + qx; Vtran = vtranmap[jj] / 255.0; // var. transparency 0...1 Iopac = Iopac * (1 - Vtran); } } scale = image[ii].scale; if (image[ii].Lmarg || image[ii].Lblend) { // left edge hard or blend margin edgedist = px2 / scale; margin = image[ii].Lmarg; if (edgedist <= margin) Iopac = 0; else { edgedist -= margin; blendist = image[ii].Lblend; if (edgedist < blendist) Iopac = Iopac * edgedist / blendist; } } if (image[ii].Rmarg || image[ii].Rblend) { // right edge edgedist = (ww2 - px2) / scale; margin = image[ii].Rmarg; if (edgedist <= margin) Iopac = 0; else { edgedist -= margin; blendist = image[ii].Rblend; if (edgedist < blendist) Iopac = Iopac * edgedist / blendist; } } if (image[ii].Tmarg || image[ii].Tblend) { // top edge edgedist = py2 / scale; margin = image[ii].Tmarg; if (edgedist <= margin) Iopac = 0; else { edgedist -= margin; blendist = image[ii].Tblend; if (edgedist < blendist) Iopac = Iopac * edgedist / blendist; } } if (image[ii].Bmarg || image[ii].Bblend) { // bottom edge edgedist = (hh2 - py2) / scale; margin = image[ii].Bmarg; if (edgedist <= margin) Iopac = 0; else { edgedist -= margin; blendist = image[ii].Bblend; if (edgedist < blendist) Iopac = Iopac * edgedist / blendist; } } red = Iopac * vpix2[0] + (1 - Iopac) * red; // add image pixel over prior green = Iopac * vpix2[1] + (1 - Iopac) * green; blue = Iopac * vpix2[2] + (1 - Iopac) * blue; } for (ii = 0; ii < Ntext; ii++) // loop overlay text images { if (text[ii].pxhi < updxlo) continue; // no overlap with update region if (text[ii].pxlo > updxhi) continue; if (text[ii].pyhi < updylo) continue; if (text[ii].pylo > updyhi) continue; px0 = text[ii].px0; // text image position in layout space py0 = text[ii].py0; ww = text[ii].ww; // text image size in layout hh = text[ii].hh; qx = px1 - px0; // text image pixel for layout pixel qy = py1 - py0; if (qx < 0 || qx > ww-1) continue; // layout pixel not in this text image if (qy < 0 || qy > hh-1) continue; pxb2 = text[ii].attr.pxb_text; pix2 = PXBpix(pxb2,qx,qy); e3part = pix2[3] / 255.0; red = pix2[0] + e3part * red; green = pix2[1] + e3part * green; blue = pix2[2] + e3part * blue; } for (ii = 0; ii < Nline; ii++) // loop overlay line/arrow images { if (line[ii].pxhi < updxlo) continue; // no overlap with update region if (line[ii].pxlo > updxhi) continue; if (line[ii].pyhi < updylo) continue; if (line[ii].pylo > updyhi) continue; px0 = line[ii].px0; // line image position in layout space py0 = line[ii].py0; ww = line[ii].ww; // line image size in layout hh = line[ii].hh; qx = px1 - px0; // line image pixel for layout pixel qy = py1 - py0; if (qx < 0 || qx > ww-1) continue; // layout pixel not in this line image if (qy < 0 || qy > hh-1) continue; pxb2 = line[ii].attr.pxb_line; pix2 = PXBpix(pxb2,qx,qy); e3part = pix2[3] / 255.0; red = pix2[0] + e3part * red; green = pix2[1] + e3part * green; blue = pix2[2] + e3part * blue; } pix3 = PXBpix(Fpxb,px1,py1); // output image pixel pix3[0] = red; pix3[1] = green; pix3[2] = blue; } pthread_exit(0); // exit thread } /******************************************************************************** MONTAGE Make an image of selected images, arranged in a compact matrix format. *********************************************************************************/ namespace montage_names { int Fww; // frame image width (input) int Fhh; // frame image height (calculated) int Fmarg; // frame margin size (input) int Ncols; // image columns in frame (input) int Nrows; // image rows in frame (calculated) int Imarg; // image margin size (input) int Iww; // image width (calculated, fixed) typedef struct { int random; // random number for sorting int Ihh; // image height (calculated, variable) int Ipx, Ipy; // image position in frame image int row, col; // image row and column number char *file; // image filename PIXBUF *pixbuf; // scaled image pixbuf } image_t; #define maxNm 1000 // max. images in montage image_t image[maxNm]; // image data int Nimages; // actual image count int maxNc = 100; // max. columns int maxNr = 100; // max. rows int colH[100]; // column heights char uniquename[100]; // unique montage file name char montagepath[500]; // suggested folder to save file } // menu function void m_montage(GtkWidget *, const char *) { using namespace montage_names; int montage_dialog_event(zdialog* zd, const char *event); int montage_mapimages(void); int montage_showframe(void); int montage_sort(void); int montage_shuffle(void); int montage_spread(void); int montage_mapfile(void); PIXBUF *pixbuf, *pixbuf2; GError *gerror = 0; int ii, nn, err, zstat, ww, hh, makemap; int coldiff, coldiffB, coldiff2; image_t imageB[maxNm]; char text[100], *filename, *pp, *pp1, *pp2; float R; F1_help_topic = "montage"; if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (checkpend("all")) return; Fblock = 1; Nimages = 0; /*** ______________________________________ | Image Montage | | | | [Select Files] NN files selected | | Frame Width [___] Margin [___] | | Image Columns [___] Margin [___] | | | | [Proceed] [Cancel] | |______________________________________| ***/ zdialog *zd = zdialog_new(E2X("Image Montage"),Mwin,Bproceed,Bcancel,null); // [Select Files] NN files selected zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=3"); zdialog_add_widget(zd,"button","files","hbf",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","labfcount","hbf",Bnofileselected,"space=10"); // Frame Width [___] Margin [___] zdialog_add_widget(zd,"hbox","hbf","dialog"); zdialog_add_widget(zd,"label","labfw","hbf",E2X("Frame Width"),"space=3"); zdialog_add_widget(zd,"zspin","Fww","hbf","1000|30000|1|2000","space=3"); zdialog_add_widget(zd,"label","space","hbf",0,"space=5"); zdialog_add_widget(zd,"label","labfm","hbf",E2X("Margin"),"space=3"); zdialog_add_widget(zd,"zspin","Fmarg","hbf","0|100|1|30","space=3"); // Image Columns [___] Margin [___] zdialog_add_widget(zd,"hbox","hbi","dialog"); zdialog_add_widget(zd,"label","labir","hbi",E2X("Image Columns"),"space=3"); zdialog_add_widget(zd,"zspin","Ncols","hbi","1|100|1|4","space=3"); // max 100 columns zdialog_add_widget(zd,"label","space","hbi",0,"space=5"); zdialog_add_widget(zd,"label","labim","hbi",E2X("Margin"),"space=3"); zdialog_add_widget(zd,"zspin","Imarg","hbi","0|50|1|20","space=3"); zdialog_restore_inputs(zd); // restore previous inputs zdialog_run(zd,montage_dialog_event,"save"); // run dialog - parallel zstat = zdialog_wait(zd); // wait for completion if (zstat != 1) goto cleanup; zdialog_free(zd); zd = 0; Nrows = 1 + (GScount - 1) / Ncols; // required image rows if (Nrows > maxNr) { zmessageACK(Mwin,E2X("exceed %d rows"),maxNr); goto cleanup; } if (GScount < Ncols) Ncols = GScount; // reduce if fewer images Iww = Fww - 2 * Fmarg - (Ncols - 1) * Imarg; // frame width less margins Iww = Iww / Ncols; // scaled image width, all images zmainloop(); Ffuncbusy = 1; Fbusy_goal = GScount; Fbusy_done = 0; for (nn = ii = 0; ii < GScount; ii++) // loop all images { zmainloop(); image[nn].file = 0; image[nn].Ihh = 0; image[nn].Ipx = 0; image[nn].Ipy = 0; image[nn].row = 0; image[nn].col = 0; image[nn].pixbuf = 0; gerror = 0; pixbuf = gdk_pixbuf_new_from_file(GSfiles[ii],&gerror); // load pixbuf image of file if (! pixbuf) { zmessageACK(Mwin,"cannot read: %s",GSfiles[ii]); Fbusy_done++; continue; } if (gdk_pixbuf_get_has_alpha(pixbuf)) { // if alpha channel present, pixbuf2 = gdk_pixbuf_stripalpha(pixbuf); // remove it g_object_unref(pixbuf); pixbuf = pixbuf2; } image[nn].file = zstrdup(GSfiles[ii]); // image file name ww = gdk_pixbuf_get_width(pixbuf); // image dimensions hh = gdk_pixbuf_get_height(pixbuf); R = 1.0 * Iww / ww; // image scale ratio image[nn].Ihh = R * hh; // scaled image height image[nn].row = nn / Ncols; // initial image row image[nn].col = nn - Ncols * image[nn].row; // initial image column pixbuf2 = gdk_pixbuf_scale_simple(pixbuf,Iww,image[nn].Ihh,BILINEAR); image[nn].pixbuf = pixbuf2; g_object_unref(pixbuf); nn++; // accumulate image count Fbusy_done++; } Ffuncbusy = 0; Fbusy_goal = 0; Nimages = nn; // final image count if (! Nimages) { zmessageACK(Mwin,"no images were found"); goto cleanup; } Nrows = 1 + (Nimages - 1) / Ncols; // final row count strncpy0(montagepath,image[0].file,500); // first selected image file 20.0 pp = strrchr(montagepath,'/'); // use this folder as default if (pp) *pp = 0; // montage file output location coldiff = montage_mapimages(); // map scaled images into frame image err = montage_showframe(); // show completed frame image if (err) goto optdone; for (ii = 0; ii < Nimages; ii++) // best map = initial map imageB[ii] = image[ii]; coldiffB = coldiff; if (coldiff < 3) { zstat = 2; goto optdone; } /*** ___________________________________ | Optimize | | | | column difference: NNN pixels | | | | [Start] [Stop] [Cancel] | |___________________________________| ***/ zd = zdialog_new(E2X("Optimize"),Mwin,Bstart,Bstop,Bcancel,null); zdialog_add_widget(zd,"label","labdiff","dialog","stuffed"); zdialog_set_modal(zd); zdialog_run(zd,null,"save"); // run dialog zstat = 0; // status = not started while (true) // optimization loop { snprintf(text,100,E2X("column difference: %d pixels"),coldiffB); // show best column diff. so far zdialog_stuff(zd,"labdiff",text); zmainloop(); if (zd->zstat) { // dialog status change zstat = zd->zstat; // new status zd->zstat = 0; // keep dialog active } if (zstat == 0) { // not started zsleep(0.1); continue; // wait for start } if (zstat < 0 || zstat >= 2) goto optdone; // [x] or [done] or [cancel] Ffuncbusy = 1; // [start] or continue while (true) { coldiff2 = coldiff; // shuffle() until no improvement montage_shuffle(); coldiff = montage_mapimages(); if (coldiff >= coldiff2) break; } if (coldiff < coldiffB) { for (ii = 0; ii < Nimages; ii++) // save new best map imageB[ii] = image[ii]; coldiffB = coldiff; err = montage_showframe(); // show completed frame image if (err) goto optdone; } if (coldiff < 3) { // stop if column diff. is tiny zstat = 2; goto optdone; } montage_sort(); // re-sort all images coldiff = montage_mapimages(); // compute new column difference } optdone: Ffuncbusy = 0; if (zstat != 2) goto cleanup; // quit if [cancel] or [x] zdialog_free(zd); zd = 0; for (ii = 0; ii < Nimages; ii++) // restore best result image[ii] = imageB[ii]; coldiff = montage_mapimages(); if (coldiff > 1) { int yn = zmessageYN(Mwin,E2X("column difference: %d pixels \n" "Make columns even?"),coldiff); if (yn) { montage_mapimages(); montage_spread(); montage_showframe(); } } /*** ___________________________________ | Save with unique montage name | | | | unique name: [__________________] | | | | [x] create image map | | | | [save] [cancel] | |___________________________________| ***/ zd = zdialog_new(E2X("Save with unique montage name"),Mwin,Bsave,Bcancel,null); zdialog_add_widget(zd,"hbox","hbname","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labname","hbname",E2X("unique name:"),"space=5"); zdialog_add_widget(zd,"zentry","uniquename","hbname","montage","size=30"); zdialog_add_widget(zd,"check","createmap","dialog",E2X("create image map"),"space=10"); pp = image[0].file; // default name: montage-filename 20.0 pp = strrchr(pp,'/'); if (pp) snprintf(uniquename,100,"montage-%s",pp+1); pp = strrchr(uniquename,'.'); // remove .ext if (pp) *pp = 0; zdialog_stuff(zd,"uniquename",uniquename); zdialog_run(zd,null,"save"); while (true) { zstat = zdialog_wait(zd); if (zstat != 1) break; zdialog_fetch(zd,"uniquename",uniquename,86); // get unique montage file name if (strlen(uniquename) > 3) break; zmessageACK(Mwin,E2X("supply a reasonable name")); zd->zstat = 0; } if (zstat != 1) goto cleanup; // user canceled strncatv(montagepath,500,"/",uniquename,null); // .../uniquename zdialog_fetch(zd,"createmap",makemap); // create image map file? if (makemap) strncatv(montagepath,500," (fotoxx montage)",null); // .../uniquename (fotoxx montage) strncatv(montagepath,500,".jpg",null); // .../uniquename [...].jpg filename = zgetfile(E2X("save montage"),MWIN,"save",montagepath,0); // save montage file, user choice if (! filename) goto cleanup; err = f_save(filename,"jpg",8,0,1); if (err) goto cleanup; zdialog_free(zd); zd = 0; f_open(filename); // make it the current file pp1 = strrchr(filename,'/'); // get actual file name used if (! pp1) goto cleanup; pp2 = strstr(pp1," (fotoxx montage)"); // get uniquename, may have changed if (! pp2) goto cleanup; *pp2 = 0; strncpy0(uniquename,pp1+1,100); zfree(filename); if (makemap) { err = montage_mapfile(); // create corresponding map file if (! err) zmessageACK(Mwin,E2X("map file saved: %s"),uniquename); } cleanup: if (zd) zdialog_free(zd); // kill dialog for (ii = 0; ii < Nimages; ii++) g_object_unref(image[ii].pixbuf); // free memory Fblock = 0; Ffuncbusy = 0; gallery(montagepath,"init",0); // 20.0 gallery(image[0].file,"paint",0); m_viewmode(0,"G"); return; } // montage dialog event and completion function int montage_dialog_event(zdialog *zd, const char *event) { using namespace montage_names; char countmess[80]; if (strmatch(event,"files")) // select images to process { zdialog_show(zd,0); gallery_select_clear(); // 20.0 gallery_select(); // get file list snprintf(countmess,80,Bfileselected,GScount); zdialog_stuff(zd,"labfcount",countmess); zdialog_show(zd,1); } if (! zd->zstat) return 1; // wait for completion if (zd->zstat != 1) return 1; // user cancel zd->zstat = 0; // keep dialog active if (GScount == 0) { // no files selected zmessageACK(Mwin,Bnofileselected); return 1; } if (GScount > maxNm) { zmessageACK(Mwin,E2X("%d max images exceeded"),maxNm); return 1; } zdialog_fetch(zd,"Fww",Fww); // frame dimensions zdialog_fetch(zd,"Fmarg",Fmarg); // frame margin zdialog_fetch(zd,"Ncols",Ncols); // image columns zdialog_fetch(zd,"Imarg",Imarg); // image margin zd->zstat = 1; // dialog finished return 1; } // Map scaled image positions into frame image. // Calculate max. difference in image column height. int montage_mapimages() { using namespace montage_names; int ii, jj; int maxcol, mincol; int row, col, bott; for (ii = 0; ii < Nimages; ii++) // loop all images { row = image[ii].row; // image row col = image[ii].col; // image column jj = ii - Ncols; // prior image in same column image[ii].Ipx = Fmarg + col * (Iww + Imarg); // image x position if (row == 0) image[ii].Ipy = Fmarg; // image y position for row 0 else image[ii].Ipy = image[jj].Ipy + image[jj].Ihh + Imarg; // image y position for row 1+ } maxcol = 0; mincol = 999999; for (ii = Nimages - Ncols; ii < Nimages; ii++) // loop last Ncols images { bott = image[ii].Ipy + image[ii].Ihh; // image y position + height if (bott > maxcol) maxcol = bott; // save max. bottom edge if (bott < mincol) mincol = bott; // save min. bottom edge col = image[ii].col; colH[col] = bott; // save column height } return (maxcol - mincol); // return column height difference } // fill the frame image from scaled images and show the frame image int montage_showframe() { using namespace montage_names; uint8 *Fpixels, *pixel; int ii, px, py, Frs, col, maxcol = 0; int ww, hh, stat, bott, err; char *file; PIXBUF *Fpixbuf, *pixbuf; GError *gerror = 0; for (ii = Nimages - Ncols; ii < Nimages; ii++) // loop last Ncols images { bott = image[ii].Ipy + image[ii].Ihh; // image y position + height if (bott > maxcol) maxcol = bott; // save max. bottom edge col = image[ii].col; colH[col] = bott; // save column height } Fhh = maxcol + Fmarg; // set frame image height if (Fhh > wwhh_limit1) { zmessageACK(Mwin,E2X("image frame is too large: %d x %d"),Fww,Fhh); return 1; } Fpixbuf = gdk_pixbuf_new(GDKRGB,0,8,Fww,Fhh); // make frame image pixbuf if (! Fpixbuf) { zmessageACK(Mwin,E2X("image frame is too large: %d x %d"),Fww,Fhh); return 1; // error return } Fpixels = gdk_pixbuf_get_pixels(Fpixbuf); // fill frame image with white Frs = gdk_pixbuf_get_rowstride(Fpixbuf); for (py = 0; py < Fhh; py++) for (px = 0; px < Fww; px++) { pixel = Fpixels + py * Frs + px * 3; pixel[0] = pixel[1] = pixel[2] = 255; } for (ii = 0; ii < Nimages; ii++) // loop all images { pixbuf = image[ii].pixbuf; // copy scaled image into frame image ww = gdk_pixbuf_get_width(pixbuf); hh = gdk_pixbuf_get_height(pixbuf); gdk_pixbuf_copy_area(pixbuf,0,0,ww,hh,Fpixbuf,image[ii].Ipx,image[ii].Ipy); zmainloop(); } file = zstrdup(temp_folder,30); // save frame image to temp file strcat(file,"/image (fotoxx montage).jpg"); stat = gdk_pixbuf_save(Fpixbuf,file,"jpeg",&gerror,"quality","90",null); if (! stat) { zmessageACK(Mwin,"GDK pixbuf: cannot save file"); zfree(file); g_object_unref(Fpixbuf); return 2; // error return } m_viewmode(0,"F"); err = f_open(file); // show frame image if (err) return 3; zfree(file); g_object_unref(Fpixbuf); return 0; } // shuffle image column assignments to make column heights even int montage_shuffle() { using namespace montage_names; int ii, jj, col, colii, coljj; int hh1, hh2, diff, maxdiff; int bestii, bestjj; image_t tempimage; for (ii = Nimages - Ncols; ii < Nimages; ii++) // loop last Ncols images { col = image[ii].col; // get column height colH[col] = image[ii].Ipy + image[ii].Ihh; // = y position + image height } for (ii = 0; ii < Nimages; ii++) // loop all images ii { zmainloop(); colii = image[ii].col; maxdiff = bestii = 0; for (jj = 0; jj < Nimages; jj++) // loop all images jj { coljj = image[jj].col; if (colii == coljj ) continue; // both images in same column hh1 = colH[colii] - image[ii].Ihh + image[jj].Ihh; // column heights if images are switched hh2 = colH[coljj] + image[ii].Ihh - image[jj].Ihh; diff = abs(colH[colii] - colH[coljj]) - abs(hh1 - hh2); // column height diff - switched diff if (diff > maxdiff) { maxdiff = diff; // best switch for nearest heights bestii = ii; bestjj = jj; } } if (maxdiff > 0) { ii = bestii; // switch the images jj = bestjj; col = image[ii].col; colH[col] = colH[col] - image[ii].Ihh + image[jj].Ihh; col = image[jj].col; colH[col] = colH[col] + image[ii].Ihh - image[jj].Ihh; tempimage = image[ii]; image[ii] = image[jj]; image[jj] = tempimage; image[jj].row = image[ii].row; image[jj].col = image[ii].col; image[ii].row = tempimage.row; image[ii].col = tempimage.col; } } return 0; } // sort images randomly to get a new starting point for shuffle int montage_sort() { using namespace montage_names; int ii; int keys[1][3] = { { 0, 4, 3 } }; // key position, length, type for (ii = 0; ii < Nimages; ii++) // populate random sort key image[ii].random = 1000000 * drandz(); MemSort((char *) image, sizeof(image_t), Nimages, keys, 1); // sort image[] for (ii = 0; ii < Nimages; ii++) { image[ii].row = ii / Ncols; // new image row, column image[ii].col = ii - Ncols * image[ii].row; } return 1; } // spread images in short columns to make column heights even. int montage_spread() { using namespace montage_names; int col, row, nrows, ii, dy; int maxH = 0, diff, diff2; if (Nrows < 2) return 0; for (ii = Nimages - Ncols; ii < Nimages; ii++) // loop last Ncols images { col = image[ii].col; // get column height colH[col] = image[ii].Ipy + image[ii].Ihh; // = y position + image height } for (col = 0; col < Ncols; col++) // get max. column height if (colH[col] > maxH) maxH = colH[col]; for (col = 0; col < Ncols; col++) // loop all columns { diff = maxH - colH[col]; if (diff < 2) continue; // column height near max, no change nrows = Nrows; // max. images per column ii = (nrows - 1) * Ncols + col; // last image[ii] this column if (ii >= Nimages) nrows -= 1; // this is a short column if (nrows < 2) break; diff2 = diff / (nrows - 1); // increase for Y-posn after row 0 for (dy = row = 0; row < nrows; row++) // loop all rows in column { ii = row * Ncols + col; if (ii == Nimages) break; image[ii].Ipy += dy; dy += diff2; // prior increase + per row increase } } return 0; } // create map file - maps the scaled image areas in the montage file // to the corresponding image files int montage_mapfile() { using namespace montage_names; int ii, lox, hix, loy, hiy; float flox, fhix, floy, fhiy; char montagemapfile[300]; FILE *fid; snprintf(montagemapfile,300,"%s/%s",montage_maps_folder,uniquename); fid = fopen(montagemapfile,"w"); if (! fid) { zmessageACK(Mwin,strerror(errno)); return 1; } for (ii = 0; ii < Nimages; ii++) { lox = image[ii].Ipx; // scaled image pixel range loy = image[ii].Ipy; hix = lox + Iww; hiy = loy + image[ii].Ihh; flox = 1.0 * lox / Fww; // rescale to 0.0 - 1.0 floy = 1.0 * loy / Fhh; // (immune to image resize) fhix = 1.0 * hix / Fww; fhiy = 1.0 * hiy / Fhh; fprintf(fid,"%.5f %.5f %.5f %.5f file: %s\n", flox,floy,fhix,fhiy,image[ii].file); } fclose(fid); return 0; } // mouse function // show the image file corresponding to clicked position in montage file void montage_Lclick_func(int mousex, int mousey) { using namespace montage_names; typedef struct { char *file; // image filename int lox, loy; // scaled image area in montage image int hix, hiy; } image_t; image_t image[maxNm]; // image data int fww, fhh, Nimages = 0; char buff[500], montagemapfile[300]; char *pp, *pp1, *pp2, *file; int ii, cc, nn, err; float flox, fhix, floy, fhiy; FILE *fid; STATB statb; fww = Fpxb->ww; // montage image dimensions fhh = Fpxb->hh; pp1 = strrchr(curr_file,'/'); // extract montage unique name if (! pp1) goto notfound; pp1++; pp2 = strstr(pp1," (fotoxx montage)"); if (! pp2) goto notfound; cc = pp2 - pp1; if (cc > 100) goto notfound; strncpy0(uniquename,pp1,100); uniquename[cc] = 0; snprintf(montagemapfile,300,"%s/%s",montage_maps_folder,uniquename); // corresp. montage map file fid = fopen(montagemapfile,"r"); if (! fid) goto notfound; // not found for (ii = 0; ii < maxNm; ii++) // loop map file recs { pp = fgets_trim(buff,500,fid,1); if (! pp) break; nn = sscanf(buff,"%f %f %f %f",&flox,&floy,&fhix,&fhiy); if (nn != 4) goto invalid; pp = strstr(buff,"file: "); if (! pp) goto invalid; image[ii].file = zstrdup(pp + 6); image[ii].lox = flox * fww; image[ii].loy = floy * fhh; image[ii].hix = fhix * fww; image[ii].hiy = fhiy * fhh; } fclose(fid); Nimages = ii; if (Nimages < 2) goto invalid; for (ii = 0; ii < Nimages; ii++) { if (mousex <= image[ii].lox) continue; if (mousex >= image[ii].hix) continue; if (mousey <= image[ii].loy) continue; if (mousey >= image[ii].hiy) continue; break; } if (ii == Nimages) goto freemem; file = image[ii].file; err = stat(file,&statb); if (err) { zmessageACK(Mwin,strerror(errno)); goto freemem; } popup_image(file,MWIN,1,512); goto freemem; notfound: zmessageACK(Mwin,Bfilenotfound2,uniquename); goto freemem; invalid: zmessageACK(Mwin,E2X("montage map file invalid: %s"),uniquename); goto freemem; freemem: for (ii = 0; ii < Nimages; ii++) if (image[ii].file) zfree(image[ii].file); return; } fotoxx-20.08/f.meta.cc000066400000000000000000014614541362435004500145540ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* Fotoxx image editor - image metadata functions. View and edit metadata ---------------------- select_meta_keys dialog to select metadata items m_meta_view_short metadata short report m_meta_view_long report all metadata m_meta_edit_main primary edit metadata dialog m_meta_manage_tags define tags (keywords) for image searching m_meta_edit_any dialog to fetch and save any image metadata by name m_meta_delete dialog to delete any image file metadata by name m_meta_captions write captions and comments at top of current image m_batch_tags batch add and delete tags for selected image files m_batch_rename_tags convert tag names for all image files using a from-to list m_batch_photo_date_time change or shift photo date/time m_batch_change_metadata add/change or delete metadata for selected image files m_batch_report_metadata batch metadata report to text file m_batch_geotags add given geotag to selected set of images Image search utilities ---------------------- m_meta_places_dates find images by location and date range m_meta_timeline find images by year and month m_autosearch search function for use with scripts m_search_images find images using any metadata and/or file names pdate_metadate convert yyyy-mm-dd to yyyymmdd ptime_metatime convert hh:mm[:ss] to hhmmss pdatetime_metadatetime convert yyyy-mm-dd hh:mm[:ss] to yyyymmddhhmmss metadate_pdate convert yyyymmddhhmmss to yyyy-mm-dd and hh:mm:ss datetimeOK validate yyyy-mm-dd hh:mm[:ss] date/time add_tag add tag to a tag list add_tag_fotoxx add tag "fotoxx" for edited image (for f_save()) set_meta_wwhh set image width/height metadata del_tag remove tag from a tag list add_recentag add tag to recent tags list, remove oldest if needed load_deftags load defined tags list from tags file and image index save_deftags save defined tags list to tags file find_deftag check if given tag is in defined tags list add_deftag add new tag to defined tags list or change category del_deftag remove tag from defined tags list deftags_stuff stuff defined tags into dialog text widget defcats_stuff stuff defined categories into dialog combobox widget tag_orphans report tags defined and not used in any image file load_filemeta load image file metadata into memory (indexed data only) save_filemeta save metadata to image file EXIF and to image index update_image_index update index data for current image file delete_image_index delete index record for deleted image file web_geocode web service to map location to earth coordinates init_geolocs load geolocations table from image index file find_location find locations matching partial location/country input put_geolocs put new location data in geolocations table validate_latlong validate earth coordinates data earth_distance compute km distance between two earth coordinates get_gallerymap get map coordinates for current gallery files m_set_map_markers set map markers: all images or current gallery Geotag mapping (W view) ----------------------- m_load_filemap load a geographic map file chosen by user filemap_position convert earth coordinates to map pixel position filemap_coordinates convert map pixel position to earth coordinates filemap_paint_dots paint red dots on map where images are located filemap_mousefunc respond to mouse movement and left clicks on map find_filemap_images find images within range of geolocation free_filemap free huge memory for filemap image Geotag mapping (M view) ----------------------- m_netmap_source choose net map source m_load_netmap initialize net map netmap_paint_dots paint red dots on map where images are located m_netmap_zoomin zoom net map in on image location netmap_zoomto callable with input zoom level netmapscale get net map scale at zoom level netmap_mousefunc respond to clicks on net map find_netmap_images find images m_netmap_locs save and recall net map locs (center, zoom level) EXIF store and retrieve ----------------------- exif_get get image metadata from list of keys exif_put update image metadata from list of keys and data exif_copy copy metadata from file to file, with revisions exif_server start exiftool server process, send data requests exif_tagdate yyyy:mm:dd hh:mm:ss to yyyymmddhhmmss tag_exifdate yyyymmddhhmmss to yyyy:mm:dd hh:mm:ss Image index functions --------------------- get_xxrec get image index record for image file put_xxrec add or update index record for an image file read_xxrec_seq read all index records sequentially, one per call write_xxrec_seq write all index records sequentially *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ char *pdate_metadate(cchar *pdate); // "yyyy-mm-dd" to "yyyymmdd" char *ptime_metatime(cchar *ptime); // "hh:mm[:ss]" to "hhmmss" char *pdatetime_metadatetime(cchar *pdatetime); // yyyy-mm-dd hh:mm[:ss] to yyyymmddhhmmss 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(int force); // load tags_deftags from index data void save_deftags(); // tags_deftags[] >> defined_tags file int find_deftag(char *tag); // find tag in tags_deftags[] int add_deftag(char *catg, char *tag); // add tag to tags_deftags[] int del_deftag(char *tag); // remove tag from tags_deftags[] void deftags_stuff(zdialog *zd, cchar *catg); // tags_deftags[] >> zd widget deftags void defcats_stuff(zdialog *zd); // defined categories >> " widget defcats cchar *web_geocode(zdialog *zd); // find earth coordinates via web service int init_geolocs(); // load geolocs table from index file int geolocs_compare(cchar *rec1, cchar *rec2); // compare geolocs recs location int find_location(zdialog *zd); // find locations matching partial inputs int put_geolocs(zdialog *zd); // Update geolocations table in memory int validate_latlong(char *lati, char *longi, float &flat, float &flong); // convert and validate earth coordinates float earth_distance(float lat1, float long1, float lat2, float long2); // compute distance from earth coordinates int get_gallerymap(); // get map coordinates for gallery files void netmap_zoomto(float flati, float flongi, int zoomlev); // zoom net map to location and zoom level float netmapscale(int zoomlev, float flat, float flong); // net map scale at given zoom and location // char variables with no data are always "", never a null pointer // 20.0 namespace meta_names { char meta_pdate[16]; // image (photo) date, yyyymmddhhmmss char meta_rating[4]; // image rating in stars, "0" to "5" char meta_wwhh[16]; // image width/height, "2345x1234" char meta_tags[tagFcc]; // tags for current image file char meta_comments[exif_maxcc]; // image comments expanded char meta_caption[exif_maxcc]; // image caption char meta_location[100], meta_country[100]; // geolocs: location, country char meta_lati[20], meta_longi[20]; // geolocs: earth coordinates (-123.4567) char p_meta_pdate[16]; // previous file metadata char p_meta_rating[4]; char p_meta_tags[tagFcc]; char p_meta_comments[exif_maxcc]; char p_meta_caption[exif_maxcc]; char p_meta_location[100], p_meta_country[100]; char p_meta_lati[20], p_meta_longi[20]; char *xmeta_data[Mxmeta]; // indexed metadata (xmeta_key[]) char *tags_deftags[maxtagcats]; // defined tags: catg: tag1, ... tagN, char tags_recentags[tagRcc] = ""; // recently added tags list char keyname[40], keydata[exif_maxcc]; zdialog *zd_mapgeotags = 0; // zdialog wanting geotags via map click struct geolocs_t { // geolocations table, memory DB char *location, *country; // maps locations <-> earth coordinates float flati, flongi; // " float, 7 digit precision }; geolocs_t **geolocs = 0; // geolocs table int Ngeolocs = 0; // size of geolocations table struct gallerymap_t { // geocoordinates for gallery files char *file; float flati, flongi; }; gallerymap_t *gallerymap = 0; int Ngallerymap = 0; } using namespace meta_names; /********************************************************************************/ // Dialog to select metadata items (for index, view, report). // The item list is an input and an output. EOL marker is null. // Fexclude: exclude items indexed by default. // returns 0/1 = no changes / changes made namespace select_meta_keys_names { GtkWidget *mtext1, *mtext2; int Fexclude; zdialog *zd; } int select_meta_keys(char *itemlist[], int exclude) { using namespace select_meta_keys_names; void select_meta_keys_clickfunc1(GtkWidget *, int line, int pos, int kbkey); void select_meta_keys_clickfunc2(GtkWidget *, int line, int pos, int kbkey); char itemfile[200], buff[100], buff2[100], *pp; FILE *fid; int zstat, ii; Fexclude = exclude; /*** __________________________________________________________ | Add Metadata Items | | | | click to select click to unselect | | _________________________ __________________________ | | | Orientation | | | | | | Rotation | | | | | | Exposure Time | | | | | | Aperture | | | | | | ... | | | | | | Other Item ... | | | | | |_________________________| |__________________________| | | | | [done] [cancel] | |__________________________________________________________| ***/ zd = zdialog_new(E2X("Add Metadata Items"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"expand"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"expand|space=3"); zdialog_add_widget(zd,"label","lab1","vb1",E2X("click to select")); zdialog_add_widget(zd,"scrwin","scroll1","vb1",0,"expand"); zdialog_add_widget(zd,"text","mtext1","scroll1",0,"expand"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"expand|space=3"); zdialog_add_widget(zd,"label","lab2","vb2",E2X("click to unselect")); zdialog_add_widget(zd,"scrwin","scroll2","vb2",0,"expand"); zdialog_add_widget(zd,"text","mtext2","scroll2",0,"expand"); mtext1 = zdialog_widget(zd,"mtext1"); textwidget_clear(mtext1); mtext2 = zdialog_widget(zd,"mtext2"); textwidget_clear(mtext2); snprintf(itemfile,200,"%s/metadata_short_list",get_zhomedir()); // metadata item list >> left widget fid = fopen(itemfile,"r"); if (! fid) { zmessageACK(Mwin,"%s \n %s",itemfile,strerror(errno)); return 0; } while (true) { pp = fgets_trim(buff,100,fid,1); if (! pp) break; if (*pp <= ' ') continue; if (Fexclude) { // m_index() or m_meta_view_short() strCompress(buff2,buff); if (strmatch(buff2,iptc_keywords_key)) continue; // exclude items indexed and if (strmatch(buff2,iptc_rating_key)) continue; // reported by default if (strmatch(buff2,exif_date_key)) continue; if (strmatch(buff2,exif_comment_key)) continue; if (strmatch(buff2,exif_usercomment_key)) continue; if (strmatch(buff2,iptc_caption_key)) continue; if (strmatch(buff2,exif_city_key)) continue; if (strmatch(buff2,exif_country_key)) continue; if (strmatch(buff2,exif_lati_key)) continue; if (strmatch(buff2,exif_longi_key)) continue; if (strmatch(buff2,"ImageHistory")) continue; // disallow 19.10 } textwidget_append(mtext1,0,"%s\n",buff); } textwidget_append(mtext1,0,"%s\n","Other Item ..."); // append "Other Item ..." fclose(fid); for (ii = 0; ii < Mxmeta; ii++) // input item list >> right widget { if (! itemlist[ii]) break; textwidget_append(mtext2,0,"%s\n",itemlist[ii]); } textwidget_set_eventfunc(mtext1,select_meta_keys_clickfunc1); // set mouse/KB event function textwidget_set_eventfunc(mtext2,select_meta_keys_clickfunc2); zdialog_resize(zd,500,300); zdialog_set_modal(zd); zdialog_run(zd,0,0); // run dialog zstat = zdialog_wait(zd); // wait for dialog completion if (zstat != 1) { // canceled zdialog_free(zd); return 0; // return "no change" } for (ii = 0; ii < Mxmeta; ii++) { // free input list if (! itemlist[ii]) break; zfree(itemlist[ii]); } for (ii = 0; ii < Mxmeta; ii++) { // replace input list with output pp = textwidget_line(mtext2,ii,1); // remove trailing \n if (! pp || ! *pp) break; itemlist[ii] = zstrdup(pp); } itemlist[ii] = 0; // mark EOL zdialog_free(zd); return 1; // return "changes made" } // get clicked tag name from input list and insert into output list void select_meta_keys_clickfunc1(GtkWidget *widget, int line, int pos, int kbkey) { using namespace select_meta_keys_names; char *pp; int disallow = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } pp = textwidget_line(widget,line,1); // get clicked line, highlight if (! pp || ! *pp) return; textwidget_highlight_line(widget,line); if (strmatch(pp,"Other Item ...")) { // get manually input metadata name pp = zdialog_text(zd->dialog,"metadata item name",0); if (! pp) return; strCompress(pp); // remove blanks if (Fexclude) { if (strmatchcase(pp,iptc_keywords_key)) disallow = 1; // exclude items indexed by default 20.02 if (strmatchcase(pp,iptc_rating_key)) disallow = 1; if (strmatchcase(pp,exif_date_key)) disallow = 1; if (strmatchcase(pp,exif_comment_key)) disallow = 1; if (strmatchcase(pp,exif_usercomment_key)) disallow = 1; if (strmatchcase(pp,iptc_caption_key)) disallow = 1; if (strmatchcase(pp,exif_city_key)) disallow = 1; if (strmatchcase(pp,exif_country_key)) disallow = 1; if (strmatchcase(pp,exif_lati_key)) disallow = 1; if (strmatchcase(pp,exif_longi_key)) disallow = 1; if (strmatchcase(pp,"ImageHistory")) disallow = 1; if (disallow) { zmessageACK(Mwin,"%s is already indexed",pp); return; } } textwidget_append2(mtext2,0,"%s\n",pp); // append to output list zfree(pp); return; } strCompress(pp); // remove blanks textwidget_append2(mtext2,0,"%s\n",pp); // append to output list return; } // get clicked tag name from output list and remove it void select_meta_keys_clickfunc2(GtkWidget *widget, int line, int pos, int kbkey) { using namespace select_meta_keys_names; char *pp1, *pp2; int ii, Nitems = 0; char *itemlist[Mxmeta]; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } pp1 = textwidget_line(widget,line,1); // get clicked line if (! pp1 || ! *pp1) return; for (line = 0; ; line++) { // read and save all lines pp2 = textwidget_line(mtext2,line,1); if (! pp2 || ! *pp2) break; if (strmatch(pp2,pp1)) continue; // skip clicked line itemlist[Nitems] = zstrdup(pp2); if (++Nitems == Mxmeta) break; } textwidget_clear(mtext2); // clear output list for (ii = 0; ii < Nitems; ii++) // write new output list { textwidget_append(mtext2,0,"%s\n",itemlist[ii]); zfree(itemlist[ii]); } return; } /********************************************************************************/ // menu function and popup dialog to show EXIF/IPTC data // window is updated when navigating to another image int metadata_report_type = 1; // called by f_open() if zd_metaview is defined void meta_view(int type) { if (type) metadata_report_type = type; if (metadata_report_type == 2) m_meta_view_long(0,0); else m_meta_view_short(0,0); return; } // menu function - short report void m_meta_view_short(GtkWidget *, cchar *menu) { int meta_view_dialog_event(zdialog *zd, cchar *event); #define NK 21 // 19.16 cchar *keyname[NK] = { "ImageSize", "FileSize", exif_date_key, "FileModifyDate", "Make", "Model", exif_focal_length_35_key, exif_focal_length_key, "ExposureTime", "FNumber", "ISO", exif_city_key, exif_country_key, exif_lati_key, exif_longi_key, iptc_keywords_key, iptc_rating_key, iptc_caption_key, exif_comment_key, exif_usercomment_key, exif_editlog_key }; char *keyval[NK]; char *focallength, chsec[12], **text; static char *file, *filen, *chsize; float fsecs; int err, ii, nn, fsize; cchar *textdelims = "!-,.:;?/)}]"; cchar *editdelims = ":|,"; GtkWidget *widget; FILE *fid; int nkx; char *keynamex[NK], *keyvalx[NK]; char extras_file[200], buff[100], *pp; F1_help_topic = "view meta"; if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (clicked_file) { // use clicked file if present file = clicked_file; clicked_file = 0; } else if (curr_file) file = zstrdup(curr_file); else return; if (FGWM == 'G') gallery(0,"paint",-1); // if gallery view, repaint if (metadata_report_type != 1) { if (zd_metaview) zdialog_free(zd_metaview); zd_metaview = 0; metadata_report_type = 1; } if (! zd_metaview) // popup dialog if not already { zd_metaview = zdialog_new(E2X("View Metadata"),Mwin,E2X("Extras"),Bcancel,null); zdialog_add_widget(zd_metaview,"scrwin","scroll","dialog",0,"expand"); zdialog_add_widget(zd_metaview,"text","exifdata","scroll",0,"expand"); zdialog_resize(zd_metaview,550,350); zdialog_run(zd_metaview,meta_view_dialog_event,"save"); zdialog_can_focus(zd_metaview,0); // set no focus 19.0 } widget = zdialog_widget(zd_metaview,"exifdata"); textwidget_clear(widget); err = exif_get(file,keyname,keyval,NK); if (err) { zmessageACK(Mwin,"exif failure"); return; } filen = strrchr(file,'/'); // get file name without folder if (filen) filen++; else filen = file; fsize = 0; if (keyval[1]) fsize = atoi(keyval[1]); // get file size in B/KB/MB units chsize = formatKBMB(fsize,3); if (keyval[2] && strlen(keyval[2]) > 19) keyval[2][19] = 0; // truncate dates to yyyy-mm-dd hh:mm:ss if (keyval[3] && strlen(keyval[3]) > 19) keyval[3][19] = 0; pp = keyval[2]; if (pp && strlen(pp) > 4 && pp[4] == ':') pp[4] = '-'; // change yyyy:mm:dd to yyyy-mm-dd if (pp && strlen(pp) > 7 && pp[7] == ':') pp[7] = '-'; pp = keyval[3]; if (pp && strlen(pp) > 4 && pp[4] == ':') pp[4] = '-'; if (pp && strlen(pp) > 7 && pp[7] == ':') pp[7] = '-'; textwidget_append(widget,0,"File %s \n",filen); textwidget_append(widget,0,"Size %s %s \n",keyval[0],chsize); textwidget_append(widget,0,"Dates photo: %s file: %s \n",keyval[2],keyval[3]); if (keyval[4] || keyval[5]) textwidget_append(widget,0,"Camera %s %s \n",keyval[4],keyval[5]); if (keyval[6] || keyval[7] || keyval[8] || keyval[9] || keyval[10]) // photo exposure data 19.16 { if (keyval[6]) focallength = keyval[6]; // 19.16 else if (keyval[7]) focallength = keyval[7]; else focallength = (char *) "null"; strcpy(chsec,"null"); if (keyval[8]) { fsecs = atof(keyval[8]); // convert 0.008 seconds to 1/125 etc. if (fsecs > 0 && fsecs <= 0.5) { fsecs = 1/fsecs; snprintf(chsec,12,"1/%.0f",fsecs); } else if (fsecs > 0) snprintf(chsec,12,"%.0f",fsecs); } textwidget_append(widget,0,"Exposure %s mm %s sec F%s ISO %s \n", focallength,chsec,keyval[9],keyval[10]); } if (keyval[11] || keyval[12] || keyval[13] || keyval[14]) // geotag data textwidget_append(widget,0,"Location %s %s %s %s \n", keyval[11],keyval[12],keyval[13],keyval[14]); if (keyval[15]) textwidget_append(widget,0,"Keywords %s \n",keyval[15]); // tags if (keyval[16]) // rating textwidget_append(widget,0,"Rating %s \n",keyval[16]); if (keyval[17]) { // caption-abstract nn = breakup_text(keyval[17],text,textdelims,40,60); textwidget_append(widget,0,"Caption %s \n",text[0]); for (ii = 1; ii < nn; ii++) textwidget_append(widget,0," %s \n",text[ii]); for (ii = 0; ii < nn; ii++) zfree(text[ii]); zfree(text); } if (keyval[18]) { // comment nn = breakup_text(keyval[18],text,textdelims,40,60); textwidget_append(widget,0,"Comment %s \n",text[0]); for (ii = 1; ii < nn; ii++) textwidget_append(widget,0," %s \n",text[ii]); for (ii = 0; ii < nn; ii++) zfree(text[ii]); zfree(text); } if (keyval[19]) { // usercomment nn = breakup_text(keyval[19],text,textdelims,40,60); textwidget_append(widget,0,"UserComment %s \n",text[0]); for (ii = 1; ii < nn; ii++) textwidget_append(widget,0," %s \n",text[ii]); for (ii = 0; ii < nn; ii++) zfree(text[ii]); zfree(text); } if (keyval[20]) { // edit history log nn = breakup_text(keyval[20],text,editdelims,40,60); textwidget_append(widget,0,"Edits %s \n",text[0]); for (ii = 1; ii < nn; ii++) textwidget_append(widget,0," %s \n",text[ii]); for (ii = 0; ii < nn; ii++) zfree(text[ii]); zfree(text); } for (ii = 0; ii < NK; ii++) // free memory if (keyval[ii]) zfree(keyval[ii]); // append extra report items if any snprintf(extras_file,200,"%s/metadata_view_extra",get_zhomedir()); fid = fopen(extras_file,"r"); if (! fid) goto finished; // no extras file for (nkx = 0; nkx < NK; nkx++) { // get list of user extras pp = fgets_trim(buff,100,fid,1); if (! pp) break; strCompress(pp); if (*pp <= ' ') { nkx--; continue; } keynamex[nkx] = zstrdup(pp); } fclose(fid); if (nkx == 0) goto finished; // empty file err = exif_get(file,(cchar **) keynamex,keyvalx,nkx); // get all items at once if (err) { zmessageACK(Mwin,"exif failure"); goto finished; } textwidget_append(widget,0,"\n"); // blank line for (ii = 0; ii < nkx; ii++) // report user extra items if (keyvalx[ii]) textwidget_append(widget,0,"%-24s : %s \n",keynamex[ii],keyvalx[ii]); for (ii = 0; ii < nkx; ii++) { // free memory if (keynamex[ii]) zfree(keynamex[ii]); if (keyvalx[ii]) zfree(keyvalx[ii]); } finished: zfree(file); return; } // menu function - long report void m_meta_view_long(GtkWidget *, cchar *menu) { int meta_view_dialog_event(zdialog *zd, cchar *event); FILE *fid; char *file, *pp, buff[1000]; GtkWidget *widget; F1_help_topic = "view meta"; if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (clicked_file) { // use clicked file if present file = clicked_file; clicked_file = 0; } else if (curr_file) file = zstrdup(curr_file); else return; if (FGWM == 'G') gallery(0,"paint",-1); // if gallery view, repaint if (metadata_report_type != 2) { if (zd_metaview) zdialog_free(zd_metaview); zd_metaview = 0; metadata_report_type = 2; } if (! zd_metaview) // popup dialog if not already { zd_metaview = zdialog_new(E2X("View All Metadata"),Mwin,Bcancel,null); zdialog_add_widget(zd_metaview,"scrwin","scroll","dialog",0,"expand"); zdialog_add_widget(zd_metaview,"text","exifdata","scroll",0,"expand|wrap"); zdialog_resize(zd_metaview,700,700); zdialog_run(zd_metaview,meta_view_dialog_event,"save"); zdialog_can_focus(zd_metaview,0); // set no focus 19.0 } widget = zdialog_widget(zd_metaview,"exifdata"); gtk_text_view_set_editable(GTK_TEXT_VIEW(widget),0); // disable widget editing gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget),GTK_WRAP_NONE); // disable text wrap textwidget_clear(widget); snprintf(command,CCC,"exiftool -s -e \"%s\" ",file); // exiftool command fid = popen(command,"r"); // get command outputs 20.0 if (fid) { while ((pp = fgets_trim(buff,1000,fid))) textwidget_append(widget,0,"%s\n",pp); // add to report window } pclose(fid); zfree(file); return; } // dialog event and completion callback function int meta_view_dialog_event(zdialog *zd, cchar *event) { char filex[200]; int zstat, ii, jj; char *itemlist[Mxmeta], *pp, buff[100]; FILE *fid; zstat = zd->zstat; if (! zstat) return 1; // wait for completion zdialog_free(zd); // kill dialog zd_metaview = 0; if (metadata_report_type != 1) return 1; // not short report if (zstat != 1) return 1; // not [extras] button snprintf(filex,200,"%s/metadata_view_extra",get_zhomedir()); // [extras] button itemlist[0] = 0; // extra metadata items = empty fid = fopen(filex,"r"); // read file of extra metadata items if (fid) { for (ii = jj = 0; ii < Mxmeta; ii++) { pp = fgets_trim(buff,100,fid,1); if (! pp) break; if (*pp <= ' ') continue; itemlist[jj++] = zstrdup(pp); } itemlist[jj] = 0; // mark EOL fclose(fid); } select_meta_keys(itemlist,1); // user edit of extras list fid = fopen(filex,"w"); // update extras list file if (! fid) { zmessageACK(Mwin,"%s \n %s",filex,strerror(errno)); return 1; } for (ii = 0; ii < Mxmeta; ii++) { if (! itemlist[ii]) break; fprintf(fid,"%s\n",itemlist[ii]); } fclose(fid); return 1; } /********************************************************************************/ // edit metadata menu function void m_meta_edit_main(GtkWidget *, cchar *menu) { void edit_imagetags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void edit_recentags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void edit_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void edit_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); int editmeta_dialog_event(zdialog *zd, cchar *event); cchar *mapquest = "Geocoding by MapQuest"; GtkWidget *widget; zdialog *zd; char *ppv, pdate2[12], ptime2[12]; char cctext[exif_maxcc+50]; char RN[4] = "R0"; int err, ii; F1_help_topic = "edit meta"; if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (clicked_file) { // use clicked file if present if (! curr_file || ! strmatch(clicked_file,curr_file)) // avoid f_open() re-entry f_open(clicked_file,0,0,1,0); clicked_file = 0; } if (! curr_file) { if (zd_editmeta) zdialog_free(zd_editmeta); zd_editmeta = 0; zd_mapgeotags = 0; return; } err = access(curr_file,W_OK); // test file can be written by me 19.1 if (err) { zmessageACK(Mwin,"%s: %s",Bnowriteperm,curr_file); return; } if (! init_geolocs()) return; // initialize geotags if (FGWM == 'G') gallery(0,"paint",-1); // if gallery view, repaint /*** ___________________________________________________________ | Edit Metadata | | | | File: filename.jpg | | Image Date: [__________] Time: [________] [prev] | | Rating (stars): (o) 1 (o) 2 (o) 3 (o) 4 (o) 5 (o) 6 | | Caption [______________________________________________] | | Comments [_____________________________________________] | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | location [_______________] country [______________] | | latitude [__________] longitude [__________] | | [Find] [Web] [Prev] [Clear] Geocoding by MapQuest | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | Current Tags [_________________________________________] | | Recent Tags [__________________________________________] | | Enter New Tag [__________] [Add] | | Matching Tags [________________________________________] | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | Defined Tags Category [______________________________|v] | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |______________________________________________________| | | | | [Prev] [Apply] [Cancel] | |___________________________________________________________| ***/ if (! zd_editmeta) // (re) start edit dialog { zd = zdialog_new(E2X("Edit Metadata"),Mwin,Bprev,Bapply,Bcancel,null); zd_editmeta = zd; zdialog_add_ttip(zd,Bapply,E2X("save metadata to file")); // File: xxxxxxxxx.jpg zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labf","hbf",E2X("File:"),"space=3"); zdialog_add_widget(zd,"label","file","hbf","filename.jpg","space=5"); // Image Date [__________] Time [_____] [prev] zdialog_add_widget(zd,"hbox","hbdt","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labdate","hbdt",E2X("Image Date"),"space=3"); zdialog_add_widget(zd,"zentry","date","hbdt",0,"size=12"); zdialog_add_widget(zd,"label","space","hbdt",0,"space=5"); zdialog_add_widget(zd,"label","labtime","hbdt",E2X("Time"),"space=3"); zdialog_add_widget(zd,"zentry","time","hbdt",0,"size=8"); zdialog_add_widget(zd,"button","ppdate","hbdt",Bprev,"space=8"); zdialog_add_ttip(zd,"date","yyyy-mm-dd"); zdialog_add_ttip(zd,"time","hh:mm[:ss]"); // Rating (stars): ʘ 1 ʘ 2 ʘ 3 ʘ 4 ʘ 5 ʘ 6 zdialog_add_widget(zd,"hbox","hbrate","dialog"); zdialog_add_widget(zd,"label","labrate","hbrate",E2X("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",E2X("Caption"),"space=3"); zdialog_add_widget(zd,"edit","caption","hbcap",0,"wrap|expand"); // Comments [__________________________________________] zdialog_add_widget(zd,"hbox","hbcom","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labcom","hbcom",E2X("Comments"),"space=3"); zdialog_add_widget(zd,"edit","comments","hbcom",0,"wrap|expand"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=3"); // location [_____________] country [____________] zdialog_add_widget(zd,"hbox","hbcc","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labloc","hbcc",E2X("Location"),"space=5"); zdialog_add_widget(zd,"zentry","location","hbcc",0,"expand"); zdialog_add_widget(zd,"label","space","hbcc",0,"space=5"); zdialog_add_widget(zd,"label","labcountry","hbcc",E2X("Country"),"space=5"); zdialog_add_widget(zd,"zentry","country","hbcc",0,"expand"); // latitude [__________] longitude [__________] zdialog_add_widget(zd,"hbox","hbll","dialog"); zdialog_add_widget(zd,"label","lablat","hbll","Latitude","space=3"); zdialog_add_widget(zd,"zentry","lati","hbll",0,"size=10"); zdialog_add_widget(zd,"label","space","hbll",0,"space=5"); zdialog_add_widget(zd,"label","lablong","hbll","Longitude","space=3"); zdialog_add_widget(zd,"zentry","longi","hbll",0,"size=10"); // [Find] [Web] [Prev] [Clear] Geocoding by MapQuest zdialog_add_widget(zd,"hbox","hbgeo","dialog",0,"space=3"); zdialog_add_widget(zd,"button","geofind","hbgeo",Bfind,"space=5"); zdialog_add_widget(zd,"button","geoweb","hbgeo",Bweb,"space=5"); zdialog_add_widget(zd,"button","geoprev","hbgeo",Bprev,"space=5"); zdialog_add_widget(zd,"button","geoclear","hbgeo",Bclear,"space=5"); zdialog_add_widget(zd,"label","labmq","hbgeo",mapquest,"space=3"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=3"); // Image Tags [________________________________________] zdialog_add_widget(zd,"hbox","hbit","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labit","hbit",E2X("Image Tags"),"space=3"); zdialog_add_widget(zd,"frame","frit","hbit",0,"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",E2X("Recent Tags"),"space=3"); zdialog_add_widget(zd,"frame","frrt","hbrt",0,"expand"); zdialog_add_widget(zd,"text","recentags","frrt",0,"wrap"); // Enter New Tag [________________] [Add] zdialog_add_widget(zd,"hbox","hbnt","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labnt","hbnt",E2X("Enter New Tag"),"space=3"); zdialog_add_widget(zd,"zentry","newtag","hbnt",0,"size=20"); zdialog_add_widget(zd,"zbutton","add","hbnt",Badd,"space=5"); // Matching Tags [_____________________________________] zdialog_add_widget(zd,"hbox","hbmt","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labmt","hbmt",E2X("Matching Tags"),"space=3"); zdialog_add_widget(zd,"frame","frmt","hbmt",0,"expand"); zdialog_add_widget(zd,"text","matchtags","frmt",0,"wrap"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=3"); // Defined Tags Category zdialog_add_widget(zd,"hbox","hbdt1","dialog"); zdialog_add_widget(zd,"label","labdt","hbdt1",E2X("Defined Tags Category"),"space=3"); zdialog_add_widget(zd,"label","space","hbdt1",0,"expand"); zdialog_add_widget(zd,"combo","defcats","hbdt1",0,"expand|space=5"); zdialog_add_widget(zd,"hbox","hbdt2","dialog",0,"expand"); zdialog_add_widget(zd,"scrwin","swdt2","hbdt2",0,"expand"); zdialog_add_widget(zd,"text","deftags","swdt2",0,"wrap"); zdialog_add_ttip(zd,"geofind",E2X("search known locations")); zdialog_add_ttip(zd,"geoweb",E2X("search using web service")); zdialog_add_ttip(zd,"geoprev",Bprevtip); zdialog_add_ttip(zd,Bprev,Bprevtip); load_deftags(0); // stuff defined tags into dialog deftags_stuff(zd,"ALL"); defcats_stuff(zd); // and defined categories widget = zdialog_widget(zd,"imagetags"); // tag widget mouse/KB event functions textwidget_set_eventfunc(widget,edit_imagetags_clickfunc); widget = zdialog_widget(zd,"recentags"); textwidget_set_eventfunc(widget,edit_recentags_clickfunc); widget = zdialog_widget(zd,"matchtags"); textwidget_set_eventfunc(widget,edit_matchtags_clickfunc); widget = zdialog_widget(zd,"deftags"); textwidget_set_eventfunc(widget,edit_deftags_clickfunc); zdialog_resize(zd,400,700); // run dialog zdialog_run(zd,editmeta_dialog_event,0); } zd = zd_editmeta; // edit metadata active zd_mapgeotags = zd; // map clicks active ppv = (char *) strrchr(curr_file,'/'); zdialog_stuff(zd,"file",ppv+1); // stuff dialog fields from curr. file metadate_pdate(meta_pdate,pdate2,ptime2); // "yyyymmddhhmmss" to zdialog_stuff(zd,"date",pdate2); // "yyyy-mm-dd" and "hh:mm:ss" zdialog_stuff(zd,"time",ptime2); 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"); // replace fake \n with real \n zdialog_stuff(zd,"caption",cctext); // (real \n not allowed in metadata) repl_1str(meta_comments,cctext,"\\n","\n"); zdialog_stuff(zd,"comments",cctext); zdialog_stuff(zd,"location",meta_location); // geotags data >> dialog zdialog_stuff(zd,"country",meta_country); zdialog_stuff(zd,"lati",meta_lati); zdialog_stuff(zd,"longi",meta_longi); zdialog_stuff(zd,"imagetags",meta_tags); zdialog_stuff(zd,"recentags",tags_recentags); return; } // mouse click functions for various text widgets for tags void edit_imagetags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // existing image tag was clicked { char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;",end); if (! txtag) return; del_tag(txtag,meta_tags); // remove tag from image zdialog_stuff(zd_editmeta,"imagetags",meta_tags); Fmetamod++; // note change zfree(txtag); return; } void edit_recentags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // recent tag was clicked { char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;",end); if (! txtag) return; add_tag(txtag,meta_tags,tagFcc); // add recent tag to image zdialog_stuff(zd_editmeta,"imagetags",meta_tags); Fmetamod++; // note change zfree(txtag); return; } void edit_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // matching tag was clicked { char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;",end); if (! txtag) return; add_tag(txtag,meta_tags,tagFcc); // add matching tag to image Fmetamod++; // note change add_recentag(txtag); // and add to recent tags zdialog_stuff(zd_editmeta,"imagetags",meta_tags); // update dialog widgets zdialog_stuff(zd_editmeta,"recentags",tags_recentags); zdialog_stuff(zd_editmeta,"newtag",""); zdialog_stuff(zd_editmeta,"matchtags",""); zdialog_goto(zd_editmeta,"newtag"); // put focus back on newtag widget zfree(txtag); return; } void edit_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // defined tag was clicked { char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;:",end); if (! txtag || end == ':') return; // tag category clicked, ignore add_tag(txtag,meta_tags,tagFcc); // add new tag to image zdialog_stuff(zd_editmeta,"imagetags",meta_tags); // from defined tags list Fmetamod++; // note change add_recentag(txtag); // and add to recent tags zdialog_stuff(zd_editmeta,"recentags",tags_recentags); zfree(txtag); return; } // dialog event and completion callback function int editmeta_dialog_event(zdialog *zd, cchar *event) // overhauled { char pdate2[12], ptime2[12]; // yyyy-mm-dd and hh:mm:ss char *metadate, *metatime; // yyyymmdd and hhmmss cchar *errmess; int ii, jj, nt, cc1, cc2, ff, err; char *pp1, *pp2; char catgname[tagcc]; char newtag[tagcc], matchtags[20][tagcc]; char matchtagstext[(tagcc+2)*20]; char cctext[exif_maxcc+50]; char RN[4] = "R0"; char location[100], country[100], lati[20], longi[20]; float flati, flongi; if (! curr_file) zd->zstat = 3; // current file gone if (strmatch(event,"cancel")) zd->zstat = 3; if (zstrstr("date time caption comments",event)) // note change but process later Fmetamod++; if (strmatch(event,"ppdate")) { // repeat last date used if (*p_meta_pdate) { metadate_pdate(p_meta_pdate,pdate2,ptime2); zdialog_stuff(zd,"date",pdate2); zdialog_stuff(zd,"time",ptime2); Fmetamod++; return 1; } } if (zstrstr("R0 R1 R2 R3 R4 R5",event)) { // note if rating changed Fmetamod++; return 1; } if (zstrstr("location country lati longi",event)) { // dialog inputs changed Fmetamod++; return 1; } if (strmatch(event,"geomap")) { // have geotags data from map click Fmetamod++; return 1; } if (strmatch(event,"geofind")) // [find] search location data { find_location(zd); // find location data via user Fmetamod++; return 1; } if (strmatch(event,"geoweb")) // [web] search location data { errmess = web_geocode(zd); // look-up in web service if (errmess) zmessageACK(Mwin,errmess); // fail else Fmetamod++; return 1; } if (strmatch(event,"geoprev")) // [prev] stuff previous geotags { zdialog_stuff(zd,"location",p_meta_location); // get last-used geotags zdialog_stuff(zd,"country",p_meta_country); zdialog_stuff(zd,"lati",p_meta_lati); zdialog_stuff(zd,"longi",p_meta_longi); Fmetamod++; } if (strmatch(event,"geoclear")) // [clear] location data { zdialog_stuff(zd,"location",""); // erase dialog fields zdialog_stuff(zd,"country",""); zdialog_stuff(zd,"lati",""); zdialog_stuff(zd,"longi",""); Fmetamod++; return 1; } if (strmatch(event,"defcats")) { // new tag category selection zdialog_fetch(zd,"defcats",catgname,tagcc); deftags_stuff(zd,catgname); } if (strmatch(event,"newtag")) // new tag is being typed in { zdialog_stuff(zd,"matchtags",""); // clear matchtags in dialog zdialog_fetch(zd,"newtag",newtag,tagcc); // get chars. typed so far cc1 = strlen(newtag); for (ii = jj = 0; ii <= cc1; ii++) { // remove foul characters if (strchr(",:;",newtag[ii])) continue; newtag[jj++] = newtag[ii]; } if (jj < cc1) { // something was removed newtag[jj] = 0; cc1 = jj; zdialog_stuff(zd,"newtag",newtag); } if (cc1 < 2) return 1; // wait for at least 2 chars. for (ii = nt = 0; ii < maxtagcats; ii++) // loop all categories { pp2 = tags_deftags[ii]; // category: aaaaaa, bbbbb, ... tagN, if (! pp2) continue; // | | pp2 = strchr(pp2,':'); // pp1 pp2 while (true) // loop all deftags in category { pp1 = pp2 + 2; if (! *pp1) break; pp2 = strchr(pp1,','); if (! pp2) break; if (strmatchcaseN(newtag,pp1,cc1)) { // deftag matches chars. typed so far cc2 = pp2 - pp1; strncpy(matchtags[nt],pp1,cc2); // save deftags that match matchtags[nt][cc2] = 0; if (++nt == 20) return 1; // quit if 20 matches or more } } } if (nt == 0) return 1; // no matches pp1 = matchtagstext; for (ii = 0; ii < nt; ii++) // matchtag list: aaaaa, bbb, cccc ... { strcpy(pp1,matchtags[ii]); pp1 += strlen(pp1); strcpy(pp1,", "); pp1 += 2; } zdialog_stuff(zd,"matchtags",matchtagstext); // stuff matchtags in dialog return 1; } if (strmatch(event,"add")) // enter new tag finished { zdialog_fetch(zd,"newtag",newtag,tagcc); // get finished tag cc1 = strlen(newtag); if (! cc1) return 1; if (newtag[cc1-1] == '\n') { // remove newline character cc1--; newtag[cc1] = 0; } for (ii = ff = 0; ii < maxtagcats; ii++) // loop all categories { pp2 = tags_deftags[ii]; // category: aaaaaa, bbbbb, ... tagN, if (! pp2) continue; // | | pp2 = strchr(pp2,':'); // pp1 pp2 while (true) // loop all deftags in category { pp1 = pp2 + 2; if (! *pp1) break; pp2 = strchr(pp1,','); if (! pp2) break; cc2 = pp2 - pp1; if (cc2 != cc1) continue; if (strmatchcaseN(newtag,pp1,cc1)) { // entered tag matches deftag strncpy(newtag,pp1,cc1); // use deftag upper/lower case ff = 1; break; } } if (ff) break; } add_tag(newtag,meta_tags,tagFcc); // add to image tag list Fmetamod++; // note change add_recentag(newtag); // and add to recent tags if (! ff) { // if new tag, add to defined tags add_deftag((char *) "nocatg",newtag); deftags_stuff(zd,"ALL"); } zdialog_stuff(zd,"newtag",""); // update dialog widgets zdialog_stuff(zd,"imagetags",meta_tags); zdialog_stuff(zd,"recentags",tags_recentags); zdialog_stuff(zd,"matchtags",""); zdialog_goto(zd,"newtag"); // put focus back on newtag widget return 1; } if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) // [prev] stuff previous file data { zd->zstat = 0; // keep dialog active if (! *meta_pdate && *p_meta_pdate) { // stuff photo date only if none metadate_pdate(p_meta_pdate,pdate2,ptime2); zdialog_stuff(zd,"date",pdate2); zdialog_stuff(zd,"time",ptime2); } for (ii = 0; ii <= 5; ii++) { // stuff rating RN[1] = '0' + ii; // radio buttons, "R0" to "R5" if (RN[1] == *p_meta_rating) zdialog_stuff(zd,RN,1); // for ratings "0" to "5" else zdialog_stuff(zd,RN,0); } zdialog_stuff(zd,"location",p_meta_location); // get last-used geotags zdialog_stuff(zd,"country",p_meta_country); zdialog_stuff(zd,"lati",p_meta_lati); zdialog_stuff(zd,"longi",p_meta_longi); zdialog_stuff(zd,"imagetags",p_meta_tags); // stuff tags strncpy0(meta_tags,p_meta_tags,tagFcc); repl_1str(p_meta_caption,cctext,"\\n","\n"); // stuff caption zdialog_stuff(zd,"caption",cctext); repl_1str(p_meta_comments,cctext,"\\n","\n"); // stuff comments zdialog_stuff(zd,"comments",cctext); Fmetamod++; return 1; } if (zd->zstat != 2) { // [cancel] or [x] zdialog_free(zd); // kill dialog zd_editmeta = 0; zd_mapgeotags = 0; // deactivate map clicks Fmetamod = 0; return 1; } zd->zstat = 0; // [apply] - keep dialog active gtk_window_present(MWIN); // return focus to main window if (! Fmetamod) return 1; // no metadata changes zdialog_fetch(zd,"date",pdate2,12); // get photo date and time zdialog_fetch(zd,"time",ptime2,12); if (*pdate2) { // date available metadate = pdate_metadate(pdate2); // validate if (! metadate) return 1; // bad, re-input strcpy(meta_pdate,metadate); // convert to yyyymmdd if (*ptime2) { // time available metatime = ptime_metatime(ptime2); // validate if (! metatime) return 1; // bad, re-input strcat(meta_pdate,metatime); // append hhmmss } } else *meta_pdate = 0; // leave empty strcpy(meta_rating,"0"); for (ii = 0; ii <= 5; ii++) { // get which rating radio button ON RN[1] = '0' + ii; zdialog_fetch(zd,RN,jj); if (jj) meta_rating[0] = '0' + ii; // set corresponding rating } zdialog_fetch(zd,"caption",cctext,exif_maxcc); // get new caption repl_1str(cctext,meta_caption,"\n","\\n"); // replace newlines with "\n" zdialog_fetch(zd,"comments",cctext,exif_maxcc); // get new comments repl_1str(cctext,meta_comments,"\n","\\n"); // replace newlines with "\n" zdialog_fetch(zd,"location",location,100); // get location from dialog zdialog_fetch(zd,"country",country,100); strTrim2(location); strTrim2(country); if (*location) { // 20.0 *location = toupper(*location); // capitalize zdialog_stuff(zd,"location",location); } if (*country) { *country = toupper(*country); zdialog_stuff(zd,"country",country); } zdialog_fetch(zd,"lati",lati,20); // get latitude, longitude zdialog_fetch(zd,"longi",longi,20); strTrim2(lati); strTrim2(longi); if (*lati || *longi) { // if coordinates present, validate err = validate_latlong(lati,longi,flati,flongi); if (err) { zmessageACK(Mwin,E2X("bad latitude/longitude: %s %s"),lati,longi); return 1; } } strncpy0(meta_location,location,100); // save geotags in image file EXIF strncpy0(meta_country,country,100); // and in image index file strncpy0(meta_lati,lati,20); strncpy0(meta_longi,longi,20); put_geolocs(zd); // update geolocs table in memory save_filemeta(curr_file); // save metadata changes to image file return 1; } /********************************************************************************/ // manage tags function - auxiliary dialog zdialog *zdmanagetags = 0; void m_meta_manage_tags(GtkWidget *, cchar *menu) { void manage_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); int managetags_dialog_event(zdialog *zd, cchar *event); GtkWidget *widget; zdialog *zd; F1_help_topic = "manage tags"; /*** ______________________________________________________________ | Manage Tags | | | | category [____________] tag [___________] [Create] [Delete] | | | | Defined Tags: _____________________________________________ | | | | | | | category1: tag11, tag12, tag13 ... | | | | category2: tag21, tag22, tag23 ... | | | | ... | | | | | | | | | | | | | | | | | | | | | | | |__________________________________________________________| | | | | [orphan tags] [Done] | |______________________________________________________________| ***/ if (zdmanagetags) return; zd = zdialog_new(E2X("Manage Tags"),Mwin,E2X("orphan tags"),Bdone,null); zdmanagetags = zd; zdialog_add_widget(zd,"hbox","hb7","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labcatg","hb7",E2X("category"),"space=5"); zdialog_add_widget(zd,"zentry","catg","hb7",0,"size=12"); zdialog_add_widget(zd,"label","space","hb7",0,"space=5"); zdialog_add_widget(zd,"label","labtag","hb7",E2X("tag"),"space=5"); zdialog_add_widget(zd,"zentry","tag","hb7",0,"size=20|expand"); zdialog_add_widget(zd,"label","space","hb7",0,"space=5"); zdialog_add_widget(zd,"button","create","hb7",Bcreate); zdialog_add_widget(zd,"button","delete","hb7",Bdelete); zdialog_add_widget(zd,"hbox","hb8","dialog"); zdialog_add_widget(zd,"label","labdeftags","hb8",E2X("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"); // deftags widget mouse/KB event func textwidget_set_eventfunc(widget,manage_deftags_clickfunc); load_deftags(0); // stuff defined tags into dialog deftags_stuff(zd,"ALL"); zdialog_resize(zd,0,400); zdialog_run(zd,managetags_dialog_event,0); // run dialog zdialog_wait(zd); zdialog_free(zd); return; } // mouse click functions for widget having tags void manage_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // tag or tag category was clicked { char *txword, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } txword = textwidget_word(widget,line,pos,",;:",end); if (! txword) return; if (end == ':') zdialog_stuff(zdmanagetags,"catg",txword); // selected category >> dialog widget else zdialog_stuff(zdmanagetags,"tag",txword); // selected tag >> dialog widget zfree(txword); return; } // dialog event and completion callback function int managetags_dialog_event(zdialog *zd, cchar *event) { void tag_orphans(GtkWidget *); char tag[tagcc], catg[tagcc]; int err, changed = 0; if (zd->zstat) { if (zd->zstat == 1) { // report orphan tags zd->zstat = 0; // keep dialog active tag_orphans(zdialog_widget(zd,"dialog")); return 1; // 19.0 } else { // done or [x] zdialog_free(zd); zdmanagetags = 0; return 1; } } 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 (zd_editmeta) // and edit metadata dialog if active deftags_stuff(zd_editmeta,"ALL"); if (zd_batchtags) // and batch tags dialog if active deftags_stuff(zd_batchtags,"ALL"); } return 1; } /********************************************************************************/ // edit EXIF/IPTC data - add or change specified EXIF/IPTC/etc. key void m_meta_edit_any(GtkWidget *, cchar *menu) { int meta_edit_any_dialog_event(zdialog *zd, cchar *event); void meta_edit_any_clickfunc(GtkWidget *, int line, int pos, int kbkey); int err; zdialog *zd; GtkWidget *mtext; char filename[200], buff[100]; cchar *pp1[1]; char *pp2[1], *pp; FILE *fid; F1_help_topic = "edit any meta"; if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (clicked_file) { // use clicked file if present if (! curr_file || ! strmatch(clicked_file,curr_file)) // avoid f_open() re-entry f_open(clicked_file,0,0,1,0); clicked_file = 0; } if (! curr_file) { if (zd_editanymeta) zdialog_free(zd_editanymeta); zd_editanymeta = 0; return; } err = access(curr_file,W_OK); // test file can be written by me 19.1 if (err) { zmessageACK(Mwin,"%s: %s",Bnowriteperm,curr_file); return; } if (FGWM == 'G') gallery(0,"paint",-1); // if gallery view, repaint /*** _____________________________________________________ | Click to Select | File: filename.jpg | | | | | (metadata list) | key name [___________] [fetch] | | | key value [__________________] | | | | | | | | | | | | [Full List] [Save] [Done] | |____________________|________________________________| ***/ if (! zd_editanymeta) // popup dialog if not already { zd = zdialog_new(E2X("Edit Any Metadata"),Mwin,E2X("Full List"),Bsave,Bdone,null); zd_editanymeta = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"expand"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"expand|space=3"); zdialog_add_widget(zd,"label","lab1","vb1",E2X("click to select"),"size=25"); zdialog_add_widget(zd,"frame","frb1","vb1",0,"expand"); zdialog_add_widget(zd,"scrwin","scrb1","frb1",0,"expand"); zdialog_add_widget(zd,"text","mtext","scrb1",0,"expand"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"expand|space=3"); zdialog_add_widget(zd,"hbox","hbf","vb2",0,"space=6"); zdialog_add_widget(zd,"label","labf","hbf",E2X("File:"),"space=3"); zdialog_add_widget(zd,"label","file","hbf","filename.jpg","space=5"); zdialog_add_widget(zd,"hbox","hbkey","vb2",0,"space=2"); zdialog_add_widget(zd,"hbox","hbdata","vb2",0,"space=2"); zdialog_add_widget(zd,"label","labkey","hbkey",E2X("key name")); zdialog_add_widget(zd,"zentry","keyname","hbkey",0,"expand"); zdialog_add_widget(zd,"button","fetch","hbkey",Bfetch,"space=5"); zdialog_add_widget(zd,"label","labdata","hbdata",E2X("key value")); zdialog_add_widget(zd,"zentry","keydata","hbdata",0,"expand"); zdialog_resize(zd,600,300); zdialog_run(zd,meta_edit_any_dialog_event,0); mtext = zdialog_widget(zd,"mtext"); // make clickable metadata list textwidget_clear(mtext); snprintf(filename,200,"%s/metadata_short_list",get_zhomedir()); fid = fopen(filename,"r"); // read metadata list if (! fid) { zmessageACK(Mwin,"%s \n %s",filename,strerror(errno)); return; } while (true) { // populate dialog, left side pp = fgets_trim(buff,100,fid,1); if (! pp) break; if (*pp <= ' ') continue; if (strstr(buff,"White Balance")) continue; textwidget_append(mtext,0,"%s \n",buff); } fclose(fid); textwidget_set_eventfunc(mtext,meta_edit_any_clickfunc); // set mouse/KB event function *keyname = 0; } zd = zd_editanymeta; pp = strrchr(curr_file,'/'); // stuff file name in dialog if (pp) zdialog_stuff(zd,"file",pp+1); if (*keyname) // update live dialog { pp1[0] = keyname; // look for key data exif_get(curr_file,pp1,pp2,1); if (pp2[0]) { strncpy0(keydata,pp2[0],exif_maxcc); zfree(pp2[0]); } else *keydata = 0; zdialog_stuff(zd,"keydata",keydata); // stuff into dialog } return; } // dialog event and completion callback function int meta_edit_any_dialog_event(zdialog *zd, cchar *event) { cchar *pp1[1]; char *pp2[1]; int err; if (! curr_file) return 1; if (strmatch(event,"fetch")) // accept entered key name { zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"keyname",keyname,40); // get key name from dialog strCompress(keyname); pp1[0] = keyname; // look for key data exif_get(curr_file,pp1,pp2,1); if (pp2[0]) { strncpy0(keydata,pp2[0],exif_maxcc); zfree(pp2[0]); } else *keydata = 0; zdialog_stuff(zd,"keydata",keydata); // stuff into dialog } if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) { // show full list zd->zstat = 0; // keep dialog active zmessageACK(Mwin,"The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names"); } else if (zd->zstat == 2) // save { zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"keyname",keyname,40); // get key name from dialog zdialog_fetch(zd,"keydata",keydata,exif_maxcc); strCompress(keyname); pp1[0] = keyname; pp2[0] = keydata; err = exif_put(curr_file,pp1,(cchar **) pp2,1); // change metadata in image file if (err) zmessageACK(Mwin,"error: %s",strerror(err)); load_filemeta(curr_file); // update image index in case update_image_index(curr_file); // searchable metadata item updated if (zd_metaview) meta_view(0); // update exif view if active } else { zdialog_free(zd); // done or cancel zd_editanymeta = 0; } return 1; } // get clicked tag name from short list and insert into dialog void meta_edit_any_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { char *pp, *pp2[1]; cchar *pp1[1]; if (! zd_editanymeta) return; if (! curr_file) return; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } pp = textwidget_line(widget,line,1); // get clicked line, highlight if (! pp || ! *pp) return; textwidget_highlight_line(widget,line); zdialog_stuff(zd_editanymeta,"keyname",pp); zdialog_fetch(zd_editanymeta,"keyname",keyname,40); // get key name from dialog strCompress(keyname); pp1[0] = keyname; // look for key data exif_get(curr_file,pp1,pp2,1); if (pp2[0]) { strncpy0(keydata,pp2[0],exif_maxcc); zfree(pp2[0]); } else *keydata = 0; zdialog_stuff(zd_editanymeta,"keydata",keydata); // stuff into dialog return; } /********************************************************************************/ // delete EXIF/IPTC data, specific key or all data void m_meta_delete(GtkWidget *, cchar *menu) { int meta_delete_dialog_event(zdialog *zd, cchar *event); zdialog *zd; char *pp; int err; F1_help_topic = "delete meta"; if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (clicked_file) { // use clicked file if present if (! curr_file || ! strmatch(clicked_file,curr_file)) // avoid f_open() re-entry f_open(clicked_file,0,0,1,0); clicked_file = 0; } if (! curr_file) return; err = access(curr_file,W_OK); // test file can be written by me 19.1 if (err) { zmessageACK(Mwin,"%s: %s",Bnowriteperm,curr_file); return; } if (FGWM == 'G') gallery(0,"paint",-1); // if gallery view, repaint /*** _________________________________________ | Delete Metadata | | | | File: [______________________________] | | | | (o) ALL (o) One Key: [______________] | | | | [apply] [cancel] | |_________________________________________| ***/ if (! zd_deletemeta) { zd = zdialog_new(E2X("Delete Metadata"),Mwin,Bapply,Bcancel,null); zd_deletemeta = zd; zdialog_add_widget(zd,"hbox","hbf","dialog"); zdialog_add_widget(zd,"label","labf","hbf",E2X("File:"),"space=3"); zdialog_add_widget(zd,"label","file","hbf",0,"space=5"); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"radio","kall","hb1",E2X("All"),"space=5"); zdialog_add_widget(zd,"radio","key1","hb1",E2X("One Key:")); zdialog_add_widget(zd,"zentry","keyname","hb1",0,"size=20"); zdialog_stuff(zd,"key1",1); zdialog_run(zd,meta_delete_dialog_event,"parent"); } zd = zd_deletemeta; pp = (char *) ""; if (curr_file) { pp = strrchr(curr_file,'/'); if (pp) pp++; else pp = curr_file; } zdialog_stuff(zd,"file",pp); return; } // dialog event and completion callback function int meta_delete_dialog_event(zdialog *zd, cchar *event) { int kall, key1; char keyname[40]; char *file; if (! zd->zstat) return 1; // wait for completion if (zd->zstat != 1) { // canceled zdialog_free(zd); zd_deletemeta = 0; return 1; } zd->zstat = 0; // dialog remains active if (! curr_file) return 1; zdialog_fetch(zd,"kall",kall); zdialog_fetch(zd,"key1",key1); zdialog_fetch(zd,"keyname",keyname,40); strCompress(keyname); if (! kall && ! key1) return 1; file = zescape_quotes(curr_file); if (kall) // update file metadata shell_ack("exiftool -m -q -overwrite_original -all= \"%s\"",file); else if (key1) shell_ack("exiftool -m -q -overwrite_original -%s= \"%s\"",keyname,file); zfree(file); load_filemeta(curr_file); // update image index in case a update_image_index(curr_file); // searchable metadata deleted if (zd_metaview) meta_view(0); // update exif view if active return 1; } /********************************************************************************/ // Show file name, caption, comment on top of current image in main window. // Menu call (menu arg not null): dialog to set user display options. // Non-menu call: write filename/caption/comment on image depending on options. void m_meta_captions(GtkWidget *, cchar *menu) // rewrite 20.0 { zdialog *zd; int nn; cchar *keynames[2] = { iptc_caption_key, exif_comment_key }; char *pp, *keyvals[2]; char fname[200], caption[200], comment[200]; static char text[604]; F1_help_topic = "captions"; if (FGWM != 'F' && FGWM != 'G') return; if (menu) // user settings dialog { zd = zdialog_new(0,Mwin,"OK",0); zdialog_add_widget(zd,"hbox","hbchoose","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labchoose","hbchoose",E2X("choose options")); zdialog_add_widget(zd,"check","filename","dialog",E2X("file name")); zdialog_add_widget(zd,"check","caption","dialog",E2X("caption")); zdialog_add_widget(zd,"check","comment","dialog",E2X("comment")); zdialog_add_widget(zd,"check","ALL","dialog","ALL"); if (Fcaptions & 1) zdialog_stuff(zd,"filename",1); if (Fcaptions & 2) zdialog_stuff(zd,"caption",1); if (Fcaptions & 4) zdialog_stuff(zd,"comment",1); if (Fcaptions & 8) zdialog_stuff(zd,"ALL",1); zdialog_set_decorated(zd,0); zdialog_resize(zd,200,0); zdialog_run(zd,0,"mouse"); zdialog_wait(zd); Fcaptions = 0; zdialog_fetch(zd,"filename",nn); if (nn) Fcaptions += 1; zdialog_fetch(zd,"caption",nn); if (nn) Fcaptions += 2; zdialog_fetch(zd,"comment",nn); if (nn) Fcaptions += 4; zdialog_fetch(zd,"ALL",nn); if (nn) Fcaptions += 8; zdialog_free(zd); } if (! Fcaptions) return; if (! curr_file) return; *text = *fname = *caption = *comment = 0; // clear output text if (Fcaptions & (1 + 8)) // file name wanted { pp = strrchr(curr_file,'/'); if (pp) pp++; else pp = curr_file; strncpy0(fname,pp,200); } if (Fcaptions & (2 + 4 + 8)) // caption or comments wanted { exif_get(curr_file,keynames,keyvals,2); // get metadata from image file if (keyvals[0]) { strncpy0(caption,keyvals[0],200); zfree(keyvals[0]); } if (keyvals[1]) { strncpy0(comment,keyvals[1],200); zfree(keyvals[1]); } if (! (Fcaptions & (2 + 8))) *caption = 0; if (! (Fcaptions & (4 + 8))) *comment = 0; } if (*fname) strcpy(text,fname); if (*caption) { if (*text) strcat(text,"\n"); strcat(text,caption); } if (*comment) { if (*text) strcat(text,"\n"); strcat(text,comment); } for (int ii = 0; text[ii]; ii++) // replace "\n" with newline chars. if (text[ii] == '\\' && text[ii+1] == 'n') memmove(text+ii," \n",2); if (*text) add_toptext(1,0,0,text,"Sans 10"); Fpaintnow(); return; } /********************************************************************************/ // menu function - add and remove tags for many files at once namespace batchtags { char addtags[tagMcc]; // tags to add, list char deltags[tagMcc]; // tags to remove, list int radadd, raddel; // dialog radio buttons char countmess[80]; } void m_batch_tags(GtkWidget *, cchar *menu) // combine batch add/del tags { using namespace batchtags; void batch_addtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void batch_deltags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void batch_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void batch_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); int batch_tags_dialog_event(zdialog *zd, cchar *event); char *ptag, *file; int zstat, ii, jj, err, yn; zdialog *zd, *zd2; GtkWidget *widget; F1_help_topic = "batch tags"; if (FGWM != 'F' && FGWM != 'G') return; // 19.0 if (checkpend("all")) return; // check nothing pending if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } Fblock = 1; /*** ________________________________________________________ | Batch Add/Remove Tags | | | | [Select Files] NN files selected | | | | (o) tags to add [________________________________] | | (o) tags to remove [________________________________] | | - - - - - - - - - - - - - - - - - - - - - - - - - - - | | Enter New Tag [___________] [Add] | | Matching Tags [_____________________________________] | | - - - - - - - - - - - - - - - - - - - - - - - - - - - | | Defined Tags Category [___________________________|v] | | | | | | | | | | | | | | | | | | | | | | | | | | |___________________________________________________| | | | | [Proceed] [Cancel] | |________________________________________________________| ***/ zd = zdialog_new(E2X("Batch Add/Remove Tags"),Mwin,Bproceed,Bcancel,null); zd_batchtags = zd; // [Select Files] NN files selected zdialog_add_widget(zd,"hbox","hbfiles","dialog",0,"space=3"); zdialog_add_widget(zd,"button","files","hbfiles",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","labcount","hbfiles",Bnofileselected,"space=10"); // (o) tags to add [_______________________________________] // (o) tags to remove [_______________________________________] zdialog_add_widget(zd,"hbox","hbtags","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vb1","hbtags",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vb2","hbtags",0,"space=3|homog|expand"); zdialog_add_widget(zd,"radio","radadd","vb1",E2X("tags to add")); zdialog_add_widget(zd,"radio","raddel","vb1",E2X("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"); // Enter New Tag [________________] [Add] zdialog_add_widget(zd,"hbox","hbnt","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labnt","hbnt",E2X("Enter New Tag"),"space=3"); zdialog_add_widget(zd,"zentry","newtag","hbnt"); zdialog_add_widget(zd,"button","add","hbnt",Badd,"space=5"); // Matching Tags [____________________________________________] zdialog_add_widget(zd,"hbox","hbmt","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labmt","hbmt",E2X("Matching Tags"),"space=3"); zdialog_add_widget(zd,"frame","frmt","hbmt",0,"space=3|expand"); zdialog_add_widget(zd,"text","matchtags","frmt",0,"wrap"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=3"); // Defined Tags Category [__________________________________|v] zdialog_add_widget(zd,"hbox","space","dialog"); zdialog_add_widget(zd,"hbox","hbdt1","dialog"); zdialog_add_widget(zd,"label","labdt","hbdt1",E2X("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); // initz. radio buttons zdialog_stuff(zd,"raddel",0); load_deftags(0); // stuff defined tags into dialog deftags_stuff(zd,"ALL"); defcats_stuff(zd); // and defined categories *addtags = *deltags = 0; snprintf(countmess,80,Bfileselected,GScount); // show selected files count 20.0 zdialog_stuff(zd,"labcount",countmess); widget = zdialog_widget(zd,"addtags"); // tag widget mouse/KB event funcs textwidget_set_eventfunc(widget,batch_addtags_clickfunc); widget = zdialog_widget(zd,"deltags"); textwidget_set_eventfunc(widget,batch_deltags_clickfunc); widget = zdialog_widget(zd,"matchtags"); textwidget_set_eventfunc(widget,batch_matchtags_clickfunc); widget = zdialog_widget(zd,"deftags"); textwidget_set_eventfunc(widget,batch_deftags_clickfunc); zdialog_resize(zd,500,500); // run dialog zdialog_run(zd,batch_tags_dialog_event,0); zstat = zdialog_wait(zd); // wait for dialog completion zdialog_free(zd); zd_batchtags = 0; if (zstat != 1) { // cancel 20.0 Fblock = 0; return; } zd2 = popup_report_open("Batch Tags",Mwin,500,200,0,0,0); // status report popup window for (ii = 0; ii < GScount; ii++) // loop all selected files { zmainsleep(0.1); // keep GTK alive file = GSfiles[ii]; // display image err = f_open(file,0,0,0); if (err) continue; popup_report_write(zd2,0,"%s \n",file); // report progress popup_report_bottom(zd2); err = access(file,W_OK); // test file can be written by me 19.1 if (err) { popup_report_write(zd2,0,"%s \n",Bnowriteperm); popup_report_bottom(zd2); continue; } for (jj = 1; ; jj++) // remove tags if present { ptag = (char *) strField(deltags,",;",jj); if (! ptag) break; if (*ptag == 0) 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 == 0) continue; err = add_tag(ptag,meta_tags,tagFcc); if (err == 2) { zmessageACK(Mwin,E2X("%s \n too many tags"),file); break; } } save_filemeta(file); // save tag changes } popup_report_write(zd2,0,"\n *** %s \n",Bcompleted); popup_report_bottom(zd2); load_deftags(1); // update defined tags list 20.0 Fblock = 0; yn = zmessageYN(Mwin,E2X("repeat with same files?")); // allow repeat with same files if (yn) { popup_report_close(zd2,0); m_batch_tags(0,0); } return; } // mouse click functions for widgets holding tags void batch_addtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // a tag in the add list was clicked { using namespace batchtags; char *txtag, end; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;",end); if (! txtag) return; del_tag(txtag,addtags); // remove tag from list zdialog_stuff(zd_batchtags,"addtags",addtags); zfree(txtag); return; } void batch_deltags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // a tag in the remove list was clicked { using namespace batchtags; char *txtag, end; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;",end); if (! txtag) return; del_tag(txtag,deltags); // remove tag from list zdialog_stuff(zd_batchtags,"deltags",deltags); zfree(txtag); return; } void batch_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // matching tag was clicked { using namespace batchtags; char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;",end); if (! txtag) return; zdialog_fetch(zd_batchtags,"radadd",radadd); // which radio button? if (radadd) { add_tag(txtag,addtags,tagMcc); // add recent tag to tag add list zdialog_stuff(zd_batchtags,"addtags",addtags); } else { add_tag(txtag,deltags,tagMcc); // add recent tag to tag remove list zdialog_stuff(zd_batchtags,"deltags",deltags); } zdialog_stuff(zd_batchtags,"newtag",""); // clear newtag and matchtags zdialog_stuff(zd_batchtags,"matchtags",""); zdialog_goto(zd_batchtags,"newtag"); // put focus back on newtag widget zfree(txtag); return; } void batch_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // a defined tag was clicked { using namespace batchtags; char *txtag, end; int radadd; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;:",end); if (! txtag || end == ':') return; // tag category clicked, ignore zdialog_fetch(zd_batchtags,"radadd",radadd); // which radio button? if (radadd) { add_tag(txtag,addtags,tagMcc); // add defined tag to tag add list zdialog_stuff(zd_batchtags,"addtags",addtags); } else { add_tag(txtag,deltags,tagMcc); // add defined tag to tag remove list zdialog_stuff(zd_batchtags,"deltags",deltags); } zfree(txtag); return; } // batchTags dialog event function int batch_tags_dialog_event(zdialog *zd, cchar *event) { using namespace batchtags; char catgname[tagcc]; int ii, jj, nt, cc1, cc2, ff; char *pp1, *pp2; char newtag[tagcc], matchtags[20][tagcc]; char matchtagstext[(tagcc+2)*20]; if (strmatch(event,"cancel")) zd->zstat = 3; if (zd->zstat) // dialog completed { if (zd->zstat == 1) { // proceed if (! GScount || (*addtags <= ' ' && *deltags <= ' ')) { zmessageACK(Mwin,E2X("specify files and tags")); zd->zstat = 0; // keep dialog active } } return 1; // cancel } if (strmatch(event,"files")) // select images to process { zdialog_show(zd,0); // hide parent dialog gallery_select(); // get new list zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); zdialog_stuff(zd,"labcount",countmess); } if (zstrstr("radadd raddel",event)) { // get state of radio buttons zdialog_fetch(zd,"radadd",radadd); zdialog_fetch(zd,"raddel",raddel); } if (strmatch(event,"defcats")) { // new tag category selection zdialog_fetch(zd,"defcats",catgname,tagcc); deftags_stuff(zd,catgname); } if (strmatch(event,"newtag")) // new tag is being typed in { zdialog_stuff(zd,"matchtags",""); // clear matchtags in dialog zdialog_fetch(zd,"newtag",newtag,tagcc); // get chars. typed so far cc1 = strlen(newtag); for (ii = jj = 0; ii <= cc1; ii++) { // remove foul characters if (strchr(",:;",newtag[ii])) continue; newtag[jj++] = newtag[ii]; } if (jj < cc1) { // something was removed newtag[jj] = 0; cc1 = jj; zdialog_stuff(zd,"newtag",newtag); } if (cc1 < 2) return 1; // wait for at least 2 chars. for (ii = nt = 0; ii < maxtagcats; ii++) // loop all categories { pp2 = tags_deftags[ii]; // category: aaaaaa, bbbbb, ... tagN, if (! pp2) continue; // | | pp2 = strchr(pp2,':'); // pp1 pp2 while (true) // loop all deftags in category { pp1 = pp2 + 2; if (! *pp1) break; pp2 = strchr(pp1,','); if (! pp2) break; if (strmatchcaseN(newtag,pp1,cc1)) { // deftag matches chars. typed so far cc2 = pp2 - pp1; strncpy(matchtags[nt],pp1,cc2); // save deftags that match matchtags[nt][cc2] = 0; if (++nt == 20) return 1; // quit if 20 matches or more } } } if (nt == 0) return 1; // no matches pp1 = matchtagstext; for (ii = 0; ii < nt; ii++) // make deftag list: aaaaa, bbb, cccc ... { strcpy(pp1,matchtags[ii]); pp1 += strlen(pp1); strcpy(pp1,", "); pp1 += 2; } zdialog_stuff(zd,"matchtags",matchtagstext); // stuff matchtags in dialog return 1; } if (strmatch(event,"add")) // enter new tag finished { zdialog_fetch(zd,"newtag",newtag,tagcc); // get finished tag cc1 = strlen(newtag); if (! cc1) return 1; if (newtag[cc1-1] == '\n') { // remove newline character cc1--; newtag[cc1] = 0; } for (ii = ff = 0; ii < maxtagcats; ii++) // loop all categories { pp2 = tags_deftags[ii]; // category: aaaaaa, bbbbb, ... tagN, if (! pp2) continue; // | | pp2 = strchr(pp2,':'); // pp1 pp2 while (true) // loop all deftags in category { pp1 = pp2 + 2; if (! *pp1) break; pp2 = strchr(pp1,','); if (! pp2) break; cc2 = pp2 - pp1; if (cc2 != cc1) continue; if (strmatchcaseN(newtag,pp1,cc1)) { // entered tag matches deftag strncpy(newtag,pp1,cc1); // use deftag upper/lower case ff = 1; break; } } if (ff) break; } if (! ff) { // if new tag, add to defined tags add_deftag((char *) "nocatg",newtag); deftags_stuff(zd,"ALL"); } add_tag(newtag,addtags,tagMcc); // add to tag add list zdialog_stuff(zd_batchtags,"addtags",addtags); zdialog_stuff(zd,"newtag",""); // update dialog widgets zdialog_stuff(zd,"matchtags",""); zdialog_goto(zd,"newtag"); // put focus back on newtag widget return 1; } return 1; } /********************************************************************************/ // menu function - rename multiple tags for selected image files namespace batchrenametags { int Ntags; // count, 1-100 char *oldtags[100]; // tags to rename char *newtags[100]; // corresponding new name #define tpcc (tagcc+tagcc+10) zdialog *zd; } // menu function void m_batch_rename_tags(GtkWidget *, cchar *menu) { using namespace batchrenametags; void batchrenametags_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void batchrenametags_taglist_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); int batchrenametags_dialog_event(zdialog *zd, cchar *event); char *file; int ii, jj, kk, ff, err, yn; int zstat, Nfiles, Nlist; GtkWidget *widget; char **filelist; char *pp, *filetag; char *oldtaglist[100], *newtaglist[100]; xxrec_t *xxrec; zdialog *zd2; F1_help_topic = "batch rename tags"; if (checkpend("all")) return; // check nothing pending if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } Fblock = 1; Ntags = Nfiles = 0; filelist = 0; /*** ____________________________________________________________________________ | Batch Rename Tags | | | | old tag name >> new tag name | | Tag [_______] Rename to [_________] [->] | aaaaaaaa >> bbbbbbbbbbb | | | ccccccccccc >> ddddddddd | | Defined Tags Category [________________|v| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |________________________________________| |_______________________________| | | | [Proceed] [Cancel] | |____________________________________________________________________________| ***/ zd = zdialog_new(E2X("Batch Rename Tags"),Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"expand"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"expand"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=8|expand"); // tag [_________________] rename to [___________________] [-->] zdialog_add_widget(zd,"hbox","hbtags","vb1",0,"space=3"); zdialog_add_widget(zd,"label","lab1","hbtags","Tag","space=3"); zdialog_add_widget(zd,"frame","frot","hbtags"); zdialog_add_widget(zd,"label","oldtag","frot",E2X("(click defined tag)")); zdialog_add_widget(zd,"label","space","hbtags",0,"space=5"); zdialog_add_widget(zd,"label","lab2","hbtags",E2X("Rename to"),"space=3"); zdialog_add_widget(zd,"zentry","newtag","hbtags",0,"expand"); zdialog_add_widget(zd,"label","space","hbtags",0,"space=3"); zdialog_add_widget(zd,"button","addtags","hbtags",">>"); zdialog_add_widget(zd,"hsep","hsep1","vb1",0,"space=5"); // Defined Tags Category [_____________________|v] zdialog_add_widget(zd,"hbox","hbdt","vb1",0); zdialog_add_widget(zd,"label","labdt","hbdt",E2X("Defined Tags Category"),"space=3"); zdialog_add_widget(zd,"combo","defcats","hbdt",0,"space=5"); zdialog_add_widget(zd,"frame","frdt","vb1",0,"expand|space=3"); zdialog_add_widget(zd,"scrwin","swdt","frdt",0,"expand"); zdialog_add_widget(zd,"text","deftags","swdt",0,"wrap"); // old tag name >> new tag name zdialog_add_widget(zd,"hbox","hblist","vb2"); zdialog_add_widget(zd,"label","lablist","hblist",E2X("old tag name >> new tag name"),"space=10"); zdialog_add_widget(zd,"frame","frlist","vb2",0,"expand|space=3"); zdialog_add_widget(zd,"scrwin","swlist","frlist"); zdialog_add_widget(zd,"text","taglist","swlist"); load_deftags(0); // stuff defined tags into dialog deftags_stuff(zd,"ALL"); defcats_stuff(zd); // and defined categories widget = zdialog_widget(zd,"deftags"); // connect mouse to defined tags widget textwidget_set_eventfunc(widget,batchrenametags_deftags_clickfunc); widget = zdialog_widget(zd,"taglist"); // connect mouse to taglist widget textwidget_set_eventfunc(widget,batchrenametags_taglist_clickfunc); zdialog_resize(zd,700,400); // run dialog zdialog_run(zd,batchrenametags_dialog_event,0); zstat = zdialog_wait(zd); // wait for dialog completion zdialog_free(zd); zd = 0; if (zstat != 1) goto cleanup; // [cancel] filelist = (char **) zmalloc(Nxxrec * sizeof(char *)); // find all affected image files Nfiles = 0; zd2 = popup_report_open("rename tags",Mwin,500,300,0,0,0); for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; if (! xxrec->tags) continue; // search for tags to rename ff = 0; for (jj = 1; ; jj++) { pp = (char *) strField(xxrec->tags,',',jj); if (! pp) break; for (kk = 0; kk < Ntags; kk++) { if (strmatchcase(pp,oldtags[kk])) { // this file has one or more tags ff = 1; // that will be renamed break; } } if (ff) break; } if (ff) { filelist[Nfiles] = zstrdup(xxrec->file); // add to list of files to process Nfiles++; popup_report_write(zd2,0,"file included: %s \n",xxrec->file); } } yn = zmessageYN(Mwin,E2X("%d tags to rename \n" "in %d image files. \n" "Proceed?"),Ntags,Nfiles); if (! yn) goto cleanup; for (ii = 0; ii < Nfiles; ii++) // loop all selected files { zmainloop(); // keep GTK alive file = filelist[ii]; // open image file err = f_open(file,0,0,0); if (err) continue; popup_report_write(zd2,0,"%s \n",file); // report progress popup_report_bottom(zd2); err = access(file,W_OK); // test file can be written by me 19.1 if (err) { popup_report_write(zd2,0,"%s \n",Bnowriteperm); continue; } Nlist = 0; for (jj = 1; ; jj++) { // loop file tags filetag = (char *) strField(meta_tags,',',jj); if (! filetag) break; for (kk = 0; kk < Ntags; kk++) { // loop tag replacement list if (strmatchcase(filetag,oldtags[kk])) { // file tag matches tag to replace oldtaglist[Nlist] = oldtags[kk]; // save old and new tags newtaglist[Nlist] = newtags[kk]; Nlist++; break; // next file tag } } } for (jj = 0; jj < Nlist; jj++) // remove old tags err = del_tag(oldtaglist[jj],meta_tags); for (jj = 0; jj < Nlist; jj++) { // add new tags if (! newtaglist[jj]) continue; // must be after removals popup_report_write(zd2,0,"%s \n",newtaglist[jj]); err = add_tag(newtaglist[jj],meta_tags,tagFcc); if (err && err != 1) popup_report_write(zd2,1,"ERROR \n"); // ignore already there, else report } save_filemeta(file); // save tag changes } popup_report_write(zd2,0,"\n *** %s \n",Bcompleted); popup_report_bottom(zd2); load_deftags(1); // update tag list 20.0 cleanup: // free resources Fblock = 0; for (ii = 0; ii < Ntags; ii++) { zfree(oldtags[ii]); zfree(newtags[ii]); } for (ii = 0; ii < Nfiles; ii++) zfree(filelist[ii]); if (filelist) zfree(filelist); return; } // a defined tag was clicked void batchrenametags_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { using namespace batchrenametags; char *txtag, end; char tagname[tagcc]; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;:",end); // clicked word if (! txtag || end == ':') return; // tag category clicked, ignore snprintf(tagname,tagcc," %s ",txtag); // add spaces for appearance zdialog_stuff(zd,"oldtag",tagname); zdialog_stuff(zd,"newtag",""); zfree(txtag); return; } // a tag list line was clicked void batchrenametags_taglist_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { using namespace batchrenametags; int ii; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } if (line >= Ntags) return; for (ii = line; ii < Ntags-1; ii++) { // remove tags pair corresponding oldtags[ii] = oldtags[ii+1]; // to the line clicked newtags[ii] = newtags[ii+1]; } Ntags--; widget = zdialog_widget(zd,"taglist"); // rewrite dialog tag list textwidget_clear(widget); for (int ii = 0; ii < Ntags; ii++) textwidget_append2(widget,0,"%s >> %s\n",oldtags[ii],newtags[ii]); return; } // batch rename tags dialog event function int batchrenametags_dialog_event(zdialog *zd, cchar *event) { using namespace batchrenametags; char catgname[tagcc]; char oldtag[tagcc], newtag[tagcc]; GtkWidget *widget; if (strmatch(event,"cancel")) zd->zstat = 2; if (zd->zstat) return 1; // dialog completed if (strmatch(event,"defcats")) { // new tag category selection zdialog_fetch(zd,"defcats",catgname,tagcc); deftags_stuff(zd,catgname); } if (strmatch(event,"addtags")) { // [ --> ] button pressed zdialog_fetch(zd,"oldtag",oldtag,tagcc); // save new pair of tag names zdialog_fetch(zd,"newtag",newtag,tagcc); strTrim2(oldtag); strTrim2(newtag); if (*oldtag <= ' ' || *newtag <= ' ') return 1; if (Ntags == 100) { zmessageACK(Mwin,E2X("max tags exceeded")); return 1; } oldtags[Ntags] = zstrdup(oldtag); newtags[Ntags] = zstrdup(newtag); Ntags++; } widget = zdialog_widget(zd,"taglist"); // rewrite dialog tag list textwidget_clear(widget); for (int ii = 0; ii < Ntags; ii++) textwidget_append2(widget,0,"%s >> %s\n",oldtags[ii],newtags[ii]); return 1; } /********************************************************************************/ // batch change or shift photo date/time void m_batch_photo_date_time(GtkWidget *, cchar *menu) { int batch_photo_time_dialog_event(zdialog *zd, cchar *event); cchar *keyname[1] = { "DateTimeOriginal" }; char *keyvalue[1]; char text[100]; char *file, olddatetime[24], newdatetime[24]; // exif format "yyyy:mm:dd hh:mm:ss" int ii, nn, cc, err, zstat; int Fyearonly, Fdateonly; int Fsetnew, Fshift, Ftest; // check boxes time_t timep; struct tm DTold, DTnew; // old and new date/time int s_years, s_mons, s_mdays, s_hours, s_mins, s_secs; // shift amounts zdialog *zd, *zd2; F1_help_topic = "batch photo date"; if (checkpend("all")) return; // check nothing pending if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } Fblock = 1; /*** __________________________________________________ | Batch Photo Date/Time | | | | [Select Files] NN files selected | | | | [x] set a new date/time: [_____________________] | | (yyyy-mm-dd hh:mm:ss) | | | | [x] shift existing date/time: | | years [__] months [__] days [__] | | hours [__] minutes [__] seconds [__] | | | | [x] test: show changes, do not update files | | | | [proceed] [cancel] | |__________________________________________________| ***/ zd = zdialog_new(E2X("Batch Photo Date/Time"),Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hbfiles","dialog",0,"space=3"); zdialog_add_widget(zd,"button","files","hbfiles",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","labcount","hbfiles",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbsetnew","dialog",0,"space=3"); zdialog_add_widget(zd,"check","Fsetnew","hbsetnew",E2X("set a new date/time:"),"space=3"); zdialog_add_widget(zd,"zentry","newdatetime","hbsetnew",0,"expand|size=15"); zdialog_add_widget(zd,"hbox","hbsetnew2","dialog"); zdialog_add_widget(zd,"label","labspace","hbsetnew2","","expand"); zdialog_add_widget(zd,"label","labtemplate","hbsetnew2","yyyy-mm-dd hh:mm[:ss]","space=5"); zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbshift1","dialog",0,"space=3"); zdialog_add_widget(zd,"check","Fshift","hbshift1",E2X("shift existing date/time:"),"space=3"); zdialog_add_widget(zd,"hbox","hbshift2","dialog"); zdialog_add_widget(zd,"label","space","hbshift2",0,"space=10"); zdialog_add_widget(zd,"label","labyears","hbshift2","years","space=5"); zdialog_add_widget(zd,"zspin","s_years","hbshift2","-99|+99|1|0"); zdialog_add_widget(zd,"label","space","hbshift2",0,"space=5"); zdialog_add_widget(zd,"label","labmons","hbshift2","months","space=5"); zdialog_add_widget(zd,"zspin","s_mons","hbshift2","-11|+11|1|0"); zdialog_add_widget(zd,"label","space","hbshift2",0,"space=5"); zdialog_add_widget(zd,"label","labmdays","hbshift2","days","space=5"); zdialog_add_widget(zd,"zspin","s_mdays","hbshift2","-30|+30|1|0"); zdialog_add_widget(zd,"hbox","hbshift3","dialog"); zdialog_add_widget(zd,"label","space","hbshift3",0,"space=10"); zdialog_add_widget(zd,"label","labhours","hbshift3","hours","space=5"); zdialog_add_widget(zd,"zspin","s_hours","hbshift3","-23|+23|1|0"); zdialog_add_widget(zd,"label","space","hbshift3",0,"space=5"); zdialog_add_widget(zd,"label","labmins","hbshift3","minutes","space=5"); zdialog_add_widget(zd,"zspin","s_mins","hbshift3","-59|+59|1|0"); zdialog_add_widget(zd,"label","space","hbshift3",0,"space=5"); zdialog_add_widget(zd,"label","labsecs","hbshift3","seconds","space=5"); zdialog_add_widget(zd,"zspin","s_secs","hbshift3","-59|+59|1|0"); zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbtest","dialog",0,"space=5"); zdialog_add_widget(zd,"check","Ftest","hbtest",E2X("test: show changes, do not update files"),"space=3"); zdialog_restore_inputs(zd); snprintf(text,100,Bfileselected,GScount); // show selected files count 20.0 zdialog_stuff(zd,"labcount",text); zstat = zdialog_run(zd,batch_photo_time_dialog_event,"parent"); retry: zstat = zdialog_wait(zd); // wait for dialog, get status if (zstat != 1) { // not [proceed] zdialog_free(zd); // cancel Fblock = 0; return; } zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"Fsetnew",Fsetnew); // checkboxes zdialog_fetch(zd,"Fshift",Fshift); zdialog_fetch(zd,"Ftest",Ftest); if (Fsetnew + Fshift != 1) { zmessageACK(Mwin,E2X("please make a choice")); goto retry; } if (GScount == 0) { zmessageACK(Mwin,E2X("no files selected")); goto retry; } Fyearonly = Fdateonly = 0; if (Fsetnew) // input is new date/time { zdialog_fetch(zd,"newdatetime",newdatetime,24); strTrim2(newdatetime); // strip leading and trailing blanks cc = strlen(newdatetime); if (cc == 4) { // have only "yyyy" strcat(newdatetime,"-01-01 00:00:00"); // append "-01-01 00:00:00" Fyearonly = 1; cc = 19; } if (cc == 10) { // have only "yyyy-mm-dd" strcat(newdatetime," 00:00:00"); // append " 00:00:00" Fdateonly = 1; // flag, change date only cc = 19; } if (cc == 16) { // have only "yyyy-mm-dd hh:mm" strcat(newdatetime,":00"); // append ":00" cc = 19; } if (cc != 19) { // must have yyyy-mm-dd hh:mm:ss zmessageACK(Mwin,E2X("invalid date/time format")); goto retry; } nn = sscanf(newdatetime,"%d-%d-%d %d:%d:%d", // yyyy-mm-dd hh:mm:ss >> DTnew &DTnew.tm_year, &DTnew.tm_mon, &DTnew.tm_mday, &DTnew.tm_hour, &DTnew.tm_min, &DTnew.tm_sec); DTnew.tm_mon -= 1; // mktime month is 0-11 if (nn != 6) { // check input format zmessageACK(Mwin,E2X("invalid date/time format")); goto retry; } timep = mktime(&DTnew); // DTnew >> timep if (timep < 0) { // validate DTnew by validating timep zmessageACK(Mwin,E2X("invalid date/time format")); goto retry; } } // DTnew is final value to use if (Fshift) { zdialog_fetch(zd,"s_years",s_years); // inputs are shifted date/time values zdialog_fetch(zd,"s_mons",s_mons); zdialog_fetch(zd,"s_mdays",s_mdays); zdialog_fetch(zd,"s_hours",s_hours); zdialog_fetch(zd,"s_mins",s_mins); zdialog_fetch(zd,"s_secs",s_secs); } zdialog_free(zd); zd2 = popup_report_open(E2X("Batch Photo Date/Time"),Mwin,500,200,0,0,0); // start popup report if (Fshift) { popup_report_write(zd2,0,"changes: year mon day hours mins secs \n"); popup_report_write(zd2,0," %4d %3d %3d %5d %4d %4d \n", s_years,s_mons,s_mdays,s_hours,s_mins,s_secs); } for (ii = 0; ii < GScount; ii++) // loop all selected files { file = GSfiles[ii]; err = f_open(file,0,0,0); // open image file if (err) continue; popup_report_write(zd2,0,"\n"); // report progress popup_report_write(zd2,0,"%s \n",file); err = access(file,W_OK); // test file can be written by me 19.1 if (err) { popup_report_write(zd2,0,"%s \n",Bnowriteperm); continue; } exif_get(curr_file,keyname,(char **) keyvalue,1); // metadata >> yyyy:mm:dd hh:mm:ss if (! keyvalue[0] && Fshift) { // ignore if Fsetnew popup_report_write(zd2,0," *** no date/time available \n"); continue; } if (keyvalue[0]) { strncpy0(olddatetime,keyvalue[0],20); // yyyy:mm:dd hh:mm:ss zfree(keyvalue[0]); } else strcpy(olddatetime,"0000:01:01 00:00:00"); // missing old date/time nn = sscanf(olddatetime,"%d:%d:%d %d:%d:%d", // yyyy-mm-dd hh:mm:ss >> DTnew &DTold.tm_year, &DTold.tm_mon, &DTold.tm_mday, &DTold.tm_hour, &DTold.tm_min, &DTold.tm_sec); DTold.tm_mon -= 1; // mktime month is 0-11 if (nn != 6 && Fshift) { popup_report_write(zd2,0," *** EXIF date/time invalid \n"); continue; } if (nn != 6) strcpy(olddatetime,"0000:01:01 00:00:00"); // missing old date/time if (Fsetnew) // set new date/time { if (Fyearonly) // change year only, leave rest { DTnew.tm_mon = DTold.tm_mon; // >> revised DTnew DTnew.tm_mday = DTold.tm_mday; // set month/day/hour/min/sec only DTnew.tm_hour = DTold.tm_hour; // year remains fixed DTnew.tm_min = DTold.tm_min; DTnew.tm_sec = DTold.tm_sec; } if (Fdateonly) // change year/mon/day only, leave time { DTnew.tm_hour = DTold.tm_hour; // >> revised DTnew DTnew.tm_min = DTold.tm_min; // set hour/min/sec only DTnew.tm_sec = DTold.tm_sec; // year/mon/day remains fixed } } if (Fshift) // shift existing date/time values { DTnew.tm_year = DTold.tm_year + s_years; DTnew.tm_mon = DTold.tm_mon + s_mons; DTnew.tm_mday = DTold.tm_mday + s_mdays; DTnew.tm_hour = DTold.tm_hour + s_hours; DTnew.tm_min = DTold.tm_min + s_mins; DTnew.tm_sec = DTold.tm_sec + s_secs; } timep = mktime(&DTnew); if (timep < 0) { popup_report_write(zd2,0," %s *** date/time conversion failed \n",olddatetime); continue; } DTnew = *localtime(&timep); snprintf(newdatetime,20,"%04d:%02d:%02d %02d:%02d:%02d", // DTnew >> yyyy:mm:dd hh:mm:ss DTnew.tm_year, DTnew.tm_mon+1, DTnew.tm_mday, // (tm_mon 0-11 >> 1-12) DTnew.tm_hour, DTnew.tm_min, DTnew.tm_sec); olddatetime[4] = olddatetime[7] = newdatetime[4] = newdatetime[7] = '-'; // format: yyyy-mm-dd popup_report_write(zd2,0," %s %s \n",olddatetime,newdatetime); if (Ftest) continue; // test only, no file updates newdatetime[4] = newdatetime[7] = ':'; // format: yyyy:mm:dd for EXIF keyvalue[0] = (char *) &newdatetime; err = exif_put(curr_file,keyname,(cchar **) keyvalue,1); // yyyy:mm:dd hh:mm:ss >> metadata load_filemeta(curr_file); // get all indexed data for file snprintf(meta_pdate,16,"%04d%02d%02d%02d%02d%02d", // update photo date, yyyymmddhhmmss DTnew.tm_year, DTnew.tm_mon+1, DTnew.tm_mday, // (tm_mon 0-11 >> 1-12) DTnew.tm_hour, DTnew.tm_min, DTnew.tm_sec); update_image_index(curr_file); // update image index rec. zmainloop(); // keep GTK alive } popup_report_write(zd2,0,"\n *** %s \n",Bcompleted); popup_report_bottom(zd2); gallery(0,"paint",-1); Fblock = 0; m_batch_photo_date_time(0,0); // repeat return; } // dialog event and completion callback function int batch_photo_time_dialog_event(zdialog *zd, cchar *event) { char countmess[80]; if (strmatch(event,"files")) // select images to process { zdialog_show(zd,0); // hide parent dialog gallery_select(); // get new file list zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); zdialog_stuff(zd,"labcount",countmess); } if (zstrstr("Fsetnew Fshift",event)) { // bugfix 19.0 zdialog_stuff(zd,"Fsetnew",0); zdialog_stuff(zd,"Fshift",0); zdialog_stuff(zd,event,1); } return 1; } /********************************************************************************/ // batch add or change any EXIF/IPTC metadata namespace batchchangemeta { zdialog *zd; } // menu function void m_batch_change_metadata(GtkWidget *, cchar *menu) // overhauled { using namespace batchchangemeta; int batch_change_metadata_dialog_event(zdialog *zd, cchar *event); void batch_change_metadata_clickfunc(GtkWidget *, int line, int pos, int kbkey); int ii, jj, err, zstat, nkeys; char keynameN[12] = "keynameN", keyvalueN[12] = "keyvalueN"; char keyname[40], keyvalue[exif_maxcc]; cchar *pp1[10], *pp2[10]; char *file, *pp, text[100]; GtkWidget *mtext; char filename[200], buff[100]; FILE *fid; zdialog *zd2; F1_help_topic = "batch change meta"; if (checkpend("all")) return; // check nothing pending if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } Fblock = 1; /** _________________________________________________________________ | Click to Select | Batch Add/Change Metadata | | | | | (metadata list) | [Select Files] NN files selected | | | | | | key name key value | | | [______________] [_____________________] | | | [______________] [_____________________] | | | [______________] [_____________________] | | | [______________] [_____________________] | | | [______________] [_____________________] | | | [______________] [_____________________] | | | [______________] [_____________________] | | | [______________] [_____________________] | | | [______________] [_____________________] | | | | | | [Full List] [apply] [cancel] | |____________________|____________________________________________| **/ zd = zdialog_new(E2X("Batch Add/Change Metadata"),Mwin,E2X("Full List"),Bapply,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"expand"); zdialog_add_widget(zd,"vbox","vb1","hb1"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"expand|space=5"); zdialog_add_widget(zd,"label","lab1","vb1",E2X("click to select"),"size=30|space=3"); zdialog_add_widget(zd,"frame","fr1","vb1",0,"expand"); zdialog_add_widget(zd,"scrwin","scr1","fr1"); zdialog_add_widget(zd,"text","mtext","scr1"); zdialog_add_widget(zd,"hbox","hbfiles","vb2",0,"space=3"); zdialog_add_widget(zd,"button","files","hbfiles",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","labcount","hbfiles",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hbkeys","vb2",0,"space=5"); zdialog_add_widget(zd,"vbox","vbname","hbkeys"); zdialog_add_widget(zd,"vbox","vbvalue","hbkeys",0,"expand"); zdialog_add_widget(zd,"label","labkey","vbname",E2X("key name")); zdialog_add_widget(zd,"label","labdata","vbvalue",E2X("key value")); zdialog_add_widget(zd,"zentry","keyname0","vbname",0,"size=20"); zdialog_add_widget(zd,"zentry","keyname1","vbname",0,"size=20"); zdialog_add_widget(zd,"zentry","keyname2","vbname",0,"size=20"); zdialog_add_widget(zd,"zentry","keyname3","vbname",0,"size=20"); zdialog_add_widget(zd,"zentry","keyname4","vbname",0,"size=20"); zdialog_add_widget(zd,"zentry","keyname5","vbname",0,"size=20"); zdialog_add_widget(zd,"zentry","keyname6","vbname",0,"size=20"); zdialog_add_widget(zd,"zentry","keyname7","vbname",0,"size=20"); zdialog_add_widget(zd,"zentry","keyname8","vbname",0,"size=20"); zdialog_add_widget(zd,"zentry","keyname9","vbname",0,"size=20"); zdialog_add_widget(zd,"zentry","keyvalue0","vbvalue",0,"size=20|expand"); zdialog_add_widget(zd,"zentry","keyvalue1","vbvalue",0,"size=20|expand"); zdialog_add_widget(zd,"zentry","keyvalue2","vbvalue",0,"size=20|expand"); zdialog_add_widget(zd,"zentry","keyvalue3","vbvalue",0,"size=20|expand"); zdialog_add_widget(zd,"zentry","keyvalue4","vbvalue",0,"size=20|expand"); zdialog_add_widget(zd,"zentry","keyvalue5","vbvalue",0,"size=20|expand"); zdialog_add_widget(zd,"zentry","keyvalue6","vbvalue",0,"size=20|expand"); zdialog_add_widget(zd,"zentry","keyvalue7","vbvalue",0,"size=20|expand"); zdialog_add_widget(zd,"zentry","keyvalue8","vbvalue",0,"size=20|expand"); zdialog_add_widget(zd,"zentry","keyvalue9","vbvalue",0,"size=20|expand"); snprintf(text,100,Bfileselected,GScount); // show selected files count 20.0 zdialog_stuff(zd,"labcount",text); mtext = zdialog_widget(zd,"mtext"); // make clickable metadata list textwidget_clear(mtext); snprintf(filename,200,"%s/metadata_short_list",get_zhomedir()); fid = fopen(filename,"r"); if (! fid) { zmessageACK(Mwin,"%s \n %s",filename,strerror(errno)); return; } while (true) { // populate metadata list pp = fgets_trim(buff,100,fid,1); if (! pp) break; textwidget_append(mtext,0,"%s \n",buff); } fclose(fid); textwidget_set_eventfunc(mtext,batch_change_metadata_clickfunc); // set mouse/KB event function nkeys = 0; // nothing selected zstat = zdialog_run(zd,batch_change_metadata_dialog_event,0); // run dialog retry: zstat = zdialog_wait(zd); // wait for completion if (zstat != 2) goto cleanup; // not [apply] for (ii = jj = 0; ii < 10; ii++) { keynameN[7] = '0' + ii; keyvalueN[8] = '0' + ii; zdialog_fetch(zd,keynameN,keyname,40); zdialog_fetch(zd,keyvalueN,keyvalue,exif_maxcc); strCompress(keyname); if (*keyname <= ' ') continue; pp1[jj] = zstrdup(keyname); pp2[jj] = zstrdup(keyvalue); jj++; } nkeys = jj; if (nkeys == 0) { zmessageACK(Mwin,E2X("enter key names")); zd->zstat = 0; goto retry; } if (GScount == 0) { zmessageACK(Mwin,E2X("no files selected")); zd->zstat = 0; goto retry; } zd2 = popup_report_open("Batch Metadata",Mwin,500,200,0,0,0); // popup report for (ii = 0; ii < nkeys; ii++) { if (*pp2[ii]) popup_report_write(zd2,0,"%s = %s \n",pp1[ii],pp2[ii]); else popup_report_write(zd2,0,"%s = DELETED \n",pp1[ii]); } ii = zdialog_choose(Mwin,"parent",Bproceed,Bproceed,Bcancel,null); if (ii != 1) { zd->zstat = 0; // cancel popup_report_close(zd2,0); goto retry; } zdialog_free(zd); zd = 0; for (ii = 0; ii < GScount; ii++) // loop all selected files { zmainloop(); // keep GTK alive file = GSfiles[ii]; // display image err = f_open(file,0,0,0); if (err) continue; popup_report_write(zd2,0,"%s \n",file); // report progress err = access(file,W_OK); // test file can be written by me 19.1 if (err) { popup_report_write(zd2,0,"%s \n",Bnowriteperm); continue; } 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 (zd_metaview) meta_view(0); // update exif view if active } popup_report_write(zd2,0,"\n *** %s \n",Bcompleted); popup_report_bottom(zd2); cleanup: if (zd) zdialog_free(zd); // kill dialog zd = 0; for (ii = 0; ii < nkeys; ii++) { // free memory zfree((char *) pp1[ii]); zfree((char *) pp2[ii]); } nkeys = 0; Fblock = 0; return; } // dialog event and completion callback function int batch_change_metadata_dialog_event(zdialog *zd, cchar *event) { using namespace batchchangemeta; char countmess[80]; if (zd->zstat == 1) // full list { zd->zstat = 0; // keep dialog active zmessageACK(Mwin,E2X("The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names")); return 1; // 19.0 } if (strmatch(event,"files")) // select images to process { zdialog_show(zd,0); // hide parent dialog gallery_select(); // get image file list zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); zdialog_stuff(zd,"labcount",countmess); } return 1; } // get clicked tag name from short list and insert into dialog void batch_change_metadata_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { using namespace batchchangemeta; int ii; char *pp; char keynameX[12] = "keynameX"; char keyname[60]; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } pp = textwidget_line(widget,line,1); // get clicked line, highlight if (! pp || ! *pp) return; textwidget_highlight_line(widget,line); for (ii = 0; ii < 10; ii++) { // find 1st empty dialog key name keynameX[7] = '0' + ii; zdialog_fetch(zd,keynameX,keyname,60); if (*keyname <= ' ') break; } if (ii < 10) zdialog_stuff(zd,keynameX,pp); return; } /********************************************************************************/ // batch report metadata for selected image files namespace batchreportmeta { char filex[200]; // file for metadata item list } // menu function void m_batch_report_metadata(GtkWidget *, cchar *menu) { using namespace batchreportmeta; int batch_report_metadata_dialog_event(zdialog *zd, cchar *event); zdialog *zd, *zd2; char buff[200]; char *file, *pp, text[100]; FILE *fid = 0; int zstat, ff, ii, err; int nkx = 0; char *keynamex[NK], *keyvalx[NK]; F1_help_topic = "batch report meta"; if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } snprintf(filex,200,"%s/metadata_report_items",get_zhomedir()); // file for items to report /*** ____________________________________________ | Batch Report Metadata | | | | [Select Files] NN files selected | | [Edit] list of reported metadata items | | | | [proceed] [cancel] | |____________________________________________| ***/ zd = zdialog_new(E2X("Batch Report Metadata"),Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hbfiles","dialog",0,"space=3"); zdialog_add_widget(zd,"button","files","hbfiles",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","labcount","hbfiles",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hbedit","dialog",0,"space=3"); zdialog_add_widget(zd,"button","edit","hbedit",Bedit,"space=5"); zdialog_add_widget(zd,"label","labedit","hbedit",E2X("list of reported metadata items"),"space=10"); snprintf(text,100,Bfileselected,GScount); // show selected files count 20.0 zdialog_stuff(zd,"labcount",text); zstat = zdialog_run(zd,batch_report_metadata_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion if (zstat != 1) goto cleanup; // cancel zdialog_free(zd); if (GScount == 0) { zmessageACK(Mwin,E2X("no files selected")); return; } fid = fopen(filex,"r"); if (! fid) { zmessageACK(Mwin,"no metadata items to report"); return; } for (nkx = ii = 0; ii < 20; ii++) // read items to report { pp = fgets_trim(buff,100,fid,1); if (! pp) break; strCompress(pp); if (*pp <= ' ') continue; if (strmatchN(pp,"enteritems",5)) continue; keynamex[nkx] = zstrdup(pp); nkx++; } fclose(fid); if (! nkx) { zmessageACK(Mwin,"no metadata items to report"); return; } zd2 = popup_report_open("metadata report",Mwin,600,400,0,0,0); for (ff = 0; ff < GScount; ff++) // loop selected files { zmainloop(10); // keep GTK alive file = GSfiles[ff]; popup_report_write(zd2,0,"%s \n",file); err = exif_get(file,(cchar **) keynamex,keyvalx,nkx); // get all report items if (err) { zmessageACK(Mwin,"exif failure"); goto cleanup; } for (ii = 0; ii < nkx; ii++) // output keyword names and values if (keyvalx[ii]) popup_report_write(zd2,0,"%-24s : %s \n",keynamex[ii],keyvalx[ii]); for (ii = 0; ii < nkx; ii++) // free memory if (keyvalx[ii]) zfree(keyvalx[ii]); popup_report_write(zd2,0,"\n"); // blank line separator popup_report_bottom(zd2); } popup_report_write(zd2,0,"\n *** %s \n",Bcompleted); popup_report_bottom(zd2); cleanup: for (ii = 0; ii < nkx; ii++) // free memory zfree(keynamex[ii]); return; } // dialog event and completion function int batch_report_metadata_dialog_event(zdialog *zd, cchar *event) { using namespace batchreportmeta; char countmess[80]; char *itemlist[Mxmeta], *pp, buff[100]; FILE *fid; int ii, jj; if (zd->zstat) zdialog_destroy(zd); if (strmatch(event,"files")) // select images to process { zdialog_show(zd,0); // hide parent dialog gallery_select(); // get new list zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); zdialog_stuff(zd,"labcount",countmess); } if (strmatch(event,"edit")) // select metadata items to report { itemlist[0] = 0; // empty list fid = fopen(filex,"r"); // read file of extra metadata items if (fid) { for (ii = jj = 0; ii < Mxmeta; ii++) { pp = fgets_trim(buff,100,fid,1); if (! pp) break; if (*pp <= ' ') continue; itemlist[jj++] = zstrdup(pp); } itemlist[jj] = 0; // mark EOL fclose(fid); } select_meta_keys(itemlist,0); // user edit of extras list fid = fopen(filex,"w"); // update extras list file if (! fid) { zmessageACK(Mwin,"%s \n %s",filex,strerror(errno)); return 1; } for (ii = 0; ii < Mxmeta; ii++) { if (! itemlist[ii]) break; fprintf(fid,"%s\n",itemlist[ii]); } fclose(fid); } return 1; } /********************************************************************************/ // batch geotags - set geotags for multiple image files void m_batch_geotags(GtkWidget *, cchar *menu) { int batch_geotags_dialog_event(zdialog *zd, cchar *event); cchar *title = E2X("Batch Geotags"); int ii, err; char *file; char location[100], country[100]; char lati[20], longi[20], text[100]; cchar *mapquest1 = "Geocoding web service courtesy of"; cchar *mapquest2 = "http://www.mapquest.com"; zdialog *zd, *zd2; F1_help_topic = "batch geotags"; if (checkpend("all")) return; // check nothing pending if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } if (! init_geolocs()) return; // initialize geotags Fblock = 1; // 19.0 /*** _____________________________________________________ | Batch Geotags | | | | [select files] NN files selected | | location [______________] country [______________] | | latitude [_______] longitude [_______] | | | | [find] [web] [proceed] [cancel] | |_____________________________________________________| ***/ zd = zdialog_new(title,Mwin,Bfind,Bweb,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"button","files","hb1",Bselectfiles,"space=10"); zdialog_add_widget(zd,"label","labcount","hb1",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labloc","hb2",E2X("location"),"space=5"); zdialog_add_widget(zd,"zentry","location","hb2",0,"expand"); zdialog_add_widget(zd,"label","space","hb2",0,"space=5"); zdialog_add_widget(zd,"label","labcountry","hb2",E2X("country"),"space=5"); zdialog_add_widget(zd,"zentry","country","hb2",0,"expand"); zdialog_add_widget(zd,"hbox","hb3","dialog"); zdialog_add_widget(zd,"label","lablat","hb3","Latitude","space=3"); zdialog_add_widget(zd,"zentry","lati","hb3",0,"size=10"); zdialog_add_widget(zd,"label","space","hb3",0,"space=5"); zdialog_add_widget(zd,"label","lablong","hb3","Longitude","space=3"); zdialog_add_widget(zd,"zentry","longi","hb3",0,"size=10"); zdialog_add_widget(zd,"hbox","hbmq","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labmq","hbmq",mapquest1,"space=3"); zdialog_add_widget(zd,"link","MapQuest","hbmq",mapquest2); snprintf(text,100,Bfileselected,GScount); // show selected files count 20.0 zdialog_stuff(zd,"labcount",text); zd_mapgeotags = zd; // activate map clicks zdialog_run(zd,batch_geotags_dialog_event,"parent"); // run dialog zdialog_wait(zd); // wait for dialog completion if (zd->zstat != 3) goto cleanup; // status not [proceed] if (! GScount) goto cleanup; // no files selected zdialog_fetch(zd,"location",location,100); // get location from dialog zdialog_fetch(zd,"country",country,100); zdialog_fetch(zd,"lati",lati,20); // and latitude, longitude zdialog_fetch(zd,"longi",longi,20); zdialog_free(zd); // kill dialog zd = zd_mapgeotags = 0; // 20.0 if (GScount == 0) goto cleanup; zd2 = popup_report_open(E2X("Adding Geotags"),Mwin,500,200,0,0,0); // status monitor popup window for (ii = 0; ii < GScount; ii++) // loop all selected files { zmainloop(10); // keep GTK alive file = GSfiles[ii]; // display image err = f_open(file,0,0,0); if (err) continue; err = access(file,W_OK); // test file can be written by me 19.1 if (err) { popup_report_write(zd2,0,"%s: %s \n",Bnowriteperm,file); continue; } if (*location) strncpy0(meta_location,location,100); // save geotags in image file EXIF if (*country) strncpy0(meta_country,country,100); // and in image index file if (*lati) strncpy0(meta_lati,lati,20); // do not stuff missing data if (*longi) strncpy0(meta_longi,longi,20); Fmetamod++; save_filemeta(file); // update file metadata & index popup_report_write(zd2,0,"%s \n",file); // report progress popup_report_bottom(zd2); } popup_report_write(zd2,0,"\n *** %s \n",Bcompleted); popup_report_bottom(zd2); cleanup: Fblock = 0; if (zd) zdialog_free(zd); zd_mapgeotags = 0; return; } // batch_geotags dialog event function int batch_geotags_dialog_event(zdialog *zd, cchar *event) { int yn, zstat, err; char countmess[80]; char location[100], country[100]; char lati[20], longi[20]; cchar *errmess; float flati, flongi; if (strmatch(event,"files")) // select images to add tags { zdialog_show(zd,0); // hide parent dialog gallery_select(); // get file list from user zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); zdialog_stuff(zd,"labcount",countmess); } if (! zd->zstat) return 1; // wait for action button zstat = zd->zstat; zd->zstat = 0; // keep dialog active if (zstat == 1) // [find] { find_location(zd); // find location data via user return 1; } else if (zstat == 2) // [web] { errmess = web_geocode(zd); // look-up in web service if (errmess) zmessageACK(Mwin,errmess); // fail return 1; // 19.0 } else if (zstat == 3) // [proceed] { zdialog_fetch(zd,"location",location,100); // get location from dialog zdialog_fetch(zd,"country",country,100); strTrim2(location); strTrim2(country); if (*location) { // 20.0 *location = toupper(*location); // capitalize zdialog_stuff(zd,"location",location); } if (*country) { *country = toupper(*country); zdialog_stuff(zd,"country",country); } zdialog_fetch(zd,"lati",lati,20); // get latitude, longitude zdialog_fetch(zd,"longi",longi,20); strTrim2(lati); strTrim2(longi); if (*lati || *longi) { // if coordinates present, validate err = validate_latlong(lati,longi,flati,flongi); if (err) goto badcoord; } if (! GScount) goto nofiles; if (*location <= ' ' || *country <= ' ' || *lati <= ' ' || *longi <= ' ') { yn = zmessageYN(Mwin,E2X("data is incomplete \n proceed?")); if (! yn) return 1; } put_geolocs(zd); // update geolocs table zd->zstat = 3; // OK to proceed zdialog_destroy(zd); return 1; // 19.0 } else zdialog_destroy(zd); // [cancel] or [x] return 1; badcoord: zmessageACK(Mwin,E2X("bad latitude/longitude: %s %s"),lati,longi); return 1; nofiles: zmessageACK(Mwin,Bnofileselected); return 1; } /********************************************************************************/ // Group images by location and date, with a count of images in each group. // Click on a group to get a thumbnail gallery of all images in the group. namespace locs_names { struct grec_t { // image geotags data char *location, *country; // group location char pdate[12]; // nominal group date, yyyymmdd int lodate, hidate; // range, days since 0 CE int count; // images in group }; grec_t *grec = 0; int Ngrec = 0; int locs_groupby, locs_daterange; int pline; int locs_comp(cchar *rec1, cchar *rec2); int locs_comp2(cchar *rec1, cchar *rec2); void locs_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); int locs_getdays(cchar *date); } // menu function void m_meta_places_dates(GtkWidget *, cchar *) { using namespace locs_names; zdialog *zd, *zd2; int zstat, ii, cc, cc1, cc2; int ww, iix, iig, newgroup; char country[100], location[100], pdate[12]; xxrec_t *xxrec; F1_help_topic = "places/dates"; if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } if (Findexvalid == 1) zmessage_post_bold(Mwin,"20/10",2,Boldindex); // warn, index missing new files if (checkpend("all")) return; // check nothing pending /*** ______________________________________ | Report Image Locations | | | | (o) Group by country | | (o) Group by country/location | | (o) Group by country/location/date | | (o) Group by date/country/location | | Combine within [ xx ] days | | | | [proceed] [cancel] | |______________________________________| ***/ zd = zdialog_new(E2X("Report Image Locations"),Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"radio","country","dialog",E2X("Group by country")); zdialog_add_widget(zd,"radio","location","dialog",E2X("Group by country/location")); zdialog_add_widget(zd,"radio","date","dialog",E2X("Group by country/location/date")); zdialog_add_widget(zd,"radio","date2","dialog",E2X("Group by date/country/location")); zdialog_add_widget(zd,"hbox","hbr","dialog"); zdialog_add_widget(zd,"label","space","hbr",0,"space=10"); zdialog_add_widget(zd,"label","labr1","hbr",E2X("Combine within"),"space=10"); zdialog_add_widget(zd,"zspin","range","hbr","0|999|1|1"); zdialog_add_widget(zd,"label","labr2","hbr",E2X("days"),"space=10"); zdialog_stuff(zd,"country",0); zdialog_stuff(zd,"location",1); // default by location zdialog_stuff(zd,"date",0); zdialog_stuff(zd,"date2",0); zdialog_restore_inputs(zd); zdialog_resize(zd,300,0); zdialog_run(zd,null,"parent"); zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); return; } zdialog_fetch(zd,"country",iix); if (iix) locs_groupby = 1; // group country zdialog_fetch(zd,"location",iix); if (iix) locs_groupby = 2; // group country/location zdialog_fetch(zd,"date",iix); if (iix) locs_groupby = 3; // group country/location/date-range zdialog_fetch(zd,"date2",iix); if (iix) locs_groupby = 4; // group date-range/country/location zdialog_fetch(zd,"range",locs_daterange); // combine recs within date range zdialog_free(zd); if (Ngrec) { // free prior memory for (iix = 0; iix < Ngrec; iix++) { if (grec[iix].location) zfree(grec[iix].location); if (grec[iix].country) zfree(grec[iix].country); } zfree(grec); } cc = Nxxrec * sizeof(grec_t); // allocate memory grec = (grec_t *) zmalloc(cc); memset(grec,0,cc); if (! Nxxrec) { zmessageACK(Mwin,"no geotags data found"); return; } for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { xxrec = xxrec_tab[ii]; grec[ii].location = zstrdup(xxrec->location); // get location and country grec[ii].country = zstrdup(xxrec->country); if (strmatch(grec[ii].location,"")) // replace "" with "null" 20.0 grec[ii].location = zstrdup("null"); // for reported location if (strmatch(grec[ii].country,"")) grec[ii].country = zstrdup("null"); strncpy0(grec[ii].pdate,xxrec->pdate,9); // photo date, truncate to yyyymmdd if (grec[ii].pdate[0] == 0) strcpy(grec[ii].pdate,"null"); grec[ii].lodate = locs_getdays(xxrec->pdate); // days since 0 CE grec[ii].hidate = grec[ii].lodate; } Ngrec = Nxxrec; if (Ngrec > 1) // sort grecs by country/location/date HeapSort((char *) grec, sizeof(grec_t), Ngrec, locs_comp); iig = 0; // 1st group from grec[0] grec[iig].count = 1; // group count = 1 for (iix = 1; iix < Ngrec; iix++) // scan following grecs { newgroup = 0; if (! strmatch(grec[iix].country,grec[iig].country)) newgroup = 1; // new country >> new group if (locs_groupby >= 2) // new location >> new group if (! strmatch(grec[iix].location,grec[iig].location)) newgroup = 1; // if group by location if (locs_groupby >= 3) if (grec[iix].lodate - grec[iig].hidate > locs_daterange) // new date >> new group if group by date newgroup = 1; // and date out of range if (newgroup) { iig++; // new group if (iix > iig) { grec[iig] = grec[iix]; // copy and pack down grec[iix].location = grec[iix].country = 0; // no zfree() } grec[iig].count = 1; // group count = 1 } else { zfree(grec[iix].location); // same group zfree(grec[iix].country); // free memory grec[iix].location = grec[iix].country = 0; grec[iig].hidate = grec[iix].lodate; // expand group date-range grec[iig].count++; // increment group count } } Ngrec = iig + 1; // unique groups count if (locs_groupby == 1) ww = 350; // group country if (locs_groupby == 2) ww = 600; // group country/location if (locs_groupby == 3) ww = 650; // group country/location/date-range if (locs_groupby == 4) ww = 650; // group date-range/country/location zd2 = popup_report_open(E2X("Image Locations"),Mwin,ww,400, // write groups to popup window 1,locs_clickfunc,"find","done",0); // 20.0 if (locs_groupby == 1) // group by country { popup_report_header(zd2,1,"%-30s %5s","Country","Count"); for (iig = 0; iig < Ngrec; iig++) { utf8substring(country,grec[iig].country,0,30); cc1 = 30 + strlen(country) - utf8len(country); popup_report_write(zd2,0,"%-*s %5d \n",cc1,country,grec[iig].count); } } if (locs_groupby == 2) // group by country/location { popup_report_header(zd2,1,"%-30s %-30s %5s","Country","Location","Count"); for (iig = 0; iig < Ngrec; iig++) { utf8substring(country,grec[iig].country,0,30); cc1 = 30 + strlen(country) - utf8len(country); utf8substring(location,grec[iig].location,0,30); cc2 = 30 + strlen(location) - utf8len(location); popup_report_write(zd2,0,"%-*s %-*s %5d \n", cc1,country,cc2,location,grec[iig].count); } } if (locs_groupby == 3) // group by country/location/date-range { popup_report_header(zd2,1,"%-26s %-26s %-10s %5s","Country","Location","Date","Count"); for (iig = 0; iig < Ngrec; iig++) { utf8substring(country,grec[iig].country,0,26); // get graphic cc for UTF-8 names cc1 = 26 + strlen(country) - utf8len(country); utf8substring(location,grec[iig].location,0,26); cc2 = 26 + strlen(location) - utf8len(location); strncpy0(pdate,grec[iig].pdate,9); // date, yyyymmdd 19.0 if (! strmatch(pdate,"null")) { memmove(pdate+8,pdate+6,2); // convert to yyyy-mm-dd memmove(pdate+5,pdate+4,2); pdate[4] = pdate[7] = '-'; pdate[10] = 0; } popup_report_write(zd2,0,"%-*s %-*s %-10s %6d \n", cc1,country,cc2,location,pdate,grec[iig].count); } } if (locs_groupby == 4) // group by date-range/country/location { if (Ngrec > 1) // re-sort by date/country/location HeapSort((char *) grec, sizeof(grec_t), Ngrec, locs_comp2); popup_report_header(zd2,1,"%-10s %-26s %-26s %5s","Date","Country","Location","Count"); for (iig = 0; iig < Ngrec; iig++) { utf8substring(country,grec[iig].country,0,26); // get graphic cc for UTF-8 names cc1 = 26 + strlen(country) - utf8len(country); utf8substring(location,grec[iig].location,0,26); cc2 = 26 + strlen(location) - utf8len(location); strncpy0(pdate,grec[iig].pdate,9); // date, yyyymmdd 19.0 if (! strmatch(pdate,"null")) { memmove(pdate+8,pdate+6,2); // convert to yyyy-mm-dd memmove(pdate+5,pdate+4,2); pdate[4] = pdate[7] = '-'; pdate[10] = 0; } popup_report_write(zd2,0,"%-10s %-*s %-*s %6d \n", pdate,cc1,country,cc2,location,grec[iig].count); } } pline = 0; return; } // Compare 2 grec records by geotags and date, // return < 0 = 0 > 0 for rec1 < = > rec2. int locs_names::locs_comp(cchar *rec1, cchar *rec2) { using namespace locs_names; int ii; char * country1 = ((grec_t *) rec1)->country; // compare countries char * country2 = ((grec_t *) rec2)->country; ii = strcmp(country1,country2); if (ii) return ii; char * loc1 = ((grec_t *) rec1)->location; // compare cities char * loc2 = ((grec_t *) rec2)->location; ii = strcmp(loc1,loc2); if (ii) return ii; int date1 = ((grec_t *) rec1)->lodate; // compare dates int date2 = ((grec_t *) rec2)->lodate; ii = date1 - date2; return ii; } // Compare 2 grec records by date and geotags, // return < 0 = 0 > 0 for rec1 < = > rec2. int locs_names::locs_comp2(cchar *rec1, cchar *rec2) { using namespace locs_names; int ii; int date1 = ((grec_t *) rec1)->lodate; // compare dates int date2 = ((grec_t *) rec2)->lodate; ii = date1 - date2; if (ii) return ii; char * country1 = ((grec_t *) rec1)->country; // compare countries char * country2 = ((grec_t *) rec2)->country; ii = strcmp(country1,country2); if (ii) return ii; char * loc1 = ((grec_t *) rec1)->location; // compare cities char * loc2 = ((grec_t *) rec2)->location; ii = strcmp(loc1,loc2); return ii; } // convert yyyymmdd date into days from 0001 C.E. int locs_names::locs_getdays(cchar *pdate) { using namespace locs_names; int year, month, day; char temp[8]; int montab[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; int elaps; if (strmatch(pdate,"null")) return 0; year = month = day = 0; strncpy0(temp,pdate,5); year = atoi(temp); if (year <= 0) year = 1; // bugfix 19.0 strncpy0(temp,pdate+4,3); month = atoi(temp); if (month <= 0) month = 1; // 19.0 strncpy0(temp,pdate+6,3); day = atoi(temp); if (day <= 0) day = 1; // 19.0 elaps = 365 * (year-1) + (year-1) / 4; // elapsed days in prior years elaps += montab[month-1]; // + elapsed days in prior months if (year % 4 == 0 && month > 2) elaps += 1; // + 1 for Feb. 29 elaps += day-1; // + elapsed days in month return elaps; } // Receive clicks on report window and generate gallery of images // matching the selected country/location/date void locs_names::locs_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { using namespace locs_names; int ii, lodate, hidate, datex; char location[100], country[100]; FILE *fid; xxrec_t *xxrec; if (checkpend("all")) return; // check nothing pending if (line >= 0) // line clicked { textwidget_scroll(widget,line); // keep line on screen textwidget_highlight_line(widget,line); // highlight pline = line; // remember last line selected } else // KBkey pressed { if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } if (kbkey >= 0xfd00) { if (kbkey == GDK_KEY_Up) pline--; // KB arrow key navigation if (kbkey == GDK_KEY_Down) pline++; if (kbkey == GDK_KEY_Page_Up) pline -= 10; if (kbkey == GDK_KEY_Page_Down) pline += 10; if (kbkey == GDK_KEY_Home) pline = 0; if (kbkey == GDK_KEY_End) pline = Ngrec - 1; if (pline < 0) pline = 0; if (pline > Ngrec-1) pline = Ngrec - 1; textwidget_highlight_line(widget,pline); // highlight line } } textwidget_scroll(widget,pline); // keep line on screen strncpy0(country,grec[pline].country,100); // selected country/location/date-range strncpy0(location,grec[pline].location,100); lodate = grec[pline].lodate; hidate = grec[pline].hidate; fid = fopen(searchresults_file,"w"); // open output file if (! fid) goto filerror; for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; if (! strmatch(xxrec->country,country)) continue; // no country match if (locs_groupby >= 2) if (! strmatch(xxrec->location,location)) continue; // no location match if (locs_groupby >= 3) { datex = locs_getdays(xxrec->pdate); if (xxrec->pdate[0] == 0 || strmatch(xxrec->pdate,"null")) datex = 0; if (datex < lodate || datex > hidate) continue; // no date match } fprintf(fid,"%s\n",xxrec->file); // output matching file } fclose(fid); free_resources(); navi::gallerytype = SEARCH; // search results gallery(searchresults_file,"initF",0); // generate gallery of matching files gallery(0,"paint",0); m_viewmode(0,"G"); return; filerror: zmessageACK(Mwin,"file error: %s",strerror(errno)); return; } /********************************************************************************/ // Produce a report of image counts by year and month. // Click on a report line to get a thumbnail gallery of images. namespace timeline_names { int Nyears = 2100; int Nperds = 12 * Nyears; int Nyears2 = 0; cchar *months = E2X("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); int colpos[14] = { 0, 6, 13, 18, 23, 28, 33, 38, 43, 48, 53, 58, 63, 68 }; } // menu function void m_meta_timeline(GtkWidget *, cchar *) { using namespace timeline_names; void timeline_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); int Ycount[Nyears], Pcount[Nperds]; // image counts per year and period int Mcount, Ecount; // counts for missing and invalid dates int ii, jj, cc; int yy, mm, pp; char pdate[8], nnnnn[8], buff[100]; xxrec_t *xxrec; zdialog *zd2; F1_help_topic = "timeline"; if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } if (Findexvalid == 1) zmessage_post_bold(Mwin,"20/10",2,Boldindex); // warn, index missing new files if (checkpend("all")) return; // check nothing pending Mcount = Ecount = 0; // clear missing and error counts for (yy = 0; yy < Nyears; yy++) // clear totals per year Ycount[yy] = 0; for (pp = 0; pp < Nperds; pp++) // clear totals per period (month) Pcount[pp] = 0; for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; strncpy0(pdate,xxrec->pdate,7); // photo date, truncate to yyyymm if (strmatch(pdate,"")) { // if missing, count ++Mcount; continue; } jj = atoi(pdate); // photo date, 0 to 209912 yy = jj / 100; // year, 0000 to 2099 mm = jj - yy * 100; // month, 1 to 12 if (yy < 0 || yy >= Nyears || mm < 1 || mm > 12) { ++Ecount; // invalid, add to error count continue; } ++Ycount[yy]; // add to year totals pp = yy * 12 + mm - 1; // add to period totals ++Pcount[pp]; } zd2 = popup_report_open("Image Timeline",Mwin,600,400,1, // write report to popup window timeline_clickfunc,0); popup_report_header(zd2,1,"year count %s",months); // "year count Jan Feb ... " if (Mcount) popup_report_write(zd2,0,"null %-6d \n",Mcount); // images with no date if (Ecount) popup_report_write(zd2,0,"invalid %-4d \n",Ecount); // images with invalid date Nyears2 = 0; for (yy = 0; yy < Nyears; yy++) // loop years { if (! Ycount[yy]) continue; // omit years without images snprintf(buff,100,"%04d %-6d ",yy,Ycount[yy]); // output "yyyy NNNNNN " cc = 13; for (mm = 0; mm < 12; mm++) { // loop months 0 - 11 pp = yy * 12 + mm; // period snprintf(nnnnn,6,"%-5d",Pcount[pp]); // output "NNNNN" memcpy(buff+cc,nnnnn,5); // 19.0 cc += 5; } buff[cc] = 0; popup_report_write(zd2,0,"%s \n",buff); Nyears2++; // count reported years } popup_report_bottom(zd2); return; } // Receive clicks on report window and generate gallery of images // matching the selected period void timeline_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { using namespace timeline_names; int ii, jj, cc; int Fnull = 0, Finvalid = 0; int yy, mm; static int pline, ppos; char *txline, pdate[8], *pp, end; FILE *fid; xxrec_t *xxrec; if (checkpend("all")) return; // check nothing pending if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } if (line == -1) // arrow key navigation { for (ii = 0; ii < 14; ii++) // current report column if (ppos == colpos[ii]) break; if (kbkey == GDK_KEY_Left) { // prior month if (ii > 2) ppos = colpos[ii-1]; else { pline -= 1; ppos = colpos[13]; } } else if (kbkey == GDK_KEY_Right) { // next month if (ii < 13) ppos = colpos[ii+1]; else { pline += 1; ppos = colpos[2]; } } else if (kbkey == GDK_KEY_Up) pline -= 1; // prior year else if (kbkey == GDK_KEY_Down) pline += 1; // next year line = pline; pos = ppos; } if (line < 0) line = 0; if (line > Nyears2) line = Nyears2; if (pos < 0) pos = 0; for (ii = 0; ii < 14; ii++) if (pos < colpos[ii]) break; pos = colpos[ii-1]; textwidget_scroll(widget,line); // keep line on screen pline = line; // remember chosen line, position ppos = pos; pp = textwidget_word(widget,line,pos," ",end); // hilite clicked word if (pp) textwidget_highlight_word(widget,line,pos,strlen(pp)); txline = textwidget_line(widget,line,1); // get clicked line if (! txline || ! *txline) return; cc = 0; if (strmatchN(txline,"null",4)) Fnull = 1; // find images with null date else if (strmatchN(txline,"invalid",7)) Finvalid = 1; // find images with invalid date else if (pos < 13) { // clicked on year or year count strncpy0(pdate,txline,5); // have "yyyy" cc = 4; } else // month was clicked { mm = (pos - 13) / 5 + 1; // month, 1-12 if (mm < 1 || mm > 12) return; strncpy(pdate,txline,4); // "yyyy" pdate[4] = '0' + mm/10; pdate[5] = '0' + mm % 10; // have "yyyymm" pdate[6] = 0; cc = 6; } fid = fopen(searchresults_file,"w"); // open output file if (! fid) goto filerror; for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; if (Fnull) { // search for missing dates if (strmatch(xxrec->pdate,"")) { fprintf(fid,"%s\n",xxrec->file); continue; } } else if (Finvalid) { // search for invalid dates if (strmatch(xxrec->pdate,"")) continue; // omit missing dates strncpy0(pdate,xxrec->pdate,7); // yyyymm jj = atoi(pdate); // 0 to 209912 yy = jj / 100; // 0 to 2099 mm = jj - yy * 100; // 1 to 12 if (yy < 0 || yy >= Nyears || mm < 1 || mm > 12) { fprintf(fid,"%s\n",xxrec->file); continue; } } else if (strmatchN(xxrec->pdate,pdate,cc)) { // screen for desired period if (strmatch(xxrec->pdate,"")) continue; // omit missing dates strncpy0(pdate,xxrec->pdate,7); // yyyymm jj = atoi(pdate); // 0 to 209912 yy = jj / 100; // 0 to 2099 mm = jj - yy * 100; // 1 to 12 if (yy < 0 || yy >= Nyears || mm < 1 || mm > 12) continue; // invalid, reject fprintf(fid,"%s\n",xxrec->file); // output matching file } } fclose(fid); free_resources(); navi::gallerytype = SEARCH; // search results gallery(searchresults_file,"initF",0); // generate gallery of matching files gallery(0,"paint",0); m_viewmode(0,"G"); return; filerror: zmessageACK(Mwin,"file error: %s",strerror(errno)); return; } /********************************************************************************/ // Search image tags, geotags, dates, stars, comments, captions // to find matching images. This is fast using the image index. // Search also any other metadata, but relatively slow. namespace search_images { zdialog *zdsearchimages = 0; // search images dialog char searchDateFrom[20] = ""; // search images char searchDateTo[20] = ""; // format is "yyyy-mm-dd hh:mm" char searchdatelo[16], searchdatehi[16]; // format is yyyymmddhhmmss char searchStarsFrom[4] = ""; char searchStarsTo[4] = ""; char searchtags[tagScc] = ""; // search tags list char searchtext[tagScc] = ""; // search comments & captions word list char searchfiles[tagScc] = ""; // search files list char searchLocations[200] = ""; // search locations int Fscanall, Fscancurr, Fnewset, Faddset, Fremset; int Ftext, Ffiles, Ftags, Fstars, Flocs; int Fdates, Fphotodate, Ffiledate, Fnulldate; int Flastver, Forglast, Fallvers, Fnochange; int Falltags, Falltext, Fallfiles, Falllocs; int Fmeta, Frepgallery, Frepmeta, Fautosearch; int Ncurrset; // current set (gallery) count 20.0 int Nsearchkeys = 0; char *searchkeys[5]; // metadata keys to search char *searchvals[5]; // data values to search for char matchtype[5]; // match type: string or number < = > int keyindexed[5]; // key included in indexed metadata int allkeysindexed; // all search keys are indexed } using namespace search_images; /********************************************************************************/ // Search function for use in scripts // $ fotoxx -m autosearch settingsfile // A search is performed using the specified search settings file. // The list of image files found by the search is written to stdout. void m_autosearch(GtkWidget *, cchar *) // 20.0 { FILE *fid; zdialog *zd; char settingsfile[200]; snprintf(settingsfile,200,"%s/search_images/%s",get_zhomedir(),commandparam); fid = fopen(settingsfile,"r"); // open settings file if (! fid) zexit("%s %s",commandparam,strerror(errno)); Fautosearch = 1; m_search_images(0,0); // open search dialog zd = zdsearchimages; func_load_widgets(zd,null,null,fid); // load settings into dialog fclose(fid); zdialog_send_event(zd,"proceed"); // execute search zdialog_wait(zd); printz("search results: %s/search_results \n",get_zhomedir()); // output file zexit("autosearch exit"); } /********************************************************************************/ // menu function void m_search_images(GtkWidget *, cchar *) { void search_searchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void search_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void search_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); int searchimages_dialog_event(zdialog*, cchar *event); zdialog *zd; GtkWidget *widget; F1_help_topic = "search images"; if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } if (checkpend("all")) return; // check nothing pending if (Findexvalid == 1) zmessage_post_bold(Mwin,"20/10",2,Boldindex); // warn, index missing new files /*** _________________________________________________________________ | Search Images | | | | images to search: (o) all (o) current set only | | matching images: (o) make new set (o) add to set (o) remove | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | [x] select last version only [x] original + last version | | [x] original + all versions [x] no change | // 20.0 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | report type: (o) gallery (o) metadata | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | date range [______________] [______________] | | (o) photo date (o) file date (yyyy-mm-dd) | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | rating range [__] [__] | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | all/any | | search tags [_______________________________________] (o) (o) | | search text [_______________________________________] (o) (o) | | search files [_______________________________________] (o) (o) | | search locations [___________________________________] (o) (o) | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | search metadata [Add] (*) | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | Enter Search Tag [________________________] | | Matching Tags [______________________________________________] | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | Defined Tags Category [____________________________________|v] | | | | | | | | | | | | | | | | | | | | | | | | | | |____________________________________________________________| | | | | [load] [save] [clear] [proceed] [cancel] | |_________________________________________________________________| ***/ zd = zdialog_new(E2X("Search Images"),Mwin,Bload,Bsave,Bclear,Bproceed,Bcancel,null); zdsearchimages = zd; zdialog_add_widget(zd,"hbox","hbs1","dialog"); zdialog_add_widget(zd,"label","labs1","hbs1",E2X("images to search:"),"space=3"); zdialog_add_widget(zd,"radio","allimages","hbs1",E2X("all"),"space=3"); zdialog_add_widget(zd,"radio","currset","hbs1",E2X("current set only"),"space=3"); zdialog_add_widget(zd,"hbox","hbm1","dialog"); zdialog_add_widget(zd,"label","labs1","hbm1",E2X("matching images:"),"space=3"); zdialog_add_widget(zd,"radio","newset","hbm1",E2X("make new set"),"space=5"); zdialog_add_widget(zd,"radio","addset","hbm1",E2X("add to set"),"space=5"); zdialog_add_widget(zd,"radio","remset","hbm1",E2X("remove"),"space=5"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=2"); zdialog_add_widget(zd,"hbox","hbsel1","dialog"); zdialog_add_widget(zd,"check","lastver","hbsel1",E2X("select last version only"),"space=3"); zdialog_add_widget(zd,"check","org+last","hbsel1",E2X("original + last version"),"space=10"); zdialog_add_widget(zd,"hbox","hbsel2","dialog"); zdialog_add_widget(zd,"check","allvers","hbsel2",E2X("original + all versions"),"space=3"); // 20.0 zdialog_add_widget(zd,"check","nochange","hbsel2",E2X("no change"),"space=10"); // 20.0 zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=2"); zdialog_add_widget(zd,"hbox","hbrt","dialog"); zdialog_add_widget(zd,"label","labrt","hbrt",E2X("report type:"),"space=3"); zdialog_add_widget(zd,"radio","repgallery","hbrt",E2X("gallery"),"space=5"); zdialog_add_widget(zd,"radio","repmeta","hbrt","Metadata","space=5"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=2"); zdialog_add_widget(zd,"hbox","hbd1","dialog"); zdialog_add_widget(zd,"label","labD","hbd1",E2X("date range"),"space=3"); zdialog_add_widget(zd,"zentry","datefrom","hbd1",0,"size=12|space=5"); zdialog_add_widget(zd,"zentry","dateto","hbd1",0,"size=12"); zdialog_add_widget(zd,"hbox","hbd2","dialog"); zdialog_add_widget(zd,"radio","photodate","hbd2",E2X("photo date"),"space=8"); zdialog_add_widget(zd,"radio","filedate","hbd2",E2X("file date")); zdialog_add_widget(zd,"label","labD","hbd2",E2X("(yyyy-mm-dd)"),"space=20"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=2"); zdialog_add_widget(zd,"hbox","hbstars","dialog"); zdialog_add_widget(zd,"label","labS","hbstars",E2X("rating range"),"space=5"); zdialog_add_widget(zd,"zentry","starsfrom","hbstars",0,"size=3|space=8"); // 19.13 zdialog_add_widget(zd,"zentry","starsto","hbstars",0,"size=3|space=8"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=2"); zdialog_add_widget(zd,"hbox","hbaa","dialog"); zdialog_add_widget(zd,"label","space","hbaa",0,"expand"); zdialog_add_widget(zd,"label","all-any","hbaa",E2X("all/any")); zdialog_add_widget(zd,"hbox","hbtags","dialog","space=1"); zdialog_add_widget(zd,"label","labtags","hbtags",E2X("search tags"),"space=3"); zdialog_add_widget(zd,"frame","frtags","hbtags",0,"expand"); zdialog_add_widget(zd,"text","searchtags","frtags",0,"expand|wrap"); zdialog_add_widget(zd,"radio","alltags","hbtags",0,"space=5"); zdialog_add_widget(zd,"radio","anytags","hbtags",0,"space=5"); zdialog_add_widget(zd,"hbox","hbtext","dialog","space=1"); zdialog_add_widget(zd,"label","labtext","hbtext",E2X("search text"),"space=3"); zdialog_add_widget(zd,"zentry","searchtext","hbtext",0,"expand"); zdialog_add_widget(zd,"radio","alltext","hbtext",0,"space=5"); zdialog_add_widget(zd,"radio","anytext","hbtext",0,"space=5"); zdialog_add_widget(zd,"hbox","hbfiles","dialog","space=1"); zdialog_add_widget(zd,"label","labfiles","hbfiles",E2X("search files"),"space=3"); zdialog_add_widget(zd,"zentry","searchfiles","hbfiles",0,"expand"); zdialog_add_widget(zd,"radio","allfiles","hbfiles",0,"space=5"); zdialog_add_widget(zd,"radio","anyfiles","hbfiles",0,"space=5"); zdialog_add_widget(zd,"hbox","hblocs","dialog","space=1"); zdialog_add_widget(zd,"label","lablocs","hblocs",E2X("search locations"),"space=3"); zdialog_add_widget(zd,"zentry","searchlocs","hblocs",0,"expand"); zdialog_add_widget(zd,"radio","alllocs","hblocs",0,"space=5"); zdialog_add_widget(zd,"radio","anylocs","hblocs",0,"space=5"); zdialog_add_ttip(zd,"searchlocs",E2X("enter cities, countries")); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=2"); zdialog_add_widget(zd,"hbox","hbmeta","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labmeta","hbmeta",E2X("search other metadata"),"space=3"); zdialog_add_widget(zd,"button","addmeta","hbmeta",Badd,"space=8"); zdialog_add_widget(zd,"label","metadata#","hbmeta","( )"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=2"); zdialog_add_widget(zd,"hbox","hbnt","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labnt","hbnt",E2X("Enter Search Tag"),"space=3"); zdialog_add_widget(zd,"zentry","entertag","hbnt",0,"size=20"); zdialog_add_widget(zd,"hbox","hbmt","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labmt","hbmt",E2X("Matching Tags"),"space=3"); zdialog_add_widget(zd,"frame","frmt","hbmt",0,"expand"); zdialog_add_widget(zd,"text","matchtags","frmt",0,"wrap|expand"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=3"); zdialog_add_widget(zd,"hbox","hbdt1","dialog"); zdialog_add_widget(zd,"label","labdt","hbdt1",E2X("Defined Tags Category"),"space=3"); zdialog_add_widget(zd,"label","space","hbdt1",0,"expand"); zdialog_add_widget(zd,"combo","defcats","hbdt1",0,"expand|space=5"); zdialog_add_widget(zd,"hbox","hbdt2","dialog",0,"expand"); zdialog_add_widget(zd,"frame","frdt2","hbdt2",0,"expand|space=3"); zdialog_add_widget(zd,"scrwin","swdt2","frdt2",0,"expand"); zdialog_add_widget(zd,"text","deftags","swdt2",0,"wrap"); if (Fautosearch) { // script file 20.0 zdialog_run(zd,searchimages_dialog_event,"save"); // bypass interactive stuff return; } widget = zdialog_widget(zd,"searchtags"); // tag widget mouse/KB event function textwidget_set_eventfunc(widget,search_searchtags_clickfunc); widget = zdialog_widget(zd,"matchtags"); textwidget_set_eventfunc(widget,search_matchtags_clickfunc); widget = zdialog_widget(zd,"deftags"); textwidget_set_eventfunc(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,"photodate",1); zdialog_stuff(zd,"filedate",0); zdialog_stuff(zd,"lastver",0); zdialog_stuff(zd,"org+last",0); // 20.0 zdialog_stuff(zd,"allvers",0); zdialog_stuff(zd,"nochange",1); // 20.0 zdialog_stuff(zd,"alltags",0); zdialog_stuff(zd,"anytags",1); zdialog_stuff(zd,"alltext",0); zdialog_stuff(zd,"anytext",1); zdialog_stuff(zd,"allfiles",0); zdialog_stuff(zd,"anyfiles",1); zdialog_stuff(zd,"alllocs",0); zdialog_stuff(zd,"anylocs",1); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_fetch(zd,"searchtags",searchtags,tagScc); strcat(searchtags," "); // trailing blank after "tagname," 20.0 load_deftags(0); // stuff defined tags into dialog deftags_stuff(zd,"ALL"); defcats_stuff(zd); // and defined categories zdialog_resize(zd,0,800); // start dialog zdialog_run(zd,searchimages_dialog_event,"save"); zdialog_wait(zd); // wait for dialog completion zdialog_free(zd); return; } // search tag was clicked void search_searchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;:",end); if (! txtag) return; del_tag(txtag,searchtags); // remove from search list zdialog_stuff(zdsearchimages,"searchtags",searchtags); zfree(txtag); return; } // matching tag was clicked void search_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;",end); if (! txtag) return; add_tag(txtag,searchtags,tagScc); // add to search tag list zdialog_stuff(zdsearchimages,"entertag",""); // update dialog widgets zdialog_stuff(zdsearchimages,"matchtags",""); zdialog_stuff(zdsearchimages,"searchtags",searchtags); zdialog_goto(zdsearchimages,"entertag"); // focus back to entertag widget zfree(txtag); return; } // defined tag was clicked void search_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;:",end); if (! txtag || end == ':') return; // tag category clicked, ignore add_tag(txtag,searchtags,tagScc); // add to search tag list zdialog_stuff(zdsearchimages,"searchtags",searchtags); zfree(txtag); return; } // search images dialog event and completion callback function int searchimages_dialog_event(zdialog *zd, cchar *event) { int datetimeOK(char *datetime); int searchimages_select(xxrec_t *xxrec); int searchimages_metadata_dialog(zdialog *zd); int searchimages_metadata_report(void); cchar dateLoDefault[20] = "0000-01-01 00:00:00"; // start of time cchar dateHiDefault[20] = "2099-12-31 23:59:59"; // end of time char *file, *file2, **vlist; char **flist, *pp, buffer[XFCC]; char mm[4] = "mm"; int match, Nmatch; int ii, jj, kk, err, cc; int nt, cc1, cc2, ff, nf; xxrec_t *xxrec; FILE *fid; char *pp1, *pp2; char entertag[tagcc], matchtags[20][tagcc]; char matchtagstext[(tagcc+2)*20]; char catgname[tagcc]; char albumfile[200]; if (strmatch(event,"proceed")) zd->zstat = 4; // "proceed" from autosearch 20.0 if (strmatch(event,"addmeta")) // get other metadata criteria Fmeta = searchimages_metadata_dialog(zd); if (Fmeta) zdialog_stuff(zd,"metadata#","(*)"); // show presence of metadata criteria else zdialog_stuff(zd,"metadata#","(_)"); if (zd->zstat == 1) { // [load] settings from file 20.0 zd->zstat = 0; func_load_widgets(zd,null,"search_images",null); zdialog_fetch(zd,"searchtags",searchtags,tagScc); strcat(searchtags," "); // trailing blank after "tagname," 20.0 } if (zd->zstat == 2) { // [save] settings to file 20.0 zd->zstat = 0; func_save_widgets(zd,null,"search_images",null); } if (zd->zstat == 3) // [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,"org+last",0); // 20.0 zdialog_stuff(zd,"allvers",0); zdialog_stuff(zd,"nochange",1); // 20.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,"photodate",1); zdialog_stuff(zd,"filedate",0); zdialog_stuff(zd,"starsfrom",""); zdialog_stuff(zd,"starsto",""); zdialog_stuff(zd,"searchtags",""); zdialog_stuff(zd,"searchtext",""); zdialog_stuff(zd,"searchfiles",""); zdialog_stuff(zd,"searchlocs",""); *searchtags = 0; Flocs = 0; *searchLocations = 0; zdialog_stuff(zd,"metadata#","( )"); Fmeta = 0; Nsearchkeys = 0; zd->zstat = 0; // keep dialog active return 1; } if (zstrstr("lastver org+last allvers nochange",event)) { // if any selected, 20.0 zdialog_stuff(zd,"lastver",0); // deselect the others zdialog_stuff(zd,"org+last",0); zdialog_stuff(zd,"allvers",0); zdialog_stuff(zd,"nochange",0); zdialog_stuff(zd,event,1); } if (strmatch(event,"entertag")) // new tag is being typed in { zdialog_stuff(zd,"matchtags",""); // clear matchtags in dialog zdialog_fetch(zd,"entertag",entertag,tagcc); // get chars. typed so far cc1 = strlen(entertag); for (ii = jj = 0; ii <= cc1; ii++) { // remove foul characters if (strchr(",:;",entertag[ii])) continue; entertag[jj++] = entertag[ii]; } if (jj < cc1) { // something was removed entertag[jj] = 0; cc1 = jj; zdialog_stuff(zd,"entertag",entertag); } if (cc1 < 2) return 1; // wait for at least 2 chars. for (ii = nt = 0; ii < maxtagcats; ii++) // loop all categories { pp2 = tags_deftags[ii]; // category: aaaaaa, bbbbb, ... tagN, if (! pp2) continue; // | | pp2 = strchr(pp2,':'); // pp1 pp2 while (true) // loop all deftags in category { pp1 = pp2 + 2; if (! *pp1) break; pp2 = strchr(pp1,','); if (! pp2) break; if (strmatchcaseN(entertag,pp1,cc1)) { // deftag matches chars. typed so far cc2 = pp2 - pp1; strncpy(matchtags[nt],pp1,cc2); // save deftags that match matchtags[nt][cc2] = 0; if (++nt == 20) return 1; // quit if 20 matches or more } } } if (nt == 0) return 1; // no matches pp1 = matchtagstext; for (ii = 0; ii < nt; ii++) // make deftag list: aaaaa, bbb, cccc ... { strcpy(pp1,matchtags[ii]); pp1 += strlen(pp1); strcpy(pp1,", "); pp1 += 2; } zdialog_stuff(zd,"matchtags",matchtagstext); // stuff matchtags in dialog return 1; } if (strmatch(event,"defcats")) { // new tag category selection zdialog_fetch(zd,"defcats",catgname,tagcc); deftags_stuff(zd,catgname); } if (zd->zstat != 4) return 1; // cancel if not [proceed] // Inputs are complete. Validate all inputs. ----------------------------------- zdialog_fetch(zd,"allimages",Fscanall); // search all images zdialog_fetch(zd,"currset",Fscancurr); // search current set (gallery) zdialog_fetch(zd,"newset",Fnewset); // matching images --> new set zdialog_fetch(zd,"addset",Faddset); // add matching image to set zdialog_fetch(zd,"remset",Fremset); // remove matching images from set if (Fremset && Fscanall) { // illogical search zmessageACK(Mwin,E2X("to remove images from current set, \n" "search current set")); zd->zstat = 0; // keep dialog active return 1; } if (Faddset && Fscancurr) { zmessageACK(Mwin,E2X("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,"org+last",Forglast); // get original + last version 20.0 zdialog_fetch(zd,"allvers",Fallvers); // get all vers. of matching image zdialog_fetch(zd,"datefrom",searchDateFrom,20); // get search date range zdialog_fetch(zd,"dateto",searchDateTo,20); zdialog_fetch(zd,"photodate",Fphotodate); // photo or file date zdialog_fetch(zd,"filedate",Ffiledate); zdialog_fetch(zd,"starsfrom",searchStarsFrom,2); // get search stars range zdialog_fetch(zd,"starsto",searchStarsTo,2); zdialog_fetch(zd,"searchtags",searchtags,tagScc); // get search tags zdialog_fetch(zd,"searchtext",searchtext,tagScc); // get search text* zdialog_fetch(zd,"searchfiles",searchfiles,tagScc); // get search /path*/file* zdialog_fetch(zd,"searchlocs",searchLocations,200); // get search locations zdialog_fetch(zd,"alltags",Falltags); // get match all/any options zdialog_fetch(zd,"alltext",Falltext); zdialog_fetch(zd,"allfiles",Fallfiles); zdialog_fetch(zd,"alllocs",Falllocs); Fdates = 0; if (*searchDateFrom) Fdates++; // search date from was given else strcpy(searchDateFrom,dateLoDefault); // else search from start of time if (*searchDateTo) Fdates++; // search date to was given else strcpy(searchDateTo,dateHiDefault); // else search to end of time Fnulldate = 0; if (strmatchcase(searchDateFrom,"null")) { // search for missing photo date Fnulldate = 1; // (user input "null") Fdates = Ffiledate = Fphotodate = 0; zdialog_stuff(zd,"photodate",1); zdialog_stuff(zd,"filedate",0); } if (Fdates) // complete partial date/time data { cc = strlen(searchDateFrom); for (ii = cc; ii < 20; ii++) // default date from: searchDateFrom[ii] = dateLoDefault[ii]; // 0000-01-01 00:00:00 cc = strlen(searchDateTo); for (ii = cc; ii < 20; ii++) // default date to: searchDateTo[ii] = dateHiDefault[ii]; // 2099-12-31 23:59:59 if (cc == 7) { // input was yyyy-mm strncpy(mm,searchDateTo+5,2); // get mm = "01" .. "12" if (strstr("04 06 09 11",mm)) memmove(searchDateTo+8,"30",2); // set dd = 30 for these months 19.0 if (strmatch(mm,"02")) { memmove(searchDateTo+8,"28",2); // set dd = 28 for month 02 ii = atoi(searchDateTo); if (ii == (ii/4)*4) memmove(searchDateTo+8,"29",2); // set dd = 29 if leap year } } ff = 0; // check search dates reasonable if (! datetimeOK(searchDateFrom)) ff = 1; // invalid year/mon/day (e.g. mon 13) if (! datetimeOK(searchDateTo)) ff = 1; // or hour/min/sec (e.g. hour 33) if (strcmp(searchDateFrom,searchDateTo) > 0) ff = 1; // search-from date > search-to date if (ff) { zmessageACK(Mwin,E2X("search dates not reasonable \n %s %s"), searchDateFrom,searchDateTo); zd->zstat = 0; return 1; } pp = pdatetime_metadatetime(searchDateFrom); // convert to yyyymmddhhmmss if (! pp) return 1; // for metadata comparisons strncpy0(searchdatelo,pp,16); pp = pdatetime_metadatetime(searchDateTo); if (! pp) return 1; strncpy0(searchdatehi,pp,16); } Fstars = 0; if (*searchStarsFrom || *searchStarsTo) { Fstars = 1; // stars was given ii = *searchStarsFrom; if (! ii) ii = '0'; if (ii < '0' || ii > '5') Fstars = 0; // validate inputs jj = *searchStarsTo; if (! jj) jj = '5'; if (jj < '0' || jj > '5') Fstars = 0; if (jj < ii) Fstars = 0; if (! Fstars) { zmessageACK(Mwin,E2X("stars range not reasonable")); zd->zstat = 0; return 1; } } Ffiles = 0; if (! blank_null(searchfiles)) Ffiles = 1; // search path / file (fragment) was given Ftext = 0; if (! blank_null(searchtext)) Ftext = 1; // search text was given Ftags = 0; if (! blank_null(searchtags)) Ftags = 1; // search tags was given Flocs = 0; if (*searchLocations) Flocs = 1; // search locations was given Fmeta = 0; if (Nsearchkeys) Fmeta = 1; // search other metadata was given // Begin search. ------------------------------------------------------------ if (checkpend("all")) return 1; // check nothing pending Fblock = 1; Ffuncbusy = 1; m_viewmode(0,"G"); Nmatch = 0; // matching images found 20.0 // Search all images, make NEW image set from matching images. if (Fscanall && Fnewset) { fid = fopen(searchresults_file,"w"); // open output file if (! fid) goto filerror; for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { zmainloop(10); // keep GTK alive if (Fescape) goto usercancel; // user cancel 19.0 xxrec = xxrec_tab[ii]; match = searchimages_select(xxrec); // test against select criteria if (match) { // all criteria passed Nmatch++; // count matches fprintf(fid,"%s\n",xxrec->file); // save matching file snprintf(paneltext,200,"images found: %d",Nmatch); // update progress } } fclose(fid); Ncurrset = Nmatch; // current set count } // Search all images, ADD matching images to current image set. if (Fscanall && Faddset) { fid = fopen(searchresults_file,"a"); // open output file APPEND 20.0 if (! fid) goto filerror; for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { zmainloop(10); // keep GTK alive if (Fescape) goto usercancel; // user cancel 19.0 xxrec = xxrec_tab[ii]; match = searchimages_select(xxrec); // test against select criteria if (match) { // all criteria passed Nmatch++; // count matches fprintf(fid,"%s\n",xxrec->file); // save matching filename snprintf(paneltext,200,"images added: %d",Nmatch); // update progress } } fclose(fid); Ncurrset += Nmatch; // new current set count 20.0 } // Search current image set, 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(10); // keep GTK alive if (Fescape) goto usercancel; // user cancel 19.0 file = gallery(0,"get",ii); if (! file) break; if (*file == '!') { // skip folders zfree(file); continue; } xxrec = get_xxrec(file); if (! xxrec) { // no image index rec? zfree(file); continue; } match = searchimages_select(xxrec); // test against select criteria if (match) { // passed Nmatch++; // count matching images fprintf(fid,"%s\n",file); // output matching image file } zfree(file); // free memory snprintf(paneltext,200,"images kept: %d",Nmatch); // update progress 20.0 } fclose(fid); Ncurrset = Nmatch; // new current set count 20.0 } // Search current image set, 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(10); // keep GTK alive if (Fescape) goto usercancel; // user cancel 19.0 file = gallery(0,"get",ii); if (! file) break; if (*file == '!') { // skip folders zfree(file); continue; } xxrec = get_xxrec(file); if (! xxrec) { // no image index rec? zfree(file); continue; } match = searchimages_select(xxrec); // test against select criteria if (match) Nmatch++; // if match, count and skip else fprintf(fid,"%s\n",file); // save retained file zfree(file); // free memory snprintf(paneltext,200,"images removed: %d",Nmatch); // update progress 20.0 } fclose(fid); Ncurrset -= Nmatch; // new current set count 20.0 } // Include only last version of matching images if (Flastver && Ncurrset) { cc = Ncurrset * 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 < Ncurrset; 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 < Ncurrset; ii++) // scan file list in memory { pp = strrchr(flist[ii],'/'); // /folder.../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 search results file Nmatch = 0; for (ii = 0; ii < Ncurrset; ii++) { file = flist[ii]; if (! file) continue; file2 = file_newest_version(file); // insure last version 20.0 if (! file2) continue; // should not happen fprintf(fid,"%s\n",file2); zfree(file); zfree(file2); Nmatch++; snprintf(paneltext,200,"included files: %d",Nmatch); // update progress 20.0 } fclose(fid); zfree(flist); Ncurrset = Nmatch; // new current set count 20.0 } // Include original and last version of matching images // 20.0 if (Forglast && Ncurrset) { cc = Ncurrset * sizeof(char *); // first, reduce selected images flist = (char **) zmalloc(cc); // to one version each fid = fopen(searchresults_file,"r"); // read file of selected image files if (! fid) goto filerror; for (ii = 0; ii < Ncurrset; ii++) // build file list in memory { file = fgets_trim(buffer,XFCC,fid); flist[ii] = zstrdup(file); } fclose(fid); for (ii = 1; ii < Ncurrset; ii++) // scan file list in memory { pp = strrchr(flist[ii],'/'); if (! pp) continue; pp = strrchr(pp,'.'); if (! pp) continue; // /folder.../filename.ext if (strmatchN(pp-4,".v",2)) pp -= 4; // (or) .../filename.vNN.ext 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 search results file for (ii = 0; ii < Ncurrset; ii++) { if (flist[ii]) { fprintf(fid,"%s\n",flist[ii]); zfree(flist[ii]); } } fclose(fid); zfree(flist); flist = (char **) zmalloc(maxgallery * sizeof(char *)); // new list for original + last ver. fid = fopen(searchresults_file,"r"); // read file of selected image files if (! fid) goto filerror; for (ii = Nmatch = 0; Nmatch < maxgallery-2; ii++) // get all versions for selected files { file = fgets_trim(buffer,XFCC,fid); if (! file) break; vlist = file_all_versions(file,nf); if (! vlist) continue; if (nf > 0) flist[Nmatch++] = zstrdup(vlist[0]); // original if (nf > 1) flist[Nmatch++] = zstrdup(vlist[nf-1]); // last version for (kk = 0; kk < nf; kk++) zfree(vlist[kk]); zfree(vlist); snprintf(paneltext,200,"included files: %d",Nmatch); // update progress 20.0 } fclose(fid); if (Nmatch > maxgallery-2) zmessageACK(Mwin,Bgallerytruncated,maxgallery); fid = fopen(searchresults_file,"w"); // new search results file if (! fid) goto filerror; for (ii = 0; ii < Nmatch; ii++) // write all versions to file { fprintf(fid,"%s\n",flist[ii]); zfree(flist[ii]); } fclose(fid); zfree(flist); Ncurrset = Nmatch; // new current set count 20.0 } // Include original and all versions of matching images. if (Fallvers && Ncurrset) { cc = Ncurrset * sizeof(char *); // first, reduce selected images flist = (char **) zmalloc(cc); // to one version each fid = fopen(searchresults_file,"r"); // read file of selected image files if (! fid) goto filerror; for (ii = 0; ii < Ncurrset; ii++) // build file list in memory { file = fgets_trim(buffer,XFCC,fid); flist[ii] = zstrdup(file); } fclose(fid); for (ii = 1; ii < Ncurrset; ii++) // scan file list in memory { pp = strrchr(flist[ii],'/'); if (! pp) continue; pp = strrchr(pp,'.'); if (! pp) continue; // /folder.../filename.ext if (strmatchN(pp-4,".v",2)) pp -= 4; // (or) .../filename.vNN.ext 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 search results file for (ii = 0; ii < Ncurrset; ii++) { if (flist[ii]) { fprintf(fid,"%s\n",flist[ii]); zfree(flist[ii]); } } fclose(fid); zfree(flist); flist = (char **) zmalloc(maxgallery * sizeof(char *)); // new list for all versions fid = fopen(searchresults_file,"r"); // read file of selected image files if (! fid) goto filerror; for (ii = Nmatch = 0; Nmatch < maxgallery-100; ii++) // get all versions for selected files { file = fgets_trim(buffer,XFCC,fid); if (! file) break; vlist = file_all_versions(file,nf); if (! vlist) continue; for (jj = 0; jj < nf; jj++) flist[Nmatch++] = vlist[jj]; zfree(vlist); snprintf(paneltext,200,"included files: %d",Nmatch); // update progress 20.0 } fclose(fid); if (Nmatch > maxgallery-100) zmessageACK(Mwin,Bgallerytruncated,maxgallery); fid = fopen(searchresults_file,"w"); // new search results file if (! fid) goto filerror; for (ii = 0; ii < Nmatch; ii++) // write all versions to file { fprintf(fid,"%s\n",flist[ii]); zfree(flist[ii]); } fclose(fid); zfree(flist); Ncurrset = Nmatch; // new current set count 20.0 } // Search complete. --------------------------------------------------------- printz("search count: %d \n", Ncurrset); *paneltext = 0; // remove progress counter Ffuncbusy = 0; Fblock = 0; if (Ncurrset == 0) { // no images found zd->zstat = 0; // stay in search dialog return 1; } snprintf(albumfile,200,"%s/Search Results",albums_folder); // save search results in the err = copyFile(searchresults_file,albumfile); // album "Search Results" if (err) zmessageACK(Mwin,strerror(err)); free_resources(); navi::gallerytype = SEARCH; // normal search results gallery(searchresults_file,"initF",0); // generate gallery of matching files if (Frepmeta) { // metadata report format searchimages_metadata_report(); // get metadata from image files navi::gallerytype = META; // report 20.0 } gallery(0,"paint",0); // position at top m_viewmode(0,"G"); zdialog_free(zd); return 1; usercancel: // cancel via escape key 19.0 printz("search canceled \n"); Fescape = 0; zmessage_post_bold(Mwin,"parent",2,Bcancel); *paneltext = 0; Ffuncbusy = 0; Fblock = 0; zd->zstat = 0; return 1; filerror: zmessageACK(Mwin,"file error: %s",strerror(errno)); *paneltext = 0; Ffuncbusy = 0; Fblock = 0; return 1; } // Test a given image against selection criteria, return match status. int searchimages_select(xxrec_t *xxrec) { int searchimages_metadata_select(xxrec_t *xxrec); cchar *pps, *ppf; int iis, iif; int Nmatch, Nnomatch; if (Ffiles) // file name match is wanted { Nmatch = Nnomatch = 0; for (iis = 1; ; iis++) { pps = strField(searchfiles,' ',iis); // step thru search file names if (! pps) break; ppf = strcasestr(xxrec->file,pps); // compare image file name if (ppf) Nmatch++; else Nnomatch++; } if (Nmatch == 0) return 0; // no match any file if (Fallfiles && Nnomatch) return 0; // no match all files (dir & file names) } if (Fnulldate && ! strmatch(xxrec->pdate,"")) return 0; // missing photo date wanted if (Fdates) // date match is wanted { if (Fphotodate) { if (strcmp(xxrec->pdate,searchdatelo) < 0) return 0; // test photo date if (strcmp(xxrec->pdate,searchdatehi) > 0) return 0; } if (Ffiledate) { // test file mode date if (strcmp(xxrec->fdate,searchdatelo) < 0) return 0; if (strcmp(xxrec->fdate,searchdatehi) > 0) return 0; } } if (Ftags) // tags match is wanted { if (! xxrec->tags) return 0; // should not happen 19.10 Nmatch = Nnomatch = 0; for (iis = 1; ; iis++) // step thru search tags { pps = strField(searchtags,",;",iis); // delimited if (! pps) break; if (*pps == 0) continue; for (iif = 1; ; iif++) // step thru file tags (delimited) { ppf = strField(xxrec->tags,",;",iif); if (! ppf) { Nnomatch++; break; } // count matches and fails if (*ppf == 0) continue; if (strmatch(pps,ppf)) { Nmatch++; break; } // match } } if (Nmatch == 0) return 0; // no match to any tag if (Falltags && Nnomatch) return 0; // no match to all tags } if (Fstars) // rating (stars) match is wanted { if (*searchStarsFrom && xxrec->rating[0] < *searchStarsFrom) return 0; if (*searchStarsTo && xxrec->rating[0] > *searchStarsTo) return 0; } if (Ftext) // text match is wanted { Nmatch = Nnomatch = 0; for (iis = 1; ; iis++) // step through search words { pps = strField(searchtext,' ',iis); if (! pps) break; if (*pps == 0) continue; ppf = strcasestr(xxrec->capt,pps); // match words in captions and comments if (! ppf) ppf = strcasestr(xxrec->comms,pps); if (ppf) Nmatch++; else Nnomatch++; } if (Nmatch == 0) return 0; // no match to any word if (Falltext && Nnomatch) return 0; // no match to all words } if (Flocs ) // location match is wanted { Nmatch = Nnomatch = 0; for (iis = 1; ; iis++) // step thru search locations { pps = strField(searchLocations,", ",iis); // comma or blank delimiter if (! pps) break; if (strmatch(pps,"null")) { // search for missing location 20.0 if (strmatch(xxrec->location,"")) Nmatch++; if (strmatch(xxrec->country,"")) Nmatch++; } else if (strcasestr(xxrec->location,pps)) Nmatch++; else if (strcasestr(xxrec->country,pps)) Nmatch++; else Nnomatch++; } if (! Nmatch) return 0; // no match found if (Falllocs && Nnomatch) return 0; } if (Fmeta) // other metadata match if (! searchimages_metadata_select(xxrec)) return 0; return 1; } /********************************************************************************/ // dialog to get metadata search criteria int searchimages_metadata_dialog(zdialog *zdp) { int searchimages_metadata_dialog_event(zdialog *zd, cchar *event); cchar *metamess = E2X("Always reported: date, stars, tags, caption, comment"); zdialog *zd; int ii, jj, zstat; char matchx[8] = "matchx"; char metafile[200], buff[100]; cchar *pp; FILE *fid; cchar *excludelist = "Comment, User Comment, Caption Abstract, Rating, " "Date Time Original, Location, Country, Keywords"; /*** ________________________________________________________ | Search and Report Metadata | | | | Always reported: date, stars, tags, caption, comment | | | | Additional Items for Report | | Keyword Match Criteria | | [_____________|v] [ report ] [______________________] | | [_____________|v] [ matches ] [______________________] | | [_____________|v] [contains ] [______________________] | | [_____________|v] [number = ] [______________________] | | [_____________|v] [number >=] [______________________] | | [_____________|v] [number <=] [______________________] | | | | [clear] [apply] [cancel] | |________________________________________________________| ***/ zd = zdialog_new("Search and Report Metadata",Mwin,Bclear,Bapply,Bcancel,null); zdialog_add_widget(zd,"label","labmeta","dialog",metamess,"space=5"); zdialog_add_widget(zd,"label","labopts","dialog",E2X("Additional Items for Report")); zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb3","hb1",0,"space=5|homog|expand"); zdialog_add_widget(zd,"label","lab1","vb1",E2X("Keyword")); // 19.0 zdialog_add_widget(zd,"comboE","key0","vb1"); zdialog_add_widget(zd,"comboE","key1","vb1"); zdialog_add_widget(zd,"comboE","key2","vb1"); zdialog_add_widget(zd,"comboE","key3","vb1"); zdialog_add_widget(zd,"comboE","key4","vb1"); zdialog_add_widget(zd,"label","lab2","vb2"); zdialog_add_widget(zd,"combo","match0","vb2"); zdialog_add_widget(zd,"combo","match1","vb2"); zdialog_add_widget(zd,"combo","match2","vb2"); zdialog_add_widget(zd,"combo","match3","vb2"); zdialog_add_widget(zd,"combo","match4","vb2"); zdialog_add_widget(zd,"label","lab3","vb3",E2X("Match Criteria")); zdialog_add_widget(zd,"zentry","value0","vb3",0,"expand"); zdialog_add_widget(zd,"zentry","value1","vb3",0,"expand"); zdialog_add_widget(zd,"zentry","value2","vb3",0,"expand"); zdialog_add_widget(zd,"zentry","value3","vb3",0,"expand"); zdialog_add_widget(zd,"zentry","value4","vb3",0,"expand"); if (! searchkeys[0]) // first call initialization { for (ii = 0; ii < 5; ii++) { searchkeys[ii] = (char *) zmalloc(40); searchvals[ii] = (char *) zmalloc(100); *searchkeys[ii] = *searchvals[ii] = 0; } } for (ii = 0; ii < 5; ii++) { matchx[5] = '0' + ii; zdialog_cb_app(zd,matchx,"report"); zdialog_cb_app(zd,matchx,"matches"); zdialog_cb_app(zd,matchx,"contains"); zdialog_cb_app(zd,matchx,"number ="); zdialog_cb_app(zd,matchx,"number =>"); zdialog_cb_app(zd,matchx,"number <="); } snprintf(metafile,200,"%s/metadata_short_list",get_zhomedir()); // metadata list >> keyword widgets 19.0 fid = fopen(metafile,"r"); if (! fid) { zmessageACK(Mwin,"%s \n %s",metafile,strerror(errno)); return 0; } while (true) { pp = fgets_trim(buff,100,fid,1); if (! pp) break; if (*pp == 0) continue; for (jj = 1; ; jj++) { pp = strField(excludelist,',',jj); if (! pp) break; if (strmatch(pp,buff)) break; } if (pp) continue; zdialog_cb_app(zd,"key0",buff); zdialog_cb_app(zd,"key1",buff); zdialog_cb_app(zd,"key2",buff); zdialog_cb_app(zd,"key3",buff); zdialog_cb_app(zd,"key4",buff); } fclose(fid); zdialog_show(zdp,0); // hide parent dialog zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,searchimages_metadata_dialog_event,"save"); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_free(zd); zdialog_show(zdp,1); // restore parent dialog if (zstat != 1) return 0; // no search criteria entered for (ii = 0; ii < Nsearchkeys; ii++) { // loop search keys keyindexed[ii] = 0; for (jj = 0; jj < Mxmeta; jj++) { // compare to xmeta keys if (! xmeta_keys[jj]) break; if (strmatchcase(searchkeys[ii],xmeta_keys[jj])) keyindexed[ii] = 1; // found, search key is indexed } } allkeysindexed = 1; // all search keys indexed for (ii = 0; ii < Nsearchkeys; ii++) if (! keyindexed[ii]) allkeysindexed = 0; // NOT return 1; } // dialog event and completion callback function int searchimages_metadata_dialog_event(zdialog *zd, cchar *event) // overhauled { int ii, jj, err; float fnum; char keyx[8] = "keyx", valuex[8] = "valuex", matchx[8] = "matchx"; char temp[100]; if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) { for (ii = 0; ii < 5; ii++) { // clear keyx[3] = '0' + ii; valuex[5] = '0' + ii; matchx[5] = '0' + ii; zdialog_stuff(zd,keyx,""); zdialog_stuff(zd,valuex,""); zdialog_stuff(zd,matchx,"matches"); zd->zstat = 0; // keep dialog active } return 1; } if (zd->zstat != 2) { Nsearchkeys = 0; // no search keys return 1; } Nsearchkeys = 0; // apply for (ii = jj = 0; ii < 5; ii++) { keyx[3] = '0' + ii; // get search key zdialog_fetch(zd,keyx,searchkeys[ii],40); strCompress(searchkeys[ii]); // remove all blanks from key names if (*searchkeys[ii] <= ' ') continue; memmove(searchkeys[jj],searchkeys[ii],40); // repack blank keys valuex[5] = '0' + ii; zdialog_fetch(zd,valuex,searchvals[ii],100); // get corresp. match value strTrim2(searchvals[jj],searchvals[ii]); // trim leading and trailing blanks matchx[5] = '0' + ii; zdialog_fetch(zd,matchx,temp,20); // get corresp. match type if (strmatch(temp,"report")) matchtype[jj] = 'r'; // report the key, no match test 19.0 if (strmatch(temp,"matches")) matchtype[jj] = 'm'; if (strmatch(temp,"contains")) matchtype[jj] = 'c'; if (strmatch(temp,"number =")) matchtype[jj] = '='; if (strmatch(temp,"number =>")) matchtype[jj] = '>'; if (strmatch(temp,"number <=")) matchtype[jj] = '<'; if (strstr(temp,"number")) { // check numeric values err = convSF(searchvals[jj],fnum); if (err) { snprintf(temp,100,E2X("bad number: %s"),searchvals[jj]); zmessageACK(Mwin,temp); zd->zstat = 0; return 1; } } if (ii > jj) *searchkeys[ii] = *searchvals[ii] = 0; jj++; } Nsearchkeys = jj; // keys found, no blanks if (Nsearchkeys) zd->zstat = 1; // keys were entered else zd->zstat = 2; // no keys were entered return 1; } // Test image file metadata against metadata select criteria. // indexed metadata = xmeta: keyname1=keyvalue1^ keyname2=keyvalue2^ ... int searchimages_metadata_select(xxrec_t *xxrec) { int searchmeta_test1(cchar *keyval, cchar matchtype, cchar *searchvals); char *keyvals[5]; int ii, jj, cc, nx = 0, pass = 1; cchar *pps, *ppf; char *file = xxrec->file; char *xmeta = xxrec->xmeta; // indexed metadata list char xkey[Mxmeta][60], xval[Mxmeta][100]; pps = xmeta; // unpack indexed metadata for (ii = 0; ii < Mxmeta; ii++) { // to xkey[], xdata[] ppf = strchr(pps,'='); if (! ppf) break; cc = ppf-pps; if (cc > 59) break; strncpy0(xkey[ii],pps,cc+1); pps = ppf + 1; ppf = strchr(pps,'^'); if (! ppf) break; cc = ppf - pps; if (cc > 99) break; strncpy0(xval[ii],pps,cc+1); pps = ppf + 1; while (*pps == ' ') pps++; } nx = ii; // xmeta keys found for (ii = 0; ii < Nsearchkeys; ii++) { // loop search keys for (jj = 0; jj < nx; jj++) { // find xmeta key if (strmatchcase(searchkeys[ii],xkey[jj])) { // if found, test xmeta data pass = searchmeta_test1(xval[jj],matchtype[ii],searchvals[ii]); // against search criteria if (! pass) return 0; // fail, no more testing needed break; } } if (jj == nx && keyindexed[ii]) { // search key indexed, not present pass = searchmeta_test1(0,matchtype[ii],searchvals[ii]); // test for "null" select criteria if (! pass) return 0; // fail } } if (allkeysindexed) return 1; // all search keys indexed, pass exif_get(file,(cchar **) searchkeys,keyvals,Nsearchkeys); // get metadata from image file for (ii = 0; ii < Nsearchkeys; ii++) { // loop search keys pass = searchmeta_test1(keyvals[ii],matchtype[ii],searchvals[ii]); // test image data if (! pass) break; // fail, no more testing needed } for (ii = 0; ii < Nsearchkeys; ii++) // bugfix: memory leak if (keyvals[ii]) zfree(keyvals[ii]); return pass; // all values pass } // test a single metadata key/value against select criteria int searchmeta_test1(cchar *keyval, cchar matchtype, cchar *searchvals) { int nth; cchar *pps; float fkey, fval; if (matchtype == 'r') return 1; // key value reported, not tested 19.0 if (! keyval) // no metadata present { if (strmatch(searchvals,"null")) return 1; // search for empty data, pass return 0; // fail } if (*searchvals <= ' ') return 1; // no search values, pass for (nth = 1; ; nth++) // loop all search values { pps = strField(searchvals,',',nth); // comma delimiter if (! pps) return 0; // no more, no match found if (matchtype == 'm') { // key matches any [wild] value if (MatchWildIgnoreCase(pps,keyval) == 0) return 1; // match not case sensitive } else if (matchtype == 'c') { // key contains any value if (strcasestr(keyval,pps)) return 1; // match not case sensitive } else if (matchtype == '=') { // numeric key equals any value fkey = atof(keyval); fval = atof(pps); if (fkey == fval) return 1; // found match } else if (matchtype == '>') { // numeric key >= one value fkey = atof(keyval); fval = atof(pps); if (fkey >= fval) return 1; // found match } else if (matchtype == '<') { // numeric key <= one value fkey = atof(keyval); fval = atof(pps); if (fkey <= fval) return 1; // found match } else return 0; // should not happen } return 0; } // Report selected metadata using a gallery window layout // with image thumbnails and selected metadata text. int searchimages_metadata_report() { using namespace navi; cchar *keys1[7] = { exif_date_key, iptc_rating_key, iptc_keywords_key, exif_city_key, exif_country_key, iptc_caption_key, exif_comment_key }; cchar *keys[20]; char **gmdlist2; char *file, *kvals[20], blank[4] = " "; char text1[2*indexrecl], text2[200]; int Nkeys, ii, jj, cc; xxrec_t *xxrec; char wwhh[16], fsize[16]; if (! Nfiles) { printz("metadata report, 0 files \n"); return 0; } cc = Nfiles * sizeof(char *); // allocate metadata list 20.0 gmdlist2 = (char **) zmalloc(cc); // Nfiles = curr. gallery files memset(gmdlist2,0,cc); // corresp. metadata list for (ii = 0; ii < 7; ii++) // set first 6 key names, fixed keys[ii] = keys1[ii]; for (ii = 0; ii < Nsearchkeys; ii++) // remaining key names from user keys[ii+7] = searchkeys[ii]; Nkeys = 7 + Nsearchkeys; // total keys to extract Gmdrows = Nkeys - 1; // report rows (location/country 1 row) Fbusy_goal = Nfiles; Fbusy_done = 0; for (ii = 0; ii < Nfiles; ii++) // loop image gallery files { zmainloop(); // keep GTK alive file = gallery(0,"get",ii); if (! file) continue; xxrec = get_xxrec(file); // get size metadata if (xxrec) { snprintf(wwhh,16,"%dx%d",xxrec->ww,xxrec->hh); // 2345x1234 19.0 snprintf(fsize,16,"%.2fmb",xxrec->fsize/FMEGA); // 12.34mb 19.0 } else { strcpy(wwhh,blank); // 19.0 strcpy(fsize,blank); } exif_get(file,keys,kvals,Nkeys); // get the metadata for (cc = jj = 0; jj < Nkeys; jj++) // add metadata to report { if (jj == 0 && kvals[0]) { kvals[0][4] = kvals[0][7] = '-'; // conv. yyyy:mm:dd to yyyy-mm-dd snprintf(text2,200,"%s: %s \n",keys[jj], kvals[jj]); } else if (jj == 1) { snprintf(text2,200,"%s: %s Size: %s %s\n", // Rating: N size: 1234x2345 12.34mb keys[1], kvals[1], wwhh, fsize); } else if (jj == 3) // combine city and country snprintf(text2,200,"Location: %s %s\n",kvals[3],kvals[4]); // Location: xxxxxxxx xxxxxxxxx else if (jj == 4) continue; // skip country else snprintf(text2,200,"%s: %s \n",keys[jj], kvals[jj]); // all others strcpy(text1+cc,text2); cc += strlen(text2); } gmdlist2[ii] = zstrdup(text1); // save metadata text per image file 20.0 for (jj = 0; jj < Nkeys; jj++) // free memory if (kvals[jj]) zfree(kvals[jj]); zfree(file); Fbusy_done++; } if (Gmdlist) printz("*** memory leak Gmdlist[] \n"); // Gmdlist[] metadata container 20.0 Gmdlist = gmdlist2; // gallery("init/F") clears it for (ii = 0; ii < Nfiles; ii++) // GFlist entry --> Gmdlist entry 20.0 GFlist[ii].mdindex = ii; // (GFlist can be later sorted) Fbusy_goal = Fbusy_done = 0; gallerytype = META; // gallery type = search results/metadata return 0; } /********************************************************************************/ // Convert date format from "yyyy-mm-dd" to "yyyymmdd". // Missing month or day ("yyyy" or "yyyy-mm") is replaced with "01". // Output user message and return null if input is 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,E2X("date format is YYYY-MM-DD")); return 0; baddate: zmessageACK(Mwin,E2X("date is invalid")); return 0; } // Convert time format from "hh:mm:ss" to "hhmmss". // Missing seconds ("hh:mm") are replaced with zero ("hhmm00"). // Output user message and return null if input not valid. 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,E2X("time format is HH:MM [:SS]")); return 0; badtime: zmessageACK(Mwin,E2X("time is invalid")); return 0; } // convert yyyy-mm-dd hh:mm[:ss] to yyyymmddhhmmss char * pdatetime_metadatetime(cchar *pdatetime) { char pdate[12], ptime[12]; static char metadatetime[20]; char *pp; int cc; strncpy0(pdate,pdatetime,11); // yyyy-mm-dd strncpy0(ptime,pdatetime+11,9); // hh:mm[:ss] cc = strlen(ptime); if (cc == 5) strcat(ptime,":00"); // hh:mm >> hh:mm:00 else if (cc != 8) return 0; pp = pdate_metadate(pdate); if (! pp) return 0; strncpy0(metadatetime,pp,9); pp = ptime_metatime(ptime); if (! pp) return 0; strncpy0(metadatetime+8,pp,7); return metadatetime; } // Convert metadata date/time "yyyymmddhhmmss" to "yyyy-mm-dd" and "hh:mm:ss" void metadate_pdate(cchar *metadate, char *pdate, char *ptime) { if (*metadate) { memcpy(pdate,metadate,4); // yyyymmdd to yyyy-mm-dd memcpy(pdate+5,metadate+4,2); memcpy(pdate+8,metadate+6,2); pdate[4] = pdate[7] = '-'; pdate[10] = 0; memcpy(ptime,metadate+8,2); // hhmmss to hh:mm:ss memcpy(ptime+3,metadate+10,2); ptime[2] = ':'; ptime[5] = 0; memcpy(ptime+6,metadate+12,2); ptime[5] = ':'; ptime[8] = 0; } else *pdate = *ptime = 0; // missing return; } // validate a date/time string formatted "yyyy-mm-dd hh:mm[:ss]" // valid year is 0000 to 2099 // return 0 if bad, 1 if OK int datetimeOK(char *datetime) // format changed { int monlim[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int cc, year, mon, day, hour, min, sec; cc = strlen(datetime); if (cc != 16 && cc != 19) return 0; if (datetime[4] != '-') return 0; if (datetime[7] != '-') return 0; if (datetime[13] != ':') return 0; if (cc == 19 && datetime[16] != ':') return 0; year = atoi(datetime); mon = atoi(datetime+5); day = atoi(datetime+8); hour = atoi(datetime+11); min = atoi(datetime+14); if (cc == 19) sec = atoi(datetime+17); else sec = 0; if (year < 0 || year > 2099) return 0; if (mon < 1 || mon > 12) return 0; if (day < 1 || day > monlim[mon-1]) return 0; if (mon == 2 && day == 29 && (year % 4)) return 0; if (hour < 0 || hour > 23) return 0; if (min < 0 || min > 59) return 0; if (sec < 0 || sec > 59) return 0; return 1; } /********************************************************************************/ // add input tag to output tag list if not already there and enough room // returns: 0 = added OK 1 = already there (case ignored) // 2 = overflow 3 = bad tag name 4 = null tag int add_tag(char *tag, char *taglist, int maxcc) { char *pp1, *pp2, tag1[tagcc]; int cc, cc1, cc2; if (! tag) return 4; strncpy0(tag1,tag,tagcc); // remove leading and trailing blanks cc = strTrim2(tag1); if (! cc) return 4; if (utf8_check(tag1)) return 3; // look for bad characters if (strpbrk(tag1,",;:")) return 3; strcpy(tag,tag1); pp1 = taglist; cc1 = strlen(tag); while (true) // check if already in tag list { while (*pp1 == ' ' || *pp1 == ',') pp1++; if (! *pp1) break; pp2 = pp1 + 1; while (*pp2 && *pp2 != ',') pp2++; cc2 = pp2 - pp1; if (cc2 == cc1 && strmatchcaseN(tag,pp1,cc1)) return 1; pp1 = pp2; } cc2 = strlen(taglist); // append to tag list if space enough if (cc1 + cc2 + 3 > maxcc) return 2; strcpy(taglist + cc2,tag); strcpy(taglist + cc2 + cc1, ", "); // add delimiter + space if (taglist == meta_tags) Fmetamod++; // image tags were changed return 0; } // add tag "fotoxx" when an edited image file is saved // used by f_save() int add_tag_fotoxx(cchar *file) { char fotoxx[8] = "fotoxx"; add_tag(fotoxx,meta_tags,tagFcc); return 1; } // set image ww/hh metadata for change outside of m_meta_edit_main() // used by file_save() in case file size is changed void set_meta_wwhh(int ww, int hh) { snprintf(meta_wwhh,16,"%dx%d",ww,hh); return; } // remove tag from taglist, if present // returns: 0 if found and deleted, otherwise 1 int del_tag(char *tag, char *taglist) { int ii, ftcc, atcc, found; char *temptags; cchar *pp; temptags = zstrdup(taglist); *taglist = 0; ftcc = found = 0; for (ii = 1; ; ii++) { pp = strField(temptags,",;",ii); // next tag if (! pp) { zfree(temptags); if (found && taglist == meta_tags) Fmetamod++; // image tags were changed return 1-found; } if (*pp == 0) 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, ... // force: read image index and build deftags list void load_deftags(int force) { int tags_Ucomp(cchar *tag1, cchar *tag2); static int Floaded = 0; FILE * fid; xxrec_t *xxrec; int ii, jj, ntags, err, cc, tcc; int ncats, catoverflow; int nocat, nocatcc; char tag[tagcc], catg[tagcc]; char tagsbuff[tagGcc]; char *pp1, *pp2; char ptags[maxtags][tagcc]; // 10000 * 50 = 0.5 MB if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } if (Floaded && ! force) return; // use memory tags if already there Floaded++; for (ii = 0; ii < maxtagcats; ii++) // clean memory tags_deftags[ii] = 0; ncats = catoverflow = 0; fid = fopen(tags_defined_file,"r"); // read tags_defined file if (fid) { while (true) { pp1 = fgets_trim(tagsbuff,tagGcc,fid); if (! pp1) break; pp2 = strchr(pp1,':'); // isolate "category:" if (! pp2) continue; // no colon cc = pp2 - pp1 + 1; if (cc > tagcc-1) continue; // category name too long strncpy0(catg,pp1,cc); // (for error message) if (strlen(pp1) > tagGcc-2) goto cattoobig; // all category tags too long pp2++; while (*pp2 == ' ') pp2++; if (strlen(pp2) < 3) continue; // category with no tags while (*pp2) { if (*pp2 == ';') *pp2 = ','; // replace ';' with ',' for Fotoxx pp2++; } tags_deftags[ncats] = zstrdup(pp1); // tags_deftags[ii] ncats++; // = category: tag1, tag2, ... tagN, if (ncats == maxtagcats) goto toomanycats; } err = fclose(fid); fid = 0; if (err) goto deftagsfilerr; } // sort the categories in ascending order for (ii = 0; ii < ncats; ii++) for (jj = ii+1; jj < ncats; jj++) { pp1 = tags_deftags[ii]; pp2 = tags_deftags[jj]; if (strcasecmp(pp1,pp2) > 0) { tags_deftags[ii] = pp2; tags_deftags[jj] = pp1; } } // move category "nocatg" to the end of the list for (ii = 0; ii < ncats; ii++) { pp1 = tags_deftags[ii]; if (strmatchN(pp1,"nocatg:",7)) { for (jj = ii; jj < ncats-1; jj++) tags_deftags[jj] = tags_deftags[jj+1]; tags_deftags[jj] = pp1; break; } } // if not already there, add category "nocatg" to the end of the list pp1 = 0; if (ncats > 0) pp1 = tags_deftags[ncats-1]; // last tag category if (pp1 && strmatchN(pp1,"nocatg:",7)) { // already 'nocatg' nocat = ncats - 1; nocatcc = strlen(pp1); pp2 = (char *) zmalloc(tagGcc); // re-allocate max. size tags_deftags[nocat] = pp2; // for following phase strcpy(pp2,pp1); zfree(pp1); } else { nocat = ncats; // add to end of list ncats++; tags_deftags[nocat] = (char *) zmalloc(tagGcc); // allocate max. size strcpy(tags_deftags[nocat],"nocatg: "); nocatcc = 8; } // search image index recs for all tags in all images // for tags not found in defined tags list, add to 'nocatg' list for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; pp1 = xxrec->tags; // should never be null while (pp1) // was: while (true) 19.10 { while (*pp1 && strchr(",; ",*pp1)) pp1++; // next image tag start if (! *pp1) break; pp2 = strpbrk(pp1,",;"); // end if (! pp2) pp2 = pp1 + strlen(pp1); cc = pp2 - pp1; if (cc > tagcc-1) { pp1 = pp2; continue; // ignore huge tag } strncpy0(tag,pp1,cc+1); // look for tag in defined tags if (find_deftag(tag)) { pp1 = pp2; // found continue; } if (nocatcc + cc + 2 > tagGcc-2) { catoverflow = 1; // nocatg: length limit reached break; } else { strcpy(tags_deftags[nocat] + nocatcc, tag); // append tag to list nocatcc += cc; strcpy(tags_deftags[nocat] + nocatcc, ", "); // + delim + blank nocatcc += 2; } pp1 = pp2; } } if (catoverflow) goto cattoobig; // parse all the tags in each category and sort in ascending order for (ii = 0; ii < ncats; ii++) { pp1 = tags_deftags[ii]; pp2 = strchr(pp1,':'); if (! pp2) { printz("defined tags file format error: %s \n",pp1); continue; } cc = pp2 - pp1 + 1; strncpy0(catg,pp1,cc); pp1 = pp2 + 1; while (*pp1 == ' ') pp1++; tcc = 0; for (jj = 0; jj < maxtags; jj++) { if (! *pp1) break; pp2 = strchr(pp1,','); if (pp2) cc = pp2 - pp1; else cc = strlen(pp1); if (cc > tagcc-1) cc = tagcc-1; strncpy0(ptags[jj],pp1,cc+1); pp1 += cc + 1; tcc += cc; while (*pp1 == ' ') pp1++; } ntags = jj; if (ntags == maxtags) goto toomanytags; HeapSort((char *) ptags,tagcc,ntags,tags_Ucomp); pp1 = tags_deftags[ii]; tcc += strlen(catg) + 2 + 2 * ntags + 2; // category, all tags, delimiters pp2 = (char *) zmalloc(tcc); tags_deftags[ii] = pp2; // swap memory zfree(pp1); strcpy(pp2,catg); pp2 += strlen(catg); strcpy(pp2,": "); // pp2 = "category: " pp2 += 2; for (jj = 0; jj < ntags; jj++) // add the sorted tags { strcpy(pp2,ptags[jj]); // append tag + delim + blank pp2 += strlen(pp2); strcpy(pp2,", "); pp2 += 2; } *pp2 = 0; } return; toomanycats: zmessageACK(Mwin,"more than %d categories",maxtagcats); if (fid) fclose(fid); return; cattoobig: zmessageACK(Mwin,"category %s is too big",catg); if (fid) fclose(fid); return; toomanytags: zmessageACK(Mwin,"category %s has too many tags",catg); if (fid) fclose(fid); return; deftagsfilerr: zmessageACK(Mwin,"tags_defined file error: %s",strerror(errno)); return; } // compare function for tag sorting int tags_Ucomp(cchar *tag1, cchar *tag2) { return strcasecmp(tag1,tag2); } // write tags_deftags[] memory data to the defined tags file if any changes were made void save_deftags() { int ii, err; FILE *fid; fid = fopen(tags_defined_file,"w"); // write tags_defined file if (! fid) goto deftagserr; for (ii = 0; ii < maxtagcats; ii++) { if (! tags_deftags[ii]) break; err = fprintf(fid,"%s\n",tags_deftags[ii]); // each record: if (err < 0) goto deftagserr; // category: tag1, tag2, ... tagN, } err = fclose(fid); if (err) goto deftagserr; return; deftagserr: zmessageACK(Mwin,"tags_defined file error: %s",strerror(errno)); return; } // find a given tag in tags_deftags[] // return: 1 = found, 0 = not found int find_deftag(char *tag) { int ii, cc; char tag2[tagcc+4]; char *pp; strncpy0(tag2,tag,tagcc); // construct tag + delim + blank cc = strlen(tag2); strcpy(tag2+cc,", "); cc += 2; for (ii = 0; ii < maxtagcats; ii++) { pp = tags_deftags[ii]; // category: tag1, tag2, ... tagN, if (! pp) return 0; // not found while (pp) { pp = strcasestr(pp,tag2); // look for delim + blank + tag + delim if (! pp) break; if (strchr(",;:", pp[-2])) return 1; // cat: tag, or priortag, tag, pp += cc; // | | } // pp pp } return 1; } // add new tag to tags_deftags[] >> category: tag1, tag2, ... newtag, // returns: 0 = added OK 1 = not unique (case ignored) // 2 = overflow 3 = bad name 4 = null/blank tag // if tag present under another category, it is moved to new category int add_deftag(char *catg, char *tag) { int ii, cc, cc1, cc2; char catg1[tagcc], tag1[tagcc]; char *pp1, *pp2; if (! catg) strcpy(catg1,"nocatg"); else strncpy0(catg1,catg,tagcc); cc = strTrim2(catg1); // remove leading and trailing blanks if (! cc) strcpy(catg1,"nocatg"); if (utf8_check(catg1)) goto badcatname; // look for bad characters if (strpbrk(catg1,",;:\"")) goto badcatname; if (! tag) return 4; strncpy0(tag1,tag,tagcc); // remove leading and trailing blanks cc = strTrim2(tag1); if (! cc) return 4; if (utf8_check(tag1)) goto badtagname; // look for bad characters if (strpbrk(tag1,",;:\"")) goto badtagname; del_deftag(tag1); // delete tag if already there cc1 = strlen(catg1); for (ii = 0; ii < maxtagcats; ii++) // look for given category { pp1 = tags_deftags[ii]; if (! pp1) goto newcatg; if (! strmatchN(catg1,pp1,cc1)) continue; // match on "catname:" if (pp1[cc1] == ':') goto oldcatg; } newcatg: if (ii == maxtagcats) goto toomanycats; cc1 = strlen(catg1) + strlen(tag1) + 6; pp1 = (char *) zmalloc(cc1); *pp1 = 0; strncatv(pp1,cc1,catg1,": ",tag1,", ",null); // category: + tag + delim + blank tags_deftags[ii] = tags_deftags[ii-1]; // move "nocatg" record to next slot tags_deftags[ii-1] = pp1; // insert new record before save_deftags(); return 0; oldcatg: // logic simplified pp2 = pp1 + cc1 + 2; // char following "catname: " cc1 = strlen(tag1); cc2 = strlen(pp1); // add new tag to old record if (cc1 + cc2 + 4 > tagGcc) goto cattoobig; pp2 = zstrdup(pp1,cc1+cc2+4); // expand string zfree(pp1); tags_deftags[ii] = pp2; strcpy(pp2+cc2,tag1); // old record + tag + delim + blank strcpy(pp2+cc2+cc1,", "); save_deftags(); return 0; badcatname: zmessageACK(Mwin,"bad category name"); return 3; badtagname: zmessageACK(Mwin,"bad tag name"); return 3; toomanycats: zmessageACK(Mwin,"too many categories"); return 2; cattoobig: zmessageACK(Mwin,"too many tags"); return 2; } // delete tag from defined tags list, tags_deftags[] // return: 0 = found and deleted, 1 = not found int del_deftag(char *tag) { int ii, cc; char tag2[tagcc+4]; char *pp, *pp1, *pp2; if (! tag || *tag <= ' ') return 1; // bad tag strncpy0(tag2,tag,tagcc); // construct tag + delim + blank cc = strlen(tag2); strcpy(tag2+cc,", "); cc += 2; for (ii = 0; ii < maxtagcats; ii++) { pp = tags_deftags[ii]; if (! pp) return 1; // not found while (pp) { pp = strcasestr(pp,tag2); // look for prior delim or colon if (! pp) break; if (strchr(",;:", pp[-2])) goto found; // cat: tag, or priortag, tag, pp += cc; // | | } // pp pp } found: for (pp1 = pp, pp2 = pp+cc; *pp2; pp1++, pp2++) // eliminate tag, delim, blank *pp1 = *pp2; *pp1 = 0; return 0; } // Stuff text widget "deftags" with all tags in the given category. // If category "ALL", stuff all tags and format by category. void deftags_stuff(zdialog *zd, cchar *acatg) { GtkWidget *widget; int ii, ff, cc; char catgname[tagcc+4]; char *pp1, *pp2; widget = zdialog_widget(zd,"deftags"); textwidget_clear(widget); for (ii = 0; ii < maxtagcats; ii++) { pp1 = tags_deftags[ii]; if (! pp1) break; pp2 = strchr(pp1,':'); if (! pp2) continue; cc = pp2 - pp1; if (cc < 1) continue; if (cc > tagcc) continue; strncpy0(catgname,pp1,cc+1); if (! strmatch(acatg,"ALL")) { ff = strmatch(catgname,acatg); if (! ff) continue; } strcat(catgname,": "); textwidget_append(widget,1,catgname); // "category: " in bold text pp2++; if (*pp2 == ' ') pp2++; if (*pp2) textwidget_append(widget,0,pp2); // "cat1, cat2, ... catN," textwidget_append(widget,0,"\n"); } return; } // Stuff combo box "defcats" with "ALL" + all defined categories void defcats_stuff(zdialog *zd) { char catgname[tagcc+2]; int ii, cc; char *pp1, *pp2; zdialog_cb_clear(zd,"defcats"); zdialog_cb_app(zd,"defcats","ALL"); for (ii = 0; ii < maxtagcats; ii++) { pp1 = tags_deftags[ii]; if (! pp1) break; pp2 = strchr(pp1,':'); if (! pp2) continue; cc = pp2 - pp1; if (cc < 1) continue; if (cc > tagcc) continue; strncpy0(catgname,pp1,cc+1); zdialog_cb_app(zd,"defcats",catgname); } return; } // report tags defined and not used in any image file void tag_orphans(GtkWidget *parent) { FILE *fid; xxrec_t *xxrec; int ii, jj, cc; int Ndeftags; char **deftags; char usedtag[tagcc], tagsbuff[tagGcc]; char *pp1, *pp2; zdialog *zd2; deftags = (char **) zmalloc(maxtags * sizeof(char *)); // allocate memory Ndeftags = 0; fid = fopen(tags_defined_file,"r"); // read tags_defined file if (fid) { while (true) { pp1 = fgets_trim(tagsbuff,tagGcc,fid); if (! pp1) break; pp1 = strchr(pp1,':'); // skip over "category:" if (! pp1) continue; cc = pp1 - tagsbuff; if (cc > tagcc) continue; // reject bad data (manual edit?) pp1++; for (ii = 1; ; ii++) { // get tags: tag1, tag2, ... pp2 = (char *) strField(pp1,",;",ii); if (! pp2) break; if (strlen(pp2) < 3) continue; // reject bad data if (strlen(pp2) > tagcc) continue; deftags[Ndeftags] = zstrdup(pp2); Ndeftags++; } } fclose(fid); } for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; pp1 = xxrec->tags; // image tags if (! pp1) continue; // should not happen while (true) { while (*pp1 && strchr(",; ",*pp1)) pp1++; // next image tag start if (! *pp1) break; pp2 = strpbrk(pp1,",;"); // end if (! pp2) pp2 = pp1 + strlen(pp1); cc = pp2 - pp1; if (cc > tagcc-1) { pp1 = pp2; continue; // ignore huge tag } strncpy0(usedtag,pp1,cc+1); // used tag, without delimiter for (jj = 0; jj < Ndeftags; jj++) // find in defined tags if (strmatch(usedtag,deftags[jj])) break; if (jj < Ndeftags) { // found zfree(deftags[jj]); Ndeftags--; while (jj < Ndeftags) { // defined tag is in use deftags[jj] = deftags[jj+1]; // remove from list and pack down jj++; } } pp1 = pp2; } } zd2 = popup_report_open("unused tags",parent,200,200,0,0,0); for (ii = 0; ii < Ndeftags; ii++) popup_report_write(zd2,0,"%s \n",deftags[ii]); popup_report_write(zd2,0,"%d unused tags \n",Ndeftags); for (ii = 0; ii < Ndeftags; ii++) zfree(deftags[ii]); zfree(deftags); return; } /********************************************************************************/ // image file EXIF/IPTC data >> memory data: // meta_pdate, meta_rating, meta_tags, meta_comments, meta_caption, // meta_location, meta_country, meta_lati, meta_longi void load_filemeta(cchar *file) { int ii, jj, cc, nkey; int ww, hh; char *pp; cchar *exifkeys[100] = { exif_date_key, iptc_keywords_key, iptc_rating_key, exif_ww_key, exif_hh_key, // replace exif_wwhh_key 19.15 exif_comment_key, iptc_caption_key, exif_city_key, exif_country_key, exif_lati_key, exif_longi_key }; char *ppv[100], *imagedate, *imagetags, *imagestars; char *imageww, *imagehh, *imagecomms, *imagecapt; char *imageloc, *imagecountry, *imagelati, *imagelongi; strncpy0(p_meta_pdate,meta_pdate,15); // save prior metadata for use by strncpy0(p_meta_rating,meta_rating,4); // edit_metadata [Prev] button strncpy0(p_meta_tags,meta_tags,tagFcc); strncpy0(p_meta_caption,meta_caption,exif_maxcc); strncpy0(p_meta_comments,meta_comments,exif_maxcc); strncpy0(p_meta_location,meta_location,100); strncpy0(p_meta_country,meta_country,100); strncpy0(p_meta_lati,meta_lati,20); strncpy0(p_meta_longi,meta_longi,20); *meta_tags = *meta_pdate = *meta_comments = *meta_caption = 0; strcpy(meta_rating,"0"); *meta_location = *meta_country = *meta_lati = *meta_longi = 0; nkey = 11; // add keys for indexed metadata for (ii = 0; ii < Mxmeta; ii++) { // from exifkeys[11]; if (! xmeta_keys[ii]) break; exifkeys[nkey] = xmeta_keys[ii]; nkey++; } exif_get(file,exifkeys,ppv,nkey); // get metadata from image file imagedate = ppv[0]; imagetags = ppv[1]; imagestars = ppv[2]; imageww = ppv[3]; imagehh = ppv[4]; imagecomms = ppv[5]; imagecapt = ppv[6]; imageloc = ppv[7]; imagecountry = ppv[8]; imagelati = ppv[9]; imagelongi = ppv[10]; if (imagedate) { exif_tagdate(imagedate,meta_pdate); // EXIF date/time >> yyyymmddhhmmss zfree(imagedate); } if (imagetags) { for (ii = 1; ; ii++) { pp = (char *) strField(imagetags,",;",ii); if (! pp) break; if (*pp == 0) 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(imagetags); } 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); } strcpy(meta_wwhh,"no data"); if (imageww && imagehh) { // 19.15 convSI(imageww,ww); convSI(imagehh,hh); if (ww > 0 && hh > 0) snprintf(meta_wwhh,15,"%dx%d",ww,hh); } if (imageww) zfree(imageww); if (imagehh) zfree(imagehh); if (imagecomms) { strncpy0(meta_comments,imagecomms,exif_maxcc); zfree(imagecomms); } if (imagecapt) { strncpy0(meta_caption,imagecapt,exif_maxcc); zfree(imagecapt); } if (imageloc) { // geotags strncpy0(meta_location,imageloc,100); zfree(imageloc); } else strcpy(meta_location,""); // replace missing data with "" if (imagecountry) { strncpy0(meta_country,imagecountry,100); zfree(imagecountry); } else strcpy(meta_country,""); if (imagelati) { strncpy0(meta_lati,imagelati,12); zfree(imagelati); } else strcpy(meta_lati,""); if (imagelongi) { strncpy0(meta_longi,imagelongi,12); zfree(imagelongi); } else strcpy(meta_longi,""); for (ii = 0; ii < Mxmeta; ii++) { // get indexed metadata if any if (! xmeta_keys[ii]) break; if (xmeta_data[ii]) zfree(xmeta_data[ii]); if (ppv[ii+11]) xmeta_data[ii] = ppv[ii+11]; else xmeta_data[ii] = zstrdup(""); } Fmetamod = 0; // no pending changes return; } // add metadata in memory to image file EXIF/IPTC data and image_index recs. void save_filemeta(cchar *file) { cchar *exifkeys[100] = { exif_date_key, iptc_keywords_key, iptc_rating_key, exif_comment_key, iptc_caption_key, exif_city_key, exif_country_key, exif_lati_key, exif_longi_key }; int nkey, ii, err; cchar *exifdata[100]; char imagedate[24]; err = access(file,W_OK); // test file can be written by me 19.1 if (err) { zmessageACK(Mwin,"%s: %s \n",Bnowriteperm,file); return; } *imagedate = 0; if (*meta_pdate) tag_exifdate(meta_pdate,imagedate); // yyyymmddhhmmss >> EXIF date/time exifdata[0] = imagedate; // update file EXIF/IPTC data exifdata[1] = meta_tags; exifdata[2] = meta_rating; exifdata[3] = meta_comments; exifdata[4] = meta_caption; exifdata[5] = meta_location; // if "" erase EXIF exifdata[6] = meta_country; if (strmatch(meta_lati,"") || strmatch(meta_longi,"")) exifdata[7] = exifdata[8] = ""; else { exifdata[7] = meta_lati; exifdata[8] = meta_longi; } nkey = 9; // add keys for indexed metadata for (ii = 0; ii < Mxmeta; ii++) { // from exifkeys[9]; if (! xmeta_keys[ii]) break; exifkeys[nkey] = xmeta_keys[ii]; // missing data ("") will be erased 20.0 exifdata[nkey] = xmeta_data[ii]; nkey++; } exif_put(file,exifkeys,exifdata,nkey); // write EXIF update_image_index(file); // update image index file if (zd_metaview) meta_view(0); // live EXIF/IPTC update Fmetamod = 0; // no pending changes return; } // update image index record (replace updated file data) // meta_xxxx data in memory >> image index record void update_image_index(cchar *file) { int ii, err, xcc; int nn, ww, hh; char xmetarec[1000]; xxrec_t xxrec; STATB statb; err = stat(file,&statb); if (err) { zmessageACK(Mwin,Bfilenotfound); return; } memset(&xxrec,0,sizeof(xxrec_t)); // new metadata record to make xxrec.file = (char *) file; // image filespec compact_time(statb.st_mtime,xxrec.fdate); // convert file date to "yyyymmddhhmmss" strncpy0(xxrec.pdate,meta_pdate,15); // photo date, "yyyymmddhhmmss" xxrec.rating[0] = meta_rating[0]; // rating '0' to '5' stars xxrec.rating[1] = 0; // make string "0" to "5" nn = sscanf(meta_wwhh,"%dx%d",&ww,&hh); if (nn == 2) { xxrec.ww = ww; // pixel dimensions 19.0 xxrec.hh = hh; } xxrec.fsize = statb.st_size; // file size, bytes 19.0 xxrec.tags = meta_tags; xxrec.capt = meta_caption; xxrec.comms = meta_comments; xxrec.location = meta_location; xxrec.country = meta_country; if (strmatch(meta_lati,"") || strmatch(meta_longi,"")) xxrec.flati = xxrec.flongi = 0; else { xxrec.flati = atof(meta_lati); xxrec.flongi = atof(meta_longi); if (xxrec.flati < -90.0 || xxrec.flati > 90.0) xxrec.flati = xxrec.flongi = 0; if (xxrec.flongi < -180.0 || xxrec.flongi > 180.0) xxrec.flati = xxrec.flongi = 0; } xcc = 0; for (ii = 0; ii < Mxmeta; ii++) { // add indexed metadata if any if (! xmeta_keys[ii]) break; if (strlen(xmeta_keys[ii]) + strlen(xmeta_data[ii]) > 100) continue; // impractical for image search strcpy(xmetarec+xcc,xmeta_keys[ii]); // construct series xcc += strlen(xmeta_keys[ii]); // "keyname=keydata^ " xmetarec[xcc++] = '='; strcpy(xmetarec+xcc,xmeta_data[ii]); xcc += strlen(xmeta_data[ii]); strcpy(xmetarec+xcc,"^ "); xcc += 2; if (xcc > 895) { printz("file metadata exceeds record size: %s \n",file); break; } } if (xcc > 0) xxrec.xmeta = zstrdup(xmetarec); else xxrec.xmeta = zstrdup(""); put_xxrec(&xxrec,file); // update image index gallery(file,"update",0); // update gallery record return; } // delete given image file from image index recs. void delete_image_index(cchar *file) { put_xxrec(null,file); return; } /********************************************************************************/ // Convert a location [country] to earth coordinates via MapQuest geocoding service. // (incomplete names may be completed with a bad guess) cchar * web_geocode(zdialog *zd) { int err; static char lati[20], longi[20]; char outfile[100], URI[300]; char *pp1, *pp2, buffer[200]; char location[100], country[100]; float flati, flongi; FILE *fid; cchar *notfound = E2X("not found"); cchar *badinputs = E2X("location and country required"); cchar *query = "http://open.mapquestapi.com/geocoding/v1/address?" "&key=Fmjtd%7Cluub2qa72d%2C20%3Do5-9u700a" "&maxResults=1" "&outFormat=csv"; zdialog_fetch(zd,"location",location,100); // get zdialog inputs zdialog_fetch(zd,"country",country,100); if (*location <= ' ' || *country <= ' ') return badinputs; *location = toupper(*location); // capitalize *country = toupper(*country); snprintf(outfile,100,"%s/web-data",temp_folder); snprintf(URI,299,"\"%s&location=%s,%s\"",query,location,country); err = shell_quiet("wget -T 10 -o /dev/null -O %s %s",outfile,URI); if (err) return strerror(err); fid = fopen(outfile,"r"); // get response if (! fid) return notfound; pp1 = fgets(buffer,200,fid); pp1 = fgets(buffer,200,fid); fclose(fid); if (! pp1) return notfound; printz("web geocode: %s \n",buffer); pp2 = (char *) strField(pp1,",",4); // look for returned location name if (! pp2 || ! *pp2) return notfound; pp2 = (char *) strField(pp1,",",7); if (! pp2 || ! *pp2) return notfound; strncpy0(lati,pp2,20); pp2 = (char *) strField(pp1,",",8); if (! pp2 || ! *pp2) return notfound; strncpy0(longi,pp2,20); err = validate_latlong(lati,longi,flati,flongi); if (err) return notfound; pp1 = strchr(lati,'.'); // keep max. 4 decimal digits if (pp1) *(pp1+5) = 0; pp1 = strchr(longi,'.'); if (pp1) *(pp1+5) = 0; zdialog_stuff(zd,"lati",lati); // stuff output to zdialog zdialog_stuff(zd,"longi",longi); return 0; } /********************************************************************************/ // Initialize for geotag functions. // Load geolocations data into memory from image index table. // Returns no. geolocations loaded. int init_geolocs() { char location[100], country[100]; float flati, flongi; double time0, time1; int cc, ii, jj; xxrec_t *xxrec; if (Ngeolocs) return Ngeolocs; // already done if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return 0; } time0 = get_seconds(); Ffuncbusy = 1; cc = (Nxxrec+1) * sizeof(geolocs_t *); // get memory for geolocs table geolocs = (geolocs_t **) zmalloc(cc); // room for Nxxrec entries geolocs[0] = (geolocs_t *) zmalloc(sizeof(geolocs_t)); // add entry for location "" (missing) geolocs[0]->location = zstrdup(""); geolocs[0]->country = zstrdup(""); geolocs[0]->flati = 0; geolocs[0]->flongi = 0; Ngeolocs = 1; // populate geolocs from image index table for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { xxrec = xxrec_tab[ii]; strncpy0(location,xxrec->location,100); strncpy0(country,xxrec->country,100); flati = xxrec->flati; flongi = xxrec->flongi; if (! *location) strcpy(location,"null"); // geocoordinates but no location 20.0 if (! *country) strcpy(country,"null"); if (Ngeolocs) { jj = Ngeolocs - 1; // eliminate sequential duplicates if (strmatch(location,geolocs[jj]->location) && strmatch(country,geolocs[jj]->country) && flati == geolocs[jj]->flati && flongi == geolocs[jj]->flongi) continue; } jj = Ngeolocs++; // fill next entry in table geolocs[jj] = (geolocs_t *) zmalloc(sizeof(geolocs_t)); geolocs[jj]->location = zstrdup(location); geolocs[jj]->country = zstrdup(country); geolocs[jj]->flati = flati; geolocs[jj]->flongi = flongi; } if (Ngeolocs > 1) HeapSort((char **) geolocs, Ngeolocs, geolocs_compare); // sort for (ii = 0, jj = 1; jj < Ngeolocs; jj++) // eliminate duplicates { if (strmatch(geolocs[jj]->location,geolocs[ii]->location) && strmatch(geolocs[jj]->country,geolocs[ii]->country) && geolocs[jj]->flati == geolocs[ii]->flati && geolocs[jj]->flongi == geolocs[ii]->flongi) { zfree(geolocs[jj]->country); // free redundant entries zfree(geolocs[jj]->location); zfree(geolocs[jj]); } else { ii++; // count unique entries if (ii < jj) geolocs[ii] = geolocs[jj]; // pack down the table } } Ngeolocs = ii + 1; // final geolocs table size time1 = get_seconds() - time0; Ffuncbusy = 0; printz("%d images, %d locations %.3f secs. \n",Nxxrec,Ngeolocs,time1); return Ngeolocs; } // Compare 2 geolocs records by country, location, latitude, longitude // return <0 0 >0 for rec1 < == > rec2. int geolocs_compare(cchar *rec1, cchar *rec2) { float diff; int ii; geolocs_t *r1 = (geolocs_t *) rec1; geolocs_t *r2 = (geolocs_t *) rec2; ii = strcmp(r1->country,r2->country); if (ii) return ii; ii = strcmp(r1->location,r2->location); if (ii) return ii; if (r1->flati == 0 && r1->flongi == 0) { // sort missing lat/long last if (r2->flati == 0 && r2->flongi == 0) return 0; else return +1; } diff = r1->flati - r2->flati; if (diff < 0) return -1; if (diff > 0) return +1; diff = r1->flongi - r2->flongi; if (diff < 0) return -1; if (diff > 0) return +1; return 0; } /********************************************************************************/ // find a geolocation from partial zdialog inputs and user choice of options // zdialog widgets: location, country, lati, longi // location and country are inputs (may be partial leading strings) // all four widgets are outputs (found location and geocoordinates) int find_location(zdialog *zd) { int find_location_dialog_event(zdialog *zd, cchar *event); zdialog *zd2; cchar *pp; GtkWidget *parent; int cc, ii, jj, kk, Nmatch, zstat, zoomlev; int flocation = 0, fcountry = 0; char location[100], country[100], text[200]; char lati[20], longi[20], *matches[20][2]; float flati1 = 999, flati2 = -999; float flongi1 = 999, flongi2 = -999; float flatic, flongic, kmrange, fmpp; init_geolocs(); // if not already zdialog_fetch(zd,"location",location,100); // get dialog inputs zdialog_fetch(zd,"country",country,100); strTrim2(location); strTrim2(country); if (*location) flocation = 1; // one of these must be present if (*country) fcountry = 1; if (! flocation && ! fcountry) return 0; for (ii = Nmatch = 0; ii < Ngeolocs; ii++) // search for exact location match { if (flocation && ! strmatchcase(location,geolocs[ii]->location)) continue; if (fcountry && ! strmatchcase(country,geolocs[ii]->country)) continue; strncpy0(location,geolocs[ii]->location,100); // save matching location strncpy0(country,geolocs[ii]->country,100); goto found_location; } for (ii = kk = Nmatch = 0; ii < Ngeolocs; ii++) // search for partial location match { if (flocation) { cc = strlen(location); if (! strmatchcaseN(location,geolocs[ii]->location,cc)) continue; } if (fcountry) { cc = strlen(country); if (! strmatchcaseN(country,geolocs[ii]->country,cc)) continue; } for (jj = 0; jj < Nmatch; jj++) // reject duplicate match { if (strmatch(geolocs[ii]->location,matches[jj][0]) && (strmatch(geolocs[ii]->country,matches[jj][1]))) break; } if (jj < Nmatch) continue; matches[Nmatch][0] = geolocs[ii]->location; // save match matches[Nmatch][1] = geolocs[ii]->country; if (Nmatch == 20) return 0; // >20 matches >> no match Nmatch++; // count matches if (Nmatch == 1) kk = ii; // note first match } if (Nmatch == 0) return 0; // no matches if (Nmatch == 1) { // one match strncpy0(location,geolocs[kk]->location,100); // save matching location strncpy0(country,geolocs[kk]->country,100); goto found_location; } parent = zd->widget[0].widget; zd2 = zdialog_new(E2X("choose location"),parent,Bdone,Bcancel,null); // multiple matches, start dialog zdialog_add_widget(zd2,"comboE","locations","dialog",0,"space=5"); for (ii = 0; ii < Nmatch; ii++) { // list locations to choose from snprintf(text,200,"%s | %s",matches[ii][0],matches[ii][1]); zdialog_cb_app(zd2,"locations",text); } zdialog_resize(zd2,300,100); zdialog_set_modal(zd2); zdialog_run(zd2,find_location_dialog_event,"mouse"); // run dialog, wait for completion zdialog_cb_popup(zd2,"locations"); // open combo box list zstat = zdialog_wait(zd2); if (zstat != 1) { // no choice made zdialog_free(zd2); return 0; } zdialog_fetch(zd2,"locations",text,200); pp = strField(text,'|',1); if (pp) strncpy0(location,pp,100); // user choice, location and country pp = strField(text,'|',2); if (pp) strncpy0(country,pp,100); strTrim2(location); strTrim2(country); zdialog_free(zd2); found_location: zdialog_stuff(zd,"location",location); // return location data to zdialog zdialog_stuff(zd,"country",country); for (ii = 0; ii < Ngeolocs; ii++) // search for location & country { if (strmatchcase(location,geolocs[ii]->location) && strmatchcase(country,geolocs[ii]->country)) { if (geolocs[ii]->flati == 0 && geolocs[ii]->flongi == 0) continue; // ignore missing values if (geolocs[ii]->flati < flati1) flati1 = geolocs[ii]->flati; // save range of geocoordinates found if (geolocs[ii]->flati > flati2) flati2 = geolocs[ii]->flati; if (geolocs[ii]->flongi < flongi1) flongi1 = geolocs[ii]->flongi; if (geolocs[ii]->flongi > flongi2) flongi2 = geolocs[ii]->flongi; } } if (flati1 == 999) { // no match, return nulls zdialog_stuff(zd,"lati",""); zdialog_stuff(zd,"longi",""); return 0; } if (flati1 == flati2 && flongi1 == flongi2) { // one match, return geocoordinates snprintf(lati,20,"%.4f",flati1); // reformat with std. precision snprintf(longi,20,"%.4f",flongi1); zdialog_stuff(zd,"lati",lati); zdialog_stuff(zd,"longi",longi); return 1; } flatic = 0.5 * (flati1 + flati2); // multiple matches flongic = 0.5 * (flongi1 + flongi2); // center of enclosing rectangle kmrange = earth_distance(flati1,flongi1,flati2,flongi2); // length of diagonal if (kmrange > 100) kmrange = 100; for (zoomlev = 12; zoomlev < 20; zoomlev++) // loop small to large scale { fmpp = netmapscale(zoomlev,flatic,flongic); // meters per pixel at zoom level fmpp = 0.001 * fmpp * 100.0; // km span of 100 pixels if (fmpp < kmrange) break; // stop when kmrange > 100 pixels } netmap_zoomto(flatic,flongic,zoomlev); // map click --> stuff zdialog lat/long return 0; } // dialog event function - get chosen location/country from multiple choices int find_location_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"locations")) zd->zstat = 1; return 1; } /********************************************************************************/ // Update geolocations table geolocs[*] // // inputs: location, country, latitude, longitude // return value: 0 OK, no geotag revision (incomplete data) // 1 OK, no geotag revision (matches existing data) // 2 OK, geotag new location/lati/longi added // -1 error, lat/long bad int put_geolocs(zdialog *zd) { char location[100], country[100]; char lati[20], longi[20]; float flati, flongi; int ii, err, cc, nn, found = 0; zdialog_fetch(zd,"location",location,100); // get location and geocoordinates zdialog_fetch(zd,"country",country,100); strTrim2(location); strTrim2(country); if (! *location || ! *country) return 0; // location not complete *location = toupper(*location); // capitalize *country = toupper(*country); zdialog_stuff(zd,"location",location); zdialog_stuff(zd,"country",country); zdialog_fetch(zd,"lati",lati,20); zdialog_fetch(zd,"longi",longi,20); strTrim2(lati); strTrim2(longi); if (! *lati && ! *longi) return 0; // geocoordinates not complete 20.0 err = validate_latlong(lati,longi,flati,flongi); if (err) { // 1 = missing, 2 = bad zmessageACK(Mwin,E2X("bad latitude/longitude: %s %s"),lati,longi); return -1; } else { snprintf(lati,20,"%.4f",flati); // reformat with std. precision snprintf(longi,20,"%.4f",flongi); } for (ii = 0; ii < Ngeolocs; ii++) // search geotags for location { if (! strmatchcase(location,geolocs[ii]->location)) continue; // case-insensitive compare if (! strmatchcase(country,geolocs[ii]->country)) continue; if (! strmatch(location,geolocs[ii]->location)) { zfree(geolocs[ii]->location); // revise capitalization geolocs[ii]->location = zstrdup(location); } if (! strmatch(country,geolocs[ii]->country)) { zfree(geolocs[ii]->country); geolocs[ii]->country = zstrdup(country); } if (flati == geolocs[ii]->flati && flongi == geolocs[ii]->flongi) found++; } if (found) return 1; geolocs_t *geolocsA = (geolocs_t *) zmalloc(sizeof(geolocs_t)); geolocs_t **geolocsB; geolocsA->location = zstrdup(location); // new geolocs record geolocsA->country = zstrdup(country); geolocsA->flati = flati; geolocsA->flongi = flongi; cc = (Ngeolocs + 1) * sizeof(geolocs_t *); geolocsB = (geolocs_t **) zmalloc(cc); for (ii = 0; ii < Ngeolocs; ii++) { // copy geolocs before new geoloc nn = geolocs_compare((cchar *) geolocs[ii], (cchar *) geolocsA); if (nn > 0) break; geolocsB[ii] = geolocs[ii]; } geolocsB[ii] = geolocsA; // insert new geolocs for (NOP; ii < Ngeolocs; ii++) // copy geolocs after new geoloc geolocsB[ii+1] = geolocs[ii]; zfree(geolocs); // geolocs --> new table geolocs = geolocsB; Ngeolocs += 1; return 2; } /********************************************************************************/ // validate and convert earth coordinates, latitude and longitude // return: 0 OK // 1 both are missing ("") // 2 invalid data // if status is > 0, 0.0 is returned for both values int validate_latlong(char *lati, char *longi, float &flati, float &flongi) { int err; char *pp; if (! *lati && ! *longi) goto status1; // both missing if (! *lati || ! *longi) goto status2; // one missing pp = strchr(lati,','); // replace comma decimal point if (pp) *pp = '.'; // with period pp = strchr(longi,','); if (pp) *pp = '.'; err = convSF(lati,flati,-90,+90); // convert to float and check limits if (err) goto status2; err = convSF(longi,flongi,-180,+180); if (err) goto status2; if (flati == 0.0 && flongi == 0.0) goto status2; // reject both = 0.0 return 0; status1: flati = flongi = 0.0; // both missing return 1; status2: // one missing or invalid flati = flongi = 0.0; return 2; } /********************************************************************************/ // compute the km distance between two earth coordinates float earth_distance(float lat1, float long1, float lat2, float long2) { float dlat, dlong, mlat, dist; dlat = fabsf(lat2 - lat1); // latitude distance dlong = fabsf(long2 - long1); // longitude distance mlat = 0.5 * (lat1 + lat2); // mean latitude mlat *= 0.01745; // radians dlong = dlong * cosf(mlat); // longitude distance * cos(latitude) dist = sqrtf(dlat * dlat + dlong * dlong); // distance in degrees dist *= 111.0; // distance in km return dist; } /********************************************************************************/ // generate a list of files and geocoordinates from the current gallery file list int get_gallerymap() { int ii, jj, cc; xxrec_t *xxrec; if (! navi::Nfiles) { // bugfix zmessageACK(Mwin,E2X("gallery is empty")); return 0; } if (gallerymap) { // free prior gallerymap for (ii = 0; ii < Ngallerymap; ii++) zfree(gallerymap[ii].file); zfree(gallerymap); gallerymap = 0; } cc = sizeof(gallerymap_t); gallerymap = (gallerymap_t *) zmalloc(navi::Nfiles * cc); for (jj = 0, ii = navi::Nfolders; ii < navi::Nfiles; ii++) // loop gallery files { xxrec = get_xxrec(navi::GFlist[ii].file); // look up in xxrec_tab if (! xxrec) continue; gallerymap[jj].flati = xxrec->flati; gallerymap[jj].flongi = xxrec->flongi; gallerymap[jj].file = zstrdup(navi::GFlist[ii].file); jj++; } Ngallerymap = jj; return Ngallerymap; } /********************************************************************************/ // choose to mark map locations for all images or current gallery only void m_set_map_markers(GtkWidget *, cchar *) { zdialog *zd; int zstat, showall = 0; F1_help_topic = "markers"; if (FGWM != 'W' && FGWM != 'M') m_viewmode(0,"?"); // set last used W/M mode 19.0 /*** _____________________________ | Set Map Markers | | | | (o) mark all image files | | (o) mark current gallery | | | | [apply] | |_____________________________| ***/ zd = zdialog_new(E2X("Set Map Markers"),Mwin,Bapply,null); zdialog_add_widget(zd,"radio","all","dialog",E2X("mark all image files")); zdialog_add_widget(zd,"radio","gallery","dialog",E2X("mark current gallery")); zdialog_stuff(zd,"all",1); zdialog_stuff(zd,"gallery",0); zdialog_restore_inputs(zd); zdialog_resize(zd,200,0); zdialog_set_modal(zd); zdialog_run(zd,null,"mouse"); zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); return; } zdialog_fetch(zd,"all",showall); // show all images zdialog_free(zd); if (showall) { if (gallerymap) { // free gallerymap for (int ii = 0; ii < Ngallerymap; ii++) zfree(gallerymap[ii].file); zfree(gallerymap); gallerymap = 0; } } else get_gallerymap(); // show gallery images only if (FGWM == 'W') Fpaint2(); if (FGWM == 'M') netmap_paint_dots(); return; } /********************************************************************************/ // Map Functions for local file maps (W view) // Maps of any scale can be user-installed. // Mercator projection is assumed (but unimportant for maps < 100 km). namespace filemap { char mapname[100]; int mapww, maphh; // map width, height float mflati[2]; // latitude range, low - high float mflongi[2]; // longitude range, low - high } int filemap_position(float flati, float flongi, int &mx, int &my); // earth coordinates > map position int filemap_coordinates(int mx, int my, float &flati, float &flongi); // map position > earth coordinates void find_filemap_images(float flati, float flongi); // find images within range of geolocation /********************************************************************************/ // load the default world map or a map chosen by the user void m_load_filemap(GtkWidget *, cchar *menu) { using namespace filemap; int load_filemap_dialog_event(zdialog *zd, cchar *event); char mapindex[200], mapfile[200]; char buff[200]; cchar *pp; zdialog *zd; int err, zstat; FILE *fid; float flati1, flati2, flongi1, flongi2; STATB statb; F1_help_topic = "file map"; if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } if (Findexvalid == 1) zmessage_post_bold(Mwin,"20/10",2,Boldindex); // warn, index missing new files if (! init_geolocs()) return; // insure geolocations are loaded m_viewmode(0,"W"); // set W view mode 19.0 snprintf(mapindex,200,"%s/maps_index",maps_folder); // check map index file exists err = stat(mapindex,&statb); // (from fotoxx-maps package) if (err) goto nomapsinstalled; if (menu && strmatch(menu,"default")) { strcpy(mapname,"World.jpg"); // use default world map goto load_map; } fid = fopen(mapindex,"r"); // open map index file if (! fid) goto nomapsinstalled; F1_help_topic = "choose map"; zd = zdialog_new(E2X("choose map file"),Mwin,Bcancel,null); // start map chooser dialog zdialog_add_widget(zd,"combo","mapname","dialog",0,"space=5"); while (true) { pp = fgets_trim(buff,200,fid,1); // get map file names if (! pp) break; pp = strField(pp,",",1); if (! pp) continue; zdialog_cb_app(zd,"mapname",pp); // add to dialog popup list } fclose(fid); snprintf(mapindex,200,"%s/maps_index",user_maps_folder); // look for user map index file err = stat(mapindex,&statb); if (err) goto choose_worldmap; fid = fopen(mapindex,"r"); // open user map index if (fid) { while (true) { pp = fgets_trim(buff,200,fid,1); // get map file names if (! pp) break; pp = strField(pp,",",1); if (! pp) continue; zdialog_cb_app(zd,"mapname",pp); // add to dialog popup list } fclose(fid); } choose_worldmap: if (*mapname && Wstate.fpxb) // show current map if any zdialog_stuff(zd,"mapname",mapname); zdialog_resize(zd,300,100); zdialog_set_modal(zd); zdialog_run(zd,load_filemap_dialog_event,"mouse"); // run dialog, get user choice zdialog_set_decorated(zd,0); // 20.0 zdialog_cb_popup(zd,"mapname"); // make the list pop-up 20.0 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_folder,mapname); // check map file exists err = stat(mapfile,&statb); if (err) { snprintf(mapfile,200,"%s/%s",user_maps_folder,mapname); // check user maps also err = stat(mapfile,&statb); } if (err) goto mapfilemissing; // not found snprintf(mapindex,200,"%s/maps_index",maps_folder); // read map index again fid = fopen(mapindex,"r"); if (! fid) goto nomapsinstalled; while (true) { pp = fgets_trim(buff,200,fid,1); // find chosen map file if (! pp) break; pp = strField(buff,",",1); if (! pp) continue; if (strmatch(pp,mapname)) break; } fclose(fid); if (pp) goto get_lat_long; // found snprintf(mapindex,200,"%s/maps_index",user_maps_folder); // read user map index again fid = fopen(mapindex,"r"); if (! fid) goto nomapsinstalled; while (true) { pp = fgets_trim(buff,200,fid,1); // find chosen map file if (! pp) break; pp = strField(buff,",",1); if (! pp) continue; if (strmatch(pp,mapname)) break; } fclose(fid); if (! pp) goto mapfilemissing; // not found in either index get_lat_long: flati1 = flati2 = flongi1 = flongi2 = 0; pp = strField(buff,",",2); // get map earth coordinates range if (! pp) goto latlongerr; // and verify data OK err = convSF(pp,flati1,-80,+80); if (err) goto latlongerr; pp = strField(buff,",",3); if (! pp) goto latlongerr; err = convSF(pp,flati2,-80,+80); if (err) goto latlongerr; pp = strField(buff,",",4); if (! pp) goto latlongerr; err = convSF(pp,flongi1,-200,+200); if (err) goto latlongerr; pp = strField(buff,",",5); if (! pp) goto latlongerr; err = convSF(pp,flongi2,-200,+200); if (err) goto latlongerr; if (flati2 < flati1 + 0.001) goto latlongerr; // require map range > 100m if (flongi2 < flongi1 + 0.001) goto latlongerr; printz("load filemap: %s \n",mapname); // no errors, commit to load map free_filemap(); // free prior map Ffuncbusy = 1; zmainloop(); Wstate.fpxb = PXB_load(mapfile,1); // load map file (with diagnostic) Ffuncbusy = 0; if (! Wstate.fpxb) return; mapww = Wstate.fpxb->ww; // save map pixel dimensions maphh = Wstate.fpxb->hh; mflati[0] = flati1; // save map earth coordinates range mflati[1] = flati2; mflongi[0] = flongi1; mflongi[1] = flongi2; m_zoom(null,"fit"); // fit window (small image >> 1x) return; nomapsinstalled: zmessageACK(Mwin,E2X("fotoxx-maps package not installed \n" "(see https://kornelix.net)")); return; mapfilemissing: zmessageACK(Mwin,E2X("map file %s is missing"),mapname); return; latlongerr: zmessageACK(Mwin,E2X("map latitude/longitude data unreasonable \n" " %.3f %.3f %.3f %.3f"),flati1,flati2,flongi1,flongi2); return; } // dialog event and completion function int load_filemap_dialog_event(zdialog *zd, cchar *event) { using namespace filemap; zdialog_fetch(zd,"mapname",mapname,100); if (! *mapname) return 1; // 20.0 if (strmatch(event,"mapname")) zd->zstat = 1; return 1; } /********************************************************************************/ // Convert latitude and longitude into map position px/py. // Return 0 if OK, +N if error (off the map). int filemap_position(float flati, float flongi, int &px, int &py) { using namespace filemap; float flati1, flati2, flongi1, flongi2; float zww, qy, qy2; flati1 = mflati[0]; // map latitude low - high range flati2 = mflati[1]; flongi1 = mflongi[0]; // map longitude low - high range flongi2 = mflongi[1]; px = py = 0; if (flati < flati1 || flati >= flati2) return 1; // flati/flongi outside map limits if (flongi < flongi1 || flongi >= flongi2) return 1; px = (flongi - flongi1) / (flongi2 - flongi1) * mapww; // return px position zww = mapww * 360.0 / (flongi2 - flongi1); // width for -180 to +180 longitude flati1 = flati1 / RAD; // convert to radians flati2 = flati2 / RAD; flati = flati / RAD; qy2 = (zww/2/PI) * (log(tan(flati2/2 + PI/4))); // flati2 distance from equator qy = (zww/2/PI) * (log(tan(flati/2 + PI/4))); // flati distance from equator py = qy2 - qy; // return py position if (px < 2 || px > mapww-3) return 1; // out of bounds if (py < 2 || py > maphh-3) return 1; // includes margins for red dot return 0; } // Convert map position px/py into latitude and longitude. // Return 0 if OK, +N if error (off the map). int filemap_coordinates(int px, int py, float &flati, float &flongi) { using namespace filemap; float flati1, flati2, flongi1, flongi2; float zww, qy, qy2; flati = flongi = 0; if (px < 0 || px > mapww) return 1; // px/py outside map size if (py < 0 || py > maphh) return 1; flati1 = mflati[0]; // map latitude low - high range flati2 = mflati[1]; flongi1 = mflongi[0]; // map longitude low - high range flongi2 = mflongi[1]; flongi = flongi1 + (1.0 * px / mapww) * (flongi2 - flongi1); // return longitude zww = mapww * 360.0 / (flongi2 - flongi1); // width for -180 to +180 longitude flati1 = flati1 / RAD; // convert to radians flati2 = flati2 / RAD; qy2 = (zww/2/PI) * (log(tan(flati2/2 + PI/4))); // lat2 distance from equator qy2 = qy2 - py; // py distance from equator qy = fabsf(qy2); flati = 2 * atan(exp(2*PI*qy/zww)) - PI/2; if (qy2 < 0) flati = -flati; flati = flati * RAD; // return latitude return 0; } // paint red dots corresponding to image locations on map void filemap_paint_dots() { int ii, err; int mx, my, dx, dy; float flati, flongi, radius; float plati = 999, plongi = 999; if (! Wstate.fpxb) return; // no map loaded cairo_t *cr = draw_context_create(gdkwin,draw_context); radius = map_dotsize / 2; cairo_set_source_rgb(cr,1,0,0); if (gallerymap) // use gallerymap[] if present { // mark gallery images on map for (ii = 0; ii < Ngallerymap; ii++) { flati = gallerymap[ii].flati; flongi = gallerymap[ii].flongi; if (flati == plati && flongi == plongi) continue; // skip repititions plati = flati; plongi = flongi; err = filemap_position(flati,flongi,mx,my); if (err) continue; dx = Cstate->mscale * mx - Cstate->morgx + Cstate->dorgx; dy = Cstate->mscale * my - Cstate->morgy + Cstate->dorgy; if (dx < 0 || dx > Dww-1) continue; if (dy < 0 || dy > Dhh-1) continue; cairo_arc(cr,dx,dy,radius,0,2*PI); cairo_fill(cr); } } else { for (ii = 0; ii < Ngeolocs; ii++) // mark all image locations on map { flati = geolocs[ii]->flati; flongi = geolocs[ii]->flongi; err = filemap_position(flati,flongi,mx,my); if (err) continue; dx = Cstate->mscale * mx - Cstate->morgx + Cstate->dorgx; dy = Cstate->mscale * my - Cstate->morgy + Cstate->dorgy; if (dx < 0 || dx > Dww-1) continue; if (dy < 0 || dy > Dhh-1) continue; cairo_arc(cr,dx,dy,radius,0,2*PI); cairo_fill(cr); } } draw_context_destroy(draw_context); return; } /********************************************************************************/ // Respond to mouse movement and left clicks on filemap image. // Set longitude and latitude, and location and country. // Show images near clicked location. void filemap_mousefunc() { int err, mx, my, px, py, ii, minii; char *location, *country; float flati, flongi, glati, glongi; float dist, mindist; float mscale = Cstate->mscale; int capturedist = (map_dotsize + 2) / 2; // mouse - marker capture distance int Fusedot = 0; static char *ploc = 0; char text[20]; zdialog *zd = zd_mapgeotags; if (Cstate != &Wstate) return; // view mode not world maps if ((Mxdrag || Mydrag)) return; // pan/scroll - handle normally if (RMclick) return; // zoom - fit window, handle normally if (LMclick && mscale < 1) return; // handle normally if not full size if (! Wstate.fpxb) return; mx = Mxposn; // mouse position, image space my = Myposn; err = filemap_coordinates(mx,my,flati,flongi); if (err) return; // off the map dist = mindist = 999999; minii = 0; for (ii = 0; ii < Ngeolocs; ii++) // find nearest location/country { glati = geolocs[ii]->flati; dist = (flati - glati) * (flati - glati); if (dist > mindist) continue; glongi = geolocs[ii]->flongi; dist += (flongi - glongi) * (flongi - glongi); // degrees**2 if (dist > mindist) continue; mindist = dist; minii = ii; } ii = minii; glati = geolocs[ii]->flati; // closest known place glongi = geolocs[ii]->flongi; location = geolocs[ii]->location; country = geolocs[ii]->country; err = filemap_position(glati,glongi,px,py); // corresp. map image position dist = sqrtf((px-mx) * (px-mx) + (py-my) * (py-my)); dist = dist * mscale; // (mouse - map) in pixels if (dist <= capturedist) Fusedot = 1; // mouse is within marker dot if (LMclick) // left mouse click { LMclick = 0; if (zd) // stuff calling dialog { if (Fusedot) { // click within dot zdialog_stuff(zd,"location",location); // use dot location data zdialog_stuff(zd,"country",country); zdialog_stuff(zd,"lati",glati,"%.5f"); // 5 decimal places zdialog_stuff(zd,"longi",glongi,"%.5f"); } else { zdialog_stuff(zd,"lati",flati,"%.5f"); // use clicked geocoordinaes only zdialog_stuff(zd,"longi",flongi,"%.5f"); } zdialog_send_event(zd,"geomap"); // activate calling dialog m_viewmode(0,&PFGWM); // restore prior view mode 7.10 } else if (location) find_filemap_images(flati,flongi); // show images in range of location else { snprintf(text,20,"%.5f %.5f",flati,flongi); // show coordinates poptext_mouse(text,20,-20,0.1,3); } } else if (location && Fusedot) { // mouse movement, no click if (! ploc || ! strmatch(location,ploc)) { // popup the location name at mouse if (strmatch(location,"null")) poptext_mouse(country,20,-20,0.1,1); // 20.0 else poptext_mouse(location,20,-20,0.1,1); ploc = location; } } else ploc = 0; return; } /********************************************************************************/ // find images within the marker size, show gallery of images. // privat function for filemap_mousefunc(), called when a location is clicked void find_filemap_images(float flati, float flongi) { int ii, nn = 0; int x1, y1, x2, y2; int capturedist = (map_dotsize + 2) / 2; // mouse - marker capture distance float glati, glongi, grange; xxrec_t *xxrec; FILE *fid; if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } filemap_position(flati,flongi,x1,y1); // target map pixel location fid = fopen(searchresults_file,"w"); // open output file if (! fid) { zmessageACK(Mwin,"output file error: %s",strerror(errno)); return; } if (gallerymap) // show gallery images at location { for (ii = 0; ii < Ngallerymap; ii++) // loop all gallery files { zmainloop(100); // keep GTK alive glati = gallerymap[ii].flati; // file geocoordinates glongi = gallerymap[ii].flongi; filemap_position(glati,glongi,x2,y2); // image map pixel location grange = sqrtf((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); // target - image pixel distance if (grange < 1.5 * capturedist) { // within distance limit, select fprintf(fid,"%s\n",gallerymap[ii].file); // output matching file nn++; } } } else { for (ii = 0; ii < Nxxrec; ii++) // show all images at location { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; glati = xxrec->flati; glongi = xxrec->flongi; filemap_position(glati,glongi,x2,y2); // image map pixel location grange = sqrtf((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); // target - image pixel distance if (grange < 1.5 * capturedist) { // within distance limit, select fprintf(fid,"%s\n",xxrec->file); // output matching file nn++; } } } fclose(fid); if (! nn) { poptext_mouse(E2X("No matching images found"),10,0,0,3); return; } free_resources(); navi::gallerytype = SEARCH; // search results gallery(searchresults_file,"initF",0); // generate gallery of matching files gallery(0,"paint",0); m_viewmode(0,"G"); return; } /********************************************************************************/ // free memory lorge memory used for filemap image // used by edit_setup() to maximize available memory void free_filemap() { if (Wstate.fpxb) PXB_free(Wstate.fpxb); Wstate.fpxb = 0; return; } /********************************************************************************/ // net maps using libchamplain (M view) // working map sources: "net-mapnik" "net-transportmap" namespace netmaps { GtkWidget *mapwidget = 0; ChamplainView *mapview = 0; ChamplainMapSourceFactory *map_factory = 0; ChamplainMapSource *map_source = 0; ChamplainMarkerLayer *markerlayer = 0; ChamplainMarker *marker[maximages]; ClutterColor *markercolor; ChamplainRenderer *renderer; ChamplainMapSource *error_source; ChamplainNetworkTileSource *tile_source; ChamplainFileCache *file_cache; ChamplainMemoryCache *memory_cache; ChamplainMapSourceChain *source_chain; // char *mapbox_access_key; // in fotoxx.h cchar *mapbox_license_text = ""; cchar *mapbox_license_uri = "https://www.mapbox.com/tos/"; cchar *mapbox_access_uri = "https://api.mapbox.com/v4/mapbox.light/#Z#/#X#/#Y#@2x.jpg70?access_token="; int mapbox_tile_size = 512; int mapbox_min_zoom = 1; // for 512x512 tiles int mapbox_max_zoom = 17; int mapbox_file_cache = 1000 * MEGA; // tile file cache max. size int mapbox_memory_cache = 1000; // tile memory cache max. tiles } void netmap_mousefunc(GtkWidget *, GdkEventButton *, void *); // mouse click function for net map void find_netmap_images(float flati, float flongi); // find images at clicked position // menu function - choose net map source void m_netmap_source(GtkWidget *, cchar *) { using namespace netmaps; int netmap_source_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int zstat, nn; char temp[200]; F1_help_topic = "net source"; m_viewmode(0,"M"); // 19.0 /*** ________________________________ | Net Map Source | | | | [_] mapnik (default) | | ----------------------------- | | [x] mapbox | | Access Key [________________] | | | | [Done] [Cancel] | |________________________________| ***/ zd = zdialog_new(E2X("Net Map Source"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"check","mapnik","hb1","mapnik (default)","space=3"); zdialog_add_widget(zd,"hsep","hsep1","dialog",0,"space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog"); zdialog_add_widget(zd,"check","mapbox","hb2","mapbox","space=3"); zdialog_add_widget(zd,"hbox","hb4","dialog"); zdialog_add_widget(zd,"label","labkey","hb4","Access Key","space=3"); zdialog_add_widget(zd,"zentry","key","hb4","","space=3|expand"); zdialog_restore_inputs(zd); zdialog_set_modal(zd); zdialog_run(zd,netmap_source_dialog_event,"mouse"); zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); return; } zdialog_fetch(zd,"mapnik",nn); if (nn) netmap_source = zstrdup("mapnik"); zdialog_fetch(zd,"mapbox",nn); if (nn) netmap_source = zstrdup("mapbox"); zdialog_fetch(zd,"key",temp,200); // save key in parameters file mapbox_access_key = zstrdup(temp); // immediately save_params(); if (mapwidget) gtk_widget_destroy(mapwidget); // remove prior map if any markerlayer = 0; m_load_netmap(0,0); zdialog_free(zd); return; } // dialog event and completion function int netmap_source_dialog_event(zdialog *zd, cchar *event) { if (zstrstr("mapnik mapbox",event)) { // unset and set two checkboxes zdialog_stuff(zd,"mapnik",0); zdialog_stuff(zd,"mapbox",0); zdialog_stuff(zd,event,1); } return 1; } /********************************************************************************/ // initialize for net maps void m_load_netmap(GtkWidget *, cchar *) { using namespace netmaps; char mapbox_total_uri[200]; F1_help_topic = "net map"; m_viewmode(0,"M"); // 19.0 if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } if (Findexvalid == 1) zmessage_post_bold(Mwin,"20/10",2,Boldindex); // warn, index missing new files if (! init_geolocs()) return; // failed Ffuncbusy = 1; if (markerlayer) { // refresh map markers netmap_paint_dots(); Ffuncbusy = 0; return; } mapwidget = gtk_champlain_embed_new(); // libchamplain map drawing area if (! mapwidget) goto fail; gtk_container_add(GTK_CONTAINER(Mvbox),mapwidget); /*** GdkWindow *gdkwin; gdkwin = gtk_widget_get_window(mapwidget); // replace "hand" cursor with arrow 20.0 gdk_window_set_cursor(gdkwin,null); // these have no effect FIXME gdk_window_set_cursor(gdkwin,arrowcursor); gdk_window_set_device_cursor(gdkwin,zfuncs::mouse,arrowcursor); ***/ mapview = gtk_champlain_embed_get_view(GTK_CHAMPLAIN_EMBED(mapwidget)); if (! mapview) goto fail; if (strmatch(netmap_source,"mapnik")) // mapnik map source (free) { map_factory = champlain_map_source_factory_dup_default(); map_source = champlain_map_source_factory_create_cached_source(map_factory,"osm-mapnik"); /** map_source = champlain_map_source_factory_create_cached_source(map_factory,"osm-transportmap"); alternative **/ champlain_view_set_min_zoom_level(mapview,3); } else if (strmatch(netmap_source,"mapbox")) // mapbox map source (licensed) { renderer = CHAMPLAIN_RENDERER(champlain_image_renderer_new()); map_factory = champlain_map_source_factory_dup_default(); strcpy(mapbox_total_uri,mapbox_access_uri); strcat(mapbox_total_uri,mapbox_access_key); tile_source = champlain_network_tile_source_new_full ( "mapbox", "mapbox", mapbox_license_text, mapbox_license_uri, mapbox_min_zoom, mapbox_max_zoom, mapbox_tile_size, CHAMPLAIN_MAP_PROJECTION_MERCATOR, mapbox_total_uri, renderer); error_source = champlain_map_source_factory_create_error_source(map_factory,256); file_cache = champlain_file_cache_new_full(mapbox_file_cache, null, renderer); memory_cache = champlain_memory_cache_new_full(mapbox_memory_cache, renderer); source_chain = champlain_map_source_chain_new(); champlain_map_source_chain_push(source_chain, error_source); champlain_map_source_chain_push(source_chain, CHAMPLAIN_MAP_SOURCE(tile_source)); champlain_map_source_chain_push(source_chain, CHAMPLAIN_MAP_SOURCE(file_cache)); champlain_map_source_chain_push(source_chain, CHAMPLAIN_MAP_SOURCE(memory_cache)); map_source = CHAMPLAIN_MAP_SOURCE(source_chain); } else { zmessageACK(Mwin,"unknown map source: %s \n",netmap_source); Ffuncbusy = 0; return; } champlain_view_set_map_source(mapview,map_source); // mapnik or mapbox markerlayer = champlain_marker_layer_new_full(CHAMPLAIN_SELECTION_SINGLE); if (! markerlayer) goto fail; champlain_view_add_layer(mapview,CHAMPLAIN_LAYER(markerlayer)); champlain_marker_layer_set_selection_mode(markerlayer,CHAMPLAIN_SELECTION_NONE); markercolor = clutter_color_new(255,0,0,255); gtk_widget_add_events(mapwidget,GDK_BUTTON_PRESS_MASK); // connect mouse events to net map G_SIGNAL(mapwidget,"button-press-event",netmap_mousefunc,0); G_SIGNAL(mapwidget,"button-release-event",netmap_mousefunc,0); G_SIGNAL(mapwidget,"motion-notify-event",netmap_mousefunc,0); netmap_paint_dots(); // paint map markers where images Ffuncbusy = 0; return; fail: zmessageACK(Mwin,"net/libchamplain failure"); Ffuncbusy = 0; return; } // paint red dots corresponding to image locations on map void netmap_paint_dots() { using namespace netmaps; float flati, flongi; float plati = 999, plongi = 999; champlain_marker_layer_remove_all(markerlayer); if (gallerymap) // use gallerymap[] if present { // mark gallery images on map for (int ii = 0; ii < Ngallerymap; ii++) { flati = gallerymap[ii].flati; // image geocoordinates flongi = gallerymap[ii].flongi; if (flati == plati && flongi == plongi) continue; // skip repititions plati = flati; plongi = flongi; marker[ii] = (ChamplainMarker *) champlain_point_new_full(map_dotsize,markercolor); champlain_location_set_location(CHAMPLAIN_LOCATION(marker[ii]),flati,flongi); champlain_marker_layer_add_marker(markerlayer,marker[ii]); } } else { for (int ii = 0; ii < Ngeolocs; ii++) // mark all images on map { flati = geolocs[ii]->flati; flongi = geolocs[ii]->flongi; marker[ii] = (ChamplainMarker *) champlain_point_new_full(map_dotsize,markercolor); champlain_location_set_location(CHAMPLAIN_LOCATION(marker[ii]),flati,flongi); champlain_marker_layer_add_marker(markerlayer,marker[ii]); } } gtk_widget_show_all(mapwidget); return; } /********************************************************************************/ // map zoom-in on location of a selected image file void m_netmap_zoomin(GtkWidget *, cchar *menu) { using namespace netmaps; static char *file = 0; float flati, flongi; xxrec_t *xxrec; F1_help_topic = "show on map"; m_viewmode(0,"M"); // 19.0 if (file) zfree(file); file = 0; if (clicked_file) { // use clicked file if present file = clicked_file; clicked_file = 0; } else if (curr_file) // else current file file = zstrdup(curr_file); else return; xxrec = get_xxrec(file); if (! xxrec) return; flati = xxrec->flati; flongi = xxrec->flongi; if (flati == 0 && flongi == 0) return; netmap_zoomto(flati,flongi,11); // 20.0 return; } // map zoom-in on specified location with specified zoom level void netmap_zoomto(float flati, float flongi, int zoomlev) { using namespace netmaps; m_load_netmap(0,0); // 19.11 champlain_view_center_on(mapview,flati,flongi); champlain_view_set_zoom_level(mapview,zoomlev); return; } // get current map scale (meters/pixel) at given zoom level and geocoordinates float netmapscale(int zoomlev, float flat, float flong) { using namespace netmaps; float fmpp = champlain_map_source_get_meters_per_pixel(map_source,zoomlev,flat,flong); return fmpp; } /********************************************************************************/ // Respond to mouse clicks on net map image. void netmap_mousefunc(GtkWidget *widget, GdkEventButton *event, void *) { using namespace netmaps; int mx, my, px, py; int mapww, maphh; int ii, minii; int capturedist = map_dotsize / 2 + 3; // mouse - marker capture distance 20.0 int Fusedot = 0; char *location = 0, *country; float flati, flongi, glati, glongi; float dist, mindist; static char *ploc = 0; static int downtime; char text[20]; zdialog *zd = zd_mapgeotags; if (! mapview) return; // net map not available mx = event->x; // mouse position in map widget my = event->y; flati = champlain_view_y_to_latitude(mapview,my); // corresp. map coordinates flongi = champlain_view_x_to_longitude(mapview,mx); dist = mindist = 999999; minii = 0; for (ii = 0; ii < Ngeolocs; ii++) // find nearest location/country { glati = geolocs[ii]->flati; dist = (flati - glati) * (flati - glati); if (dist > mindist) continue; glongi = geolocs[ii]->flongi; dist += (flongi - glongi) * (flongi - glongi); // degrees**2 if (dist > mindist) continue; mindist = dist; minii = ii; } ii = minii; glati = geolocs[ii]->flati; // nearest known location (dot marker) glongi = geolocs[ii]->flongi; location = geolocs[ii]->location; country = geolocs[ii]->country; px = champlain_view_longitude_to_x(mapview,glongi); // corresp. map location py = champlain_view_latitude_to_y(mapview,glati); dist = sqrtf((px-mx) * (px-mx) + (py-my) * (py-my)); // distance in pixels if (dist <= capturedist) Fusedot = 1; // mouse is within marker dot if (event->type == GDK_BUTTON_PRESS) { downtime = event->time; return; } if (event->type == GDK_BUTTON_RELEASE) // detect button click { // to ignore drags if (event->time - downtime > 600) return; if (zd) // stuff calling dialog { if (Fusedot) { // click within dot zdialog_stuff(zd,"location",location); // use nominal dot location data zdialog_stuff(zd,"country",country); zdialog_stuff(zd,"lati",glati,"%.5f"); // 5 decimal places zdialog_stuff(zd,"longi",glongi,"%.5f"); } else { zdialog_stuff(zd,"lati",flati,"%.5f"); // use clicked geocoordinates only zdialog_stuff(zd,"longi",flongi,"%.5f"); } zdialog_send_event(zd,"geomap"); // activate calling dialog m_viewmode(0,&PFGWM); // restore prior view mode 7.10 } else if (event->button == 1) { // left click if (Fusedot) find_netmap_images(flati,flongi); // on marker - show corresp. images else { champlain_view_center_on(mapview,flati,flongi); // zoom-in at clicked location 19.0 champlain_view_zoom_in(mapview); mapww = gtk_widget_get_allocated_width(mapwidget); // move mouse to center maphh = gtk_widget_get_allocated_height(mapwidget); move_pointer(mapwidget,mapww/2,maphh/2); snprintf(text,20,"%.5f %.5f",flati,flongi); // show coordinates 19.0 poptext_mouse(text,20,-20,0.1,3); } } else if (event->button == 3) // right click champlain_view_zoom_out(mapview); // zoom out 19.0 return; } downtime = 0; // mouse motion if (location && Fusedot) { if (! ploc || ! strmatch(location,ploc)) { // popup the location name at mouse if (strmatch(location,"null")) poptext_mouse(country,20,-20,0.1,1); // 20.0 else poptext_mouse(location,20,-20,0.1,1); ploc = location; } } else ploc = 0; return; } // find images within the marker size, show gallery of images. // privat function for netmap_mousefunc(), called when a location is clicked void find_netmap_images(float flati, float flongi) { using namespace netmaps; int ii, nn = 0; int x1, y1, x2, y2; int capturedist = (map_dotsize + 2) / 2; // mouse - marker capture distance float glati, glongi, grange; FILE *fid; xxrec_t *xxrec; if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } x1 = champlain_view_longitude_to_x(mapview,flongi); // target map pixel location y1 = champlain_view_latitude_to_y(mapview,flati); fid = fopen(searchresults_file,"w"); // open output file if (! fid) { zmessageACK(Mwin,"output file error: %s",strerror(errno)); return; } if (gallerymap) // show gallery images at location { for (ii = 0; ii < Ngallerymap; ii++) // loop all gallery files { zmainloop(100); // keep GTK alive glati = gallerymap[ii].flati; // image geocoordinates glongi = gallerymap[ii].flongi; x2 = champlain_view_longitude_to_x(mapview,glongi); // image map pixel location y2 = champlain_view_latitude_to_y(mapview,glati); grange = sqrtf((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); // mouse - image pixel distance if (grange < 1.5 * capturedist) { // within distance limit, select fprintf(fid,"%s\n",gallerymap[ii].file); // output matching file nn++; } } } else // show all images at location { for (ii = 0; ii < Nxxrec; ii++) { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; glati = xxrec->flati; glongi = xxrec->flongi; if (glati == 0 && glongi == 0) continue; x2 = champlain_view_longitude_to_x(mapview,glongi); // image map pixel location y2 = champlain_view_latitude_to_y(mapview,glati); grange = sqrtf((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); // mouse - image pixel distance if (grange < 1.5 * capturedist) { // within distance limit, select fprintf(fid,"%s\n",xxrec->file); // output matching file nn++; } } } fclose(fid); if (! nn) { poptext_mouse(E2X("No matching images found"),10,0,0,3); return; } free_resources(); navi::gallerytype = SEARCH; // search results gallery(searchresults_file,"initF",0); // generate gallery of matching files gallery(0,"paint",0); m_viewmode(0,"G"); return; } /********************************************************************************/ // Save current map location (center and scale) with a given name, // or retrieve a previously saved map location. namespace netmap_locs_names { zdialog *zdnetmaploc = 0; char locname[60]; double loclati = 0, loclongi = 0; int loczoom = 12; char netmaplocfile[200]; char buff[100]; } // menu function void m_netmap_locs(GtkWidget *, cchar *) { using namespace netmap_locs_names; int netmap_locs_dialog_event(zdialog *zd, cchar *event); void netmap_locs_clickfunc(GtkWidget *, int line, int pos, int kbkey); zdialog *zd; GtkWidget *mtext; cchar *pp; FILE *fid; F1_help_topic = "net locs"; m_viewmode(0,"M"); // 19.0 snprintf(netmaplocfile,200,"%s/netmap_locations",get_zhomedir()); // net map locations file /*** ________________________________ | Net Map Locations | | ______________________________ | || || || map location name 1 || || long map location name 2 || scrolling window || map location name 3 || || ... || ||______________________________|| | | | map location: [______________] | text entry for location name | | | [add] [delete] [done] | |________________________________| [location] empty until filled-in or a location from the list is clicked [add] current location is added to list or replaced [delete] current location is deleted from list location position and scale is from current map location location list is kept in alphabetic order ***/ if (zdnetmaploc) return; // already active zd = zdialog_new(E2X("Net Map Locations"),Mwin,Badd,Bdelete,Bdone,null); zdnetmaploc = zd; zdialog_add_widget(zd,"frame","frlocs","dialog",0,"expand"); zdialog_add_widget(zd,"scrwin","scrlocs","frlocs",0,"expand"); zdialog_add_widget(zd,"text","mtext","scrlocs"); zdialog_add_widget(zd,"hbox","hbvn","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labvn","hbvn",E2X("map location:"),"space=3"); zdialog_add_widget(zd,"zentry","locname","hbvn","","space=3"); zdialog_resize(zd,200,300); zdialog_run(zd,netmap_locs_dialog_event,"mouse"); mtext = zdialog_widget(zd,"mtext"); // map location list in dialog textwidget_clear(mtext); fid = fopen(netmaplocfile,"r"); // map location list file if (fid) { while (true) { pp = fgets_trim(buff,100,fid,1); // read location | lati | longi | zoom if (! pp) break; pp = strField(buff,'|',1); // isolate location if (! pp) continue; if (strlen(pp) < 2) continue; textwidget_append(mtext,0,"%s \n",pp); // write into dialog list } fclose(fid); } textwidget_set_eventfunc(mtext,netmap_locs_clickfunc); // set mouse/KB event function return; } // dialog event and completion callback function int netmap_locs_dialog_event(zdialog *zd, cchar *event) { using namespace netmaps; using namespace netmap_locs_names; int ff, nn; cchar *pp, *pp2; GtkWidget *mtext; FILE *fidr; if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) // [add] new map location record { zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"locname",locname,60); if (strTrim2(locname) < 2) { zmessageACK(Mwin,E2X("supply a reasonable name")); return 1; } loclati = champlain_view_get_center_latitude(mapview); // get current map location loclongi = champlain_view_get_center_longitude(mapview); loczoom = champlain_view_get_zoom_level(mapview); snprintf(buff,100,"%s|%.4f|%.4f|%d",locname,loclati,loclongi,loczoom); // prepare new location rec. linedit_open(netmaplocfile); ff = 0; while (true) // read next location record { pp = linedit_get(); if (! pp) break; nn = strcasecmp(locname,pp); // compare new location with location record if (! ff && nn < 0) { // new location < location record linedit_put(buff); ff = 1; } linedit_put(pp); // write location record } if (! ff) linedit_put(buff); // insert new location last linedit_close(); goto update_dialog; } if (zd->zstat == 2) // [delete] selected map location record { zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"locname",locname,60); linedit_open(netmaplocfile); while (true) // read next location record { pp = linedit_get(); if (! pp) break; pp2 = strField(pp,'|',1); // get location name if (! pp2) continue; if (strmatch(locname,pp2)) continue; // omit deleted location linedit_put(pp); // write location record } linedit_close(); goto update_dialog; } zdialog_destroy(zd); // [done] or [x] zdnetmaploc = 0; return 1; update_dialog: mtext = zdialog_widget(zd,"mtext"); // map location name list in dialog textwidget_clear(mtext); // clear list fidr = fopen(netmaplocfile,"r"); // update dialog list from file if (! fidr) return 1; while (true) { pp = fgets_trim(buff,100,fidr,1); // read location | lati | longi | zoom if (! pp) break; pp = strField(buff,'|',1); // isolate location if (! pp) continue; if (strlen(pp) < 2) continue; textwidget_append2(mtext,0,"%s \n",pp); // write into dialog list } fclose(fidr); return 1; } // get clicked location name and set corresponding map location and zoom level void netmap_locs_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { using namespace netmap_locs_names; cchar *pp1, *pp2; FILE *fidr; zdialog *zd = zdnetmaploc; if (! zd) return; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } pp1 = textwidget_line(widget,line,1); // get clicked line, highlight if (! pp1 || ! *pp1) return; textwidget_highlight_line(widget,line); strTrim2(locname,pp1); zdialog_stuff(zd,"locname",locname); fidr = fopen(netmaplocfile,"r"); // open/read netmap locs file if (! fidr) { zmessageACK(Mwin,strerror(errno)); return; } while (true) // read next location record { pp2 = fgets_trim(buff,100,fidr); if (! pp2) break; pp2 = strField(buff,'|',1); if (! pp2) continue; if (strmatch(locname,pp2)) break; // found matching record } fclose(fidr); if (! pp2 || ! strmatch(locname,pp2)) goto notfound; loclati = loclongi = loczoom = 0; pp1 = strField(buff,'|',2); // get map location data from record if (! pp1) goto baddata; loclati = atof(pp1); if (loclati <= -90 || loclati >= +90) goto baddata; pp1 = strField(buff,'|',3); if (! pp1) goto baddata; loclongi = atof(pp1); if (loclongi <= -180 || loclongi >= +180) goto baddata; pp1 = strField(buff,'|',4); if (! pp1) goto baddata; loczoom = atoi(pp1); if (loczoom < 1 || loczoom > 20) goto baddata; netmap_zoomto(loclati,loclongi,loczoom); // set this map location return; notfound: printz("net map location not found: %s \n",locname); return; baddata: printz("net map location invalid: %s %.4f %.4f %d \n", locname,loclati,loclongi,loczoom); return; } /******************************************************************************** Functions to read and write exif/iptc or other metadata *********************************************************************************/ // get EXIF/IPTC metadata for given image file and EXIF/IPTC key(s) // returns array of pointers to corresponding key values // if a key is missing, corresponding pointer is null // returned strings belong to caller, are subject for zfree() // up to 100 keynames may be requested per call // returns: 0 = OK, +N = error int exif_get(cchar *file, cchar **keys, char **kdata, int nkeys) { char *pp, geostring[20]; char *inputs[120], *outputs[100]; // higher limits int cc, ii, jj, err; float geofloat; uint ucc; if (nkeys < 1 || nkeys > 99) zappcrash("exif_get nkeys: %d",nkeys); cc = nkeys * sizeof(char *); // clear outputs memset(kdata,0,cc); inputs[0] = (char *) "-m"; // options for exiftool inputs[1] = (char *) "-s2"; inputs[2] = (char *) "-n"; inputs[3] = (char *) "-fast"; // -fast2 loses maker notes jj = 4; for (ii = 0; ii < nkeys; ii++) // build exiftool inputs { cc = strlen(keys[ii]); // -keyname if (! cc) { printz("exif_get() null key \n"); return 1; } inputs[jj] = (char *) zmalloc(cc+2); inputs[jj][0] = '-'; strcpy(inputs[jj]+1,keys[ii]); if (strmatchcase(keys[ii],"location")) // make "location" = "city" strcpy(inputs[jj]+1,"city"); jj++; } inputs[jj] = zstrdup(file); // filename last jj++; err = exif_server(jj,inputs,outputs); // get exif outputs if (err) return 1; // exif_server() failure for (ii = 4; ii < jj; ii++) // free memory zfree(inputs[ii]); for (ii = 0; ii < nkeys; ii++) // search outputs { pp = outputs[ii]; // keyname: keyvalue if (! pp) break; if (utf8_check(pp)) { // detect invalid UTF-8 string 20.0 if (strlen(pp) > 40) pp[40] = 0; printz("invalid UTF-8 metadata: %s \n %s \n",pp,file); zfree(pp); continue; } for (jj = 0; jj < nkeys; jj++) { if (strmatchcase(keys[jj],"location")) { // "location" comes back "city" if (! strmatchcaseN(pp,"city",4)) continue; if (strlen(pp) > 6) kdata[jj] = zstrdup(pp+6); } else if (strmatchN(keys[jj],"GPSLatitude",11)) { // round to 5 decimal places if (! strmatchN(pp,"GPSLatitude",11)) continue; err = convSF(pp+13,geofloat); if (err > 1) continue; snprintf(geostring,20,"%.5f",geofloat); kdata[jj] = zstrdup(geostring); } else if (strmatchN(keys[jj],"GPSLongitude",12)) { // round to 5 decimal places if (! strmatchN(pp,"GPSLongitude",12)) continue; err = convSF(pp+14,geofloat); if (err > 1) continue; snprintf(geostring,20,"%.5f",geofloat); kdata[jj] = zstrdup(geostring); } else { ucc = strlen(keys[jj]); // look for matching input keyname if (! strmatchcaseN(pp,keys[jj],ucc)) continue; if (strlen(pp) > ucc+2) kdata[jj] = zstrdup(pp+ucc+2); } } zfree(pp); } return 0; } /********************************************************************************/ // create or change EXIF/IPTC metadata for given image file and key(s) // // command: // exiftool -m -overwrite_original -keyname="keyvalue" ... "file" // // NOTE: exiftool replaces \n (newline) in keyvalue with . (period). int exif_put(cchar *file, cchar **keys, cchar **kdata, int nkeys) { int ii, jj, cc, err; char *inputs[40]; if (nkeys < 1 || nkeys > 30) zappcrash("exif_put nkeys: %d",nkeys); // higher limit err = access(file,W_OK); // test file can be written by me 19.1 if (err) { printz("%s: %s \n",Bnowriteperm,file); return 1; } inputs[0] = (char *) "-m"; // exiftool options inputs[1] = (char *) "-overwrite_original"; // -P preserve date removed jj = 2; for (ii = 0; ii < nkeys; ii++) // build exiftool inputs { if (! kdata[ii]) continue; // skip missing data cc = strlen(keys[ii]) + strlen(kdata[ii]) + 3; inputs[jj] = (char *) zmalloc(cc); inputs[jj][0] = '-'; // -keyname=value if (strmatchcase(keys[ii],"location")) { // make "location" = "city" strcpy(inputs[jj]+1,"city"); cc = 4; } else { strcpy(inputs[jj]+1,keys[ii]); cc = strlen(keys[ii]); } inputs[jj][cc+1] = '='; strcpy(inputs[jj]+cc+2,kdata[ii]); jj++; if (strmatchcase(keys[ii],"GPSLatitude")) { // take care of latitude N/S if (*kdata[ii] == '-') inputs[jj] = zstrdup("-GPSLatitudeRef=S"); else inputs[jj] = zstrdup("-GPSLatitudeRef=N"); jj++; } if (strmatchcase(keys[ii],"GPSLongitude")) { // and longitude E/W if (*kdata[ii] == '-') inputs[jj] = zstrdup("-GPSLongitudeRef=W"); else inputs[jj] = zstrdup("-GPSLongitudeRef=E"); jj++; } } inputs[jj] = zstrdup(file); // last input is filename jj++; exif_server(jj,inputs,0); // outputs discarded for (ii = 2; ii < jj; ii++) // free memory zfree(inputs[ii]); return 0; } /********************************************************************************/ // copy EXIF/IPTC data from one image file to new (edited) image file // if nkeys > 0: added keys[] to be replaced with new values kdata[] // exiftool -m -tagsfromfile file1 -all -xmp -iptc -icc_profile // [-keyname=newvalue ...] file2 -overwrite_original int exif_copy(cchar *file1, cchar *file2, cchar **keys, cchar **kdata, int nkeys) { char *inputs[40]; // revised int err, cc, ii, jj, nfixed; if (nkeys > 30) zappcrash("exif_copy() nkeys %d",nkeys); // higher limit err = access(file2,W_OK); // test file can be written by me 19.1 if (err) { printz("%s: %s \n",Bnowriteperm,file2); return 1; } inputs[0] = (char *) "-m"; // -m (suppress warnings) inputs[1] = (char *) "-tagsfromfile"; // -tagsfromfile inputs[2] = zstrdup(file1); // file1 inputs[3] = (char *) "-all"; // -all inputs[4] = (char *) "-xmp"; // -xmp inputs[5] = (char *) "-iptc"; // -iptc inputs[6] = (char *) "-icc_profile"; // -icc_profile nfixed = jj = 7; // no. fixed inputs for (int ii = 0; ii < nkeys; ii++) // -keyname=keyvalue { if (! kdata[ii]) continue; // skip missing data cc = strlen(keys[ii]) + strlen(kdata[ii]) + 3; inputs[jj] = (char *) zmalloc(cc); *inputs[jj] = 0; strncatv(inputs[jj],cc,"-",keys[ii],"=",kdata[ii],null); jj++; } inputs[jj++] = zstrdup(file2); // file2 inputs[jj++] = (char *) "-overwrite_original"; // -overwrite_original exif_server(jj,inputs,0); zfree(inputs[2]); // free memory for (ii = nfixed; ii < nfixed+nkeys; ii++) zfree(inputs[ii]); return 0; } /******************************************************************************** int exif_server(int Nrecs, char **inputs, char **outputs) Server wrapper for exiftool for put/get exif/iptc data. This saves perl startup overhead for each call (0.1 >> 0.01 secs). Input records: -opt1 -opt2 ... -keyword1=value1 -keyword2=value2 ... filename.jpg (null) Returned: list of pointers to resulting output records. There are <= Nrecs outputs corresponding to keyword inputs. These are formatted "keyword: value" (text string). If < Nrecs output, unused outputs are null pointers. These are subject to zfree(). If outputs = null, outputs are discarded. Returns 0 if OK, +N if error. First call: Starts exiftool with pipe input and output files. Subsequent calls: The existing exiftool process is re-used so that the substantial startup overhead is avoided. To kill the exiftool process: exif_server(0,0,0). There are two servers which can run parallel if called by two threads. The exiftool perl process has a significant memory leak that can reach gigabytes if Fotoxx image index function processes 100K files or more. Solution: exit perl process after 1000 calls and start over with next call. *********************************************************************************/ #include namespace exif_server_names // 19.0 { char inputfile1[200]; char inputfile2[200]; } int exif_server(int Nrecs, char **inputs, char **outputs) { using namespace exif_server_names; int exif_server1(int Nrecs, char **inputs, char **outputs); // 2 parallel server processes int exif_server2(int Nrecs, char **inputs, char **outputs); static int fcf = 1, busy1 = 0, busy2 = 0; double time0, time1; int err; if (! Nrecs) { printz("stop EXIF servers \n"); zmainsleep(0.1); exif_server1(0,0,0); exif_server2(0,0,0); return 0; } if (fcf) // first call 19.0 { fcf = 0; snprintf(inputfile1,200,"%s/exiftool_input1",temp_folder); // exiftool input files snprintf(inputfile2,200,"%s/exiftool_input2",temp_folder); } time0 = get_seconds(); while (true) { if (resource_lock(busy1)) { // use resource locks err = exif_server1(Nrecs,inputs,outputs); resource_unlock(busy1); return err; } if (resource_lock(busy2)) { err = exif_server2(Nrecs,inputs,outputs); resource_unlock(busy2); return err; } time1 = get_seconds(); if (time1 - time0 > 2) break; zsleep(0.001); } zexit("exiftool (perl) is not responding"); return 0; // not executed } int exif_server1(int Nrecs, char **inputs, char **outputs) // exiftool process 1 { using namespace exif_server_names; char *inputfile = inputfile1; // only difference in 2 servers NOTE static int fcf = 1; // first call flag static int Ncalls = 0; static FILE *fid1 = 0, *fid2 = 0; // exiftool input, output files int ii, cc, err; char *pp, command[200]; char outrec[exif_maxcc]; // single output record if (! Nrecs) // kill exiftool if requested { if (fcf) return 0; // never started fid1 = fopen(inputfile,"a"); if (fid1) { fprintf(fid1,"-stay_open\nFalse\n"); // tell it to exit fclose(fid1); } if (fid2) pclose(fid2); fcf = 1; // restart if called again return 0; } if (fcf) // first call only { fid1 = fopen(inputfile,"w"); // start exiftool input file if (! fid1) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); return 1; } snprintf(command,200,"exiftool -stay_open True -@ %s; exit",inputfile); // exit shell when exit exiftool fid2 = popen(command,"r"); // start exiftool and output file if (! fid2) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); fclose(fid1); return 2; } fcf = 0; Ncalls = 0; } Ncalls++; // count calls for (ii = 0; ii < Nrecs; ii++) fprintf(fid1,"%s\n",inputs[ii]); // write to exiftool, 1 per record err = fprintf(fid1,"-execute\n"); // tell exiftool to process if (err < 0) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); return 3; } fflush(fid1); // flush buffer if (outputs) { cc = Nrecs * sizeof(char *); // clear outputs memset(outputs,0,cc); } for (ii = 0; ii < 99; ii++) // get ALL exiftool outputs { pp = fgets_trim(outrec,exif_maxcc,fid2,1); if (! pp) break; if (strncmp(outrec,"{ready}",7) == 0) break; // look for output end if (outputs && ii < Nrecs) outputs[ii] = zstrdup(outrec); // add to returned records } return 0; } int exif_server2(int Nrecs, char **inputs, char **outputs) // exiftool process 2 { using namespace exif_server_names; char *inputfile = inputfile2; // only difference in 2 servers NOTE static int fcf = 1; // first call flag static int Ncalls = 0; static FILE *fid1 = 0, *fid2 = 0; // exiftool input, output files int ii, cc, err; char *pp, command[200]; char outrec[exif_maxcc]; // single output record if (! Nrecs) // kill exiftool if requested { if (fcf) return 0; // never started fid1 = fopen(inputfile,"a"); if (fid1) { fprintf(fid1,"-stay_open\nFalse\n"); // tell it to exit fclose(fid1); } if (fid2) pclose(fid2); fcf = 1; // restart if called again return 0; } if (fcf) // first call only { fid1 = fopen(inputfile,"w"); // start exiftool input file if (! fid1) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); return 1; } snprintf(command,200,"exiftool -stay_open True -@ %s; exit",inputfile); // exit shell when exit exiftool fid2 = popen(command,"r"); // start exiftool and output file if (! fid2) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); fclose(fid1); return 2; } fcf = 0; Ncalls = 0; } Ncalls++; // count calls for (ii = 0; ii < Nrecs; ii++) fprintf(fid1,"%s\n",inputs[ii]); // write to exiftool, 1 per record err = fprintf(fid1,"-execute\n"); // tell exiftool to process if (err < 0) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); return 3; } fflush(fid1); // flush buffer if (outputs) { cc = Nrecs * sizeof(char *); // clear outputs memset(outputs,0,cc); } for (ii = 0; ii < 99; ii++) // get ALL exiftool outputs { pp = fgets_trim(outrec,exif_maxcc,fid2,1); if (! pp) break; if (strncmp(outrec,"{ready}",7) == 0) break; // look for output end if (outputs && ii < Nrecs) outputs[ii] = zstrdup(outrec); // add to returned records } return 0; } /********************************************************************************/ // convert between EXIF and fotoxx tag date formats // EXIF date: yyyy:mm:dd hh:mm:ss 20 chars. // tag date: yyyymmddhhmmss 16 chars. // void exif_tagdate(cchar *exifdate, char *tagdate) { int cc; memset(tagdate,0,15); cc = strlen(exifdate); if (cc > 3) strncpy(tagdate+0,exifdate+0,4); if (cc > 6) strncpy(tagdate+4,exifdate+5,2); if (cc > 9) strncpy(tagdate+6,exifdate+8,2); if (cc > 12) strncpy(tagdate+8,exifdate+11,2); if (cc > 15) strncpy(tagdate+10,exifdate+14,2); if (cc > 18) strncpy(tagdate+12,exifdate+17,2); tagdate[14] = 0; return; } void tag_exifdate(cchar *tagdate, char *exifdate) { int cc; memset(exifdate,0,20); cc = strlen(tagdate); strcpy(exifdate,"1900:01:01 00:00:00"); if (cc > 3) strncpy(exifdate+0,tagdate+0,4); if (cc > 5) strncpy(exifdate+5,tagdate+4,2); if (cc > 7) strncpy(exifdate+8,tagdate+6,2); if (cc > 9) strncpy(exifdate+11,tagdate+8,2); if (cc > 11) strncpy(exifdate+14,tagdate+10,2); if (cc > 13) strncpy(exifdate+17,tagdate+12,2); exifdate[19] = 0; return; } /******************************************************************************** Functions to read and write image index file on disk and update the image index memory table (xxrec table). *********************************************************************************/ // Get the image index record for the given image file. // Returns pointer to xxrec or null if not found. // Returned xxrec fields are NOT subjects for zfree() // If file is not indexed or modified since index, // 19.13 // a dummy xxrec in static memory is returned. // (only good until next call to get_xxrec()). xxrec_t * get_xxrec(cchar *file) { int ii, jj, kk, rkk, last, err; STATB statb; char fdate[16], *RP; static xxrec_t xxrec; if (! file || *file != '/') return 0; RP = f_realpath(file); // use real path 20.0 if (! RP) return 0; err = stat(RP,&statb); if (err) goto ret0; if (image_file_type(RP) > VIDEO) goto ret0; // not indexable 19.10 if (! Findexvalid) goto noxxrec; // index not valid 19.10 if (! Nxxrec) goto noxxrec; // index empty 19.10 ii = Nxxrec / 2; // next table entry to search jj = (ii + 1) / 2; // next increment last = Nxxrec - 1; // last entry rkk = 0; while (true) // binary search { kk = strcmp(xxrec_tab[ii]->file,RP); // compare table entry to file2 if (kk > 0) { ii -= jj; // too high, go back in table if (ii < 0) break; } else if (kk < 0) { ii += jj; // too low, go forward in table if (ii > last) break; } else { compact_time(statb.st_mtime,fdate); // index record found if (! strmatch(fdate,xxrec_tab[ii]->fdate)) break; // check mod time matches 19.13 return xxrec_tab[ii]; } jj = jj / 2; // reduce increment if (jj == 0) { jj = 1; // step by 1 element if (! rkk) rkk = kk; // save last direction else { if (rkk > 0 && kk < 0) break; // if direction change, fail if (rkk < 0 && kk > 0) break; } } } noxxrec: // file not in index or index stale 19.13 memset(&xxrec,0,sizeof(xxrec_t)); // build dummy xxrec xxrec.file = RP; compact_time(statb.st_mtime,xxrec.fdate); strcpy(xxrec.pdate,"null"); xxrec.fsize = statb.st_size; xxrec.tags = zstrdup("null"); xxrec.capt = zstrdup("null"); xxrec.comms = zstrdup("null"); xxrec.location = zstrdup("null"); xxrec.country = zstrdup("null"); xxrec.xmeta = zstrdup("null"); return &xxrec; ret0: zfree(RP); return 0; } /********************************************************************************/ // Add or update the image index record for the given image file. // If xxrec is null, delete the table entry for the file. // Append new index record data to the image index file on disk. // (previous record, if any, is now superseded but still present) // Return 0 if success, +N if error (diagnosed). int put_xxrec(xxrec_t *xxrec, cchar *file) { int ii, iix, nn, err; int Fadd, Freplace, Fdelete; char *RP; xxrec_t *xxrec_new = 0; FILE *fid; STATB statb; if (! file || *file != '/') { zmessageACK(Mwin,"put_xxrec() file: %s",file); return 0; } if (! Findexvalid) return 1; // image index not valid RP = f_realpath(file); // use real path 20.0 if (! RP) xxrec = null; if (RP) { err = stat(RP,&statb); // check for regular file if (err || ! S_ISREG(statb.st_mode)) xxrec = null; // if not, set xxrec = null } if (xxrec) // caller xxrec with valid file { xxrec_new = (xxrec_t *) zmalloc(sizeof(xxrec_t)); // make new xxrec with data from caller memset(xxrec_new,0,sizeof(xxrec_t)); xxrec_new->file = RP; compact_time(statb.st_mtime,xxrec_new->fdate); // refresh file mod date/time xxrec_new->fsize = statb.st_size; // refresh file size 19.0 if (xxrec->pdate[0]) strcpy(xxrec_new->pdate,xxrec->pdate); else strcpy(xxrec_new->pdate,"null"); xxrec_new->ww = xxrec->ww; // image pixel dimensions 19.0 xxrec_new->hh = xxrec->hh; if (xxrec->rating[0]) strcpy(xxrec_new->rating,xxrec->rating); else strcpy(xxrec_new->rating,"0"); if (xxrec->tags) xxrec_new->tags = zstrdup(xxrec->tags); else xxrec_new->tags = zstrdup("null"); if (xxrec->capt) xxrec_new->capt = zstrdup(xxrec->capt); else xxrec_new->capt = zstrdup("null"); if (xxrec->comms) xxrec_new->comms = zstrdup(xxrec->comms); else xxrec_new->comms = zstrdup("null"); if (xxrec->location) xxrec_new->location = zstrdup(xxrec->location); // 19.13 else xxrec_new->location = zstrdup("null"); if (xxrec->country) xxrec_new->country = zstrdup(xxrec->country); // 19.13 else xxrec_new->country = zstrdup("null"); xxrec_new->flati = xxrec->flati; xxrec_new->flongi = xxrec->flongi; if (xxrec->xmeta) xxrec_new->xmeta = zstrdup(xxrec->xmeta); // 19.13 else xxrec_new->xmeta = zstrdup("null"); } nn = -1; // empty xxrec_tab[] >> not found if (RP) { for (iix = 0; iix < Nxxrec; iix++) { // find file in xxrec_tab[] 19.0 nn = strcmp(RP,xxrec_tab[iix]->file); if (nn <= 0) break; // file goes before or at posn iix } // = posn to add/replace/delete } Fadd = Freplace = Fdelete = 0; if (nn != 0 && ! xxrec) return 0; // nothing to do if (nn != 0 && xxrec) Fadd = 1; // add new xxrec if (nn == 0 && ! xxrec) Fdelete = 1; // delete existing xxrec if (nn == 0 && xxrec) Freplace = 1; // replace existing xxrec if (Fdelete) { if (RP) zfree(RP); zfree(xxrec_tab[iix]->file); // delete existing entry zfree(xxrec_tab[iix]->tags); zfree(xxrec_tab[iix]->capt); zfree(xxrec_tab[iix]->comms); zfree(xxrec_tab[iix]->location); zfree(xxrec_tab[iix]->country); zfree(xxrec_tab[iix]->xmeta); zfree(xxrec_tab[iix]); Nxxrec--; for (ii = iix; ii < Nxxrec; ii++) // pack down xxrec_tab[ii] = xxrec_tab[ii+1]; return 0; } if (Fadd) { if (Nxxrec == maximages) { zmessageACK(Mwin,"exceed %d max files, cannot continue",maximages); quitxx(); } for (ii = Nxxrec; ii > iix; ii--) // make empty slot xxrec_tab[ii] = xxrec_tab[ii-1]; xxrec_tab[iix] = xxrec_new; // insert new entry Nxxrec++; } if (Freplace) { zfree(xxrec_tab[iix]->file); // replace old entry 20.0 zfree(xxrec_tab[iix]->tags); zfree(xxrec_tab[iix]->capt); zfree(xxrec_tab[iix]->comms); zfree(xxrec_tab[iix]->location); zfree(xxrec_tab[iix]->country); zfree(xxrec_tab[iix]->xmeta); zfree(xxrec_tab[iix]); xxrec_tab[iix] = xxrec_new; // replace with new entry } fid = fopen(index_file,"a"); // append new record to image index file if (! fid) goto file_err; nn = fprintf(fid,"file: %s\n",RP); // file real path name if (! nn) goto file_err; nn = fprintf(fid,"data: %s %s %s %d %d %d\n", // file date, photo date, rating, xxrec_new->fdate, xxrec_new->pdate, xxrec_new->rating, // width, height, file size xxrec_new->ww, xxrec_new->hh, xxrec_new->fsize); if (! nn) goto file_err; nn = fprintf(fid,"tags: %s\n",xxrec_new->tags); // tags record if (! nn) goto file_err; nn = fprintf(fid,"capt: %s\n",xxrec_new->capt); // caption record if (! nn) goto file_err; nn = fprintf(fid,"comms: %s\n",xxrec_new->comms); // comments record if (! nn) goto file_err; nn = fprintf(fid,"gtags: %s^ %s^ %.4f^ %.4f\n", xxrec_new->location, xxrec_new->country, xxrec_new->flati, xxrec_new->flongi); if (! nn) goto file_err; nn = fprintf(fid,"xmeta: %s\n",xxrec_new->xmeta); if (! nn) goto file_err; nn = fprintf(fid,"END\n"); // EOL if (! nn) goto file_err; err = fclose(fid); if (err) goto file_err; return 0; file_err: zmessageACK(Mwin,"image index write error\n %s",strerror(errno)); if (fid) fclose(fid); fid = 0; return 3; } /********************************************************************************/ // Read image index files sequentially, return one index record per call. // Set ftf = 1 for first read, will be reset to 0. // Returns xxrec or null for EOF or error. // Returned xxrec_t and its allocated pointers are subject to zfree(). // Used by index_rebuild() function. xxrec_t * read_xxrec_seq(int &ftf) { xxrec_t *xxrec = 0; static FILE *fid = 0; static char buff[indexrecl]; cchar *pp, *pp2; float flati, flongi; char fdate[16], pdate[16], rating[4]; int nn, ww, hh, fsize; if (ftf) // initial call { ftf = 0; fid = fopen(index_file,"r"); if (! fid) return 0; // no index file ? *buff = 0; // insure no leftover data } while (true) // read to next "file: " record { pp = fgets_trim(buff,indexrecl,fid); if (! pp) { fclose(fid); // EOF return 0; } if (strmatchN(pp,"file: ",6)) break; } xxrec = (xxrec_t *) zmalloc(sizeof(xxrec_t)); // allocate returned xxrec memset(xxrec,0,sizeof(xxrec_t)); xxrec->file = zstrdup(buff+6); // image file name while (true) // get recs following "file" record { pp = fgets_trim(buff,indexrecl,fid); if (! pp) break; if (strmatchN(pp,"END",3)) break; // end of recs for this file else if (strmatchN(pp,"data: ",6)) { // new format 19.0 nn = sscanf(pp+6,"%15s %15s %1s %d %d %d", fdate, pdate, rating, &ww, &hh, &fsize); // file date, photo date, rating, if (nn == 6) { // width, height, file size strncpy0(xxrec->fdate,fdate,16); if (strmatch(pdate,"undated")) strcpy(xxrec->pdate,""); else strncpy0(xxrec->pdate,pdate,16); strncpy0(xxrec->rating,rating,2); xxrec->ww = ww; xxrec->hh = hh; xxrec->fsize = fsize; } } else if (strmatchN(pp,"tags: ",6)) // tags xxrec->tags = zstrdup(pp+6); else if (strmatchN(pp,"capt: ",6)) // caption xxrec->capt = zstrdup(pp+6); else if (strmatchN(pp,"comms: ",7)) // comments xxrec->comms = zstrdup(pp+7); else if (strmatchN(pp,"gtags: ",7)) { // geotags pp += 7; pp2 = strField(pp,"^",1); if (pp2) xxrec->location = zstrdup(pp2); else xxrec->location = zstrdup(""); pp2 = strField(pp,"^",2); if (pp2) xxrec->country = zstrdup(pp2); else xxrec->country = zstrdup(""); flati = flongi = 999; pp2 = strField(pp,"^",3); if (pp2) flati = atof(pp2); pp2 = strField(pp,"^",4); if (pp2) flongi = atof(pp2); if (flati < -90.0 || flati > 90.0) flati = flongi = 0; if (flongi < -180.0 || flongi > 180.0) flati = flongi = 0; xxrec->flati = flati; xxrec->flongi = flongi; } else if (strmatchN(pp,"xmeta: ",7)) // indexed metadata xxrec->xmeta = zstrdup(pp+7); } if (! xxrec->fdate[0]) // supply defaults for missing items strcpy(xxrec->fdate,""); if (! xxrec->pdate[0]) strcpy(xxrec->pdate,""); if (! xxrec->rating[0]) strcpy(xxrec->rating,"0"); if (! xxrec->tags) xxrec->tags = zstrdup(""); if (! xxrec->capt) xxrec->capt = zstrdup(""); if (! xxrec->comms) xxrec->comms = zstrdup(""); if (! xxrec->location) xxrec->location = zstrdup(""); if (! xxrec->country) xxrec->country = zstrdup(""); if (! xxrec->xmeta) xxrec->xmeta = zstrdup(""); return xxrec; } /********************************************************************************/ // Write the image index files sequentially, 1 record per call // Set ftf = 1 for first call, will be reset to 0. // Set xxrec = 0 to close file after last write. // Returns 0 if OK, otherwise +N (diagnosed). // Used by index_rebuild() function. int write_xxrec_seq(xxrec_t *xxrec, int &ftf) // file need not exist 19.10 { static FILE *fid = 0; int err, nn; char pdate[16]; if (ftf) // first call { ftf = 0; fid = fopen(index_file,"w"); if (! fid) goto file_err; } if (! xxrec) { // EOF call if (fid) { err = fclose(fid); fid = 0; if (err) goto file_err; } return 0; } nn = fprintf(fid,"file: %s\n",xxrec->file); // output: filename record if (! nn) goto file_err; strncpy0(pdate,xxrec->pdate,16); // 20.0 if (! *pdate) strcpy(pdate,"undated"); nn = fprintf(fid,"data: %s %s %s %d %d %d\n", // file date, photo date, rating, 19.0 xxrec->fdate, pdate, xxrec->rating, // width, height, file size xxrec->ww, xxrec->hh, xxrec->fsize); if (! nn) goto file_err; if (xxrec->tags) nn = fprintf(fid,"tags: %s\n",xxrec->tags); // tags: aaaaa, bbbbb, ... else nn = fprintf(fid,"tags: \n"); if (! nn) goto file_err; if (xxrec->capt) nn = fprintf(fid,"capt: %s\n",xxrec->capt); // caption: text else nn = fprintf(fid,"capt: \n"); if (! nn) goto file_err; if (xxrec->comms) nn = fprintf(fid,"comms: %s\n",xxrec->comms); // comments: text else nn = fprintf(fid,"comms: \n"); if (! nn) goto file_err; nn = fprintf(fid,"gtags: %s^ %s^ %.4f^ %.4f\n", xxrec->location, xxrec->country, xxrec->flati, xxrec->flongi); if (! nn) goto file_err; if (xxrec->xmeta) nn = fprintf(fid,"xmeta: %s\n",xxrec->xmeta); // xmeta: name1^ name2^ ... else nn = fprintf(fid,"xmeta: \n"); if (! nn) goto file_err; nn = fprintf(fid,"END\n"); // EOL if (! nn) goto file_err; return 0; file_err: zmessageACK(Mwin,"image index write error\n %s",strerror(errno)); if (fid) fclose(fid); fid = 0; quitxx(); // unconditional exit 20.05 return 2; } fotoxx-20.08/f.pixmap.cc000066400000000000000000004672751362435004500151320ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* Fotoxx image edit - functions for pixmap memory images PXM_make create PXM pixmap RGB[A] float PXM_free free PXM pixmap memory PXM_audit audit PXM pixmap for errors PXM_clear clear PXM pixmap white/opaque or black/transparent PXM_addalpha add alhpa channel to PXM pixmap PXM_subalpha remove alhpa channel from PXM pixmap PXM_copy copy (duplicate) PXM pixmap PXM_copy_area copy area from one PXM pixmap to another PXM_rescale copy and rescale PXM pixmap PXM_rotate copy and rotate PXM pixmap PXB_make create PXB pixmap RGB[A] uint8 PXB_free free PXB pixmap memory PXB_addalpha add alhpa channel to PXB pixmap PXB_subalpha remove alpha channel from PXB pixmap PXB_copy copy (duplicate) PXB pixmap PXB_copy_area copy area from one PXB into another PXB_subpxb create new PXB from section of existing PXB PXB_half rescale PXB pixmap to 1/2 size PXB_rescale rescale PXB pixmap - ww/hh independent PXB_resize resize PXB pixmap - max. ww/hh, preserve ratio PXB_rescale_fast fast rescale PXB pixmap - ww/hh independent PXB_resize_fast fast resize PXB pixmap - max. ww/hh, preserve ratio PXB_rotate rotate PXB pixmap, any angle vpixel get virtual pixel at any PXM or PXB image location (float) PXM_PXB_copy copy PXM pixmap to PXB pixmap PXM_PXB_update copy/convert PXM pixmap area to PXB pixmap area PXB_PXB_update copy/rescale input PXB pixmap area to output PXB area PXB_load load an image file into a PXB pixmap (8-bit RGB) PXM_load load an image file into a PXM pixmap (float RGB) PXB_save save a PXB pixmap to a file with (8 bit RGB) PXM_save save a PXM pixmap to a file with (8/16 bit RGB) JPG_PXB_load load a .jpg file into a PXB pixmap (8-bit RGB) JPG_PXM_load load a .jpg file into a PXM pixmap (float RGB) PXB_JPG_save save a PXB pixmap to a .jpg file (8-bit RGB) PXM_JPG_save save a PXM pixmap to a .jpg file (8-bit RGB) TIFF_PXB_load load a .tif file into a PXB pixmap (8-bit RGB) TIFF_PXM_load load a .tif file into a PXM pixmap (float RGB) PXB_TIFF_save save a PXB pixmap to a .tif file (8/16-bit 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 (8-bit RGB) PNG_PXM_load load a .png file into a PXM pixmap (float RGB) PXB_PNG_save save a PXB pixmap to a .png file (8/16-bit RGB) PXM_PNG_save save a PXM pixmap to a .png file (8/16-bit RGB) HEIC_PXB_load load a .heic file into a PXB pixmap (8-bit RGB) 20.0 HEIC_PXM_load load a .heic file into a PXM pixmap (float RGB) PXB_HEIC_save save a PXB pixmap to a .heic file (8/16-bit RGB) PXM_HEIC_save save a PXM pixmap to a .heic file (8/16-bit RGB) JP2_PXB_load load a .jp2 file into a PXB pixmap (8-bit RGB) 20.0 JP2_PXM_load load a .jp2 file into a PXM pixmap (float RGB) PXB_JP2_save save a PXB pixmap to a .jp2 file (8/16-bit RGB) PXM_JP2_save save a PXM pixmap to a .jp2 file (8/16-bit RGB) ANY_PXB_load load other image file into a PXB pixmap (8-bit RGB) ANY_PXM_load load other image file into a PXM pixmap (float RGB) RAW_PXB_load load a RAW file into a PXB pixmap (8-bit RGB) RAW_PXM_load load a RAW file into a PXM pixmap (float RGB) RAW_PXB_load_dcraw load a RAW file into a PXB pixmap (8-bit RGB) 20.0 RAW_PXM_load_dcraw load a RAW file into a PXM pixmap (float RGB) RAW_PXB_load_rawtherapee load a RAW file into a PXB pixmap (8-bit RGB) 20.0 RAW_PXM_load_rawtherapee load a RAW file into a PXM pixmap (float RGB) pixelvert convert pixel format and channel count *********************************************************************************/ #define EX extern // disable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************* PXM pixmap functions - RGB[A] float pixel map pixel RGB values may range from 0.0 to 255.99 *********************************************************************************/ // initialize PXM pixmap - allocate memory PXM * PXM_make(int ww, int hh, int nc) { int64 ww6 = ww, hh6 = hh; int64 npix = ww6 * hh6; int64 cc = npix * nc * sizeof(float); if (ww < 10 || hh < 10) { // impose reasonableness limits zmessageACK(Mwin,"image too small: %d x %d",ww,hh); return 0; } if (ww > wwhh_limit1 || hh > wwhh_limit1 || npix > wwhh_limit2) { // avoid image dimensions too big zmessageACK(Mwin,"image too big: %dx%d",ww,hh); return 0; } PXM *pxm = (PXM *) zmalloc(sizeof(PXM)); strcpy(pxm->wmi,"pxmpix"); pxm->ww = ww; pxm->hh = hh; pxm->nc = nc; // nc = 3/4 for no/alpha channel pxm->rs = ww * nc; // bytes = rs * sizeof(float) 19.0 pxm->pixels = (float *) zmalloc(cc); // crashes if too big return pxm; } // free PXM pixmap void PXM_free(PXM *&pxm) { if (! pxm) return; if (! strmatch(pxm->wmi,"pxmpix")) zappcrash("PXM_free(), bad PXM %s",pxm->wmi); strcpy(pxm->wmi,"xxxxxx"); zfree(pxm->pixels); zfree(pxm); pxm = 0; return; } // audit the contents of a PXM pixmap void PXM_audit(PXM *pxm) { int px, py; int err1 = 0, err2 = 0; // check RGB within bounds 20.0 float *pix; for (py = 0; py < pxm->hh; py++) for (px = 0; px < pxm->ww; px++) { pix = PXMpix(pxm,px,py); for (int ii = 0; ii < 3; ii++) { if (pix[ii] < 0) { err1 = 1; pix[ii] = 0; } if (pix[ii] >= 256.0) { err2 = 1; pix[ii] = 255.9; } } } if (err1) printz("PXM_audit(): corrected RGB < 0 \n"); if (err2) printz("PXM_audit(): corrected RGB >= 256.0 \n"); return; } // clear a PXM pixmap to white/opaque or black/transparent void PXM_clear(PXM *pxm, int BW) { int px, py, ii; float *pix, rgba; if (BW) rgba = 255.0; else rgba = 0; for (py = 0; py < pxm->hh; py++) for (px = 0; px < pxm->ww; px++) { pix = PXMpix(pxm,px,py); for (ii = 0; ii < pxm->nc; ii++) pix[ii] = rgba; } return; } // add an alpha channel to a PXM pixmap (RGB >> RGBA) void PXM_addalpha(PXM *pxm) { int64 cc; // 19.15 int ww, hh, nc1, nc2, ii; float *pixels1, *pixels2, *pix1, *pix2; ww = pxm->ww; hh = pxm->hh; nc1 = pxm->nc; if (nc1 > 3) return; pixels1 = pxm->pixels; nc2 = nc1 + 1; 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->rs = ww * nc2; // bytes = rs * sizeof(float) 19.0 pxm->pixels = pixels2; return; } // remove alpha channel from a PXM pixmap (RGBA >> RGB) void PXM_subalpha(PXM *pxm) // 20.0 { uint cc; int ww, hh, nc1, nc2, ii; float *pixels1, *pixels2, *pix1, *pix2; ww = pxm->ww; hh = pxm->hh; nc1 = pxm->nc; if (nc1 < 4) return; pixels1 = pxm->pixels; nc2 = 3; cc = ww * hh * nc2 * sizeof(float); pixels2 = (float *) zmalloc(cc); cc = nc2 * sizeof(float); pix1 = pixels1; pix2 = pixels2; for (ii = 0; ii < ww * hh; ii++) { memcpy(pix2,pix1,cc); pix1 += nc1; pix2 += nc2; } zfree(pixels1); pxm->nc = nc2; pxm->rs = ww * nc2; pxm->pixels = pixels2; return; } // create a copy of a PXM pixmap PXM * PXM_copy(PXM *pxm1) { uint cc; int ww, hh, nc; PXM *pxm2; ww = pxm1->ww; hh = pxm1->hh; nc = pxm1->nc; cc = ww * hh * nc * sizeof(float); pxm2 = PXM_make(ww,hh,nc); if (! pxm2) return 0; memcpy(pxm2->pixels,pxm1->pixels,cc); return pxm2; } // create a copy of a PXM pixmap rectangular area PXM * PXM_copy_area(PXM *pxm1, int orgx, int orgy, int ww2, int hh2) { float *pix1, *pix2; PXM *pxm2 = 0; int px1, py1, px2, py2; int nc, pcc; nc = pxm1->nc; pcc = nc * sizeof(float); pxm2 = PXM_make(ww2,hh2,nc); if (! pxm2) return 0; 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 pixmap to new width and height. The scale ratios may be different for width and height. Method is better than bilinear: For all input pixels [p] having some overlap area with an output pixel: output pixel = sum ( overlap[p] * input[p] ) / sum ( overlap[p] ) NOT THREAD SAFE *********************************************************************************/ namespace pxmrescale { float *ppix1, *ppix2; int ww1, hh1, ww2, hh2, nc; int *px1L, *py1L; float *pxmap, *pymap; int maxmapx, maxmapy; int nwt; } PXM * PXM_rescale(PXM *pxm1, int ww, int hh) { using namespace pxmrescale; void * pxm_rescale_thread(void *arg); PXM *pxm2; int px1, py1, px2, py2; int pxl, pyl, pxm, pym, ii; float scalex, scaley; float px1a, py1a, px1b, py1b; float fx, fy; ww1 = pxm1->ww; // input PXM hh1 = pxm1->hh; nc = pxm1->nc; ppix1 = pxm1->pixels; pxm2 = PXM_make(ww,hh,nc); // output PXM if (! pxm2) return 0; // too big or no memory ww2 = ww; hh2 = hh; ppix2 = pxm2->pixels; memset(ppix2, 0, ww2 * hh2 * nc * sizeof(float)); // clear output pixmap 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; } nwt = NWT; // threads if (nwt > 4) nwt = 4; // slower > 4 19.15 do_wthreads(pxm_rescale_thread,nwt); zfree(px1L); zfree(py1L); zfree(pxmap); zfree(pymap); return pxm2; } void * pxm_rescale_thread(void *arg) // worker thread function { using namespace pxmrescale; int index = *((int *) arg); int px1, py1, px2, py2; int pxl, pyl, pxm, pym, ii; float *pixel1, *pixel2; float fx, fy, ftot; float chan[6]; int pcc = nc * sizeof(float); for (py2 = index; py2 < hh2; py2 += nwt) // loop output y-pixels { pyl = py1L[py2]; // corresp. 1st input y-pixel for (px2 = 0; px2 < ww2; px2++) // loop output x-pixels { pxl = px1L[px2]; // corresp. 1st input x-pixel memset(chan,0,pcc); // initz. output pixel for (py1 = pyl, pym = 0; ; py1++, pym++) // loop overlapping input y-pixels { ii = py2 * maxmapy + pym; // get y-overlap fy = pymap[ii]; if (fy < 0) break; // no more pixels for (px1 = pxl, pxm = 0; ; px1++, pxm++) // loop overlapping input x-pixels { ii = px2 * maxmapx + pxm; // get x-overlap fx = pxmap[ii]; if (fx < 0) break; // no more pixels ftot = fx * fy; // area overlap = x * y overlap pixel1 = ppix1 + (py1 * ww1 + px1) * nc; for (ii = 0; ii < nc; ii++) chan[ii] += pixel1[ii] * ftot; // add input pixel * overlap } } pixel2 = ppix2 + (py2 * ww2 + px2) * nc; // save output pixel memcpy(pixel2,chan,pcc); } } pthread_exit(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 accommodate 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. NOT THREAD SAFE *********************************************************************************/ namespace pxmrotate { float *ppix1, *ppix2; int ww1, hh1, ww2, hh2, nc; float angle; } PXM * PXM_rotate(PXM *pxm1, float anglex, int fast) // fast option { using namespace pxmrotate; PXM *PXM_rotate90(PXM *pxm, int angle); void *PXM_rotate_thread(void *); void *PXM_rotate_thread_fast(void *); PXM *pxm2; ww1 = pxm1->ww; // input PXM hh1 = pxm1->hh; nc = pxm1->nc; ppix1 = pxm1->pixels; angle = anglex; while (angle < -180) angle += 360; // normalize, -180 to +180 while (angle > 180) angle -= 360; if (angle >= -180.0 && angle < -179.99) // use lossless version for angles return PXM_rotate90(pxm1,180); // of -180, -90, 0, 90, 180 if (angle > -90.01 && angle < -89.99) return PXM_rotate90(pxm1,-90); if (angle > -0.01 && angle < 0.01) return PXM_copy(pxm1); if (angle > 89.99 && angle < 90.01) return PXM_rotate90(pxm1,90); if (angle > 179.99 && angle <= 180.0) return PXM_rotate90(pxm1,180); angle = angle * PI / 180; // radians, -PI to +PI ww2 = ww1*fabsf(cosf(angle)) + hh1*fabsf(sinf(angle)); // rectangle containing rotated image hh2 = ww1*fabsf(sinf(angle)) + hh1*fabsf(cosf(angle)); pxm2 = PXM_make(ww2,hh2,nc); // output PXM if (! pxm2) return 0; ppix2 = pxm2->pixels; if (fast) do_wthreads(PXM_rotate_thread_fast,NWT); else do_wthreads(PXM_rotate_thread,NWT); return pxm2; } void * PXM_rotate_thread(void *arg) { using namespace pxmrotate; int index = *((int *) (arg)); int px2, py2, px0, py0; float *pix0, *pix1, *pix2, *pix3; float px1, py1; float f0, f1, f2, f3, chan[6]; float a, b, ww15, hh15, ww25, hh25; int pcc = nc * sizeof(float); ww15 = 0.5 * ww1; hh15 = 0.5 * hh1; ww25 = 0.5 * ww2; hh25 = 0.5 * hh2; a = cosf(angle); b = sinf(angle); for (py2 = index; py2 < hh2; py2 += NWT) // loop through output pixels for (px2 = 0; px2 < ww2; px2++) { px1 = a * (px2 - ww25) + b * (py2 - hh25) + ww15; // (px1,py1) = corresponding py1 = -b * (px2 - ww25) + a * (py2 - hh25) + hh15; // point within input pixels px0 = px1; // pixel containing (px1,py1) py0 = py1; if (px0 < 0 || px0 > ww1-2 || py0 < 0 || py0 > hh1-2) { // if outside input pixel array pix2 = ppix2 + (py2 * ww2 + px2) * nc; // output is black memset(pix2,0,pcc); continue; } pix0 = ppix1 + (py0 * ww1 + px0) * nc; // 4 input pixels based at (px0,py0) pix1 = pix0 + ww1 * nc; pix2 = pix0 + nc; pix3 = pix1 + nc; f0 = (px0+1 - px1) * (py0+1 - py1); // overlap of (px1,py1) f1 = (px0+1 - px1) * (py1 - py0); // in each of the 4 pixels f2 = (px1 - px0) * (py0+1 - py1); f3 = (px1 - px0) * (py1 - py0); for (int ii = 0; ii < nc; ii++) // sum the weighted inputs chan[ii] = f0 * pix0[ii] + f1 * pix1[ii] + f2 * pix2[ii] + f3 * pix3[ii]; pix2 = ppix2 + (py2 * ww2 + px2) * nc; // output pixel memcpy(pix2,chan,pcc); } pthread_exit(0); } void * PXM_rotate_thread_fast(void *arg) { using namespace pxmrotate; int index = *((int *) (arg)); int px2, py2, px0, py0; float *pix0, *pix2; float px1, py1; float a, b, ww15, hh15, ww25, hh25; int pcc = nc * sizeof(float); ww15 = 0.5 * ww1; hh15 = 0.5 * hh1; ww25 = 0.5 * ww2; hh25 = 0.5 * hh2; a = cosf(angle); b = sinf(angle); for (py2 = index; py2 < hh2; py2 += NWT) // loop through output pixels for (px2 = 0; px2 < ww2; px2++) { px1 = a * (px2 - ww25) + b * (py2 - hh25) + ww15; // (px1,py1) = corresponding py1 = -b * (px2 - ww25) + a * (py2 - hh25) + hh15; // point within input pixels px0 = px1; // pixel containing (px1,py1) py0 = py1; if (px0 < 0 || px0 > ww1-2 || py0 < 0 || py0 > hh1-2) { // if outside input pixel array pix2 = ppix2 + (py2 * ww2 + px2) * nc; // output is black memset(pix2,0,pcc); continue; } pix0 = ppix1 + (py0 * ww1 + px0) * nc; // input pixel pix2 = ppix2 + (py2 * ww2 + px2) * nc; // output pixel memcpy(pix2,pix0,pcc); } pthread_exit(0); } PXM * PXM_rotate90(PXM *pxm1, int angle) // angle = -90, 90, 180 { using namespace pxmrotate; int px1, py1, px2, py2, nc, pcc; float *pix1, *pix2; PXM *pxm2; if (angle == 0) return PXM_copy(pxm1); ww1 = pxm1->ww; // input PXM hh1 = pxm1->hh; nc = pxm1->nc; pcc = nc * sizeof(float); if (angle == -90) { ww2 = hh1; hh2 = ww1; } else if (angle == 90) { ww2 = hh1; hh2 = ww1; } else if (angle == 180) { ww2 = ww1; hh2 = hh1; } else zappcrash("PXM_rotate2() bad angle %d",angle); pxm2 = PXM_make(ww2,hh2,nc); // output PXM if (! pxm2) return 0; 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[A] uint8 pixel map and PIXBUF wrapper *********************************************************************************/ // Create PXB pixmap with pixels cleared to zero // nc = 3/4 for RGB/RGBA image PXB * PXB_make(int ww, int hh, int nc) // nc instead of ac 19.0 { uint rs, cc, ac; uint8 *pixels; if (nc != 3 && nc != 4) zappcrash("PXB_make() nc: %d",nc); PXB *pxb = (PXB *) zmalloc(sizeof(PXB)); strcpy(pxb->wmi,"pxbpix"); pxb->ww = ww; pxb->hh = hh; pxb->nc = nc; rs = ww * nc; pxb->rs = rs; cc = ww * hh * nc; pixels = (uint8 *) zmalloc(cc); pxb->pixels = pixels; ac = nc - 3; // alpha channel 0/1 pxb->pixbuf = gdk_pixbuf_new_from_data(pixels,GDKRGB,ac,8,ww,hh,rs,0,0); // make congruent pixbuf if (! pxb->pixbuf) { zmessageACK(Mwin,"PXB_make(): pixbuf allocation failure"); quitxx(); } return pxb; } // Free PXB pixmap - release memory void PXB_free(PXB *&pxb) { if (! pxb) return; if (! strmatch(pxb->wmi,"pxbpix")) zappcrash("PXB_free(), bad PXB"); strcpy(pxb->wmi,"xxxxxx"); g_object_unref(pxb->pixbuf); zfree(pxb->pixels); zfree(pxb); pxb = 0; return; } // add an alpha channel to a PXB pixmap void PXB_addalpha(PXB *pxb) { uint8 *pixels1, *pixels2, *pix1, *pix2; int ww, hh, nc, rs, cc; nc = pxb->nc; if (nc == 4) return; if (nc != 3) zappcrash("PXB_addalpha() nc: %d",nc); ww = pxb->ww; hh = pxb->hh; pixels1 = pxb->pixels; cc = ww * hh * 4; pixels2 = (uint8 *) zmalloc(cc); pix1 = pixels1; pix2 = pixels2; for (int ii = 0; ii < ww * hh; ii++) { memcpy(pix2,pix1,3); pix2[3] = 255; // opacity = max. pix1 += 3; pix2 += 4; } zfree(pixels1); pxb->pixels = pixels2; pxb->nc = 4; rs = ww * 4; pxb->rs = rs; g_object_unref(pxb->pixbuf); pxb->pixbuf = gdk_pixbuf_new_from_data(pixels2,GDKRGB,1,8,ww,hh,rs,0,0); if (! pxb->pixbuf) { zmessageACK(Mwin,"PXB_addalpha(): pixbuf allocation failure"); quitxx(); } return; } // remove an alpha channel from a PXB pixmap void PXB_subalpha(PXB *pxb) { uint8 *pixels1, *pixels2, *pix1, *pix2; int ww, hh, nc, rs, cc; nc = pxb->nc; if (nc == 3) return; if (nc != 4) zappcrash("PXB_subalpha() nc: %d",nc); ww = pxb->ww; hh = pxb->hh; pixels1 = pxb->pixels; cc = ww * hh * 3; pixels2 = (uint8 *) zmalloc(cc); pix1 = pixels1; pix2 = pixels2; for (int ii = 0; ii < ww * hh; ii++) { memcpy(pix2,pix1,3); pix1 += 4; pix2 += 3; } zfree(pixels1); pxb->pixels = pixels2; pxb->nc = 3; rs = ww * 3; pxb->rs = rs; g_object_unref(pxb->pixbuf); pxb->pixbuf = gdk_pixbuf_new_from_data(pixels2,GDKRGB,0,8,ww,hh,rs,0,0); if (! pxb->pixbuf) { zmessageACK(Mwin,"PXB_subalpha(): pixbuf allocation failure"); quitxx(); } return; } // Copy a PXB pixmap to a new PXB pixmap PXB * PXB_copy(PXB *pxb1) { int ww, hh, nc, cc; ww = pxb1->ww; hh = pxb1->hh; nc = pxb1->nc; PXB *pxb2 = PXB_make(ww,hh,nc); cc = ww * hh * nc; memcpy(pxb2->pixels,pxb1->pixels,cc); return pxb2; } // copy an area from an input PXB to an output PXB // input area (px1, py1, ww, hh) copied to output area at (px2, py2) void PXB_copy_area(PXB *pxb1, int px1, int py1, int ww, int hh, PXB *pxb2, int px2, int py2) { int row1, row2; if (px1 < 0) goto overflow; if (px1 + ww > pxb1->ww) goto overflow; if (py1 < 0) goto overflow; if (py1 + hh > pxb1->hh) goto overflow; if (px2 < 0) goto overflow; if (px2 + ww > pxb2->ww) goto overflow; if (py2 < 0) goto overflow; if (py2 + hh > pxb2->hh) goto overflow; if (pxb1->nc != pxb2->nc) zappcrash("PXB_copy_area(): NC unequal"); for (row1 = py1, row2 = py2; row1 < py1 + hh; row1++, row2++) memcpy(pxb2->pixels + row2 * pxb2->rs + px2 * pxb2->nc, pxb1->pixels + row1 * pxb1->rs + px1 * pxb1->nc, ww * pxb1->nc); return; overflow: zappcrash("PXB_copy_area() overflow: input: %d %d %d %d " "output: %d %d",px1,py1,ww,hh,px2,py2); } // create a new PXB pixmap from a subsection of an existing PXB PXB * PXB_subpxb(PXB *pxb1, int px1, int py1, int ww, int hh) { PXB *pxb2 = PXB_make(ww,hh,pxb1->nc); if (! pxb2) return 0; PXB_copy_area(pxb1,px1,py1,ww,hh,pxb2,0,0); return pxb2; } /********************************************************************************/ // rescale a PXB pixmap image to 1/2 size using NWT threads // if rows or columns are odd, the last one is omitted typedef struct { uint8 *pixels1, *pixels2; int ww1, hh1, rs1, nc1; int ww2, hh2, rs2, nc2; } pxb_half_data_t; PXB * PXB_half(PXB *pxb1) { void * pxb_half_thread(void *arg); pxb_half_data_t P; PXB *pxb2; P.ww1 = pxb1->ww; // input image data P.hh1 = pxb1->hh; P.rs1 = pxb1->rs; P.nc1 = pxb1->nc; P.pixels1 = pxb1->pixels; P.ww2 = P.ww1 / 2; // output image data P.hh2 = P.hh1 / 2; P.nc2 = P.nc1; pxb2 = PXB_make(P.ww2,P.hh2,P.nc2); // output PXB P.pixels2 = pxb2->pixels; P.rs2 = pxb2->rs; void *args[NWT][2]; pthread_t tid[NWT]; for (int ii = 0; ii < NWT; ii++) { args[ii][0] = &P; args[ii][1] = &Nval[ii]; tid[ii] = start_Jthread(pxb_half_thread,args[ii]); } for (int ii = 0; ii < NWT; ii++) wait_Jthread(tid[ii]); return pxb2; } void * pxb_half_thread(void *arg) { int row, col, ch, nc, rgb; uint8 *pix1a, *pix1b, *pix2; void ** args = (void **) arg; pxb_half_data_t *P = (pxb_half_data_t *) args[0]; int index = * ((int *) args[1]); nc = P->nc1; for (row = index; row < P->hh2; row += NWT) // loop output rows { pix1a = P->pixels1 + P->rs1 * row * 2; // 2 input rows pix1b = pix1a + P->rs1; pix2 = P->pixels2 + P->rs2 * row; // output row for (col = 0; col < P->ww2 * nc; col += nc) // output col { for (ch = 0; ch < nc; ch++) // loop channels { rgb = pix1a[ch] + pix1a[ch+nc] + pix1b[ch] + pix1b[ch+nc]; // 4 input RGB[A] values pix2[ch] = rgb / 4; // --> 1 output RGB[A] } pix1a += 2 * nc; pix1b += 2 * nc; pix2 += nc; } } pthread_exit(0); } /******************************************************************************** Rescale PXB pixmap image to new width and height. 19.0 The scale ratios may be different for width and height. Method is better than bilinear: For all input pixels [p] having some overlap area with an output pixel: output pixel = sum ( overlap[p] * input[p] ) / sum ( overlap[p] ) *********************************************************************************/ typedef struct { uint8 *pixels1; uint8 *pixels2; int ww1, hh1, rs1; int ww2, hh2, rs2; int nc1, nc2; int *px1L, *py1L; float *pxmap, *pymap; int maxmapx, maxmapy; } pxb_rescale_data_t; PXB * PXB_rescale(PXB *pxb1, int ww, int hh) { void * pxb_rescale_thread(void *arg); pxb_rescale_data_t P; PXB *pxb2; int px1, py1, px2, py2; int pxl, pyl, pxm, pym, ii; float scalex, scaley; float px1a, py1a, px1b, py1b; float fx, fy; int Fhalf = 0; if (ww <= pxb1->ww/2 && hh <= pxb1->hh/2) { // if scaling down to < 1/2 size, pxb1 = PXB_half(pxb1); // use fast 1/2 scale down first Fhalf = 1; } P.ww1 = pxb1->ww; P.hh1 = pxb1->hh; P.nc1 = pxb1->nc; P.rs1 = pxb1->rs; P.pixels1 = pxb1->pixels; pxb2 = PXB_make(ww,hh,P.nc1); // output PXB P.ww2 = ww; // output image data P.hh2 = hh; P.rs2 = pxb2->rs; P.nc2 = P.nc1; P.pixels2 = pxb2->pixels; scalex = 1.0 * P.ww1 / P.ww2; // compute x and y scales scaley = 1.0 * P.hh1 / P.hh2; if (scalex <= 1) P.maxmapx = 2; // compute max input pixels else P.maxmapx = scalex + 2; // mapping into output pixels P.maxmapx += 1; // for both dimensions if (scaley <= 1) P.maxmapy = 2; // (pixels may not be square) else P.maxmapy = scaley + 2; P.maxmapy += 1; // (extra entry for -1 flag) P.pymap = (float *) zmalloc(P.hh2 * P.maxmapy * sizeof(float)); // maps overlap of < maxmap input P.pxmap = (float *) zmalloc(P.ww2 * P.maxmapx * sizeof(float)); // pixels per output pixel P.py1L = (int *) zmalloc(P.hh2 * sizeof(int)); // maps first (lowest) input pixel P.px1L = (int *) zmalloc(P.ww2 * sizeof(int)); // per output pixel for (py2 = 0; py2 < P.hh2; py2++) // loop output y-pixels { py1a = py2 * scaley; // corresponding input y-pixels py1b = py1a + scaley; if (py1b >= P.hh1) py1b = P.hh1 - 0.001; // fix precision limitation pyl = py1a; P.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 * P.maxmapy + pym; // save it P.pymap[ii] = 0.9999 * fy / scaley; } ii = py2 * P.maxmapy + pym; // set an end marker after P.pymap[ii] = -1; // last overlapping pixel } for (px2 = 0; px2 < P.ww2; px2++) // do same for x-pixels { px1a = px2 * scalex; px1b = px1a + scalex; if (px1b >= P.ww1) px1b = P.ww1 - 0.001; pxl = px1a; P.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 * P.maxmapx + pxm; P.pxmap[ii] = 0.9999 * fx / scalex; } ii = px2 * P.maxmapx + pxm; P.pxmap[ii] = -1; } void *args[NWT][2]; pthread_t tid[NWT]; for (ii = 0; ii < NWT; ii++) { args[ii][0] = &P; args[ii][1] = &Nval[ii]; tid[ii] = start_Jthread(pxb_rescale_thread,args[ii]); } for (ii = 0; ii < NWT; ii++) wait_Jthread(tid[ii]); zfree(P.px1L); zfree(P.py1L); zfree(P.pxmap); zfree(P.pymap); if (Fhalf) PXB_free(pxb1); return pxb2; } void * pxb_rescale_thread(void *arg) // worker thread function { int px1, py1, px2, py2; int pxl, pyl, pxm, pym, ii; uint8 *pix1, *pix2; float fx, fy, ftot; float red, green, blue, alpha; void ** args = (void **) arg; pxb_rescale_data_t *P = (pxb_rescale_data_t *) args[0]; int index = * ((int *) args[1]); for (py2 = index; py2 < P->hh2; py2 += NWT) // loop output y-pixels { pyl = P->py1L[py2]; // corresp. 1st input y-pixel for (px2 = 0; px2 < P->ww2; px2++) // loop output x-pixels { pxl = P->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 * P->maxmapy + pym; // get y-overlap fy = P->pymap[ii]; if (fy < 0) break; // no more pixels for (px1 = pxl, pxm = 0; ; px1++, pxm++) // loop overlapping input x-pixels { ii = px2 * P->maxmapx + pxm; // get x-overlap fx = P->pxmap[ii]; if (fx < 0) break; // no more pixels ftot = fx * fy; // area overlap = x * y overlap pix1 = P->pixels1 + py1 * P->rs1 + px1 * P->nc1; red += pix1[0] * ftot; // add input pixel * overlap green += pix1[1] * ftot; blue += pix1[2] * ftot; if (P->nc1 == 4) alpha += pix1[3] * ftot; } pix2 = P->pixels2 + py2 * P->rs2 + px2 * P->nc2; // save output pixel pix2[0] = red; pix2[1] = green; pix2[2] = blue; if (P->nc2 == 4) pix2[3] = alpha; } } } pthread_exit(0); } /********************************************************************************/ // resize PXB to given max. width or height, preserving aspect ratio // same as PXB_rescale() but one size argument instead of width and height PXB * PXB_resize(PXB *pxb1, int size) { int ww1, hh1, ww2, hh2; PXB *pxb2; ww1 = pxb1->ww; hh1 = pxb1->hh; if (ww1 >= hh1) { ww2 = size; hh2 = 1.0 * size * hh1 / ww1 + 0.5; } else { hh2 = size; ww2 = 1.0 * size * ww1 / hh1 + 0.5; } pxb2 = PXB_rescale(pxb1,ww2,hh2); return pxb2; } /********************************************************************************/ // fast PXB pixmap rescale using 'half binomial' interpolation // works best when the rescale ratio is between 0.5 and 2.0 typedef struct { int ww1, hh1, ww2, hh2, rs1, rs2, nc1, nc2; uint8 *pixels1, *pixels2; float xscale, yscale; int nwt; } pxb_rescale_fast_data_t; PXB * PXB_rescale_fast(PXB *pxb1, int ww, int hh) { void * pxb_rescale_fast_thread(void *arg); pxb_rescale_fast_data_t P; PXB *pxb2; P.ww1 = pxb1->ww; // input image data P.hh1 = pxb1->hh; P.rs1 = pxb1->rs; P.nc1 = pxb1->nc; P.pixels1 = pxb1->pixels; P.ww2 = ww; // output image data P.hh2 = hh; P.nc2 = P.nc1; pxb2 = PXB_make(P.ww2,P.hh2,P.nc2); P.rs2 = pxb2->rs; P.pixels2 = pxb2->pixels; P.xscale = 1.0 * (P.ww1-1) / P.ww2; P.yscale = 1.0 * (P.hh1-1) / P.hh2; P.nwt = NWT; // limit to 4 threads 20.0 if (P.nwt > 4) P.nwt = 4; // (more not helpful) void *args[NWT][2]; pthread_t tid[NWT]; for (int ii = 0; ii < P.nwt; ii++) { // start working threads args[ii][0] = &P; args[ii][1] = &Nval[ii]; tid[ii] = start_Jthread(pxb_rescale_fast_thread,args[ii]); } for (int ii = 0; ii < P.nwt; ii++) // wait for all done wait_Jthread(tid[ii]); return pxb2; } void * pxb_rescale_fast_thread(void *arg) { int px1, py1, px2, py2; uint8 *pix10, *pix11, *pix2; void ** args = (void **) arg; int index = * ((int *) args[1]); set_cpu_affinity(index); // stay on same cpu if possible 20.0 pxb_rescale_fast_data_t *P = (pxb_rescale_fast_data_t *) args[0]; for (py2 = index; py2 < P->hh2; py2 += P->nwt) // loop output pixels for (px2 = 0; px2 < P->ww2; px2++) { px1 = px2 * P->xscale; // input pixel corresponding py1 = py2 * P->yscale; // to output pixel pix10 = P->pixels1 + py1 * P->rs1 + px1 * P->nc1; // input pixel RGB data pix11 = pix10 + P->nc1; // next pixel right pix2 = P->pixels2 + py2 * P->rs2 + px2 * P->nc2; // output pixel pix2[0] = (pix10[0] + pix11[0]) / 2; pix2[1] = (pix10[1] + pix11[1]) / 2; pix2[2] = (pix10[2] + pix11[2]) / 2; } pthread_exit(0); } /********************************************************************************/ // fast resize PXB to given max. width or height, preserving aspect ratio // same as PXB_rescale_fast() but one size argument instead of width and height PXB * PXB_resize_fast(PXB *pxb1, int size) { int ww1, hh1, ww2, hh2; PXB *pxb2; ww1 = pxb1->ww; hh1 = pxb1->hh; if (ww1 >= hh1) { ww2 = size; hh2 = 1.0 * size * hh1 / ww1 + 0.5; } else { hh2 = size; ww2 = 1.0 * size * ww1 / hh1 + 0.5; } pxb2 = PXB_rescale_fast(pxb1,ww2,hh2); return pxb2; } /******************************************************************************** PXB * PXB_rotate(PXB *pxb, float angle) Rotate PXB pixmap image through any angle (degrees). The returned pixmap has the same size as the original, but the pixmap envelope is increased to accommodate the rotated original (e.g. a 100x100 pixmap rotated 45 deg. needs a 142x142 pixmap). Pixels added around the rotated pixmap have RGB[A] = 0. Angle is in degrees. Positive direction is clockwise. PXB must have 3 or 4 channels (RGB or RGBA). Loss of resolution is about 1/2 pixel. Algorithm: create output PXB big enough for rotated input PXB compute coefficients for affine transform loop all output pixels (px2,py2) get corresp. input pixel (px1,py1) using affine transform if outside of PXB 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) *********************************************************************************/ PXB * PXB_rotate(PXB *pxb1, float angle) { PXB *pxb2; int nc, ac; int ww1, hh1, rs1, ww2, hh2, rs2; int px2, py2, px0, py0; float px1, py1; float f0, f1, f2, f3, red, green, blue, alpha = 0; float a, b, d, e, ww15, hh15, ww25, hh25; uchar *ppix1, *ppix2, *pix0, *pix1, *pix2, *pix3; ww1 = pxb1->ww; hh1 = pxb1->hh; nc = pxb1->nc; ac = 0; if (nc == 4) ac = 1; // has alpha channel rs1 = ww1 * nc; 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) { pxb2 = PXB_copy(pxb1); // angle is zero within my precision return pxb2; } ww2 = int(ww1*fabsf(cosf(angle)) + hh1*fabsf(sinf(angle))); // rectangle containing rotated pixmap hh2 = int(ww1*fabsf(sinf(angle)) + hh1*fabsf(cosf(angle))); pxb2 = PXB_make(ww2,hh2,nc); // create output pxb2 if (! pxb2) return 0; // initially RGBA = 0 rs2 = ww2 * nc; ppix1 = pxb1->pixels; ppix2 = pxb2->pixels; 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 (px0 < 0 || px0 >= ww1-1 || py0 < 0 || py0 >= hh1-1) // if outside input pixel array, continue; // leave black pix0 = ppix1 + py0 * rs1 + px0 * nc; // 4 input pixels based at (px0,py0) pix1 = pix0 + rs1; 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); 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 (ac) alpha = f0 * pix0[3] + f1 * pix1[3] + f2 * pix2[3] + f3 * pix3[3]; pix2 = ppix2 + py2 * rs2 + px2 * nc; // output pixel pix2[0] = int(red); pix2[1] = int(green); pix2[2] = int(blue); if (ac) pix2[3] = int(alpha); } return pxb2; } /********************************************************************************/ // Get a virtual pixel at location (px,py) (real) in a PXB pixmap. // Get the overlapping integer pixels and build a composite. // Output vpix is uint8[4] supplied by caller. // Returns 1 if OK, 0 if px/py out of limits for pxm. int vpixel(PXB *pxb, float px, float py, uint8 *vpix) { int ww, hh, rs, nc, ac, px0, py0; uint8 *pix0, *pix1, *pix2, *pix3; float f0, f1, f2, f3; ww = pxb->ww; hh = pxb->hh; rs = pxb->rs; nc = pxb->nc; ac = nc - 3; px0 = px; // integer pixel containing (px,py) py0 = py; if (px0 < 0 || py0 < 0) return 0; if (px0 > ww-2 || py0 > hh-2) return 0; f0 = (px0+1 - px) * (py0+1 - py); // overlap of (px,py) f1 = (px0+1 - px) * (py - py0); // in each of the 4 pixels f2 = (px - px0) * (py0+1 - py); f3 = (px - px0) * (py - py0); pix0 = PXBpix(pxb,px0,py0); // pixel (px0,py0) pix1 = pix0 + rs; // (px0,py0+1) pix2 = pix0 + nc; // (px0+1,py0) pix3 = pix1 + nc; // (px0+1,py0+1) vpix[0] = f0 * pix0[0] + f1 * pix1[0] + f2 * pix2[0] + f3 * pix3[0]; // sum the weighted inputs vpix[1] = f0 * pix0[1] + f1 * pix1[1] + f2 * pix2[1] + f3 * pix3[1]; vpix[2] = f0 * pix0[2] + f1 * pix1[2] + f2 * pix2[2] + f3 * pix3[2]; if (ac) vpix[3] = f0 * pix0[3] + f1 * pix1[3] + f2 * pix2[3] + f3 * pix3[3]; return 1; } // Get a virtual pixel at location (px,py) (real) in a PXM pixmap. // Get the overlapping float pixels and build a composite. // Output vpix is float[4] supplied by caller. // Returns 1 if OK, 0 if px/py out of limits for pxm. int vpixel(PXM *pxm, float px, float py, float *vpix) { int ww, hh, nc, ac, px0, py0; float *pix0, *pix1, *pix2, *pix3; float f0, f1, f2, f3; ww = pxm->ww; hh = pxm->hh; nc = pxm->nc; ac = nc - 3; px0 = px; // integer pixel containing (px,py) py0 = py; if (px0 < 1 || py0 < 1) return 0; // off-image or edge pixel if (px0 > ww-2 || py0 > hh-2) return 0; f0 = (px0+1 - px) * (py0+1 - py); // overlap of (px,py) f1 = (px0+1 - px) * (py - py0); // in each of the 4 pixels f2 = (px - px0) * (py0+1 - py); f3 = (px - px0) * (py - py0); pix0 = PXMpix(pxm,px0,py0); // pixel (px0,py0) pix1 = pix0 + ww * nc; // (px0,py0+1) pix2 = pix0 + nc; // (px0+1,py0) pix3 = pix1 + nc; // (px0+1,py0+1) vpix[0] = f0 * pix0[0] + f1 * pix1[0] + f2 * pix2[0] + f3 * pix3[0]; // sum the weighted inputs vpix[1] = f0 * pix0[1] + f1 * pix1[1] + f2 * pix2[1] + f3 * pix3[1]; vpix[2] = f0 * pix0[2] + f1 * pix1[2] + f2 * pix2[2] + f3 * pix3[2]; if (ac) // alpha channel vpix[3] = f0 * pix0[3] + f1 * pix1[3] + f2 * pix2[3] + f3 * pix3[3]; // remove test of vpix[2] < 1 return 1; } /********************************************************************************/ // Copy a PXM pixmap image (RGB float) to a PXB pixmap image (RGB uint8). // NOT THREAD SAFE namespace PXM_PXB_copy_names { PXM *pxm1; PXB *pxb2; int ww, hh, nc; } PXB * PXM_PXB_copy(PXM *pxm) { using namespace PXM_PXB_copy_names; void * PXM_PXB_copy_thread(void *); ww = pxm->ww; hh = pxm->hh; nc = pxm->nc; pxm1 = pxm; pxb2 = PXB_make(ww,hh,nc); do_wthreads(PXM_PXB_copy_thread,NWT); return pxb2; } void * PXM_PXB_copy_thread(void *arg) // speedup with threads 19.0 { using namespace PXM_PXB_copy_names; int index = *((int *) arg); int px, py; float *pix1; uint8 *pix2; for (py = index; py < hh; py += NWT) { 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 (nc == 4) pix2[3] = pix1[3]; pix1 += nc; pix2 += nc; } } pthread_exit(0); } // Update a PXB section from an updated PXM section. // PXM and PXB must have the same dimensions. // px3, py3, ww3, hh3: modified section within pxm1 to propagate to pxb2; void PXM_PXB_update(PXM *pxm1, PXB *pxb2, int px3, int py3, int ww3, int hh3) { float *pix1; uint8 *pix2; int px, py; int lox, hix, loy, hiy; int nc1, nc2, ac; if (pxm1->ww + pxm1->hh != pxb2->ww + pxb2->hh) { printz("PXM_PXB_update() call error %d %d %d %d \n", pxm1->ww, pxm1->hh, pxb2->ww, pxb2->hh); return; } lox = px3; hix = px3 + ww3; if (lox < 0) lox = 0; if (hix > pxb2->ww) hix = pxb2->ww; loy = py3; hiy = py3 + hh3; if (loy < 0) loy = 0; if (hiy > pxb2->hh) hiy = pxb2->hh; nc1 = pxm1->nc; nc2 = pxb2->nc; if (nc1 > 3 && nc2 > 3) ac = 1; // alpha channel else ac = 0; for (py = loy; py < hiy; py++) { pix1 = PXMpix(pxm1,lox,py); pix2 = PXBpix(pxb2,lox,py); for (px = lox; px < hix; px++) { pix2[0] = pix1[0]; // 0.0 - 255.99 >> 0 - 255 pix2[1] = pix1[1]; pix2[2] = pix1[2]; if (ac) pix2[3] = pix1[3]; pix1 += nc1; pix2 += nc2; } } return; } // Update an output PXB section from a corresponding input PXB section. // The two PXBs represent the same image at different scales (width/height). // Input pxb1 must be larger or equal to output pxb2. // px3, py3, ww3, hh3: modified section within pxb1 to propagate to pxb2; void PXB_PXB_update(PXB *pxb1, PXB *pxb2, int px3, int py3, int ww3, int hh3) { static int pww1 = 0, phh1 = 0, pww2 = 0, phh2 = 0; static int *px1L = 0, *py1L = 0; static float scalex = 1, scaley = 1; static float *pxmap = 0, *pymap = 0; static int maxmapx = 0, maxmapy = 0; uint8 *ppix1, *ppix2; int ww1, hh1, ww2, hh2, rs1, rs2, nc1, nc2, ac; int px1, py1, px2, py2; int pxl, pyl, pxm, pym, ii; float px1a, py1a, px1b, py1b; float fx, fy, ftot; uint8 *pixel1, *pixel2; float red, green, blue, alpha; int lox, hix, loy, hiy; ww1 = pxb1->ww; hh1 = pxb1->hh; rs1 = pxb1->rs; nc1 = pxb1->nc; ppix1 = pxb1->pixels; ww2 = pxb2->ww; hh2 = pxb2->hh; rs2 = pxb2->rs; nc2 = pxb2->nc; ppix2 = pxb2->pixels; if (nc1 > 3 && nc2 > 3) ac = 1; // alpha channel else ac = 0; if (ww1 == pww1 && hh1 == phh1 && ww2 == pww2 && hh2 == phh2) // if the sizes are the same as before, goto copy_pixels; // the pixel mapping math can be avoided. pww1 = ww1; phh1 = hh1; pww2 = ww2; phh2 = hh2; if (px1L) { // unless this is the first call, zfree(px1L); // free prior map memory zfree(py1L); zfree(pxmap); zfree(pymap); } scalex = 1.0 * ww1 / ww2; // compute x and y scales scaley = 1.0 * hh1 / hh2; if (scalex <= 1) maxmapx = 2; // compute max input pixels else maxmapx = scalex + 2; // mapping into output pixels maxmapx += 1; // for both dimensions if (scaley <= 1) maxmapy = 2; // (pixels may not be square) else maxmapy = scaley + 2; maxmapy += 1; // (extra entry for -1 flag) pymap = (float *) zmalloc(hh2 * maxmapy * sizeof(float)); // maps overlap of < maxmap input pxmap = (float *) zmalloc(ww2 * maxmapx * sizeof(float)); // pixels per output pixel py1L = (int *) zmalloc(hh2 * sizeof(int)); // maps first (lowest) input pixel px1L = (int *) zmalloc(ww2 * sizeof(int)); // per output pixel for (py2 = 0; py2 < hh2; py2++) // loop output y-pixels { py1a = py2 * scaley; // corresponding input y-pixels py1b = py1a + scaley; if (py1b >= hh1) py1b = hh1 - 0.001; // fix precision limitation pyl = py1a; py1L[py2] = pyl; // 1st overlapping input pixel for (py1 = pyl, pym = 0; py1 < py1b; py1++, pym++) // loop overlapping input pixels { if (py1 < py1a) { // compute amount of overlap if (py1+1 < py1b) fy = py1+1 - py1a; // 0.0 to 1.0 else fy = scaley; } else if (py1+1 > py1b) fy = py1b - py1; else fy = 1; ii = py2 * maxmapy + pym; // save it pymap[ii] = 0.9999 * fy / scaley; } ii = py2 * maxmapy + pym; // set an end marker after pymap[ii] = -1; // last overlapping pixel } for (px2 = 0; px2 < ww2; px2++) // do same for x-pixels { px1a = px2 * scalex; px1b = px1a + scalex; if (px1b >= ww1) px1b = ww1 - 0.001; pxl = px1a; px1L[px2] = pxl; for (px1 = pxl, pxm = 0; px1 < px1b; px1++, pxm++) { if (px1 < px1a) { if (px1+1 < px1b) fx = px1+1 - px1a; else fx = scalex; } else if (px1+1 > px1b) fx = px1b - px1; else fx = 1; ii = px2 * maxmapx + pxm; pxmap[ii] = 0.9999 * fx / scalex; } ii = px2 * maxmapx + pxm; pxmap[ii] = -1; } copy_pixels: px3 = px3 / scalex; // convert input area to output area py3 = py3 / scaley; ww3 = ww3 / scalex + 2; hh3 = hh3 / scaley + 2; lox = px3; hix = px3 + ww3; if (lox < 0) lox = 0; if (hix > ww2) hix = ww2; loy = py3; hiy = py3 + hh3; if (loy < 0) loy = 0; if (hiy > hh2) hiy = hh2; for (py2 = loy; py2 < hiy; py2++) // loop output y-pixels { pyl = py1L[py2]; // corresp. 1st input y-pixel for (px2 = lox; px2 < hix; px2++) // loop output x-pixels { pxl = px1L[px2]; // corresp. 1st input x-pixel red = green = blue = alpha = 0; // initz. output pixel for (py1 = pyl, pym = 0; ; py1++, pym++) // loop overlapping input y-pixels { ii = py2 * maxmapy + pym; // get y-overlap fy = pymap[ii]; if (fy < 0) break; // no more pixels for (px1 = pxl, pxm = 0; ; px1++, pxm++) // loop overlapping input x-pixels { ii = px2 * maxmapx + pxm; // get x-overlap fx = pxmap[ii]; if (fx < 0) break; // no more pixels ftot = fx * fy; // area overlap = x * y overlap pixel1 = ppix1 + py1 * rs1 + px1 * nc1; red += pixel1[0] * ftot; // add input pixel * overlap green += pixel1[1] * ftot; blue += pixel1[2] * ftot; if (ac) alpha += pixel1[3] * ftot; } } pixel2 = ppix2 + py2 * rs2 + px2 * nc2; // save output pixel pixel2[0] = red; // 0.0 - 255.996 >> 0 - 255 pixel2[1] = green; pixel2[2] = blue; if (ac) pixel2[3] = alpha; } } return; } /******************************************************************************** primary image file read and write functions PXB pixmap <--> file 8-bit RGB[A] PXM pixmap <--> file float RGB[A] *********************************************************************************/ // Load an image file into a PXB pixmap (8 bit color). // Also sets the following file scope variables: // f_load_type = "jpg" "tif" "png" "RAW" "video" "other" // f_load_bpc = disk file bits per color = 8/16 // f_load_size = disk file size // If Fack is true, failure will cause a popup ACK dialog // Do not call from a thread with Fack true. PXB * PXB_load(cchar *file, int Fack) { int err; FTYPE ftype; char *file2 = 0, *framefile; cchar *pext; PXB *pxb = 0; STATB statb; err = stat(file,&statb); if (err || ! S_ISREG(statb.st_mode)) { printz("%s %s \n",Bfilenotfound,file); // 20.0 goto errret; } f_load_size = statb.st_size; f_load_bpc = 8; // default ftype = image_file_type(file); if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) { printz("file type not supported: %s \n",file); goto errret; } if (ftype == VIDEO) // video file - extract frame 1 { framefile = zstrdup(temp_folder,20); // output .jpg file strcat(framefile,"/frame1.jpg"); file2 = zescape_quotes(file); err = shell_quiet("ffmpeg -i \"%s\" -v 8 -frames 1 -y %s",file2,framefile); zfree(file2); if (err) { printz("cannot get video frame 1: %s %s \n",file,strerror(err)); goto errret; } pxb = ANY_PXB_load(framefile); if (! pxb) goto errret; strcpy(f_load_type,"video"); } if (ftype == IMAGE) { pext = strrchr(file,'/'); if (! pext) pext = file; pext = strrchr(pext,'.'); if (! pext) pext = ""; if (strcasestr(".jpg .jpeg",pext)) { pxb = JPG_PXB_load(file); strcpy(f_load_type,"jpg"); } else if (strcasestr(".tif .tiff",pext)) { pxb = TIFF_PXB_load(file); strcpy(f_load_type,"tif"); } else if (strcasestr(".png",pext)) { pxb = PNG_PXB_load(file); strcpy(f_load_type,"png"); } else if (strcasestr(".heic",pext)) { // 20.0 pxb = HEIC_PXB_load(file); strcpy(f_load_type,"heic"); } else if (strcasestr(".jp2",pext)) { // 20.0 pxb = JP2_PXB_load(file); strcpy(f_load_type,"jp2"); } else { pxb = ANY_PXB_load(file); strcpy(f_load_type,"other"); } } if (ftype == RAW) { pxb = RAW_PXB_load(file,Fautobright,Fmatchthumb); strcpy(f_load_type,"RAW"); } if (pxb) return pxb; errret: strcpy(f_load_type,"none"); f_load_bpc = f_load_size = 0; if (Fack) zmessageACK(Mwin,"file error: %s (see log file)",file); 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" "RAW" "other" // f_load_bpc = disk file bits per color = 8/16 // f_load_size = disk file size // If Fack is true, failure will cause a popup ACK dialog // Do not call from a thread with Fack true. PXM * PXM_load(cchar *file, int Fack) { int err; FTYPE ftype; cchar *pext; PXM *pxm = 0; STATB statb; err = stat(file,&statb); if (err || ! S_ISREG(statb.st_mode)) { printz("%s %s \n",Bfilenotfound,file); goto errret; } f_load_size = statb.st_size; f_load_bpc = 8; // default ftype = image_file_type(file); if (ftype != IMAGE && ftype != RAW) { printz("file type not supported: %s \n",file); goto errret; } if (ftype == IMAGE) { pext = strrchr(file,'/'); if (! pext) pext = file; pext = strrchr(pext,'.'); if (! pext) pext = ""; if (strcasestr(".jpg .jpeg",pext)) { pxm = JPG_PXM_load(file); strcpy(f_load_type,"jpg"); } else if (strcasestr(".tif .tiff",pext)) { pxm = TIFF_PXM_load(file); strcpy(f_load_type,"tif"); } else if (strcasestr(".png",pext)) { pxm = PNG_PXM_load(file); strcpy(f_load_type,"png"); } else if (strcasestr(".heic",pext)) { // 20.0 pxm = HEIC_PXM_load(file); strcpy(f_load_type,"heic"); } else if (strcasestr(".jp2",pext)) { // 20.0 pxm = JP2_PXM_load(file); strcpy(f_load_type,"jp2"); } else { pxm = ANY_PXM_load(file); strcpy(f_load_type,"other"); } } if (ftype == RAW) { pxm = RAW_PXM_load(file,Fautobright,Fmatchthumb); strcpy(f_load_type,"RAW"); } if (pxm) return pxm; errret: strcpy(f_load_type,"none"); f_load_bpc = f_load_size = 0; if (Fack) zmessageACK(Mwin,"file error: %s (see log file)",file); return 0; } /********************************************************************************/ // Save a PXB pixmap to a specified file, bits/color, and quality level. // If output file is JPEG, bpc is 8 and quality is used for compression level. // If output file is TIFF or PNG, bpc may be 8 or 16 and quality is ignored. // If Fack is true, failure will cause a popup ACK dialog. // Return 0 if OK, +N if error int PXB_save(PXB *pxb, cchar *file, int bpc, int quality, int Fack) { int err; cchar *pext; pext = strrchr(file,'/'); if (! pext) pext = file; pext = strrchr(pext,'.'); if (! pext) pext = ""; if (strcasestr(".tif .tiff",pext)) err = PXB_TIFF_save(pxb,file,bpc); else if (strcasestr(".png",pext)) err = PXB_PNG_save(pxb,file,bpc); else if (strcasestr(".heic",pext)) // 20.0 err = PXB_HEIC_save(pxb,file); else if (strcasestr(".jp2",pext)) // 20.0 err = PXB_JP2_save(pxb,file); else err = PXB_JPG_save(pxb,file,quality); if (! err) return 0; if (Fack) zmessageACK(Mwin,"file error: %s (see log file)",file); return err; } /********************************************************************************/ // Save a PXM pixmap to a specified file, bits/color, and quality level. // If output file is JPEG, bpc is 8 and quality is used for compression level. // If output file is TIFF or PNG, bpc may be 8 or 16 and quality is ignored. // If Fack is true, failure will cause a popup ACK dialog. // Return 0 if OK, +N if error int PXM_save(PXM *pxm, cchar *file, int bpc, int quality, int Fack) { int err; cchar *pext; pext = strrchr(file,'/'); if (! pext) pext = file; pext = strrchr(pext,'.'); if (! pext) pext = ""; if (strcasestr(".tif .tiff",pext)) err = PXM_TIFF_save(pxm,file,bpc); else if (strcasestr(".png",pext)) err = PXM_PNG_save(pxm,file,bpc); else if (strcasestr(".heic",pext)) // 20.0 err = PXM_HEIC_save(pxm,file); else if (strcasestr(".jp2",pext)) // 20.0 err = PXM_JP2_save(pxm,file); else err = PXM_JPG_save(pxm,file,quality); if (! err) return 0; if (Fack) zmessageACK(Mwin,"file error: %s (see log file)",file); return err; } /******************************************************************************** JPG file read and write functions 19.0 *********************************************************************************/ #pragma GCC push_options // stop GCC from removing necessary code #pragma GCC optimize ("O1") // (all calls to jpeg_xxxx functions) // JPEG error handler function typedef struct libjpeg_error_mgr *libjpeg_error_ptr; struct libjpeg_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; void libjpeg_error_exit (j_common_ptr cinfo) { libjpeg_error_ptr errptr = (libjpeg_error_ptr) cinfo->err; // (*cinfo->err->output_message) (cinfo); // suppress secondary message longjmp(errptr->setjmp_buffer, 1); } // Load PXB pixmap from JPG file using JPG library. // set size = 0 to load a full image size // set size = N to load an image with width and height <= size PXB * JPG_PXB_load(cchar *file, int size) // 19.0 { struct jpeg_decompress_struct cinfo; struct libjpeg_error_mgr jerr; FILE *fid = 0; uint8 **row_pointers = 0; uint8 *pixels = 0; uint8 *buffer; int err, ww, hh, num, nc1, nc2, cc, row, nb; PXB *pxb = 0, *pxb2 = 0; STATB statb; err = stat(file,&statb); if (err || ! S_ISREG(statb.st_mode)) goto errret; // no print error 19.13 fid = fopen(file,"r"); if (! fid) goto errret; cinfo.err = jpeg_std_error(&jerr.pub); // replace standard error handler jerr.pub.error_exit = libjpeg_error_exit; // which exits on error if (setjmp(jerr.setjmp_buffer)) { jpeg_destroy_decompress(&cinfo); goto errret; } jpeg_create_decompress(&cinfo); // initz. for read jpeg_stdio_src(&cinfo,fid); jpeg_read_header(&cinfo,TRUE); ww = cinfo.image_width; // image size hh = cinfo.image_height; if (size) // used for thumbnails { if (ww >= hh) { // calculate num for actual size num = 16 * size / ww; // minimally >= target size if (ww * num / 16 < size) num++; } else { num = 16 * size / hh; if (hh * num / 16 < size) num++; } if (num == 0) num = 1; if (num < 16) { cinfo.scale_num = num; // target size = num/16 * full size cinfo.scale_denom = 16; } } jpeg_start_decompress(&cinfo); nb = cinfo.rec_outbuf_height; // rows per read nc1 = cinfo.output_components; // color channels if (nc1 != 1 && nc1 != 3) { printz("%d colors not supported: %s \n",nc1,file); goto errret; } ww = cinfo.output_width; // actual output dimensions hh = cinfo.output_height; cc = ww * hh * nc1; pixels = (uint8 *) zmalloc(cc); // allocate memory for RGB image cc = hh * sizeof(void *); // allocate row pointers row_pointers = (uint8 **) zmalloc(cc); for (row = 0; row < hh; row++) // initz. row pointers row_pointers[row] = (uint8 *) pixels + row * ww * nc1; while (cinfo.output_scanline < cinfo.output_height) { // read rows row = cinfo.output_scanline; buffer = row_pointers[row]; jpeg_read_scanlines(&cinfo,&buffer,nb); } jpeg_finish_decompress(&cinfo); // wrap-up jpeg_destroy_decompress(&cinfo); fclose(fid); zfree(row_pointers); row_pointers = 0; nc2 = 3; pxb = PXB_make(ww,hh,nc2); // output PXB, no alpha channel pixelvert(pixels,pxb->pixels,ww,hh,nc1,nc2); // convert pixel data zfree(pixels); pixels = 0; if (size) // make final exact size { if (ww >= hh) { hh = hh * size / ww; ww = size; } else { ww = ww * size / hh; hh = size; } pxb2 = PXB_rescale(pxb,ww,hh); PXB_free(pxb); pxb = pxb2; } f_load_bpc = 8; return pxb; errret: if (errno) printz("error: %s \n",strerror(errno)); printz("jpeg file error: %s \n",file); if (fid && fileno(fid) != -1) fclose(fid); if (row_pointers) zfree(row_pointers); if (pixels) zfree(pixels); return 0; } // Load PXM pixmap from JPG file using JPG library. PXM * JPG_PXM_load(cchar *file) { struct jpeg_decompress_struct cinfo; struct libjpeg_error_mgr jerr; FILE *fid = 0; uint8 **row_pointers = 0; uint8 *pixels = 0; uint8 *buffer; int err, ww, hh, nc1, nc2, cc, row, nb; PXM *pxm = 0; STATB statb; err = stat(file,&statb); if (err || ! S_ISREG(statb.st_mode)) goto errret; // no print error 19.13 fid = fopen(file,"r"); if (! fid) goto errret; cinfo.err = jpeg_std_error(&jerr.pub); // replace standard error handler jerr.pub.error_exit = libjpeg_error_exit; // which exits on error if (setjmp(jerr.setjmp_buffer)) { jpeg_destroy_decompress(&cinfo); goto errret; } jpeg_create_decompress(&cinfo); // initz. for read jpeg_stdio_src(&cinfo,fid); jpeg_read_header(&cinfo,TRUE); ww = cinfo.image_width; // image size hh = cinfo.image_height; jpeg_start_decompress(&cinfo); nb = cinfo.rec_outbuf_height; // rows per read nc1 = cinfo.output_components; // color channels if (nc1 != 1 && nc1 != 3) { printz("%d colors not supported: %s \n",nc1,file); goto errret; } ww = cinfo.output_width; // actual output dimensions hh = cinfo.output_height; cc = ww * hh * nc1; pixels = (uint8 *) zmalloc(cc); // allocate memory for RGB image cc = hh * sizeof(void *); // allocate row pointers row_pointers = (uint8 **) zmalloc(cc); for (row = 0; row < hh; row++) // initz. row pointers row_pointers[row] = (uint8 *) pixels + row * ww * nc1; while (cinfo.output_scanline < cinfo.output_height) { // read rows row = cinfo.output_scanline; buffer = row_pointers[row]; jpeg_read_scanlines(&cinfo,&buffer,nb); } jpeg_finish_decompress(&cinfo); // wrap-up jpeg_destroy_decompress(&cinfo); fclose(fid); zfree(row_pointers); row_pointers = 0; nc2 = 3; pxm = PXM_make(ww,hh,nc2); // output PXM, no alpha channel if (! pxm) goto errret; pixelvert(pixels,pxm->pixels,ww,hh,nc1,nc2); // convert pixel data 19.0 PXM_audit(pxm); // audit/repair 20.0 zfree(pixels); pixels = 0; f_load_bpc = 8; return pxm; errret: if (errno) printz("error: %s \n",strerror(errno)); printz("jpeg file error: %s \n",file); if (fid && fileno(fid) != -1) fclose(fid); if (row_pointers) zfree(row_pointers); if (pixels) zfree(pixels); return 0; } // Write PXB pixmap to JPG file using JPG library. // quality is compression quality 0-100 // returns 0 if OK, +N if error. int PXB_JPG_save(PXB *pxb, cchar *file, int quality) { struct jpeg_compress_struct cinfo; struct libjpeg_error_mgr jerr; FILE *fid = 0; int ww, hh, nc1, nc2, cc, row, nn; uint8 **row_pointers = 0; uint8 *pixels1, *pixels2 = 0; fid = fopen(file,"w"); if (! fid) goto errret; cinfo.err = jpeg_std_error(&jerr.pub); // replace standard error handler jerr.pub.error_exit = libjpeg_error_exit; // which exits on error if (setjmp(jerr.setjmp_buffer)) { jpeg_destroy_compress(&cinfo); goto errret; } jpeg_create_compress(&cinfo); // initz. for write jpeg_stdio_dest(&cinfo,fid); ww = pxb->ww; // image dimensions hh = pxb->hh; nc1 = pxb->nc; // input channels (3/4) nc2 = 3; // output channels pixels1 = pxb->pixels; // input pixels pixels2 = (uint8 *) zmalloc(ww * hh * nc2); // output pixels pixelvert(pixels1,pixels2,ww,hh,nc1,nc2); // convert pixel data 19.0 cinfo.image_width = ww; cinfo.image_height = hh; cinfo.input_components = nc2; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); // default compression parameters jpeg_set_quality(&cinfo,quality,TRUE); // compression quality 0-100 jpeg_start_compress(&cinfo,TRUE); cc = hh * sizeof(void *); // allocate row pointers row_pointers = (uint8 **) zmalloc(cc); for (row = 0; row < hh; row++) // initz. row pointers row_pointers[row] = pixels2 + row * ww * nc2; nn = jpeg_write_scanlines(&cinfo,row_pointers,hh); // write rows if (nn != hh) goto errret; jpeg_finish_compress(&cinfo); // wrap-up jpeg_destroy_compress(&cinfo); fclose(fid); zfree(row_pointers); zfree(pixels2); row_pointers = 0; pixels2 = 0; return 0; errret: if (errno) printz("error: %s \n",strerror(errno)); printz("jpeg file error: %s \n",file); if (fid && fileno(fid) != -1) fclose(fid); if (row_pointers) zfree(row_pointers); if (pixels2) zfree(pixels2); return 3; } // Write PXM pixmap to JPG file using JPG library. // quality is compression quality 0-100 // returns 0 if OK, +N if error. int PXM_JPG_save(PXM *pxm, cchar *file, int quality) { struct jpeg_compress_struct cinfo; struct libjpeg_error_mgr jerr; FILE *fid = 0; int ww, hh, nc1, nc2, cc, row, nn; uint8 **row_pointers = 0; uint8 *pixels2 = 0; float *pixels1; fid = fopen(file,"w"); if (! fid) goto errret; cinfo.err = jpeg_std_error(&jerr.pub); // replace standard error handler jerr.pub.error_exit = libjpeg_error_exit; // which exits on error if (setjmp(jerr.setjmp_buffer)) { jpeg_destroy_compress(&cinfo); goto errret; } jpeg_create_compress(&cinfo); // initz. for write jpeg_stdio_dest(&cinfo,fid); ww = pxm->ww; // image dimensions hh = pxm->hh; nc1 = pxm->nc; // 3 or 4 channels RGB[A] nc2 = 3; pixels1 = pxm->pixels; // input float * pixels2 = (uint8 *) zmalloc(ww * hh * nc2); // output uint8 * pixelvert(pixels1,pixels2,ww,hh,nc1,nc2); // convert pixel data 19.0 cinfo.image_width = ww; cinfo.image_height = hh; cinfo.input_components = nc2; // RGB cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); // default compression parameters jpeg_set_quality(&cinfo,quality,TRUE); // compression quality 0-100 jpeg_start_compress(&cinfo,TRUE); cc = hh * sizeof(void *); // allocate row pointers row_pointers = (uint8 **) zmalloc(cc); for (row = 0; row < hh; row++) // initz. row pointers row_pointers[row] = pixels2 + row * ww * nc2; nn = jpeg_write_scanlines(&cinfo,row_pointers,hh); // write rows if (nn != hh) goto errret; jpeg_finish_compress(&cinfo); // wrap-up jpeg_destroy_compress(&cinfo); fclose(fid); zfree(row_pointers); zfree(pixels2); row_pointers = 0; pixels2 = 0; return 0; errret: if (errno) printz("error: %s \n",strerror(errno)); printz("jpeg file error: %s \n",file); if (fid && fileno(fid) != -1) fclose(fid); if (row_pointers) zfree(row_pointers); if (pixels2) zfree(pixels2); return 2; } /********************************************************************************/ #pragma GCC pop_options // revert to normal optimization /******************************************************************************** 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 pixmap from TIFF file using TIFF library. // Native TIFF file bits/color >> f_load_bpc PXB * TIFF_PXB_load(cchar *file) { static int ftf = 1; TIFF *tiff; PXB *pxb; char *tiffbuff; uint16 bpc, nc1, nc2, pmi; int err, ww, hh, rs1, rps, stb, nst; int row, strip, cc; STATB statb; err = stat(file,&statb); if (err || ! S_ISREG(statb.st_mode)) goto errret; // no print error 19.13 if (ftf) { TIFFSetWarningHandler(tiffwarninghandler); // intercept TIFF warning messages ftf = 0; } tiff = TIFFOpen(file,"r"); if (! tiff) { printz("file error: %s %s \n",file,strerror(errno)); goto errret; } 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, &nc1); // no. channels (colors), 1/2/3/4 TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rps); // rows per strip TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &pmi); // 1 = grayscale, 2 = RGB stb = TIFFStripSize(tiff); // strip size nst = TIFFNumberOfStrips(tiff); // number of strips if (ww > wwhh_limit1 || hh > wwhh_limit1 || ww * hh > wwhh_limit2) { printz("image too big, %dx%d",ww,hh); TIFFClose(tiff); goto errret; } if (bpc != 8 && bpc != 16) { // bits/color TIFFClose(tiff); return ANY_PXB_load(file); // fallback to pixbuf loader } rs1 = ww * nc1; // row stride if (bpc == 16) rs1 = rs1 * 2; tiffbuff = (char *) zmalloc(hh * rs1 + 1000000); // tiff input buffer (crash prone) for (strip = 0; strip < nst; strip++) // read strips { row = strip * rps; // 1st row in strip cc = TIFFReadEncodedStrip(tiff,strip,tiffbuff+row*rs1,stb); if (cc > 0) continue; if (cc == 0) break; TIFFClose(tiff); zfree(tiffbuff); return ANY_PXB_load(file); // fallback to pixbuf loader 19.0 } TIFFClose(tiff); if (nc1 == 2 || nc1 == 4) nc2 = 4; // alpha channel present else nc2 = 3; pxb = PXB_make(ww,hh,nc2); // create PXB if (! pxb) { printz("PXB_make() failure"); goto errret; } if (bpc == 8) pixelvert((uint8 *) tiffbuff,pxb->pixels,ww,hh,nc1,nc2); // convert pixel data 19.0 if (bpc == 16) pixelvert((uint16 *) tiffbuff,pxb->pixels,ww,hh,nc1,nc2); zfree(tiffbuff); f_load_bpc = bpc; return pxb; errret: return 0; } // Load PXM pixmap from TIFF file using TIFF library. // Native TIFF file bits/color >> f_load_bpc PXM * TIFF_PXM_load(cchar *file) { static int ftf = 1; TIFF *tiff; PXM *pxm; char *tiffbuff; uint16 bpc, nc1, nc2, pmi; int err, ww, hh, rs1, rps, stb, nst; int row, strip, cc; STATB statb; err = stat(file,&statb); if (err || ! S_ISREG(statb.st_mode)) goto errret; // no print error 19.13 if (ftf) { TIFFSetWarningHandler(tiffwarninghandler); // intercept TIFF warning messages ftf = 0; } tiff = TIFFOpen(file,"r"); if (! tiff) { printz("file error: %s %s \n",file,strerror(errno)); goto errret; } TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &ww); // width TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &hh); // height TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bpc); // bits per color, 8/16 TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &nc1); // no. channels (colors), 1/2/3/4 TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rps); // rows per strip TIFFGetField(tiff, TIFFTAG_SAMPLEFORMAT, &pmi); // 1 = grayscale, 2 = RGB stb = TIFFStripSize(tiff); // strip size nst = TIFFNumberOfStrips(tiff); // number of strips if (ww > wwhh_limit1 || hh > wwhh_limit1 || ww * hh > wwhh_limit2) { printz("image too big, %dx%d",ww,hh); TIFFClose(tiff); goto errret; } if (bpc != 8 && bpc != 16) { // bits/color TIFFClose(tiff); return ANY_PXM_load(file); // fallback to pixbuf loader } rs1 = ww * nc1; // row stride if (bpc == 16) rs1 = rs1 * 2; tiffbuff = (char *) zmalloc(hh * rs1 + 1000000); // tiff input buffer (crash prone) for (strip = 0; strip < nst; strip++) // read strips { row = strip * rps; // 1st row in strip cc = TIFFReadEncodedStrip(tiff,strip,tiffbuff+row*rs1,stb); if (cc > 0) continue; if (cc == 0) break; TIFFClose(tiff); // failed zfree(tiffbuff); return ANY_PXM_load(file); // fallback to pixbuf loader 19.0 } TIFFClose(tiff); if (nc1 == 2 || nc1 == 4) nc2 = 4; // alpha channel else nc2 = 3; pxm = PXM_make(ww,hh,nc2); // create output PXM if (! pxm) { TIFFClose(tiff); goto errret; } if (bpc == 8) pixelvert((uint8 *) tiffbuff,pxm->pixels,ww,hh,nc1,nc2); // convert pixel data 19.0 if (bpc == 16) pixelvert((uint16 *) tiffbuff,pxm->pixels,ww,hh,nc1,nc2); PXM_audit(pxm); // audit/repair 20.0 zfree(tiffbuff); f_load_bpc = bpc; return pxm; errret: return 0; } // Write PXB pixmap to TIFF file using TIFF library. // TIFF file bpc is 8 or 16. // Returns 0 if OK, +N if error. int PXB_TIFF_save(PXB *pxb, cchar *file, int bpc) { static int ftf = 1; TIFF *tiff; char *tiffbuff; int tiffstat = 0; int ww, hh, row; // int not uint int nc1, nc2, rs2, pm = 2, pc = 1; int comp = tiff_comp_method; // 20.0 if (ftf) { TIFFSetWarningHandler(tiffwarninghandler); // intercept TIFF warning messages ftf = 0; } tiff = TIFFOpen(file,"w"); if (! tiff) { printz("file error: %s %s \n",file,strerror(errno)); goto errret; } ww = pxb->ww; hh = pxb->hh; nc1 = pxb->nc; nc2 = nc1; rs2 = ww * nc2; // output row stride if (bpc == 16) rs2 = rs2 * 2; tiffbuff = (char *) zmalloc(hh * rs2); TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, ww); TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, bpc); TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, nc2); TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, pm); TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, pc); TIFFSetField(tiff, TIFFTAG_COMPRESSION, comp); if (bpc == 8) pixelvert(pxb->pixels,(uint8 *) tiffbuff,ww,hh,nc1,nc2); if (bpc == 16) pixelvert(pxb->pixels,(uint16 *) tiffbuff,ww,hh,nc1,nc2); for (row = 0; row < hh; row++) { tiffstat = TIFFWriteScanline(tiff,tiffbuff+row*rs2,row,0); if (tiffstat != 1) break; } TIFFClose(tiff); zfree(tiffbuff); if (tiffstat == 1) return 0; errret: return 2; } // Write PXM pixmap 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; char *tiffbuff; int tiffstat = 0; int ww, hh, row; // int not uint int nc1, nc2, rs2, pm = 2, pc = 1; int comp = tiff_comp_method; // 20.0 if (ftf) { TIFFSetWarningHandler(tiffwarninghandler); // intercept TIFF warning messages ftf = 0; } tiff = TIFFOpen(file,"w"); if (! tiff) { printz("file error: %s %s \n",file,strerror(errno)); goto errret; } ww = pxm->ww; hh = pxm->hh; nc1 = pxm->nc; nc2 = nc1; rs2 = ww * nc2; // output row stride if (bpc == 16) rs2 = rs2 * 2; tiffbuff = (char *) zmalloc(hh * rs2); TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, ww); TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, bpc); TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, nc2); TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, pm); TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, pc); TIFFSetField(tiff, TIFFTAG_COMPRESSION, comp); if (bpc == 8) pixelvert(pxm->pixels,(uint8 *) tiffbuff,ww,hh,nc1,nc2); if (bpc == 16) pixelvert(pxm->pixels,(uint16 *) tiffbuff,ww,hh,nc1,nc2); for (row = 0; row < hh; row++) { tiffstat = TIFFWriteScanline(tiff,tiffbuff+row*rs2,row,0); if (tiffstat != 1) break; } TIFFClose(tiff); zfree(tiffbuff); if (tiffstat == 1) return 0; errret: return 2; } /******************************************************************************** PNG file read and write functions *********************************************************************************/ // Load PXB pixmap from PNG file using PNG library. // Native PNG file bits/color >> f_load_bpc PXB * PNG_PXB_load(cchar *file) { png_structp pngstruct = 0; png_infop pnginfo = 0; FILE *fid = 0; int err, ww, hh, bpc, nc1, nc2, rs1, row, colortype; uchar *pngbuff, **pngrows; PXB *pxb; STATB statb; err = stat(file,&statb); if (err || ! S_ISREG(statb.st_mode)) goto errret; // no print error 19.13 fid = fopen(file,"r"); // open file if (! fid) { printz("file error: %s %s \n",file,strerror(errno)); goto errret; } pngstruct = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0); // create png structs if (! pngstruct) goto errret; pnginfo = png_create_info_struct(pngstruct); if (! pnginfo) goto errret; if (setjmp(png_jmpbuf(pngstruct))) goto errret; // 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) nc1 = 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) nc2 = 3; else if (colortype == PNG_COLOR_TYPE_GRAY_ALPHA || colortype == PNG_COLOR_TYPE_RGB_ALPHA) nc2 = 4; else { png_destroy_read_struct(&pngstruct,&pnginfo,0); return ANY_PXB_load(file); // fallback to pixbuf loader } if (ww > wwhh_limit1 || hh > wwhh_limit1 || ww * hh > wwhh_limit2) { png_destroy_read_struct(&pngstruct,&pnginfo,0); printz("image too big, %dx%d",ww,hh); goto errret; } 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 (nc1 > 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,nc2); // create output PXB if (! pxb) { png_destroy_read_struct(&pngstruct,&pnginfo,0); printz("PXB_make() failed"); goto errret; } rs1 = ww * nc1; // png input row stride if (bpc == 16) rs1 = rs1 * 2; pngbuff = (uchar *) zmalloc(hh * rs1); // png file input buffer for (row = 0; row < hh; row++) // get all png rows memcpy(pngbuff+row*rs1,pngrows[row],rs1); png_destroy_read_struct(&pngstruct,&pnginfo,0); // release memory if (bpc == 8) pixelvert((uint8 *) pngbuff,pxb->pixels,ww,hh,nc1,nc2); // convert pixel data 19.0 if (bpc == 16) pixelvert((uint16 *) pngbuff,pxb->pixels,ww,hh,nc1,nc2); zfree(pngbuff); f_load_bpc = bpc; return pxb; errret: if (fid && fileno(fid) != -1) fclose(fid); return 0; } // Load PXM pixmap from PNG file using PNG library. // Native PNG file bits/color >> f_load_bpc PXM * PNG_PXM_load(cchar *file) { png_structp pngstruct = 0; png_infop pnginfo = 0; FILE *fid = 0; int err, ww, hh, bpc, nc1, nc2, rs1, row, colortype; uchar *pngbuff, **pngrows; PXM *pxm; STATB statb; err = stat(file,&statb); if (err || ! S_ISREG(statb.st_mode)) goto errret; // no print error 19.13 fid = fopen(file,"r"); // open file if (! fid) { printz("file error: %s %s \n",file,strerror(errno)); goto errret; } pngstruct = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0); // create png structs if (! pngstruct) goto errret; pnginfo = png_create_info_struct(pngstruct); if (! pnginfo) goto errret; if (setjmp(png_jmpbuf(pngstruct))) goto errret; // 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) nc1 = 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) nc2 = 3; else if (colortype == PNG_COLOR_TYPE_GRAY_ALPHA || colortype == PNG_COLOR_TYPE_RGB_ALPHA) nc2 = 4; else { png_destroy_read_struct(&pngstruct,&pnginfo,0); return ANY_PXM_load(file); // fallback to pixbuf loader } if (ww > wwhh_limit1 || hh > wwhh_limit1 || ww * hh > wwhh_limit2) { png_destroy_read_struct(&pngstruct,&pnginfo,0); printz("image too big, %dx%d",ww,hh); goto errret; } if (bpc != 8 && bpc != 16) { png_destroy_read_struct(&pngstruct,&pnginfo,0); return ANY_PXM_load(file); // use standard pixbuf loader } if (nc1 > 4) { // not gray (+alpha) or RGB (+alpha) png_destroy_read_struct(&pngstruct,&pnginfo,0); return ANY_PXM_load(file); // use standard pixbuf loader } pxm = PXM_make(ww,hh,nc2); // create PXM if (! pxm) { png_destroy_read_struct(&pngstruct,&pnginfo,0); goto errret; } rs1 = ww * nc1; // png input row stride if (bpc == 16) rs1 = rs1 * 2; pngbuff = (uchar *) zmalloc(hh * rs1); // png file input buffer for (row = 0; row < hh; row++) // get all png rows memcpy(pngbuff+row*rs1,pngrows[row],rs1); png_destroy_read_struct(&pngstruct,&pnginfo,0); // release memory if (bpc == 8) pixelvert((uint8 *) pngbuff,pxm->pixels,ww,hh,nc1,nc2); // convert pixel data 19.0 if (bpc == 16) pixelvert((uint16 *) pngbuff,pxm->pixels,ww,hh,nc1,nc2); PXM_audit(pxm); // audit/repair 20.0 zfree(pngbuff); f_load_bpc = bpc; return pxm; errret: if (fid && fileno(fid) != -1) fclose(fid); return 0; } // Write PXB pixmap to PNG file using PNG library. // File bpc is 8 or 16. // returns 0 if OK, +N if error. int PXB_PNG_save(PXB *pxb, cchar *file, int bpc) { png_structp pngstruct; png_infop pnginfo; FILE *fid = 0; uchar *pngbuff, **pngrows; int ww, hh, nc1, nc2, rs2, row; if (bpc != 8 && bpc != 16) { printz("PNG BPC not 8/16: %s",file); goto errret; } fid = fopen(file,"w"); // open output file if (! fid) { printz("file error: %s %s \n",file,strerror(errno)); goto errret; } ww = pxb->ww; hh = pxb->hh; nc1 = pxb->nc; nc2 = nc1; pngstruct = png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0); // create png structs if (! pngstruct) goto errret; pnginfo = png_create_info_struct(pngstruct); if (! pnginfo) goto errret; if (setjmp(png_jmpbuf(pngstruct))) goto errret; // setup error handling png_init_io(pngstruct,fid); // initz. for writing file png_set_compression_level(pngstruct,png_comp_level); // 20.0 if (nc1 == 4) 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); rs2 = ww * nc2; // png row length if (bpc == 16) rs2 = rs2 * 2; pngbuff = (uchar *) zmalloc(hh * rs2); // allocate png file data pngrows = (uchar **) zmalloc(hh * sizeof(char *)); // allocate png row pointers png_set_rows(pngstruct,pnginfo,pngrows); for (row = 0; row < hh; row++) // set row pointers to row data pngrows[row] = pngbuff + row * rs2; if (bpc == 8) pixelvert(pxb->pixels,(uint8 *) pngbuff,ww,hh,nc1,nc2); // convert pixel data 19.0 if (bpc == 16) pixelvert(pxb->pixels,(uint16 *) pngbuff,ww,hh,nc1,nc2); png_write_png(pngstruct,pnginfo,PNG_TRANSFORM_SWAP_ENDIAN,0); // write the file fclose(fid); png_destroy_write_struct(&pngstruct,&pnginfo); // release memory zfree(pngbuff); zfree(pngrows); return 0; errret: if (fid && fileno(fid) != -1) fclose(fid); return 2; } // Write PXM pixmap 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 = 0; uchar *pngbuff, **pngrows; int ww, hh, nc1, nc2, rs2, row; if (bpc != 8 && bpc != 16) { printz("PNG BPC not 8/16: %s",file); goto errret; } fid = fopen(file,"w"); // open output file if (! fid) { printz("file error: %s %s \n",file,strerror(errno)); goto errret; } ww = pxm->ww; hh = pxm->hh; nc1 = pxm->nc; nc2 = nc1; pngstruct = png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0); // create png structs if (! pngstruct) goto errret; pnginfo = png_create_info_struct(pngstruct); if (! pnginfo) goto errret; if (setjmp(png_jmpbuf(pngstruct))) goto errret; // setup error handling png_init_io(pngstruct,fid); // initz. for writing file png_set_compression_level(pngstruct,png_comp_level); // 20.0 if (nc1 == 4) 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); rs2 = ww * nc2; // png row length if (bpc == 16) rs2 = rs2 * 2; pngbuff = (uchar *) zmalloc(hh * rs2); // allocate png file data pngrows = (uchar **) zmalloc(hh * sizeof(char *)); // allocate png row pointers png_set_rows(pngstruct,pnginfo,pngrows); for (row = 0; row < hh; row++) // set row pointers to row data pngrows[row] = pngbuff + row * rs2; if (bpc == 8) pixelvert(pxm->pixels,(uint8 *) pngbuff,ww,hh,nc1,nc2); // convert pixel data 19.0 if (bpc == 16) pixelvert(pxm->pixels,(uint16 *) pngbuff,ww,hh,nc1,nc2); png_write_png(pngstruct,pnginfo,PNG_TRANSFORM_SWAP_ENDIAN,0); // write the file fclose(fid); png_destroy_write_struct(&pngstruct,&pnginfo); // release memory zfree(pngbuff); zfree(pngrows); return 0; errret: if (fid && fileno(fid) != -1) fclose(fid); return 2; } /******************************************************************************** HEIC file read functions (write functions are not implemented) *********************************************************************************/ // Load PXB pixmap from HEIC file using HEIC library (heif-convert). // Crutch: convert .heic file to .jpg and load .jpg file. // f_load_bpc = 8 PXB * HEIC_PXB_load(cchar *file, int size) // 20.0 { int err; char *jpegfile = 0, *jpegfix = 0; char *pp; PXB *pxb; STATB statb; if (! Fheif) { zmessageACK(Mwin,E2X("HEIF files not supported")); return 0; } err = stat(file,&statb); // check file exists if (err || ! S_ISREG(statb.st_mode)) return 0; // no print error jpegfile = zstrdup(file,8); // file.heic >> file.jpg pp = strrchr(jpegfile,'.'); if (! pp) goto errret; strcpy(pp,".jpg"); err = shell_quiet("heif-convert -q %d \"%s\" \"%s\" >/dev/null ", // convert to .jpg jpeg_def_quality, file, jpegfile); err = stat(jpegfile,&statb); if (err) { jpegfix = zstrdup(jpegfile); // failed, may have multiple output files pp = strrchr(jpegfix,'.'); // file-1.jpg, file-2.jpg, etc. strcpy(pp,"-1.jpg"); err = stat(jpegfix,&statb); if (err) goto errret; rename(jpegfix,jpegfile); // if file-1.jpg, rename to file.jpg zfree(jpegfix); // (file-2.jpg etc. remain) } pxb = JPG_PXB_load(jpegfile,size); // make PXB from jpeg file remove(jpegfile); zfree(jpegfile); return pxb; errret: if (jpegfile) remove(jpegfile); if (jpegfile) zfree(jpegfile); if (jpegfix) zfree(jpegfix); return 0; } // Load PXM pixmap from HEIC file using HEIC library (heif-convert). // Crutch: convert .heic file to .jpg and load .jpg file. // f_load_bpc = 8 PXM * HEIC_PXM_load(cchar *file) // 20.0 { int err; char *jpegfile = 0, *jpegfix = 0; char *pp; PXM *pxm; STATB statb; if (! Fheif) { zmessageACK(Mwin,E2X("HEIF files not supported")); return 0; } err = stat(file,&statb); if (err || ! S_ISREG(statb.st_mode)) return 0; // no print error jpegfile = zstrdup(file,8); pp = strrchr(jpegfile,'.'); if (! pp) goto errret; strcpy(pp,".jpg"); err = shell_quiet("heif-convert -q %d \"%s\" \"%s\" >/dev/null ", jpeg_def_quality, file, jpegfile); err = stat(jpegfile,&statb); if (err) { jpegfix = zstrdup(jpegfile); pp = strrchr(jpegfix,'.'); strcpy(pp,"-1.jpg"); err = stat(jpegfix,&statb); if (err) goto errret; rename(jpegfix,jpegfile); zfree(jpegfix); } pxm = JPG_PXM_load(jpegfile); remove(jpegfile); zfree(jpegfile); return pxm; errret: if (jpegfile) remove(jpegfile); if (jpegfile) zfree(jpegfile); if (jpegfix) zfree(jpegfix); return 0; } int PXB_HEIC_save(PXB *pxb, cchar *file) { zmessageACK(Mwin,"save to .heic file not implemented \n"); return 0; } int PXM_HEIC_save(PXM *pxm, cchar *file) { zmessageACK(Mwin,"save to .heic file not implemented \n"); return 0; } /******************************************************************************** JP2 file read functions (write functions are not implemented) *********************************************************************************/ // Load PXB pixmap from JP2 file using jpeg2000 utility (opj_decompress). // Crutch: convert .jp2 file to .tif and load .tif file. // f_load_bpc = 8 PXB * JP2_PXB_load(cchar *file) // 20.0 { int err, handle; char *tiffile = 0; char *pp; PXB *pxb; STATB statb; zdialog *zdm = 0; if (! Fjp2) { zmessageACK(Mwin,E2X("JP2 files not supported")); return 0; } err = stat(file,&statb); // check file exists if (err || ! S_ISREG(statb.st_mode)) return 0; // no print error tiffile = zstrdup(file,8); // file.jp2 >> file.tif pp = strrchr(tiffile,'.'); if (! pp) goto errret; strcpy(pp,".tif"); if (statb.st_size > 2 * MEGA) // may need a long time zdm = zmessage_post(Mwin,"20/20",0,"processing .jp2 file ..."); handle = shell_asynch("opj_decompress -i \"%s\" -o \"%s\" >/dev/null",file,tiffile); while (true) { zmainsleep(0.1); err = shell_asynch_status(handle); if (err >= 0) break; } if (zdm) zdialog_free(zdm); if (err) goto errret; err = stat(tiffile,&statb); if (err) goto errret; pxb = TIFF_PXB_load(tiffile); // make PXB from tif file remove(tiffile); zfree(tiffile); return pxb; errret: if (tiffile) remove(tiffile); if (tiffile) zfree(tiffile); return 0; } // Load PXM pixmap from JP2 file using jpeg2000 utility (opj_decompress). // Crutch: convert .jp2 file to .tif and load .tif file. // f_load_bpc = 8 PXM * JP2_PXM_load(cchar *file) // 20.0 { int err; char *tiffile = 0; char *pp; PXM *pxm; STATB statb; zdialog *zdm = 0; if (! Fjp2) { zmessageACK(Mwin,E2X("JP2 files not supported")); return 0; } err = stat(file,&statb); // check file exists if (err || ! S_ISREG(statb.st_mode)) return 0; // no print error tiffile = zstrdup(file,8); // file.jp2 >> file.tif pp = strrchr(tiffile,'.'); if (! pp) goto errret; strcpy(pp,".tif"); if (statb.st_size > 2 * MEGA) // may need a long time zdm = zmessage_post(Mwin,"20/20",0,"processing .jp2 file ..."); err = shell_quiet("opj_decompress -i \"%s\" -o \"%s\" >/dev/null ", // convert to .tif file, tiffile); if (zdm) zdialog_free(zdm); if (err) goto errret; err = stat(tiffile,&statb); if (err) goto errret; pxm = TIFF_PXM_load(tiffile); // make PXM from tif file remove(tiffile); zfree(tiffile); return pxm; errret: if (tiffile) remove(tiffile); if (tiffile) zfree(tiffile); return 0; } int PXB_JP2_save(PXB *pxb, cchar *file) { zmessageACK(Mwin,"save to .jp2 file not implemented \n"); return 0; } int PXM_JP2_save(PXM *pxm, cchar *file) { zmessageACK(Mwin,"save to .jp2 file not implemented \n"); return 0; } /******************************************************************************** Other file types read and write functions (via gdk_pixbuf library) *********************************************************************************/ // Load PXB pixmap from other file types using the pixbuf library. // bpc = 8. PXB * ANY_PXB_load(cchar *file) { GError *gerror = 0; PXB *pxb; PIXBUF *pixbuf; int err, ww, hh, px, py, nc1, nc2, ac, rs; uint8 *pixels1, *pix1; uint8 *pixels2, *pix2; STATB statb; err = stat(file,&statb); if (err || ! S_ISREG(statb.st_mode)) goto errret; // no print error 19.13 pixbuf = gdk_pixbuf_new_from_file(file,&gerror); if (! pixbuf) goto errret; ww = gdk_pixbuf_get_width(pixbuf); hh = gdk_pixbuf_get_height(pixbuf); nc1 = gdk_pixbuf_get_n_channels(pixbuf); ac = gdk_pixbuf_get_has_alpha(pixbuf); rs = gdk_pixbuf_get_rowstride(pixbuf); pixels1 = gdk_pixbuf_get_pixels(pixbuf); nc2 = 3 + ac; pxb = PXB_make(ww,hh,nc2); if (! pxb) { g_object_unref(pixbuf); goto errret; } pixels2 = pxb->pixels; for (py = 0; py < hh; py++) { pix1 = pixels1 + rs * py; pix2 = pixels2 + py * pxb->rs; if (nc1 >= 3) { for (px = 0; px < ww; px++) { memcpy(pix2,pix1,3); if (nc1 == 4) pix2[3] = pix1[3]; // uint8 > unit8 pix1 += nc1; pix2 += nc2; } } else { for (px = 0; px < ww; px++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; if (nc1 == 2) pix2[3] = pix1[1]; pix1 += nc1; pix2 += nc2; } } } g_object_unref(pixbuf); return pxb; errret: printz("pixbuf read error: %s \n",file); if (gerror) printz("%s \n",gerror->message); return 0; } // Load PXM pixmap from other file types using the pixbuf library. // bpc = 8. PXM * ANY_PXM_load(cchar *file) { GError *gerror = 0; PXM *pxm; PIXBUF *pixbuf; int err, ww, hh, px, py, nc1, nc2, ac, rs; uint8 *pixels1, *pix1; float *pixels2, *pix2; STATB statb; err = stat(file,&statb); if (err || ! S_ISREG(statb.st_mode)) goto errret; // no print error 19.13 pixbuf = gdk_pixbuf_new_from_file(file,&gerror); if (! pixbuf) goto errret; ww = gdk_pixbuf_get_width(pixbuf); hh = gdk_pixbuf_get_height(pixbuf); nc1 = gdk_pixbuf_get_n_channels(pixbuf); ac = gdk_pixbuf_get_has_alpha(pixbuf); rs = gdk_pixbuf_get_rowstride(pixbuf); pixels1 = gdk_pixbuf_get_pixels(pixbuf); nc2 = 3 + ac; pxm = PXM_make(ww,hh,nc2); if (! pxm) { g_object_unref(pixbuf); goto errret; } pixels2 = pxm->pixels; for (py = 0; py < hh; py++) { pix1 = pixels1 + rs * py; pix2 = pixels2 + py * ww * nc2; if (nc1 >= 3) { for (px = 0; px < ww; px++) { pix2[0] = pix1[0]; // uint8 > float pix2[1] = pix1[1]; pix2[2] = pix1[2]; if (nc1 == 4) pix2[3] = pix1[3]; pix1 += nc1; pix2 += nc2; } } else { for (px = 0; px < ww; px++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; if (ac) pix2[3] = pix1[1]; pix1 += nc1; pix2 += nc2; } } } g_object_unref(pixbuf); PXM_audit(pxm); // audit/repair 20.0 return pxm; errret: printz("pixbuf read error: %s \n",file); if (gerror) printz("%s \n",gerror->message); return 0; } /******************************************************************************** RAW file read functions. There are no write functions. *********************************************************************************/ int raw_match_thumb_color(cchar *rawfile, PXM *pxm); // 20.0 int raw_match_thumb_color(cchar *rawfile, PXB *pxb); // RAW file to PXB pixmap (pixbuf, 8-bit color) PXB * RAW_PXB_load(cchar *rawfile, int autobright, int matchthumb) // 20.0 { PXB *pxb; if (Frawloader == 1 || ! Frawtherapeecli) pxb = RAW_PXB_load_dcraw(rawfile,autobright); else pxb = RAW_PXB_load_rawtherapee(rawfile,autobright); if (! pxb) return 0; if (matchthumb) raw_match_thumb_color(rawfile,pxb); return pxb; } // RAW file to PXM pixmap (float color) PXM * RAW_PXM_load(cchar *rawfile, int autobright, int matchthumb) // 20.0 { PXM *pxm; if (Frawloader == 1 || ! Frawtherapeecli) pxm = RAW_PXM_load_dcraw(rawfile,autobright); else pxm = RAW_PXM_load_rawtherapee(rawfile,autobright); if (! pxm) return 0; if (matchthumb) raw_match_thumb_color(rawfile,pxm); return pxm; } // RAW file to PXB pixmap (pixbuf, 8-bit color) // 20.0 PXB * RAW_PXB_load_dcraw(cchar *rawfile, int autobright) { int err; char *tiffile, *pp; PXB *pxb; cchar *command; cchar *command1 = "dcraw -w -T -q 0 \"%s\" "; // tiff-8 output cchar *command2 = "dcraw -w -T -q 0 -W \"%s\" "; if (autobright) command = command1; else command = command2; pp = zescape_quotes(rawfile); // make tiff file from raw file err = shell_quiet(command,pp); zfree(pp); if (err) return 0; tiffile = zstrdup(rawfile,8); // tiff file name = rawfile.tiff pp = strrchr(tiffile,'/'); pp = strrchr(pp,'.'); if (! pp) pp = tiffile + strlen(tiffile); strcpy(pp,".tiff"); pxb = TIFF_PXB_load(tiffile); // make raw PXB from tiff file remove(tiffile); zfree(tiffile); return pxb; } // RAW file to PXB pixmap (pixbuf, 8-bit color) // 20.0 // use 1/2 size output file for faster thumbnail creation PXB * RAW_PXB_load_dcraw_half(cchar *rawfile) { int err; char *tiffile, *pp; PXB *pxb; cchar *command = "dcraw -w -T -q 0 \"%s\" "; pp = zescape_quotes(rawfile); // make tiff file from raw file err = shell_quiet(command,pp); zfree(pp); if (err) return 0; tiffile = zstrdup(rawfile,8); // tiff file name = rawfile.tiff pp = strrchr(tiffile,'/'); pp = strrchr(pp,'.'); if (! pp) pp = tiffile + strlen(tiffile); strcpy(pp,".tiff"); pxb = TIFF_PXB_load(tiffile); // make raw PXB from tiff file remove(tiffile); zfree(tiffile); return pxb; } // RAW file to PXM pixmap (float color) // 20.0 PXM * RAW_PXM_load_dcraw(cchar *rawfile, int autobright) { int err; char *tiffile, *pp; PXM *pxm; cchar *command; cchar *command1 = "dcraw -w -T -6 -q 0 \"%s\" "; // tiff-16 output cchar *command2 = "dcraw -w -T -6 -q 0 -W \"%s\" "; if (autobright) command = command1; else command = command2; pp = zescape_quotes(rawfile); // make tiff file from raw file err = shell_quiet(command,pp); zfree(pp); if (err) return 0; tiffile = zstrdup(rawfile,8); // tiff file name = rawfile.tiff pp = strrchr(tiffile,'/'); pp = strrchr(pp,'.'); if (! pp) pp = tiffile + strlen(tiffile); strcpy(pp,".tiff"); pxm = TIFF_PXM_load(tiffile); // make raw PXM from tiff file remove(tiffile); zfree(tiffile); return pxm; } // RAW file to PXB pixmap (pixbuf, 8-bit color) PXB * RAW_PXB_load_rawtherapee(cchar *rawfile, int autobright) // 20.0 { int err; PXB *pxb = 0; char *pp, *tiffile; cchar *command; cchar *command1 = "rawtherapee-cli -q -d -t -b16 -Y -c \"%s\" >/dev/null"; cchar *command2 = "rawtherapee-cli -q -t -b16 -Y -c \"%s\" >/dev/null"; Ffuncbusy++; zmainsleep(0.1); if (autobright) command = command1; else command = command2; pp = zescape_quotes(rawfile); // use rawtherapee err = shell_quiet(command,pp); // convert to rawfile.tif zfree(pp); if (err) { printz("RAW conversion failed: %s \n",rawfile); Ffuncbusy--; return 0; } tiffile = raw_to_tiff(rawfile); // rawfile.tif pxb = TIFF_PXB_load(tiffile); remove(tiffile); zfree(tiffile); Ffuncbusy--; return pxb; } // RAW file to PXM pixmap (float color) PXM * RAW_PXM_load_rawtherapee(cchar *rawfile, int autobright) // 20.0 { int err; PXM *pxm = 0; char *pp, *tiffile; cchar *command; cchar *command1 = "rawtherapee-cli -q -d -t -b16 -Y -c \"%s\" >/dev/null"; cchar *command2 = "rawtherapee-cli -q -t -b16 -Y -c \"%s\" >/dev/null"; Ffuncbusy++; zmainsleep(0.1); if (autobright) command = command1; else command = command2; pp = zescape_quotes(rawfile); // use rawtherapee err = shell_quiet(command,pp); // convert to rawfile.tif zfree(pp); if (err) { printz("RAW conversion failed: %s \n",rawfile); Ffuncbusy--; return 0; } tiffile = raw_to_tiff(rawfile); // rawfile.tif pxm = TIFF_PXM_load(tiffile); remove(tiffile); zfree(tiffile); Ffuncbusy--; return pxm; } // Use the RAW file embedded thumbnail as a template for RAW image color balance. // Input PXM RGB values are revised to match thumbnail histogram. // returns: 0 = OK, +N = error (PXM not changed). int raw_match_thumb_color(cchar *rawfile, PXM *Rpxm) { PXB *Tpxb; char *thumbfile, *pp; int ii, jj, kk, err, rgb, step; int Tpix, Rpix, Tpix3[3], Rpix3[3]; int Tdist[3][1024], Rdist[3][1024], Rval[3][1024]; int Rsum, Tsum, Rbin, Tbin; uint8 *Tpixel; float *Rpixel; STATB statb; pp = zescape_quotes(rawfile); // get embedded thumbnail file err = shell_quiet("dcraw -e \"%s\" ",pp); // from raw file zfree(pp); if (err) return 1; thumbfile = zstrdup(rawfile,12); // rawfile.thumb.jpg pp = strrchr(thumbfile,'/'); pp = strrchr(pp,'.'); if (! pp) pp = thumbfile + strlen(thumbfile); strcpy(pp,".thumb.jpg"); err = stat(thumbfile,&statb); if (! err) Tpxb = JPG_PXB_load(thumbfile); // make thumb PXB else { strcpy(pp+6,".ppm"); Tpxb = ANY_PXB_load(thumbfile); } remove(thumbfile); zfree(thumbfile); if (! Tpxb) return 2; // fail Tpix = Tpxb->ww * Tpxb->hh; // total thumb PXB pixels Rpix = Rpxm->ww * Rpxm->hh; // total raw PXM pixels memset(Tdist,0,3*1024*sizeof(int)); // clear thumb distribution for (ii = 0; ii < Tpix; ii++) // loop thumb pixels for (rgb = 0; rgb < 3; rgb++) { Tpixel = Tpxb->pixels + ii * 3; // build thumb brightness distribution jj = 4 * Tpixel[rgb]; // for each RGB channel Tdist[rgb][jj]++; Tdist[rgb][jj+1] = Tdist[rgb][jj+2] = Tdist[rgb][jj+3] = Tdist[rgb][jj]; // duplicate 4x for 1024 bins } for (ii = 0; ii < 255; ii++) // ramp each set of 4 duplicates for (rgb = 0; rgb < 3; rgb++) // 10 10 10 10 20 20 20 20 40 ... { // >> 10 12 15 17 20 25 30 35 40 ... jj = 4 * ii; step = Tdist[rgb][jj+4] - Tdist[rgb][jj]; for (kk = 1; kk < 4; kk++) Tdist[rgb][jj+kk] += kk * step / 4; } memset(Rdist,0,3*1024*sizeof(int)); // clear raw distribution for (ii = 0; ii < Rpix; ii++) // loop raw pixels for (rgb = 0; rgb < 3; rgb++) { Rpixel = Rpxm->pixels + ii * 3; // build raw brightness distribution jj = 4 * Rpixel[rgb]; // 0.0 - 255.99 >> bins 0 - 1023 if (jj > 1023) jj = 1023; Rdist[rgb][jj]++; } for (rgb = 0; rgb < 3; rgb++) Tpix3[rgb] = Rpix3[rgb] = 0; for (ii = 0; ii < 1024; ii++) // get distribution totals for (rgb = 0; rgb < 3; rgb++) { Tpix3[rgb] += Tdist[rgb][ii]; Rpix3[rgb] += Rdist[rgb][ii]; } for (ii = 0; ii < 1024; ii++) // make Tdist totals = Rdist totals for (rgb = 0; rgb < 3; rgb++) Tdist[rgb][ii] *= 1.0 * Rpix3[rgb] / Tpix3[rgb]; for (rgb = 0; rgb < 3; rgb++) // low to high { for (Rsum = 0, Rbin = 0; Rbin < 1024; Rbin++) { Rsum += Rdist[rgb][Rbin]; for (Tsum = 0, Tbin = 0; Tbin < 1024; Tbin++) { Tsum += Tdist[rgb][Tbin]; if (Tsum > Rsum) break; } if (Tbin == 256) Tbin = 255; Rval[rgb][Rbin] = Tbin; } } for (rgb = 0; rgb < 3; rgb++) // high to low { for (Rsum = 0, Rbin = 1023; Rbin >= 0; Rbin--) { Rsum += Rdist[rgb][Rbin]; for (Tsum = 0, Tbin = 1023; Tbin >= 0; Tbin--) { Tsum += Tdist[rgb][Tbin]; if (Tsum > Rsum) break; } if (Tbin < 0) Tbin = 0; Rval[rgb][Rbin] = 0.5 * (Rval[rgb][Rbin] + Tbin); // average two distributions } } for (ii = 0; ii < Rpix; ii++) // loop raw pixels { Rpixel = Rpxm->pixels + ii * 3; for (rgb = 0; rgb < 3; rgb++) // loop RGB color { Rbin = 4 * Rpixel[rgb]; // old brightness Rpixel[rgb] = 0.25 * Rval[rgb][Rbin]; // set new brightness } } return 0; } // PXB version of above. int raw_match_thumb_color(cchar *rawfile, PXB *Rpxb) { PXB *Tpxb; char *thumbfile, *pp; int ii, jj, kk, err, rgb, step; int Tpix, Rpix, Tpix3[3], Rpix3[3]; int Tdist[3][1024], Rdist[3][1024], Rval[3][1024]; int Rsum, Tsum, Rbin, Tbin; uint8 *Tpixel, *Rpixel; STATB statb; pp = zescape_quotes(rawfile); // get embedded thumbnail file err = shell_quiet("dcraw -e \"%s\" ",pp); // from raw file zfree(pp); if (err) return 1; thumbfile = zstrdup(rawfile,12); // rawfile.thumb.jpg pp = strrchr(thumbfile,'/'); pp = strrchr(pp,'.'); if (! pp) pp = thumbfile + strlen(thumbfile); strcpy(pp,".thumb.jpg"); err = stat(thumbfile,&statb); if (! err) Tpxb = JPG_PXB_load(thumbfile); // make thumb PXB else { strcpy(pp+6,".ppm"); Tpxb = ANY_PXB_load(thumbfile); } remove(thumbfile); zfree(thumbfile); if (! Tpxb) return 2; // fail Tpix = Tpxb->ww * Tpxb->hh; // total thumb PXB pixels Rpix = Rpxb->ww * Rpxb->hh; // total raw PXB pixels memset(Tdist,0,3*1024*sizeof(int)); // clear thumb distribution for (ii = 0; ii < Tpix; ii++) // loop thumb pixels for (rgb = 0; rgb < 3; rgb++) { Tpixel = Tpxb->pixels + ii * 3; // build thumb brightness distribution jj = 4 * Tpixel[rgb]; // for each RGB channel Tdist[rgb][jj]++; Tdist[rgb][jj+1] = Tdist[rgb][jj+2] = Tdist[rgb][jj+3] = Tdist[rgb][jj]; // duplicate 4x for 1024 bins } for (ii = 0; ii < 255; ii++) // ramp each set of 4 duplicates for (rgb = 0; rgb < 3; rgb++) // 10 10 10 10 20 20 20 20 40 ... { // >> 10 12 15 17 20 25 30 35 40 ... jj = 4 * ii; step = Tdist[rgb][jj+4] - Tdist[rgb][jj]; for (kk = 1; kk < 4; kk++) Tdist[rgb][jj+kk] += kk * step / 4; } memset(Rdist,0,3*1024*sizeof(int)); // clear raw distribution for (ii = 0; ii < Rpix; ii++) // loop raw pixels for (rgb = 0; rgb < 3; rgb++) { Rpixel = Rpxb->pixels + ii * 3; // build raw brightness distribution jj = 4 * Rpixel[rgb]; // 0.0 - 255.99 >> bins 0 - 1023 if (jj > 1023) jj = 1023; Rdist[rgb][jj]++; } for (rgb = 0; rgb < 3; rgb++) Tpix3[rgb] = Rpix3[rgb] = 0; for (ii = 0; ii < 1024; ii++) // get distribution totals for (rgb = 0; rgb < 3; rgb++) { Tpix3[rgb] += Tdist[rgb][ii]; Rpix3[rgb] += Rdist[rgb][ii]; } for (ii = 0; ii < 1024; ii++) // make Tdist totals = Rdist totals for (rgb = 0; rgb < 3; rgb++) Tdist[rgb][ii] *= 1.0 * Rpix3[rgb] / Tpix3[rgb]; for (rgb = 0; rgb < 3; rgb++) // low to high { for (Rsum = 0, Rbin = 0; Rbin < 1024; Rbin++) { Rsum += Rdist[rgb][Rbin]; for (Tsum = 0, Tbin = 0; Tbin < 1024; Tbin++) { Tsum += Tdist[rgb][Tbin]; if (Tsum > Rsum) break; } if (Tbin == 256) Tbin = 255; Rval[rgb][Rbin] = Tbin; } } for (rgb = 0; rgb < 3; rgb++) // high to low { for (Rsum = 0, Rbin = 1023; Rbin >= 0; Rbin--) { Rsum += Rdist[rgb][Rbin]; for (Tsum = 0, Tbin = 1023; Tbin >= 0; Tbin--) { Tsum += Tdist[rgb][Tbin]; if (Tsum > Rsum) break; } if (Tbin < 0) Tbin = 0; Rval[rgb][Rbin] = 0.5 * (Rval[rgb][Rbin] + Tbin); // average two distributions } } for (ii = 0; ii < Rpix; ii++) // loop raw pixels { Rpixel = Rpxb->pixels + ii * 3; for (rgb = 0; rgb < 3; rgb++) // loop RGB color { Rbin = 4 * Rpixel[rgb]; // old brightness Rpixel[rgb] = 0.25 * Rval[rgb][Rbin]; // set new brightness } } return 0; } /******************************************************************************** pixel format conversion functions 19.0 input pixel buffer: uint8/uint16/float with 1/2/3/4 channels output pixel buffer: uint8/uint16/float with 3/4 channels row stride is assumed to have no padding at row ends and no shortening of the last row (not compatible with GDK pixbufs) *********************************************************************************/ // copy from uint8 buffer to uint8 buffer void pixelvert(uint8 *buff1, uint8 *buff2, int ww, int hh, int nc1, int nc2) { uint8 *pix1; uint8 *pix2; int rs1 = ww * nc1, rs2 = ww * nc2; int row, col; for (row = 0; row < hh; row++) { pix1 = buff1 + row * rs1; pix2 = buff2 + row * rs2; if (nc1 == 1 && nc2 == 3) { for (col = 0; col < ww; col++) { memset(pix2,pix1[0],3); pix1 += nc1; pix2 += nc2; } } if (nc1 == 1 && nc2 == 4) { for (col = 0; col < ww; col++) { memset(pix2,pix1[0],3); pix2[3] = 255; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 3) { for (col = 0; col < ww; col++) { memset(pix2,pix1[0],3); pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 4) { for (col = 0; col < ww; col++) { memset(pix2,pix1[0],3); pix2[3] = pix1[1]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 3) { for (col = 0; col < ww; col++) { memcpy(pix2,pix1,3); pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 4) { for (col = 0; col < ww; col++) { memcpy(pix2,pix1,3); pix2[3] = 255; pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 3) { for (col = 0; col < ww; col++) { memcpy(pix2,pix1,3); pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 4) { for (col = 0; col < ww; col++) { memcpy(pix2,pix1,4); pix1 += nc1; pix2 += nc2; } } } return; } // copy from uint8 buffer to uint16 buffer void pixelvert(uint8 *buff1, uint16 *buff2, int ww, int hh, int nc1, int nc2) { uint8 *pix1; uint16 *pix2; int rs1 = ww * nc1, rs2 = ww * nc2; int row, col; for (row = 0; row < hh; row++) { pix1 = buff1 + row * rs1; pix2 = buff2 + row * rs2; if (nc1 == 1 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0] * 256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 1 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0] * 256; pix2[3] = 255 * 256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0] * 256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0] * 256; pix2[3] = pix1[1] * 256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0] * 256; pix2[1] = pix1[1] * 256; pix2[2] = pix1[2] * 256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0] * 256; pix2[1] = pix1[1] * 256; pix2[2] = pix1[2] * 256; pix2[3] = 255 * 256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0] * 256; pix2[1] = pix1[1] * 256; pix2[2] = pix1[2] * 256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0] * 256; pix2[1] = pix1[1] * 256; pix2[2] = pix1[2] * 256; pix2[3] = pix1[3] * 256; pix1 += nc1; pix2 += nc2; } } } return; } // copy from uint8 buffer to float buffer void pixelvert(uint8 *buff1, float *buff2, int ww, int hh, int nc1, int nc2) { uint8 *pix1; float *pix2; int rs1 = ww * nc1, rs2 = ww * nc2; int row, col; for (row = 0; row < hh; row++) { pix1 = buff1 + row * rs1; pix2 = buff2 + row * rs2; if (nc1 == 1 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 1 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; pix2[3] = 255; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; pix2[3] = pix1[1]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0]; pix2[1] = pix1[1]; pix2[2] = pix1[2]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0]; pix2[1] = pix1[1]; pix2[2] = pix1[2]; pix2[3] = 255; pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0]; pix2[1] = pix1[1]; pix2[2] = pix1[2]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0]; pix2[1] = pix1[1]; pix2[2] = pix1[2]; pix2[3] = pix1[3]; pix1 += nc1; pix2 += nc2; } } } return; } // copy from uint16 buffer to uint8 buffer void pixelvert(uint16 *buff1, uint8 *buff2, int ww, int hh, int nc1, int nc2) { uint16 *pix1; uint8 *pix2; int rs1 = ww * nc1, rs2 = ww * nc2; int row, col; for (row = 0; row < hh; row++) { pix1 = buff1 + row * rs1; pix2 = buff2 + row * rs2; if (nc1 == 1 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0] >> 8; pix1 += nc1; pix2 += nc2; } } if (nc1 == 1 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0] >> 8; pix2[3] = 255; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0] >> 8; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0] >> 8; pix2[3] = pix1[1] >> 8; pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0] >> 8; pix2[1] = pix1[1] >> 8; pix2[2] = pix1[2] >> 8; pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0] >> 8; pix2[1] = pix1[1] >> 8; pix2[2] = pix1[2] >> 8; pix2[3] = 255; pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0] >> 8; pix2[1] = pix1[1] >> 8; pix2[2] = pix1[2] >> 8; pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0] >> 8; pix2[1] = pix1[1] >> 8; pix2[2] = pix1[2] >> 8; pix2[3] = pix1[3] >> 8; pix1 += nc1; pix2 += nc2; } } } return; } // copy from uint16 buffer to uint16 buffer void pixelvert(uint16 *buff1, uint16 *buff2, int ww, int hh, int nc1, int nc2) { uint16 *pix1; uint16 *pix2; int rs1 = ww * nc1, rs2 = ww * nc2; int row, col; for (row = 0; row < hh; row++) { pix1 = buff1 + row * rs1; pix2 = buff2 + row * rs2; if (nc1 == 1 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 1 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; pix2[3] = 255; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; pix2[3] = pix1[1]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 3) { for (col = 0; col < ww; col++) { memcpy(pix2,pix1,6); pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 4) { for (col = 0; col < ww; col++) { memcpy(pix2,pix1,6); pix2[3] = 255 * 256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 3) { for (col = 0; col < ww; col++) { memcpy(pix2,pix1,6); pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 4) { for (col = 0; col < ww; col++) { memcpy(pix2,pix1,8); pix1 += nc1; pix2 += nc2; } } } return; } // copy from uint16 buffer to float buffer void pixelvert(uint16 *buff1, float *buff2, int ww, int hh, int nc1, int nc2) { uint16 *pix1; float *pix2; float f256 = 1.0 / 256.0; int rs1 = ww * nc1, rs2 = ww * nc2; int row, col; for (row = 0; row < hh; row++) { pix1 = buff1 + row * rs1; pix2 = buff2 + row * rs2; if (nc1 == 1 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0] * f256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 1 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0] * f256; pix2[3] = 255; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0] * f256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0] * f256; pix2[3] = pix1[1] * f256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0] * f256; pix2[1] = pix1[1] * f256; pix2[2] = pix1[2] * f256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0] * f256; pix2[1] = pix1[1] * f256; pix2[2] = pix1[2] * f256; pix2[3] = 255; pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0] * f256; pix2[1] = pix1[1] * f256; pix2[2] = pix1[2] * f256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0] * f256; pix2[1] = pix1[1] * f256; pix2[2] = pix1[2] * f256; pix2[3] = pix1[3] * f256; pix1 += nc1; pix2 += nc2; } } } return; } // copy from float buffer to uint8 buffer void pixelvert(float *buff1, uint8 *buff2, int ww, int hh, int nc1, int nc2) { float *pix1; uint8 *pix2; int rs1 = ww * nc1, rs2 = ww * nc2; int row, col; for (row = 0; row < hh; row++) { pix1 = buff1 + row * rs1; pix2 = buff2 + row * rs2; if (nc1 == 1 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 1 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; pix2[3] = 255; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; pix2[3] = pix1[1]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0]; pix2[1] = pix1[1]; pix2[2] = pix1[2]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0]; pix2[1] = pix1[1]; pix2[2] = pix1[2]; pix2[3] = 255; pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0]; pix2[1] = pix1[1]; pix2[2] = pix1[2]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0]; pix2[1] = pix1[1]; pix2[2] = pix1[2]; pix2[3] = pix1[3]; pix1 += nc1; pix2 += nc2; } } } return; } // copy from float buffer to uint16 buffer void pixelvert(float *buff1, uint16 *buff2, int ww, int hh, int nc1, int nc2) { float *pix1; uint16 *pix2; int rs1 = ww * nc1, rs2 = ww * nc2; int row, col; for (row = 0; row < hh; row++) { pix1 = buff1 + row * rs1; pix2 = buff2 + row * rs2; if (nc1 == 1 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0] * 256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 1 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0] * 256; pix2[3] = 255 * 256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0] * 256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0] * 256; pix2[3] = pix1[1] * 256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0] * 256; pix2[1] = pix1[1] * 256; pix2[2] = pix1[2] * 256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0] * 256; pix2[1] = pix1[1] * 256; pix2[2] = pix1[2] * 256; pix2[3] = 255 * 256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0] * 256; pix2[1] = pix1[1] * 256; pix2[2] = pix1[2] * 256; pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix1[0] * 256; pix2[1] = pix1[1] * 256; pix2[2] = pix1[2] * 256; pix2[3] = pix1[3] * 256; pix1 += nc1; pix2 += nc2; } } } return; } // copy from float buffer to float buffer void pixelvert(float *buff1, float *buff2, int ww, int hh, int nc1, int nc2) { float *pix1; float *pix2; int rs1 = ww * nc1, rs2 = ww * nc2; int row, col; for (row = 0; row < hh; row++) { pix1 = buff1 + row * rs1; pix2 = buff2 + row * rs2; if (nc1 == 1 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 1 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; pix2[3] = 255; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 3) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 2 && nc2 == 4) { for (col = 0; col < ww; col++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; pix2[3] = pix1[1]; pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 3) { for (col = 0; col < ww; col++) { memcpy(pix2, pix1, 3 * sizeof(float)); pix1 += nc1; pix2 += nc2; } } if (nc1 == 3 && nc2 == 4) { for (col = 0; col < ww; col++) { memcpy(pix2, pix1, 3 * sizeof(float)); pix2[3] = 255; pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 3) { for (col = 0; col < ww; col++) { memcpy(pix2, pix1, 3 * sizeof(float)); pix1 += nc1; pix2 += nc2; } } if (nc1 == 4 && nc2 == 4) { for (col = 0; col < ww; col++) { memcpy(pix2, pix1, 4 * sizeof(float)); pix1 += nc1; pix2 += nc2; } } } return; } fotoxx-20.08/f.process.cc000066400000000000000000003170261362435004500152760ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* Fotoxx image edit - Process Menu functions m_batch_convert batch rename, convert, resize, move batch_sharp_func callable sharpen function m_batch_upright find rotated files and upright them m_batch_deltrash delete or trash selected files m_batch_RAW convert RAW files to jpg/png/tif, 8/16 bits m_burn_DVD burn images to DVD/Blue-Ray disc m_export_filelist select files and generate a file list (text) m_export_files select files and export to a folder m_edit_script edit a script file calling multiple edit functions m_edit_script_addfunc edit_done() hook to insert dialog settings into script m_run_script run script like edit function for current image file m_batch_script run script for batch of selected image files *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) #include /********************************************************************************/ // Batch file rename, convert, resize, move namespace batch_convert { int Fsametype, Fsamesize, maxww, maxhh; int Fsamebpc, newbpc, jpeg_quality; int Fdelete, Fcopymeta, Fupright, Fsharpen, Freplace; int Fplugdate, Fplugseq, Fplugname; char newloc[500], newname[200], newext[8]; int baseseq, addseq; int amount, thresh; int Foverlay, ovpos, ovwidth; int Fovconst, ovscrww, ovscrhh; char *ovfile = 0; }; // menu function void m_batch_convert(GtkWidget *, cchar *) { using namespace batch_convert; int batch_convert_dialog_event(zdialog *zd, cchar *event); int batch_sharp_func(PXM *pxm, int amount, int thresh); zdialog *zd, *zd2; int zstat; char *infile, *outfile, tempfile[300]; char *inloc, *inname, *inext; char *outloc, *outname, *outext; char *tempname, seqplug[8], seqnum[8]; char plugyyyy[8], plugmm[4], plugdd[4]; char **oldfiles, **newfiles; char *pp, *pp1, *ppv[2], text[100]; int ii, jj, cc, err, Fjpeg; int outww, outhh, outbpc, Fdelete2; int Noldnew; char orientation; float scale, wscale, hscale; PXM *pxmin, *pxmout; STATB statdat; xxrec_t *xxrec; cchar *exifkey[1] = { exif_orientation_key }; cchar *exifdata[1]; GdkPixbuf *ovpxb; GError *gerror = 0; float imageR, screenR; int pww, phh, orgx, orgy, nc, rs; int px1, py1, px2, py2; uint8 *pix0, *pix1; float *pix2, f1, f2; F1_help_topic = "batch convert"; if (checkpend("all")) return; // check nothing pending if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } Fblock = 1; /*** ______________________________________________________________ | Batch Convert | | | | [Select Files] N files selected | | | | New Name [________________________________________________] | | Sequence Numbers base [_____] adder [____] | | New Location [__________________________________] [browse] | | | | (o) tif (o) png (o) jpg [90] jpg quality (o) no change | | Color Depth: (o) 8-bit (o) 16-bit (o) no change | | Max. Width [____] Height [____] [x] no change | | [x] Delete Originals [x] Copy Metadata [x] Upright | | [x] Sharpen amount [___] threshold [___] | | | | [x] Overlay Image [open] Width % [ 23 ] | | Overlay Position [_] [_] [_] [_] [_] [_] [_] [_] [_] | | [x] make constant width for screen [_____] [_____] | | | | [proceed] [cancel] | |______________________________________________________________| ***/ zd = zdialog_new(E2X("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",E2X("New Name"),"space=5"); zdialog_add_widget(zd,"zentry","newname","hbname",0,"expand"); zdialog_add_widget(zd,"hbox","hbseq","dialog"); zdialog_add_widget(zd,"label","labseq","hbseq",E2X("Sequence Numbers"),"space=5"); zdialog_add_widget(zd,"label","space","hbseq",0,"space=8"); zdialog_add_widget(zd,"label","labbase","hbseq",E2X("base"),"space=5"); zdialog_add_widget(zd,"zentry","baseseq","hbseq",0,"size=5"); zdialog_add_widget(zd,"label","space","hbseq","","space=3"); zdialog_add_widget(zd,"label","labadder","hbseq",E2X("adder"),"space=5"); zdialog_add_widget(zd,"zentry","addseq","hbseq",0,"size=3"); zdialog_add_widget(zd,"label","space","hbseq",0,"space=60"); // push back oversized entries zdialog_add_widget(zd,"hbox","hbloc","dialog"); zdialog_add_widget(zd,"label","labloc","hbloc",E2X("New Location"),"space=5"); zdialog_add_widget(zd,"zentry","newloc","hbloc",0,"expand"); zdialog_add_widget(zd,"button","browse","hbloc",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hbft","dialog"); zdialog_add_widget(zd,"label","labtyp","hbft",E2X("New File Type"),"space=5"); zdialog_add_widget(zd,"radio","tif","hbft","tif","space=4"); zdialog_add_widget(zd,"radio","png","hbft","png","space=4"); zdialog_add_widget(zd,"radio","jpg","hbft","jpg","space=2"); zdialog_add_widget(zd,"zspin","jpgqual","hbft","10|100|1|90","size=3"); zdialog_add_widget(zd,"label","labqual","hbft","jpg quality","space=6"); zdialog_add_widget(zd,"radio","sametype","hbft",E2X("no change"),"space=6"); zdialog_add_widget(zd,"hbox","hbcd","dialog"); zdialog_add_widget(zd,"label","labcd","hbcd",E2X("Color Depth:"),"space=5"); zdialog_add_widget(zd,"radio","8-bit","hbcd","8-bit","space=4"); zdialog_add_widget(zd,"radio","16-bit","hbcd","16-bit","space=4"); zdialog_add_widget(zd,"radio","samebpc","hbcd",E2X("no change"),"space=4"); zdialog_add_widget(zd,"hbox","hbwh","dialog"); zdialog_add_widget(zd,"label","labw","hbwh",E2X("max. Width"),"space=5"); zdialog_add_widget(zd,"zentry","maxww","hbwh","1000","size=5"); zdialog_add_widget(zd,"label","space","hbwh",0,"space=5"); zdialog_add_widget(zd,"label","labh","hbwh",Bheight,"space=5"); zdialog_add_widget(zd,"zentry","maxhh","hbwh","700","size=5"); zdialog_add_widget(zd,"check","samesize","hbwh",E2X("no change"),"space=12"); zdialog_add_widget(zd,"label","space","hbwh",0,"space=30"); // push back oversized entries zdialog_add_widget(zd,"hbox","hbopts","dialog"); zdialog_add_widget(zd,"check","delete","hbopts",E2X("Delete Originals"),"space=3"); zdialog_add_widget(zd,"check","copymeta","hbopts",E2X("Copy Metadata"),"space=5"); zdialog_add_widget(zd,"check","upright","hbopts",E2X("Upright"),"space=5"); zdialog_add_widget(zd,"hbox","hbsharp","dialog"); zdialog_add_widget(zd,"check","sharpen","hbsharp",E2X("Sharpen"),"space=3"); zdialog_add_widget(zd,"label","space","hbsharp",0,"space=8"); zdialog_add_widget(zd,"label","labamount","hbsharp",Bamount,"space=3"); zdialog_add_widget(zd,"zspin","amount","hbsharp","0|400|1|100"); zdialog_add_widget(zd,"label","space","hbsharp",0,"space=8"); zdialog_add_widget(zd,"label","labthresh","hbsharp",Bthresh,"space=3"); zdialog_add_widget(zd,"zspin","thresh","hbsharp","0|100|1|20"); zdialog_add_widget(zd,"hsep","ovsep","dialog","","space=3"); zdialog_add_widget(zd,"hbox","hbov1","dialog"); // overlay image zdialog_add_widget(zd,"check","ckov","hbov1",E2X("Overlay Image"),"space=3"); zdialog_add_widget(zd,"button","imgov","hbov1",Bopen,"space=3"); zdialog_add_widget(zd,"label","space","hbov1",0,"space=5"); zdialog_add_widget(zd,"label","labww","hbov1",E2X("Width %"),"space=3"); zdialog_add_widget(zd,"zspin","ovwidth","hbov1","1.0|100.0|0.5|10.0","space=3"); zdialog_add_widget(zd,"label","space","hbov1",0,"space=5"); zdialog_add_widget(zd,"hbox","hbov2","dialog"); zdialog_add_widget(zd,"label","labpos","hbov2",E2X("Position"),"space=3"); zdialog_add_widget(zd,"imagebutt","ovTL","hbov2","overlayTL.png","size=20|space=2"); zdialog_add_widget(zd,"imagebutt","ovTC","hbov2","overlayTC.png","size=20|space=2"); zdialog_add_widget(zd,"imagebutt","ovTR","hbov2","overlayTR.png","size=20|space=2"); zdialog_add_widget(zd,"imagebutt","ovCL","hbov2","overlayCL.png","size=20|space=2"); zdialog_add_widget(zd,"imagebutt","ovCC","hbov2","overlayCC.png","size=20|space=2"); zdialog_add_widget(zd,"imagebutt","ovCR","hbov2","overlayCR.png","size=20|space=2"); zdialog_add_widget(zd,"imagebutt","ovBL","hbov2","overlayBL.png","size=20|space=2"); zdialog_add_widget(zd,"imagebutt","ovBC","hbov2","overlayBC.png","size=20|space=2"); zdialog_add_widget(zd,"imagebutt","ovBR","hbov2","overlayBR.png","size=20|space=2"); zdialog_add_widget(zd,"hbox","hbov3","dialog"); zdialog_add_widget(zd,"check","ovconst","hbov3",E2X("Make constant size for screen:"),"space=3"); zdialog_add_widget(zd,"label","space","hbov3","","space=3"); zdialog_add_widget(zd,"label","ovlabww","hbov3",Bwidth,"space=3"); zdialog_add_widget(zd,"zentry","ovscrww","hbov3","","size=5|space=3"); zdialog_add_widget(zd,"label","space","hbov3","","space=3"); zdialog_add_widget(zd,"label","ovlabhh","hbov3",Bheight,"space=3"); zdialog_add_widget(zd,"zentry","ovscrhh","hbov3","","size=5|space=3"); zdialog_add_ttip(zd,"newname",E2X("plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s")); snprintf(text,100,Bfileselected,GScount); // show selected files count 20.0 zdialog_stuff(zd,"fcount",text); zdialog_stuff(zd,"tif",0); zdialog_stuff(zd,"png",0); zdialog_stuff(zd,"jpg",0); zdialog_stuff(zd,"jpgqual",jpeg_def_quality); zdialog_stuff(zd,"sametype",1); // same file type zdialog_stuff(zd,"8-bit",0); zdialog_stuff(zd,"16-bit",0); zdialog_stuff(zd,"samebpc",1); // same bits/color 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); // sharpen amount zdialog_stuff(zd,"thresh",20); // sharpen threshold zdialog_stuff(zd,"ovconst",0); // overlay constant size - no zdialog_stuff(zd,"ovscrww",zfuncs::monitor_ww); // screen pixel size zdialog_stuff(zd,"ovscrhh",zfuncs::monitor_hh); *newloc = 0; oldfiles = newfiles = 0; Noldnew = 0; Foverlay = 0; ovpos = 0; Fovconst = 0; zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,batch_convert_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_free(zd); if (zstat != 1) goto cleanup; // canceled if (! GScount) goto cleanup; free_resources(); // no curr. file if (Fdelete) { cc = GScount * sizeof(char *); // input files are to be deleted: oldfiles = (char **) zmalloc(cc); // reserve space to hold list of newfiles = (char **) zmalloc(cc); // old/new filespecs to update albums } zd2 = popup_report_open("Processing files",Mwin,600,300,0,0,0); // status monitor popup window for (ii = 0; ii < GScount; ii++) // loop selected files { zmainloop(); // keep GTK alive infile = GSfiles[ii]; // input file popup_report_write(zd2,0,"\n"); popup_report_write(zd2,0,"%s \n",infile); // log each input file parsefile(infile,&inloc,&inname,&inext); // parse folder, filename, .ext if (! inloc || ! inname || ! inext) continue; outloc = zstrdup(inloc); // initial output = input file outname = zstrdup(inname); outext = zstrdup(inext,4); cc = strlen(outloc) - 1; // remove trailing '/' if (outloc[cc] == '/') outloc[cc] = 0; if (*newname) { zfree(outname); outname = zstrdup(newname); // may contain $-plugins } if (Fplugname) // insert old file name { cc = strlen(outname) - 8 + strlen(inname); if (cc < 1) cc = 1; tempname = zstrdup(outname,cc); repl_1str(outname,tempname,"$oldname",inname); // ...$oldname... >> ...inname... zfree(outname); outname = tempname; } if (Fplugdate) // insert EXIF date { xxrec = get_xxrec(infile); // get photo date, yyyymmddhhmmdd 19.0 if (strmatch(xxrec->pdate,"null")) { popup_report_write(zd2,0,"no photo date, skipped \n"); // 19.13 zfree(outloc); zfree(outname); zfree(outext); continue; } strncpy0(plugyyyy,xxrec->pdate,5); // yyyy strncpy0(plugmm,xxrec->pdate+4,3); // mm strncpy0(plugdd,xxrec->pdate+6,3); // dd tempname = zstrdup(outname,8); repl_Nstrs(outname,tempname,"$yyyy",plugyyyy,"$mm",plugmm,"$dd",plugdd,null); zfree(outname); outname = tempname; } if (Fplugseq) // insert sequence number { pp = strcasestr(outname,"$s"); // find $s... in output name if (pp) for (cc = 1; pp[cc] == pp[1] && cc < 6; cc++); // length of "$s...s" 2-6 chars. strncpy0(seqplug,pp,cc+1); jj = baseseq + ii * addseq; // new sequence number snprintf(seqnum,8,"%0*d",cc-1,jj); // 1-5 chars. tempname = zstrdup(outname,8); repl_1str(outname,tempname,seqplug,seqnum); // ...$ssss... >> ...1234... zfree(outname); outname = tempname; } if (*newloc) { // new location was given zfree(outloc); outloc = zstrdup(newloc); } if (Fsametype) { if (strcasestr(".jpg .jpeg",inext)) strcpy(outext,".jpg"); // new .ext from existing .ext else if (strcasestr(".png",inext)) strcpy(outext,".png"); else if (strcasestr(".tif .tiff",inext)) strcpy(outext,".tif"); else strcpy(outext,".jpg"); // unknown >> .jpg } else strcpy(outext,newext); // new .ext was given cc = strlen(outloc) + strlen(outname) + strlen(outext) + 4; outfile = (char *) zmalloc(cc); snprintf(outfile,cc,"%s/%s%s",outloc,outname,outext); Fjpeg = 0; // bugfix 19.0 if (strmatch(outext,".jpg")) Fjpeg = 1; zfree(outloc); zfree(outname); zfree(outext); pxmin = PXM_load(infile,0); // read input file if (! pxmin) { popup_report_write(zd2,1,E2X("file type not supported: %s \n"),inext); zfree(outfile); continue; } popup_report_write(zd2,0,"%s \n",outfile); // log each output file if (*newloc) { err = stat(outfile,&statdat); // check if file exists in new location if (! err) { popup_report_write(zd2,1,"%s \n",Bfileexists); zfree(outfile); continue; } } if (Fupright) // upright image if turned { exif_get(infile,exifkey,ppv,1); // get EXIF: Orientation if (ppv[0]) { orientation = *ppv[0]; // single character zfree(ppv[0]); } else orientation = '1'; // if missing assume unrotated pxmout = 0; if (orientation == '6') pxmout = PXM_rotate(pxmin,+90); // rotate clockwise 90 deg. if (orientation == '8') pxmout = PXM_rotate(pxmin,-90); // counterclockwise 90 deg. if (orientation == '3') pxmout = PXM_rotate(pxmin,180); // 180 deg. if (pxmout) { PXM_free(pxmin); // input image unrotated pxmin = pxmout; } } outbpc = f_load_bpc; // input file bits/color outww = pxmin->ww; // input file size outhh = pxmin->hh; if (! Fsamebpc) outbpc = newbpc; // new bits/color if (Fjpeg) outbpc = 8; // if jpeg, force 8 bits/color 19.0 if (Fsamesize) pxmout = pxmin; // same size, output = input else { wscale = hscale = 1.0; if (outww > maxww) wscale = 1.0 * maxww / outww; // compute new size if (outhh > maxhh) hscale = 1.0 * maxhh / outhh; if (wscale < hscale) scale = wscale; else scale = hscale; if (scale > 0.999) pxmout = pxmin; // no change else { outww = outww * scale; outhh = outhh * scale; pxmout = PXM_rescale(pxmin,outww,outhh); // rescaled output file PXM_free(pxmin); // free memory } } if (Fsharpen) // auto sharpen output image batch_sharp_func(pxmout,amount,thresh); if (Foverlay) // add overlay image { pww = outww * 0.01 * ovwidth; // overlay width, % image width if (Fovconst) { // make overlay width constant for imageR = 1.0 * outww / outhh; // images with variable ww/hh screenR = 1.0 * ovscrww / ovscrhh; if (imageR < screenR) pww = pww * screenR / imageR; // image < screen, increase width } ovpxb = gdk_pixbuf_new_from_file_at_scale(ovfile,pww,-1,1,&gerror); // read and scale overlay image if (! ovpxb) { popup_report_write(zd2,1,"overlay file error: %s \n",ovfile,gerror->message); zfree(outfile); PXM_free(pxmout); gerror = 0; continue; } pww = gdk_pixbuf_get_width(ovpxb); // get actual overlay pixbuf data phh = gdk_pixbuf_get_height(ovpxb); nc = gdk_pixbuf_get_n_channels(ovpxb); rs = gdk_pixbuf_get_rowstride(ovpxb); pix0 = gdk_pixbuf_get_pixels(ovpxb); orgx = orgy = -1; if (ovpos == 1) orgx = orgy = 0; // top left if (ovpos == 2) { orgx = (outww-pww)/2; orgy = 0; } // top center if (ovpos == 3) { orgx = outww-pww; orgy = 0; } // top right if (ovpos == 4) { orgx = 0; orgy = (outhh-phh)/2; } // center left if (ovpos == 5) { orgx = (outww-pww)/2; orgy = (outhh-phh)/2; } // center center if (ovpos == 6) { orgx = outww-pww; orgy = (outhh-phh)/2; } // center right if (ovpos == 7) { orgx = 0; orgy = outhh-phh; } // bottom left if (ovpos == 8) { orgx = (outww-pww)/2; orgy = outhh-phh; } // bottom center if (ovpos == 9) { orgx = outww-pww; orgy = outhh-phh; } // bottom right if (orgx < 0) orgx = 0; if (orgy < 0) orgy = 0; if (orgx + pww > outww) pww = outww - orgx; if (orgy + phh > outhh) phh = outhh - orgy; for (py1 = 0; py1 < phh; py1++) // loop all pixels in overlay image for (px1 = 0; px1 < pww; px1++) { pix1 = pix0 + py1 * rs + px1 * nc; // copy-from overlay pixel px2 = orgx + px1; // copy-to image pixel py2 = orgy + py1; pix2 = PXMpix(pxmout,px2,py2); if (nc > 3) f1 = pix1[3] / 256.0; // visible part else f1 = 1; f2 = 1.0 - f1; pix2[0] = f1 * pix1[0] + f2 * pix2[0]; // combine overlay and image pix2[1] = f1 * pix1[1] + f2 * pix2[1]; pix2[2] = f1 * pix1[2] + f2 * pix2[2]; } g_object_unref(ovpxb); // free overlay pixbuf ovpxb = 0; } pp1 = strrchr(outfile,'/'); // get filename.ext if (pp1) pp1++; else pp1 = outfile; snprintf(tempfile,300,"%s/%s",temp_folder,pp1); // temp file for EXIF/IPTC copy err = PXM_save(pxmout,tempfile,outbpc,jpeg_quality,0); // write output file to temp file 19.0 if (err) { popup_report_write(zd2,1,"%s \n",E2X("cannot create new file")); zfree(outfile); PXM_free(pxmout); continue; } if (Fcopymeta) // copy EXIF/IPTC if requested { if (Fupright) { // if image possibly uprighted exifdata[0] = ""; // remove exif:orientation exif_copy(infile,tempfile,exifkey,exifdata,1); // (set = 1 does not work) } else exif_copy(infile,tempfile,0,0,0); // else no EXIF change } err = copyFile(tempfile,outfile); // copy tempfile to output file if (err) popup_report_write(zd2,1,"%s \n",strerror(err)); remove(tempfile); // remove tempfile Fdelete2 = 0; // figure out if input file can be deleted if (Fdelete) Fdelete2 = 1; // user says yes if (err) Fdelete2 = 0; // not if error if (strmatch(infile,outfile)) Fdelete2 = 0; // not if overwritten by output if (Fdelete2) { // delete input file remove(infile); delete_image_index(infile); // remove from image index delete_thumbfile(infile); // delete thumbnail, file and cache } if (! err) { load_filemeta(outfile); // update image index for output file update_image_index(outfile); } if (Fdelete2) { // if input file was deleted, oldfiles[Noldnew] = zstrdup(infile); // mark for updating albums newfiles[Noldnew] = zstrdup(outfile); Noldnew++; } zfree(outfile); PXM_free(pxmout); } if (Noldnew) { // update albums for renamed/moved files popup_report_write(zd2,0,"%s \n",E2X("updating albums ...")); album_batch_rename(oldfiles,newfiles,Noldnew); } popup_report_write(zd2,0,"\n *** %s \n",Bcompleted); popup_report_bottom(zd2); cleanup: if (Noldnew) { for (ii = 0; ii < Noldnew; ii++) { zfree(oldfiles[ii]); zfree(newfiles[ii]); } zfree(oldfiles); zfree(newfiles); Noldnew = 0; } Fblock = 0; gallery(navi::galleryname,"init",0); // refresh file list gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // repaint from same position return; } // dialog event and completion callback function int batch_convert_dialog_event(zdialog *zd, cchar *event) { using namespace batch_convert; char *pp, badplug[20]; char countmess[80]; char *ploc, *ofile, *oname = 0; int ii, cc, yn, err; GdkPixbuf *ovpxb; GError *gerror = 0; if (strmatch(event,"files")) // select images to convert { zdialog_show(zd,0); // hide parent dialog gallery_select(); // get list of files to convert zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); // update dialog zdialog_stuff(zd,"fcount",countmess); } if (strmatch(event,"browse")) { zdialog_fetch(zd,"newloc",newloc,500); ploc = zgetfile(E2X("Select folder"),MWIN,"folder",newloc); // new location browse if (! ploc) return 1; zdialog_stuff(zd,"newloc",ploc); zfree(ploc); } if (zstrstr("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 (zstrstr("tif png jpg sametype",event)) { // gtk fails to do this correctly zdialog_stuff(zd,"tif",0); zdialog_stuff(zd,"png",0); zdialog_stuff(zd,"jpg",0); zdialog_stuff(zd,"sametype",0); zdialog_stuff(zd,event,1); } if (zstrstr("8-bit 16-bit samebpc",event)) { // gtk fails to do this correctly zdialog_stuff(zd,"8-bit",0); zdialog_stuff(zd,"16-bit",0); zdialog_stuff(zd,"samebpc",0); zdialog_stuff(zd,event,1); } zdialog_fetch(zd,"jpg",ii); // if jpeg, force 8 bits/color if (ii) { zdialog_stuff(zd,"16-bit",0); zdialog_stuff(zd,"samebpc",0); zdialog_stuff(zd,"8-bit",1); } if (strmatch(event,"imgov")) { // [open] overlay image file if (ovfile) ofile = gallery_select1(ovfile); else ofile = gallery_select1(saved_areas_folder); if (! ofile) return 1; if (image_file_type(ofile) != IMAGE) { // check it is an image file zmessageACK(Mwin,E2X("unknown file type")); return 1; } if (ovfile) zfree(ovfile); ovfile = ofile; } if (strmatch(event,"ovTL")) ovpos = 1; // overlay position = top left if (strmatch(event,"ovTC")) ovpos = 2; // top center if (strmatch(event,"ovTR")) ovpos = 3; // top right if (strmatch(event,"ovCL")) ovpos = 4; // center left if (strmatch(event,"ovCC")) ovpos = 5; // center center if (strmatch(event,"ovCR")) ovpos = 6; // center right if (strmatch(event,"ovBL")) ovpos = 7; // bottom left if (strmatch(event,"ovBC")) ovpos = 8; // bottom center if (strmatch(event,"ovBR")) ovpos = 9; // bottom right // wait for dialog completion via [proceed] button if (zd->zstat != 1) return 1; zd->zstat = 0; // keep active until inputs OK zdialog_fetch(zd,"newname",newname,200); // new file name zdialog_fetch(zd,"baseseq",baseseq); // base sequence number zdialog_fetch(zd,"addseq",addseq); // sequence number adder zdialog_fetch(zd,"newloc",newloc,500); // new location (folder) 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,"jpgqual",jpeg_quality); // jpeg quality zdialog_fetch(zd,"delete",Fdelete); // delete originals zdialog_fetch(zd,"copymeta",Fcopymeta); // copy metadata zdialog_fetch(zd,"upright",Fupright); // upright rotation zdialog_fetch(zd,"sharpen",Fsharpen); // auto sharpen zdialog_fetch(zd,"amount",amount); // sharpen amount zdialog_fetch(zd,"thresh",thresh); // sharpen threshold zdialog_fetch(zd,"ckov",Foverlay); // overlay image zdialog_fetch(zd,"ovwidth",ovwidth); // overlay width 1-100% zdialog_fetch(zd,"ovconst",Fovconst); // flag, make overlay size constant zdialog_fetch(zd,"ovscrww",ovscrww); // for this screen width zdialog_fetch(zd,"ovscrhh",ovscrhh); // and this screen height zdialog_fetch(zd,"sametype",Fsametype); zdialog_fetch(zd,"jpg",ii); if (ii) strcpy(newext,".jpg"); zdialog_fetch(zd,"tif",ii); if (ii) strcpy(newext,".tif"); zdialog_fetch(zd,"png",ii); if (ii) strcpy(newext,".png"); zdialog_fetch(zd,"samebpc",Fsamebpc); zdialog_fetch(zd,"8-bit",ii); if (ii) newbpc = 8; zdialog_fetch(zd,"16-bit",ii); if (ii) newbpc = 16; if (! GScount) { zmessageACK(Mwin,Bnofileselected); return 1; } strTrim2(newname); if (! blank_null(newname)) { Fplugdate = Fplugseq = Fplugname = 0; // validate plugins pp = newname; while ((pp = strchr(pp,'$'))) { if (strmatchN(pp,"$yyyy",5)) Fplugdate = 1; // $yyyy else if (strmatchN(pp,"$mm",3)) Fplugdate = 1; // $mm else if (strmatchN(pp,"$dd",3)) Fplugdate = 1; // $dd else if (strmatchN(pp,"$s",2)) Fplugseq = 1; // $s...s (sequence number cc) else if (strmatchN(pp,"$oldname",8)) Fplugname = 1; // $oldname else { for (cc = 0; pp[cc] > ' ' && cc < 20; cc++); strncpy0(badplug,pp,cc); zmessageACK(Mwin,E2X("invalid plugin: %s"),badplug); return 1; } pp++; } if (! Fplugname && ! Fplugseq) { zmessageACK(Mwin,E2X("you must use either $s or $oldname")); return 1; } if (Fplugseq && (baseseq < 1 || addseq < 1)) { zmessageACK(Mwin,E2X("$s plugin needs base and adder")); return 1; } if (! Fplugseq && (baseseq > 0 || addseq > 0)) { zmessageACK(Mwin,E2X("base and adder need $s plugin")); return 1; } } strTrim2(newloc); // check location if (! blank_null(newloc)) { cc = strlen(newloc) - 1; if (newloc[cc] == '/') newloc[cc] = 0; // remove trailing '/' err = check_create_dir(newloc); // create if needed if (err) return 1; } if (! Fsamesize && (maxww < 20 || maxhh < 20)) { zmessageACK(Mwin,E2X("max. size %d x %d is not reasonable"),maxww,maxhh); return 1; } if (Foverlay) { if (! ovfile) { // overlay image file zmessageACK(Mwin,E2X("specify overlay image file")); return 1; } ovpxb = gdk_pixbuf_new_from_file(ovfile,&gerror); // verify image file if (! ovpxb) { zmessageACK(Mwin,gerror->message); return 1; } g_object_unref(ovpxb); oname = strrchr(ovfile,'/'); // filename.png if (! oname) oname = ovfile; oname++; if (! ovpos) { // overlay position zmessageACK(Mwin,E2X("specify overlay position")); return 1; } } Freplace = 0; if (*newname <= ' ' && *newloc <= ' ' && Fsametype) Freplace = 1; /** Convert NN image files 0 Rename to xxxxxxx 1 Convert to .ext N-bits/color 2 Resize within NNxNN 3 Output to /.../... 4 Copy Metadata Upright Sharpen 5 Overlay Image: xxxxxxxxxx 6 *** Delete Originals *** 7 *** Replace Originals *** 8 PROCEED? 9 **/ char mess0[60], mess1[100], mess2[60], mess2a[60], mess3[60], mess4[550], mess5[80], mess6[200], mess7[60], mess8[60], mess9[40]; char warnmess[1000]; *mess0 = *mess1 = *mess2 = *mess2a = *mess3 = *mess4 = *mess5 = *mess6 = *mess7 = *mess8 = *mess9 = 0; snprintf(mess0,60,E2X("Convert %d image files"),GScount); if (*newname) snprintf(mess1,100,"\n %s %s",E2X("Rename to"),newname); if (! Fsametype) snprintf(mess2,60,"\n %s %s",E2X("Convert to"),newext); if (! Fsamebpc) snprintf(mess2a,60,"\n %d-bits/color",newbpc); if (! Fsamesize) snprintf(mess3,60,"\n %s %dx%d",E2X("Resize within"),maxww,maxhh); if (*newloc) snprintf(mess4,550,"\n %s %s",E2X("Output to"),newloc); if (Fcopymeta || Fupright || Fsharpen) strcat(mess5,"\n "); if (Fcopymeta) { strcat(mess5,E2X("Copy Metadata")); strcat(mess5," "); } if (Fupright) { strcat(mess5,E2X("Upright")); strcat(mess5," "); } if (Fsharpen) { strcat(mess5,E2X("Sharpen")); strcat(mess5," "); } if (Foverlay) { snprintf(mess6,200,"\n %s: %s",E2X("Overlay Image"),oname); } if (Fdelete) snprintf(mess7,60,"\n %s",E2X("*** Delete Originals ***")); if (Freplace) snprintf(mess8,60,"\n %s",E2X("*** Replace Originals ***")); snprintf(mess9,40,"\n\n%s",E2X("PROCEED?")); snprintf(warnmess,1000,"%s %s %s %s %s %s %s %s %s %s %s", mess0,mess1,mess2,mess2a,mess3,mess4,mess5,mess6,mess7,mess8,mess9); yn = zmessageYN(Mwin,warnmess); if (! yn) return 1; zd->zstat = 1; // [proceed] return 1; } /********************************************************************************/ // callable sharpen function for batch_convert and batch_RAW_convert // amount: 0 to 400 strength of applied algorithm // thresh: 0 to 100 contrast level below which sharpen is diminished namespace batch_sharp_names { PXM *pxm1, *pxm2; int BSamount, BSthresh; } int batch_sharp_func(PXM *pxm, int amount, int thresh) // 14.06 { using namespace batch_sharp_names; void * batch_sharp_wthread(void *arg); pxm2 = pxm; // output pxm1 = PXM_copy(pxm2); // input BSamount = amount; BSthresh = thresh; do_wthreads(batch_sharp_wthread,NWT); // worker threads 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; } pthread_exit(0); } /********************************************************************************/ // Batch upright image files. // Look for files rotated 90˚ (according to EXIF) and upright them. char **bup_filelist = 0; int bup_filecount = 0; int bup_allfiles; void m_batch_upright(GtkWidget *, cchar *) { int batch_upright_dialog_event(zdialog *zd, cchar *event); zdialog *zd, *zd2; int zstat; char *infile, *tempfile; char *pp1, *pp2, *ppv[1], text[100]; int ii, err, bpc; char orientation; PXM *pxmin, *pxmout; cchar *exifkey[1] = { exif_orientation_key }; cchar *exifdata[1]; F1_help_topic = "batch upright"; if (checkpend("all")) return; // check nothing pending if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } Fblock = 1; /*** ___________________________________ | Batch Upright | | | | [Select Files] N files selected | | [x] Survey all files | | | | [proceed] [cancel] | |___________________________________| ***/ zd = zdialog_new(E2X("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",E2X("Survey all files"),"space=5"); snprintf(text,100,Bfileselected,GScount); // show selected files count 20.0 zdialog_stuff(zd,"fcount",text); 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 zd2 = popup_report_open("Processing files",Mwin,500,200,0,0,0); // status monitor popup window if (bup_allfiles) // "survey all files" selected { bup_filelist = (char **) zmalloc(Nxxrec * sizeof(char *)); for (ii = 0; ii < Nxxrec; ii++) bup_filelist[ii] = zstrdup(xxrec_tab[ii]->file); bup_filecount = Nxxrec; } for (ii = 0; ii < bup_filecount; ii++) // loop selected files { zmainloop(); // keep GTK alive infile = bup_filelist[ii]; // input file popup_report_write(zd2,0,"%s \n",infile); // log each output file exif_get(infile,exifkey,ppv,1); // get EXIF: Orientation if (! ppv[0]) continue; orientation = *ppv[0]; if (orientation == '1') continue; // not rotated pxmin = PXM_load(infile,0); // read input file if (! pxmin) { popup_report_write(zd2,1,"%s \n",E2X("file cannot be read")); continue; } pxmout = 0; if (orientation == '6') pxmout = PXM_rotate(pxmin,+90); // rotate clockwise 90 deg. if (orientation == '8') pxmout = PXM_rotate(pxmin,-90); // counterclockwise if (orientation == '3') pxmout = PXM_rotate(pxmin,180); // 180 deg. PXM_free(pxmin); if (! pxmout) continue; // not rotated tempfile = zstrdup(infile,12); // temp file needed for EXIF/IPTC copy pp1 = strrchr(infile,'.'); pp2 = strrchr(tempfile,'.'); strcpy(pp2,"-temp"); strcpy(pp2+5,pp1); bpc = f_load_bpc; // input file bits/color if (strmatch(f_load_type,"tif")) err = PXM_TIFF_save(pxmout,tempfile,bpc); else if (strmatch(f_load_type,"png")) err = PXM_PNG_save(pxmout,tempfile,bpc); else err = PXM_JPG_save(pxmout,tempfile,jpeg_def_quality); PXM_free(pxmout); if (err) { popup_report_write(zd2,1," upright failed \n"); zfree(tempfile); continue; } exifdata[0] = ""; // remove exif:orientation exif_copy(infile,tempfile,exifkey,exifdata,1); // (set = 1 does not work) err = copyFile(tempfile,infile); // copy temp file to input file if (err) popup_report_write(zd2,1,"%s \n",strerror(err)); remove(tempfile); // remove tempfile zfree(tempfile); if (! err) { load_filemeta(infile); // update image index update_image_index(infile); popup_report_write(zd2,0," uprighted \n"); } gallery(0,"paint",-1); // repaint gallery 19.0 } popup_report_write(zd2,0,"\n *** %s \n",Bcompleted); popup_report_bottom(zd2); cleanup: if (bup_filecount) { // free memory for (ii = 0; ii < bup_filecount; ii++) zfree(bup_filelist[ii]); zfree(bup_filelist); bup_filelist = 0; bup_filecount = 0; } Fblock = 0; return; } // dialog event and completion callback function int batch_upright_dialog_event(zdialog *zd, cchar *event) { char countmess[80]; int ii; if (strmatch(event,"files")) // select images to convert { if (bup_filecount) { for (ii = 0; ii < bup_filecount; ii++) // free prior list zfree(bup_filelist[ii]); zfree(bup_filelist); bup_filelist = 0; bup_filecount = 0; } zdialog_show(zd,0); // hide parent dialog gallery_select(); // get new list zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); // update dialog zdialog_stuff(zd,"fcount",countmess); zdialog_stuff(zd,"allfiles",0); if (! GScount) return 1; bup_filelist = (char **) zmalloc(GScount * sizeof(char *)); // copy selected files for (ii = 0; ii < GScount; ii++) bup_filelist[ii] = GSfiles[ii]; bup_filecount = GScount; GScount = 0; } if (zd->zstat != 1) return 1; // wait for [proceed] zdialog_fetch(zd,"allfiles",bup_allfiles); // get "survey all" option if (! bup_allfiles && ! bup_filecount) { // nothing selected zmessageACK(Mwin,Bnofileselected); zd->zstat = 0; // keep dialog active } if (bup_allfiles && bup_filecount) { zmessageACK(Mwin,E2X("cannot select both options")); zd->zstat = 0; } return 1; } /********************************************************************************/ // Batch delete or trash image files. int bdt_option; // 1/2 = delete/trash void m_batch_deltrash(GtkWidget *, cchar *) { int batch_deltrash_dialog_event(zdialog *zd, cchar *event); zdialog *zd, *zd2; int zstat, ii, err, gstat; char *file, text[100]; 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(E2X("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",E2X("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",E2X("trash"),"space=5"); zdialog_add_widget(zd,"radio","trash","hbdt",0); bdt_option = 2; snprintf(text,100,Bfileselected,GScount); // show selected files count 20.0 zdialog_stuff(zd,"fcount",text); zdialog_stuff(zd,"delete",0); zdialog_stuff(zd,"trash",1); zdialog_run(zd,batch_deltrash_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_fetch(zd,"delete",bdt_option); // get delete/trash option if (! bdt_option) bdt_option = 2; zdialog_free(zd); if (zstat != 1) goto cleanup; // canceled if (! GScount) goto cleanup; free_resources(); // no curr. file zd2 = popup_report_open("Processing files",Mwin,500,200,0,0,0); // status monitor popup window for (ii = 0; ii < GScount; ii++) // loop selected files { zmainloop(); // keep GTK alive file = GSfiles[ii]; // log each file popup_report_write(zd2,0,"%s \n",file); err = stat(file,&statb); // file exists? if (err || ! S_ISREG(statb.st_mode)) { popup_report_write(zd2,1,"file not found \n"); continue; } if (bdt_option == 1) { // delete file err = remove(file); if (err) { popup_report_write(zd2,1,"%s \n",strerror(err)); continue; } } if (bdt_option == 2) { // move file to trash gfile = g_file_new_for_path(file); gstat = g_file_trash(gfile,0,&gerror); // move file to trash g_object_unref(gfile); if (! gstat) { popup_report_write(zd2,1,"%s \n",gerror->message); continue; } } delete_image_index(file); // delete file in image index delete_thumbfile(file); // delete thumbnail, file and cache } popup_report_write(zd2,0,"\n *** %s \n",Bcompleted); popup_report_bottom(zd2); popup_report_write(zd2,0,E2X("Purging deleted files from albums \n")); // purge deleted files from albums 20.0 album_purge_replace_all(); // replace with new version if poss. popup_report_write(zd2,0,"\n *** %s \n",Bcompleted); popup_report_bottom(zd2); cleanup: Fblock = 0; gallery(navi::galleryname,"init",0); // refresh file list gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // repaint from same position return; } // dialog event and completion callback function int batch_deltrash_dialog_event(zdialog *zd, cchar *event) { char countmess[80]; int ii; if (strmatch(event,"files")) // select images to convert { zdialog_show(zd,0); // hide parent dialog gallery_select(); zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); // update dialog zdialog_stuff(zd,"fcount",countmess); } if (strmatch(event,"delete")) { // delete radio button zdialog_fetch(zd,"delete",ii); if (ii) bdt_option = 1; zdialog_stuff(zd,"trash",0); } if (strmatch(event,"trash")) { // trash radio button zdialog_fetch(zd,"trash",ii); if (ii) bdt_option = 2; zdialog_stuff(zd,"delete",0); } if (zd->zstat != 1) return 1; // wait for [proceed] if (! GScount) { // nothing selected zmessageACK(Mwin,Bnofileselected); zd->zstat = 0; // keep dialog active } return 1; } /********************************************************************************/ // convert multiple RAW files to tiff, jpeg, or png namespace batch_raw { char location[400], biasfile[400]; cchar *filetype = 0; int bpc, jpeg_quality; float resize; int Fsharpen, Fdead, Fbias; int amount, thresh; }; void m_batch_RAW(GtkWidget *, cchar *menu) { using namespace batch_raw; int batch_raw_dialog_event(zdialog *zd, cchar *event); zdialog *zd, *zd2; cchar *title = E2X("Batch Convert RAW Files"); char *rawfile, *infile, *outfile, *pp; char text[100]; int zstat, ii, err; FTYPE ftype; int cc, ww2, hh2; PXM *pxm1, *pxm2; F1_help_topic = "batch raw"; if (checkpend("all")) return; // check nothing pending Fblock = 1; m_viewmode(0,"G"); // gallery view 20.0 /*** ________________________________________________________ | Batch Convert RAW Files | | | | [Select Files] N image files selected | | output location [___________________________] [Browse] | | File Type: (o) tif (o) png (o) jpg [90] jpg quality | | Color Depth: (o) 8-bit (o) 16-bit | | Resize (o) 1.0 (o) 3/4 (o) 2/3 (o) 1/2 (o) 1/3 | | [x] Sharpen amount [___] threshold [___] | | [x] Fix dead pixels [load] dead pixel map file | // 20.0 | [x] Fix pixel bias [load] pixel bias map file | // 20.0 | | | [proceed] [cancel] | |________________________________________________________| ***/ zd = zdialog_new(title,Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=2"); zdialog_add_widget(zd,"button","files","hb1",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","fcount","hb1",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hbout","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labout","hbout",E2X("output location"),"space=5"); zdialog_add_widget(zd,"zentry","location","hbout",0,"space=5|expand"); zdialog_add_widget(zd,"button","browselocation","hbout",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hbft","dialog"); zdialog_add_widget(zd,"label","labtyp","hbft",E2X("File Type"),"space=5"); zdialog_add_widget(zd,"radio","tif","hbft","tif","space=4"); zdialog_add_widget(zd,"radio","png","hbft","png","space=4"); zdialog_add_widget(zd,"radio","jpg","hbft","jpg","space=2"); zdialog_add_widget(zd,"zspin","jpgqual","hbft","10|100|1|90","size=3"); zdialog_add_widget(zd,"label","labqual","hbft",E2X("jpg quality"),"space=6"); zdialog_add_widget(zd,"hbox","hbcd","dialog"); zdialog_add_widget(zd,"label","labcd","hbcd",E2X("Color Depth:"),"space=5"); zdialog_add_widget(zd,"radio","8-bit","hbcd","8-bit","space=4"); zdialog_add_widget(zd,"radio","16-bit","hbcd","16-bit","space=4"); zdialog_add_widget(zd,"hbox","hbsize","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labsize","hbsize",E2X("Resize"),"space=5"); zdialog_add_widget(zd,"label","space","hbsize",0,"space=5"); zdialog_add_widget(zd,"radio","1.0","hbsize","1.0","space=5"); zdialog_add_widget(zd,"radio","3/4","hbsize","3/4","space=5"); zdialog_add_widget(zd,"radio","2/3","hbsize","2/3","space=5"); zdialog_add_widget(zd,"radio","1/2","hbsize","1/2","space=5"); zdialog_add_widget(zd,"radio","1/3","hbsize","1/3","space=5"); zdialog_add_widget(zd,"hbox","hbsharp","dialog",0,"space=2"); zdialog_add_widget(zd,"check","sharpen","hbsharp",E2X("Sharpen"),"space=3"); zdialog_add_widget(zd,"label","space","hbsharp",0,"space=8"); zdialog_add_widget(zd,"label","labamount","hbsharp",E2X("amount"),"space=3"); zdialog_add_widget(zd,"zspin","amount","hbsharp","0|400|1|100"); zdialog_add_widget(zd,"label","space","hbsharp",0,"space=8"); zdialog_add_widget(zd,"label","labthresh","hbsharp",E2X("threshold"),"space=3"); zdialog_add_widget(zd,"zspin","thresh","hbsharp","0|100|1|20"); zdialog_add_widget(zd,"hbox","hbdead","dialog",0,"space=2"); zdialog_add_widget(zd,"check","fixdead","hbdead",E2X("Fix dead pixels"),"space=3"); zdialog_add_widget(zd,"label","space","hbdead",0,"space=10"); zdialog_add_widget(zd,"button","loadead","hbdead",Bload,"space=5"); zdialog_add_widget(zd,"label","labloadead","hbdead",E2X("dead pixel map file")); zdialog_add_widget(zd,"hbox","hbbias","dialog",0,"space=2"); zdialog_add_widget(zd,"check","fixbias","hbbias",E2X("Fix pixel bias"),"space=3"); zdialog_add_widget(zd,"label","space","hbbias",0,"space=10"); zdialog_add_widget(zd,"button","loadbias","hbbias",Bload,"space=5"); zdialog_add_widget(zd,"label","labloadbias","hbbias",E2X("pixel bias map file")); snprintf(text,100,Bfileselected,GScount); // show selected files count 20.0 zdialog_stuff(zd,"fcount",text); *location = 0; Fdead = Fbias = 0; zdialog_stuff(zd,"tif",0); zdialog_stuff(zd,"png",0); zdialog_stuff(zd,"jpg",0); zdialog_stuff(zd,"jpgqual",jpeg_def_quality); zdialog_stuff(zd,"8-bit",0); zdialog_stuff(zd,"16-bit",0); zdialog_restore_inputs(zd); // get prior inputs if any zdialog_resize(zd,500,0); zdialog_stuff(zd,"fixdead",0); // turn off initially zdialog_stuff(zd,"fixbias",0); zdialog_run(zd,batch_raw_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_free(zd); if (zstat != 1) goto cleanup; if (! GScount) goto cleanup; m_viewmode(0,"F"); zd2 = popup_report_open("Converting RAW files",Mwin,500,200,0,0,0); // status monitor popup window for (ii = 0; ii < GScount; ii++) // loop all RAW files { zmainloop(); rawfile = GSfiles[ii]; // filename.raw popup_report_write(zd2,0,"%s \n",rawfile); // write to log window ftype = image_file_type(rawfile); if (ftype != RAW) { popup_report_write(zd2,1," unknown RAW file type \n"); continue; } pxm1 = RAW_PXM_load(rawfile,Fautobright,Fmatchthumb); // 20.0 if (! pxm1) continue; if (Fdead) { // fix dead pixels 20.0 err = dead_pixels_fix(pxm1); if (err) popup_report_write(zd2,1," fix dead pixels not done \n"); } if (Fbias) // apply pixel bias corrections 20.0 { err = pixel_bias_fix(pxm1); if (err) popup_report_write(zd2,1," fix pixel bias not done \n"); } zmainloop(); if (resize < 1.0) // resize down if wanted { ww2 = resize * pxm1->ww; hh2 = resize * pxm1->hh; pxm2 = PXM_rescale(pxm1,ww2,hh2); PXM_free(pxm1); pxm1 = pxm2; if (! pxm1) { popup_report_write(zd2,1," resize failed \n"); continue; } } if (Fsharpen) // sharpen if wanted batch_sharp_func(pxm1,amount,thresh); outfile = zstrdup(rawfile,5); // output file name = RAW file name pp = strrchr(outfile,'.'); // rename: *.tif *.jpg *.png if (pp) strcpy(pp,filetype); err = PXM_save(pxm1,outfile,bpc,jpeg_quality,1); PXM_free(pxm1); if (err) { popup_report_write(zd2,1," file type conversion failed \n"); zfree(outfile); continue; } exif_copy(rawfile,outfile,0,0,0); // copy metadata from RAW file if (*location && ! samefolder(location,outfile)) { infile = zstrdup(outfile); // copy to new location zfree(outfile); pp = strrchr(infile,'/'); // /raw-location/filename.ext cc = strlen(pp); // | outfile = zstrdup(location,cc+1); // pp strcat(outfile,pp); // /new-location/filename.ext err = copyFile(infile,outfile); // copy to new location if (err) popup_report_write(zd2,1," %s \n",strerror(err)); remove(infile); // remove tempfile zfree(infile); } f_open(outfile,0,0,0); // open converted file update_image_index(outfile); popup_report_write(zd2,0,"%s \n",outfile); // write output file to log window zfree(outfile); } popup_report_write(zd2,0,"\n *** %s \n",Bcompleted); popup_report_bottom(zd2); cleanup: // clean up and return Fblock = 0; gallery(navi::galleryname,"init",0); // refresh file list gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // repaint from same position return; } // dialog event and completion callback function int batch_raw_dialog_event(zdialog *zd, cchar *event) { using namespace batch_raw; int ii, err, cc; char countmess[80], *ploc; if (strmatch(event,"files")) // select images to convert { zdialog_show(zd,0); // hide parent dialog gallery_select(); // get list of files to convert zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); // stuff count into dialog zdialog_stuff(zd,"fcount",countmess); } if (strmatch(event,"browselocation")) { // new location browse zdialog_fetch(zd,"location",location,400); if (*location <= ' ' && topfolders[0]) strncpy0(location,topfolders[0],400); ploc = zgetfile(E2X("Select folder"),MWIN,"folder",location); if (! ploc) return 1; zdialog_stuff(zd,"location",ploc); zfree(ploc); } if (strmatch(event,"fixdead")) // fix dead pixels checkbox 20.0 { zdialog_fetch(zd,"fixdead",ii); if (ii) event = "loadead"; } if (strmatch(event,"loadead")) // load dead pixel map file 20.0 { err = dead_pixels_map_load(); if (! err) { Fdead = 1; zdialog_stuff(zd,"fixdead",1); } else { Fdead = 0; zdialog_stuff(zd,"fixdead",0); } } if (strmatch(event,"fixbias")) // fix pixel bias checkbox 20.0 { zdialog_fetch(zd,"fixbias",ii); if (ii) event = "loadbias"; } if (strmatch(event,"loadbias")) // load pixel bias map file 20.0 { zdialog_show(zd,0); err = pixel_bias_map_load(); zdialog_show(zd,1); if (! err) { Fbias = 1; zdialog_stuff(zd,"fixbias",1); } else { Fbias = 0; zdialog_stuff(zd,"fixbias",0); } } if (zstrstr("tif png jpg",event)) { // gtk fails to do this correctly zdialog_stuff(zd,"tif",0); zdialog_stuff(zd,"png",0); zdialog_stuff(zd,"jpg",0); zdialog_stuff(zd,event,1); } if (zstrstr("8-bit 16-bit",event)) { // gtk fails to do this correctly zdialog_stuff(zd,"8-bit",0); zdialog_stuff(zd,"16-bit",0); zdialog_stuff(zd,event,1); } zdialog_fetch(zd,"jpg",ii); // if jpeg, force 8 bits/color if (ii) { zdialog_stuff(zd,"16-bit",0); zdialog_stuff(zd,"8-bit",1); } // wait for dialog completion via [proceed] button if (zd->zstat != 1) return 0; zd->zstat = 0; // keep dialog active until inputs OK if (! GScount) { // no RAW files selected zmessageACK(Mwin,Bnofileselected); return 1; } zdialog_fetch(zd,"location",location,400); // output location strTrim2(location); if (! blank_null(location)) { cc = strlen(location) - 1; if (location[cc] == '/') location[cc] = 0; // remove trailing '/' err = check_create_dir(location); // create if needed if (err) return 1; } filetype = ".tif"; // bugfix 19.2 zdialog_fetch(zd,"jpg",ii); if (ii) filetype = ".jpg"; zdialog_fetch(zd,"tif",ii); if (ii) filetype = ".tif"; zdialog_fetch(zd,"png",ii); if (ii) filetype = ".png"; bpc = 8; // bugfix 19.2 zdialog_fetch(zd,"8-bit",ii); if (ii) bpc = 8; zdialog_fetch(zd,"16-bit",ii); if (ii) bpc = 16; zdialog_fetch(zd,"jpgqual",jpeg_quality); // jpeg quality 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; } /********************************************************************************/ // burn images to DVD/BlueRay optical disc namespace burn_images { zdialog *zd; char mydvd[60]; }; // menu function void m_burn_DVD(GtkWidget *, cchar *) // overhauled { using namespace burn_images; int burn_dialog_event(zdialog *zd, cchar *event); FILE *fid; char *pp, text[100]; int ii, Fdvd, Ndvd, zstat; cchar *growisofs = "growisofs -Z %s -iso-level 4 -r -path-list %s 2>&1"; // growisofs command char burnlist[200], buffer[200]; char dvddev[20][4], dvddesc[40][4], dvddevdesc[60][4]; // up to 4 DVD/BR devices F1_help_topic = "burn DVD/BRD"; if (checkpend("all")) return; // check nothing pending if (! Fgrowisofs) { zmessageACK(Mwin,E2X("growisofs not installed")); return; } Fdvd = Ndvd = 0; *mydvd = 0; fid = popen("lshw -class disk","r"); // find all DVD/BR devices if (! fid) { zmessageACK(Mwin,strerror(errno)); return; } while (true) { pp = fgets_trim(buffer,200,fid,1); if (! pp) break; if (strstr(buffer,"*-")) { // start of some device if (strstr(buffer,"*-cdrom")) Fdvd = 1; // start of DVD/BD device else Fdvd = 0; continue; } if (! Fdvd) continue; // ignore recs for other devices if (strstr(buffer,"description:")) { pp = strstr(buffer,"description:"); // save DVD/BD description pp += 12; strncpy0(dvddesc[Ndvd],pp,40); continue; } if (strstr(buffer,"/dev/")) { pp = strstr(buffer,"/dev/"); // have /dev/sr0 or similar format if (pp[7] < '0' || pp[7] > '9') continue; pp[8] = 0; strcpy(dvddev[Ndvd],pp); // save DVD/BD device Ndvd++; if (Ndvd == 4) break; continue; } } pclose(fid); if (Ndvd < 1) { zmessageACK(Mwin,E2X("no DVD/BlueRay device found")); return; } for (ii = 0; ii < Ndvd; ii++) // combine devices and descriptions { // for use in GUI chooser list strcpy(dvddevdesc[ii],dvddev[ii]); strcat(dvddevdesc[ii]," "); strcat(dvddevdesc[ii],dvddesc[ii]); } /*** ______________________________________ | Burn Images to DVD/BlueRay | | | | [Select Files] NNN files selected | | [Select device] [_____________|v] | | | | [Start] [Cancel] | |______________________________________| ***/ zd = zdialog_new(E2X("Burn Images to DVD/BlueRay"),Mwin,Bstart,Bcancel,null); zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=5"); zdialog_add_widget(zd,"button","files","hbf",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","fcount","hbf",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hbdvd","dialog",0); zdialog_add_widget(zd,"label","labdvd","hbdvd",E2X("Select device"),"space=5"); zdialog_add_widget(zd,"combo","dvd","hbdvd",0,"expand"); snprintf(text,100,Bfileselected,GScount); // show selected files count 20.0 zdialog_stuff(zd,"fcount",text); for (int ii = 0; ii < Ndvd; ii++) // put available drives into list zdialog_cb_app(zd,"dvd",dvddevdesc[ii]); zdialog_resize(zd,300,0); zdialog_run(zd,burn_dialog_event,"parent"); // run dialog, wait for response zstat = zdialog_wait(zd); zdialog_free(zd); if (zstat != 1) return; if (! GScount) return; // no files selected snprintf(burnlist,200,"%s/burnlist",get_zhomedir()); // write files to burnlist file fid = fopen(burnlist,"w"); if (! fid) { zmessageACK(Mwin,strerror(errno)); return; } for (ii = 0; ii < GScount; ii++) fprintf(fid,"%s\n",GSfiles[ii]); fclose(fid); if (! *mydvd) return; // no device selected pp = strstr(mydvd," "); // remove description if (pp) *pp = 0; snprintf(command,200,growisofs,mydvd,burnlist); // start burn process popup_command(command,600,400,Mwin); return; } // dialog event and completion function int burn_dialog_event(zdialog *zd, cchar *event) { using namespace burn_images; char countmess[80]; if (strmatch(event,"files")) // select images to burn { zdialog_show(zd,0); // hide parent dialog gallery_select(); // get list of files to convert zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); // update dialog zdialog_stuff(zd,"fcount",countmess); } if (strmatch(event,"dvd")) zdialog_fetch(zd,"dvd",mydvd,60); // get user device choice return 1; } /********************************************************************************/ // Select files and output a file containing the selected file names. namespace imagelist_images { zdialog *zd; char outfile[300]; }; // menu function void m_export_filelist(GtkWidget *, cchar *) { using namespace imagelist_images; int export_filelist_dialog_event(zdialog *zd, cchar *event); FILE *fid; int ii, zstat; cchar *title = E2X("Create a file of selected image files"); char text[100]; F1_help_topic = "export file list"; if (checkpend("all")) return; // check nothing pending /*** ____________________________________________ | Create a file of selected image files | | | | [Select Files] NNN files selected | | Output File [__________________] [Browse] | | | | [Proceed] [Cancel] | |____________________________________________| ***/ zd = zdialog_new(title,Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hbif","dialog",0,"space=3"); zdialog_add_widget(zd,"button","infiles","hbif",Bselectfiles,"space=3"); zdialog_add_widget(zd,"label","Nfiles","hbif",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hbof","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labof","hbof",E2X("Output File"),"space=3"); zdialog_add_widget(zd,"zentry","outfile","hbof",0,"size=30|space=5"); zdialog_add_widget(zd,"button","browse","hbof",Bbrowse,"space=5"); snprintf(text,100,Bfileselected,GScount); // show selected files count 20.0 zdialog_stuff(zd,"Nfiles",text); if (*outfile) zdialog_stuff(zd,"outfile",outfile); zdialog_run(zd,export_filelist_dialog_event,"parent"); // run dialog, wait for response retry: zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); return; } zdialog_fetch(zd,"outfile",outfile,300); // get output file from dialog if (! GScount) { zmessageACK(Mwin,E2X("no input files selected")); zd->zstat = 0; goto retry; // no input files } if (! *outfile) { zmessageACK(Mwin,E2X("no output file selected")); zd->zstat = 0; goto retry; // no input files } fid = fopen(outfile,"w"); // open output file if (! fid) { zmessageACK(Mwin,strerror(errno)); // error zd->zstat = 0; goto retry; // no input files } zdialog_free(zd); for (ii = 0; ii < GScount; ii++) // write input file names to output fprintf(fid,"%s\n",GSfiles[ii]); fclose(fid); zmessageACK(Mwin,Bcompleted); return; } // dialog event and completion function int export_filelist_dialog_event(zdialog *zd, cchar *event) { using namespace imagelist_images; char *file; char countmess[80]; if (strmatch(event,"infiles")) // select image files { gallery_select(); // get list of files to convert snprintf(countmess,80,Bfileselected,GScount); // update dialog zdialog_stuff(zd,"Nfiles",countmess); } if (strmatch(event,"browse")) { file = zgetfile(E2X("Output File"),MWIN,"save",outfile,0); if (file) zdialog_stuff(zd,"outfile",file); else zdialog_stuff(zd,"outfile",""); } return 1; } /********************************************************************************/ // Export selected image files to another folder with optional downsizing. // Only the metadata relevant for web photo services is copied. namespace export_files_names { char tolocation[500]; int Fsamesize; int maxww, maxhh; int Fmeta; }; // menu function void m_export_files(GtkWidget*, cchar *menu) // overhauled { using namespace export_files_names; int export_files_dialog_event(zdialog *zd, cchar *event); zdialog *zd, *zd2; int ii, cc, err, ww, hh, zstat; PXM *pxmin, *pxmout; char *infile, *outfile, *pp, text[100]; float scale, wscale, hscale; #define NK 10 cchar *keys[NK] = { exif_date_key, iptc_keywords_key, exif_copyright_key, exif_comment_key, exif_usercomment_key, iptc_caption_key, exif_city_key, exif_country_key, exif_lati_key, exif_longi_key }; char *kdata[NK]; F1_help_topic = "export files"; if (checkpend("all")) return; // check nothing pending /*** __________________________________________________ | Export Files | | | | [Select Files] N files selected | | To Location [________________________] [Browse] | | Max. Width [____] Height [____] [x] no change | | [x] export metadata | // 20.0 | | | [proceed] [cancel] | |__________________________________________________| ***/ zd = zdialog_new(E2X("Export Files"),Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hbf","dialog"); zdialog_add_widget(zd,"button","files","hbf",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","fcount","hbf",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hbloc","dialog"); zdialog_add_widget(zd,"label","labloc","hbloc",E2X("To Location"),"space=5"); zdialog_add_widget(zd,"zentry","toloc","hbloc",0,"expand"); zdialog_add_widget(zd,"button","browse","hbloc",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hbwh","dialog"); zdialog_add_widget(zd,"label","labw","hbwh",E2X("max. Width"),"space=5"); zdialog_add_widget(zd,"zentry","maxww","hbwh","1000","size=5"); zdialog_add_widget(zd,"label","space","hbwh",0,"space=5"); zdialog_add_widget(zd,"label","labh","hbwh",Bheight,"space=5"); zdialog_add_widget(zd,"zentry","maxhh","hbwh","700","size=5"); zdialog_add_widget(zd,"check","samesize","hbwh",E2X("no change"),"space=12"); zdialog_add_widget(zd,"hbox","hbmeta","dialog"); zdialog_add_widget(zd,"check","meta","hbmeta",E2X("export metadata")); zdialog_restore_inputs(zd); // preload prior location snprintf(text,100,Bfileselected,GScount); // show selected files count 20.0 zdialog_stuff(zd,"fcount",text); zdialog_resize(zd,400,0); zdialog_run(zd,export_files_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_free(zd); if (zstat != 1) return; zd2 = popup_report_open("exporting files",Mwin,500,200,0,0,0); for (ii = 0; ii < GScount; ii++) // loop selected files { infile = GSfiles[ii]; // input filespec popup_report_write(zd2,0,"%s \n",infile); pp = strrchr(infile,'/'); // construct output filespec if (! pp) continue; cc = strlen(pp) + 8; outfile = zstrdup(tolocation,cc); strcat(outfile,pp); pp = strrchr(outfile,'.'); // outfile is type .jpg 20.0 if (! pp) continue; strcpy(pp,".jpg"); pxmin = PXM_load(infile,0); // read input file if (! pxmin) { popup_report_write(zd2,1," %s \n",E2X("file type not supported")); zfree(outfile); continue; } ww = pxmin->ww; // input file size hh = pxmin->hh; if (Fsamesize) pxmout = pxmin; // same size, output = input else { wscale = hscale = 1.0; if (ww > maxww) wscale = 1.0 * maxww / ww; // compute new size if (hh > maxhh) hscale = 1.0 * maxhh / hh; if (wscale < hscale) scale = wscale; else scale = hscale; if (scale > 0.999) pxmout = pxmin; // no change else { ww = ww * scale; hh = hh * scale; pxmout = PXM_rescale(pxmin,ww,hh); // rescaled output file PXM_free(pxmin); // free memory } } err = PXM_JPG_save(pxmout,outfile,jpeg_def_quality); // write output file if (err) popup_report_write(zd2,1," %s \n",E2X("cannot create new file")); PXM_free(pxmout); if (Fmeta) { // optional copy metadata 20.0 err = exif_get(infile,keys,kdata,NK); exif_put(outfile,keys,(cchar **) kdata,NK); } zfree(outfile); } popup_report_write(zd2,0,"\n *** %s \n",Bcompleted); popup_report_bottom(zd2); return; } // dialog event and completion callback function int export_files_dialog_event(zdialog *zd, cchar *event) { using namespace export_files_names; int cc, err; char countmess[80]; char *ploc; STATB stbuff; if (strmatch(event,"files")) // select files to export { gallery_select(); // get list of files to convert snprintf(countmess,80,Bfileselected,GScount); // update dialog zdialog_stuff(zd,"fcount",countmess); } if (strmatch(event,"browse")) { zdialog_fetch(zd,"toloc",tolocation,500); ploc = zgetfile(E2X("Select folder"),MWIN,"folder",tolocation); // new location browse if (! ploc) return 1; zdialog_stuff(zd,"toloc",ploc); zfree(ploc); } if (! zd->zstat) return 1; // wait for dialog completion if (zd->zstat != 1) return 1; // [cancel] if (! GScount) { // [proceed] zmessageACK(Mwin,Bnofileselected); // no files selected zd->zstat = 0; // keep dialog active return 1; } zdialog_fetch(zd,"toloc",tolocation,500); // get output location err = stat(tolocation,&stbuff); if (err || ! S_ISDIR(stbuff.st_mode)) { zmessageACK(Mwin,E2X("location is not a folder")); zd->zstat = 0; } cc = strlen(tolocation) - 1; // remove trailing '/' if present if (tolocation[cc] == '/') tolocation[cc] = 0; zdialog_fetch(zd,"samesize",Fsamesize); // get resize options zdialog_fetch(zd,"maxww",maxww); zdialog_fetch(zd,"maxhh",maxhh); zdialog_fetch(zd,"meta",Fmeta); // metadata option 20.0 return 1; } /******************************************************************************** Build a script file with one or more predefined edit functions that can be executed like a single edit function. fotoxx.h: char scriptfile[200]; // current script file FILE *script_fid; // script file FID int Fscriptbuild; // flag, script build in progress ***/ // menu function void m_edit_script(GtkWidget *, cchar *menu) { int edit_script_dialog_event(zdialog *zd, cchar *event); zdialog *zd; F1_help_topic = "script files"; if (checkpend("all")) return; // check nothing pending /*** _________________________________________ | Script Files | | | | [start] begin making a script file | | [close] finish making a script file | | | | [cancel] | |_________________________________________| ***/ zd = zdialog_new(E2X("Script Files"),Mwin,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"button","start","hb1",Bstart,"space=5"); zdialog_add_widget(zd,"label","labstart","hb1",E2X("begin making a script file")); zdialog_add_widget(zd,"hbox","hb2","dialog"); zdialog_add_widget(zd,"button","close","hb2",Bclose,"space=5"); zdialog_add_widget(zd,"label","labclose","hb2",E2X("finish making a script file")); zdialog_run(zd,edit_script_dialog_event,"parent"); return; } // dialog event and completion function int edit_script_dialog_event(zdialog *zd, cchar *event) { int edit_script_start(void); int edit_script_close(void); if (zd->zstat) { // cancel zdialog_free(zd); if (script_fid) fclose(script_fid); script_fid = 0; Fscriptbuild = 0; return 1; } if (strmatch(event,"start")) // start building script edit_script_start(); if (strmatch(event,"close")) { // script if finished edit_script_close(); zdialog_free(zd); } return 1; } // start building a script file // if Fscriptbuild is active, edit_done() saves edit settings in script file int edit_script_start() { char *pp; if (checkpend("all")) return 0; if (Fscriptbuild) { zmessageACK(Mwin,E2X("script already started")); return 0; } pp = zgetfile(E2X("start a new script file"),MWIN,"save",scripts_folder,1); if (! pp) return 0; strncpy0(scriptfile,pp,200); // script file name from user zfree(pp); pp = strrchr(scriptfile,'/'); // script name = file base name if (! pp || strlen(pp) > 99) { zmessageACK(Mwin,"script file name too big"); return 0; } script_fid = fopen(scriptfile,"w"); // open script file if (! script_fid) { zmessageACK(Mwin,strerror(errno)); return 0; } fprintf(script_fid,"script name: %s\n",pp+1); // write header record Fscriptbuild = 1; zmessageACK(Mwin,E2X("perform edits to be included in the script file")); return 1; } // this function is called from edit_done() when Fscriptbuild is active // save all widget settings in the script file data void edit_script_addfunc(editfunc *CEF) { int err; fprintf(script_fid,"menu: %s\n",CEF->menuname); // write "menu: menu name" if (CEF->zd) { err = func_save_widgets(CEF->zd,CEF->sd,CEF->funcname,script_fid); // write widget settings if (err) { zmessageACK(Mwin,E2X("script file error")); return; } } zmessageACK(Mwin,E2X("%s added to script"),CEF->funcname); return; } // complete and save a script file under construction int edit_script_close() { if (! Fscriptbuild) { zmessageACK(Mwin,E2X("no script file was started")); return 0; } fprintf(script_fid,"menu: end"); fclose(script_fid); script_fid = 0; Fscriptbuild = 0; zmessageACK(Mwin,E2X("script file closed")); return 1; } // present a popup menu for user to select a script file // run the given function using the selected script file // called by m_run_script() and m_batch_script() // 'runfunc' arg: void runfunc(GtkWidget *, cchar *scriptname); void select_script(cbFunc *runfunc) { static char **scriptnames = 0; static GtkWidget *scriptmenu = 0; static int ns = 0; char buff[200], *pp; int ii, kk; FILE *fid; if (scriptnames) { for (ii = 0; ii < ns; ii++) // free prior memory zfree(scriptnames[ii]); zfree(scriptnames); scriptnames = 0; } if (scriptmenu) gtk_widget_destroy(scriptmenu); scriptmenu = 0; ns = zreaddir(scripts_folder,scriptnames); // get all script names if (ns < 1) { // from script file folder zmessageACK(Mwin,E2X("no script files found")); return; } scriptmenu = create_popmenu(); // create popup menu for script names add_popmenu_item(scriptmenu,Bcancel,0,0,0); for (ii = kk = 0; ii < ns; ii++) { snprintf(scriptfile,200,"%s/%s",scripts_folder,scriptnames[ii]); // read script file 1st record fid = fopen(scriptfile,"r"); if (! fid) continue; pp = fgets_trim(buff,200,fid,1); // read "script name: scriptname" if (pp && strmatch(pp+13,scriptnames[ii])) { // must match file name add_popmenu_item(scriptmenu,scriptnames[ii],runfunc,0,0); // add to popup menu kk++; } fclose(fid); } if (kk) popup_menu(0,scriptmenu); // present menu else zmessageACK(Mwin,E2X("no script files found")); return; } // execute a script file using the current image file void run_script(GtkWidget *, cchar *scriptname) { char buff[200]; char *pp, menuname[40]; int ii, err; if (! curr_file) return; if (script_fid) printz("*** run_script(): script_fid not 0 \n"); snprintf(scriptfile,200,"%s/%s",scripts_folder,scriptname); // script file from script name printz("start script: %s \n",scriptfile); script_fid = fopen(scriptfile,"r"); // open script file if (! script_fid) { zmessageACK(Mwin,E2X("script error: %s \n %s"),scriptfile,strerror(errno)); goto badfile; } pp = fgets_trim(buff,200,script_fid,1); // read "script name: scriptname" if (! pp) goto badfile; if (! strmatch(pp+13,scriptname)) goto badfile; // must match script name while (true) // process script file { Fscriptrun = 1; // script file active 20.0 pp = fgets_trim(buff,200,script_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 (ii = 0; menutab[ii].menu; ii++) // convert menu name to menu function if (strmatch(menuname,menutab[ii].menu)) break; if (! menutab[ii].menu) { zmessageACK(Mwin,E2X("unknown edit function: %s"),menuname); goto badfile; } printz("start edit: %s for file: %s \n",menuname,curr_file); // 19.0 menutab[ii].func(0,menutab[ii].arg); // call the menu function zmainsleep(0.1); if (CEF && CEF->zd) { err = func_load_widgets(CEF->zd,CEF->sd,CEF->funcname,script_fid); // read and load dialog settings if (err) { zmessageACK(Mwin,E2X("load widgets failed: %s"),menuname); goto badfile; } zdialog_send_event(CEF->zd,"apply"); // finish edit wait_thread_idle(); // insure complete bugfix 19.0 zdialog_send_event(CEF->zd,"done"); printz("finish edit \n"); } } fclose(script_fid); // close script file script_fid = 0; Fscriptrun = 0; // script file not active 20.0 return; badfile: zmessageACK(Mwin,E2X("script file format error: %s"),scriptname); if (script_fid) fclose(script_fid); script_fid = 0; Fscriptrun = 0; return; } // execute a script file using the pre-selected image files void batch_script(GtkWidget *, cchar *scriptname) { char *imagefile; char *newfilevers; int ii, err; if (! GScount) { // preselected file list zmessageACK(Mwin,Bnofileselected); return; } for (ii = 0; ii < GScount; ii++) // loop image files to process { imagefile = GSfiles[ii]; err = f_open(imagefile,0,0,1,0); // open, current image file if (err) { zmessageACK(Mwin,E2X("open failure: %s \n %s"),imagefile,strerror(errno)); return; } run_script(0,scriptname); // run script for image file newfilevers = file_new_version(curr_file); // get next avail. file version name if (! newfilevers) return; // (includes error message) 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,0,1); // save file zfree(newfilevers); } zmessage_post_bold(Mwin,"20/20",3,E2X("script complete")); return; } // menu function // select and run a script file using the current image file void m_run_script(GtkWidget *, cchar *menu) { F1_help_topic = "script files"; select_script(run_script); return; } // menu function // select and run a script file using multiple selected files void m_batch_script(GtkWidget *, cchar *menu) { int batch_script_dialog_event(zdialog *zd, cchar *event); zdialog *zd; char text[100]; F1_help_topic = "script files"; if (checkpend("all")) return; // check nothing pending /*** _________________________________________ | Batch Script | | | | [Select Files] NN files selected | | [Select Script] script file to run | | | | [cancel] | |_________________________________________| ***/ zd = zdialog_new(E2X("Batch Script"),Mwin,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"button","select-files","hb1",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","fcount","hb1",Bnofileselected); zdialog_add_widget(zd,"hbox","hb2","dialog"); zdialog_add_widget(zd,"button","select-script","hb2",E2X("Select Script"),"space=5"); zdialog_add_widget(zd,"label","labscript","hb2",E2X("script file to run")); snprintf(text,100,Bfileselected,GScount); // show selected files count 20.0 zdialog_stuff(zd,"fcount",text); zdialog_run(zd,batch_script_dialog_event,"parent"); return; } // dialog event and completion function int batch_script_dialog_event(zdialog *zd, cchar *event) { char countmess[80]; F1_help_topic = "script files"; if (zd->zstat) { // cancel zdialog_free(zd); return 1; } if (strmatch(event,"select-files")) { gallery_select(); // get new file list if (GScount) { snprintf(countmess,80,Bfileselected,GScount); // update dialog file count zdialog_stuff(zd,"fcount",countmess); } else zdialog_stuff(zd,"fcount",Bnofileselected); } if (strmatch(event,"select-script")) { // select and run a script file if (! GScount) zmessageACK(Mwin,Bnofileselected); else { select_script(batch_script); zdialog_free(zd); } } return 1; } fotoxx-20.08/f.tools.cc000066400000000000000000007460611362435004500147650ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* Fotoxx image edit - Tools menu functions m_index dialog to create/update image index file index_rebuild create/update image index file m_quick_index quick incremental index rebuild, no UI index_rebuild_old use old image index file without updates m_move_fotoxx_home move fotoxx home folder from default ~/.fotoxx m_preferences user preferences and settings dialog m_KBshortcuts edit keyboard shortcuts KBshortcuts_load load KB shortcuts file into memory m_RGB_dist show RGB brightness distribution graph m_magnify magnify the image within a radius of the mouse m_duplicates find all duplicated images m_show_RGB show RGB values for clicked image positions m_color_profile convert from one color profile to another m_calibrate_printer printer color calibration print_calibrated print current image file with calibrated colors m_gridlines setup for grid lines m_line_color choose color for foreground lines m_darkbrite highlight darkest and brightest pixels m_map_pixel_bias map raw pixel bias (camera sensor), save to file pixel_bias_map_load load pixel bias data from a file pixel_bias_fix correct pixel bias in an image m_map_dead_pixels map raw dead pixels (camera sensor), save to file dead_pixels_map_load load dead pixels data from a file dead_pixels_fix fix dead pixels in an image m_monitor_color monitor color and contrast check m_monitor_gamma monitor gamma check and adjust m_change_lang choose GUI language m_untranslated report missing translations m_phone_home_allow allow/block anonymous usage statistics m_resources print memory allocated and CPU time used m_appimage_files show files included in appimage container m_zappcrash zappcrash test *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" /********************************************************************************/ // Index Image Files menu function // Dialog to get top image folders, thumbnails folder, indexed metadata items. // Update the index_config file and generate new image index. namespace index_names { zdialog *zd_indexlog = 0; xxrec_t *xxrec = 0, **xxrec_old = 0, **xxrec_new = 0; int Xthread1, Xthread2, Tthread1, Tthread2; int Ffullindex = 0; // user: force full re-index int Fuserkill; } // index menu function void m_index(GtkWidget *, cchar *) { using namespace index_names; void index_callbackfunc(GtkWidget *widget, int line, int pos, int kbkey); int index_dialog_event(zdialog *zd, cchar *event); zdialog *zd; FILE *fid; char filespec[200], buff[200], sthumbfolder[200]; char *pp; GtkWidget *widget; int line, ii, cc, zstat; cchar *greet1 = E2X("Folders for image files " "(subfolders included automatically)."); cchar *greet2 = E2X("Select to add, click on X to delete."); cchar *greet3 = E2X("folder for thumbnails"); cchar *greet4 = E2X("extra metadata items to include in index"); cchar *greet5 = E2X("force a full re-index of all image files"); cchar *termmess = E2X("Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast."); F1_help_topic = "index files"; m_viewmode(0,"0"); // no view mode 19.0 if (checkpend("all")) return; // check nothing pending Fblock = 1; Ffullindex = 0; /*** ______________________________________________________________ | Index Image Files | | | | Folders for image files (subdirs included automatically). | | [Select] Select to add, click on X to delete. | | __________________________________________________________ | | | X /home//Pictures | | | | X /home//... | | | | | | | | | | | | | | | |__________________________________________________________| | | | | [Select] folder for thumbnails ___________________________ | | |__________________________________________________________| | | | | [Select] extra metadata items to include in index | | | | [x] force a full re-index of all image files | | | | [Help] [Proceed] [Cancel] | |______________________________________________________________| ***/ zd = zdialog_new(E2X("Index Image Files"),Mwin,Bhelp,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hsep","space","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbgreet1","dialog"); zdialog_add_widget(zd,"label","labgreet1","hbgreet1",greet1,"space=3"); zdialog_add_widget(zd,"hbox","hbtop","dialog"); zdialog_add_widget(zd,"button","browsetop","hbtop",Bselect,"space=3"); // browse top button zdialog_add_widget(zd,"label","labgreet2","hbtop",greet2,"space=5"); zdialog_add_widget(zd,"hbox","hbtop2","dialog",0,"expand"); zdialog_add_widget(zd,"label","space","hbtop2",0,"space=3"); zdialog_add_widget(zd,"vbox","vbtop2","hbtop2",0,"expand"); zdialog_add_widget(zd,"label","space","hbtop2",0,"space=3"); zdialog_add_widget(zd,"frame","frtop","vbtop2",0,"expand"); zdialog_add_widget(zd,"scrwin","scrtop","frtop",0,"expand"); zdialog_add_widget(zd,"text","topfolders","scrtop"); // topfolders text zdialog_add_widget(zd,"hsep","space","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbthumb1","dialog"); zdialog_add_widget(zd,"button","browsethumb","hbthumb1",Bselect,"space=3"); // browse thumb button zdialog_add_widget(zd,"label","labgreet3","hbthumb1",greet3,"space=5"); zdialog_add_widget(zd,"hbox","hbthumb2","dialog"); zdialog_add_widget(zd,"frame","frthumb","hbthumb2",0,"space=5|expand"); zdialog_add_widget(zd,"zentry","sthumbfolder","frthumb"); // thumbnail folder zdialog_add_widget(zd,"hsep","space","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbxmeta","dialog"); zdialog_add_widget(zd,"button","browsxmeta","hbxmeta",Bselect,"space=3"); // browse xmeta metadata zdialog_add_widget(zd,"label","labgreet4","hbxmeta",greet4,"space=5"); zdialog_add_widget(zd,"hsep","space","dialog",0,"space=5"); // force full re-index zdialog_add_widget(zd,"hbox","hbforce","dialog"); zdialog_add_widget(zd,"check","forcex","hbforce",greet5,"space=3"); widget = zdialog_widget(zd,"topfolders"); // set mouse/KB event function textwidget_set_eventfunc(widget,index_callbackfunc); // for top folders text window textwidget_clear(widget); // default top folder textwidget_append(widget,0," X %s\n",getenv("HOME")); // /home/ snprintf(sthumbfolder,200,"%s/thumbnails",get_zhomedir()); // default thumbnails folder zdialog_stuff(zd,"sthumbfolder",sthumbfolder); // /home//.fotoxx/thumbnails xmeta_keys[0] = 0; // default no indexed metadata snprintf(filespec,200,"%s/index_config",index_folder); // read index_config file, // stuff data into dialog widgets fid = fopen(filespec,"r"); if (fid) { textwidget_clear(widget); while (true) { pp = fgets_trim(buff,200,fid,1); if (! pp) break; if (strmatchN(buff,"thumbnails:",11)) { // if "thumbnails: /..." if (buff[12] == '/') // stuff thumbnails folder zdialog_stuff(zd,"sthumbfolder",buff+12); } else if (strmatchN(buff,"metadata:",9)) { // if "metadata:" for (ii = 0; ii < Mxmeta; ii++) { // build indexed metadata list pp = (char *) strField(buff+9,"^",ii+1); if (! pp) break; xmeta_keys[ii] = zstrdup(pp); } xmeta_keys[ii] = 0; // mark EOL } else textwidget_append(widget,0," X %s\n",buff); // stuff " X /dir1/dir2..." } fclose(fid); } zdialog_resize(zd,500,500); // run dialog zdialog_run(zd,index_dialog_event,"save"); zstat = zdialog_wait(zd); // wait for completion while (zstat == 1) { zd->zstat = 0; // keep dialog active m_help(0,"Help"); zstat = zdialog_wait(zd); } if (zstat != 2) // canceled { zdialog_free(zd); zmessageACK(Mwin,termmess); // index not finished Fblock = 0; return; } snprintf(filespec,200,"%s/index_config",index_folder); // open/write index config file fid = fopen(filespec,"w"); if (! fid) { // fatal error zmessageACK(Mwin,"index_config file: \n %s",strerror(errno)); Fblock = 0; quitxx(); // unconditional exit 20.05 } widget = zdialog_widget(zd,"topfolders"); // get top folders from dialog widget for (line = 0; ; line++) { pp = textwidget_line(widget,line,1); // loop widget text lines if (! pp || ! *pp) break; pp += 4; // skip " X " if (*pp != '/') continue; strncpy0(buff,pp,200); // /dir1/dir2/... cc = strlen(buff); if (cc < 5) continue; // ignore blanks or rubbish if (buff[cc-1] == '/') buff[cc-1] = 0; // remove trailing '/' fprintf(fid,"%s\n",buff); // write top folder to config file } zdialog_fetch(zd,"sthumbfolder",buff,200); // get thumbnails folder from dialog strTrim2(buff); // remove surrounding blanks cc = strlen(buff); if (cc && buff[cc-1] == '/') buff[cc-1] = 0; // remove trailing '/' fprintf(fid,"thumbnails: %s\n",buff); // thumbnails folder >> config file *buff = 0; for (ii = 0; ii < Mxmeta; ii++) { // indexed metadata >> config file if (! xmeta_keys[ii]) break; strncatv(buff,200,xmeta_keys[ii],"^ ",0); } fprintf(fid,"metadata: %s\n",buff); fclose(fid); zdialog_free(zd); // close dialog index_rebuild(2,1); // build image index and thumbnail files if (! Findexvalid) m_index(0,0); // failed, try again Fblock = 0; // OK navi::galleryname = zstrdup(topfolders[0]); // set current gallery return; } // ------------------------------------------------------------------------------ // mouse click function for top folders text window // remove folder from list where "X" is clicked void index_callbackfunc(GtkWidget *widget, int line, int pos, int kbkey) { GdkWindow *gdkwin; char *pp; char *dirlist[maxtopfolders]; int ii, jj; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } gdkwin = gtk_widget_get_window(widget); // stop updates between clear and refresh gdk_window_freeze_updates(gdkwin); for (ii = jj = 0; ii < maxtopfolders; ii++) // loop text lines in widget { // " X /dir1/dir2/... " pp = textwidget_line(widget,ii,1); if (! pp || strlen(pp) < 4) break; if (ii == line && pos < 3) continue; // if "X" clicked, skip deleted line dirlist[jj] = zstrdup(pp); jj++; } textwidget_clear(widget); for (ii = 0; ii < jj; ii++) // stuff remaining lines back into widget { textwidget_append(widget,0,"%s\n",dirlist[ii]); zfree(dirlist[ii]); } gdk_window_thaw_updates(gdkwin); return; } // ------------------------------------------------------------------------------ // index dialog event and completion function int index_dialog_event(zdialog *zd, cchar *event) { using namespace index_names; int ii, yn; GtkWidget *widget; char **flist, *pp, *sthumbfolder; cchar *topmess = E2X("Choose top image folders"); cchar *thumbmess = E2X("Choose thumbnail folder"); cchar *xmetamess = E2X("All image files will be re-indexed. \n" " Continue?"); if (strmatch(event,"browsetop")) { // [browse] top folders flist = zgetfiles(topmess,MWIN,"folders",getenv("HOME")); // get top folders from user if (! flist) return 1; widget = zdialog_widget(zd,"topfolders"); // add to dialog list for (ii = 0; flist[ii]; ii++) { textwidget_append2(widget,0," X %s\n",flist[ii]); // " X /dir1/dir2/..." zfree(flist[ii]); } zfree(flist); } if (strmatch(event,"browsethumb")) { // [browse] thumbnail folder pp = zgetfile(thumbmess,MWIN,"folder",getenv("HOME")); if (! pp) return 1; sthumbfolder = zstrdup(pp,12); if (! strstr(sthumbfolder,"/thumbnails")) // if not containing /thumbnails, strcat(sthumbfolder,"/thumbnails"); // append /thumbnails zdialog_stuff(zd,"sthumbfolder",sthumbfolder); zfree(sthumbfolder); zfree(pp); } if (strmatch(event,"browsxmeta")) { // [select] yn = zmessageYN(Mwin,xmetamess); // add optional indexed metadata if (! yn) return 1; ii = select_meta_keys(xmeta_keys,1); if (ii) Ffullindex = 1; // changes made, force full re-index } if (strmatch(event,"forcex")) { // force full re-index zdialog_fetch(zd,"forcex",ii); if (ii) Ffullindex = 1; } if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) { // [help] zd->zstat = 0; showz_docfile(Mwin,"userguide","index_files"); return 1; } return 1; // [proceed] or cancel status } // ------------------------------------------------------------------------------ // set up a minimal index for user who does not want to index void index_noindex() { FILE *fid; char sthumbfolder[200], indexfile[200]; topfolders[0] = zstrdup(getenv("HOME")); // default top image folder Ntopfolders = 1; // /home/ snprintf(sthumbfolder,200,"%s/thumbnails",get_zhomedir()); // default thumbnails folder thumbfolder = zstrdup(sthumbfolder); // /home//.fotoxx/thumbnails snprintf(indexfile,200,"%s/index_config",index_folder); // open/write index config file fid = fopen(indexfile,"w"); if (! fid) { // fatal error zmessageACK(Mwin,"%s \n %s",indexfile,strerror(errno)); quitxx(); // unconditional exit 20.05 } fprintf(fid,"%s\n",topfolders[0]); // write top folder fprintf(fid,"thumbnails: %s\n",sthumbfolder); // write thumbnail folder fclose(fid); return; } // ------------------------------------------------------------------------------ // Rebuild the image index from the index config data. // index level = 0/1/2 = no index / old files only / old + new files // Called from main() when Fotoxx is started (index level from user setting) // Called from menu function m_index() (index level = 2) void index_rebuild(int indexlev, int menu) // overhauled for removable storage 20.0 { using namespace index_names; void index_rebuild_old(); int indexlog_dialog_event(zdialog *zd, cchar *event); int index_compare(cchar *rec1, cchar *rec2); void *index_thread(void *); void *thumb_thread(void *); void * index_sync(void *arg); GtkWidget *wlog; FILE *fid; char **topfol2, **misstop2; int *topcc2, *misscc2; int yn, ii, jj, nn; int Ntop, Nthumb; int ftf, NF, orec, orec2, nrec, xrec, comp; int cc, cc1, cc2, err; int Nt1, Nt2; xxrec_t *xxrec, **xxrec_new = 0, **xxrec_old = 0; int Nnew, Nold; int Xupdates, Tupdates, Tdeletes; int *Xstatus = 0, *Tstatus = 0; char *pp, *pp1, *pp2; char filespec[200], buff[XFCC+500]; char **flist, *file, *thumbfile; STATB statb; double startime, time0; cchar *indexmess = E2X("No image file index was found.\n" "An image file index will be created.\n" "Your image files will not be changed.\n" "This may need considerable time if you \n" "have many thousands of image files."); cchar *indexerr2 = E2X("Thumbnails folder: %s \n" "Please remove."); cchar *thumberr = E2X("Thumbnails folder: \n %s \n" "must be named .../thumbnails"); cchar *duperr = E2X("Duplicate or nested folders: \n %s \n %s \n" "Please remove."); startime = get_seconds(); // index function start time Findexvalid = 0; Fblock = 1; Fuserkill = 0; // get current top image folders and thumbnails folder // from /home//.fotoxx/image_index/index_config for (ii = 0; ii < Ntopfolders; ii++) // free prior zfree(topfolders[ii]); Ntop = Nthumb = 0; snprintf(filespec,200,"%s/index_config",index_folder); // read index config file fid = fopen(filespec,"r"); if (fid) { while (true) { // get top image folders pp = fgets_trim(buff,200,fid,1); if (! pp) break; if (strmatchN(buff,"thumbnails: /",13)) { // get thumbnails folder if (thumbfolder) zfree(thumbfolder); thumbfolder = zstrdup(buff+12); Nthumb++; } else if (strmatchN(buff,"metadata:",9)) { // get indexed metadata keys for (ii = 0; ii < Mxmeta; ii++) { pp = (char *) strField(buff+9,"^",ii+1); if (! pp) { xmeta_keys[ii] = 0; break; } xmeta_keys[ii] = zstrdup(pp); } } else { topfolders[Ntop] = zstrdup(buff); if (++Ntop == maxtopfolders) break; } } fclose(fid); } Ntopfolders = Ntop; if (indexlev == 0) // indexing is disabled 20.0 { if (Nthumb != 1) // no index_config file, or invalid { thumbfolder = zstrdup("",200); // set default thumbnails folder snprintf(thumbfolder,200,"%s/thumbnails",get_zhomedir()); fid = fopen(filespec,"w"); // create stump index config file if (! fid) { // (thumbnails folder only) zmessageACK(Mwin,"index config file error: %s",strerror(errno)); quitxx(); } fprintf(fid,"thumbnails: %s\n",thumbfolder); fclose(fid); } err = stat(thumbfolder,&statb); // create thumbnails folder if needed if (err || ! S_ISDIR(statb.st_mode)) err = shell_ack("mkdir -p -m 0750 \"%s\" ",thumbfolder); if (err) { zmessageACK(Mwin,"%s \n %s",thumbfolder,strerror(errno)); quitxx(); } Nthumb = 1; printz("thumbnails folder: \n"); // print thumbnails folder printz(" %s \n",thumbfolder); Ntopfolders = 0; // no top image folders printz("no image index: reports disabled \n"); // no image index Findexvalid = 0; Fblock = 0; return; } if (! Ntopfolders) { // if nothing found, must ask user zmessageACK(Mwin,E2X("specify at least 1 top image folder")); goto cleanup; } if (Nthumb != 1) { // 0 or >1 thumbnail folders zmessageACK(Mwin,E2X("specify 1 thumbnail folder")); goto cleanup; } cc = strlen(thumbfolder) - 11 ; // check /thumbnails name if (! strmatch(thumbfolder+cc,"/thumbnails")) { zmessageACK(Mwin,thumberr,thumbfolder); goto cleanup; } err = stat(thumbfolder,&statb); // create thumbnails folder if needed if (err || ! S_ISDIR(statb.st_mode)) err = shell_ack("mkdir -p -m 0750 \"%s\" ",thumbfolder); // use shell mkdir if (err) { zmessageACK(Mwin,"%s \n %s",thumbfolder,strerror(errno)); goto cleanup; } for (ii = 0; ii < Ntopfolders; ii++) { // disallow top dir = thumbnail dir if (strmatch(topfolders[ii],thumbfolder)) { zmessageACK(Mwin,indexerr2,topfolders[ii]); goto cleanup; } } for (ii = 0; ii < Ntopfolders; ii++) // check for duplicate folders for (jj = ii+1; jj < Ntopfolders; jj++) // or nested folders { cc1 = strlen(topfolders[ii]); cc2 = strlen(topfolders[jj]); if (cc1 <= cc2) { pp1 = zstrdup(topfolders[ii],2); pp2 = zstrdup(topfolders[jj],2); } else { pp1 = zstrdup(topfolders[jj],2); pp2 = zstrdup(topfolders[ii],2); cc = cc1; cc1 = cc2; cc2 = cc; } strcpy(pp1+cc1,"/"); strcpy(pp2+cc2,"/"); nn = strmatchN(pp1,pp2,cc1+1); zfree(pp1); zfree(pp2); if (nn) { zmessageACK(Mwin,duperr,topfolders[ii],topfolders[jj]); // user must fix goto cleanup; } } for (ii = jj = Nmisstops = 0; ii < Ntopfolders; ii++) // check top image folders { err = stat(topfolders[ii],&statb); if (err || ! S_ISDIR(statb.st_mode)) // move missing top image folders misstops[Nmisstops++] = topfolders[ii]; // into a separate list else topfolders[jj++] = topfolders[ii]; // (poss. not mounted) } Ntopfolders = jj; // valid top image folders if (! Ntopfolders) { // none, must ask user zmessageACK(Mwin,indexmess); goto cleanup; } topfol2 = (char **) zmalloc(Ntopfolders * sizeof(char *)); // top folders with '/' appended topcc2 = (int *) zmalloc(Ntopfolders * sizeof(int)); // cc of same misstop2 = (char **) zmalloc(Nmisstops * sizeof(char *)); // missing top folders with '/' misscc2 = (int *) zmalloc(Nmisstops * sizeof(int)); // cc of same for (ii = 0; ii < Ntopfolders; ii++) { // save top folders with appended '/' topfol2[ii] = zstrdup(topfolders[ii],2); // for use with later comparisons strcat(topfol2[ii],"/"); topcc2[ii] = strlen(topfol2[ii]); } for (ii = 0; ii < Nmisstops; ii++) { misstop2[ii] = zstrdup(misstops[ii],2); strcat(misstop2[ii],"/"); misscc2[ii] = strlen(misstop2[ii]); } printz("top image folders: \n"); for (ii = 0; ii < Ntopfolders; ii++) // print top folders printz(" %s\n",topfolders[ii]); if (Nmisstops) for (ii = 0; ii < Nmisstops; ii++) // print missing top folders printz(" %s *** not mounted *** \n",misstops[ii]); printz("thumbnails folder: \n"); // print thumbnails folder printz(" %s \n",thumbfolder); if (indexlev == 1) { printz("old image index: reports will omit new files \n"); // image index has old files only index_rebuild_old(); Fblock = 0; return; } if (indexlev == 2) // update image index for all image files printz("full image index: reports will be complete \n"); // create log window for reporting status and statistics if (zd_indexlog) zdialog_free(zd_indexlog); // make dialog for output log zd_indexlog = zdialog_new(E2X("build index"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd_indexlog,"frame","frame","dialog",0,"expand"); zdialog_add_widget(zd_indexlog,"scrwin","scrwin","frame"); zdialog_add_widget(zd_indexlog,"text","text","scrwin"); wlog = zdialog_widget(zd_indexlog,"text"); zdialog_resize(zd_indexlog,700,500); zdialog_run(zd_indexlog,indexlog_dialog_event,0); textwidget_append(wlog,0,"top image folders:\n"); // log top image folders for (ii = 0; ii < Ntopfolders; ii++) textwidget_append(wlog,0," %s\n",topfolders[ii]); if (Nmisstops) // log missing top image folders for (ii = 0; ii < Nmisstops; ii++) textwidget_append(wlog,0," %s *** not mounted *** \n",misstops[ii]); textwidget_append(wlog,0,"thumbnails folder: \n"); // log thumbnails folder textwidget_append(wlog,0," %s \n",thumbfolder); textwidget_scroll(wlog,-1); if (Ffullindex) { // user: force full re-index Nold = 0; goto get_new_files; } // read old image index file and build "old list" of index recs textwidget_append2(wlog,0,"reading image index file ...\n"); cc = maximages * sizeof(xxrec_t *); xxrec_old = (xxrec_t **) zmalloc(cc); // "old" image index recs Nold = 0; ftf = 1; while (true) { xxrec = read_xxrec_seq(ftf); // read curr. index recs if (! xxrec) break; err = stat(xxrec->file,&statb); // file status if (err) { // file is missing for (ii = 0; ii < Nmisstops; ii++) if (strmatchN(xxrec->file,misstop2[ii],misscc2[ii])) break; // within missing top folders? if (ii == Nmisstops) continue; // no, exclude file } else { // file is present if (! S_ISREG(statb.st_mode)) continue; // not a regular file, remove for (ii = 0; ii < Ntopfolders; ii++) if (strmatchN(xxrec->file,topfol2[ii],topcc2[ii])) break; // within top folders? if (ii == Ntopfolders) continue; // no, exclude file } xxrec_old[Nold] = xxrec; // file is included if (++Nold == maximages) { zmessageACK(Mwin,"exceeded max. images: %d \n",maximages); Fblock = 0; quitxx(); // unconditional exit 20.05 } } textwidget_append2(wlog,0,"index files found: %d \n",Nold); printz("index files found: %d \n",Nold); // sort old index recs in order of file name and file mod date if (Nold > 1) HeapSort((char **) xxrec_old,Nold,index_compare); // replace older recs with newer recs that were appended to the file // and are now sorted in file name sequence if (Nold > 1) { for (orec = 0, orec2 = 1; orec2 < Nold; orec2++) { if (strmatch(xxrec_old[orec]->file,xxrec_old[orec2]->file)) xxrec_old[orec] = xxrec_old[orec2]; else { orec++; xxrec_old[orec] = xxrec_old[orec2]; } } Nold = orec + 1; // new count } get_new_files: // find all image files and create "new list" of index recs textwidget_append2(wlog,0,"find all image files ...\n"); cc = maximages * sizeof(xxrec_t *); xxrec_new = (xxrec_t **) zmalloc(cc); // "new" image index recs Nnew = 0; for (ii = 0; ii < Ntopfolders; ii++) { err = find_imagefiles(topfolders[ii],1+16+32,flist,NF); // image files, recurse folders, if (err) { // omit symlinks 20.0 zmessageACK(Mwin,"find_imagefiles() failure \n"); Fblock = 0; quitxx(); // unconditional exit 20.05 } if (Nnew + NF > maximages) { zmessageACK(Mwin,"exceeded max. images: %d \n",maximages); Fblock = 0; quitxx(); // unconditional exit 20.05 } for (jj = 0; jj < NF; jj++) { file = flist[jj]; nrec = Nnew++; xxrec_new[nrec] = (xxrec_t *) zmalloc(sizeof(xxrec_t)); // allocate xxrec xxrec_new[nrec]->file = file; // filespec stat(file,&statb); compact_time(statb.st_mtime,xxrec_new[nrec]->fdate); // file mod date } // remaining data left null if (flist) zfree(flist); } textwidget_append2(wlog,0,"image files found: %d \n",Nnew); printz("image files found: %d \n",Nnew); if (Nnew == 0) { // no images found zmessageACK(Mwin,E2X("Top folders have no images")); goto cleanup; } // warn if large index job is about to begin if (Nold == 0 && ! menu) { // only if not started by user menu 20.0 yn = zmessageYN(Mwin,E2X("0 old files found, %d new files found.\n" "A full re-index is required. Continue?"),Nnew); if (! yn) goto cleanup; } // sort new index recs in order of file name and file mod date if (Nnew > 1) HeapSort((char **) xxrec_new,Nnew,index_compare); // create image index table in memory if (xxrec_tab) { for (ii = 0; ii < Nxxrec; ii++) // free memory for old xxrec_tab { if (xxrec_tab[ii]->file) zfree(xxrec_tab[ii]->file); if (xxrec_tab[ii]->tags) zfree(xxrec_tab[ii]->tags); if (xxrec_tab[ii]->capt) zfree(xxrec_tab[ii]->capt); if (xxrec_tab[ii]->comms) zfree(xxrec_tab[ii]->comms); if (xxrec_tab[ii]->location) zfree(xxrec_tab[ii]->location); if (xxrec_tab[ii]->country) zfree(xxrec_tab[ii]->country); if (xxrec_tab[ii]->xmeta) zfree(xxrec_tab[ii]->xmeta); zfree(xxrec_tab[ii]); } zfree(xxrec_tab); } cc = maximages * sizeof(xxrec_t *); // make new table with max. capacity xxrec_tab = (xxrec_t **) zmalloc(cc); Nxxrec = 0; Xstatus = (int *) zmalloc(maximages); // 1/2/3 = file missing/need-update/OK Tstatus = (int *) zmalloc(maximages); // 1/2/3 = thumb missing/need-update/OK // merge and compare old and new index recs nrec = orec = xrec = 0; while (true) // merge old and new index recs { if (nrec == Nnew && orec == Nold) break; // both EOF, done if (nrec < Nnew && orec < Nold) // if neither EOF, compare comp = strcmp(xxrec_old[orec]->file, xxrec_new[nrec]->file); else comp = 0; if (nrec == Nnew || comp < 0) { // old index rec has no match xxrec_tab[xrec] = xxrec_old[orec]; // copy old index rec to output Xstatus[xrec] = 1; // mark file as missing xxrec_old[orec] = 0; orec++; } else if (orec == Nold || comp > 0) { // new index rec has no match xxrec_tab[xrec] = xxrec_new[nrec]; // copy new index rec to output Xstatus[xrec] = 2; // mark file as needing update xxrec_new[nrec] = 0; nrec++; } else { // old and new index recs match xxrec_tab[xrec] = xxrec_old[orec]; // copy old index rec to output if (strmatch(xxrec_old[orec]->fdate, xxrec_new[nrec]->fdate)) // compare file dates Xstatus[xrec] = 3; // same, mark file as up to date else { Xstatus[xrec] = 2; // different, mark as needing update strcpy(xxrec_tab[xrec]->fdate, xxrec_new[nrec]->fdate); // use current file date } xxrec_old[orec] = 0; orec++; zfree(xxrec_new[nrec]->tags); // free memory for new index rec zfree(xxrec_new[nrec]->capt); zfree(xxrec_new[nrec]->comms); zfree(xxrec_new[nrec]->location); zfree(xxrec_new[nrec]->country); zfree(xxrec_new[nrec]->xmeta); xxrec_new[nrec] = 0; nrec++; } Tstatus[xrec] = 3; // thumbnail OK if (Xstatus[xrec] > 1) // if file not missing, if (! thumbfile_OK(xxrec_tab[xrec]->file)) // test and mark thumbnail Tstatus[xrec] = 2; xrec++; // count output recs if (xrec == maximages) { zmessageACK(Mwin,"max. image limit reached: %d",xrec); goto cleanup; } } Nxxrec = xrec; if (xxrec_new) zfree(xxrec_new); // free memory if (xxrec_old) zfree(xxrec_old); xxrec_new = xxrec_old = 0; for (ii = Xupdates = Tupdates = 0; ii < Nxxrec; ii++) { // count updates required if (Xstatus[ii] == 2) Xupdates++; // index rec updates if (Tstatus[ii] == 2) Tupdates++; // thumbnail file updates } textwidget_append(wlog,0,"index updates needed: %d thumbnails: %d \n",Xupdates,Tupdates); printz("index updates needed: %d thumbnails: %d \n",Xupdates,Tupdates); // Process entries needing update in the new index table // (new files or files dated later than in old index table). // Get updated metadata from image file EXIF/IPTC data. // Add missing or update stale thumbnail file. textwidget_append(wlog,0,"updating image index and thumbnails ... \n"); textwidget_append2(wlog,0,"\n"); printz("updating image index and thumbnails ... \n"); Xupdates = Tupdates = Tdeletes = 0; Xthread1 = Xthread2 = 0; Tthread1 = Tthread2 = 0; Nt1 = 2; Nt2 = 4; // no. parallel threads 19.20 if (Ftinycomputer) Nt2 = 2; // computer with inadequate memory 19.20 for (xrec = 0; xrec < Nxxrec; xrec++) // loop all index recs { if (Fuserkill || Fshutdown) break; // killed by user if (Xstatus[xrec] == 2) { // update metadata Xupdates++; while (Xthread1 - Xthread2 == Nt1) zsleep(0.001); // exif_server count (2) Xthread1++; Xstatus[xrec] = xrec; // separate arg per thread start_detached_thread(index_thread,&Xstatus[xrec]); } if (Tstatus[xrec] == 2) { // update thumbnail Tupdates++; while (Tthread1 - Tthread2 == Nt2) zsleep(0.001); // 4 parallel threads 19.20 Tthread1++; // (more not faster) Tstatus[xrec] = xrec; start_detached_thread(thumb_thread,&Tstatus[xrec]); } if (Xstatus[xrec] == xrec || Tstatus[xrec] == xrec) { // update counters file = xxrec_tab[xrec]->file; textwidget_replace(wlog,0,-1,"%d %d %s \n",Xupdates,Tupdates,file); } if (Xupdates && Xupdates == 2000 * (Xupdates / 2000)) // update image index file { // every 2000 updated recs. ftf = 1; for (ii = 0; ii < xrec; ii++) write_xxrec_seq(xxrec_tab[ii],ftf); write_xxrec_seq(null,ftf); // close output } } time0 = get_seconds(); while (true) // wait for last threads to exit { zsleep(0.01); if (get_seconds() - time0 > 20) { // limit the wait time ii = Xthread1 - Xthread2; if (ii) printz("*** %d index threads pending \n",ii); // note the failure ii = Tthread1 - Tthread2; if (ii) printz("*** %d thumbnail threads pending \n",ii); break; } if (Xthread2 != Xthread1) continue; if (Tthread2 != Tthread1) continue; break; } if (Fuserkill || Fshutdown) goto cleanup; textwidget_replace(wlog,0,-1,"index updates: %d thumbnails: %d \n", // final statistics Xupdates, Tupdates); printz("index updates: %d thumbnail updates: %d, deletes: %d \n", Xupdates, Tupdates, Tdeletes); // write updated index records to image index file printz("writing updated image index file \n"); if (Nxxrec) { err = 0; ftf = 1; for (xrec = 0; xrec < Nxxrec; xrec++) write_xxrec_seq(xxrec_tab[xrec],ftf); write_xxrec_seq(null,ftf); // close output start_detached_thread(index_sync,0); // commit index file to disk } // keep index records in memory only for files actually present textwidget_append2(wlog,0,"all image files, including unmounted folders: %d \n",Nxxrec); printz("all image files, including unmounted folders: %d \n",Nxxrec); for (ii = jj = 0; ii < Nxxrec; ii++) { err = stat(xxrec_tab[ii]->file,&statb); if (err) continue; xxrec_tab[jj++] = xxrec_tab[ii]; } Nxxrec = jj; // new count textwidget_append2(wlog,0,"image files in currently mounted folders: %d \n",Nxxrec); printz("image files in currently mounted folders: %d \n",Nxxrec); // find orphan thumbnails and delete them err = find_imagefiles(thumbfolder,2+16+32,flist,NF); // thumbnails, recurse dirs, if (err) { // ignore symlinks 20.0 zmessageACK(Mwin,strerror(errno)); NF = 0; } textwidget_append2(wlog,0,"thumbnails found: %d \n",NF); printz("thumbnails found: %d \n",NF); textwidget_append2(wlog,0,"deleting orphan thumbnails ... \n"); Tdeletes = 0; for (ii = 0; ii < NF; ii++) { zmainloop(100); thumbfile = flist[ii]; file = thumb2imagefile(thumbfile); // thumbnail corresponding file if (file) { // exists, keep thumbnail zfree(file); zfree(thumbfile); continue; } pp = thumbfile + strlen(thumbfolder); // corresponding file within for (jj = 0; jj < Nmisstops; jj++) // a missing top image folder? if (strmatchN(misstop2[jj],pp,misscc2[jj])) break; if (jj < Nmisstops) continue; // yes, keep thumbnail remove(thumbfile); // remove thumbnail file zfree(thumbfile); Tdeletes++; } if (flist) zfree(flist); textwidget_append2(wlog,0,"thumbnails deleted: %d \n",Tdeletes); printz("thumbnails deleted: %d \n",Tdeletes); textwidget_append2(wlog,0,"%s\n",Bcompleted); // index complete and OK printz("index time: %.1f seconds \n",get_seconds() - startime); // log elapsed time if (Nxxrec) { Findexvalid = 2; // image index is complete Findexlev = 2; // fotoxx command: index new files 20.0 FMindexlev = 1; // file manager: old index only } cleanup: // free allocated memory if (Fshutdown) return; // process terminating if (xxrec_old) zfree(xxrec_old); if (xxrec_new) zfree(xxrec_new); xxrec_old = xxrec_new = 0; if (Xstatus) zfree(Xstatus); if (Tstatus) zfree(Tstatus); Xstatus = Tstatus = 0; if (zd_indexlog && ! menu) // if not manual run, kill log window zdialog_send_event(zd_indexlog,"exitlog"); Fblock = 0; // unblock Ffullindex = 0; // full re-index done if (! Findexvalid) { if (menu) return; // called manually else m_index(0,0); // called from fotoxx startup } return; } // ------------------------------------------------------------------------------ // thread process - call sync() asynchronously void * index_sync(void *arg) // 20.0 { double t0, t1; t0 = get_seconds(); sync(); t1 = get_seconds() - t0; printz("sync() after index build %.3f \n",t1); pthread_exit(0); } // ------------------------------------------------------------------------------ // thread process - create thumbnail file for given image file void * thumb_thread(void *arg) { using namespace index_names; int xrec; char *file; xrec = *((int *) arg); file = xxrec_tab[xrec]->file; // image file to check update_thumbfile(file); // do thumbnail update if needed zadd_locked(Tthread2,+1); // completions pthread_exit(0); } // ------------------------------------------------------------------------------ // thread process - get image file metadata and build image index rec. // thread has no GTK calls void * index_thread(void *arg) { using namespace index_names; int xrec, err, ii, nkey, xcc, acc; int ww, hh; char *file, *pp; float flati, flongi; char xmetarec[1000]; // max. extra metadata, bytes STATB statb; cchar *exifkeys[100] = { "FileName", exif_date_key, iptc_keywords_key, // first 12 keys are fixed iptc_rating_key, exif_ww_key, exif_hh_key, // (replace exif_wwhh_key) 19.15 exif_comment_key, iptc_caption_key, exif_city_key, exif_country_key, exif_lati_key, exif_longi_key }; char *ppv[100], *exiffile = 0; char *exifdate = 0, *iptctags = 0, *iptcrating = 0; char *exifww = 0, *exifhh = 0, *iptccapt = 0, *exifcomms = 0; char *exifcity = 0, *exifcountry = 0, *exiflat = 0, *exiflong = 0; xrec = *((int *) arg); file = xxrec_tab[xrec]->file; // image file nkey = 12; // add keys for indexed metadata for (ii = 0; ii < Mxmeta; ii++) { // from exifkeys[12] if (! xmeta_keys[ii]) break; exifkeys[nkey] = xmeta_keys[ii]; nkey++; } err = exif_get(file,exifkeys,ppv,nkey); // get exif/iptc metadata if (err) { printz("exif_get() failure: %s \n",file); // metadata unreadable goto exit_thread; } exiffile = ppv[0]; // base file name pp = strrchr(file,'/'); if (! exiffile || ! strmatch(exiffile,pp+1)) { // image file has no metadata printz("exif_get() no data: %s \n",file); goto exit_thread; } exifdate = ppv[1]; // exif/iptc metadata returned iptctags = ppv[2]; // 12 fixed keys iptcrating = ppv[3]; exifww = ppv[4]; exifhh = ppv[5]; exifcomms = ppv[6]; iptccapt = ppv[7]; exifcity = ppv[8]; exifcountry = ppv[9]; exiflat = ppv[10]; exiflong = ppv[11]; if (exifdate && strlen(exifdate) > 3) // exif date (photo date) exif_tagdate(exifdate,xxrec_tab[xrec]->pdate); if (iptcrating && strlen(iptcrating)) { // iptc rating '0' - '5' xxrec_tab[xrec]->rating[0] = *iptcrating; xxrec_tab[xrec]->rating[1] = 0; } else strcpy(xxrec_tab[xrec]->rating,"0"); // not present if (exifww && exifhh) { // 19.15 convSI(exifww,ww); convSI(exifhh,hh); if (ww > 0 && hh > 0) { xxrec_tab[xrec]->ww = ww; xxrec_tab[xrec]->hh = hh; } } err = stat(file,&statb); xxrec_tab[xrec]->fsize = statb.st_size; // file size 19.0 if (iptctags && strlen(iptctags)) { // iptc tags xxrec_tab[xrec]->tags = iptctags; iptctags = 0; } if (iptccapt && strlen(iptccapt)) { // iptc caption xxrec_tab[xrec]->capt = iptccapt; iptccapt = 0; } if (exifcomms && strlen(exifcomms)) { // exif comments xxrec_tab[xrec]->comms = exifcomms; exifcomms = 0; } if (exifcity && strlen(exifcity)) { xxrec_tab[xrec]->location = exifcity; exifcity = 0; } if (exifcountry && strlen(exifcountry)) { xxrec_tab[xrec]->country = exifcountry; exifcountry = 0; } if (exiflat && exiflong) { flati = atof(exiflat); flongi = atof(exiflong); if (flati < -90.0 || flati > 90.0) flati = flongi = 0; if (flongi < -180.0 || flongi > 180.0) flati = flongi = 0; xxrec_tab[xrec]->flati = flati; xxrec_tab[xrec]->flongi = flongi; } xcc = 0; for (ii = 12; ii < nkey; ii++) // extra indexed metadata if any { if (! ppv[ii]) continue; acc = strlen(exifkeys[ii]) + strlen(ppv[ii]) + 3; if (acc > 100 || acc + xcc > 998) { // limit each key < 100, all < 1000 20.0 printz("indexed metadata too big, omitted: %s %s \n",exifkeys[ii],file); continue; } strcpy(xmetarec+xcc,exifkeys[ii]); // construct series xcc += strlen(exifkeys[ii]); // "keyname=keydata^ " xmetarec[xcc++] = '='; strcpy(xmetarec+xcc,ppv[ii]); xcc += strlen(ppv[ii]); strcpy(xmetarec+xcc,"^ "); xcc += 2; zfree(ppv[ii]); } if (xcc > 0) xxrec_tab[xrec]->xmeta = zstrdup(xmetarec); exit_thread: if (! xxrec_tab[xrec]->pdate[0]) strcpy(xxrec_tab[xrec]->pdate,"null"); // missing values are "null" if (! xxrec_tab[xrec]->tags) xxrec_tab[xrec]->tags = zstrdup("null"); if (! xxrec_tab[xrec]->capt) xxrec_tab[xrec]->capt = zstrdup("null"); if (! xxrec_tab[xrec]->comms) xxrec_tab[xrec]->comms = zstrdup("null"); if (! xxrec_tab[xrec]->location) xxrec_tab[xrec]->location = zstrdup("null"); if (! xxrec_tab[xrec]->country) xxrec_tab[xrec]->country = zstrdup("null"); if (! xxrec_tab[xrec]->xmeta) xxrec_tab[xrec]->xmeta = zstrdup("null"); if (exiffile) zfree(exiffile); if (exifdate) zfree(exifdate); // free EXIF data if (iptcrating) zfree(iptcrating); if (exifww) zfree(exifww); if (exifhh) zfree(exifhh); if (iptctags) zfree(iptctags); if (iptccapt) zfree(iptccapt); if (exifcomms) zfree(exifcomms); if (exifcity) zfree(exifcity); if (exifcountry) zfree(exifcountry); if (exiflat) zfree(exiflat); if (exiflong) zfree(exiflong); zadd_locked(Xthread2,+1); pthread_exit(0); } // ------------------------------------------------------------------------------ // index log window dialog response function int indexlog_dialog_event(zdialog *zd, cchar *event) { using namespace index_names; int nn; cchar *canmess = E2X("Cancel image index function?"); if (strmatch(event,"exitlog")) { // auto-kill from index_rebuild() zmainloop(); zsleep(0.5); zdialog_free(zd); zd_indexlog = 0; return 1; } if (! zd->zstat) return 1; // continue if (zd->zstat == 1 || zd->zstat == 2) { // [Done] or [Cancel] button if (Findexvalid) { zdialog_free(zd); // index completed, kill dialog zd_indexlog = 0; return 1; } nn = zdialog_choose(Mwin,"mouse",canmess,Bcontinue,Bcancel,null); // incomplete, ask for confirmation if (nn == 1) { zd->zstat = 0; // continue return 1; } } zdialog_free(zd); // cancel, kill index job zd_indexlog = 0; Fuserkill = 1; return 1; } // ------------------------------------------------------------------------------ // sort compare function - compare index records and return // <0 0 >0 for rec1 < rec2 rec1 == rec2 rec1 > rec2 int index_compare(cchar *rec1, cchar *rec2) { xxrec_t *xxrec1 = (xxrec_t *) rec1; xxrec_t *xxrec2 = (xxrec_t *) rec2; int nn = strcmp(xxrec1->file,xxrec2->file); if (nn) return nn; nn = strcmp(xxrec1->fdate,xxrec2->fdate); return nn; } /********************************************************************************/ // quick incremental index rebuild with no user interface void m_quick_index(GtkWidget *, cchar *) // 20.0 { char *galleryname = 0; F1_help_topic = "quick index"; if (checkpend("all")) return; // check nothing pending if (FGWM == 'G') { if (navi::galleryname && navi::gallerytype == GDIR) // save current gallery galleryname = zstrdup(navi::galleryname); } index_rebuild(2,0); // run incremental index if (galleryname) { gallery(galleryname,"init",0); // restore gallery and scroll position gallery_memory("get"); gallery(0,"paint",-1); m_viewmode(0,"G"); } return; } /********************************************************************************/ // Rebuild image index table from existing image index file // without searching for new and modified files. void index_rebuild_old() { using namespace index_names; int ftf, cc, err, rec, rec2, Ntab; STATB statb; Fblock = 1; Findexvalid = 0; // read image index file and build table of index records cc = maximages * sizeof(xxrec_t *); xxrec_tab = (xxrec_t **) zmalloc(cc); // image index recs Ntab = 0; ftf = 1; while (true) { xxrec = read_xxrec_seq(ftf); // read curr. index recs if (! xxrec) break; err = stat(xxrec->file,&statb); if (err) continue; // skip missing files 20.0 if (! S_ISREG(statb.st_mode)) continue; xxrec_tab[Ntab] = xxrec; Ntab++; if (Ntab == maximages) { zmessageACK(Mwin,"exceeded max. images: %d \n",maximages); Fblock = 0; quitxx(); // unconditional exit 20.05 } zmainloop(100); } // sort index recs in order of file name and file mod date if (Ntab) HeapSort((char **) xxrec_tab,Ntab,index_compare); // replace older recs with newer (appended) recs now sorted together if (Ntab) { for (rec = 0, rec2 = 1; rec2 < Ntab; rec2++) { if (strmatch(xxrec_tab[rec]->file,xxrec_tab[rec2]->file)) xxrec_tab[rec] = xxrec_tab[rec2]; else { rec++; xxrec_tab[rec] = xxrec_tab[rec2]; } } Ntab = rec + 1; // new count } Nxxrec = Ntab; if (Nxxrec) Findexvalid = 1; // index OK but missing new files Fblock = 0; // unblock return; } /********************************************************************************/ // move fotoxx home folder with all contained user files // does not return. starts new session if OK. void m_move_fotoxx_home(GtkWidget *, cchar *) { int yn, nn, err; FILE *fid; STATB statb; char oldhome[200], newhome[200]; char oldstring[200], newstring[200]; char *pp, configfile[200], buff[200], fotoxxhome[100]; cchar *mess1 = E2X("Do you want to move Fotoxx home? \n" " from: %s \n to: %s"); cchar *mess3 = E2X("moving files ..."); F1_help_topic = "move fotoxx home"; if (checkpend("all")) return; // check nothing pending strncpy0(oldhome,get_zhomedir(),200); // get present home folder pp = oldhome + strlen(oldhome); // remove trailing '/' if present if (pp[-1] == '/') pp[-1] = 0; err = stat(oldhome,&statb); // check old home exists if (err) { zmessageACK(Mwin,"old fotoxx home: %s \n",strerror(errno)); zexit("exit fotoxx"); } if (! S_ISDIR(statb.st_mode)) { // check if a folder zmessageACK(Mwin,"old location is not a folder"); zexit("exit fotoxx"); } retry: pp = zgetfolder(E2X("new Fotoxx home folder"),MWIN,oldhome); // get new home folder if (! pp) return; strncpy0(newhome,pp,200); zfree(pp); pp = newhome + strlen(newhome); // remove trailing '/' if present if (pp[-1] == '/') pp[-1] = 0; err = stat(newhome,&statb); // check new home folder exists if (err) { zmessageACK(Mwin,strerror(errno)); goto retry; } if (! S_ISDIR(statb.st_mode)) { // check if a folder zmessageACK(Mwin,E2X("new location is not a folder")); goto retry; } if (strchr(newhome,' ')) { // disallow blanks in path name zmessageACK(Mwin,E2X("new location name contains a space")); goto retry; } if (! strcasestr(newhome,"fotoxx")) // add /fotoxx if not already 19.0 strncatv(newhome,200,"/fotoxx",null); if (strmatch(oldhome,newhome)) goto retry; yn = zmessageYN(Mwin,mess1,oldhome,newhome); // ask user to proceed if (! yn) goto retry; printz("%s \n",mess3); // "moving files ..." poptext_mouse(mess3,0,0,0,0); err = shell_ack("cp -R %s/* %s",oldhome,newhome); // copy all files from old home to new if (err) { zmessageACK(Mwin,"copy failed: %s \n",strerror(errno)); zexit("exit fotoxx"); } zmainsleep(2); poptext_mouse(0,0,0,0,0); snprintf(configfile,200,"%s/image_index/index_config",newhome); // read (new) index config file fid = fopen(configfile,"r"); if (! fid) { zmessageACK(Mwin,"index_config file: %s",strerror(errno)); m_quit(0,0); } while (true) { pp = fgets_trim(buff,200,fid,1); // look for record "thumbnails: /..." if (! pp) break; if (strmatchN(pp,"thumbnails: /",13)) break; } fclose(fid); if (! pp) { zmessageACK(Mwin,E2X("index_config file has no thumbnails folder")); m_quit(0,0); } snprintf(oldstring,200,"thumbnails: %s/thumbnails",oldhome); // if thumbnails folder inside, snprintf(newstring,200,"thumbnails: %s/thumbnails",newhome); // oldhome, change to newhome nn = zsed(configfile,oldstring,newstring,null); if (nn < 0) { zmessageACK(Mwin,E2X("update thumbnail folder failed")); m_quit(0,0); } if (nn == 0) zmessageACK(Mwin,E2X("thumbnails folder not changed")); else zmessageACK(Mwin,E2X("new thumbnails folder: %s/thumbnails"),newhome); snprintf(fotoxxhome,100,"%s/.fotoxx-home",getenv("HOME")); // write new ~/.fotoxx-home file fid = fopen(fotoxxhome,"w"); if (! fid) { zmessageACK(Mwin,"cannot write .fotoxx-home file: %s",strerror(errno)); m_quit(0,0); } fprintf(fid,"%s",newhome); fclose(fid); zmessageACK(Mwin,E2X("Fotoxx will restart")); new_session(0); // start fotoxx again zsleep(1); // delay before SIGTERM in quitxx() 19.0 quitxx(); // exit this session return; } /********************************************************************************/ // user preferences and settings dialog namespace preferences { cchar *startopt[8][2] = { { "recent", E2X("Recent Files Gallery") }, // fotoxx startup display options { "newest", E2X("Newest Files Gallery") }, { "specG", E2X("Specific Gallery") }, { "album", E2X("Album Gallery") }, { "prevG", E2X("Previous Gallery") }, { "prevF", E2X("Previous File") }, { "specF", E2X("Specific File") }, { "blank", E2X("Blank Window") } }; cchar *tiffopt[4][2] = { // TIFF file compression options 20.0 { "NONE", "1" }, { "LZW", "5" }, { "PACKBITS", "32773" }, { "DEFLATE", "8" } }; int NTO = 4; char *choosefile = 0; // file/gallery/album to display } // menu function void m_preferences(GtkWidget *, cchar *) { using namespace preferences; int preferences_dialog_event(zdialog *zd, cchar *event); int ii, zstat; zdialog *zd; char txrgb[20], pct_scale[40]; snprintf(pct_scale,40,E2X("%c of scale"),'%'); // prepare text "% of scale" 20.0 /*** _________________________________________________________________ | Preferences and Settings | | | | Startup Display: [________________|v] [browse] | (popup file chooser) | Background Colors: F-view [###] G-view [###] | | Menu Style: (o) Icons (o) Text (o) Both Icon size [__] | | Menu Colors: Text [###] Background [###] | | Dialog Font: [ Ubuntu Bold 11 ] [choose] | (popup font chooser) | Image Zoom Speed: [ 2 ] zooms per 2x size increase | | Image Pan Mode: (o) drag (o) scroll [x] fast | | JPEG file save quality: [ 90 ] (90 = high quality) | | TIFF file compression method [_1_] | 20.0 | PNG file compression level [_1_] | 20.0 | Curve Node Separation, Capture Range: [ 5 ] % of scale | | Map Marker Size: [ 8 ] pixels | | Show Images: (o) last version only (o) all images | | Image Position in Window: (o) centered (o) right side | | Image Index Level: | | [ 2 ] Fotoxx started directly (2) | 0/1/2 = none/old/old+search new | [ 1 ] Fotoxx started by file manager (1) | | RAW File Loader: (o) dcraw (o) RawTherapee | 20.0 | RAW Conversion Options: | 20.0 | [x] extend dynamic range for dim images | | [x] use embedded image as a guide | | RAW File Types: [ .arw .srf .sr2 .crw .cr2 .cr3 .dng .mdc ] | | Video File Types: [ .mp4 .mov .avi .wmv .mpeg .h264 .webm ] | | Video File Play Command: [ ffplay -loglevel 8 -autoexit |v] | shell command 20.0 | | | [done] [cancel] | |_________________________________________________________________| ***/ F1_help_topic = "preferences"; m_viewmode(0,"0"); // file view mode if (checkpend("all")) return; // check nothing pending Fblock = 1; zd = zdialog_new(E2X("Preferences and Settings"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbstart","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labstart","hbstart",E2X("Startup Display:"),"space=3"); zdialog_add_widget(zd,"label","space","hbstart",0,"space=5"); zdialog_add_widget(zd,"combo","startopt","hbstart",0,"space=5"); zdialog_add_widget(zd,"button","browse","hbstart",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hbbg","dialog",0,"space=2"); // F-view and G-view background color zdialog_add_widget(zd,"label","labbg","hbbg",E2X("Background Colors:"),"space=5"); zdialog_add_widget(zd,"label","space","hbbg",0,"space=5"); zdialog_add_widget(zd,"label","labfbg","hbbg",E2X("F-View"),"space=5"); zdialog_add_widget(zd,"colorbutt","FBrgb","hbbg"); zdialog_add_widget(zd,"label","space","hbbg",0,"space=5"); zdialog_add_widget(zd,"label","labgbg","hbbg",E2X("G-View"),"space=5"); zdialog_add_widget(zd,"colorbutt","GBrgb","hbbg"); zdialog_add_widget(zd,"hbox","hbms","dialog",0,"space=2"); // menu style zdialog_add_widget(zd,"label","labms","hbms",E2X("Menu Style:"),"space=5"); zdialog_add_widget(zd,"label","space","hbms",0,"space=5"); zdialog_add_widget(zd,"radio","icons","hbms",E2X("Icons"),"space=3"); zdialog_add_widget(zd,"radio","text","hbms",E2X("Text"),"space=3"); zdialog_add_widget(zd,"radio","both","hbms",E2X("Both"),"space=3"); zdialog_add_widget(zd,"label","space","hbms",0,"space=10"); zdialog_add_widget(zd,"label","labis","hbms",E2X("Icon size")); zdialog_add_widget(zd,"zspin","iconsize","hbms","26|64|1|32","space=3"); // 20.0 zdialog_add_widget(zd,"hbox","hbmt","dialog",0,"space=2"); // menu text and background color zdialog_add_widget(zd,"label","labmt","hbmt",E2X("Menu Colors:"),"space=5"); zdialog_add_widget(zd,"label","space","hbmt",0,"space=5"); zdialog_add_widget(zd,"label","labmb","hbmt",E2X("Text"),"space=5"); zdialog_add_widget(zd,"colorbutt","MFrgb","hbmt"); zdialog_add_widget(zd,"label","space","hbmt",0,"space=5"); zdialog_add_widget(zd,"label","labmb","hbmt",E2X("Background"),"space=5"); zdialog_add_widget(zd,"colorbutt","MBrgb","hbmt"); zdialog_add_widget(zd,"hbox","hbfont","dialog",0,"space=2"); // menu and dialog font zdialog_add_widget(zd,"label","labfont","hbfont",E2X("Dialog Font:"),"space=5"); zdialog_add_widget(zd,"label","space","hbfont",0,"space=5"); zdialog_add_widget(zd,"zentry","font","hbfont","Sans 10","size=20"); zdialog_add_widget(zd,"button","choosefont","hbfont",Bchoose,"space=5"); zdialog_add_widget(zd,"hbox","hbz","dialog",0,"space=2"); // image zoom options zdialog_add_widget(zd,"label","labips","hbz","Image Zoom Speed:","space=5"); zdialog_add_widget(zd,"label","space","hbz",0,"space=5"); zdialog_add_widget(zd,"zspin","zoomcount","hbz","1|8|1|2","size=3"); zdialog_add_widget(zd,"label","labz","hbz","zooms per 2x size increase","space=5"); zdialog_add_widget(zd,"hbox","hbp","dialog",0,"space=2"); // image pan options zdialog_add_widget(zd,"label","labpan","hbp","Image Pan Mode:","space=5"); zdialog_add_widget(zd,"label","space","hbp",0,"space=5"); zdialog_add_widget(zd,"radio","drag","hbp","drag","space=3"); zdialog_add_widget(zd,"radio","scroll","hbp","scroll","space=3"); zdialog_add_widget(zd,"check","fast","hbp","fast","space=5"); zdialog_add_widget(zd,"hbox","hbjpeg","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labqual","hbjpeg",E2X("JPEG file save quality:"),"space=5"); zdialog_add_widget(zd,"label","space","hbjpeg",0,"space=5"); zdialog_add_widget(zd,"zspin","jpegqual","hbjpeg","1|100|1|90"); zdialog_add_widget(zd,"label","labqual","hbjpeg",E2X("(90 = high quality)"),"space=10"); zdialog_add_widget(zd,"hbox","hbtiff","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labcomp","hbtiff",E2X("TIFF file compression method"),"space=5"); zdialog_add_widget(zd,"label","space","hbtiff",0,"space=5"); zdialog_add_widget(zd,"combo","tiffcomp","hbtiff","NONE"); zdialog_add_widget(zd,"label","labcomp","hbtiff",0,"space=10"); zdialog_add_widget(zd,"hbox","hbpng","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labcomp","hbpng",E2X("PNG file compression level"),"space=5"); zdialog_add_widget(zd,"label","space","hbpng",0,"space=5"); zdialog_add_widget(zd,"zspin","pngcomp","hbpng","1|9|1|1"); zdialog_add_widget(zd,"label","labcomp","hbpng",0,"space=10"); zdialog_add_widget(zd,"hbox","hbncap","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labncap","hbncap",E2X("Curve Node Separation, Capture Range:"),"space=5"); zdialog_add_widget(zd,"label","space","hbncap",0,"space=5"); zdialog_add_widget(zd,"zspin","nodecap","hbncap","3|20|1|5","size=3"); zdialog_add_widget(zd,"label","labncap","hbncap",pct_scale,"space=10"); zdialog_add_widget(zd,"hbox","hbmmk","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labmmk","hbmmk",E2X("Map Marker Size:"),"space=5"); zdialog_add_widget(zd,"label","space","hbmmk",0,"space=5"); zdialog_add_widget(zd,"zspin","map_dotsize","hbmmk","5|20|1|8","size=3"); zdialog_add_widget(zd,"label","labmmk","hbmmk","pixels","space=10"); zdialog_add_widget(zd,"hbox","hbshowver","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labshowver","hbshowver",E2X("Show Images (F-view, G-view):"),"space=5"); zdialog_add_widget(zd,"label","space","hbshowver",0,"space=5"); zdialog_add_widget(zd,"radio","lastver","hbshowver",E2X("last version only"),"space=5"); zdialog_add_widget(zd,"radio","allvers","hbshowver",E2X("all images"),"space=5"); zdialog_add_widget(zd,"hbox","hbshift","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labshift","hbshift",E2X("Image Position in Window:"),"space=5"); zdialog_add_widget(zd,"label","space","hbshift",0,"space=5"); zdialog_add_widget(zd,"radio","centered","hbshift",E2X("centered"),"space=5"); zdialog_add_widget(zd,"radio","shiftright","hbshift",E2X("right side"),"space=5"); zdialog_add_widget(zd,"hbox","hbxlev1","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labxlev1","hbxlev1",E2X("Image Index Level:"),"space=5"); zdialog_add_widget(zd,"hbox","hbxlev2","dialog"); zdialog_add_widget(zd,"label","space","hbxlev2",0,"space=12"); zdialog_add_widget(zd,"zspin","indexlev","hbxlev2","0|2|1|2","size=3"); zdialog_add_widget(zd,"label","labxlev2","hbxlev2",E2X("Fotoxx started directly (2)"),"space=5"); zdialog_add_widget(zd,"hbox","hbxlev3","dialog"); zdialog_add_widget(zd,"label","space","hbxlev3",0,"space=12"); zdialog_add_widget(zd,"zspin","fmindexlev","hbxlev3","0|2|1|1","size=3"); zdialog_add_widget(zd,"label","labfmxlev2","hbxlev3",E2X("Fotoxx started by file manager (1)"),"space=5"); zdialog_add_widget(zd,"hbox","hbraw","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labraw","hbraw",E2X("RAW File Loader:"),"space=5"); zdialog_add_widget(zd,"label","space","hbraw",0,"space=5"); zdialog_add_widget(zd,"radio","dcraw","hbraw","dcraw","space=3"); zdialog_add_widget(zd,"radio","rawtherapee","hbraw","RawTherapee","space=3"); zdialog_add_widget(zd,"hbox","hbrcon1","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labrcon1","hbrcon1",E2X("RAW Conversion Options:"),"space=5"); zdialog_add_widget(zd,"hbox","hbrcon2","dialog"); zdialog_add_widget(zd,"label","space","hbrcon2",0,"space=10"); zdialog_add_widget(zd,"check","autobright","hbrcon2",E2X("extend dynamic range for dim images"),"space=5"); zdialog_add_widget(zd,"hbox","hbrcon3","dialog"); zdialog_add_widget(zd,"label","space","hbrcon3",0,"space=10"); zdialog_add_widget(zd,"check","matchthumb","hbrcon3",E2X("use embedded image as a guide"),"space=5"); zdialog_add_widget(zd,"hbox","hbrtyp","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labrtyp","hbrtyp",E2X("RAW File Types:"),"space=5"); zdialog_add_widget(zd,"label","space","hbrtyp",0,"space=5"); zdialog_add_widget(zd,"zentry","rawtypes","hbrtyp",".raw .rw2"); zdialog_add_widget(zd,"hbox","hbvtyp","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labvtyp","hbvtyp",E2X("Video File Types:"),"space=5"); zdialog_add_widget(zd,"label","space","hbvtyp",0,"space=5"); zdialog_add_widget(zd,"zentry","videotypes","hbvtyp",".mp4 .mov"); zdialog_add_widget(zd,"hbox","hbvcom","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labvcom","hbvcom",E2X("Video File Play Command:"),"space=5"); zdialog_add_widget(zd,"label","space","hbvcom",0,"space=5"); zdialog_add_widget(zd,"comboE","videocomm","hbvcom",video_command,"size=30"); // stuff dialog fields with current settings for (ii = 0; ii < 8; ii++) zdialog_cb_app(zd,"startopt",startopt[ii][1]); // startup display option list for (ii = 0; ii < 8; ii++) { if (strmatch(startdisplay,startopt[ii][0])) // set current option zdialog_stuff(zd,"startopt",startopt[ii][1]); } snprintf(txrgb,20,"%d|%d|%d",FBrgb[0],FBrgb[1],FBrgb[2]); // F-view background color zdialog_stuff(zd,"FBrgb",txrgb); snprintf(txrgb,20,"%d|%d|%d",GBrgb[0],GBrgb[1],GBrgb[2]); // G-view background color zdialog_stuff(zd,"GBrgb",txrgb); zdialog_stuff(zd,"icons",0); // menu style zdialog_stuff(zd,"text",0); zdialog_stuff(zd,"both",0); if (strmatch(menu_style,"icons")) zdialog_stuff(zd,"icons",1); else if (strmatch(menu_style,"text")) zdialog_stuff(zd,"text",1); else zdialog_stuff(zd,"both",1); zdialog_stuff(zd,"iconsize",iconsize); // icon size snprintf(txrgb,20,"%d|%d|%d",MFrgb[0],MFrgb[1],MFrgb[2]); // menus font color zdialog_stuff(zd,"MFrgb",txrgb); snprintf(txrgb,20,"%d|%d|%d",MBrgb[0],MBrgb[1],MBrgb[2]); // menus background color zdialog_stuff(zd,"MBrgb",txrgb); zdialog_stuff(zd,"font",dialog_font); // curr. dialog font zdialog_stuff(zd,"zoomcount",zoomcount); // zooms for 2x increase zdialog_stuff(zd,"drag",0); // image drag/scroll options zdialog_stuff(zd,"scroll",0); zdialog_stuff(zd,"fast",0); if (Fdragopt == 1) zdialog_stuff(zd,"drag",1); // drag image (mouse direction) if (Fdragopt == 2) zdialog_stuff(zd,"scroll",1); // scroll image (opposite direction) if (Fdragopt == 3) zdialog_stuff(zd,"drag",1); // fast drag if (Fdragopt == 4) zdialog_stuff(zd,"scroll",1); // fast scroll if (Fdragopt >= 3) zdialog_stuff(zd,"fast",1); // fast option zdialog_stuff(zd,"jpegqual",jpeg_def_quality); // default jpeg file save quality for (ii = 0; ii < NTO; ii++) // TIFF file compression options 20.0 zdialog_cb_app(zd,"tiffcomp",tiffopt[ii][0]); for (ii = 0; ii < NTO; ii++) // set current option 20.0 if (tiff_comp_method == atoi(tiffopt[ii][1])) zdialog_stuff(zd,"tiffcomp",tiffopt[ii][0]); zdialog_stuff(zd,"pngcomp",png_comp_level); // current PNG compression level 20.0 zdialog_stuff(zd,"nodecap",splcurve_minx); // edit curve min. node distance zdialog_stuff(zd,"map_dotsize",map_dotsize); // map dot size if (Flastversion) { zdialog_stuff(zd,"lastver",1); // prev/next shows last version only zdialog_stuff(zd,"allvers",0); } else { zdialog_stuff(zd,"lastver",0); // prev/next shows all versions zdialog_stuff(zd,"allvers",1); } if (Fshiftright) { zdialog_stuff(zd,"shiftright",1); // shift image to window right side zdialog_stuff(zd,"centered",0); } else { zdialog_stuff(zd,"shiftright",0); // keep image centered in window zdialog_stuff(zd,"centered",1); } zdialog_stuff(zd,"indexlev",Findexlev); // index level, always zdialog_stuff(zd,"fmindexlev",FMindexlev); // index level, file manager call if (Frawloader == 1) { // RAW file loader option 20.0 zdialog_stuff(zd,"dcraw",1); zdialog_stuff(zd,"rawtherapee",0); } else { zdialog_stuff(zd,"dcraw",0); zdialog_stuff(zd,"rawtherapee",1); } zdialog_stuff(zd,"autobright",Fautobright); // RAW loader, auto-brighten option 20.0 zdialog_stuff(zd,"matchthumb",Fmatchthumb); // " " match embedded image color 20.0 zdialog_stuff(zd,"rawtypes",RAWfiletypes); // RAW file types zdialog_stuff(zd,"videotypes",VIDEOfiletypes); // VIDEO file types zdialog_cb_app(zd,"videocomm",video_command); // video player options 20.0 zdialog_cb_app(zd,"videocomm","gst-launch-1.0 playbin uri=file://"); zdialog_cb_app(zd,"videocomm","ffplay -loglevel 8 -autoexit "); zdialog_cb_app(zd,"videocomm","totem "); zdialog_cb_app(zd,"videocomm","vlc "); zdialog_cb_app(zd,"videocomm","firefox "); zdialog_cb_app(zd,"videocomm","chromium-browser "); // run dialog and wait for completion zdialog_resize(zd,500,600); zdialog_run(zd,preferences_dialog_event,"parent"); // run dialog and wait for completion zstat = zdialog_wait(zd); zdialog_free(zd); Fblock = 0; if (zstat == 1) { new_session(0); // start new session if changes made zsleep(1); // delay before SIGTERM in quitxx() 19.0 quitxx(); } return; } // preferences dialog event function int preferences_dialog_event(zdialog *zd, cchar *event) { using namespace preferences; int ii, jj, nn; char *pp, temp[200]; cchar *ppc; char txrgb[20]; GtkWidget *font_dialog; if (zd->zstat && zd->zstat != 1) return 1; // cancel if (strmatch(event,"browse")) // browse for startup folder or file { zdialog_fetch(zd,"startopt",temp,200); // get startup option for (ii = 0; ii < 8; ii++) { if (strmatch(temp,startopt[ii][1])) { zfree(startdisplay); startdisplay = zstrdup(startopt[ii][0]); } } if (! choosefile && topfolders[0]) // set a default location choosefile = zstrdup(topfolders[0]); if (strmatch(startdisplay,"specG")) { // specific gallery pp = zgetfile(E2X("Select startup folder"),MWIN,"folder",choosefile); if (! pp) return 1; zfree(choosefile); choosefile = pp; } if (strmatch(startdisplay,"specF")) { // specific image file pp = zgetfile(E2X("Select startup image file"),MWIN,"file",choosefile); if (! pp) return 1; zfree(choosefile); choosefile = pp; } if (strmatch(startdisplay,"album")) { // specific album pp = zgetfile(E2X("Select startup album"),MWIN,"file",albums_folder); if (! pp) return 1; zfree(choosefile); choosefile = pp; } } if (strmatch(event,"choosefont")) // choose menu/dialog font { zdialog_fetch(zd,"font",temp,200); font_dialog = gtk_font_chooser_dialog_new(E2X("select font"),MWIN); gtk_font_chooser_set_font(GTK_FONT_CHOOSER(font_dialog),temp); gtk_dialog_run(GTK_DIALOG(font_dialog)); pp = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(font_dialog)); gtk_widget_destroy(font_dialog); if (! pp) return 1; zdialog_stuff(zd,"font",pp); zsetfont(pp); dialog_font = zstrdup(pp); g_free(pp); } if (strmatch(event,"rawtherapee")) { if (! Frawtherapeecli) { zmessageACK(Mwin,E2X("rawtherapee-cli (rawtherapee) is not installed")); zdialog_stuff(zd,"dcraw",1); zdialog_stuff(zd,"rawtherapee",0); Frawloader = 1; } } // wait for dialog completion, process all inputs if (zd->zstat != 1) return 1; // wait for [done] button if (choosefile) // startup file/gallery/album { zdialog_fetch(zd,"startopt",temp,200); // get startup option for (ii = 0; ii < 8; ii++) { if (strmatch(temp,startopt[ii][1])) { zfree(startdisplay); startdisplay = zstrdup(startopt[ii][0]); } } if (strmatch(startdisplay,"album")) { // startup option = album if (startalbum) zfree(startalbum); startalbum = zstrdup(choosefile); } if (strmatch(startdisplay,"specG")) { // startup option = folder gallery if (startfolder) zfree(startfolder); startfolder = zstrdup(choosefile); if (image_file_type(startfolder) != FDIR) { zmessageACK(Mwin,E2X("startup folder is invalid")); zd->zstat = 0; return 1; } } if (strmatch(startdisplay,"specF")) { // startup option = image file if (startfile) zfree(startfile); startfile = zstrdup(choosefile); if (image_file_type(startfile) != IMAGE) { zmessageACK(Mwin,E2X("startup file is invalid")); zd->zstat = 0; return 1; } } } zdialog_fetch(zd,"FBrgb",txrgb,20); // F-view background color ppc = strField(txrgb,"|",1); if (ppc) FBrgb[0] = atoi(ppc); ppc = strField(txrgb,"|",2); if (ppc) FBrgb[1] = atoi(ppc); ppc = strField(txrgb,"|",3); if (ppc) FBrgb[2] = atoi(ppc); zdialog_fetch(zd,"GBrgb",txrgb,20); // G-view background color ppc = strField(txrgb,"|",1); if (ppc) GBrgb[0] = atoi(ppc); ppc = strField(txrgb,"|",2); if (ppc) GBrgb[1] = atoi(ppc); ppc = strField(txrgb,"|",3); if (ppc) GBrgb[2] = atoi(ppc); zdialog_fetch(zd,"icons",nn); // menu style = icons if (nn) { if (! strmatch(menu_style,"icons")) { zfree(menu_style); menu_style = zstrdup("icons"); } } zdialog_fetch(zd,"text",nn); // menu style = text if (nn) { if (! strmatch(menu_style,"text")) { zfree(menu_style); menu_style = zstrdup("text"); } } zdialog_fetch(zd,"both",nn); // menu style = icons + text if (nn) { if (! strmatch(menu_style,"both")) { zfree(menu_style); menu_style = zstrdup("both"); } } zdialog_fetch(zd,"iconsize",nn); // icon size if (nn != iconsize) { iconsize = nn; } zdialog_fetch(zd,"MFrgb",txrgb,20); // menu text color ppc = strField(txrgb,"|",1); if (ppc) MFrgb[0] = atoi(ppc); ppc = strField(txrgb,"|",2); if (ppc) MFrgb[1] = atoi(ppc); ppc = strField(txrgb,"|",3); if (ppc) MFrgb[2] = atoi(ppc); zdialog_fetch(zd,"MBrgb",txrgb,20); // menu background color ppc = strField(txrgb,"|",1); if (ppc) MBrgb[0] = atoi(ppc); ppc = strField(txrgb,"|",2); if (ppc) MBrgb[1] = atoi(ppc); ppc = strField(txrgb,"|",3); if (ppc) MBrgb[2] = atoi(ppc); zdialog_fetch(zd,"zoomcount",zoomcount); // zooms for 2x image size zoomratio = pow( 2.0, 1.0 / zoomcount); // 2.0, 1.4142, 1.2599, 1.1892 ... zdialog_fetch(zd,"drag",nn); // drag/scroll option if (nn) Fdragopt = 1; // 1/2 = drag/scroll else Fdragopt = 2; zdialog_fetch(zd,"fast",nn); // 3/4 = drag/scroll fast if (nn) Fdragopt += 2; zdialog_fetch(zd,"jpegqual",jpeg_def_quality); // JPEG file save quality zdialog_fetch(zd,"tiffcomp",temp,20); // TIFF file compression method for (ii = 0; ii < NTO; ii++) if (strmatch(temp,tiffopt[ii][0])) break; if (ii < NTO) tiff_comp_method = atoi(tiffopt[ii][1]); zdialog_fetch(zd,"pngcomp",png_comp_level); // PNG file compression level zdialog_fetch(zd,"nodecap",splcurve_minx); // edit curve min. node distance zdialog_fetch(zd,"map_dotsize",map_dotsize); // map dot size zdialog_fetch(zd,"lastver",Flastversion); // prev/next shows last version only zdialog_fetch(zd,"shiftright",Fshiftright); // shift image to right margin zdialog_fetch(zd,"indexlev",Findexlev); // index level, started directly zdialog_fetch(zd,"fmindexlev",FMindexlev); // ... started via file manager zdialog_fetch(zd,"dcraw",nn); // RAW loader selection 20.0 if (nn == 1) Frawloader = 1; else Frawloader = 2; zdialog_fetch(zd,"autobright",Fautobright); // RAW loader, auto-brighten option 20.0 zdialog_fetch(zd,"matchthumb",Fmatchthumb); // " " match embedded image color 20.0 zdialog_fetch(zd,"rawtypes",temp,200); // RAW file types, .raw .rw2 ... pp = zstrdup(temp,100); for (ii = jj = 0; temp[ii]; ii++) { // insure blanks between types if (temp[ii] == '.' && temp[ii-1] != ' ') pp[jj++] = ' '; pp[jj++] = temp[ii]; } if (pp[jj-1] != ' ') pp[jj++] = ' '; // insure 1 final blank pp[jj] = 0; if (RAWfiletypes) zfree(RAWfiletypes); RAWfiletypes = pp; zdialog_fetch(zd,"videotypes",temp,200); // VIDEO file types, .mp4 .mov ... pp = zstrdup(temp,100); for (ii = jj = 0; temp[ii]; ii++) { // insure blanks between types if (temp[ii] == '.' && temp[ii-1] != ' ') pp[jj++] = ' '; pp[jj++] = temp[ii]; } if (pp[jj-1] != ' ') pp[jj++] = ' '; // insure 1 final blank pp[jj] = 0; if (VIDEOfiletypes) zfree(VIDEOfiletypes); VIDEOfiletypes = pp; zdialog_fetch(zd,"videocomm",temp,200); // selected video command 20.0 zfree(video_command); video_command = zstrdup(temp); save_params(); // done, save modified parameters Fpaint2(); return 1; } /********************************************************************************/ // keyboard shortcuts namespace KBshortcutnames { zdialog *zd; int Nreserved = 14; // reserved shortcuts (hard coded) cchar *reserved[14] = { "F1", "F10", "F11", "Escape", "Delete", "Left", "Right", "Up", "Down", "First", "Last", "Page_Up", "Page_Down", "Z" }; kbsutab_t kbsutab2[maxkbsu]; // KB shortcuts list during editing int Nkbsu2; } // KB shortcuts menu function void m_KBshortcuts(GtkWidget *, cchar *) { using namespace KBshortcutnames; int KBshorts_dialog_event(zdialog *zd, cchar *event); void KBshortcuts_edit(); int zstat, ii; GtkWidget *widget; F1_help_topic = "KB shortcuts"; /*** ____________________________________________ | Keyboard Shortcuts | | | | Reserved Shortcuts | | Z Toggle 1x / fit window | | F1 User Guide, Context Help | | F10 Full Screen with menus | | F11 Full Screen without menus | | Escape Quit dialog, Quit Fotoxx | | Delete Delete/Trash | | Arrow keys Navigation | | Page keys Navigation | | Home/End Navigation | | | | Custom Shortcuts | | + Zoom+ | | - Zoom- | | F File View | | G Gallery View | | M Map View | | L List View | | R Rename | | K KB Shortcuts | | T Trim/Rotate | | ... ... | | | | [edit] [cancel] | |____________________________________________| ***/ zd = zdialog_new(E2X("Keyboard Shortcuts"),Mwin,Bedit,Bcancel,null); zdialog_add_widget(zd,"scrwin","scrlist","dialog",0,"space=5|expand"); zdialog_add_widget(zd,"text","shortlist","scrlist",0,"expand"); widget = zdialog_widget(zd,"shortlist"); // list fixed shortcuts textwidget_append(widget,1,E2X("Reserved Shortcuts \n")); textwidget_append(widget,0,E2X(" Z Toggle 1x / fit window \n")); textwidget_append(widget,0,E2X(" F1 User Guide, Context Help \n")); textwidget_append(widget,0,E2X(" F10 Full Screen with menus \n")); textwidget_append(widget,0,E2X(" F11 Full Screen without menus \n")); textwidget_append(widget,0,E2X(" Escape Quit dialog, Quit Fotoxx \n")); textwidget_append(widget,0,E2X(" Delete Delete/Trash \n")); textwidget_append(widget,0,E2X(" Arrow keys Navigation \n")); textwidget_append(widget,0,E2X(" Page keys Navigation \n")); textwidget_append(widget,0,E2X(" Home/End Navigation \n")); textwidget_append(widget,0,"\n"); textwidget_append(widget,1,"Custom Shortcuts \n"); for (ii = 0; ii < Nkbsu; ii++) // list custom shortcuts textwidget_append(widget,0," %-14s %s \n", kbsutab[ii].key, E2X(kbsutab[ii].menu)); zdialog_resize(zd,400,600); zdialog_run(zd,KBshorts_dialog_event,"save"); zstat = zdialog_wait(zd); zdialog_free(zd); if (zstat == 1) KBshortcuts_edit(); return; } // dialog event and completion function int KBshorts_dialog_event(zdialog *zd, cchar *event) { if (zd->zstat) zdialog_destroy(zd); return 1; } // KB shortcuts edit function void KBshortcuts_edit() { using namespace KBshortcutnames; void KBshorts_callbackfunc1(GtkWidget *widget, int line, int pos, int kbkey); void KBshorts_callbackfunc2(GtkWidget *widget, int line, int pos, int kbkey); int KBshorts_keyfunc(GtkWidget *dialog, GdkEventKey *event); int KBshorts_edit_dialog_event(zdialog *zd, cchar *event); int ii; GtkWidget *widget; char *sortlist[maxkbsf]; /*** _________________________________________________________________ | Edit KB Shortcuts | |_________________________________________________________________| | Alt+G Grid Lines | Blur | | Alt+U Undo | Bookmarks | | Alt+R Redo | Captions | | T Trim/Rotate | Color Depth | | V View Main | Color Sat | | Ctrl+Shift+V View All | Copy to Cache | | ... ... | Copy to Clipboard | | Done dialog button | Current Album | // dialog button KB shortcut | ... ... | ... | |_________________________________________|_______________________| | | | shortcut key: (enter key) (no selection) | | | | [add] [remove] [done] [cancel] | |_________________________________________________________________| ***/ zd = zdialog_new(E2X("Edit KB Shortcuts"),Mwin,Badd,Bdelete,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hblists","dialog",0,"expand"); zdialog_add_widget(zd,"scrwin","scrlist","hblists",0,"space=5|expand"); zdialog_add_widget(zd,"text","shortlist","scrlist",0,"expand"); zdialog_add_widget(zd,"scrwin","scrmenus","hblists",0,"expand"); zdialog_add_widget(zd,"text","menufuncs","scrmenus"); zdialog_add_widget(zd,"hbox","hbshort","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labshort","hbshort",E2X("shortcut key:"),"space=5"); zdialog_add_widget(zd,"label","shortkey","hbshort",E2X("(enter key)"),"size=10"); zdialog_add_widget(zd,"label","shortfunc","hbshort",E2X("(no selection)"),"space=5"); for (ii = 0; ii < Nkbsu; ii++) { // copy current shortcuts list kbsutab2[ii].key = zstrdup(kbsutab[ii].key); kbsutab2[ii].menu = zstrdup(kbsutab[ii].menu); } Nkbsu2 = Nkbsu; widget = zdialog_widget(zd,"shortlist"); // show shortcuts list in dialog, textwidget_clear(widget); // translated for (ii = 0; ii < Nkbsu2; ii++) textwidget_append(widget,0,"%-14s %s \n", kbsutab2[ii].key, E2X(kbsutab2[ii].menu)); textwidget_set_eventfunc(widget,KBshorts_callbackfunc1); // set mouse/KB event function for (ii = 0; ii < Nkbsf; ii++) // copy eligible shortcut funcs, sortlist[ii] = zstrdup(E2X(kbsftab[ii].menu)); // translated HeapSort(sortlist,Nkbsf); // sort copied list widget = zdialog_widget(zd,"menufuncs"); // clear dialog textwidget_clear(widget); for (ii = 0; ii < Nkbsf; ii++) // show sorted translated funcs list textwidget_append(widget,0,"%s\n",sortlist[ii]); textwidget_set_eventfunc(widget,KBshorts_callbackfunc2); // set mouse/KB event function widget = zdialog_widget(zd,"dialog"); // capture KB keys pressed G_SIGNAL(widget,"key-press-event",KBshorts_keyfunc,0); zdialog_resize(zd,600,400); zdialog_run(zd,KBshorts_edit_dialog_event,"save"); return; } // mouse callback function to select existing shortcut from list // and stuff into dialog "shortfunc" void KBshorts_callbackfunc1(GtkWidget *widget, int line, int pos, int kbkey) { using namespace KBshortcutnames; char *txline; char shortkey[20]; char shortfunc[60]; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } txline = textwidget_line(widget,line,1); // get clicked line if (! txline || ! *txline) return; textwidget_highlight_line(widget,line); strncpy0(shortkey,txline,14); // get shortcut key and menu strncpy0(shortfunc,txline+15,60); zfree(txline); strTrim(shortkey); strTrim(shortfunc); zdialog_stuff(zd,"shortkey",shortkey); // stuff into dialog zdialog_stuff(zd,"shortfunc",shortfunc); return; } // mouse callback function to select new shortcut function from menu list // and stuff into dialog "shortfunc" void KBshorts_callbackfunc2(GtkWidget *widget, int line, int pos, int kbkey) { using namespace KBshortcutnames; char *txline; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_docfile(Mwin,"userguide",F1_help_topic); return; } txline = textwidget_line(widget,line,1); // get clicked line if (! txline || ! *txline) return; textwidget_highlight_line(widget,line); zdialog_stuff(zd,"shortfunc",txline); // stuff into dialog zfree(txline); return; } // intercept KB key events, stuff into dialog "shortkey" int KBshorts_keyfunc(GtkWidget *dialog, GdkEventKey *event) { using namespace KBshortcutnames; int Ctrl = 0, Alt = 0, Shift = 0; int key, ii, cc; char keyname[20]; key = event->keyval; if (event->state & GDK_CONTROL_MASK) Ctrl = 1; if (event->state & GDK_SHIFT_MASK) Shift = 1; if (event->state & GDK_MOD1_MASK) Alt = 1; if (key == GDK_KEY_Escape) return 0; // pass escape (cancel) to zdialog if (key == GDK_KEY_F1) { // key is F1 (context help) KBevent(event); // send to main app return 1; } if (key >= GDK_KEY_F2 && key <= GDK_KEY_F9) { // key is F2 to F9 ii = key - GDK_KEY_F1; strcpy(keyname,"F1"); keyname[1] += ii; } else if (key > 255) return 1; // not a simple Ascii key else { *keyname = 0; // build input key combination if (Ctrl) strcat(keyname,"Ctrl+"); // [Ctrl+] [Alt+] [Shift+] key if (Alt) strcat(keyname,"Alt+"); if (Shift) strcat(keyname,"Shift+"); cc = strlen(keyname); keyname[cc] = toupper(key); // x --> X, Ctrl+x --> Ctrl+X keyname[cc+1] = 0; } for (ii = 0; ii < Nreserved; ii++) if (strmatch(keyname,reserved[ii])) break; if (ii < Nreserved) { zmessageACK(Mwin,E2X("\"%s\" Reserved, cannot be used"),keyname); Ctrl = Alt = 0; return 1; } zdialog_stuff(zd,"shortkey",keyname); // stuff key name into dialog zdialog_stuff(zd,"shortfunc",E2X("(no selection)")); // clear menu choice return 1; } // dialog event and completion function int KBshorts_edit_dialog_event(zdialog *zd, cchar *event) { using namespace KBshortcutnames; int KBshorts_edit_menufuncs_event(zdialog *zd, cchar *event); void KBshorts_callbackfunc2(GtkWidget *widget, int line, int pos, int kbkey); int ii, jj; GtkWidget *widget; char shortkey[20]; char shortfunc[60]; char kbfile[200]; FILE *fid = 0; if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) // add shortcut { zd->zstat = 0; // keep dialog active if (Nkbsu2 == maxkbsu) { zmessageACK(Mwin,"exceed %d shortcuts",maxkbsu); return 1; } zdialog_fetch(zd,"shortkey",shortkey,20); // get shortcut key and menu zdialog_fetch(zd,"shortfunc",shortfunc,60); // from dialog widgets if (*shortkey <= ' ' || *shortfunc <= ' ') return 0; for (ii = 0; ii < Nkbsu2; ii++) // find matching shortcut key in list if (strmatch(kbsutab2[ii].key,shortkey)) break; if (ii < Nkbsu2) { // if found, remove from list zfree(kbsutab2[ii].key); zfree(kbsutab2[ii].menu); for (jj = ii; jj < Nkbsu2; jj++) kbsutab2[jj] = kbsutab2[jj+1]; --Nkbsu2; } for (ii = 0; ii < Nkbsu2; ii++) // find matching shortcut func in list if (strmatch(shortfunc,E2X(kbsutab2[ii].menu))) break; if (ii < Nkbsu2) { // if found, remove from list zfree(kbsutab2[ii].key); zfree(kbsutab2[ii].menu); for (jj = ii; jj < Nkbsu2; jj++) kbsutab2[jj] = kbsutab2[jj+1]; --Nkbsu2; } for (ii = 0; ii < Nkbsf; ii++) // look up shortcut func in list if (strmatch(shortfunc,E2X(kbsftab[ii].menu))) break; // of eligible menu funcs if (ii == Nkbsf) return 1; strncpy0(shortfunc,kbsftab[ii].menu,60); // english menu func ii = Nkbsu2++; // add new shortcut to end of list kbsutab2[ii].key = zstrdup(shortkey); kbsutab2[ii].menu = zstrdup(shortfunc); widget = zdialog_widget(zd,"shortlist"); // clear shortcuts list textwidget_clear(widget); for (ii = 0; ii < Nkbsu2; ii++) // show updated shortcuts in dialog textwidget_append2(widget,0,"%-14s %s \n", kbsutab2[ii].key,E2X(kbsutab2[ii].menu)); return 1; } if (zd->zstat == 2) // remove shortcut { zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"shortkey",shortkey,20); // get shortcut key if (*shortkey <= ' ') return 0; for (ii = 0; ii < Nkbsu2; ii++) // find matching shortcut key in list if (strmatch(kbsutab2[ii].key,shortkey)) break; if (ii < Nkbsu2) { // if found, remove from list zfree(kbsutab2[ii].key); zfree(kbsutab2[ii].menu); for (jj = ii; jj < Nkbsu2; jj++) kbsutab2[jj] = kbsutab2[jj+1]; --Nkbsu2; } widget = zdialog_widget(zd,"shortlist"); // clear shortcuts list textwidget_clear(widget); for (ii = 0; ii < Nkbsu2; ii++) // show updated shortcuts in dialog textwidget_append2(widget,0,"%-14s %s \n", kbsutab2[ii].key,E2X(kbsutab2[ii].menu)); zdialog_stuff(zd,"shortkey",""); // clear entered key and menu zdialog_stuff(zd,"shortfunc",E2X("(no selection)")); return 1; } if (zd->zstat == 3) // done - save new shortcut list { zdialog_free(zd); // kill menu funcs list locale_filespec("user","KB-shortcuts20",kbfile); // update shortcuts file 20.0 fid = fopen(kbfile,"w"); if (! fid) { zmessageACK(Mwin,strerror(errno)); return 1; } for (ii = 0; ii < Nkbsu2; ii++) fprintf(fid,"%-14s %s \n",kbsutab2[ii].key,kbsutab2[ii].menu); fclose(fid); KBshortcuts_load(); // reload shortcuts list from file 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; char *key, *menu; FILE *fid; for (ii = 0; ii < Nkbsu; ii++) { // clear shortcuts data zfree(kbsutab[ii].key); zfree(kbsutab[ii].menu); } Nkbsu = 0; err = locale_filespec("user","KB-shortcuts20",kbfile); // 20.0 if (err) return 0; fid = fopen(kbfile,"r"); if (! fid) return 0; for (ii = 0; ii < maxkbsu; ) { 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; key = zstrdup(pp1); // shortcut key or combination pp1 = pp2 + 1; while (*pp1 && *pp1 == ' ') pp1++; if (! *pp1) continue; menu = zstrdup(pp1); // corresp. menu, English if (strstr(zdialog_button_shortcuts,menu)) // if a dialog button shortcut zdialog_KB_addshortcut(key,menu); // register with zdialog kbsutab[ii].key = zstrdup(key); // add to shortcuts table kbsutab[ii].menu = zstrdup(menu); ii++; } Nkbsu = ii; fclose(fid); return 0; } /********************************************************************************/ // show a brightness distribution graph - live update as image is edited namespace RGB_dist_names { GtkWidget *drawwin_dist, *drawwin_scale; // brightness distribution graph widgets int RGBW[4] = { 1, 1, 1, 0 }; // " colors: red/green/blue/white (all) } // menu function void m_RGB_dist(GtkWidget *, cchar *menu) // menu function { using namespace RGB_dist_names; int show_RGB_dist_dialog_event(zdialog *zd, cchar *event); GtkWidget *frdist, *frscale, *widget; zdialog *zd; m_viewmode(0,"F"); // file view mode 19.0 if (! curr_file) return; if (menu && strmatch(menu,"kill")) { if (zd_RGB_dist) zdialog_free(zd_RGB_dist); zd_RGB_dist = 0; return; } if (zd_RGB_dist) { // dialog already present gtk_widget_queue_draw(drawwin_dist); // refresh drawing windows return; } if (menu) F1_help_topic = "RGB distribution"; zd = zdialog_new(E2X("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(); // distribution drawing area gtk_container_add(GTK_CONTAINER(frdist),drawwin_dist); G_SIGNAL(drawwin_dist,"draw",RGB_dist_graph,RGBW); drawwin_scale = gtk_drawing_area_new(); // brightness scale under distribution gtk_container_add(GTK_CONTAINER(frscale),drawwin_scale); gtk_widget_set_size_request(drawwin_scale,300,12); G_SIGNAL(drawwin_scale,"draw",brightness_scale,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_RGB_dist_dialog_event,"save"); widget = zdialog_widget(zd,"dialog"); // stop focus on this window gtk_window_set_accept_focus(GTK_WINDOW(widget),0); zd_RGB_dist = zd; return; } // dialog event and completion function int show_RGB_dist_dialog_event(zdialog *zd, cchar *event) { using namespace RGB_dist_names; if (zd->zstat) { zdialog_free(zd); zd_RGB_dist = 0; return 1; } if (zstrstr("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 1; } // draw brightness distribution graph in drawing window void RGB_dist_graph(GtkWidget *drawin, cairo_t *cr, int *rgbw) { int bin, Nbins = 256, distribution[256][4]; // bin count, R/G/B/all int px, py, dx, dy; int ww, hh, winww, winhh; int ii, rgb, maxdist, bright; uint8 *pixi; if (! Fpxb) return; if (rgbw[0]+rgbw[1]+rgbw[2]+rgbw[3] == 0) return; 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++) distribution[bin][rgb] = 0; ww = Fpxb->ww; // image area within window hh = Fpxb->hh; for (ii = 0; ii < ww * hh; ii++) { 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 for (rgb = 0; rgb < 3; rgb++) { // get R/G/B brightness levels bright = pixi[rgb] * Nbins / 256.0; // scale 0 to Nbins-1 if (bright < 0 || bright > 255) { printz("pixel %d/%d: %d %d %d \n",px,py,pixi[0],pixi[1],pixi[2]); return; } ++distribution[bright][rgb]; } bright = (pixi[0] + pixi[1] + pixi[2]) * 0.333 * Nbins / 256.0; // R+G+B, 0 to Nbins-1 ++distribution[bright][3]; } 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 (distribution[bin][rgb] > maxdist) // which can be huge maxdist = distribution[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 * distribution[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 brightness_scale(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; } /********************************************************************************/ // 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 Xmag; // magnification, 1 - 5x int Mx, My; // mouse location, image space int Mrad; // mouse radius } // menu function void m_magnify(GtkWidget *, cchar *) { using namespace magnify_names; F1_help_topic = "magnify image"; cchar * mess = E2X("Drag mouse on image. \n" "Left click to cancel."); /*** __________________________ | Magnify Image | | | | Drag mouse on image. | | Left click to cancel. | | | | radius [_____] | | X-size [_____] | | | | [cancel] | |__________________________| ***/ m_viewmode(0,"F"); // file view mode 19.0 if (! curr_file) return; if (zd_magnify) { // toggle magnify mode zdialog_send_event(zd_magnify,"kill"); return; } else { zdialog *zd = zdialog_new(E2X("Magnify Image"),Mwin,Bcancel,null); zd_magnify = zd; zdialog_add_widget(zd,"label","labdrag","dialog",mess,"space=5"); zdialog_add_widget(zd,"hbox","hbr","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labr","hbr",Bradius,"space=5"); zdialog_add_widget(zd,"zspin","Mrad","hbr","50|500|10|200"); zdialog_add_widget(zd,"hbox","hbx","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labx","hbx",E2X("X-size"),"space=5"); zdialog_add_widget(zd,"zspin","Xmag","hbx","2|10|1|2"); zdialog_fetch(zd,"Mrad",Mrad); // initial mouse radius zdialog_fetch(zd,"Xmag",Xmag); // 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,"Mrad"); // initializations zdialog_send_event(zd,"Xmag"); } takeMouse(magnify_mousefunc,dragcursor); // connect mouse function return; } // dialog event and completion callback function int magnify_names::magnify_dialog_event(zdialog *zd, cchar *event) { using namespace magnify_names; if (strmatch(event,"kill")) zd->zstat = 1; // from slide show if (zd->zstat) { // terminate zd_magnify = 0; zdialog_free(zd); freeMouse(); return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(magnify_mousefunc,dragcursor); if (strmatch(event,"Mrad")) { zdialog_fetch(zd,"Mrad",Mrad); // new mouse radius return 1; } if (strmatch(event,"Xmag")) { zdialog_fetch(zd,"Xmag",Xmag); // new magnification return 1; } return 1; } // pixel paint mouse function void magnify_names::magnify_mousefunc() { using namespace magnify_names; static int ftf = 1; if (! curr_file) return; if (FGWM != 'F') return; if (! zd_magnify) return; if (Mxdown) Fpaint2(); // drag start, erase prior if any Mxdown = 0; if (Mxdrag || Mydrag) // drag in progress { Mx = Mxdrag; // save mouse position My = Mydrag; Mxdrag = Mydrag = 0; magnify_dopixels(ftf); // magnify pixels inside mouse gdk_window_set_cursor(gdkwin,blankcursor); ftf = 0; } else { if (! ftf) Fpaint2(); // refresh image ftf = 1; gdk_window_set_cursor(gdkwin,dragcursor); } return; } // Get pixels from mouse circle within full size image Fpxb, // magnify and move into Mpxb, paint. void magnify_names::magnify_dopixels(int ftf) { using namespace magnify_names; int Frad; // mouse radius, image space int Fx, Fy, Fww, Fhh; // mouse circle, enclosing box, Fpxb static int pFx, pFy, pFww, pFhh; PIXBUF *pxb1, *pxb2, *pxbx; int ww1, hh1, rs1, ww2, hh2, rs2; uint8 *pixels1, *pixels2, *pix1, *pix2; int nch = Fpxb->nc; int px1, py1, px2, py2; int xlo, xhi, ylo, yhi; int xc, yc, dx, dy; float R2, R2lim; cairo_t *cr; Frad = Mrad / Mscale; // keep magnify circle constant 20.0 // get box enclosing PRIOR mouse circle, restore those pixels if (! ftf) // continuation of mouse drag { pxb1 = gdk_pixbuf_new_subpixbuf(Fpxb->pixbuf,pFx,pFy,pFww,pFhh); // unmagnified pixels, Fpxb if (! pxb1) return; ww1 = pFww * Mscale; // scale to Mpxb hh1 = pFhh * Mscale; pxbx = gdk_pixbuf_scale_simple(pxb1,ww1,hh1,BILINEAR); g_object_unref(pxb1); pxb1 = pxbx; px1 = pFx * Mscale; // copy into Mpxb py1 = pFy * Mscale; gdk_pixbuf_copy_area(pxb1,0,0,ww1,hh1,Mpxb->pixbuf,px1,py1); g_object_unref(pxb1); } // get box enclosing current mouse circle in Fpxb Fx = Mx - Frad; // mouse circle, enclosing box Fy = My - Frad; // (Fpxb, 1x image) Fww = Fhh = Frad * 2; // clip current mouse box to keep within image if (Fx < 0) { Fww += Fx; Fx = 0; } if (Fy < 0) { Fhh += Fy; Fy = 0; } if (Fx + Fww > Fpxb->ww) Fww = Fpxb->ww - Fx; if (Fy + Fhh > Fpxb->hh) Fhh = Fpxb->hh - Fy; if (Fww <= 0 || Fhh <= 0) return; pFx = Fx; // save this box for next time pFy = Fy; pFww = Fww; pFhh = Fhh; // scale box for Mpxb, then magnify by Xmag pxb1 = gdk_pixbuf_new_subpixbuf(Fpxb->pixbuf,Fx,Fy,Fww,Fhh); if (! pxb1) return; ww1 = Fww * Mscale; hh1 = Fhh * Mscale; pxbx = gdk_pixbuf_scale_simple(pxb1,ww1,hh1,BILINEAR); g_object_unref(pxb1); pxb1 = pxbx; // unmagnified pixels scaled to Mpxb rs1 = gdk_pixbuf_get_rowstride(pxb1); pixels1 = gdk_pixbuf_get_pixels(pxb1); ww2 = ww1 * Xmag; hh2 = hh1 * Xmag; pxb2 = gdk_pixbuf_scale_simple(pxb1,ww2,hh2,BILINEAR); // corresp. magnified pixels rs2 = gdk_pixbuf_get_rowstride(pxb2); pixels2 = gdk_pixbuf_get_pixels(pxb2); // copy magnified pixels within mouse radius only xlo = (ww2 - ww1) / 2; // pxb2 overlap area with pxb1 xhi = ww2 - xlo; ylo = (hh2 - hh1) / 2; yhi = hh2 - ylo; xc = (Mx - Fx) * Mscale; // mouse center in pxb1 yc = (My - Fy) * Mscale; R2lim = Frad * Mscale; // mouse radius in pxb1 for (py2 = ylo; py2 < yhi; py2++) // loop pxb2 pixels for (px2 = xlo; px2 < xhi; px2++) { px1 = px2 - xlo; // corresp. pxb1 pixel py1 = py2 - ylo; if (px1 < 0 || px1 >= ww1) continue; if (py1 < 0 || py1 >= hh1) continue; dx = px1 - xc; dy = py1 - yc; R2 = sqrtf(dx * dx + dy * dy); if (R2 > R2lim) continue; // outside mouse radius pix1 = pixels1 + py1 * rs1 + px1 * nch; pix2 = pixels2 + py2 * rs2 + px2 * nch; memcpy(pix1,pix2,nch); } px1 = Fx * Mscale; // copy into Mpxb py1 = Fy * Mscale; gdk_pixbuf_copy_area(pxb1,0,0,ww1,hh1,Mpxb->pixbuf,px1,py1); g_object_unref(pxb1); g_object_unref(pxb2); cr = draw_context_create(gdkwin,draw_context); if (! cr) return; Fpaint4(Fx,Fy,Fww,Fhh,cr); draw_mousecircle(Mx,My,Frad,0,cr); draw_context_destroy(draw_context); return; } /********************************************************************************/ // find all duplicated files and create corresponding gallery of duplicates namespace duplicates_names { int thumbsize; int Nfiles; char **files; int Fallfiles, Fgallery; } // menu function void m_duplicates(GtkWidget *, const char *) { using namespace duplicates_names; int duplicates_dialog_event(zdialog *zd, cchar *event); void randomize(); PIXBUF *pxb; GError *gerror; uint8 *pixels, *pix1; uint8 *pixelsii, *pixelsjj, *pixii, *pixjj; FILE *fid = 0; zdialog *zd; int Ndups = 0; // image file and duplicate counts int thumbsize, pixdiff, pixcount; int zstat, ii, jj, kk, cc, err, ndup; int ww, hh, rs, px, py; int trgb, diff, sumdiff; int percent; char text[100], *pp; char tempfile[200], albumfile[200]; typedef struct // thumbnail data { int trgb; // mean RGB sum int ww, hh, rs; // pixel dimensions char *file; // file name uint8 *pixels; // image pixels } Tdata_t; Tdata_t **Tdata = 0; F1_help_topic = "find duplicates"; if (checkpend("all")) return; // check nothing pending m_viewmode(0,"G"); // gallery view mode 19.0 if (Findexvalid == 0) { zmessageACK(Mwin,Bnoindex); // no image index 20.0 return; } free_resources(); Fblock = 1; // randomize(); 1-shot /*** _______________________________________________ | Find Duplicate Images | | | | (o) all files (o) current gallery | | File count: nnnn | | | | thumbnail size [ 64 ] [calculate] | | pixel difference [ 2 ] pixel count [ 2 ] | | | | Thumbnails: nnnnnn nn% Duplicates: nnn nn% | | | | /topfolder/subfolder1/subfolder2/... | | imagefile.jpg | | | | [proceed] [cancel] | |_______________________________________________| ***/ zd = zdialog_new(E2X("Find Duplicate Images"),Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hbwhere","dialog",0,"space=5"); zdialog_add_widget(zd,"radio","allfiles","hbwhere",E2X("All files"),"space=3"); zdialog_add_widget(zd,"radio","gallery","hbwhere",E2X("Current gallery"),"space=8"); zdialog_add_widget(zd,"hbox","hbfiles","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labfiles","hbfiles",E2X("File count:"),"space=3"); zdialog_add_widget(zd,"label","filecount","hbfiles","0"); zdialog_add_widget(zd,"hbox","hbthumb","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labthumb","hbthumb",E2X("Thumbnail size"),"space=3"); zdialog_add_widget(zd,"zspin","thumbsize","hbthumb","32|512|16|256","space=3"); zdialog_add_widget(zd,"zbutton","calculate","hbthumb",Bcalculate,"space=5"); zdialog_add_widget(zd,"hbox","hbdiff","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labdiff","hbdiff",E2X("Pixel difference"),"space=3"); zdialog_add_widget(zd,"zspin","pixdiff","hbdiff","1|20|1|1","space=3"); zdialog_add_widget(zd,"label","space","hbdiff",0,"space=8"); zdialog_add_widget(zd,"label","labsum","hbdiff",E2X("Pixel count"),"space=3"); zdialog_add_widget(zd,"zspin","pixcount","hbdiff","1|999|1|1","space=3"); zdialog_add_widget(zd,"hbox","hbstats","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labthumbs1","hbstats","Thumbnails:","space=3"); zdialog_add_widget(zd,"label","thumbs","hbstats","0"); zdialog_add_widget(zd,"label","Tpct","hbstats","0%","space=3"); zdialog_add_widget(zd,"label","space","hbstats",0,"space=5"); zdialog_add_widget(zd,"label","labdups1","hbstats",E2X("Duplicates:"),"space=3"); zdialog_add_widget(zd,"label","dups","hbstats","0"); zdialog_add_widget(zd,"label","Dpct","hbstats","0%","space=3"); zdialog_add_widget(zd,"hbox","hbfolder","dialog",0,"space=3"); zdialog_add_widget(zd,"label","currfolder","hbfolder"," ","space=8"); zdialog_add_widget(zd,"hbox","hbfile","dialog"); zdialog_add_widget(zd,"label","currfile","hbfile"," ","space=8"); zdialog_stuff(zd,"allfiles",1); // default all files Fallfiles = 1; files = (char **) zmalloc(maximages * sizeof(char *)); for (ii = jj = 0; ii < Nxxrec; ii++) // count all files { pp = xxrec_tab[ii]->file; pp = image2thumbfile(pp); // omit those without a thumbnail if (! pp) continue; files[jj++] = pp; } Nfiles = jj; snprintf(text,20,"%d",Nfiles); // file count >> dialog zdialog_stuff(zd,"filecount",text); zdialog_run(zd,duplicates_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for user inputs if (zstat != 1) goto cleanup; // canceled if (Nfiles < 2) { zmessageACK(Mwin," <2 images"); 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(Tdata_t); // allocate memory for thumbnail data Tdata = (Tdata_t **) zmalloc(cc); memset(Tdata,0,cc); Ffuncbusy = 1; for (ii = 0; ii < Nfiles; ii++) // screen out thumbnails not { // matching an image file pp = thumb2imagefile(files[ii]); if (pp) zfree(pp); else { zfree(files[ii]); files[ii] = 0; } } for (ii = 0; ii < Nfiles; ii++) // read thumbnails into memory { if (Fkillfunc) goto cleanup; if (! files[ii]) continue; Tdata[ii] = (Tdata_t *) zmalloc(sizeof(Tdata_t)); Tdata[ii]->file = files[ii]; // thumbnail file files[ii] = 0; kk = thumbsize; gerror = 0; // read thumbnail >> pixbuf pxb = gdk_pixbuf_new_from_file_at_size(Tdata[ii]->file,kk,kk,&gerror); if (! pxb) { printz("file: %s \n %s",Tdata[ii]->file,gerror->message); zfree(Tdata[ii]->file); zfree(Tdata[ii]); Tdata[ii] = 0; continue; } ww = gdk_pixbuf_get_width(pxb); // pixbuf dimensions hh = gdk_pixbuf_get_height(pxb); rs = gdk_pixbuf_get_rowstride(pxb); pixels = gdk_pixbuf_get_pixels(pxb); Tdata[ii]->ww = ww; // thumbnail dimensions Tdata[ii]->hh = hh; Tdata[ii]->rs = rs; cc = rs * hh; Tdata[ii]->pixels = (uint8 *) zmalloc(cc); memcpy(Tdata[ii]->pixels,pixels,cc); // thumbnail pixels trgb = 0; // compute mean RGB sum for (py = 0; py < hh; py++) for (px = 0; px < ww; px++) { pix1 = pixels + py * rs + px * 3; trgb += pix1[0] + pix1[1] + pix1[2]; } Tdata[ii]->trgb = trgb / ww / hh; // thumbnail mean RGB sum g_object_unref(pxb); // bugfix 19.0 snprintf(text,100,"%d",ii); // stuff thumbs read into dialog zdialog_stuff(zd,"thumbs",text); percent = 100.0 * ii / Nfiles; // and percent read snprintf(text,20,"%02d %c",percent,'%'); zdialog_stuff(zd,"Tpct",text); zmainloop(); // keep GTK alive } for (ii = jj = 0; ii < Nfiles; ii++) // remove empty members of Tdata[] if (Tdata[ii]) Tdata[jj++] = Tdata[ii]; Nfiles = jj; // new count for (ii = 0; ii < Nfiles; ii++) // replace thumbnail filespecs { // with corresp. image filespecs if (! Tdata[ii]) continue; pp = thumb2imagefile(Tdata[ii]->file); zfree(Tdata[ii]->file); Tdata[ii]->file = pp; } snprintf(tempfile,200,"%s/duplicate_images",temp_folder); // open file for gallery output fid = fopen(tempfile,"w"); if (! fid) goto filerror; Ndups = 0; // total duplicates for (ii = 0; ii < Nfiles; ii++) // loop all thumbnails ii { zmainloop(); if (Fkillfunc) goto cleanup; percent = 100.0 * (ii+1) / Nfiles; // show percent processed snprintf(text,20,"%02d %c",percent,'%'); zdialog_stuff(zd,"Dpct",text); if (! Tdata[ii]) continue; // removed from list pp = strrchr(Tdata[ii]->file,'/'); if (! pp) continue; *pp = 0; zdialog_stuff(zd,"currfolder",Tdata[ii]->file); // update folder and file zdialog_stuff(zd,"currfile",pp+1); // in dialog *pp = '/'; trgb = Tdata[ii]->trgb; ww = Tdata[ii]->ww; hh = Tdata[ii]->hh; rs = Tdata[ii]->rs; pixelsii = Tdata[ii]->pixels; ndup = 0; // file duplicates for (jj = ii+1; jj < Nfiles; jj++) // loop all thumbnails jj { if (! Tdata[jj]) continue; // removed from list if (abs(trgb - Tdata[jj]->trgb) > 1) continue; // brightness not matching if (ww != Tdata[jj]->ww) continue; // size not matching if (hh != Tdata[jj]->hh) continue; pixelsjj = Tdata[jj]->pixels; sumdiff = 0; for (py = 0; py < hh; py++) for (px = 0; px < ww; px++) { pixii = pixelsii + py * rs + px * 3; pixjj = pixelsjj + py * rs + px * 3; diff = abs(pixii[0] - pixjj[0]) + abs(pixii[1] - pixjj[1]) + abs(pixii[2] - pixjj[2]); if (diff < pixdiff) continue; // pixels match within threshold sumdiff++; // count unmatched pixels if (sumdiff >= pixcount) { // if over threshold, py = hh; px = ww; } // break out both loops } if (sumdiff >= pixcount) continue; // thumbnails not matching if (ndup == 0) { fprintf(fid,"%s\n",Tdata[ii]->file); // first duplicate, output file name ndup++; Ndups++; } fprintf(fid,"%s\n",Tdata[jj]->file); // output duplicate image file name zfree(Tdata[jj]->file); // remove from list zfree(Tdata[jj]->pixels); zfree(Tdata[jj]); Tdata[jj] = 0; ndup++; Ndups++; snprintf(text,100,"%d",Ndups); // update total duplicates found zdialog_stuff(zd,"dups",text); zmainloop(); } } fclose(fid); fid = 0; if (Ndups == 0) { zmessageACK(Mwin,"0 duplicates"); goto cleanup; } navi::gallerytype = SEARCH; // generate gallery of duplicate images gallery(tempfile,"initF",0); gallery(0,"paint",0); // position at top m_viewmode(0,"G"); snprintf(albumfile,200,"%s/Duplicate Images",albums_folder); // save search results in the err = copyFile(tempfile,albumfile); // album "Duplicate Images" if (err) zmessageACK(Mwin,strerror(err)); cleanup: zdialog_free(zd); if (fid) fclose(fid); fid = 0; if (files) { for (ii = 0; ii < Nfiles; ii++) if (files[ii]) zfree(files[ii]); zfree(files); } if (Tdata) { for (ii = 0; ii < Nfiles; ii++) { if (! Tdata[ii]) continue; zfree(Tdata[ii]->file); zfree(Tdata[ii]->pixels); zfree(Tdata[ii]); } zfree(Tdata); } Ffuncbusy = 0; Fkillfunc = 0; Fblock = 0; return; filerror: zmessageACK(Mwin,"file error: %s",strerror(errno)); goto cleanup; } // dialog event and completion function int duplicates_dialog_event(zdialog *zd, cchar *event) { using namespace duplicates_names; double freemem, cachemem, reqmem; char text[20], *pp, *pp2; int nn, ii, jj; if (strmatch(event,"allfiles")) { zdialog_fetch(zd,"allfiles",nn); Fallfiles = nn; if (! Fallfiles) return 1; for (ii = jj = 0; ii < Nxxrec; ii++) // count all files { pp = xxrec_tab[ii]->file; pp = image2thumbfile(pp); // omit those without thumbnail if (! pp) continue; files[jj++] = pp; } Nfiles = jj; snprintf(text,20,"%d",Nfiles); // file count >> dialog zdialog_stuff(zd,"filecount",text); return 1; } if (strmatch(event,"gallery")) { zdialog_fetch(zd,"gallery",nn); Fgallery = nn; if (! Fgallery) return 1; for (ii = jj = 0; ii < navi::Nfiles; ii++) // scan current gallery { pp = gallery(0,"get",ii); if (! pp) break; if (*pp == '!') { // skip folders zfree(pp); continue; } pp2 = image2thumbfile(pp); // get corresp. thumbnail file zfree(pp); if (! pp2) continue; files[jj++] = pp2; // save thumbnail } Nfiles = jj; snprintf(text,20,"%d",Nfiles); // file count >> dialog zdialog_stuff(zd,"filecount",text); return 1; } if (strmatch(event,"calculate")) // calculate thumbnail size { zd->zstat = 0; // keep dialog active parseprocfile("/proc/meminfo","MemFree:",&freemem,0); // get amount of free memory parseprocfile("/proc/meminfo","Cached:",&cachemem,0); freemem += cachemem; // KB freemem *= 1024; for (thumbsize = 16; thumbsize <= 512; thumbsize += 8) { // find largest thumbnail size reqmem = 0.7 * thumbsize * thumbsize * 3 * Nfiles; // that probably works if (reqmem > freemem) break; } thumbsize -= 8; // biggest size that fits if (thumbsize < 16) { zmessageACK(Mwin,E2X("too many files, cannot continue")); return 1; } zdialog_stuff(zd,"thumbsize",thumbsize); // stuff into dialog return 1; } if (! zd->zstat) return 1; // wait for user input if (zd->zstat != 1) Fkillfunc = 1; // [cancel] or [x] return 1; // [proceed] } // Make small random changes to all images. // Used for testing and benchmarking Find Duplicates. void randomize() { using namespace duplicates_names; char *file; int px, py; int ii, jj, kk; float *pixel; for (ii = 0; ii < Nxxrec; ii++) // loop all files { if (drandz() > 0.95) continue; // leave 5% duplicates zmainloop(); // keep GTK alive file = zstrdup(xxrec_tab[ii]->file); if (! file) continue; printf(" %d %s \n",ii,file); // log progress f_open(file); // open and read file E0pxm = PXM_load(file,1); if (! E0pxm) continue; jj = 2 + 49 * drandz(); // random 2-50 pixels for (kk = 0; kk < jj; kk++) { px = E0pxm->ww * drandz(); // random pixel py = E0pxm->hh * drandz(); pixel = PXMpix(E0pxm,px,py); pixel[0] = pixel[1] = pixel[2] = 0; // RGB = black } f_save(file,"jpg",8,0,1); // write file PXM_free(E0pxm); zfree(file); } return; } /********************************************************************************/ // Show RGB values for 1-9 pixels selected with mouse-clicks. // Additional pixel position tracks active mouse position void show_RGB_mousefunc(); int show_RGB_timefunc(void *); zdialog *RGBSzd; int RGBSpixel[10][2]; // 0-9 clicked pixels + current mouse int RGBSnpix = 0; // no. clicked pixels, 0-9 int RGBSdelta = 0; // abs/delta mode int RGBSlabels = 0; // pixel labels on/off void m_show_RGB(GtkWidget *, cchar *) { int show_RGB_event(zdialog *zd, cchar *event); cchar *mess = E2X("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"; m_viewmode(0,"F"); // file view mode 19.0 if (! curr_file) return; // no image file if (! E0pxm && ! E1pxm && ! E3pxm) { E0pxm = PXM_load(curr_file,1); // never edited if (! E0pxm) return; // get poss. 16-bit file 19.5 curr_file_bpc = f_load_bpc; } /*** _________________________________________ | | | Click image to select pixels. | | [x] delta [x] labels | | | // remove EV option 19.2 | 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 | | xxxxx xxxxx xxxxx xxxxx xxxxx | | | | [clear] [done] | |_________________________________________| ***/ RGBSnpix = 0; // no clicked pixels yet RGBSlabels = 0; // no labels yet 19.5 if (RGBSzd) zdialog_free(RGBSzd); // delete previous if any zdialog *zd = zdialog_new(E2X("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,"vbox","vbdat","dialog",0,"space=5"); // vbox for pixel values zdialog_add_widget(zd,"hbox","hbpix","vbdat"); zdialog_add_widget(zd,"label","labheader","hbpix",header); // Pixel Red Green Blue zdialog_labelfont(zd,"labheader","monospace 9",header); for (ii = 0; ii < 10; ii++) { // 10 hbox's with 10 labels 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,dotcursor); // 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) { if (zd->zstat) { if (zd->zstat == 1) { // clear zd->zstat = 0; // keep dialog active RGBSnpix = 0; // clicked pixel count = 0 erase_toptext(102); // erase labels on image } else { // done or kill freeMouse(); // disconnect mouse function zdialog_free(RGBSzd); // kill dialog RGBSzd = 0; erase_toptext(102); } Fpaint2(); return 0; // 19.0 } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(show_RGB_mousefunc,dotcursor); // 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,E2X("Edit function must be active")); } } if (strmatch(event,"labels")) // get labels on/off zdialog_fetch(zd,"labels",RGBSlabels); return 0; } // mouse function // fill table positions 0-8 with last clicked pixel positions and RGB data // next table position tracks current mouse position and RGB data 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 return; // must have E0/E1/E3 19.5 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 clicked pixel } ii = RGBSnpix; // labeled position to fill, 0-8 RGBSpixel[ii][0] = Mxclick; // save newest pixel RGBSpixel[ii][1] = Myclick; RGBSnpix++; // count is 1-9 } ii = RGBSnpix; // fill last position from active mouse RGBSpixel[ii][0] = Mxposn; RGBSpixel[ii][1] = Myposn; return; } // timer function // display RGB values for last 0-9 clicked pixels and current mouse position int show_RGB_timefunc(void *arg) { static char label[9][4] = { " A ", " B ", " C ", " D ", " E ", // labels A-I for last 0-9 clicked pixels " F ", " G ", " H ", " I " }; PXM *pxm = 0; int ii, jj, px, py; int ww, hh; float red3, green3, blue3; float *ppixa, *ppixb; char text[100], pixx[8] = "pixx"; if (! RGBSzd) return 0; // user quit, cancel timer if (! curr_file) return 0; if (! E0pxm && ! E1pxm && ! E3pxm) { E0pxm = PXM_load(curr_file,1); if (! E0pxm) return 0; // get poss. 16-bit file curr_file_bpc = f_load_bpc; } if (E3pxm) pxm = E3pxm; // report image being edited else if (E1pxm) pxm = E1pxm; else if (E0pxm) pxm = E0pxm; else return 0; if (RGBSdelta && ! E3pxm) { RGBSdelta = 0; // delta mode only if edit active zdialog_stuff(RGBSzd,"delta",RGBSdelta); // update dialog 19.2 } ww = pxm->ww; hh = pxm->hh; for (ii = 0; ii < RGBSnpix; ii++) // 0-9 clicked pixels 19.2 { 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 < RGBSnpix + 1; jj++) { RGBSpixel[jj-1][0] = RGBSpixel[jj][0]; // remove pixel outside limits RGBSpixel[jj-1][1] = RGBSpixel[jj][1]; // and pack the remaining down } // include last+1 = curr. mouse position ii--; RGBSnpix--; } 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"); } } for (ii = 0; ii < 10; ii++) // loop positions 0 to 9 { pixx[3] = '0' + ii; // widget names "pix0" ... "pix9" if (ii > RGBSnpix) { // no pixel there yet zdialog_stuff(RGBSzd,pixx,""); // blank report line continue; } px = RGBSpixel[ii][0]; // next pixel to report py = RGBSpixel[ii][1]; ppixa = PXMpix(pxm,px,py); // get pixel RGB values red3 = ppixa[0]; green3 = ppixa[1]; blue3 = ppixa[2]; if (RGBSdelta) { // delta RGB for edited image 19.2 ppixb = PXMpix(E1pxm,px,py); // "before" image E1 red3 -= ppixb[0]; green3 -= ppixb[1]; blue3 -= ppixb[2]; } if (ii == RGBSnpix) // last table position snprintf(text,100," %5d %5d ",px,py); // mouse pixel, format " xxxx yyyy" else snprintf(text,100," %c %5d %5d ",'A'+ii,px,py); // clicked pixel, format " A xxxx yyyy" snprintf(text+14,86," %6.2f %6.2f %6.2f ",red3,green3,blue3); zdialog_labelfont(RGBSzd,pixx,"monospace 9",text); } Fpaint2(); return 1; } /********************************************************************************/ // convert color profile of current image editfunc EFcolorprof; char ICCprofilename[100]; // new color profile name char colorprof1[200] = "/usr/share/color/icc/colord/AdobeRGB1998.icc"; char colorprof2[200] = "/usr/share/color/icc/colord/sRGB.icc"; // menu function void m_color_profile(GtkWidget *, cchar *menu) { int colorprof_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int err; cchar *exifkey[2], *exifdata[2]; F1_help_topic = "color profile"; m_viewmode(0,"0"); // file view mode 19.0 EFcolorprof.menuname = menu; EFcolorprof.menufunc = m_color_profile; EFcolorprof.funcname = "color_profile"; EFcolorprof.Frestart = 1; // allow restart EFcolorprof.Fscript = 1; // scripting supported if (! edit_setup(EFcolorprof)) return; // setup edit *ICCprofilename = 0; // no color profile change /*** ________________________________________________________ | Change Color Profile | | | | input profile [___________________________] [Browse] | | output profile [___________________________] [Browse] | | | | [Apply] [Done] [Cancel] | |________________________________________________________| ***/ zd = zdialog_new(E2X("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",E2X("input profile"),"space=5"); zdialog_add_widget(zd,"zentry","prof1","hb1",0,"expand|size=30"); zdialog_add_widget(zd,"button","butt1","hb1",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); zdialog_add_widget(zd,"label","lab2","hb2",E2X("output profile"),"space=5"); zdialog_add_widget(zd,"zentry","prof2","hb2",0,"expand|size=30"); zdialog_add_widget(zd,"button","butt2","hb2",Bbrowse,"space=5"); zdialog_stuff(zd,"prof1",colorprof1); zdialog_stuff(zd,"prof2",colorprof2); zdialog_run(zd,colorprof_dialog_event,"save"); // run dialog, parallel zdialog_wait(zd); // wait for completion if (! *ICCprofilename) return; // no color profile change m_file_save_version(0,0); // save as new version and re-open shell_quiet("rm -f %s/undo_*",temp_folder); // remove undo/redo files URS_pos = URS_max = 0; // reset undo/redo stack exifkey[0] = exif_colorprof2_key; // remove embedded color profile exifdata[0] = ""; exifkey[1] = exif_colorprof1_key; // set new color profile name exifdata[1] = ICCprofilename; err = exif_put(curr_file,exifkey,exifdata,2); if (err) zmessageACK(Mwin,E2X("Unable to change EXIF color profile")); zmessageACK(Mwin,E2X("automatic new version created")); return; } // dialog event and completion callback function int colorprof_dialog_event(zdialog *zd, cchar *event) { cchar *title = E2X("color profile"); char *file; float *fpix1, *fpix2; float f256 = 1.0 / 256.0; uint Npix, nn; cmsHTRANSFORM cmsxform; cmsHPROFILE cmsprof1, cmsprof2; if (strmatch(event,"apply")) zd->zstat = 1; // from script if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"butt1")) { zdialog_fetch(zd,"prof1",colorprof1,200); // select input profile file = zgetfile(title,MWIN,"file",colorprof1); if (! file) return 1; zdialog_stuff(zd,"prof1",file); zfree(file); } if (strmatch(event,"butt2")) { zdialog_fetch(zd,"prof2",colorprof2,200); // select output profile file = zgetfile(title,MWIN,"file",colorprof2); if (! file) return 1; zdialog_stuff(zd,"prof2",file); zfree(file); } if (! zd->zstat) return 1; // wait for user completion if (zd->zstat == 1) zd->zstat = 0; // [apply] - keep dialog open if (zd->zstat) { if (zd->zstat == 2 && CEF->Fmods) edit_done(0); // commit edit else { edit_cancel(0); // discard edit *ICCprofilename = 0; // no ICC profile change } 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,E2X("unknown cms profile %s"),colorprof1); return 1; } cmsprof2 = cmsOpenProfileFromFile(colorprof2,"r"); if (! cmsprof2) { zmessageACK(Mwin,E2X("unknown cms profile %s"),colorprof2); return 1; } // calculate the color space transformation table Ffuncbusy = 1; zmainsleep(0.2); cmsxform = cmsCreateTransform(cmsprof1,TYPE_RGB_FLT,cmsprof2,TYPE_RGB_FLT,INTENT_PERCEPTUAL,0); if (! cmsxform) { zmessageACK(Mwin,"cmsCreateTransform() failed"); Ffuncbusy = 0; return 1; } fpix1 = E0pxm->pixels; // input and output pixels fpix2 = E3pxm->pixels; Npix = E0pxm->ww * E0pxm->hh; for (uint ii = 0; ii < 3 * Npix; ii++) // rescale to range 0 - 0.9999 fpix2[ii] = f256 * fpix1[ii]; while (Npix) // convert image pixels { nn = Npix; if (nn > 100000) nn = 100000; // do 100K per call cmsDoTransform(cmsxform,fpix2,fpix2,nn); // speed: 3 megapixels/sec for 3 GHz CPU fpix2 += nn * 3; Npix -= nn; zmainloop(20); // keep GTK alive } fpix2 = E3pxm->pixels; Npix = E0pxm->ww * E0pxm->hh; for (uint ii = 0; ii < 3 * Npix; ii++) { // rescale back to 0 - 255.99 fpix2[ii] = fpix2[ii] * 256.0; if (fpix2[ii] > 255.9) fpix2[ii] = 255.9; if (fpix2[ii] < 0) fpix2[ii] = 0; // compensate cms bug } cmsInfoType it = (cmsInfoType) 0; cmsGetProfileInfoASCII(cmsprof2,it,"en","US",ICCprofilename,100); // new color profile name cmsDeleteTransform(cmsxform); // free resources cmsCloseProfile(cmsprof1); cmsCloseProfile(cmsprof2); Ffuncbusy = 0; CEF->Fmods++; // image is modified CEF->Fsaved = 0; Fpaint2(); // update window image return 1; } /********************************************************************************/ // printer color calibration tool namespace calibprint { int dialog_event(zdialog *zd, cchar *event); void printchart(); void scanchart(); void fixchart(); void processchart(); // parameters for RGB step size of 23: 0 23 46 69 ... 253 (253 --> 255) // NC colors per RGB dimension (12) (counting both 0 and 255) // step size from 16 to 23 // CS color step size (23) // TS tile size in pixels (70) // ROWS chart rows (50) ROWS x COLS must be >= NC*NC*NC // COLS chart columns (35) #define NC 12 #define CS 23 #define TS 70 #define ROWS 50 #define COLS 35 #define NC2 (NC*NC) #define NC3 (NC*NC*NC) int RGBvals[NC]; int Ntiles = NC3; int chartww = COLS * TS; // chart image size int charthh = ROWS * TS; int margin = 80; // chart margins char printchartfile[200]; char scanchartfile[200]; } // menu function void m_calibrate_printer(GtkWidget *, cchar *menu) { using namespace calibprint; zdialog *zd; cchar *title = E2X("Calibrate Printer"); F1_help_topic = "calibrate printer"; m_viewmode(0,"F"); // file view mode 19.0 for (int ii = 0; ii < NC; ii++) // construct RGBvals table RGBvals[ii] = CS * ii; RGBvals[NC-1] = 255; // set last value = 255 /*** ______________________________________ | Calibrate Printer | | | | (o) print color chart | | (o) scan and save color chart | | (o) align and trim color chart | | (o) open and process color chart | | (o) print image with revised colors | | | | [Proceed] [Cancel] | |______________________________________| ***/ zd = zdialog_new(title,Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"radio","printchart","dialog",E2X("print color chart")); zdialog_add_widget(zd,"radio","scanchart","dialog",E2X("scan and save color chart")); zdialog_add_widget(zd,"radio","fixchart","dialog",E2X("align and trim color chart")); zdialog_add_widget(zd,"radio","processchart","dialog",E2X("open and process color chart")); zdialog_add_widget(zd,"radio","printimage","dialog",E2X("print image with revised colors")); zdialog_stuff(zd,"printchart",1); zdialog_stuff(zd,"scanchart",0); zdialog_stuff(zd,"fixchart",0); zdialog_stuff(zd,"processchart",0); zdialog_stuff(zd,"printimage",0); zdialog_resize(zd,250,0); zdialog_run(zd,dialog_event,"parent"); return; } // dialog event and completion function int calibprint::dialog_event(zdialog *zd, cchar *event) { using namespace calibprint; int nn; F1_help_topic = "calibrate printer"; if (! zd->zstat) return 1; // wait for [proceed] or [cancel] if (zd->zstat != 1) { // cancel zdialog_free(zd); return 1; } zdialog_fetch(zd,"printchart",nn); if (nn) { printchart(); return 1; } zdialog_fetch(zd,"scanchart",nn); if (nn) { scanchart(); return 1; } zdialog_fetch(zd,"fixchart",nn); if (nn) { fixchart(); return 1; } zdialog_fetch(zd,"processchart",nn); if (nn) { processchart(); return 1; } zdialog_fetch(zd,"printimage",nn); if (nn) { print_calibrated(); return 1; } zd->zstat = 0; // keep dialog active return 1; } // generate and print the color chart void calibprint::printchart() { using namespace calibprint; int ii, cc, fww, fhh, rx, gx, bx; int row, col, px, py; int chartrs; uint8 *chartpixels, *pix1; PIXBUF *chartpxb; GError *gerror = 0; fww = chartww + 2 * margin; fhh = charthh + 2 * margin; chartpxb = gdk_pixbuf_new(GDKRGB,0,8,fww,fhh); // make chart image if (! chartpxb) { zmessageACK(Mwin,"cannot create pixbuf"); return; } chartpixels = gdk_pixbuf_get_pixels(chartpxb); // clear to white chartrs = gdk_pixbuf_get_rowstride(chartpxb); cc = fhh * chartrs; memset(chartpixels,255,cc); for (py = 0; py < charthh; py++) // fill chart tiles with colors for (px = 0; px < chartww; px++) { row = py / TS; col = px / TS; ii = row * COLS + col; if (ii >= Ntiles) break; // last chart positions may be unused rx = ii / NC2; gx = (ii - NC2 * rx) / NC; // RGB index values for tile ii bx = ii - NC2 * rx - NC * gx; pix1 = chartpixels + (py + margin) * chartrs + (px + margin) * 3; pix1[0] = RGBvals[rx]; pix1[1] = RGBvals[gx]; pix1[2] = RGBvals[bx]; } for (py = margin-10; py < fhh-margin+10; py++) // add green margin around tiles for (px = margin-10; px < fww-margin+10; px++) // for easier de-skew and trim { if (py > margin-1 && py < fhh-margin && px > margin-1 && px < fww-margin) continue; pix1 = chartpixels + py * chartrs + px * 3; pix1[0] = pix1[2] = 0; pix1[1] = 255; } snprintf(printchartfile,200,"%s/printchart.png",printer_color_folder); gdk_pixbuf_save(chartpxb,printchartfile,"png",&gerror,null); if (gerror) { zmessageACK(Mwin,gerror->message); return; } g_object_unref(chartpxb); zmessageACK(Mwin,"Print chart in vertical orientation without margins."); print_image_file(Mwin,printchartfile); // print the chart return; } // scan the color chart void calibprint::scanchart() { using namespace calibprint; zmessageACK(Mwin,E2X("Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/"),printer_color_folder); return; } // edit and fix the color chart void calibprint::fixchart() { using namespace calibprint; char *pp; zmessageACK(Mwin,E2X("Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY.")); pp = zgetfile("scanned color chart file",MWIN,"file",printer_color_folder,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,E2X("Open the trimmed color chart file")); pp = zgetfile("trimmed color chart file",MWIN,"file",printer_color_folder,1); if (! pp) return; strncpy0(scanchartfile,pp,200); chartpxb = gdk_pixbuf_new_from_file(scanchartfile,&gerror); // scanned chart without margins if (! chartpxb) { if (gerror) zmessageACK(Mwin,gerror->message); return; } chartww = gdk_pixbuf_get_width(chartpxb); charthh = gdk_pixbuf_get_height(chartpxb); chartpixels = gdk_pixbuf_get_pixels(chartpxb); chartrs = gdk_pixbuf_get_rowstride(chartpxb); chartnc = gdk_pixbuf_get_n_channels(chartpxb); fww = 1.0 * chartww / COLS; fhh = 1.0 * charthh / ROWS; for (row = 0; row < ROWS; row++) // loop each tile for (col = 0; col < COLS; col++) { ii = row * COLS + col; if (ii >= Ntiles) break; ylo = row * fhh; // tile position within chart image yhi = ylo + fhh; xlo = col * fww; xhi = xlo + fww; Rsum = Gsum = Bsum = nn = 0; for (py = ylo+fhh/5; py < yhi-fhh/5; py++) // get tile pixels less 20% margins for (px = xlo+fww/5; px < xhi-fww/5; px++) { pix1 = chartpixels + py * chartrs + px * chartnc; Rsum += pix1[0]; Gsum += pix1[1]; Bsum += pix1[2]; nn++; } Rout = Rsum / nn; // average tile RGB values Gout = Gsum / nn; Bout = Bsum / nn; rx = ii / NC2; gx = (ii - NC2 * rx) / NC; bx = ii - NC2 * rx - NC * gx; ERR1[rx][gx][bx][0] = Rout - RGBvals[rx]; // error = (scammed RGB) - (printed RGB) ERR1[rx][gx][bx][1] = Gout - RGBvals[gx]; ERR1[rx][gx][bx][2] = Bout - RGBvals[bx]; } g_object_unref(chartpxb); // anneal the error values to reduce randomness for (int pass = 1; pass <= 4; pass++) // 4 passes { for (rx = 0; rx < NC; rx++) // use neighbors in 3 channels for (gx = 0; gx < NC; gx++) for (bx = 0; bx < NC; bx++) { r1 = rx-1; r2 = rx+1; g1 = gx-1; g2 = gx+1; b1 = bx-1; b2 = bx+1; if (r1 < 0) r1 = 0; if (r2 > NC-1) r2 = NC-1; if (g1 < 0) g1 = 0; if (g2 > NC-1) g2 = NC-1; if (b1 < 0) b1 = 0; if (b2 > NC-1) b2 = NC-1; Rsum = Gsum = Bsum = nn = 0; for (ry = r1; ry <= r2; ry++) for (gy = g1; gy <= g2; gy++) for (by = b1; by <= b2; by++) { Rsum += ERR1[ry][gy][by][0]; Gsum += ERR1[ry][gy][by][1]; Bsum += ERR1[ry][gy][by][2]; nn++; } ERR2[rx][gx][bx][0] = Rsum / nn; ERR2[rx][gx][bx][1] = Gsum / nn; ERR2[rx][gx][bx][2] = Bsum / nn; } for (rx = 0; rx < NC; rx++) for (gx = 0; gx < NC; gx++) for (bx = 0; bx < NC; bx++) { ERR1[rx][gx][bx][0] = ERR2[rx][gx][bx][0]; ERR1[rx][gx][bx][1] = ERR2[rx][gx][bx][1]; ERR1[rx][gx][bx][2] = ERR2[rx][gx][bx][2]; } for (rx = 1; rx < NC-1; rx++) // use neighbors in same channel for (gx = 0; gx < NC; gx++) for (bx = 0; bx < NC; bx++) ERR2[rx][gx][bx][0] = 0.5 * (ERR1[rx-1][gx][bx][0] + ERR1[rx+1][gx][bx][0]); for (rx = 0; rx < NC; rx++) for (gx = 1; gx < NC-1; gx++) for (bx = 0; bx < NC; bx++) ERR2[rx][gx][bx][1] = 0.5 * (ERR1[rx][gx-1][bx][1] + ERR1[rx][gx+1][bx][1]); for (rx = 0; rx < NC; rx++) for (gx = 0; gx < NC; gx++) for (bx = 1; bx < NC-1; bx++) ERR2[rx][gx][bx][2] = 0.5 * (ERR1[rx][gx][bx-1][2] + ERR1[rx][gx][bx+1][2]); for (rx = 0; rx < NC; rx++) for (gx = 0; gx < NC; gx++) for (bx = 0; bx < NC; bx++) { ERR1[rx][gx][bx][0] = ERR2[rx][gx][bx][0]; ERR1[rx][gx][bx][1] = ERR2[rx][gx][bx][1]; ERR1[rx][gx][bx][2] = ERR2[rx][gx][bx][2]; } } // pass loop // save finished color map to user-selected file zmessageACK(Mwin,E2X("Set the name for the output calibration file \n" "[your calibration name].dat")); snprintf(mapfile,200,"%s/%s",printer_color_folder,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_folder,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 = E2X("Color map file to use"); char mapfile[200]; FILE *fid; PIXBUF *pixbuf; GError *gerror = 0; uint8 *pixels, *pix1; char *pp, *pp2, printfile[100]; int ww, hh, rs, nc, px, py, nn, err; int R1, G1, B1, R2, G2, B2; int RGB[NC][NC][NC][3], ERR[NC][NC][NC][3]; int rr1, rr2, gg1, gg2, bb1, bb2; int rx, gx, bx; int ii, Dr, Dg, Db; float W[8], w, Wsum, D, Dmax, F; float Er, Eg, Eb; F1_help_topic = "print calibrated"; m_viewmode(0,"F"); // file view mode 19.0 if (! curr_file) { zmessageACK(Mwin,E2X("Select the image file to print.")); return; } for (int ii = 0; ii < NC; ii++) // construct RGBvals table RGBvals[ii] = CS * ii; RGBvals[NC-1] = 255; // set last value = 255 zd = zdialog_new(title,Mwin,Bbrowse,Bproceed,Bcancel,null); // show current color map file zdialog_add_widget(zd,"hbox","hbmap","dialog"); // and allow user to choose another zdialog_add_widget(zd,"label","labmap","hbmap",0,"space=3"); zdialog_stuff(zd,"labmap",colormapfile); zdialog_resize(zd,250,0); zdialog_run(zd,0,"parent"); zstat = zdialog_wait(zd); zdialog_free(zd); if (zstat == 1) { // [browse] snprintf(mapfile,200,"%s/%s",printer_color_folder,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_folder,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,E2X("file format error")); fclose(fid); return; } } fclose(fid); pixbuf = gdk_pixbuf_copy(Fpxb->pixbuf); // get image pixbuf to convert if (! pixbuf) { if (gerror) zmessageACK(Mwin,gerror->message); return; } if (checkpend("all")) return; Fblock = 1; Ffuncbusy = 1; ww = gdk_pixbuf_get_width(pixbuf); hh = gdk_pixbuf_get_height(pixbuf); pixels = gdk_pixbuf_get_pixels(pixbuf); rs = gdk_pixbuf_get_rowstride(pixbuf); nc = gdk_pixbuf_get_n_channels(pixbuf); poptext_window(MWIN,E2X("converting colors..."),300,200,0,-1); for (py = 0; py < hh; py++) for (px = 0; px < ww; px++) { pix1 = pixels + py * rs + px * nc; R1 = pix1[0]; // image RGB values G1 = pix1[1]; B1 = pix1[2]; rr1 = R1/CS; // get color map values surrounding RGB rr2 = rr1 + 1; if (rr2 > NC-1) { // if > last entry, use last entry rr1--; rr2--; } gg1 = G1/CS; gg2 = gg1 + 1; if (gg2 > NC-1) { gg1--; gg2--; } bb1 = B1/CS; bb2 = bb1 + 1; if (bb2 > NC-1) { bb1--; bb2--; } ii = 0; Wsum = 0; Dmax = CS; for (rx = rr1; rx <= rr2; rx++) // loop 8 enclosing error map nodes for (gx = gg1; gx <= gg2; gx++) for (bx = bb1; bx <= bb2; bx++) { Dr = R1 - RGBvals[rx]; // RGB distance from enclosing node Dg = G1 - RGBvals[gx]; Db = B1 - RGBvals[bx]; D = sqrtf(Dr*Dr + Dg*Dg + Db*Db); if (D > Dmax) W[ii] = 0; else W[ii] = (Dmax - D) / Dmax; // weight of node Wsum += W[ii]; // sum of weights ii++; } ii = 0; Er = Eg = Eb = 0; for (rx = rr1; rx <= rr2; rx++) // loop 8 enclosing error map nodes for (gx = gg1; gx <= gg2; gx++) for (bx = bb1; bx <= bb2; bx++) { w = W[ii] / Wsum; Er += w * ERR[rx][gx][bx][0]; // weighted sum of map node errors Eg += w * ERR[rx][gx][bx][1]; Eb += w * ERR[rx][gx][bx][2]; ii++; } F = 1.0; // use 100% of calculated error R2 = R1 - F * Er; // adjusted RGB = image RGB - error G2 = G1 - F * Eg; B2 = B1 - F * Eb; if (R2 < 0) R2 = 0; if (G2 < 0) G2 = 0; if (B2 < 0) B2 = 0; if (R2 > 255) R2 = 255; // preserving RGB ratio does not help if (G2 > 255) G2 = 255; if (B2 > 255) B2 = 255; pix1[0] = R2; pix1[1] = G2; pix1[2] = B2; zmainloop(100); // keep GTK alive } poptext_killnow(); Ffuncbusy = 0; Fblock = 0; snprintf(printfile,100,"%s/printfile.png",temp_folder); // 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,E2X("Image colors are converted for printing.")); print_image_file(Mwin,printfile); 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; m_viewmode(0,"F"); // file view mode 19.0 if (menu && strmatchN(menu,"grid ",5)) // grid N from some edit functions currgrid = menu[5] - '0'; else if (! widget) { // from KB shortcut toggle_grid(2); // toggle grid lines on/off return; } else currgrid = 0; // must be menu call if (currgrid == 0) F1_help_topic = "grid lines"; /*** ____________________________________________ | Grid Lines | | | | x-spacing [____] y-spacing [____] | | x-count [____] y-count [____] | | x-enable [_] y-enable [_] | | | | x-offset =================[]============= | | y-offset ==============[]================ | | | | [Done] | |____________________________________________| ***/ zd = zdialog_new(E2X("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",E2X("x-spacing")); zdialog_add_widget(zd,"label","lab2x","vb1",E2X("x-count")); zdialog_add_widget(zd,"label","lab4x","vb1",E2X("x-enable")); zdialog_add_widget(zd,"zspin","spacex","vb2","20|200|1|50","space=2"); zdialog_add_widget(zd,"zspin","countx","vb2","0|100|1|2","space=2"); zdialog_add_widget(zd,"check","enablex","vb2",0); zdialog_add_widget(zd,"label","lab1y","vb3",E2X("y-spacing")); zdialog_add_widget(zd,"label","lab2y","vb3",E2X("y-count")); zdialog_add_widget(zd,"label","lab4y","vb3",E2X("y-enable")); zdialog_add_widget(zd,"zspin","spacey","vb4","20|200|1|50"); zdialog_add_widget(zd,"zspin","county","vb4","0|100|1|2"); zdialog_add_widget(zd,"check","enabley","vb4",0); zdialog_add_widget(zd,"hbox","hboffx","dialog"); zdialog_add_widget(zd,"label","lab3x","hboffx",Bxoffset,"space=7"); zdialog_add_widget(zd,"hscale","offsetx","hboffx","0|100|1|0","expand"); zdialog_add_widget(zd,"label","space","hboffx",0,"space=20"); zdialog_add_widget(zd,"hbox","hboffy","dialog"); zdialog_add_widget(zd,"label","lab3y","hboffy",Byoffset,"space=7"); zdialog_add_widget(zd,"hscale","offsety","hboffy","0|100|1|0","expand"); zdialog_add_widget(zd,"label","space","hboffy",0,"space=20"); G = currgrid; // grid logic overhaul if (! gridsettings[G][GXS]) // if [G] never set, get defaults for (int ii = 0; ii < 9; ii++) gridsettings[G][ii] = gridsettings[0][ii]; zdialog_stuff(zd,"enablex",gridsettings[G][GX]); // current settings >> dialog widgets zdialog_stuff(zd,"enabley",gridsettings[G][GY]); zdialog_stuff(zd,"spacex",gridsettings[G][GXS]); zdialog_stuff(zd,"spacey",gridsettings[G][GYS]); zdialog_stuff(zd,"countx",gridsettings[G][GXC]); zdialog_stuff(zd,"county",gridsettings[G][GYC]); zdialog_stuff(zd,"offsetx",gridsettings[G][GXF]); zdialog_stuff(zd,"offsety",gridsettings[G][GYF]); zdialog_set_modal(zd); zdialog_run(zd,gridlines_dialog_event,"parent"); zdialog_wait(zd); zdialog_free(zd); return; } // dialog event function int gridlines_dialog_event(zdialog *zd, cchar *event) { int G = currgrid; if (zd->zstat) // done or cancel { if (zd->zstat != 1) return 1; if (gridsettings[G][GX] || gridsettings[G][GY]) gridsettings[G][GON] = 1; else gridsettings[G][GON] = 0; Fpaint2(); return 1; } if (strmatch(event,"enablex")) // x/y grid enable or disable zdialog_fetch(zd,"enablex",gridsettings[G][GX]); if (strmatch(event,"enabley")) zdialog_fetch(zd,"enabley",gridsettings[G][GY]); if (strmatch(event,"spacex")) // x/y grid spacing (if counts == 0) zdialog_fetch(zd,"spacex",gridsettings[G][GXS]); if (strmatch(event,"spacey")) zdialog_fetch(zd,"spacey",gridsettings[G][GYS]); if (strmatch(event,"countx")) // x/y grid line counts zdialog_fetch(zd,"countx",gridsettings[G][GXC]); if (strmatch(event,"county")) zdialog_fetch(zd,"county",gridsettings[G][GYC]); if (strmatch(event,"offsetx")) // x/y grid starting offsets zdialog_fetch(zd,"offsetx",gridsettings[G][GXF]); if (strmatch(event,"offsety")) zdialog_fetch(zd,"offsety",gridsettings[G][GYF]); if (gridsettings[G][GX] || gridsettings[G][GY]) // if either grid enabled, show grid gridsettings[G][GON] = 1; Fpaint2(); return 1; } // toggle grid lines on or off // action: 0 = off, 1 = on, 2 = toggle: on > off, off > on void toggle_grid(int action) { int G = currgrid; if (action == 0) gridsettings[G][GON] = 0; // grid off if (action == 1) gridsettings[G][GON] = 1; // grid on if (action == 2) gridsettings[G][GON] = 1 - gridsettings[G][GON]; // toggle grid if (gridsettings[G][GON]) if (! gridsettings[G][GX] && ! gridsettings[G][GY]) // if grid on and x/y both off, gridsettings[G][GX] = gridsettings[G][GY] = 1; // set both grids on Fpaint2(); return; } /********************************************************************************/ // choose color for foreground lines // (area outline, mouse circle, trim rectangle ...) void m_line_color(GtkWidget *, cchar *menu) { int line_color_dialog_event(zdialog *zd, cchar *event); zdialog *zd; F1_help_topic = "line color"; m_viewmode(0,"F"); // file view mode 19.0 zd = zdialog_new(E2X("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[0] == BLACK[0] && LINE_COLOR[1] == BLACK[1] && LINE_COLOR[2] == BLACK[2]) // 19.0 zdialog_stuff(zd,Bblack,1); if (LINE_COLOR[0] == WHITE[0] && LINE_COLOR[1] == WHITE[1] && LINE_COLOR[2] == WHITE[2]) zdialog_stuff(zd,Bwhite,1); if (LINE_COLOR[0] == RED[0] && LINE_COLOR[1] == RED[1] && LINE_COLOR[2] == RED[2]) zdialog_stuff(zd,Bred,1); if (LINE_COLOR[0] == GREEN[0] && LINE_COLOR[1] == GREEN[1] && LINE_COLOR[2] == GREEN[2]) 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,Bblack)) memcpy(LINE_COLOR,BLACK,3*sizeof(int)); // set selected color 19.0 if (strmatch(event,Bwhite)) memcpy(LINE_COLOR,WHITE,3*sizeof(int)); if (strmatch(event,Bred)) memcpy(LINE_COLOR,RED,3*sizeof(int)); if (strmatch(event,Bgreen)) memcpy(LINE_COLOR,GREEN,3*sizeof(int)); if (CEF && CEF->zd) zdialog_send_event(CEF->zd,"line_color"); Fpaint2(); if (zd->zstat) zdialog_free(zd); // [x] button return 1; } /********************************************************************************/ // 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 = E2X("Darkest and Brightest Pixels"); F1_help_topic = "dark/bright pixels"; m_viewmode(0,"F"); // file view mode 19.0 if (! curr_file) return; // no image file /** ______________________________________ | Darkest and Brightest Pixels | | | | Dark Limit ===[]============ NNN | | Bright Limit ============[]=== NNN | | | | [Done] | |______________________________________| **/ zdialog *zd = zdialog_new(title,Mwin,Bdone,null); // darkbrite dialog zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=3|homog|expand"); zdialog_add_widget(zd,"vbox","vb3","hb1",0,"homog|space=3"); zdialog_add_widget(zd,"label","labD","vb1",E2X("Dark Limit")); zdialog_add_widget(zd,"label","labB","vb1",E2X("Bright Limit")); zdialog_add_widget(zd,"hscale","limD","vb2","0|255|1|0","expand"); zdialog_add_widget(zd,"hscale","limB","vb2","0|255|1|255","expand"); zdialog_add_widget(zd,"label","valD","vb3"); zdialog_add_widget(zd,"label","valB","vb3"); zdialog_rescale(zd,"limD",0,0,255); zdialog_rescale(zd,"limB",0,255,255); zdialog_stuff(zd,"limD",darklim); // start with prior values zdialog_stuff(zd,"limB",brightlim); zdialog_resize(zd,300,0); zdialog_run(zd,darkbrite_dialog_event,"save"); // run dialog - parallel zd_darkbrite = zd; // global pointer for Fpaint*() zdialog_send_event(zd,"limD"); // initz. NNN labels zdialog_send_event(zd,"limB"); return; } // darkbrite dialog event and completion function int darkbrite_dialog_event(zdialog *zd, const char *event) // darkbrite dialog event function { using namespace darkbrite; char text[8]; if (zd->zstat) { zdialog_free(zd); zd_darkbrite = 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 zd_darkbrite 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 } /********************************************************************************/ // RAW pixel bias function // Aggregate many (>10) gray RAW images made under identical conditions. // Average the pixels to detect brightness differences from camera sensors. namespace map_pixel_bias_names { zdialog *zd; char bias_map_file[200] = ""; int Eww, Ehh; // gray image dimensions int Npix; // size of pixR/G/B float *pixR, *pixG, *pixB; // RGB values by pixel } // menu function void m_map_pixel_bias(GtkWidget *, cchar *menu) // 20.0 { using namespace map_pixel_bias_names; int map_pixel_bias_dialog_event(zdialog *zd, cchar *event); char text[100]; F1_help_topic = "map pixel bias"; if (checkpend("all")) return; pixR = 0; /*** ___________________________________ | Map RAW Pixel Bias | | | | [Select Files] N files selected | | | | mean RGB: N.N N.N N.N | | pixel bias: NN.N NN.N NN.N | | | | [measure] [save] [cancel] | |___________________________________| ***/ zd = zdialog_new(E2X("Map RAW Pixel Bias"),Mwin,Bmeasure,Bsave,Bcancel,null); zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=5"); zdialog_add_widget(zd,"button","selectfiles","hbf",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","fcount","hbf",0,"space=10"); zdialog_add_widget(zd,"hbox","hbim","dialog"); zdialog_add_widget(zd,"label","labim","hbim",E2X("mean RGB:"),"space=5"); zdialog_add_widget(zd,"label","labimdata","hbim","no data","space=5"); zdialog_add_widget(zd,"hbox","hbpb","dialog"); zdialog_add_widget(zd,"label","labpb","hbpb","pixel bias:","space=5"); zdialog_add_widget(zd,"label","labpbdata","hbpb","no data","space=5"); snprintf(text,100,Bfileselected,GScount); // show selected files count 20.0 zdialog_stuff(zd,"fcount",text); zdialog_run(zd,map_pixel_bias_dialog_event,"parent"); return; } // dialog event and completion callback function int map_pixel_bias_dialog_event(zdialog *zd, cchar *event) { using namespace map_pixel_bias_names; void map_pixel_bias_measure(zdialog *zd); void map_pixel_bias_save(); char countmess[80]; if (strmatch(event,"selectfiles")) // select files { zdialog_show(zd,0); gallery_select(); // get list of files to convert snprintf(countmess,80,Bfileselected,GScount); // update dialog zdialog_stuff(zd,"fcount",countmess); zdialog_show(zd,1); } if (zd->zstat) { if (zd->zstat == 1) // [measure] { zd->zstat = 0; map_pixel_bias_measure(zd); return 1; } else if (zd->zstat == 2) // [save] { zd->zstat = 0; map_pixel_bias_save(); return 1; } else // [cancel] or [x] { zdialog_free(zd); if (pixR) { zfree(pixR); zfree(pixG); zfree(pixB); } return 1; } } return 1; } // aggregate all gray image pixels and measure brightness variations. void map_pixel_bias_measure(zdialog *zd) { using namespace map_pixel_bias_names; PXM *pxm = 0; int im, Nim, nn; int px, py, qx, qy, ip, iq; float *pix; char text[100]; double imageR, imageG, imageB; float neighborR, neighborG, neighborB; float diffR, diffG, diffB; // RGB differences if (GScount < 10) { zmessageACK(Mwin,E2X("select at least 10 RAW image files")); return; } if (pixR) { zfree(pixR); zfree(pixG); zfree(pixB); pixR = 0; } Nim = GScount; // image count Ffuncbusy = 1; Fbusy_goal = Nim; for (im = 0; im < Nim; im++) // loop all images { pxm = RAW_PXM_load(GSfiles[im],0,0); // load image if (! pxm) { zmessageACK(Mwin,E2X("cannot read file \n %s"),GSfiles[im]); goto cleanup; } if (image_file_type(GSfiles[im]) != RAW) { zmessageACK(Mwin,E2X("not a RAW file \n %s"),GSfiles[im]); goto cleanup; } if (im == 0) { // first image Eww = pxm->ww; // set dimensions for all images Ehh = pxm->hh; Npix = Eww * Ehh; pixR = (float *) zmalloc(Npix * sizeof(float)); // allocate memory for RGB pixel sums pixG = (float *) zmalloc(Npix * sizeof(float)); pixB = (float *) zmalloc(Npix * sizeof(float)); } if (pxm->ww != Eww || pxm->hh != Ehh) { // check image dimensions match zmessageACK(Mwin,E2X("dimensions do not match: %s"),GSfiles[im]); PXM_free(pxm); goto cleanup; } for (py = 0; py < Ehh; py++) // sum RGB values by pixel for (px = 0; px < Eww; px++) { pix = PXMpix(pxm,px,py); ip = py * Eww + px; pixR[ip] += pix[0]; pixG[ip] += pix[1]; pixB[ip] += pix[2]; } PXM_free(pxm); Fbusy_done++; zmainsleep(0.1); } imageR = imageG = imageB = 0; for (py = 0; py < Ehh; py++) // sum RGB values for all images for (px = 0; px < Eww; px++) { pix = PXMpix(pxm,px,py); ip = py * Eww + px; imageR += pixR[ip]; imageG += pixG[ip]; imageB += pixB[ip]; } nn = Eww * Ehh * Nim; // no. pixels x no. images imageR = imageR / nn; // mean RGB values for all images imageG = imageG / nn; imageB = imageB / nn; snprintf(text,100,"%.2f %.2f %.2f",imageR,imageG,imageB); zdialog_stuff(zd,"labimdata",text); for (py = 0; py < Ehh; py++) // mean RGB values by pixel for (px = 0; px < Eww; px++) { pix = PXMpix(pxm,px,py); ip = py * Eww + px; pixR[ip] = pixR[ip] / Nim; pixG[ip] = pixG[ip] / Nim; pixB[ip] = pixB[ip] / Nim; } diffR = diffG = diffB = 0; for (py = 2; py < Ehh-2; py++) // RGB offsets from neighbor pixel mean for (px = 2; px < Eww-2; px++) { neighborR = neighborG = neighborB = 0; for (qy = py-2; qy <= py+2; qy++) // RGB sum of 5x5 block for (qx = px-2; qx <= px+2; qx++) { iq = qy * Eww + qx; neighborR += pixR[iq]; neighborG += pixG[iq]; neighborB += pixB[iq]; } neighborR = neighborR / 25; // RGB mean of 5x5 neighbor block neighborG = neighborG / 25; neighborB = neighborB / 25; ip = py * Eww + px; diffR += fabsf(pixR[ip] - neighborR); // sum RGB offset from 5x5 block mean diffG += fabsf(pixG[ip] - neighborG); diffB += fabsf(pixB[ip] - neighborB); } nn = (Eww-2) * (Ehh-2); diffR = diffR / nn; // mean RGB offset from 5x5 neighbor block diffG = diffG / nn; diffB = diffB / nn; snprintf(text,100,"%.2f %.2f %.2f",diffR,diffG,diffB); zdialog_stuff(zd,"labpbdata",text); cleanup: Ffuncbusy = 0; Fbusy_goal = Fbusy_done = 0; return; } // save pixel bias map to a file void map_pixel_bias_save() { using namespace map_pixel_bias_names; cchar *keys[2] = { "Make", "Model" }; // exif camera data char *data[2]; char *file; FILE *fid = 0; int px, py, qx, qy, rx, ry, iq, ir; double sumR, sumG, sumB; float meanR, meanG, meanB; float offsetR, offsetG, offsetB; if (! pixR) return; // no data available if (! *bias_map_file) { exif_get(GSfiles[0],keys,data,2); // default bias measure file: if (! data[0]) data[0] = zstrdup("unknown"); // .../pixel_bias_map make model if (! data[1]) data[1] = zstrdup("unknown"); snprintf(bias_map_file,200,"%s/pixel_bias_map %s %s", pixel_maps_folder, data[0], data[1]); } file = zgetfile(E2X("Pixel Bias Map file"),MWIN,"save",bias_map_file); // choose file for write if (! file) return; fid = fopen(file,"w"); // open for write if (! fid) { zmessageACK(Mwin,strerror(errno)); return; } strncpy0(bias_map_file,file,200); // remember chosen file name Ffuncbusy = 1; fprintf(fid,"pixel_bias_map \n"); // write headers fprintf(fid,"dimensions %d %d \n",Eww,Ehh); fprintf(fid,"pixel RGB offsets \n"); for (py = 2; py < Ehh-2; py++) // RGB offsets from neighbor pixel mean for (px = 2; px < Eww-2; px += 10) { qy = py; for (qx = px; qx < px+10 && qx < Eww-2; qx++) { sumR = sumG = sumB = 0; for (ry = qy-2; ry <= qy+2; ry++) // RGB sum of 5x5 neighbor block for (rx = qx-2; rx <= qx+2; rx++) { ir = ry * Eww + rx; sumR += pixR[ir]; sumG += pixG[ir]; sumB += pixB[ir]; } meanR = sumR / 25; // RGB mean of 5x5 neighbor block meanG = sumG / 25; meanB = sumB / 25; iq = qy * Eww + qx; offsetR = meanR / pixR[iq]; // pixel RGB offset from 5x5 block offsetG = meanG / pixG[iq]; // e.g. 1% too bright >> 0.99 offsetB = meanB / pixB[iq]; fprintf(fid,"%.3f %.3f %.3f ",offsetR,offsetG,offsetB); } fprintf(fid,"\n"); zmainloop(100); } fclose(fid); Ffuncbusy = 0; return; } // Load pixel bias map from a file. // returns: 0 = OK called by m_batch_RAW namespace pixel_bias_map_names { int Eww, Ehh; float *offsetR = 0, *offsetG, *offsetB; } int pixel_bias_map_load() { using namespace pixel_bias_map_names; char *file; int nn, ii; int px, py, qx, qy; FILE *fid = 0; if (offsetR) { zfree(offsetR); zfree(offsetG); zfree(offsetB); offsetR = 0; } file = zgetfile(E2X("Pixel Bias Map file"),MWIN,"file",pixel_maps_folder); // choose file if (! file) return 1; fid = fopen(file,"r"); if (! fid) { zmessageACK(Mwin,strerror(errno)); return 2; } nn = fscanf(fid,"pixel_bias_map "); // read headers nn = fscanf(fid,"dimensions %d %d ",&Eww,&Ehh); if (nn != 2) goto filerr; nn = fscanf(fid,"pixel RGB offsets "); nn = Eww * Ehh; offsetR = (float *) zmalloc(nn * sizeof(float)); offsetG = (float *) zmalloc(nn * sizeof(float)); offsetB = (float *) zmalloc(nn * sizeof(float)); Ffuncbusy = 1; Fbusy_goal = Eww * Ehh; Fbusy_done = 0; for (py = 2; py < Ehh-2; py++) // get RGB offsets from neighbor for (px = 2; px < Eww-2; px += 10) // pixel means { qy = py; for (qx = px; qx < px+10 && qx < Eww-2; qx++) { ii = qy * Eww + qx; nn = fscanf(fid,"%f %f %f ",&offsetR[ii],&offsetG[ii],&offsetB[ii]); if (nn != 3) goto filerr; } Fbusy_done += 10; zmainloop(10); } fclose(fid); Ffuncbusy = 0; Fbusy_goal = Fbusy_done = 0; return 0; filerr: zmessageACK(Mwin,E2X("invalid pixel bias map file")); fclose(fid); if (offsetR) { zfree(offsetR); zfree(offsetG); zfree(offsetB); offsetR = 0; } Ffuncbusy = 0; Fbusy_goal = Fbusy_done = 0; return 3; } // apply pixel bias corrections for given PXM image // returns: 0 = OK called by m_batch_RAW int pixel_bias_fix(PXM * pxm) { using namespace pixel_bias_map_names; int px, py, ii; float *pix; if (! offsetR) return 1; // no map file loaded if (Eww != pxm->ww || Ehh != pxm->hh) { // dimensions wrong zmessageACK(Mwin,E2X("image dimensions do not match pixel bias file")); return 2; } for (py = 2; py < Ehh-2; py++) for (px = 2; px < Eww-2; px++) { pix = PXMpix(pxm,px,py); ii = py * Eww + px; pix[0] = pix[0] * offsetR[ii]; pix[1] = pix[1] * offsetG[ii]; pix[2] = pix[2] * offsetB[ii]; if (pix[0] > 255) pix[0] = 255; if (pix[1] > 255) pix[1] = 255; if (pix[2] > 255) pix[2] = 255; } return 0; } /********************************************************************************/ // Search and map RAW image dead pixels (gray image outliers) namespace map_dead_pixels_names { zdialog *zd; char dead_pixels_file[200] = ""; // dead pixel map file int Eww, Ehh; // gray image dimensions int threshold = 80; // dead pixel threshold char *rawfile = 0; // RAW file to use PXM *pxm = 0; typedef struct { // dead pixel table int cx, cy; // center pixel int Np; // pixel count, 0-9 int px[9], py[9]; // pixels in dead group } deadpix_t; deadpix_t deadpixtab[50]; // dead pixel table int Ndead, maxdead = 50; int deadreptab[9][12] = // dead pixel replacement table { // px py rx ry replacement pixels { -1,-1, -2,-2, -2,-1, -1,-2, 9, 9, 9, 9 }, // px/py position in 3x3 group { 0,-1, 0,-2, 9, 9, 9, 9, 9, 9, 9, 9 }, // corresp. 1-4 replacement pixels { 1,-1, 1,-2, 2,-2, 2,-1, 9, 9, 9, 9 }, // 9/9 = EOL { -1, 0, -2, 0, 9, 9, 9, 9, 9, 9, 9, 9 }, { 0, 0, -2, 0, 0,-2, 2, 0, 0, 2, 9, 9 }, { 1, 0, 2, 0, 9, 9, 9, 9, 9, 9, 9, 9 }, { -1, 1, -2, 1, -2, 2, -1, 2, 9, 9, 9, 9 }, { 0, 1, 0, 2, 9, 9, 9, 9, 9, 9, 9, 9 }, { 1, 1, 2, 1, 2, 2, 1, 2, 9, 9, 9, 9 } }; } // menu function void m_map_dead_pixels(GtkWidget *, cchar *menu) // 20.0 { using namespace map_dead_pixels_names; int map_dead_pixels_dialog_event(zdialog *zd, cchar *event); F1_help_topic = "map dead pixels"; if (checkpend("all")) return; m_viewmode(0,"F"); /*** ___________________________________ | Map RAW Dead Pixels | | | | [select] gray RAW image file | | [__] RGB threshold | | dead pixels found: NN | | RAW loader: xxxxxxx | | | | [find] [save] [cancel] | |___________________________________| ***/ zd = zdialog_new(E2X("Map RAW Dead Pixels"),Mwin,Bfind,Bsave,Bcancel,null); zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=5"); zdialog_add_widget(zd,"button","select","hbf",Bselect,"space=5"); zdialog_add_widget(zd,"label","labsel","hbf",E2X("gray RAW image file"),"space=10"); zdialog_add_widget(zd,"hbox","hbthresh","dialog",0,"space=5"); zdialog_add_widget(zd,"zspin","thresh","hbthresh","10|255|1|80","space=5"); zdialog_add_widget(zd,"label","labthresh","hbthresh",E2X("RGB threshold"),"space=5"); zdialog_add_widget(zd,"hbox","hbdead","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labdead","hbdead",E2X("dead pixels found:"),"space=5"); zdialog_add_widget(zd,"label","deadpixels","hbdead","no data"); zdialog_add_widget(zd,"hbox","hbrload","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labrload","hbrload","RAW loader:","space=5"); zdialog_add_widget(zd,"label","rawloader","hbrload","none","space=5"); zdialog_stuff(zd,"thresh",threshold); if (Frawloader == 1) zdialog_stuff(zd,"rawloader","dcraw"); if (Frawloader == 2) zdialog_stuff(zd,"rawloader","RawTherapee"); zdialog_run(zd,map_dead_pixels_dialog_event,"parent"); return; } // dialog event and completion callback function int map_dead_pixels_dialog_event(zdialog *zd, cchar *event) { using namespace map_dead_pixels_names; void map_dead_pixels_find(zdialog *zd); void map_dead_pixels_save(); char *pp; int err, Nth = 0; if (strmatch(event,"select")) // select RAW image { if (rawfile) zfree(rawfile); rawfile = gallery_select1(null); if (! rawfile) return 1; if (image_file_type(rawfile) != RAW) { zmessageACK(Mwin,E2X("not a RAW file")); return 1; } pxm = RAW_PXM_load(rawfile,0,0); if (! pxm) { zmessageACK(Mwin,E2X("cannot load RAW file")); return 1; } err = f_open(rawfile,Nth,0,1,0); if (err) return 1; pp = strrchr(rawfile,'/'); if (pp) zdialog_stuff(zd,"labsel",pp+1); } if (strmatch(event,"thresh")) // get RGB threshold zdialog_fetch(zd,"thresh",threshold); if (zd->zstat) { if (zd->zstat == 1) // [find] { zd->zstat = 0; map_dead_pixels_find(zd); return 1; } else if (zd->zstat == 2) // [save] { zd->zstat = 0; map_dead_pixels_save(); return 1; } else // [cancel] or [x] { zdialog_free(zd); if (rawfile) zfree(rawfile); rawfile = 0; if (pxm) PXM_free(pxm); pxm = 0; Ndead = 0; return 1; } } return 1; } // map dead pixels (high contrast with gray background) void map_dead_pixels_find(zdialog *zd) { using namespace map_dead_pixels_names; static zdialog *zd2 = 0; int ii, jj; int px, py, qx, qy, rx, ry; float R, G, B; float match, contrast, maxcon; float *pix1; char text[20]; if (! pxm) { zmessageACK(Mwin,E2X("choose a gray RAW file")); return; } if (zd2) popup_report_close(zd2,0); zd2 = popup_report_open("Dead Pixels",Mwin,600,300,0,0,0); Eww = pxm->ww; Ehh = pxm->hh; Ndead = 0; for (py = 5; py < Ehh-5; py++) // loop image pixels for (px = 5; px < Eww-5; px++) // (omit edges) { R = G = B = 0; for (qy = py-3; qy <= py+3; qy++) // loop 7x7 neighborhood for (qx = px-3; qx <= px+3; qx++) { pix1 = PXMpix(pxm,qx,qy); R += pix1[0]; // get pixel RGB sums G += pix1[1]; B += pix1[2]; } R = 0.0204 * R; // RGB sums/49 = RGB means G = 0.0204 * G; B = 0.0204 * B; pix1 = PXMpix(pxm,px,py); match = RGBMATCH(pix1[0],pix1[1],pix1[2],R,G,B); // compare pixel to 7x7 block contrast = 1 - match; if (contrast < 0.004 * threshold) continue; // not a dead pixel candidate maxcon = -1; rx = ry = 0; for (qy = py-1; qy <= py+1; qy++) // loop 3x3 block around candidate for (qx = px-1; qx <= px+1; qx++) { pix1 = PXMpix(pxm,qx,qy); // find pixel with highest contrast match = RGBMATCH(pix1[0],pix1[1],pix1[2],R,G,B); // will be center of dead group contrast = 1 - match; if (contrast > maxcon) { maxcon = contrast; rx = qx; ry = qy; } } qx = rx; // qx/qy = center of dead group qy = ry; for (ii = 0; ii < Ndead; ii++) // search dead pixel table for (jj = 0; jj < deadpixtab[ii].Np; jj++) // to see if qx/qy included if (qx == deadpixtab[ii].px[jj] && qy == deadpixtab[ii].py[jj]) break; if (ii < Ndead) continue; // yes, ignore qx/qy if (Ndead == maxdead) break; // add to dead pixels table ii = Ndead++; deadpixtab[ii].cx = qx; deadpixtab[ii].cy = qy; jj = 0; for (ry = qy-1; ry <= qy+1; ry++) // loop 3x3 block around qx/qy for (rx = qx-1; rx <= qx+1; rx++) { pix1 = PXMpix(pxm,rx,ry); // test contrast of pixel match = RGBMATCH(pix1[0],pix1[1],pix1[2],R,G,B); contrast = 1 - match; if (contrast < 0.5 * maxcon) continue; // low, omit from dead group deadpixtab[ii].px[jj] = rx; // high, include in dead group deadpixtab[ii].py[jj] = ry; jj++; } deadpixtab[ii].Np = jj; // final pixel count popup_report_write(zd2,0,"dead pixel group: %d %d ",qx,qy); for (jj = 0; jj < deadpixtab[ii].Np; jj++) popup_report_write(zd2,0,"%d %d ", deadpixtab[ii].px[jj], deadpixtab[ii].py[jj] ); popup_report_write(zd2,0,"\n"); px = qx + 2; // continue search } erase_topcircles(); // erase prior circles for (ii = 0; ii < Ndead; ii++) // write circles around dead pixels { px = deadpixtab[ii].cx; py = deadpixtab[ii].cy; add_topcircle(px,py,20); } snprintf(text,20,"%d",Ndead); // update dead pixel count in dialog if (Ndead == maxdead) strcat(text,"+"); zdialog_stuff(zd,"deadpixels",text); Fpaint2(); // update window return; } // save pixel map data to a file void map_dead_pixels_save() { using namespace map_dead_pixels_names; cchar *keys[2] = { "Make", "Model" }; // exif camera data char *data[2]; char *file; FILE *fid = 0; int ii, jj; if (! Ndead) return; if (! *dead_pixels_file) { exif_get(rawfile,keys,data,2); // dead pixels RAW file: if (! data[0]) data[0] = zstrdup("unknown"); // .../dead_pixels make model if (! data[1]) data[1] = zstrdup("unknown"); snprintf(dead_pixels_file,200,"%s/dead_pixels %s %s", pixel_maps_folder, data[0], data[1]); } file = zgetfile(E2X("dead pixels file"),MWIN,"save",dead_pixels_file); // choose file for write if (! file) return; fid = fopen(file,"w"); // open for write if (! fid) { zmessageACK(Mwin,strerror(errno)); return; } strncpy0(dead_pixels_file,file,200); // remember chosen file name zfree(file); fprintf(fid,"dead pixels \n"); // write headers fprintf(fid,"dimensions %d %d \n",Eww,Ehh); fprintf(fid,"dead pixel groups \n"); for (ii = 0; ii < Ndead; ii++) // write dead pixels table { fprintf(fid,"center: %d %d ", deadpixtab[ii].cx, deadpixtab[ii].cy); fprintf(fid," Np: %d ",deadpixtab[ii].Np); // Np px py px py ... \n for (jj = 0; jj < deadpixtab[ii].Np; jj++) fprintf(fid," %5d %5d ", deadpixtab[ii].px[jj], deadpixtab[ii].py[jj] ); fprintf(fid,"\n"); } fclose(fid); return; } // Load dead pixel list from previously saved file. // returns: 0 = OK Called by m_batch_RAW() int dead_pixels_map_load() { using namespace map_dead_pixels_names; char *file = 0; FILE *fid = 0; int ii, jj, nn, Np; int cx, cy, px, py; file = zgetfile(E2X("dead pixels file"),MWIN,"file",pixel_maps_folder); // choose file if (! file) return 1; fid = fopen(file,"r"); // open file if (! fid) { zmessageACK(Mwin,Bfilenotfound); zfree(file); return 2; } zfree(file); nn = fscanf(fid,"dead pixels "); // read header if (nn != 0) goto filerr; nn = fscanf(fid,"dimensions %d %d ",&Eww,&Ehh); // image dimensions if (nn != 2) goto filerr; nn = fscanf(fid,"dead pixel groups "); // read header if (nn != 0) goto filerr; for (ii = 0; ii < maxdead; ii++) { nn = fscanf(fid,"center: %d %d ",&cx,&cy); // read central pixel if (nn == EOF) break; if (nn != 2) goto filerr; deadpixtab[ii].cx = cx; deadpixtab[ii].cy = cy; nn = fscanf(fid,"Np: %d ",&Np); // read pixel count if (nn != 1) goto filerr; deadpixtab[ii].Np = Np; for (jj = 0; jj < Np; jj++) { nn = fscanf(fid,"%d %d ",&px,&py); // read pixel group members if (nn != 2) goto filerr; deadpixtab[ii].px[jj] = px; deadpixtab[ii].py[jj] = py; } } Ndead = ii; // final pixel count fclose(fid); fid = 0; if (Ndead > 0 && nn == EOF) return 0; filerr: zmessageACK(Mwin,E2X("invalid dead pixels file")); if (fid) fclose(fid); return 3; } // Fix all dead pixels in deadpixtab for given image in memory. // returns: 0 = OK Called by m_batch_RAW() int dead_pixels_fix(PXM *pxm) { using namespace map_dead_pixels_names; int ii, jj, kk, mm; int Np, Nr; int cx, cy, px, py, rx, ry; float R, G, B; float *pix1; if (! Ndead) { zmessageACK(Mwin,E2X("no dead pixels data available")); return 1; } if (Eww != pxm->ww || Ehh != pxm->hh) { zmessageACK(Mwin,E2X("image dimensions do not match dead pixels file")); return 2; } for (ii = 0; ii < Ndead; ii++) // loop dead pixels table { cx = deadpixtab[ii].cx; // center pixel of dead pixel group cy = deadpixtab[ii].cy; Np = deadpixtab[ii].Np; // dead group pixel count for (jj = 0; jj < Np; jj++) // loop dead group pixels { px = deadpixtab[ii].px[jj]; py = deadpixtab[ii].py[jj]; for (kk = 0; kk < 9; kk++) // find row in dead pixel { // replacement table if (px-cx != deadreptab[kk][0]) continue; if (py-cy == deadreptab[kk][1]) break; } if (kk == 9) { printz("no dead pixel replacement: %d %d %d %d \n",cx,cy,px,py); return 3; } R = G = B = 0; // sum RGB for replacement pixels Nr = 0; for (mm = 2; mm < 10; mm += 2) // loop replacement pixels rx/ry { rx = deadreptab[kk][mm]; // pixel, relative to cx/cy ry = deadreptab[kk][mm+1]; if (rx == 9) break; // EOL rx = rx + cx; // relative to absolute ry = ry + cy; pix1 = PXMpix(pxm,rx,ry); // accumulate RGB sum R += pix1[0]; G += pix1[1]; B += pix1[2]; Nr++; } pix1 = PXMpix(pxm,px,py); // replace dead pixel with mean pix1[0] = R / Nr; // RGB of replacement pixels pix1[1] = G / Nr; pix1[2] = B / Nr; } } return 0; } /********************************************************************************/ // monitor color and contrast test function void m_monitor_color(GtkWidget *, cchar *) { char file[200]; int err; char *savecurrfile = 0; char *savegallery = 0; zdialog *zd; cchar *message = E2X("Brightness should show a gradual ramp \n" "extending all the way to the edges."); F1_help_topic = "monitor color"; m_viewmode(0,"F"); // file view mode 19.0 if (checkpend("all")) return; // check nothing pending Fblock = 1; if (curr_file) savecurrfile = zstrdup(curr_file); // 20.0 if (navi::galleryname) savegallery = zstrdup(navi::galleryname); snprintf(file,200,"%s/moncolor.png",get_zimagedir()); // color chart 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_set_modal(zd); zdialog_run(zd,0,"0/0"); zdialog_wait(zd); // wait for dialog complete zdialog_free(zd); restore: Fzoom = 0; if (savecurrfile) { // 20.0 f_open(savecurrfile); zfree(savecurrfile); } if (savegallery) { gallery(savegallery,"init",0); gallery(0,"sort",-2); // recall sort and position zfree(savegallery); } else gallery(topfolders[0],"init",0); Fblock = 0; return; } /********************************************************************************/ // check and adjust monitor gamma void m_monitor_gamma(GtkWidget *, cchar *) { int mongamma_dialog_event(zdialog *zd, cchar *event); int err; char gammachart[200]; zdialog *zd; char *savecurrfile = 0; char *savegallery = 0; cchar *permit = "Chart courtesy of Norman Koren"; cchar *website = "http://www.normankoren.com/makingfineprints1A.html#gammachart"; F1_help_topic = "monitor gamma"; m_viewmode(0,"F"); // file view mode 19.0 if (checkpend("all")) return; // check nothing pending err = shell_quiet("which xgamma"); // check for xgamma if (err) { zmessageACK(Mwin,"xgamma program is not installed"); return; } Fblock = 1; if (curr_file) savecurrfile = zstrdup(curr_file); // 20.0 if (navi::galleryname) savegallery = zstrdup(navi::galleryname); snprintf(gammachart,200,"%s/mongamma.png",get_zimagedir()); // gamma chart file err = f_open(gammachart); if (err) goto restore; Fzoom = 1; // scale 100% (required) gtk_window_set_title(MWIN,"monitor gamma"); zd = zdialog_new("monitor gamma",Mwin,Bdone,null); // start user dialog zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=8"); zdialog_add_widget(zd,"label","labgamma","hb1","gamma","space=5"); zdialog_add_widget(zd,"hscale","gamma","hb1","0.6|1.4|0.02|1.0","expand"); zdialog_add_widget(zd,"hbox","hb2","dialog"); zdialog_add_widget(zd,"link",permit,"hb2",website); zdialog_resize(zd,200,0); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_set_modal(zd); zdialog_run(zd,mongamma_dialog_event,"0/0"); zdialog_wait(zd); // wait for dialog complete zdialog_free(zd); restore: Fzoom = 0; if (savecurrfile) { // 20.0 f_open(savecurrfile); zfree(savecurrfile); } if (savegallery) { gallery(savegallery,"init",0); gallery(0,"sort",-2); // recall sort and position zfree(savegallery); } else gallery(topfolders[0],"init",0); Fblock = 0; return; } // dialog event function int mongamma_dialog_event(zdialog *zd, cchar *event) { double gamma; if (strmatch(event,"gamma")) { zdialog_fetch(zd,"gamma",gamma); shell_ack("xgamma -quiet -gamma %.2f",gamma); } return 0; } /********************************************************************************/ // set GUI language void m_change_lang(GtkWidget *, cchar *) { #define NL 8 zdialog *zd; int ii, cc, val, zstat; char lang1[NL], *pp; cchar *langs[NL] = { "en English", "de German", // english first "ca Catalan", "es Spanish", "fr French", "it Italian", "pt Portuguese", null }; cchar *title = E2X("Available Translations"); F1_help_topic = "change language"; zd = zdialog_new(E2X("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::zlocale); // current language for (ii = 0; langs[ii]; ii++) // match on lc_RC if (strmatchN(zfuncs::zlocale,langs[ii],cc)) break; if (! langs[ii]) for (ii = 0; langs[ii]; ii++) // failed, match on lc alone if (strmatchN(zfuncs::zlocale,langs[ii],2)) break; if (! langs[ii]) ii = 0; // failed, default english zdialog_stuff(zd,langs[ii],1); zdialog_resize(zd,200,0); zdialog_set_modal(zd); zdialog_run(zd,0,"mouse"); // run dialog zstat = zdialog_wait(zd); for (ii = 0; langs[ii]; ii++) { // get active radio button zdialog_fetch(zd,langs[ii],val); if (val) break; } zdialog_free(zd); // kill dialog if (zstat != 1) return; // user cancel if (! val) return; // no selection strncpy0(lang1,langs[ii],NL); pp = strchr(lang1,' '); // isolate lc_RC part *pp = 0; locale = zstrdup(lang1); // set parameter 'locale' 19.0 save_params(); new_session("-p"); // replace readlink() etc. 19.0 zsleep(1); // delay before SIGTERM in quitxx() 19.0 quitxx(); // exit return; } /********************************************************************************/ // report missing translations in a popup window // also export translation file for current locale 20.0 void m_untranslated(GtkWidget *, cchar *) { int ftf = 1, Nmiss = 0; cchar *missing; zdialog *zd2; F1_help_topic = "missing translations"; zd2 = popup_report_open("missing translations",Mwin,400,200,0,0,0); popup_report_write(zd2,1,"missing translations for locale %s \n",zfuncs::zlocale); while (true) { missing = E2X_missing(ftf); if (! missing) break; popup_report_write(zd2,0,"%s \n",missing); Nmiss++; } popup_report_write(zd2,0,"%d missing translations \n",Nmiss); // ------------------------------------------------------------------------------ char tranfile[200], *pp; // ask to export translation file 20.0 int yn, err; // (trapped in appimage container) STATB statB; yn = zmessageYN(Mwin,"export the translation .po file?"); if (! yn) return; locale_filespec("locale","translate.po",tranfile); printz("translation file: %s \n",tranfile); err = stat(tranfile,&statB); if (err) { zmessageACK(Mwin,"file not found"); return; } shell_ack("cp %s %s",tranfile,get_zhomedir()); // copy to fotoxx home folder pp = strrchr(tranfile,'/'); zmessageACK(Mwin,"created: %s/%s",get_zhomedir(),pp+1); return; } /********************************************************************************/ // user can allow or block anonymous usage statistics sent to web host void m_phone_home_allow(GtkWidget *, cchar *) // 20.0 { phone_home_allow(Mwin); return; } /********************************************************************************/ // show CPU time since last time shown void m_resources(GtkWidget *, cchar *) // 20.0 { static double time0 = 0.0; double time1; double mem = 0; char buff1[100], buff2[1000]; char *pp = 0; FILE *fid; int MB, pagesize; int mega = 1024 * 1024; F1_help_topic = "show resources"; time1 = CPUtime(); printz("CPU time: %.3f seconds \n", time1 - time0); time0 = time1; snprintf(buff1,100,"/proc/self/stat"); // read file /proc/self/stat fid = fopen(buff1,"r"); if (fid) { pp = fgets(buff2,1000,fid); fclose(fid); } if (pp) { pp = strchr(pp,')'); // closing ')' after (short) filename if (pp) { parseprocrec(pp+1,22,&mem,null); // get real memory pagesize = sysconf(_SC_PAGESIZE); // system page size MB = mem * pagesize / mega; printz("real memory MB: %d \n",MB); } } return; } /********************************************************************************/ // list files included in the appimage container // .../fotoxx/* files are excluded void m_appimage_files(GtkWidget *, cchar *) // 20.0 { zdialog *zd; FILE *fid; char buff[100]; cchar *HERE, *pp; cchar *command = "echo HERE: $HERE; find $HERE/usr/bin; " "find $HERE/usr/lib; find $HERE/usr/share"; F1_help_topic = "appimage files"; if (! zfuncs::appimagexe) { zmessageACK(Mwin,"not an appimage build"); return; } zd = popup_report_open("appimage container included files",Mwin,500,500,0,0,0); HERE = getenv("HERE"); popup_report_write(zd,0,"HERE: %s\n",HERE); fid = popen(command,"r"); if (! fid) return; while (true) { pp = fgets_trim(buff,100,fid); if (! pp) break; if (strstr(pp,"/fotoxx")) continue; pp = strstr(pp,HERE); if (! pp) continue; pp += strlen(HERE); popup_report_write(zd,0,"%s\n",pp); } pclose(fid); popup_report_top(zd); return; } /********************************************************************************/ // zappcrash test - make a segment fault void m_zappcrash_test(GtkWidget *, cchar *) // 19.0 { printz("zappcrash from menu Zappcrash Test \n"); zappcrash("zappcrash from menu Zappcrash Test"); return; } fotoxx-20.08/f.warp.cc000066400000000000000000005321511362435004500145670ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* Fotoxx image edit - warp menu functions m_unbend straighten curvature added by pano or improve perspective m_perspective select a tetragon and convert into a rectangle m_warp_area select an area and warp interior with mouse drags m_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_unwarp_closeup select a face in a close-up photo, remove distortion m_flatbook flatten a photographed book page m_area_rescale rescale while leaving selected areas unchanged m_waves distort an image with a wave pattern m_twist twist image centered at mouse position m_sphere sphere image projection, image shrinks from center to edge m_stretch image scale expands from center to edge m_inside_out turn an image inside-out m_tiny_planet wrap a panorama image around a circle m_escher_spiral generate inward-spiraling recursive image *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // unbend an image // straighten curvature added by pano or improve perspective float unbend_lin_horz, unbend_lin_vert; // unbend values from dialog float unbend_cur_horz, unbend_cur_vert; float unbend_x1, unbend_x2, unbend_y1, unbend_y2; // unbend axes scaled 0 to 1 int unbend_hx1, unbend_hy1, unbend_hx2, unbend_hy2; int unbend_vx1, unbend_vy1, unbend_vx2, unbend_vy2; editfunc EFunbend; // menu function void m_unbend(GtkWidget *, cchar *) // overhauled { int unbend_dialog_event(zdialog* zd, cchar *event); void * unbend_thread(void *); void unbend_mousefunc(); F1_help_topic = "unbend"; EFunbend.funcname = "unbend"; EFunbend.FprevReq = 1; // use preview EFunbend.Frestart = 1; // restart allowed EFunbend.threadfunc = unbend_thread; // thread function EFunbend.mousefunc = unbend_mousefunc; // mouse function if (! edit_setup(EFunbend)) return; // setup edit PXM_addalpha(E0pxm); PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); /*** _______________________ | Unbend | | ______ | | | | [0__] | | | | | | |______| | | | | ______ | | | | [0__] | | | | | | |______| | | | | ______ | | | | [0__] | | | | | | |______| | | | | ______ | | | | [0__] | | | | | | |______| | | | | | | [grid] [done] [cancel] | |________________________| ***/ zdialog *zd = zdialog_new(E2X("Unbend"),Mwin,Bgrid,Bdone,Bcancel,null); EFunbend.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=10|homog"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=10|homog"); zdialog_add_widget(zd,"icon","VL","vb1","unbend vert linear.png","size=64"); zdialog_add_widget(zd,"icon","VC","vb1","unbend vert curved.png","size=64"); zdialog_add_widget(zd,"icon","HL","vb1","unbend horz linear.png","size=64"); zdialog_add_widget(zd,"icon","HC","vb1","unbend horz curved.png","size=64"); zdialog_add_widget(zd,"zspin","splinvert","vb2","-99|99|1|0"); zdialog_add_widget(zd,"zspin","spcurvert","vb2","-99|99|1|0"); zdialog_add_widget(zd,"zspin","splinhorz","vb2","-99|99|1|0"); zdialog_add_widget(zd,"zspin","spcurhorz","vb2","-99|99|1|0"); unbend_x1 = unbend_x2 = unbend_y1 = unbend_y2 = 0.5; // initial axes thru image middle unbend_lin_horz = unbend_lin_vert = 0; unbend_cur_horz = unbend_cur_vert = 0; currgrid = 2; // use unbend grid takeMouse(unbend_mousefunc,dragcursor); // connect mouse function signal_thread(); zdialog_run(zd,unbend_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int unbend_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) // dialog complete { currgrid = 0; // restore normal grid settings if (zd->zstat == 1) { // toggle grid zd->zstat = 0; m_gridlines(0,"grid 2"); // grid settings dialog return 1; } else if (zd->zstat == 2) { // done CEF->Fmods = 0; if (unbend_cur_vert || unbend_cur_horz || // image3 modified unbend_lin_vert || unbend_lin_horz) { CEF->Fmods = 1; CEF->Fsaved = 0; // done } edit_fullsize(); // get full size image signal_thread(); edit_done(0); // commit edit } else edit_cancel(0); // cancel, discard edit draw_toplines(2,0); // erase axes-lines return 1; } if (strmatch(event,"splinvert")) { // get new unbend value zdialog_fetch(zd,"splinvert",unbend_lin_vert); signal_thread(); // trigger thread } if (strmatch(event,"splinhorz")) { zdialog_fetch(zd,"splinhorz",unbend_lin_horz); signal_thread(); } if (strmatch(event,"spcurvert")) { zdialog_fetch(zd,"spcurvert",unbend_cur_vert); signal_thread(); } if (strmatch(event,"spcurhorz")) { zdialog_fetch(zd,"spcurhorz",unbend_cur_horz); signal_thread(); } return 1; } // unbend mouse function // adjustable axes void unbend_mousefunc() { cchar *close; float dist1, dist2; float mpx = 0, mpy = 0; if (LMclick) { // left mouse click mpx = Mxclick; mpy = Myclick; } if (Mxdrag || Mydrag) { // mouse dragged mpx = Mxdrag; mpy = Mydrag; } if (! mpx && ! mpy) return; mpx = 1.0 * mpx / E3pxm->ww; // scale mouse position 0 to 1 mpy = 1.0 * mpy / E3pxm->hh; if (mpx < 0.2 || mpx > 0.8 ) { // check reasonable position if (mpy < 0.1 || mpy > 0.9) return; } else if (mpy < 0.2 || mpy > 0.8) { if (mpx < 0.1 || mpx > 0.9) return; } else return; close = "?"; // find closest axis end-point dist1 = 2; dist2 = mpx * mpx + (mpy-unbend_y1) * (mpy-unbend_y1); if (dist2 < dist1) { dist1 = dist2; close = "left"; } dist2 = (1-mpx) * (1-mpx) + (mpy-unbend_y2) * (mpy-unbend_y2); if (dist2 < dist1) { dist1 = dist2; close = "right"; } dist2 = (mpx-unbend_x1) * (mpx-unbend_x1) + mpy * mpy; if (dist2 < dist1) { dist1 = dist2; close = "top"; } dist2 = (mpx-unbend_x2) * (mpx-unbend_x2) + (1-mpy) * (1-mpy); if (dist2 < dist1) { dist1 = dist2; close = "bottom"; } if (strmatch(close,"left")) unbend_y1 = mpy; // set new axis end-point if (strmatch(close,"right")) unbend_y2 = mpy; if (strmatch(close,"top")) unbend_x1 = mpx; if (strmatch(close,"bottom")) unbend_x2 = mpx; signal_thread(); // trigger thread LMclick = Mxdrag = Mydrag = 0; return; } // unbend thread function void * unbend_thread(void *arg) { void * unbend_wthread(void *); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 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; do_wthreads(unbend_wthread,NWT); // worker threads paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed, stop g++ warning } void * unbend_wthread(void *arg) // worker thread function { int index = *((int *) arg); int vstat, px3, py3, cx3, cy3; float dispx, dispy, dispx2, dispy2; float px1, py1, vx1, vx2, hy1, hy2; float curvert, curhorz, linvert, linhorz; float vpix[4], *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); curvert = int(unbend_cur_vert * 0.01 * E3pxm->hh); // -0.99 to +0.99 curhorz = int(unbend_cur_horz * 0.01 * E3pxm->ww); linvert = int(unbend_lin_vert * 0.0013 * E3pxm->hh); // -0.13 to +0.13 linhorz = int(unbend_lin_horz * 0.0013 * E3pxm->ww); vx1 = unbend_vx1; vx2 = unbend_vx2; hy1 = unbend_hy1; hy2 = unbend_hy2; for (py3 = index; py3 < E3pxm->hh; py3 += NWT) // step through F3 pixels for (px3 = 0; px3 < E3pxm->ww; px3++) { cx3 = vx1 + (vx2 - vx1) * py3 / E3pxm->hh; // center of unbend cy3 = hy1 + (hy2 - hy1) * px3 / E3pxm->ww; dispx = 2.0 * (px3 - cx3) / E3pxm->ww; // -1.0 .. 0.0 .. +1.0 (roughly) dispy = 2.0 * (py3 - cy3) / E3pxm->hh; // -1.0 .. 0.0 .. +1.0 dispx2 = -cosf(0.8 * dispx) + 1; // curved dispy2 = -cosf(0.8 * dispy) + 1; pix3 = PXMpix(E3pxm,px3,py3); // output pixel px1 = px3; // input pixel = output py1 = py3; px1 += dispx * dispy * linhorz; // move input pixel py1 += dispy * dispx * linvert; px1 -= dispx * dispy2 * curhorz; // change sign py1 -= dispy * dispx2 * curvert; vstat = vpixel(E1pxm,px1,py1,vpix); if (vstat) memcpy(pix3,vpix,pcc); else memset(pix3,0,pcc); } pthread_exit(0); } /********************************************************************************/ // Convert a selected tetragon area into a rectangle, converting the // rest of the image to match and keeping straight lines straight. namespace perspective { int PSP_pixel[4][2]; // last 0-4 pixels clicked int PSP_npix; // count of pixels char PSP_pixlab[4][4] = { " A ", " B ", " C ", " D " }; int PSP_corner = 0; editfunc EFperspective; int dialog_event(zdialog *zd, cchar *event); void mousefunc(void); void warpfunc(void); void KBfunc(int key); } // menu function void m_perspective(GtkWidget *, cchar *) { using namespace perspective; cchar *PSP_message = E2X( " 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 = "perspective"; EFperspective.menufunc = m_perspective; EFperspective.funcname = "perspective"; EFperspective.Frestart = 1; // restart allowed EFperspective.mousefunc = mousefunc; // mouse function if (! edit_setup(EFperspective)) return; // setup edit PXM_addalpha(E0pxm); PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); PSP_npix = 0; // no pixels yet zdialog *zd = zdialog_new(E2X("Perspective Correction"),Mwin,Bapply,Breset,Btrim,Bdone,null); zdialog_add_widget(zd,"label","lab1","dialog",PSP_message,"space=3"); EFperspective.zd = zd; zdialog_run(zd,dialog_event,"save"); // run dialog, parallel takeMouse(mousefunc,dragcursor); // connect mouse function return; } // dialog completion callback function int perspective::dialog_event(zdialog *zd, cchar *event) { using namespace perspective; int ii, px, py; int minx, maxx, miny, maxy; if (strmatch(event,"done")) zd->zstat = 3; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 4; // from f_open() if (strmatch(event,"focus")) // toggle mouse capture 12.01 takeMouse(mousefunc,dragcursor); if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) // apply { erase_toptext(102); // erase points warpfunc(); // do the warp zd->zstat = 0; // keep dialog active } else if (zd->zstat == 2) // reset { edit_reset(); zd->zstat = 0; for (ii = 0; ii < PSP_npix; ii++) // show pixel labels on image { px = PSP_pixel[ii][0]; py = PSP_pixel[ii][1]; add_toptext(102,px,py,PSP_pixlab[ii],"Sans 8"); } Fpaint2(); } else if (zd->zstat == 3) // trim { if (PSP_npix < 4) { // wait for 4 corners zd->zstat = 0; return 1; } erase_toptext(102); // erase labels warpfunc(); // do the warp edit_done(0); minx = miny = 99999; // find trim limits maxx = maxy = 0; for (ii = 0; ii < 4; ii++) { if (PSP_pixel[ii][0] < minx) minx = PSP_pixel[ii][0]; if (PSP_pixel[ii][0] > maxx) maxx = PSP_pixel[ii][0]; if (PSP_pixel[ii][1] < miny) miny = PSP_pixel[ii][1]; if (PSP_pixel[ii][1] > maxy) maxy = PSP_pixel[ii][1]; } trimx1 = minx; // set parameters for trim function trimy1 = miny; trimx2 = maxx; trimy2 = maxy; m_trim_rotate(0,"keep"); } else if (zd->zstat == 4) { // done erase_toptext(102); // erase labels edit_done(0); // commit edit } else { // cancel erase_toptext(102); // erase labels edit_cancel(0); // discard edit } return 1; } // mouse function - click on 4 corners of tetragon void perspective::mousefunc() { 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() { 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,E2X("must have 4 corners")); return; } for (ii = 0; ii < 4; ii++) { // get 4 selected tetragon points trpx[ii] = PSP_pixel[ii][0]; trpy[ii] = PSP_pixel[ii][1]; } // sort 4 points in clockwise order NW, NE, SE, SW for (ii = 0; ii < 4; ii++) { // sort top to bottom (y order) for (jj = ii; jj < 4; jj++) { if (trpy[jj] < trpy[ii]) { tempx = trpx[ii]; tempy = trpy[ii]; trpx[ii] = trpx[jj]; trpy[ii] = trpy[jj]; trpx[jj] = tempx; trpy[jj] = tempy; } } } if (trpx[1] < trpx[0]) { // sort upper two left, right tempx = trpx[0]; tempy = trpy[0]; trpx[0] = trpx[1]; trpy[0] = trpy[1]; trpx[1] = tempx; trpy[1] = tempy; } if (trpx[2] < trpx[3]) { // sort lower two right, left tempx = trpx[2]; tempy = trpy[2]; trpx[2] = trpx[3]; trpy[2] = trpy[3]; trpx[3] = tempx; trpy[3] = tempy; } if (trpx[0] < trpx[3]) sqpx0 = sqpx3 = trpx[0]; // rectangle enclosing tetragon else sqpx0 = sqpx3 = trpx[3]; if (trpx[1] > trpx[2]) sqpx1 = sqpx2 = trpx[1]; else sqpx1 = sqpx2 = trpx[2]; if (trpy[0] < trpy[1]) sqpy0 = sqpy1 = trpy[0]; else sqpy0 = sqpy1 = trpy[1]; if (trpy[2] > trpy[3]) sqpy2 = sqpy3 = trpy[2]; else sqpy2 = sqpy3 = trpy[3]; /*** sqpx0 = sqpx3 = 0.5 * (trpx[0] + trpx[3]); // rectangle bisecting tetragon sides sqpx1 = sqpx2 = 0.5 * (trpx[1] + trpx[2]); sqpy0 = sqpy1 = 0.5 * (trpy[0] + trpy[1]); sqpy2 = sqpy3 = 0.5 * (trpy[2] + trpy[3]); ***/ cdx0 = sqpx0 - trpx[0]; // displavement of tetragon corner cdy0 = sqpy0 - trpy[0]; // to corresponding rectangle corner cdx1 = sqpx1 - trpx[1]; cdy1 = sqpy1 - trpy[1]; cdx2 = sqpx2 - trpx[2]; cdy2 = sqpy2 - trpy[2]; cdx3 = sqpx3 - trpx[3]; cdy3 = sqpy3 - trpy[3]; sqww = 1.0 / (sqpx1 - sqpx0); // rectangle width and height sqhh = 1.0 / (sqpy3 - sqpy0); for (py3 = 0; py3 < E3pxm->hh; py3++) // loop all output pixels for (px3 = 0; px3 < E3pxm->ww; px3++) { f0 = (1.0 - (px3 - sqpx0) * sqww) * (1.0 - (py3 - sqpy0) * sqhh); f1 = (px3 - sqpx0) * sqww * (1.0 - (py3 - sqpy0) * sqhh); f2 = (px3 - sqpx0) * sqww * (py3 - sqpy0) * sqhh; f3 = (1.0 - (px3 - sqpx0) * sqww) * (py3 - sqpy0) * sqhh; dispx = cdx0 * f0 + cdx1 * f1 + cdx2 * f2 + cdx3 * f3; dispy = cdy0 * f0 + cdy1 * f1 + cdy2 * f2 + cdy3 * f3; px1 = px3 - dispx; // input virtual pixel for px3/py3 py1 = py3 - dispy; pix3 = PXMpix(E3pxm,int(px3),int(py3)); // output pixel vstat = vpixel(E1pxm,px1,py1,vpix1); // output pixel = input virtual pixel if (vstat) memcpy(pix3,vpix1,pcc); else memset(pix3,0,pcc); // voided pixel } CEF->Fmods++; // image is modified CEF->Fsaved = 0; Fpaint2(); // update window return; } /********************************************************************************/ // warp/distort area - select image area and pull with mouse float *WarpAx, *WarpAy; // memory for pixel warp vectors int WarpAcc, WarpAnew; int WarpA_started; int WarpA_areanumber; editfunc EFwarpA; void WarpA_init(); void WarpA_warpfunc(float mdx, float mdy, float mdw, float mdh, int acc); void WarpA_warpfunc2(float mdx, float mdy, float mdw, float mdh, int acc); void WarpA_mousefunc(); void WarpA_expand(); void WarpA_edgeblend(); // menu function void m_warp_area(GtkWidget *, cchar *) { int WarpA_dialog_event(zdialog *zd, cchar *event); cchar *WarpA_message = E2X( " 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(E2X("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",E2X("start warp"),"space=5"); zdialog_add_widget(zd,"button","reset","hb1",Breset,"space=5"); WarpAcc = E3pxm->ww * E3pxm->hh * sizeof(float); WarpAx = (float *) zmalloc(WarpAcc); // get memory for pixel warp vectors WarpAy = (float *) zmalloc(WarpAcc); memset(WarpAx,0,WarpAcc); memset(WarpAy,0,WarpAcc); WarpA_started = 0; WarpA_areanumber = 0; WarpAnew = 0; zdialog_run(zd,WarpA_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int WarpA_dialog_event(zdialog * zd, cchar *event) { int init = 0; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit zfree(WarpAx); // release memory zfree(WarpAy); return 1; } if (! sa_validate()) init = 1; // area invalid for curr. image file if (sa_stat != 3 || sa_mode == mode_image) // no select area active init = 1; if (WarpA_started && WarpA_areanumber != areanumber) // select area changed init = 1; if (init) { memset(WarpAx,0,WarpAcc); memset(WarpAy,0,WarpAcc); WarpA_started = 0; WarpA_areanumber = 0; return 1; } if (strmatch(event,"start")) // start warp { if (sa_stat != 3 || sa_mode == mode_image) { // no select area active zmessageACK(Mwin,E2X("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; // int ii = mdy * Fpxb->ww + mdx; // disable drag outside area // if (! sa_pixmap[ii]) return; mdw = Mxdrag - Mxdown; // drag increment mdh = Mydrag - Mydown; WarpA_warpfunc(mdx,mdy,mdw,mdh,0); // warp image warped = 1; Mxdrag = Mydrag = 0; return; } else if (warped) { warped = 0; WarpA_warpfunc(mdx,mdy,mdw,mdh,1); // drag done, update warp vectors if (WarpAnew) WarpA_expand(); } return; } // warp image and accumulate warp memory // mdx/y = mouse initial position // mdw/h = mouse drag vector void WarpA_warpfunc(float mdx, float mdy, float mdw, float mdh, int acc) { int ii, px, py, ww, hh, vstat; float ddx, ddy, dpe, dpm1, dpm2, dpm; float mag, dispx, dispy; float vpix[4], *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); if (sa_stat != 3) return; // area erased ww = E1pxm->ww; hh = E1pxm->hh; ii = mdy * ww + mdx; if (! sa_pixmap[ii]) { // if mouse drag outside select area WarpA_warpfunc2(mdx,mdy,mdw,mdh,acc); // then use alternate function return; } for (py = sa_miny; py < sa_maxy; py++) // loop all pixels in area for (px = sa_minx; px < sa_maxx; px++) { ii = py * ww + px; dpe = sa_pixmap[ii]; // distance from area edge if (dpe < 1) continue; // outside area dpe -= 1; // rebase edge = 0 ddx = (px - mdx); // pixel distance to mouse position ddy = (py - mdy); // before drag dpm1 = sqrt(ddx*ddx + ddy*ddy); ddx -= mdw; // pixel distance to mouse position ddy -= mdh; // after drag dpm2 = sqrt(ddx*ddx + ddy*ddy); dpm = 0.5 * (dpm1 + dpm2); // mean mag = (dpe / (dpm + dpe)); // 1...0 for pixel at mouse...edge dispx = -mdw * mag; // pixel movement from drag movement dispy = -mdh * mag; dispx += WarpAx[ii]; // add this warp to prior dispy += WarpAy[ii]; if (acc) { // mouse drag done, WarpAx[ii] = dispx; // accumulate warp memory WarpAy[ii] = dispy; continue; } vstat = vpixel(E1pxm,px+dispx,py+dispy,vpix); // input virtual pixel if (vstat) { pix3 = PXMpix(E3pxm,px,py); // output pixel memcpy(pix3,vpix,pcc); } } ww = sa_maxx - sa_minx; // update window hh = sa_maxy - sa_miny; Fpaint3(sa_minx,sa_miny,ww,hh,0); CEF->Fmods++; CEF->Fsaved = 0; return; } // warp function when mouse is dragged outside select area void WarpA_warpfunc2(float mdx, float mdy, float mdw, float mdh, int acc) { int ii, jj, px, py, dpx, dpy, dpe; int xlo, xhi, ylo, yhi; int ww, hh, vstat; float ddx, ddy, dpm1, dpm2, dpm; float mag, dispx, dispy; float mdmax, mdmax2; float vpix[4], *pix1, *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); #define distance(px1,py1,px2,py2) \ sqrtf((px2-px1)*(px2-px1)+(py2-py1)*(py2-py1)) WarpAnew = 1; // note select area will expand ww = E1pxm->ww; hh = E1pxm->hh; ylo = sa_miny; // select area enclosing rectangle yhi = sa_maxy; xlo = sa_minx; xhi = sa_maxx; if (mdy < ylo) ylo = mdy - 200; // extend area to mouse posn + more if (mdy > yhi) yhi = mdy + 200; if (mdx < xlo) xlo = mdx - 200; if (mdx > xhi) xhi = mdx + 200; if (ylo < 0) ylo = 0; if (yhi > hh) yhi = hh; if (xlo < 0) xlo = 0; if (xhi > ww) xhi = ww; mdmax = distance(mdx,mdy,xlo,ylo); // find max. mouse distance from mdmax2 = distance(mdx,mdy,xhi,ylo); // enclosing rectangle corners if (mdmax2 > mdmax) mdmax = mdmax2; mdmax2 = distance(mdx,mdy,xhi,yhi); if (mdmax2 > mdmax) mdmax = mdmax2; mdmax2 = distance(mdx,mdy,xlo,yhi); if (mdmax2 > mdmax) mdmax = mdmax2; for (py = ylo; py < yhi; py++) // loop all pixels in enclosing rectangle for (px = xlo; px < xhi; px++) { ddx = (px - mdx); // pixel distance to mouse initial position ddy = (py - mdy); dpm1 = sqrt(ddx*ddx + ddy*ddy); ddx -= mdw; // pixel distance to mouse curr. position ddy -= mdh; dpm2 = sqrt(ddx*ddx + ddy*ddy); dpm = 0.5 * (dpm1 + dpm2); // mean mag = 1.0 - dpm / mdmax; // 1...0 for pixel-mouse 0...mdmax dispx = -mdw * mag; dispy = -mdh * mag; // pixel movement from drag movement ii = py * ww + px; dispx += WarpAx[ii]; // add this warp to prior dispy += WarpAy[ii]; dpx = px + dispx; // source virtual pixel dpy = py + dispy; // (nearest real pixel) if (dpx < 0) dpx = 0; if (dpx > ww-1) dpx = ww-1; if (dpy < 0) dpy = 0; if (dpy > hh-1) dpy = hh-1; jj = dpy * ww + dpx; dpe = sa_pixmap[jj]; // distance from area edge if (dpe < 1) // outside area dispx = dispy = 0; if (acc) { // mouse drag done, WarpAx[ii] = dispx; // accumulate warp memory WarpAy[ii] = dispy; continue; } if (dpe) { // source pixel inside area vstat = vpixel(E1pxm,px+dispx,py+dispy,vpix); // = input virtual pixel if (vstat) { pix3 = PXMpix(E3pxm,px,py); // output pixel memcpy(pix3,vpix,pcc); continue; } } pix1 = PXMpix(E1pxm,px,py); // pixel is unchanged pix3 = PXMpix(E3pxm,px,py); memcpy(pix3,pix1,pcc); } ww = xhi - xlo; // update window hh = yhi - ylo; Fpaint3(xlo,ylo,ww,hh,0); CEF->Fmods++; CEF->Fsaved = 0; return; } // expand select area if pulled outside the original bounds void WarpA_expand() { int ii, px, py, ww, hh; int blend = sa_blendwidth; 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_blendwidth = 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_blendwidth) 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_blendwidth) continue; // beyond blend area dispx = WarpAx[ii]; // warp dispy = WarpAy[ii]; vstat = vpixel(E1pxm,px+dispx,py+dispy,vpix); // input virtual pixel (new) if (! vstat) continue; pix1 = PXMpix(E1pxm,px,py); // input pixel (old) pix3 = PXMpix(E3pxm,px,py); // output pixel dnew = sa_blendfunc(dist); dold = 1.0 - dnew; pix3[0] = dnew * vpix[0] + dold * pix1[0]; pix3[1] = dnew * vpix[1] + dold * pix1[1]; pix3[2] = dnew * vpix[2] + dold * pix1[2]; } ww = sa_maxx - sa_minx; // update window hh = sa_maxy - sa_miny; Fpaint3(sa_minx,sa_miny,ww,hh,0); return; } /********************************************************************************/ // warp/distort whole image with a curved transform // fix perspective problems (e.g. curved walls, leaning buildings) namespace warpC_names { float *WarpCx, *WarpCy; // memory of all dragged pixels float WarpCmem[5][100]; // undo memory, last 100 drags int NWarpC; // WarpCmem count int WarpCdrag; int E3ww, E3hh; float $mdx, $mdy, $mdw, $mdh; // warpC_warpfunc() and warpC_wthread() float $D, $span; // use these $ args int $acc; editfunc EFwarpC; int WarpC_dialog_event(zdialog *zd, cchar *event); void WarpC_warpfunc(); void WarpC_mousefunc(void); void * WarpC_wthread(void *arg); } // menu function void m_warp_curved(GtkWidget *, cchar *) { using namespace warpC_names; cchar *WarpC_message = E2X( " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]."); int px, py, ii; F1_help_topic = "warp curved"; EFwarpC.menufunc = m_warp_curved; EFwarpC.funcname = "warp_curved"; EFwarpC.FprevReq = 1; // use preview EFwarpC.mousefunc = WarpC_mousefunc; // mouse function if (! edit_setup(EFwarpC)) return; // setup edit PXM_addalpha(E0pxm); PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); zdialog *zd = zdialog_new(E2X("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",E2X("warp span"),"space=8"); zdialog_add_widget(zd,"zspin","span","hb2","0.00|1.0|0.01|0.1","space=1"); currgrid = 3; // use warp_curved grid NWarpC = WarpCdrag = 0; // no drag data int cc = E3pxm->ww * E3pxm->hh * sizeof(float); WarpCx = (float *) zmalloc(cc); // get memory for pixel displacements WarpCy = (float *) zmalloc(cc); for (py = 0; py < E3pxm->hh; py++) // no pixel displacements for (px = 0; px < E3pxm->ww; px++) { ii = py * E3pxm->ww + px; WarpCx[ii] = WarpCy[ii] = 0.0; } E3ww = E3pxm->ww; // preview dimensions E3hh = E3pxm->hh; zdialog_restore_inputs(zd); // restore previous inputs zdialog_fetch(zd,"span",$span); // save span value zdialog_run(zd,WarpC_dialog_event,"save"); // run dialog, parallel takeMouse(WarpC_mousefunc,dragcursor); // connect mouse function return; } // dialog event and completion callback function int warpC_names::WarpC_dialog_event(zdialog * zd, cchar *event) { using namespace warpC_names; int px, py, ii; int fpx, fpy, epx, epy, vstat; float scale, dispx, dispy; float vpix[4], *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) goto complete; if (strmatch(event,"undolast")) { if (NWarpC == 1) event = "undoall"; else if (NWarpC) { // undo most recent drag ii = --NWarpC; $mdx = WarpCmem[0][ii]; $mdy = WarpCmem[1][ii]; $mdw = -WarpCmem[2][ii]; $mdh = -WarpCmem[3][ii]; $span = WarpCmem[4][ii]; zdialog_stuff(zd,"span",$span); $acc = 0; WarpC_warpfunc(); // undrag image $acc = 1; WarpC_warpfunc(); // undrag memory } } if (strmatch(event,"undoall")) // undo all drags { NWarpC = 0; // erase undo memory for (py = 0; py < E3pxm->hh; py++) // reset pixel displacements for (px = 0; px < E3pxm->ww; px++) { ii = py * E3pxm->ww + px; WarpCx[ii] = WarpCy[ii] = 0.0; } edit_reset(); // restore image 1 } if (strmatch(event,"grid")) m_gridlines(0,"grid 3"); // grid settings dialog if (strmatch(event,"span")) zdialog_fetch(zd,"span",$span); return 1; complete: currgrid = 0; // restore normal grid settings if (zd->zstat) { if (NWarpC == 0) zd->zstat = 2; // no warps, cancel if (zd->zstat == 1) // done { edit_fullsize(); // get full size image scale = 1.0 * (E3pxm->ww + E3pxm->hh) / (E3ww + E3hh); for (fpy = 0; fpy < E3pxm->hh; fpy++) // scale net pixel displacements for (fpx = 0; fpx < E3pxm->ww; fpx++) // to full image size { epx = E3ww * fpx / E3pxm->ww; epy = E3hh * fpy / E3pxm->hh; ii = epy * E3ww + epx; dispx = WarpCx[ii] * scale; dispy = WarpCy[ii] * scale; vstat = vpixel(E1pxm,fpx+dispx,fpy+dispy,vpix); // input virtual pixel pix3 = PXMpix(E3pxm,fpx,fpy); // output pixel if (vstat) memcpy(pix3,vpix,pcc); else memset(pix3,0,pcc); // voided pixel } signal_thread(); edit_done(0); // commit edit } else edit_cancel(0); // discard edit zfree(WarpCx); // release memory zfree(WarpCy); } return 1; } // WarpC mouse function void warpC_names::WarpC_mousefunc() { 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; do_wthreads(WarpC_wthread,NWT); // worker threads 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 } pthread_exit(0); // exit thread } /********************************************************************************/ // warp/distort whole image with a linear transform (almost) // fix perspective problems (e.g. leaning buildings) namespace warpL_names { float *WarpLx, *WarpLy; // memory of all dragged pixels float WarpLmem[4][100]; // undo memory, last 100 drags int NWarpL; // WarpLmem count int WarpLdrag; int E3ww, E3hh; float $mdx, $mdy, $mdw, $mdh; // warpL_warpfunc() and warpL_wthread() int $D, $Dx, $Dy, $acc; // use these $ args editfunc EFwarpL; int WarpL_dialog_event(zdialog *zd, cchar *event); void WarpL_mousefunc(void); void WarpL_warpfunc(); void * WarpL_wthread(void *arg); } // menu function void m_warp_linear(GtkWidget *, cchar *) { using namespace warpL_names; cchar *WarpL_message = E2X( " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]."); int px, py, ii; F1_help_topic = "warp linear"; EFwarpL.menufunc = m_warp_linear; EFwarpL.funcname = "warp_linear"; EFwarpL.FprevReq = 1; // use preview EFwarpL.mousefunc = WarpL_mousefunc; // mouse function if (! edit_setup(EFwarpL)) return; // setup edit PXM_addalpha(E0pxm); PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); zdialog *zd = zdialog_new(E2X("Warp linear"),Mwin,Bdone,Bcancel,null); EFwarpL.zd = zd; zdialog_add_widget(zd,"label","lab1","dialog",WarpL_message,"space=3"); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=10"); zdialog_add_widget(zd,"button","undolast","hb1",Bundolast,"space=5"); zdialog_add_widget(zd,"button","undoall","hb1",Bundoall,"space=5"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=6"); zdialog_add_widget(zd,"button","grid","hb2",Bgrid,"space=10"); currgrid = 4; // use warp_linear grid NWarpL = WarpLdrag = 0; // no drag data int cc = E3pxm->ww * E3pxm->hh * sizeof(float); WarpLx = (float *) zmalloc(cc); // get memory for pixel displacements WarpLy = (float *) zmalloc(cc); for (py = 0; py < E3pxm->hh; py++) // no pixel displacements for (px = 0; px < E3pxm->ww; px++) { ii = py * E3pxm->ww + px; WarpLx[ii] = WarpLy[ii] = 0.0; } E3ww = E3pxm->ww; // preview dimensions E3hh = E3pxm->hh; zdialog_run(zd,WarpL_dialog_event,"save"); // run dialog, parallel takeMouse(WarpL_mousefunc,dragcursor); // connect mouse function return; } // dialog event and completion callback function int warpL_names::WarpL_dialog_event(zdialog * zd, cchar *event) { using namespace warpL_names; int px, py, ii; int fpx, fpy, epx, epy, vstat; float scale, dispx, dispy; float vpix[4], *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) goto complete; if (strmatch(event,"undolast")) { if (NWarpL == 1) event = "undoall"; else if (NWarpL) { // undo most recent drag ii = --NWarpL; $mdx = WarpLmem[0][ii]; $mdy = WarpLmem[1][ii]; $mdw = -WarpLmem[2][ii]; $mdh = -WarpLmem[3][ii]; $acc = 0; WarpL_warpfunc(); // undrag image $acc = 1; WarpL_warpfunc(); // undrag memory } } if (strmatch(event,"undoall")) // undo all drags { NWarpL = 0; // erase undo memory for (py = 0; py < E3pxm->hh; py++) // reset pixel displacements for (px = 0; px < E3pxm->ww; px++) { ii = py * E3pxm->ww + px; WarpLx[ii] = WarpLy[ii] = 0.0; } edit_reset(); // restore image 1 } if (strmatch(event,"grid")) m_gridlines(0,"grid 4"); // grid settings dialog return 1; complete: currgrid = 0; // restore normal grid settings if (zd->zstat != 1 || NWarpL == 0) // cancel or no warps made edit_cancel(0); else { edit_fullsize(); // get full-size E1/E3 scale = 1.0 * (E3pxm->ww + E3pxm->hh) / (E3ww + E3hh); for (fpy = 0; fpy < E3pxm->hh; fpy++) // scale net pixel displacements for (fpx = 0; fpx < E3pxm->ww; fpx++) // to full image size { epx = E3ww * fpx / E3pxm->ww; epy = E3hh * fpy / E3pxm->hh; ii = epy * E3ww + epx; dispx = WarpLx[ii] * scale; dispy = WarpLy[ii] * scale; vstat = vpixel(E1pxm,fpx+dispx,fpy+dispy,vpix); // input virtual pixel pix3 = PXMpix(E3pxm,fpx,fpy); // output pixel if (vstat) memcpy(pix3,vpix,pcc); else memset(pix3,0,pcc); // voided pixel } signal_thread(); edit_done(0); } zfree(WarpLx); // release memory zfree(WarpLy); return 1; } // WarpL mouse function void warpL_names::WarpL_mousefunc() { 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; } do_wthreads(WarpL_wthread,NWT); // worker threads CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window return; } // working thread to process the pixels void * warpL_names::WarpL_wthread(void *arg) { 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 } pthread_exit(0); // exit thread } /********************************************************************************/ // warp/distort whole image using affine transform // (straight lines remain straight) float WarpF_old[3][2]; // 3 original image points float WarpF_new[3][2]; // corresponding warped points float WarpF_coeff[6]; // transform coefficients float WarpF_Icoeff[6]; // inverse transform coefficients int WarpF_ftf; // first time flag editfunc EFwarpF; void WarpF_warpfunc(); // image warp function void WarpF_mousefunc(void); void WarpF_affine(float po[3][2], float pn[3][2], float coeff[6]); // compute affine transform coefficients void WarpF_invert(float coeff[6], float Icoeff[6]); // compute reverse transform coefficients // menu function void m_warp_affine(GtkWidget *, cchar *) { int WarpF_dialog_event(zdialog *zd, cchar *event); cchar *WarpF_message = E2X( " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]."); F1_help_topic = "warp affine"; EFwarpF.menufunc = m_warp_affine; EFwarpF.funcname = "warp_affine"; EFwarpF.FprevReq = 1; // use preview EFwarpF.mousefunc = WarpF_mousefunc; // mouse function if (! edit_setup(EFwarpF)) return; // setup edit PXM_addalpha(E0pxm); PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); zdialog *zd = zdialog_new(E2X("Warp affine"),Mwin,Bdone,Bcancel,null); EFwarpF.zd = zd; zdialog_add_widget(zd,"label","lab1","dialog",WarpF_message,"space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=6"); zdialog_add_widget(zd,"button","grid","hb2",Bgrid,"space=10"); currgrid = 5; // use warp_affine grid WarpF_ftf = 1; // 1st warp flag zdialog_run(zd,WarpF_dialog_event,"save"); // run dialog, parallel takeMouse(WarpF_mousefunc,dragcursor); // connect mouse function return; } // dialog event and completion callback function int WarpF_dialog_event(zdialog *zd, cchar *event) { float scale; int ww, hh; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"grid")) m_gridlines(0,"grid 5"); // grid settings dialog if (! zd->zstat) return 1; // wait for completion currgrid = 0; // restore normal grid settings if (zd->zstat != 1 || ! CEF->Fmods) { edit_cancel(0); return 1; } ww = E3pxm->ww; // preview image dimensions hh = E3pxm->hh; edit_fullsize(); // get full-size images scale = 1.0 * (E3pxm->ww + E3pxm->hh) / (ww + hh); // preview to full-size scale factor WarpF_old[0][0] = WarpF_old[0][0] * scale; // re-scale new and old points WarpF_old[0][1] = WarpF_old[0][1] * scale; WarpF_old[1][0] = WarpF_old[1][0] * scale; WarpF_old[1][1] = WarpF_old[1][1] * scale; WarpF_old[2][0] = WarpF_old[2][0] * scale; WarpF_old[2][1] = WarpF_old[2][1] * scale; WarpF_new[0][0] = WarpF_new[0][0] * scale; WarpF_new[0][1] = WarpF_new[0][1] * scale; WarpF_new[1][0] = WarpF_new[1][0] * scale; WarpF_new[1][1] = WarpF_new[1][1] * scale; WarpF_new[2][0] = WarpF_new[2][0] * scale; WarpF_new[2][1] = WarpF_new[2][1] * scale; WarpF_warpfunc(); // warp full-size image edit_done(0); return 1; } // WarpF mouse function void WarpF_mousefunc() { 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 do_wthreads(WarpF_wthread,NWT); // worker threads 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 } pthread_exit(0); // exit thread } /******************************************************************************** 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; } /********************************************************************************/ // Unwarp closeup face photo - shrink magnified areas closest to camera int unwarpCU_started; int unwarpCU_areanumber; float unwarpCU_warpval; int unwarpCU_cx, unwarpCU_cy; editfunc EFunwarpCU; int unwarpCU_dialog_event(zdialog *zd, cchar *event); void unwarpCU_warpfunc(); void unwarpCU_mousefunc(); // menu function void m_unwarp_closeup(GtkWidget *, cchar *) { cchar *unwarpCU_message = E2X( " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n"); F1_help_topic = "unwarp closeup"; EFunwarpCU.menufunc = m_unwarp_closeup; EFunwarpCU.funcname = "unwarp_closeup"; EFunwarpCU.Farea = 2; // select area usable EFunwarpCU.mousefunc = unwarpCU_mousefunc; // mouse function if (! edit_setup(EFunwarpCU)) return; // setup edit /*** ______________________________________ | Unwarp Closeup | | | | Use Select Area to select a face. | | Click on the center of distortion. | | Move the slider. | | | | [===============[]================] | | | | [done] [cancel] | |______________________________________| ***/ zdialog *zd = zdialog_new(E2X("Unwarp Closeup"),Mwin,Bdone,Bcancel,null); EFunwarpCU.zd = zd; zdialog_add_widget(zd,"label","lab1","dialog",unwarpCU_message,"space=3"); zdialog_add_widget(zd,"hbox","hbw","dialog",0,"space=5"); zdialog_add_widget(zd,"hscale","warpval","hbw","0.0|1.0|0.01|0.0","space=5|expand"); takeMouse(unwarpCU_mousefunc,dragcursor); // connect mouse function unwarpCU_started = 0; unwarpCU_areanumber = 0; zdialog_run(zd,unwarpCU_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int unwarpCU_dialog_event(zdialog * zd, cchar *event) { int init = 0; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } if (! sa_validate()) init = 1; // area invalid for curr. image file if (sa_stat != 3 || sa_mode == mode_image) // no select area active init = 1; if (unwarpCU_started && unwarpCU_areanumber != areanumber) // select area changed init = 1; if (init) { unwarpCU_started = 0; unwarpCU_areanumber = 0; return 1; } if (strmatch(event,"focus")) // reconnect mouse takeMouse(unwarpCU_mousefunc,dragcursor); if (strmatch(event,"warpval")) { // slider movement zdialog_fetch(zd,"warpval",unwarpCU_warpval); unwarpCU_warpfunc(); } return 1; } // mouse function void unwarpCU_mousefunc() { if (LMclick) { unwarpCU_cx = Mxclick; // capture central point unwarpCU_cy = Myclick; } else if (Mxdrag || Mydrag) { unwarpCU_cx = Mxdrag; unwarpCU_cy = Mydrag; } else return; LMclick = Mxdrag = Mydrag = 0; if (sa_stat != 3 || sa_mode == mode_image) { // no select area active zmessageACK(Mwin,E2X("no active Select Area")); unwarpCU_started = 0; Mdrag = 0; return; } unwarpCU_started = 1; // unwarp can proceed unwarpCU_areanumber = areanumber; sa_edgecalc(); // calculate area edge distances unwarpCU_warpfunc(); return; } // warp image according to slider position void unwarpCU_warpfunc() { int ii, px, py, ww, vstat; float hsize, vsize, hpos, vpos; float warpval = unwarpCU_warpval; float ed, cx, cy, dx, dy; float vpix[4], *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); if (sa_stat != 3) return; // area erased hsize = sa_maxx - sa_minx; // area size vsize = sa_maxy - sa_miny; cx = unwarpCU_cx; // unwarp center (nose tip) cy = unwarpCU_cy; ww = E1pxm->ww; for (py = sa_miny; py < sa_maxy; py++) // loop all pixels in area for (px = sa_minx; px < sa_maxx; px++) { ii = py * ww + px; if (sa_pixmap[ii] < 1) continue; // pixel outside area hpos = 2.0 * (px - cx) / hsize; // horizontal pixel position, -1 ... +1 vpos = 2.0 * (py - cy) / vsize; // vertical pixel position, -1 ... +1 ed = sa_pixmap[ii]; // pixel edge distance hpos = hpos * ed / hsize; // scale pixel position vpos = vpos * ed / vsize; dx = sinf(PI * hpos); // pixel displacement, -1 ... +1 dy = sinf(PI * vpos); dx = dx * 0.1 * warpval * hsize; // pixel displacement, -10% ... +10% dy = dy * 0.1 * warpval * vsize; vstat = vpixel(E1pxm,px+dx,py+dy,vpix); // input virtual pixel if (vstat) { pix3 = PXMpix(E3pxm,px,py); // output pixel memcpy(pix3,vpix,pcc); } } Fpaint3(sa_minx,sa_miny,hsize,vsize,0); CEF->Fmods++; CEF->Fsaved = 0; return; } /********************************************************************************/ // Flatten a photographed book page. // Compensate for page curvature at the center binding. editfunc EFflatbook; // edit function data namespace flatbook { int Tmx[20], Tmy[20], Bmx[20], Bmy[20]; // top/bottom mouse click points int Tnm = 0, Bnm = 0; // top/bottom click points counts int Tbase, Bbase; // top/bottom points, low values int ww, hh; // image dimensions int E3warped; // flag, E3 image is warped double *Tfy, *Bfy; // derived top/bottom y-shifts [ww] double *Tfx, *Bfx; // derived top/bottom x-shifts [ww] double Tstretch, Bstretch; // top/bottom x-shift stretch factors } // menu function void m_flatbook(GtkWidget *, const char *) { using namespace flatbook; int flatbook_dialog_event(zdialog* zd, const char *event); void * flatbook_thread(void *); void flatbook_mousefunc(); void flatbook_draw(); cchar *title = E2X("Flatten Book Page"); cchar *guide = E2X("Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: "); cchar *stretch = E2X("Stretch curved-down surfaces:"); zdialog *zd; F1_help_topic = "flatten book"; EFflatbook.menufunc = m_flatbook; EFflatbook.funcname = "flatbook"; // func name, no preview, no area EFflatbook.threadfunc = flatbook_thread; EFflatbook.mousefunc = flatbook_mousefunc; if (! edit_setup(EFflatbook)) return; // setup edit int cc = E1pxm->ww * sizeof(double); // allocate memory Tfy = (double *) zmalloc(cc); Bfy = (double *) zmalloc(cc); Tfx = (double *) zmalloc(cc); Bfx = (double *) zmalloc(cc); if (ww != E1pxm->ww) Tnm = Bnm = 0; // clear prior points if image if (hh != E1pxm->hh) Tnm = Bnm = 0; // size is different ww = E1pxm->ww; // set image size hh = E1pxm->hh; E3warped = 0; // no E3 warp yet /*** _________________________________ | Flatten Book Page | | | | Trim image to isolate one page. | | Map top and bottom edges with | | 4+ mouse clicks, then flatten: | | [clear] [flatten] [undo] | | | | Stretch curved-down surfaces: | | Top: ========[]============== | | Bottom: =========[]========== | | | | [done] [cancel] | |_________________________________| ***/ zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); // flatbook dialog EFflatbook.zd = zd; zdialog_add_widget(zd,"hbox","hbg","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labg","hbg",guide,"space=8"); zdialog_add_widget(zd,"hbox","hbf","dialog"); zdialog_add_widget(zd,"button","clear","hbf",Bclear,"space=10"); zdialog_add_widget(zd,"button","flatten","hbf",Bflatten,"space=10"); zdialog_add_widget(zd,"button","undo","hbf",Bundo,"space=10"); zdialog_add_widget(zd,"hbox","space","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbs1","dialog"); zdialog_add_widget(zd,"label","labs1","hbs1",stretch,"space=8"); zdialog_add_widget(zd,"hbox","hbs2","dialog"); zdialog_add_widget(zd,"label","labs2","hbs2",E2X("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",E2X("Bottom:"),"space=8"); zdialog_add_widget(zd,"hscale","bottom","hbs3","1|30|0.01|1","expand|space=5"); zdialog_restore_inputs(zd); // restore prior inputs zdialog_resize(zd,300,0); zdialog_run(zd,flatbook_dialog_event,"save"); // run dialog - parallel takeMouse(flatbook_mousefunc,dragcursor); // connect mouse function if (Tnm + Bnm > 0) flatbook_draw(); return; } // flatbook dialog event and completion function int flatbook_dialog_event(zdialog *zd, const char *event) // flatbook dialog event function { using namespace flatbook; void flatbook_draw(); if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { zfree(Tfy); // free memory zfree(Bfy); zfree(Tfx); zfree(Bfx); if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // cancel or destroy return 1; } if (strmatch(event,"clear")) { // clear all mouse points Tnm = Bnm = 0; edit_undo(); flatbook_draw(); } if (zstrstr("flatten top bottom",event)) { if (Tnm < 4 || Bnm < 4) return 1; zdialog_fetch(zd,"top",Tstretch); zdialog_fetch(zd,"bottom",Bstretch); signal_thread(); // trigger update thread } if (strmatch(event,"undo")) { edit_undo(); E3warped = 0; flatbook_draw(); } if (strmatch(event,"line_color")) flatbook_draw(); // refresh lines after window redraw return 1; } // flatbook mouse function void flatbook_mousefunc() { using namespace flatbook; void flatbook_draw(); int ii, jj, mx, my, dist; int Fadd = 0, close; if (! (LMclick || RMclick || Mxdrag || Mydrag)) return; // ignore mouse movement if (E3warped) return; // E3 image warped, ignore mouse if (LMclick || RMclick) { // left or right mouse click mx = Mxclick; my = Myclick; if (LMclick) Fadd = 1; LMclick = RMclick = 0; } else { // mouse drag mx = Mxdrag; my = Mydrag; Mxdrag = Mydrag = 0; Fadd = 1; } close = 0.01 * ww; // 1% of image size if (my < hh/2) { for (ii = 0; ii < Tnm; ii++) { // compare mouse position with dist = abs(mx-Tmx[ii]); // existing top points if (dist < close) { Tnm--; // delete any too close for (jj = ii--; jj < Tnm; jj++) { Tmx[jj] = Tmx[jj+1]; Tmy[jj] = Tmy[jj+1]; } } } } else { for (ii = 0; ii < Bnm; ii++) { // same for bottom points dist = abs(mx-Bmx[ii]); if (dist < close) { Bnm--; for (jj = ii--; jj < Bnm; jj++) { Bmx[jj] = Bmx[jj+1]; Bmy[jj] = Bmy[jj+1]; } } } } if (Fadd) // add new mouse position { if (my < hh/2) { // add to top points if (Tnm == 20) return; for (ii = 0; ii < Tnm; ii++) if (mx < Tmx[ii]) break; for (jj = Tnm; jj > ii; jj--) { Tmx[jj] = Tmx[jj-1]; Tmy[jj] = Tmy[jj-1]; } Tmx[ii] = mx; Tmy[ii] = my; Tnm++; } else { // add to bottom points if (Bnm == 20) return; for (ii = 0; ii < Bnm; ii++) if (mx < Bmx[ii]) break; for (jj = Bnm; jj > ii; jj--) { Bmx[jj] = Bmx[jj-1]; Bmy[jj] = Bmy[jj-1]; } Bmx[ii] = mx; Bmy[ii] = my; Bnm++; } } edit_undo(); // erase and redraw flatbook_draw(); return; } // generate spline curves from mouse points and draw on the image void flatbook_draw() { using namespace flatbook; float Tnx[20], Tny[20], Bnx[20], Bny[20]; // top/bottom spline nodes float *ppix3; int topmax, topmin, bottmax, bottmin; // limits of image update areas int ii, px, py, qx, qy, npq; topmax = topmin = bottmax = bottmin = 0; npq = 3; // pixel block for drawing nodes if (Tnm > 0) topmax = topmin = Tmy[0]; for (ii = 0; ii < Tnm; ii++) // draw top mouse points { px = Tmx[ii]; py = Tmy[ii]; for (qy = py-npq; qy <= py+npq; qy++) for (qx = px-npq; qx <= px+npq; qx++) { if (qx < 0 || qx > ww-1) continue; if (qy < 0 || qy > hh-1) continue; ppix3 = PXMpix(E3pxm,qx,qy); ppix3[0] = LINE_COLOR[0]; ppix3[1] = LINE_COLOR[1]; ppix3[2] = LINE_COLOR[2]; if (qy > topmax) topmax = qy; if (qy < topmin) topmin = qy; } } if (Bnm > 0) bottmax = bottmin = Bmy[0]; for (ii = 0; ii < Bnm; ii++) // draw bottom mouse points { px = Bmx[ii]; py = Bmy[ii]; for (qy = py-npq; qy <= py+npq; qy++) for (qx = px-npq; qx <= px+npq; qx++) { if (qx < 0 || qx > ww-1) continue; if (qy < 0 || qy > hh-1) continue; ppix3 = PXMpix(E3pxm,qx,qy); ppix3[0] = LINE_COLOR[0]; ppix3[1] = LINE_COLOR[1]; ppix3[2] = LINE_COLOR[2]; if (qy > bottmax) bottmax = qy; if (qy < bottmin) bottmin = qy; } } if (Tnm > 3) // top mouse points { Tbase = Tmy[0]; // find lowest top point for (ii = 0; ii < Tnm; ii++) if (Tmy[ii] < Tbase) Tbase = Tmy[ii]; for (ii = 0; ii < Tnm; ii++) { // copy top points to spline nodes Tnx[ii] = Tmx[ii]; // and convert to 0 base Tny[ii] = Tmy[ii] - Tbase; } spline1(Tnm,Tnx,Tny); // generate spline curve for (px = 0; px < ww; px++) // generate top curve y shifts Tfy[px] = spline2(px); for (px = 0; px < ww; px++) { // draw top curve over image py = Tfy[px] + Tbase; if (py < 0 || py > hh-1) continue; ppix3 = PXMpix(E3pxm,px,py); ppix3[0] = LINE_COLOR[0]; ppix3[1] = LINE_COLOR[1]; ppix3[2] = LINE_COLOR[2]; if (py < topmin) topmin = py; if (py > topmax) topmax = py; } } if (Bnm > 3) // bottom mouse points { Bbase = Bmy[0]; // find lowest bottom point for (ii = 0; ii < Bnm; ii++) if (Bmy[ii] < Bbase) Bbase = Bmy[ii]; for (ii = 0; ii < Bnm; ii++) { // copy bottom points to spline nodes Bnx[ii] = Bmx[ii]; // and convert to 0 base Bny[ii] = Bmy[ii] - Bbase; } spline1(Bnm,Bnx,Bny); // generate spline curve for (px = 0; px < ww; px++) // generate bottom curve y shifts Bfy[px] = spline2(px); for (px = 0; px < ww; px++) { // draw bottom curve over image py = Bfy[px] + Bbase; if (py < 0 || py > hh-1) continue; ppix3 = PXMpix(E3pxm,px,py); ppix3[0] = LINE_COLOR[0]; ppix3[1] = LINE_COLOR[1]; ppix3[2] = LINE_COLOR[2]; if (py < bottmin) bottmin = py; if (py > bottmax) bottmax = py; } } if (Tnm) Fpaint3(0,topmin,ww,topmax-topmin,0); // paint modified areas if (Bnm) Fpaint3(0,bottmin,ww,bottmax-bottmin,0); if (Tnm + Bnm) CEF->Fmods++; // necessary CEF->Fsaved = 0; return; } // flatbook thread function - warp image based on input parameters void * flatbook_thread(void *) { using namespace flatbook; void * flatbook_wthread(void *arg); // worker thread int px; double Tslope, Bslope, slope, Rt, Rb; while (true) { thread_idle_loop(); while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 E3warped = 1; // flag, E3 image is warped Tfx[0] = Bfx[0] = 0; for (px = 1; px < ww; px++) // loop page width { Tslope = fabs(Tfy[px] - Tfy[px-1]); // slope of top function Bslope = fabs(Bfy[px] - Bfy[px-1]); // slope of bottom function slope = Tslope * Tstretch; // mean slope, stretched slope = cos(atan(slope)); Tfx[px] = Tfx[px-1] + slope; // resulting pixel x-shift slope = Bslope * Bstretch; slope = cos(atan(slope)); Bfx[px] = Bfx[px-1] + slope; } Rt = (ww-1) / Tfx[ww-1]; // make overall width the same Rb = (ww-1) / Bfx[ww-1]; for (px = 0; px < ww; px++) { Tfx[px] = Rt * Tfx[px]; // (using separate loops causes Bfx[px] = Rb * Bfx[px]; // GCC optimization bug) } do_wthreads(flatbook_wthread,NWT); // worker threads CEF->Fmods++; // image is modified CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.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 } } pthread_exit(0); } /********************************************************************************/ // Rescale an image while leaving selected areas unchanged. namespace area_rescale_names { editfunc EFarea_rescale; int Fsetups = 0; int dragx, dragy; int E3ww, E3hh; char *sqrow, *sqcol; int Nsqrow, Nsqcol; int *npx, *npy; int dialog_event(zdialog *zd, cchar *event); void setups(); void cleanups(); void mousefunc(); void warpfunc(); void *warpthread(void *); } // menu function void m_area_rescale(GtkWidget *, cchar *) { using namespace area_rescale_names; cchar *message = E2X(" Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]."); F1_help_topic = "area rescale"; EFarea_rescale.menufunc = m_area_rescale; EFarea_rescale.funcname = "area_rescale"; EFarea_rescale.Farea = 2; // select area usable EFarea_rescale.mousefunc = mousefunc; // mouse function if (! edit_setup(EFarea_rescale)) return; // setup edit PXM_addalpha(E0pxm); PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); zdialog *zd = zdialog_new(E2X("Area Rescale"),Mwin,Bproceed,Bdone,Bcancel,null); EFarea_rescale.zd = zd; zdialog_add_widget(zd,"label","lab1","dialog",message,"space=3"); zdialog_run(zd,dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int area_rescale_names::dialog_event(zdialog * zd, cchar *event) { using namespace area_rescale_names; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) { // [proceed] zd->zstat = 0; // keep dialog active if (sa_stat != 3) zmessageACK(Mwin,E2X("select areas first")); else setups(); // start drag and warp return 1; } if (zd->zstat != 2 || dragx + dragy == 0) { // [cancel] or no change edit_cancel(0); cleanups(); return 1; } edit_done(0); // [done] cleanups(); m_trim_rotate(0,"auto"); // set up automatic trim return 1; } // do setups based on select area data void area_rescale_names::setups() { int ii, spx, spy, sum; cleanups(); // free prior if any dragx = dragy = 0; // no drag data E3ww = E3pxm->ww; // image dimensions E3hh = E3pxm->hh; sqrow = (char *) zmalloc(E3hh); // maps squishable rows/cols sqcol = (char *) zmalloc(E3ww); memset(sqrow,1,E3hh); // mark all rows/cols squishable memset(sqcol,1,E3ww); for (spy = 0; spy < E3hh; spy++) // loop all source pixels for (spx = 0; spx < E3ww; spx++) { ii = spy * E3ww + spx; // pixel within area? if (sa_pixmap[ii]) sqrow[spy] = sqcol[spx] = 0; // mark row/col non-squishable } Nsqrow = Nsqcol = 0; for (spy = 0; spy < E3hh; spy++) // count total squishable rows/cols Nsqrow += sqrow[spy]; for (spx = 0; spx < E3ww; spx++) Nsqcol += sqcol[spx]; npx = (int *) zmalloc(E3ww * sizeof(int)); // count of squishable rows/cols npy = (int *) zmalloc(E3hh * sizeof(int)); // predeeding a given row/col for (sum = spx = 0; spx < E3ww; spx++) { if (sqcol[spx]) sum++; npx[spx] = sum; } for (sum = spy = 0; spy < E3hh; spy++) { if (sqrow[spy]) sum++; npy[spy] = sum; // squishable rows < spy } Fsetups = 1; sa_clear(); // clear area takeMouse(mousefunc,dragcursor); // connect mouse function return; } // free allocated memory void area_rescale_names::cleanups() { if (! Fsetups) return; Fsetups = 0; zfree(sqrow); zfree(sqcol); zfree(npx); zfree(npy); return; } // mouse function void area_rescale_names::mousefunc() { using namespace area_rescale_names; float R; if (Mxdrag || Mydrag) // mouse drag underway { R = 1.0 * Mxdown / E3ww; // ignore drag not from NW corner if (R > 0.2) return; R = 1.0 * Mydown / E3hh; if (R > 0.2) return; dragx = Mxdrag - Mxdown; // drag amount dragy = Mydrag - Mydown; warpfunc(); // drag image Mxdrag = Mydrag = 0; } return; } // warp function void area_rescale_names::warpfunc() { using namespace area_rescale_names; do_wthreads(warpthread,NWT); // worker threads CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window return; } // warp thread void * area_rescale_names::warpthread(void *arg) { using namespace area_rescale_names; int index = *((int *) (arg)); int spx, spy, dpx, dpy; float Rx, Ry; float *spix, *dpix; int nc = E1pxm->nc, pcc = nc * sizeof(float); for (spy = index; spy < E3hh; spy += NWT) // loop all source pixels for (spx = 0; spx < E3ww; spx++) { if (spx < dragx || spy < dragy) { // pixels < dragx/dragy: spix = PXMpix(E3pxm,spx,spy); // black, transparent memset(spix,0,pcc); } Rx = 1.0 * npx[spx] / Nsqcol; // squishable pixel ratios, 0 - 1.0 Ry = 1.0 * npy[spy] / Nsqrow; dpx = spx + dragx * (1.0 - Rx); // destination pixel dpy = spy + dragy * (1.0 - Ry); if (dpx < 0 || dpx > E3ww-1) continue; // necessary, why? if (dpy < 0 || dpy > E3hh-1) continue; dpix = PXMpix(E3pxm,dpx,dpy); // source pixel >> destination pixel spix = PXMpix(E1pxm,spx,spy); memcpy(dpix,spix,pcc); } pthread_exit(0); } /********************************************************************************/ // make waves menu function // distort the image with a wave pattern // independent horizontal and vertical wavelengths // variance option: make waves more or less irregular // perspective option: wavelengths lengthen going down namespace waves_names { editfunc EFwaves; int ww, hh; // image dimensions int WLV, WLH; // vertical and horizontal wavelengths int AMPV, AMPH; // vertical and horizontal amplitudes int VARV, VARH; // vertical and horizontal variance int PERSP; // perspective 0-100 } // menu function void m_waves(GtkWidget *, const char *) { using namespace waves_names; int waves_dialog_event(zdialog* zd, const char *event); void * waves_thread(void *); F1_help_topic = "make waves"; EFwaves.menufunc = m_waves; EFwaves.funcname = "make_waves"; EFwaves.Farea = 2; // select area usable EFwaves.threadfunc = waves_thread; if (! edit_setup(EFwaves)) return; /*** ___________________________________ | Make waves | | | | Horizontal Vertical | | wavelength [___] [___] | | amplitude [___] [___] | | variance [___] [___] | | | | perspective [___] | | | | [apply] [done] [cancel] | |___________________________________| ***/ ww = E3pxm->ww; hh = E3pxm->hh; zdialog *zd = zdialog_new(E2X("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",E2X("wavelength"),"expand"); zdialog_add_widget(zd,"label","labamp","vbw1",E2X("amplitude"),"expand"); zdialog_add_widget(zd,"label","labamp","vbw1",E2X("variance"),"expand"); zdialog_add_widget(zd,"label","labh","vbw2",E2X("horizontal"),"space=1"); zdialog_add_widget(zd,"zspin","wlh","vbw2","3|500|1|50","expand"); zdialog_add_widget(zd,"zspin","amph","vbw2","0|100|1|20","expand"); zdialog_add_widget(zd,"zspin","varh","vbw2","0|100|1|20","expand"); zdialog_add_widget(zd,"label","labh","vbw3",E2X("vertical"),"space=1"); zdialog_add_widget(zd,"zspin","wlv","vbw3","3|500|1|50","expand"); zdialog_add_widget(zd,"zspin","ampv","vbw3","0|100|1|20","expand"); zdialog_add_widget(zd,"zspin","varv","vbw3","0|100|1|20","expand"); zdialog_add_widget(zd,"hsep","sepp","dialog",0,"space=3"); zdialog_add_widget(zd,"hbox","hbp","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labp","hbp",E2X("perspective"),"space=3"); zdialog_add_widget(zd,"zspin","persp","hbp","0|100|1|0","space=5"); zdialog_restore_inputs(zd); // restore previous inputs zdialog_run(zd,waves_dialog_event,"save"); // run dialog - parallel return; } // waves dialog event and completion function int waves_dialog_event(zdialog *zd, const char *event) // waves dialog event function { using namespace waves_names; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { // apply zd->zstat = 0; // keep dialog active edit_reset(); zdialog_fetch(zd,"wlv",WLV); // get user inputs zdialog_fetch(zd,"wlh",WLH); zdialog_fetch(zd,"ampv",AMPV); zdialog_fetch(zd,"amph",AMPH); zdialog_fetch(zd,"varv",VARV); zdialog_fetch(zd,"varh",VARH); zdialog_fetch(zd,"persp",PERSP); signal_thread(); // calculate return 1; } if (zd->zstat == 2) edit_done(0); // commit edit else edit_cancel(0); // discard edit } return 1; } // thread function - multiple working threads to update image void * waves_thread(void *) { using namespace waves_names; void * waves_wthread(void *); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 do_wthreads(waves_wthread,NWT); // worker threads CEF->Fmods++; // image modified CEF->Fsaved = 0; // not saved paintlock(0); // unblock window paint 19.0 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_blendwidth) { // blend edges f1 = sa_blendfunc(edist); f2 = 1.0 - f1; pix1 = PXMpix(E1pxm,px,py); red = f1 * red + f2 * pix1[0]; green = f1 * green + f2 * pix1[1]; blue = f1 * blue + f2 * pix1[2]; } pix3 = PXMpix(E3pxm,px,py); // destination pixel pix3[0] = red; pix3[1] = green; pix3[2] = blue; } } pthread_exit(0); // exit thread } /********************************************************************************/ // Twist the image with max. rotation at mouse center and none at farthest edge namespace twist_names { editfunc EFtwist; int cx, cy; int ww, hh; float twist; float center; float rotate; } // menu function void m_twist(GtkWidget *, cchar *) { using namespace twist_names; int twist_dialog_event(zdialog *zd, cchar *event); void * twist_thread(void *); void twist_mousefunc(); F1_help_topic = "twist"; cchar *title = E2X("Twist"); m_zoom(0,"fit"); // zoom to fit window EFtwist.menufunc = m_twist; EFtwist.funcname = "twist"; EFtwist.FprevReq = 1; // use preview image EFtwist.threadfunc = twist_thread; // thread function EFtwist.mousefunc = twist_mousefunc; // mouse function if (! edit_setup(EFtwist)) return; // setup edit PXM_addalpha(E0pxm); // add alpha channel if missing PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); /*** _________________________________________ | Twist | | | | Drag mouse to set center | | | | Twist ==============[]============= | | Center ==========[]================= | | Rotate ======[]===================== | | | | [Reset] [Done] [Cancel] | |_________________________________________| ***/ zdialog *zd = zdialog_new(title,Mwin,Breset,Bdone,Bcancel,null); EFtwist.zd = zd; zdialog_add_widget(zd,"label","labtip","dialog",E2X("Drag mouse to set center")); zdialog_add_widget(zd,"hbox","hba","dialog",0,"space=3"); zdialog_add_widget(zd,"label","laba","hba",E2X("Twist"),"space=3"); zdialog_add_widget(zd,"hscale","twist","hba","-1.0|+1.0|0.01|0.0","space=5|expand"); zdialog_add_widget(zd,"hbox","hbp","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labp","hbp",Bcenter,"space=3"); zdialog_add_widget(zd,"hscale","center","hbp","1.0|3.0|0.1|1.0","space=5|expand"); zdialog_add_widget(zd,"hbox","hbc","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labc","hbc",Brotate,"space=3"); zdialog_add_widget(zd,"hscale","rotate","hbc","-4.0|4.0|0.01|0.0","space=5|expand"); ww = E3pxm->ww; hh = E3pxm->hh; cx = ww / 2; // initial data cy = hh / 2; twist = 0.0; center = 1.0; rotate = 0.0; signal_thread(); zdialog_resize(zd,300,0); zdialog_run(zd,twist_dialog_event,"save"); // run dialog - parallel takeMouse(twist_mousefunc,dragcursor); // connect mouse function return; } // dialog event and completion callback function int twist_dialog_event(zdialog *zd, cchar *event) { using namespace twist_names; void twist_mousefunc(); if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); signal_thread(); return 1; } if (zd->zstat) { if (zd->zstat == 1) { // reset zd->zstat = 0; twist = 0.0; center = 1.0; rotate = 0.0; zdialog_stuff(zd,"twist",0.0); zdialog_stuff(zd,"center",1.0); zdialog_stuff(zd,"rotate",0.0); signal_thread(); } else if (zd->zstat == 2) { // done wait_thread_idle(); // insure thread done float R = 1.0 * (E0pxm->ww + E0pxm->hh) / (E3pxm->ww + E3pxm->hh); cx = R * cx; // scale geometries to full size cy = R * cy; edit_fullsize(); // get full size image signal_thread(); edit_done(0); // commit edit } else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(twist_mousefunc,dragcursor); // connect mouse function if (strmatch(event,"blendwidth")) signal_thread(); if (strmatch(event,"twist")) { zdialog_fetch(zd,"twist",twist); signal_thread(); } if (strmatch(event,"center")) { zdialog_fetch(zd,"center",center); signal_thread(); } if (strmatch(event,"rotate")) { zdialog_fetch(zd,"rotate",rotate); signal_thread(); } return 1; } // get mouse position and set new center for twist void twist_mousefunc() { using namespace twist_names; if (! LMclick && ! Mdrag) return; cx = Mxposn; // new twist center = mouse position cy = Myposn; signal_thread(); // trigger image update LMclick = Mxdrag = Mydrag = 0; return; } // thread function void * twist_thread(void *) { using namespace twist_names; void * twist_wthread(void *arg); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 PXM_clear(E3pxm,0); // clear E3 to black/transparent do_wthreads(twist_wthread,NWT); // worker threads CEF->Fmods++; CEF->Fsaved = 0; paintlock(0); // unblock window paint 19.0 Fpaint2(); } return 0; // not executed, stop g++ warning } // working thread void * twist_wthread(void *arg) { using namespace twist_names; int index, vstat; int px3, py3, px33, py33; float qx, qy; float vpix[4], *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); int Dx, Dy, Dnw, Dne, Dse, Dsw; float D, DN, Dmax; float T, Tp, Tq, Tmax; ww = E3pxm->ww; hh = E3pxm->hh; Dnw = cx * cx + cy * cy; // get distance from center Dne = (ww-cx) * (ww-cx) + cy * cy; // to nearestest corner Dse = (ww-cx) * (ww-cx) + (hh-cy) * (hh-cy); Dsw = cx * cx + (hh-cy) * (hh - cy); Dmax = Dnw; if (Dne > Dmax) Dmax = Dne; if (Dse > Dmax) Dmax = Dse; if (Dsw > Dmax) Dmax = Dsw; Dmax = sqrtf(Dmax); Tmax = 6 * PI * twist; // twist angle index = *((int *) arg); for (py3 = index; py3 < hh; py3 += NWT) // loop all output pixels for (px3 = 0; px3 < ww; px3++) { Dx = px3 - cx; // px/py relative to cx/cy Dy = py3 - cy; D = Dx * Dx + Dy * Dy; // distance pixel to center if (D == 0) continue; D = sqrtf(D); Tp = asinf(Dy/D); // angle of pixel line to center if (Dx < 0) { if (Dy > 0) Tp = PI - Tp; else Tp = - PI - Tp; } DN = pow((D/Dmax),center); // distance normalized, 0.0 ... 1.0 T = Tmax * DN; // rotation at distance from center T = T + rotate; Tq = Tp + T; // rotated pixel angle qx = D * cosf(Tq); // rotated pixel position qy = D * sinf(Tq); qx = 2.0 * qx + cx; // 19.0 qy = 2.0 * qy + cy; vstat = vpixel(E1pxm,qx,qy,vpix); // input pixel px33 = px3 - (cx - ww/2); // move output image to center py33 = py3 - (cy - hh/2); if (px33 < 0 || px33 > ww-1) continue; if (py33 < 0 || py33 > hh-1) continue; pix3 = PXMpix(E3pxm,px33,py33); // output pixel if (vstat) memcpy(pix3,vpix,pcc); else memset(pix3,0,pcc); } pthread_exit(0); } /********************************************************************************/ // project image on to a sphere with adjustable radius (flatness) // image shrinks with increasing radius namespace sphere_names { int E3ww, E3hh; // image dimensions int Xcen, Ycen, D1; // center and diameter of sphere float flatten; // flatten parameter float magnify; // magnify parameter editfunc EFsphere; // edit function data } // menu function void m_sphere(GtkWidget *, const char *) { using namespace sphere_names; int sphere_dialog_event(zdialog* zd, const char *event); void sphere_mousefunc(void); void * sphere_thread(void *); cchar *title = E2X("Spherical Projection"); F1_help_topic = "sphere"; m_zoom(0,"fit"); // zoom to fit window 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); // add alpha channel if missing PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); E3ww = E3pxm->ww; E3hh = E3pxm->hh; Xcen = E3ww / 2; // set dialog defaults Ycen = E3hh / 2; D1 = E3ww; if (E3hh < E3ww) D1 = E3hh; flatten = 0; magnify = 1.0; /*** _____________________________ | Spherical Projection | | | | Drag mouse to set center | | | | Flatten ======[]========== | | Magnify ==========[]====== | | | | [done] [cancel] | |_____________________________| ***/ zdialog *zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); // sphere dialog CEF->zd = zd; zdialog_add_widget(zd,"label","labtip","dialog",E2X("Drag mouse to set center")); zdialog_add_widget(zd,"hbox","hbflat","dialog",0,"space=5"); 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",Bmagnify,"space=5"); zdialog_add_widget(zd,"hscale","magnify","hbmag","1.0|2.0|0.001|1.0","expand"); zdialog_resize(zd,250,0); zdialog_run(zd,sphere_dialog_event,"save"); // run dialog - parallel takeMouse(sphere_mousefunc,0); // connect mouse function signal_thread(); // trigger update thread return; } // sphere dialog event and completion function int sphere_dialog_event(zdialog *zd, const char *event) { using namespace sphere_names; float scale; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) // done { edit_fullsize(); // get full size image scale = 1.0 * (E3pxm->ww + E3pxm->hh) / (E3ww + E3hh); // scale up the parameters E3ww = E3pxm->ww; E3hh = E3pxm->hh; Xcen = scale * Xcen; Ycen = scale * Ycen; D1 = scale * D1; 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() { using namespace sphere_names; int edist; if (! LMclick && ! Mdrag) return; LMclick = Mxdrag = Mydrag = 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; D1 = 2 * edist; // new sphere diameter signal_thread(); return; } // thread function - multiple working threads to update image void * sphere_thread(void *) { using namespace sphere_names; void * sphere_wthread(void *arg); // worker thread while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 PXM_clear(E3pxm,0); // clear E3 to black/transparent do_wthreads(sphere_wthread,NWT); // worker threads CEF->Fmods++; // image modified CEF->Fsaved = 0; // not saved paintlock(0); // unblock window paint 19.0 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, px33, py33, dx, dy, vstat; float px1, py1, *pix3, vpix[4]; float s1, s2, D2, T; int nc = E1pxm->nc, pcc = nc * sizeof(float); D2 = D1 / (1.0 - flatten); // image diameter for (py3 = index; py3 < E3hh; py3 += NWT) // loop output pixels for (px3 = 0; px3 < E3ww; px3++) { dx = px3 - Xcen; // distance from mouse center dy = py3 - Ycen; s1 = sqrtf(dx*dx + dy*dy); // dist. from center to output pixel T = s1 * PI / D2 / magnify; // sine of subtended angle if (T > 1.0) continue; T = asinf(T); // subtended angle, 0 - PI/2 s2 = D2 * T / PI; // corresp. dist. on sphere px1 = Xcen + dx * s2 / s1; // input virtual pixel py1 = Ycen + dy * s2 / s1; vstat = vpixel(E1pxm,px1,py1,vpix); if (! vstat) continue; px33 = px3 - (Xcen - E3ww/2); // move to image center py33 = py3 - (Ycen - E3hh/2); if (px33 < 0 || px33 > E3ww-1) continue; if (py33 < 0 || py33 > E3hh-1) continue; pix3 = PXMpix(E3pxm,px33,py33); // output pixel memcpy(pix3,vpix,pcc); } pthread_exit(0); // exit thread } /********************************************************************************/ // Stretch image. // Image expands with increasing radius. namespace stretch_names { int E3ww, E3hh; // image dimensions int Xcen, Ycen, D1; // center and diameter of circle float magnify; // magnify parameter float flatten; // flatten parameter editfunc EFstretch; // edit function data } // menu function void m_stretch(GtkWidget *, const char *) // 19.0 { using namespace stretch_names; int stretch_dialog_event(zdialog* zd, const char *event); void stretch_mousefunc(void); void * stretch_thread(void *); cchar *title = E2X("Stretch Image"); F1_help_topic = "stretch"; m_zoom(0,"fit"); // zoom to fit window EFstretch.menufunc = m_stretch; EFstretch.funcname = "stretch"; // function name EFstretch.FprevReq = 1; // use preview edit mode EFstretch.mousefunc = stretch_mousefunc; // mouse function EFstretch.threadfunc = stretch_thread; // thread function if (! edit_setup(EFstretch)) return; // setup edit PXM_addalpha(E0pxm); // add alpha channel if missing PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); E3ww = E3pxm->ww; E3hh = E3pxm->hh; Xcen = E3ww / 2; // set dialog defaults Ycen = E3hh / 2; D1 = E3ww; if (E3hh < E3ww) D1 = E3hh; magnify = 1.0; /*** _____________________________ | Stretch Image | | | | Drag mouse to set center | | | | Flatten ======[]========== | | Magnify ==========[]====== | | | | [done] [cancel] | |_____________________________| ***/ zdialog *zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); // stretch dialog CEF->zd = zd; zdialog_add_widget(zd,"label","labtip","dialog",E2X("Drag mouse to set center")); zdialog_add_widget(zd,"hbox","hbflat","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labflat","hbflat",Bflatten,"space=5"); zdialog_add_widget(zd,"hscale","flatten","hbflat","0.0|1.49|0.001|0.0","expand"); zdialog_add_widget(zd,"hbox","hbmag","dialog"); zdialog_add_widget(zd,"label","labmag","hbmag",Bmagnify,"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,stretch_dialog_event,"save"); // run dialog - parallel takeMouse(stretch_mousefunc,0); // connect mouse function signal_thread(); // trigger update thread return; } // stretch dialog event and completion function int stretch_dialog_event(zdialog *zd, const char *event) { using namespace stretch_names; float scale; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) // done { edit_fullsize(); // get full size image scale = 1.0 * (E3pxm->ww + E3pxm->hh) / (E3ww + E3hh); // scale up the parameters E3ww = E3pxm->ww; E3hh = E3pxm->hh; Xcen = scale * Xcen; Ycen = scale * Ycen; D1 = scale * D1; 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 stretch_mousefunc() { using namespace stretch_names; int edist; if (! LMclick && ! Mdrag) return; LMclick = Mxdrag = Mydrag = 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; D1 = 2 * edist; // new stretch diameter signal_thread(); return; } // thread function - multiple working threads to update image void * stretch_thread(void *) { using namespace stretch_names; void * stretch_wthread(void *arg); // worker thread while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint 19.0 PXM_clear(E3pxm,0); // clear E3 to black/transparent do_wthreads(stretch_wthread,NWT); // worker threads CEF->Fmods++; // image modified CEF->Fsaved = 0; // not saved paintlock(0); // unblock window paint 19.0 Fpaint2(); // update window } return 0; // not executed, stop warning } void * stretch_wthread(void *arg) // worker thread function { using namespace stretch_names; int index = *((int *) (arg)); int px3, py3, px33, py33, dx, dy, vstat; float px1, py1, *pix3, vpix[4]; float s1, s2, D2, R, T; int nc = E1pxm->nc, pcc = nc * sizeof(float); D2 = D1 / (1.5 - flatten); // diameter of output image R = D2 / 2.0; // radius for (py3 = index; py3 < E3hh; py3 += NWT) // loop output pixels for (px3 = 0; px3 < E3ww; px3++) { dx = px3 - Xcen; // distance from mouse center dy = py3 - Ycen; s1 = sqrtf(dx*dx + dy*dy); // center to output pixel T = s1 / R; // subtended angle if (T > 0.5 * PI) continue; // limit 90 deg. s2 = R * sinf(T) * (3 - magnify); // center to input pixel px1 = Xcen + dx * s2 / s1; // input v.pixel py1 = Ycen + dy * s2 / s1; vstat = vpixel(E1pxm,px1,py1,vpix); if (! vstat) continue; px33 = px3 - (Xcen - E3ww/2); // move to image center py33 = py3 - (Ycen - E3hh/2); if (px33 < 0 || px33 > E3ww-1) continue; if (py33 < 0 || py33 > E3hh-1) continue; pix3 = PXMpix(E3pxm,px33,py33); // output pixel memcpy(pix3,vpix,pcc); } pthread_exit(0); // exit thread } /********************************************************************************/ // Turn an image inside-out. // Convert pixels within the largest circle fitting within the image. // Replace each pixel having distance R from center with a pixel on the // same center to edge line having distance R from edge of circle. namespace inside_out_names { editfunc EFinsideout; int ww, hh, cx, cy, pixcc; float R, T, hole; // radius, theta } // menu function void m_inside_out(GtkWidget *, cchar *menu) // 19.0 { using namespace inside_out_names; int inside_out_dialog_event(zdialog *zd, cchar *event); void * inside_out_thread(void *); void inside_out_mousefunc(); F1_help_topic = "inside-out"; m_zoom(0,"fit"); // zoom to fit window EFinsideout.menuname = menu; EFinsideout.menufunc = m_inside_out; EFinsideout.funcname = "inside_out"; // function name EFinsideout.FprevReq = 1; // use preview image EFinsideout.threadfunc = inside_out_thread; // thread function EFinsideout.mousefunc = inside_out_mousefunc; // mouse function if (! edit_setup(EFinsideout)) return; // open edit function PXM_addalpha(E0pxm); // add an alpha channel if missing PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); ww = E3pxm->ww; // image dimensions hh = E3pxm->hh; cx = ww / 2; cy = hh / 2; T = 0.0; hole = 100; pixcc = E3pxm->nc * sizeof(float); // RGBA pixel size /*** ________________________________ | Inside-out | | Drag mouse to set center | | | | Rotate ===========[]========= | | Hole ======[]=============== | | | | [Done] [Cancel] | |________________________________| ***/ zdialog *zd = zdialog_new(E2X("Inside-out"),Mwin,Bdone,Bcancel,null); EFinsideout.zd = zd; zdialog_add_widget(zd,"label","labtip","dialog",E2X("Drag mouse to set center")); zdialog_add_widget(zd,"hbox","hbrot","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labrot","hbrot",Brotate,"space=3"); zdialog_add_widget(zd,"hscale","rotate","hbrot","-4.0|4.0|0.01|0.0","space=5|expand"); zdialog_add_widget(zd,"hbox","hbhole","dialog"); zdialog_add_widget(zd,"label","labhole","hbhole",E2X("Center Hole"),"space=3"); zdialog_add_widget(zd,"hscale","hole","hbhole","0|1000|1|100","space=5|expand"); takeMouse(inside_out_mousefunc,dragcursor); zdialog_run(zd,inside_out_dialog_event,"save"); signal_thread(); return; } // dialog event and completion callback function int inside_out_dialog_event(zdialog *zd, cchar *event) { using namespace inside_out_names; void inside_out_mousefunc(); if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"focus")) // toggle mouse capture takeMouse(inside_out_mousefunc,dragcursor); // connect mouse function if (strmatch(event,"rotate")) { zdialog_fetch(zd,"rotate",T); signal_thread(); } if (strmatch(event,"hole")) { zdialog_fetch(zd,"hole",hole); signal_thread(); } if (zd->zstat) { if (zd->zstat == 1) { // done wait_thread_idle(); // insure thread done float S = 1.0 * E0pxm->ww / E3pxm->ww; cx = S * cx; // scale params to full size cy = S * cy; hole = S * hole; edit_fullsize(); // get full size image signal_thread(); edit_done(0); // commit edit } else edit_cancel(0); // discard edit } return 1; } // mouse function void inside_out_mousefunc() { using namespace inside_out_names; if (! LMclick && ! Mdrag) return; // wait for click or drag cx = Mxposn; // mouse position = new center cy = Myposn; signal_thread(); // convert image LMclick = Mxdrag = Mydrag = 0; return; } // main thread function void * inside_out_thread(void *) { using namespace inside_out_names; void * inside_out_wthread(void *arg); while (true) { thread_idle_loop(); // wait for work or exit request ww = E3pxm->ww; // image dimensions hh = E3pxm->hh; // (preview or full size) R = cx; // R = minimum center - edge distance if (cy < cx) R = cy; if (ww - cx < R) R = ww - cx; if (hh - cy < R) R = hh - cy; if (R < 50) continue; // ignore absurd center while (Fpaintrequest) zmainsleep(0.01); paintlock(1); PXM_clear(E3pxm,0); // clear E3 to black/transparent do_wthreads(inside_out_wthread,NWT); // do worker threads paintlock(0); // unblock window paint CEF->Fmods++; // E3 modified, not saved CEF->Fsaved = 0; Fpaint2(); // update output image } return 0; // not executed, stop g++ warning } // worker thread function void * inside_out_wthread(void *arg) { using namespace inside_out_names; int index = *((int *) arg); float px1, py1, px3, py3, px33, py33; float dx1, dy1, dx3, dy3, r1, r3, T1, T3; float pix1[4], *pix3; for (py3 = index; py3 < hh; py3 += NWT) // loop target pixels for (px3 = 0; px3 < ww; px3++) { px33 = px3 - (cx - ww/2); // move to image center py33 = py3 - (cy - hh/2); if (px33 < 0 || px33 > ww-1) continue; if (py33 < 0 || py33 > hh-1) continue; dx3 = px3 - cx; // target pixel relative to center dy3 = py3 - cy; r3 = sqrtf(dx3*dx3 + dy3*dy3); // target pixel distance from center if (r3 < hole) continue; if (r3 > R) continue; // outside image circle T3 = asinf(dy3 / r3); // target pixel angle from center if (dx3 < 0) T3 = PI - T3; if (T3 > PI) T3 -= 2 * PI; if (T3 < PI) T3 += 2 * PI; T1 = T3 + T; // source pixel angle from center if (T1 > PI) T1 -= 2 * PI; // + user rotate if (T1 < PI) T1 += 2 * PI; r1 = R * (1 + (r3 - hole) / (hole - R)); // r3: hole >> R r1: R >> 0 dx1 = r1 * cosf(T1); // source pixel relative to center dy1 = r1 * sinf(T1); px1 = dx1 + cx; // add center py1 = dy1 + cy; if (! vpixel(E1pxm,px1,py1,pix1)) continue; // get source pixel pix3 = PXMpix(E3pxm,int(px33),int(py33)); // output pixel if (px33 < 0 || px33 > ww-1) continue; if (py33 < 0 || py33 > hh-1) continue; memcpy(pix3,pix1,pixcc); } pthread_exit(0); } /********************************************************************************/ // Convert image to 'tiny planet' format. namespace tiny_planet_names { editfunc EFtinyplanet; int ww1, hh1, ww3, hh3, ww9, hh9; int cx9, cy9, cx3, cy3, pixcc; int cuttop, cutbott, rotate; float hole, R1, R2; int revR, revT; } // menu function void m_tiny_planet(GtkWidget *, cchar *menu) // 19.0 { using namespace tiny_planet_names; int tiny_planet_dialog_event(zdialog *zd, cchar *event); void tiny_planet_process(); char texthole[20], texttop[20], textbott[20]; F1_help_topic = "tiny planet"; if (! curr_file) return; // no current image if (Fpxb->ww < Fpxb->hh) { zmessageACK(Mwin,E2X("image width must be greater than height")); return; } EFtinyplanet.menuname = menu; EFtinyplanet.menufunc = m_tiny_planet; EFtinyplanet.funcname = "tiny_planet"; // function name EFtinyplanet.FprevReq = 1; // use preview image if (! edit_setup(EFtinyplanet)) return; // open edit function PXM_addalpha(E0pxm); // add alpha channel if missing PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); ww1 = E1pxm->ww; // image dimensions hh1 = E1pxm->hh; pixcc = 4 * sizeof(float); // RGBA pixel size /*** ___________________________________ | Tiny Planet | | | | Center Hole ==========[]========= | | Cut Top ======[]================= | | Cut Bottom =[]=================== | | Rotate =========[]=============== | | [ ] Reverse R [ ] Theta | | | | [Reset] [Done] [Cancel] | |___________________________________| hh is image height in pixels blank image area blank |-------------|-----------------|--------- center R1 R2 < 0.4*hh 0.5*hh = hole fixed ***/ zdialog *zd = zdialog_new("Tiny Planet",Mwin,Bdone,Bcancel,null); EFtinyplanet.zd = zd; snprintf(texthole,20,"0|%.0f|1|%.0f", 0.4 * hh1, 0.1 * hh1); // hole range and default zdialog_add_widget(zd,"hbox","hbhole","dialog"); zdialog_add_widget(zd,"label","labhole","hbhole",E2X("Center Hole"),"space=3"); zdialog_add_widget(zd,"hscale","hole","hbhole",texthole,"space=3|expand"); snprintf(texttop,20,"0|%.0f|1|0", 0.3 * hh1); // cut-off top range zdialog_add_widget(zd,"hbox","hbtop","dialog"); zdialog_add_widget(zd,"label","labcuttop","hbtop",E2X("Cut Top"),"space=3"); zdialog_add_widget(zd,"hscale","cuttop","hbtop",texttop,"space=3|expand"); snprintf(textbott,20,"0|%.0f|1|0", 0.3 * hh1); // cut-off bottom range zdialog_add_widget(zd,"hbox","hbbott","dialog"); zdialog_add_widget(zd,"label","labcutbott","hbbott",E2X("Cut Bottom"),"space=3"); zdialog_add_widget(zd,"hscale","cutbott","hbbott",textbott,"space=3|expand"); zdialog_add_widget(zd,"hbox","hbrotate","dialog"); // rotate range zdialog_add_widget(zd,"label","labrotate","hbrotate",Brotate,"space=3"); zdialog_add_widget(zd,"hscale","rotate","hbrotate","0|360|1|0","space=3|expand"); zdialog_add_widget(zd,"hbox","hbrev","dialog"); zdialog_add_widget(zd,"zbutton","revR","hbrev",E2X("Reverse R"),"space=3"); zdialog_add_widget(zd,"zbutton","revT","hbrev",E2X("Theta"),"space=8"); hole = 0.1 * hh1; // default settings revR = revT = 0; cuttop = cutbott = 0; zdialog_run(zd,tiny_planet_dialog_event,"save"); tiny_planet_process(); // make initial image return; } // dialog event and completion callback function int tiny_planet_dialog_event(zdialog *zd, cchar *event) { using namespace tiny_planet_names; void tiny_planet_process(); if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat != 1) edit_cancel(0); // cancel else // done { float R = 1.0 * E0pxm->ww / ww1; // scale params to full size cuttop = R * cuttop; cutbott = R * cutbott; hole = R * hole; edit_fullsize(); // get full size input image ww1 = E1pxm->ww; hh1 = E1pxm->hh; tiny_planet_process(); // make full size output image PXM_free(E9pxm); edit_done(0); } return 1; } if (strmatch(event,"focus")) return 1; zdialog_fetch(zd,"hole",hole); zdialog_fetch(zd,"cuttop",cuttop); // get dialog inputs zdialog_fetch(zd,"cutbott",cutbott); zdialog_fetch(zd,"rotate",rotate); if (strmatch(event,"revR")) revR = 1 - revR; if (strmatch(event,"revT")) revT = 1 - revT; zmainloop(); tiny_planet_process(); // make image return 1; } void tiny_planet_process() { using namespace tiny_planet_names; int py, cc; float *pix1, *pix9; void * tiny_planet_wthread(void *arg); ww9 = ww1; // make input image with cuttoffs hh9 = hh1 - cuttop - cutbott; if (E9pxm) PXM_free(E9pxm); E9pxm = PXM_make(ww9,hh9,4); for (py = 0; py < hh9; py++) { pix1 = PXMpix(E1pxm,0,py + cuttop); pix9 = PXMpix(E9pxm,0,py); memcpy(pix9,pix1, ww1 * pixcc); } ww3 = hh3 = hh9 + 2; // make square output image PXM_free(E3pxm); E3pxm = PXM_make(ww3,hh3,4); cc = ww3 * hh3 * pixcc; memset(E3pxm->pixels,0,cc); // all pixels black and transparent cx9 = ww9 / 2; // input image center cy9 = hh9 / 2; cx3 = ww3 / 2; // output image center cy3 = hh3 / 2; if (hole > 0.4 * hh9) hole = 0.4 * hh9; // sanity limit R1 = hole; // image range, R1 to R2 R2 = hh9 / 2; do_wthreads(tiny_planet_wthread,NWT); // do worker threads CEF->Fmods++; // E3 modified, not saved CEF->Fsaved = 0; Fpaint2(); // update output image return; } // worker thread function void * tiny_planet_wthread(void *arg) { using namespace tiny_planet_names; int index = *((int *) arg); int px3, py3, dx, dy; float px9, py9, R, theta, pix9[4], *pix3; for (py3 = index; py3 < hh3; py3 += NWT) // loop output pixels for (px3 = 0; px3 < ww3; px3++) { dx = px3 - cx3; dy = py3 - cy3; R = sqrtf(dx*dx + dy*dy); // pixel distance from center if (R < 1) continue; if (R > R2) continue; // outside R2 circle if (R < R1) continue; // inside R1 circle if (dx >= 0 && dy >= 0) theta = asinf(dy/R); // 0 to 90 deg. else if (dx < 0 && dy >= 0) theta = PI - asinf(dy/R); // 90 to 180 else if (dx < 0 && dy < 0) theta = PI + asinf(-dy/R); // 180 to 270 else theta = 2 * PI - asinf(-dy/R); // 270 to 360 theta = theta + rotate / 57.296; // apply user rotation if (theta < 0) theta = 2 * PI - theta; if (theta >= 2 * PI) theta = theta - 2 * PI; px9 = ww9 * 0.5 * theta / PI; // R, theta --> input pixel py9 = hh9 * (R - R1) / (R2 - R1); if (revT) px9 = ww9-1 - px9; // if reverse theta if (revR) py9 = hh9-1 - py9; // if reverse R pix3 = PXMpix(E3pxm,px3,py3); // interpolate nearest 4 pixels if (vpixel(E9pxm,px9,py9,pix9)) memcpy(pix3,pix9,pixcc); } pthread_exit(0); } /********************************************************************************/ // generate an image that repeats itself spiraling inward namespace escher_spiral_names { editfunc EFescher_spiral; int Eww, Ehh; int cx, cy; int width; float rim; float color[3]; } // menu function void m_escher_spiral(GtkWidget *, cchar *menu) // 20.0 { using namespace escher_spiral_names; int escher_spiral_dialog_event(zdialog *zd, cchar *event); void * escher_spiral_thread(void *); void escher_spiral_mousefunc(); F1_help_topic = "escher spiral"; m_zoom(0,"fit"); // zoom to fit window EFescher_spiral.menuname = menu; EFescher_spiral.menufunc = m_escher_spiral; EFescher_spiral.funcname = "escher_spiral"; // function name EFescher_spiral.threadfunc = escher_spiral_thread; // thread function EFescher_spiral.mousefunc = escher_spiral_mousefunc; // mouse function if (! edit_setup(EFescher_spiral)) return; // open edit function Eww = E1pxm->ww; // source image dimensions Ehh = E1pxm->hh; cx = Eww / 2; // initial center cy = Ehh / 2; PXM_subalpha(E3pxm); // remove dest. image alpha channel /*** ________________________________ | Escher Spiral | | | | Click mouse to change center | | Width % [ 40 ] | | Rim % [ 1 ] Color [ ### ] | | | | [Apply] [Done] [Cancel] | |________________________________| ***/ zdialog *zd = zdialog_new("Escher Spiral",Mwin,Bapply,Bdone,Bcancel,null); EFescher_spiral.zd = zd; zdialog_add_widget(zd,"label","labtip","dialog",E2X("Click mouse to change center")); zdialog_add_widget(zd,"hbox","hbw","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labw","hbw",E2X("Width %"),"space=3"); zdialog_add_widget(zd,"zspin","width","hbw","10|80|1|40","space=5"); zdialog_add_widget(zd,"hbox","hbr","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labr","hbr",E2X("Rim %"),"space=3"); zdialog_add_widget(zd,"zspin","rim","hbr","0|9|0.1|2","space=5"); zdialog_add_widget(zd,"label","space","hbr",0,"space=6"); zdialog_add_widget(zd,"label","labc","hbr",Bcolor,"space=3"); zdialog_add_widget(zd,"colorbutt","color","hbr","0|0|0","space=3"); width = 40; rim = 2; color[0] = color[1] = color[2] = 0; zdialog_restore_inputs(zd); // restore previous inputs zdialog_run(zd,escher_spiral_dialog_event,"save"); takeMouse(escher_spiral_mousefunc,dragcursor); return; } // dialog event and completion callback function int escher_spiral_dialog_event(zdialog *zd, cchar *event) { using namespace escher_spiral_names; void escher_spiral_mousefunc(); char text[20]; cchar *pp; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"focus")) // toggle mouse capture takeMouse(escher_spiral_mousefunc,dragcursor); // connect mouse function if (zd->zstat) { if (zd->zstat == 1) // apply { zd->zstat = 0; signal_thread(); return 1; } if (zd->zstat == 2) edit_done(0); // done else edit_cancel(0); // discard edit } if (zstrstr("width rim color",event)) { zdialog_fetch(zd,"width",width); zdialog_fetch(zd,"rim",rim); zdialog_fetch(zd,"color",text,20); pp = strField(text,"|",1); if (pp) color[0] = atoi(pp); pp = strField(text,"|",2); if (pp) color[1] = atoi(pp); pp = strField(text,"|",3); if (pp) color[2] = atoi(pp); } return 1; } // mouse function void escher_spiral_mousefunc() { using namespace escher_spiral_names; if (! LMclick) return; // wait for left click cx = Mxposn; // mouse position = new center cy = Myposn; LMclick = Mxdrag = Mydrag = 0; signal_thread(); return; } // main thread function void * escher_spiral_thread(void *) { using namespace escher_spiral_names; void escher_spiral_function(); while (true) { thread_idle_loop(); // wait for work or exit request while (Fpaintrequest) zmainsleep(0.01); paintlock(1); // block window paint escher_spiral_function(); // transform image paintlock(0); // unblock window paint CEF->Fmods++; // E3 modified, not saved CEF->Fsaved = 0; Fpaint2(); // update output image } return 0; // not executed, stop g++ warning } void escher_spiral_function() { using namespace escher_spiral_names; float revs, rad, step; float sr, sr1, sr2, dr, dr1, dr2; float T, W, F; float dx, dy, sx, sy; float *dpix, spix[4]; int px, py, cc, ii, *Npix; int pcc = 3 * sizeof(float); cc = Eww * Ehh * pcc; // clear dest. image memset(E3pxm->pixels,0,cc); cc = Eww * Ehh * sizeof(int); // clear dest. image pixel counts Npix = (int *) zmalloc(cc); memset(Npix,0,cc); revs = 0; // source image sweeps (revolutions) rad = cx; // radius is nearest edge if (Eww - cx < rad) rad = Eww - cx; // from designated center if (cy < rad) rad = cy; if (Ehh - cy < rad) rad = Ehh - cy; W = 0.01 * (100 - width); // width = 25% >> W = 0.75 sr2 = rad; // source image segment outer radius sr1 = sr2 * W; // inner radius dr2 = rad; // initial dest. segment outer radius dr1 = dr2 * W; // inner radius revs = 0; // sections revolutions while (true) { step = 0.7 / dr2; // 0.7 pixels at radius dr2 revs += step / (2 * PI); // rotate segments by tiny step F = pow(W,revs); // 1.0 0.75 0.56 0.42 0.32 ... dr2 = rad * F; // dest. image segment outer radius dr1 = dr2 * W; // inner radius if (dr1 < 2) break; T = 2 * PI * revs - 0.5 * PI; // segment angle for (sr = sr1; sr <= sr2; sr++) // loop source image segment position { dr = dr1 + (dr2 - dr1) * (sr - sr1) / (sr2 - sr1); // corresp. dest. image segment position if (100 * (sr2 - sr) / (sr2 - sr1) <= rim) // source pixel > rim threshold memcpy(spix,color,pcc); else { sx = sr * cos(T) + cx; // source image virtual pixel sy = sr * sin(T) + cy; vpixel(E1pxm,sx,sy,spix); } dx = dr * cos(T) + cx; // dest. image pixel dy = dr * sin(T) + cy; px = int(dx); py = int(dy); dpix = PXMpix(E3pxm,px,py); dpix[0] += spix[0]; // add source to dest. pixel dpix[1] += spix[1]; dpix[2] += spix[2]; ii = py * Eww + px; // count pixels added. ++Npix[ii]; } } for (py = 0; py < Ehh; py++) // loop dest. pixels for (px = 0; px < Eww; px++) { ii = py * Eww + px; // get count of accumulated dest. pixels ii = Npix[ii]; if (ii > 1) { // > 1: make average pixel dpix = PXMpix(E3pxm,px,py); dpix[0] = dpix[0] / ii; dpix[1] = dpix[1] / ii; dpix[2] = dpix[2] / ii; } else if (ii == 0) // 0: copy source pixel memcpy(PXMpix(E3pxm,px,py),PXMpix(E1pxm,px,py),pcc); } zfree(Npix); return; } fotoxx-20.08/f.widgets.cc000066400000000000000000002200771362435004500152650ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* Fotoxx window and menu build functions build_widgets build widgets and menus for F/G/W/M view modes m_viewmode set current F/G/W/M view mode popup_menufunc image/thumb right-click menu func image_Rclick_popup popup menu for image right-click gallery_Lclick_func thumbnail left-click function gallery_Rclick_popup popup menu for thumbnail right-click m_favorites function to generate favorites menu favorites_callback response function for clicked menu *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are defined) /********************************************************************************/ GtkWidget *mFile, *mGallery, *mMap, *mMeta, *mArea; GtkWidget *mEdit, *mEnhance, *mEffects, *mWarp, *mComb; GtkWidget *mProc, *mTools, *mHelp; GtkWidget *popmenu_image, *popmenu_raw, *popmenu_video; GtkWidget *popmenu_thumb, *popmenu_album; // initialize widgets and menus for F/G/W/M view modes // called from main() before gtk_main() loop is entered void build_widgets() { Mwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); // create main window gtk_window_set_title(MWIN,Frelease); MWhbox = gtk_box_new(HORIZONTAL,0); // main window top container gtk_container_add(GTK_CONTAINER(Mwin),MWhbox); MWmenu = gtk_box_new(VERTICAL,0); // container for main window menus gtk_box_pack_start(GTK_BOX(MWhbox),MWmenu,0,1,0); MWvbox = gtk_box_new(VERTICAL,0); // container for F/G/M/W views gtk_box_pack_start(GTK_BOX(MWhbox),MWvbox,1,1,0); G_SIGNAL(Mwin,"delete-event",delete_event,0); // connect signals to main window G_SIGNAL(Mwin,"destroy",destroy_event,0); G_SIGNAL(Mwin,"window-state-event",state_event,0); G_SIGNAL(Mwin,"key-press-event",KBpress,0); // connect KB events to main window G_SIGNAL(Mwin,"key-release-event",KBrelease,0); // F view widgets - image file Fhbox = gtk_box_new(HORIZONTAL,0); // top container gtk_box_pack_start(GTK_BOX(MWvbox),Fhbox,1,1,0); Fvbox = gtk_box_new(VERTICAL,0); // vbox for image gtk_box_pack_start(GTK_BOX(Fhbox),Fvbox,1,1,0); Fpanel = gtk_box_new(HORIZONTAL,0); // panel over image gtk_box_pack_start(GTK_BOX(Fvbox),Fpanel,0,0,0); gtk_widget_set_size_request(Fpanel,0,20); Fpanlab = gtk_label_new("panel"); gtk_box_pack_start(GTK_BOX(Fpanel),Fpanlab,0,0,0); Fpanelshow = 1; // panel normally shows Fdrawin = gtk_drawing_area_new(); // image drawing area gtk_box_pack_start(GTK_BOX(Fvbox),Fdrawin,1,1,0); gtk_widget_hide(Fhbox); gtk_widget_add_events(Fdrawin,GDK_BUTTON_PRESS_MASK); // connect mouse events to image window gtk_widget_add_events(Fdrawin,GDK_BUTTON_RELEASE_MASK); gtk_widget_add_events(Fdrawin,GDK_BUTTON_MOTION_MASK); gtk_widget_add_events(Fdrawin,GDK_POINTER_MOTION_MASK); gtk_widget_add_events(Fdrawin,GDK_SCROLL_MASK); G_SIGNAL(Fdrawin,"button-press-event",mouse_event,0); // connect signals G_SIGNAL(Fdrawin,"button-release-event",mouse_event,0); G_SIGNAL(Fdrawin,"motion-notify-event",mouse_event,0); G_SIGNAL(Fdrawin,"scroll-event",mouse_event,0); G_SIGNAL(Fdrawin,"draw",Fpaint,0); drag_drop_dest(Fdrawin,drop_event); // accept drag-drop file // G view widgets - thumbnail gallery Ghbox = gtk_box_new(HORIZONTAL,0); // top container gtk_box_pack_start(GTK_BOX(MWvbox),Ghbox,1,1,0); Gvbox = gtk_box_new(VERTICAL,0); // 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(E2X("Album")); // [Album] button in panel gtk_box_pack_start(GTK_BOX(Gpanel),Galbum,0,0,3); Gtop = gtk_button_new_with_label(E2X("TOP")); // [TOP] button in panel gtk_box_pack_start(GTK_BOX(Gpanel),Gtop,0,0,3); Gsep = gtk_label_new(0); gtk_label_set_markup(GTK_LABEL(Gsep),"@"); gtk_box_pack_start(GTK_BOX(Gpanel),Gsep,0,0,10); Gsep = gtk_separator_new(HORIZONTAL); // separator line gtk_box_pack_start(GTK_BOX(Gvbox),Gsep,0,0,3); Gscroll = gtk_scrolled_window_new(0,0); // scrolled window for gallery gtk_scrolled_window_set_policy(SCROLLWIN(Gscroll),NEVER,ALWAYS); Gadjust = gtk_scrolled_window_get_vadjustment(SCROLLWIN(Gscroll)); gtk_box_pack_start(GTK_BOX(Gvbox),Gscroll,1,1,0); Gdrawin = gtk_drawing_area_new(); // gallery drawing area gtk_container_add(GTK_CONTAINER(Gscroll),Gdrawin); gtk_widget_hide(Ghbox); gtk_widget_add_events(Gdrawin,GDK_BUTTON_PRESS_MASK); // connect mouse events to gallery window gtk_widget_add_events(Gdrawin,GDK_BUTTON_RELEASE_MASK); gtk_widget_add_events(Gdrawin,GDK_POINTER_MOTION_MASK); G_SIGNAL(Gtop,"clicked",navi::newtop,0); G_SIGNAL(Galbum,"clicked",navi::newalbum,0); G_SIGNAL(Gdrawin,"button-press-event",navi::mouse_event,0); G_SIGNAL(Gdrawin,"button-release-event",navi::mouse_event,0); G_SIGNAL(Gdrawin,"motion-notify-event",navi::mouse_event,0); G_SIGNAL(Gdrawin,"draw",navi::gallery_paint,null); drag_drop_source(Gdrawin,navi::gallery_dragfile); // start file drag-drop drag_drop_dest(Gdrawin,navi::gallery_dropfile); // accept drag-drop file // M view widgets - internet maps Mhbox = gtk_box_new(HORIZONTAL,0); // top container gtk_box_pack_start(GTK_BOX(MWvbox),Mhbox,1,1,0); Mvbox = gtk_box_new(VERTICAL,0); // vbox for net map window gtk_box_pack_start(GTK_BOX(Mhbox),Mvbox,1,1,0); gtk_widget_hide(Mhbox); // W view widgets - local map files Whbox = gtk_box_new(HORIZONTAL,0); // top container gtk_box_pack_start(GTK_BOX(MWvbox),Whbox,1,1,0); Wvbox = gtk_box_new(VERTICAL,0); // vbox for file map window gtk_box_pack_start(GTK_BOX(Whbox),Wvbox,1,1,0); Wdrawin = gtk_drawing_area_new(); // filemap drawing area gtk_box_pack_start(GTK_BOX(Wvbox),Wdrawin,1,1,0); gtk_widget_hide(Whbox); gtk_widget_add_events(Wdrawin,GDK_BUTTON_PRESS_MASK); // connect mouse events to filemap window gtk_widget_add_events(Wdrawin,GDK_BUTTON_RELEASE_MASK); gtk_widget_add_events(Wdrawin,GDK_BUTTON_MOTION_MASK); gtk_widget_add_events(Wdrawin,GDK_POINTER_MOTION_MASK); gtk_widget_add_events(Wdrawin,GDK_SCROLL_MASK); G_SIGNAL(Wdrawin,"button-press-event",mouse_event,0); // connect signals G_SIGNAL(Wdrawin,"button-release-event",mouse_event,0); G_SIGNAL(Wdrawin,"motion-notify-event",mouse_event,0); G_SIGNAL(Wdrawin,"scroll-event",mouse_event,0); G_SIGNAL(Wdrawin,"draw",Fpaint,0); // menu popup text (tool tips) ---------------------------------------- // main menu buttons cchar * File_tip = E2X("Rename, copy/move, delete, print"); cchar * Gallery_tip = E2X("Thumbnails, bookmarks, albums, slide show"); cchar * Maps_tip = E2X("View images by map location"); cchar * favorites_tip = E2X("Custom favorites menu"); cchar * prev_next_tip = E2X("Left/right click: previous/next (also arrow keys)"); cchar * zoom_menu_tip = E2X("Left/right click: zoom image/thumb size, meta view, list view \n" // 20.04 " (keyboard [-] and [+] keys also work)"); cchar * save_tip = E2X("Save modified file as new version or new file"); cchar * meta_tip = E2X("Metadata: captions, tags, ratings, geotags, search images"); cchar * areas_tip = E2X("Select areas to edit separately, save, copy and paste"); cchar * undo_redo_tip = E2X("Left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit"); cchar * edit_tip = E2X("Image edit basic functions"); cchar * enhance_tip = E2X("Image repair and enhance"); cchar * effects_tip = E2X("Artistic effects (filters)"); cchar * warp_tip = E2X("Image warp, unwarp, transform"); cchar * combine_tip = E2X("HDR, HDF, panorama, stack, mashup"); cchar * process_tip = E2X("Batch processing, custom scripts"); cchar * tools_tip = E2X("Image index, user preferences, shortcuts, utilities"); cchar * help_tip = E2X("User Guide, recent changes, log file, about"); // file menu cchar * Fview_tip = E2X("Current File (R-click or key F)"); cchar * new_session_tip = E2X("Open a parallel Fotoxx session"); cchar * cycle2files_tip = E2X("Cycle 2 Prior Files"); cchar * cycle3files_tip = E2X("Cycle 3 Prior Files"); cchar * view360_tip = E2X("View a 360 degree panorama image file"); cchar * rename_tip = E2X("Change file name"); cchar * permissions_tip = E2X("View and change file permissions"); // 20.0 cchar * blank_image_tip = E2X("Create a blank image"); cchar * blank_window_tip = E2X("Toggle - blank or restore window"); cchar * copy_move_tip = E2X("Copy or Move file to new location"); cchar * copyto_desktop_tip = E2X("Copy file to the desktop"); cchar * copyto_clipboard_tip = E2X("Copy file to the clipboard"); cchar * set_wallpaper_tip = E2X("Set file as desktop wallpaper (GNOME)"); cchar * show_on_net_map_tip = E2X("Show location on Internet map"); cchar * deltrash_tip = E2X("Delete or trash file"); cchar * print_tip = E2X("Print the current image"); cchar * print_calibrated_tip = E2X("Print current image with adjusted colors"); cchar * quit_tip = E2X("Quit Fotoxx"); // gallery menu cchar * Gview_tip = E2X("Thumbnail Gallery (R-click or key G)"); cchar * thumbview_tip = E2X("Gallery view with thumbnails and file data"); // 20.04 cchar * metaview_tip = E2X("Gallery view with thumbnails and basic metadata"); cchar * listview_tip = E2X("Gallery view with small thumbnails and file names"); cchar * recentfiles_tip = E2X("Gallery of recently viewed image files"); cchar * newfiles_tip = E2X("Gallery of newest image files"); cchar * jump_begin_tip = E2X("Jump to beginning [home]"); cchar * jump_end_tip = E2X("Jump to end [end]"); cchar * source_folder_tip = E2X("Set gallery from current image file"); cchar * sort_order_tip = E2X("Change sort order"); cchar * alldirs_tip = E2X("List all folders, click any for gallery view"); cchar * select_files_tip = E2X("select input files for album, batch, script functions"); cchar * bookmarks_tip = E2X("Set and recall bookmarked image locations"); cchar * manage_albums_tip = E2X("Organize images into albums"); cchar * update_albums_tip = E2X("Update albums for new file versions"); cchar * album_mass_update_tip = E2X("Mass update album files"); cchar * gallery2album_tip = E2X("Save current gallery as album"); cchar * slideshow_tip = E2X("Start a slide show"); // map menu cchar * map_view_tip = E2X("Maps (R-click or key M)"); cchar * net_map_tip = E2X("Open Internet map"); cchar * net_source_tip = E2X("Choose Internet map source"); cchar * net_locs_tip = E2X("Internet map locations"); cchar * file_map_tip = E2X("Open file map"); cchar * choose_map_tip = E2X("Choose file map"); cchar * markers_tip = E2X("Set map markers for all images or current gallery"); // metadata menu cchar * meta_view_main_tip = E2X("List a few key metadata items"); cchar * meta_view_all_tip = E2X("List all metadata items"); cchar * meta_edit_main_tip = E2X("Edit image tags/geotags/caption/rating ..."); cchar * meta_manage_tags_tip = E2X("Define tags (keywords) used for searching images"); cchar * meta_edit_any_tip = E2X("Edit any image metadata"); cchar * meta_delete_tip = E2X("Remove selected image metadata"); cchar * meta_captions_tip = E2X("Show file name, captions, comments"); cchar * meta_places_dates_tip = E2X("Find all images for a location [date]"); cchar * meta_timeline_tip = E2X("Show image counts by month, select and report"); cchar * search_images_tip = E2X("Find images meeting search criteria"); // select area menu cchar * select_tip = E2X("Select object or area for editing"); cchar * select_hairy_tip = E2X("Select hairy or irregular edge"); cchar * select_find_gap_tip = E2X("Find a gap in an area outline"); cchar * select_show_tip = E2X("Show (outline) existing area"); cchar * select_hide_tip = E2X("Hide existing area"); cchar * select_enable_tip = E2X("Enable area for editing"); cchar * select_disable_tip = E2X("Disable area for editing"); cchar * select_invert_tip = E2X("Reverse existing area"); cchar * select_clear_tip = E2X("Erase existing area"); cchar * select_copy_tip = E2X("Copy area for later pasting into image"); cchar * select_paste_tip = E2X("Paste previously copied area into image"); cchar * select_load_tip = E2X("Open a file and paste as area into image"); cchar * select_save_tip = E2X("Save area to a file with transparency"); // edit menu cchar * trim_rotate_tip = E2X("Trim/Crop margins and/or Rotate"); cchar * upright_tip = E2X("Auto upright a rotated image based on EXIF data"); cchar * retouch_tip = E2X("Adjust brightness, contrast, color"); cchar * resize_tip = E2X("Change pixel dimensions"); cchar * adjust_RGB_tip = E2X("Adjust color using RGB or CMY colors"); cchar * adjust_HSL_tip = E2X("Adjust color using HSL colors"); cchar * markup_tip = E2X("Draw on image: text, line/arrow, box, ellipse"); cchar * paint_image_tip = E2X("Paint image pixels using the mouse"); cchar * copy_pixels1_tip = E2X("Copy pixels within an image using the mouse"); cchar * copy_pixels2_tip = E2X("Copy pixels from one image to another using the mouse"); cchar * paint_edits_tip = E2X("Paint edit function gradually with mouse"); cchar * undo_edits_tip = E2X("Incrementally undo prior edits gradually with mouse"); cchar * plugins_tip = E2X("Edit plugins menu or run a plugin function"); cchar * raw_therapee_tip = E2X("Specialized program for editing RAW files"); // enhance menu cchar * voodoo1_tip = E2X("Fast auto enhance that may work OK"); cchar * voodoo2_tip = E2X("Fast auto enhance that may work OK"); cchar * brite_dist_tip = E2X("Edit brightness distribution"); cchar * gradients_tip = E2X("Magnify brightness gradients to enhance details"); cchar * flatten_tip = E2X("Flatten brightness distribution to enhance details"); cchar * retinex_tip = E2X("Rescale RGB - reduce color caste and fog/haze"); cchar * sharpen_tip = E2X("Make the image look sharper"); cchar * blur_tip = E2X("Blur the image, different methods"); cchar * denoise_tip = E2X("Filter noise from low-light photos"); cchar * redeyes_tip = E2X("Fix red-eyes from electronic flash"); cchar * match_colors_tip = E2X("Match colors on one image with another"); cchar * smart_erase_tip = E2X("Remove unwanted objects"); cchar * chromatic1_tip = E2X("Fix color fringes in outer areas of an image"); cchar * chromatic2_tip = E2X("Fix color band on dark/bright feature edges"); cchar * vignette_tip = E2X("Change brightness or color radially"); cchar * remove_dust_tip = E2X("Remove dust spots from scanned slides"); // Effects menu cchar * sketch_tip = E2X("Convert to simulated sketch"); cchar * cartoon_tip = E2X("Convert image into a cartoon drawing"); cchar * line_drawing_tip = E2X("Convert to line drawing (edge detection)"); cchar * emboss_tip = E2X("Create an embossed or 3D appearance"); cchar * tiles_tip = E2X("Convert to square tiles"); cchar * dither_tip = E2X("Convert to dithered dots"); cchar * painting_tip = E2X("Convert into a simulated painting"); cchar * texture_tip = E2X("Add texture to an image"); cchar * pattern_tip = E2X("Tile image with a repeating pattern"); cchar * mosaic_tip = E2X("Create a mosaic with tiles made from all images"); cchar * color_mode_tip = E2X("Make BW/color, negative/positive, sepia"); cchar * color_depth_tip = E2X("Reduce color depth (posterize)"); cchar * shift_colors_tip = E2X("Shift/convert colors into other colors"); cchar * alien_colors_tip = E2X("Change color hue using an algorithm"); cchar * brite_ramp_tip = E2X("Add a brightness/color ramp across the image"); cchar * paint_transp_tip = E2X("Paint image transparency using the mouse"); cchar * mirror_tip = E2X("Mirror image horizontally or vertically"); cchar * anykernel_tip = E2X("Process an image using a custom kernel"); // warp menu cchar * unbend_tip = E2X("Remove curvature, esp. panoramas"); cchar * perspective_tip = E2X("Straighten objects seen from an angle"); cchar * warp_area_tip = E2X("Distort image areas using the mouse"); cchar * unwarp_closeup_tip = E2X("Unwarp closeup face photo to remove distortion"); cchar * warp_curved_tip = E2X("Distort the whole image using the mouse"); cchar * warp_linear_tip = E2X("Distort the whole image using the mouse"); cchar * warp_affine_tip = E2X("Distort the whole image using the mouse"); cchar * flatbook_tip = E2X("Flatten a photographed book page"); cchar * area_rescale_tip = E2X("Rescale image outside selected areas"); cchar * waves_tip = E2X("Warp an image with a wave pattern"); cchar * twist_tip = E2X("Twist image centered at mouse position"); cchar * sphere_tip = E2X("Make a spherical projection of an image"); cchar * stretch_tip = E2X("Image scale increases from center to edge"); cchar * inside_out_tip = E2X("Turn an image inside-out"); cchar * tiny_planet_tip = E2X("Convert an image into a Tiny Planet"); cchar * escher_spiral_tip = E2X("Generate an inward spiraling recursive image"); // combine menu cchar * HDR_tip = E2X("Combine bright/dark images for better detail"); cchar * HDF_tip = E2X("Combine near/far focus images for deeper focus"); cchar * stack_paint_tip = E2X("Combine images to erase passing people, etc."); cchar * stack_noise_tip = E2X("Combine noisy images into a low-noise image"); cchar * stack_layer_tip = E2X("Combine image layers, mouse select and expose"); cchar * stack_slider_tip = E2X("Compare two images separated by sliding boundary"); cchar * image_diffs_tip = E2X("Show differences between two images"); cchar * pano_horz_tip = E2X("Combine images into a panorama"); cchar * pano_vert_tip = E2X("Combine images into a vertical panorama"); cchar * pano_PT_tip = E2X("Combine images into a panorama (panorama tools)"); cchar * montage_tip = E2X("Combine images into a montage of images"); cchar * mashup_tip = E2X("Arrange images and text in a layout (montage)"); // process menu cchar * batch_convert_tip = E2X("Rename/convert/resize/move multiple files"); cchar * batch_upright_tip = E2X("Upright multiple rotated image files"); cchar * batch_deltrash_tip = E2X("Delete or Trash multiple files"); cchar * batch_RAW_tip = E2X("Convert camera RAW files to tiff/png/jpeg"); cchar * burn_DVD_tip = E2X("Burn selected image files to DVD/BlueRay disc"); cchar * export_filelist_tip = E2X("Create a file of selected image files"); cchar * export_files_tip = E2X("Export selected image files to a folder"); cchar * batch_tags_tip = E2X("Add/remove tags for multiple images"); cchar * batch_rename_tags_tip = E2X("Convert tag names for all images"); cchar * batch_photo_DT_tip = E2X("change or shift photo dates/times"); cchar * batch_change_mdata_tip = E2X("Add/change/delete metadata for multiple images"); cchar * batch_report_mdata_tip = E2X("Report metadata for multiple images"); cchar * batch_geotags_tip = E2X("Add/revise geotags for multiple images"); cchar * edit_script_tip = E2X("Build a custom script with multiple edit functions"); cchar * run_script_tip = E2X("Run custom script to edit the current image file"); cchar * batch_script_tip = E2X("Run custom script to edit a batch of image files"); // tools menu cchar * index_tip = E2X("Index new files and make thumbnails"); cchar * quick_index_tip = E2X("Quick incremental index update"); cchar * move_fotoxx_home_tip = E2X("Move Fotoxx home folder"); cchar * preferences_tip = E2X("User preferences and settings"); cchar * KBshortcuts_tip = E2X("Keyboard Shortcuts"); cchar * RGB_dist_tip = E2X("Show RGB brightness distribution"); cchar * magnify_tip = E2X("Magnify image around the mouse position"); cchar * duplicates_tip = E2X("Search all image files and report duplicates"); cchar * show_RGB_tip = E2X("Show RGB colors at mouse click"); cchar * color_profile_tip = E2X("Convert to another color profile"); cchar * calib_printer_tip = E2X("Calibrate printer colors"); cchar * gridlines_tip = E2X("Show or revise grid lines"); cchar * line_color_tip = E2X("Change color of foreground lines"); cchar * darkbrite_tip = E2X("Highlight darkest and brightest pixels"); cchar * map_pixel_bias_tip = E2X("map raw pixel bias (camera sensor, vignette)"); cchar * map_dead_pixels_tip = E2X("map raw dead pixels (camera sensor)"); cchar * monitor_color_tip = E2X("Chart to adjust monitor color"); cchar * monitor_gamma_tip = E2X("Chart to adjust monitor gamma"); cchar * change_lang_tip = E2X("Change the GUI language"); cchar * untranslated_tip = E2X("Report missing translations"); cchar * phone_home_allow_tip = E2X("Anonymous usage statistics"); cchar * resources_tip = E2X("Memory and CPU (to terminal/logfile)"); cchar * appimage_files_tip = E2X("List files included in appimage container"); cchar * zappcrash_test_tip = E2X("test crash report with source line numbers"); // help menu cchar * user_guide_tip = E2X("Read the user guide"); cchar * recent_changes_tip = E2X("Recent user guide changes"); cchar * edit_overview_tip = E2X("Overview of all edit functions"); // 20.0 cchar * changelog_tip = E2X("List updates by Fotoxx version"); cchar * logfile_tip = E2X("View the log file and error messages"); cchar * manpage_tip = E2X("Fotoxx Man Page - summary of capabilities"); // 20.0 cchar * command_params_tip = E2X("List command line parameters"); cchar * translations_tip = E2X("How to do Fotoxx translations"); cchar * homepage_tip = E2X("Show the Fotoxx web page"); cchar * license_tip = E2X("Fotoxx license - terms of use"); // 19.0 cchar * privacy_tip = E2X("Fotoxx privacy policy"); // 19.1 cchar * about_tip = E2X("Version, contact, credits"); // build menu table --------------------------------------------------------- #define MENU(_topmenu, _text, _icon, _desc, _func, _arg) \ me = Nmenus++; \ if (me >= maxmenus) zappcrash("maxmenus exceeded"); \ menutab[me].topmenu = _topmenu; \ menutab[me].menu = _text; \ menutab[me].icon = _icon; \ menutab[me].desc = _desc; \ menutab[me].func = _func; \ if (_arg) menutab[me].arg = _arg; \ else menutab[me].arg = _text; \ int me; Nmenus = 0; mFile = create_popmenu(); MENU(mFile, E2X("File View F"), 0, Fview_tip, m_viewmode, "F" ); MENU(mFile, E2X("New Session"), 0, new_session_tip, m_new_session, 0 ); MENU(mFile, E2X("Source Folder"), 0, source_folder_tip, m_source_folder, 0 ); MENU(mFile, E2X("Cycle 2"), 0, cycle2files_tip, m_cycle2files, 0 ); MENU(mFile, E2X("Cycle 3"), 0, cycle3files_tip, m_cycle3files, 0 ); MENU(mFile, E2X("View 360° Pano"), 0, view360_tip, m_view360, 0); MENU(mFile, E2X("Rename"), 0, rename_tip, m_rename, 0 ); MENU(mFile, E2X("Permissions"), 0, permissions_tip, m_permissions, 0 ); // 20.0 MENU(mFile, E2X("Blank Image"), 0, blank_image_tip, m_blank_image, 0 ); MENU(mFile, E2X("Blank Window"), 0, blank_window_tip, m_blank_window, 0 ); MENU(mFile, E2X("Copy/Move"), 0, copy_move_tip, m_copy_move, 0 ); MENU(mFile, E2X("Copy to Desktop"), 0, copyto_desktop_tip, m_copyto_desktop, 0 ); MENU(mFile, E2X("Copy to Clipboard"), 0, copyto_clipboard_tip, m_copyto_clip, 0 ); MENU(mFile, E2X("Set Wallpaper"), 0, set_wallpaper_tip, m_wallpaper, 0 ); MENU(mFile, E2X("Show on Map"), 0, show_on_net_map_tip, m_netmap_zoomin, 0 ); MENU(mFile, E2X("Delete/Trash"), 0, deltrash_tip, m_delete_trash, 0 ); MENU(mFile, E2X("Print"), 0, print_tip, m_print, 0 ); MENU(mFile, E2X("Print Calibrated"), 0, print_calibrated_tip, m_print_calibrated, 0 ); MENU(mFile, E2X("Quit"), 0, quit_tip, m_quit, 0 ); mGallery = create_popmenu(); MENU(mGallery, E2X("Gallery View G"), 0, Gview_tip, m_viewmode, "G" ); MENU(mGallery, E2X("Thumb View"), 0, thumbview_tip, m_thumbview, 0 ); // 20.04 MENU(mGallery, E2X("Meta View"), 0, metaview_tip, m_metaview, 0 ); MENU(mGallery, E2X("List View"), 0, listview_tip, m_listview, 0 ); MENU(mGallery, E2X("Recent"), 0, recentfiles_tip, m_recentfiles, 0 ); MENU(mGallery, E2X("Newest"), 0, newfiles_tip, m_newfiles, 0 ); MENU(mGallery, E2X("GoTo First"), 0, jump_begin_tip, navi::menufuncx, "First" ); MENU(mGallery, E2X("GoTo Last"), 0, jump_end_tip, navi::menufuncx, "Last" ); MENU(mGallery, E2X("Source Folder"), 0, source_folder_tip, m_source_folder, 0 ); MENU(mGallery, E2X("Sort Gallery"), 0, sort_order_tip, navi::menufuncx, "Sort" ); MENU(mGallery, E2X("All Folders"), 0, alldirs_tip, m_alldirs, 0 ); MENU(mGallery, E2X("Select Files"), 0, select_files_tip, m_select_files, 0 ); MENU(mGallery, E2X("Bookmarks"), 0, bookmarks_tip, m_bookmarks, 0 ); MENU(mGallery, E2X("Manage Albums"), 0, manage_albums_tip, m_manage_albums, 0 ); MENU(mGallery, E2X("Update Albums"), 0, update_albums_tip, m_update_albums, 0 ); MENU(mGallery, E2X("Album Mass Update"), 0, album_mass_update_tip, m_album_mass_update, 0 ); MENU(mGallery, E2X("Gallery to Album"), 0, gallery2album_tip, m_gallery2album, 0 ); MENU(mGallery, E2X("Slide Show"), 0, slideshow_tip, m_slideshow, 0 ); mMap = create_popmenu(); MENU(mMap, E2X("Map View M"), 0, map_view_tip, m_viewmode, "?" ); // prior view 20.0 MENU(mMap, E2X("Net Map"), 0, net_map_tip, m_viewmode, "M"); MENU(mMap, E2X("Net Source"), 0, net_source_tip, m_netmap_source, 0 ); MENU(mMap, E2X("Net Locs"), 0, net_locs_tip, m_netmap_locs, 0 ); MENU(mMap, E2X("File Map"), 0, file_map_tip, m_viewmode, "W" ); MENU(mMap, E2X("Choose Map"), 0, choose_map_tip, m_load_filemap, 0 ); MENU(mMap, E2X("Markers"), 0, markers_tip, m_set_map_markers, 0 ); mMeta = create_popmenu(); MENU(mMeta, E2X("View Meta"), 0, meta_view_main_tip, m_meta_view_short, 0 ); MENU(mMeta, E2X("View All Meta"), 0, meta_view_all_tip, m_meta_view_long, 0 ); MENU(mMeta, E2X("Edit Meta"), 0, meta_edit_main_tip, m_meta_edit_main, 0 ); MENU(mMeta, E2X("Manage Tags"), 0, meta_manage_tags_tip, m_meta_manage_tags, 0 ); MENU(mMeta, E2X("Edit Any Meta"), 0, meta_edit_any_tip, m_meta_edit_any, 0 ); MENU(mMeta, E2X("Delete Meta"), 0, meta_delete_tip, m_meta_delete, 0 ); MENU(mMeta, E2X("Captions"), 0, meta_captions_tip, m_meta_captions, 0 ); MENU(mMeta, E2X("Places/Dates"), 0, meta_places_dates_tip, m_meta_places_dates, 0 ); MENU(mMeta, E2X("Timeline"), 0, meta_timeline_tip, m_meta_timeline, 0 ); MENU(mMeta, E2X("Search"), 0, search_images_tip, m_search_images, 0 ); mArea = create_popmenu(); MENU(mArea, E2X("Select"), 0, select_tip, m_select, 0 ); MENU(mArea, E2X("Select Hairy"), 0, select_hairy_tip, m_select_hairy, 0); MENU(mArea, E2X("Find Gap"), 0, select_find_gap_tip, m_select_find_gap, 0 ); MENU(mArea, E2X("Show"), 0, select_show_tip, m_select_show, 0 ); MENU(mArea, E2X("Hide"), 0, select_hide_tip, m_select_hide, 0 ); MENU(mArea, E2X("Enable"), 0, select_enable_tip, m_select_enable, 0 ); MENU(mArea, E2X("Disable"), 0, select_disable_tip, m_select_disable, 0 ); MENU(mArea, E2X("Invert"), 0, select_invert_tip, m_select_invert, 0 ); MENU(mArea, E2X("Clear"), 0, select_clear_tip, m_select_clear, 0 ); MENU(mArea, E2X("Copy"), 0, select_copy_tip, m_select_copy, 0 ); MENU(mArea, E2X("Paste"), 0, select_paste_tip, m_select_paste, 0 ); MENU(mArea, E2X("Load"), 0, select_load_tip, m_select_load, 0 ); MENU(mArea, E2X("Save"), 0, select_save_tip, m_select_save, 0 ); mEdit = create_popmenu(); MENU(mEdit, E2X("Trim/Rotate"), 0, trim_rotate_tip, m_trim_rotate, 0 ); MENU(mEdit, E2X("Upright"), 0, upright_tip, m_upright, 0 ); MENU(mEdit, E2X("Retouch"), 0, retouch_tip, m_retouch, 0 ); MENU(mEdit, E2X("Resize"), 0, resize_tip, m_resize, 0 ); MENU(mEdit, E2X("Adjust RGB"), 0, adjust_RGB_tip, m_adjust_RGB, 0 ); MENU(mEdit, E2X("Adjust HSL"), 0, adjust_HSL_tip, m_adjust_HSL, 0 ); MENU(mEdit, E2X("Markup"), 0, markup_tip, m_markup, 0 ); // 20.0 MENU(mEdit, E2X("Paint Image"), 0, paint_image_tip, m_paint_image, 0 ); MENU(mEdit, E2X("Copy Pixels 1"), 0, copy_pixels1_tip, m_copypixels1, 0 ); MENU(mEdit, E2X("Copy Pixels 2"), 0, copy_pixels2_tip, m_copypixels2, 0 ); MENU(mEdit, E2X("Paint Edits"), 0, paint_edits_tip, m_paint_edits, 0 ); MENU(mEdit, E2X("Undo Edits"), 0, undo_edits_tip, m_undo_edits, 0 ); MENU(mEdit, E2X("Plugins"), 0, plugins_tip, m_plugins, 0); MENU(mEdit, E2X("Raw Therapee"), 0, raw_therapee_tip, m_rawtherapee, 0); mEnhance = create_popmenu(); MENU(mEnhance, E2X("Voodoo 1"), 0, voodoo1_tip, m_voodoo1, 0 ); MENU(mEnhance, E2X("Voodoo 2"), 0, voodoo2_tip, m_voodoo2, 0); MENU(mEnhance, E2X("Brite Dist"), 0, brite_dist_tip, m_brite_dist, 0 ); MENU(mEnhance, E2X("Gradients"), 0, gradients_tip, m_gradients, 0 ); MENU(mEnhance, E2X("Flatten"), 0, flatten_tip, m_flatten, 0 ); MENU(mEnhance, E2X("Global Retx"), 0, retinex_tip, m_gretinex, 0 ); MENU(mEnhance, E2X("Zonal Retx"), 0, retinex_tip, m_zretinex, 0 ); MENU(mEnhance, E2X("Sharpen"), 0, sharpen_tip, m_sharpen, 0 ); MENU(mEnhance, E2X("Blur"), 0, blur_tip, m_blur, 0 ); MENU(mEnhance, E2X("Denoise"), 0, denoise_tip, m_denoise, 0 ); MENU(mEnhance, E2X("Red Eyes"), 0, redeyes_tip, m_redeyes, 0 ); MENU(mEnhance, E2X("Match Colors"), 0, match_colors_tip, m_match_colors, 0 ); MENU(mEnhance, E2X("Smart Erase"), 0, smart_erase_tip, m_smart_erase, 0 ); MENU(mEnhance, E2X("Chromatic1"), 0, chromatic1_tip, m_chromatic1, 0 ); // 20.0 MENU(mEnhance, E2X("Chromatic2"), 0, chromatic2_tip, m_chromatic2, 0 ); // 20.0 MENU(mEnhance, E2X("Vignette"), 0, vignette_tip, m_vignette, 0 ); MENU(mEnhance, E2X("Remove Dust"), 0, remove_dust_tip, m_remove_dust, 0 ); mEffects = create_popmenu(); MENU(mEffects, E2X("Sketch"), 0, sketch_tip, m_sketch, 0 ); MENU(mEffects, E2X("Cartoon"), 0, cartoon_tip, m_cartoon, 0 ); MENU(mEffects, E2X("Line Drawing"), 0, line_drawing_tip, m_line_drawing, 0 ); MENU(mEffects, E2X("Emboss"), 0, emboss_tip, m_emboss, 0 ); MENU(mEffects, E2X("Tiles"), 0, tiles_tip, m_tiles, 0 ); MENU(mEffects, E2X("Dither"), 0, dither_tip, m_dither, 0 ); MENU(mEffects, E2X("Painting"), 0, painting_tip, m_painting, 0 ); MENU(mEffects, E2X("Texture"), 0, texture_tip, m_texture, 0 ); MENU(mEffects, E2X("Pattern"), 0, pattern_tip, m_pattern, 0 ); MENU(mEffects, E2X("Mosaic"), 0, mosaic_tip, m_mosaic, 0 ); MENU(mEffects, E2X("Color Mode"), 0, color_mode_tip, m_color_mode, 0 ); MENU(mEffects, E2X("Color Depth"), 0, color_depth_tip, m_color_depth, 0 ); MENU(mEffects, E2X("Shift Colors"), 0, shift_colors_tip, m_shift_colors, 0 ); MENU(mEffects, E2X("Alien Colors"), 0, alien_colors_tip, m_alien_colors, 0 ); MENU(mEffects, E2X("Brite Ramp"), 0, brite_ramp_tip, m_brite_ramp, 0 ); MENU(mEffects, E2X("Paint Transp"), 0, paint_transp_tip, m_paint_transp, 0 ); MENU(mEffects, E2X("Mirror"), 0, mirror_tip, m_mirror, 0 ); MENU(mEffects, E2X("Custom Kernel"), 0, anykernel_tip, m_anykernel, 0 ); mWarp = create_popmenu(); MENU(mWarp, E2X("Unbend"), 0, unbend_tip, m_unbend, 0 ); MENU(mWarp, E2X("Perspective"), 0, perspective_tip, m_perspective, 0 ); MENU(mWarp, E2X("Warp area"), 0, warp_area_tip, m_warp_area, 0 ); MENU(mWarp, E2X("Warp curved"), 0, warp_curved_tip, m_warp_curved, 0 ); MENU(mWarp, E2X("Warp linear"), 0, warp_linear_tip, m_warp_linear, 0 ); MENU(mWarp, E2X("Warp affine"), 0, warp_affine_tip, m_warp_affine, 0 ); MENU(mWarp, E2X("Unwarp Closeup"), 0, unwarp_closeup_tip, m_unwarp_closeup, 0 ); MENU(mWarp, E2X("Flatten Book"), 0, flatbook_tip, m_flatbook, 0 ); MENU(mWarp, E2X("Area Rescale"), 0, area_rescale_tip, m_area_rescale, 0); MENU(mWarp, E2X("Make Waves"), 0, waves_tip, m_waves, 0); MENU(mWarp, E2X("Twist"), 0, twist_tip, m_twist, 0); MENU(mWarp, E2X("Sphere"), 0, sphere_tip, m_sphere, 0); MENU(mWarp, E2X("Stretch"), 0, stretch_tip, m_stretch, 0); MENU(mWarp, E2X("Inside-out"), 0, inside_out_tip, m_inside_out, 0); MENU(mWarp, E2X("Tiny Planet"), 0, tiny_planet_tip, m_tiny_planet, 0); MENU(mWarp, E2X("Escher Spiral"), 0, escher_spiral_tip, m_escher_spiral, 0); // 20.0 mComb = create_popmenu(); MENU(mComb, "HDR", 0, HDR_tip, m_HDR, 0 ); MENU(mComb, "HDF", 0, HDF_tip, m_HDF, 0 ); MENU(mComb, E2X("Stack/Paint"), 0, stack_paint_tip, m_stack_paint, 0 ); MENU(mComb, E2X("Stack/Noise"), 0, stack_noise_tip, m_stack_noise, 0 ); MENU(mComb, E2X("Stack/Layer"), 0, stack_layer_tip, m_stack_layer, 0 ); MENU(mComb, E2X("Stack/Slider"), 0, stack_slider_tip, m_stack_slider, 0 ); // 20.0 MENU(mComb, E2X("Image Diffs"), 0, image_diffs_tip, m_image_diffs, 0); MENU(mComb, E2X("Panorama"), 0, pano_horz_tip, m_pano_horz, 0 ); MENU(mComb, E2X("V. Panorama"), 0, pano_vert_tip, m_pano_vert, 0 ); MENU(mComb, E2X("PT Panorama"), 0, pano_PT_tip, m_pano_PT, 0 ); MENU(mComb, E2X("Mashup"), 0, mashup_tip, m_mashup, 0 ); MENU(mComb, E2X("Montage"), 0, montage_tip, m_montage, 0 ); mProc = create_popmenu(); MENU(mProc, E2X("Batch Convert"), 0, batch_convert_tip, m_batch_convert, 0 ); MENU(mProc, E2X("Batch Upright"), 0, batch_upright_tip, m_batch_upright, 0 ); MENU(mProc, E2X("Batch Delete/Trash"), 0, batch_deltrash_tip, m_batch_deltrash, 0 ); MENU(mProc, E2X("Batch RAW"), 0, batch_RAW_tip, m_batch_RAW, 0 ); MENU(mProc, E2X("Burn DVD/BlueRay"), 0, burn_DVD_tip, m_burn_DVD, 0 ); MENU(mProc, E2X("Export File List"), 0, export_filelist_tip, m_export_filelist, 0 ); MENU(mProc, E2X("Export Files"), 0, export_files_tip, m_export_files, 0 ); MENU(mProc, E2X("Batch Tags"), 0, batch_tags_tip, m_batch_tags, 0 ); MENU(mProc, E2X("Batch Rename Tags"), 0, batch_rename_tags_tip, m_batch_rename_tags, 0 ); MENU(mProc, E2X("Batch Photo Date"), 0, batch_photo_DT_tip, m_batch_photo_date_time, 0 ); MENU(mProc, E2X("Batch Change Meta"), 0, batch_change_mdata_tip, m_batch_change_metadata, 0 ); MENU(mProc, E2X("Batch Report Meta"), 0, batch_report_mdata_tip, m_batch_report_metadata, 0 ); MENU(mProc, E2X("Batch Geotags"), 0, batch_geotags_tip, m_batch_geotags, 0 ); MENU(mProc, E2X("Edit Script"), 0, edit_script_tip, m_edit_script, 0 ); MENU(mProc, E2X("Run Script"), 0, run_script_tip, m_run_script, 0); MENU(mProc, E2X("Batch Script"), 0, batch_script_tip, m_batch_script, 0); mTools = create_popmenu(); MENU(mTools, E2X("Index Files"), 0, index_tip, m_index, 0 ); MENU(mTools, E2X("Quick Index"), 0, quick_index_tip, m_quick_index, 0 ); // 20.0 MENU(mTools, E2X("Move Fotoxx Home"), 0, move_fotoxx_home_tip, m_move_fotoxx_home, 0 ); MENU(mTools, E2X("Preferences"), 0, preferences_tip, m_preferences, 0 ); MENU(mTools, E2X("KB Shortcuts"), 0, KBshortcuts_tip, m_KBshortcuts, 0 ); MENU(mTools, E2X("RGB Distribution"), 0, RGB_dist_tip, m_RGB_dist, 0 ); MENU(mTools, E2X("Magnify Image"), 0, magnify_tip, m_magnify, 0 ); MENU(mTools, E2X("Find Duplicates"), 0, duplicates_tip, m_duplicates, 0 ); MENU(mTools, E2X("Show RGB"), 0, show_RGB_tip, m_show_RGB, 0 ); MENU(mTools, E2X("Color Profile"), 0, color_profile_tip, m_color_profile, 0 ); MENU(mTools, E2X("Calibrate Printer"), 0, calib_printer_tip, m_calibrate_printer, 0 ); MENU(mTools, E2X("Grid Lines"), 0, gridlines_tip, m_gridlines, 0 ); MENU(mTools, E2X("Line Color"), 0, line_color_tip, m_line_color, 0 ); MENU(mTools, E2X("Dark/Bright Pixels"), 0, darkbrite_tip, m_darkbrite, 0 ); MENU(mTools, E2X("Map Pixel Bias"), 0, map_pixel_bias_tip, m_map_pixel_bias, 0 ); // 20.0 MENU(mTools, E2X("Map Dead Pixels"), 0, map_dead_pixels_tip, m_map_dead_pixels, 0 ); // 20.0 MENU(mTools, E2X("Monitor Color"), 0, monitor_color_tip, m_monitor_color, 0 ); MENU(mTools, E2X("Monitor Gamma"), 0, monitor_gamma_tip, m_monitor_gamma, 0 ); MENU(mTools, E2X("Change Language"), 0, change_lang_tip, m_change_lang, 0 ); MENU(mTools, E2X("Missing Translations"), 0, untranslated_tip, m_untranslated, 0 ); MENU(mTools, E2X("Phone Home"), 0, phone_home_allow_tip, m_phone_home_allow, 0 ); // 20.0 MENU(mTools, E2X("Show Resources"), 0, resources_tip, m_resources, 0 ); MENU(mTools, E2X("Appimage Files"), 0, appimage_files_tip, m_appimage_files, 0 ); // 20.0 MENU(mTools, E2X("Zappcrash Test"), 0, zappcrash_test_tip, m_zappcrash_test, 0 ); mHelp = create_popmenu(); MENU(mHelp, E2X("User Guide"), 0, user_guide_tip, m_help, 0 ); MENU(mHelp, E2X("Recent Changes"), 0, recent_changes_tip, m_help, 0 ); MENU(mHelp, E2X("Edit Functions Overview"), 0, edit_overview_tip, m_help, 0 ); // 20.0 MENU(mHelp, E2X("Change Log"), 0, changelog_tip, m_help, 0 ); MENU(mHelp, E2X("Log File"), 0, logfile_tip, m_help, 0 ); MENU(mHelp, "Man Page", 0, manpage_tip, m_help, 0 ); // 20.0 MENU(mHelp, E2X("Command Params"), 0, command_params_tip, m_help, 0 ); MENU(mHelp, E2X("Translations"), 0, translations_tip, m_help, 0 ); MENU(mHelp, E2X("Home Page"), 0, homepage_tip, m_help, 0 ); MENU(mHelp, E2X("License"), 0, license_tip, m_help, 0 ); // 19.0 MENU(mHelp, E2X("Privacy"), 0, privacy_tip, m_help, 0 ); // 19.1 MENU(mHelp, E2X("About"), 0, about_tip, m_help, 0 ); // main menu buttons - must be last MENU(0, E2X("File"), "menuF.png", File_tip, (cbFunc *) popup_menu, (cchar *) mFile); MENU(0, E2X("Gallery"), "menuG.png", Gallery_tip, (cbFunc *) popup_menu, (cchar *) mGallery); MENU(0, E2X("Maps"), "menuM.png", Maps_tip, (cbFunc *) popup_menu, (cchar *) mMap); MENU(0, E2X("Favorites"), "favorites.png", favorites_tip, m_favorites, 0 ); MENU(0, E2X("Prev/Next"), "prev-next.png", prev_next_tip, m_prev_next, 0 ); MENU(0, E2X("Zoom/±"), "zoom_menu.png", zoom_menu_tip, m_zoom_menu, 0 ); MENU(0, E2X("Save"), "save.png", save_tip, m_file_save, 0 ); MENU(0, E2X("Meta"), "meta.png", meta_tip, (cbFunc *) popup_menu, (cchar *) mMeta); MENU(0, E2X("Areas"), "areas.png", areas_tip, (cbFunc *) popup_menu, (cchar *) mArea); MENU(0, E2X("Undo/Redo"), "undo_redo.png", undo_redo_tip, m_undo_redo, 0 ); MENU(0, E2X("Edit"), "edit.png", edit_tip, (cbFunc *) popup_menu, (cchar *) mEdit); MENU(0, E2X("Enhance"), "enhance.png", enhance_tip, (cbFunc *) popup_menu, (cchar *) mEnhance); MENU(0, E2X("Effects"), "effects.png", effects_tip, (cbFunc *) popup_menu, (cchar *) mEffects); MENU(0, E2X("Warp"), "warp.png", warp_tip, (cbFunc *) popup_menu, (cchar *) mWarp); MENU(0, E2X("Combine"), "combine.png", combine_tip, (cbFunc *) popup_menu, (cchar *) mComb); MENU(0, E2X("Process"), "process.png", process_tip, (cbFunc *) popup_menu, (cchar *) mProc); MENU(0, E2X("Tools"), "tools.png", tools_tip, (cbFunc *) popup_menu, (cchar *) mTools); MENU(0, E2X("Help"), "help.png", help_tip, (cbFunc *) popup_menu, (cchar *) mHelp); int Vmenus = Nmenus; // visible menu count // internal menus that are not shown in the user menus MENU(0, "Copy Pixels 3", 0, "Copy Pixels Source", m_copypixels3, 0 ); MENU(0, "Validate User Guide", 0, "check all user guide links", m_help, 0); // 20.0 MENU(0, "Autosearch", 0, "Write file list to stdout", m_autosearch, 0); // 20.0 MENU(0, "dither0", 0, "Roy Lichtenstein Effect", m_dither0, 0); // 20.0 MENU(0, "dither1", 0, "pure RGB or black/white dots", m_dither1, 0); // 20.0 MENU(0, "dither2", 0, "RGB mix with given bit-depth", m_dither2, 0); // 20.0 MENU(0, "dither3", 0, "custom palette colors", m_dither3, 0); // 20.0 // build the menu buttons for the main menu --------------------------- float frgb[3], brgb[3]; frgb[0] = MFrgb[0] / 256.0; // menu font color frgb[1] = MFrgb[1] / 256.0; // convert range to 0-1 frgb[2] = MFrgb[2] / 256.0; brgb[0] = MBrgb[0] / 256.0; // menu background color brgb[1] = MBrgb[1] / 256.0; // convert range to 0-1 brgb[2] = MBrgb[2] / 256.0; Vmenu *Xvm = Vmenu_new(MWmenu,frgb,brgb); // create main menu int siz = iconsize; // user preferences parameter for (me = 0; me < Vmenus; me++) { if (menutab[me].topmenu) // submenu within top menu add_popmenu_item(menutab[me].topmenu, menutab[me].menu, menutab[me].func, menutab[me].arg, menutab[me].desc); else // top menu (button) { if (strmatch(menu_style,"icons")) { // icons only if (menutab[me].icon) Vmenu_add(Xvm, 0, menutab[me].icon,siz,siz,menutab[me].desc, menutab[me].func, menutab[me].arg); else // no icon, use menu text Vmenu_add(Xvm, menutab[me].menu, 0, 0, 0, menutab[me].desc, menutab[me].func, menutab[me].arg); } else if (strmatch(menu_style,"text")) { // text only Vmenu_add(Xvm, menutab[me].menu, 0, 0, 0, menutab[me].desc, menutab[me].func, menutab[me].arg); } else // icons + menu text Vmenu_add(Xvm, menutab[me].menu, menutab[me].icon, siz, siz, menutab[me].desc, menutab[me].func, menutab[me].arg); } } Vmenu_add_RMfunc(Xvm, 0, m_viewmode, "F"); // add right-mouse function 19.0 Vmenu_add_RMfunc(Xvm, 1, m_viewmode, "G"); // for top 3 menu buttons Vmenu_add_RMfunc(Xvm, 2, m_viewmode, "?"); // build table of eligible menus for KB shortcut assignment ----------------- #define KBshort(_menu, _func, _arg) \ me = Nkbsf++; \ if (me >= maxkbsf) zappcrash("maxkbs exceeded"); \ kbsftab[me].menu = _menu; \ kbsftab[me].func = _func; \ kbsftab[me].arg = _arg; Nkbsf = 0; // menu called function arg KBshort("Adjust RGB", m_adjust_RGB, 0 ); KBshort("Adjust HSL", m_adjust_HSL, 0 ); KBshort("All Folders", m_alldirs, 0 ); KBshort("Blank Window", m_blank_window, 0 ); KBshort("Blur", m_blur, 0 ); KBshort("Bookmarks", m_bookmarks, 0 ); KBshort("Brite Dist", m_brite_dist, 0 ); KBshort("Captions", m_meta_captions, "x" ); KBshort("Change Language", m_change_lang, 0 ); KBshort("Color Depth", m_color_depth, 0 ); KBshort("Color Mode", m_color_mode, 0 ); KBshort("Copy/Move", m_copy_move, 0 ); KBshort("Copy to Clipboard", m_copyto_clip, 0 ); KBshort("Copy to Desktop", m_copyto_desktop, 0 ); KBshort("Current Album", m_current_album, 0 ); // 20.0 KBshort("Cycle 2", m_cycle2files, 0 ); KBshort("Cycle 3", m_cycle3files, 0 ); KBshort("Delete Meta", m_meta_delete, 0 ); KBshort("Delete/Trash", m_delete_trash, 0 ); KBshort("Denoise", m_denoise, 0 ); KBshort("Edit Any Meta", m_meta_edit_any, 0 ); KBshort("Edit Meta", m_meta_edit_main, 0 ); KBshort("File View", m_viewmode, "F" ); KBshort("Flatten", m_flatten, 0 ); KBshort("Gallery", m_viewmode, "G" ); KBshort("Global Retx", m_gretinex, 0 ); KBshort("Gradients", m_gradients, 0 ); KBshort("Grid Lines", m_gridlines, 0 ); KBshort("KB Shortcuts", m_KBshortcuts, 0 ); KBshort("Line Color", m_line_color, 0 ); KBshort("List View", m_listview, 0 ); // 20.0 KBshort("Magnify Image", m_magnify, 0 ); KBshort("Manage Albums", m_manage_albums, 0 ); KBshort("Map View", m_viewmode, "?" ); KBshort("Markup", m_markup, 0 ); KBshort("Meta View", m_metaview, 0 ); // 20.0 KBshort("Mirror", m_mirror, 0 ); KBshort("Newest", m_newfiles, 0 ); KBshort("New Version", m_file_save_version, 0 ); KBshort("Places/Dates", m_meta_places_dates, 0 ); KBshort("Print", m_print, 0 ); KBshort("Print Calibrated", m_print_calibrated, 0 ); KBshort("Raw Therapee", m_rawtherapee, 0 ); KBshort("Recent", m_recentfiles, 0 ); KBshort("Red Eyes", m_redeyes , 0 ); KBshort("Redo", m_redo, 0 ); KBshort("Rename", m_rename, 0 ); KBshort("RGB Distribution", m_RGB_dist, 0 ); KBshort("Permissions", m_permissions, 0 ); // 20.0 KBshort("Replace", m_file_save_replace, 0 ); KBshort("Resize", m_resize, 0 ); KBshort("Retouch", m_retouch, 0 ); KBshort("Save", m_file_save, 0 ); KBshort("Search", m_search_images, 0 ); KBshort("Select Files", m_select_files, 0 ); // 20.0 KBshort("Sharpen", m_sharpen, 0 ); KBshort("Show Hidden", m_show_hidden, 0 ); KBshort("Show on Map", m_netmap_zoomin, 0 ); KBshort("Source Folder", m_source_folder, 0 ); KBshort("Timeline", m_meta_timeline, 0 ); KBshort("Trim/Rotate", m_trim_rotate, 0 ); KBshort("Undo", m_undo, 0 ); KBshort("Upright", m_upright, 0 ); KBshort("Preferences", m_preferences, 0 ); KBshort("View Meta", m_meta_view_short, 0 ); KBshort("View 360° Pano", m_view360, 0 ); KBshort("Voodoo 1", m_voodoo1, 0 ); KBshort("Voodoo 2", m_voodoo2, 0 ); KBshort("Zonal Retx", m_zretinex, 0 ); // zdialog completion buttons that can have KB shortcuts KBshort("Done", 0, 0 ); KBshort("Cancel", 0, 0 ); KBshort("Apply", 0, 0 ); KBshort("Reset", 0, 0 ); // build right-click popup menus -------------------------------------------- cchar *menupopimage = E2X("Popup Image"); cchar *menumeta1 = E2X("View Meta"); cchar *menumeta1A = E2X("View All Meta"); cchar *menumeta2 = E2X("Edit Meta"); cchar *menumeta3 = E2X("Edit Any Meta"); cchar *menurename = E2X("Rename"); cchar *menupermissions = E2X("Permissions"); // 20.0 cchar *menucopymove = E2X("Copy/Move"); cchar *menucopytodesktop = E2X("Copy to Desktop"); cchar *menucopytoclip = E2X("Copy to Clipboard"); cchar *menualbumaddselfiles = E2X("Add Selected Files Here"); cchar *menualbumaddcurrfile = E2X("Add Current File Here"); cchar *menuremovefromalbum = E2X("Remove from Album"); cchar *menutrimrotate = E2X("Trim/Rotate"); cchar *menuresize = E2X("Resize"); cchar *menuupright = E2X("Upright"); cchar *menuvoodoo1 = "Voodoo 1"; cchar *menuvoodoo2 = "Voodoo 2"; cchar *menuretouch = E2X("Retouch"); cchar *menubritedist = E2X("Brite Dist"); cchar *menuflatten = E2X("Flatten"); cchar *menugradients = E2X("Gradients"); cchar *menuselect = E2X("Select Area"); cchar *menuopenraw = E2X("Raw Therapee"); cchar *menunetzoom = E2X("Show on Map"); cchar *menudeltrash = E2X("Delete/Trash"); popmenu_image = create_popmenu(); // popup menu for image files add_popmenu_item(popmenu_image,menumeta1,popup_menufunc,"view metadata"); add_popmenu_item(popmenu_image,menumeta1A,popup_menufunc,"view all metadata"); add_popmenu_item(popmenu_image,menumeta2,popup_menufunc,"edit metadata"); add_popmenu_item(popmenu_image,menumeta3,popup_menufunc,"edit any metadata"); add_popmenu_item(popmenu_image,menurename,popup_menufunc,"rename"); add_popmenu_item(popmenu_image,menupermissions,popup_menufunc,"permissions"); add_popmenu_item(popmenu_image,menucopymove,popup_menufunc,"copymove"); add_popmenu_item(popmenu_image,menucopytodesktop,popup_menufunc,"copytodesktop"); add_popmenu_item(popmenu_image,menucopytoclip,popup_menufunc,"copytoclip"); add_popmenu_item(popmenu_image,menuupright,popup_menufunc,"upright"); add_popmenu_item(popmenu_image,menutrimrotate,popup_menufunc,"trim/rotate"); add_popmenu_item(popmenu_image,menuresize,popup_menufunc,"resize"); add_popmenu_item(popmenu_image,menuvoodoo1,popup_menufunc,"voodoo1"); add_popmenu_item(popmenu_image,menuvoodoo2,popup_menufunc,"voodoo2"); add_popmenu_item(popmenu_image,menuretouch,popup_menufunc,"retouch"); add_popmenu_item(popmenu_image,menubritedist,popup_menufunc,"brite dist"); add_popmenu_item(popmenu_image,menuflatten,popup_menufunc,"flatten"); add_popmenu_item(popmenu_image,menugradients,popup_menufunc,"gradients"); add_popmenu_item(popmenu_image,menuselect,popup_menufunc,"select"); add_popmenu_item(popmenu_image,menunetzoom,popup_menufunc,"netmap_zoomin"); add_popmenu_item(popmenu_image,menudeltrash,popup_menufunc,"delete/trash"); popmenu_raw = create_popmenu(); // popup menu for RAW files add_popmenu_item(popmenu_raw,menuopenraw,m_rawtherapee,0); add_popmenu_item(popmenu_raw,menumeta1,popup_menufunc,"view metadata"); add_popmenu_item(popmenu_raw,menumeta1A,popup_menufunc,"view all metadata"); add_popmenu_item(popmenu_raw,menumeta2,popup_menufunc,"edit metadata"); add_popmenu_item(popmenu_raw,menurename,popup_menufunc,"rename"); add_popmenu_item(popmenu_raw,menupermissions,popup_menufunc,"permissions"); add_popmenu_item(popmenu_raw,menucopymove,popup_menufunc,"copymove"); add_popmenu_item(popmenu_raw,menucopytodesktop,popup_menufunc,"copytodesktop"); add_popmenu_item(popmenu_raw,menunetzoom,popup_menufunc,"netmap_zoomin"); add_popmenu_item(popmenu_raw,menudeltrash,popup_menufunc,"delete/trash"); popmenu_video = create_popmenu(); // popup menu for VIDEO files add_popmenu_item(popmenu_video,menumeta1,popup_menufunc,"view metadata"); add_popmenu_item(popmenu_video,menumeta1A,popup_menufunc,"view all metadata"); add_popmenu_item(popmenu_video,menumeta2,popup_menufunc,"edit metadata"); add_popmenu_item(popmenu_video,menurename,popup_menufunc,"rename"); add_popmenu_item(popmenu_video,menupermissions,popup_menufunc,"permissions"); add_popmenu_item(popmenu_video,menucopymove,popup_menufunc,"copymove"); add_popmenu_item(popmenu_video,menucopytodesktop,popup_menufunc,"copytodesktop"); add_popmenu_item(popmenu_video,menunetzoom,popup_menufunc,"netmap_zoomin"); add_popmenu_item(popmenu_video,menudeltrash,popup_menufunc,"delete/trash"); popmenu_thumb = create_popmenu(); // gallery thumbnail popup menu add_popmenu_item(popmenu_thumb,menupopimage,popup_menufunc,"popimage"); add_popmenu_item(popmenu_thumb,menumeta1,popup_menufunc,"view metadata"); add_popmenu_item(popmenu_thumb,menumeta1A,popup_menufunc,"view all metadata"); add_popmenu_item(popmenu_thumb,menumeta2,popup_menufunc,"edit metadata"); add_popmenu_item(popmenu_thumb,menumeta3,popup_menufunc,"edit any metadata"); add_popmenu_item(popmenu_thumb,menurename,popup_menufunc,"rename"); add_popmenu_item(popmenu_thumb,menupermissions,popup_menufunc,"permissions"); add_popmenu_item(popmenu_thumb,menucopymove,popup_menufunc,"copymove"); add_popmenu_item(popmenu_thumb,menucopytodesktop,popup_menufunc,"copytodesktop"); add_popmenu_item(popmenu_thumb,menucopytoclip,popup_menufunc,"copytoclip"); add_popmenu_item(popmenu_thumb,menuupright,popup_menufunc,"upright"); add_popmenu_item(popmenu_thumb,menunetzoom,popup_menufunc,"netmap_zoomin"); add_popmenu_item(popmenu_thumb,menudeltrash,popup_menufunc,"delete/trash"); popmenu_album = create_popmenu(); // album thumbnail popup menu add_popmenu_item(popmenu_album,menupopimage,popup_menufunc,"popimage"); add_popmenu_item(popmenu_album,menumeta1,popup_menufunc,"view metadata"); add_popmenu_item(popmenu_album,menumeta1A,popup_menufunc,"view all metadata"); add_popmenu_item(popmenu_album,menumeta2,popup_menufunc,"edit metadata"); add_popmenu_item(popmenu_album,menumeta3,popup_menufunc,"edit any metadata"); add_popmenu_item(popmenu_album,menucopytodesktop,popup_menufunc,"copytodesktop"); add_popmenu_item(popmenu_album,menucopytoclip,popup_menufunc,"copytoclip"); add_popmenu_item(popmenu_album,menualbumaddselfiles,popup_menufunc,"albumaddselfiles"); add_popmenu_item(popmenu_album,menualbumaddcurrfile,popup_menufunc,"albumaddcurrfile"); add_popmenu_item(popmenu_album,menuremovefromalbum,popup_menufunc,"removefromalbum"); return; } // right-click popup menu response function void popup_menufunc(GtkWidget *, cchar *menu) { if (strmatch(menu,"popimage")) gallery_popimage(); // funcs for main and gallery windows if (strmatch(menu,"upright")) m_upright(0,0); // 20.0 if (strmatch(menu,"view metadata")) meta_view(1); if (strmatch(menu,"view all metadata")) meta_view(2); if (strmatch(menu,"edit metadata")) m_meta_edit_main(0,0); // these use clicked_file if defined, if (strmatch(menu,"edit any metadata")) m_meta_edit_any(0,0); // else they use curr_file. if (strmatch(menu,"rename")) m_rename(0,0); if (strmatch(menu,"permissions")) m_permissions(0,0); // 20.0 if (strmatch(menu,"copymove")) m_copy_move(0,0); if (strmatch(menu,"copytodesktop")) m_copyto_desktop(0,0); if (strmatch(menu,"delete/trash")) m_delete_trash(0,0); if (strmatch(menu,"netmap_zoomin")) m_netmap_zoomin(0,0); if (strmatch(menu,"copytoclip")) m_copyto_clip(0,0); if (strmatch(menu,"removefromalbum")) album_removefile(clicked_posn); // funcs for album gallery depend if (strmatch(menu,"albumaddselfiles")) album_addselfiles(2); // on 'clicked_posn' being set if (strmatch(menu,"albumaddcurrfile")) album_addcurrfile(2); if (strmatch(menu,"trim/rotate")) m_trim_rotate(0,0); // functions using curr_file only if (strmatch(menu,"resize")) m_resize(0,0); // (not for gallery/thumbnail click) if (strmatch(menu,"voodoo1")) m_voodoo1(0,0); if (strmatch(menu,"voodoo2")) m_voodoo2(0,0); if (strmatch(menu,"retouch")) m_retouch(0,0); if (strmatch(menu,"brite dist")) m_brite_dist(0,0); if (strmatch(menu,"flatten")) m_flatten(0,0); if (strmatch(menu,"gradients")) m_gradients(0,0); if (strmatch(menu,"select")) m_select(0,0); return; } // main window mouse right-click popup menu void image_Rclick_popup() { int ftype; if (! curr_file) return; ftype = image_file_type(curr_file); if (ftype == IMAGE) popup_menu(Mwin,popmenu_image); if (ftype == RAW) popup_menu(Mwin,popmenu_raw); if (ftype == VIDEO) popup_menu(Mwin,popmenu_video); return; } // gallery thumbnail mouse left-click function // open the clicked file in view mode F void gallery_Lclick_func(int Nth) { char *file; int err; if (clicked_file) { // lose memory of clicked thumbnail zfree(clicked_file); clicked_file = 0; } if (checkpend("busy block mods")) return; file = gallery(0,"get",Nth); if (! file) return; err = f_open(file,Nth,0,1); // clicked file >> current file zfree(file); if (! err) m_viewmode(0,"F"); return; } // gallery thumbnail mouse right-click popup menu void gallery_Rclick_popup(int Nth) { FTYPE ftype; clicked_posn = Nth; // clicked gallery position (0 base) clicked_file = gallery(0,"get",Nth); // clicked_file is subject for zfree() if (! clicked_file) return; ftype = image_file_type(clicked_file); if (navi::gallerytype == ALBUM) popup_menu(Mwin,popmenu_album); else if (ftype == IMAGE) popup_menu(Mwin,popmenu_thumb); else if (ftype == RAW) popup_menu(Mwin,popmenu_raw); else if (ftype == VIDEO) popup_menu(Mwin,popmenu_video); return; } /********************************************************************************/ // set window view mode, F/G/M/W void m_viewmode(GtkWidget *, cchar *fgwm) { static char lastWM[4] = "M"; if (FGWM == *fgwm) return; // no change if (*fgwm == '0') // set no view mode (blank window) 19.0 { gtk_widget_hide(Fhbox); gtk_widget_hide(Ghbox); gtk_widget_hide(Whbox); gtk_widget_hide(Mhbox); FGWM = '0'; PFGWM = '0'; // remember last F/G view Cstate = 0; // no F/W image drawing area Cdrawin = 0; gdkwin = 0; if (zd_deltrash) m_delete_trash(0,0); // set target file in active dialog if (zd_copymove) m_copy_move(0,0); if (zd_metaview) meta_view(0); if (zd_rename) m_rename(0,0); if (zd_permissions) m_permissions(0,0); // 20.0 } if (*fgwm == 'F') // set F view mode for image file { gtk_widget_hide(Ghbox); gtk_widget_hide(Whbox); gtk_widget_hide(Mhbox); gtk_widget_show_all(Fhbox); FGWM = 'F'; PFGWM = 'F'; // remember last F/G view set_mwin_title(); Cstate = &Fstate; // set drawing area Cdrawin = Fdrawin; gdkwin = gtk_widget_get_window(Fdrawin); // GDK window if (zd_deltrash) m_delete_trash(0,0); // set target file in active dialog if (zd_copymove) m_copy_move(0,0); if (zd_metaview) meta_view(0); if (zd_rename) m_rename(0,0); if (zd_permissions) m_permissions(0,0); // 20.0 } if (*fgwm == 'G') // set G view mode for thumbnail gallery { gtk_widget_hide(Fhbox); gtk_widget_hide(Whbox); gtk_widget_hide(Mhbox); gtk_widget_show_all(Ghbox); FGWM = 'G'; PFGWM = 'G'; // remember last F/G view Cstate = 0; // no F/W image drawing area Cdrawin = 0; gdkwin = 0; if (curr_file) gallery(curr_file,"paint",0); // set gallery posn. at curr. file else gallery(0,"paint",-1); // else leave unchanged } if (*fgwm == 'M') // set M view mode for net maps { if (CEF) return; // don't interrupt edit func. gtk_widget_hide(Fhbox); gtk_widget_hide(Ghbox); gtk_widget_hide(Whbox); gtk_widget_show_all(Mhbox); FGWM = 'M'; lastWM[0] = 'M'; Cstate = 0; // no F/W image drawing area Cdrawin = 0; gdkwin = 0; m_load_netmap(0,"init"); // load net initial map gtk_window_set_title(MWIN,E2X("Fotoxx Image Locations")); // window title } if (*fgwm == 'W') // set W view mode for file maps { if (CEF) return; // don't interrupt edit func. gtk_widget_hide(Fhbox); gtk_widget_hide(Ghbox); gtk_widget_hide(Mhbox); gtk_widget_show_all(Whbox); FGWM = 'W'; lastWM[0] = 'W'; Cstate = &Wstate; // set drawing area Cdrawin = Wdrawin; gdkwin = gtk_widget_get_window(Wdrawin); // GDK window if (! Wstate.fpxb) m_load_filemap(0,"default"); // no map loaded, load default map gtk_window_set_title(MWIN,E2X("Fotoxx Image Locations")); // window title Fpaintnow(); } if (*fgwm == '?') m_viewmode(0,lastWM); // use last set W/M mode 19.0 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"; snprintf(menuconfigfile,200,"%s/menu-config",favorites_folder); Gmenuz(Mwin,E2X("Favorites"),menuconfigfile,favorites_callback); return; } // response function for clicked menu // a menu function is called as from the text menus void favorites_callback(cchar *menu) { int ii; if (! menu) return; if (strmatchcase(menu,"quit")) return; if (strmatchcase(menu,"help")) { // special case showz_docfile(Mwin,"userguide","favorites"); // 'help' menu in favorites menu return; } for (ii = 0; ii < Nmenus; ii++) { // search for menu function if (! menutab[ii].menu) continue; if (! menutab[ii].topmenu) continue; if (strmatchcase(menu,menutab[ii].menu)) break; if (strmatchcase(E2X(menu),menutab[ii].menu)) break; } if (ii == Nmenus) { zmessageACK(Mwin,E2X("invalid menu name: %s"),menu); return; } menutab[ii].func(0,menu); // start the function return; } fotoxx-20.08/fotoxx.cc000066400000000000000000006246611362435004500147310ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ********************************************************************************* main main program - defaults, initializations, command line options first_startup first fotoxx startup, user initial index decision release_startup new release startup, merge config files with user changes delete_event response function for main window delete event destroy_event response function for main window destroy event state_event response function for main window fullscreen state change drop_event response function for main window file drag-drop event paintlock block window paint during E1/E3 image updates 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 Fpaint3_thread Fpaint3, callable from threads Fpaint0 same as Fpaint3 but uses E0 instead of E3 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 KBevent send KB key from dialog to main window KBpress KB key press event function win_fullscreen set main window full screen status win_unfullscreen restore main window to former size set_mwin_title update the main window title bar draw_pixel draw one overlay pixel using image space erase_pixel erase one pixel draw_line draw overlay line in image space erase_line erase line draw_toplines draw or redraw a set of overlay lines draw_gridlines draw grid lines over image add_toptext add to set of overlay text strings draw_toptext draw set of overlay text strings erase_toptext remove text from set of overlay text strings draw_text draw overlay text on window in image space add_topcircle add a circle to set of overlay circles draw_topcircles draw the set of overlay circles in window space erase_topcircles erase the set of overlay circles draw_mousecircle draw a circle around pointer in image space draw_mousecircle2 2nd instance for paint/clone tracking circle draw_mousearc draw an ellipse around pointer in image space splcurve_init set up a spline curve drawing area splcurve_adjust mouse event function to manipulate curve nodes splcurve_addnode add an anchor point to a curve splcurve_resize resize drawing area if too small splcurve_draw draw curve through nodes splcurve_generate generate x/y table of values from curve splcurve_yval get curve y-value for given x-value splcurve_load load curve data from a saved file splcurve_save save curve data to a file edit_setup start an image edit function CEF_invalid diagnostic edit_cancel cancle image edit edit_done finish image edit edit_fullsize convert preview to full size edit edit_undo undo current edit (reset) edit_redo redo current edit edit_reset reset all edit changes func_load_widgets load zdialog widgets and curves from a file func_save_widgets save zdialog widgets and curves to a file func_load_prev_widgets load last-used widgets (for [prev] buttons) func_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 do_wthreads start N working threads, wait for completion save_params save parameters when fotoxx exits load_params load parameters at fotoxx startup free_resources free resources for the current image file *********************************************************************************/ #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[]) // Fotoxx main program { char *homedir, *temp, *pp; char *pid, *pidlist; int Fclone=0, cloxx=0, cloyy=0, cloww=0, clohh=0; int ii, jj, cc, err; char cssfile[200]; STATB statb; int Fexiftool = 0, Fdcraw = 0; // mandatory programs 20.0 FTYPE ftype; FILE *fid; char filename[200], buff[200]; double freememory, cachememory; double startsecs = get_seconds(); // start time err = appimage_install("fotoxx"); // if appimage, menu integration 19.1 if (err == 4) { printz("failed to create ~/.local/share/applications/fotoxx.desktop \n" // 19.1 "desktop menu integration did not work \n"); exit(0); } printz("%s \n",Frelease); // print fotoxx version 20.0 if (argc > 1 && strmatchV(argv[1],"-ver","-v",0)) // exit if nothing else wanted exit(0); if (argc > 1 && strmatch(argv[1],"-uninstall")) { // uninstall appimage 19.0 appimage_unstall(); // (does not return) exit(0); } setpgid(0,0); // make a new process group 19.0 setenv("GTK_THEME","default",0); // set theme if missing (KDE etc.) setenv("GDK_BACKEND","x11",0); // wayland 20.0 if (gtk_clutter_init(&argc,&argv) != CLUTTER_INIT_SUCCESS) // intiz. clutter and GTK zexit("initz. clutter and GTK failure"); 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); homedir = 0; if (argc > 2 && strmatch(argv[1],"-home")) homedir = argv[2]; // relocate user folder zinitapp(Frelease,homedir); // initz. app folders 20.0 // modify GTK widgets to take less screen space snprintf(cssfile,200,"%s/widgets.css",get_zhomedir()); GtkStyleProvider *provider = (GtkStyleProvider *) gtk_css_provider_new(); gtk_style_context_add_provider_for_screen(zfuncs::screen,provider,999); gtk_css_provider_load_from_path(GTK_CSS_PROVIDER(provider),cssfile,0); // initialize externals to default values (saved parameters will override these) strcpy(zfuncs::zappname,Frelease); // app name and version Ffirsttime = 1; // first startup (params override) Findexlev = 2; // direct exec: old index + search new FMindexlev = 1; // file manager: old index only 20.0 Pindexlev = -1; // no -index command parameter xxrec_tab = 0; // no image index yet Nxxrec = Findexvalid = 0; Prelease = zstrdup("unknown"); // prev. release (params override) mwgeom[0] = mwgeom[1] = 100; // default main window geometry mwgeom[2] = 1200; mwgeom[3] = 800; *paneltext = 0; // no status bar text trimsizes[0] = zstrdup("1920x1080"); // default trim size memory trimsizes[1] = zstrdup("1600x900"); trimsizes[2] = zstrdup("1440x900"); trimsizes[3] = zstrdup("1280x1024"); trimsizes[4] = zstrdup("1366x768"); trimsizes[5] = zstrdup("1280x800"); trimsizes[6] = zstrdup("1024x768"); 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"); editresize[0] = 1600; // default initial resize size editresize[1] = 1200; currgrid = 0; // default initial grid settings 20.0 for (ii = 0; ii < 6; ii++) { // 6 grids gridsettings[ii][GON] = 0; // grid disabled gridsettings[ii][GX] = gridsettings[ii][GY] = 1; // x/y-lines enabled gridsettings[ii][GXS] = gridsettings[ii][GYS] = 100; // x/y-lines spacing gridsettings[ii][GXC] = gridsettings[ii][GYC] = 2; // x/y-lines count gridsettings[ii][GXF] = gridsettings[ii][GYF] = 0; // x/y-lines offset } menu_style = zstrdup("both"); // default menu style 20.0 iconsize = 32; // default icon size FBrgb[0] = FBrgb[1] = FBrgb[2] = 50; // F view background color GBrgb[0] = GBrgb[1] = GBrgb[2] = 200; // G view background color MFrgb[0] = MFrgb[1] = MFrgb[2] = 250; // menu font color MBrgb[0] = MBrgb[1] = MBrgb[2] = 80; // menu background color dialog_font = zstrdup("Sans 10"); // default dialog font splcurve_minx = 5; // default curve node separation % startdisplay = zstrdup("prevF"); // start with previous image Fdragopt = 1; // image drag with mouse Fshiftright = 0; // shift right for max. left margin Fmenublock = 1; // menus blocked 20.0 zoomcount = 2; // zooms to reach 2x image size zoomratio = sqrtf(2); // corresp. zoom ratio Nkbsu = 0; // KB shortcut list is empty map_dotsize = 8; // map dot size, mouse capture dist curr_file = 0; // no curr. file navi::galleryname = 0; // no curr. gallery navi::gallerytype = TNONE; curr_album = 0; // no current album copymove_loc = 0; // copy/move target folder RGB_chooser_file = 0; // users RGB color chooser file thumbfolder = 0; // no thumbnail folder navi::thumbsize = 256; // gallery default thumbnail size commandmenu = 0; // command line menu function commandparam = 0; // command menu parameter 20.0 commandalbum = 0; // command line album gallery video_command = zstrdup("vlc"); // default video play command 20.0 initial_file = 0; // start with image file or folder jpeg_def_quality = 90; // default .jpeg save quality tiff_comp_method = 1; // default TIFF compression method 20.0 png_comp_level = 1; // default PNG compression level 20.0 Frawloader = 1; // default RAW file loader, dcraw 20.0 Fautobright = 1; // RAW loader auto brighten, ON 20.0 Fmatchthumb = 0; // RAW loader match thumb color OFF 20.0 ownertran = E2X("owner"); // for permissions dialogs 20.0 grouptran = E2X("group"); othertran = E2X("other"); RWtran = E2X("read+write"); ROtran = E2X("read only"); NOtran = E2X("no access"); lens_mm = 35; // pano lens parameter netmap_source = zstrdup("mapnik"); // default net map source mapbox_access_key = zstrdup("undefined"); // mapbox map source access key colormapfile = zstrdup("undefined"); // printer calibration color map ss_KBkeys = zstrdup("BNPX"); // default slide show control keys Fcaptions = 0; // no show captions/comments 20.0 imagefiletypes = zstrdup(".jpg .jpeg .png .tif .tiff .bmp .ico .ppm .gif .svg .xpm .tga ",20); // 20.0 RAWfiletypes = zstrdup(".arw .srf .sr2 .crw .cr2 .cr3 .dng .mdc .mrw .nef .nrw .raw .rw2 .srw ",20); // 20.0 VIDEOfiletypes = zstrdup(".mp4 .flv .mov .avi .wmv .mpeg .mpg .h264 .webm ",20); 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; memcpy(LINE_COLOR,RED,3*sizeof(int)); // initial foreground drawing color for (int ii = 0; ii < 100; ii++) // static integer values 0-99 Nval[ii] = ii; // file and folder names in user folder /home//.fotoxx/* snprintf(index_folder,199,"%s/image_index",get_zhomedir()); // image index folder snprintf(index_file,199,"%s/index20",index_folder); // image index file index20 20.0 snprintf(tags_defined_file,199,"%s/tags_defined",get_zhomedir()); // defined tags file snprintf(recentfiles_file,199,"%s/recent_files",get_zhomedir()); // recent files file snprintf(saved_areas_folder,199,"%s/saved_areas",get_zhomedir()); // saved areas folder snprintf(albums_folder,199,"%s/albums",get_zhomedir()); // albums folder snprintf(gallerymem_file,199,"%s/gallery_memory",get_zhomedir()); // recent gallery memory snprintf(saved_curves_folder,199,"%s/saved_curves",get_zhomedir()); // saved curves folder snprintf(drawtext_folder,199,"%s/draw_text",get_zhomedir()); // add_text folder snprintf(favorites_folder,199,"%s/favorites",get_zhomedir()); // favorites folder snprintf(mashup_folder,199,"%s/mashup",get_zhomedir()); // mashup projects folder snprintf(slideshow_folder,199,"%s/slideshows",get_zhomedir()); // slide show folder snprintf(slideshow_trans_folder,199,"%s/slideshow_trans",get_zhomedir()); // slide show transitions snprintf(pattern_folder,199,"%s/patterns",get_zhomedir()); // pattern files folder snprintf(retouch_folder,199,"%s/retouch",get_zhomedir()); // retouch settings folder snprintf(custom_kernel_folder,199,"%s/custom_kernel",get_zhomedir()); // custom kernel files folder snprintf(printer_color_folder,199,"%s/printer_color",get_zhomedir()); // printer calibration folder snprintf(scripts_folder,199,"%s/custom_scripts",get_zhomedir()); // custom script files folder snprintf(search_images_folder,199,"%s/search_images",get_zhomedir()); // search_images settings folder 20.0 snprintf(searchresults_file,199,"%s/search_results",get_zhomedir()); // output of image search function snprintf(maps_folder,199,"/usr/share/fotoxx-maps/data"); // map files in fotoxx-maps package snprintf(user_maps_folder,199,"%s/user_maps",get_zhomedir()); // map files made by user snprintf(montage_maps_folder,199,"%s/montage_maps",get_zhomedir()); // montage map files made by user snprintf(palettes_folder,199,"%s/palettes",get_zhomedir()); // color palettes folder 19.0 snprintf(pixel_maps_folder,199,"%s/pixel_maps",get_zhomedir()); // pixel map files folder 20.0 err = stat(index_folder,&statb); // create missing folders if (err) mkdir(index_folder,0750); err = stat(saved_areas_folder,&statb); if (err) mkdir(saved_areas_folder,0750); err = stat(albums_folder,&statb); if (err) mkdir(albums_folder,0750); err = stat(saved_curves_folder,&statb); if (err) mkdir(saved_curves_folder,0750); err = stat(drawtext_folder,&statb); if (err) mkdir(drawtext_folder,0750); err = stat(mashup_folder,&statb); if (err) mkdir(mashup_folder,0750); err = stat(slideshow_folder,&statb); if (err) mkdir(slideshow_folder,0750); err = stat(slideshow_trans_folder,&statb); if (err) mkdir(slideshow_trans_folder,0750); err = stat(printer_color_folder,&statb); if (err) mkdir(printer_color_folder,0750); err = stat(scripts_folder,&statb); if (err) mkdir(scripts_folder,0750); err = stat(palettes_folder,&statb); if (err) mkdir(palettes_folder,0750); err = stat(search_images_folder,&statb); if (err) mkdir(search_images_folder,0750); err = stat(maps_folder,&statb); // alternative locs for map files 19.0 if (err) { snprintf(maps_folder,199,"%s/.local/share/fotoxx-maps/data",getenv("HOME")); err = stat(maps_folder,&statb); } if (err) snprintf(maps_folder,199,"missing"); printz("maps folder: %s \n",maps_folder); err = stat(user_maps_folder,&statb); if (err) mkdir(user_maps_folder,0750); err = stat(montage_maps_folder,&statb); if (err) mkdir(montage_maps_folder,0750); err = stat(palettes_folder,&statb); if (err) mkdir(palettes_folder,0750); err = stat(pixel_maps_folder,&statb); if (err) mkdir(pixel_maps_folder,0750); load_params(); // restore parameters from last session for (ii = 1; ii < argc; ii++) // command line parameters { char *pp = argv[ii]; if (strmatch(pp,"-home")) ii++; // -home homedir skip, see above else if (strmatchV(pp,"-home","-h",0)) // -h -help list parameters showz_docfile(Mwin,"userguide","command parameters"); // 20.0 else if (strmatchV(pp,"-debug","-d",0)) // -d -debug Fdebug = 1; else if (strmatchV(pp,"-lang","-l",0) && argc > ii+1) // -l -lang lc_RC language/region code strncpy0(locale,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 Frecent = 1; else if (strmatchV(pp,"-new","-n",0)) // -n -new newest files Fnew = 1; else if (strmatchV(pp,"-album","-a",0) && argc > ii+1) // -a -album "name" album commandalbum = zstrdup(argv[++ii]); else if (strmatchV(pp,"-prev","-p",0)) // -p -prev previous file Fprev = 1; else if (strmatchV(pp,"-blank","-b",0)) // -b -blank blank window Fblank = 1; else if (strmatchV(pp,"-menu","-m",0) && argc > ii+1) // -m -menu "name" menu function commandmenu = zstrdup(argv[++ii]); else if (strmatchV(pp,"-index","-x",0) && argc > ii+1) { // -x -index N set index level jj = atoi(argv[++ii]); // (0/1/2 = none/old/old+new files) if (jj >= 0 && jj <= 2) Pindexlev = jj; } else { // assume command param or initial file if (commandmenu) { commandparam = zstrdup(pp); continue; } initial_file = zstrdup(pp); if (*pp == '\'' || *pp == '"') // if quotes, remove them 20.0 strncpy0(initial_file,pp+1,strlen(pp)-1); if (*initial_file != '/') { // if no initial '/' cc = strlen(initial_file); // assume relative to CWD temp = zstrdup(getcwd(0,0),cc+4); strncatv(temp,200,"/",initial_file,0); // prepend CWD/ initial_file = temp; } break; } } E2Xinit(locale,0); // setup locale, translations setlocale(LC_NUMERIC,"en_US.UTF-8"); // stop comma decimal points zsetfont(dialog_font); // set default font for widgets build_widgets(); // build window widgets and menus if (Fclone) { // clone: open new window gtk_window_move(MWIN,cloxx,cloyy); // at passed position gtk_window_resize(MWIN,cloww,clohh); // with passed size } else { gtk_window_move(MWIN,mwgeom[0],mwgeom[1]); // main window geometry gtk_window_resize(MWIN,mwgeom[2],mwgeom[3]); // defaults or last session params } gtk_widget_show_all(Mwin); arrowcursor = gdk_cursor_new_for_display(display,GDK_TOP_LEFT_ARROW); // cursor for selection dragcursor = gdk_cursor_new_for_display(display,GDK_CROSSHAIR); // cursor for dragging drawcursor = gdk_cursor_new_for_display(display,GDK_PENCIL); // cursor for drawing lines blankcursor = gdk_cursor_new_for_display(display,GDK_BLANK_CURSOR); // invisible cursor dotcursor = gdk_cursor_new_for_display(display,GDK_DOT); // cursor for show_RGB() // ------------------------------------------------------------------------------ // check that necessary programs are installed err = shell_quiet("which heif-convert >/dev/null 2>&1"); // file.heic to .jpg converter 20.0 if (! err) Fheif = 1; err = shell_quiet("which opj_decompress >/dev/null 2>&1"); // file.jp2 to .tif converter 20.0 if (! err) Fjp2 = 1; err = shell_quiet("which dcraw >/dev/null 2>&1"); // check for Dcraw if (! err) Fdcraw = 1; err = shell_quiet("which exiftool >/dev/null 2>&1"); // check for exiftool if (! err) Fexiftool = 1; err = shell_quiet("which rawtherapee >/dev/null 2>&1"); // check for Raw Therapee if (! err) Frawtherapee = 1; err = shell_quiet("which rawtherapee-cli >/dev/null 2>&1"); // Raw Therapee command line 20.0 if (! err) Frawtherapeecli = 1; err = shell_quiet("which growisofs >/dev/null 2>&1"); // check for growisofs if (! err) Fgrowisofs = 1; err = shell_quiet("which addr2line >/dev/null 2>&1"); // check for addr2line if (! err) Faddr2line = 1; err = shell_quiet("which ffmpeg >/dev/null 2>&1"); // check for ffmpeg if (! err) Ffmpeg = 1; err = shell_quiet("which hugin >/dev/null 2>&1"); // need all of it if (! err) PTtools = 1; if (Fexiftool + Fdcraw < 2) { // check mandatory dependencies strcpy(buff,E2X("Please install missing programs:")); if (! Fdcraw) strcat(buff,"\n dcraw (RAW files and thumbnails)"); if (! Fexiftool) strcat(buff,"\n exiftool (view/edit image metadata)"); zmessageACK(Mwin,buff); quitxx(); // unconditional exit 20.05 } if (! Fheif) printz("HEIF files not supported \n"); // iPhone photos 20.0 else if (! strstr(imagefiletypes,".heic")) // add .heic file type 20.0 strcat(imagefiletypes,".heic "); if (! Fjp2) printz(".jp2 files not supported \n"); // jpeg2000 files 20.0 else if (! strstr(imagefiletypes,".jp2")) // add .jp2 file type 20.0 strcat(imagefiletypes,".jp2 "); if (! Frawtherapee) printz("RawTherapee not installed \n"); if (! Frawtherapeecli) printz("rawtherapee-cli not installed \n"); if (! Frawtherapeecli) Frawloader = 1; // force dcraw loader 20.0 if (! Fgrowisofs) printz("growisofs not installed \n"); if (! Ffmpeg) printz("ffmpeg not installed \n"); if (! PTtools) printz("Panorama Tools (Hugin) not installed \n"); if (! Faddr2line) printz("addr2line not installed \n"); // delete fotoxx temp files if owner process is no longer running fid = popen("pidof fotoxx","r"); // get active fotoxx PIDs 20.0 pidlist = fgets_trim(buff,200,fid); pclose(fid); pidlist = zstrdup(pidlist); snprintf(temp_folder,200,"%s/tempfiles-*",get_zhomedir()); // get existing /.../tempfiles-xxxxx snprintf(buff,200,"find %s -type d 2>/dev/null",temp_folder); fid = popen(buff,"r"); while ((pp = fgets_trim(buff,200,fid))) { // loop temp folders pid = strrchr(pp,'-'); // -xxxxx (pid) if (! pid) continue; if (strstr(pidlist,pid+1)) continue; // PID still active, do not delete shell_quiet("rm -R -f -v %s",pp); // delete folder and contents } pclose(fid); // set up temp folder /.../tempfiles-xxxx/ where xxxx is owner PID fotoxxPID = getpid(); // 19.0 snprintf(temp_folder,200,"%s/tempfiles-%d",get_zhomedir(),fotoxxPID); err = mkdir(temp_folder,0750); if (err) { zmessageACK(Mwin,"%s: %s",temp_folder,strerror(errno)); zexit("cannot create temp folder"); } printz("temp files: %s \n",temp_folder); // file name template for undo/redo files snprintf(URS_filename,100,"%s/undo_nn",temp_folder); // /.../tempfiles/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); Ftinycomputer = 0; // flag, inadequate memory 19.20 if (freememory < 4000) { Ftinycomputer = 1; printz("computer has inadequate memory \n"); } printz("image size limits for good performance: \n"); printz(" view: %.0f megapixels \n",(freememory-1000)/6); // F + preview, 3 bytes/pixel each printz(" edit: %.0f megapixels \n",(freememory-1000)/64); // + E0/E1/E3/ER, 16 bytes/pixel each // get locale specific name for /home//Desktop strcpy(desktopname,"Desktop"); snprintf(filename,200,"%s/.config/user-dirs.dirs",getenv("HOME")); fid = fopen(filename,"r"); if (fid) { while (true) { pp = fgets_trim(buff,200,fid); if (! pp) break; if (! strmatchN(pp,"XDG_DESKTOP_DIR=",16)) continue; pp = strchr(pp+16,'/'); if (! pp) continue; strncpy0(desktopname,pp+1,100); cc = strlen(desktopname); if (desktopname[cc-1] == '"') desktopname[cc-1] = 0; printz("locale desktop name: %s \n",desktopname); break; } fclose(fid); } // miscellaneous m_viewmode(0,"F"); // set F mode initially printz("screen width: %d height: %d \n", // monitor pixel size zfuncs::monitor_ww,zfuncs::monitor_hh); NWT = get_nprocs(); // get SMP CPU count if (NWT <= 0) NWT = 2; if (NWT > max_threads) NWT = max_threads; // compile time limit printz("using %d threads \n",NWT); zdialog_inputs("load"); // load saved dialog inputs zdialog_geometry("load"); // load saved dialogs position/size gallery_memory("load"); // load recent gallery positions KBshortcuts_load(); // load KB shortcuts from file Fmenublock = 0; // set menus free from here 20.0 // check for first time Fotoxx install // perform image index function at each startup if (Ffirsttime) { first_startup(); // initial user indexing decision 20.0 Ffirsttime = 0; // reset first time flag Prelease = zstrdup(Frelease); // set installed release } else { // not first startup if (Pindexlev >= 0) index_rebuild(Pindexlev,0); // use -index command parameter else if (initial_file) index_rebuild(FMindexlev,0); // use file manager index level else index_rebuild(Findexlev,0); // user standard index level } if (! thumbfolder) { // check index was done 20.0 zmessageACK(Mwin,E2X("Index aborted")); zexit("index aborted"); } if (! strmatch(Prelease,Frelease)) { // release change? Prelease = zstrdup(Frelease); // update installed release showz_textfile("doc","changelog",Mwin); // show change log release_housekeeping(); // do release housekeeping 20.03 } // set Linux current working directory if (! navi::galleryname) { if (topfolders[0]) gallery(topfolders[0],"init",0); // default 1st top image folder else { pp = getcwd(0,0); // else use curr. directory 20.0 if (pp) gallery(pp,"init",0); if (pp) free(pp); } } // set current file and gallery from command line if present if (initial_file) { // file parameter (or folder) printz("initial file: %s \n",initial_file); ftype = image_file_type(initial_file); if (ftype == FDIR) { // folder gallery(initial_file,"init",0); // initz. gallery gallery(0,"sort",-2); // recall sort and position m_viewmode(0,"G"); } else if (ftype == IMAGE || ftype == RAW || ftype == VIDEO) { // image file f_open(initial_file); gallery(initial_file,"init",0); // initz. gallery from initial file 20.0 gallery(0,"sort",-2); } else { printz(" -invalid file \n"); if (curr_file) zfree(curr_file); // 20.0 curr_file = 0; if (navi::gallerytype == GDIR) gallery(navi::galleryname,"init",0); else gallery(navi::galleryname,"initF",0); gallery(0,"sort",-2); m_viewmode(0,"G"); } zfree(initial_file); initial_file = 0; } else if (commandalbum) { // -album parameter printz("initial album: %s \n",commandalbum); snprintf(filename,200,"%s/albums/%s",get_zhomedir(),commandalbum); err = stat(filename,&statb); if (err) { printz("invalid album file: %s \n",commandalbum); commandalbum = 0; } else album_show(filename); } else if (Fprev) { // start with previous file if (curr_file && *curr_file == '/') f_open(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 if (curr_file) zfree(curr_file); curr_file = 0; if (navi::galleryname) zfree(navi::galleryname); navi::galleryname = 0; navi::gallerytype = TNONE; set_mwin_title(); } // if no command line option, get startup display from user preferences else if (strmatch(startdisplay,"album")) { printz("initial album: %s \n",startalbum); err = stat(startalbum,&statb); if (err) { printz("invalid album file: %s \n",startalbum); commandalbum = 0; } else album_show(startalbum); // bugfix 19.0 if (curr_file) zfree(curr_file); curr_file = 0; } else if (strmatch(startdisplay,"recent")) { // start with recent files gallery m_recentfiles(0,0); if (curr_file) zfree(curr_file); curr_file = 0; } else if (strmatch(startdisplay,"newest")) { // start with newest files gallery m_newfiles(0,"file"); // by file mode date if (curr_file) zfree(curr_file); curr_file = 0; } else if (strmatch(startdisplay,"prevG")) { // start with previous gallery if (navi::gallerytype != TNONE) { if (navi::gallerytype == GDIR) gallery(navi::galleryname,"init",0); else gallery(navi::galleryname,"initF",0); gallery(0,"sort",-2); // recall sort and position m_viewmode(0,"G"); } if (curr_file) zfree(curr_file); curr_file = 0; } else if (strmatch(startdisplay,"prevF")) { // start with previous image file err = 1; if (curr_file && *curr_file == '/') err = f_open(curr_file); if (err) { // not available, use prev. gallery 20.0 if (navi::gallerytype != TNONE) { if (navi::gallerytype == GDIR) gallery(navi::galleryname,"init",0); else gallery(navi::galleryname,"initF",0); gallery(0,"sort",-2); m_viewmode(0,"G"); } } } else if (strmatch(startdisplay,"specG")) { // start with specified gallery (folder) if (startfolder && *startfolder == '/') { gallery(startfolder,"init",0); gallery(0,"sort",-2); // recall sort and position m_viewmode(0,"G"); } if (curr_file) zfree(curr_file); curr_file = 0; } else if (strmatch(startdisplay,"specF")) // start with given image file f_open(startfile); save_params(); // save parameters now g_timeout_add(10,gtimefunc,0); // start periodic function (10 ms) 19.0 startsecs = get_seconds() - startsecs; // 19.0 printz("startup time: %.1f secs.\n",startsecs); if (commandmenu) { // startup menu on command line printz("start menu: %s \n",commandmenu); for (ii = 0; ii < Nmenus; ii++) { // convert menu name to menu function if (! menutab[ii].menu) continue; // separator, null menu if (strmatchcase(commandmenu,E2X(menutab[ii].menu))) break; } if (ii < Nmenus) menutab[ii].func(0,menutab[ii].arg); // call the menu function } gtk_main(); // start processing window events printz("return from gtk_main() \n"); return 0; } /********************************************************************************/ // Fotoxx first startup - initial user decision about indexing void first_startup() // 20.0 { zdialog *zd; int yn, zstat, nn; cchar *defer1 = E2X(" Defer image file indexing:"); cchar *defer2 = E2X(" • Fotoxx will start immediately \n" " • View and edit image files will work normally \n" " • Image search, batch and map functions will not work \n" " • Thumbnail galleries will be slow"); cchar *index1 = E2X(" Index image files now:"); cchar *index2 = E2X(" • Initial indexing may need considerable time \n" " • Subsequent startups will be fast \n" " • Full functionality will be available \n" " • Thumbnail galleries will be fast"); cchar *info = E2X(" Indexing time depends on the number of image files and the \n" " speed of your computer. This can be a few hundred to a few \n" " thousand per minute. After indexing is done, startup time \n" " should be quite fast. You can change index options later, \n" " using these menus: Tools > Index and Tools > Preferences. "); if (Ftinycomputer) { yn = zmessageYN(Mwin,E2X("Main memory is too small to run Fotoxx. \n" "You can try anyway if you wish. \n" " Continue?")); if (! yn) quitxx(); } F1_help_topic = "first startup"; /*** ______________________________________________________________ | Fotoxx First Startup | | | | (o) Defer image file indexing: | | • Fotoxx will start immediately | | • View and edit image files will work normally | | • Image search, batch and map functions will not work | | • Thumbnail galleries will be slow | | | | (o) Index image files now: | | • Initial indexing may need considerable time | | • Subsequent startups will be fast | | • Full functionality will be available | | • Thumbnail galleries will be fast | | | | Indexing time depends on the number of image files and the | | speed of your computer. This can be a few hundred to a few | | thousand per minute. After indexing is done, startup time | | should be quite fast. You can change index options later, | | using these menus: Tools > Index and Tools > Preferences. | | | | [Help] [Proceed] [Quit] | |______________________________________________________________| ***/ zd = zdialog_new(E2X("Fotoxx First Startup"),Mwin,Bhelp,Bproceed,Bquit,0); zdialog_add_widget(zd,"radio","defer1","dialog",defer1); zdialog_add_widget(zd,"text","defer2","dialog",defer2); zdialog_add_widget(zd,"hbox","space","dialog",0,"space=5"); zdialog_add_widget(zd,"radio","index1","dialog",index1); zdialog_add_widget(zd,"text","index2","dialog",index2); zdialog_add_widget(zd,"hbox","space","dialog",0,"space=5"); zdialog_add_widget(zd,"text","info","dialog",info,"space=10"); zdialog_stuff(zd,"defer1",0); zdialog_stuff(zd,"index1",1); zdialog_run(zd,0,"parent"); zstat = zdialog_wait(zd); while (zstat == 1) { // [Help] zd->zstat = 0; m_help(0,"Help"); zstat = zdialog_wait(zd); } if (zstat == 2) { // [Proceed] zdialog_fetch(zd,"index1",nn); zdialog_free(zd); if (nn) { // do indexing Findexlev = 2; // fotoxx command: full index process FMindexlev = 1; // file manager: use curr. index m_index(0,0); } else { Findexlev = FMindexlev = 0; // use no index index_noindex(); } } else { // [Quit] zdialog_free(zd); quitxx(); } } /********************************************************************************/ // Fotoxx new release first startup - merge default config files with // possible user additions, save in user's Fotoxx home folder. // /usr/share/fotoxx/data/metadata_short_list: merge user additions // /usr/share/fotoxx/data/userguide: copy unconditionally void release_housekeeping() // 20.03 { FILE *fid; char buff[100]; int maxMSL = 200; // metadata_short_list max. items char mslfileB[200], mslfileU[200]; char *pp, *mslistB[200], *mslistU[200]; int ii, jj, NB = 0, NU = 0, NN = 0; char maxexceeded[100]; snprintf(maxexceeded,100,"exceed %d metadata items",maxMSL); snprintf(mslfileB,200,"%s/metadata_short_list",get_zdatadir()); // base file (release file) snprintf(mslfileU,200,"%s/metadata_short_list",get_zhomedir()); // user file with poss. additions fid = fopen(mslfileB,"r"); if (! fid) zexit("cannot find file %s",mslfileB); while (true) { // read base file recs pp = fgets_trim(buff,100,fid,1); if (! pp) break; if (NB == maxMSL) zexit(maxexceeded); mslistB[NB] = zstrdup(pp); // save list of base metadata keys NB++; } fclose(fid); fid = fopen(mslfileU,"r"); if (fid) { while (true) { // read user file recs pp = fgets_trim(buff,100,fid,1); if (! pp) break; if (NU == maxMSL) zexit(maxexceeded); mslistU[NU] = zstrdup(pp); // save list of user metadata keys NU++; } fclose(fid); } for (ii = 0; ii < NU; ii++) // compare lists for (jj = 0; jj < NB; jj++) if (strmatch(mslistU[ii],mslistB[jj])) *mslistU[ii] = 0; // mark user items found in base list fid = fopen(mslfileU,"w"); if (! fid) zexit("cannot write file %s",mslfileU); // write new user file for (ii = 0; ii < NB; ii++) fprintf(fid,"%s\n",mslistB[ii]); // write base metadata keys for (ii = 0; ii < NU; ii++) { if (*mslistU[ii]) { // add non-matching user keys fprintf(fid,"%s\n",mslistU[ii]); NN++; // count additions if (NB + NN == maxMSL) zexit(maxexceeded); } } fclose(fid); for (ii = 0; ii < NB; ii++) // free memory zfree(mslistB[ii]); for (ii = 0; ii < NU; ii++) zfree(mslistU[ii]); shell_quiet("cp -f %s/userguide %s/userguide",get_zdatadir(),get_zhomedir()); // refresh user guide return; } /********************************************************************************/ // functions for main window event signals int delete_event() // main window [x] button { printz("main window delete event \n"); m_quit(0,0); // returns if user bails out 20.0 return 1; } int destroy_event() // main window destroyed { printz("main window destroy event \n"); // no user bailout possible quitxx(); return 0; } int state_event(GtkWidget *, GdkEvent *event) // main window state changed { int state = ((GdkEventWindowState *) event)->new_window_state; // track window fullscreen status if (state & GDK_WINDOW_STATE_FULLSCREEN) Ffullscreen = 1; else if (state & GDK_WINDOW_STATE_MAXIMIZED) Ffullscreen = 1; else Ffullscreen = 0; return 0; } void drop_event(int mousex, int mousey, char *file) // file drag-drop event { if (! file) return; printz("drag-drop file: %s \n",file); f_open(file,0,0,1); return; } /********************************************************************************/ // block image updates from threads during painting by main thread // block main thread painting during image updates from threads // lock = 1: block updates lock = 0: unblock // Fpaintlock: 1 = locked status, 0 = not locked void paintlock(int lock) // 19.0 { if (lock) { for (int ii = 0; ii < 5000; ii++) { // try up to 5 seconds if (resource_lock(Fpaintlock)) return; // OK, locked for caller zsleep(0.001); if (ii == 1000) printz("waiting for Fpaintlock \n"); } zexit("Fpaintlock, give up"); // failed, no way out } else { resource_unlock(Fpaintlock); return; } } /********************************************************************************/ // Periodic function (10 milliseconds) int gtimefunc(void *) { static int domore = 0; if (Fshutdown) return 0; // shutdown underway if (Fpaintrequest && ! Fpaintlock && Cdrawin) // paint request pending 19.0 gtk_widget_queue_draw(Cdrawin); if (zd_thread && zd_thread_event) { // send dialog event from thread if (! CEF || CEF->thread_status < 2) { // only if thread done or not busy zdialog_send_event(zd_thread,zd_thread_event); zd_thread_event = 0; } } Fpaint3_main(); // update window area from thread if (--domore > 0) return 1; // do rest every 200 milliseconds domore = 10; update_Fpanel(); // update top panel information return 1; } /********************************************************************************/ // update F window top panel with current status information // called from timer function void update_Fpanel() { static double time1 = 0, time2, cpu1 = 0, cpu2, cpuload; char *pp, text1[300], text2[200]; char digitblank[4] = " "; // digit blank: 0x2007 0x00 static char ptext1[300] = ""; int ww, hh, scale, bpc; static int ftf = 1; double file_MB = 1.0 / MEGA * curr_file_size; static cchar *reduced, *areaactive, *dialogopen; static cchar *blocked, *modified; if (! Fpanelshow) return; // panel currently hidden if (ftf) { ftf = 0; reduced = E2X("(reduced)"); areaactive = E2X("area active"); dialogopen = E2X("dialog open"); blocked = E2X("blocked"); modified = "mod"; } if (FGWM == 'G') goto update_busy; if (FGWM != 'F') return; *text1 = *text2 = 0; if (! time1) { time1 = get_seconds(); cpu1 = jobtime(); } time2 = get_seconds(); // compute process cpu load % if (time2 - time1 > 1.0) { // at 1 second intervals cpu2 = jobtime(); cpuload = 100.0 * (cpu2 - cpu1) / (time2 - time1); time1 = time2; cpu1 = cpu2; } snprintf(text1,300,"CPU: %.0f%c",cpuload,'%'); // CPU: 123% if (cpuload <= 99) strcat(text1,digitblank); // keep width constant 3 digits 20.0 if (cpuload <= 9) strcat(text1,digitblank); if (Fslideshow) { gtk_label_set_label(GTK_LABEL(Fpanlab),text1); // done if slide show 19.0 gtk_widget_show_all(Fpanel); return; } 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," %.2fMB",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",E2X("edits"),URS_pos); strncatv(text1,300,text2,0); } if (Fmetamod) strncatv(text1,300," ","metadata",0); } else if (Fpxb) { snprintf(text2,100," %dx%d",Fpxb->ww,Fpxb->hh); strncatv(text1,300,text2,0); } if (sa_stat == 3) strncatv(text1,300," ",areaactive,0); if (zfuncs::zdialog_busy) strncatv(text1,300," ",dialogopen,0); if (Fblock) strncatv(text1,300," ",blocked,0); // "blocked" if (CEF && CEF->Fmods) strncatv(text1,300," ",modified,0); // "mod" if (*paneltext) strncatv(text1,300," ",paneltext,0); // application text if (curr_file) { pp = strrchr(curr_file,'/'); // "filename.jpg" if (pp && Ffullscreen && ! Ffuncbusy && ! Fthreadbusy) { strncpy0(text2,pp+1,100); strncatv(text1,300," ",text2,0); } } if (! strmatch(text1,ptext1)) { // if text changed, update panel bar gtk_label_set_label(GTK_LABEL(Fpanlab),text1); gtk_widget_show_all(Fpanel); strcpy(ptext1,text1); } // Show BUSY label if Ffuncbusy or Fthreadbusy active. // Show progress counter if Fbusy_goal > 0 // added to top panel: xx% or BUSY if no xx% update_busy: static GtkWidget *busylabel = 0, *donelabel = 0; static cchar *busytext = " BUSY "; static char donetext[] = " xx% "; static char *doneposn = 0; GtkWidget *FGpanel; int pct; char nn[4]; if (! doneposn) doneposn = strstr(donetext,"xx%"); // get position to insert % done 19.13 if (FGWM == 'F') FGpanel = Fpanel; else if (FGWM == 'G') FGpanel = Gpanel; else return; if (Fbusy_done > 0 && Fbusy_done < Fbusy_goal) { // add " xx%" completion to top panel pct = 100 * Fbusy_done / Fbusy_goal; if (pct > 99) pct = 99; snprintf(nn,4,"%02d",pct); memcpy(doneposn,nn,2); // 19.13 if (! donelabel) { donelabel = gtk_label_new(""); gtk_box_pack_start(GTK_BOX(FGpanel),donelabel,0,0,0); } gtk_label_set_markup(GTK_LABEL(donelabel),donetext); if (busylabel) gtk_widget_destroy(busylabel); // 20.0 busylabel = 0; gtk_widget_show_all(FGpanel); return; } else { if (donelabel) gtk_widget_destroy(donelabel); donelabel = 0; } if (Ffuncbusy || Fthreadbusy) { // add " BUSY" to top panel if (! busylabel) { busylabel = gtk_label_new(null); gtk_label_set_markup(GTK_LABEL(busylabel),busytext); gtk_box_pack_start(GTK_BOX(FGpanel),busylabel,0,0,5); } } else { if (busylabel) gtk_widget_destroy(busylabel); busylabel = 0; } gtk_widget_show_all(FGpanel); return; } /********************************************************************************/ // GTK3 "draw" function for F and W mode drawing windows. // Paint window when created, exposed, resized, or image modified (edited). // Update window image if scale change or window size change. // Otherwise do only the draw function (triggered from GTK). // Draw the image section currently within the visible window. // May NOT be called from threads. See Fpaint2() for threads. int Fpaint(GtkWidget *Cdrawin, cairo_t *cr) { PIXBUF *pixbuf; PXB *pxb1; GdkRGBA rgba; static int pdww = 0, pdhh = 0; // prior window size float wscale, hscale, mscale; int fww, fhh; // current image size at 1x int mww, mhh; // scaled image size int morgx, morgy; int dorgx, dorgy; int centerx, centery; int Fsmallimage; int Frefresh = 0; int mousex, mousey; // mouse position after zoom float magx, magy; // mouse drag, magnification ratios uint8 *pixels, *pix, bgpix[3]; int rs, px, py; if (Fshutdown) return 1; // shutdown underway if (! Cdrawin || ! gdkwin || ! Cstate || ! Cstate->fpxb) { // no image Fpaintrequest = 0; return 1; } if (Fview360) return 1; Dww = gdk_window_get_width(gdkwin); // (new) drawing window size Dhh = gdk_window_get_height(gdkwin); if (Dww < 20 || Dhh < 20) return 1; // too small if (Dww != pdww || Dhh != pdhh) { // window size changed Frefresh = 1; // image refresh needed pdww = Dww; pdhh = Dhh; } if (Fpaintrequest && ! Fpaintlock) { // image changed, need to paint 19.0 Frefresh = 1; // image refresh needed paintlock(1); if (FGWM == 'F' && (E0pxm || E3pxm)) { // insure F-view and E0 or E3 if (E3pxm) pxb1 = PXM_PXB_copy(E3pxm); // update fpxb from E0/E3 image else pxb1 = PXM_PXB_copy(E0pxm); // or use already edited image PXB_free(Cstate->fpxb); Cstate->fpxb = pxb1; } paintlock(0); Fpaintrequest = 0; // must be after E3 capture bugfix 19.0 } 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; wscale = 1.0 * Dww / fww; // calc. image scale for fit window hscale = 1.0 * Dhh / fhh; if (wscale < hscale) mscale = wscale; // use greatest ww/hh ratio else mscale = hscale; if (mscale > 1) Fsmallimage = 1; // flag, image < window size 20.0 else Fsmallimage = 0; if (Cstate->fzoom == -1) { // fit to window A 20.0 Cstate->mscale = mscale; // small image: expand to fit window Cstate->fzoom = 0; zoomx = zoomy = 0; // no zoom target } else if (Cstate->fzoom == 0) { // fit to window B 20.0 Cstate->mscale = mscale; if (Fsmallimage) Cstate->mscale = 1; // small image: use 1x size 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 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 Frefresh = 1; // image refresh needed } if (! Cstate->mpxb) Frefresh++; // need to make mpxb if (Frefresh) { // 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 (drag) Cstate->morgy -= round(Mwdragy); } if (Fdragopt == 2) { Cstate->morgx += round(Mwdragx); // opposite direction (scroll) Cstate->morgy += round(Mwdragy); } if (Fdragopt == 3) { Cstate->morgx -= round(Mwdragx * magx); // same direction, fast Cstate->morgy -= round(Mwdragy * magy); } if (Fdragopt == 4) { Cstate->morgx += round(Mwdragx * magx); // opposite direction, fast Cstate->morgy += round(Mwdragy * magy); } } if (dww < Dww) Cstate->dorgx = 0.5 * (Dww - dww); // if scaled image < window, else Cstate->dorgx = 0; // center image in window if (dhh < Dhh) Cstate->dorgy = 0.5 * (Dhh - dhh); else Cstate->dorgy = 0; if (Fshiftright && dww < Dww-1) Cstate->dorgx = Dww-1 - dww; // shift image to right margin if (zoomx || zoomy) { // requested zoom center Cstate->morgx = Cstate->mscale * zoomx - 0.5 * dww; // corresp. window position within image Cstate->morgy = Cstate->mscale * zoomy - 0.5 * dhh; } if (Cstate->morgx < 0) Cstate->morgx = 0; // maximize image within window if (Cstate->morgy < 0) Cstate->morgy = 0; // (no unused margins) if (Cstate->morgx + dww > mww) Cstate->morgx = mww - dww; if (Cstate->morgy + dhh > mhh) Cstate->morgy = mhh - dhh; if (zoomx || zoomy) { // zoom target mousex = zoomx * Cstate->mscale - Cstate->morgx + Cstate->dorgx; mousey = zoomy * Cstate->mscale - Cstate->morgy + Cstate->dorgy; // mouse pointer follows target move_pointer(Cdrawin,mousex,mousey); zoomx = zoomy = 0; // reset zoom target } if (zd_darkbrite) darkbrite_paint(); // update dark/bright pixels rgba.red = 0.00392 * FBrgb[0]; // window background color rgba.green = 0.00392 * FBrgb[1]; // 0 - 255 --> 0.0 - 1.0 rgba.blue = 0.00392 * FBrgb[2]; rgba.alpha = 1.0; gdk_cairo_set_source_rgba(cr,&rgba); // background color to entire window cairo_paint(cr); // < 0.0001 seconds morgx = Cstate->morgx; // window position in (larger) image morgy = Cstate->morgy; dorgx = Cstate->dorgx; dorgy = Cstate->dorgy; if (Frefresh) { // renew background within image area if (BGpixbuf) g_object_unref(BGpixbuf); BGpixbuf = gdk_pixbuf_new(GDKRGB,0,8,dww,dhh); pixels = gdk_pixbuf_get_pixels(BGpixbuf); rs = gdk_pixbuf_get_rowstride(BGpixbuf); bgpix[0] = FBrgb[0]; // background color bgpix[1] = FBrgb[1]; bgpix[2] = FBrgb[2]; for (py = 0; py < dhh; py++) for (px = 0; px < dww; px++) { pix = pixels + py * rs + px * 3; if (py % 10 < 2 && px % 10 < 2) // add periodic black dots memset(pix,0,3); else memcpy(pix,bgpix,3); } } gdk_cairo_set_source_pixbuf(cr,BGpixbuf,dorgx,dorgy); // paint background image cairo_paint(cr); // < 0.01 seconds, 4K x 2K window pixbuf = Cstate->mpxb->pixbuf; // get image section within window pixbuf = gdk_pixbuf_new_subpixbuf(pixbuf,morgx,morgy,dww,dhh); gdk_cairo_set_source_pixbuf(cr,pixbuf,dorgx,dorgy); // paint image section cairo_paint(cr); // < 0.01 seconds, 4K x 2K window g_object_unref(pixbuf); if (Cstate == &Fstate) { // view mode is image if (Ntoplines) draw_toplines(1,cr); // draw line overlays if (gridsettings[currgrid][GON]) draw_gridlines(cr); // draw grid lines if (Ntoptext) draw_toptext(cr); // draw text strings if (Ntopcircles) draw_topcircles(cr); // draw circles if (Fshowarea) sa_show(1,cr); // draw select area outline if (Frefresh && zd_RGB_dist) m_RGB_dist(0,0); // update brightness distribution } if (Cstate == &Wstate) // view mode is world maps filemap_paint_dots(); return 1; } /********************************************************************************/ // Repaint modified image immediately. // May NOT be called from threads. void Fpaintnow() { if (! Cdrawin || ! gdkwin || ! Cstate || ! Cstate->fpxb) { // no image printz("Fpaintnow(), no image \n"); return; } Fpaintrequest = 1; // request repaint of changed image gtk_widget_queue_draw(Cdrawin); while (Fpaintrequest) zmainloop(); // bugfix 19.0 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() { if (Fpaintrequest) return; // do not overlap bugfix 19.0 Fpaintrequest = 1; // request repaint of changed image return; } // Update a section of Fpxb and Mpxb from an updated section of E3pxm, // then update the corresponding section of the drawing window. // This avoids a full image refresh, E3pxm > fpxb > mpxb > drawing window. // px3, py3, ww3, hh3: modified section within E3pxm to be propagated. // May NOT be called from threads. void Fpaint3(int px3, int py3, int ww3, int hh3, cairo_t *cr) { int crflag = 0; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } PXM_PXB_update(E3pxm,Fpxb,px3,py3,ww3,hh3); // E3pxm > Fpxb, both 1x scale PXB_PXB_update(Fpxb,Mpxb,px3,py3,ww3,hh3); // Fpxb > Mpxb, scaled up or down Fpaint4(px3,py3,ww3,hh3,cr); // update drawing window from Mpxb if (crflag) draw_context_destroy(draw_context); return; } // Fpaint3 callable ONLY from threads. // Prepare data about region to update. // Main thread (below) does the window update. namespace Fpaint3_thread_names // window area to update from thread { int Fpaint3_lock = 0; int pending = 0; int px3a, py3a, ww3a, hh3a; } void Fpaint3_thread(int px3, int py3, int ww3, int hh3) { using namespace Fpaint3_thread_names; while (pending) zsleep(0.001); resource_lock(Fpaint3_lock); if (px3 < px3a) { ww3a += (px3a - px3); px3a = px3; } if (py3 < py3a) { hh3a += (py3a - py3); py3a = py3; } if (px3 + ww3 > px3a + ww3a) ww3a += px3 + ww3 - (px3a + ww3a); if (py3 + hh3 > py3a + hh3a) hh3a += py3 + hh3 - (py3a + hh3a); pending = 1; resource_unlock(Fpaint3_lock); return; } // called by gtimefunc() each timer cycle void Fpaint3_main() { using namespace Fpaint3_thread_names; if (! pending) return; resource_lock(Fpaint3_lock); cairo_t *cr = draw_context_create(gdkwin,draw_context); Fpaint3(px3a,py3a,ww3a,hh3a,cr); draw_context_destroy(draw_context); pending = 0; resource_unlock(Fpaint3_lock); return; } // same as Fpaint3 but uses E0pxm instead of E3pxm void Fpaint0(int px3, int py3, int ww3, int hh3, cairo_t *cr) { int crflag = 0; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } PXM_PXB_update(E0pxm,Fpxb,px3,py3,ww3,hh3); PXB_PXB_update(Fpxb,Mpxb,px3,py3,ww3,hh3); Fpaint4(px3,py3,ww3,hh3,cr); if (crflag) draw_context_destroy(draw_context); return; } // Repaint a section of the Mpxb image in the visible window. // px3, py3, ww3, hh3: area to be repainted (in 1x image space). // May NOT be called from threads. // Writes directly on the window (cairo pixbuf paint). void Fpaint4(int px3, int py3, int ww3, int hh3, cairo_t *cr) { PIXBUF *pixbuf, *bgpixbuf; int px1, py1, ww1, hh1; int px2, py2, ww2, hh2; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image px2 = Mscale * px3 - 2; // 1x image space to Mpxb space py2 = Mscale * py3 - 2; // (expanded a few pixels) ww2 = Mscale * ww3 + 2 / Mscale + 4; hh2 = Mscale * hh3 + 2 / Mscale + 4; if (px2 < Morgx) { // reduce to currently visible window ww2 = ww2 - (Morgx - px2); px2 = Morgx; } if (py2 < Morgy) { hh2 = hh2 - (Morgy - py2); py2 = Morgy; } if (px2 + ww2 >= Mpxb->ww) ww2 = Mpxb->ww - px2 - 1; // stay within image if (py2 + hh2 >= Mpxb->hh) hh2 = Mpxb->hh - py2 - 1; if (ww2 <= 0 || hh2 <= 0) return; px1 = px2 - Morgx + Dorgx; // corresp. position in drawing window py1 = py2 - Morgy + Dorgy; if (px1 + ww2 >= Dww) ww2 = Dww - px1 - 1; // stay within window if (py1 + hh2 >= Dhh) hh2 = Dhh - py1 - 1; if (ww2 <= 0 || hh2 <= 0) return; pixbuf = gdk_pixbuf_new_subpixbuf(Mpxb->pixbuf,px2,py2,ww2,hh2); // Mpxb area to paint if (! pixbuf) { printz("Fpaint4() pixbuf failure \n"); return; } px2 = px1; // corresp. position in drawing window py2 = py1; px1 = px2 - Dorgx; // corresp. position in background image py1 = py2 - Dorgy; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } if (Mpxb->nc > 3) { // alpha channel present ww1 = ww2; // draw background image to area hh1 = hh2; if (px1 + ww1 > dww) ww1 = dww - px1; if (py1 + hh1 > dhh) hh1 = dhh - py1; if (ww1 > 0 && hh1 > 0) { bgpixbuf = gdk_pixbuf_new_subpixbuf(BGpixbuf,px1,py1,ww1,hh1); if (bgpixbuf) { gdk_cairo_set_source_pixbuf(cr,bgpixbuf,px2,py2); cairo_paint(cr); g_object_unref(bgpixbuf); } else printz("Fpaint4() bgpixbuf failure \n"); } } gdk_cairo_set_source_pixbuf(cr,pixbuf,px2,py2); // draw area to window cairo_paint(cr); g_object_unref(pixbuf); if (Fshowarea) { px3 = (px2 - Dorgx + Morgx) / Mscale; // back to image scale, expanded py3 = (py2 - Dorgy + Morgy) / Mscale; ww3 = ww2 / Mscale + 2; hh3 = hh2 / Mscale + 2; sa_show_rect(px3,py3,ww3,hh3,cr); // refresh select area outline } if (crflag) draw_context_destroy(draw_context); return; } /********************************************************************************/ // F/W view - window mouse event function - capture buttons and drag movements void mouse_event(GtkWidget *widget, GdkEventButton *event, void *) { void mouse_convert(int xpos1, int ypos1, int &xpos2, int &ypos2); int button, time, type, scroll; static int bdtime = 0, butime = 0; static int dragstart = 0, mdragx0, mdragy0; char *pp; #define GAPR GDK_AXIS_PRESSURE if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image type = event->type; button = event->button; // button, 1/2/3 = left/center/right time = event->time; Mwxposn = event->x; // mouse position in window Mwyposn = event->y; scroll = ((GdkEventScroll *) event)->direction; // scroll wheel event mouse_convert(Mwxposn,Mwyposn,Mxposn,Myposn); // convert to image space KBcontrolkey = KBshiftkey = KBaltkey = 0; if (event->state & GDK_CONTROL_MASK) KBcontrolkey = 1; if (event->state & GDK_SHIFT_MASK) KBshiftkey = 1; if (event->state & GDK_MOD1_MASK) KBaltkey = 1; if (type == GDK_SCROLL) { // scroll wheel = zoom zoomx = Mxposn; // zoom center = mouse position zoomy = Myposn; if (scroll == GDK_SCROLL_UP) m_zoom(0,"in"); if (scroll == GDK_SCROLL_DOWN) m_zoom(0,"out"); return; } if (type == GDK_BUTTON_PRESS) { // button down dragstart = 1; // possible drag start bdtime = time; // time of button down Mbutton = button; mdragx0 = Mwxposn; // window position at button down mdragy0 = Mwyposn; Mxdown = Mxposn; // image position at button down Mydown = Myposn; Mxdrag = Mydrag = 0; } if (type == GDK_MOTION_NOTIFY) { if (dragstart) { // drag underway Mdrag = 1; Mwdragx = Mwxposn - mdragx0; // drag increment, window space Mwdragy = Mwyposn - mdragy0; mdragx0 = Mwxposn; // new drag origin = current position mdragy0 = Mwyposn; Mxdrag = Mxposn; // drag position, image space Mydrag = Myposn; mouse_dragtime = time - bdtime; // track drag duration gdk_event_get_axis((GdkEvent *) event, GAPR, &wacom_pressure); // wacom tablet stylus pressure } else Mwdragx = Mwdragy = Mxdrag = Mydrag = 0; } if (type == GDK_BUTTON_RELEASE) { // button up Mxclick = Myclick = Mdrag = dragstart = 0; // reset click and drag status butime = time; // time of button up if (butime - bdtime < 500) { // < 0.5 secs down, call it a click 19.0 if (Mbutton == 1) LMclick++; // left mouse click if (Mbutton == 3) RMclick++; // right mouse click Mxclick = Mxposn = Mxdown; // click = button down position Myclick = Myposn = Mydown; if (button == 2) { // center button click zoomx = Mxclick; // re-center at mouse (Doriano) zoomy = Myclick; gtk_widget_queue_draw(Cdrawin); } } Mxdown = Mydown = Mxdrag = Mydrag = Mdrag = Mbutton = 0; // forget buttons and drag } Fmousemain = 1; // mouse acts on main window if (Mcapture) Fmousemain = 0; // curr. function handles mouse if (mouseCBfunc) Fmousemain = 0; // mouse owned by callback function if (KBcontrolkey) Fmousemain = 1; // mouse acts on main window if (mouseCBfunc && ! Fmousemain) { // pass to callback function (* mouseCBfunc)(); // remove busy test Fmousemain = 1; // click/drag params are processed here } // unless reset by callback func. if (FGWM == 'W') filemap_mousefunc(); // geomap mouse function if (! Fmousemain) return; // curr. function handles mouse if (curr_file && LMclick && FGWM == 'F') { // F-view, left click on image pp = strrchr(curr_file,'/'); if (! pp) pp = curr_file; if (strstr(pp,"(fotoxx montage)")) { // click on image montage file, montage_Lclick_func(Mxclick,Myclick); // popup corresp. image file LMclick = 0; } } if (LMclick) { // left click = zoom request LMclick = 0; zoomx = Mxclick; // zoom center = mouse zoomy = Myclick; m_zoom(0,"in"); } if (RMclick) { // right click RMclick = 0; if (Cstate->fzoom) { // if zoomed image, reset to fit window zoomx = zoomy = 0; m_zoom(0,"fit"); } else if (curr_file && FGWM == 'F') image_Rclick_popup(); // image right-click popup menu } if (Mxdrag || Mydrag) // drag = scroll by mouse gtk_widget_queue_draw(Cdrawin); return; } // convert mouse position from window space to image space void mouse_convert(int xpos1, int ypos1, int &xpos2, int &ypos2) { xpos2 = (xpos1 - Cstate->dorgx + Cstate->morgx) / Cstate->mscale + 0.5; ypos2 = (ypos1 - Cstate->dorgy + Cstate->morgy) / Cstate->mscale + 0.5; if (xpos2 < 0) xpos2 = 0; // if outside image put at edge if (ypos2 < 0) ypos2 = 0; if (E3pxm) { if (xpos2 >= E3pxm->ww) xpos2 = E3pxm->ww-1; if (ypos2 >= E3pxm->hh) ypos2 = E3pxm->hh-1; } else { if (xpos2 >= Cstate->fpxb->ww) xpos2 = Cstate->fpxb->ww-1; if (ypos2 >= Cstate->fpxb->hh) ypos2 = Cstate->fpxb->hh-1; } return; } /********************************************************************************/ // set new image zoom level or magnification // zoom: meaning: // in zoom-in in steps // out zoom-out in steps // fit zoom to fit window (image < window >> 1x) // fit+ zoom to fit window (image < window >> fit window) // toggle toggle 1x and fit window (Z-key) (image < window >> fit window) void m_zoom(GtkWidget *, cchar *zoom) // overhauled 20.0 { int fww, fhh; float scalew, scaleh, fitscale, fzoom2; float Czoom, Gzoom = 0, pixels; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image if (E3pxm) { // edit image active fww = E3pxm->ww; // 1x image size fhh = E3pxm->hh; } else { fww = Cstate->fpxb->ww; fhh = Cstate->fpxb->hh; } scalew = 1.0 * Dww / fww; // calc. scale to fit window scaleh = 1.0 * Dhh / fhh; if (scalew < scaleh) fitscale = scalew; else fitscale = scaleh; Czoom = Cstate->mscale; // current image scale if (strmatch(zoom,"Zoom+")) zoom = "in"; // menu button: + = zoom in if (strmatch(zoom,"Zoom-")) zoom = "fit"; // - = fit window if (strmatch(zoom,"fit")) Gzoom = 0; // fit window (image < window >> 1x) if (strmatch(zoom,"fit+")) Gzoom = -1; // fit window (image < window >> fit window) if (strmatch(zoom,"toggle")) { // alternate 1x and fit window (Z-key) if (fabsf(Czoom - fitscale) < 0.01) Gzoom = 1; // at or near fit window: zoom to 1x else Gzoom = -1; // else fit window (image < win >> fit win) } for (fzoom2 = 0.0625; fzoom2 < 4.0; fzoom2 *= zoomratio) // find nearest natural ratio if (Czoom < fzoom2 * sqrt(zoomratio)) break; // fzoom2 = 0.0625 ... 4.0 if (strmatch(zoom,"in")) { // zoom in - make image larger Gzoom = fzoom2 * zoomratio; // next size step larger pixels = Gzoom * Gzoom * fww * fhh; // new image size in pixels if (pixels > 500 * MEGA) Gzoom = Czoom; // limit size to 500 megapixels (8 GB) } if (strmatch(zoom,"out")) { // zoom out - make image smaller Gzoom = fzoom2 / zoomratio; if (fitscale <= 1 && Gzoom < fitscale) Gzoom = -1; // large image: limit = fit window 20.06 if (fitscale > 1 && Gzoom < 1) Gzoom = 1; // small image: limit = 1x } if (Gzoom > 0 && Gzoom != 1 && Gzoom != fitscale) { if (Gzoom > 0.124 && Gzoom < 0.126) Gzoom = 0.125; // hit these ratios exactly else if (Gzoom > 0.24 && Gzoom < 0.26) Gzoom = 0.25; else if (Gzoom > 0.49 && Gzoom < 0.51) Gzoom = 0.50; else if (Gzoom > 0.99 && Gzoom < 1.01) Gzoom = 1.00; else if (Gzoom > 1.99 && Gzoom < 2.01) Gzoom = 2.00; else if (Gzoom > 3.99) Gzoom = 4.0; // max. allowed zoom } if (FGWM == 'W') { // optimize for file maps if (strmatch(zoom,"in") && Gzoom < 1.0) Gzoom = 1.0; // zoom from small to 1x directly if (strmatch(zoom,"out")) Gzoom = 0.0; // zoom from large to fit window directly } Cstate->fzoom = Gzoom; // set new zoom size if (! Gzoom) zoomx = zoomy = 0; // no requested zoom center Fpaint2(); // refresh window return; } /********************************************************************************/ // function for dialogs to call to send KB keys for processing by main app void KBevent(GdkEventKey *event) { KBpress(0,event,0); return; } // keyboard event functions // GDK key symbols: /usr/include/gtk-3.0/gdk/gdkkeysyms.h namespace trimrotate { void KBfunc(int key); } // keyboard functions called from here namespace perspective { void KBfunc(int key); } namespace mashup { void KBfunc(int key); } namespace view360 { void KB_func(int key); } int KBpress(GtkWidget *win, GdkEventKey *event, void *) // keyboard key was pressed { int ii, jj, cc; char shortkey[20] = ""; cchar *action = 0; char *pp, *file = 0; KBkey = event->keyval; // input key if ((KBkey & 0xfff0) == 0xffe0) return 1; // Ctrl/Shift/Alt key KBcontrolkey = KBshiftkey = KBaltkey = 0; // look for combination keys if (event->state & GDK_CONTROL_MASK) KBcontrolkey = 1; if (event->state & GDK_SHIFT_MASK) KBshiftkey = 1; if (event->state & GDK_MOD1_MASK) KBaltkey = 1; if (KBshiftkey && KBkey == GDK_KEY_plus) KBshiftkey = 0; // treat Shift [+] same as [+] if (KBkey == GDK_KEY_equal) KBkey = GDK_KEY_plus; // treat [=] same as [+] if (KBkey == GDK_KEY_KP_Add) KBkey = GDK_KEY_plus; // treat keypad [+] same as [+] if (KBkey == GDK_KEY_KP_Subtract) KBkey = GDK_KEY_minus; // treat keypad [-] same as [-] if (KBkey == GDK_KEY_F1) { // F1 >> user guide showz_docfile(Mwin,"userguide",F1_help_topic); // text docfile 20.0 return 1; } if (Fslideshow) { // slide show active 19.0 if (KBkey == GDK_KEY_F10) Fescape = 1; // tell slide show to quit else if (KBkey == GDK_KEY_F11) Fescape = 1; else if (KBkey == GDK_KEY_Escape) Fescape = 1; else ss_KBfunc(KBkey); // pass other keys to slide show return 1; } if (Fview360) { // view360 active view360::KB_func(KBkey); // pass KB keys to view360 return 1; } if (KBkey == GDK_KEY_F10) { // F10: fullscreen toggle with menu if (! Ffullscreen) win_fullscreen(0); // toggle full-screen mode and back else win_unfullscreen(); return 1; } if (KBkey == GDK_KEY_F11) { // F11: fullscreen toggle no menu if (! Ffullscreen) win_fullscreen(1); // toggle full-screen mode and back else win_unfullscreen(); return 1; } if (KBkey == GDK_KEY_Escape) { // ESC key 19.0 if (Fescape) quitxx(); // 2nd try, quit now else if (Ffuncbusy || Fthreadbusy) Fescape = 1; // ask busy function/thread to quit else if (Ffullscreen) win_unfullscreen(); // exit full screen mode else delete_event(); // allow bailout if unsaved changes 20.0 return 1; } if (KBkey == GDK_KEY_p) // P key, play video file 20.0 { // (OK to also use P in shortcuts) if (! curr_file) return 1; if (image_file_type(curr_file) == VIDEO) { file = zescape_quotes(curr_file); shell_quiet("%s \"%s\"",video_command,file); // command from user preferences 20.0 zfree(file); return 1; } pp = strrchr(curr_file,'.'); // play animated GIF file if (pp && strstr(".gif .GIF",pp)) { play_gif(curr_file); return 1; } } if (KBkey == GDK_KEY_Delete) action = (char *) "Delete"; // reserved shortcuts if (KBkey == GDK_KEY_Left) action = (char *) "Left"; if (KBkey == GDK_KEY_Right) action = (char *) "Right"; if (KBkey == GDK_KEY_Up) action = (char *) "Up"; if (KBkey == GDK_KEY_Down) action = (char *) "Down"; if (KBkey == GDK_KEY_Home) action = (char *) "First"; if (KBkey == GDK_KEY_End) action = (char *) "Last"; if (KBkey == GDK_KEY_Page_Up) action = (char *) "Page_Up"; if (KBkey == GDK_KEY_Page_Down) action = (char *) "Page_Down"; if (KBkey == GDK_KEY_z) action = (char *) "toggle-zoom"; // Z-key 20.0 if (! action) // custom shortcut { if (KBkey >= GDK_KEY_F2 && KBkey <= GDK_KEY_F9) { // input key is F2 to F9 ii = KBkey - GDK_KEY_F1; strcpy(shortkey,"F1"); // convert to "F2" etc. shortkey[1] += ii; } if (! *shortkey && KBkey < 256) // single ascii character { if (KBcontrolkey) strcat(shortkey,"Ctrl+"); // build input key combination if (KBaltkey) strcat(shortkey,"Alt+"); // [Ctrl+] [Alt+] [Shift+] key if (KBshiftkey) strcat(shortkey,"Shift+"); cc = strlen(shortkey); shortkey[cc] = KBkey; shortkey[cc+1] = 0; } if (*shortkey) { // find key in shortcut list for (ii = 0; ii < Nkbsu; ii++) if (strmatchcase(shortkey,kbsutab[ii].key)) break; if (ii < Nkbsu) action = kbsutab[ii].menu; // corresp. action or function } } if (! action) { if (strlen(shortkey) == 1) // 19.0 printz("shortcut key not found: %c \n",toupper(*shortkey)); else printz("shortcut key not found: %s \n",shortkey); return 1; } if (zstrstr(zdialog_button_shortcuts,action)) { printz("dialog button shortcut, ignored \n"); return 1; } if (FGWM == 'G') { // G view mode navi::KBaction(action); // pass KB action to gallery return 1; } if (strmatch(action,E2X("Show Hidden"))) return 1; // only meaningful in G view if (FGWM == 'M') { // map view mode ii = strmatchV(action,"File View","Gallery",0); // allow only view mode changes if (! ii) { if (strlen(shortkey) == 1) // 19.0 printz("shortcut key ignored: %c %s \n",toupper(*shortkey),action); else printz("shortcut key ignored: %s %s \n",shortkey,action); return 1; } } if (KBcapture) return 1; // let current function handle it if (Fmashup) { // mashup active, pass KB key mashup::KBfunc(KBkey); return 1; } if (CEF && CEF->menufunc == m_trim_rotate) { // trim_rotate active, pass KB key trimrotate::KBfunc(KBkey); return 1; } if (CEF && CEF->menufunc == m_perspective) { // perspective active, pass KB key perspective::KBfunc(KBkey); return 1; } if (strmatch(action,"Left")) { // left arrow - previous image m_prev(0,0); return 1; } if (strmatch(action,"Right")) { // right arrow - next image m_next(0,0); return 1; } if (strmatch(action,"Delete")) { // delete key - delete/trash dialog m_delete_trash(0,0); return 1; } if (strmatch(action,"Zoom+")) { // zoom center = mouse position zoomx = Mxposn; zoomy = Myposn; m_zoom(0,"in"); // zoom-in return 1; } if (strmatch(action,"Zoom-")) { // zoom to fit window m_zoom(0,"fit"); // small image >> 1x return 1; } if (strmatch(action,"toggle-zoom")) { // toggle zoom 1x / fit window 20.0 m_zoom(0,"toggle"); // (Z-key) return 1; } // look up action in KB shortcut table, get corresp. function and arg. for (jj = 0; jj < Nkbsf; jj++) if (strmatchcase(action,kbsftab[jj].menu)) break; if (jj == Nkbsf) { printz("shortcut menu func not found: %s %s \n",shortkey,action); return 1; } if (! kbsftab[jj].func) { printz("shortcut func null - ignored \n"); return 1; } kbsftab[jj].func(0,kbsftab[jj].arg); // call the menu function return 1; } int KBrelease(GtkWidget *win, GdkEventKey *event, void *) // KB key released { KBkey = 0; // reset current active key return 1; } /********************************************************************************/ // set the main window to fullscreen status // (with no menu or panel) void win_fullscreen(int hidemenu) { if (FGWM == 'F' && hidemenu) { // if F window, hide panel gtk_widget_hide(MWmenu); gtk_widget_hide(Fpanel); Fpanelshow = 0; } if (hidemenu) gtk_window_fullscreen(MWIN); else gtk_window_maximize(MWIN); while (! Ffullscreen) zmainloop(); return; } // restore window to former size and restore menu etc. void win_unfullscreen() { gtk_window_unfullscreen(MWIN); // restore old window size gtk_window_unmaximize(MWIN); gtk_widget_show(MWmenu); gtk_widget_show(Fpanel); Fpanelshow = 1; while (Ffullscreen) zmainloop(); return; } /********************************************************************************/ // update information in main window title bar namespace meta_names { extern char meta_pdate[16]; // image (photo) date, yyyymmddhhmmss } void set_mwin_title() { GTYPE gtype; int cc, fposn, Nfiles, Nimages; char *pp, titlebar[250]; char pdate[12], ptime[12], pdatetime[24]; char fname[100], gname[100], ffolder[100]; if (FGWM != 'F') return; 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(ffolder,curr_file,cc+2); // get folder/path/ if short enough else { strncpy(ffolder,curr_file,96); // or use /folder/path... strcpy(ffolder+95,"..."); } Nfiles = navi::Nfiles; // total gallery files (incl. folders) Nimages = navi::Nimages; // total image files fposn = file_position(curr_file,curr_file_posn); // curr. file in curr. gallery? if (fposn >= 0) { curr_file_posn = fposn; fposn = fposn + 1 - Nfiles + Nimages; // position among images, 1-based } if (*meta_names::meta_pdate) { metadate_pdate(meta_names::meta_pdate,pdate,ptime); // get formatted date and time strncpy0(pdatetime,pdate,11); // yyyy-mm-dd hh:mm:ss strncpy0(pdatetime+11,ptime,9); pdatetime[10] = ' '; } else strcpy(pdatetime,"(undated)"); gtype = navi::gallerytype; if (gtype == GDIR) // gallery name = folder snprintf(titlebar,250,"Fotoxx %d/%d %s %s %s", fposn,Nimages,ffolder,fname,pdatetime); else { if (gtype == SEARCH || gtype == META) strcpy(gname,"SEARCH RESULTS"); else if (gtype == ALBUM) { pp = strrchr(navi::galleryname,'/'); if (! pp) pp = navi::galleryname; else pp++; strcpy(gname,"ALBUM: "); strncpy0(gname+7,pp,87); } else if (gtype == RECENT) strcpy(gname,"RECENT FILES"); else if (gtype == NEWEST) strcpy(gname,"NEWEST FILES"); else strcpy(gname,"NO GALLERY"); if (fposn > 0) snprintf(titlebar,250,"Fotoxx %s %d/%d %s %s %s", // window title bar gname,fposn,Nimages,ffolder,fname,pdatetime); else snprintf(titlebar,250,"Fotoxx %s (*)/%d %s %s %s", // image not in gallery gname,Nimages,ffolder,fname,pdatetime); } gtk_window_set_title(MWIN,titlebar); return; } /********************************************************************************/ // draw a pixel using foreground color. // px, py are image space. void draw_pixel(int px, int py, cairo_t *cr, int fat) { int qx, qy; static int pqx, pqy; static uint8 pixel[12]; // 2x2 block of pixels static PIXBUF *pixbuf1 = 0, *pixbuf4 = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image if (! pixbuf1) { pixbuf1 = gdk_pixbuf_new_from_data(pixel,GDKRGB,0,8,1,1,3,0,0); // 1x1 pixels pixbuf4 = gdk_pixbuf_new_from_data(pixel,GDKRGB,0,8,2,2,6,0,0); // 2x2 pixels } if (Cstate->fpxb->nc > 3) // omit transparent pixels if (PXBpix(Cstate->fpxb,px,py)[3] < 128) return; qx = Mscale * px - Morgx; // image to window space qy = Mscale * py - Morgy; if (qx == pqx && qy == pqy) return; // avoid redundant points pqx = qx; pqy = qy; if (qx < 0 || qx > dww-2) return; // keep off image edges if (qy < 0 || qy > dhh-2) return; if (Mscale <= 1 && ! fat) { // write 1x1 pixels pixel[0] = LINE_COLOR[0]; pixel[1] = LINE_COLOR[1]; pixel[2] = LINE_COLOR[2]; gdk_cairo_set_source_pixbuf(cr,pixbuf1,qx+Dorgx,qy+Dorgy); cairo_paint(cr); } else { // write 2x2 fat pixels pixel[0] = pixel[3] = pixel[6] = pixel[9] = LINE_COLOR[0]; pixel[1] = pixel[4] = pixel[7] = pixel[10] = LINE_COLOR[1]; pixel[2] = pixel[5] = pixel[8] = pixel[11] = LINE_COLOR[2]; gdk_cairo_set_source_pixbuf(cr,pixbuf4,qx+Dorgx,qy+Dorgy); cairo_paint(cr); } return; } // erase one drawn pixel - restore from window image Mpxb. // px, py are image space. void erase_pixel(int px, int py, cairo_t *cr) { GdkPixbuf *pixbuf; static int pqx, pqy; int qx, qy; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image qx = Mscale * px; // image to window space qy = Mscale * py; if (qx == pqx && qy == pqy) return; // avoid same target pixel pqx = qx; pqy = qy; if (qx < 0 || qx > Mpxb->ww-2) return; // pixel outside scaled image if (qy < 0 || qy > Mpxb->hh-2) return; if (qx < Morgx || qx > Morgx + dww-2) return; // pixel outside drawing window if (qy < Morgy || qy > Morgy + dhh-2) return; pixbuf = gdk_pixbuf_new_subpixbuf(Mpxb->pixbuf,qx,qy,2,2); // 2x2 Mpxb area to copy qx = qx - Morgx + Dorgx; // target pixel in window qy = qy - Morgy + Dorgy; gdk_cairo_set_source_pixbuf(cr,pixbuf,qx,qy); cairo_paint(cr); g_object_unref(pixbuf); return; } /********************************************************************************/ // draw line. // coordinates are image space. // type = 1/2 for solid/dotted line void draw_line(int x1, int y1, int x2, int y2, int type, cairo_t *cr) { float px1, py1, px2, py2; double dashes[2] = { 4, 4 }; double R, G, B; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image px1 = Mscale * x1 - Morgx + Dorgx; // image to window space py1 = Mscale * y1 - Morgy + Dorgy; px2 = Mscale * x2 - Morgx + Dorgx; py2 = Mscale * y2 - Morgy + Dorgy; if (px1 > Dww-2) px1 = Dww-2; // play nice if (py1 > Dhh-2) py1 = Dhh-2; if (px2 > Dww-2) px2 = Dww-2; if (py2 > Dhh-2) py2 = Dhh-2; R = LINE_COLOR[0] / 255.0; // use line color G = LINE_COLOR[1] / 255.0; B = LINE_COLOR[2] / 255.0; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } cairo_set_source_rgb(cr,R,G,B); if (type == 2) cairo_set_dash(cr,dashes,2,0); // dotted line else cairo_set_dash(cr,dashes,0,0); cairo_move_to(cr,px1,py1); // draw line cairo_line_to(cr,px2,py2); cairo_stroke(cr); if (crflag) draw_context_destroy(draw_context); return; } // erase line. refresh line path from mpxb window image. // double line width is erased. // coordinates are image space. void erase_line(int x1, int y1, int x2, int y2, cairo_t *cr) { float pxm, pym, slope; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } if (abs(y2 - y1) > abs(x2 - x1)) { slope = 1.0 * (x2 - x1) / (y2 - y1); for (pym = y1; pym <= y2; pym++) { pxm = x1 + slope * (pym - y1); erase_pixel(pxm,pym,cr); } } else { slope = 1.0 * (y2 - y1) / (x2 - x1); for (pxm = x1; pxm <= x2; pxm++) { pym = y1 + slope * (pxm - x1); erase_pixel(pxm,pym,cr); } } if (crflag) draw_context_destroy(draw_context); return; } /********************************************************************************/ // draw pre-set overlay lines on top of image // arg = 1: paint lines only (because window repainted) // 2: erase lines and forget them // 3: erase old lines, paint new lines, save new in old void draw_toplines(int arg, cairo_t *cr) { int ii; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } if (arg == 2 || arg == 3) // erase old lines for (ii = 0; ii < Nptoplines; ii++) erase_line(ptoplines[ii].x1,ptoplines[ii].y1, ptoplines[ii].x2,ptoplines[ii].y2,cr); if (arg == 1 || arg == 3) // draw new lines for (ii = 0; ii < Ntoplines; ii++) draw_line(toplines[ii].x1,toplines[ii].y1, toplines[ii].x2,toplines[ii].y2,toplines[ii].type,cr); if (crflag) draw_context_destroy(draw_context); if (arg == 2) { Nptoplines = Ntoplines = 0; // forget lines return; } for (ii = 0; ii < Ntoplines; ii++) // save for future erase ptoplines[ii] = toplines[ii]; Nptoplines = Ntoplines; return; } /********************************************************************************/ // draw a grid of horizontal and vertical lines. // grid line spacings are in window space. void draw_gridlines(cairo_t *cr) { int G = currgrid; int px, py, gww, ghh; int startx, starty, endx, endy, stepx, stepy; int startx1, starty1; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image if (! gridsettings[G][GON]) return; // grid lines off gww = dww; // grid box size ghh = dhh; startx = Dorgx; // starting corner (top left) starty = Dorgy; if (CEF && strmatch(CEF->funcname,"trim/rotate")) { // trim/rotate function is active gww = Mscale * (trimx2 - trimx1); // fit grid box to trim rectangle ghh = Mscale * (trimy2 - trimy1); startx = Mscale * trimx1 - Morgx + Dorgx; starty = Mscale * trimy1 - Morgy + Dorgy; } endx = startx + gww; endy = starty + ghh; stepx = gridsettings[G][GXS]; // space between grid lines stepy = gridsettings[G][GYS]; // (window space) if (gridsettings[G][GXC]) stepx = gww / (1 + gridsettings[G][GXC]); // if line counts specified, if (gridsettings[G][GYC]) // set spacing accordingly stepy = ghh / ( 1 + gridsettings[G][GYC]); if (stepx < 20) stepx = 20; // sanity limits 20.0 if (stepy < 20) stepy = 20; startx1 = startx + stepx * gridsettings[G][GXF] / 100; // variable starting offsets starty1 = starty + stepy * gridsettings[G][GYF] / 100; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } cairo_set_source_rgb(cr,1,1,1); // white lines if (gridsettings[G][GX] && stepx) for (px = startx1; px < endx; px += stepx) { cairo_move_to(cr,px,starty); cairo_line_to(cr,px,endy); } if (gridsettings[G][GY] && stepy) for (py = starty1; py < endy; py += stepy) { cairo_move_to(cr,startx,py); cairo_line_to(cr,endx,py); } cairo_stroke(cr); cairo_set_source_rgb(cr,0,0,0); // adjacent black lines if (gridsettings[G][GX] && stepx) for (px = startx1+1; px < endx+1; px += stepx) { cairo_move_to(cr,px,starty); cairo_line_to(cr,px,endy); } if (gridsettings[G][GY] && stepy) for (py = starty1+1; py < endy+1; py += stepy) { cairo_move_to(cr,startx,py); cairo_line_to(cr,endx,py); } cairo_stroke(cr); if (crflag) draw_context_destroy(draw_context); return; } /********************************************************************************/ // maintain a set of text strings written over the image in the window. // add a new text string to the list. // multiple text strings can be added with the same ID. // px and py are image space. void add_toptext(int ID, int px, int py, cchar *text, cchar *font) { if (Ntoptext == maxtoptext) { printz("*** maxtoptext exceeded \n"); return; } int ii = Ntoptext++; toptext[ii].ID = ID; toptext[ii].px = px; toptext[ii].py = py; toptext[ii].text = text; toptext[ii].font = font; return; } // draw current text strings over the image in window. // called from Fpaint(). void draw_toptext(cairo_t *cr) { int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } for (int ii = 0; ii < Ntoptext; ii++) draw_text(toptext[ii].px,toptext[ii].py,toptext[ii].text,toptext[ii].font,cr); if (crflag) draw_context_destroy(draw_context); return; } // delete text strings having the given ID from the list void erase_toptext(int ID) { int ii, jj; for (ii = jj = 0; ii < Ntoptext; ii++) { if (toptext[ii].ID == ID) continue; else toptext[jj++] = toptext[ii]; } Ntoptext = jj; return; } // draw overlay text on window, black on white background // coordinates are image space void draw_text(int px, int py, cchar *text, cchar *font, cairo_t *cr) { static PangoFontDescription *pangofont = 0; static PangoLayout *pangolayout = 0; static char priorfont[40] = ""; int ww, hh; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; px = Mscale * px - Morgx + Dorgx; // image to window space py = Mscale * py - Morgy + Dorgy; if (! strmatch(font,priorfont)) { // change font strncpy0(priorfont,font,40); if (pangofont) pango_font_description_free(pangofont); if (pangolayout) g_object_unref(pangolayout); pangofont = pango_font_description_from_string(font); // make pango layout for font pangolayout = gtk_widget_create_pango_layout(Cdrawin,0); pango_layout_set_font_description(pangolayout,pangofont); } pango_layout_set_text(pangolayout,text,-1); // add text to layout pango_layout_get_pixel_size(pangolayout,&ww,&hh); if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } cairo_set_source_rgb(cr,1,1,1); // draw white background cairo_rectangle(cr,px,py,ww,hh); cairo_fill(cr); cairo_move_to(cr,px,py); // draw layout with text cairo_set_source_rgb(cr,0,0,0); pango_cairo_show_layout(cr,pangolayout); if (crflag) draw_context_destroy(draw_context); return; } /********************************************************************************/ // maintain a set of circles drawn over the image in the window // px, py are image space, radius is window space void add_topcircle(int px, int py, int radius) { if (Ntopcircles == maxtopcircles) { printz("*** maxtopcircles exceeded \n"); return; } int ii = Ntopcircles++; topcircles[ii].px = px; topcircles[ii].py = py; topcircles[ii].radius = radius; return; } // draw current circles over the image in the window // called from window repaint function Fpaint() void draw_topcircles(cairo_t *cr) { double R, G, B; double px, py, rad; // 20.0 int ii, crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image R = LINE_COLOR[0] / 255.0; // use LINE_COLOR G = LINE_COLOR[1] / 255.0; B = LINE_COLOR[2] / 255.0; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } for (ii = 0; ii < Ntopcircles; ii++) { px = topcircles[ii].px * Mscale - Morgx + Dorgx + 0.5; // image to window space py = topcircles[ii].py * Mscale - Morgy + Dorgy + 0.5; rad = topcircles[ii].radius; // radius is window space cairo_new_path(cr); // bugfix 19.6 cairo_set_source_rgb(cr,R,G,B); cairo_arc(cr,px,py,rad,0,2*PI); // draw 360 deg. arc cairo_stroke(cr); } if (crflag) draw_context_destroy(draw_context); return; } // erase top circles (next window repaint) void erase_topcircles() { Ntopcircles = 0; return; } /********************************************************************************/ // Draw circle around the mouse pointer. // Prior circle will be erased first. // Used for mouse/brush radius in select and paint functions. // cx, cy, rad: center and radius of circle in image space. // if Ferase, then erase previous circle only. void draw_mousecircle(int cx, int cy, int rad, int Ferase, cairo_t *cr) { int px3, py3, ww3, hh3; static int ppx3, ppy3, pww3 = 0, phh3; int px, py, pok; double R, G, B; double t, dt, t1, t2; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->mpxb) return; // no image gdk_window_freeze_updates(gdkwin); // smoother image update 20.0 if (pww3 > 0) { // erase prior Fpaint4(ppx3,ppy3,pww3,phh3,cr); // refresh from Mpxb pww3 = 0; } if (Ferase) { gdk_window_thaw_updates(gdkwin); // 20.0 return; // erase only, done } px3 = cx - rad - 2; // convert pointer center + radius py3 = cy - rad - 2; // to block position, width, length ww3 = 2 * rad + 4; hh3 = 2 * rad + 4; ppx3 = px3; // remember pixel block area ppy3 = py3; // to erase in next call pww3 = ww3; phh3 = hh3; cx = cx * Mscale - Morgx + Dorgx; // convert to window coordinates cy = cy * Mscale - Morgy + Dorgy; rad = rad * Mscale; R = LINE_COLOR[0] / 255.0; // use LINE_COLOR G = LINE_COLOR[1] / 255.0; B = LINE_COLOR[2] / 255.0; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } cairo_set_source_rgba(cr,R,G,B,1.0); t1 = t2 = -1; // angle limits of arc to draw dt = 1.0 / rad; for (t = 0; t < 2*PI; t += dt) // loop 0-360 degrees { px = cx + rad * cos(t); // pixel on mouse circle py = cy + rad * sin(t); pok = 1; // assume pixel OK to draw if (px < Dorgx || py < Dorgy) pok = 0; // outside image limits if (px >= Dorgx+dww || py >= Dorgy+dhh) pok = 0; if (pok) { // pixel ok, add to arc if (t1 < 0) t1 = t; // start of arc to draw t2 = t; // end of arc, so far } else if (t1 >= 0) { // pixel not ok cairo_arc(cr,cx,cy,rad,t1,t2); // draw accumulated arc cairo_stroke(cr); t1 = t2 = -1; // start over } } if (t1 >= 0) { cairo_arc(cr,cx,cy,rad,t1,t2); // draw rest of arc cairo_stroke(cr); } if (crflag) draw_context_destroy(draw_context); gdk_window_thaw_updates(gdkwin); // 20.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 rad, int Ferase, cairo_t *cr) { int px3, py3, ww3, hh3; static int ppx3, ppy3, pww3 = 0, phh3; int px, py, pok; double R, G, B; double t, dt, t1, t2; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->mpxb) return; // no image if (pww3 > 0) { // erase prior Fpaint4(ppx3,ppy3,pww3,phh3,cr); // refresh from Mpxb pww3 = 0; } if (Ferase) return; // erase only, done px3 = cx - rad - 2; // convert pointer center + radius py3 = cy - rad - 2; // to block position, width, length ww3 = 2 * rad + 4; hh3 = 2 * rad + 4; ppx3 = px3; // remember pixel block area ppy3 = py3; // to erase in next call pww3 = ww3; phh3 = hh3; cx = cx * Mscale - Morgx + Dorgx; // convert to window coordinates cy = cy * Mscale - Morgy + Dorgy; rad = rad * Mscale; R = LINE_COLOR[0] / 255.0; // use LINE_COLOR G = LINE_COLOR[1] / 255.0; B = LINE_COLOR[2] / 255.0; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } cairo_set_source_rgba(cr,R,G,B,1.0); t1 = t2 = -1; // angle limits of arc to draw dt = 1.0 / rad; for (t = 0; t < 2*PI; t += dt) // loop 0-360 degrees { px = cx + rad * cos(t); // pixel on mouse circle py = cy + rad * sin(t); pok = 1; // assume pixel OK to draw if (px < Dorgx || py < Dorgy) pok = 0; // outside image limits if (px >= Dorgx+dww || py >= Dorgy+dhh) pok = 0; if (pok) { // pixel ok, add to arc if (t1 < 0) t1 = t; // start of arc to draw t2 = t; // end of arc, so far } else if (t1 >= 0) { // pixel not ok cairo_arc(cr,cx,cy,rad,t1,t2); // draw accumulated arc cairo_stroke(cr); t1 = t2 = -1; // start over } } if (t1 >= 0) { cairo_arc(cr,cx,cy,rad,t1,t2); // draw rest of arc cairo_stroke(cr); } if (crflag) draw_context_destroy(draw_context); return; } /********************************************************************************/ // Draw ellipse around the mouse pointer. // Prior ellipse will be erased first. // cx, cy, cww, chh: center and axes of ellipse in image space. // if Ferase, then erase previous ellipse only. void draw_mousearc(int cx, int cy, int cww, int chh, int Ferase, cairo_t *cr) { int px3, py3, ww3, hh3; static int ppx3, ppy3, pww3 = 0, phh3; int px, py; float a, b, a2, b2; float x, y, x2, y2; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } if (pww3 > 0) { // erase prior Fpaint4(ppx3,ppy3,pww3,phh3,cr); // refresh from Mpxb pww3 = 0; } if (Ferase) { if (crflag) draw_context_destroy(draw_context); return; } px3 = cx - (cww + 2) / 2; // convert pointer center + radius py3 = cy - (chh + 2) / 2; // to block position, width, length ww3 = cww + 2; hh3 = chh + 2; ppx3 = px3; // remember pixel block area ppy3 = py3; // to erase in next call pww3 = ww3; phh3 = hh3; a = cww / 2; // ellipse constants from b = chh / 2; // enclosing rectangle a2 = a * a; b2 = b * b; for (y = -b; y < b; y++) // step through y values, omitting { // curve points covered by x values y2 = y * y; x2 = a2 * (1 - y2 / b2); x = sqrtf(x2); // corresp. x values, + and - py = y + cy; px = cx - x + 0.5; draw_pixel(px,py,cr); // draw 2 points on ellipse px = cx + x + 0.5; draw_pixel(px,py,cr); } for (x = -a; x < a; x++) // step through x values, omitting { // curve points covered by y values x2 = x * x; y2 = b2 * (1 - x2 / a2); y = sqrtf(y2); // corresp. y values, + and - px = x + cx; py = cy - y + 0.5; draw_pixel(px,py,cr); // draw 2 points on ellipse py = cy + y + 0.5; draw_pixel(px,py,cr); } if (crflag) draw_context_destroy(draw_context); return; } /******************************************************************************** spline curve setup and edit functions Support multiple frames with curves in parallel (edit curve(s) and image mask curve) Usage: Add frame widget to dialog or zdialog. Set up drawing in frame: sd = splcurve_init(frame, callback_func) Initialize no. of curves in frame (1-10): sd->Nspc = n Initialize active flag for curve spc: sd->fact[spc] = 1 Initialize vert/horz flag for curve spc: sd->vert[spc] = hv Initialize anchor points for curve spc: sd->nap[spc], sd->apx[spc][xx], sd->apy[spc][yy] Generate data for curve spc: splcurve_generate(sd,spc) Curves will now be shown and edited inside the frame when window is realized. The callback_func(spc) will be called when curve spc is edited. Change curve in program: set anchor points, call splcurve_generate(sd,spc) Get y-value (0-1) for curve spc and given x-value (0-1): yval = splcurve_yval(sd,spc,xval) If faster access to curve is needed (no interpolation): kk = 1000 * xval; if (kk > 999) kk = 999; yval = sd->yval[spc][kk]; ***/ // initialize for spline curve editing // initial anchor points are pre-loaded into spldat before window is realized spldat * splcurve_init(GtkWidget *frame, void func(int spc)) { int cc = sizeof(spldat); // allocate spc curve data area spldat * sd = (spldat *) zmalloc(cc); memset(sd,0,cc); sd->drawarea = gtk_drawing_area_new(); // drawing area for curves gtk_container_add(GTK_CONTAINER(frame),sd->drawarea); sd->spcfunc = func; // user callback function gtk_widget_add_events(sd->drawarea,GDK_BUTTON_PRESS_MASK); // connect mouse events to drawing area gtk_widget_add_events(sd->drawarea,GDK_BUTTON_RELEASE_MASK); gtk_widget_add_events(sd->drawarea,GDK_BUTTON1_MOTION_MASK); G_SIGNAL(sd->drawarea,"motion-notify-event",splcurve_adjust,sd); G_SIGNAL(sd->drawarea,"button-press-event",splcurve_adjust,sd); G_SIGNAL(sd->drawarea,"realize",splcurve_resize,sd); G_SIGNAL(sd->drawarea,"draw",splcurve_draw,sd); return sd; } // modify anchor points in curve using mouse int splcurve_adjust(void *, GdkEventButton *event, spldat *sd) { int ww, hh, kk; int mx, my, button, evtype; static int spc, ap, mbusy = 0, Fdrag = 0; // drag continuation logic int minspc, minap, apset = 0; float mxval, myval, cxval, cyval; float dist2, mindist2 = 0; float dist, dx, dy; float minx = 0.01 * splcurve_minx; // % to absolute distance mx = event->x; // mouse position in drawing area my = event->y; evtype = event->type; button = event->button; if (evtype == GDK_MOTION_NOTIFY) { if (mbusy) return 0; // discard excess motion events mbusy++; zmainloop(); mbusy = 0; } if (evtype == GDK_BUTTON_RELEASE) { Fdrag = 0; return 0; } ww = gtk_widget_get_allocated_width(sd->drawarea); // drawing area size hh = gtk_widget_get_allocated_height(sd->drawarea); if (mx < 0) mx = 0; // limit edge excursions if (mx > ww) mx = ww; if (my < 0) my = 0; if (my > hh) my = hh; if (evtype == GDK_BUTTON_PRESS) Fdrag = 0; // left or right click if (Fdrag) // continuation of drag { if (sd->vert[spc]) { mxval = 1.0 * my / hh; // mouse position in curve space myval = 1.0 * mx / ww; } else { mxval = 1.0 * mx / ww; myval = 1.0 * (hh - my) / hh; } if (ap < sd->nap[spc] - 1) { // not the last anchor point dx = sd->apx[spc][ap+1] - mxval; // get distance to next anchor point if (dx < 0.01) return 0; // x-value not increasing, forbid dy = sd->apy[spc][ap+1] - myval; dist = sqrtf(dx * dx + dy * dy); if (dist < minx) return 0; // too close, forbid } if (ap > 0) { // not the first anchor point dx = mxval - sd->apx[spc][ap-1]; // get distance to prior anchor point if (dx < 0.01) return 0; // x-value not increasing, forbid dy = myval - sd->apy[spc][ap-1]; dist = sqrtf(dx * dx + dy * dy); if (dist < minx) return 0; // too close, forbid } apset = 1; // mxval/myval = new node position } else // mouse click or new drag begin { minspc = minap = -1; // find closest curve/anchor point mindist2 = 999999; for (spc = 0; spc < sd->Nspc; spc++) // loop curves { if (! sd->fact[spc]) continue; // not active if (sd->vert[spc]) { mxval = 1.0 * my / hh; // mouse position in curve space myval = 1.0 * mx / ww; } else { mxval = 1.0 * mx / ww; myval = 1.0 * (hh - my) / hh; } for (ap = 0; ap < sd->nap[spc]; ap++) // loop anchor points { cxval = sd->apx[spc][ap]; cyval = sd->apy[spc][ap]; dist2 = (mxval-cxval)*(mxval-cxval) + (myval-cyval)*(myval-cyval); if (dist2 < mindist2) { mindist2 = dist2; // remember closest anchor point minspc = spc; minap = ap; } } } if (minspc < 0) return 0; // impossible spc = minspc; // nearest curve ap = minap; // nearest anchor point } if (evtype == GDK_BUTTON_PRESS && button == 3) // right click, remove anchor point { if (sqrtf(mindist2) > minx) return 0; // not close enough if (sd->nap[spc] < 3) return 0; // < 2 anchor points would remain sd->nap[spc]--; // decr. before loop for (kk = ap; kk < sd->nap[spc]; kk++) { sd->apx[spc][kk] = sd->apx[spc][kk+1]; sd->apy[spc][kk] = sd->apy[spc][kk+1]; } splcurve_generate(sd,spc); // regenerate data for modified curve gtk_widget_queue_draw(sd->drawarea); sd->spcfunc(spc); // call user function return 0; } if (! Fdrag) // new drag or left click { if (sd->vert[spc]) { mxval = 1.0 * my / hh; // mouse position in curve space myval = 1.0 * mx / ww; } else { mxval = 1.0 * mx / ww; myval = 1.0 * (hh - my) / hh; } if (sqrtf(mindist2) < minx) // anchor point close enough, { // move this one to mouse position if (ap < sd->nap[spc]-1) { // not the last anchor point dx = sd->apx[spc][ap+1] - mxval; // get distance to next anchor point if (dx < 0.01) return 0; // x-value not increasing, forbid dy = sd->apy[spc][ap+1] - myval; dist = sqrtf(dx * dx + dy * dy); if (dist < minx) return 0; // too close, forbid } if (ap > 0) { // not the first anchor point dx = mxval - sd->apx[spc][ap-1]; // get distance to prior anchor point if (dx < 0.01) return 0; // x-value not increasing, forbid dy = myval - sd->apy[spc][ap-1]; dist = sqrtf(dx * dx + dy * dy); if (dist < minx) return 0; // too close, forbid } apset = 1; // mxval/myval = new node position } else // none close, add new anchor point { minspc = -1; // find closest curve to mouse mindist2 = 999999; for (spc = 0; spc < sd->Nspc; spc++) // loop curves { if (! sd->fact[spc]) continue; // not active if (sd->vert[spc]) { mxval = 1.0 * my / hh; // mouse position in curve space myval = 1.0 * mx / ww; } else { mxval = 1.0 * mx / ww; myval = 1.0 * (hh - my) / hh; } cyval = splcurve_yval(sd,spc,mxval); dist2 = fabsf(myval - cyval); if (dist2 < mindist2) { mindist2 = dist2; // remember closest curve minspc = spc; } } if (minspc < 0) return 0; // impossible if (mindist2 > minx) return 0; // not close enough to any curve spc = minspc; if (sd->nap[spc] > 49) { zmessageACK(Mwin,E2X("Exceed 50 anchor points")); return 0; } if (sd->vert[spc]) { mxval = 1.0 * my / hh; // mouse position in curve space myval = 1.0 * mx / ww; } else { mxval = 1.0 * mx / ww; myval = 1.0 * (hh - my) / hh; } for (ap = 0; ap < sd->nap[spc]; ap++) // find anchor point with next higher x if (mxval <= sd->apx[spc][ap]) break; // (ap may come out 0 or nap) if (ap < sd->nap[spc] && sd->apx[spc][ap] - mxval < minx) // disallow < minx from next return 0; // or prior anchor point if (ap > 0 && mxval - sd->apx[spc][ap-1] < minx) return 0; for (kk = sd->nap[spc]; kk > ap; kk--) { // make hole for new point sd->apx[spc][kk] = sd->apx[spc][kk-1]; sd->apy[spc][kk] = sd->apy[spc][kk-1]; } sd->nap[spc]++; // up point count apset = 1; // mxval/myval = new node position } } if (evtype == GDK_MOTION_NOTIFY) Fdrag = 1; // remember drag is underway if (apset) { sd->apx[spc][ap] = mxval; // new or moved anchor point sd->apy[spc][ap] = myval; // at mouse position splcurve_generate(sd,spc); // regenerate data for modified curve if (sd->drawarea) gtk_widget_queue_draw(sd->drawarea); // redraw graph if (sd->spcfunc) sd->spcfunc(spc); // call user function } return 0; } // add a new anchor point to a curve // spc: curve number // px, py: node coordinates in the range 0-1 int splcurve_addnode(spldat *sd, int spc, float px, float py) { int ap, kk; float minx = 0.01 * splcurve_minx; // % to absolute distance for (ap = 0; ap < sd->nap[spc]; ap++) // find anchor point with next higher x if (px <= sd->apx[spc][ap]) break; // (ap may come out 0 or nap) if (ap < sd->nap[spc] && sd->apx[spc][ap] - px < minx) // disallow < minx from next return 0; // or prior anchor point if (ap > 0 && px - sd->apx[spc][ap-1] < minx) return 0; for (kk = sd->nap[spc]; kk > ap; kk--) { // make hole for new point sd->apx[spc][kk] = sd->apx[spc][kk-1]; sd->apy[spc][kk] = sd->apy[spc][kk-1]; } sd->apx[spc][ap] = px; // add node coordinates sd->apy[spc][ap] = py; sd->nap[spc]++; // up point count return 1; } // if height/width too small, make bigger int splcurve_resize(GtkWidget *drawarea) { int ww = gtk_widget_get_allocated_width(drawarea); int hh = gtk_widget_get_allocated_height(drawarea); if (hh < ww/2) gtk_widget_set_size_request(drawarea,ww,ww/2); return 1; } // for expose event or when a curve is changed // draw all curves based on current anchor points int splcurve_draw(GtkWidget *drawarea, cairo_t *cr, spldat *sd) { int ww, hh, spc, ap; float xval, yval, px, py, qx, qy; ww = gtk_widget_get_allocated_width(sd->drawarea); // drawing area size hh = gtk_widget_get_allocated_height(sd->drawarea); if (ww < 50 || hh < 50) return 0; cairo_set_line_width(cr,1); cairo_set_source_rgb(cr,0.7,0.7,0.7); for (int ii = 0; ii < sd->Nscale; ii++) // draw y-scale lines if any { px = ww * sd->xscale[0][ii]; py = hh - hh * sd->yscale[0][ii]; qx = ww * sd->xscale[1][ii]; qy = hh - hh * sd->yscale[1][ii]; cairo_move_to(cr,px,py); cairo_line_to(cr,qx,qy); } cairo_stroke(cr); cairo_set_source_rgb(cr,0,0,0); for (spc = 0; spc < sd->Nspc; spc++) // loop all curves { if (! sd->fact[spc]) continue; // not active if (sd->vert[spc]) // vert. curve { for (py = 0; py < hh; py++) // generate all points for curve { xval = 1.0 * py / hh; yval = splcurve_yval(sd,spc,xval); px = ww * yval; if (py == 0) cairo_move_to(cr,px,py); cairo_line_to(cr,px,py); } cairo_stroke(cr); for (ap = 0; ap < sd->nap[spc]; ap++) // draw boxes at anchor points { xval = sd->apx[spc][ap]; yval = sd->apy[spc][ap]; px = ww * yval; py = hh * xval; cairo_rectangle(cr,px-2,py-2,4,4); } cairo_fill(cr); } else // horz. curve { for (px = 0; px < ww; px++) // generate all points for curve { xval = 1.0 * px / ww; yval = splcurve_yval(sd,spc,xval); py = hh - hh * yval; if (px == 0) cairo_move_to(cr,px,py); cairo_line_to(cr,px,py); } cairo_stroke(cr); for (ap = 0; ap < sd->nap[spc]; ap++) // draw boxes at anchor points { xval = sd->apx[spc][ap]; yval = sd->apy[spc][ap]; px = ww * xval; py = hh - hh * yval; cairo_rectangle(cr,px-2,py-2,4,4); } cairo_fill(cr); } } return 0; } // generate all curve data points when anchor points are modified int splcurve_generate(spldat *sd, int spc) { int kk, kklo, kkhi; float xval, yvalx; spline1(sd->nap[spc],sd->apx[spc],sd->apy[spc]); // compute curve fitting anchor points kklo = 1000 * sd->apx[spc][0] - 30; // xval range = anchor point range if (kklo < 0) kklo = 0; // + 0.03 extra below/above kkhi = 1000 * sd->apx[spc][sd->nap[spc]-1] + 30; if (kkhi > 1000) kkhi = 1000; for (kk = 0; kk < 1000; kk++) // generate all points for curve { xval = 0.001 * kk; // remove anchor point limits yvalx = spline2(xval); if (yvalx < 0) yvalx = 0; // yval < 0 not allowed, > 1 OK sd->yval[spc][kk] = yvalx; } sd->mod[spc] = 1; // mark curve modified 20.0 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 success, sd is initialized from file data // returns 1 if fail (invalid file data), sd not modified int splcurve_load(spldat *sd, FILE *fid) { char *pfile, *pp, buff[300]; int nn, ii, jj, err, myfid = 0; int Nspc, fact[10], vert[10], nap[10]; float apx[10][50], apy[10][50]; if (! fid) // request file from user { pfile = zgetfile(E2X("load curve from a file"),MWIN,"file",saved_curves_folder); 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); if (! pp) goto fail; // 19.13 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,E2X("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(E2X("save curve to a file"),MWIN,"save",saved_curves_folder); 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]); // active flag, vert flag, anchors for (jj = 0; jj < sd->nap[ii]; jj++) // anchor point values fprintf(fid,"%.4f/%.4f ",sd->apx[ii][jj],sd->apy[ii][jj]); fprintf(fid,"\n"); } if (myfid) fclose(fid); return 0; } /******************************************************************************** edit transaction management edit_setup() get E0 if none, E0 > E1 > E3 edit_cancel() free (E1 E3 ER) edit_done() E3 > E0, free (E1 ER) add to undo stack edit_undo() E3 > ER, E1 > E3 edit_redo() ER > E3 edit_reset() free ER, E1 > E3 edit_fullsize() free (E1 E3) E0 > E1 > E3 ********************************************************************************* Setup for a new edit transaction Create E1 (edit input) and E3 (edit output) pixmaps from previous edit (E0) or image file (new E0). FprevReq 0 edit full-size image 1 edit preview image unless select area exists Farea 0 select_area is invalid and will be deleted (e.g. rotate) 1 select_area not used but remains valid (e.g. red-eye) 2 select_area can be used and remains valid (e.g. gamma) *********************************************************************************/ int edit_setup(editfunc &EF) { int yn, rww, rhh, ftype; int Fpreview; char *pp; if (! curr_file) return 0; // no image file if (! Fpxb) return 0; // not loaded yet 20.0 m_viewmode(0,"F"); // 19.0 ftype = image_file_type(curr_file); if (ftype != IMAGE && ftype != RAW) { // not editable pp = strrchr(curr_file,'/'); if (pp) pp++; zmessageACK(Mwin,E2X("File cannot be edited \n %s"),pp); return 0; } if (FGWM != 'F') m_viewmode(0,"F"); // insure file view mode if (checkpend("busy block")) return 0; // blocking function if (CEF && CEF->zd) // if pending edit, complete it zdialog_send_event(CEF->zd,"done"); if (checkpend("edit")) return 0; // failed (HDR etc.) if (URS_pos > maxedits-2) { // undo/redo stack capacity reached zmessageACK(Mwin,E2X("Too many edits, please save image")); return 0; } if (Fscriptbuild && ! EF.Fscript) { // this function not scriptable zmessageACK(Mwin,E2X("this function cannot be scripted")); return 0; } free_filemap(); // free map memory for edit usage sa_validate(); // delete area if not valid if (EF.Farea == 0 && sa_stat) { // select area will be lost, warn user yn = zmessageYN(Mwin,E2X("Select area cannot be kept.\n" "Continue?")); if (! yn) return 0; sa_clear(); // clear area if (zd_sela) zdialog_free(zd_sela); } if (EF.Farea == 1 && sa_stat) { // select area kept, not used 20.0 yn = zmessageYN(Mwin,E2X("Select area will be ignored. \n" "Continue?")); if (! yn) return 0; } if (EF.Farea == 2 && sa_stat && sa_stat != 3) { // select area exists and can be used, yn = zmessageYN(Mwin,E2X("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 (URS_pos == 0) save_undo(); // initial image >> undo/redo stack Fpreview = 0; // assume no preview if (EF.FprevReq && ! Fzoom) // preview requested by edit func. Fpreview = 1; if (EF.Farea == 2 && sa_stat == 3) // not if select area active Fpreview = 0; if (E0pxm->ww * E0pxm->hh < 2000000) // if image is small, don't use preview Fpreview = 0; if (E0pxm->ww < 1.4 * Dww && E0pxm->hh < 1.4 * Dhh) // if image slightly larger than window, Fpreview = 0; // don't use preview if (Fpreview) { if (Fpxb->ww * Dhh > Fpxb->hh * Dww) { // use preview image 1.4 * window size rww = 1.4 * Dww; if (rww < 1200) rww = 1200; // at least 1200 on one side rhh = 1.0 * rww * Fpxb->hh / Fpxb->ww + 0.5; } else { rhh = 1.4 * Dhh; if (rhh < 1200) rhh = 1200; rww = 1.0 * rhh * Fpxb->ww / Fpxb->hh + 0.5; } if (rww > Fpxb->ww) Fpreview = 0; } if (Fpreview) { E1pxm = PXM_rescale(E0pxm,rww,rhh); // scale image to preview size sa_show(0,0); // hide select area if present } else E1pxm = PXM_copy(E0pxm); // else use full size imagez E3pxm = PXM_copy(E1pxm); // E1 >> E3 CEF = &EF; // set current edit function CEF->Fmods = 0; // image not modified yet CEF->Fpreview = Fpreview; CEF->thread_command = CEF->thread_status = 0; // no thread running CEF->thread_pend = CEF->thread_done = CEF->thread_hiwater = 0; // no work pending or done if (CEF->threadfunc) start_thread(CEF->threadfunc,0); // start thread func if any Fpaintnow(); // update image synchronous return 1; } /********************************************************************************/ // print error message if CEF invalid int CEF_invalid() { if (CEF) return 0; printz("CEF invalid \n"); return 1; } /********************************************************************************/ // process edit cancel // keep: retain zdialog, mousefunc, curves void edit_cancel(int keep) { if (CEF_invalid()) return; wrapup_thread(9); // tell thread to quit, wait if (CEF_invalid()) return; PXM_free(E1pxm); // free E1 E3 ER E8 E9 PXM_free(E3pxm); PXM_free(ERpxm); PXM_free(E8pxm); PXM_free(E9pxm); if (! keep) { if (CEF->zd == zd_thread) zd_thread = 0; // thread -> zdialog event if (CEF->sd) zfree(CEF->sd); // free curves data if (CEF->mousefunc == mouseCBfunc) freeMouse(); // if my mouse, free mouse if (CEF->zd) zdialog_free(CEF->zd); // kill dialog } CEF = 0; // no current edit func Fpaintnow(); // update image synchronous return; } /********************************************************************************/ // process edit apply // save current edits (E3 image) in E1 image. // accumulated 'apply's are in one edit step. void edit_apply() // 20.0 { if (CEF_invalid()) return; wrapup_thread(8); // tell thread to finish, wait if (CEF_invalid()) return; PXM_free(E1pxm); // copy edits in E3 to source image E1 E1pxm = PXM_copy(E3pxm); Fpaintnow(); // update image synchronous return; } /********************************************************************************/ // process edit done // keep: retain zdialog, mousefunc, curves void edit_done(int keep) { if (CEF_invalid()) return; wrapup_thread(8); // tell thread to finish, wait if (CEF_invalid()) return; if (CEF->Fmods) { // image was modified PXM_free(E0pxm); E0pxm = E3pxm; // E3 updated image >> E0 E3pxm = 0; PXM_free(E1pxm); // free E1 ER E8 E9 PXM_free(ERpxm); PXM_free(E8pxm); PXM_free(E9pxm); URS_pos++; // next undo/redo stack position URS_max = URS_pos; // image modified - higher mods obsolete save_undo(); // save undo state (for following undo) if (Fscriptbuild) // edit script file in progress - edit_script_addfunc(CEF); // add edit function to script } else { // not modified PXM_free(E1pxm); // free E1 E3 ER E8 E9 PXM_free(E3pxm); PXM_free(ERpxm); PXM_free(E8pxm); PXM_free(E9pxm); } if (! keep) { if (CEF->zd == zd_thread) zd_thread = 0; // thread -> zdialog event if (CEF->sd) zfree(CEF->sd); // free curves data if (CEF->mousefunc == mouseCBfunc) freeMouse(); // if my mouse, free mouse if (CEF->zd) zdialog_free(CEF->zd); // kill dialog } CEF = 0; // no current edit func Fpaintnow(); // update image synchronous return; } /********************************************************************************/ // Convert from preview mode (window-size pixmaps) to full-size pixmaps. // Called by the edit function prior to edit_done(). void edit_fullsize() { if (CEF_invalid()) return; wait_thread_idle(); // wait for thread idle if (CEF_invalid()) return; if (! CEF->Fpreview) return; // FprevReq ignored if small image 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 E3 Fzoom = 0; CEF->Fpreview = 0; return; } // edit undo, redo, reset functions // these apply within an active edit function void edit_undo() { if (CEF_invalid()) return; wait_thread_idle(); // wait for thread idle if (CEF_invalid()) return; if (! CEF->Fmods) return; // not modified PXM_free(ERpxm); // E3 >> redo copy ERpxm = E3pxm; E3pxm = PXM_copy(E1pxm); // E1 >> E3 CEF->Fmods = 0; // reset image modified status if (CEF->zd) zdialog_send_event(CEF->zd,"undo"); // notify edit function 19.5 Fpaintnow(); // update image synchronous return; } void edit_redo() { if (CEF_invalid()) return; wait_thread_idle(); // wait for thread idle if (CEF_invalid()) return; if (! ERpxm) return; // no prior undo PXM_free(E3pxm); // redo copy >> E3 E3pxm = ERpxm; ERpxm = 0; CEF->Fmods = 1; // image modified if (CEF->zd) zdialog_send_event(CEF->zd,"redo"); // notify edit function 19.5 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 PXM_free(ERpxm); // delete redo copy PXM_free(E3pxm); E3pxm = PXM_copy(E1pxm); // E1 >> E3 CEF->Fmods = 0; // reset image modified status Fpaintnow(); // update image synchronous return; } /********************************************************************************/ // Load/save all function widget data from/to a file. // dirname for data files: /home//.fotoxx/funcname // where zdialog data is saved for the respective function. // return 0 = OK, +N = error int func_load_widgets(zdialog *zd, spldat *sd, cchar *funcname, FILE *fid) // 20.0 { using namespace zfuncs; cchar *mess = E2X("Load settings from file"); int myfid = 0; char *filename, dirname[200], buff[1000]; char *wname, *wdata, wdata2[1000]; char *pp, *pp1, *pp2; int ii, kk, err, cc1, cc2; if (! fid) // fid from script { snprintf(dirname,200,"%s/%s",get_zhomedir(),funcname); // folder 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 if (strstr("defcats deftags",wname)) continue; // 20.0 pp2 += 3; if (*pp2 == ' ') pp2++; wdata = pp2; // widget data cc2 = strlen(wdata); if (cc2 < 1) wdata = (char *) ""; if (cc2 > 1000) 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,E2X("file data does not fit dialog")); if (myfid) fclose(fid); return 1; } int func_save_widgets(zdialog *zd, spldat *sd, cchar *funcname, FILE *fid) // 20.0 { using namespace zfuncs; cchar *mess = E2X("Save settings to a file"); int myfid = 0; char *filename, dirname[200]; char *wtype, *wname, *wdata, wdata2[1000]; int ii, cc; cchar *editwidgets = "entry zentry edit text togbutt check combo comboE " // widget types to save "radio spin zspin hscale vscale colorbutt"; if (! fid) // fid from script { snprintf(dirname,200,"%s/%s",get_zhomedir(),funcname); // folder 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: if (strstr("defcats deftags",wname)) continue; // 20.0 wdata = zd->widget[ii].data; // widgetname == widgetdata if (! wdata) continue; cc = strlen(wdata); if (cc > 900) continue; repl_1str(wdata,wdata2,"\n","\\n"); // replace newline with "\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 function dialogs // load or save last-used widgets int func_load_prev_widgets(zdialog *zd, spldat *sd, cchar *funcname) // 20.0 { using namespace zfuncs; char filename[200]; FILE *fid; int err; snprintf(filename,200,"%s/%s/%s",get_zhomedir(),funcname,"last-used"); fid = fopen(filename,"r"); if (! fid) { zmessageACK(Mwin,"%s \n %s",filename,strerror(errno)); return 1; } err = func_load_widgets(zd,sd,funcname,fid); fclose(fid); return err; } int func_save_last_widgets(zdialog *zd, spldat *sd, cchar *funcname) // 20.0 { using namespace zfuncs; char filename[200], dirname[200]; FILE *fid; int err; snprintf(filename,200,"%s/%s/%s",get_zhomedir(),funcname,"last-used"); fid = fopen(filename,"w"); if (! fid) { snprintf(dirname,200,"%s/%s",get_zhomedir(),funcname); // create missing folder err = mkdir(dirname,0760); if (err) { printz("%s \n %s \n",dirname,strerror(errno)); return 1; } fid = fopen(filename,"w"); // open again } if (! fid) { printz("%s \n %s \n",filename,strerror(errno)); return 1; } err = func_save_widgets(zd,sd,funcname,fid); fclose(fid); return err; } /******************************************************************************** undo / redo menu buttons *********************************************************************************/ // [undo/redo] menu function // Call m_undo() / m_redo() if left / right mouse click on menu. // If A key is pressed, call undo_all() or redo_all(). // If middle mouse button is clicked, pop-up a list of all edit steps // and choose a step to go back to. void m_undo_redo(GtkWidget *, cchar *) { void undo_redo_choice(GtkWidget *, cchar *menu); GtkWidget *popmenu; int button = zfuncs::vmenuclickbutton; char menuitem[40], flag; F1_help_topic = "undo/redo button"; if (! curr_file) return; // 19.0 if (FGWM != 'F') return; if (button == 1) { if (KBkey == GDK_KEY_a) undo_all(); // undo all steps else m_undo(0,0); // undo one step } if (button == 2) // go back to selected edit step { if (URS_max == 0) return; popmenu = create_popmenu(); for (int ii = 0; ii < 30; ii++) { // insert all edit steps if (ii > URS_max) break; if (ii == URS_pos) flag = '*'; // flag step matching current status else flag = ' '; snprintf(menuitem,40,"%d %s %c",ii,URS_funcs[ii],flag); add_popmenu_item(popmenu,menuitem,undo_redo_choice,(char *) &Nval[ii],0); } popup_menu(Mwin,popmenu); } if (button == 3) { if (KBkey == GDK_KEY_a) redo_all(); // redo all steps else m_redo(0,0); // redo one step } return; } // popup menu response function void undo_redo_choice(GtkWidget *, cchar *menu) { int nn = *((int *) menu); if (nn < 0 || nn > URS_max) return; URS_pos = nn; load_undo(); return; } // [undo] menu function - reinstate previous edit in undo/redo stack void m_undo(GtkWidget *, cchar *) { if (! curr_file) return; // 19.0 if (FGWM != 'F') return; 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 (! curr_file) return; // 19.0 if (FGWM != 'F') return; 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 (! curr_file) return; // 19.0 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 (! curr_file) return; // 19.0 if (FGWM != 'F') return; if (CEF) return; // not if edit active if (URS_pos == URS_max) return; URS_pos = URS_max;; // image with last edit applied load_undo(); return; } // Save E0 to undo/redo file stack // stack position = URS_pos void save_undo() // overhauled for 4 GB files { char *pp, buff[24]; FILE *fid; int ww, hh, nc, nn; uint cc1, cc2; uint ccmax = 128 * MEGA; ww = E0pxm->ww; hh = E0pxm->hh; nc = E0pxm->nc; pp = strstr(URS_filename,"undo_"); // get undo/redo stack filename to use if (! pp) zappcrash("undo/redo stack corrupted"); snprintf(pp+5,3,"%02d",URS_pos); fid = fopen(URS_filename,"w"); if (! fid) goto writefail; snprintf(buff,24,"fotoxx %05d %05d %d",ww,hh,nc); // write header nn = fwrite(buff,20,1,fid); if (nn != 1) goto writefail; cc1 = ww * hh * nc * sizeof(float); // write ww * hh RGB(A) pixels cc2 = 0; while (cc1) { if (cc1 <= ccmax) { pp = (char *) E0pxm->pixels; nn = fwrite(pp+cc2,cc1,1,fid); if (nn != 1) goto writefail; break; } else { pp = (char *) E0pxm->pixels; nn = fwrite(pp+cc2,ccmax,1,fid); if (nn != 1) goto writefail; cc1 -= ccmax; cc2 += ccmax; } } fclose(fid); if (URS_pos == 0) { // stack posn. 0 = original image strcpy(URS_funcs[0],"original"); // function name for original image URS_saved[0] = 1; // original image already on disk } else { // stack position if (CEF_invalid()) return; // must have an edit function strncpy0(URS_funcs[URS_pos],CEF->funcname,32); // edit function name URS_saved[URS_pos] = 0; // not yet saved to disk } return; writefail: zmessageACK(Mwin,"undo/redo stack write failure: %d \n" "(may be out of disk space)",errno); zexit("undo/redo stack write failure: %d",errno); } // Load E0 from undo/redo file stack // stack position = URS_pos void load_undo() // overhauled { char *pp, buff[24]; FILE *fid; int ww, hh, nc, nn; uint cc1, cc2; uint ccmax = 128 * MEGA; // reduced from 256 pp = strstr(URS_filename,"undo_"); if (! pp) goto err1; snprintf(pp+5,3,"%02d",URS_pos); fid = fopen(URS_filename,"r"); if (! fid) goto err2; nn = fread(buff,20,1,fid); if (nn != 1) goto err3; buff[20] = 0; // null at end of data bugfix 19.0 nn = sscanf(buff,"fotoxx %d %d %d",&ww,&hh,&nc); if (nn != 3) goto err4; PXM_free(E0pxm); E0pxm = PXM_make(ww,hh,nc); cc1 = ww * hh * nc * sizeof(float); // bytes to read cc2 = 0; // bytes done while (cc1) { if (cc1 <= ccmax) { pp = (char *) E0pxm->pixels; // read entire (remaining) file nn = fread(pp+cc2,cc1,1,fid); if (nn != 1) goto err3; break; } else { pp = (char *) E0pxm->pixels; // read max. part of file nn = fread(pp+cc2,ccmax,1,fid); if (nn != 1) goto err3; cc1 -= ccmax; cc2 += ccmax; } } fclose(fid); sa_validate(); // delete area if invalid if (Cdrawin) Fpaintnow(); // update image synchronous return; err1: printz("err1: %s \n",URS_filename); // extended diagnostics goto readfail; err2: printz("err2: open() failure, errno: %d %s \n",errno,strerror(errno)); goto readfail; err3: printz("err3: fread() failure, errno: %d %s \n",errno,strerror(errno)); goto readfail; err4: printz("err4: %s \n",buff); goto readfail; readfail: zmessageACK(Mwin,"undo/redo stack read failure"); zexit("undo/redo stack read failure"); } /********************************************************************************/ // check for busy or pending conditions and message the user // returns 1 if busy or pending condition // 0 if nothing is pending or user decides to discard mods // conditions: // edit edit function is active (CEF not null) // mods current file has unsaved mods (image or metadata edits) // busy function is still running // block edit function mutex flag // all check all the above // quiet suppress user message int checkpend(cchar *list) { int edit, mods, busy, block, all, quiet, pend, choice; cchar *modmess = E2X("This action will discard changes to current image"); cchar *activemess = E2X("prior function still active"); cchar *keep = E2X("Keep"); cchar *discard = E2X("Discard"); if (Fmenublock) return 1; // menus blocked 20.0 edit = mods = busy = block = all = quiet = pend = 0; if (strstr(list,"edit")) edit = 1; if (strstr(list,"mods")) mods = 1; if (strstr(list,"busy")) busy = 1; if (strstr(list,"block")) block = 1; if (strstr(list,"all")) all = 1; if (strstr(list,"quiet")) quiet = 1; if (all) edit = mods = busy = block = 1; if ((edit && CEF) || (busy && (Ffuncbusy || Fthreadbusy)) || (block && Fblock)) { pend = 1; if (! quiet) zmessage_post_bold(Mwin,"20/20",2,activemess); } if (! pend && mods) { if (CEF && CEF->Fmods && ! CEF->Fsaved) pend = 1; // active edits unsaved if (URS_pos > 0 && URS_saved[URS_pos] == 0) pend = 1; // completed edits unsaved if (Fmetamod) pend = 1; // metadata edits unsaved if (pend && ! quiet) { choice = zdialog_choose(Mwin,"mouse",modmess,keep,discard,null); // ask user if (choice == 2) { // choice is discard if (CEF && CEF->zd) zdialog_send_event(CEF->zd,"cancel"); if (URS_pos > 0) undo_all(); // undo prior edits Fmetamod = 0; // discard metadata edits pend = 0; } else m_viewmode(0,"F"); // keep - back to current image } } return pend; // 1 if something pending } /********************************************************************************/ // zdialog mouse capture and release void takeMouse(mcbFunc func, GdkCursor *cursor) // capture mouse for dialog { if (! Cdrawin) return; if (! gdkwin) return; freeMouse(); mouseCBfunc = func; Mcapture = 1; gdk_window_set_cursor(gdkwin,cursor); return; } void freeMouse() // free mouse for main window { if (! Cdrawin) return; if (! gdkwin) return; if (! Mcapture) return; mouseCBfunc = 0; Mcapture = 0; gdk_window_set_cursor(gdkwin,0); // set normal cursor return; } /******************************************************************************** functions to manage working threads main level thread management start_thread(func,arg) start thread running signal_thread() signal thread that work is pending wait_thread_idle() wait for pending work complete wrapup_thread(command) wait for exit or command thread exit thread function thread_idle_loop() wait for pending work, exit if commanded thread_exit() exit thread unconditionally thread_status (thread ownership 0 no thread is running 1 thread is running and idle (no work) 2 thread is working 0 thread has exited thread_command (main program ownership) 0 idle, initial status 8 exit when pending work is done 9 exit now, unconditionally thread_pend work requested counter thread_done work done counter thread_hiwater high water mark *********************************************************************************/ // start thread that does the edit work void start_thread(threadfunc func, void *arg) { CEF->thread_status = 1; // thread is running CEF->thread_command = CEF->thread_pend = CEF->thread_done = CEF->thread_hiwater = 0; // nothing pending start_detached_thread(func,arg); return; } // signal thread that new work is pending void signal_thread() { if (CEF_invalid()) return; if (CEF->thread_status > 0) CEF->thread_pend++; return; } // wait for edit thread to complete pending work and become idle void wait_thread_idle() { if (CEF_invalid()) return; if (! CEF->thread_status) return; if (CEF->thread_pend == CEF->thread_done) return; while (CEF->thread_status && CEF->thread_pend > CEF->thread_done) { zmainsleep(0.01); // 19.0 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) { // wait for thread to finish zmainsleep(0.01); // 19.0 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 in thread } // exit thread unconditionally, called only from edit threads void thread_exit() { 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) // threadfunc: void * func(void *index) // start Nt threads and wait for all to exit void do_wthreads(threadfunc func, int Nt) // 19.0 { pthread_t tid[max_threads]; zadd_locked(Fthreadbusy,+1); for (int ii = 0; ii < Nt; ii++) tid[ii] = start_Jthread(func, &Nval[ii]); for (int ii = 0; ii < Nt; ii++) wait_Jthread(tid[ii]); zadd_locked(Fthreadbusy,-1); return; } /********************************************************************************/ // table for loading and saving adjustable parameters between sessions typedef struct { char name[20]; char type[12]; int count; void *location; } param; #define Nparms 56 param paramTab[Nparms] = { // name type count location { "fotoxx release", "char", 1, &Prelease }, { "locale", "char", 1, &locale }, { "first time", "int", 1, &Ffirsttime }, { "window geometry", "int", 4, &mwgeom }, { "thumbnail size", "int", 1, &navi::thumbsize }, { "menu style", "char", 1, &menu_style }, { "icon size", "int", 1, &iconsize }, { "F-base-color", "int", 3, &FBrgb }, { "G-base-color", "int", 3, &GBrgb }, { "menu font color", "int", 3, &MFrgb }, { "menu background", "int", 3, &MBrgb }, { "index level", "int", 1, &Findexlev }, { "FM index level", "int", 1, &FMindexlev }, { "dialog font", "char", 1, &dialog_font }, { "drag option", "int", 1, &Fdragopt }, { "zoom count", "int", 1, &zoomcount }, { "map_dotsize", "int", 1, &map_dotsize }, { "last version", "int", 1, &Flastversion }, { "shift right", "int", 1, &Fshiftright }, { "curve node dist %", "float", 1, &splcurve_minx }, { "start display", "char", 1, &startdisplay }, { "start album", "char", 1, &startalbum }, { "start image file", "char", 1, &startfile }, { "start folder", "char", 1, &startfolder }, { "curr file", "char", 1, &curr_file }, { "galleryname", "char", 1, &navi::galleryname }, { "gallerytype", "int", 1, &navi::gallerytype }, { "current album", "char", 1, &curr_album }, { "copy/move loc", "char", 1, ©move_loc }, { "RGB chooser file", "char", 1, &RGB_chooser_file }, { "netmap source", "char", 1, &netmap_source }, { "mapbox access key", "char", 1, &mapbox_access_key }, { "grid base", "int", 10, &gridsettings[0] }, { "grid trim/rotate", "int", 10, &gridsettings[1] }, { "grid unbend", "int", 10, &gridsettings[2] }, { "grid warp curved", "int", 10, &gridsettings[3] }, { "grid warp linear", "int", 10, &gridsettings[4] }, { "grid warp affine", "int", 10, &gridsettings[5] }, { "RAW file types", "char", 1, &RAWfiletypes }, { "video file types", "char", 1, &VIDEOfiletypes }, { "trim sizes", "char", 10, &trimsizes }, // 20.0 { "video command", "char", 1, &video_command }, // 20.0 { "trim buttons", "char", 5, &trimbuttons }, // 19.0 { "trim ratios", "char", 5, &trimratios }, { "edit resize", "int", 2, &editresize }, { "jpeg def quality", "int", 1, &jpeg_def_quality }, { "tiff comp method", "int", 1, &tiff_comp_method }, // 20.0 { "png comp level", "int", 1, &png_comp_level }, // 20.0 { "RAW file loader", "int", 1, &Frawloader }, // 20.0 { "RAW auto brighten", "int", 1, &Fautobright }, // 20.0 { "RAW match thumb", "int", 1, &Fmatchthumb }, // 20.0 { "lens mm", "float", 1, &lens_mm }, { "SS KB keys", "char", 1, &ss_KBkeys }, { "line color", "int", 3, &LINE_COLOR }, // 19.0 { "Fcaptions", "int", 1, &Fcaptions }, // 20.0 { "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; snprintf(buff,199,"%s/parameters",get_zhomedir()); // open output file fid = fopen(buff,"w"); if (! fid) return; for (int ii = 0; ii < Nparms; ii++) // write table of state data { name = paramTab[ii].name; type = paramTab[ii].type; count = paramTab[ii].count; location = paramTab[ii].location; charloc = (char **) location; intloc = (int *) location; floatloc = (float *) location; fprintf(fid,"%-20s %-8s %02d ",name,type,count); // write parm name, type, count for (int kk = 0; kk < count; kk++) // write "value" "value" ... { if (strmatch(type,"char")) { if (! *charloc) *text = 0; // missing, use "" 20.0 else repl_1str(*charloc++,text,"\n","\\n"); // replace newlines with "\n" fprintf(fid," \"%s\"",text); } if (strmatch(type,"int")) fprintf(fid," \"%d\"",*intloc++); if (strmatch(type,"float")) fprintf(fid," \"%.2f\"",*floatloc++); } fprintf(fid,"\n"); // write EOR } fprintf(fid,"\n"); fclose(fid); // close file return; } // load parameters from file /.../.fotoxx/parameters void load_params() { FILE *fid; int ii, err, pcount; int Idata; float Fdata; char buff[1000], text[1000], *pp; char name[20], type[12], count[8], data[1000]; void *location; char **charloc; int *intloc; float *floatloc; snprintf(buff,199,"%s/parameters",get_zhomedir()); // open parameters file fid = fopen(buff,"r"); if (! fid) return; // none, defaults are used while (true) // read parameters { pp = fgets_trim(buff,999,fid,1); if (! pp) break; if (*pp == '#') continue; // comment if (strlen(pp) < 40) continue; // rubbish err = 0; strncpy0(name,pp,20); // parm name strTrim2(name); strncpy0(type,pp+22,8); // parm type strTrim2(type); strncpy0(count,pp+32,4); // parm count strTrim2(count); err = convSI(count,pcount); strncpy0(data,pp+38,1000); // parm value(s) strTrim2(data); for (ii = 0; ii < Nparms; ii++) // match file record to param table { if (! strmatch(name,paramTab[ii].name)) continue; // parm name if (! strmatch(type,paramTab[ii].type)) continue; // parm type if (paramTab[ii].count != pcount) continue; // parm count break; } if (ii == Nparms) continue; // not found, ignore file param location = paramTab[ii].location; // get parameter memory location charloc = (char **) location; intloc = (int *) location; floatloc = (float *) location; for (ii = 1; ii <= pcount; ii++) // get parameter value(s) { if (strmatch(type,"char") && pcount == 1) { // one quoted string with possible pp = strrchr(data,'"'); // embedded blanks and quotes if (pp) *pp = 0; pp = data + 1; if (strlen(pp) == 0) break; repl_1str(pp,text,"\\n","\n"); // replace "\n" with real newlines *charloc++ = zstrdup(text); break; } 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); } if (curr_file && ! *curr_file) curr_file = 0; // no current file >> null 20.0 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 (! fkeepundo) shell_quiet("rm -f %s/undo_*",temp_folder); // remove undo/redo files if (Fshutdown) return; // stop here if shutdown mode URS_pos = URS_max = 0; // reset undo/redo stack Fmetamod = 0; // no unsaved metadata changes sa_clear(); // clear select area Nptoplines = Ntoplines = 0; // no toplines Ntopcircles = 0; // no topcircles Ntoptext = 0; // no toptext Fbusy_goal = 0; // not busy if (curr_file) { if (zd_sela) zdialog_free(zd_sela); // 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 PXB_free(Fpxb); PXM_free(E0pxm); PXM_free(E1pxm); PXM_free(E3pxm); PXM_free(ERpxm); PXM_free(E8pxm); PXM_free(E9pxm); return; } fotoxx-20.08/fotoxx.desktop000066400000000000000000000005041362435004500157750ustar00rootroot00000000000000[Desktop Entry] Name=Fotoxx GenericName=Photo Editor Comment=Edit photos and manage collection Categories=Graphics;Photography; Keywords=photo;image;raw;edit;metadata;album; Type=Application Terminal=false MimeType=image/bmp;image/gif;image/tiff;image/jpeg;image/png; Exec=fotoxx %f Icon=/usr/share/fotoxx/icons/fotoxx.png fotoxx-20.08/fotoxx.h000066400000000000000000003563331362435004500145710ustar00rootroot00000000000000/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. *********************************************************************************/ #include #include #include #include "jpeglib.h" #include #include #include #include "zfuncs.h" // Fotoxx definitions #define Frelease "fotoxx-20.08" // release Feb 19 2020 #define Flicense "Free software - GNU General Public License v.3" #define Fhomepage "https://kornelix.net" // kornelix home page #define Fgitlab "https://gitlab.com/fotoxx" // GitLab home 20.0 #define Fcontact "mkornelix@gmail.com" #define Ftranslators "Translators: \n" \ " Doriano Blengino, André Campos Rodovalho, \n" \ " J. A. Miralles Puignau, Xavier Ribes, Erich Küster " #define MEGA (1024 * 1024) // 1 million as 2**20 #define FMEGA (1.0 * MEGA) #define PI 3.141592654 #define RAD (180.0/PI) #define PXMpix(PXM,px,py) (PXM->pixels+(py)*(PXM->rs)+(px)*(PXM->nc)) // PXM and PXB pixel (RGB/RGBA nc = 3/4) #define PXBpix(PXB,px,py) (PXB->pixels+(py)*(PXB->rs)+(px)*(PXB->nc)) // e.g. red = PXMpix(...)[0] #define pixbright(pix) (0.25*(pix)[0]+0.65*(pix)[1]+0.10*(pix)[2]) // overall brightness 0-255 // color match function for integer/float RGB colors 0-255 // returns 0.0 to 1.0 = perfect match #define RGBMATCH(r1,g1,b1,r2,g2,b2) \ 0.0000000596 * (256.0 - fabsf(float(r1-r2))) \ * (256.0 - fabsf(float(g1-g2))) \ * (256.0 - fabsf(float(b1-b2))) #define PIXMATCH(pix1,pix2) \ (RGBMATCH(pix1[0],pix1[1],pix1[2],pix2[0],pix2[1],pix2[2])) // compile time limits that could be increased #define thumbfilesize 512 // thumbnail file pixel size #define wwhh_limit1 30000 // max. image width or height #define wwhh_limit2 (512*MEGA) // max. image width x height (8 GB) 19.0 #define max_threads 8 // max. threads (hyperthreads useless) #define maxtopfolders 200 // max. no. of top image folders #define indexrecl 2000 // max. image index rec. (>XFCC >tagFcc) #define maximages 1000000 // max. image files supported #define maxgallery 40000 // max. gallery files supported 20.0 #define maxtagcats 200 // max tag categories #define tagcc 50 // max cc for one tag or category ID #define tagFcc 1000 // max tag cc for one image file #define maxtags 100000 // max tags and tags/category #define tagGcc 200000 // max tag cc for one category #define tagMcc 1000 // max tag cc for batch add tags #define tagScc 500 // max tag cc for search tags #define tagRcc 300 // max tag cc for recent tags #define GSmax 100000 // max files for gallery_select() #define Mxmeta 20 // max extra indexed metadata keys // EXIF/IPTC keys for embedded image metadata #define iptc_keywords_key "Keywords" // keywords (tags) #define iptc_rating_key "Rating" // star rating #define exif_ww_key "ImageWidth" // image width and height 19.15 #define exif_hh_key "ImageHeight" // (replace exif_wwhh_key) #define exif_date_key "DateTimeOriginal" // photo date/time #define exif_orientation_key "Orientation" // orientation #define exif_rollangle_key "RollAngle" // roll angle - minor level error #define exif_editlog_key "ImageHistory" // edit history log #define exif_comment_key "Comment" // image comment #define exif_usercomment_key "UserComment" // image comment #define iptc_caption_key "Caption-Abstract" // image caption #define exif_copyright_key "Copyright" // image copyright #define exif_focal_length_35_key "FocalLengthIn35mmFormat" // focal length, 35mm equivalent 19.16 #define exif_focal_length_key "FocalLength" // focal length, real 19.16 #define exif_city_key "City" // city/location name (geotags) #define exif_country_key "Country" // country name #define exif_lati_key "GPSLatitude" // latitude in degrees (-180 to +180) #define exif_longi_key "GPSLongitude" // longitude in degrees (-180 to +180) #define exif_colorprof1_key "ICCProfileName" // ICC color profile name, e.g. "sRGB" #define exif_colorprof2_key "ICC_Profile" // embedded color profile data #define exif_maxcc 2000 // max. cc for exif/iptc text // GTK/GDK shortcuts #define TEXTWIN GTK_TEXT_WINDOW_TEXT #define NODITHER GDK_RGB_DITHER_NONE #define ALWAYS GTK_POLICY_ALWAYS #define NEVER GTK_POLICY_NEVER #define AUTO GTK_POLICY_AUTOMATIC #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 cchar *build_date_time; // build date and time 19.0 extern char *appimagexe; // appimage executable path name 19.0 extern char *progexe; // executable path name 19.0 extern timeb startime; // fotoxx startup time extern int monitor_ww, monitor_hh; // monitor pixel dimensions extern int appfontsize; // app font size extern char zappname[40]; // app name/version extern char zlocale[8]; // current language lc_RC extern char zimagedir[200]; // where application image files 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 zdialog *zdialog_list[zdialog_max]; // active zdialog list extern pthread_t tid_main; // main() thread ID } enum FTYPE { IMAGE, RAW, VIDEO, FDIR, THUMB, OTHER, FNF }; // file types, FNF = file not found enum GTYPE { TNONE, GDIR, SEARCH, META, RECENT, NEWEST, ALBUM }; // gallery types enum GSORT { SNONE, FNAME, FDATE, PDATE, FSIZE, PSIZE }; // gallery sort types 19.0 enum GSEQ { QNONE, ASCEND, DESCEND }; // gallery sort sequence struct GFlist_t { // current gallery file list in memory char *file; // /folder.../filename char fdate[16]; // file date: yyyymmddhhmmss char pdate[16]; // photo date: yyyymmddhhmmss int fsize; // file size, bytes 19.0 int psize; // image size, pixels 19.0 int mdindex; // index to metadata list Gmdlist[*] }; // externals from f.gallery namespace navi { // gallery() data extern GFlist_t *GFlist; // gallery file data table extern char **Gmdlist; // corresp. metadata list extern int Gmdrows; // text rows in metadata list extern int Nfiles; // gallery file count (images + subdirs) extern int Nfolders; // gallery subfolder count extern int Nimages; // gallery image file count extern char *galleryname; // folder path or gallery type name extern GTYPE gallerytype; // gallery type: folder, recent, etc. extern GSORT gallerysort; // filename/file-date/photo-date extern GSEQ galleryseq; // ascending/descending extern int gallerypainted; // gallery paint is complete extern int galleryposn; // gallery target scroll position extern int scrollposn; // current scroll position extern int xwinW, xwinH; // image gallery window size extern int thumbsize; // curr. thumbnail display size extern int genthumbs; // counts thumbnails generated extern double thumbcache_MB; // thumbnail cache size, MB int gallery_paint(GtkWidget *, cairo_t *); // gallery window paint function void menufuncx(GtkWidget *, cchar *menu); // gallery menu buttons function void changefolder(GtkWidget *widget, GdkEventButton *event); // gallery folder change function void newtop(GtkWidget *widget, GdkEventButton *event); // gallery change top folder function void newalbum(GtkWidget *widget, GdkEventButton *event); // gallery change album function int mouse_event(GtkWidget *, GdkEvent *, void *); // gallery window mouse event function void gallery_sort(); // gallery choose sort order and sort char * gallery_dragfile(); // gallery window file drag function void gallery_dropfile(int mx, int my, char *file); // gallery window file drop function int KBaction(cchar *action); // gallery window KB key function } EX int GScount; // gallery_select(), file count EX char *GSfiles[GSmax]; // gallery_select(), selected files // GTK etc. parameters EX GtkWidget *Mwin, *MWhbox, *MWvbox, *MWmenu; // main window containers EX GtkWidget *Fhbox, *Fvbox, *Fpanel, *Fpanlab, *Fdrawin; // F window widgets, file view EX GtkWidget *Ghbox, *Gvbox, *Gsep, *Gpanel, *Gtop, *Galbum; // G window widgets, gallery view EX GtkWidget *Gscroll, *Gdrawin; EX GtkAdjustment *Gadjust; EX GtkWidget *Mhbox, *Mvbox; // M window widgets, net map view EX GtkWidget *Whbox, *Wvbox, *Wdrawin; // W window widgets, world map view EX GtkWidget *Cdrawin; // curr. drawing window, Fdrawin/Wdrawin EX GdkWindow *gdkwin; // corresp. GDK window EX PIXBUF *BGpixbuf; // window background pixbuf EX GdkCursor *arrowcursor; // main window cursors EX GdkCursor *dragcursor; EX GdkCursor *drawcursor; EX GdkCursor *blankcursor; EX GdkCursor *dotcursor; EX int BLACK[3], WHITE[3], RED[3], GREEN[3], BLUE[3]; // RGB values 0-255 EX int LINE_COLOR[3]; // line drawing color, one of the above EX draw_context_t draw_context; // GDK window drawing context // user preferences and settings EX char *locale; // language and region setting EX char *menu_style; // menu style: icons/text/both EX int FBrgb[3]; // F view background color, RGB 0-255 EX int GBrgb[3]; // G view background color, RGB 0-255 EX int MFrgb[3]; // menu font color, RGB 0-255 EX int MBrgb[3]; // menu background color, RGB 0-255 EX int iconsize; // menu icon size EX char *dialog_font; // dialog font e.g. "sans 10" EX char *startalbum; // start album: file name EX char *startdisplay; // start view: recent/prev/blank/file/folder EX char *startfolder; // start folder (startdisplay=folder) EX char *startfile; // start image file (startdisplay=file) EX int Fdragopt; // 1/2 = 1x drag / magnified scroll EX int Fshowhidden; // show hidden folders in gallery view EX int Flastversion; // prev/next button gets last versions only EX int Fshiftright; // shift image to right margin EX int zoomcount; // zoom count to reach 2x (1-8) EX float zoomratio; // corresp. zoom ratio: 2**(1/zoomcount) EX int map_dotsize; // map dot size / mouse capture size EX int jpeg_def_quality; // default jpeg save quality EX int tiff_comp_method; // TIFF file compression method 20.0 EX int png_comp_level; // PNG file compression level 20.0 EX cchar *ownertran, *grouptran, *othertran; // translations: owner, group, other EX cchar *RWtran, *ROtran, *NOtran; // " for read+write, read only, no access EX int Frawloader; // RAW file loader, 1/2 = dcraw/RT 20.0 EX int Fautobright; // RAW loader auto brighten on/off 20.0 EX int Fmatchthumb; // RAW loader match thumb color on/off 20.0 EX char *RAWfiletypes; // recognized RAW files: .raw .rw2 ... EX char *myRAWtypes; // RAW types encountered EX char *VIDEOfiletypes; // recognized video files: .mov .mp4 ... EX char *myVIDEOtypes; // VIDEO types encountered #define CCC (XFCC*2+200) // max. command line size EX char command[CCC]; // (command, parameters, 2 filespecs) // menu name and function table typedef void menufunc_t(GtkWidget *,cchar *); // menu function type struct menutab_t { GtkWidget *topmenu; // parent menu cchar *menu; // menu text cchar *icon; // menu icon cchar *desc; // menu short description (tooltip) menufunc_t *func; // menu function (GtkWidget *, cchar *) cchar *arg; // argument }; #define maxmenus 250 EX menutab_t menutab[maxmenus]; EX int Nmenus; // KB shortcut tables struct kbsftab_t { // eligible menus for KB shortcut cchar *menu; // menu name menufunc_t *func; // function cchar *arg; // argument }; #define maxkbsf 100 EX kbsftab_t kbsftab[maxkbsf]; EX int Nkbsf; struct kbsutab_t { // user-defined shortcuts char *key; // key name, e.g. "Shift+U" char *menu; // menu name, e.g. "Redo" }; #define maxkbsu 50 EX kbsutab_t kbsutab[maxkbsu]; EX int Nkbsu; // general parameters EX char desktopname[100]; // locale specific desktop folder EX char *Prelease; // prior fotoxx version EX int Ffirsttime; // first time startup EX int mwgeom[4]; // main window position and size EX pid_t fotoxxPID; // my process PID 19.0 EX int NWT; // working threads to use EX int Nval[100]; // static integer values 0-99 EX char FGWM; // curr. view mode: 'F' 'G' 'W' 'M' EX char PFGWM; // prior view mode EX int Frawtherapee; // flag, Raw Therapee installed EX int Frawtherapeecli; // flag, " " (commind line) installed 20.0 EX int Fheif; // flag, heif-convert installed 20.0 EX int Fjp2; // flag, opj_decompress installed 20.0 EX int Fgrowisofs; // flag, growisofs available EX int PTtools; // flag, pano tools available EX int Ffmpeg; // flag, ffmpeg available EX int Faddr2line; // flag, addr2line available EX int Ftinycomputer; // flag, memory too little for Fotoxx 19.20 EX int Fshutdown; // app shutdown underway EX int Fdebug; // debug flag EX int Findexlev; // 0/1/2 = none/old/old+new image files EX int FMindexlev; // Findexlev if start via file manager EX int Pindexlev; // Findexlev if command parameter EX char *xmeta_keys[Mxmeta]; // indexed metadata names (compressed) EX int Fmenublock; // menus blocked 20.0 EX int Fkillfunc; // flag, running function should quit EX int Fpaintlock; // flag, window updates locked 19.0 EX int Fpaintrequest; // window paint request pending 19.0 EX int Fmetamod; // image metadata unsaved changes EX int Fblock; // edit functions mutex flag EX int Ffuncbusy; // function is busy/working 19.0 EX int Fthreadbusy; // thread is busy/working 19.0 EX int Ffullscreen; // flag, window is fullscreen EX int Fpanelshow; // show or hide F view top panel EX int Fmashup; // flag, mashup function is active EX int Fslideshow; // flag, slide show is active EX int Fview360; // flag, view360 function is active EX int Fescape; // KB escape was pressed 19.0 EX char *ss_KBkeys; // slide show KB keys, packed EX int Frecent; // start with recent files gallery EX int Fnew; // start with newly added files gallery EX int Fprev; // start with previous file EX int Fblank; // start with blank window EX char *topmenu; // latest top-menu selection EX char *commandmenu; // command line menu function EX char *commandparam; // command line parameter 20.0 EX char *commandalbum; // command line album gallery EX char *video_command; // command to show video file 20.0 EX cchar *F1_help_topic; // current function help topic EX int Fcaptions; // show image captions/comments EX char *netmap_source; // net map source EX char *mapbox_access_key; // mapbox map source access key EX char *topfolders[maxtopfolders]; // user top-level image folders EX int Ntopfolders; // topfolders[] count EX char *misstops[maxtopfolders]; // missing top folders (not mounted) 20.0 EX int Nmisstops; // missing top folders count EX char *thumbfolder; // thumbnails folder EX char *initial_file; // initial file on command line EX char *curr_file; // current image file EX char curr_file_type[8]; // jpg / tif / png / other EX int curr_file_bpc; // image file bits/color, 8/16 EX int curr_file_size; // image file size on disk EX int curr_file_posn; // position in current gallery, 0-last EX int last_file_posn; // remember when curr. file deleted EX int curr_file_count; // count of images in current set EX char *curr_album; // current or last used album 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 *imagefiletypes; // supported image types: .jpg .png etc. EX char *colormapfile; // curr. printer color map file EX char *copymove_loc; // copy/move folder last used EX char *RGB_chooser_file; // user's color chooser image file EX int Fscriptbuild; // edit script build is in-progress EX char scriptfile[200]; // current script file name EX FILE *script_fid; // current script file FID EX int Fscriptrun; // flag, script file is active 20.0 EX char f_load_type[8]; // data set by PXB/PXM_load() 19.0 EX int f_load_bpc; EX int f_load_size; EX char *f_save_file; // data set by PXB/PXM_save() EX char f_save_type[8]; EX int f_save_bpc; EX uint f_save_size; EX VOL double Fbusy_goal, Fbusy_done; // BUSY message progress tracking EX char paneltext[200]; // top panel application text // files and folders in /home//.fotoxx/ EX char index_folder[200]; // image index folder EX char index_file[200]; // image index file 19.0 EX char temp_folder[200]; // temp. files folder EX char tags_defined_file[200]; // tags defined file EX char recentfiles_file[200]; // file of recent image files EX char albums_folder[200]; // folder for saved image albums EX char gallerymem_file[200]; // file for recent galleries memory EX char saved_areas_folder[200]; // folder for saved select area files EX char saved_curves_folder[200]; // folder for saved curve data EX char retouch_folder[200]; // folder for saved retouch settings EX char custom_kernel_folder[200]; // folder for saved custom kernel data EX char drawtext_folder[200]; // folder for m_draw_text files EX char favorites_folder[200]; // folder for favorites menu EX char mashup_folder[200]; // folder for mashup projects EX char KBshortcuts[200]; // keyboard shortcuts file EX char slideshow_folder[200]; // folder for slide show files EX char slideshow_trans_folder[200]; // folder for slide show transition files EX char pattern_folder[200]; // folder for pattern files EX char printer_color_folder[200]; // folder for printer calibration EX char scripts_folder[200]; // folder for script files EX char search_images_folder[200]; // folder for search function settings 20.0 EX char searchresults_file[200]; // file for search results EX char maps_folder[200]; // folder for fotoxx-maps files EX char user_maps_folder[200]; // folder for user-added map files EX char montage_maps_folder[200]; // folder for montage map files EX char palettes_folder[200]; // folder for color palett files 19.0 EX char pixel_maps_folder[200]; // folder for pixel map files 20.0 // fotoxx PXM and PXB pixmaps struct PXM { // PXM pixmap, 3 x float per pixel char wmi[8]; // self-identifier int ww, hh, nc, rs; // width, height, channels, row stride float *pixels; // ww * hh * nc * sizeof(float) }; struct PXB { // PXB pixmap, 3 x uint8/pixel char wmi[8]; // self-identifier int ww, hh, nc, rs; // width, height, channels, row stride uint8 *pixels; // hh * rs bytes (in pixbuf) PIXBUF *pixbuf; // parallel GDK pixbuf sharing pixels }; // parameters for F/W image window painting (functions Fpaint() etc.) struct FWstate { 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 }; EX FWstate Fstate, Wstate; // parameters for F and W windows EX FWstate *Cstate; // current parameters, F or W #define Fpxb Fstate.fpxb // F/W window parameters #define Mpxb Fstate.mpxb #define Morgx Fstate.morgx #define Morgy Fstate.morgy #define Dorgx Fstate.dorgx #define Dorgy Fstate.dorgy #define Fzoom Fstate.fzoom #define Mscale Fstate.mscale #define Pscale Fstate.pscale EX PXM *E0pxm; // edit pixmap, original image EX PXM *E1pxm; // edit pixmap, base image for editing EX PXM *E3pxm; // edit pixmap, edited image EX PXM *ERpxm; // edit pixmap, undo/redo image EX PXM *E8pxm; // scratch image for some functions EX PXM *E9pxm; // scratch image for some functions EX int Dww, Dhh; // main/drawing window size EX int dww, dhh; // Dpxb size in drawing window, <= Dww, Dhh EX int zoomx, zoomy; // req. zoom center of window EX int Mbutton; // mouse button, 1/3 = left/right EX int Mwxposn, Mwyposn; // mouse position, window space EX int Mxposn, Myposn; // mouse position, image space EX int LMclick, RMclick; // mouse left, right click EX int Mxclick, Myclick; // mouse click position, image space EX int Mdrag; // mouse drag underway EX int mouse_dragtime; // current drag duration, milliseconds EX double wacom_pressure; // Wacom tablet, stylus pressure 0.0 to 1.0 EX int Mwdragx, Mwdragy; // drag increment, window space EX int Mxdown, Mydown, Mxdrag, Mydrag; // mouse drag vector, image space EX int Fmousemain; // mouse acts on main window not dialog EX int Mcapture; // mouse captured by edit function EX int KBcapture; // KB key captured by edit function EX int KBkey; // active keyboard key EX int KBcontrolkey; // keyboard key states, 1 = down EX int KBshiftkey; EX int KBaltkey; // lines, text and circles drawn over window image whenever repainted struct topline_t { int x1, y1, x2, y2; // endpoint coordinates in image space int type; // 1/2 = solid/dotted }; #define maxtoplines 8 // max. top lines EX topline_t toplines[8], ptoplines[8]; // top lines, prior top lines EX int Ntoplines, Nptoplines; // current counts struct toptext_t { int ID, px, py; cchar *text; cchar *font; }; #define maxtoptext 100 // max. text strings EX toptext_t toptext[100]; // text strings EX int Ntoptext; // current count struct topcircle_t { int px, py; int radius; }; #define maxtopcircles 200 // max. circles on image EX topcircle_t topcircles[200]; // circles EX int Ntopcircles; // current count // spline curve data typedef void spcfunc_t(int spc); // callback function, spline curve edit EX float splcurve_minx; // min. anchor point dist, % scale struct spldat { // spline curve data GtkWidget *drawarea; // drawing area for spline curves spcfunc_t *spcfunc; // callback function when curve changed 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 int Nspc; // number of curves, 1-10 int fact[10]; // curve is active int vert[10]; // curve is vert. (1) or horz. (0) int mod[10]; // curve is edited/modified 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 }; // select area data #define sa_initseq 10 // initial sequence number (0/1/2 reserved) #define sa_maxseq 9999 // ultimate limit 64K #define mode_rect 1 // select rectangle by drag/click #define mode_ellipse 2 // select ellipse by drag #define mode_draw 3 // freehand draw by drag/click #define mode_follow 4 // follow edge indicated by clicks #define mode_replace 5 // adjust edge by dragging mouse #define mode_mouse 6 // select area within mouse (radius) #define mode_onecolor 7 // select one matching color within mouse #define mode_allcolors 8 // select all matching colors within mouse #define mode_image 9 // select whole image EX VOL int sa_stat; // 0/1/2/3/4 = none/edit/xx/fini/disab EX int sa_mode; // 1-7 = curr. select area edit method EX uint16 sa_endpx[10000], sa_endpy[10000]; // last pixel drawn per sequence 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 int sa_lastx, sa_lasty; // last selected mouse position EX float sa_colormatch; // color range to match (0.001 to 1.0) EX int sa_calced; // edge calculation done EX int sa_blendwidth; // edge blend width EX int sa_minx, sa_maxx; // enclosing rectangle for area EX int sa_miny, sa_maxy; EX char *sa_stackdirec; // pixel search stack EX int *sa_stackii; EX int sa_maxstack; EX int sa_Nstack; EX int sa_currseq; // current select sequence no. EX int sa_Ncurrseq; // current sequence pixel count EX char *sa_pixselc; // maps pixels selected in current cycle EX uint16 *sa_pixmap; // 0/1/2+ = outside/edge/inside edge dist EX uint8 *sa_pixmap2; // inside pixel via sa_finish_auto() 19.0 EX uint sa_Npixel; // total select area pixel count EX uint8 *sa_hairy_alpha; // m_select_hairy() transparency poop EX int sa_fww, sa_fhh; // valid image dimensions for select area EX int Fshowarea; // show select area outline EX int areanumber; // increasing sequential number // grid lines parameters, for each grid lines set [G] #define GON 0 // gridsettings[G][0] grid lines on/off #define GX 1 // [G][1] x-lines on/off #define GY 2 // [G][2] y-lines on/off #define GXS 3 // [G][3] x-lines spacing #define GYS 4 // [G][4] y-lines spacing #define GXC 5 // [G][5] x-lines count #define GYC 6 // [G][6] y-lines count #define GXF 7 // [G][7] x-lines offset #define GYF 8 // [G][8] y-lines offset #define G9 9 // [G][9] unused EX int currgrid; // current grid params set, 0-5 EX int gridsettings[6][10]; // settings for 6 sets of grid lines // Image index record. // pdate, wwhh, tags, capt, comms, gtags may have "null" (char) as a missing value. struct xxrec_t { char *file; // image filespec char fdate[16]; // file date, yyyymmddhhmmss char pdate[16]; // EXIF photo date, yyyymmddhhmmss char rating[4]; // IPTC rating, "0" to "5" stars int ww, hh; // image width x height, pixels 19.0 int fsize; // image file size, bytes 19.0 char *tags; // IPTC tags char *capt; // IPTC caption char *comms; // EXIF comments char *location; // city, park, monument ... char *country; // country float flati, flongi; // earth coordinates char *xmeta; // indexed metadata }; EX xxrec_t **xxrec_tab; // image index table, file sequence EX int Nxxrec; // count, < maximages EX int Findexvalid; // 0/1/2 = no / yes:old / yes:old+new // image edit function data struct editfunc { // 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/usable int Fmods; // flag, image modifications by this func int Fpreview; // flag, using smaller image for edits int Frestart; // flag, OK to restart with new file int FusePL; // flag, OK to use with paint/lever edits int Fscript; // flag, function can be scripted int Fsaved; // current mods are saved to disk zdialog *zd; // edit dialog spldat *sd; // 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; }; EX editfunc *CEF; // current active edit function // undo/redo stack data #define maxedits 100 // undo/redo stack size EX char URS_filename[100]; // stack image filename template EX int URS_pos; // stack position, 0-99 EX int URS_max; // stack max. position, 0-99 EX char URS_funcs[100][32]; // corresp. edit func. names EX int URS_saved[100]; // corresp. edit saved to disk or not EX int URS_reopen_pos; // reopen last saved file, stack position struct textattr_t { // attributes for gentext() function char text[1000]; // text to generate image from char font[80]; // font name int size; // font size int tww, thh; // generated image size, unrotated float angle; // text angle, degrees float sinT, cosT; // trig funcs for text angle char color[4][20]; // text, backing, outline, shadow "R|G|B" int transp[4]; // corresponding transparencies 0-255 int towidth; // outline width, pixels int shwidth; // shadow width int shangle; // shadow angle -180...+180 PXB *pxb_text; // image with text/outline/shadow }; struct lineattr_t { // attributes for genline() function int length, width; // line length and width, pixels int larrow, rarrow; // left/right arrow head size (0 = no arrow) int lww, lhh; // generated image size, unrotated float angle; // line angle, degrees float sinT, cosT; // trig funcs for line angle char color[4][20]; // line, background, outline, shadow "R|G|B" int transp[4]; // corresponding transparencies 0-255 int towidth; // outline width, pixels int shwidth; // shadow width int shangle; // shadow angle -180...+180 PXB *pxb_line; // image with line/outline/shadow }; // dialogs with global visibility EX zdialog *zd_RGB_dist; // brightness distribution window EX zdialog *zd_darkbrite; // highlight dark/bright pixels zdialog EX zdialog *zd_editmeta; // edit metadata zdialog EX zdialog *zd_editanymeta; // edit any metadata zdialog EX zdialog *zd_deletemeta; // delete metadata zdialog EX zdialog *zd_batchtags; // batch tags zdialog EX zdialog *zd_metaview; // view metadata zdialog EX zdialog *zd_filesave; // file save zdialog EX zdialog *zd_rename; // rename file zdialog EX zdialog *zd_permissions; // file permissions dialog 20.0 EX zdialog *zd_copymove; // copy/move file zdialog EX zdialog *zd_deltrash; // delete/trash zdialog EX zdialog *zd_sela; // select area zdialog EX zdialog *zd_magnify; // magnify image zdialog EX zdialog *zd_album_update; // album_replacefile zdialog 19.0 EX zdialog *zd_gallery_select1; // gallery_select1 zdialog EX zdialog *zd_gallery_select; // gallery_select zdialog EX zdialog *zd_edit_bookmarks; // bookmarks edit zdialog EX zdialog *zd_ss_imageprefs; // slide show image prefs zdialog // method for running thread to send an event to an active zdialog EX zdialog *zd_thread; // active zdialog EX cchar *zd_thread_event; // event to send // edit function parameters EX int trimx1, trimy1, trimx2, trimy2; // trim rectangle NW and SE corners EX char *trimsizes[10]; // previous trim sizes "NNNNxNNNN" 20.0 EX char *trimbuttons[5]; // trim dialog button labels EX char *trimratios[5]; // corresponding aspect ratios EX int editresize[2]; // edit resize width, height EX float lens_mm; // pano lens mm setting // GTK functions (fotoxx main) int main(int argc, char * argv[]); // main program void first_startup(); // initial user indexing decision 20.0 void release_housekeeping(); // new release config file updates 20.03 int delete_event(); // window delete event function int destroy_event(); // window destroy event function int state_event(GtkWidget *, GdkEvent *event); // window state event function void drop_event(int mousex, int mousey, char *file); // file drag-drop event function int gtimefunc(void *arg); // periodic function void update_Fpanel(); // update F window information panel void paintlock(int lock); // block or unblock window updates int Fpaint(GtkWidget *, cairo_t *); // F and W drawing area paint function void Fpaintnow(); // window repaint synchronously void Fpaint2(); // window repaint (thread callable) void Fpaint3(int px, int py, int ww, int hh, cairo_t *cr); // update Mpxb area from updated E3 area void Fpaint0(int px, int py, int ww, int hh, cairo_t *cr); // update Mpxb area from updated E0 area void Fpaint4(int px, int py, int ww, int hh, cairo_t *cr); // update Dpxb area from updated Mpxb void Fpaint3_thread(int px, int py, int ww, int hh); // Fpaint3 for threads (define update area) void Fpaint3_main(); // update Fpaint3 area in main thread void mouse_event(GtkWidget *, GdkEventButton *, void *); // mouse event function void m_zoom(GtkWidget *, cchar *); // zoom image +/- void KBevent(GdkEventKey *event); // pass dialog KB events to main app int KBpress(GtkWidget *, GdkEventKey *, void *); // KB key press event function int KBrelease(GtkWidget *, GdkEventKey *, void *); // KB key release event function void win_fullscreen(int hidemenu); // full screen without menu/panel void win_unfullscreen(); // restore to prior size with menu etc. void set_mwin_title(); // main window title = curr_file path // cairo drawing functions (fotoxx main) void draw_pixel(int px, int py, cairo_t *cr, int fat = 0); // draw pixel void erase_pixel(int px, int py, cairo_t *cr); // erase pixel void draw_line(int x1, int y1, int x2, int y2, int type, cairo_t *cr); // draw line or dotted line (type 1/2) void erase_line(int x1, int y1, int x2, int y2, cairo_t *cr); // erase line void draw_toplines(int arg, cairo_t *cr); // draw all pre-set overlay lines void draw_gridlines(cairo_t *cr); // draw grid lines on image void add_toptext(int ID, int px, int py, cchar *text, cchar *font); // add text string with ID on window void draw_toptext(cairo_t *cr); // draw text strings when window repainted void erase_toptext(int ID); // remove all text strings with ID void draw_text(int px, int py, cchar *text, cchar *font, cairo_t *cr); // draw text on window void add_topcircle(int px, int py, int radius); // draw circle on window void draw_topcircles(cairo_t *cr); // draw circles when window repainted void erase_topcircles(); // remove all circles void draw_mousecircle(int cx, int cy, int rad, int Ferase, cairo_t *cr); // draw circle around mouse pointer void draw_mousecircle2(int cx, int cy, int rad, int Ferase, cairo_t *cr); // 2nd circle for m_copypixels1() void draw_mousearc(int cx, int cy, int ww, int hh, int Ferase, cairo_t *cr); // draw ellipse around pointer // spline curve edit functions (fotoxx main) spldat * splcurve_init(GtkWidget *frame, void func(int spc)); // initialize spline curves int splcurve_adjust(void *, GdkEventButton *event, spldat *); // curve editing function int splcurve_addnode(spldat *, int spc, float px, float py); // add a new node to a curve int splcurve_resize(GtkWidget *); // adjust drawing area height int splcurve_draw(GtkWidget *, cairo_t *, spldat *); // spline curve draw signal function int splcurve_generate(spldat *, int spc); // generate data from anchor points float splcurve_yval(spldat *, int spc, float xval); // get curve y-value int splcurve_load(spldat *sd, FILE *fid = 0); // load curve from a file int splcurve_save(spldat *sd, FILE *fid = 0); // save curve to a file // edit support functions (fotoxx main) int edit_setup(editfunc &EF); // start new edit transaction void edit_cancel(int keep); // cancel edit (keep zdialog etc.) void edit_apply(); // apply edit, dialog > new edit 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 func_load_widgets(zdialog *zd, spldat *sd, cchar *fname, FILE *fid); // load zdialog widgets from a file int func_save_widgets(zdialog *zd, spldat *sd, cchar *fname, FILE *fid); // save zdialog widgets to a file int func_load_prev_widgets(zdialog *zd, spldat *sd, cchar *fname); // save last-used zdialog widgets int func_save_last_widgets(zdialog *zd, spldat *sd, cchar *fname); // load last-used zdialog widgets void m_undo_redo(GtkWidget *, cchar *); // undo / redo if left / right mouse click void m_undo(GtkWidget *, cchar *); // undo one edit void m_redo(GtkWidget *, cchar *); // redo one edit void undo_all(); // undo all edits of current image void redo_all(); // redo all edits of current image void save_undo(); // undo/redo save function void load_undo(); // undo/redo load function int checkpend(cchar *list); // check for lock, busy, pending changes typedef void mcbFunc(); // callback function type EX mcbFunc *mouseCBfunc; // current edit mouse function void takeMouse(mcbFunc func, GdkCursor *); // capture mouse for edit dialog void freeMouse(); // free mouse for main window // thread support functions (fotoxx main) typedef void * threadfunc(void *); // edit thread function void start_thread(threadfunc func, void *arg); // start edit thread void signal_thread(); // signal thread, work is pending void wait_thread_idle(); // wait for work complete void wrapup_thread(int command); // wait for exit or command exit void thread_idle_loop(); // thread: wait for work or exit command void thread_exit(); // thread: exit unconditionally void do_wthreads(threadfunc func, int Nt); // start worker threads and wait // 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 // window and menu builder (f.widgets.cc) void build_widgets(); // build widgets for F/G/W/M view modes void popup_menufunc(GtkWidget *, cchar *menu); // image/thumb right-click menu func void image_Rclick_popup(); // popup menu for image right-click void gallery_Lclick_func(int Nth); // function for thumbnail left-click void gallery_Rclick_popup(int Nth); // popup menu for thumbnail right-click void m_viewmode(GtkWidget *, cchar *fgw); // set current F/G/W/M view mode void m_favorites(GtkWidget *, cchar *); // graphic popup menu, user favorites // pixmap/pixbuf memory image and file I/O functions (f.pixmap.cc) PXM * PXM_make(int ww, int hh, int nc); // create a PXM pixmap void PXM_free(PXM *&pxm); // free PXM pixmap void PXM_audit(PXM *pxm); // audit contents of a PXM pixmap void PXM_clear(PXM *pxm, int BW); // clear PXM pixmap to value BW (0/255) void PXM_addalpha(PXM *pxm); // add alpha channel to PXM pixmap void PXM_subalpha(PXM *pxm); // remove alpha channel from PXM pixmap 20.0 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 PXM * PXM_rescale(PXM *pxm, int ww, int hh); // rescale PXM pixmap (ww/hh) PXM * PXM_rotate(PXM *pxm, float angle, int fast = 0); // rotate PXM pixmap PXB * PXB_make(int ww, int hh, int nc); // create a PXB pixmap 19.0 void PXB_free(PXB *&pxb); // free PXB pixmap void PXB_addalpha(PXB *pxb); // add alpha channel to PXB pixmap void PXB_subalpha(PXB *pxb); // remove alpha channel from PXB pixmap PXB * PXB_copy(PXB *pxb); // copy (duplicate) PXB pixmap void PXB_copy_area(PXB *, int, int, int, int, PXB *, int, int); // copy area from one PXB to another PXB * PXB_subpxb(PXB *pxb1, int px1, int py1, int ww, int hh); // create new PXB from PXB section PXB * PXB_half(PXB *pxb1); // rescale PXB pixmap to 1/2 size PXB * PXB_rescale(PXB *pxb, int ww, int hh); // rescale PXB pixmap (ww/hh) PXB * PXB_resize(PXB *pxb1, int size); // resize PXB pixmap to max. ww/hh PXB * PXB_rescale_fast(PXB *pxb1, int ww, int hh); // rescale " " faster, less quality PXB * PXB_resize_fast(PXB *pxb1, int size); // resize PXB pixmap to max. ww/hh PXB * PXB_rotate(PXB *pxb, float angle); // rotate PXB pixmap 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) 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 PXB * PXB_load(cchar *filespec, int fack); // load file to PXB pixmap, 8 bpc PXM * PXM_load(cchar *filespec, int fack); // load file to PXM pixmap, 8/16 bpc int PXB_save(PXB *pxb, cchar *filespec, int bpc, int quality, int fack); // save PXB pixmap to file int PXM_save(PXM *pxm, cchar *filespec, int bpc, int quality, int fack); // save PXM pixmap to file PXB * JPG_PXB_load(cchar *file, int size = 0); // JPG file to PXB, 8 bpc PXM * JPG_PXM_load(cchar *file); // JPG file to PXM, 8 bpc int PXB_JPG_save(PXB *pxb, cchar *file, int quality); // PXB to JPG file, 8 bpc int PXM_JPG_save(PXM *pxm, cchar *file, int quality); // PXM to JPG file, 8 bpc PXB * HEIC_PXB_load(cchar *file, int size = 0); // HEIC file to PXB, 8 bpc 20.0 PXM * HEIC_PXM_load(cchar *file); // HEIC file to PXM, 8 bpc int PXB_HEIC_save(PXB *pxb, cchar *file); // PXB to HEIC file, 8 bpc int PXM_HEIC_save(PXM *pxm, cchar *file); // PXM to HEIC file, 8 bpc PXB * JP2_PXB_load(cchar *file); // JP2 file to PXB, 8 bpc 20.0 PXM * JP2_PXM_load(cchar *file); // JP2 file to PXM, 8 bpc int PXB_JP2_save(PXB *pxb, cchar *file); // PXB to JP2 file, 8 bpc int PXM_JP2_save(PXM *pxm, cchar *file); // PXM to JP2 file, 8 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 PXB_TIFF_save(PXB *pxb, cchar *filespec, int bpc); // PXB to TIFF file, 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 PXB_PNG_save(PXB *pxb, cchar *filespec, int bpc); // PXB to PNG file, 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 PXB * RAW_PXB_load(cchar *rawfile, int autobright, int matchthumb); // RAW file to PXB, 8 bpc 20.0 PXM * RAW_PXM_load(cchar *rawfile, int autobright, int matchthumb); // RAW file to PXM, 16 bpc 20.0 PXB * RAW_PXB_load_dcraw(cchar *rawfile, int autobright); // RAW file to PXB, 8 bpc 20.0 PXB * RAW_PXB_load_dcraw_half(cchar *rawfile); // RAW file to PXB, 8 bpc, 1/2 size 20.0 PXM * RAW_PXM_load_dcraw(cchar *rawfile, int autobright); // RAW file to PXB, 16 bpc 20.0 PXB * RAW_PXB_load_rawtherapee(cchar *rawfile, int autobright); // RAW file to PXB, 8 bpc 20.0 PXM * RAW_PXM_load_rawtherapee(cchar *rawfile, int autobright); // RAW file to PXB, 16 bpc 20.0 void pixelvert(uint8 *buff1, uint8 *buff2, int ww, int hh, int nc1, int nc2); // convert pixel buffer format void pixelvert(uint8 *buff1, uint16 *buff2, int ww, int hh, int nc1, int nc2); // and/or channel count void pixelvert(uint8 *buff1, float *buff2, int ww, int hh, int nc1, int nc2); void pixelvert(uint16 *buff1, uint8 *buff2, int ww, int hh, int nc1, int nc2); void pixelvert(uint16 *buff1, uint16 *buff2, int ww, int hh, int nc1, int nc2); void pixelvert(uint16 *buff1, float *buff2, int ww, int hh, int nc1, int nc2); void pixelvert(float *buff1, uint8 *buff2, int ww, int hh, int nc1, int nc2); void pixelvert(float *buff1, uint16 *buff2, int ww, int hh, int nc1, int nc2); void pixelvert(float *buff1, float *buff2, int ww, int hh, int nc1, int nc2); // file menu functions (f.file.cc) void m_new_session(GtkWidget *, cchar *); // start parallel fotoxx session void new_session(cchar *args); // callable function void m_open_drag(int x, int y, char *file); // open drag-drop file void m_cycle2files(GtkWidget *, cchar *); // open/cycle 2 previous files void m_cycle3files(GtkWidget *, cchar *); // open/cycle 3 previous files void m_view360(GtkWidget *, cchar *); // view a 360 degree panorama int f_open(cchar *file, int n = 0, int kp = 0, int ak = 1, int z = 0); // open new current image file int f_open_saved(); // open saved file, retain undo/redo stack void f_preload(int next); // start preload of next file void m_prev(GtkWidget *, cchar *); // open previous file in gallery void m_next(GtkWidget *, cchar *); // open next file in gallery void m_prev_next(GtkWidget *, cchar *); // left/right mouse >> prev/next image 20.0 void m_zoom_menu(GtkWidget *, cchar *); // left/right mouse >> image/thumb zoom 20.0 void m_rename(GtkWidget *, cchar *); // rename an image file (same location) void m_permissions(GtkWidget *, cchar *); // view and change file permissions 20.0 void m_blank_image(GtkWidget *, cchar *); // create new blank image file int create_blank_file(cchar *file, int ww, int hh, int RGB[3]); // create new blank image file, callable void m_blank_window(GtkWidget *, cchar *); // blank / unblank window void play_gif(cchar *file); // play GIF file animation 20.0 void m_copy_move(GtkWidget *, cchar *); // copy or move image file to new location void m_copyto_desktop(GtkWidget *, cchar *); // copy image file to desktop void m_copyto_clip(GtkWidget *, cchar *file); // copy an image file to the clipboard void m_wallpaper(GtkWidget *, cchar *file); // current file >> desktop wallpaper 19.0 void m_delete_trash(GtkWidget *, cchar *); // delete or trash an image file void m_print(GtkWidget *, cchar *); // print an image file void m_print_calibrated(GtkWidget *, cchar *); // print an image file with adjusted colors void m_quit(GtkWidget *, cchar *); // exit application with checks void quitxx(); // exit unconditionally void m_help(GtkWidget *, cchar *menu); // various help menu functions void m_file_save(GtkWidget *, cchar *); // save modified image file to disk void m_file_save_replace(GtkWidget *, cchar *menu); // for KB shortcut Save File (replace) void m_file_save_version(GtkWidget *, cchar *menu); // for KB shortcut Save File Version char * file_rootname(cchar *file); // get root name without .vNN.ext char * file_basename(cchar *file); // get base name /.../filename.ext 19.0 char ** file_all_versions(cchar *file, int &nf); // get all versions for given file 19.0 char * file_new_version(cchar *file); // get next avail. file version name 19.0 char * file_newest_version(cchar *file); // get newest version or original file 19.0 char * file_prior_version(cchar *file); // get prior version or original file 19.0 int f_save(char *outfile, cchar *type, int bpc, int qual, int ack); // save current image file to disk 19.0 int f_save_as(); // save_as dialog (choose file and bpc) int linedit_open(cchar *filespec); // open text file for line editing char * linedit_get(); // read next existing record int linedit_put(cchar *record); // put next output record int linedit_close(); // finish, output file replaces input int find_imagefiles(cchar *dr, int fgs, char **&fils, int &nf, int zf = 1); // find all image files within a folder char * raw_to_tiff(cchar *rawfile); // RAW filespec >> corresp. tiff filespec int set_permissions(GtkWidget *win, cchar *file, cchar *p1, char *p2); // show and set file permissions 20.0 int conv_permissions(cchar *perms, mode_t &mode); // conv. "read+write" etc. to mode_t 20.0 int conv_permissions(mode_t mode, char *perms); // conv. mode_t to "read+write" etc. 20.0 char * f_realpath(cchar *infile); // get real path for file with symlinks 20.0 // thumbnail gallery and navigation functions (f.gallery.cc) char * gallery(cchar *filez, cchar *action, int Nth); // display image gallery window, navigate void gallery_memory(cchar *action); // save/recall gallery sort and posn. void set_gwin_title(); // main window title = gallery name char * prev_next_file(int index, int lastver); // get prev/next file with last version option char * prev_next_gallery(int index); // get prev/next gallery (physical folder) int file_position(cchar *file, int Nth); // get rel. position of file in gallery void m_thumbview(GtkWidget *, cchar *); // gallery, thumbnails + file data 20.04 void m_metaview(GtkWidget *, cchar *); // gallery, thumbnails + basic metadata 20.0 void m_listview(GtkWidget *, cchar *); // gallery, tiny thumbnails + file names 20.0 void m_recentfiles(GtkWidget *, cchar *); // gallery of recently viewed files void add_recent_file(cchar *file); // add file to recent file list void m_newfiles(GtkWidget *, cchar *); // gallery of newest files FTYPE image_file_type(cchar *file); // file type: FNF folder IMAGE RAW THUMB OTHER char * thumb2imagefile(cchar *thumbfile); // get image file for thumbnail file char * image2thumbfile(cchar *imagefile); // get thumbnail file for image file int thumbfile_OK(cchar *imagefile); // check thumbnail file exists and not stale int update_thumbfile(cchar *imagefile); // create or refresh thumbnail file void delete_thumbfile(cchar *imagefile); // delete thumbnail for file PIXBUF * get_cache_thumb(cchar *imagefile, PXB *thumbpxb); // get thumbnail from cache, add if needed void preload_thumbs(int targetfile); // preload thumbnails into cache void gallery_popimage(); // popup big image of clicked thumbnail char * gallery_select1(cchar *gfolder); // select one file from gallery window void gallery_select1_Lclick_func(int Nth); // gallery_select1, thumbnail left-click void gallery_select_clear(); // clear gallery_select() file list int gallery_select(); // select files from gallery window void gallery_select_Lclick_func(int Nth); // gallery_select, thumbnail left-click void gallery_select_Rclick_func(int Nth); // gallery_select, thumbnail right-click void m_select_files(GtkWidget *, cchar *); // files for album/batch/script funcs 20.0 void m_source_folder(GtkWidget *, cchar *); // set gallery from current file folder void m_alldirs(GtkWidget *, cchar *); // list folders, click for gallery void m_bookmarks(GtkWidget *, cchar *); // select bookmarked image, goto gallery posn void m_edit_bookmarks(GtkWidget *, cchar *); // edit bookmarks (gallery/file position) void edit_bookmarks_Lclick_func(int Nth); // thumbnail click response function void m_show_hidden(GtkWidget *, cchar *); // for KB shortcut "show hidden files" void m_current_album(GtkWidget *, cchar *); // for KB shortcut "Current Album" 20.0 // albums menu (f.albums.cc) void m_manage_albums(GtkWidget *, cchar *); // create and edit image albums int album_create_from_gallery(cchar *newalbumfile); // create album from current gallery void album_show(cchar *albumfile = 0); // set/show current album gallery void album_purge_replace(cchar *file); // purge/replace deleted files void album_purge_replace_all(); // same, all album files 20.0 void album_addselfiles(int mode); // add selected files to album 20.0 void album_addcurrfile(int mode); // add current file to album 20.0 void album_addfile(cchar *file, int posn); // add one file to album (drag/drop) void album_removefile(int posn); // remove file from album void album_movefile(int pos1, int pos2); // move file position (drag/drop) void m_update_albums(GtkWidget *, cchar *); // update albums for new file version void update_albums_Lclick_func(int Nth); // m_update_albums() thumbnail click void m_album_mass_update(GtkWidget *, cchar *); // album mass update tools void album_batch_rename(char **oldfiles, char **newfiles, int Nf); // update albums for m_batch_convert() void m_gallery2album(GtkWidget *, cchar *); // save current gallery as album 20.0 // slide show menu (f.albums.cc) void m_slideshow(GtkWidget *, cchar *); // enter or leave slideshow mode void ss_imageprefs_Lclick_func(int Nth); // slideshow image prefs thumbnail click func void ss_KBfunc(int KBkey); // slideshow keyboard input // metadata menu functions (f.meta.cc) int select_meta_keys(char *itemlist[], int Fexclude); // dialog - select metadata items void meta_view(int type); // popup metadata report void m_meta_view_short(GtkWidget *, cchar *); // view selected EXIF/IPTC data void m_meta_view_long(GtkWidget *, cchar *); // view all EXIF/IPTC data void m_meta_edit_main(GtkWidget *, cchar *); // edit date/rating/tags dialog void m_meta_manage_tags(GtkWidget *, cchar *); // manage tags (keywords) 20.0 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_meta_captions(GtkWidget *, cchar *); // show caption/comments at the top void m_batch_tags(GtkWidget *, cchar *); // add and remove tags void m_batch_rename_tags(GtkWidget *, cchar *); // rename tags for all image files void m_batch_photo_date_time(GtkWidget *, cchar *menu); // change or shift photo dates/times void m_batch_change_metadata(GtkWidget *, cchar *); // add/change/delete metadata void m_batch_report_metadata(GtkWidget *, cchar *); // metadata report void m_batch_geotags(GtkWidget *, cchar *menu); // add or change geotags void m_meta_places_dates(GtkWidget *, cchar *); // report images by location and date range void m_meta_timeline(GtkWidget *, cchar *); // report images by month void m_autosearch(GtkWidget *, cchar *); // search images, write to stdout 20.0 void m_search_images(GtkWidget *, cchar *); // search image metadata void metadate_pdate(cchar *metadate, char *pdate, char *ptime); // "yyyymmddhhmm" > "yyyy-mm-dd" + "hh:mm:ss" void load_filemeta(cchar *file); // load metadata for image file void save_filemeta(cchar *file); // save metadata in EXIF and image index int add_tag_fotoxx(cchar *file); // add "fotoxx" tag to an edited file void set_meta_wwhh(int ww, int hh); // set ww/hh outside m_meta_edit() void update_image_index(cchar *file); // update image index for image file void delete_image_index(cchar *file); // delete image index for image file void m_set_map_markers(GtkWidget *, cchar *); // set markers for all images or gallery only void m_load_filemap(GtkWidget *, cchar *); // load a file map chosen by user (W view) void filemap_mousefunc(); // mouse function for file map view mode void filemap_paint_dots(); // paint red dots on map image locations void free_filemap(); // free memory used by file map image void m_netmap_source(GtkWidget *, cchar *); // choose net map source (M view) void m_load_netmap(GtkWidget *, cchar *); // load the initial net map void netmap_paint_dots(); // paint red dots on map image locations void m_netmap_zoomin(GtkWidget *, cchar *); // zoom net map to given location void m_netmap_locs(GtkWidget *, cchar *); // save amd recall net map locations int exif_get(cchar *file, cchar **keys, char **kdat, int nk); // get EXIF/IPTC data for given keys int exif_put(cchar *file, cchar **keys, cchar **kdat, int nk); // put EXIF/IPTC data for given keys int exif_copy(cchar *f1, cchar *f2, cchar **keys, cchar **kdat, int nk); // copy EXIF/IPTC data from file to file int exif_server(int Nrecs, char **inputs, char **outputs); // run exiftool as a server process void exif_tagdate(cchar *exifdate, char *tagdate); // yyyy:mm:dd:hh:mm:ss to yyyymmddhhmmss void tag_exifdate(cchar *tagdate, char *exifdate); // yyyymmddhhmmss to yyyy:mm:dd:hh:mm:ss xxrec_t * get_xxrec(cchar *file); // get index record from memory table int put_xxrec(xxrec_t *xxrec, cchar *file); // add/update index table and file xxrec_t * read_xxrec_seq(int &ftf); // read image index records 1-last int write_xxrec_seq(xxrec_t *xxrec, int &ftf); // write image index records 1-last // select area menu functions (f.area.cc) void m_select(GtkWidget *, cchar *); // select area within image void m_select_hairy(GtkWidget *, cchar *); // select hairy or irregular edge void m_select_find_gap(GtkWidget *, cchar *); // find gap in area outline 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_clear(GtkWidget *, cchar *); // clear area void m_select_copy(GtkWidget *, cchar *); // copy area to default file void m_select_save(GtkWidget *, cchar *); // copy area to designamted file void m_select_load(GtkWidget *, cchar *); // load file and paste as area in image void m_select_paste(GtkWidget *, cchar *); // paste last copied area into image void sa_geom_mousefunc(); // select rectangle or ellipse void sa_draw_mousefunc(); // edge line drawing function int sa_nearpix(int mx, int my, int rad, int &px, int &py, int fx); // find nearest pixel void sa_draw_line(int px1, int py1, int px2, int py2, cairo_t *cr); // draw a connected line void sa_draw1pix(int px, int py, cairo_t *cr); // add one pixel to select area void sa_mouse_select(); // select by mouse (opt. color match) void sa_nextseq(); // start next sequence number void sa_unselect_pixels(cairo_t *cr); // remove current selection void sa_pixmap_create(); // create area pixel maps void sa_finish(); // finish - map interior pixels void sa_finish_auto(); // finish - interior pixels already known void sa_unfinish(); // set finished area back to edit mode void sa_map_pixels(); // map edge and interior pixels void sa_show(int flag, cairo_t *cr); // show or hide area outline void sa_show_rect(int px1, int py1, int ww, int hh, cairo_t *cr); // show area outline inside a rectangle int sa_validate(); // validate area for curr. image void sa_enable(); // enable area void sa_disable(); // disable area void sa_invert(); // invert area void sa_clear(); // clear area void sa_edgecalc(); // calculate edge distances void sa_edgecreep(int); // adjust area edge +/- float sa_blendfunc(int edgedist); // calculate edge blend factor // image edit functions - Edit menu (f.edit.cc) void m_trim_rotate(GtkWidget *, cchar *); // combined trim/rotate function void m_upright(GtkWidget *, cchar *); // auto upright based on EXIF 20.0 void m_retouch(GtkWidget *, cchar *); // brightness, contrast, color void blackbodyRGB(int K, float &R, float &G, float &B); // convert deg. K to RGB factors void m_resize(GtkWidget *, cchar *); // resize image void m_adjust_RGB(GtkWidget *, cchar *); // color adjust using RGB or CMY colors void m_adjust_HSL(GtkWidget *, cchar *); // color adjust with HSL void HSLtoRGB(float H, float S, float L, float &R, float &G, float &B); // convert HSL color space to RGB void RGBtoHSL(float R, float G, float B, float &H, float &S, float &L); // convert RGB color space to HSL void m_markup(GtkWidget *, cchar *); // draw text/line/box/oval on image 20.0 void m_draw_text(GtkWidget *, cchar *); // draw 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_draw_line(GtkWidget *, cchar *); // draw line/arrow on image void load_line(zdialog *zd); // load line attributes from a file void save_line(zdialog *zd); // save line attributes to a file int genline(lineattr_t *attr); // generate line/arrow image from attributes void m_draw_box(GtkWidget *, cchar *); // draw box on image 20.0 void m_draw_oval(GtkWidget *, cchar *); // draw oval on image 20.0 void m_paint_image(GtkWidget *, cchar *); // paint pixels with the mouse int RGB_chooser(zdialog *zd, cchar *butt, uint8 rgb[3]); // dialog, get color from color file 19.0 int HSL_chooser(zdialog *zd, cchar *butt, uint8 RGB[3]); // dialog, get color from HSL dialog 19.0 void m_copypixels1(GtkWidget *, cchar *); // copy pixels within one image void m_copypixels2(GtkWidget *, cchar *); // copy pixels from imageA to imageB void m_copypixels3(GtkWidget *, cchar *); // source image process for the above void m_paint_edits(GtkWidget *, cchar *); // paint edits gradually by mouse painting void m_undo_edits(GtkWidget *, cchar *); // undo prior edits by mouse painting 20.0 void m_plugins(GtkWidget *, cchar *); // edit plugins menu or run a plugin function void m_rawtherapee(GtkWidget *, cchar *rawfile); // edit RAW file with Raw Therapee 19.0 // image edit functions - Enhance menu (f.enhance.cc) void m_voodoo1(GtkWidget *, cchar *); // automatic image retouch void m_voodoo2(GtkWidget *, cchar *); // automatic image retouch void m_brite_dist(GtkWidget *, cchar *); // edit brightness distribution void m_gradients(GtkWidget *, cchar *); // magnify brightness gradients void m_flatten(GtkWidget *, cchar *); // flatten brightness distribution void m_gretinex(GtkWidget *, cchar *); // rescale RGB levels - global void m_zretinex(GtkWidget *, cchar *); // rescale RGB levels - zonal void m_sharpen(GtkWidget *, cchar *); // sharpen image void sharp_GR_callable(int amount, int thresh); // callable gradient sharpen void m_blur(GtkWidget *, cchar *); // blur image void m_blur_background(GtkWidget *, cchar *); // blur image outside of selected areas void m_denoise(GtkWidget *, cchar *); // image noise reduction void m_redeyes(GtkWidget *, cchar *); // red-eye removal void m_match_colors(GtkWidget *, cchar *); // set image2 colors to match image1 void m_smart_erase(GtkWidget *, const char *); // smart erase object void m_chromatic1(GtkWidget *, const char *); // reduce lateral chromatic aberration 20.0 void m_chromatic2(GtkWidget *, const char *); // reduce axial chromatic aberration 20.0 void m_vignette(GtkWidget *, cchar *); // vignette tool void m_remove_dust(GtkWidget *, cchar *); // remove dust spots // image edit functions - Effects menu (f.effects.cc) void m_sketch(GtkWidget *, cchar *); // simulated sketch void m_cartoon(GtkWidget *, cchar *); // cartoon drawing void m_line_drawing(GtkWidget *, cchar *); // outline drawing void m_emboss(GtkWidget *, cchar *); // simulated embossing void m_tiles(GtkWidget *, cchar *); // tile array (pixelate) void m_dither(GtkWidget *, cchar *); // dithered dots 19.0 void m_dither0(GtkWidget *, cchar *); // Roy Lichtenstein dots void m_dither1(GtkWidget *, cchar *); // pure RGB or B&W dots void m_dither2(GtkWidget *, cchar *); // RGB reduced bit-depth dots void m_dither3(GtkWidget *, cchar *); // custom palette color dots void m_painting(GtkWidget *, cchar *); // simulated painting void m_texture(GtkWidget *, cchar *); // add texture to image void m_pattern(GtkWidget *, cchar *); // tile image with a pattern void m_mosaic(GtkWidget *, cchar *); // make mosaic with tiles from images void m_color_mode(GtkWidget *, cchar *); // B+W/color, negative/positive, sepia void m_color_depth(GtkWidget *, cchar *); // set color depth 1-16 bits/color void m_shift_colors(GtkWidget *, cchar *); // shift colors into other colors void m_alien_colors(GtkWidget *, cchar *); // revise hues using an algorithm void m_brite_ramp(GtkWidget *, cchar *); // add brightness ramp across image void m_paint_transp(GtkWidget *, cchar *); // paint transparnecy with the mouse void m_mirror(GtkWidget *, cchar *); // mirror image horz. or vert. void m_anykernel(GtkWidget *, cchar *); // apply custom convolution kernel // image edit functions - Warp menu (f.warp.cc) void m_unbend(GtkWidget *, cchar *); // unbend panoramas void m_perspective(GtkWidget *, cchar *); // warp tetragon into rectangle void m_warp_area(GtkWidget *, cchar *); // warp image within an area void m_unwarp_closeup(GtkWidget *, cchar *); // warp image within closeup face area void m_warp_curved(GtkWidget *, cchar *); // warp image, curved transform void m_warp_linear(GtkWidget *, cchar *); // warp image, linear transform void m_warp_affine(GtkWidget *, cchar *); // warp image, affine transform void m_flatbook(GtkWidget *, cchar *); // flatten a photographed book page void m_area_rescale(GtkWidget *, cchar *); // rescale image, select areas unchanged void m_waves(GtkWidget *, cchar *); // warp image using a wave pattern void m_twist(GtkWidget *, cchar *); // twist image centered at mouse void m_sphere(GtkWidget *, cchar *); // image spherical projection void m_stretch(GtkWidget *, cchar *); // image stretch projection 19.0 void m_inside_out(GtkWidget *, cchar *); // turn an image inside-out 19.0 void m_tiny_planet(GtkWidget *, cchar *); // convert image to tiny planet 19.0 void m_escher_spiral(GtkWidget *, cchar *); // generate inward spiraling image 20.0 // image edit functions - Combine menu (f.combine.cc) void m_HDR(GtkWidget *, cchar *); // make HDR composite image void m_HDF(GtkWidget *, cchar *); // make HDF composite image void m_stack_paint(GtkWidget *, cchar *); // stack / paint image void m_stack_noise(GtkWidget *, cchar *); // stack / noise reduction void m_stack_layer(GtkWidget *, cchar *); // stack / layer paint image 19.0 void m_stack_slider(GtkWidget *, cchar *); // stack / slider (split image) 20.0 void m_pano_horz(GtkWidget *, cchar *); // make panorama composite image void m_pano_vert(GtkWidget *, cchar *); // make vertical panorama void m_pano_PT(GtkWidget *, cchar *); // make panorama via pano tools void m_image_diffs(GtkWidget *, cchar *); // show differences between 2 images // image edit functions - mashup and montage (f.mashup.cc) void m_mashup(GtkWidget *, cchar *); // arrange images and text in custom layout void m_montage(GtkWidget *, cchar *); // combine images into an image montage void montage_Lclick_func(int mx, int my); // montage image click function // process menu functions (f.process.cc) void m_batch_convert(GtkWidget *, cchar *); // rename/convert/resize/export image files int batch_sharp_func(PXM *pxm, int amount, int thresh); // callable sharpen func used by batch funcs void m_batch_upright(GtkWidget *, cchar *); // upright rotated image files void m_batch_deltrash(GtkWidget *, cchar *); // delete or trash selected files void m_batch_RAW(GtkWidget *, cchar *); // convert RAW files using Raw Therapee 20.0 void m_burn_DVD(GtkWidget *, cchar *); // burn selected images to CD/DVD void m_export_filelist(GtkWidget *, cchar *); // create file of selected image files void m_export_files(GtkWidget *, cchar *); // export image files to a folder void m_edit_script(GtkWidget *, cchar *); // build script file with N edit funcs void edit_script_addfunc(editfunc *); // edit_done() hook to add script func void m_run_script(GtkWidget *, cchar *); // run script on current image file void m_batch_script(GtkWidget *, cchar *); // run script on batch of image files // tools menu functions (f.tools.cc) void m_index(GtkWidget *, cchar *); // rebuild image index and thumbnails void index_noindex(); // make minimum index 20.0 void index_rebuild(int level, int menu); // index rebuild function void m_quick_index(GtkWidget *, cchar *); // quick incremental index, no UI 20.0 void m_move_fotoxx_home(GtkWidget *, cchar *); // move fotoxx home folder void m_preferences(GtkWidget *, cchar *); // user preferences and settings void m_KBshortcuts(GtkWidget *, cchar *); // edit KB shortcuts, update file int KBshortcuts_load(); // load KB shortcuts at startup time void m_RGB_dist(GtkWidget *, cchar *); // show brightness distribution graph void RGB_dist_graph(GtkWidget *drawin, cairo_t *cr, int *); // draw brightness distribution graph void brightness_scale(GtkWidget *drawarea, cairo_t *cr, int *); // draw black to white horizontal band void m_magnify(GtkWidget *, cchar *); // magnify image within a radius of the mouse void m_duplicates(GtkWidget *, cchar *); // find duplicate image files void m_show_RGB(GtkWidget *, cchar *); // show RGB values at mouse click void m_color_profile(GtkWidget *, cchar *); // convert to another color profile void m_calibrate_printer(GtkWidget *, cchar *); // calibrate printer colors void print_calibrated(); // print image with adjusted colors 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_darkbrite(GtkWidget *, cchar *); // highlight the darkest and brightest pixels void darkbrite_paint(); // paint function called from Fpaint() void m_map_pixel_bias(GtkWidget *, cchar *); // map raw pixel bias (sensor, vignette) 20.0 int pixel_bias_map_load(); // load pixel bias data from a file 20.0 int pixel_bias_fix(PXM *pxm); // do pixel bias corrections on image 20.0 void m_map_dead_pixels(GtkWidget *, cchar *); // map raw dead pixels (camera sensor) 20.0 int dead_pixels_map_load(); // load dead pixels map from a file 20.0 int dead_pixels_fix(PXM *pxm); // fix dead pixels after RAW_PXM_load() 20.0 void m_monitor_color(GtkWidget *, cchar *); // check monitor brightness and color void m_monitor_gamma(GtkWidget *, cchar *); // adjust monitor gamma void m_change_lang(GtkWidget *, cchar *); // change language void m_untranslated(GtkWidget *, cchar *); // report missing translations void m_phone_home_allow(GtkWidget *, cchar *); // allow anonymous usage statistics 20.0 void m_resources(GtkWidget *, cchar *); // report CPU and memory usage void m_appimage_files(GtkWidget *, cchar *); // list files in appimage container 20.0 void m_zappcrash_test(GtkWidget *, cchar *); // zappcrash test function /********************************************************************************/ // translatable strings used in multiple dialogs #define Badd E2X("Add") #define Baddall E2X("Add All") #define Ball E2X("All") #define Bamount E2X("Amount") #define Bangle E2X("Angle") #define Bapply E2X("Apply") #define Bauto E2X("Auto") #define Bblack E2X("Black") #define Bblendwidth E2X("Blend Width") #define Bblue E2X("Blue") #define Bbottom E2X("Bottom") #define Bbrightness E2X("Brightness") #define Bbrowse E2X("Browse") #define Bcalculate E2X("Calculate") #define Bcancel E2X("Cancel") #define Bcenter E2X("Center") #define Bchange E2X("Change") #define Bchoose E2X("Choose") #define Bclear E2X("Clear") #define Bclickthumbtoselect E2X("click thumbnail to select file") #define Bclose E2X("Close") #define Bcolor E2X("Color") #define Bcompleted E2X("COMPLETED") #define Bcontinue E2X("continue") #define Bcontrast E2X("Contrast") #define Bcopy E2X("Copy") #define Bcreate E2X("Create") #define Bcurvefile E2X("Curve File:") #define Bcut E2X("Cut") #define Bdeband E2X("Deband") #define Bdelete E2X("Delete") #define Bdisable E2X("Disable") #define Bdisplay E2X("Display") #define Bdone E2X("Done") #define Bedge E2X("edge") #define Bedit E2X("Edit") #define Benable E2X("Enable") #define Berase E2X("Erase") #define Bfetch E2X("Fetch") #define Bfileexists E2X("output file already exists") #define Bfilenotfound E2X("file not found") #define Bfilenotfound2 E2X("file not found: %s") #define Bfileselected E2X("%d image files selected") #define Bfind E2X("Find") #define Bfinish E2X("Finish") #define Bflatten E2X("Flatten") #define Bfont E2X("Font") #define Bgallerytruncated E2X("gallery truncated to %d images") #define Bgreen E2X("Green") #define Bgrid E2X("Grid") #define Bheight E2X("Height") #define Bhelp E2X("Help") #define Bhide E2X("Hide") #define Bimage E2X("Image") #define Bimages E2X("Images") #define Binsert E2X("Insert") #define Binvert E2X("Invert") #define Bkeep E2X("Keep") #define Bleft E2X("Left") #define Blength E2X("Length") #define Blimit E2X("limit") #define Bload E2X("Load") #define Bmagnify E2X("Magnify") #define Bmake E2X("Make") #define Bmap E2X("Map") #define Bmatchlevel E2X("Match Level:") #define Bmax E2X("Max") #define Bmeasure E2X("Measure") #define Bmouseradius E2X("mouse radius") #define Bnegative E2X("Negative") #define Bnew E2X("New") #define Bnext E2X("Next") #define Bno E2X("No") #define Bnowriteperm E2X("no write permission") #define Bnoimages E2X("no images") #define Bnoindex E2X("image index disabled") #define Bnofileselected E2X("no image files selected") #define Bnone E2X("None") #define Bnoselection E2X("no selection") #define Boldindex E2X("image index not updated") #define Bopacitycenter E2X("opacity center") #define Bopacityedge E2X("opacity edge") #define Bopen E2X("Open") #define Bpaintradius E2X("Paint Radius") #define Bpaste E2X("Paste") #define Bpause E2X("Pause") #define Bpercent E2X("Percent") #define Bpower E2X("Power") #define Bpresets E2X("Presets") #define Bprev E2X("Prev") #define Bprevtip E2X("use previous input") #define Bproceed E2X("Proceed") #define Bquit E2X("Quit") #define Bradius E2X("Radius") #define Brange E2X("range") #define Bred E2X("Red") #define Bredo E2X("Redo") #define Breduce E2X("Reduce") #define Bremove E2X("Remove") #define Brename E2X("Rename") #define Breplace E2X("Replace") #define Breserved E2X("Reserved") #define Breset E2X("Reset") #define Bright E2X("Right") #define Brotate E2X("Rotate") #define Brun E2X("Run") #define Bsave E2X("Save") #define Bsearch E2X("Search") #define Bseconds E2X("Seconds") #define Bselect E2X("Select") #define Bselectfiles E2X("Select Files") #define Bshow E2X("Show") #define Bsize E2X("Size") #define Bstart E2X("Start") #define Bstop E2X("Stop") #define Bstrength E2X("Strength") #define Bareanotfinished E2X("the area is not finished") #define Bthresh E2X("Threshold") #define Btop E2X("Top") #define Btransparency E2X("Transparency") #define Btrash E2X("Trash") #define Btrim E2X("Trim") #define Bundoall E2X("Undo All") #define Bundolast E2X("Undo Last") #define Bundo E2X("Undo") #define Bunfinish E2X("Unfinish") #define Bupdate E2X("Update") #define Bview E2X("View") #define Bweb E2X("Web") #define Bwhite E2X("White") #define Bwidth E2X("Width") #define Bxoffset E2X("x-offset") #define Byoffset E2X("y-offset") #define Byes E2X("Yes") fotoxx-20.08/fotoxx.png000066400000000000000000000233271362435004500151200ustar00rootroot00000000000000PNG  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-20.08/images/000077500000000000000000000000001362435004500143215ustar00rootroot00000000000000fotoxx-20.08/images/HDF-paint.jpg000066400000000000000000000305711362435004500165430ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 170 246 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{׶+?o[ Cd="yHlC(7Dݚ_gZ&hm*ȶ2 qnOM}CGH@?}'+</ _BaXX_\xofw&Ko)Ҽ:{'Ya>Pr烗UIaҟ.[Z~}G+|Kg°ٷW:^(;i6ۛ I0Ƣ7ROs!=^Y\[\C0o qF @?+ УweߗJ?g};PpXF6N0 /+ .$LHTxn '~ߍ[#~}G+׼kD5{#DPŞ!uчt(YAe'*X:o˩=tF^ɵkkm%,QWB3BO0m%3>}G+Mὶ_ZxaMjVqM48r^)H|J\~% , c$ "P] >}^~ yfG- I =+[ٌ\Hc~ v_~_srmz]W曤u!ho %AY|敮 .ol>|?ֺvy8 v_Eu-̚Λc_heX,f 2'9!#,YXZ?u8#FUzuڍ7m TzEO[<9mw{s-Υg]Y HȆ}UIŽW8! wI30z+ WmAڵ K0NOcԴ+մs XeI5~:[x lT0PgL9>SXHmRԏ2U\W%.3('늜ҋ4βΛ5u}/C8\hIПww'Ï3n~{}֚U_ { {V@ &_{ù>şkù>şkrOƟtxw^:ubV*1L9@ܟ?? j?ܟ?? jopl~'xL}m[@a%6_i guV?uZޤo\ P(H p"l ,_F,_FpjQĶPa |p3VVIĄg>pe@_uù>şkù>şkrz__ϊW9?IirdE+2s5~ 𦗬iF6:^E+ N.#%ЕH> ,_F,_F˿_o#{߉^%tiįe}#k*c8m_0$# v$db_28P ykB/hd%6zS%ճE ,ǠQù>şkù>şkrovw_y7>5x6VRaqilWs:FA= P_7",=eW+w'Ïw'Ï(\T{I(@*QVévڤȂ5xTg秩ܟ?? j?ܟ?? jvasSk+8 m`PI',qߓn3Z"c' <qؓ_tù>şkù>şk͈tFcue_ .Y~|=> Vf`dzq^s_Og5?{z6ΌlXίO[vIy$}UY:w}_]մk-F WIiKo1KGvܠZ[ 7^dks-kIϖʊJǔݓgӴW:hwzǎFNχV$Pߺ̒vH_UZյK=sV{b4v9RBN H:_ }E|qψDŽ5m>O |Bkx"UQHa7v1I]8^[>=k NJtKĖZ7+ioK=y;ZBI|#S^!suuy)DEh6~"!+ "7 yv}oWq~\o_?6unݎsER}jK; "<=ԑKxLЧӡ/Q+1V`;O94CC+'w~&~\g8ҹ?{_[EؗdkO4p9 t&&r[ZH+G5l6%Ԝ܊ЮGL|:e-dvGWSxsUsz`"IM=i^uFJmnƃݏ|C}bTigoiWxZӴO 3%|Ajح) <=;I<.׺ @`[,u9meO:]ބH皷En_Hnlpp2B/Ov5v[FܶӪG#7}En^KPjU M8meF#>EdxM;F,HX @f謋rf}3O b xf6k#H,LO==遯Ee_+Z?Guc4Ee_+Z?Guc4EgWr[H] s<+BQ@Q@Q@fk7>eϔfsVdx͕.lD9ǚJ_~^|SV޸+CڍNNXH#f׊ǍF_js?dKD ̬N2ך[ۦ31 pqj5m&Ok~5m"WVEJWk˸1(Aʒ1\ŧ@&{]:M;B6SYV-]e!؎#Fr&4kqo74]JkY.zf`X˽탐ffq~:6AMY/cҮHI$B$ ӆ5|֣'n-WONI"H# 469}?ளQ-^\k;d8#*'N3]W/h~粺a$wI#!#+;2bV' $}2U/H fWϏGxS?u~';^'n,61h9xd_.Z(Ħߘ(}9i*]Εwd"EW-oòmR焼o4=}:t1'ʜ qXgddvj>qa\Ij0&yi8+9G9 6{ߩ*Ilj7]OE/+]t7K*MoB N8Lon"o_Vn2[ddO3vGx;9CpJ$74ۭ/O;Vd{'>aPn0y8Ɔ?MFCJ'i,{S)G9##qm!>kѾ8ig[>sGlܰ=\pU^o(&V֝J/i&=:%7zNR ōSm :k< % u eܭnQ)/jږo5=KPKԸPxeP(99|G|}EOźmEb]$p]Mk(2ζzl\n>|>~5k-tP'ݼ[gRn6g,8*K{lj4˯4>F))Ȗ.puV-o2JØ|u{^_=um[ɥK-̍{Şg/[=/Y| h^O)Qn5w`d n`|{} [KKyn߅ͯ7q^)F VN~sM^M༕P[J؜ 7|GQL +"oh6z%荌qt1tm'?(n"Fmc$j'U}\3 :+xÌ ExFq]7o1 iiiՍ7  ,EQEQEVEƇs>뚆q=V EEdȖ'W鎾կEe_Z6?GصucթE02ůߛFZjԢ3۸ }jnعTi5E(((+].c/iirϖW&*k"`OOin{qi םxV?xSǫOuVk#uE0_=.AqDPՆϊt>6K[xh$Fpml$61-JݟÞ('oO}BHTOt\#1ѝ}uE]__?+u/i+59R1stcfRyQ/tc}&7t[[K"XjIIuw:oסu.;.I ou;$u1*IaER((((((((((xHXa 1=eƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*յ<76V-œcrĉPT`hύ?U_x:_ e> )hןύ?U_x:_ eQair2#:dw\ZTyc/RX.oRi7c2 ͧs!,&_I >~>6_j?W/ 5U~ )h'3_I ^~>6_j?W/ 5U~ )h'3_I ^~>6_jW%Fc@55ޭ_^[Ihj5ߑijڄjYԅn7olXm95WG/lxwꗚޓ{F[E[̆uF rQWiy7㾖}jy.dx.c+=ƪ0G<;$Դ 6_m0LwB,A1C%}G~:,o<+໽v&luEE<1n1m>tخTco9`+65]XhK3k-HaVk|]s#6͠]+2x{GӴƺފhz%QB`' 2# ў}'BhZ ?ź퟇R}UtdUv.h٘I$gSv ^?̱W~ xBSR{I&`]ɹS;WWGh]F;t[Q Ԍ^NM65ET((((((+|5ZƧysjl܀3k8V}oµ?3Ί?ZQ 7x[B.GVGEvtP;&J}=lш(Bµ=Mo3]µ?3[vtP ϭvbV}o@85=Dw$WM{5=uQ@fotoxx-20.08/images/HDR.jpg000066400000000000000000002010511362435004500154370ustar00rootroot00000000000000JFIFHHExifMM*V^(if%HH023170100Fotoxx:trim_rotate| Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?O_(n渕#ˤX3s?l>|au^jl}'?DWqsW5F|=$~X`e'=Uv-HLJuM"O%Ae 2MkSJn6_wf/6c1$ 1=G*AzHG{^ ?u?J6>$0BkkIb3\<#úޣ+[E_> %aq*+??'Y_ j~ = xV-xG\ v]ZVVT\MlzP G/to: ۻ {eT#( $Vs$%]8DQ  |7ag?K+ |MZ:Qu2PIlmݞ*=u$|"}J]^ZJ7eԉ2,88D-DQ ??پ% RPҧ;}(e+ # |Ehڝδ)7#ʳo~_>>}G+4υ~ k-^$ПYy[xFp v_2]`]Xqs?t 3^!f,V4+V=*F!..|2v23Li{Bko9_DQ #%Okÿs0H4Җ#2:'ko_3TmM? YgPၥ`p8z9{i_x@D{7ßCS^LAu# I`W',qzW2zOMH7mi%Brp09+-qi$<11(35 $tO} h% xkxev(C*$]x|0oo<wZ|K׏չkrNl?%4/7 *7#Hw$V }z ׈~D_S\CUDaU?0l08No5p2\< @23>Mlm_/}G+|iš|_Yo[T&^dTo'j¯ xcI2kɪxB 9'U%TPPFt2)QXz}ӧZjZ}M;^i^QʽĞoaR}wF6̚זBJ .`RBO ӓFHэ6\9ydd(,k"d0U5jz-6[m ( CK[OO%L%ܰ*/ oϭvWWGvwkmmb1٠| ݺG'cw8 O.D;^9" }#"j:ZZl!?c؜^0{Ú5Ʃ,- Ĭ0 pWz^st 4?y|ϝȄ$$d+0Ƭcew'd}K,ss, }^}QMDW0,=T_NßAj7 \LP8#o|*/u#now!RDJjH8h`>#|sSy[✨O<gE#DžTpHMķN)\H};Ir=.ssP0k ۯjqRXFB7@ ?^Ԅcm8ߌ۟N) {㊙`$9^" H曜cN`@1Qa_J@0n'=n@[r9-tX#6DΊr~U`+wß(~5x[Ox|@dJ&h6tR #ɮÞ+|!&>[ʺ}#Ђ8 ־q9kø z,~_jM)&f770Z[[I`0ę8ŞѨu-#^4jC3fUfR#Js_iø z?`_Z={ ZK-:4VĖ޾yQDkr"0M99ZƏk:}RiגXX@ݲ5; +_GU#<[C'[k?UM;}g9 ]%Oj~%|Cq+t쮢(XD=\`_Z=??0x'M;u[j0ip@\%RCZo_\}C[i\mH_h&lkwßIy]ɍ ['<rbb_S ?}sC*q9kryY攵įǦ01[vQm/<%sߥ}sC*q9kY˽I^\ $Kn|=i}#`$۞W???0xwßt*,;N$u8=x=i[JE8"dzjn`SC B*je'}i9+5"eP1Re#^)1 1Ӷ)ž82TtCNx>NVܣn>PyF:&zdy  1M*qϮ) .F8❷) 1'H@'1RaO&x@Ͻ !Q{M9p#'aԝJri|Z𿉼_ax/Wf{ud(ݑ*@8# _*|4m~2Պm+RlP̲/⢽vOƶ&_l5S,e@'l@{F9J~Ou:i i| ]SHME>p$ uA+>8||0x5xsCv6sRM\6yl6HGG8+_?gq39_+-5K_5O}I!Ka|sm EzMb%k?z ??Sw]m@Zީ;.DT[tP'"V+_?gq3J~OuEb%k?z ??Sw]m@Zީ;.DT[tP'"V+_?gq3J~OuEb%k?z ??Sw]7;%OkPXS=,y>Úw㿍g xLsgn-Yŷ$y{!"V7~8yHl4}rQ+EA&}~~&o;;7FT,]x?s iDs$ʲ}ZLnc&<#i^?- -O[3vQ%Ȭ?JJ<`$Kqj:) ac9}@|O?]i|yhavVQYIҾ{MU_w?)|/?4OOk~[$U}3?x$oMNj.sfAth2Ÿo([$ r2p*TUh{wDY ӴAռc 1@i;sduI>V^U"N@?RykOjWZPhv3fPgݵSxEg>4w]^L'SW:쥦v"c1h)rկN'8ω  /ƫ6n;=1W'8VE W^oaE6^NsAםP!OZe3Cׇ7O}ӴAA G*r\F{WQ Y/Hܯ= ixAqcstuJЫ 0YT<Mh]y(@~¢`A9TR+e/:?9h] yez'Gw-K/(~'c-  tz`W\N_[\ U=h1|I Mzv6s旜M/޾ێ;'F3b1J#@TdiT`8N*}rzv)Ld'4m9inOpi@=w ֜OҚri=?£Q)ʤzu;e\`up6U@8H#'CT#r;q/}5&rqҔ p#NqSơ'Z-ίe MY6X-M[1yWGpc+K IedpI} 8pO88JrPj6e<}NdnϷzi~}i "'>ZS/=vr0Ji`z}3RUC!$s֥9)@ JU$3H]}GZ+&G|ؿt>0$tem"qRƪOID?ǷGzېq1H)ǭ.vcS'9@:})Ҥ-Hl}M7=N(19_MHpғkz`Fwݗ-iVmeKZU QEQEr>K?]0{MB2a`C*d\i~ygF?<3Um3S;mn.b;beNtP'H׼A/n#cA _#U4QH((w?.E5z/o:$ M^?ࣧ3c~[$p}%s?I|QK|Br:sPf:9H{O\wH }30^ڮY_^on2np QSAcjJ+4qZZ!>cRw}(W46[kY` =3X1wf=Iɧ]Oz-&mA[VM:Ȑ/nS$d}{_ xL~KfeMoK,ls{O-TX?cKjOA8ȣ?&_|CiB\pKrn'?*\uiGVrN/b=mV) $j7#ydnNzj=x_W7;3-\qNv5kOIe|&Q9X5 X>!j^%m]bN$}qTs+:RU nIru Q^#ǭaYK=jz \),'Ң6öjH8eKxqP^rRW6uwWWOEH=iQbSRrѿ* 3}W~v}ࢿeJ~3*G8\S$t#<[ abHƤT= )A14ckG`ݺ49S@j3'Ӝd ?FWݗ-iVmeKZT(((( w߆5.!y+4Ϥj! ,r\=j?ay) c_@Ԥ]1㙭$mC%~eΫJ__|PSL ڣUFuA2HH<БL$>0muf@5xzײiX]C{es ͻT#!RQ@Q@c7/COǴ+ +F mÛdzPu:ֱ?;( #^;ixwL4U"+keŒ'1$ĒI$MmQH((((w?.E5z/o:$ M^?ࣉE|$V|PW}G})HGA?o^6p~5|}ͪE$IH7#:gJ+!# 5"&GL< o'&LVҭYߕd, }@#[+g GCMpLg9޴̷pDs >_Z_Xl]+aI-EI)Dd_jt ^iO`\{ ;O 񭲿-lH=n~um9Y}c/<#C^KʱHq3(heQ ~?4oEeƜryl l. bI9ZitO~..adVzrî9k⦵-揧둥yBi*; ׳~̾ Ӽ!a:FsBkuQxm1!ay~i7B`#k)Fm͋) 6Ҫ;OoCSOG(0I;2۔+dR,qSjѴ^O\6sI=܁"=ُESM"2]ʜw0ShO']}*~١$Huv~Irr5+WiL,YCyLGͶ?"+4onm,o5ItD2 qg~ⵟ6M$ onR]Q_2mpWP}q޿}?ࢿeJyJԀ럥($篡yHP"1Ӂ~s1J3pH?y +2s)F:zN=N\`ԻsIx,xSqZPGOҤ1G<"8FHL|?J\.V0ARJ`yRq֓iNp*o, (GPxU\$ I91ȠV<ZyCCL$9U#'*7 BޟsֆQ, dz?xő-ɷEs^Pj IM9[ |].>m5QLl|C:Oa+_GxiѴ諮_ʷ r`C9k;χ/|QxH08ʼn9\SA-̖א=p̥Y~V5L"bUxJF z1 }1X3D3`1Q㚛 8:@wdcR2EH0)?)jǂ03ɩ#RzC[#ӟ^B9Cb8*}3izn]VhIv_>wT&ƓV`%ʩÒId 'wվ)k Td76ͱ ~ÑЏ ~В5o#TG7u!Z2*Cӭ:h(m$q 8>g|8;RoBO>PF<<®pGQEQEQEQEQEQEsw?)|/?y'']j_ ǴM l~_I 5/jз]F`].r@fiWGs8@+t"2D9?R?& b0}x[麷=[SfR{֍[t'}`sr,phh [G,$p8.6}~ueZ\iڥK[C^n'. ;'p 7Lcwun?\[ +JG^ko6b-$Ng<ܩBc Q? }x&/l T'# UJotnz[Ҝof,}qS+KM>ٴ AV|6$uX' fY67_$b?2KS)YDǵNp{ҿλmq ktܥx\cnv Q$͔Юs|)cRyIYjx3c'*<, $FQ$ۓS;Z\J}>uSzoy(Ӭ|Do4so$ܽ) <7eqj"k˨/p6*`>)Sm&$_bKGE}Dnn$XR\̢pޡ~*ۄcDiρ]|\uyL4\ig,l ;xk xC/b֗sw.Y)'vH:W4x_W|UjRiZھ$hv<x0pΚַ}+.yKs&X`^0WRqn?&vG&zq'T$t'$(FÌ{fMkr[aUwF<ҿWńx ;Q *9m@$yPAzk//mWT=2+Ĭ(YB'hxge{>[_ό7?gOimqj~<7J;vmcUԮ3'> xO-jCuTY7:WκÍsJLXAnksg~?:jP{Tj淇 QQk3`כnU+9~~kY2lGb2# Xi-ƣ]A,y$-kVw ]f/5° 0>b 3q+%8w'(mvL)8+E䶿.5s6Bc`xm!'*yh{ۇ#_׭L-eiJO@Ojq٭*<O#;+-Kv4XY%1ڂ^>|--R|K*TVrq FDb,r={H9oW?W~O(Ҹ+4|s_JE:pPRYUK2 N 򎽺ҸsOǧ<+:0m_#9= 4ЬH rjD]Є܀Ry!yV\݆m88Sy,pӦ2Hlb0x8jRq)ʀ/YU? Gip8R25 #5" =QT'ڥX0 v4cs"!֚|>ƥ^6I?\S,aY ַ|!KLYN $k5[vD^V8$(O]cY'o +%RN'ȴWg77-vgb^?^m%.}:sXW햓#9syUniq/aҦ<>Ի w3 /#`Cڭ$c}joZh!q#ԚGqlqOr1ӯ=6W1P1r 36O< `t94B(.~6y:zsьI<ԌО{S['y5c،5ʓf"0yDY4?LPpgR<0 v@#Ҝ|r“ӧjo?5/ZҬ iQEQEQEQEQEQQ\EiO"C jYvTҼb_>4Ht߅rGx{̒B  $7SaZ~Z~$|cE\ƗZún<̚C[ I)+\KVvs ĚDq.7BO˱Op|uAe _]0Uy}'SNA8%>7g$s>_;ǔٕa/=Z>Yl=ՇU`x*@ Enחx\g$gH|-H/D @GԬٱ+|!)H 4QYAR ʜLԩQEQEQEQEQEG{覯E{MU_w?)|/?4oU~#羏S"4BŬR'?c=o^-,TSk2+Vpsqz c⏡H+{k%=V7:rMicX$;gCq3k? v7h$ۙRR<*(9/qP`n676}tVrcXFNQo_=ꚶig-PC4)g=[$ mS(#1$0n ck7@[Uf3::}^ִ k C3BYF8t4jfܟK-Ʀ0N Ҡ>'wg @6铚Ҋ;Ku2FzRv9iMƔsck$qG`C9y4%,sұՔpӊo]比Ϙ+?ady".~)Hr#\Ϗ+xٖ}nGMs1^ưKIɱK˻sI-djOXrCѾ-k>w4I:ܶ#.ӊ괏wjB]6HY22OA/tWMprT /8灏°/V飞OJ%N3!^;4>=Faڭ!;G n 連̒ʬ3jʱUTV#jU>]UL=BJryһSƷAqkM2Xf,Tc8${JSW:4V+d$sd\ݝ H r$-Q:Tq s;|Tg_+jM/渲ԃЈ`8c2{: O_jϕ !ܪ{W38VLockak3?qN{~4NQاZ#{ݰ& ݭJ(h%_1$P;xe9k?ioFĴu7IgKߘB|֍znO[βr|Tndb]N0 :b*x[㖽~]SAIXM[$:`׫˩|O ^xR5-R/]\OEg־Y'+?e6E{KaTQ7}1w%_UP>$\$}Cҿ c`?R`ȅ9M]r:GUFֻ3ETVw8JADYqX}bq2r˒{ĹUl>E~v\3ڕQor2Ss*J?W~w2_Tpמqt Wz 8t_z$9!8= G.h,kÿ?g?G&m2;u:7{@o6v$p+Vom٦yn-bE"ypw\wWGg_"&{^Lv8'׊)~ &)[E,G9?^v/PF@2x۴o+%ʌmz=]W?WJqR| /푕z_ n\9mP܁c>-\}?+/d|e '͞NN3}s_ ܬ HmmcFHuQƴRk[ *1n:tFOjvNsM*ASӌcO^A}O$sF@cJab=siSҐ5eKZUy!7֕QEQEQEQ\׏%x[f&vBӔY.J'dh298kÿ ZNvO^yXF0wK!Xr?i&llkv8P& F^ &kyn5;ͦY%7̣f8^rv(W_E|Y}$Zd`r ep"g$*GUt%QEQE/t :d8s:2Aɬ>"| 2O3+/[-r}~|O-*[ |J6 ŬpM xNWU^}߂z5xuÞ,E{G4yr@Ó2k}Ox+%^-uJ.<;/2cQLj|Hĝo *\sepdeXX:RH H((($ M^ξ#=?SW_=|*h)H+'p>:*|!x[ﵹfʈ,I$&s^"WſT(/9#?ߋ&~'M~&a?1%~~&M~=ǩ'~ÒC7i'o(5 HyEſ@4_䏇Q YWX68}6Ro-dSy5qջ3qңkC/^!swo3Eeϗ=8-˟ <ۂcI!7ɪCCAR%I]۞gPDV$Hg'.W'}VѢ[oet/ηR Y#EXFFH5o j<5֡[BĒYr}_W`~UVv\sRG^Ԃ>^dc_Z,CV@jj޼p@9s_=|/x6q܉s;c~)kxPxYөO]t D/G]^hnxNyos~ H 8.ccԭfm\úͰ.7w+MG kj\]BpQgξGxwSf򌮶vĂd# lWjsC]m%;w9/v7dyQ_;C1J0ʨ))cvsҭO :čnA PI89v{5TȬp=LF9ǽL|܌SᶒyA2UsOҎs~VK_ ͩmL#ꃎGqOYߴnk!b3<kÞ ,U29>?6ק?PG }@9GVt[^Un9P c=s1[> Ks`":B[qs?̮ev8R2 `5q2]]yX{8S/4WÖw{]VI<iYQݸکxLԴ29#Y#_랸$w3܉.aDt#d=sǵKy$*E nl_C*08 49#A8 u r$zҕ =zTIt޼sAV!sA\KgO3c%QK~8+6O'#XӔ?|;k;k?趚흴x̉daHs6w9h<C2GbF@޻Nt:՝-yPePIw_ V=VgAӭɒ&* SM-{>%}Z[4|SWN/OY/̞nCCwQY.~KI?/?Z}}ǫ|;&hNf5O:]G2[kO?73e[iQA OG 736@W}{+/~_?_WKH? ZњL in++R~ʪ?#xW񏉼[xMY.t[kˤym,VN<0OoѦ _Q KozgůvFҾ4zE夁dW;pj䏗憬 _Q KozZ) _Q KozZ(ַ=G+/~_hk?Z}}ǫ9W_k>¿[_Q KozZ(ַ=G+/~_ki6w/ɧiӯdEBvT_]As_Q Kozg^iG7*iV:v&?t,1o޵FNqF3]ĸVMKKdep4;XH& w):zmcV|9T!Z>3L&QR4U JF;T3TL= 5)^i}h9KEc~כ2+e7_lTmw?dZBAI$[&5BIj^OZKp ,W▅n\:y\,[GϞIb3jn>4漘iwqy[rpz>'/㍆$d0\A>K>Ѣ-eG'*qsEޗqT֖WiE˜e!#ב)]=-?-ww/Q2c?1MwY]/Zf]"w_ DaQGj]*CSL"^|Fg2OmwRNp+IIFWzA:i* U҅e!3P={Tz;+yl$mЈH/\%! |8GSTѴrD&H t~ed[>t: %_6.@}Iʍ$\BMHFZ$pU$X89Vj6jehgmG-K*~V:Z#yWOu&D;vv#Z#7>8PA>\h?SVHZx&ѯmvyCRwH I*uO2ΞmՐL s $[f(UULJ'󪺾$,h@!I21Y7es%c"еgt{nn幹{+P1O$5"CsX|g }WۍJeX">Ifßm=Njt !%qּX1m;3y~ַ7$*lL+yIF%O~5wNI㸌p*E:u kUZHXr}2M:2hL nWs$5 >x\KQjvoM};X5Hdhշ {z"bq9)tdv9]i-, NWx!zN59Z^ wEUrGU98(-Ms6esӟ+VRY$Kg^n585O^]3mc0 9|z)Z﹭ lIexgV -w9'vH֒P}B#rdv$`湭F%rKFv`r=q?u K?*1JGקnya֩_q۾9$g;؜\2"%`9nkZDoQx7L,홬`S#^8<A }pvwӡW.J1r~JExjoZ< `LT9tx4 }4PXv NGE/׬]:o!T6I3s*-y%Xl*HC. ?> % FCo<|+ZSgwr5r0V<NrrZg;xR+-EP:O]xn 2C+@x6oo7u #KnqyC->=Z ㏌ w:L׳xG`%pT9 vP^/>om;EBѯfѵBuDrqOcavPOc^' ڶwW"} r\F ebW6Qx>~1WoE6zVw0~Ő[7NԬZǞ`ef $_TMڤW6փilH ꡊdAvehڞڦJ4ڮ=jz_w~ Xxrx2_M-^]ϩAW%ks\-آiQazڽ'#O& xC?'jZ'lh `?֟'G𝭷¿l|;ƫ6}zSԞ=<x֍/5CK/d):"CFHs)i _WG*R+^BLjFt*[[kFJ mi:t$/-7,p@8˷0r20*pA EZ^l6>{Eǽ1GJs1QU$¹Y"=i*Fۊ̧9z}1@K?-iK2c'G@PG?aC(C ~-c9\Sh7}j7[cc|\ם7'Ѯǥʣq }垥(ԐJͷ|O#ا Fy^12<@mƙOȺl-g:`P->f,DZsuYXo 19Zt]<;L`I̻x ]q d kX{O=vsUdžm=9?=kf({ q}wH|۷sRMwszׄu],ZCh#4L#_g~bUO#ƒ$n^u~"I%JJmDƩi\)eې3یsOA*QQvUkx,QCtZ#. ?Z j'k젾,Qa3gq]^YMyCGu[+6ԣy%wQU/i$:ƑuẽKyV6~[D !wTýO]~'k v휟^:i3*]6Gڪcν%ſ$&A5?Ozf:Lp̊ZOٞῇo<]ck,^4Oڬ71>˕~s^jJr9#8T|s6Q$2xfjѢ-d3v?C4i'2ܢݴ$P3BmgiBI.7"bh9ʏm'*G~u)ӧ0F[i=:~5%$~$M \z]a B x;s{C W:^bjbw_!J\rdcjѴծB 7ZcE+rm8w>-f7spx^j1ƍvkV#Ak+٬hrI Oaxt+zr{W(߱B{:~iN/ o *vOxWEnK`Bv׃J^5OA]hXA۸[1i^OG]/D_@~PN=q]Gݵ~Ƀ\׿5Mw0 uG\ԠS%!좷9dVs_~}XYhwVk{+ɮ% *@?:`WQBvWW IJEjYkPG< rەA|;vNG>O&{%wI[wm8614O9UF`95'ky~jO-x_ǟt_ xJƅ[ SMQۓMEI8O>+?c'k x{9m .1gsD%b2yj5)z_x?W?|[I6gTӮ/IŢM qr1׵/x-hRxoU&细S [ىluu]U}NHԞ5sr3'M^.>ExzϡY3B߁O]D`X*ZUS*)hn |I/<]–p_lfiJ)dI ҽJcӭKXUS 1UMš'!Gxm@qd ӛrZ(((((((7W9]oC>4!sg.$2Gb+ƴ麕BqbI(} z~Wn3liҵ'q.7q'Ȭ@Ōbbn 0GƴtE$vAj *Ðp<+ UWM4}Rt"c]I2P+S #2s3La]h6=ݿd->n}`V}2 ˮg_YcԵ5- [OXlctُÒrN3㋨jE%yf`r G2#(;>P'iA MRX_GN\ѻ<)FVAb~Ks ߕxW|,+VYAr쟴κjZGmH[kFO aWq\YUŴ6^b9.n3p$'xCx!ՠ^AGr#,Hݯ`[g{TҖMEF g?~^8oy&ːcr]d @*}r&1\ '_hP|)X^Y#v=kmsYm6`uȮ*O6Fk>QX]_:^emgnܬ9Sq^oI@8Nx/:LwWXUہ_1d{;)^ǩ&`P[ZVA\ypH𸈺I=N+"1c,2\iV"c!d#mpx4[sIڪv>suڢ+VJkf/ݤlUf?jم1J%Iȩ_RdHYĭ:s_EMhE9xV!Ԟ_Q)Zsmago5ERk,(dx138ϹFf%uQ:Qj.^x/]'[N):in"HW^0=H^kxGGD5q!Q6ߘbꑌzy/.GC} xbmJY`2ij+LHU{Je)?CrJ*6k|Mm1n,$kn HrttZ_"puot kzJ /ח"P6_f!GRKE` *t =kտhj>Җ"t`T A;9nkɫ 4=XTʛ͝12<;|ĆHūFmd㻏7c=Ixm'_% 'ֹK;"]#mA_/%c9N}O?xjcUͽ yA ~<v:qk~϶:Jj3 ّWx||#nds^Ӯ~ޕ麰-nGY$g?ƼM= Llʬp1>W}:[Cu?+:qp};h(/WֵH̨*X&8eD>[Fkƚ5m ]<r3#Qt&Y,LW y~iA5:?GT]KF0ȼpéfBwr3 m=#u~6vP%Ģ|Hijc: (78'MDZc%RUDqIrzfkvڷt Yi=Gm,葉ErQ+֓Di.bѡXdLf񑜞"xL./dӮURJ)IψT$u`V<4FZhr޲݁#:\,BD3!51yst&id2̎nFud zc ;.rKs ^şy>о|'pCd~:tv||LQi7I lu`9\tOz?=b !*^ Ā6=5ƶ!Sq(J קQZtdF<*+yc`ڬso1 +|eXas9i"ŞҖv}Qj3&W@{8<~58GڱT2c:kB$,7<2CkU $4=n𖱪|;nmMlX]dHBOp5MkupӶ,!2=G0+Oৈt/Lڇ, Jg 1|ZyXj:ŹH'RK tM7N?fM5ݽ"'ʁJڇSK{;a3yu ۥykOp}IH(B?.Q Iܲ: a<8DZ5_G|iڍ)6&n&%P7}8qj GV-la0Ed\Њ|S>bp/q?O qױxCT֯<#Gg=UvHZLAWZ*-w>,c '`̻yQ'$>P[I#ێϳCzuH^q. d́О@^OL+.xAwQi`xInQoFtڂMʵXӨ1ommڳ훒W`ONz⽃↻iះZGlţhuhʫo$۠,qKcvlم#`0ֽW-/ЯXc2#}sF@'fP|qJnߩ IܳiyXۭől$hb1Ӽ68Ah%?f3O(gZ >&ivm7Pіc=kBmfbfHpFGZ ލyje%uס 8㇮POWnMgU@u|3Ǻ%4]b3NԓK ceo*HxF䑌n;@_[y_(Ѽ+ k5mVՠo>c-L \hn NN;^g#R!-$,ΘMviwnyP xÒMK$S +x񆣯K^难V4:jܺ4.d&M*|=+~>3uic$\s#$p\sCMQK3N> _'~aoR#^YbwImHr7dO9?4 šYzNӖH2{ K=qMZN=RT Kֱ4>sD_ۘQ gpf9 E{&Rw1|K6,THbI*!Fln ;R5u^L?UcM}6vw/cNIuhVxl DW2cw0-]l17Cc?? eymnzLiмeo x;wMǏn7_j=ht e՛ʞ6e ֖L=W:lQtn# o-A@8 ]=/~} W6ۦs1:~y"OHJnюz`׊h!1_VNK-;GtT+rryom߈`Xnu{KmVg*\d)ɅTMr,P(,C $>cZ6Y׌2*&U}<ic~okxJ? i:ѿooZUټE,x~be8obN{|hN^wPWZǡ!E .g2$k'ﳶ5}Hv>?{GM|gFzi>ڛjH ZT?γDqݰ*¯u?i6^*6hΕ&imi,[^t"V i ppm/Ǻ3<̯#KL|n 3Yznԟε) (((+{MUWG??nW||+vsҼjCh76Ln@A_hGh4VKXґ,h8?y>%µW%;pΤ6ݟABUh$Āgdzh' 9hr-Me)[Fk_ccLl]Y]]D ݷKꡗֱ ̸mcc}kՌ>+۷jۨ~n#ҪGm׻%WLz?>N'؅-߬7c|u<¼z7}K;{( ^GexɎ[WhG8zb}gHټ׌OQWޞ$!.n@Lm"ͽ ow6D˪$JM݁>y< iep>pH}e@+H.IR4ӓuR)4li i}6M W_ ix#۳9\w?gZ[h-s^"J;p/QNJt_P[i$专Rp0N3]/s|%hh}Z#.>Ղ&Pڠ(UzW&(9b%_ |I6SIkPy+1ܤ2ÐGcW4h:,2 mn9e%fdxI]#WG+l9aoZ.꛾]uɦ[JG=ӔZ{dB}ɯ+=,&gEqds^<1O%4d}j }jKB!wok:ᶢ{VћRiwlpFP18s|ڒg*S(FnÃ-S .3#֝{@I#q8@m}xWy+}}R̃M[T7R$1{⾋fMCNMj~"  0s8o(m߱me6PI.' ~Cz# -DV쥕NyNI40ilVP$2I2.vsԺS@Dӯ ]y i?boH 33{'5։}-Xӌ[b8W;z_~km[{pðXޢV"W4`cߞ<%S} &ܒבKlW5:eL"(6g8qsR]u4ab'r*F:}+X-:As͕"]9?Nݫoxva)[IOx5 0kBi+JVV[K;L#߉FGbBNj[iWR/S9ftHz =mo 6ܗinKD='Zu)o*K7hJm?ƼXU rI_N}Ӷ>B 9"?zu!F2"X*AeebTFrq%iF]<ΒTsWu<}$lH?1Hjr|u@:R xG}_@b"\񞼊c^-J6L=ͼ퓂c>;z[iwז-'bSqg޾^Or|n*#?͋w.ɾK0d#w&cѹ7;rJW;}5TYY =W[p؄%f1ƮԾ zAhV7ѭG$oy$)՝EBۃ ^ >?u> ooz ;Gl}?vr9\"XTuhMHG/V +?<6 /pA_G~6cx!]GQcZ'T<;sOi$MqI2brc$؛qkofiqq-2;Y8^9s+Ct+CS~DOsgw_~&;OZޟ]O #,B9C)x62$ |Oޡ3[a#Oyfi iO!B*ČdsӽqNoKnBJo[[ \qY #H(Px }G|jo5$Ign!y#d+2)\g5(G({ ֶUɚ ݀;TʅI#+٢4GS ,y=p3f<]<=+u C_o6<J&X\Z\]N"A u*%ClQ^dLٯ>2D5Cb-v=<׷t"ᖫd M$kυh֥;=V_{&q ݻФ'1̎8/DWy/^5A+/s?|W:ѯ//˻V6RBIVR SЀx5)ޒO.Tz-5(B'pG?үBLUCnzu#~(u_rk[_ml (2@=k|miltQ 7 >XhBG88 >4Fд{eSZK$SToU GBEt> 't[ Lrf.Ivi gbI.X$stS𕧆tW--ͬy?(vbg1W*߳׀Da< [4R 7:2 9F08ME-念u.$f2fw`ZR袀9oKk]ۄ͍v7L\7zz-X4dbTHkU5'Ԭ G]D#}U%p47a>W<=5[I-n-P``ea9<4"OOlm7핾d=wB=????O̗XwKl4Y44Ѥ(H؉Qp5{n{FG&8d B] zO&?xk-qKҮmilㅈ%@4 ݮSs}:{l_fmVZL@y^c[˹hW1[]:<"3t85τ.zڧYdQ] JJTH7™TE|B??U@V ݗ.IX&=+Kj++SVܚO..ia|a3\?mD8->\Aぎπ?G )L̑AP%5PXiZkm0DATQEQEQEWG7S@~E: )_omX~$ܨ+|`dq+,V+-A[έ3JAt]Yi$8c:Ws; " jZhjb[ITBq z^ ֆKq@XEKLmu5_4g@/!,\d.Fۂ;^Qnh:M^^Z`7q$MGe{4gk5s CR5Ep3C[o&@OK(GSD!m% o'@gE)Tg&zKxL[Uv+P$ Gp28^-OxSIӼ!/n..Q&dȗbzǥpG(d_;^2Fφ,'ȳy3++r Su >N [M0@oG茾F@8#!NzeAx>mK iuK$(Oz|Gh/nYs:sU:TB2q0z.BO}iXmcdxdrA" ~< W{KMfVҴȵ;¦@@\s$[X٨tũwVG5mhkm=FX5@L13>A<Ϩ](c;H9= u\qqԍW$u}Z+uXݚ b"«J9 vsm#R{ -Bdfnw`XgdgG_YBZ>7usx40v898ڍͽsmm 3 g2\eӃO=jjÙw~dEiSu.-cK'Om",n!bM^Zf8x0Yi}!M<ԗn߻$rVUe$tS#~>6_ NxuM7TY 3{ؼĮcw޼E~MD~Z i3Lb#v搔Wx/RBMc᥄ MhxUsP3r9"k9nXC3 Im>-;}.jT$曖ǓSFlS|TZKk2[[u~8nzmjsTUN[LNIN ŹNOO5O}kpJ^gmDe@zE~K_-umd(S *V &sߍcEF|pQJumڤzeMe<ͮD{Pq$9+;x[独l5nkU2HA #'ċq W~ojW3kcړM) .0ryFhfYysf kh1 \ 17&86t-~gHy$ْ۟A5ݿM]I\o0 tO=%{]cEȁv+qr'e'O<ߴ׾4|6⨵/<[sc$PiqF8RfVҞ8U2+%~QYb9c$ҡs]mO߀T]BmbK8c ¿ Sv"N dO^$e`Y 1pwGz{W;_ta{%rjW+)g?q㵙r#v< e|;i⳪ikHnnnyI#,dxdf&e.ZrÞ+aJ3I4 CD𗍢7iWsZ0; 8gꞻ8/ <_I\"#El_xWi{3~gO'Y*>!QѣYn&w.r^(&eW\9\=+8қJ2Z=t3*#xOU=kİ\x-Hl"%9f> Uc=ڌ4ah]ǀ2FsǭxBl,oE+y紕cWˎ15gFRFT9 XL`8BvH31\m^u/ڍީ3=3Ox~0ޑ>>nR Vv1/sI?4,^i4MNc2h.I0zes޽Zx4uThԺM׭m Su[-U6w ޾:5|oa4 =SdA ;q6ķ^Wi][Fc<0=4X;ܞumjhQ!Ŷb^@xxnkYA9+еvԴ.gFwmȸ IiCV՛^?z[c՗",&'Xj~%|2֭4WȳY kĿ<VPY޶`8'8^3xgUҴ QwD'k'qLSU&h{Oӣ'!Kuo1$brv$}矅 %b̳],"px]՗ZW,[ aڼJ%sтUTZC/Wt 6i6Q 995^n|i aV2I=RsԴ8 ċ"G>~т˥(kuL͸f2p@Z/j7u6{|"1]^ 2:fu_W:twmV\YH*<8ɭ)TI&oR:Uuπz|gǏM$Ӣm[&MP (Cs6mṽT/o汆tIAX)=V;Kh>2.m+MF=ЖUx(ӓ[ "ݎ 9G8'@#^_!8 >v53{|AʺxkI5))#at#E4Uo?W_ uOM3zj9|3,q, RO /. ^n_ Ǵ^]]+R]ڕ@.5o٦2s;^? 'E(/-“H;#e|oS(jmkv9 ۮg2X(c'3˯xSViz>ڢi@$(,@[|H)M5=yl.mφT#:~Ue5xc434+A`1+?e !eM?eU=rD_&OF Gj^1񇎴k#ROqSBęYd*B`sۜ|D# Նx7K-%"K$ HTXPVrH*tOmmV𵽽1,їFgVÀ@'5g9Ӕ)Gz*7kkFk[xn8lG,CxȬS|B@=2pkK?x %;[&(C[yh]`OBǡGB/+!'0/$Tvv~ 8W#_Z[pM&F浺mRfEñ z3I_o>;ͻBgbU_6%}2PI7p*38<>GַHv[ꒈF| w~|ZxS񖳫_"2ݺ0p0Ez}M_VCogK Fv R)A]S9M]H xx[l4:h!I`̗#|Y'ß xZCk}KP0wr2H<:և5 m_׉Og7 ~D]ik2Ėcq{rƧ/ݵקcM}ۿ ~!j4-[Ttk~Li<(q9]O|Cgw<wRX#*W'=5 X]sNfa/!Uc" rQHؠI~*9dD4m1=,kէ5*o.~iUQ5MkU=ߌGPԵ&mYgmP+޿)"u_÷ztWEm6@H_+[{k)om-|ˆ^yu9'0| ƾ,g}&5mi$k}ܴ%p1'zqElt.ပ5b|_?TdxgSLgٔuVÍG$~N|IRL+(U.0~  ?j=GᆃkKG7)o[C}q0*),A @>clxK|? nthZ3%/ \8)$}7iKfS珃P] sH|e951[hEn/C+:6#1NH]_úρgt= m:[I- {Uc$TKm6Z˙XWR2pAWԌݻ(} T_^?7Bkvd%I$%Ē_=O=~Fhѣ% ߈>q$ւA %D3\6o?g ;sKE/"G(O1bFNONߥ{ <ΛO+U_3fG[ٛ+i+'rs8|O{ᯎ\xH!T*> qFXWm-Ake`tllK;$_.XB` Hfv㏃gO-7Uc,% AlA6mÃԭ^+׳}:+iғs(=HH ҡ+oGl#$Ars~C 5a }hApHm|okjE9_Sn$]$oq$c>j[ va$b@5WS; l|JWOxb 2PB>̉q5^?7c-׭r1N[T毵bifw#] Lvf`sgcrCt,mԝyi-@Cz<;O'|}vDw:(ˤ^g^&u!j룀]hq(8Z!(S:u9rqD xEK;NWqy]L"KXWz?yZ}rYOkpp}|C0xK.]-~l|c Jo F{ld6.v<_G_|DP_jXCm!TOA3 jΫi>vFFvwUkamw|Hh<]]y2ZͧH yY hWNnD#VϘdW_ :sc F //`I}S UP5s||ִ/M1 䎧d}+ Zj#_b.e1'_ W8.4oQ477< I$m$QK(=NR>N.5*DR,H9APFG ,ĥz$NޟwuVcks.iH gxb2? lnךյ¡ih PV0't*q r2doOI!Cv.7\>T*#]|di.Zȑ$RxA,"؎esǘNOs}+#MO:AN+gǞ#,lφ&'X7Qȫn@댷]圮~ M+mcϵh:*)< jp9jk!F%ݗ6L3׼y:j%jw,@+I1#(<]YϐKyrFN?1ӣ)IZַZcM)'{ M;OKֶ6 $+I$MX|:io 1}9ǵ·'wg:֊O? (c ( ( ( ( ( ( ( ( ʹ7?iqorA4bDM*r=0zеZR-6VWG)FA#`QO:4yk3 [ZnS: Œ`81qgǿsS5o.z{}ZN$^jx^8FVPYH2p zFoxv F3úEZ~MA?$Yy<O{M'NúU0^I+|и'9%8M[O-6;xo_E{_Mxt5Ȭc$Y 0k5(HK tٙgE!KY7䂹;W;GCCƥS;pc]yx`LtmZiZ6vZGR0vb~_o6GĺA*J=?7|[cضiFĨ\y"URG8ҭ0HReYF\W|)=D5-wgX`{[)'Ѭ"[,.uy$n%G=[xcU屵H q[Mj־%] -7:OPKLoDb6![#ksx xsH6dM+T&9hAi'%NAHsAķ:Ekx"0OHYk$?YYIX /to(^ ^5yNH=w[wIԮT(O;=5O z>|20Kgiʩi6($O(omjPm'/1g 3n5e6rN7{PXF$g]a/Z-վ$Yp$1\--o~]EP.Ȳ܏|i]áx/M"! g>HAߏy޹WnA[89_{4X]4B0 嚧Kݽqs;fF$?ᅫ jbiSnjAijCg=&x 2#:~6ּ&~>^ kpd1wmZtSTosQ$W7Ly&iKR9k}rw6-.fԆr˳̕K@$^%Xx!^uQJFJǸyڮnsCu[BIaFԫFUGB5ʓzH.E.vh%;. *qڽ+ou?;,Sy!R9lX^=/$^/c~UƌRrt?i8>dҨkI9 ѕ߯%gҨk]mȕo8fX 9F-f+^Vw;ɸU!5K'Pl 8l&y]\ bq6e`\w p`pAQI.I=gۉ'J3A(oU"(7򲟢ӄL߳ ө,:m)oN*xĖJkB|?};}>E+;󅄟ZYuy4qX?ĹVmSI-e}JUȍ*%Ry#jTlE%#O;V鴂ђzddc5GdWD|cki[4W*rYBM"$Ӎ{¬T]¿Hxoj1s2K9\rAkEe2Tɯj,0g'bDz<4#MFMh|S%J;sp?1P|D,0CzCp2 Ӏ MkXi^-mH41 >TO[hb%hGN+5/1[r}|P@9aM(Brݛ6kpZYsKu$gһ ~پּRBcTU;c#7i=ee76x_ܾ?Ԭ n0Z1?Rz׎& wL%vǞH`xպ{H\=?OKiiNO3$37siʶi*=}iM >W~q+5$~JQVG/)xHtX?3\yJS%N:65 gfz#9ci/X 9&K:O߉5i^II-a E|wFIe( (1+e!#uvSZ+hN;֡W[2|[G%hb^}ƏKEĽ?ؗ_,{<?/>EXx5{LԮ^ÚmÜ[BRFMQ` GЩG*=M (?4*kOUOk2t9IbYO dQEVS\YG.VOTz GQE?Q*hQ CSG8?Š(*=M ?Q*hQEDӴ; a${u~)hgp <{(~qԟNϊ>=:ׇ"uk;c iBz y%KUh1IEk%kv_x;gm=[ +|w:b*!I{fA&W\yZ?$ }}Jry(D7&~ Qu6<yq+a@T[T/>!ym<7zMw cY|'JF z!#Z$Ɖ[( z.p (ʯq6KcDpx)zR0?џaDgQN<#5?I<%)RAqT(xџ?"$O$O(T;زI/Epf #7ƣ=?QS>vFIQ E|q=5:b)!s23F?<)C|r@Gg?#E>T+NFN1]o%Jn;RK]FVrI4* #89P]gω_7|5&uxRf#![2TH_VWǫX[wn2p:_>(.3pط'5@Ubg5$'tVmΏ Ⱥo5#w./$,9;QEBue-dP}{#|7s~N_\ۑ=EV+c.g{%?j 2:rtڝ5N2gI4ѓ)rG^}P~,)bX0o-)#Uoa_kTLsKQqQE&՛2x,Uܽ kZq#c53}*(U[H"|~ё_>]J,d }Y3EP]=~:@D:;=CzW|8RB2sPT{(8KU?g.hKSiFH'\"[1ߧJƿiUX]|1҂6{=V$\)?melZ~ߴݒOzYt#3QEhz/?`'TRŌyb4DR,NoEqOovzT{| GQE.T=>>[-XuN¼UbFH4h9P\r>hkBB]oN7}T,ޜp;z(Gc'in*z|~GQ2cO<E'Rh_|sq`=sV 7+|g!qs3$Ο(9z`<H?Ӈצ'OבsQO3C7 [tE/ÑfqEr&w[[dc4*9?#kzRH9;VnC`u"q?(¹fotoxx-20.08/images/KB-shortcuts.jpg000066400000000000000000002066331362435004500173650ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 718 398 0 C     C   f" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?=O nǽk}|\i~\O?ȣ'VOڽW['Q+'^}ދ(Igz>EIg}|Wjk}|>Edޏ{`5Ei?"~GڽY ^5x ?,#[xKK;x-ts3ه[IvJxi1A[ŧiF!&唇]ś?xzߘۣxO?Ȧ p@$G''EC.u_KI<"\B΢up2nv6ׂ<)Ozi;+C5 M x=7_>kG|>EzCω}jEѵ(4h-n\ʞ`i 8]g]6/t=zR}U:* iEsZi+i?"I؏;G]>xo V?)"cM(e$ij>?WڵvlxS^fGuS!GL| <gq&$WE[v@-<7|u|sGcn <1!\7֍A :+Rt‘zJ7i!+[m~(Ig_Rx{ĺΓ-ݓeiP~P~RqGG^*] ;i?"Y?j^#['Q+'^}ދ(7DI@RTVOڽΖ!5}>倶V@O^clXj\WWVgΎ*ݖgB$F*{Y#^?_!-nG_&1ןWINU# .T) v;Cae5D Տ^]Igp|=GhgAiڋ6&e)w1k7Ʒ"Ú}>Z`%~PvE\丼[۹>彴;ݽNWu#Yϖu"MBK<@I7 x|sO [L*2ך+Mn< <7mΚ'>}k,5~φľ;S`>_ f7dxN d>s'֏Zq|6z?_w ϿZ>}k_q|6z,?>}hIOwmkG_\I'ֿA?_w wmkGs'֏Zq|6z?_w ϿZ>}k_q|6z,> ӼSnړYQG 脔I\NT]&{eyinńRKw-9 &c>hq8#'?iø oX=/;?Eym=>C{j:WV-m%[^mm󀴂pA z2zcVDxDо&.JMtW/;?G;6#gw?i+-n-Rm+4hP;sɬ*#g.."XUbېdoJ_q|6z\EL>mzV 9mm,|8 i> ji]aLRxDHc(M# H99kowmkG_O~5xz,%3X F86Ajx~<|Cė:~*+\fZѡ,o`9<_w wmkG`π-jZ/u=JKFS=#';6#/Cmcm V~}_/;?G;6#Ӱ\I'ֿA?_w wmkGs'փp\ HE~ø oX=/;?E|CX@ߍ>bƳw.ԉyT_}M}/Cmcø oX= x[owi #lLEOʣԼ@"(#41cowmkG_\[¾7ӈc[)mc>\Ԭӭ v=̎ _w k>:V=RɈ[חW*W)/ٟC x*5讯N_'Kp5U_ m^5wUcJ4ZZlX_=0烜dic7(âY8H_`U; :5-;Jңխoxcp^Kk$FBǓ8 W׈cI_AF &#{r!KPKښWկK_z}?sQ \ol$dJ(s 9R8״їQ7hX'R8<ھCx?w|;W_o%ؑ6h>Ad߳mCViUehkX_<%(\<`)Bqki~^sQoc~ h'~Ox ᗉ"-r^l7(a>N8__ mni|Gk>q< Kh4*r0\֦%$w+_>R_GIM|ui:|֛D H%'py%!];IFJ#h 3_xKNS//<=:.%7hQ{@H%z͏>*|\|9Lj_@tVGn2(9,9j)_m;;k-zJ_{Q[kLR>) b\Oz#o麞}ZX7ɧ[Mfۏ?y ܌ȷO66Z{MSGuű[ 41x2Tzx, \ڃʬeB)?)|&a߆(|kU4:$ \c m86woMmQ{?[ޏO~"x+Tm=U;*#i?vqc| .J[菪"k1c9)ǵ|⏅On7W_ /uY`-cJ+-W`Lf^N?QOx7ԯ4o~ FT|Sm=6!6^䈧(wTE-9okOA<9@;G _֊, _O/VX /A<9@;G _(_xsvA<9@;[Q`0?xsv·ha ߅ ??nEß  ߅ ݢ'?a~(ß E O/Q'?a~+v, _O/VX /A<9@;G _(_xsvA<9@;[Q`0?rc|EXZCghBA>v3߁WWJZ*@t*׿1sx/Ubeċ &ajvK"LHWE,:=oSzïiibM4c=sLFu#*/_Z߄P/ N)n!ٗU=}*#<=O jzks V+,4 >R@| ڄקܿ૖r='Ú<=6,vWq,CHgֆk05x~f]n7g=8hT, $B6q +y 4f* EB7#Ւ[ F$[UV2J>5E,xz'ѯmmo/8m;^$-L9k/Nt-8go Fxٸ l Hy~=V/Piz-d54,Vp /;sݯ $=~'9,Ӽ=V1E6rǧ&YͲ0qDE2X䜓MSⅯ;{xlSM}6/71Ǘ= {E6uMԸC|gAy枞MztlrByF,/Sf4x[y޾ mrm5HV$S<`rx_>xzJڝߎ4Z4GJ]LmIp7u;Att5ӌq)E2!oɻ9ʎwk۩W_'OJkIIH-~9kX7DtzyBT8$r1tnk:6ۅ/c0FoNƍxlA|+.]OZMu0Q"(4#?MS^&tHkZ͕ݾP%'`+?슴??KMwe߫z~<މZCXB<ʱ*)bfkg@h>5_uW^'uU/Gu;YcRsahlxgIxKϼp]6h19IhWX~F\_jݷͻGk+{Jz/ umE!mKy){Jе/Tl1]L 2_=|Fwch%ޒ=̩ }TLp+%EET' 19'[6OkM43ݯ??>u흞M'E8/cIq+0-U|cAޞUinA NGdž|C៍~&$^ oi^!,+XO0gu>KySxxMme/mRiDEdNW%]I$Oe=o~ z *c$62;<۷c3Kg+i[E:t7=10ہ\qk]xfQ[CskrLnđ*8$08 k~ 2|"oxZ1IFHefBHᾠvnJ7z9Z^-uzMŏnYx@HPjpV1a2xdx Y*o=,:5TxS)&x|5wmtM"5 yU'F &FFO>?7[{;:!'m&0Y,8u}ط]M;Im5/zǂs|D ּ7:l I[b##K]7cu[;k >,#:;3m;Et5-ORuxkP4[+rʠVY9HkQ_u :wߍhWƇiT; QnKrHR^,<ª84]|UMmxAQI5(Vi|=['p  W|rG.sj#YTOk=FΑSqy]G?<)&%Е-[ID;{Xǚv6y$FW4,6_9/'3F?qV?4 i6zL4=6[ql C?ߏ$״V&[XܓBg A= |^)IxUځkl$q/O-_9iSH-ǗSxrCYy1466IQYA%@RÞHPwoM??[hݥ/:zu:|HҼq}$o"[;T.$T9ܜgIWF:t 3N!oo`xm8G d{ 4Z?<^]l,]I"䑅x:RIkP `¸zN;4S㳊Kvk3Ԧԛ~nRI(~ Y|Þ ɦRWI>Y(ch<\k2Mӯk]6;3yQ*q'wyū VOҵkXEuD G9e'+|AW2֚wFTLH@r;s/dW%%ZK7ĽS>t!:2۝V1Zs2@z.s{yZO.}$aY& 5qk[[i22Y2f5x_~9&[eg"4P>`ev[^h>,|":sIn XpA>/5i_N r7u{7K 3kA$q6dgQ!6`1ɪ_ÛVċ%6wbjN$Dy^'oKQu߃u -+?^'DYJ\&|xѾx~fӴ{˴V$0Fx(r@QMG'}/{_ AYxź֭6 M`)>ز /S6צ|DaZ[5)/.uPR[Y`JC^r׷ }:;~6>qVOgz'/KWKfsYNҙ!Xr [k:g_>.W>)cF< s'޽r;]{ᗌoY輪,OBgT b-ȯ6Fi[K3GqX#䅸L>?fmo熒]GOo^E3;I]n8~l}u=J~Z.ZXXA eq & E*+ @Z xYuk k-Π)Ir8Tg]wDk8$D)din)!au&Z,DmeҊ : qFMAM~)_*wqN[Ov?~8їT%[C#D~i5pJATVyxk?Ky~o+kjdhYyЁڽ$_yhT3Fh њ(4QEQE#?:RGR`>(_ ^UXQV>džaω?>^'Quʑ8Q$ M_QZX?@5[i^J5k;; 6  {i+8%wPm-Z; F-F̓FHFۄ;cŞ#t[(-.5=:n#(* 畵]W#[u/@>c~c*` _x>a^MxQI*+:(oBe_o9xƹ[-t -K%{*?d)A/LL0B- {o3~ xwri%q4=K[ _Unt&ŚLjPBJ5 6;[kֶLgǵf_cωΡ7Z^[Ȩ_r#q'࿇won!ėqpDd1NZm禞~?Z|KW4%u+6e U7AҨES4h:֫{ki1ivI4sjf s(yW//gT+}*mDFkE,F >m9|3n#ޥҿeR?ÿ ={c zdѹ3 Bn˿O=U]?ھQҾ# _rFV:: to$ma{ZZ4^3[]0Oql!LzYχ%ďs 6C g|f$V-91Jh_Vߦkb2ąz3 T7RK[:hծzu;D~阰f% g 8[9onuP,ɵ2t S +?%j:.꺄²HncTʅC`({z;oxwP{l.&kD"=ęQ;Y+>r VcԿi_|U_Ga~=fKld]8l*=𥇎.oVKB|WKv<힝mqêX3͜9YF;z+Tc5_۴s'%SԂ 2_ѥk[Jx^ _\Ey]H1`ss鷠|mlj"=mz HYAղk1|'* iZj6>k{yۘ?" pT# YxvCti.]g[*AU-dža~|/ {?-4'CUCObj[’Dvpt{M~'u]kMk ?4WPU]%˅Mv|URhv}7؛HG~aY7> jW],#E͉.<{f;`*1+k_vT۷|GxsLR#wö@Ӓ^ByPEP meEzFxgPݮ.{H$ l)]==k*pkYx]Iii{iw T`,~"~:+}u8Wzoo<>.7稷^8T/m;kعz&jmgT-OQm!I6l.KR@//E<o\j)ul+*/҉]wHJfj^-WIJh4mf҄ e6 "P2A<}}Z֓iZ֟IT(qєya^AvU4o߯}-!eO;zAEHGƯS,ex;e+[i<ʑGvpN9=+Ѿ;_jibö3z΋)r7;T9_ZS{lcv]x$GW<q⼏OcKyvAP1I^=}4ݹ_~~_xK_.5ׅoL[#efE2̿)x>1  Kj66W$YL_qE U ,|uOƚLJx[h1C )RP*펄7^K|44-Cl~"ook-nծ]`a0QoVݶ_OHҮ/k4˙P4s:;Բ^Dw][LմH޳0=$ZrQڌp'kݴmTU{KvFV6y b9j=ri^.A6vig.TIZIb_(3 ^5]|u|Oe&VC {t1ʅ'3<ߔ/ " /;~zs8ke_Eq]5ks%VۅQ WtN^ớm:Z8kyGF3.*AasSFmCƽ s$Y` v*u~w~o[˿id.-KyVh&*6{Ym󽼷{[fEc|SxúLJ C څ#$M*~S;s]džowL֬x5 xKZ)UCܩ_t?1-Y5rhvΔd WEam,{cv]eWBqk-A0U[<*_'O>Wl/xAfoK`$T321 x*r9| y{xB\U湒HHь%N2[woM'_m:/TӮƸLQ+C"Ά `VU{/_w㩯~O_G)i}Z^yݤ0H1{GT1 |X*ԿЭM)ĺo*=?ֹmw>3_ \YVE1=ن#1[%|fE)vw{Ǚ;}{|/qk:>iѵDcKX<"!pĂV/ x?@Vj67wɁ]US: f?f5-&طωڦ;h#smi/|x?^-|3JAE0huVa ˮ~K/jv7:Z]{@)&[VfMFv}GjѬ 麎:VƵ±M<g3hm$suh&2I`ιƹ?xU: SA* @7NI9MhᦟNe_اߊ&{_ xFP&.,K|Ay_hZߴBF*X~"X-^A4}E5K F3u*RY;>2}ͧ(xBW|#E5A 2FX 3^SzxJX<{eCM-6VCvH8 k׆uG̼մ5n #*O=+h{ZO+M[EmBWyͽYNp 1rVrVޒ\$5W}lz'/>v_blMG+Fqk|W~.|KG4htYXG*sEJ쥣OI,EƝ$ivodYdI/߁OǎӨ] t>XO pBsVRuK]׼CP~vt4 K}Y&4(uB/򸕸jCEE- Qu |ַcbrvr6WgxyM)l-<$E{f~lgivxENy}_eKIFvoaXXSXZ|{^]/;6V}[.|3(:={XB/.ü(gp7d&?m/|3HfNa żI#yV#҈TC:~zO_DӮ`PhV5ׇ4[ )(PcҴKj|wMմVvՔX@ >I<[|yw_oZ5h:ޅhMli~o )/,(ᵕ5Px~ ']WZ>)X" xvZ4/İ\L:VMcdb]TysJ~!;T[_#y`#\ɭQ{K_ſ i3ٵW0<^-kI+s%f'p2F8?IWRWL텥?coui_Gs[¶wё5ɕnqS]׌<;{ 5 ]~ x`gUGRq\2dZy_#xk/Ǐ&ib-j^~olC*2$MgSy]MXPօ<{C c0SZym߈4o dh"ChL{r_|_xFMBGS,q\"JےN=J8|6;v-WLbYAݴȶ20 7 8 x'9 RGQڼoIs6WXrZXD R0AT< Nj]+ogo]-]_k 4j>>֣ҵ ˭_[LqBbdHFjǃ-kYm"RZmʪ)8$֧>i%owq, G&ݦ(0sJ#ޝ7ése%wwN{[RD$MiS;s~n&p0'<<&~'J^|0,XC)38=5UoAЯu- Moeg~N 2f&t9So >/d[jYIJқe"1ADH>^^/_շZjrkԌw]=ȐvB0G#gs^[6]nz H_~_Yz.wa Iw2~nW{:m4K}KV0[;o-UYp 53_??E/ /uxaL7DS!Gv> M'o~s^'ibد~m.m-&+]n\n"=h9^9O~ msZ֯i$X`#?ZWY&wTS0"M(_ ^UXQV>dža5 Zž5MWĶtl923 gyO6Z友ӵ;:Cll#/46q+2pIP {_ůi:ޡ`ב_uy34y8Oj'"2juq%hZN6II__%i/կOx7?[u/ NuN{m"C0C*dd8Zb]BT;XƼS|ޟmJ1>|Ng4;rglv V'-&o3Ҵ{J M^$ޒ 26ߚ|7x:l]h4#ĞXJӼqx_Rrf?.2J{J)&֟vu~Kj~& _W7;jZyYm3v"t@c<P_xyf1G6Ĝ}jM}C.?f#^%=2mjL&ViQ[U;Pp$^w;ٞQԴ[s,M!;$YepU״FOJ{۽KKh"n3y2%(W#TEr͠jV`: ;$V]Νiͬʮ`ƞTj%# wR\[{浿"*]kEQTPQE1]R⒀ ( ( ( KO^^?)i`S0ET/Ube_ ^MJx|ӭfGyRIG5,Yџ?|jtْ}mK~gM2% (co4GkMzOe,wiiFQ띯@b2qԾSW֮.]j([u T*198_7[}ɯә|>$>oV6 u={\EgV ;OxAw7:S^xw MCRqLҢ@18$4>m6/ v_*t+Fp9|>Ě7ۮmku+bFJc}ˑQoPVO#ՏFuD- 7O&}*MM\ M"GHhVsUm+A^(IC @i#֙EխEw5;(u8ʐ{{u-I{_s<3O_="'W~!}7I4*$~P/ϳOO@ JMOmRQLq#,$uO5!l~g=+ټa=5;V$lHeD0> 6(|bÐܭ]]+y`dfݓ99vnO%ﮯR#o7mO|Y?5r}=|A-%AyشN59$Z~ ^h:m(~r  7+ p,9S |Mfem6D,5 )(Ky_1rp;|'=[U8b2M{pEۢ+my`5jM[ko?! 3>6xj3hi~<='w4ZO|)CIk;" vi>x 3w; J^ Vw= kw";E4enk|&_2x95]&x5b^H9rI#<*;^5GXݥ֣+M)f)NӃѱEr]Ui]Zyz!;6o.~m?wōǍ5i6:i"{mJL_D_E}x/ ͨwo %嶴kmPDrd r`kt~Ѭ"{gS$A2Ģg?(Z )uxJG\j%G4tV%6j~ y-[dlfT4KmkUMoS:-E|H\ ?jO5}wKŖ:MiZ݌w YtTScv3^xWEҬ-Xדc9gwneW-,j:U ˍ)ĶFYXd"@Ld:gHˢiv~F>9|G57CԴ-:kH yNsJd!'<׿h|Dt6 5Kki #(3 Dg ^Ċ '{3e2-mŴ|ШRz ֳkɡ?-o!oV.Ǘp y^h2w0$!x[ZǷW+;w~x{wiNK>&X;B/-㴂7|-p^-~?)|Jִ}W\>׬dI8VREw8$Ȅ[zuwy{m5I4)*ŘĠJ g5Hͱ[[(NZTv[ZyǗ ^!ӡ}B&u'Y%!V| a^S|bρ'fMG+;;-I,L.3 [k߼G3 XéiWn-g\z]'w4O Ki+ji{<Ґ@vb~˽CM+_>[OcJ(5ȟc2[|яc+~2~# NG:M/RNSHʠHX %P {W/kexl2̶vɡdup@l5_֍/̘=vHk:ࣩ>uCJQ8VGZI9@ s8 +XGյjwPyGKYI 2xv:XӬ6#Ɣ֗heE(ffěf=I's[MoC;Og}qk5UeʕU ms.jKk!+,4c45ƛY"k~p `+O_j)ᛣ[J@lYƊTqҽYuk,ҼMui2kbyV1v.A=2e<xɼQc=t|ہo:yE`H.'֦-T_йjmݿo<,mZMizwn巒IL>7^|i׌"Kյ~-NRLXFz^xZZ6o8-..eRy{9,$[n P\ƳkZ1g}#>,DYozտ/)| oị;MKT׬_CLYdםj]e>mT\Oѭnt.ROs`y#*޼ר|nM_ i:%E}bPVubdVL2A" iCMooky&;udI+],vw Y`9TKmin|I}7:qO0Qdl3ol˹*/ ZV>-3s5J%ˌ[]z'ďٛK<x[acلjwf@F 0+?<'j_\{KI{UCFZ2W>[ͫ}ּtOS9t=z\] CӮ?Bkx临ɐ,W?)i`SNEokh ;UFW ?)i`S&L}QP1_ ^UXQV~džaxDGڿ?W+s'[x> *hE-rʰF2ac@IbxxQ(|_kqklCtj]nPc91p㞕|g|BݦJ_F^[dXc H(j1P^[sdy"Fd19$ѰQQ\E{mżqo2 #& d0#"QUo5[: {Y$1[E4;Xrp ZQEQEQEQEQEQEQEQEQEQEQEQEQE#?:RGR`>(_' Tʾ󿶋^BaL_? ,5 \3<\{SgsKƟ |5| ԭ;/o4MG k|w]ݪakB};{K˝sKHcueqSx[V5}A%! n (%$8O|>'x~<7w+khq|̻p[n9q޵srkXPM=l믡I.ǟw7T 5G|BLjtO=:g|9j,"Ֆ=/3F94t94? `:.mLT2OCMNc)nGC lP7߃Z4޶Vh߈~-~%ny /-ƍ^o}X ~lcfd9h畀]V'&ޑ},V7KymO-H)UhF "F*@j\R-}j/mCF?x_d)ֵ[k$~$'i#c@H\(ϙ~,ŸzO xWW?5+/]2gmsmv xrsN+:5>mX[Sͼi3su0=.ڣV;S@kV_u沴ݧ;nߑ{׊ZĽǶᄚ]yhىE ܟg+ڻGֳ_[S7*-^tWF_ Lj3JK &:>kwmi^70lm 6A9Zv6"H#@G)^3KKz|_^HPdMρ?~/(hbA{e3 O nϋf} ⮱xQ|{:@>$VFI ,gc=kO:~sZmr糆4V8d NQR'mtt氵m?*(žOAU~[=Ÿ|~t|Bd74 Եx5;g0L9R#215~_?|5=_[ڼ𮉨i7Z6sEGc5< 1ۊlno/f4omFd/e@펕|9%;J"mrT~՚%p//O/][KNiǜ$|lR9}k]]Ox1`0NTk;~lhQ\π"5 szOk5 erFSch(rG_Ag%Y)R xW~7-_ˠFL[HAl!C@ܢss m(qJA} I@b((((((((+G%-? uzy䥧O}QRXQV>dža~|/ {?-4_\? k9^ 5 EGv8px=Ѽo:Y%WiuBl89y~-~Okzڀe$ $UGIIo {)QtG0<9tKAˣ~QE1Q@Q@Q@Q@ τIu[ϢVfKdh~z)SN'iWxKGbgԴ`։~]gY%2b<ʯnk~5:rjˮy$VIHQ3vMvb6y#IIPdƬ7õL- ltzz-w.|G '6M1 5Pkחzo mxML vrT ԜWx#xJ ŷu?P屹/y$pLs{Ǐ%xgbOB]\YMeS:0ʱ֪ GmWܴș?,}g'[{Ye|=<:]$`=bAz~AGƞ1=4so9sL0b(~^z93"H+?:9 *1 >-i$viߛϐ~;x n:uén4WWo*"I,?ig`A&sR厵sMe4H֢{{l##$1VEpJs:mkLڈP,cԯxdUfmÃI 8-Tmr m}lW緫gn4?~Y/OIv,cwx,G]sEla~S@5J#2('>ݬ(EkqSL]B11<"PPQEQEQEQEQEQEQEWJZ KOI(G~|/ {?-WbEX_.Zhkе=Wž42ZsY$~0&i&M2[ ܇hr)!+Vm/ +_(((((i(/S5xIӼWk?֭5 'Nyt>k|Ѣ"]!qٚ)i kPn_"[Gj`am@.#a c ?G,k==f]5}S+x#O%I6x ۔r9"qh?bԺ_O#Mq_tMGJխf5lNOA!L[B  k5=+>*x#GǭjWK:hmI&vQvI Rth%tD![dܖ\v_kF|6m.-/`Iݭxc(J6;╓F+_1fn:gŻ=ݝ^8ɼ'&U'qڬ2 PAn_|#Kg/nN%sm{H oG,n;YIק7ľ&?jĉ}x~[UD]܂ҩzdԭfտկ_1?q|9:ž>j1x Jgŕ"N3O&y- 8>KcZ4/Zg֚3K H1K x|<5ޥj,Luԡ/he]BX@jwﯛz&'^Kwo(jvvS^[{ryTI(^XFq5fQ@Q@Q@Q@Q@Q@y䥧O@?)Ԙ*@t*׿1sx/Ubekov>{R bS8C1h9$qVoYUcmjĂ%Շ'os]_w]#Cu;}OWҳlTLyU ISJ/iz] mEt˲T20v03޴{Kn/+7oF*FQEQEQEQE%(@ xeƣ[WaM:ƶցG ys \* #^== |Kgdm Zyb-gI¢;ٟ͉YHD=8jS4𿈬|?4L>ncI9ݲ?'w_<o ZFH軝ck:JXs((ɿWrfwϙ^~%.5 ;Kzd7|G& >>?OoZҔQhBx'߅_G0kzޕ=iw[%P!2dA/CqҴ6Z#ŷu֫ok,15Q#y*dg3w'hII_/3j6zg~)xKLLk8oc9'1@#08zbu0xw0ݦoz$i Q+x85wM߉tvƍ{)hn"A  qz_md4_ ǫj̻&?I*N3"NV~kyBKm%mguѮ9_iW ot{i6AtU5\ҶA4M{.o4Ktvnwwƛ-S_hU n_w8]C?t'.Gi FČ6|lW`8RA<`$کO ){>U]z3δ=SYjͣAJ.bP6%^Y/JEb70Ğ ׎@ͬQ]TľSE1Z|Z~A̱k:}I''ʧ[?$wx5YKm Q7@mJ}w@$i=ςX-'H] =J)o[SNOٮxW᭤ō֚y"vmѱ>05J|2:"M[Mu pIltoG̹+öikZ}i!EĤS!"1-t_GfK:Y}R@dH F=Z4ۣ+~ ݯ)E4O+ڷ< 4ZQj}cH !V68'8CI?:/Uм15̒Ũfh; rg/Z캧?hLj^QvV̎E,9&#!kc ڥ`X[[9fd8$U+nۏNzT h6AuMG^-t6k")I +)=Nx5)nR~^G_8xBIܬ ׮n,g=ZK,:)3_ZY|bsI&ͣ[B-K@FT(b0=k4֐K~mXRnO/QE((((((+G%-? uzy䥧O}QRXQV>dža~|/ {?-4vxZC-ZGP>#-&9eb;XJ~3~h#MY4y5(tm I$c1E&Q&2A Ex[D/ khReSv- dx oO z,oW>RʮPi8aQ?7pÝQ@Q@Q@Q@Q@~>CCүΙ.O֭u(Mi$Y<26?()2Jc`1\ZC]5ׅ4de=V/#ctuK<9x~z#'Ӯ 72.>feP2xtO'@|eoGvw:m.-]:Y7,A(-~w=//Ϳyg-VA>_}H5=P{9{eplsoc@߄nӨxvxnSȵ~'k ieBz"޺1,*8FZF&T{+ '.?QE((((((+G%-? uzy䥧O}QRXQV>dža~|/ {?-4<|B֋閚˲&C ՝w~NAic E$/tȁ|6:Z}beΥ_[iu'b%Y =ɨ|;M#5:ޕr NK%v*[_w/Ѣ(QEQEQEQEP:@@2 ~"i|Cn Sȶ OFIʁ%8i^'Vİ]3F5?H[ {vI?C"y/m>.x_A-]GEE# 4gwc**q{gբ-nd{-0 ]4Ga% x=9w*Io5WT*,>*4M‡.MqkgQ6HUڡ][q㯥Z~uymmwZ߁M%K] -rvIj\MF9nk_XgƗ]Ծ Nn`¡sIչ<"収PID"+8:zՕjw:~n}'^o5 YVan.E(h)ڔ~=7jߋ_~Kmޗ-$-.- ` & f3WϞ<🄵nT|Zt C`#{r"n$Ɥ{֗-Ɲm.k3[1#nGҚ7[m/k]h((((((((+G%-? uzy䥧O}QRXQV>dža~|/ {?-4fT:e `2 $q14^W=v,$𝵴fmWO1yƃsc͎EIl YOZX{W.,0"O&(w<ot5K[_0z[/EPEPEPEPEP@Ex)P4Y fHhga!YeIɧh?/-5gP&js&w=DjK+ 23_ܾo=]Ng j,n/4+z执_px'I̖Wi.eٿͿ՞Ex?~Nl :Clvm tF[.0B#w9z5f5Pךkץ~ɞEPEPEPEPEP@E(@H߳Aszwe^kW1\ZOf?0Fwt5wE<9/E}Oơg]+iwS,GۈwᘪmdͶ>+w5߉?[Jm^*؅_byYC|}?|B8<%+6m^MyH|[i|̑1J6_u*+ssmwcV'?yūjܦϷmD;2#>My?fh|!}'ދr6~4W HS̍kX^G}PodN]ͳ/+!>Ş5ui<_I^Yߵi-;3!)yKvT֢5 /B'Mw6J$6`H U_ ! Y5=KCԴ[߷XjZKƳ!aѕe9y텥O?¸+=s_o8B.t5Q* 9Rφ8>Fnx//nݮ]@/[q T+'.^?Q?z'g7:DvIݛ |ֻKҭ/ ;O^1բknf[Q$rg% ckl:zƕu)X5G۔29鞵~>6> [6Q|ok-.kβ Bg'O/':$Ԣ?pvٯÕ% Z|mhWomqql\=!r Ϩ_EMwŗm{O)#|=,;3,~,7-/Rx^vƤ79fz0k?KÍ'Z^>5x}nwX̶N,娭`CϭDL ٍwtѬ##n]%I8 b>g-Zъ\KW|@|3;iu Jl%"[w"ty1w50C#m~\֙nG Ylʲ2ѻXVv4䋓S֒Q@Q@q|'}:\[-cQ]Zeg,#F1Pi#Eaaui̗Zе7)Wg[fy4_W?~.#NxNsP[IfvA#(%%v7=gkjEZDr`gd<%KJXsRM(_o}D:V d.[]bh.w$v"uM&oou{ۙ<%YM*Ȋ@:+47:>]?~%X-hQXqnn.uMF{,NDžQ`^cd|mSNZt8kPJ+ ||ch6fa%ZƝR,Fe #;O[%n/nm:̲لb|$۾lcomu?ڍݵڡVsT `p=+ğfOx 6^M`>k5M,jd)P/FJwh+^ҿE[;SdmKkul'U &XP2\3+?>0i^ JҴ=n]Tie~Plق0;dc'5o7\kvtbg5G4~)~mm-}|զR^Ԯ$R/ټ$9 AmKN4 K^ VzU*ZN 6xr(ھE͈(*8tx_wtHt-'K8}[34;NXEAEP0((((?)#?:QEHbEX_.Z<5 \.yd񕸂CF=xo?[^]l21 sxkK*G,4yc ao0݇RI9ۿz?j:o|aqj"`Lɸd6@Ws|UѼEύ5#SE Y;6JR|W~$u|u!} K`䖪-ypivdk,˶,nGǸ6O|Z|g>sHP88Azϋ~8jyc q= h?2Hq!%~&ldmgfA8;sN7ߺܮF\dOZJS֒((֊Qր<ÏXk:N y\$8Ɍ 2,1msV> xū{ ů%ݥS%uewQ)!86go?|okXI>kږZ_k,e]&ë 6DClW9$(a_8{'ol|MckwqxះUSnWPԐ >r%z?e_jyxF&Gqh|Hu[,m b -J>᭾_3|K{:_GxIе6iqK}Ď`1\tO!s ;i߇l; FB{fپ:(m[NH#2[G%B8dx?f-u;t|nt%*Eur$`V\Z''>hu6T>xSz?53YhZzvypJݹ`،NYH1Z +i6Z-om!!mxGjaWZ[d]E5Ug\_j-7z߈N*3o5"cq(99Y+JWܯ__Y?Vsa$P0*6mXQ6gnzz? K745KKI"77 d7dža~|/ {?-4G_6iqe ctTwDpŹyϥdwKdvz4Mmm-'NH٢Af\0(4[Z,-Dh-RP1> 6\l|uIƝ^=ag/%||-‘*JN6?y^o=7O𖅤v:&gcg/mkoiqA'?:(V9恥N{ cMլ\k[hJ M븖 `#HaB$qUE:;UgѴ5xVmR8M^Tα)<8\ ( ( ( Z)GZnGËV7v`Z47,B-1d%''p涠u֡emF;ᜂ '繯<k{"NJMG>94ȏEX5 ;0b|͏/VIOljQnCӴ Pwk0[bRŷcʀ|'PmW:~7'h7t?Y!LD׺6<^BsPqU< YYǬxcFuHRZ}[8 PNAk[ SΓ: |5SJIڻ-N[8cozËĺ޳O[n漓M2ijl [ȕ`3 I6En>m+Ez__*Qwr>4MZȾUk"*px֮G[&[.MĪ9BgWLO3HXfHa )}3Zu#f>Q)vܿMVzȜ eWkQt\y%YDoZ4֚6 8]h)*ώ1eZE6oi<#3`w,I'$ƾ?.|ͪͩkZFtPa[t[<e#_Z>;>>xP珴 g;IQsZ⪟"9;T8sRz5-muf֟֗>(# ,#-'#ֻNyMχt %u7Z ҖTgL$uEHD#wh^&<:Zm.nX;ه˓ԧ^#>htv鄝K˷Tz? kWW-}#IUBmt̞_xᩑ~Im:]G>\躮bn4!eOaKdF1"|57xkÚg xkRtF}-ua5ox&oad֮ I=S^&!KݽoS-|}ޥj:Yj/'oXԿmsz {8ɤd^ڼGmn4$XKW9>'|Sg졼۸Z2n ;8hJKt /4Q5\cso?1vgv4_3xƍwhxðs{̱ D"Ȁrè!'~ 9nl=d6z| A+"$`rW:ZN?csEKKџ{FM|/@M5E/WOҬӵP݌CJ_ͯo$4dQHbѓIE.M4PEQEQEQEQEWJZ KOI(G~|/ {?-WbEX_.Zh <oϬ]:Ưm&Hk3s$j%AP&)a@sL# ukq^,qVߍ'W-mifs&> i7iwz5&xUKq{5 D$aiCT6NߖM$/w:/}[/3M2Cr{A񷇼U+Ţ^"'ayW8BF3_.6ڟ MӼM?<5nw;Kb3g$OZ_ xb?|]Ilf5b챮!x>n[}%h:k[ש3<+tվҚnmIfginM x?3apo|e<%u( ;]h 9,Pml2g n-ַs=sw\|]LFC V}Tv`|%~ Ҥh>0Y\[7pc4XzFMVO}Zzzǚ>/z=}o> yek$쭵ekͼ@ZغlcĚEz fD2[L2|3-~xN[\6>5ՖJ*we(>o@-3Ú<Ɩ?'{w͵C!D@@r*+j_k}ֳ=] ~K=!kT1+4Z,z˾==P\:9q;Wxz4Am[wV}R]\[]HlYQ&a~ ^ăִNeyR[5A6dYGːI{-iIY~r.\%"(((((((RGWWJZLE :?cxkj*׿1s@lĒSEk4MO3oXHw'|L(i坍sjw~$1,ObaAX B#^'|Emlg! tF9XdSx{ku_Wy,G\];K*eؑ@1Uym?fFz-Q@Q@Q@Q@Q@P:7Ƌ[sG޽>+-KU]UvحuaPm6E5ʗ%a_ h3]V~owĺZ X B,4R0v۷+mq|!5U<$ 7y:kѶ8% Gsו>5fm_iR~W^ֵS(BtrPy>뺫m WGĿM<yXXt׶w-ڭk[?~(x7O.6WEG#F с#|Fg-E|C+tg׮إcfa,Ji}6 N+ThKgy] $Fg$!,s&=kUUJn˘%T`̅ $5M{[k6~W gwsz~__OM^=F4>6NOR&xbOiɯݪ6q> c_MZ7Ǎ]ҡZ_C:"O5RwOqזWKxg]-~U0A"H{S(fx6F%fg*N/KM:h}o]Véݽ~ՇɬOҼsℱCu&[NG$AywG]Ft.} T F!9PK@ZyV5]߂u iy , ʶRH vڧ5 dnzi.k=kkzޑj۽;M*/.#mqڥ ֮z k-Vvk$RjY1e>YoM8_>=x#na𝮅g}q 1*г*E_ UyA|>7gR[m!2VQ[t"$@o[t܊~'Zv=¾-oëZZ)eKspG<%]ֻ}/ >'E%YiHcH]&@Q| Ӽcx"X|j}wA%V[r/\IBÑ\q|`]WJt5 5nL; *Y\?I$ռ(ΟXmB4}swIz>{W.~r fHug>e<sdm>vYKsU 0kе7|fcycqA pF&#wx|Oy=3j|76Zl'%X)*\݊KVK|tɿ \v_ N⧃?;{'E<;ۻ۽b=CQRZ"M"C[svKF~EPEPEP^?)i`S+G%-? u&袊<5 \^?cxki6hQ8b.@B89_7I-;ٿ+zލ>6յ :\M.|p'ڱKܩ&}g|7-O64ä_Wv[bH73 X!rNNMS|/ҭ<u}4}VsFUa;>]OOψ~4+.olgx܅° NoFKxzW;,WDBG+kW &Y%F%U *9'*iz淠Gaoþ*_i#HQqM95~7|V}M9/DֲHyoîl0z>*v< 㩼9:ğcy2@#S ~32zx TgIqk=,K<"Ajʀ8Z<}:9A[PKwWeCǷ3=ꚼ[kwo] +rҗ7G] xKMbKҮ.^ٱot7^ք|o|9&*j6?gI_qPZ|5NaH+O 6FiI*:f;⎱Ih"O/o%o1$+DYYrHAMY;V_jվ[C,,xSQFU2ljvH-Iss")6`c#9 xR{ح<=2k| tʕ5CV~Zߊo,d>륍nݡq< Ͳ[_|]hGZ.5.?d61hdo1Ӛͷߕy|IsY]/]ΕG|/jEV`8Y [|STt/X4TL XJgb$QE;,_.4 mψaxRBo'1%PćncQ4z:dִv[ȗpIlC?QվOmkOj)}7Ѽ=Zj.6@\}8@ lzwSŗ|Jҭ/l56QؼRqFvY^_w.7)'dey61_ ͬ,I[+K r#0g I3Vt?<;]VKo3t-VXF.Lbu*j_٦Ljh?tO+-{ md+4]Y~G,;dxcW_Ya qpʸ a+(nnn-K~EQEQEWJZ KOI(G~|/ {?-WbEX_.Zh,|Awv+=)b@D,1cw4"ZipfCgoqr8G Zi2ܡxLжYiuxP/jU:ll08|NO9&ow?wbf_&\*( ( ( ( ( Q֒ր x,蚆>Xۈ1;>$q+iFM[izև-zVݠ{W8Y6W8^a%@4ɼ'^¶b[uDId-TA8${kWK=gLԵ+]= t^4hUÙd-;VeçgK;y|_4]*K ( ( ( ( ( ( ( ( ( ( KO^^?)i`S0ET/Ube_ ^MC㷎մ+EP5̖\Ge uAyĖ~ k?Q@Ӥ>Kqw FdIY 9`@J_i:ֵOz_5]܀A כsy}.{]CS30=v oĢ[,IENrĐI'<8Zu+;7oB;^񆙣pke,s1ɓe^;6x~><煿 <kPXOZ^Z:5C0>aRHW<+1ҭ/KNG721*$?]~^>^^I쬌1]:G*lcK=f_%p## oL'-y"kUw , -tZm/⥿. ͼ& #*„0n;8 1uRi6+#CH˓ (0Sߧ0V/y9S|qpxZw}fϛsҽgIoi[ Zֽ&Hl)Bhܓ^h61;IvQL(i(h[oڧŇK?QCG4K:بeeHҽ@<%ROĶm5ƗoV{UˆW2*@LnVz,%n!(pެH§wO 4\W7g14Y1uwـۻT^\%~{w@N+^~韵Tu |?oZ.R B,(/Kl!Z&^š$}O Fkeӵͫ^מҒ`,~yT0UQrOQΥV-.-S` 6+6KӀ+X[>V_ǩQE,((((((((((+G%-? uzy䥧O}QRXQV>dža~|/ {?-4_ǯe^}gX-tk[ĵioP@$q8<5kx 1;;{i Cvq<-/W.4=\N:Kysʌ%v4xJdַ^%|S4M6@np9$sU/o"[z: (Q@Q@Q@Q@(IJ:?~.4{ms[ԣ>;m ty[eMT-+ 'D Xl סxK hI&u\^Gs+,y&h Ew_5۝73A;"62A+ιmOxo-?^j6P]\\, 1(YvbqʀWz'<cAIӧ{˫lqU !2OZxĖOZIKA(1A,R+(aAK{uY.[I}ޗWq1u_j>!Ote viXp3?k^]@t8/?޼ } !>io8{ٷAfywukpCXAsXh &p5[ۻkǑc +@\Q2[]o+5n&ySG $~t@S|9P0O]|qxfUhzg-=D\,Q%f_-28s\Ӡg׼AZjwzŶq wpH,K]āMZ{=h_?RuY4IltR)-$`ΐX P89/6u=F((((%QF( ( ( ( KO^^?)i`S0ET/Ube_ ^MXvYti| Qd -Իb/-8Ó^߉߅]XًMqY4Xlgs b;?W[YIc[Og.+8Iƒo|-Rsݮo^frC9fHe%N][y_dkǵJ)_$׾#v⫽PI%/VУ (R:1⹏| 🊯B5=N.|S/ Njc_=_I]O޷XE_u[ oj.ҵ }CD_ $ 18q|ro jڦe'P76w0 D~ :+{sZܭ7ױ,EMY.<1a.鋤],o[R yC=ؤk𿎟S4kG ?%ʕpwǷ OU2|@|3jڅ+ 7lPaU[?~^tsN:ךe6 yI4VQ*ڳslvkJu(=/mg}/QZ7{?kr)n6̑Q#b`+Q\vCy'bu4M>v'XE9! Nw)nV-~ǼMxJW{5|O=[.ؤ&i<Eli_|w /ZVe?jjߤX}٘DrbzWxWvDV.K4r)WV'$ھu+\[_ KGg9S $}oЭvy?/-~|p O X}׍fW[(|`a9Ժho i^.,WBx\fF|*=>4ۿ gdmK'!"TTpGpi3cx[5KNմ5m4ː&##%OEi>WQJ;]M/鐓OjmG'E͸bQ% [k_O=iv@Kx̞dvvXӄjMߧQEQEQEWJZ KOI(G~|/ {?-WbEY\3abe쨨aUi$ OgĺW oCTo^[6OP 4sğ|#[NҵhXjZ6]3@AI:\6v1F(m4R4b1ER_m\k)z|W(F @(F1F(\QJ( ( ( ( ( ( ( ( ( ( KO^^?)i`S0ET/W~eYN#77VbEW4cNkZU֯p<PѸ* GjM\Y&|u.Oxe?h6Z=?M'8/$f8e `ccLn?d/?x 4O .2y6䀧$c7ÛMxZ9KKM= Z"X #oE'sܤfp*4W. oZyI_}toiy$Jѧ><>&Y~xSiOdX~H#u,e@)¶kj+:ĹtK X&_ס-MzNQmeڿv?<|.m._Gۍ^;, lFʹ8\i̿/u߂+>a|NLV>u&qcindդVs1Ϻ=3vcjI8?h_u,H +9.$HcΉY$G^0HhoC7lIYt/{CDpM$Vzy_$V/Owgm&G|W5֡sLѥa9QWyn800rw/ُ=ᇆo~ JuWšܚRٮ`KLc;]SA;y'?c?o>ZFڥo3y2%s99Oο.7V)s&c ˗ɑT1ߞ{R)œKk{v*s溴f[ƞ OD&umfl{&[1 .~_W={?|H.=NIKY3\$}A g#? wxgF5H{E'qeU 'X bG*(%Qs^gZv鶞mt"i֐VSd2 ,2y>xsI𮚺~h 3f؀ $I&6Qi%ޝȒ_|WyhĚ|Mx{Bc75rM6^Ylqc0sgG/7χ׆46=sgaE3H*;rH3_ |[KB Žƫs$#93+'RwMn]NMC5—u!XNDFT6viy;ۦ{%8KNx=kk9ya{fͽ`$yŎ1@=O5__)] --4+m>ՖHL ̏#&d#<״®־'5Ϳ",U2Y5ku C.FeP 8J֑^j7zncj3}{Kd7vlk+]?VdoNm^>"վx__x_ѵmJx/uq&,p+â(,Oj>Fo[&㍿dža~|/ {?-4W0y}j]n(԰{C(=pF!w,n Y|-{-Q$;ftYq6GkޝSm/=ܟt/U i $2۝šw忖Q__e?i 4MOŖwn..-f2`H80+Cվu\âi/ 6jܘVK S-ͷ{v=FM{\:uC Ci Tֹ_|@t_4_kr&[&$yO'@av©eme~&n)mn/>36n&xmJe6lA)*rNhz6l%-n,"Kx5˒- 㩯 -o4?,>'ŏựw~@Xx=ulKX"[ZP;Bz|^@C[C#2vng&KQ5 yǶ#aMO ufwDkbA!] NlagAiJNqX? |[ͯhV25uiihehYC ݿ__%e%7uTRR(((((((((((+G%-? uzy䥧O}QRXQV>dža~|/ {?-4)Qi7Ë{);-3Zu+(#{H( Xb=#B7>=~ξ'4,'h6+(cڽ~-< o\M;Kkm7}4K=i(=hHQEQER|m9b+F4_ZK34eZmh3⳾^?WxYmbyRKp0~Btk?hM9|1yfLKFE1ZW"sw2O6=&+w*XӚ$~ ܚ}=k$~ǗڷÆZ]+P>-t 2پW;<Lsoau0=BLgx&z'[Owv ?$Y%КXOa=hxWZ1!O|̨xZwmkyv?>a-xQu[Y$egc3qIu{C^\_kzíx0C8&(@~eb9!lGVu-bM6 yqC&܌urD?g?k/yo,mka6w)]A<ߏ~IFȁVo0ns psɡ#mG@i!2$)fBBpIo#v!CԞTԡ,ʲ>cFKSztZ柫 S&6GM0%ݲwdyܭ|N+w7?7Ҵ+I.hΗiMY-JdU+boݏXK|4 -EP(䍏1yEqҾ״7TtԚE1D.~ R/Z%ԛڭna9br䍫\_,.ۋOnvE)IH((i(?ǷV$ԡ~!k $f܊q@$JDsZUJoy5x{m[ 6{Dȣ2 1y?VZ$Z|$+}>"xSULJ|Q^k*PKtA wȥ d/:Se3'ĺ||5TPе? k}#7ʖb;@_jΟXԣHZƄ7g|9mal@gq|5'>3*u۸cdun$D7F$ I|L~i~,'[MFMivrKpqN\w_~zE xD>4 k-b Q9E[J!`A98f=Ok.M·txeYUdh]KJ78R}Ev^Lj|=#D[vn{8m&ܟbQI-Т* ( ( ( ( ( ( ( ( ( KO^^?)i`S0ET/Ube_Z[ml>;@/_~Mu!i7-H'|R4k_'c=h=Ri??>]?7|W\?썇l;;To K(.\)Y#נ3=g@כ#]JN X4,3zvh>{TO_<3G-gx>%Ь[/FlQDF1cb1|1Ƌ_1^u4Z,4׺Ken.LI ܸTe?4Zk]z6TgN!OZJ(EPEP@E Ҽ]lbƿx^ՇZ-PU9Im$lVӊ?ĭG_5cLDz^yx Gk,-({NhJV@RKK|~*<9L:pЮ,5iK6019lp})gc~7GZ֟؋{i_G e"0)? ]LC J,`i}+M!ƲcM{Ti1LQd5_z¿h6J?Ob-~g_t+PIg#[G+A СT($f|(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQED#CW1R=W{i<ϵ|8\ mnڍ/K/%7,9lwb~n19yff;Sa}I֭h vc4]9 ӋeKc'hos.og *e{jWl%eFY Nnd׳s}9#xI#GS=XC, !y[NQ,WȦͰJגGBNSW<=ksp(Pс apOC\׏$iAt{Z]%V{$/p , qK.Xd{)l\GO)_WL[rmI띛qPE=O.5z(-QŨ#7Un' խ;r?|chUK*{`I%X|/|b*/Vam:Ak"Kui# %=໓8sVcXhaQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEa[Q@Tjߠ>'AQb4Q@LAK (R{}_~(T![Q@fotoxx-20.08/images/KB-shortcuts2.jpg000066400000000000000000002213001362435004500174330ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 543 598 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?=O nǽk}|\i~\O?ȣ'VOڽW['Q+'^}ދ(Igz>EIg}|Wjk}|>Edޏ{`5Ei?"~Gڽi?"Y?j^X oGO?Ȭ{z,O?ȣ'VOڽW['Q+'^}ދ(Igz>EIg}|Wjj5OEJ@+ou)>!_k6^ͼ$ϽvLvҫgÞ<|iN}0x{|IoXEtFՑNgb:cқ%o9mV(ʉ$HTHwЌEJUkgs/[Oi_k$m! <<96,|#ᯊ3~.t{c(`BuK#5wH,mxe*+F 3z^94 )jk!P` .7ddZKy^̋R"Em|Iy,}|^mQgʍխ` @c/<xW3.EIg}|Wjk}|>Edޏ{`5Ei?"~Gڽi?"Y?j^X oGO?Ȭ{z,O?ȣ'VOڽW['Q+'^}ދ(Igz=hI40`{ [j1O >{r4Ca'ᑟj_rb\-n,nn>,d8@^;!\/D+ʁOڵ"&F(I@"mryu`cn?"֯.G"uc.yȽ Sݴb4_l^NЯln wᚯyiLL;Lp*Y1&6V0n1~8҄o&>%ȡP7En47kKTT絹 VR85i'97cS‚x_Gs{Ym#TQ/.ᜨT~3^dnO5KB5Crexm{_.N4}Pj* /u}h}3mQ?{n4񺲜mR}=z<~}}_?;x?G;6#Oi>3Ckcø> o=.~~}_?;x?G;6#Oi>3Ckcø> o=.~~}_?;x?G;6#Oi>3Ckcø> o=.~~}_?;x?G;6#Oi>3Ckcø> o=.~~}_?;x?G;6#Oi>3Ckcø> o=.~~}_?;x?G;6#Oi>3Ckcø> o=.|9௉*k}yy]lW - *@Ĩu=1ZG#AcSsjd#nI?2_hø> o=?;x?E]k?kbR,wm-ͷV,r>~'xz^kx fEP3gop|5z\?PSK/xLЬ٭-t+.Ԋ(س$$v+CSj^+MUYg*db’gh<$ٟ?;x?G;6#ӳ%՟j(5~mwR'f{邖޸ہ 01Һ|ktxtwœJ-Zr]<3ھp|5z?v l|3|GWu.bץmI~ }ǖr h65os/ CoŗB-(ϻCI}3Ckcø> o=ז>?v>B7лZ0YV;Av>(SvAgnϴRa1܋ u-w2cԬ5@w!dԬ? 5\AZ_.-m.%#go %bٸp'4,Ҽ?m|A*xnr-Ac̅')t ,v}kk_c÷ Y=.~}_:x_G;8#Oi> 7C?ie÷ Y=.~}_:x_G;8#Oi> 7C?ie÷ Y=.~}_:x_G;8#Oi> MσǍ 7C?ie÷ Y=.|xBO')͏ƴ5]ۼћP +>Xܿ:x_G;8#:]sڰY}S,4VGNUݪGB03t ,vk_w|oK/Gp|5zyvmZ;(ͺiѧ|ReX)H`C=e~&B2e,#=?_=uң 0TEl6"*ד}[t<+(4.^*ǩG~"~,izexmj员(TrMm6[SlxRM/ֱBf7Bx?Ȭok!上&H/!yTGNqdZ>Əv9trmܼv}ik2{[K= ,7:ofײ: Pȥ6Gˎ5 ^(x&mQPؤ*H2ᶰ+7MtJ/ kNskx1+$Yv|(CߴҵHIgdnYWYePV&#wn*WZyDLn={sgkg\Ooƒ#ҎW>//F2OY[C V4PHd|,|(^-&֡񥷋;5VlfHT+0܇s8_K|RjBsOMFXavop𪝡mz=Yb^\<5[4( {pRFbX¯iW|+eh:Eʹw'AbC `Atb=x"ռT菣}r,HeId1"kWGMKßn|:eFX|OW _|Com4+JdKin)-O"ui#F$C32+%mE=&\^*Rۻc5mhl5}7/>Κ\}γqy1!v؄@,k7)%#CnKS\m<`k68Nֲ|1_bq}KHZ?>O%YP 7?rO ],U>E=Tq^wo^a*O=A H c8de_=~ <ዟk:nRէHl6(k?2q[#gK4/~Kti:敡\jcJ{TNZL|ySw<OwMIӮi<sXHѻ/NHb=leu_}N/|7{Ilm ,(G2럲m}M״;mU-Qhq xRfoh[u %'OS(υ^֜RPA2eϯjΕzrMb֓d00?!Wž A]+Ɔ)bUî @v0y15_&&amh0xre";BJ 0H9dHІF6nvZӢbկk (Š((((ğtsx^@5;8Q:KݙNXr7qA־_?ڿm=CXqiڮc&y KKchq~z赿[QI_z@O EGOn3;b&_xZo}Mm~)sΌs8^Wz3oiϦj1ҵ(,,aVb+I8Pt'OZNᯏ~+A|Gofaխʵx"edA'9(}戓O^}ͯv%VѠ T}2xNV4p8pTb/D7]^8{% j$ ـC^Q7Sr$U͔>.[/ĉ<&k5#5]]ss\:i׊,54Zl&c (pm8<'{}vwV}[ɯEuoi< ܋};'2$B+]V {¹9nÿj-5 B;xfWȠIVV ׉>li\bK/,8ɕ$fmQSg,>$\xSU$ Ǐ+f4{Kܻc'c<{i~;|)u7ڍơmpʿguYvs Uc'xk^ M5]3Pӥ5"Aɐ __{%ee۳~Z/7=[5X:4+U ZXE 8Z3|7t$5Fj?ŴI~%|U/ڭSBe[]T(´7BRYv.?Z־RƚlPE%#i\wsrWvw)c&dɽtoOAE)I@Q@Q@Q@EYowEHn_|C,7S@.>4ٟWN߈Q4HYP:+#+1D`H3_<5kmGOm Π̶p2ܕR#PX< ? [Du %|7muuGHdG0W/?v4=_25]^۴q;ۓ &Bcܰ mg9Y~?{m[ߧϥ=RDӮ/4 t2Muu*j:35ϯ7bZeT. Ļ'nsk~㏅ךne{l,iz+YQPx%o*6?Ek\On[[Alc{dWJM}'KFjv8&+ hdH<ŭ/#׵=2]VA㌰hȐb!O5O j~ӼWi?x7FR6j|Q_W^*풝GIҾt5ZAnr6xَ$ 1}nZ%Wuy "#?-Ry:H|Ox *{CUX\7ۃے_g4^ВK"nL/,GJ˟~v~Z{'_OӥXM:P4eQ~+<-[G 麾wZXJĽ-FB l׭dD_h#bIu|I-RWjzLmVhZ=Ʈw5X-tRF4BD HfgWƲIuw0ΖZCeq GEBF 07e.|3aI .lC)%@08 zßM^( wQ`YHU>oNA{ڽok!?|EռEkZiZudžI,gűaS!2rrOV_'BF^5 2EMMZ J@YrH`ƼWk\>{X"мk] K5A8݀W;I6_R>C-/Vᥖ9"5k]%u{wuվ!s|<)&ҵ]nNh.cH"rͤ><)SW^񘹶4jEtItoݔE 3@zoxNyq3lb9&gxOYFK74r]: ?(nQB`>? K_Dz~emz|v{A|?ҴRe]1-'kv4f39q͏=SLOf#t# KGxoYj"WptۍY)I2f`)%'Ny~ xrh羲/>V\kn$bxՇ#p1`wdެ鹉N{[Vboi \Z\Ap.CTbH qָ_?^Ʃ sϳGTfK+Z_)ͧҼAm<7kϣuQK<Dnˆx߭ܝ͕=9jt-sR&$+}%\0`:,6@NN|ٲ-<5sꗺ7/ZYsHb3FH@0{ q<{}_zW~a2 td\"-lďfOKFk^}z7˲/~GtQEQEQEP:C#w)eŸoi!O75 {R~%|#qZj[I$i&&I#Y_i/xz;4E)Y)_,H˓vqk>1i J Y!^%2G,HLF\7j-~x#Woezl׿5N3%ޗymGSO@+|"`fv1ye߀k^z.qzy-L#2>00AW'KN >!a&?݁%ۉ^+O}Auũ[A\\ɕZD`8̏JۧMn}.~=y>&Mv MzZXaet>v( 9pߴo럴xVc'Bv)KXeiR+uvhU?@+o;/U=4f+1VIl\F$ǣ)Ki ihƀ*EQEQEQEQEQEQEy7BDw.פפr#}I8W* '\_ſ "l4q]kvS,iB~+^A.\[ORvx#y!p)5'e~jJ߳G)m-|V75zϻN$vlCb|>[ml4dZm)7w12szen\cmc4p* ( ( ( (־yb y|'ܽTDM~BX'+a־i*?uk6:f\7 n N"i۞vҥ?DE-=.𵗋`&Ri )8,[pI8]e3T<_w>׬WG8WT1-!նq?_/u :fêɩxɥ,voXHB0K.zm>$W~vM<[IF5F"FrG{o롞5#_a+MNH;I$1,+<ZZxz_ί%^b :9#^xjOm9ОN>o'5+MxannD$$g)" p e)y>j\x_!kKV֬e-n49$z?89k-/6uywk-YXnmfPD= WFϊ WG3]kt+Dim-ZG- , pxgτּ*+dv:ٛ9roo<1Rҥnal6Dy_.IiRA{ҵhşYY{Pu+47[^7tʂFp8fUm OM= j:}cRI>͐A?zҎ{4^eIvk% IJhڀԮ%H.l{}5$s"#|h𭿂5?=LhO/Dcr@xzזn>'j._ čikvktՕ.n-s ,1A4i|HV!܈$8_e]_+ḣbkױYӝ--ۗtFɻ[?|J%{[K5v#gnܱVkr?e~-Zq;'|7|̷/Qۚ"_mo/~#{NXmNxQu+SJmSx!%qY~/Z7Z]^\+\ilbXKïxS◃|Kz<8og5p$JM:b;;? พ;DJ:rw{?ݖݯ/'fี֩* [q fOo< z/ 5+;].}S["jWgسO,j̓O5VcYoGk)P>iX+◁uO \d;| dEw({+y_-|O_:ΙgrK捭&6^1^t>kK,= Jwhmo<Zy:i9ÌY_> x▨0‹}m|zjИXW~p$E$|;>7K VDӴ譶AyakV$e3B~wzwjhח#G+Q!][uq{ KSK p, ńV柢\Bkp.QVFs]$W7 O%Ʋ/aj^h'ŻYT٭߅?GFm>^Eh氐liз׷/__OGNPo/m6DWQ9 n8 @\ƅI%4_ZX[xAٔ- 2`;?Y/dV =B0ȲE Sad^W~ڮ=xMԼCyZ4 - g_vL}T ٿ]|?;xD0SZ棡?Xw 1h%%2=+koË+_ԼA}-'g,Y2ࣨx5,GIhQƽm,QZN\Fn$ *6^2:aφ玼5ᴗV^)8uhu(k9&:kcqc,5TmdVޥ-z4??j & ޏ\XYon' YQ0͎ճk^Ҽ7TMWߦ qڼd#;FI|g:x+umGORSQ,kuy# d\c!w~BoVhi"`$&ݼzҏNnso=N]y{/K[|O ~$#~o0{d axG=k7^8[ VEӴCwa{Ee1f&ߍ^M[OS ( ( ( ( ( ( (8~!^RGeC T2 7RK>PKi'𮟭[xgP0H[Y# -vHћx#~B ^xVP4sAY.Fex6f4"UW+ʀE[V'Կ&mbCwBUflnU/9|_ٮY!]]njHJyF9s^ãxG񦙪M_G5݋4-aG,:uO?n}sB-u{ҒDP,]ڂp22wNo-tCev17ߏoR|t8_No}@g ޳jĺXӮ5Uw5C"c77>cғE=V]&$TEh؅X۔R2AWBE_}hM K(>`!_xzojͫLN`ϩmi;?'MEPHQ(((((֊ZHn_|C,7S$?7x!b_)]f-~'^U6夗6b?"Q '-W.qSiGMuߍ)uhX|A=;|EDۚGc& Z׊GDQo(#`Xs弼/rko i 4Gw5qct@t[N䃟Ix_g?꺮ee%#-14-2 ?un NwwW=˿OkgtfV~,]ThuJ,0n b_u/{i^ky'iid˷i` ;)GZg¿ۗQ#%J! j8/UbexWΕ1!H\Gjˑgj3]|AfMʕQ0ã'f.!unojZ]#+ KJMCk-벫:x<7w7tk #3@RӐFq+7>Su+^{kr=<*MX~W5=歩[x-FM\27*\y(@ GLqQֱ)}v[m#m=EIk" AUNsu~FnMTX 7y-]g8^͵lOU>}wβI$dmUUQVIͽK'TrVΊ(hQ(((((((((ɾ.פפr#]sF'$Ы;@Oy TOi?*@bEQwG^i`/K9!B86WXqWXQY'WR#t #euE)&օFɫ ȗ5}WxD|AFs\ɀ~U(=k_5g[>lK MW:" LvvKdgQEAAEPEPEPEP_=~OxE|A':ǗsswPҴ؜y)")231$9/W떺QAsuxSı0oq?ݥ{/:5];/Zo+}r˨\y\ ̘d`N-|xzv:hmc\+M9-mgkfܡF ONoie +$ě#HL\(+|4[|.I˫\=Ĭ’N'8 Tr:ΊT/.U-6m>"x|Mno9Ӭey,03[t]f#Fs21vȪ?xcS 3E5Ɩ}vp tg % syS+ mq$F#et?)v5KIU]Zÿx755ukzg=S\[jW 䙝o"@N R7k4}Kؑ;XI\|O__ hf&=Rj{ O;%tc@Þ %>.g#^6v+"%PN9'$fFwv_~ yTm{wok埵 0,JxlM{ KGJ VhFs db|F@ѯt?wzo*,1F&Hşbk~!MwbeIEq-ȍ%с oHִ,-Z;yhgfqeqRJm_s3Vi%]Khz?6e]'Lmmܶ}D#[;P2rI<\?=wD%=)oR8b808~q^'o+kq|o%ŵ1(qȬWc<#C=2|S;<NZ=v?OKu2ܚ+:zE֫cmef;c8S8>X_P^Y\yg: "Iz2)((֊ZHn_|C,7S$?7x!b_)]f#Aik5R][Y$Smؗ,7Ȭd|=Mmo7ͣ7 9QNf2 ҽ;] oxxQ}g΂G[XF![uE+ YшZ1) i[H֟#"_bKMwڮr/F_7g{߫_w 㯉tx|h"gn<6T=IJ 鐤ۣ R7tV/qऻ^IymcrɶQp%WtOD'2vmƠGRU)xB5\zZh N^[TWYO`X0|o?ӥxhQvT^dwie=y%GDw/?O{~ϚjRx׆^tO YX rS`ՄH$wX׼_]cdZD:ؽԮ;'J)Uݽ뵹k0:$5_ xgi=cB#Le!H$v#5GxOC\j]EԮn=VAHGW(X&xs~CkzfqNhlV^V%l(^ !cͺZ篐߇_tQEQEQEQEQEQEQEy7BDw.פפr#}I8W* '\_&oكj۠f&rKH8'^|Qi:Kyr1VF\fP :5O^.jvvnldy5ݽfߛ"f!Tm1Fy?FW=ufv3w:bmGhV/'B:RH#kVo6]BMx3PubK=w eF=3Qϭt8U CVwok}\q  Rr x(((u~;{[~4|St:=*@A|"$}\x6TI*,W]H•cQ%%tN<?|Wֿ] EtԱдbiey$^k,܅_'|#ڧPѵOjh8a)1)L8$7I "}Oqմm .k?إޟkn-+Xf1a[-NM}Ag݄[/Gf_쭁8kxFu JIvUeod'6ݟJ_;M/#F_4m:V};ĸo5lXAB@lv3Uk i^"^w&ͮةQºG<&<=c޵E$*3R`O5tp|V⏊h1/ Ndy$bbrmlwf}?↥6h;' #UHpb|qנj <=Aeqs (%ew[zt6~ϓ֏koا%jJ)V]JLJ}ۈׁŽ9}qkNxm?eiKvǶBf'YWϺqΫZVv!o ݕR]"D41FslҼF|<>OhZi8 5*fXGBy{'|1KD xT?|z֙m:;Yh!TrW0h]A}/[|C^kYLj; ײ7qc Ȭ>M]h$ץVI:gt}"e0ۮr,{?*ܦȔQE Z(h!O74EYowEZZOgRSͅ<2֫?bv,B(IU}/s:[]7Cb%0799溏 +At-`FR@妚k$Wֿc>>>%)#kW:-|; m  `*~W;mmex-_򕡹+$c=p; w'֞.4UeƥD+norp xO]h 4x-uLЮF!9Z6hN>*xC'4oO0kiY*>)+HX<`--2MgS4__OiW(WFK ѕP:T~!ӯ]T|?m xIm`x#K+mݍxvŷz|cLgRB#ZI۷ 3g5|'t1_h {sf_ [{a7KֵnP)'$qӮHgZeߞFqU9 <5#|cx|c|'&p>ot?%N'eětG @"?|'qr;8tuVb2y#8ɮR# h/f8_aRGpViu}ɤsKɳizDA`̋#:vzT*o)&tծB$ؤQѣ|'mffb$g oݶ^E\8DJKI8SVisoZ[y/Ɵ?z#whdgWCC5{VDӭ5KLװZFO'sKwXh!D"גll2BxS.ETST|#fŊ94g]d+;19f O$"@`gWI^o=c:enmԣJD.;q7cU\zGu|;p0>'?n `:pn-~@;FNxY< ᫭ =KdZ{mѹ9Xu1D(}FH hEDgNozYW45iYKYܰ! ĐNy5 +t>ᬮyQwl]%I7dU6K[w++P>O{k\[Y^1M!+RSo;[v׷ͧ x?xG >5kx 6kעoýVյ ;NҴ#P~8IA/]qCwj͸K1&a$ ֽ=6M /Z6c@ٍQ7iZ#N(̀g(2zܭM~&p\=O|5|$Z͕z֟ꚋj-#;F) i#=Y#~>]CA跍k\20Xϗn7`X4j@}1|;mixKM2,|΁pǁG7 \SxkGKd#oUBAwO`]=QiO~[Rk[PurvpH>a_Apz( % QUE8%TBIQRPQEQEQE?kO k#^^UȎC@ '\z'4@rG~n뿳-{/?g4BXTP9$`u;פG~x_ZW|)mc[[ 6YKp/ 5ub8Oj_ ~5|m5d<:'n:@r gWi%i< m5 ޑq, 2f16 GN񞍫U,VҒ78؋0nEw~m݂0kg&V}_H~~%{6ѬZ<x⵮gy. fo(/ #္$:ҽMXSW+ؾ=i:mghh_I],i,@KvG 1z08+ߏ47Wo;4&ۋiC\ '*xD*n B>ǚw#$cviI?_>3/CZ-ֵx_1#+8߆sMy:ݠAKCA+ 5|'x:q)Y4}TMrW ExNe{h4" 7t{RC,r ufibY=|M=kDydbw*l^"<< 蹧k՗]]&xGß C⯍5mZUH֭ƛnڄokՂǨ`GpNjt/Zxޡ}]f!1*N澰t^(UK[_O Uª!uds֥$[J}-n-|Ꮚd2ib敯ܵzfyo-CUFA"KZxM_kx@ԭu9_:Fcy7%2b8'8n3N;?M;|? _桪PKi}_Ck`2G{$ޒ8-Mu~Φߴ瀥#>uva9{Wњ7ߥ+Nrw#+09=k> xG>)V:f3ebuy^HpUD4SI߿MBZ7ջ=>D?n,>γo}jrk3=āWP$o3 خA_4GNKKA#d+*Fdgt|cq}H>1zF>UԮbv_;rwBJǍ4߇G-mL[RԑewUW͆ܘu ⽮ƾӴK}f_t:בd'i0{R5 i_qhwķn:gmy}[l}||ឧ'XxIn-P4eaXmb w—wVd6X?|i[iPxBv#dwVI4'g=᧻>qo C $pc zjLOc(ǕF+QE%EYowEHn_|C,7S@#aEߋ8m;EgLj`\,:+#k{ś7>ՇĘ|uqsy˓B%V6<);eoI3Q+HVs%GA4lؿL>e?R߆m&7&ȞH9" I $*{Zi/'*{MzOM#Yr+l5+rf[L *$1kog4=RŲjouehf۱0 9⾆>9ۯ쏈tX.[.@̋(2@QZsj6-MtJ@~'4ʏN^__fכvO?e hO3O7ƛ$+uMg"\Lk"H%w9bO;vi7MG5S'|O9 H^Sχ n\4O}$!Y%mH=U~7xC׌5D>uK!RW1OD4 M woJ_^Qo;O筤8mPgirJ+ö:|cͷ ~#j%T72޲ZaQʐÃ_d㏁Ki6w yYI-wa@]BIO>k:_ ˫F#b?tRJ"Z%ٯ5r}:4 "Fkz}z{faHEm 2r?6$^د)A&\/hƯo\\C"8~-IQۀ>3g\]/0~= 4Oizt^YCzjiȪ@w'hg=j[xcMu+iu-oy=I u(g6@Ixú%ޣz}>=q@#f`* NѼ&~V-.})Zώ|I.Xh65w蘖ܯQs1rJ[~7&d/75OXx{÷nMjSYv_΃x'"mKS)VܧjwO5;};Þ.5ibs>5xMTn[G2\#/"pnpR]Aӵ9s,ϊk⟇ZIfm{2vu*.N$Σ/,/<Z?uH+]^1LI=ޢO=[x ɃV\0Y|M)$F 3NF1_<?K&Kp-!}?<@ *2Hb83;qQ]g 5.[ZMcysn7]2JPT8㟅[^Z?u/:D6QZ=cMr9ڭW`W5]/MռM_hZA&j^k6n0 -xb33U>U ;7򇀿gmFMmpmMĖ:jO4r3QۥĪwmݝm?uԾ#Yݭ]jrN-ϓlx_+G[V?->ߥ+=>xAN$wZeeq Wz:YB\B UEwwBON/yy[~ tes_;xfR 6?+m&\-,4t]$ko{YlA8~5"S }`PĞ*bnt~)}|~m/S.}-F>4^+ m#q`8@c@GzwIgxg]փrGSj񘨶P-+Q|vo nռYk9O1#b`u|_|JG[mwGLmF6`2qx$L(FkQP0o;x3ž"o3 4Yjz[ܪD%#PH5jOn=>}Cj{%!v/i_2bZnf܉P~+Ⱦ;ƯZޭkVS[Pkh5hFZ[PfRvJŷ^x_ 7d3 dH<33dҴOio+A;PrCJQlןi&+c#r21^)SBoPDclR&I>\;\Sz-k5SB%&/?1U#ިijvz及4[VxXD$(b`Tq uO&]#Ƶm+Y |1K7^^uPx'*$r>/ߊx^[=u{SLqA<`Y5W~xbSKEk8o$jʥ>y K`=K~ iF9 Jy~PNG_y~[Fܶ\GW}^kZKWҠ0ٓWpHJ->Y?բ[wnv;L6pޛװk^_ŭ?IԵ-/L=>TY =8լhvڸ]6ҶKCZJ( (((ɾ.פפr#]sF'$Ы;@Oy TOi?*@bEY]:.Z;GLn+oRV0X}mʌyŽ֤_}\ Ȳ^O*H9GS׌zUyŸ:'&KXݒxȚ9Iʕq5vŠ((((( ߌ +a.OESͣ N[.@wI0'oC¾ x]ޏ kϪ%vl㌲?!Uf#P1'C'/l~;x7Z|BR,lo #;y{w^\d֞1>5P?u}TF|*4dd[ ()lP:zmů ZjjZjh)yLy#G@H*p [|b&𥦣=ֵ Qs%S.b{W \7)J*i/' ~(Q䶷3篇?.6n9ik_ K6Rl5K!:OB9 816w/X|vϪhoks1,3)qs^g⾹_Kþ𭯉OG}*+m;|LoE- u|j{\xf[ *;)dhmrH8 u^6ݿW'5_?kW|Q4F( g$;Ԑ!8BXzOּZ< {MKWXa@eD6a-qJָ;[K˙umjro)q{#i@u(+E[{7%~#GxbNk[ϦNq$RqD5~?0e .-ǸuU OZK۽zɞEyc |C&~e3[$ID6JsXo_:6=,O?eq7Q]kqH̶eԶɽRKŒWeޞcO74EYowE8 Chzg[3 auev$PHqZ,rêXi)}{zvxOyiiEIa2V}eh)9WK>@v6zUEciܴ0H- fv&Ii? uwx7uy/7PpxgyUдvAıy#-Hi.v}/~_Wq[F7ïO%#ioPvԌq?gOy?N Pfݤ&[/"8ҽK@4{|.֔oDJҙCe͸Hy_,$N[O xWI8LKĞDĩ6[6:GަSKMgtZɿHr5cߥy3X6|y m Ewq[Gf6/7;5M~orzv%nKQG϶ύxr]ֵ|4I#id22nsk[O5 @A{%;Aew!pʀD$ICʜ0UDiυ|Aa iz-Ki71#-5UB;ettk3e\Ikiu5 >ʑ@(6N2*TSe%o| WJ?<3[}XY}\lq#!V^z}pǟh/i56wOzdGlmrF+7h>:𧇆kkw2`f$  xȭ/;;=5_\h4/ %ʃ p΍69g":`(((ɾ.פפr#]sF'$Ы;@Oy TOi?*@bEeiGă4=41ndd.UXrx+V?[3\Y[Kdmɐ!@JRبJ?>|5yg}#Nէ]]z˩0җ-F@s~1VE]+h 5ƙ}0Hs!\pXtZ{~Z* ( ( ( ( -G7Oxz[mb[w1#)yC}_])־#\Sn"xzy5[nYT4xF:70Q|KG_ !_LbgY7r6JOj~ ÿz|6-MjX:Jc*>06/~7{OSY^Xkv{%|ntX[!n$|RtÚVer7:@"2ݑАA gqm%grșYݿ/.>O^:z[KˋIfX ^nA銋7<5ivbi˩XkW`8K1 `VjiƚuY}Sl;xfuRGjyG>hwdՠZmdžn2FU2n^\%/ߌ8k|M>آ'xc8w*Fz_<3x;iaMo++ Mp[iቌ=ǏmOBw{~f(gu>޹o~.XI-+=/Z[ E# goTl#*[E*:,ԅMm,xH˜2p,=s~+?.ŨDmdq$s5GWįxO ׃t)ou ^iV(8AG>;Rڇjծ-Yz+pZ[rDfdu=i]o va|EoAG"EѼQ-Djg1B \! A5.hm3cծfTv@-ѯow5{%揯[Au h/)>A+r5uÖ^^ÿeX-ԮA!U8um׈  ZtZW+ƑFA-FMq>|c6WZ-jfI>Ђ%X5ERT3er;]gω3<7 YC:Awk*0%bqF s?-q/K-n:Vê[[{c-$z|Qt{߭oO^C_)9I}o6k6!%m_csrYMmifo+9\2Cpp=-kR]oRONG:M;yo*pNy+u +Gtǽ%]\8a6s޳_WW;j^nu :2 dX% aTBg*VOw/a;݆MW]N}&Q)h5 M1^;/JA:ޤ]%b-ngܧ1 یuƿ<.W]-!VZKv B+DmvK+u浟*-|? ]xSO5MKkZV 7#$)#$fi3<W_K/I+XF yY`x?]՜>EWwa2|%ӯ?VROxٽ#2Mв`^awS^(l/f 5G֮54l"#*xf#o)VSЌ6Q-5Ylnn,'x-A9< ^Fi5{_ר^>}Ѿ xb\ԭK> mFk߱ؤN'5i3de@Cxk^*ѿu R[.ono`Ljq E}ě'xw2ucw'.+Eв{W.9>Mo|yjNԦ:y".b( +Ҩk~Cw{]=O8?/|oexY^]Iv~L%}z94PV ( ( (|Uįsi-ڮmlnL2Tqm ]4K zkQu(b#slS z45]~}:3pm g DGn20ϨWMKᶋfVut 6v"F\*y# I䳱=r}Oު_<Oҵë~fkEw8+)L=1ZoN6vXEaF.ޞ+mhxfMw⾭ M [cr.Vk7ai 2%Ia{Ljx}$˩$2[8=VXcB:)Gݲ]?çmA4{ Ѽ5)gQM6K{}`F~]n8W6 o^x{A/t:\nG}n@x'_DSʒʽO<7ƺM潨gTx)-l,0;W?0ues޺?5^!K/ GF`"%F``WZMɽ^}2lopKC[$[XÍX~þCi[PFRE9*s^+hZm=?ߟgx'oѭ.薺ewiMw!_L$(hZG}ny?7x!b_)]fW_K/Pw>oj0äĦM(wC}@G'ϵs> >-[KsOPolЖXGlqRxWtx~4Zu䷼.7evd>hI|ht<=}YñA yWEVM27>ՓZ|Wg%~˚G{^׬n<}BHK8]o\\9ci#4oٟZ>(״ <)c E̱Ho ぎ= ҵ+y HƣF4-J|ʣW/ӚiouQ߃q'0oGaXF_1Fe,2:Y{we'4*;y^Ϸџzwt>& u:\:lm[aapV9b9:gzOׄMxĞ"26KX(Qʹ$5LEgxm:1K4y!qJ~9v >&Q65LJum .Úу=֎VGui KUSZEγo-ƮG |g. r|$}=;iAŹk{G[ '[j#U09;S YӗJԵQ<֨( 2+(  *b&[~KWhc ( ( ( ( ( ( ( (ƾRu0^'WGGpʧ1]+O|&mbnM>WrY2FU,{9A|0Yx/\5Q-f;[i[(c\Dg2HŎKpC||K cu6gž"4ӵ;{_6@ DqF6I C-^"־C>M׼1{qPk 7otŷB9$bM%-vLWk >ܱEx'.![Mί- 4/,?Te'87įL -Z.v[+|q-TyJiBht>_ixRM]vGQ^) o𦳩]޴= \A\C~*Ք5M;Bqm a-o+Rg3Mj?7'o=i)((+>#~xľ%t6⸭``=6&b,)ʿr0z{X_|a f񞕠kzԒ闺5âͥ\5%ӵc&8zPKCMT^-Σ#Kha'}Cy@n힆tK|*Ul/}r+|ʦWeOտl+ /3ZDK;]f n_#*#hџ&U8R@n'~':kM!xS:g[kk|q ,;@9[kZ+/+Iw_ox@\I75I!]&P1ȻYpXQ~0[;-Zc ,b#n%Kq[:7 w4lG@f;@"rﭾ/ ^_mGA!NSqxCH E%+#y&]#?r{_YZO Z[$YyQ'9:5wu]BCMOt&@R&;W~:ex2 $lEWgmⷙ_%0;!'(sm=Skȷ9FoO=mJ->]fNKab2ù5ܓg(v >x_jZ|Ez.ZvF# ͵UW'T)Ѱ;:Z%|򤉁G"2JS׶+|ھKb^7L5j%ygw#m`DbW K)=;\&8:[[ס-q羄G+"1Q RT8RjzǕh((Zow|@<7^&)ntA1o ەa;`z_|m4~;מ-W N A7 $1-$dӌy梺_c +]9uM/%ӢP`[_Pd0S&Fȭ߳G!ukjAfGY|§bHpONGR? OxJסbBgPJ,+yh/3v9eFo᮫_5i7ɢIdTݘ0FA %E-ݧk[J_wKmusTGG+LVˤ!cV<BU>Jxhkú[ى?5"flII(#K]xsOk.0G{PD"A{|P};MsP晬7ךTEۓ1f O V𧄚U>{̲"B2r  |o$M4{{vn_I}_4GZMQ=q<=$/,)GpKM)f` q¿$gᧇF%n#!HRT_FjiV'kx]V6Bػ}k8>hlTRM=Z-E@QEQEQEQEQEQEQEQEy7BDw.פפr#}I8W* '\_<i*4}Z;6SQȌ?KԵ?q=e\jm##(AWRI^[u. %E{Om6(կD"G]ux??>8Xhdk10q;3S=vE{5o%%nm~E~[|((((((Zu?>?3]E:G?Bf*{.[|#g}tfIw^Q˵vv56j)]̥{[_O3a/AZjZՅn.--GHohYgMǾkm>iP|CV:sEwo-D(X1PGKh{Ey[ke3s[!᳓y-|kjz'ԴVmDBh>nǹ 0 qsDt}~ fusgmys Yc}v[A1̱@Zp*/ ~^ R,zܨD*Qc^o~'/8VXAX(獳8̤W[\:+Uo]>;IZ7s*A=3:oTݴi}|_4 -*[խ!jZд =;UIu@w,cq6Ĝ}kO;.Zi6v'L-s- fZAPL]ĝWYCۋm@f:<8.ʐ2 @j^of<ӻj/'?~!ӎWl5\ի2}/ 2GB+G?åα}$R9o,󹡹|D''gϊe]ҼIk~3+I$-uhE'vXy>1xk?0A1s+|Ax(xçO]Dviv0x5bo:>1l5 /_x_ _jI 4N>o)[r:Qn]ok|$W9Ib|<@l~&\Is[\IIl3@ 0W];/z3;JBePKrvYmb|tКwۛyHn_|C,7S$?7x!b_)]f 5oC/h䋤ҵH8zW-gOwh{퍊޲kA)[yOz?ߌ>ɪZkaռAjQ-gB#YbI`|ڬ=j>4~~ƚ?5Bin'S}F.ڬ6Ipk+ݽ6K'y_o>}x9ext.ԔLC#FWqhAѭ4yytBo:y9]>VφW[Kױd?wiܞ񏕉paڸo:ŗ^0X<;k Au}Huv6,}۰ynj/K+dݩW*xzXZx AկV:Ӳ#R# l&S/WTu #GM^k$BrP8“֡J.J79>M+NQx0 :4}]V]sM-S4P,Й t+6Vﴍ"IԣM:%k!T`n`rqԓEչֿI05E|:x⿌;=Cz5𶯥/ghȺIcdwE >\wiQEHŠ((((('3$d /,8:7zS1YMդMۇ'M7D^qU=@Oy P1_ > |^?]SE'hk{lH(,Τr-Ho Amt i:L2I*[D3w9bI1~5χa]jقU㷝V (X '~cZ]Ue~km|k j]zE!8%qJ n^'[X ۣ\xWᮍ t'4UƆ,cDPp 8i>>N RmF^baQm( (]nV+{ %|%kN&׾9Sо!ikɢG*Kn8"pX3W>iBPQٱ;hO_p}<[$)QEQEVĞ%t -x+I$G3^񞋨xV[gDkkt-y!}a% )e_we3ѵ/ߗ+=¿FCyeo5nRfFIᝋs/ i~-WKXcv #`р5o⯏u !kW7jmHKhceRp_S65=G{_O~xW3kZ,crҖŽdE\ 0AUtσ|V [BXݵiji01쨨 VUqqiG4VlםryY UOޱi5φaӬOh@,#Z)]--nxz{RO gn)R>b#k0$``[+cHk 3 2:" ϾFgh Aag&t,0Hd=t {d1^]Iy4qmip<`{U_BŦֱN1lHdUWI#eua#)Y=|M;2/5=JN;xdyIxІD7spgX4Ki]Oqn@B#hvBʼ^E5OM]4|VkiѬl,Xŀ3̈aRH3m=kZhxŋn7 <!x03Q!d8|oíVQ4۩h[DBƎcR@8@QEAQEQEQEQEQEQEy_?toƟ(o+쿊8'?g(Q׌PG'4@r/UʂG~kr^"I&ծ@ ;r=/WQS>(G-|ƃ̏:v);"='_ #] =,m]DH:6pbzn}ǃtei>l9bKg&ث wc<k}sw3^wVf17.gu;YЮD|&(QEQEWx\ni&ks Cg'./+8vm3[9$Ҝo.4em_E>|U֑/b]/a^"8ҬC DӢ{k/adpCʸ;`Xrrry#w]x|mӕeӧkq: \xRʒ^|8qq3LzƳyuis40jE1DXFV@+86dk{6~nG? c}KTWmjh{kRH[[dvd$CxWƴK jBMQIg @HaЭg~:/]zR֥ZXn5ݾ.pژ$tl^I_v_49iw޵3Z(Q@Q@Q@Q@Q@Q@EYowEHn_|C,7S@/|/Ϗb/i^5X+{XuA9 =1[ GBxkH_:ym-BnݦmN+4[iߴ/᤮uya1-3v\WUލ6s92,*d<&@A'"K{+zيmEJOv~д ]Ni6L X!mvg'=j~xK楠WE5;Ni蠷Cko5hzEQRXQEQEQEQEQEQEQEQEQEqѿO_~P)׬ξ4?E^31BKkh/UʧI8W* 6:?.nfVIf( K1<95+ξ$xUG/Mm弿fXHw,һHOGwhOI:n 0# <5izV4N]:ʭ("a^/]OLݜzh,as`_H (:t<֝mCI [Y}CzkJGqA烟=[{+Jw{/~ :Eeyqg.`#,> o ) Eq > Ѿ$CG|EkciuCp?-!x|>վLJ[^]&TQnGKde6 !4_ޣ-zLJ_qS:Up'm.dPHHۖ\1]F"ƊtW~x^*}}]KY6u:>) K,L\9ݑƝoַ֯ZēDir&4m_b(QEQEk~ԼCX &K6o8ۖ\1]_|hcxǿ5(9gqZ%Kh¡#a 'e_M맩gZ6Vk%X١r BГߚ_6Azr<]\^utf3u .zaɯ) (((((((hu4F?,R#?,C#w)eŸojoc:6OncNEk{Ǒ9ZCӅ^>C5ϒa ` ߋo0t+[ x3Ztž{bHb  %F7d/bx7U&&/?YW@L.G? mJ>7mI7§wVlMmgZJ+[ ؠg''5|4_ i 4糛sX^O6Bhl" }ksToMxr]Fo\32My&R.O6)~/|9];ij뚵@E429uokn7NSXQ\x?mE\w `yX1W9攝ʊH4>ּ9jF]VDY])X)6{g"ĺW4x5]7QӦEt3pÃ50l>uI!6ذ+6;Ӽ[-^nNIM}6o5$&̪ 1im7k/r ;_7c':^麞 HϏݫ8au{oe}8<Q{3ԟJ dHӵ;VLlwHM+G c.W8Q[ǂUwiPw-MnBI('=(OM-?]~Wz{>1@B9 tqq w7<7׊"n!U 0*@ [ׂ<[om{p|43i|S#: X|y5=p"$*F2O##5ã~~W$) ((+Ư \F59TxEɵo(mKFRsWɪ|%cRψu3Piϧ^h:d'_, B }Ralٵ}o/g;zQGwIdx뚇#=֝c3S_ ۣWkUi".euec Ȫ_R#x?_>msg-,ۃy$""4l5t[bk_U4I~WGtx[B1 q$ط9z^m;X[WվDssgv"`巃#ھ__u-it:BZݫHnTHXAx@O4o‘HG$Ze VO,3yBz~+/ ^x.TðFVWc'mX zWq^"L8+qi<[pl[,HFqvv3aETQEQEQEQEQEQEQEQEQEy_?toƟ(o+쿊8'?g(Q׌PG'4@r/UʂG~7wl/flgܻHG/?E?4UfE((((4Q@hP3Ef(((((((֊ZHn_|C,7S$?7x!b_)]fK'LzV3Ef(((((((((((+#^:px}OGdu$nE>?ל_OD^qT*:z筬')t;mNwmdc\zHbPKp3OiYoZWi鳭w z`uMhqx֣O`Gk/[Ǧx/X}AP gj?4?,7QMuoo{cg4qm p]ϴw5 6J(u4mj"-72P,iYoZMx֣Ow6j61L"I4 F;s*ȗ8>M7OiYoZ [+)Xo/|ϳ@4_hsS%cq4j4?,7G&'}m闚:X<RjהX)@]YC+(ȣP gj?4?,7N0ȽQUc{lu[ΰ( ?&'%Gss W2!I_hiYoZMo:8S4&@35x֣OԫFo9mc~F@g&'Wӵ M`-ݵZܣ"$cP gj?4?,7T.5*%"[Y]w"ky&@g&'CAIy'-cdSS:=AdP gj?4?,7IE4?,7G&'(giP>_k%Ro*Fj:5oԖRl2֍_MϞ->{բOӵ&#FIbD,I2-A5J55_(.2c~3I5u_hޭuyhs\4KvxQJ195&=GK'ž=~ը~cq#tc|cQPp B% F^8ҡOxӬ#EVsֲ4QG?tφ&fZ&Ji|bW[kBʹ.ڇ:co> /Rtk +It)=4{Onl TATasN{Оi| ԛn3Up]I~v\pr>hg󨢊8!H8"L$ ''{Ӂ.I/4놱B"~+X`7ިoU.>7oJ4>Oq!XnnP%sЬmuMj5-% ;ߴB^*ݽV[[nšHUX%@OOZ:$ Lӕ+F[ڦh%ޡ1i L rX`*/uiXY@Ke/۔l[H޷oom!v3`\JzY7vJ+G7Ե ,aw ovmcmF`lb[|gHD)c xK$=G4JK'OnroJъ}/M<5j#MO@iusqpR Fj \ݧ7|.5Y[5 } 1؃^^.x~VI$^JH[ls֥g{;-fO5,x-݀X gv:_׸Owv.?>2"x(+osYtô&A xEү6ÆRC}*?AG_nk}jѼ?ǧY^:[DٜsI+Cľ ӼM.]j˥pF>g!~]񃭥xSա|eRtxtyl]:KvN(3"*|~~9 ;K$xGVG'ދ;;a(qG5:ǎA~ϗ;ڥj7z;3Fne-Sb^xY5KB^lfˬ]-KPש᷒5]a]/2q).OKEå>oƪR<M6-CO:^ [W<6xξp(< q.^ׅ/Ri4hwEqar 1[uUDH% N0E]8(!A` NIMͿ JE~Ţ* ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (6"9CM}<`3?iP3?&<`@ 3?&E33?h}<`3?iP3?&<`@ 3?&E33?h}<`3?iP3?&<`@ 3?&E33?h}<`3?iP3?&<`@ 3?&E33?h}<`3?iP3?&<`@ 3?&E33?h}<`3?i Hy+H UY uO<`3?i.Kco9Fۜ?&<`~[ʌ\M 0L̳c*@ ?&<`i!jd'q8?{Ԧ+2n^M(3?&- nG[oo9@3?&- 6Oluk!՗?ʀ$3?&dWs2}aRqumǥ@g0љ4ҷC7?km(ٟxFg0qs?rݴR 9e F2)nFxFg0rM+^5M^\K+<ҟݑcM(s?&<`- _o9Lf?MxMZ6rR 9q.\:ըjvo$3T#n<s{Fu%gC,$;uf0$Myr_GOvWZr]ElX/EDv;[s POZ7_w~r݀?%x&wk|e]jp<^N$5d189^yw?LOזi {CLf:H 264+;hR{_kB!M{ZIۿs|qXZ'6R^˨رC+,aF8?ZVZZ,JFb̾`ĝ9/4_Z#IozcW{n**|;Nu ˡ$\[0hn%A5x{O,oE{ܝOt4\.H=qOMo~gM!Dž,J|k:&mN}"kM*qyPc3]}z%ҸJ_=x.Dfшȩ[Z0{5nCHxwZf^ 14l^²nMn]rOV?&.l촟yZj _*X`7#ӵUUel ׵ oGq-̚$N(W vUGL 77W ksNuē3Ж+M~BW }[?iOG}.Rb9"YA+jV89FR$Sqab3ך|(HF6N{xPqn<3 =+Ь&V!$ +rDdջYՄUc. r wJk1i⟈~̒ebE1U%LeyHnͮϤ<7YMM?ײ4$Q!\3e%?2!=ϭJ2zƄмYa4w{\.$wJxŻTOw6>45Y^=3~ L]Ӭ VQ:qǥoJM/E滸o/y*7_7_ ʷ?N׵E >tєʱ@X oV9 ]  Օ݋:dCtb kA}_WCEscXkAcWCEscXsJͮw)T7n=H֭~Z)$5ZR uC{_ k?"({_ k?"({_ k?"(IO;Hk 펃]fotoxx-20.08/images/RGB-dist.jpg000066400000000000000000000455311362435004500164060ustar00rootroot00000000000000JFIFExifMM*V^(1fixgnome-screenshot0230?0100Fotoxx:trim_rotate| Fotoxx:voodoo1|resize| Fotoxx:paint_clone| 7http://ns.adobe.com/xap/1.0/ 1600 2560 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?ub* ˚f:tځX%oݳ`+w5mG2Grг}w}nö΄^6ݵc٦̑;T_̬yjoZ<~^69klm(ZOino}ݫjEq4~y⋭:mRٯ5ɭck]5W5[:^l-A5he_rlOybEh ?XחO6v5{VŎg-Xu4G'nXo)GaThv?]䶺uKu[VIvݿ/V5oV.|lV+u5_G$nZ)}۵7Mv?ۯ[Ol`fXIw7oX͍yut%Lw _|ג &]^f{]/ٕ^.ncZ9?w2}@v_|ӕo.=y|GY9֯&Pdh|1ܲ}w̴jWdθ*~fjb'vmM~ZO56iO!7ͺOVƃ{^O iHg"emwP-1̋dUW,eFW5GP_|ѕC_|ѕCzr)>؊)cG 0cͣjعJ>ejG9?ۿ*,{-mIV8Fo ;4~̯hrw<\פcx}w۶ ?o&?o&f>x%䛛_ZlwkŬnX]k٣>fh٣>fha6ɂ-nިh6h=s}Sš>}Y'ګn$rwjZ4[9R̻Uw4 M4 M9yo<5Ɵqcc,Mu7ZMg+MσmV;7+2s7~z?7F?7Gxwu.[Uhn>'ۼnm> y-6ico7ޮf@ls34r9yDŽ[;7_&Ya4ʭMjuK[;[w=$_Wls34ls34rfh٢>fhs}Z.u+eVUos7;7º.GZجWJd|4 M4 M9y~`Kh6ͻxWE/OYdUXHʿ*ozF?7F?7G(sMmAwj7_e~bcOUv6hv?7G,?ͣͮf@ls35mmv4_ M7f@Ahڮ}y?Lwϵ=GI oo+UY# փVS~k_Hu;CV;3nw2۹۳Y{ydE+W_~ CL%=\]˻\7;׶~~&|6}Zokux)U0JMծmEWdQEQEQEQEQEQEQEQEQEQEQEQEQEQEgk ?Aj٪?Aj: 3Fտ%?o kAPʟ*|j=-yʻ3mV[uox:]c1xg_]#^H.zvsG4eoM綏ử6Fm­2?#p_.sŁͪSu5n^%xy/ <´^P<}sGT*QEQQEQEQEQEQEQEQEQEQEQEQEQEQECUY-WAT5_}/|t@g71׫KZ ҳFտ%Q=./bh/ܱkyr_g/k9{ fW[\j{N{eVo V|xfOgkedS4 j߽5!(a'Z_n91gØ=9v?,tO K yVw"ț_FXӌ%ғERSpkox5 gA޻[^kVtOl0*4"o5mZCXx3nhնwW[U{˖&Q^qaEPEPEPEPEPEPEPEPEPEPEP Wf_K_Pi UU0^-h5g׫KW\5gQ^-Tvg~xSȫI_q׌SY-Om}ag7m&Y#o=̬{&/E MBE\V>$ |I&k6Inտݧ~XߋV]TYJ`43پ0i><4x_ x7º~c񦸼y;w+2ײ5^.5Jvy?4jmfeDx#l>}+ZvLq%s.j:AP4iy2c%ק=}B%Q=.Pӿkg=Z[,-BMCRi&۹7gnUi0[eii5Uʿ)H;9OF,O"&j߽]_^xWŞqL5.Uej[-oG_O=ֽ_6ƫ$p#mmp_d~ [iZ\:ng{cqoX@Wk?7׿b)Ύ1ZRܒg Jӕz  kOPc]U^Kˋݪ^_J\r9%8×Ws ~|]\CeGG32/˵¯jzXu46ݼwm4UUU~_eK #=֊ށjriڵѳ+@+/ޭ9 OE"iXJRѬNQQEkcxQ@Š((((((( ?Aj: 㠪9`Oo Z棢xe?[{ۛmF۹~]9?տ*>32fhU[T,y9ORQ<Ϳ--7yL{ohw.[[ <+q?kgZ:Z­$,?Z/;=Ǔx5 s|~ee{/߇Qcσe,WM]W*l(QKSֿa0 /]OxK|Bڌk=#GvUf}ou:m?,/~"Ӽ_Cq:{^G4,c/[vvহOj+C=Qu K?-Uc۹o}xJrc ^M+'ewMKJn6{ 嶟p/𴍷u|;k ~;񦙠Xjw2GIp7ݵ~]TzD|iqq[["IsnOj/۫ O|;iۡUc~_3n⯑kWU/uu3.hZ}n~g|] j^ \lIy%n3uV>I[i@&Weɍdo&$G?wv]ڿ"eV=Z$~[+7.寢drGt;6 uI~yYU2ZMy6{?ZKJe1%ICw?N4K[դk!]|ܭ+af|7ſ ѣ[[xTd_Wvݻ-:oKUht'sn;|HnhZy,v۵mޯ7uR{(3Xn"1P%V~Z}Ncg4ύt/ rYnE4af[۶| gGoܵ$mzlھUӮ>Y.Ykn]+|~Q1v1>aqLz_}?H4vP1nm*֍kYJU5 UZGeXN |ygJejV2^k,F$VWݏ<%)Ԯ|S9.÷ g6eohVO|0V|Wiz} ooyon߻E|oᯭ7׹NcqfNOHS\//;Of/ochY[lqm|7|;׾ƾ:\,u INE6_&ݛjXE9^V9>3OĞ񟁾"POiImuCeY'Ͷ+_7e<;Z fmKF.o{jG9%W C3=-iFK]f8µ[rXiѴ[|/-yv3w{}gBg|iok ا&fk|1O<+-Oh^5]Oom$,ѷW6_;M,_5D~Т?>/| hLJ%^i{r2civm] ooB|&kI׾Geob-wTjcFۙ[2^0G]_|q|zs5V#L>Ҧ˲UvȲ6;Ķq40jvp fѳ}-k "59k+.۫gO:|5|C;1t775ڎZ%z2;Z'rҫE>WikM׾[h4 tFkImn&̻Ws2]jO?l,5Kۓhݤ]2խV"Ǘ<FݹuL+ᶱi_ݭ̿gVգfۻs||`fKm>5{!Y<ݷZ*9gi8~ 97UnMx^7n?|6΅4do%[ͷnmo8֮n<1? i6Vx}{'͹chjW?a/',J\Vխ<W;ϊմ贍^k(6KJ5#煫*~$Pi UU Wf_K_hr0G^-|Qyj l5KGKhVO%I>m?o kVxu?N&5_fBeN5qXJ?oLqknO_ 5>"[ˍGgif^,[ܚ~7 llJ_=o? XHe[oos *x>'jNey~ZV?wijQN.Nm-RZl YhQ\玼y8濯}KU3M崛w6ߺyY(ӄyt\׊b/-%x8K8df]M~|^^2u+O =|#2Sv4*s|_K?J[R8K$o~qJu*> /<x#wCĖ+ku\[,2Iʫ^HK]0)bq3Uy +CM 兽- )s }]SAJR^1o<XϦ=+/ܻZ,:׃M+W> ͥȭUM߇Ŭ<'Jq-#DgR[,|t>.+KaG$22UU5ee2^)SƩA.)X$q4"c2fo}we_%/<_J#MlfiFV|y~Z詄-xh_.\QMsEy|owj0x_\QYgZ*sn5+}V_xiK}1νuf>󥢻7={-mJm6[neܻ8M1xs-yx[xzh֩N/WW~ o ot+qksjr4qhm̪­^Eya핏o jx,MZ^w>=-e:8d3H__#Nox]&mLa7ͷoU6ͬѴrC*Yk+/.4^еoZ}-;}:䍿7gܶSzK{ =š7/|I$ɹU~2ٛY'į?5x.SK#_Mu qyn}a=TuM>6i7kVh+|2t//}_?+/ڧN]gMоA/YiJ j摷m/> |FǫGOBң h,ymۗnwW{ῄ#z׺'t",MܫF>'5QR~YiǗNg]ącMRQEu|? nr ( ( ( (ޯ2|_1_ imjא2Ʒ ەYԛkj>|!Oxzf4}֙-K9YcۻoF/xó:| a/!w˹]?|^Oi&OuG#Ki'$mk֥U㈣IVZ&Y$H'oo]_}=$jKs^ϴ1j6):Km..<7o-VYi+KOx6O jE5fH[?ޯTZ2&&xtqܮ>uae:xP?6am ̉yʭR>$}oE: e f۫4+džfLgl\\ܶ3Wߴ'_?zt6Py/^T2Ƽ~)JײV۱S&:~nngZYW+~x T<'ϩGzվeWzL\1ܿ —%Z(L)KE_g]gF͠dXګ܇AYڢſQNF:Ug*yo /5Ke4M_Aۯ`[}J}E'!m۫sJGAZPo}'F0m`1l| c*c:GQEb}QEQEQEQEQEQEQEQEQEQE[Qԭ+YvsǹZfP=+Q_j&>=R$]߼_3mv6;a/uij07/w>E7Zmm K{٣e˹vj;4 xxfM[u]?/_GQ]q1q-OQ|#ߴF. 4-2})nFKcmʫmž:ST96Mf5_T|<3V#D]B׎FׯRVT+Pʏ:-Ưbږ_-ws|{ΟKK>STt>)M.9<[vUo1~X*pتyg(ӉOZA_.~&գ灵>"<5▆YEZ71V8t]?}\Tfګou~U`?mQtXm۬o_& -?P}Ri#|YUm'|0j;K;9-4gۿUՅ< VY{|`Uc~Hʊ($((((((((((((lB'g^|]=ff1YnoWʟ}99|~)?pk>(մXlcմXѷ7̻UMgo'ڲZTw1u1^pCៅ=KiԢ o n: տi V+)TaRo9_0ڽoG(__ _PTa-;ßO9_6o֎P?i᯾'/sx{+VvTV|9o+oG(_BO9N_ڃ/5CY?~Q+zL+}h ';}hn÷o~Gx-oS{}i';~kG(^o-#"zi_'~kZ}Կ2wŚg)ï|U r'zRP>_ i'wV-n2.~ZַG(Q/ō-,_'/ =g?|_Z@ "j Tmٿxسh՗Ԡlr/u[7lXT%o9R.tv[sɵzڳWCጒWז'׬o:HlXSᆨ9[Էvoἓ)sr1w;^u8.صU[]*Ӿ>E5vѥg^]zsE KxcmUmvks_&Xm]L͵bř3ý'+u9O6V] sfO+nhe5}z7":/P7G3m+m]/ÿԾ**ɯXJm6MY#?#>efvg&txTnUmM{ܠzBOG]߆%]j:ckޞ8ץqxñ]AmwR+\EU#Rcj=[2Ʒ i]U?5iwG$Z}+/8\x6[ƻfhmVOjSuo׵~7^Rb=RejʭW(з G./u_5ȵz;o_KG)4?fm4*_w|ۿhKDְi>~ofʳ4ە"ۻq0evW6c7^ox^$ljV|ݭ~^}5Wnckޞ{[x_7j]k^<V}q7rcVv?מR6XRkMm]+ʵc?iNDi vvMD۫[&,}{ǚNJWE$;KxfsI#H|H* .atUm~$ꚗK᳾llm.Ieh_j]mIG4Y-n.i#Xw7*̿jRLc4V56~^y>,zoF,kw#4jo<5^x^,uRfi#rǩD^&+??Yz 查,u%PSoƯi-cCmfXڨV:[II]Z7G/cَ?&Gwɦĺ}ygHѬp.ڬ>ox^=R{t2HdUe]ʭ堣[_& ݼ:*@o9tbP(I[ܫ$jzEglӵ5 ;uYXf+3.գEWjRDV-!Oqj芞m/Β-дm< .2z*n>xu'cnqش-owl ++neۗzxA}Ⱥks/foMVwTv-x ZƋhqyui,_[3+{tkv״T~+KU|?s5k O}W| ÿ$m]]Iy4qwQ qCM=UԛVkeF~_io»=]Rֵ.>upfo⑕mYkPuk5-^->JFDdo/W--s'-[z=W7lnMtl2m-yNߋ&muOC7ojZ(>9Ծx_P]]]KRk XvuWZ 7ږ}gk>mQviw |w2|~]] r^mRծeYheea]?zZTgdHWD69>o]\y?} zG*\G']V6|W3WEc/7#gǦf_-YvտE'ܯ~-|(,fW[ƙao,3VVjoe\_j^o++vҞjsޗyfiOm۷wUh?4m5sZ[O.#mWj]̫wnIGG,1Z5wv7ŏtmYZ9#š1Z}ėVw q#nmwvirh\6o}w4{nZI77mU_h((mfotoxx-20.08/images/add-metadata-items.jpg000066400000000000000000001052071362435004500204550ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 328 500 0 C     C   '" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?D|{꺝ܗ4YbRI8ZF'BmA#ϊ8: %q MǕ_0O63ʹC hzGSha-7Qo]Yڳk-_ 35Qtr*y|3%5~vr/Zl#R HAgf璠 TkiEG}'?DW2O_i:^wAm>ҷ%B+$ègO_x`σt'Jԡ'50: ќ,HkͯɋI'z\cg`OR'?½V|?n|;Yh:um{:q"2)08!>*t=Wݶjh.[̒w0*6GLQm}KW,Q"M'ۏȯo xO qiڌ-Gakde2Jr9=I MFk+޳k6.1V[//BXx$o>[<_lK ~#O6oEgqm oǰ?ܓ#1RGl>BkD.jpjXv!EP D-m}%_E#+>xgBBuu^+-59I/ A6fR]W01V'>2L:P[DIfVXDG)PmSp}$?qOn>|P῀.Iψ+gYDbu;yVX֦p2Aʹg(Kt/Tմ i5 vg m]Tg8;OGڽn>|?z>EqOn>|O{z,ۏ(qOb}ޏ{`6}Gۏ+^}ދ >}Xj^X DQ Wjm'?DV'ڽWo?}'?±>Gڽn>|?z>EqOn>|O{z,ۏ(qOb}ޏ{`6}SYkw:uw,'c)#GڽI韶Wl4KiaHbHi2ZBhE]+#(܇M#W[WD"I$rL~+/&goO9ؿb^7H09/-'77j35Ԛ~*e'xDPz ?[G3'l_MtS 0PC"&Vퟒu5˨XR$}}IE $܂:W'3'l_M s5)W7,w!$#zҨ}3'l_M s4rږa\V$ڈbAU K\'55{-R2_PFQU@9&P᝾? bhv_Dß-Z|y*ħij&`/"uV&ci5{j_w[r6ĥؑc㎀ ?[G3'l_M.]ͿU"U֫\]O}gv˶smR,aUG05gZ[,g-W>TOuMn$g~;|/ ?[O݅}%ǟS5u8 ۤ:(U01G5.Azg&G&i8iI?2nl0qW3'l_M s4ts]'MO}3'l_M s4rߵZ>}k/&goO9ؿ9Boڏj> ?[G3'l_Ms7G֏Zrv_Dß-/&PGڏ~;|/ ?[G(\QGֿ\᝾? bhv_Dß-.~F__ s4;|/?#~}hQ/goO9ؿ?᝾? bh j>}3'l_M s4rߵZ>}k/&goO9ؿ9Boڏj> ?[G3'l_Ms!4W3'l_MrD i ϖtՒ2xC7?@C6?@ IymMDǖt($ca4$cq4OSKne [D8A!A'c>"t6GWK2y3ZFIX\3$kۂ]Ы`^Kg) tJ{;2G Jh&]l@k]{?k{žt{R!Cq\BAXLI ̦I cUPR|a?NӇ?,c=Ι[@s2۪,d=R<' kmV=C:͵S%v,nA$RNjƍ:VzTJђ 'Dk{D-Z7vL/:k,"ՠ^S ]a\+!#)9_ Z^C}ōw:i v[e 0&TlMѼ^]qNhn.dSvypr0x^]V:=Ŷ(h`݈ *Fnm"2HPiuv/S|Gաm.V7zeW6dK +UyWPamhl{AR,zݼ[@ӆfb|ܱ4u@OU5CW4e|M t|+Elq&m ,LK05#H𭞟i*Fu[8c$ήu].kAg__lmn֒Bw=BaHIB29fZhVtZ\ykE4%fYj3vAN tkzφZM̺&A-+Uwf2f.DW|𧇥U|-idQͺUnvzנ$#>[K_qSCE?ȓ8?<3-QO0H:j9XB$"6v-Nx–I3@\?Q?ؠ ~}(j*=k{G}u MQXoUe֪Ikj2Fab eawk{G}y|GҠ֋[ηV^=żG,laG}[ĐxgG6Z޽$4%WGxľd;e. WZ xǺ4^s_]~Xm39pt 9|;M:j 36;Krױd˖@bG %.:n5.GQ|̤9Tl~_ 6.#M˿img/J \]mi]л@vligĭ/&J &Cp_~ 68𶛭]kC|IJK9ܤҾPpqix߆w躞d}$NWmr$XE HʶGKV_8|Dş/xx~7:h62$q0+pD|>a/]DmV[iH6q\02vcu^/߇[^}Y[ɩdbB&hJ&BJ񾱬j:j-BXځ$Ϊ##ÕC\kfA֛O h,>u6u!>g?ixJo~ ak<;5Էa},C?[?6+ٮxRG3g}AΒ_;w΄31*dHv:rksuewu#Z\^ηH4V{Eʺ[8,Fc5/goE#uZǐx|SK֑\Դ+5k/j:<i-悲#a0koĶڊhiu֫uc+j+uio GoYwBE1Sse2r:qw2-A<1HDSp9_< aP};ul_S!w4vv䐿}jBI_>4㏇pWg¶/<=qX%s-Εݦ%pk JR9oWoéI[-c+).fw0' ~Ϟ/tnk57:aHw+ 8]<+|;&w,n/oD$orXxi4V_:J*=k{G}QP(\?PT?k{G}>5b œ8>>( ξ{'G6 `ּ7 RFmRv͖,@|@%c$:}eEuZ?Ž;~ ~  M٭._I$淺RSoh5'RKKyBiڄqG$pՁa$.I;k;{)ְte\ER-.L#,vX;OG=>ױynޙ1ۥ])\w"yQQ $eӑ>Mn#g*2qzjZNHegXs9z_K{dcnvNG9V %j-|9{uob*Ȳ 'jES-tM2df:y-.L#,vX;OWh _l^7]zgn\w"yQQ $eӑ>v}L6r1#/Gw[ޤK2gr9=jQgK6F6js&l2Y0pe?7\ujZNYVdYqxo Ga8`Yc$^H{xvz~oȁN߯cL?ԼmOod0Ke~WV^FAmte@~#5 _z?z*[,:֑(ݶˢ^j4XZqyx߈t![Vrkco UK@Wq/S9ǣxƞ.=G]Y~<gx7R/0Tk~2[QOxwH0hv6CBY!dB*#$@jM/zߍ[ ~Ct 7V#]CY {h!KyČTX^G̀Ăq|J]ᴼ}[>R DF¬XYwPOA_9?oRMx"LiݖV) Dc6.,ósCƟ*G֚ s\$6ZBҮڣZ.~'Y?uߴ?iմK @Cj mZOrg]N%3ğo5ۯxZF%h-/亷Xn$\H8f[ r6tv֋Rggi.f].#A&''x}1XFiWl9aFG\W[i6iԮeIuA#~da{W'#Xj(E_g?g?_g?g?_(`i `i ~:/Q:/U(@OG@OW g?g?_(`i `i ~:/Q:/U(@OG@OW g?y֗ v(PƱDD*Z=?zyu5CkIET5~A]*W~+u)=eqgp.1$Umߛ//K]5hGGŗ.oƯo-TEiTF)v+o9kFRv@Ȗ` QI R3ݳMhg평!WPIy|} _9Z5nBZw}+c7GOMƁVzōΰe̚d%[^ү|ngiN#uZ&4w/_ m ŷ?~Om)|sA zZmݾ]\O/qaV&%B*2D>_:~hݧ",JYE Sg8s Qa'<;Kr'4n4j%88Ηߋvko=%+żyX'<_c5ψt[UJZNJܥǖJ4lĔpvO xNgi26vy T9]yN嚴QEQEQEQEQEQEQE0~rOJo<̒~|6z++hhbOՌRw7Đpl=:P&x#5 gz?z*'akm/GnqGc?|Koڱ>.&5.ěK8vDgWF266~|>]O_hܚn_m?sr+)dw( _HgI; KIfMCQiom-$$"H9e\*sRPM>Ͼ_olx7ό6-wxWLii bMD^N#~)\_MV_.Vb¬mCd ͆8a;u4h\޾{J-J1JW7zޛ@FQ .=ȧ5̚?!1i,>|/yVMgRLӵZOhtFӥqj Qeؓ 4^-O _h-~*{~+9vimqFDѐU"(dFro~t /4FXYX˅ce 4:M!"Ӗ` Fv@ű*#@V=.f񎟅fj6*}-=㷍qZOl _iRhwΫMwxf4y%1U0~K[Ꭽk<-fѵ2%ҴJ\I$*xMcFmR\ "l0q~9o^)O /WI$siE5ayhz+ߌzf'–ouFmF/ߕYcy19m7⿂5ȭ,~xsTݗoM42.xc #u>eEuw|J"K=vŦY[Kcu!B,# $K‚-7;Mz~jߍ_玼WEu5#]j6/h !?,6=l |߈^( hM52j, !hق$ógxƺ-n"Lvo{ U$K:-"8?7Q>7 A|=Z6Qummb!bWlF>V˕nҏKg 6x.?%O i,+N'xmlq=$[e&K{hNJ1J/9ckz֯i6v&|I9⻫;-#[0RROt_1^1|-gMY[n_ U"Hd]Ig/ rj=~紗_OZ[홬ZiWƉ[;+Nv[kmk}Gs۳TA# TDᮞM/˅S)o t '^ob-filʥx9h;߇z~#,-|7kp޳<ۿ4[+"VC |Bu6zI^< f{}rbTEQQmze 뮿i}h:g񯅴kI+CvnQ GveA\\zu_N{%4LeB^ uM"mWX<~!lAMaiO%a+nOi?ey/im(,d滙&,ECI3]Q+{[7ڕZ{Y\P@F~b8'[I +2l&xč55խmJnS̉ Kc8/\mW-~w_k>"HΏ;Kg'n++y^dxMSeQ "mR=COEnda8FHQoT7jF|_ìˠXBswMNN~w A5ǖ#|"d_X#졶'b#\ƃ8$i%_'gxo6Ix/#̈́yq96~~_x]>?V KquwDG(_;|G,q洼G./źfk]x}* `wA3a$ђ(~=xzg}_]}]!̷Y ̪Q'hbp1Nuuwb!uX"FIHA[IMsE5:`?f/j~`eŵ_ \[Z^K cN,aٍ7Hb3^mZ gdjry?> @|Sj~+|?h ?.my%cF$9;M}\.s,)&rTյo&.5hKEQ@Q@Q@Q@Q@x&y۹ү oll~i4cWoˆ{L_هFkUO#5 _z?z*W~x;Z:vsZAO?_x\Cu/xR&ˍJ=,>ʭХ{]T׺ ң|qy6s't=qy,hccqrX7'=&>ui[w_. _JIm%OY+nЋyZ>d-dEMGWIƷ[<'P`i*<-VdTJ.P}ڊov<S{Hc\zkm{i^f#0o6wXxo^ד[<5!B#/ݖM)>UJׯ}ǘM2;NjtOk[tlג[U'̅w,oTqgxgmӼ!+oK& ƴD2N򠕭wgJ<`QE (((((((+ˬncZ/kaX֓R(k//Su_Ě ?3iqJ77q6C)`k?:cp:𦟯]]-M2:ۘQ.yR6;~;OZۥiz>o~ڟrL E!ܓ8;l[ZM׊$"Z"7>bry{n[#Oq~683ƾWO[P^Hydߵm8+#uOUvY/_xݵO(Q@Q@Q@Q@Q@Q@Q@Q@xvz~oN߯o½7VSaIs&x#5 _z?z*'akm/GnqGcШ ( ( ( ( ( ( ( ( ( ( ( ( /kaXֽFFV?!Ԣ*@? )|iM-6Eƽ2H [p|T //<3xEi?oj$_}[͉߿KtO=K-~,5=\>t#KH $HYYe-tԴ͟pYPo6`]2vk ?þ.]|=hKkKż,Rv 3\?GkEgii|[o;YVQ9L!pCF"e{>_O0<+MCYfaeg:_5^kxјG $ TǍwG<7*xGxZ$mIS3ƨNH\::t}YO|u]k:} 0\l!8T\4&0 e>Ú|T#΁'mMպef$ɘy1(ZWɿ[~ |{߇ 4C7k^ 4jȷ #nr+|_xYs.Ş^ 9ed@$,\,}'Ο*En-bҵ-n/)kYDF *. +WέX뗗:h]Ţ:E%hFVIāՃ#'\`~3z.ۋ}·%vfa6ɉ#(vbpxÛ}z/xY.I.mDy()@!xfHӵzwu|%g>L$-|QQ6ڸ~//'þ,O=o5[}[i$242-'P`aц}-ܕ#~_;1],55cd@[gz] g{G{@tj_b횄m/+nvI&Hյ]SLlu-)$͝ʀ2Cd)]iQEfxHԵKGlm0D\#h%2p!TQEQEQEQEQE4ɧM[ ǯ\׾Wiyw@H?3_[i=uهFkT=B((((((((((((((+ˬncZ/kaX֓R(k//!:Gx;^; E]Vx6[(`_NYr %,<߈+IkݼV@B16?ÞGI'?p ^<3/ƍk_O~?@4={AҼ?83J*Go |LZ;_dž/x8lJFf#nPqA2OWo _=hvIqrK=\[ J`O.|\tA(oR?yyvXkO~ ~ӗ3Knt&X?sknB$($TOĚ,s|C+_k3h>-xXHbK݌|+m|Y}NYФ4=?<5|7x5?xDL71;]ۉd$O H~Si~On+[oه{,0ٴtS5ÅŸ AwO𧇛t iȓ0,/1X8qȯR/SJzmcn&8M; ݸV|:7:wp Eu8(N0M'-?{nxaXB,lܜsڗ/ |.R; _[} E Hcc8PqҺd6C1mFz 9$ɠG/mWFפIѯׯ"Gz 4$|HP<9Ay?l~^q8]FA$v6ȽaPGBEs9-Н?e4M;?I_-ݵBѨDl$s|q>oڌ>6ED" rfO,MPN9kOT;hc#0S9a*I⺌4I4gH S}+GOß~_0|MԵ[-,|W&4iK>"IK[%cT$ƺo.I5=ro< x+r-;A;'iZBC ,qUQN:CV|叏kx'/dGyD$˴i7E 7_Ow.+ixYo [PQV'GaDKm+8 yiH>z +qrnBv u[юK~Gʶ$񎧡|ubY#}iHmS31G< Fn |~τ/IxEе]>bmn!6b<ϓj)#,IU`VV/"9=I&>>Cwyc88vzRZk>7a'Z٥\8t(t{ẖ'ͷ*v&UrySh%% }+I=09փ!]6˹s_iakjN yĝ2-/6D,RJDQSMhߝ y_cᵧB]Kw%ı(] q^׭ү=?%i]S-/Gn&*(Š((((((((((((FV?!Q.ƿHmi05(&?:/ gs:ƑzKx4裚>@̍,16#$}?:~׼m[x_O%.=w4!S m9ߌqһKwo-cuUڶ j~ۣOoee,wI:7|BK -C<\|y Mi)vKq7Db8 (}-<yjگox=Z98{rR ;޹Ժv⛍2oxO,> 6ɯPpdT)ES vʿ4zW]Ko~@ø5? h XPTwH [`W2|,r^!ͪ8k '8%Dq9ʂv˰8-m"d{ dY}1KπrLeݖc{iwemf%n akFz':_ ^hVO}>s3[a0;A xO1h5 Ej:Ŏqu2=īeK؁ I;v{gLW_5dTV^g~k}N % 53_x0|Oik,Qkj+oayj-'ú$_˴U_Vk:?<]}_T6-!u)4٢Ǹ,p|ShεG ٬om qXKvs*o qտ$y>GvG/'Z]q<΀oXrѩe):) dׇG\xVOu}}2̓Z-_(;FgVw9qh$K}EP0((((j$~E$)k+&G4~$Rێ3=93BV?B{iWz#ޏ^_JzQLaEPEPEPEPEPEPEPEPEPEPEPEP^]e#w+רחY_ 6QEHYqKR, g¾*lJ+KK9{IkN» //>!PBZD|%|iuuE2iw0by$'J4{f7^-ĺxݭF|0DYlĹ; ''oIǗ!ؚK{Q^E \]p鸪sfdnei+lB,+!F1屌dkK5O5;ѼQRk2H%dݕTsD~5?&r׻/_e+{}-*;A6#:&`sw27GSToxq9^].ZR 42C ʯh_x7rmlu3anvO% . }=xC7M(u&-|6Img̒e}2qI+2Q7Wo}MZh2TvwFi[,|$t:yT(e[$`ά18Wu>/Eg⨬4 _iдXnͬ4q(<7k1POגjMj:\Z6&s3e38BQԯ 4&?jK7ko/í }8jr5ܽK`oivd__newiiz͞FLo'>oۆ*|9r={ŗ^,ifku`Bs Mt/rǗ|M%y#FT6FsۙZBdcj((QEQEQEQE$OnbP|>߽{%x˶.)#:$} LuO+дUg?1^׭ҤESQEQEQEQEQEQEQEQEQEQEQEQEWY_ 65_7x± &QR_t_\9~ij>*;;fw{k&AN;//>%Ϸu$5+X=n|?rAZm.بX6ātB PM&WE+u>r,bY*ʱrV/y2='O"1>-aAx詔%NЁ>R@ۀp~_|PeԵK?z56٧͔r8)#9WVUӺo|@> x[I VβhݦLIU\gd zSJOAjvwKk4J$bnPNxqksa-qjsx3ر%f`$BWk/u/x/*%|=x&ŵfb&/MV[_ajڡ6?; Tn?Lڶo9\u%YqpBQc'+Ҥ}4QE ( ( ( ( ߊfĖM(^>s^^;>X/+d2+9o㛨|We-sgLU6@-+rǖ_Moxڕ?:mP5,-=ۉv_9mm8ȗt^gxWl^)2Kyhsy _ƑZx5iI_}4x\ J-O!@+oҺ@?htX)|/uzjWG,|kv@\ yI.›-mǶ56tH"mh~ a$-onW[:՞񅜷kzE#V:Q-݆$@J*JV z0'KD}G|>P[%?dQIʄkqãP5{f~#YtOx6IX]:4Xn#qc=7>/c6:}]ÉaB-&IBR$Է8ut_O_EƑg*߂G%扪A ^lhyIVoׄ~(^&$} xEGmr9 ` ~ɖ}K°vv:W'E 2G3CRi%[9ou/L ~!iԯ6T m47Ge*rĸmt`Rwݗ='ngͣk".,Mj8g`΃h,jN[5z#acy-6'MfX 4p6lrq'x:fmEhڽyD.RHAozNKwѰU2q[^y~rN89=MRQ;<|/vW|G{tHo"*6X$?1xpmO}S]m^^-[!6LMdD9$$C {KLU:MDi7:Z72骪S6y8Fis֑699R?GQ {@ԋJ4H4fOF6c$,#j&FÑȥh#j&,ޑk.8 p?* {KLFDi:ݴvsoHtYH E~MΖd̺j}& N8R)i#(R?GQjEoDi6[ik70άij0o ps(ҿh'C 7YᑷSP;~g~5Ohڜ_۰FzN nrpkoj?.|R,~w;bC5?̱1F!Vv߇zj^ү]q 7l] ]Z5)cXaydkS=)sfA|g(u\ҭmQaZK,sHFTɴKOT) z-NJ.45nNojGtdI%v[Nv}z<-[]isCG{1 #sOx2?7@җ\iL~"a=7XϽ4M}λF;wClGE y2I fBNQy}f6G[XKޯ;Ϩ v>g;DSAʻACwjvw< 'VOF_Sүt`gH1cR^&@CdrGſ3GAqG<="&YDe 6N0 צh?|mV=OGƍ,w6цvL6>? <6s ̜ rr T؄#{/sZqx^{{Kb12?|zWUxFE ~ VenhCCe7f"+H MLN HBg< izcOPGyl! 9 ᳕Aݛv+^%ռIq\7ĺmͤ4eXgҫǟ wtYWc?ipA Gj\"IKg%x&_ Η xKCn 265 359 0 C     C   C" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Eռ} WT摝#,JI!KHмCcsWz$yG\+rI*O5RXe( Ρ{pM{׈|e>Mm*aKFupW*8Ojv:XYB#yJ6O ƝJ!egMq=Ķ5-H&c~jOR^Et,U>`s]E.4AHmGc 0;Ε6izeKj8&<'$]x,BPdslr¹^ɫ}q5͍[^ZʌgǨG&f׺[2(ܜkixr{V{ rk yǰ;_D#MUXooܟGΑk.PTa.3OF E 55 x#IAhR[;dFIJ (:z>I >}^MqW^_}kCmVB C2ɪ(gԦ<h*Euy Ҭpy8~}}@2mwg MhzG MTX=FG##ҽ/!}'˫jzeeǔ$1Y+1Ugod&] >}^+?_]oxAt#XJԭhՁ(Byx+G_ ̷ ccu6mRNR{EJ qԥ}?n>|?:/ ma=[~+խw۬J!OYwl#9+_/z?t}+R[K>]FJC n]^ׯ3}Gۏ+n>v?bOi-;taFT9C0RMnm'?DV'ڽW;qOn>|O{z,ۏ(qOb}ޏ{`6}Gۏ+^}ދ >}Xj^X DQ Wjm'?DV'ڽWo?)!^6NC A"GڽDx_74M6\jn~pKHq#$p|ǚ*de`tھhބ$9F~=|0} W\ \Tk2sTqo?ּKy ߵ^ڈ ?+ҠFRhI > WG3'\?MoN)AB]:bjJi9I+kZ.6FuO4衖D&&8 JfzaD_+ &15ZEo)I9#I늡__ ?xkp4=|0}?'gjӴ+6I$DK irmW1M rKPKWNNz9a1Ѵj#-kQʼ%-AԏgO5?ើ> i'K[~[_I55=l>m~lhL(BF,ێ,hzv%0nfm+,`v P)i WG3'\?M% _V|=Ӵ]y_4%ϟCȮ n8=+ǿ"mOi=$֖['|L9Vl#= &gO5|/=Cz櫧V7zZE6g.]T1%lpzb|{4;gijjVwQ^&޳C ;>gO5?ើ> h?\>*x[!~Қjz6hT0ɮ_zǏVG-kVŴh٦i:bHlg8$Ww3'\?M_ ?xkp4( k6c8"S#k ǖ'c\Wڏ~=|0} WRed7+~E__ ?xkp4=|0}ȿZ>}k &gO59B_ڏj> WG3'\?Ms/G֏ZtzaD_+ &PGڏ~=|0} WG(\QGֿ]?ើ> hzaD_+.~E__ ?xkp4=|0}?"}hQOgO5?ើ> h twh׏gO5( \zxsFr]kem+4q4r@'NW }|yW:~ucag RXZܖ 8y:քzVP@>oo<[~e\ǦEli-9=k파4o:D }IcRSx߄|3.{6w4ln>{W_,錴`n ?_o |1Z]ƲtE#g%7#7_6σwi[8p1$߳^y}xH|Cm-dž0\ "(,L>_4~i+ȳWm?"~Ɖs^U楩0ʨ ˤgsg#W{^%ִ ǩ'bn$Iѕ PCkmS֍3r7u|=:Z>=߇/_NA2 2:J*2 6r:"4]qK>w'F5ơdZig@fr TkdK W⌷_]׏|-Ϡ:R<hؒ+;= p𗍵oO':gJ⹰)Hxd2.Cg.>ߏNci+xo,M95$l!)q2&1pNy_~xT~%"ɡjZ=izX&I̡Wn$nU#>;j42m@;Eͼ0Jȯ+\"d%ⲼeCFI'~aie<^O4K'>Q%.σچEEssh$V6:ErvɂCl<,7Ago,YPhȷe$R_ZAHJ&|7 ˉwI pj$Jf׭)^Qk S R&l噤c&"̄Anֿ dG~T}#QФUc6V*sH8eb [| r <g᥎!˘syq8Up=k _QE# ( ( ( ( ( ( ( ( ( ( ( KU4Dsɖ2aVuOW㕡\YmˠYip]Iq BKy0;*@N?v r'T?9\7m߉e2@LRc+''T~!k+/Y4X v%wgh7=?_Qg+)|?G_E6[ C r[T,x. ]qQC-|Og9ԡ[y[ghIg(uOW><\xêMh<,d]@uv}qgUi5;^j K#`Mz;~;'T?9Gݟ_|I| ;JRGvҲ)gE^pK@|To%ZNxnd0RDuR+GBA g(uOWua{xDF:PѬuH؅ 29SZ|#j%4VM. u*׃g҅7cuOW}O=FYI 2I$V c:ƏMuku沵I&c!e#lsсEI)Z8JC8g|9˧,tZj3[ʹQAR7o9^SaS)[4UWxVvJ˳=/?~@M+(?c/O=SHdϢU`iK $IʧEuN?v rvM!_|T96g|&֕$cޡU֥Z'T?9Gݟ +3S~N?v r( 'T?9Gݟ (??_Qg+BuOWy:+Т3S~N?v r( 'T?9Gݟ (??_Qg+BuOWy:+Т3S~>*nm^<ʖ줏cPEP^u i=|gx5]")f1%pAF{sz-f|Bg*}w˷y;߈>!zݭ"48%Ou,Ɇ:u4ַ⏀ Ѵ5;neFC)o1z\_ |% $~Ylbo5P@U6+ic[u,'xrO<1!&N\&P`k|81/0{F{OEy}Ɨmeul@i'1li RQM64'M~LWÞ Ya+>'K X$92Ϋd~S+:#5淧xo c[vZ\kkpW={6 #$T~ ~u#-x[< cJve\唐",6Co|K閧hj$A$v] 'E9{׿QErYkG>ɠOšhidHJ_d2a023j᷍-Q<.,,uagIr[] چڦzhKu#@;3F3q _MǫBR.\FJҔcgQ^ _MǨjo / o؟L/NOAjxt5 s՞&ۨBld>A^|V6j'8=> SKt-Q\GPQEQEQEQEQEQEQEQEQEQEWxO|y ;k 2(oa$ʒ)Ju1^_6x׼y^;8ufGk۾w;¿3(Uo^`7~>=|RltOz~soK%2j,)Q#;j `acIY,đ;q,,rI6/4d~,eOhZEA5ŝna2>aЌQ~ KԤm2 _x%^_)`_|e7x9pS6iM=R55}3@-ok[`)RRdX*m38;OO|K좿+&=U#!x&EwU%H ¿"~|qxb]ÚeݬZfI  яX*:|1]|iQ?xFGX "YN@[ۧm+/ j|/{EjI)A 9 1ٻ;_ oE}%qnnLe>/+ϕ~;W|UafFwpjFqj4~[nݴa g-O^I9%*"ȈSmDyAsßƋqƕMnH ޣp2ObjωӴYL^8T݃?Crqi46 ;6G3p63 '!珴_w!ec_@p+rF2zf'օMӼ-։hc*dTː]2FOSĉYէL;4$F' qTSOׂMCRt.=-` iHm7;IrmGQx-gYYmveb>|$ý+X^pRV .HFb>00xe.z~D|W~*kQ6wx͹FU߼: 'ž8ӛhQ-g>MV17dxʅV|H w&[ᆵJeʴ*ѵeR8$ Pkǵo>5KGxG3xxY\mLƒ2lm*c/#{ǿ^xo î7o;H =~Qsr++']ƙɨy:|}&Hm%DEibPF&By*q $] xfJjbK8Xê8bdISA~CO=nCҵ3/!cm][pl124d{{Jߎ}<[i7ږïQekY,~f  C;aSq8G🈵MPY4H-M yd&O跲-꡷5O3}}{|4MRO լtqދݷAsyk//;AX=:k{torD&&3d}f*2@Ax7NM{ΏQxmaKfoI|dm|@\мVN]zT1mȱ<  _ _^2lmΡlK[X! J/[fi94Ԅ䒿]3X#U /Y4 *-(в!`\j~v$WSkھǕqg [Ҭ`-.Q ?, |mŗ? |V񖳡KL4],nu 10l>ߊgi/Vik?d-rM6qʟ_ߵ7caڐ]Jm5K=GOsFo*32m7qng^34J:{w&qw"+ii"gWzrifxiR©nc8gši:g4]V/9f@pч*1]3mM[~zomqwV7\m1+UUIa4~VK$ nV32,^;׊KiI)Tn P2)gz2#`+ n)JM۵k 7PA4$ȤDB0* rAAJGY]HeaGB+Caf-JokRZ:K;lU%E ?$KhfW 0 cjm!&h+ xßR |33%Z^%GtLq|{*}hWȟ ~.D>$xv5 gMM.ztW"Xu#BX8ٺM4,k%;AvfFBw*!T՝o%I5u_ +x:!k+^o.oMmag#9kc'i)x[_Z,km^PneIهpΏ>vZo$Q _:"Wa7*+x]֛|'BH?>}w Ŕ0 Aj/A}w ʊ7#?nuE лu7(϶]:>MOۯiGvZo$Q _:"Wa7*+x]֛_C uΕqK5೅Xg%YrݜTK\*8j1=|Qa}^H$qMj&E|j|@~&-/3[;=?NQ;.S(O԰%6U]7Sō$Fڵ&徑tqD-3G!p$jS47K=cO闚/CWou C_7/nб:o uh8TLWо > R]SOL'ۗ[;֮\WQR0"Eﵭv|Ijw&h#hdX H>5V| {6?:Ok:浦Z${Y-1xXm1?<^1m||ko֭@nG} lWlޘ61}_F{'GOΝ]?}²WO̺6ql't.i6G4Bʣ WO"tjz>4KE43 cmRcm=F{'G}dP:7Qĺ7yB&2}',rHe;XGu M ԯ4K[4 72+qF9NH>m=F{'BawZsL~xK.ivxV_pA>⼃^8t_BEui#6XNXCq֏ =K7Kxcĭ|7&oS~eBMHp/- Ú֝[6#AIa{\*v쑀yp2p=nyjmK_Y׉4-Btm˥hFQ+˸:n◈K{X5 k.k}j&Ih%]H>mkS]Ex_~'Q.|=ͤ!8͹FuP[ 4l2O \!5[j%[z Hkgw[yq@63WW]zw.=zսn4]bXeI{Dȭ6.G'<y^^eoQ3IQ,e y{ 5/mÚ +T/{{xԠCI,kwJd|vR; ((((((((((((((T"xǺccip.y%,Jx0Hu Ub AA(㖋+Gx ި'c}M`j ?|'tmJ4Y3m#)P\ת2NASƏmDxFmul("x1}wYw? b9[IkZ-YnfY<ɶ9@14R$;1~ZΝ|/uZO}ܯOwndď$Jb Xjm[1jjz]k7 H"c`T0G9(=BuGױM{kF;gh-$ͿL p1o߳]/eu>/m^OC ڭѬdQ\aG4Pm_W_ Oٿ^ՏA&o[}I ոDm(ܕ/0WOYx4:o?, M RH@a>qO@E.wzw O?SjpJ4Y KvS6~nWQE6|Ē[QHaEPEPEPEPEPEPEPEs>#,3N-~vn956K^'F0D]rpmGB{@ݒtQEQEQEQEQE_¹gfu.w%ƛ ;5Nw|_CV.#Zj=3\X'R2fڬ| nthumoMK{饊Mf#oA v&Ҽ|IuPg#4bq5a=x M{4Yf3\˩]K,o)RdtbpKmsK_iyfKvyYkW'MR}Gfӯ;o!8¿ |C,&ͭ<75#܋۸$"<\[*WCfc|≯to6QV;I *8xϔ0AAɮ:_s·9K>MI..f&7̙`sw{][NqnMn/]KC^$΋G`7.-&VTbWP@nXgL p56i5WSk""l2ڤ0*17o x{ZttҵDR0@%ؐCw(n޿K\5?ǭjjڅM/+$s y N>Bq|Vv:15%wI#%Vr: ck?<|;Hdx[dTp񒈨BF1w>g|zS/(&泪蚫躍%}v J$Ϝ>*ПZ]jzMB-u; n=2'錃Wk&uDhb*._3xzj| [K:"Q [K:"W4UrMj_/"| [K:"Q [K:"W4Q_?7$V?'տ]3%տ]3%}sE\>#Aj_/"| [K:"Q [K:"W4Q_?7$V?'տ]3%տ]3%}sE\>#Aj_/"| [K:"Q [K:"W4Q_?7$V?'տ]3%տ]3%}sE\>#Aj_/"| [K:"S&>)2Ww0| :@㨯)CV`ޓj?7$5YlqXЫH\,OrIڦygvg[R0klƷixC= WP.x :zͩxb4{i.ˉ%O9ʢ`3O~3t/jXOp*0p='Z}+XmA1 DE - 8_NO5G'Z}:?7 ?j j(T\/U ͻZ!1F@ZѢ ( k1\x?y5ܶvmƥo3ڤr+ԫɾ.4~bndHfHs.]̠dB {3[/\k{mB"e!!'@}pɞ/|k-5c.mme -0\f}85wN]VWH͑#G1g^O~6?#:kcuC+ t}NYʚIR<6rX$^kCgm.<'սx = TX0pZ=iq2nAjxnxOFaHKR9#ԕ%g`qR<Ȗ]5zL Oj.b U'*C 񮫠èxC\xk:kL/SM6il|8mt$2(OdWI6Sx+>ԼEhQ/Ү-PnLp svկl'_ϩ<itwk}s8PeC<| ĝsI׋ +[w{H5kWBH1=}XSt~W"sqiJYEP¿]?~(E?"~K!''֪ϙW2($Q G.~}NC&CU?̿_H֊+g]C w(YEP¿]>K!!_/$}kE| G.~,Q_ESAɿO/>JYEP¿]?~(E?"}jKZ_%,Q_E?WGD?oD>S|%䏭kPxh7.ngBU>Q_ O`~hYEP¿]úi5 xTUfxQy*# $fd Hz(FemJJҦoѳ?Xη$EyojتDi&-vWv.#rW^X3;WI^kx:o WSC`db3 ]z:Կ'sK.A/ qEpj_G ڗiwqEpj_G ڗiwqEpj_G ڗiwqEpj_G ڗiwqEpj_G ڗiwqEpj_]>izL6W u2̮ā9 ( * 8D*9T紌oKytEgb[[ zl]hQYؖfķ7@V%ٿ?-o=o?6o.KytEgb[[ zl]hQYؖfķ7@V%ٿ?-o=o?6o.KytEgb[[ zl]hQYؖfķ7@O KO=o}heA*A?ؖfķ7@_ŷe׌X0OX!f\t&g:;p&Oř1bO$Iķ7G%ٿТ-o=o?6o.4(Kytb[[ +?zl]ؖfBķ7G%ٿТ-o=o?6o.4(Kytb[[ +?zl]>"%YKrJhQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@cxn5hGQ\;am^ @GwG.|1~J}H>A5hgG[jzV:u HSX ^T.<5Lxɵ^Lºi+{rg͌k|iZ힡{>ԯ_BF.DfYN?v"'F MjMa=?R#(G/i_Wʚ_K-7:r~n,nMٗP rᅙ5,j2xD4.jXK!|V-N!oG2j$SehƒF7Yy٧diGTpx]">{J8¾r>!|97ĖZMz"hR* )9|G֞"-|ghũ"k(bGWy2>9fڳӛG_o+ ? W7XYCcĺΫ_xVэ5ֱАa >8'k?^!о &nKbo"b$i61;2ᱴez7mr[KV.i.ȏq=aPN;=ȫ6ֺ_mthl&W!ex¶8v+[h+2{ ۘ4,RHR|$`#1ͼM>o+vtQEAAEPEPEPEPEPEPEPEPEPEPEPEPEP\'K-:S [GvV\2º (|Yۃqu? fH$rA855ώ#} CuhP""!0 cڽZ|q gQѤyJ$c8F=dO'& ! Z%CGzxOW/c)e*BTdpN"o='Gazul~RXw63 297 359 0 C     C    C" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&se䚡y*_-VnhoRA"LF:|m0j.tBҡy>}OjهbܿCF3)Z1}eZy2:d}j)u#H$SYkPQ+Nc.2 )޽vY҅O)ceqܱl'z^tUTsp//'{~% ̏fdЎ:E6V)@ww`@HZi,ie"6c>lv+ퟁ#>*Yêj=0_^Jnare6ǰ[*-,"$ VBW~.]kN[moF}9zgZ#uJccO( 'n5#v5}RSōcOd֡E66G0X G0X F6Bl _k,P4(VhNBA7n}?s8\ 2yn>Rd$r7O:*T!G5\DԪ'fLu*zw=u4١9]Z'\J~}*s.ubU{ .GB}qڴ>}kKpG1"F4h:Guo9uU.g`ʙت GSk o^osc{>$S\IX&rՕcx|>r0G?wbK| jM-HZI]SNI 2s|Gi⸒9bTI\"y`?/|R?Q8?ȣ:<~xJ t F56_6Fڧq[wៅַN pk@qһ`zs^8?ȣpK:<xV}KWХ0jw"TTUU=]Ɓx_Btm2mZdgB"bY IMt?cx|>r0Fol>8?ȣpG#tfZ_cx|>r0Fol>8?ȣpG#tfVn䳴(2is(S: }ǭy|}r,R<2G$Ur0AQ9|*亰š\G/G(V_ժ+X}oGqj,|cZNnt$n;2028%bZ]˿l>}֗1"}֗1"}֗1"V).[Gr% n[[`gF|m 2y䵎 F5 񃟼A# k?<)ⓦ_维6s DB꒺Ί} _<þ =i:<603G#y8A/ mw`Mb\}$~غrdw#y ufD$k־xk]SxlK>.1^#_%ngQBC%<'o6YO?鯦F)l\7$a.PFgR=v_ĶOA >9զZ?5X(iO):xv_|%ؚ\ZETv*U.I?ľ~_zkVQ%M$#xDg* *t/ g:6qaOCXߛ-U`wSII.MO|DKI3Xk@9^uFdCZÓèo汓ƚ4w[wUt0$(AOŞ:ּ4 Դ2G 34\T' |-=:K/:Ljnqn,縻h&?٣;FXr1/t-A֗=>ËύtXtLȶWov. mM<<N>?xlN_b>!n1lw?xwXbKG ] Ozyz[9b pʰ={A[.s=ΣlGqosLVR y+;7h[޿U\?^H?Ixv!;lm߻BEw> Y]k\mm0K5EIU8pA>0>&j735Uۈe;;=yHjKrz?|1h~'j3iOﭮl'g[HcXᐏ к]|SKNM_sh^-n|%7Jҩ U r;be̶?}Fh]-ъm f<$5.<+;ڞ 5.E2e^N5[T<[|"yHtҵ/j: ow,*F#l lM.![L/|zMBy<$ll.X+gw'*g_z╶ƱF|IcY5YiVlLÎVKߖ9<7Z JtwUo4Ds,wVɜrEn↝;JEޕuu}) sȒMQo8MQia'*RH紓P{7yeIv:[k @5='ȴ{JZA j ,354X[BNwZyΝ^5-WObeE,P:FOVoh/ ׈4˫M:LznZ]{OeEWşRQEQEQEQEQEQ^C/ƫ^l!4袓KLTrL劂@F>֮W=RK{RZ Y$`$er8W%[h xėBjouoo4 L(&7ldf n+վ.WCtڄCDYu>plDgc<>x.GI$.ő3nlg.3Ѩj?Z?OZ-äK 1Ks"BVX1N2z^C) dSh_MΉe 7I"8uO*I؟, YQk ]K3[rG I<0׎o,Ind1 $R0UA3Oӭ4(mam1h=>*GR_XCtKq$pd`s9ɭx~:W#XfGgpEOQ-l'ׁS'ndPIySKkUEDa1&Ec#tXZ^C{ͨ=E IQ$^hP a y*iv05;lm.o-`hrA/>(_9Nj4x<1&Y|$!]h]ݍW8J_WEܛF$m.\6 x$ \Rp/ڏ>;qj6ik&F(`od $[T$飉nTjЧ^Ѩr̞*G$G3'rI/%WI~j%O~˦Nn.cbbZ-ҒQ#R9ɧ]Ŭ]No 84/hva6 eyl ;^eNʰ8fy3'rI/%Wmៈ<{6wY]^-$<)_! rBp;Fmw@7]uIZ'#udUѕAO/<"=QfHـ>__jR4U OYiԳj:b9;W}}rxV +5{l-+nA: xW!tWOj.-X vv%2@kij1I/z>qq"[.Z;l E1 |Exy9wx:EMJL/o.asqD0s!/_Ӿg\Kik-:\][\Udkl;_נj+;hڎڝ1k=EWlաHΧ "18> 45 lKu5"=Bn#*1 "GQ~ve#y-5 K{۹ l2O$q2ۇ`@rkԑTWBdЊvQE󇏾 \vҭCX.nYv@rgk !O Es [k[Z=i%ť4 -1\Kx4!ī 3佀,,X<&("TRV_Ϟ-v sLM̒jm܅h@!# pNz_ |0|+.#~omY|EO^؍/K&VR`Ib 3o|#q5MWv+ek mM0F쌜♬.|c(/b=ZI śJ"h} o+UBU)5>oOܗտbj^mXkVVk4zi|0G$QB342Foܲ?JU'jHp^Yχ $xu'Mnڌq d zOý+–$ݟ4}YTYe̦\!^'*E±:7ۡjZ o𶹭jzu{m܉ 4sIL\Fyxz7OvR!g,280)[N^~ ~LoW{hُƖZ-t#SRѴ Mg,6 R2OM: 8"X(@XɲTR9Lh!D7݉ 2u־o߄(t:Kk:5Ex$澴#4&?/I;s4ZM滫-χK'0Ka%NFy>!־'MRxLbW1(}n0iܓSF{Rдw׿ %Ȭ5V:_O+Im|+, .I&❋nkďC촿jriSxA6,k]B9 fa*ch/Uo iw/+}y./WGwu[{{>팃V3ߕ#f[on[m8ݿo q[q~U«d4Q6r_SMzz>n_~?VOX['suy&P?3PpΉ$9 o>k`H;dWF*vi3^D= F꧐r⧢C0` KxaiposxK`eYH Pž_ 6:mwma0a,WKJןJ I0IPS85Lu@gj:_ Tv xs]5XԵlC E[(ui_tOWҵ-Vmj#Mm-kp/|2lPH uC>]YfBbtB 9kǑN}XȨPp6}ӪW>9P珬(k 7M>|Mj"X/l.,.l~֑2I, > 29 BVT:+@WR;呂06 fhŽ]?fͥͯɣIX5;ycW 4+ E}$]cc;c{ '5y7f |=𷈴8<&zW{ȒY;6psﱦ.!٬m6&{{] Fn7洁t^xGWP:EƋf{dcvmRVnV_/\4*Hef cT-s '&KbwQmuQ462bDIeA*҂v".]WgxRS4Y,D,Mk# os'b?g"}R+E9lQ>B<ʱ:##$l˒kEKgoO8_<3 mOSmR[mzV" <2H껇"R{W3_&wuJյ"jzBj,i+)w.:׳M?xRĞ9V BT+d, UOa8i7:m嶫k3TY㷸8 !BTxoLi~b}~s zGLyͳyRIFw(Ẉ 0HcfBّ ؀k x]0ur֫GQP/|vBSg<#tѝ(7aXT^\+(ü1@{O);G<;›N<#u{|b(ü1@{O);G<;›N<#>_='~ <;›Nü1@{O);_W(?Q_b(ü1@{O);G<;›N<#>_='~ <;›Nü1@{O);_W(?Q_b(ü1@{O);G<;›N<#>_='~ <;›Nü1@{O);_W(?Q_b(ü1@{O);Zt_^'<ciڴ&~y<EH\YPF0_L}+'-:OmtʌIF j'W )4. Iʥק8!!% qUE:+`-5@KFH?Ue'ke|k19GE9Ho.}{fE͆k2O-y@l78y;J[NV#zb _NQ _NWo?*6/G@ZmbD2NXr [ CwwzOBe¦g;K rsI-կ{}_qZ/<^~'-Ƒen{-&9\f7tQIA W^ fn 8ڻ8q,r I@\dd0z^% ZxD{$'ϜݒrK5_j׺^:Oq)U@NDP4PQRjZ_ZC*FE5vw7t“??Y+hA(Ѓ|7U(o?Y+hA(Ѓ|7U(o?Y+hA(Ѓ|7U(o?Y+hA(Ѓ|7U(o?Y+hA(Ѓ|7U(o?Y+hA(Ѓ|7U(o?Y+hA(Ѓ|7U(,oտY+hA(Ѓ|7U(o?Y+hA(Ѓ|7U(o?Y+hA)VeP͖8FUb(_'QJi\h> ׭GQ,V]# -Ñq+ĞxDŽeX}Y!b1Т^ >7nmn/둉"bTV`pC \~SzzčZީsIn0`#p(Z-?Oy]⯄_ umPr);w6R̦% 6X5M+NƅNy4i~"}GV. |UbbJ/g<7uc5:+{bh-H7(B9Tr?v j0:/lV!K0u$h " ownߍ㪿Vh/YiCO $țO%a";tRxPH=+ 7e5k=J=u2' ;H mtB9, mcg5=emwm ņ\ZLƱ n" W##h9xc΍kΙu#."(@BT F~^>s߇ZNJGNץ_%."@?6XUFWmp95|gG ]sSnԭksnd0r*[.Y=4x^o-5(^iNNXlw|99jߋgXQԭ/Ѯ [-N4Q'7TBS(ÏZMx &z5[Mq6,WIwv_hp淼?&ZxrYx158%e@Y7}/] :,Zڄ[[8G$n`;~EZ~C_z5ɧĭu,cc4%$iiM |Ί(,(((((((((((((((_7t[L IyG=@X5l#L䅊$qO@KBrtӵ?/Ic5*\kۯb%:cw_՟@KBrO >Wf])?'uM_?(Ŀ/'Qh̻&+SN?)?'uMYĿ/'Q_OiwLVX4SN_O?пE2?A5=G_ҭԮ/ v}A5~ xBxRd?hb=r~ײ <-Rȣ' =8ox_Z,X.^19yu<יR^{?tb?5\sۘg%wuk#vzY`i |ln]:_&]?᧋ЍWa/_be._F.yT?rܘݫ]S]B徲İ9OO_P [W |MLbGqj+_܏jk:z.<CO³.<64 L-+Lϫ IW.qݏjk:z/5q/qT, ̸y\, i0)_1׈^J:54 Z[ 'B|6h_X?½,|mZ3L S5 ^B|8`cW?§O~+gW)?¾sU3?x]=u{ig񎺱j׀SSo)g{b} oz߁/TɎf$;uLk:&5j ^K 6|M:_& QOG&ԗ9rg#o5ENk9xdMsW>"8;| FiS5jyoPOKL~:|Af{з|Ynk6_?+W§kЩO+)-~3N'LsZ6|h|Mnk:<Cs5msǫ^ ]2o&)(Q |`ψmH<^:xPƒn?7R??u׍PO.WPJ[#fMG,˨VL[7P?Aɿjc4ΪJV43j_נ~~[Cþ;y@|*.λl~#6tl*O1Ҹmk$^Ha{{HE!cc \x_ʰE6kdF ~м[W'ſM6c à{cPPbA=Y45qv#;vg7_mwǶٞ(I[6+Gio4}CKt-KOA$7VC,R  G>{]jGq<E4iw.Hz׆xI[ߍ fݭ$- %Ƌ$w$ CִAWZ<9N&bHQ;UF1OOK~~пMfK/t]v. Q-B*wFY<z/x_ #=EzvMhzΙ5CH)R@VwuٷUnu$T5Il% Tp8J\vzH6öZΖ6L."Br'h8??_ ?&cx~./oECnٴ%i \n1g|W 7o?x]}OWo,Fm+4o6UH !$W/пM>7RhtҷW6'R+>qI]'[T>"tn5[[ 4Kok6>f=Who~ .uJ1%6U,<˕4]۩JiE3A9BQ4?ϧ _G??_ ?&`fcXΏO ?_ >ß/G K(cXΏO?inIa:>?I,пM`hZ^\i^yqkwyumv@! Kɂ` eOi' \DѴZj(혴Y;p $W5li`#%11Sºv&=$TR2}+My}[.fJP[.vc2W+:пMs/kS/jx|X"s/hß/Xڞ/)HS/??_ ?&A9BQ5a?<_>R,?@s/hß/Xڞ/)HS/??_ ?&A9BQ5a?<_>R,?@s/hß/Xڞ/)HS/??_ ?&c:Omi!j}jx|X"iZ%m͖طY0ecPaEP\}]#JHӵTZim#'x 5W|3χ.{mgInڕ^l(AbU Sеi`}SV$6H,Ifg8*Ox^?ӓOGX#VW%q,FyO 4txS"xc7I}t5O6ikvk['[c[dd gÍSm~f(XG^] 9o7G4mEU>}+˿>m~f(aCm}b\ޅx{TC-ޙr=d;X 烒yiφ6ߙI?i|YnBp?@'ZXFƳ_Sm7ZF{̋`OxwPa:ۍM>'P{唞I9sRx+6[XZ_6bv8C !ۍ,zO^ ӼA 1X~ɪ€M`XbAc9nXxTѭ.g !PG21Ir V%zW/H j+'+o+o j+'+o+o j+'+o+o j+'+o+o j+'+o+o j+'+oM6xZYpA' :( _>5¾Ս;iV}bVXX.ʜWW-Pmi5-.5nlYaUV 2P]Ov.ҬtߴO*35DBXY@Ʊi_ּ1}eݜV^3MTxB'8F`]va/ zKd5{q(.eTsO+c8p;Յn- }ף]Epwvs0]Hga[_ķ6ZTs^\,Hm,%H2,2`2jNᆿbOyaV8r>Eܝ3^m\_i2$79F9VZ[~"fH{yH;"+12֏/g6/?uG/Ǿ:ﵝnÖ~$tUo718#){g ޏ}cӦ/na"28avv|mN]y4C{՞-C2c5O$]חMxQfSMp2N%X/^9/Ë_ھ-? hzM̭͢u,2Y5|*܌ay5X~${Ӽ »Wj+_?gjȾx2ڿ%Q |Pqq-+=imm%(5U$s_趑_}m2\p*7aZEWhz<_,.e-ӡ1&Q'*χ.m|FsEr Z8^S',b$;x$o :?D?iu(G .tTP;Q]?!K9]C_r@tWEEs4Q@ :?D?iu(G .tTP;Q]ʚUwKܞu}vVQEW/g+džQ\ =OIG񮯤v[X ¢ Wˏ>J_Z+uh565{)fU\,[Ⱦ7dWi!-5_zV @q8q[Z4M^E~}ŪH$. ,sBvi)M][w>i|-{OΗk&cy Уc$ĩ?5cx.?<9c'FLSU1]KkΔ³T =3g%fki4:KnHd8ڤ`W<LrI^,7/y'p*b*.F0 ߂Wqm%ſ>G_χqٯHo&f rF*F#"xx(+咗.AEr2ŏ-ڏy1:w<'[M ZKi`^0j$P}I6^ֵ[\Za,U- rgy;&_yχ~ŏÊfP/Uִ Ǚq EϦMUѼ_FLլQ>%lo gQſ?K+8]-_FP/vxI<[C$»(%oJ_F-rmw~fmuPEPU 4 ˙V9誣$BW:N!\MIbE C2es7fjFX-bfHͮ1l.eF7OKUńi#F7um`Iqps %`q?xۑ@_jV :{> eqŎ,"+t+7o/uoڇ7~nbUyI)F VH\_4/2OGzKk KWݎa><:a0\\ɬY[[=;C*$['uamxMҴ/Ci-$P6IʡB=pAL߳ι7IéXTB[Y`2bd k|ě txTSC]~t,Z1fZP E£l c# mF;-Gyj[dYYT$&`0:k]xN_ [$a6MpLe9nedfy[(5{=L,̌hX ~lva/&UQxSzܚ:P^Y/<頒hyw #C+}1ڟ_:?uv]u2lTJNIsH {۪h5ڗ~zm+k|Tb剹w+߁Z% O7t0=vm"PwBdJ_-Wqj>KuKgq?jW1ѹII#aTzy85KFVW=3I$v,pI$}3׷S:ߺ_ן ??IjG5OZ"֟2,??ᗾЙc}IWewf^|4%-ן ??IjZ ?L^YBe'Eן ??IjG5OZ"֟2,??ᗾЙc}IQ|ilgx&g5OZ" y)UӧQW೶T_M6Z4IyāIE[@39'j| ߈v1Xi㶌H$J* ɭO gB{ &?[T_.w67Y ZyETw{e`ʅ?PqUmVm<"o{3Egn+vy^ Eh@۶g(ݶW?Z4Pw?mVm<"o{3Egn+vy^ Eh@۶g(ݶW?Z4Pw?mVm<"o{3Egn+vy^ EhMGQ]>8 3J"oN(o{3n+ڇ oj,jo{3n+ڇ oj,jo{3n+sxk(^Clm"R(Vl֝i<#(/#d؞N}}Vݝ,0˧GХUѭ?zVt[].Xoj,jбO?@mQm<"oj,jбO?@mQm<"a}Z\Y\m!1"2v\z^ko̐Zzc)DdL8=Gn+vy^ EV?X{PcU~,n+vy^ EV?X{PcU~,n+ìA<t{IT~$ڇ gO {+:g!K.pH(̧(F(((((((((((@uor<\>I q㿈/<55֪I!!"p0sϦ8<=@hrA׼FED+ d1HЈм.0fb?◎;Cl㳃M+Z%7"O=kYb&.n=hiI⋋ahQ<,7HcЯR_SmݪG<`qI&&Z^ <}s͉MFeŝ[IJyLzבkDּ1&Y wZ\p2H QOz߉)}=>355⦭ >Nm'W$BD(:5'h2'Wzuޗ IxlؙB ]s6FͮD薚|mq%$K3V%H 5x$e Yjf;y%! ܉ip  -~k|}*Gu8 k(5,,-&Z5`#>[ 䌌>5x3A#/!q`%TECr+ȼ3s~UkkZIo0D{Ic界U$c?$ :yfZVem`h^X˘XnV p-oowkq]],}; *F}{{d0β[DI*=$7qI$I,ԧ5?ѭMDg\wW8-3 OAV!X9@2p_u~)_ˮ'[%̇ ƒ1"O>+KMZJ {?K-n-#֍JmoN9oOkܷlFm|Oeسp]7hQU؅wL9 e1sto^ ii\jWBgQ爸0O1|d“sN>7#w6ofk{i/ZGW'$HԃА;O>ؼck7o {D:i:oj^l|/6ss,9! #[v GF=?uCšmgSkVMn #NH8נ|_1q|ysW|+klKjJ n%h⌈s7*Oo3gL*VmN((((((((((((+Z=ޫoj&Ʒ ʍe 1kr?Z' fdx|O+8ϖ2-3='ա&LSa dY]6Sӂ4KwXB285Ep2-3?Z' f@GDbϖwP+UU:qK 1L;+9bȿ1L-ΐE8S"^N2*; h#_o1GGDb(h1Q/?c7?Z' f}UVq#WH,K2WtPEPfotoxx-20.08/images/adjust-RGB.jpg000066400000000000000000000276421362435004500167400ustar00rootroot00000000000000JFIFHHExifMM*V^(1fixHHgnome-screenshot0230-0100Fotoxx:retouch_combo| Fotoxx:resize|sharpen|http://ns.adobe.com/xap/1.0/ 141 500 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?(0>&˭.ageL-&GFF 'wBx= 5-NLeԯmlc^U #.,Dվpkܺy5gt>^]ז<9SX 56_JMyVKSn\œg @'RwM+^&V-yCQ֒{h.f-a }y̑,;]nnۉ#D̉YHKg?15ַcl5R=Z8>A/$:Ikz7o[3ijT:X R\ E_hk7קq.4M'sՊm?{d, jvu2]<ȾT#'h*cLzSҦ|Sgis>;\4OF%%X66rv8XϠ]^g%&c?e+*߻Bq>^h E`-CsqǺXupn+#)'zoP5QEs2 ť!ѴWO>UQGCEyįo?K+?*?XΏ%g^e+?*+?*y>SEyįX?GįX?G!0N_z-ȅ5Y`H%Oқ2s\;rcq\ǁouuD0pm`X-;g ׋6 bWf3h̃ =k)+qp+5km?MoǪ:Z+o[O=Fzrhkm?MoǨ4M5?2۴$]ZRR-6)f]D<[7)wLcSwN٠ ?Wu'4B!_zntwKĞd;.\}+X]4a&4M$̲"rHdJ@)wa7esե(Z5TC+ ocGRWc}yeiI%g g2 '`T:[xSWH.⵴5Ib$ds$d di[K]?/z}FD Hi2FXn`1|dg^Kow|:5xᲺeή9xVH7m^ppIsŋ5g7Z[ `dVL(\oS̠Pz=kεSjKg U'2JvP #٭:R}~g^e+?*+?*X;SEyįX?GįX?G!0N_zm6GXѝUQOa^i+?*}CQ5jfVfM6h!1qH?J"M~ Q+}H[\M:a!=zPJ’ۣqOmr$b|Gpzᴙuwh>7 kiܩ%}nro7W'm?tW5޶\z& \zrQ'l/(D\BPPrc4()UUszơ7!bXnm̽vqJDDU@}VGu4M*+{?4C&QLZβCr]iig$Q#erAdzP75}8j6ZĖa3I q89:㜃Ltk]c#Lc'SxC3Gu#E G+:Y|R9wK`S[feCʲ,?NƫD8VޡRxduOn#Ap>ө^f,3O}GTWa#\nws@I&xKS c 4hT32 aklAaG4d+yfszH7܈%T;vHa]e)azdJ"FcD#a1GCZxG}"嶒źDmSgޓŞ9tM.XlTy#`T˺XHxk_d矒,c=G%cb4JUq-- ;% [A'uthQEQEPU*F=WS[ :VB=p3@_GMO68Y3ϡi I(DQ *?s&D"A|S4˓wd0#Ik ",[^k.U3 c~@v",JLѐ q3t54SzXQtWNҡK)QiHN[1#= kWE5%ĒN}f]d+ 8n( {ZvOm[^;<ȚB!}ӊ'Ӵ}ޘl-ʔg#Uki07p9--]R[$Q#e21OC4>m]NѼ6A0eH>9;,bh,-a#l*(nJ\'eK{}BFDC"8*x5JQRVeN;c g@G).N.Gk713zT™?oB@'R]߂<΁GQ g@ G3zԾK]}c g@ 3zUS.xwH61{#XC!i9\gkwVK6H 8@}+}2` = Cgime(#'qX('׊1PV9ʤ^>.BtUTIϭ }l?]tPF.+XEQEW- :,ڭiʇT! {{WSEy lLI-CzN+DIv!xu5wPƱmzvab!tX[WX6]~nzt[#D8 ($:MPPJ7j$N{^EDjFΓ13zT™?o+?Rm#L޷tUt[?*:>K}v~S:]H4{eNjCr>`xקBSZ2VrIZ&Y Ԝ^a)ѩZYۇe9ZGg*X~b\7ڣhz9BtTϭ QV>.Z(+G[EJ<ŵj1]%ȣCJXh9$Os>(?ƾ-E$Deh7Kc<vZ.Cd@2E9M[P_-*P,/aA \!j$Pɒ'54ih^%;ѢrH sۊ5\~&@d3)X%Xt$ЃU,'ZusL)vgr<]x N3//--lܺ}Z̶M}ʫ;$80x9 b{?٦Ӗ.+i~ .|/jjKyeiz\rpI}I7$'v){h^`=zaԯXGGHRBň x+3T N eZ;9!¡@O8 arp$!'|j_ksi'Smj$f[f:eMh't'6WR#3BNprA5A2 4R -QYC#$ҪjԬ{=YJ\Цv76[lzޫæbOvfh| uh&uKqkisw+JvYLI9Qϫ,ciڣh/_^0Ct>e$Ky$di#11\. Sk4ӆ&ne*$ز݌q:KI?VWWחwF }u1 &TG\pWd{DU' g's(gصZWfv9bI5rXbv R] aRܸY?{cKA[_Z??+o=]n@?c ?4 V<}Nl7Ա[굤xZ[ eC#ޕfi ?tV6ֶ C$@Tp:RկsE{PbT؂_Hְ3h1Ia%'y9ԯ@x`H^}{֒[*[[`lwG< #QzIڏջ[ϵ_ȷkoTA #QzIڏջ[ϵ_ȷko@_?mǨ o=[E[ߕ <+ #QzIڏջ[ϵ_ȷko@n/nWvT2d{jQ3g ʝ@Q@ߎnki-VL*8ku9|'wZo\]Guls[OU !G'Wuiz>mRG "^s羡w$je2:DT8%FO^O #U-[EK}&Be؁觜Emq=;G;kXM2G>HxlX7?V冧s ̏ymu^Hscҡu2L%mXViQz<>yJ牵n*kd0KA$h 29ֻҲf4/5KHrJ*ӅMp7[e5 ^"!X @q{W}U䱲Kcg#$VsSkf4x^Z'-K럕uWq7OX;-o_w~l7Ա[?jX\wٚwt(ӿ,G-o_w+?-K럕/&AQ'rg W2q,$YRgU@qdXI0­0ya=iiVP,QK Ĭz'Iks o=G$@mG[-Q[ϵ_f$@mG[?$j?Vo>"}aHo>B@ 41J"+Fih(Icyd$FIA;ym6Iuqh 2n(/. ]#X,e0ܱc#~a\Sznq\<_j=+ϼOC` ԦfxvhٟRel#Z&dҧiw@ځ=vTl._3 EM]vM3"G|\s/x㞵sXּ/g?.umugo8Yef)XgdSz2SmEytB0iy-THycEdϴv`Ez+Xi([|/v b ZRvW.1v쯉]e|I#V>:~Q^c'GWğ O?E~?yWe|I#Q'Gߏz,'[{hLK OJhHH zqҹG.n-z+ekqkg+Gy%Ҭ 觌\"$bxy~zʼnyPF-`y=+shk7_g'}Yhk7_g'}Yhk7_gFƧxOfԏ(q bfV T`${g4(+p?"4%$Q1L~?@m۽x47om޹摘%$54 $8=E8!bNzT>L~?GOzRbXw54.d9%G4y1IEG4y1IEG4y1IEG4y1IEG4y1W5wV #mxt*{MҢYn\mkݷ >j4%4%4%4%4%4%=eWjc?<ƀ% '29`K#'C4y1JrXJɏGhc?ɏGhc?ɏGhc?ɏGhc?&?C}ɏGh7he.>"fǪS<Ə&?C} (Ə&?C} (Ə&?C} (Ə&?C} (,j*&@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@WO, ˉn-Þ݈ouOMOz^t]ZwO׌Ak%u~'Kpth !s]5i9|nZms9^(q=Dӽ.:.-;z֕E.o59|@Z;{e,6j: hqjv&jm${sJvhZCM=$q"x䕤w /"۪C'$ 3{=NK+i'dLm0'@*=O\F/fms+ O-;͕j\ٴEb!$d}kK}_R<3}="KKt'[9cߑUœ[j=7 N?tK?]c]RD]`mEmfډY *% vz- me"l@&X' ŽHs5m>?L9-C5ƣ?cP$&C`%CHj ho%My5Ӕ\P? N~%IE{h.牶4p3w$gSm4 i7SxuOEm$&FX8 N?K8"'PXkdE"321n;dzN:Ub2LnX;1q?{wt,$qKsG/fLʗ?]ռD<@.M==.~ X"A1Uء"0 -^dx]"KǷ3LCg8 `_lK!@|0Z\j\unQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@PE_H E ί[>gR_ M5ţY@m$ifo#1$ {%zM>҄9Lr`IwǦ$5idBXd@5'M拥^۴zeд'=_=A>? 3xl]3RWVvGmn`h".6o֯}h gr Ami,/*;JT+B*,jzoX-V;uki[(Pr$YA䑜lg;ʕ ?i6jLy?ʝdPB=Ntخ"ӭA4C 3ML7?G#g%ݽZE#!=@9_(]«2 d(ƒJ 265 300 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Dռ{ WT摝#,JI!KIAcshWz$yĊpX߆kIRkheb4QAΡ{pM{߈|gh ] Umb+%:" ^hƾwxYB#yJ6O J!egs5Ķ5MHfc~T|Z.ϨS鹈k㨥ݦxZ`L#,2鸎k i}h'H~SI$]x,Dq#Y+_<\ɫq5͕[ڽʌǨG&fךɷFCS {09 ϵG+=gz71z,W׎oxQ4iQ| fQ\ϽTa.3OB2Q}'?¶umr['5lMgq3$A:t$ޓ@ >}^ &.~#ZG{kã=\&בZk8m#̽3U5_?\jhȗwK(Hy* 7,֛+I`2mwe&~c$^a~bG?I \KWז~gHo!f;dq\䖌($1G_;[MBOnY#/$dO ccq|קlo;? |exN?83x×IwkyddhG >A$W@t C~}MR#;h2Y@þ\r\ywT囱DSvO˓Q~Z#~s>E/ۏ+ѵ/žkS.4K{1Y-,~{H,m9nqgf^~*s\@y`EHCdr[+~W]]oDQ AZWt|T_vqE Gny3+ =i!n/MrW6JhWLppI5N6mvN>qn>|?_o|+xEO K$xMJPuU'0#y8n/ gŠ'C?4/4hSat# 288$sڈǛE|<|걉&Hn<ߑ`7G/D}n}'Z_}/Ja 6¬/7mۛ ZO/G|,u+FU.-&D1$D; `̫5RNj}'?£VKI@18~5[Box276W֭rVXʄl8b:84 k2Q|+Xm 3$Nфd~blɿWK<?Wk 5e[դ"M"Y3WPvˍt:?|#=KŢ)u}?V>%A{cu }^Mǟ m2I> GڼLp_sa2me\)Œb|@mz<]uHY2u&R6ȧoO/._n>|Mwuy挧$rbV;ǭA+|jM/2@-9~8|-Q Z|Q{"8 !:db}3=8Y_[+k\\&6LI gbxg$_{]OR6õ2[uf9#$$_ @G8|-Q cXҿK_<6iȱU&Hc\Hƾ 4.(V |e}NS>Ё(p[B?g~n}-k5]WŷڤϨɥMgq9`Z[o, 2r<|\,[58l8R[^U,8Q?>Ё(p[B?U% >͟ZG6:͕Zk3Mqi%ϚhێvmU.x\m4ËX0Evs2~ozFp[B?o—/>om'㧍?{Eo.4i#΋xVI9xö4avvѹXH0ȱS cu G3>7%MSMH-$(R1V L>Ё(p[B?|ho|G,Z\j k$H|.«ɴ`'66Ю\Ltz@g8R $Gv.mA cڱg0'޿M>Ё(p[B?m~Vj_u_EI loD0*T6HcvҰ^ G3ɂ6 uf?[@ wWOſ ?=@iw'ǨoOPqEp~-]I?[@Q\?}z?.$wWOſ ?=@iw'ǨoOPqEp~-]I?[@Q\?}z?.$wWOſ ?=@ixwߕIkm+P("Ɵh׈4z}4G7aENuyE|c>2zO4XF,.txl!6H̏%1J:bŤk6sk^7zaK1X;pRÕWOw=g/t[VZ1[X{}B5gn,6297-)<^Ӽ0Z_6 xWrDD<%mgo@M5sب8oMb>񮘶2-vѢ42͹C*$dW/3xNJhZM [sؘPQ Ix/C( F>ķZ.i~*O,$xuA8"pqm| ߊqxxJM:)`'ţMű€Tyóuk,ZŖ/ -^-.C$rl'' AAHW`-y634?-!UZu-FHӮm-byErp^Xj^qٓDe>0GiѩWqo\ui䗫=i"Pjuy5w_PW1U'ngB91_s#4?}k_7b׺iY\^ΰ> h>/u;[M:;SJ_v_ꖱ]kQ:6J~# OV݈c֡SmN4xwV^NTrk@+> j=rMVM-/S!;,fHrRx1MopxεVԼ]44c>"i i*v1`s^G#𹿊9Z<>|и~s΢_ ݿa/mz-څP.nndX}Yvu)ƞi#U#Ar:EĒJ֚u|Wv%Ok05 o!dU_w8hVmoȫΛwGZ]MHތXdr2 ^PdmMp<:. Aut;(}#yt]~Hw_?G Gߢ0??O><:. Auuiiv' c '񩨠F?jJ,k3.`_CWV?}{SXVnY`8zM;4&տy7>" Cgna>Vjeo)&7:t19>Ls8lq#~|PYܽm$ +(|%f7|3^%5v\,#svJKK]s|o?n?b^PQ׮1uuq,Vxb$UA;s95'K-gM/º"7B=CHƛ k.c`V+]a <fUo1bxWHּ9.w{Wtm#S:rߧ>r>%[<_ct}!"yX̒i>dmr5[G4Wzx^/ KVL-Il8kAҤ)Ojj4[FK$/+#cHeSP*sw^)%[U  އv?⊵k>^t^'[ 5[QM!D#O$i!ny"c>:ӵoE^!W֩iV 0CPm5iZ'K_y-5!A#r*w? hkk@5xm>ޭCy[u5 '~;i[Fl(N-h*֯ylU_l&6wg=\$^SY,'Ә]Pq$.T Qr~bh>'kD㤦[͹RIpFvL68^Oov$<#4VэⲵIcP yx'kykm6a2CnnRS_?~'H-[Q6^jmi<2J[HX,r*k]/ izxYDYqbId `g䒻yTޙOM]-䵷#9q*3=p|}oA&k,|Cu_>5¡m"32I@x$㩧++ۿjkû Z<]k)/.,HYP;K;"xw|fEvz5ax)Χ(1<|Ǐ58 yXx'V&8f"XU!/F$#pU͟7Ki ?VmJ7ϧ4DIG]윀Y_?Lwh/-kv6.ts;[ 5ci7편R{V|kiUֵaibNY1ܤ=O+|Ok^Vk{t`2"Ha@KU#W&֭xu֣>kx,D[d~e|wwίiTȱK[dhrpW}2]x<+2Wj͕B{[bn`~?e#B/OoƯ[o=G>&SmY[{{]BkCqvF$)X)&F2pf-)#M| MҼ|-2Y#ܞPWYyr;~Xo޵o+uoi}hַOjUB=1^|շGDk"1Hpv)VzV ;M:Ʊfw AX&-C:ck-deYۜ:O4-tywPm?h?4 C?nO4-ugG!/ƀ+ywPm+ nY;E|?v@_ i_?[sJ۟7V4>h'Wu?<ҿ՟C_h C?nO4-ugG!/ƀ+ywPm+ nY;E|?v@_ i_?[sJ۟7V4>h'W n?m^èsneX3k7ZZ@‚8mT^zP8R>ִfk=߈|D7f;q+$W$@ݓWijwoCp'{PH=+O :O Ƨ? 鶚͑vMºEK0GqռAj|;_Z o Kq\Ub X m槃"P|j<xF [+졣q!f6Q\NDռq!4$Aw,FI<Ԑ3 5Uּ#_]Cь:@u8mxSTq͒&(`큁6zUi.9)WG(e!;VPUe<z/ i>/_ #3EHnb~A3Z~/./Gnca1ۢV5\/__ q=wTW z?_?\_@ee#uEp~/./G//Q\/__ q=wTW z?_?\_@ee#uEp~/./G//Q\/__ q=wTW z봘ӠK݅㎄C \Ɨ.vCa]^jvi%ə˖q$ '*t*?yaҼA!o)HQM;^ѵ/ c> 粰C\̍kfy:r!mĶ*_WƷ.5FeuK5̲>ƮYc!$gԧhnO[,q`/}z]ַmw%{yH/FЛ_?i?7hh;j^k-qqwvj坋J&H1Rh|?<)ڽn%,Bk]ZΛgg뺄Z}ԯ~j"3bPaRĐK]<yK`^i]JSts*,?(B·Shⱚ-N;Y1M$+(G(YYLH`Tv5uHN[;Mj$yB&IT*E!1j|Z%ZrͦD+Kc>i#T)$\h hz6Z ; BPA @ݞ(տ47,>#L$!MUf0s3ʻe+bq";'9|&`v5>kM5EB ߻|\}ݧ.6m;mU|o Z7mf-5Uk9m1 `P&@7ޮOC"O?m64uH6elyymKu+H hcia9\xJTd5I.n|hPtm/VE? Q1:I$FȮQ3S|L^JxSK`|][^ypD Q;ry⾣ӡ3]Ek$GiIR-7ǠwCi5fx~x<]go=VŶVmPecbd^-izeƱa-U.ψүO&Kkm)i$k[D# (,OyHyd;ޅ_73!/h<#Mixet25jDc0 λ@ߍSWxGß,/41 ОX.!?fȍHpΧϩy[K4GRIzdEt`!Bvw_կbj$WVť)qkqE,2.taR; -1|#v,~X?P5SRv`f_M<-?>4 ak]_ww1hi:UmqIo,@t[ #nN )͹+HE4 $jF ܽjrG0|"io S^joh@OQAQW){I3H<O &f_M<-(fyҴ}#[dH4}YEUa ֺ+ω,6E'&YCV TG HzF.9xeu=w:d yJ(;ϡqO aE 1? ,?B|?@K_(('u ۢ1? ,?B|?@K_(('u ۢ1? ,?B|?@K_(+^+XRcXAD{ +/[wŌiZ6g] wwsKsvo/֥t}ft>lYb`p}M JKWMw3=>}k?Z6^};ֿ3h6ʟ{x[9)|Kþо4kH4.6onr3dI&aA;T+aι/u|>ukT<ǧ*ӬN˘C`ۏgQ EٴY4hnZF3g2!Q3Td?[hp[C^h-fLŹ ;  o>z_juB,QT-R@reUʱrO ^Y|CAY)FX2+Fyhn[lom4mk9R%7p뤋<"]K 5h,29e8@9xUuv>WɓxPίkζsGi;Ub#  s|h r0'% 끦}+ƚGH/.%A,q+ *.)4/i!o{uILm}8ȖfuhBf/:EPCg~c1HJ͗ %;kkYjI\F;&t+Bp3:և#D@e閶u[(n

&ORCP =kQF$[6 2co𮯭OGi&0qk.CQx$ F[y~28_{3WWGx⾑#Ծ 5MS5B\-x ;X{W77OBMEl(L9Aiwa;y\?[~^@uC)A;s!3q5h|G|I>yqu>rϛldE5(9qoF-^ct .q.=vH$O6R He1ؼ1[]RJ5(-n$y]7 $[C$k=sZxM|<+5nX©h؂;W50YmCGORԍĶh6ўXMԥ/#{~~-ixwA=֣aI݄aCNLCB l EfKRGe$sJ愺-n$bI LL ?.^"H-#,@'o' L8¤ >]>]X3?? L8€+g^1tg^1uc L8C]>]X3?? L8€+g^1tg^1uc L8C]>]X3?? L8€+i^1u_ũCu*Uܻ+?iV0o,q¢ *崗*;9&juA7@ʻ]C UPuA7GUo?iQYW_ te]fuA7GUo?iQYW_ te]f"Il2(]ƱPʺ*7n4쫯 ?3{|AJʺ*7n4쫯 ?3{|AJʺ*7n4쫯 ?3{|AJʺ*7n4쫯 ?3{|AJʺ*7n4쫯 ?3{|AJʺ*7n4쫯 mJ+`o (((((((dcFUIr="Qu+I/#F2M#ɸʪ8[u|]K J˃ 8ŧy60\qG|n)Z]_{=}r=wO95wz/$Swzp+υ!)xgW|om,t)͵$1H0Ѱ_n6 ➗kirOp/䴶a(AvQ?.1B6p!^קF%{TZ2L뷻u|uqxFS >8]N4ce򥤄c(".QqWm5y %Ar G+UR_!Bi]HxRCsX$ >[Hd}pĖJI+*=*:ޡslQK&qkw eUphx@UaH<[{2j<2tum*TA5=RS6z查xwWF{Sk^HSwz}y|!> t3YIS\"c")1Ӈp)>~^z 9n`/ts%2DYN 7Mkku%T~%{Tiv𾯫XKG{ag5&MVd.XI$e`Ht gxW\UoE[kZ|wӞU*T</7$o9/,YkQ~sH 8Aקv>$-DEߓ+B`Ga@20pN77ėq ׉ЏКhA+-I{3(F;+qH )-]W=f i$KzADj;6$t */ |652յNZ~S0iⳒ xIr W?{ L*Dk/^{,3Xb&1Nn<psDj;%{T|%Kt?gc4 i09I˂cQolU|Y ׉k1<}v7gvg9n?x>КO: ybws޻oWoH?__#bi݊o7ʐOG8>̌>%}xB EM@`Xv}[0B~p+6pzu;ZFRFi ~:Z_4{*M_r,QiXxsI c6 ^p=+[?/&ج'Uoihn,S7Fcs*/E=B-e8l"H$s -SZx/ =[N),Vq) C p8n3\q)]i& +#&eydR@3]+ з _M/Bޑ14j+wsmsm 2$"[=;Wq -G+ з _M;XF[p[ٶ4ʑ%?3yf&:i qi鶳+,6/Ёؤfotoxx-20.08/images/adjust-britedist2.jpg000066400000000000000000000456221362435004500203770ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231%Ơ0100Fotoxx:retouch_combo|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 237 316 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?յɵː'8#DVIRrJɟQW.vcY_m3,9o~3Xkbʲj7.#$@;v較jٽGmW*J\>dЍS'܅ywB- Σ7\wM=Ɖ!]|6I%Z6[:t%MZIrɱ2s1k~Jj+Ŭknoq{ngno/Rw 28 Mβi*r{ш؟xT4Nm5 3Kk"iI#3ݙ#=2(wAk'6'O5״ooKcFtд85[hp71ީ?O$K'5jEOJZBH"o.PC¡-Ikdx;^Fg>$q]BHKFQs-G =>LUذҵidᥒ6yZ0VnU/F/^&oxR? &tnRRw,Cu2WQI/t<#ZsOfu[x<vʰېuĮfSHM=.?.o.=.?.o._\E|!hͫx>Ǩ_3:}@>uuH }FǪA[ortlOqsuV?|k$i#9wC;z\\]z\\]Uv-lOqstl_qsuW~tyO΋o =n.o.{ ~~t]/g}\+d~!b=KM'i^g?:a LhZg$r[FAl]~۵枷s! ʨqe4IA8wdϵ?7%mDHݕ!##.J!ծ>o>>}-,b8 wwVV.Ywxm-hHףHk=ZQѴ(/jNmٌߚ[zh~*6[>mܶ޲ڇ\פyZ?yZ?r|<_Kux}:dU'NˆȇnG+$浼s3hjuuks^- ߱|# O+Gޏ+Gޗ,{>|zdDAQgww rI'ٰE>qfO zTK 3i7#I֗HTbZcZa$ F~֑ԉIIhr_W? ,=޾v_:~_++6s}D2zb It5ckd3X^3δe/8G37/x@džoY~-/bݧ>Dql&0l~%Z;V·ZFa7P5ѤU]H8 ȯO X|*5bMYi%\^ymz؊--Υ$NxQEWxϨ$Vnj|A]XivzQ?Fdq 94>"k/ 5Ҽ Ls\`Vk~%@9*:+؜U)묧}(u>\YHo'hՔh)ϟu'I<-Zbi&T7LNҬO$g5+%0tj^tԒqnWgQEQ@Q@Q@TW7P\JD$G8&۲%{7O >+3{ʸcRB1#5N"7c;X:;5+Sa$=N[҅lM)B3ٴQZhQEQEQEVO\5E-Zd|!KCjZ?~s.^è^? |O˖悽sS?λg|/͓z$ú'mo5S i {=ίg!l#dMTۓJ$wDӍ~bRm^M6I99'&n]AK2vjm}Gf:8f89Rp_|GZgᏊ^\|{Gu,u;cD3h B\vN[G0<o'ǖnrtZy/v֎F¢Ҕh'oRInC◇~xFxK=.)[;4vX <'~:>.íMgq{y EYu9|TD+^_c.'2 I|y91f$rI_O*LEIo&%%^8m*[xنO?ƾ22O& I NvWw(q4pm0]v8j_?ǟ2k>GH,eI] °FW ΃;'q،¢VI$]RI/$4T4xCouꍥiWkem-3+0EHxOk ?iM\OxG%&{EԎ& 'E`FцیFjòoㆱ㾵n*.,oY+[K w[6r0䎇9럴d?..ƱuVʋ(Sqs VsBOyV]=^(ζ:M;ڜ,Y6O*υE7'7kZ=my@M|@~r.{\:=κ_']Zܴ팗dykQoia/5mmցT$J]Twd`/ Yx? ?43K V/5x6&&!R6y]1~5~|;h)Ӽ_h׺vqu^0,26;F }.FW^^i^[uV4!Z%yB6WK4W?E~jO)>*i g|Qqޅ[i%6![R(R1b<uEuHkK), k HbVx]<=9qi(jC \0m[wJ-oE||| SCiz-'+Ap"alNqȯ|]>){Khα[MdU`&n%9 ?[V/ZmR_^ s׷ռ% >Rx0jc'q 2-׊¿N4}{+=ik[bq)C'''~y4^z+ѥ)G>5j[f2!#Qc\ b#cD?#XZa}r.՚DvFܢ_~? )aUܹuB22~lfV V~*h4h]d[9`܁8|uoZj>!$G[T6UBU#sMi֞$se$S%۪ Yǰ$Aⶥ-fE[Sާ9=2N.QzvEd֭\_a}n#W?ק$SԒ^Ĥ0"=㏂>(4:G=@#- Rڼ`W>'fkybopmŸgtcaK8=K*u՗Nv]fC+m_VS-4vqU{ʢo CD&iKBΡ Åb#Ke+OKwrф"kA9fG19e#$tT5fz;ӞQ% :M2-;( |O? nx|"z :&71=O 9-5V"^:b[ K딞1eeFȶkh #{vuSə:/ٷ{v9oItO8ĞOH(OHѩRÒNM}C1zᕬeq(B8ЂA:w^=G$W[΁xmum6TW7,t`kᯄ>1>=^8#xitywh7Be%|w*O}^E^'_ޛm<6,M&X2pP{uBzT.k孽w^)8JJR]brVI4ևXéX^[{{hR8 GZ~C77LnQH.n* =z׈~ Ke௎#ŧ]) ,Œ^u^&u5-~,cėF̖!3@g\ p̝43[Y~w# `n* )fߖׇ?ivן|3i_4yF}srjz,:]!s]׉53J/_i:lknі8nFUGLێ,Yp8mi_>$Zkc2-B^Xx2Ydrze8 Gw}YxS ͌JtmF# ̓W*XՔCrjսՒN'9 GhѥSWRf[vWkw4|}CHg.R J9 W<=u%Εë#y#sآ& F6K7B*F.mmmQEwEPU-2YPu)-bH_FVji#OZiE?t/2ݣ[Dc n. n/>e?HrRJ2ET0.@ۚf o~ ƺqXM<"k )VDd|+G8b8kfX\.vSOzJ+~IyŻ/^ſ |OӮf{d[ZC ~U_.|"4_ c +z_7ׇ r2 j782{ ]OIռF%H>YQ纀sf> nm+E áj]kZ-9%Y  c vz1mR&3v-0X\RAIt_+C9~&|:Y|Dկ={I4M[]g,J,1.Y1>6q[GF2TcG/ 7-h5?_ tB}'1,2D.$fM_[AvG H#5T ? l$N4"2j]+[E(km <\(AAyn];)>'{!Cvωt>cQ] F.!Np3?ho7/΋ZS+m .>=kz+YfESϧʼsk䤥o=[rߝx_6W7}WLo4Z:4,DJI8SV-|kOGÚ=|G.-<;<_fBu.m y9;W;M>%x=wBק}cwaOBjQeb9lIׁ\?|V"K+#yqiמLi ]Y³|ӚqWԝEyJk%!sBN\nZo!hZi:7:D}.AnȍC);G`I^c` SXa+ 22wzz/ay5[ۚ%{ǛI-UUNTRM}ގ>,c폋~ h Aq3Y j3M!f5\+3Ao| Xs%ډ WQ+_tW<9SY6t^+t$#Cةʼ5jNrrVr9ݚvH_Eo隇u]#_Ʒ+SPē+yYn/KF1_DWuKkĚGt0I{_s H% CqZOKxzo{DuZisdaT 1N9]Vx~X4WPoVoxnV'$q$ko*>S{;֑}JƤWz+-[ԼzOs%vh_<B2}ŮAu,+uQ2{կD/|JOi'!5|'I <FՂӐU;iMA$~#|gi~w}W?x:،c?};M{.z D_S= Grߴ7q~iut|sP"`zۇ^koItO8dO x+m wIJ7WWɧ^dF,[m`~QW{riuV|6=ʊ((((((((((((+$ZG/-,G-ytV&V qW:4kN&DώgW?ž觳Ot=E,? 0@|!g\I4Xἴ썤λv1Hkѫļ4K/9 Wrj -v~brzc{ӎcjumhIGbtϖT WcIs..-*dp4lK `gr߳xGQEggcxjg3J!0$,@t+\hχ>%~)X]ؔVpia`LʸA#k|E;UMaF PJU^CO'1ȸru&G$Q/%$2Wsڡ**[Ef#guM6y&7pqj`[wHPw GSg᷊I?ۿ¾JƺZ$*-O#_c%wv{ώOLK}:q*Iz W=C6*IwEG|WZm|j>jS$W,u~k=ZiM$o+li";á] ̶ﵽSzu_0|]$QGhWSJȠ̶]vjW?x:RyiƽEg%{z $ú'mիho&6?o^\fER(((((((((((((gO Hx%I`eEmK+|sbhWj/׌My Ls+sf_N[caaN6?7qV(EPEPEPEPEPEPEPEPEPEPEPEPEPEPT>55r" #d=@3~’,حPEگu߱ςY?cKM7[ypbf +w~<:tho&6?o^^SC1zj(@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEb]>F[Vni]>F[Vn09oItO8׫Wcn|n|KA u+ZGFU""r9݀Cj4O?A/R(0/O> ?Meœ?h?ahh4|zx> =:0DŽſp?hh<"zxg8|j/ 44W=?EwTW>+h6p$(;E=>h:6=rEe#8=:^t߲TtP2IЬ@;mڵ-~كGG\REOxx}4;1O}4;1EblWP/OCXq'= @[1E"-~ fοnS|%N, ~Vہjہ~KúHhb?g/MK\`8-HGiXpiX=8[`3Ҿ<"4pG%CY I ?8(ځZq}m/4_[|:-Kz.nV?H`M2&!,?ոVq_IZJ(>m# 5zJMz.zVW M:-(}#p<8[jF8݉H];VRJYioE-VQ]Ha5kz_C5z.Unx%8_#+t:xQKk/Ghڵ-Jdž/GOj#ڊV>Y.mmZy5U`;O1OV~J#^3t"-z[]BMltiyR3|ʪ"l Ü f:-s[K! Y?ؖ%@kKĞ^Qu8TƗyO3'%:NLQ:>㻽iR^+kKMv[4YT'dI,pŹ5fZm VW-4V(%`ckgKWw\t\Hfx1mfnh`sn 5W\mleK%EU ~M #'`i5-rYC4wl;w"<,ek> x?]hv:^f.mP2ʭ1VĠNc2@+R)aZӢK* hrň‡#,+1PON6{+{붃@4*Y`@dz3i+uU-cA ڭ6R p 8Hg|V<=KE-gKXpГGῈOxV e@f'n3 ӎu_FQ$n[s{'/OKkTfydf{SZ VTk"!0(f$J4Oc⨡fmEu}BEm=H`ߜZ Pԧ񖽦jZK(R4ڠ}֤[e!GԯืZ6n`s0;g=_A{y)k_n4ۉ,ӆoa3#8bdP)u>%7YX "50!,4h𙈐/4e pW44 * ZO2YK3w$-,M (Š(-w)]b]GCƘ~? _QE Q/J(_}Ҋ(bo>Gؿ(Q/J(_}Ҋ(bo>Gؿ(Q/J(_}Ҋ(bo>Gؿ(Q/J(_}Ҋ(bo>Gؿ(VCD G")?fotoxx-20.08/images/album-mass-update.jpg000066400000000000000000001270121362435004500203470ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 408 453 0 C     C   2S" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?KN]*;{oƣ'>կM=EEo'hTOTP2_M=j{I}Si?GڦF%TO紟Ѩ ~74}o'j*(_M=j{I} W_ Z'.<^3DM%l0k>k( yiЖi3: "*3LKl4mv W԰xFúoi3.z9Z?k :j־3𖗦j7t -oRsܜ3S{Ig;}o'hTO^:3}[J 4R~ ,_7qVe^+\xWC aa$Kdׯ1&oM=j{I}o<'_˫w` W+pA}g?ɾ:? i|$6$-/1oCz`yȦ_vץ my'}TO紟ѯ/e A~$i͡Mu%XY,78\Weh>75'ƗGm;d~(Ö=&c럙tc$i^+Ë \KDІwx83[Vmz )&f#GfQ88MTOٟ<៉Oh6>&3sF J$!b"^p GҴ93/ᇋZ ռ3?ë,zV:|HqRT)8z^k_}o'hTOd Uo|I7'FG47>ڤbejgß7jCL{u&PxazqCvi=޿SkڦFM=EE0%TO紟Ѩk[9eLgdHX(>74}o'iv6uo-0Y#(/ڦFM=n|}>;k72vpa#;N5Ľ~5Ԭm[eetQ!6? דfUt*啹>:ex}9]՚ٞUJPPL{y*+菈4й?Z<1|GDQf=Ik;+KpW*W__Z_Q8 _}õ|#Ei=G;W?V\.| E~.+owgceKSët(*O;Eҿ^?+% pE~ët(*O;G:Jy\.~yWk??u]M?Z[%ȻÖؐ2xu:Jyët(*O;Nџ,x񇊭ioXr[IS4+',JI2Tko|F5*C[Xcծ9a% K!$T`cպWK'[DRNA5>_wx?Su] W,"/uӂ[A^P~- ]ShRՁkkqoSW+% ҿ^? ݶ6%ed%N:GuWU8#XJܥ?tg}_y񏊵Os-uHGNM]FAGI#G=}ҿ^?+% N]44[~ؾ2Z5-ED +䏗:_꺝ׯ.￶IF1$+_unEIhV_Q/?TvA|mGN ߅t/\W ^jzjհ9?6{gު|&o6nnu# +% ҿ^?RvmXO<[ij>t+e(eH>bAkޑ942m+Om&LDK79xL' ?պWK'[DRZi￟GW_猧t=F/vVFsAiXL:<fE&1+Nu12oo0p@ dgpK}"U|CoǼױе& ' O,A=NҾ?><9>'LA핎2' ?(<%jj:k%I H,k`F*߈|:į[xKP]xM=;N֚(nu ;Tm c`~~+vÿ_w4&V 5s7?T4/@YxJ?᰾S vEBm<҅Kp:gwěNB^i&=F@ IX0;XNJ4_ k|X&yt\$V NO4/_x-^>j]C6q.n.jpI r:?@wZީUpI9|a+᷅t OiQ(R!Tp⎊|QrZ2qgwU <)tli?El%p->,r!y"!]`pxqLu?떷^k}*Qbc9/9#pEx׈!G𧈵o1⛉;;mzm6Ke$ BeGg}, ue3q%xY,J֭!J7FЃ8KU5?G?[Mxn>mT9Q6?vnvs^)i#Z׉(ӣ02"6QNp'+_:xNFEow tl_hwDF#oQG|0> {|^}GXĺrC-\JK+@k {-Kn?t j/xMml#9;0 Cxs3].#έY>oew+rxCzvj%_1t(𾭣fod1RJ h^z^/L48?0N ۜdI]6?_vi/_'eqcmE'k!I 85~O#wgĭK%Է\?ƌ$ zc0N n>YM>֡\}FXa)OQ}vS:PU-]#ylDz &rW# s/m6߳Z[[Q]3D0X+0889͗퍩-dhfAw_>c.򘄃*K\f|G"ZK\ .n,.`tky#.{{t>ZSkRy[u޾Z#ݵxG#;OxQuk}^[dX2q`pGiZTGsZ4kK}<%* %5xOZxm.5F}GU}HZE ,hc烒ǠKҾ e؋!we\m8\- y'G}B? {]JKd !8_,| ҾX|jc F@%ϨMus|7 A27 TB9Q]];?x}Iy-#n|9SpsJjukIjd7u !%Oե]iIJ33%Fq^m)dѬ,5OZEx-1X.ʬ@ ^?نU|RtOh>$Ԣկ4f6Ya^39%u WQIz6dwcaN|{hֱ)_35|_k~Ѣ0Y,.TQ\m;~6|WkiWYJH- G 灚߳4zdžko_j:߈UPkvCb~T FOMOjQeys![2-|Scrqxe$Ai>'|Qe7dzMpݻașLғ[EE]`h/5%^Ҭ[uM"k{\DIl% zzĚ7 o%);clM s|#E-6y5ŦW6S̞CO+Hۡ2n T)lV~5%˟gFR*mu*ߔ`}ɭMfmsl6yneP=3"q[[tvzn?7vlx>]oi ";2&hX3_#OBw6ST!)cO"]ZyݡLnJW=2s4cÚ@.oymqx b cgh^h&=9對2 F 3Gx+ȓFzQi[}Fyo߲F@yc~)ˌ^\Y##]ΛTzWȿ|IZkQL˦rx`}9چti<:x<]r1M&XE'o:~Om(u+մܬB7*9 _ ^?GB4Zţ$®<`HCe[7efm/? $pk4qm`?U=#|3ѼEQk:VQWSFͥm+~Kڍo>oB??"H&8J+C恵Hb6};K{4_x^|qoXY|Gn4{(DzڋMѵ~孵独!kkäH<c._z_64-^ծV\a (1'$z%6+ !sɯ~*hAޅ'ǚXlM5$ 0>Iќ)֝]3ៈnӾ,6~7m.%䈸L6XzTYk?"žw&4kKKékI@@QųJ*m}څ'˭s%h?H}(1FʂHw--y^ x[\Fκ=c %h۷$J?4ok+M2Mޠ-Ϳiv; O_JAoS&EVF0u _SXkI(@ Ёs_|\//'ock(+d7ƢŰBp,k?þ UjzT}Y̒*R8\I4EsXڝ.2dsX.\?5MCBio!'Gz ''UխlU^WZ6؃hw:62!{ OC_?Jxcvy_5=Éwa qRq ;v[1-:@ʆv GZSuK-نȪH>"᥷^~. ͢i݁ [=G͒mj3v><=vgki % H !li Ml$t3u+  QO8?/g"I49eM_GܗYD[=+~?ۏ&O f>)kzH:.%:*%`r8Z~3V6_i˯_n:֢=*0۾__U4 Mx%u V.4;WP3p  ~׶6hCOGyi_>moYe3\K,7BEf6m >^xM}Q_'M_xo mc՞Aj<+ѪVx8Ǐڔphv7>ӵuѮ5#B/ wawEgp o|>گ~2>[gEkޖ#́J|9h.Mq]&1<ǩ^[V V@͗3߶4p[YÖZ5k--AyJZ9b w|)x9h.u:LW>שM'n!Rn8$O'z/ QL$`ӣI0>bXgEWV{+SZL/;@; ’yf_,|s]^uantGsgplV&[ĈqǯT;,`–#^UxwӭMK{]x8m4uM;Ce9c<↷CLgዥtN/fm㈨XϙGWZ|}KUad޹>[ y pG戓cڏuxº_#F&\KEu)9P_jERQEQET֫cqlĪFH+RDq!ɠJѾ ] Igʠ(-88gZ]Ե9MF+D,bM1$9< k*kO Z%?U eKpGkWiMg'FI#; FB ]Y;;;~S~ %Ca ݴkZ[Fi 0dd`=20q?x5o Ekڟ垷{:NSyUX JO>w5mM 6 zTgS(1sk+9$yZ&j,PRK0Qm,x>~<sg_ߪXYܰsum?ӎ9@~ω9a[Bky"B$7> +kO񶓪xVŴIVapr6= ty:߈x^ F $$-Ά6N< u-'U+.6VF|ʿ1 dsz=_BxqMWƝ=_\wixhos* iӽWxlm+کi<g; F&wq{ť展OyQ{-6^.ɿ㺾˭XԆng \| % /ZxZkz3^F*"wWx+Ě۫G׾mўTC(H鞄WAFWw}?|1O ]ޝ^;kgbÖ%UrJ 2px_$xqe2ݍH8cEX,xI]s|/m'7:\L5ׅxNPX qF`7oPD²m,";@۱ ~5as7'OR-4edoް9m͏-#ÉkR_~&Ԣ#b(m[G Rrwgp:w<=.lfl~%[hU)Iݞ8pR&eF׷}R>̠W%pi-oDE~zσ|[OS_jZZm+# .b )-Q ZhJbßMnx<`Q|g|>5Le8VQp 9fQӾO{;^卧K-U, C mjV]ʫugEw^%1^Y,,BNJJѕO^:Z ӻV2s@6;^E6$|>!|5;O^1Yk!H>?aSķKiiwso q"vd먤VcZj|{Mb/ j#]KI.,PSy-29Z iVMqlrw=s^oIcQ`}$P7 Z%[XVG acv8|{0y%i++/׿I^H⻖e8R@eR4Ku6ڵٞ7[$S߹4{ђ v̱(0N>P@APYcGImٸCg֜?ZNg]~9O!+_Pxnc啢<Ǎ9}ỏ^e懧xe|15̾'.4=g4Cs Wgiւ)O!v'; rO< ~i cm 6UdjVׯkGO >6?xCz-Olk$ k:4O$LrqG_|y[\v:4.|?~mj#k) 9.!)t=6H]#FV5p n.;bh1 H)g[K] q\Z]Mgv9*kMB4{oZO;@t2UӬN4"G, b듃啾n]A C~l6+VDt FyZ_ר޿|Jo _ĚM6ium5Yex3‘ȬK'Ԭ|3E?OtA$Z"ZyM< ]Z}6j[Uw}.rs j?Jq[C(@/.}s[Oyw6c1]D+(-;x(e@L ǩl iEF? 0>|g][!/>>;z u<@;_ǪIu< k_n I]a\^\xᶓV>t~nc6y`y~^KwgY]K2x-rrdOiVZŷྷʹdLMVV>,ԴK %oXxǭ^@ZGc6p0rkcPxy,z6uE6֧R $_#=F54:Hd^Cnc rHdq֘UH 0L?p}{Խt5>Ö>%4}z@'u+}B!r( 1@P}H +Z6^&H/J~lNTv_ }+g#Oxt+hl "9O_Zmt[# 0xd]08$pqڛ<5xO?a$k4uo!RpZ5+ V 5>-Ioy7o֦OݪƠHtȭ`M: η`P?2 aO܎~c^N4x7^!N]JOʲQp򙥆%3N2*+5[Z-OԫR+^i/Q]j |CrHjŃVV{|@.jkXBt"Yc(@hq5ŴWpI$ȥ^92=AZ=m,JtHZ_b޿Uj~%YSǎl5ͬm0`@[=_ ~(wyxJ++O)m5-//0ʰ<?[kHҬ Фv9WP 9ZEJHas%4qҪd>AմZόu /GNȱٵⒸlM9 r2]'l4ayL2k/43QyNi#X\+nU9܁RUwX>S&ȵETQEQEK[tkmGo#zUO6Ŷ hzdր9 &X^[ԧC0Rqdj_WEZg ,Q$BRK6~QZR+FW>moyx^->7vF 7(k|]Z ~|7Űյn-*&aebOk߲|-+DҾ!xj-é[I# \2r=j9f-SDM#As #=Ƀ4qʠ˻2tVof_OGP׈|Wo_V|14qZՕ,Jщ `~PqoD[ڷu 6+Վ;#.1h\*@5'u_tO jONPHF} NJchxgOЕV_<5$`81ȪQz_e~i-_gVi&:ZRͿ .:[2B3%|7y7uyحMb9e4qUvΧ&=R[6\,:ʳI ႗3q*7'е-2!x{1vv\Ъz0da{ 3žԼpڶ:xvQ\gr:Z>_W'5M7UL[&k=Y%-!xšu? {gtN-Q.'1e=wv=mt ߖsͬ|=7j>2Hڔ3%8 ]Fp7?ki4ۭwYK,V[sK1'o50b<]--!8rvy8c穮í_ƐoSmÚ6_oVkYƍ/n#6/|ҷiOŗ=;{uVR!u\A A|z!Z6 V[fY8JgxCƣ񗉍m#,-m_r*˳1.# wCߍiU-->R\+qnc ?Cm/z\%w_Jz$Ho/-U䫙B~`8 kOl x7ڿ6;]GZIb>%D) )ϛ2u [kZO]NK/mfYrN/i'o_Znu %0D2.2T=x z.-.t )moQ.,cf'tp)VP.xuI[JX~|oQoqyj6f۴6Qx*Ou#}~vRp[#qT eW^GXhזk]Z O21`u ~ ߁/o5M=FDH#a/=qr1_mox:cgF6x3JWf2):Hߺ H=}ki$kO7LS-F1, d;Yrq9{wcִ^&,|CFMF.bXQԍ23!ٯS⯉ah x;swm]%HbX 9P=?׺Ϗ- .2izwG c)~:[/6~:}瓬<!8. |8??[* 2FmchvBe=: e<>nk=|7){46r[q,qvTwWh$Ӽ?_?Euo&ʲH<N ֍+eɼ#Kœ+;{9m߉QQX#=A#/:F_j=V֒ڳG:09#$W5PczUoF 5$w|~"罵:1kiRtieQfNWiWWeݛRį-й(YIRA ƣu>kio}=A"enev#QEQEr_*[elj3h㶖Y)b8IuW Eu;,kGuMG4 U,>^z50 (Q@SLլ_p^dm2 M5fZueć"d8E`:'쮵Ʋ+`]ɐV#8<܊ޠ(+>7ѼBjMlڵZeX^O2CSN ("CI#D:hUm;R,a9pA@6[Ə!{%Mg?+ g؂Q) 0z}~"/tB#=-dތ0rG t cOnm>; k1ˁ".%I9~n$1i3|s/u CT׮ƗjP[Kiou yj:3c_57>&++~]VxdXo%x#+ܜV |%e 4rxUHΝq,)Wvwby| 7Hג_}$`vqjLx}c?YXv):)E7OP@ r I;ixwCa.}.)$”%}/doi:o[M`6jwsEV"<M66SEӵӯInvdHg.NB|m;&vyiJ>![xGӭ#,` h0 C|I NzEogj<:tknPۙcnd'#ѮӒK8P$0yNuZo i~> !O gm8dhĒĜrIsIƷGzg.k:-:0JldbH` W_w⧊0XSJ #Re2;2B׮oÿ 2}a&:\jws[HAxV2pGb*)1%KcVm_b:p2G /C^LWc;JK;}c_KEkLm2wr8\ƃO|GwGWAmOM.nnl f a$L0+|a#}oogj<|D$4Khkm,~G­>\č&n_ߊt?^ٴƉ}O"|)'+eI08P}?̧n֧954N\j>Kw5n3(v;o@H_Ctz|766fm$ܴ&,.5~Soto/mT5x۞gmyxuu@Ok[Rȱʡ*VCO,_6|gxB棫Gmgim.:4hVdB#q84OWm:8] u͍] /^x/kQ\Ԯ&$tׁ_B5 6TY4 Mvl08]]Yƃ^O|Fq.˫+Xn`7):͍VO/-R}WºF2jvV,g0׻^ Dg;}Me|@oߟvo+6pyxu] KmMon[|rI9>{/N>_{~]{/i*h/-tj>P"k>A-|? [u~R<֍)tZ{/~_C3[YPKۇM4y !wdx7@{Ŏkmb`@š`cK:2;oO1NJ<-/y|==V\Mq$o cC/xD|u}4ťá=ٽo݉0>OOk^ -楨Zjr ͹@>UTm)H& u]!M5/7dEvˇ3H_9qi4տ$No6gi[GuIH>| `sֽY|2>27 Ay?1|/7ݍfs췭|Lqm_D."knVE2#Z[v {澈o4h/k>Y[g%NG+x0G' /~*]xNo/c۹$љ Ă+=oF[{zjwp | J"$g+wJ| 4iKmprKk\);’*w`g5.6/5u+>t|<+i)u˽itbdybqW#8kZ?>xC)O[ZϦYi:bMgvKsl?ks֝?XGKIo8G1+U=V4N/muFe8]#H=aW̛myZI']? z͖K|%m*0 (*$g$n5 'uQc}OÆöRI_I26w=_YUumKn-{RAmi\y-QHaEPEPUuK.Thay= OUSW{&!7[a'BȁIVFGsjZrmM*+Efb&pƔQ,?i?-ct>׵t5VF3 ²J__<+|5xmhd)l$,f(a" S뚦5oڴgqoaXs k>2x_ƞ*5Hе 0'RZ6UJF/&n=_ -a2AwL0@P@`NUy>$|]+ÖZNׁ^O> o? ^P![un5(lUOOj< n|csj#VJLֲ(rH S}W{C|=״sYi葤]OlZ9Q\@9#&J|<_ 54|MRϝ"G?+*s|2֡.孏nf I%0l»_/ICM}MѠԭm"+(x^8# ~//K[V5 %v1I "(ȪpAr5_OK]jHB'-6I$Qc22+?c߇uмACVGws,Z'^7aG\$.[x}CH综ԭҌ^UQL+FќZث\>|MoEZjj -#O=,>]Ny^S_uT>iVKy@ Cm*GI#? |/ G˫i׌ x{L OZ͓xc WʰxH߰y(Oeo?ߠuw/|eΡk [h7:֍}ޚݽZ^$]DԜ"V)yJr`~F95x~_^j뺮gqs p.<-$cy_ς^|+K}Ks[[Wz7D3}H<ءY71݌f=F?4Mm6=i.mX$B)9䁚$gXqwz]#QxMYY aobd@8xDdxA|?oW .a57 )3׌J)i?|B >w+S9mM%!PP@b=9_oe#i'YԴPO[Q#&PC7%:m''Br[+OQwXt^@"y<0)>:"/ k>!mi'IW y~^lȀmR+O^ 5xf-rQxZB#oR8\xh#mib?ZkK9Bݴ.g1g,%ɗwbړ2^Qٿ_=WP~^2 ]xE$k"᠊gF< 3QՆR_#%ִ)u4S0 Kd1yXU'w_??^"<1Ohڟ5cR\<ۣp[\u >t6oTh46=۬d!݁ ⧞qQ_~>-oY@ YĮ D&ᑕ2 ?O] \u(Utx,z09^io#KI l67cp~ a? [Il>Y1-*Usל GV"ö:uέek1nR)-$di&h"?;oowc{fPQE(((())f!T z Zz_:++,}ĈYc!8]zwvL X6H0 j灯OF%bվdmHe)>TһQ__ &OIexKQ׏>,)ik}mJLǁ{f3^K_'}sim] -sm{Y ׹eM[]?̋V^4=CSk;NA5 r>_Ð&inS &8V=Mx|WB5=V:we;'xźd:$gīcx}PI5^kG;E ܚ[6"[3+'[jɣZZe<3_K$LevSb ׍"|#^>.FHbl|.i14n x*ry6I(E{ ~176g>K]+CbqV_K/?Ex^sF(DSn鍣T9 i1n{-xGijzǜ,,)^:)F]u|CCAmg'j-V?ƫn}nؕ Z6rC}kW~63(/3|U׬# g>5i &wj"m$1Ƿ``2H'h+WI}tx o Ŭjzki03,r&d!8mmu&Mկt3[tIeXmG82IU Jkz]-ջe$#Wo|F4_7O>4 ɠmY R{]0G5>o_ h~!ԞmvUM*ܼ1CUFI,|Av v|U}STcȉdHr1qWNO [[oq\6%m 7ÁCm׆ގ(*N^Wuӥuk?cHRTиgn硯?"VG?J(fͨH3 -ӥKvk]Ϧφ_UM|YD3Qlaq~òiijPYrBF_-|@|WGR|Cyosw}I'ڌ"p23֟|#?? ,ѽtӯ΍o0#\,V8A_oITW| omG^.U,WTmM½sѩ*1m' cG#j}3RSK 7#Az`V 7 Խ7}-sq8bR碨' Kk跑e- Y1RF}|s?j5HuM_þ#$XZi$- $ʻG 9<4,|=~PoKu F*(; Y_2]?^'b)T1L)xOxAc.WU/tf !-݀S"oM)_ x3ŏIxX| */Ԓ|ZދM:VɢY--t<$yX|yO|KemMC6VmӑqE,Jqw,q'@Z\_O+i9;=&bҭuHuZ}،A $+?ŖŶۍ]hNƕqv4``HNZ;~'=n(((*9KdE Q`jW`Ӯ0m'q mIii ⷊlW*Nkh>ɽnT|̊UC``.V&(t=;RoZx<҃mrqf/5+_jyב]d =zP ag ycNЮNnX\̲4Q;_V|;Vt 飹C,P,QWbԓT+Nվ:|9M"8\iHKeIu})}<;ú핇4tŬ*U䛘*v9* =E5ѠC!?fO:&խ}buɢaEBWWkO<}N}6=*KkK n$SѺ)EE#㷆>mZ6xm1m-nC9 ]OR:x7YE76sǨ^N S{I{SR~O&|Ao ܺާ-bghe.H9oeM⺎Q׶S ` ٷFHvcs~E}'źe9wn8,Oӊ!鹔~"HWMq<]uy-KZF(!^~^<=\L 2[YjzV-#xhfBOCNt5o+ᮃiΡ=.LԤ;;e Nђ`HO8X7ǯ1|[ f?O2Ks@ #$ Kc4ρ~m|%mc ݴ>Ry%獼6uq4Tӷi3,Q_-=2j5dw>–>ѠxD(4GlY,I$^xoqFGm#_oɒbH6v5|x?OU<=J! W=`g>ǥE !(xq>5̼x'J5M kVkOusIK lΛ\:xs@U&{oeMr xC0\ZV੼O]CYM6@b8H+]Nx7ftmc`pٕy'f~׆xS].`'[%RdX TڋW3||Gux] x5b{X̖PIx?g<g]Znm{,Sam| ?yt [&bYP0ROW[ t. j~mե059#r0qfo&;[M/GgӮmn;Y pw 2IݜW ߇ƻU]SJ[V$4ȑy_[rHe[iH72c-艣im޳]b+ Zfmj{rd)v7k:㳖m3Ogx$`joizϋ4 BI2FN.9_㗂u][U"_ZE>mLO%Ff.]@ic7U6S#}G5GN-axO\g ꚮ5:D&rX''Gvׅ>xo\ߍ2}W|Dc/u'WGJӮWVy7[Y!pZ8C/߅|G^RWմ\ntu+{MZ(f-snJKD*]0I ҵo/<ZW(ӬZ[A%QNdQ ċud thq_uIxK&! YK٤H 1x01*}7Wz_7{:~[G[aX˰@ 8&jYU$4n|}U[C5a34鉋ΘrzGas CաU ޏb.UCdȡ~JLj]6-?Z]9 Ќ!*` KEo$aš⟉|O [xcL[;˳Dr*&ARxgn|}1Q#?5?<ya.1aoEo5wi3!xW#XRO>]H$>5 ,,"1P_;p:'^5m3QVv6Ѿv S_[70֖#]ƶ>mZ׮/#:+ +s{{[1hgθ;#; *l1τψҾVZF"H,ͪ#?mOWߌ}.iv <1 h@(` }GTgOqnn溻b5fbZoQ%cHuh^w<3~\15۴qLCfS1UӼA=|.m: X$e[-(`p>EWO4xCe# RPZ^w>WŞ3F.'!HgP84 8si|\ 3φ4 }*ugG\<p=*o3pO-G} OxGZXp\ d_3Jm,ipdjNXjB/ C▍kiz-muDI22hCʁ-[me|xwm]~!P[A3+I@Cgy>?yY.Z;/Xޓ/WW^?ؒZ~ur2HKB6n9ʊ>'n>sn`X'}G|3=+>ឧig~C U@X2I5xhVWկ(bE*EP?wҕZEE٦ϊ>jV  |QRkۙR)auڽ/񇌛1^i~I]t˛Ap0Qdx\f͞¾za#I^mxy/Ե+jwI}hװiu84 6^5fos?g<=ӢxBhtCNOs8ĸV UiqxPwH ۴/1v 8^_ROw!'-#Ho5飖|Ȉ6qy<ןxo`/76'Dv&c^C%n ;=9Zy"MwXJ0?w>6 M:ldҿ}cDCMy\ ';WEB߇n,uu_Bun2@߻,By/_$A 3wY~ͺTgÒi+NwiK%ޙy r][Dۡ`T/SUfZ?ž2]mi#xN/5ޑ=V{x7,& ^Ǻ<.IcOTY%dR PFNGZP4O lz?(!,s̩#nN |܂: 4Euar(tFXg ESo ]Mu+_?fCԧ.;T.mjYif ]!gAs8] ̺υ|E^"ouPn{D$yIBO^_Lg4o^pE$-VxnRO0jv+KC/'»vCĆ-2O:IwGqG<3\F#4&"YP8`O]j;h'#Ck^hέ grL)XdlB/!Q8eQmnf0K=ݺ#$mJ8RrN]lj ӯ(=e-ʊp9[=G^<{]b)&h)Z&܎He 2VbڞwfS44BՂF7! è#=:4i.i*fit[RIO8PL΁GQYz_xč&a֗ s'y NK2x~|G /KiO6y0,UM\;(<5 UNjOk6O=f 0]Á\bOx?hi,<3/ v~e ޣ&q%w ,+2;84(Ki>Hl.u5,dnby("mTRfx kzosRk,//4MAa udAƏk'~(-VwZQ4p?<n&_C _A|=Gs4x~] ssZkVw1G{m-ȍmۡVSۜB]/mپpzt.4X|oxOѦԖI, [i]imz^/zl2f;y9v1uwB?J}c]mj~Vob9A36c֖N^N^ԼE%sW' =6'99([{ o[4~i}MIxGF4{v_-X]r i 4K;OԞ[NeylUU~uQHe{rg6"a*'ROo9W >N?v r'ROo9W >N?v r'ROo9W >N?v r'ROo9W >N?v r'ROo9W >N?v r'ROo9W >N?v r'ROo9W >N?v r'ROo9W >N?v r'ROo9W >N?v r'ROo9W >N?v r'ROo9W >N?v r'ROo9W >N?v r'ROo9W >N?v r'ROo9WғyڼGk(ִ xvt|@@]xm䢿 wn p?5u?_6{-[lpJ4GE_i(u/V)ž!F[ilpo=һmCcٶm/!KlQ#*҂yu/Vy:+ʡΙQΙRݧHԮyف#V#:g=\7ßO$WvWRyΧ5ʐ#yct4QEQEQEQEQEQEQEQEQEQEQEQEQEQExZ &/]-s05J Itl]R7*SMT4|o߈*|Ѵv>ٻ6v|oũ<NE8?^/ i6ZTfXSgk;^Gc]hҦhJ'''=h+~_|ȼa/?GY$g1}7766q\xNn/$mV7x| m#Bm}[H<>;ui~蔣GOĝwkMMJV$KI~/~xo'n%4sN>jap26Fi4X#u}O׉&&WѶŸK۫L2] \69u CHz7"mɤx}uiJElY4LPw1(o>in ~* du^&o| `jvH>&EQ'ěƑ哟VbFz˯7A, Aʭ&H 1NRc|7> Oj֯|Osqu yFjl#>KBx>=Dޑi`ns4jڄl!.-0MV$tdѼi.7|tݫLqe&7|p7jJvU>^ Z~,M>_hXj8`x ,Oןdӣ8 _ bVXŁC}uiP7$ #[V%*KDs/ZB?.Eo xwd,{Z tIɰ$XE l<>kswi:57 "d$v 9ޝƍ'tզJ 250 684 0 C     C   #" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?úM4,t#(UF\'5V Rljm}~T}~U>Jsk_,6}*>}*]ksk_,6}*>}*]ksk_,6}*>}*]ksk_,6}*>}*]ksk_,6}*>}*]ksk]  $V/tN~o?__.ڥt3oʴ$ҵ8=tZ+<2^^tGݾwE,"J`1'pJw߳·iBE{ۿ!$eۢgn6vK!J?fC>}*>}*x1ai埌l4 Zx7;I<'kkn/b3 fWv_6ak5n" KvBR8Kɥ'O֠44/nlFc{Ryr+o 8|s&A-aKN0=wB^#tOm|m]IovyP2s֛}zfWvooc,e3G|í}iH<7⭾]&ԉD9T+3=k/>!GP_S{d~rn6'8@ݟG^8[þm9"^5b#qq֮i^փCXZfO-4 +Kɦ!h`[;'4Ҿ/_l>ߕ^tc[I_Lѵ-U"8 n\1D {/xM<;jM𭦅wmCOPIo"a0rFF8ێޜ|l -u;7^#_wi&[4<ʹMq-IE=)[:բkT+qGtSz麽DIHWPxW]w~wcԵj;-7L{;^4&8WZohI۬imn [hn򔻢F ѲykS寶oʏoʺK([w{5v9AW9;]%oCk_,+_l>ߕl>ߕb`QV/] _l>ߕl>ߕb`QV/] _l>ߕl>ߕb`QV/] _l>ߕl>ߕb`QV/] 6ZGٵחďTCXjMVQn Fѭ@J 8ϷZn5 pVor~Tnz~5o<.-3xdNvN¹,ٵזq#Ucq:u[-GBHu6y( angE:Es5o9KQ)̇<(_RH. D=vAr_Zlǣy$*́s=[)kh׎Aֺ7BE~3nc.z19EfOIoQi[2ocv3|^Ǖ[}}4}grsN^Ϻm͸ʀghcWpY4b'k֢kTe@Gb´"~'2OrO}( x|1?5a?Х[%BO`ŽP猟?_Q??4b'k֊9BV'2OVe=+&\Xw0<-ͨxnG{OI7ΛFݸ `zWc/i,=[s\%Ԗir=~OVI*i)k#յ}=:\d :c5ϊ8.icWdx"F>['`q5E⟊4ŌV~ =^'G, kZa]]\iV kg",xS_tR 6?eϥMkjPK[ N´4ߍ4oͦpU%#$~QM;l~:x?⟍Z[xo]4[{)W?|W_j1QueLq$Ol;JOщ猟Z)V'2O{j"q–S~QG(\Vu}SFw{'/L{F)5SSYo^{Qܤ0+Խ\v}w[?gv?J\__V¼_]x/K_.֬U&yQL$T@X|y'5Fg'G>#Yh:azn:K_Q>[H ijb_!^EC$>_ծ?;߉u#U𮫫AͰinIaȢ|;#xzW³k[JkV bv z֥޵mWs&^dRGpFۏ18Bǥbh>#ok66CoyyGҊY.͖_ck#hbv*Rn}_ăX̞PҌj/>ا^5whW,uJۙcrt sێk!|+(<\iמ"Q/m JL6 udRN@ܦLJ|WacV[ xfQY Bv:. *W#>ҿ$Myx~Pؾ-nnm`VW;VHʜߊ!|)U:}5O3L"E's+#i?w +kXxiƉ%\%\Yo11x/Z>XjQ_\k:4$@>q*7 q8[;| -bMx.})c3I#f#?.V׽o{΋%/u-#Q𾫧XwvR[M O됼sVY|e~_ ֮,m$gcbb$+ׁ4Q6 [z~$9 yQZ2W*O:mMY. a|TwP #j1}5^'>WWoxB[OGĊ\ y^9\L&oz;O^G$QlInլ]*\~+x+Xxs K๕d1 gF:5z3=$Ɏ66־>]g]GMbBjY5? ,7 HR],(Z +ӍweWIl5OSԂdf$ngU&o} XTQZBB Cm,Xs&ڮ|#վxZҼWj1iK-LrDdz5x'[.xDVڎjg.4mwr-6mZM)Μ=eƭcEmf(ND`_zPh|u᫙!dn >k&;=kl{~ڿ?AӢtiZi_;xSzG5fke4Iekq>D!ځ\3k m'P|LቼIkð0d0HjτuYw^"O*Yخ8>Oz>(_^?Cp2[|(Rxĉv5_<χ4u[ZNũ^_E v5Zo~$Ś^"q1m޾P5 x+'\4_ Az$zT7V-6o`Xd) 85#x)f޿xc~~<6/nlG#-Ń#mFA4_7Oo//mSxr"DZ-h896QS^IA#`}J NId 1$3_+xA_Լ9ifH6"eK̮`c*I !|ƗZnHn ǥEuh2c- ܾcyY{{EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQ^ ~-r_=xQ&t-HP_<\|a;单ii1K|0!1 csҬxC|CNYi敩I]͠ճ'VYN@#i+[+?ik__|5K-sW..tdiiɱzu o⩳?:tnEeU! ? f9慯`ݏjss bkS9!9>:S~'x/au[Y$1f$רx:xIϦjwvQаySZ3cI ?79¹Ϗ^?-𙡎4pr*@npk__|XO^UV{-eI osW'^%yroC79Fvqj:\HHy"FۜmU);?o][_xzi?G#&O/W~]W{_V*vTC@'9yk>o  ? H$Wsq֓W{Duxzi?G#&O/W~P_;vWZ'$B+,Em3d܁W'*OL?›_?5 }_Pφ\^[5.fD,j_oByS79FV+z-[s`ňفGSx)"h6Onwc!rL6HvI-]Olj禓1or|- ZDյ:麼zdlMõDyz#&O/Qlj禓~8 |cXGIwye72=迋cQ1]R[,p*8GU*(1orxzi?^I{I5 Bοk|=dtP?r"1Ju|(ŚO&-5YtCYĮ#ejם2SOovxzi?G#&O/W '<zcMg@mH'XK0h#~[I/|O3C2; { cI ?79¡_+_|S_4 R-5uyKs ;.3sj˿Ŵ'Zyq|)YZ6qTwڑFs6+t1orxzi?^W#޽[LO- -8X%d%$ F3]w$x g-c y"wp?\i_oK@cI ?79±9|y3mᴋGu/h!H#i yHDTdDj:kÐx[!jױ;[eeH$CcI ?79¸/֚W؇^լ;f(:pqnR37qz_Hz]o4-K̬5d<9%' ~g1orxzi?^k_PuO ǯ4<)xd{Q^H"iHfhKƤvU4gCu{x~t)DXPc &>auklj禓1orbx[㬞11A1wpq ~gvzy4ME~cijYuV(|f#U1Y_?:F"hOgGW6o .q+2Ι*|d(._#?79F OK!5DZU":v'WQG į;GV#ƲuIbx s&r}+jC'|An-56b|ޯz[_ ߳Ix^ZuforP.+*U5ZF-,%7muo %0%#\~V'Q<~ 7j&U7^^fCN~?5uOi*Zk\ɧkٿNjGOxJ<73jֺ6<gdP7F~ $~ŭ=~Dݼݚ :lֿi?էkl{^ii{{W^O?5[Kj.rUvTNk;' _>~+ۯ-$C$責 +dg0+k_4QZTVbr%$]͞Yw>/C-Ē^%Dk(.浾%ǎM?Rl](5R[35JLC A]~kSZ6k_4Rvq{k~C^윖߃y<{=񆍧_Z&v%ռYR"2p AϭK| xT'u \I3hfM8Gș~0?0\;5uOi(٭Ӫ߫O1IZ>}bKZo>f׾&[.m?b[ _jVW&VU yo=ړWխ|S+kP$iUc5x?hԾxN'ҭ-n8ZLde£QO٭Ӫ߫O1FkSZ񊘥տͱh!M/O&=&X>!徟'm/VEVX%H5kU>(x[|O|Cl|_irD$;&|0ϊ]ľrk 塊!/ y]kSZ6k_4RRݬ6S_.>fC{[h5qϴFgI'yvG~žNx̉K=3fCN~?5uOi*]WI˙ng^ \ hwL[qN~P<ޯ,ouïlqT[CNA8c٭Ӫ߫O1FkSZ''w]!EEYZ[<{¿1|K&x[CэFLbr'ue-+ 5Ai| 4*xbu9k_4QZTVbY9|,|3fjk:%.\ndpά7ru)_ >hZo| vylg᧏pfyסlֿi?էfCN~?`y6(xzWxKu &y,;+kk U8˖`u# O|pu΃{#{%I\[] KA/$] 8Oa^ZTVb :wZo[Є_x;m>k^ Uċ #pFRdk!l|Wio׶~{-@_I|Ag@{kSZ6k_4TwNG<0͝t-HKbe2r6vW/_zhi.4Bg5̗FIb -"*Ȋ;Wֿi?էfCN~?6jև蟲'4;A=׍4독g&4qOk$*u |NºK}/No7 e>a :k٭Ӫ߫O1FkSZjM[i;oxP[cKfmvNI..0digUo'p!o˥k/5N]GMHDryp)Etqԩk_4QZTVbٻ[ܫX~ioxƊ|qw ;zf4ֶY2-B=IX'?-W_N|GCRZe-Ch:]]I7wr^ZTVb :^֖ZZ="?+\kSZM5 U7W\nQЊ.3J(ƭ[2ZmE"Yn^+ MS_sss=-qh~pDxpH;z/˫-3JK27I%Ph7@$;wv =Wz毫1moSXbB}IY3՟5WNsɠ\mYn9nYJw_ean2_4=nS4+ۓL[ ڰk~ yq e7%|?Mcsnѽ>ax <3wR[[X.3E#*o w|+B=O_gۭOW#ntɄWa.mdcIqq0A/>gsv9F-Nmж($o +jUI菴\W3(s_tO|{mOᮯ[?hI^}fF]gt{g`#Ej]? /ů8,[dٱ`|ex쿯Hυ]??±;kim[XxnH:d,ӛČJxNxw"<;2Sx?PjzZ\G=MEڂV#r1/ݗM#_W>wLD uK ǠZf/FҘB"vڤLSf>t_G g1OT qH,$r$cW_SkW᎝ZY Lַܺp`nr(n| h+ л"|+Bߍdo|?O:c@$c5O|'M{Ǟ9j~!ִڪAQʒO nvm>JwI?V>A,^[4ֆM3D%htB#9υ]??¾E`u CQo4OǾ wLD SYo3FeOxSK𦟬C]KIhAq'ⱜSK⏎>@4gQ{kڷ*+X"5`U%_Vtmhυ]??W>wLD ڼV6_OxS~u6lK+$F!۰22nOR}nƓh^)|Eyceo?䷜ 6]$m᳑Ҷqv-fjj[y~7#W>wLD W G%q\Cn d)x k9G5o ZXO|nGZͦ|ɒJ,Ө;?t2Vs5i udž絹xsxuvrddV\'/cXͥߋ>e}<56\"{If_*2ѡe! ZԼ= T47D㲶Wsɯ+f,]=O/믇x9!4^_?B.dr\Q!FHW5N. x־)+_EOc%ͤ)0c,nBerH8]mqn+ л"|+B5ˍhwԵ_%=@ؤO,D#0,s8MwCھxIw|wLD >0^v4![M4M5YeAAl>%I>xG ^oK[`0JH24ӉooQ +~&|j-?W&hBƧ *_4|:q xúyf¶uλqsWW:D ,R|W^¯Gƾlum"煮cYُwif_/@jǼx\=@wLD ?\W3+5[#j$~2Kt-IPJty bY$ 5G>6>"K_|i>xT$ss8%n-G]8 Vm} ¿.\W=j+þZEno$gn29'W|t/ zν9mkouo,Qhic*W֚/ٛPƧw6teS$2(HTEiwo _ևҒx^>7{ /Cl:~8ۿs:՟/W> %sK18׋G|,ߴume[Mr%ڒ=!}v9m͚sx>/j}_LOӼ/m4FNEp9rq*'<^v=wZxC4 E5,-)soP|tFz s_tO!9?[B5/9tר,ِSa̾ޣW~-[yIs_tO 4 ;;-An뛨$~5oM+=BSRK³iF't&[:լb1YJF'pFkZot]Wk "T {dk2}7TږkZ}i{s9 Y]wH6aGzE 0.^Sqo\-Rw2$6O=*>x'Ǘ^xn]Ϫiܺ/gR@ >cqCDP0@; u / ~'oo&(Y㑢,q QL}W΍s<:e΋ɥYPb*;qDnZfG1뭿UCj ng?zv>lKE;1}tMSFҼ73HWmP]c8폇Po/Lm\q8P3D.ھEaNh\hڶ.ɥjӽΣbZ/%lndىep+| X|{K=4Eǟ-Eqp8}CWk.VݰbXO4;̑ RTd}[֍s<:d:6F<[ʄ>KyPێ(3&ņc.eHfmNSA--oX2֚EiIuY^>d;'AOcxcA9o: M̺nMQF ) M}w@t=6ZPrM%x<Z5)"4o.:+H@s^? |ֽO kZmݨjSw2%gnN&]100sX x{Z +;ZbQIIpA۴mtEfNK 'Nҵú֙K+-CIIP@2`&$|=^ w .&8iE1__-mFO8-'"KxVFJv_3O ]F vbC>lՊ(i|3佒OlKKӕ *R'!>d H xмS i6^:Nl 0**UZ)݁j^ 귺<#{^n.4(drH$܁L]KStӬu QKk?.[QZV 8QQH:[$FfLi nSӔpX~Ɩ_ $=EpmcJKz#;sqZQ},nQ]¯hOt}] meiis $.Q_TҼ?oB4 xcOk*F;YüKVay#?()݁?|3^ Xb8,ZH IvId͹SZ8˶#WhlB߷ Mo뷚G{nZvl2pfE+V?Uiz.fv*\iv(Dll2pT+Qg_U!+JСy$K-ں:(nخddl3h[s&jb0Zuى kj>,ളqA pfEeË^xH%+osE`x0(MqYw/Gi{jĩ"ʺOލϞ:V?u[FAkmn@U (@0t |мI _ jZGu6w?rrs\<9goXFZionsJۘ/#0:WQBv3k'W3no-;{N3 g1E|#~ikMӔ%G<62` Ӣ2oVtmiW:ZIk')BW_'OUյ;+BԵ{OFm1^klcəfH${U(nb |U}ZtM_C(a/՚"sllFvU|9ޑK]'^jNB2;S&FA%s xV'GˣHBc$3šy77K{s&Gl)ܾNJ٢ڇ4 ^֬ChxsNZ.yc1R`S*ES__ڼ~g?aw@<:rIzקQE|vEizx6˿f%ޖXi^jgE7+e&koG.R)'RI5E XQH+ľ+WiV։{}-mA#E7b~U@IEr-".wNYs+̃X.H*G#sY?1Z|-<3a.k!H7c*Fdou=89W?neOk K442;gM*nm@VcU&tk~Gw,!Z1̓y 9L{-2q xOUڏCլ/!EKo(ʓA }jXux@-_ĭo]d_3fvףAzWxv&֎>- LʛixgR+NM4ϖҗu嶱 %z_O|𖻬Rj$F*83?z^Mٷ>¥"q7selQ_%EZl-|G}2)mtsa=o,uiso[YKuH3 cT`dc/<}E5mHO]͞ OT ׅޮso6[;)"sXHAk쿪Fkm|E:fyj"$rPv};E-r_ii/A(Jq>+[W? I\Gskq55m͵$5VVV;– ִMC@ž5yV ͭ oGP=|{J65U]'g2c<W@ (EYk: 6D?Ĥ`ך˭1C?]i`%*6AUb39p2Ez{P}?N71$+oǾ07sukω.dDIF{+0$+Z(Z;|/ٿMÿ:q?^Vo:HYQJN9Mw{⦻xzH|Oagg[kx6[+s@c~VF6T|<{U6An~u/<3ZPẖvlJ_ߌ|@|U }NT[Q\˪,~zȖ(U#Fx icĵ߁q8-}i%w4VB* nHO[_>"i^ Ե7%2\^iWEZ5x ?>V8IChPGSYxZ1ΐ=ÌHQWsD'oT >7VK[]_|ǡ~^>燵k{oY. :(2<;  h꿳Nj7SѢ-nߩ*/^߉??>ߩ*/^߉??>ߩ*/^߉??c`:=Ρ*1 ,<pzj:(#Gp8ky IoMğMAOQoIoMğMAOQoIӓTGUnXfV~@vƖ ^_Ckwd2pz :( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ~JkMG[/_=ilm(.\;Iq(n*IJ4?boY<4y"ʗ Í*>f=/<==Z6S$n͇Sprt kZV㔽״WRPmZ~[Zoî L9QZ!'wrAӵmcúqekqZ Ar??*0T~IM2Igoazi%ig 㩯/oז>,Qڅ 3QQD"{6VklGwĚPJ~ןi~GWHu vGQy*t5_5ڿ"2Nṵ)5lHi!F ;V^g)CAO$H[YB&e Ys3+c> 쥲kJbK$n=2*uIhھNOU EZK]Gk1jsq)"v?Ga OW|E}%+jc0,nַo#o|/|h0w-Rd$ 1=J{h^/C|ZlUh4o6ȏk#l'oT\\|<6mڶjZd:>ݬjxo ʦ>QuzWĺVڧgԼGU/ty-~_0v$`ĶU?/n|U㉵ zf=ҢDl_[?!}CHx\i9-cB G1xsľSLVZk XJ 9J ll 1600 2560 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((c" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?fI1'O#{zR*ſ22yy\,[(*GEſ22yy\,[(*GEſ22yy\,[(*GEſ22yy\,[(*GEſ22yy\,[(*GEſ22yy\,[(*GEſ22yy\,[(*GEſ22yy\,[(*GEſ22y|XԼG!~*LfG2P '`jBF3(:׃{ s^է#YK*2t#s?xX jdMPi '?Jwd(+5XHYx%G1!{<;6noj[_ExYnvړnG\w;[(+~#Uմ7Kk5YT (2>:t-PDl2FRX( Fͥ22K%,mKݶoȶϒH$b0ωD͞iȍ̇kH={Ӹ{WU5]VHR,C\ $W\:Xi.lż]c ciGNgΡsi7jYAraH.m# +$jr2/^7I ;Hmvx&b9ҰF9O_VޘZukCh-uܮwm=/֝&Q < f+) z^!˸ {)niM-aUg4m.{P$xglkZ V}4LIG<[q9m7݄#[is溢$%~|Нsi֗E3m/2m$iw~Vr,#mFIǏ5W0O<^[C!5e,n0F t-E}_[nͤWyUlF迭Qeex|IKkgmǺifO"\ pHL n2oo`u;vE9,LW'cEB=̣̪gg+~eeT==XQUO3ޏ3ދ~eeT==XQUO3ޏ3ދ~eeT==XQT(X|Ծa8uF,kRO3\(aΏCa?7#3C)5˙z*lÚ'y֏0חj'~/8ox+^uj$ n\zg۟ َ;0Z\N,6S^@֝^ϵMƩE0Z^ϵMƨ/?TrE0Z^ϵMƩDhHܻprڎVț>y֠P/Zy5.,0iEXtjVȳZg0z?o0z?o񣕇2+y֏0՟ƏƎVȭZ9Xs"}hY h håa>g0z?o0z?o񣕇2+y֏0՟ƏƎVȭZ9Xs"}jVS}5Nу$` F}S0z?o0z?o񣕅чy\MyKp)X!XUahдI* 9_ƏƎVӴ7LӴKH$Tv.l+oXķ Q7޷>>9X]V66+eogo@UҤӭ#1Z?aa.JT{MJ W 3# wvzq_G=7=7ÙjqE]siWq\ZMsܕVu2+c0z?o0z?o񣕅ч&2iO Ԣ{h1{A;"9ґv7Z h hatejV6:Ngow':FY MG3pO=7=7Ù @ '$(ixCN&ƑJrtaa.KĞtJ-ttdn`dѳKOH%J}Xwks0z?o0z?o񣕇20n [#O R[$j8AQ.z:{ݠ 5 3o}G4}G4r*>LmvL̃r1[aa9)!䴟l+l$yquzqV6sȻKx6a9=7=7]'KTEӬ_n\\7^iZdSA,ZuK&'XTy֬G4}G4rEo0Z=7=7ÙG}j`G`G+dU=B1E.):}ьz.qmxzj!$vM iN޼P$.;,rI gҳԔ!hngN U=펺=/1iDx(p|x.oSjf6[g?Y?)ٮ)D62.vXg r?cjJgϿ?/]>%;FKp9U uG1Y Sny⸻xLt|_5g/$P#y5]\[X9k*,lܫ%ŔVg]#nݞsvK=2oam5Ű@Z 4"kҸ5q.4Q'ntp8|fX#,GI/b$ ,ɼ98܍Qz֓"Z , Mߦ|Cu_jf}1pbpI6Ԗ>uu#"E }\2GCMыmV"KBR?nA!0AZ'RUH[7pǏpi4_hfVn29M!BdۿW<ȡm.Zl| >^G}$I[,I ԣ&Y( ;&#kV[hu{cO4IێsZ)JZt:udq:^= m_5 B.=æz0(dEDIY`ԮV hua(75סR?xv@qwc@ӕ\rCTM0ܺ}8OjRcn}>ʻ^ߨR,}gߛ?[jnMcN!@=qAW4$\y_tEE @73MXpیZh*4?'#8qhWUҾ2֒CHp=2:/??EA6(oYӟ -ΤWuqs{\rCQ/??EE6tGr~ec=ps 0ܺ}8Oj?>Sş6HaHPk&ݙA0j}9-X74Hm6A@6G#W-m6!;jnf+&fd#z2=ErSmcM:mǁc@NG#W/s2>i 9\ǧcLOw'V;@4z2=Es 0ܺ}8OjRcn}>ʀ:|QFGfR,}gߛ?[jnMc?#Q+Ss1Y45'IoM2>|wր:QFGZmMP!l< =ԙ N,vcӱz2=E&Xç;+ zԘ[n]>ɜ#Q+MI3` s?*[}Hoi~l:lQFGZSw':mCm;jnf"M6bGn1hdz厦h*4?##8qhSut7,i~0u9Q\ΤWuqs{ϩ2Ɔ>ܟ[c4z2=Es 0 }8yBjLmٟOv9ӜJ=E}Hoi~lm6!6B:QFGZ-Mdl,09$vփ668q@NG#W-60oYӞ`:[IЯIAf=;#Q+Re :}?2ǮhmIe |u#Q+MI3`?-X74H`6A@6G#W/m6!6B-Mdl,09'ӌZr=Ec668qhSut7,iS(N~ϧZJw;1,,haP=ps@>G#W0ړ `˧ ͏BjLmٟOpNq(dzm"ѧFA/͟S-7r~Ӧ1ӿPS(Emœ$qAFggc#Q+Sut7,i?ΖRt+}:PsqُN=dz'ԙcChO̭1냚RaltC?!8y(dzRcn} sKoVFi~l:lQFG^Sw':mCm;$Zɦ٤Y8prOz2=ErSmqމ7YMx2v~0u9m@;+ʪvsPŤ[ҫ ~Nz5ɬ,1cW#V犭t߶[y6ˉ$\T\Ps/4^ ̬weN;֥Z$Sud!Xg3<^ ) kɪ[40MЮdg;5iKqco4IŻvFJ+7ծ /g M|ێ 8+ (Š* k{(c6T 袊(*zny{.v[Ҝ{ &%#ȆRFw `Y1>4Ӌ]x֒["ƫq2TQ"CW5/y4$=R#l! 8fmWĺvA%ͥ{]X>`qt7o%Km夲++nyƍlrG',}R%(yc3 OSx{4\J2F0c#NšDszxNt"(*mW*r<u,VԵ;{ao*4lK36A1+؏@BkW61[Z%dRX##~UW-M amyܵ%@@ROXS@Škxg 9$E &:SE =A U_< =學s Bw]z*>PECwuogww:[Mm3^ui1;Uq]Wt5Ma)ar\5ZZdS[}]:*D9cX =(['Njph bhil B>e^@xs76zH.4?IH#fvQ$MXe5?S:~~лتl gITIxGS#A _Jݥ̾xh !$R89KPfvu0gkec7\EЗYXjQY Х5Nm.u+1`Tc3%G q=zQEAwuogx i\(,NOjOEPEPEPEP-ɤDCAhԤqBH04؛UIm؉X8ϚxT\;gP(]wczVn;d./b{Ybcd61o,&sn~}}WMKf887;:徔.;sw|(w,v팚?b[CS{Y$D,iTY 8qZxA5Ŧ[Yl|_ aB)M}m]byᝧDw ]g+wz: %ZVa 6+1\|"n|+cO氅e+ `0œrq߹U T`ڎ-Q@Š(>6ʶ\*Z'`34|P[RC؊V9, 7xuqjd:w$ʢwgW!Վ[Mխݯ[ycb猱2i,-{6[đJĬɵAO͒v iIOskSZkzG J;; aT 'H ͼltڤyq8$p6R6֏i2Ŭ )X#h2{zW}L/o/ls 9 k6v:rSsr-1:fTLӮ(A'lj5j=.@p$+7č/-_20>^wڻCO{WnRޝKg,I;͞&=z|{w,gxHI}VFkd'L qMz?E̚,*.*g%@' h T Ͻ...L8 x~⁜ռi\YA+djNw/jX/N=ԷZ C$}ĕ^oH(aI0 O,i9vX_tZ!7)%@BoR?.ȥvyiIvSo^>SS/7y|n[ˍ- -.ٮ@bYrT#ZcgĚh.)l-bUv2mep0;*扨+mo$&)Oosnatb A7t$IFxAҫNՔDm @/$[ m- dQ3uKȬ,eI$2v?ELho혭Mw;] ŕDg9 %%aM2}#Bp#X#$6v·-Sx#- ђP퍉呸`cO-MJK{[Jb!fKv+ia@E 鶅 'K+I$BxP^TnקA6ypE;`V= :M%z]Ժ`WhɂJa籮ZKtQ՚#%0lJ(.KYE e{:N\ƳǀeA^\D0X -C_1^iŬ)"N %ֶEe ٿ`ywG^M40"~%t@[jP\VZ;ě3R3>)Iխlf{gH˂Cf=jt[ioԠ*G |nMkbe7)"B4q;QLaX0 l56LV%dlQwʱ`Nzz-uo`i57ףլBU>Ew+7o9ZkE|Gqo}$oddZ" g +k:nsZڔ ہFG>^{zF|#W-S Y*#'}n+tXjXi6;ʱ@eI!Q@<Ǧhuݔ)TuWR^WnsA]Gu9tٶ ;upUrʫ82Y,涏P&ȼH ;TeG T1(u0@-bQCZjϩH˔4PF3ݎqe뗍yq-ZtwwE7 D9#̮úχ;EnMj9"MFxv+WӵX G$CwE$U*\Nַs>!mOS2XG tFr,I-Ԝc[kUֱ[C$O$ph\qޮv*ȉ!Հdp>巁Ya2T)=q隦$?O޹p  _ 4;  HެI~zs sqt$FYSy` /aD];Px' ţ&ad5+3Fmt4xoh&.omu##HP[40O4;Kѱ/$*.ֲlG%M ܣ)@ !o\wk6WÖ09L] #61ڻoj-mṈtwqW)ʃ0  Emڵy0LZ8I!h k@ cjw6[i-f;I #$M)B 30*orm?[gӂ7I|6,$$1@".X>աnPt?ڰϤ}0!"9#(9I=໽5ۣ}2Kwn#r$f  >Vi }FVԯ~2[05 2NYڥnS󵢰5/z}[ <@BC PǦM^5=&ιw|ȰPG"})E`7"^m?ކ070sh隥ciwg:7Qy㿥^(ͼF<8~$_([C|QI>|[8^k84MK\Et=7]fΑHeڧxIo V.R5Kic pQԼG[jb9d3y٤!0F2{Z;XVxv_50=$XGϻ p= Ƀc$g`2a;~1$''-sO$#[\}`ۑPN<HNu [VϷ(0ĀP({]hDӛDew{k}:A&=Tr{7]"Sf։w=ĶNJY [X4 iķ)$636ѹ ϭGxHuYV]$ClΑ+8PHM.9_{K.4;H8ffXhG zf\뚔^2uui!pEȟwx]vyai=_wU v@UbMdxGц%Cl!!soQ%c}<7e6t^<8߻×qvn;HXm^^ّS `:y:ڔZƖH֚TH?VS^֘ߡOMg[cs9+-NT|gM FԵ??ȥmlg\ Ƽs?Ucjs7#lz18\o?o8P!XRF*[Box<1Z';ŶbyA E\?GWU-f4^Q[5 ʏơAݻ9|ЮlCmug;%Hq ?4PiLdj0,]Qh㜔[LZzznu5,9[w`_?.NGJhzǝipX I@!X@Nw+?ЇSrx0,BBsL+但@z\Qk"`I7##n2ڊirqef`iPgo4fmF)r{_h ]$追-@'VƩ%ZI?F<m<8_47Ɵ@??wQc >3kVm^\h0ͼ)0*@Uz|[ɵ l`0 2N;rk'|PV[K/Fn@1HRjvԤލv1Xgjvm,J7.wnxsVjMztK=΃h"Fmwۂ;gڼ>2xՈơikҦ澇{T?וT\\z%&K m1w?RF2"I uԴbn%xbhd3'~^4ei6N 3rPO<@]ZƻM|D|_ua]5vO:w=3۵n [+o\;on''Y5Nޖ[{{!ȭ&mA=Οỻoql̊ G^{/Eot< ,k*tΦ;mX[xPm"u\VbhQdF<Uu{}f"o'*dpsJOxhfMʧźuWGm]u_7.RԬii<}:8#s ?,Ҧ-id|ʫlf#;On٢fD`sVş%On&f3ぃQ,kt$,[ zjMwж(ch&dJ.J|!y*:ɍkOhc&oq.ʻ;Ě{A5W6&s!Z0{Jia\psJ.wB "nxxBD`}*:ܫje6i,,-XݙI *vɯ펆54alrH7=@[xhL* ~Tbc8F0U:`s:=dߓ&yVœk?M=.5#UT'a`#Ry'ok,OCyG$ȾDIy ۞f[-gr* }Mx׉$j:u^Dѩ r`9ad死&[5o#->Ý m#H[9{oO Eų9#쩕}+<#u5yG!rFI8AI;jiɤ~_rBpԪ%9w&5U\ӋkJnL +.rHTvdfxzՇlu(c8>^Tdޱƾ%ia!Rӊ쥂4h㫎M٦vQ ^.xl!{3sAxf)bդu&}gkwmw~ml)0.82c5/'ugAl]ý{73x^O>6c F 66g2Y-Ȋxq1 #Mt>Ӽ_1=w08>dugpW Rb"u [JKKc\yiz?ĨD3J-c+"ofp;IڿI?&124y #C_jDHBtfeP9T]ٷ$RJ,乓Oosg ] I+:#gz* K{L +Ev!o&6 %!` ELٻFq#}SYNh D69}MsVSsJ8w6J42" 7[sʅ#hqSN{VHvD|p ]+S[29Fl*pz"}LI0V+d[8oLV- TvOR2y"3|5nյZݭX[ dCLuV-&Lfp tm=M]H/5ksuvך~AZʒ#w*T0$W3s+ܓR9uTV,fϵbZ;!Gq5` c5x;sZoڭ'<;yV7 IG${9&O򢰝[nqd]8,4-/cL'=+ŚVTԑkҥNKsϩQ=J/T]1]g 3+M{`S?ho,g+Fta>L=**=*zQP;+F?]]V2ucF}v&_7_j졍V%V4.90\'q]\!#r0H9 Gjht.DgNG\B>Ut]}AsysXė`|c8ǯ%}Aqm%u8S\W! [\ȹ)򎣦Ak6.a!ߐ:gu]VLFSz bsߊBǓrZYB9>fB/XvvGD)]$ryR$t;E9O "GoYT׏}蔯 ǂ|?`G6⎼Xop?RI]N.N"V8K6b5j`U]/fWW,E;aJkk(mʌ}oYXk{?lu.9-X'f웨"e*O8kϩ$F-hP##uΥrqKuO˩&hnX+6dhHc8tdփ{֏ƠDFcXs^! ثZ܃ȯXSgY'CMbL%g\nWh &uBm2)Pă^E7b׉ׁ_jAVpe&9$ QWW ~fRMyݲi.T vŎ_|^V 3>͞3:G'–VQ n 0GAY_shJDņ쁑:qOR&y] :3XdF`@[-)k@ t;z`uŘsU,++F}׫V8w:T凩k\oZX&/9˂WWϨi8).mcDڠA'V# l@Qe%{{?˛&V8Bc+', i[ߘ.lfch9ac4i ;cyό|GtoIyY$dgc5 ] fHʹ'βzqmuFI{׳[P(.'W<ɒ7_Ns-R A/ZoAu*'^)u;]2v7r>O[M֛ y>z)5iń %O*kuJn^vr_(ЫO%,WhU@e]׽Sb6v<IŨȐo1Ht^ule vGC\ѲF›YQqGr\lNI.b:a]UpdT=궡kuo8 4Μ:\sgajѶ.8\C$9;EaxvUԣIب XF[ҳ]m;N?\s[ӦsMڝٹ0I y|YyQ.y G $sۥx^3\g9kNg..+WΞ[)s=xbEjZM+!F;X/_7P2nL*ɫ.O< kV+zR23S\G^ 3@}Z&U#|M>\YI, cc 1ٹu},a`r4ԙˈ`u5mr C8 gZ?>7 Y%c~Uiq$\+襏ҽj8Ȩs+1th.ΧnMiŎciHIl+}?Ϧmn.\rzU66yZltD| t8U*BPhzwğmdlA>ӤQnF@ˑ B!D#>Yb 2N5-/pr8q]E];OVChm"2NT+ɼGKQLʺi>Sdp:g5zơwOUXtZ֚@Hs 򧞿5JrV=2՛L5W4rDp>`9=J 7k߳ɘv*/ѦEFc2HbQkeVgRx8ݫ+in$vb3+FĶXk{u1y֬V׉hFkF/ę'Sfqb 2ʁ}h{g&pX'?vOڮh:P5ܗm&`#rsnþۤ%o>0~a]&bM^{c#D|봃~k{]|`{&-O5z=Rh[q\4hق~UNMBv*+Sq^kDz|j Ω4V66_̋{8`ܕ;zU3|=,RT wYX7}MmiS;hx{SI25m5-\i]ؿO/^;$~Դ {A$M^IܛPŘ=kV$v6zrh#0B d֛Дzo_ʏ-?y|Q薷u 6kuD}>bO#n=jaZ?~DnץG`=O/G_ʹ ꚳkIeqsiSpH&Ⴌ͂ WRQIO{CQ湶iB0v~1C0@nǫz COҼONu)ten`ʟkVoy3ɩ LJ,? [*vE$_CAJ\ƀʀN 8lӌf hG2D~d>J>_Mxw^6gWbG]kբU(Ѷ*7=:vt\yn[;yYPG`vJb3V!1V1-`gbgR? ޳t? h:nm&m;dF 8[5m=I$8~bMw 1*k7",p9VҸg͈{Rlu/-I"FC@j2O.;{* t;_]3sH,HN\Ƭn##ӊOڵ4p urbUB5 5Dr3v[iA<3L7U *G09AϽviVn0j MK+H;WxKGKm:yD1x2ap,2x3\]\1ޛO9w5&/3кiJ0mcE 6<@ɑN+|O[GonJnz_$[$2Jf zڇ;+4ʏLX@ Ȯָ01ƄT7^Ykp24n`ר榪c5+I\Pzb : ;ORQ1r I$;z=wSi6oL2]LΤ2ǜQEo֙ZAu}oom9)%p䌀 rxHdPсE _pZERΙ~0kec oHI*m1Q<hiHaK$pQ짼 I' q[K4͊` Y'yfcG{msʬn$alyAfM5F-$g=@S #$uXdQ@hsZmo%q$>"%s؞Gb+7V IF}F[ql$Ԯ.8pL#l+]uݽֳG2#DvT({kjAXV+{1b!|:d~дK}2'DS4,+;p=Z/uKoter=E:i-7&NL{W#G4(aEPEPEPEPEPEPEPVylkq|g\4^#T7E$,\+/`=k`yM8HXʹKFjZtb4x<ğ^})9nyJ73jWw:uiOjHIRivD#i9PHմm_R}NO5"Z^12Sݖ.t6fs%%WDs!\w4KeG4HZŗ|ʻIRds9Z#}y|Mzͦ4(WdJ ^p+x+|-GjsVqr4A`'^ 'B `}/W/IJ d_D)N2:YƯUт $IE~l2' I$R $^*]`x[; QSUa!ع}3OsZ%ĂA h>zƹ`'QFZ?ɍE4UfB$$/% ^`Yiin,RO&Y$ rzu]7v:NnYnW{(/8=1M3EoqcCB:Vli"jc#h&:~.e`e;c"o=7W>rv8 0׊ҼEc4kn2Tg5몢N)@#,M(SF- {MF+ۛP1J=EsBnt)ePkEc"[ wxXVa+aN3۽s:_e+t9$ sJ2RW 4X"U44WK{W7<I$94T{(3+OQ~EXdY6&ynhkd$E VlCxxˍJRN[2Cys@xr\W9W~€;v'5$vѡ2},:I=?>獉XV4a$}v1X BO*RU6}8S_q4𾳩xVoݮy6|*r=۟(G/.IϲuMΕYk2I yJg꩓ٮ(Ƥ𧈃t]E;q ݫz):)Whšjú.A Hm|) $8O̸<kz(+{v|"`cҵ%yJ#;ix_,u͍=hQKfϚ?hOa||(zTZ~5k'ı\g-=+ *'3 θrLOLԼ|Yo~\8j(h=>k"5c&B~{泮lvJ[xfkyavARVl5)_C/34+JL@-k^MkY:IJj[ٵ߿PxpbFsUm/Oc)kRfA!0!R5]%ZnmYLcz@z_[ˡm6ej~d: AbR@?y+zWIY ۭҋn}TB6;W>H_Ӭ|snNd{2lmJ L+0><.(=;^}c%}no1ZaFl1l5I椗ZŊ\(8xg26RA]Ð8Z6x٬mKD4&)꣎3KeXXy Tv6ؒgvs{k[]} ATр/Ic=k6-(Q#RF:p=*!Q@Q@Q@Q@Q@Q@Q@.\+<] /V=QEQEQEQEQEQEQEQEQEQEyIB;ڃ$bBEz=qn]&?*E5M5()zg= ]cE?f vھrg}j_{3[EF\bZ{k$GVׅ?!'r?U4w9TI^1QutQEQQEQEQEQEQEQEQEQEQE?K_:ڬ_ :_X]:< m!Dg$ Hꇈ3mWsi0$ RvMYx}] ~ഺ u2ۈ°cb3W隌&{icKTW,"(@,>o.~kO [/ӓO46s鬐,䂠,zԴ9" jvQyڶ,[ AW~bq=ȾǶiZ%[ 7Ym~pr9x\ӵ+xnaK dd mb1IYJE+eLI'Z,4+(,uRmtX3G* Jy\? xg[\Ʒ!U3Hj:ڽ֚mM!# k.{x\Ү/zѹcVu]/rߛ\ϥi,XHH!eP Fh Oow}sfݶD鳭&Ь MD\^Kᱪ[\YR@m>X0K.P dK{×mwZ] hGٔga0_\y[h5{=572+1E^VV׆ص*r[+-NiāUi H5Lr%ޜ#-5fT|Km#lnI Y) (((((((w!X_B(((((((((($qnӽOڠa~r"* sjpYMkv ~kͱ5hFV:F9Џ45_yCsm Ɵp|T0p{*VAG,8_g; %(:ԓʦcgp!+ΞeQ7 ʮuK6JJ>򂻷k]_4"Υ\O`~OI\Hvc$P7rGl]W?!'r?W%eZGNtmUP;z{sɗ z7 זejs.J/5va8N0횞zMovjEŮxSv֢S<;-kjhw&Oɷ {tOaּC ii$Q] iؒ㳫(rTiEr7cE[)E:ZDs0\)ppyhi*+RgTi23~+65,o-${;rRc9S>Zv&BQax`x9S# r2G(Š_h.Mv38ls9QEQEQEQEQEQEbAq_zw!XQEQEQEQEQEQEQEQEQEQEƲѸʰkp%'_vZnhR0AэU݀Ƽ%E&~T3shKd$??]ݬe41+k{EYnpg<"ݟM[5ŸS5qvKJGʧ}SB#b"\$G#c+Vug/$utU VSg3# A,zXcןJ{j D@.f]<ՙ(s]U#)A S ad*IXp}1[M}h]@BS u$vfv 6Ry424I,.F2>(((()8TO:̱4+k4MDnP;O8\s5G"dgz6~(bV[8a KE\Kr 3{4پwS2(LsC@/|Ij< \ge`r' mTG/,:T9QI ;;Axh-dlVX eKqg&&kh-4kNX^I <#0H13Yq?ėicgsasrcS$[C EZ (˵mzsgad'IL ARH8'V azuÛ%\ +1gF$ #obq^E;X ēj u5GxI O&L xQ],> ( ( ( ( ( (1|] /V=jxhby$LCtdɬEsKX7Fu=Q@Q@Q@Q@Q@FTOx_r0=Ϩ NS]‚p2sO.Q\爼[g;E-mguzݔYIw VWFe'qgfogr[Hea+?: BORT@CQ,~R~;ګ;Xd#{kud6k rK&$ Sj fQz3O [#bqюy_h אƒQd8aj:/q[Vq.ҾiEmiy}lt]C`RzG;[h@DT` S/5_}: qaH&*[x Cx hV-Q(& -H!x\ikX:+ͮe{΍=2@.p)H;uy<ڳ&[a3Q%xbh\+_gPHP{Xymc\dqE`ӭMR-AK{Xgsp8#'=1:Z( ( 񼒏 AlM$y)o·ZΏ}KPx&RH~c^G;+RŬn%RӤh!O+io0 Hbr9[ [Z^gyz̞tij)H`|r'>#iz^XKwc=$bٺ7S!B1ܺ]uyks{[q`$RY_hYC3&;W\kEk)qn&HQݤ f`rMіG90 <1o-YIkn6iʁ 0T=k]If]r]2xK8眂L+ ;f[XlJі#{Y\FAڻPwN:p[Tx1<Ŗ+!X0xEk~-2,XH`9<{WOI PjZ;8vQԅRqUèX\ݠ{{)V#zv.ks25ޜm8c.OG ;:Cirԑb39e%U8-fQgax;Ee;CĪu$9zTqJO.U4x{ K{"1 "ax;m+<[n}jkqkaf`]C4(jGc 8f+ %m¹햬Xԯ(D"<@}` NA#_wjZEzvo-f6Rk ? zqQxSWPm ]\]H2)>UTgf]ju<y,ඵ5-AYl9Y;^ph-hi „!F2}SwFn}jpAK=HeoF9 ~*l>עkUg|y7mN>8|1ŭ5)Ud͵NI^6):s{iREvoq+ƻS `gROZl~jCz_%u&ݘ1)Ey4uiP\s2H2V7*Ep lvQi \K O& f=2c=ӵ8MJŝrIf÷vMѾw i˦f0s~(T?^[jZuƠb<T׽`j7sN Ǒ3Kq#1+61U s(A[CW౵֡/LғK8&t9]WFmNT-kd6,@{ޓvWxRWASIu-[1*!@bAہ^e[E[N9f%UFxBOU5`Y"[/4&x3HP}3\ihv &ᑑ#cQ{[0%1Q;#*^[o Η&^.uk[{$:RT G'5u[n XxZ" zs\YVDPү[E{x-YG%ً󹱟7nKR7m|w!u);w\'J] OFy4`[Ʊېd=*߆F4Sɨi^+;Xdpj7׾ӵlbܼ>ldeԃ,8$-wQ_bE%2J1l\#/8Ps  Zw=KXjOw\ɸc8WI\;+; dvI=묢EF]#ھld̊{#RnJWE]?Ok鶭VKH0O8`Q^a\c.nNd P$`0_x1ּAcouVhK献W=nZMz$W"$^_;zΤlʹ'#h8ǥr ihB4K0|%H?>6==OW.Q^aHO }>5[-圶 # -z/yvpf[8s)__jBԴo UƅOpGˡj76z0¢#K(=h,H e^x!Һ+pqm,URk?6t|y~Pb&@|ӵK FSХPv@+m*zW_ lݭn!T'^ dsm>#YIcEsu4M̪8*$6s R=FE7v4+ q#&|i}^\ [xJtlqU~ZXZ%d"dc3c_QmRٯGKK[R (RX!.(RIe6`fQiHLM4ndک%f&_7{n-A(nF~\rAFEKPz5Յv -<3=i5[jS'Noowy \, 9;ʪ7);Gw)cmVN]ZWAiujOms5q.r\%A{;vk22$m $vvMOFծui$^FNn6 ]~R2rxVbpZfx6l,,`'~U~1X BOO#U[·7Fs,L,*lx1XEsox*^cY_#м3hvMY.&?0.C0@;qZ:sjV'H S~EW |M ?_\SYS[hz)Pu:cA'-m-{XT,Gfa'̚|V Z5M;chZ7<k#,jO/T=cyeK)tGF޶h0B58EDn,cdi}N[dCilndPC#;W~UW!EuԊGJNpR}B",Hu-e}2[Pڸkx"#O'$ g{۳ScFNp a4ɵ4u)/{dL=9jT xFMF) bO$wC9 +o k`$3.Ð\ɭ)V95.-u8.yx pZHs(]p;o:_ ZmB띫pk.rhtkVh0A}GFX_] Y?Ξd` WksyqܵЕĬX1.r:^,6yjS{W;%ny7Mdp^$n^&mBVҥTX L*xqCq5/VꚬWsdel {$-ԑ⤰:zm%X/⡁?BߝkwJDl\ȶ.@v zi]_C<'.VolaԮl3C2{%*O?jZۻVy.3Hn Vτ;uo5΅K#{deyHr֊k=m`[a+1ڥŧi\+ C^iiz6q J*p:zڵ]G\Ӯ!|EO3Of-vqŔh6cVC88 hf{_OpBȑV.j~R#PҬnEC7P9'JzΥso>=un;s0X2ō0ž]I5Z7nUĊ<ѷh_ds޻o8t4%Y%}2;'ڻ̏*NІqS~ aZY)/=drUvu BFnq%>OY|#AX"ؠ;1N;yEnG$*Dj(;w=֝鶷3IeOPHGp]6!ҡS2_mDD_0$Cp@^*λ'FrO8x_AFӮ crl)^gkVpK8P-5Q@Š((((?!C'n漳ÖZ]捪ܩq漊! qҽG㵳]HPqK'ḔL(ly;T(LOSỗHqhMM>laB`(e܌uOeIxtUM I;ez9l7+9qqQ山d-1=@m,">wZri:%͸eyԮ8Fg,YkZlt6W%P>.lzA,pd} ַu@P}Fn5&I8yƭc)l\[%#E{TN[>¸g=mz؎ƨʲȝ/ XG{\]o1g,?{$Y|5ȊUZRsq^wt44݆=W,IgG1J}\8͑Cv|DV?)kkҿh*ڧsE-ypNBf ${?\5_+cճ?E5y< *.'GEҼ9evpL,wylN1I潶+(RF-ueTDTX=s60=OihɐZCgzVΩ>c[ 1ۜļҵAoyq!146Ƽ ֺCi[Nh=YUI4ӣǎZB7IqL_ 6 ՝mcY<`= aUfr~)7+2B1Eih6Ii\E8¤;$e V2Y~j$<{=zW5 {?/SAͩ#>ٛ/jhF7Z8]s7jM▂7W־!|,BIBzy5V$׽rv 1+1$8U }߱)J0OTPc-z|?m`;8Av^2v~^wm.pKGu#;WxSB#qԨGwtSb}BXKn1^hVXN;q^g}F{jjdcSlumxВyC\}GQt萝1$UI[c%|/_To@Z/FNQ3u;QTuqa!l+K<vּڷc+ܯe0H΅pVK#ӡ'*wgsq)z_ݰsӚԥ.o]ƹ>=}Ȇ)cI"߼Bv?Bx՘%$pCHI=kQRe%{;FloܦI7N?_|~i̛Fd2q ( ( ( ( (|,DI;:q$+q!;PGb$AG~"]֩E,RL봦 NզwcTRLm$c<&k7.fXK1?Kiim >mvp1Zħr\߳f8`<-59K{;i+9b71&c7}P$s,Z-lp㉇A=\f HeVU' 5ZZgtP(=]>]xɲ6ݍzfvkg$G51.DR0@bTsQ8?Rךץ~UOZ4Wep5 >ٱ?d0ܗg&(=+ivv :['?/;Ns7\RY,t0\I&$jkU(}8,uB76~ =.v)I8X'5ЫǖL:/:?EMr֏rEM4 Hx!ASIXFFUzTT4$H^M>0Ǯ̯g072:! 6 S'"j>'r^.QڳU?t;ybl2kgŸ+r8 BOr*񞯤E=b+9by??YJ1bMWezE3KY,d.q'hnY%#p;zrяTBCnX.L7_CٴSX׏aqN{NIl ޶"vf2'Ohնܫ<+V3rn5BFU>ka8ަ]--0{Axm$rT_ *I6[9Grxƙb3cnNWCc 飗Ͷ '+o·-vI`-=Z?S^nrDyd籫8;i-%Q#5KMV:K"?ITVS0oỳD7d|cmÔc$Jn p5߼z64\.;`/eb֚uZ>0x,&{ͨZه3 Ar3=xe`(yd,)ϭydιR>gutvi#< ;qkU@&acUrXsOLoT9v I#-k8ڱ9NQ^h͇CW{^g==Ȼ;w\"Q.mj\gn=7ZCKdHd]T'O}{ho.kMw4zb)$g鶷aCfD|vq@T*0-fRj{jqک_MӴ5Ky"H"EDw9p $(zl<_lw4SuXXS x W|MεCcpvs%,rEbp עgǬjw~6=)-Pg9Yu]4]>=M\ *+#T4m=-D4L[wݟ@#PjKΔI4 e DV?)joגi.Q//pzu)9D}b[[ldbwbG'Vi0AmKwWV3\'SU3_1~n,`sXvmbH9E4^T|Ǯ0֫;nV-{P_!S:So^xȤk5ZUY>Jyȃ8h?-XB#ۜ0rsڢ2;u4㽘>Ci>ye'89 +(G!Z]l'%f{ߩ\\UBѭ\fߏe=Cg埲^^CCZeG.ēfh@GxmQ@!YîھKsXFxX~@yTs{EgMijsiVjQ%ۡG#>G5ukK-NHզ7mZWp\Tu4iz{I ֗H!WdedB r W]Ey,QF-PKm%b;yg'8$;B÷[&jm|2HVPU[deOԦWqnʁ6ZxC}s^ooxQI;\Mmp`1QVW|W\]w EkVHL׾k\/"1`d~54P=(5;4]G˗SyQIY#I?xYGzS5 xXť sqo CH2)O,(d׭QG 15? ^1ŵbO@ѼjZ?ZV__Z iVPLwI(}HP K=Q=2+ rЂjK(3>:x"/)w8-4u6䙟jH1oLs^f8 En\db@ ;-BvPmG009qXUz4mzޗsK $ygk0} VEjyJ;:N[Zf{d83FQP.=:XUnۜc'?rWz2`VIg0_R# m;P 4rF$%CV}ָ%II*x%oQIJ?e摤oa!c!wG[vաj\Ȅ-0+8h\xUlpHGҭ\kiqI3([vP 66m-Z|Nƛ#dqm9'=+v7#sFMF '@zzdֲR<lPNbS\g)ܶt9`ٖ+eUcw%`w<1Cq"K y@{Gì aBD0qK:yG_?mS-7**^#J\K\׀/p sG]ZSW9*M&9+X<FNc֯xGN Q*! na3Z.mmogcE3V{i[.2TxZ) fFrOcWmHŬ.Ur@zV|Aed!dURP88l9ԉQ$j W])M'{k[t·|)! ?b@J$GӊvEb-Kᯄ;$Osq)G{~f=qUTx'h_(;yUQUd &*] 1x`pĊ3g¯m2:i`.`h&幖I_1B+%O<`ݒcO]qhsi6wsage l2@#:$dVĞ1Kg[:g'5 :;y"em$Ѥe(xڋ1b/r&i{罒e]ͽVe<OmbL׼7o췷Gr2R $NFS94{{{mVO>^BdYxg5}Tմ+gby$1﷒% {NqYZo+I6v;~aߗJu.|wAo[٭++vhAfn˽;VƓC2^ȶo,۳ƒ9P[NφM!tUou4ΖRcszT Zn/R*mfbt-&6hV;1U-msav68;Z!I pA֠6n֐\-̗7Q hT} %;9|7z2MoZ=+H`@1k>aIn\1M%I^Tߊ]>K]{Tmvhkoi.n]1'y]0[ZIWr%eٚU0Y /$}k~!&PM=gX* 9byAx3] lGmEC- @e`F>`Gq@<n%כ6 ُ+nĎϭn\LE诞`crHq ^m V[]^kF;!Hز #*kqR_}eWPX3Ĺ]8lאڜdcm[d]3bFFpǭfj~5HaytmxՉMgHc9glm""v24dm ̷ƻmiuj1spdR0 78z]?!_vvvph &H7j]7y SFj^hn>. Lgxqr2$y-<9v.O-,I6̍z7JsZ]T]ӮE/ag>Rb’̃Eu4]G\kȠ7쁑171Fj-&px|PwBՉ2;qFwQE >"iPVWrָoW^Ѽ] /V=rÈAw~BYJs ]EXw9OWw#?|bqnf(\σ?(,G\J?Ay< ț^_CFWQEZ/x~ RJx3Gj@z RWGE vTmSiHֺY[]bqbTt$E|DV?)kb$CR5ߴGmSր=TWіMs&~Uwոu;@ #AZK#Ӯ>YqG޽o ǂi8\i]^G>ƯG MgǓWHca$;vמ<elZ#sϸ<פi㛘'ن`#9+ \ݶ6^{_sukP?|? Z_ivryA9#+݅WIR!YFM{s¯Q.'J 9?6x] i+Xŷ{)54$qp8V{gE{S[68lncσgK& U4[/< :gp?(ܬs8ĩ]<ʶ|)! ?bxXDC*#GV)! ?b ^8jzEQ@Q@Q@Q@Q@Q@Q@Q@Q@t'?mPEG^,-!;O Aݕƕ݋Wh:,~MRu+\n>" ̣np6sWG|M[Ȳ+:F.ry \zWxT𭯘#Q61I9?0.vڅ:F s_כ%@,#a\(@;+O~z*\iYs4Q owI l^sOi&}jz7lbKyHEoc\|1AE`yj2&xN=GLbk5KDR@wTо2t;oK)ZqfzV"Kg ذY&y0x8?2Eii+{Om-qC2'uIO8l78HS/xj +Ѯ8ıi?7I"uGZ[Ü E-&9 t(/?O?ϯZ)"t5xV_;Z}` ڈ=.\-,y͔s޾7N58+KGTTQ tYg-ox/מ׆u[")`N0@ao`Ikqџze ̲N~}2AxSeRl~brbɷAoʭxSB#a+otՕqX%Xa8omc1l<(YIU+ZZ[h睧@طIZCm@|c;ALu6И-6D| bM7kSvZ[hu[j׷R]DKjG M2Vi tZ˪61_B DzF%NkSvZ[h.<g$wuqwʻNƅ#EAq洴4nx.%u[?tRD6ygmjokSvU4ܷ(Uq}mKx/bdwz֩iyȓESq";2G=O|skSvZ[h "?SitchɻBd$1i6MORTKe2dGˏ`A*֧>r g28ֿ;3bqlԭ;/Y,(rz;34 Ӄ6}nkZцh1 \ڊ ?v@0>vo=X,"`ps)xi38D Fg"YCv M#W`P(`絳O 8 H.mOʾS^o]?)gYz_N-8'XR䇚Md:@U Gydmu;ZZ?%>02\24ػiRs9 ΟQMUԼL3.9Q8vźe_l6?6p|ﳏzBR E` "hBQ+ iTT!8W.|mdC oީ.T~wh1- YhJ\~h[#@Q NC=XrEhv=xC=Go|fLn l(9} \} ^" +FmBLbWmf]/& . ?,I}01J>PYi= & pN|'+ n'6p[ ? O.[~u,Yi9[7|uchBq٧qsS@,ѐCeMn9qO0t |J8Q,>{߶MWk? 22!'̖tmPts/^,KDx܏KF*pzO5I蔡@C1) GfnZ\k ~#}L3zw?~q*%Ԋ*4N^9I1<0k*$_:dwC;PȠ 05 LtZ( Lڠ1X) '+/#KmeLpJL}O ٌI(,V2\_EN>< K՟F䁝?΁#K`?xBF&9*\}\~ pӓĬ'\.w8wcodʆ}q65wZ/ NuBשf2@3fw[`רN\05hFtᄱxI]GҼfI$Ld~]M=W\6 PrA>EߞZBw9 *'(6:bn?nFl8FCx!,f##j` F\O!-+[->eb SKj.J [esŧ{ob>fV(|t߈&B),:(+.%.hJ ^E }_L[Qfp\>kUN^WIuK36TL&VOUloL2SyCɥ|܈YMhO1ZgaΌbM(oyNxm&8Ӈcc\,nQntD!ӝk^rån!7*]h7T N_εq齭=H9kd[ JR%eRJɎLp~YΏʍj )F8!ÿiY~f~L](&џhz(|Q tL}dHq5!>U[f?y2@V~^3(NvrYe%8Iqb2xʑYx-8S#9ŁlBE!C̕tgn2O^fZyhK߳IT,BsIfQ퓱=[C+en"~BhԈBHlM.F^̥5Dd@Nad4V6*AJ҄BE=04? ;E-[e)lFֲJ~lh#\bgoW+JdZ(deB d8\_M^3y1"yLd U@Ռ/32t]^X{r zp;YlF][YK}%mՏ͍Ȏސ m*y$;rm^Ҳ0L֐ι`W58+clISJby= ƕ&ZL{Y/bةb* tyO%e?t274usϧ8~ob|ybJB.&O (-cw[ %fRXLQ_V F>X)|a `[6 - ֓DCGid9 ̅0+6!YYqѕhsNyr#?..'} 44g B;s `":\tLqs\HL4ml+!ه`dEeb+ak13x1>fֿJzS}kDi__ Y@;Ic#3iD3*fD^?脳pS*JvY\U>2%].`Rوƥ|t- WБ'k-30S_@% Owdpͷq6c}xԎ`;&fWۘm5Tҿ+84$3O28|l:#lCVUf[ o{-rrth#<6qu jW {IHG230KW#Uy`lO|Vu`pQ񨆺UBȈ!CIKJVÁTt۽JoTfL&j3@F"oܹZ'R2~[Ũ.ט/'#z'+SRfwʧa:"m8^-vÈԆ{w/$aqz/ Ù!OX& D@;C`Æ[6טivzmڙD,J\.\O'A8$5@|hCHhp)1 RLCa8X81\ !nK oUֻ 4;Gub*SaC݇K=Xb;5@vdSţwEyƁ(~w fb9#Q6F i SO]1}3WPjրEy=dB ֝y)y%.$"Z@4ƂT{1Ѹ5wPy 뽯IdC=2J ތ8}# 0e|I8d|iMNĊ%3 G[vCw~?K>ELf2WRO#A@yY=Df'C'PDIeb,`T.ǰkz`4mEX !V# 0lDy9EgWT3yR ̼cxneשK}1b+reD8-~.04BޝcvrȰe3aydaS " UW'Ia]h)=BhzC8:q/W2o6̀Iuxn8l>;W !7ih̰i-rྦྷa#Z+t7{h c\n Y/wЇ΀lPmsHk$kfجXigNGHCf0S 9lvLK &To"Ϗea4Ul!uoΑYe5st-R2D]ei6IT:_YʷKWF^Kፊ,)iY,q?G+'?๽ҟwᬎrDrnd4}i"j/w&'RhZ-fg1jg!/ ةB*㽠h10sQH"m(m0mA#^O^]@Mo.:Ɵ5 `2(`rl YҳG:ehyq:TJʙkعf;utj_^M` C/Mr5Hd~o+;S&BXl0#wsBc |.ꂺK9A&: .;H{<G{9%G|=&hoÕx7C;'3ٗ2o(wZfFB]\ pRn]g'LUgFa >l;؎ZY~P)ǂn%Iclۧu%HK?Aa姷渜25p#DuNP_1XkN-G`$b \KȨ &gR/mu̦R Pwfo`(flrSۨrN1{3;4Vr"y& lƐgd=MP,ϗF Fj4睅r~'# s`R( `hԠX61b ,4j|v|j| ]~i`ccp 業l˗!JmIRFс{=zGƴR*dW@745 D2jW-+ 7ű`6LđƍdCG AK(C`d(:nRߏqLmb!@ d騼@a@E}%>L ߰\ք<{ Ǐ31&CtCB!$8_j_Mەߧ6!%q G \S{S/-sc9uVs49ADhy,2˼=ƨ81< N.ư҉77aZN&s::([>Mt%+f8*N [fT9o@l;|/Omy~' MvSN>N3IL+N8i|l%K#H*pDL ,t:Fb&< 6p=EM=3SgtpZV{+=#| ?Ո`- (@eL|P^>g! L+n?~skr~,sRRKaH(F] 6!ISꂓ1*ddՁ*&ӈLЀ01/CfCM%)q GWS 9;>zng,_H֫Lm@鞅fBfx4jnX ,b/"& Y= v=7TNRbY1ց VVa]W5>5% *{U A$|+Ȳ96 FA}al1eZtX,6"#oaU]r }OmZkh WP`& b%§eBIJ$f H3-f+/ rb vvv)\ . 2YY@u2L "$ƋVP195E73"X05S!8&/aDMIԆiuoKQy!y4M{ۺVz\"@Q$y*uZRZ"Car&J@p^ƴݍm8t-Ě~4d S3|dn"U`WP 8㽟x0>qޏT1UaŬu go|庫K}_$z7+gF;G H=W0- 'NM7=liFxc;Vjm/ǒ ncafz vkiBa>L}J=e|AEr-խL=*KN$3ƍCgW/..}PBI"'`{7":Y"ls<Ь IA@*1ܴ GjAMk#-ҍxI e F2xle{1AyUk6ٸsoE K2raq1cߝw}ޝKʥvyҭqȓd$3x8niCzEp-[r/4 2TvW=w<WW#b*0'9$O͕rS+j-1KՆ՛\NzuK^2^>O 9Q@DbΝ'?Ym4Z}?#M}ȝ\L4`rrx+\*C,s9l\VaKT,bo'zc8ٸ1hiLc+J0p0lb@NxP7/NO}Ѧ7[ԂMtAB"D gc+6wuq Hw -t'eZߋtbldƎEYjQkk6Zƻh1 @${8}W6qaDTtd> cvu|-Q$rTvjl.Bwūc984}%@d:6عfƦ<\N D&pO,9ћmnF ) .tes蒏z1*{2ndXF0t j+j/[7XΖ֥~w2ߟJ f;vm\k\Zgsr^ʊ щDMS,:>H>4/"`o󲬧kM۲ˊ{Q(4NgvϺgǿgjĘ2nH#9]AaZ#<9T-?wm?m [Zl KanO'lqgSz_BJ5U4#û1;,|ʤp؊sK8g{eH[q;u&Rg5/YjO0Ob魋YkG+.|s?ohϼ;honG& nWhˣ見Yݽe^ֽ-"p7u::2)0>5z_m{J`foYzswSs˼ᛋ1TPA4!Lq|p߆G{k,&CO{{sog2hYʇxÒa6$񉍌:o'K?<bRtIME :.!%; eXIfII*  n|(2i%@gThumb 3.2.8HH2015:05:05 13:58:4602210100@@Fotoxx:paint_transp|clone|NE(HHJFIFC  !"$"$C@@"7!1Aa"Qq2Bb#RCr+!1AQ"2aq# ?X$3'4xh"W]TMXR~O4SAqfTBPJu4ŧj6jkU$r5؀sWsӋ۲ɷo Zaԡ̱.:Z'PVބsǭJ%oTysRO._ES!76S)I qL$:ⲟrt@{(j-!j+(LwH5ú)JѕMR= ^C1ȏ."yyV- Ob;$8Ɦnu*Ңw"~> 6l}>5s0ձNi;Heקּ[Ni!I# xfgkQ'\w:-9Jp 6@楫)zq`EIáRs m589! ^ďfZ@r^o&'#|lh{H/PGTB:'uD C(Lnm +NGOtp1 U<* Zb.']&e^ Az`׼|{-M7qI?2N^-N[^;_Dg>~\y[v2$em""$kqE$%'Bߊ\mNdqaH@ȥeohTFd4ccee BTpIN ROALJjbZY12ޭ0h - ֶ:g~[BA*IH>:ͻ[QZ}[6vA L.ڣ(e{+i QBnii,= Fzxj. Yv^*7%DP(qݒM0 WUڈsYF4^Y~t\ $Os^ TE~jÚȌr:fE=>Osy0#pӠ J"<SIԍ2r邓= DJ>xIo$# ?|%:&SQOCʱ(n=`VJ@HEn[[Q<%H\Ėe0Tkieᎄ3o FaR:N'[Oyi;)'5pۯ\wbulRC+{)wuG1qak{v1lGSV5)L5/ðqcMdQ9{ۮ)-S9J'K $s*^QJ{.nס GٙKt|: 'QzMt6NNNlV[Q8{=(2B3Vgڛ-IP\{6sC-^Vn;*4|3[1 > s=5[i sՊY9m_iTXtXML:com.adobe.xmp 1 2 3 0 6 64 64 1 0 ,sW`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-20.08/images/alien-colors.jpg000066400000000000000000000241531362435004500174170ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 141 173 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&KdUU,G$iV2o"t;@fkz Ա$9Ҿ2ďLt2C,mn}Z?6ނ>xX1J_NQRRcmT" du#QˬGIFpn>loO5j]d+3OLݲk.%lm'B$u[Cmr?Q]xLRUR?1,NS dxYO 9;u|7tv:mKa=6d t8'I חoCq_B9?''^񦃯'։˧LzE1U C1# 8_izOďjX/ŤvqE)E.| zv4 [> csu5WÏ2e.QW>7|o;kMxc^Zj>gow$l΍ H8 V>x񭥀{c9kXB#@؞ru!G_Wʞ$ޑ/ ];.iVݑ'Frsa4s/5 -su[hW6ph$$e;_Vc#+[yHeW}jzvn3! $Pw*@=pzTxs4f $R -׾Z6Z)#ohzW}GmȮJ:X֍= &cM 7 Xx& I#4,+E'?g")?OjoZQi%Hm^y5?a1"G#96Zc\!M"X5"k,fo3(`;c| /ӥ4yOn|Ae@p3vqkoDMoiuD{yZT :0ܯ|?.gl೬X;[Y5H3wy@U 4MQ[G٧yLA!dPQ~eR}Ju=/:4k)eA+{x]$%Bԝ8<ֽ[K;Dȍ 1g9'Wzޗk:K;5lj,&>VqZ(ED&RIyVe65yOԬ!xMܙ E`` Jz+~Ɵ_[x[.aZ%Z³ķP<a7 hW¾$?֓zݾu,3ELC<زT/=ƹn 彅T.S t;r+~,-.KVJ2h36( ( ( ( ( ( (1WB[g["&UwX#v z[g?^&-+:Zj6>n,I=el 0:?YПv_G,O׿/Io&k_(nBfl"o6w|H_jWzyX&i 0ݰ;zԞ8|i֒{XY,xAb. O`imLֿ$Qt̙/HiTMu{!roE6OBE\EPEP^q|utW:NWu֯6P0C$XQX.+W)7{&kgxV?V*OrXW1ݵYL[iji_:A4m?Z! Ģ%~^xnj\w4~,LvIhI2ଘ.Z'!oyECWjFF@gw܋^{tgB"?98=+CO]ZiZ֍Zޮ-QE{)cF$w:5`݆kW֠y:M\JbtqmOVlAyo |Ik:~6mo sQ)y\7` m밽뿗uc.[/^/'i-llC @L DYiڧW9o'_!txm^EӋAeuݐ6Pρ4{"_6wc>5դKx>Cd' :#~7УsA]k:ނSKݐAP0= [y+[ 㺂9q$R(tu9 "|KI(#mѴM>1=a߂)8 t6KD45UUB@Uvo~sb|._`D嵼ٿ$R&dE8RߡQܥ~6?=46-,u-+N{ 4C$RDH2 ܁~zߴ78ji>& vm$;XvE4dc9+_=;_ּm&4 e$.ҹr8$x1%g k:]j%zAJEI(N[,wt_o_uuwHJ@69֖|' ˏ_TXe/Z٨(([Ir#ЃЄ$w+U_쫯 Ң3{|Aʺ *+7*7n쫯 Ң3{|Aʺ *Ҽ=hz]c-j lT*Zؓ$쫯 ?3{|Ab-b-?*7n쫯 `3^![{E^![{Ee]fuA7@Z l'W`وUUf<]쫯 ?3{|AEfe]fuA7@TVrw*^9RZ4QEQEQE+4@;D]ZCT8s|5J#nm$a-Lj.$z=V|} =-<54д~WҬukh3mꟿpv[\:7oc7]²qǭ2D'"zo?uyk Ow|I|'9y4[G*ڼv֫ hdvʰ?!x~)%kޯe@]c|[tK[-jqi*Z 80,ݘ;)˷î㑜 /mVxOѭREB_9VBݺKvWT<7ZA$j0y$/T7¾:>t^xOouJxR8 G6576p`WojZ躓Gg5)䵽]&xW,fEt.FY؀|9t_[U\\o}#uH;)`j髂QBF*>jd|0*J ( (9>g&v쪗11I>¸x/7m?X%DrHaxϮR$'DY#pUCXg$kH$64/xv@ÚAmi)ucOzk2Uڽٗ``p0 {u|-B֏4½ Z?EѨ{umu} 2kdEȄD`$9=+{B1 ^O*<| se~"^["j#Cwm7Km"hP05|#~}*7lRW%|7>x[DWKH덬ãziʭ+ S{o]zn99`2{o6rKoKex dPDCsw|-B֏4½ Z?E>-"; Agd|&A?+;K擡i1H}vZ4fotoxx-20.08/images/alien-colors2.jpg000066400000000000000000000546201362435004500175030ustar00rootroot00000000000000JFIFHHjExifMM*V^(if%8HH0230x0100Fotoxx:zonal-flatten|cartoon|retouch_combo|tonemap|tonemap|paint_clone| Fotoxx:alien_colors| Fotoxx:trim_rotate|resize|NE6Photoshop 3.08BIM art, fotoxx,  http://ns.adobe.com/xap/1.0/ 8 1280 1920 2 2 5 XICC_PROFILE HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)KmC     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?_: 'FIkmQYYd/;&vD/82`mo-S=;347H'93JWBFRx>"Z=ygqb"c$F4<OW^n0KZt*s|# ZNAk !J9]>PTlExoItY'&&yp$+Y؅bMe˖qdzѩW9ɸFomntXP1gWw i%ܼd^i-\[?9W'/uVBW1Ǵ~k]I54%ƩHRi݈8I➚^h\I1|"y77Jl d -cr@I?z IisQGSc265I9#ڹGV? mV }5MK-W8BI?3Mi|_𖉦1y{k*Hn '$"? |Gm|!wṢt%3Zqg, 0Rr;s}uJ!ȭva\ө|">tZ2-đv:WśjiPi Ӓh^XqorF#hz/<ҺJL-,{]'Cwxآٲ"A!Ebq=lq}gLWً.O/ }CI,8XH.;ݜrә{k{?_%^u[9k%wcq RPv~Q:W ) d琪r\>n;Ft*su/UkֹCݵ3نs=?Ӓ[AϦÒY p,,*?wiD8o} N> ;Xo]x1X J#їX,.ıbęAzm9Ku^N-u=b] 7Ztѥ$)K*yL71eه NZihiŞCgy$|(|ԟJ%xx Bp{)5װnE.ԙN38hjl\ʭ5XZ8vWg'>O,eTVvX##.s9b|S&XiVAf~ )끔D~>osE3mi 3 WrUR_ƽVG&xXR+;PG~wTV_.mh-#7w*'"6 rI/I⽥:|cMwm ,xBU$4FO$?:VnH`h@8csu5~!lLbkdd"Y(F@zyXgRIk;}LJ>",y6G1XBYA<ïW;ؙ˦W/À\gw!XpWtZV}GImx$ai)#/ 7O2> u;~_w԰GkPa;n ^⿅%sh6ZMe+1E~\ ;c`'HBCjs׮yX@_Zk[+}%BjvwBXePJ6)cWjZ牙ZP-UClj=z.\7_V#t]_v~g|;^oSiAt?9Gӆn e~_U׍FEGK,Uq}< -l5}B{v^fIsϸ=k~4K6|0\Ŷ _]?PYL3mئuې2C/~_#]O<5hpt8Åq fOҬMa(ª biV2{%Qu<A;j Y6)pZ-Cn1Im|&֗*S?`kVs$w7-Q~+/|IÚM|_2E.mA99W=Ε\zFYݣs' <^iݙ\$lc-V!9"D&Ֆ> g%⵼yXHTӣHc!cAUy֍MOx-fh◉"}ǡ٧^Pў$G~(| ް^Es>(yH{ '( 2QJY[-Uk="ŭ2c=kϡoZ|]c$f#9rG]'M<[[X2Ԟy q9޶mWMDyn r}gX Nɻ|ghpp#p>rwg,Oc~/>/S:ha>&پIVP>}inpXp Ox &ʰqvGV\OTYT%QMzҶJx_#t:xkʪ{$qSvGJN[{cUێkWJL;wc%c5>"jSj8EN/txwU?ަF O6NT1$4v3p{ת|#֡J[8BAltX]$0%/i-.ºuQGuKeUs2>ZλЬhv7jVbO<5-3NԴPĪ~9r;`@<o¾މHFi-, g990=O/܃i>S:)Vݬ{{cOq\h b5KfP OdOrIWwc>4(vgQi" $~\wi_aA,PQ;S #,*UH#FGZexX8*={mWp01dvn`E1Hb0GZo[gQɈD۞M*Oҽǿmk4/oczim*w ;>~^5Ƈir"2[4 Q=uTzSוa[]>G w׊:MBC֌rv@G؛[ƚn[>MLypWT'I D>xm[Aca%(U@30SCo=N61Vq]{|A s[ n̉eT5$g|z<R-"ApD5UG0kwFr+|w\Zjw IWjش$z?0P~v >4Osڽu+ZΧg3yj-k6DбOGEiڴ ?85F]YK_ʽ;9AJ楮?SݤŐVAcPPy8?*f6+l)j3,iP՝ۺA`rr[H͍b}kGMGitD:kLjstxY >9_mc/{(v dms FrEe\_Z̦eS[z=sFS ,8BtFG֞_6j6uPVφe:ͻXFktc3xAJEK}3QT8G=/0sV{j.q'͖6KFl/<eqKji Hķqʂ3X~/φ+dO ^]/cJ ҐBOه}ft#R2|l}aW?kxIoǙg% Sk'QqZKv+luMZ!~'ҔyNc |>1>>Zt&7DYNG}ٶ&L:_*T)UK/'M76V?  .}y9bxH QxBGҦlne[F\3=+[sE m@4'vyчVG//Sx|bl?z7X澓kNq_kO@> mSJ3EXžvgODd~LIID*kė8k֛#Zq,1& ]_z(q[-NDח*(C,(\DgԵ}: $ī,c'{L!-mg#?3~^]\L]JUp;s5Q.Y}&k 1L<1J[~Kg 1{>dURA|Ѩ[ie%"ce8@s6fu"~aO݁_?%W%guJ%.?S:^E%ٶ!9nx&/x—7:W6xx.RG:<iyzϖ;%̋' o$wu F#{i,0k^5#QGwxLFKxTrWDX.~u'O+ enN|ėhؕsD2n|˜gp\x5[,0(}n>7Ф]՘aeїLw{; [jΰB$C}}+4Ohk%OF>>Y'#JZ4`Yy\[ic~oXo+gq}wok.閬lq0+b@'n=:GZ/$X˸'4/5^4#TV;d!h% ƝjއZ4n g~#ԢIVJaR,Zy<Ǐǖ0T7n/c_'|{֣-|?6MIVyIDGǩ$L_+Ƨz>71i⥬>vؾ!<C4?DM.9?|i-#|w@/9cY2 7 { H#-طI.-m"iϦڻ ?֧{lxWY 4#z4CRO#`hh#l>a$ZkŽ5ymX.eI[lkLO*=Hp^2~mƿ3죽m7LO&4kV,Q! $F ;OW]|~-ľ hHo$MQ4(CfۍɌ9T`%N/.Nfa$KOE$],J1 ~WSc?ihfנh.@/lcP)*.?د}k^|=\{i}i<9cu : ς:6(V9-,3q_?a}aׄ}ĬSX Uz^>,=kҾ\{K[e/pFyRQB&I)d?W5  }HS%FE4]7RẒcsDZ?kZ"U篭rR]@-$t?,*^I݂k̨3rU&ubxD[ipDfb*6yM9e| {^՗B;nzq[]r+1TlSD7:M8\JloJo]Ŷc ~e'?Z޽nye_r jV6ȑ#\zќ{rR$M3"FU߇Hkm08$H$(-zk]{SV0A ƾQ?MӬHfQH1=IEeؔC˞1#LmwQ6$p±8# w:jkz$|[rUx?3g49S^ݕFrPFqx|?vk<ۮi€[9]}Wzu'nS5 # 6SRpxӅH]G*M bDT״^Py{);0T Ԝu!WxC/ Fi^2 /mƭ^OzG6W'#RBI%q%}2T[3#UM7W'5JvkeSxv~ώb Sֶ mi#eC 2|Y^ԥ<]IzfشWŴ7*ǂv`d}c$gtV{;{Kg6٢QjXFDR6ꯈ>Ed&y?tWHY`ol*Ǧq__,=Ohݿ<<0e wNZ_\톁k tg'w-c,e̱R*[z|ĕb!.՚9Y&H-x*a9??\wڜMx^jOKZ_x"RFt6g[A 8]ƛsy*QR4V6W+q#G9?2xZԴW=.0؈]~]ϓwoD׮,=ơ#ZY2/'18?Y|]:= s!ԉIU'R02x8֚h) A%08ƚMώ]ns}wa_QFMF*'LUOq7<=?/l/u%Ni@"Qs~{7}BgV4lUYt`O)|'y}(iROhc\fpYv%@rI)~9S=}Fc,Q[oHPWi#Z{/P7yyjDI 6ú v`uY4oZn^4Y[p1Y{_:E_cOK"8V3<T)~jk⣪:ZFq Es sK,C2Q;d'ңZq< |-;hσ~[}SMl&S[.w )NsB8]eO|Z|o}!/EmH%?gXDs’B e5KMc@OePbeF yz_|qK e𷅼axOFd}JkS)2++@8A^)[揍QnU"ו?<ž׼/QԴF,(tudtdlC ~~ÿ/|u62XHOrX,RVLWڇ_^/[\y-[oo""3EyBHY[Ǿ3/M{-Q-S}2S`U4R>S͖7U;1_=a^}v5yiW_<_:{[۫!I4Ma9[7;u\p㝲H:+_ 5[aoHw<ZkQHzWaTޑpk ?<TuAr= rFq\Nn5y`Y]6`?S=ij1(X@!baΉq%VR_Fq˞t"ݽL6L̏ۑطzVEH3U_\]+]B#9pT}kU`U@dgN-4_-E$*I(]۝ϡm]p2zp:Ǡ^+FUn|>+*u%d`|F|Gq*^Aq EXIe0#"`<ǡPmuMBʹll.i3Bı >{ULƶ sӺ+L |rzk^NGRJ4s)4]2T]I܇QX3&=74mb˴䝣Z5>R]Z/Z.^L/d;JyH|ީx$F2BW'G3)LU;?3?*P}^'ӧhL5:CMaw2~xھ9<a$c_6 E`YHX⽌p_>'N41U!Mhv6l ļf#'z̻3Weg_Qdgk30WGJ% 5ml̏fotoxx-20.08/images/all-folders.jpg000066400000000000000000000323221362435004500172310ustar00rootroot00000000000000JFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:sharpen| 5http://ns.adobe.com/xap/1.0/ 237 300 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((," }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{Oޏj.ѺF,Mj^[q+Rw˷gsWxڿdK|#2p+voV ׺Z${PNH1?:{`|̈2{A^M\\IEO0h58h e:t{jeu (ܹk|q P*CdV眂:V^-%܏`$A~7go_AQyoɾ"i.>͋ѣw#z0 W=?o_隝܃R|Nڼ0way秽 ە_AW M5i+ߥZ E"26W#,HqT67;kż2،'hfʤEy_AQpPxQu4 /t{guzQ"oeCsuSxY )5)kg3fU@s4\v=K+?*24=-:ȴP2˖ Nsޫj/յh|CaeMm`'6eeI!F@87mXXLʼzx^[]CR<W sZ`fuح`gHR6CVg>y̯ʲ4k˫*PWr[q0Dw s̢[ʌGE_AUO22o+?*2eeW~Te}U<<.ʪyy\ y_AQT((c)VU A2S^?VD@R9lveC48Vʛ]8ZH4``ן³ug wXy|d #Gq;;׮JuYZGf<4<.W_h(q-brIrk$#p|oNd~~g?ӥAR,E.yjHCh?>O~3~8ӧ8U={zn4M>kۋne֮.E:y6 +ح  \ZYxBM,Ԩz>8<owGy^/$ifX_٭}$d ܜo!^h:kN*D,3mG~k?;סilckMAЬ%ycF-Z~gK;ף<owE GRg?fD^ggK;ף<owE]yy/y^=atE{{Կ{z?YGRg?fD^ggK;ף<owE]y |V ^140cZUk?VC1Zq*UݽbJm1ln5^Kgd/1fJU 9{'3#Q-&-it,:3gڏx>YWVNK?820u(du>x~K>dQ!dl6я)wӜsT5?jk IZ`;v7@7ʞ0jӴ.:yHC34hEu NNI?}lgId*@t#9nD =)|T O/NO/NO/NO/NO/NO/NO/NO/NO/NO/N3x`8p>u|i==uRiU3ZnFks5-٥i;@E,F'u$𧉭k"XcGjGǀ*Kk[5Qxm@nbp1kK `ӗH TRܻ}Zw!}kyb§~0h׵oðjBhpxH]B>'$8HcdM/0`I\KU,&m1_GId VƣG]e@͸i"G*6N034'fBj)Rՠh(έ+?S@ݧ5ig 'St C<~Xdr`bT a[:5]_LX{!d]Ub"D{S-ېrkzvr>a(Ē,{+NXrIIO|?;{T򠷷䌘9tb y;=y 0F{][[YZ۱T + >aY ї)y>g ~׸6D 5aHީp"dp hk:e]麔^um+HR r>!j.yqoqusveɶ6EUH#va  maYw7ף41[ͦ]w l* אs v7~Ү xo=N8RHW+xB a0 mu FX+'9f쓞s@s~*Djdtsl2.xyw5ڽ:N/QXndfV*0#sZQEQEQEQEQEQEQEQEQEQEu-*m:>[Tڈg'Z~nY?JVdp+7C^"c%rX2zOz\Vmpp# ѯ@;(y㥵,-.纵s%Wd5bxbHn#IaJ::V=EPG;D+ڪ|Mt4Ir5K M̐71g!d< =.+=2 Dp[#2rp9$5_Zx_M{l<77"!y A8Pۉ ֬Y]1u-B 8 KUeh6vW/\Phl/7x$ 09yh4߇Rh\[%v̮veQwׁII<`2`_Jwb:-{(((((((((((((XkA9=*hK iUg*1.vWOqaEbW |5:4H+̤ }FA}"=D!%6Xv~S5iyn]G­8D(A۝3:qZP7V!";ih=F;?8S]xKGK"J{EjiQjF {5C29L2ǭy捼Eꖰzńh~cU+F*MEVZԠ zw4ib~WYm #9p=_G״}iFtAY%1N5^wxVZFH a Kjs@EPEPEPEPEPEPEPEPEPeTCY/ϤOg*TZ~n(((y8J6%}SI4O}L}LoQكW*@<9*B d(}1m'\WѯumFk;S=ſmcr3e Ϸ' _Zs{kSڤ )8`Iy0A5f/yàR^%ᲖR,,#2slWmYGS\SxgVTm<;>vӧđݵY]r23sV+\4}j_7jWC o,V"_]،Qm ;kLWMvdxgId` ZU Dv7)(#%r]3I9$0 ( ( ( ( ( ( ( ( ( ( (/ϭ ?}&2{?V꥟tQEQEQE88{f:͢[hҞGSѨz*Qj)}='Jz5iOF >ҞGSѨz*Qj)}='Jz5iOF >ҞGSѨz*Qj)}=%S gէFF6HU1!p%x'ٛn§ 3Qf۟?>s|G}*z(7fo# ??Gٛn§ 3Qf۟?>s|G}*z(7fo# ??Gٛn§ 3Qf۟?>s|G}*z(7fo# ??Gٛn§ 3Qf۟?>s|G}*z(7fo# M6XeȰNge\sX3Qf۟?bP{Sz/j_ϳ7fo# CK`zK@V `5WIWHYAPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPT/):L'w^.$Wn("x #{qjuUmc,n$ 143 263 0 C     C   s" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?! n$X4,Iyk"o$ml]N$oJAn_$_MGeQ*1|I~{J1G9Ds r~ b9# >fZ ]bp7m"}K$4x3sOZ޷:jgtSa3K@ 㰯R_iQ< MB'm[d( Fn~P1`gsI- $xegR1t,xG4KxcIK8Į .%J|qr9,}'ߍ sK$3( d8 : Mlu}wMI(eC~!xdũiYmhA ٌp9"}hgjmޛ'y%6ȫCZ?icbd``ӵ|7O῏>-xF<=:݆mn[y&+gԌVo~/S_<oEAsKj [gfG xoBЖA7q׊zJ sUA_Y&#XMnnHiSiyyWk9-g࿆>5X~ xLҡ:.+O>eW$?!p1$s?b2_qHeH| JPѦX6YMA(eH }+v/ kI³zm֕qwaMJ&Y#9 m)݇)m]^]<0[F+FIǧ5i1lUҮ q%ۯkfѭ|gixVX$hv2$Eߜe Jt_"^B>|;qްvE 0EG]%];xW\pGۓ !u~Ե{}KS{GWSx>A}xCq#(r8}xK9N<GS$' Qץ`xӅi^u'Ah˫]y?tQKX5UŜkt;\mR|WmI'9#=kvR$q?c4-wN0`c?'^3)Є՚ꪖ'@OK1mIW}}RI!5xo?ahױi7-ŵ`22:c{H@?LcOέ 8߱_/?Ə_/?ƻ6%1~nįPcCAqcԿ_cԿ_uz:6s{7-i8Ak^#Ѭum2/tEŽ_vX ARpArڣQEv{qQSȇc 0Lh'=_#|4n/]RмW Fa bB.,3"+ Wb%2ެa}޾~1 چiis/n4w+G©#|Ngc3{#5oCW7ޣ~| =jfْFb*dl<ğ: b³kW0hƕeiZ\i1iټI,|˷Wj澃x\{c<įDK`g;Ug=k{]WJ֓]}kkKg `PFpzeʼnywg^?:ޥB&ӗ\v0Ž"iˉ2zO|7 뷾75;QS l(a{O ;5jWG/tȠa$6ȤVl3 ^{Icݒm͸` cf>8ۇq8Wq㟊v?@|Sዋ{8mQJJfvG2hU ã`#࿀4tVkh bbɺMگ <j\|7j_\[8*9-L2x@׼Pn<;=4짗>P,>\)w:_|gkgku-ŎdCm$Q![ 3^ |dƶIh&)VbCn`̹!F[O32*x䑖26G* *F29qщc5??[jI [k7DW)]cp܃JeJ+_ZtsOᮁol6į5u]@yQjCHׅ_K[TN„˫q5:4_SχP+Z$7)٣l܆w6zBҼQG幌H 8>VO|*eY.`k 0)+1̀0k|zs]fL|y*(!PHd99I$5jt}ڭтg|na~Qi7>=ЧdҼfӛJtBK=#Q_ Ӌmj-tZwP|@?o xD[[٘Y,eQ+Rʘf'9 o.xº~?ǁ-|1{PeO6U.Ab_c,8Gú#u >%wyx`2IF\ 4uSgh~V[-/7C/~`rG8~}^44ۭ)_Juu\Ӯp q^sVu_@~#4}^hn<+`K0NEwox|m_ٚl}8(Q3m|M[kNM>A~=[/<3'fr4iZ׉wqgvIcח Ynx2}rxK>4Х|zFE 4)Ȑl I)8i3xDƱ s_oϺ\yon ;^|7fb/$Zn/cc|umƟt\jzeلSɥʰ+ .IHq|16G5ȱWIYؖe+@V6]t_jwi|c5l"S=*dgfa¸K@|6Ԣ4N+/Ovf>NF7(S炾"/F}>R *YX8#+[?ᶥS\4m-Kuѝi0$. zS8=<}ohRxv7::~Dz#H~Pߐve%pr֒,`)G[?? S:\Dڦ77%iT^gE1^waW' W+-oEt[6;h'ԞX}6;_pܠwG}Sp>]]3Fc}Ez_ſy\7"@Ϗ.p 3>爵o|)'4NmyIPDeq?{O:͘{ |χ-~%)Z3io>mnw|FHȗO>-ևM%_2m8xLG)$3_KmY}ҍ^~Z >VѾ|C𶇠:woxj};Kׯm- ]1)6HUfg=şj>^-kXëCxwT?弹_=:6v0*pw5o'ឳ?BIWRo:edaX> {i:ř2&)؉L`1` OlSƍ//tOiZ_C5Vgma#C#ڿk|G%; \B cKnj 07(澋GӧZrdBgl`u>lᧈ޽(N9`Dzc+V |L_mzw>/M&j]H0Dۢf|RTd qkvYב!XNxWO,ɵ_kwWSIXqd|3վ|CaC#I_φXP)LdHsQbPҵ cG5u,q{Wg;gk)ݼ54vA.i$Aq4X8o*sGHK]Wž.Mb6 XỶe.AbdFj,q_͙ȿ_k3+.j:rxS~֓VGC5ҼUMy&f_2EcBI_D>"?odk?v2≮8|9_[Zi֠yG$bV,wom< Xi3Uγcq4V?Ƥe3UlqŏTlDI?&,sE8@# ?.{ e(Ͽ?›E;> vO8O*1֡ǥ:Avd]xL*ũ)YiqD#W6Tk^Ĺ>|;濼!8\IqF_TlƶA=rL̻),R!cqI.dI_t P>"i?ZûYth[R!o2-V5]5sj:仠Q跍s=~0g\aa6h:VV׶?h[%L?4Fw cɏ?#=ۆtlΖX[Wk\Lʄa9O8M_~i:Ʊ}vѳBifwۯj~QeF̳#d&7ERԋ#քdXmi@o_no)e񮈚܋15~GT~q'j{><%e[xvy1jƶHN+x#m t.) @I"..aUgbdxkZ/8[MU6إE r;Ǐ6F<_LѯaSE;;8\uy@8r~GM>6`֏I?N|RʟAtYY׉_2fyYUoUϫQAG߇ugcu{4ڌ :Cz4O?[Mmi>Z K|r{x=QcP-I)ޡVR=Av⿃tE[iv$K-KrT qgM6-g.ӫ~gmipO"];]A#yEh6 Y?/V 0YG yd,qvk}Ԉ 8Siun6^zPZi%ljaz|ǦBӠ1l ngRı|9"aKM;'';+X񿌟 UssA|!>;K-7]I--<&$gC*'uquվkrK)]>P@ Ww*@߄?"YЗs#'9_])8,+xW5N K;R;(`f+:& Z<߳^0&qW~Rx|7yS햷ߺ1EϹc=$ikl }gf1;ӺXf棢$GZy0eé-]㻏 u''8M;Qx6I|[uuucV)q dd(V ^C |CvN0mR( xOp9V:#>8podh2> iiODE T'iWHzHAޥ}.~qg6rlg><.2i#T8>Nr6|+CJuRZQZ~E0 <ϵ>9c=MR( (֝H#(d!>• ~QU 4ED&hTwGr2EO62XF3(([-Scx} PA`qOP3~TQ@ ?E80Ҫï hEPfotoxx-20.08/images/area-rescale2.jpg000066400000000000000000000506351362435004500174420ustar00rootroot00000000000000JFIFHHExifMM*JR(iZHH0230G0100Fotoxx:trim_rotate|trim_rotate| Fotoxx:trim_rotate|trim_rotate|resize|http://ns.adobe.com/xap/1.0/ 600 1000 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&AsWEH1^B+G+p⬄Y>mTTzUc5-BVܗhK1Q,qA9]٦F#ҥԅJ(QHi[R9w#EXjZb[U[dSn 5FĊgq'y5U$(={]fn'oj)޴C[^E:Q7UуGhQL|u$LЪQְ#Jpm-˅:hdXNPF3 iG2"aW}Ks2R: C9¬s9[m ydX^K7jvJFgN83j_m; &;P$`p ҸMG{=ʹ#Zud4Sx_\xƶpDhv;w5xRvNv@}qֹ?K4v0LdeJֳD|mՁڸ*;%b,åj&Yc*,b8'A޸MwW-5=@U8vz2i+,Q/,*8>ME!1IzTA%&k6W{B2X:śϜy5x_.h/d?gӊYP`S@iɡӚW]ޖp;-*mn(g6Zi^A:PWrߘAZC9>)Qj4W )XqJLHF1g>1C$@nz_-UxbS^I$~剘9+4Fԣ<0'd$Ͼ<<{WsmXLS>b8pE;RYdVrI>~"EC-xG 7\ߊ眵MTa$r؟.Lb9p{X0 \q 5kl][wI?֢&he*HqO:N~D')ݒdMO]+!-Pt K2$@@8%4A۫Լtbvɀ@$ \r}͚Bi3Mc]0j$҆"NM4ojVa\!Lj<ܜQaˑJ YJ>7BhjJ̷rTVRy/Ce!6g*KJ7'EAhKg89Ÿ$sZWxu5&A5pԙPH*I"$@EF_PȨ oLރRH[ҫW%s?h<<:gpt2s\[M:䤋VfZE̲$K n^YehE#x-Ar2\Lխvmp1TNXn$ךmz4E,X˜ =k լDDI)r{8†gR9v(ۃڼłMbOǽbs;d7sXٹv#6$k LRe f aȩD걤x|3ȪXN5F<iNgeZ ,3['>S_y|w\ 8]X0 PU1wIZCK\(L-osyVٺ`px[:ff6S(q)&|IN CQqoT}Y*,=%UHI"NEJHp؞j.US^C]Md_NF0;Vu&kVv_/uM:7QΪPҼ7Dx88&e'R_~6{ԶHhIQ`Jkάܤ:Cm"ΪD۟jXdkmB0]SLw;$i=`ָ)ue݋s@tZgwc_&%*X$W$2;gr]k.ʰrN8z/ۘ<Q#Ip OQ;yqvCW׀)NJ"|ƺQuhvr7. f1T95> ab( sp?i#ӣkE\E#|㯽TZZ4QoS|i6LXç7SA\{ CΖV7?Fl{{אSSkIiu19A۱]JW&Q*GVSgX^AiͫA[d8xGqܒq}G/A%?+дۮ_3m mHHwZ|7v1J+BO)>cz+C+ 2h)/tViǿX֓Wӕܧ*}q[ \]ԚjUFIxdEˮ"SK;:ju;[R3Qdfd 3i Jն#H c#4p($/-T_9W/eǵRdf'j)!u^r8C귓JD=;<߻c6A f*w 56=ݸ`򪁜Ϋk(P6$@ `8GDr`eE# kHFƶ֏y򌻡c+,=ήɳyfy<,(V8wg{(O)F d'뗹ss6XmTI$Oedk'q'HBҜ#1g.4dUr@ȥ6]Z҄s4A,mRsZΝHGxz;X |=3ҽ{׊$a1^H#qrs\xGB$Y(HqVUR؍?7@q/AZh6f;mܔgȭ?xF2CȏFNSB_B>5G^2}Ē䖇j/E[)iV0yCzֳ{K]ϴ`(/T7ep Ue;:{ os~Ezƃk"OGq_5ܸ2TzZY>->9G:qNr)F~n V&x v-ę]SjqvgZwWEf F\UlqNsB2wl6HJ2\/p)"ե`$,u5 [jGY,[Pazb\{TI%)%J$BC(u=XʤDF{֜MY9:U95cVcǷir vu9‘GSZ¢[Ԧr+kZͶ^[xSgWH>cBz橽Xu9L x[u4JΜV1۩>g#Rf5aqYʫgevw1CZ2,[Xg+cWU-OcrO8"^fs}@;\ޔ:ck5*ÿA;{V92 qXK 6\1ί/)d L5Inv({ƁCutс=v;TZ7*QFC9QmlEQʲK[5)Z)-ʽd3N_0 ҭۛIk*H=ޥ1DTg,͓#gdRGM BhD2)^=@ $QNNI\YV";I!vp})4k.O浕%ZG[6pDNkZ/0H j/0x^^ ٝJyocX[+Ie1})sY/X(BC9#c]ܼ3/T<چ%:O$-yrB7:]$wN)"^3 7N#!0{W̚U~c[:n6yF`rx4N14 BWi?/baIlI &{:װOXŦ A#dps=1\.ɩ!HTqQiW֚k7ޙqVUƣut$2ʮFpjąyȪ ӤXu$mbryȩ'E(QD|J3UbĚv{"ܤC$X#"2@5{f 8̝EH`AmIX\8'ʽ':V̅bvxBrTRDOj58<)$r~aמ6ʐGl͚] J$DŖ$=jM.IEǻ\4r]c:\% :!a Z,O 28׍ꖤ*,GRq"O$,qn% drBbʠ Vv1;>:1CV/lfvJ'.M]IqtQ ڼ=;e={UDiKNtҋݺ,Gz:.hb֏{q{6"$d #w1k* ::c]M0CuRO'ךMpaY_wZr:Bw7ٔl 8:}qO4H.pʣgO\w5Hx?0%GRfi1pASkݣs.jɒ^A co~z҉ >VU wS?N0tH|[涥;a#=+EAPC ?6se rM+m3;6}I`U=#P}V(BY5,r2?$9>^r yJLV4@rb{ֽԀx䘪8ln:+kWF&v(nשs Kvf9끏S%w8<z7]2 >eH\cԥj7%8'jSk] ]2֣I$C8U-w\\LEL@Pjgi`⑧D[rFkĔخ<Ē# >eAfO"_̇Γw*9V>Ҫ8طf9E[ҹWO=z5`bz#.ol&B?0'ږxҗSHcEjM7Տ}qEku5m$y^JA_=+Mk[2U5wRCo0wL9Zuu-VTv6A晓\e׋-1if"|cK3ȇ`|\jB;0Jc')yΡƽ:GUNߤ窌:qic*dMYX[$U$s[G8ݗ8iE5zMm Y_<H7mMxĜϵkiA[G, I}ֱWzĺ JȼEkw5N|eӏ~j\ HyZ4^mֹio۴r?z ] ιYn#q z{Vr6oc8 '˨Gw8[H}WNtEH9y^Sx#$jê6 F/V3cq^r=ZŦ>nFJM^qL^R+"]^*'=*^DD {]k7ta,:z̨NCJu6T\9ڃ8V})n"f;P}M3.FNyޚJVw_"cEĵKc%+MYϛ/-!3X J!_±5U+˔jnL8u%d65$iܪO75Re @ 88x4Ffwl7U+^I26 c` 9\ӕCUi#c `$? f9r%+Mf$+.8N̤rWCgE5ʎJBIgi G==}+LSo) `خwH4wYzs]FvxB!u }+&c/̚1/<)`vu(O5d2(PX7a?j.yba[ZtZdWru \HXC}3NFz KUKKXq''Cdz4ٌrԜ{V7pYW+?\֢3b[8##?qZ,{ ސYiWI$uZAED HZ2$BEE40ZJ(q9?krw*6JX͎@0ʌV`e][l}Gt&MrK$ʓ}OYsO:3iqZKkX^5i^7w`2X gpC"$2GExnׂML&P!"G|Wė(|ߖ3sUN~T纖&46OsjrȭvI=Z-=ڜ~\0&ݙb7ү٬e%l1s'"+.k_l:7~-wwLc_H$W z`c<3?r[nvc[IiԆ'%zⴣBi^f:Zu-Byơ9 6z}+SL˅u9#\ լ/dK?Oh؎? 1sԕAhmƑڔ[(cey X$dg:ӆgƣm,R0^2+9npm4ђF8{$j^<ֳ5[fnFr2zcޯGn#;a!pwPv2·3,u[h卼?_I~Ԗ֋ǡѵ}k/[40cYH~k#5s^L:hV''tRczv$߫SxtGNVǘUWTӮK=<܎}r2Bpx7&)L-I'C=ORʎ#2hol+!;&DNt^{j_J r!l(EU<L c 8HuXױ7c.Z#q],N~ ' Iݿ*ު1"O[ |myKL!|§һ'40K(/MnEȰIcW?γM٠U>XfV1FjfMw;nc:aɹqɒ]>6*@ w1}Toyo&th_`sW>9;"A\^IP;Ac,6P\DX:AFP~`q|ĐvԬmG#YFz8%=^0\iFi?U!F: qҲ溞͈11(rq?: +٘Ok Y]܎Xlھg]m ~ȣwrNr/jFVDnz xAn].i=0\3)iV) r"H;4s{NS@Ub0ľ+ak?ηԾܝ99R.fn{%f&NGJ,۝J z~9:ϡ?_[jq;Jh9qLQ Obe2Ź<2mV-O4xell[F 8'??V/3HIZ5(Hg̘mgz@౳AG GQAZ,>2`+_<팶:u+Z]q/׽l@=@sӥ_]5di{$a,L2HVboXK M5[,Ƴ٘'J>p5!Xg}{ wUV(*̩tbB>rO|y-Ǯٿo8`FdDVAmsM'D-1)2.}+U,#9N6Vr&}I]K tOHoZSZ[ddž q89V`"#.F?*5XVXZA2e0xU OAe lĻ֭G q; mcuGId-Z9UK;^A(&%/p5.`!6pGҪI$!xз)jb0՚)˚(w#+6w}io;q2ݑ̀IzfLbxR+^KR] g7?Z2pHJu SCw$&=mt\w3.}xI4;?)p8*xm9NEg'y8ifGg5эhXW zqL}~*۳fm'O2F̭n$iMKۨ}dCHZaCn8R_ʢ"w$ZUI;HX^<~ǬyDMFS++ #ӽO#/珚EVg$zAeԺ܇U{Xܪ $~zHQ!c?[ӝZP]x̿&;g@@=x'^ۙH ?J?*r: ahdU^3LJ쿉zߕI&GwbwZ~ dul+XmOT Fg5JXtj܅u$>µ&v'Y. g3C5,%tW5ċʻO#>v r dy'u'q:?/oΗm=g pcz&$w+ː\YvV6d@p?!X+OQ䶥yj' 'e9B)NOFݗln=sq=y4:򫁴+77LZv I9k^}ҳD@$Z͘nm9:6nx$335\{-$e v}+W5Q9Y0NȆH0PL}:V%Qe*qE/ϗœK_olƦeʮza_|9|C?kkХt6 GO89Rd<܏֠) 8v$px[!Ͻz6<:f;0I# *I$zά cfS+ Ͼ,y?•!Nj(1Ҁ?AU!Y?*IgX,;!So݊#PDp; Ծiu@<>ђ8v>)K@SJl6;뻑—p$Ǹ1&A*&n'mPGP@Ϊ U z@l}M0 D܂V?Q NOS(NUY9ROP,M)cA RQBQNJnW~~bAjbC-$@N|" 2q#Gt&;XA_а^_wڄnm;-?{k)a෹4غQQƅޕi. R@c\@X NOTf#\itrIn$U3?;fWg}ڰC x̛P0k!p>$[1S֠Goo~j4C ||pⳕdVkJ >3Z3kŠ\:̹X'ʰˎ;ք^Rc>R1jʀ+6~MJ8%^ bcje.Mt2>\U)3"6f9Yԯ/ T⪅FnI?\e#qUo0E*sP6 JڿU921e HPu2 sxy$dm#* Y0ѿԀSr_gG+2_$R\hlϓ&Fuf5eOq`" x<)u5?4reTcJ$N;y?ʬ`G1 9G8#ܜP$5|d OEM, }>qg7\vsOZigjl0{xیzb㝀Zɸ쭃X8#>-J@ީ3^j`EcO<`3W+>7})VHקGTJjZ4e^\=xOO%!E"$ct'?!xZN۬1$#8kuV8/HϘ:HTK8!zg<\dFYBIݽ?OStTGTe}i?Er'`~bxmOEm$V9<1֢Yg1Sy๕LZ^մ}v]K2񇀞yqP`pb~l#*;()wjǙKѱ#sNxӑJ+<1R!'9CA }sV3⫏ՃFiFc͓: q%c%\ UX7vk6Z2nN)~&ۺqL3}O s]VtId8ٿ?(?,1Z3Pcc1$ =k OH|lO3Z{Mpc*PH?zGqqnF*13m~Pe Gߌ۱U|Px0 Wۅfa\,6J 9x-7\G=>lS }޵TLt6ia{RDዮ hajn[OCEJ )>Vm*!@M=]j|NAc2yUم0< ArW}@$J?$?W!/zҹ$w'Q"3g1^y{_k[ѵv ]GF=O<]⨴O g-b!7p=5/hn|cA9!w:dG/d-et0by<)D-SwJhbY`A{ Kx[hmyk);23ްTJ"pg-U9]+w_-ec=e qNx=|W+]YI$16lߺ1 fWF[oq!C#z.=ŬZc^iK h<^&\lkK2+hC*y\TFuʌIcǭ:I:3e C;$\c;³ho 59uT/]˃C#8[Xc<׊kcwR)SE}7U.c)MD:Z|҈vC+1ӟҽ*#,#.Ռ ֠iR{}J sc7 2nb q0˃R!BҬ1PO\ۆ1}jHS@<`U7fMp8i`ߋJLpnsT31PaH*0$cx ib9Xjyݜ֔6\+.sSY1$ڑ zܓ~}j"xQך CP[wbA$Ԯ Wc&]SNVopgټ͏'y_ire]J H޶|c_1X9嫦3l.ٸ8qէ)6zJuEJ-$@3MRm#=zkswEA\>iˌJY_ݮsҜ,UlNJ@se[$ @JeM惒sTU^p9c%G ՁaY$7gEsEHD;.HV泮 +e_8+oSh|H 6؎sE0H<ٿ1Y5wUjdGOY_*AǿN0qH|I#'e.i 5 m 1w4ae-ҢA3nqke~8>ĀRp=k/W<V e~5&M%N6dF{ ïy])H&PO EAя$]E )J#8agIK/#`U^@Π::P~4pI@;6-0? *[۾@ 3ٛca֢(ybO9=Щ1R\2˿ВjFB~Q.JÒǩ+73ErGuj4#78,2~ dwKplhF%YCT HqHpxD1'har@sH=)A4'BW*B䅰N:k OEv!G#VQO?_ˊ2TڹMN8eE"ocwecMSº5#:u*xC91`ZZ6~)wGjsg<s^,-SьV^Z6#\WS׎.]4j\[hwwf Yu#,14 8Mq-G]ƙ٥x TUk*ײ3k=QBezK<#+$/GAhD%[y=M=Z+}DTѷ :Vv>}K?&{ 塌o鎕8UPLXN|)Udz8JOhm%̅ߟZRF1i^g "ʮQƲ!O7!s:T43PUdP5"g'i^Ջ{ s&˂Ҩͽʰ{5~wKdQppۇaߞ$ʬ1G#b)o23FrJe۹QX])wpjc냌) Xޘaߕ[FO fotoxx-20.08/images/areas.png000066400000000000000000000034731362435004500161310ustar00rootroot00000000000000PNG  IHDR,,Z pHYs  IDATX ՙAn0 E3nM hAomM'0-RlJEt^oI~ ▨'M[bL`ºudk\)<1!{c5/P\w' /9i%6&OX+{|΄e4FA|[ ȍʇ9RxwQR2s`r VyW)=ҽm+(—^HT-A2rJ’2mQڋRԝ^B[).ahZXa*p Y֯ CVDU(Ĉ妓ʷC 0 Dj Q/powPZHaԅ$b QV!$ 8Im{@!)|`#jPLr<ҫߣDPQ8 ?V Nn`fX:lflr W. ݯ6HWal'ݷK0Qdm z.ajZۛ0t ur 0GEi^Z.A@]d _a;l2ZĻ@Xؾ^ڃN"Bn6rT}lz_gj-G1j]Y"̃ۃsB즣8gS7۟S5ߎQ|0]D`obڣ$74bm5Q)]zAya%v9VyN2`4 P;`>m q>*L "StIME;+ FeXIfMM*bj(2ri%HH2015:06:26 07:59:430231Ƞ0100Fotoxx:paint_transp|NEf6/iTXtXML:com.adobe.xmp 44 44 0 z `zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-20.08/images/batch-add-change-metadata.jpg000066400000000000000000001471141362435004500216430ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 383 654 0 C     C   YM" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&KdUU,G$iVSyo"g(oRA"|ć#zW^7}EΟ!24mOAASڼ,vaGw/|(ьJVmwj^d G֢X̒;kIoEEŸB@϶s^C;S"HWhf =.!l$&/*Gq-Gy]xLRUR?1,{(]ȋarkccU[i-'`XG:H# yx|T(c/ӥoFG,C[E+^n\,lyru!_jj,tĚ4WFK c7]֣Ήqw,S$N,Is˭}'Wy|{//oKx>Tk<4&0@YHC IE/EKOx3EJ -"0`NvJO2m}slk-J6x.QX4LX s}1SLI>#vR Ӿ0(潗<#+}2=i v;ԣ{Xi)Jl3%->goOf{_Utլ&A#ުN#+~7>=O[úij7(IUi]b 2:Xx_ > |YF4-#!xli&xfc8+l}f@hF0ުN#=qnlrǎ1޶H!cg%1D%$[+9lnl4VO> 3a[? ׈<[]jXA%~ q"OXsO( ~"LG?|Y/nt6WmZHeVx!KWgw|:;-KE#D\C3/;fn{ͥc껽FK"BTzjH432*~(7%ix[F#vf8pfG1/^=qjapd3#|m<+_q쿐O+O:׌,(|T-/7?n՚%hw2lHV> }w"Xj7֚=ƜX#HŔp:&ݿB9?z9ǜ_Q'Wz>G0X G0X G0X Ҏ`.D`Jb^cj2\jX+a?Q+**5tC 0sMſx/v 6iYh&{E'{]mȣ6?VQPG-\DԪ'fLu*$𹕤PZ)W>ҺGmȭ9su碷wZEdV "aI Z }x|91#sw:=Rs{k|X6w }*l.&1F.u,CɯR }x|9s/_k- AfC;mM(fs{o:[._[ԺxTν$h;{Gmȣu7Wt8tE%lU[$K矕3qZtgƚ_l4eD$-e'mȣ6?Q:yŸZֻ]h\QI.fh."ÿJ0Pz -,4i}ʤK#&o|aŢkEBEm2х*G*@5x|>od FWWx|>oxK+]1mGFTg73IqJkm)wxSL$RNM!x.v*n潿6?Q(i9>ië ZK&9!)1hvyx>aXxnK [- ۥv={Wq( rΎ_ '+FtxNIxRq <ysG^h_k5fMIJ- #-*a^( r0G-.ow%0I,O0ѻZΟ5K k;EeEjWq3ZFHdqׯx|>oO%ۤ(QI@r]9zmȣ6?QÝv)l`E9b(LB(((WŭO]TR*v޸V`~a+kǀ>!^(mtηíͨ/$BhVO6Hd]_AKfNi6sx\CɚHl5?"8fAP2;hk {Lo϶^ӴR_Үm'Vw.Y\i w0xVG/m+\RW_,|" &Aov2zO.^/ZNVe:Vy<--y'fkty]bd pf%F_ԕz[𷏴i7WZ}Cg2,:;tnיxGo<)iUmm-Px!{ ʹ>G>hvm q`yՐUr˼)o7Zeu SOY[^(.,o%Ռs#2b;3|?GvxĹJ31\ˍ* S\hW_,Z%բKT؇OAYWmkI x@K_RI ɦ;ȅ8o++zםM?xSQCV4-BOr-ro0eU]Ċ-m_>:}TX٬lRC$+cNj\jz\m^5}I!w؏ MXl]O[ B?x{R[4I$[Wʴdf1dR |r&NL]%410Z#m[K[Z _u(8|(-JX.D6򄋔!+#潮 Pꮡ 0A5.[Ǻw ]mM*Oҭ/f6tkj%vs^2K+A9Ulr dgRv_GԒ(0(((+O<-1:+8}O~*c.|5L+4Q4c%Cr@mZ<_mc=:;/Q3̱:9`d$6ATaѬ!-2QQ]\ĒG 85GK\j!7#ʼo;ŚW> x U. g!/(G-n$չ Nj诃>5xT=oG}e5'jVutU乖M6b0WϏ-KGM΋ygeiv-'T34EӖ՛oԮW>g|'N7LR 5da,yE|k+ޕ~-F:e|1tZ{Z8N>Z?i%K5=ON߈'I,oE+`'#=)54oct?~&ukMJ{5#Hb"a5_j6NO}x\m/u Hit4q&Sr´a/:mRcx⼽!o}5QN3ɕek mY?Ԙ>}ExO_5+3i׍cl5mB)rP.C'7>[YZoW^##mhԭ.DׁEkW3W.[PԖxǜІ d hb}H:/l4]3iiֳj40mI+N֪O1|RI8EKTuOXG˨i0\^izƖye G K22Еn&{>u "K4&33JA!=XN=]L]#'b7 Kp$ǘPxCęuw[Ϣow}'KtPVfvė% )Ahe'"+Z|q7<~>u[˫{d;QAv>Sb~ymuk&2ETQEQEQEQEQEQEQEWi๳c5lܱu $ix"]xn+#qF*3~ "k2t;xi $P@$W^ ⯄Z't׈d0~H$ul")&bq 4֊kMoo>?<xWNӴ OrD}q|g$=;LU-o-&9^Q Z*Nɣ[h>2ѵ,:uΟ 00Dځi]F v4Ohז(jZ]G}{y1Fšxbi#;Ń z~_}5"?޺exqsWX*K:o63y7쏢j|Ikjz{uM?SkXf8΢Zy#ݓ}ީw9L?^HEng`!D^_1MN#q(bWAE!)EFQs)`L!{X>:( CÒ2Z#fLÌֈ- u]†( 3۱ۻ=;nh-sWH*ݢ0t:o6 Mg7k$D|7Fܳ?jlp@.AhÓ1ajz߆5(T_Om:ױQR3/>"|=lZ咐Vki"R:a):Li űfDd'*(cè#ռ)X$݁ C?6? m<'5+A$ h+h>%^|:.ʊz|`qQ|B{]~/qqm#mbxLzײQHu`j%fOl'+|Ƕi~is9{Yc. ~$ײQ@ë2qq3 VR((((((((ϯ.R*|}c8=|t^Gsĺ{x4dKpe?.U^\5eu20o@{k.xFOxU8WY_55$wPr7t4֟~R=KAxom#Z,|J#Qmge C$-ʁpMr~ \{{ma &{lincfx5Ssyggz-ص+_ )T]s\41%IUvD~$A x^9eu_iF`2M*4ra Ȅ2Rz[~&*:}_>)_&_PmsEw!"I&˻ @Ul׳xǛ<1j/׼%YEmHiq׎(T*>^qV'H?> zGEQj+i2+ Ǥt_Uxc=#5i2+ Ǥt_Uxc=#5i2+ Ǥt_Uxc=#5i2+ Ǥt_Uxc=#5i2+ Ǥt_Uxc=#5i2+ Ǥt_Uxc=#5i2+ Ǥt_Uxc=#5i2+ Ǥt_Uxc=#5i26mM#+(O*'H|MƗ^^o3}*_j:(5i2ϐiC?5}dFD딿9Ϳh㻰h&%D`9VpAGPkf4/zZ*((((((#K^zgpKFdQt1P <ֽ| W:/N}chizBKp90+-&Ve-xƺCx'xNO7=*iqff޻0W5'/?}E|5px"Vd%ޣwqw6;B+X$[39լ-tv;K G "4%lldRݒ}m>#V/Z/{F4X絿d;f[?=on#4A-oZ$h7$3H pwx#1&8T<B2Zݿgέh5XaxGY}^{{ iL,ؗ=wm~_z/]jڙn䀑~*fU\*3bB*J ( ( ( ( ( (2񶟩Ɩ!ēq +*k%[v2"+Wfl`-ov0zXþ2<+^ymfV&1]GI^,E{e&YUPK[=4ICXѶpTA}ZXt/zVsr꺢K%Hm `WWȺ_h36sĚm~#}[=$۫ϔ-loR7u_kNh4}^{1k+HRN0F{ y_>B['M>6[BGq9),^ES$`wF/i(5wzƜof.ddȊN0͞N+xXo5/$jvyRx1tpvW|ݏΰq}]O6ݓx62;xBA@lr"0i}*^4mB+wl`c+e{6xH+:yAU R'㨮ky6K-G"HWp9ylB__c[U^OOs4n_ ZjXs.VCG'(sn^i,iF-hye$mbNӽ{˟K:Z셭 PłX$?=ROI}7u2* ˂i,ҊWx{~%y2A&h4+`ױ>`[%~FBξzڣOmF Dۇv>^QxrŚޗS^)| (&6o 5K7WO_{rK:tWZmUMb͐qf*1_7r_?WaO>y+W_"boG(C'>|E} W E~*ޏQN7}?_9?UAg/o71_7rz?/G_?W8+oboG(+W_"휿~?q4W~*ޏQ W E9_dhUA_9?rϧ<<_*c|ɼ, "NKE ;/0z7n'_0i)H"Wν7.|ai__xn~ky`:y7K;43ۋ8Ox'Iѿgx*ğukr'yVY]5G#IrJ twpWM-{X/4$QO`ӿR_=^3^2,/w×mco:KNk'-; ѣ k` LxosPtxvc&[H]mOG/4$QO`ӿR^?q_4NJ;?e׼j6֗ܽäGD% Qώ?kOV^/^-n kiCQ&op5N,8u0}C/4$QO`ӿR]sDd\W#8>⦪j ;p/ rU_Z]dp3__@2u$ȼm}~]3|&76,}RW7wňg;B/$j ԒIt- q.6tf;HW{E08}HϬi 6tV$OJ>ct2 hQJp-[t(Β[ySH<054A$WuE'_0i)H"(/!>"NKE ;/QE  w _('_0i)H, O`ӿRG!>"NKEwTQ`8_B|EC?  w _+/4$W%k#>'O+|9[IԴ\"f6ܼWb$jY`O~y66Wq:E5I@$Y+t^~֭~i~f3t;c63돴uOkdִc՗F`O@?*>E~).׵k;]]nc7s$;9,^M8qZP]blz&?K(a/!>"NKE ;/QE?Z>wmzX-@AKe R9Mj=cJ`m?G@^򥅄1zW rI}>mJ%"($hv)M®i ~W!'/~zޕwxRW:m?в]8i !ەwupC񭧉u?Z/[MkO.P2ml X&~ =-QvoE=#eaqfI!c:RN3_Fhhiod* یTY>}?v#O>^G)/=W>"u8jqx/:Ʋr#a oS_WQE HqoX(((((((*Ahzԍ մK"0N@ ֯VwjX7ywlޅwc#8q@ҜAq%z7"&\^sJ A_+ѿf4/z:}SGԧ򭯯5{yg0{pkN ?dxw{>GSxڈ3yvcFGĞizԵ%{dwڷ,G@+Ӿ$~ڦZ[QS̱@4DQݘ,K)$c9]8cT5Jf𕗆6"Z֠-RE0OkxB5qw;/tm'Ze6.$'fW,b\)$ 8V)/ЛG)/ЛMht$MG? |sLgE$R$.Z%i;ܞ)71~jvwy>dZs{ hdYwc8f߄7fMEٳ_uKUy>mZ^xlCKnܮ+P@’ 'G’ 'G RU . hԲ?rBnQBnQ=@?,GWk%tO(%tO)jYƏK/' K&Q K&PYe?p?,\)/ЛG)/Л@gWhԲ?rBnQBnQuږ__R~5’ 'G’ 'GjYƏK/' K&Q K&PYe?p?,\)/ЛG)/Л@gWhԲ?rBnQBnQuږ__R~5’ 'G’ 'GjYƼ__7O.J2}!ǭwBnQy犬-oL;*F>`Ii0=3ƾϏ%[Z&ǫYy75eRo\;9|7x[GtMiScmE,@N'vKߩ e!-HoFIY]5;1/xMb+inm \k0F#$NR^7D(´[o+t iM5­\IYZY Ff9wc8WIe?p?x77f#<-6rvt yDO)RUN>HW9h/#_^XkHZ^(b8i!Rn=7ږ__R~5’ 'G’ 'G?,GWk%tO(%tO('S'n!Ѵ2~o[icH"!n3w@\l潚%J`\:^XJ? ||>/h%^4$loYZ=> Tzͣ -)9:93_<|yh>YkQ AWݮc_D!P]ԋK~c&,Ő]b O|3WxԭuK->#Kז]IJ$x`dAo?~"ho5=G°7+[[\ɠG!FWȓtL/UȼhگOᫍrY'QS$07)T߫?Eŵ˿E_ϥIЯo,s< {Sn6VYHr3fhKZ|/SY&Ika'0L(ʍ$o뭿1/KG^g~(j~ SZxr]ޥNpmT$Axd`͹z9QM5O`) (((((+7:k+9D76 B;! I9Y&[ެ^ͤi׊_+ѿf4/zSG=l߳OrG= ww^ռ[:xEg6֠GNHsI|w;c>m7Nsci{5rHgFIlcв8/U6gBO/V?QxIO|[ )yizI6{r`J8 Aז9;[tW >V:kuĴv1yMxwmnvJܤ:C3Gs bXq/o.j"IRE ,1qbn7Qm>hYeY(*=[[}b[qJկ뒑R>o7'V*|Њw|RV\?71e]<{ѾSn-nOOp`а1uI)H$M;^>v$Uz XHiRM|Gtph_L$> K䬥֌<$6܌cp[~#|-۠|Rּk;6:iVX.͗KDQZKu]xVM7Mh $R. m_>>񦁥7ōCW _! ↣m{5Lj?Q1y\ o$[mHUDՈ|5/~i ?Ε'EΞMg&ʎtnTn8ryfMu]诐x"◊uKL`E3 eXf$h?/~jSҡĝXX$F|LvUoyǯTM/7>⢾Eioo>t:w5YtO8Id[Ͳ&pn/i(>5-x'7'M(tN;~ z5QL(((|MwwaZO [yhzҬj͠oU1+YZKr#c |YJiOR9`iOR磡=OlI"|zI"|z(((((((((((((((+|{"on**ȿ[۟ ܇J((EPEPEPEPEPkTi>U=cJ`m?GʖG]}ko!iԴ4z,IJ!!LPapH_룿>חK'ÿ W4w6/jxsQwd ;;3^xJ]SGV CqZ[$C[E|/?ox˧WSxwOt[ zm2K3uih þKNx[QO ^#G-XS]t|_) Bv7Gďcg\_jivF Hc22rFw< wM# "4\lL%x;r@N+Ͼ1[p|C'+'@B+$I]c\͆+)[yn(:_>>ѵ}jKZZxd}D>\ bAM]~)x7:?dOmw @. :c8?\1Svw\dmO Mټ=ox$2KEy Eh#;>+|wZMzu0qMCkvGbIXk9Abp⭷G3wKh 4xnZ+wm9ʷ0w1ޱt}Z֛]E%3٥s);)Exo),ሴzņl|n%Y|셾]8_^ !׼7-85]V_[i[\̇l*ꪥp ↭+y?CMF/(Ɔ,caQ2cT(hQBa䞱OO4" 0:((((*ǡhz,m,Vv\:/V^:ŝϨɧm#ܣ&%^|QJq%z7"&\^sJiOR磡=OF5%m Q;cIC}meF7gttaA =S|$>O _=vt8oGdž]??1Oǫ}?<5aA =]X?1OǨ xkhpaA =G~#c_.WsE xk>tz(7~#c_.Q EtzGdž]?,  }?<5w4Q`8oGdž]??1Oǫ}?<5aA =]X?1OǨ xkhpaA =G~#c_.WsE xk>tz(7~#c_.Q Etz/Iע;IgHپbHv=s=?A&t?w_Á3'OGdž]?t7~#c_.Q EtzGdž]?,  }?<5w4Q`8oGdž]??1Oǫ}?<5aA =]X$W5˛K-uцf9[OXOrtִ E;X˿y'O^8Nh1h^=u(t\i̊G;>ى``557ӯD.$P>s\ß٣EMڴ.u}376z55`bEd|*ت\%FoތN kS4+Mh|nӣ3 hT:c;BiLjo}TLD nM%"dp> Mu~ῃ𿎯[_:(|Ml,G3&)9湋GN7 Լ_LvM=ɧ-nJ:ml?+K_zw퍡j,2x[[<,oZծyp`(bRw p +7ƿ^>xS6v5C6fA~[sa<+_z:%ӼOci{dl4m-ڗ(%f/ .24kK|wm?> `ҳ t' ?Oº]y'i*/ԾڕΣpnk禳9H?R|7=5]`wW WY禳9@•uw(+PyEp|7=5]?Jozk?:Q\)_ MgWrRwW WY禳9@•uw(+PyEp|7=5]?Jozk?:Q\)_ MgWrRwW WY禳9@•uw(+PyEp|7=5]?Jozk?:Q\)_ MgWrRwW WY禳9@xwH"禳9\Co{Fȱ!3JҸbG園ܱjX:Q\,|;4#InbXY'IM+S?R|7=5]`wW WY禳9@•uw(+PyEp|7=5]?Jozk?:Q\)_ MgWrRR?}\ _x[cd-oa U̓&kIK8OL+vxϵ| |9>#Ix'L]G\_ I.aڋ7k}*{ .C:(.Wrmq:Tq~7<48^,T}I}g,Ѵ4im)>kw10<#"TnooqeN:~wzjze^4Kw@}+vÄot<en8ҭ2Fkgv M^ײb[-K}[&sY|U}9{Mw [\PnQ"+ٗ>Kw;Χ|!HM^KHȀ*,` txPw?*xCO:~btUӯ J'*"q s9Eд='Ah4]>NFa E;@=M]!/{k +OAx%|?W4Y|CnW Fy2cpP xO'75iZ"{94g+`9Zz;3r>#MӾiwwꖚRǧQr8wLF2FGs %C(1F%Q@Q@Q@Q@Q@Q@Q@gxI:M0KYmݻoBFqY&<7YJ!%,Wc#G"<cUDo?J_~\?X7_տ#wVZ]~ȟ'u?/:E>ݵjZG½·*r~yDž$ ?@u'(o ?OQp:+o ?OQ cuW# cDž$ ?E먮GDž$ ?G- I"Q\- I"[$E[$E<-A'I.]Er?<-A'I?lx[O\lx[O'(u'(o ?OQp:+o ?OQ cuW# cDž$ ?E먮GDž$ ?G- I"Q\- I"[$E[$E<-A'I.]^+sW'+Ηl@ew%x<;T3Z֯/1B,-sUY)6$d]7tϏھԵtw㘅ٚ )tȅN̨𨘌 q!BfVKɽ~ ^ }ii^Int\“[#Œer9Q?-{'_ .mcHChV[;) xKI/% _-LW'_㾵|o<{?u2jzWo28kwU Df-$im!v9fE$JK_&5l$|&/>[jv:IcsjԥK`ķ&} ^)m:x|*u!e߃t=3J՚aiP"_2ų1_`Kh}Ѵ ]kF}fc+09T#ejzjiVy_b[}*8Y'bb9UR泍ӯ{l5~g;u;>;ҵ/ ,VdIlAN&QcwiEn\<;Zj2Եa]>Nt1-7LF3p6j+ڋ~5^O-|=wXi%/d gS^ZwbEKYlgUWi/1PpCݵD%euW <7wmlu+-㱆1tpZl2~e$gϏ|Wt/PԬL|?eM-A}˃4;be#E=FTY~X<|/SSӯ[xN)&'yVY#Sq^cZ~5u|Gsi,_McY&Y|>iX,3q k~ V=>I+N=Iq=OKfWF8^kMp_u/I}HC% -[$q)@b vi_chuOxt<5Z鳛%fu-X#h#)$PzX|Gḵd o3ͷ$>kr}mm_FMկOc( x-JI>.Aln-|'pkIg8/"^LеBT# GG2 Iru/_0|ni_LM ð2kFKuYɅ۸Hr޻_ڃ-?z:=OUuH-dF9VFWVX ~"鸖^OþΝk^njSH&k|ˈTT!|li~Қ~Gh MrYYi JZ_2dTVVrv 0o KZ 7aٌ׊~\|,5h^3uVBZ7O _=vu|$>O _=vt((((((((((((((+|{"on**ȿ[۟ ܇J((EPEPEPEPEPkTi>U=cJ`m?Gʖ'P6P(Uoζfܫ}1\g75/Z:qiF/gWPخ9Pwi7d`qm!k0b28M|^&{Ꮙ3UN ^AAԴ z5YxX8u$VmlO_e4nǞ4axR[{6d2A%2FZOOE6A>?./߈zڭΟ=|TZl\p%+ ®I._ ~ ]ǧxOtie<Hy9|z|IDCt0u;Ak6Ri 곴}&Ĉ]rkyKv?1k,u^=wYs^,z|:{oL"Pb2sxIO Y[C_Yi7_Q\:>|;O| j+tc[-quٶZP%*߼pp%k[~7?۷^|smk-ڵ",DHrwbT].Z=|=NWⶓxJΡcϢSGIgfm PF u$mq!/ɬxJ_Eg_k>ԖiZfܞ2ҩڜpsOğxQ}=3O׈$ƙ=Բ\D0qTo'ÌxOߴ;\ׅtm Eѯe4\{qVYmAԩ# n_NѣѵoهOQ-`[ⷈi~-S2|-1]6]_s KqXI٧O2vǒ+H;|P$ C^M1dn7V`#J4u%)(+jEwWEM}Y{OtZ8yьoc 'd,ǒr7xKx@$_YEH~!q^)~:N]|3i ƱM+EUJea2'F+~)xğAcy` kxo4ͺm晫&XLrM)b5;>B~5g'φ zu1Xռo73̘+_h!dž===$kKT(FFyTՙiKm5h-@KǙnc0e8#3է|Gk>)f'@XN>~v=S'N調׹6\?Af2vPLyVX*uo$ +&r3zTw #olg;OW|ͬ[ڬ38E UlFE`1>%YYmA˩?f(Eϖ&i],àPMݾZ$OjhװyvLpA(gghkkm\JCBω)_;U'a]OK΀8ω)_;Q#= r蘒sG zn|HOBܟb(B?۟?Sпw'>$ЧO1Gď/+8ω)_;Q#= r蘒sG zn|HOBܟb(B?۟?Sпw'>$ЧO1Gď/+8ω)_;Q#= r蘒sG zn|HOBܟb(B?۟?Sпw'>$ЧO1Gď/+8ω)_;Q#= r蘒sG zn|HOBܟb(B?p'k!(+s ±U=;7ǿ/VvH""#$`ua#= rJ)n|HOBܟbω)_;W}Ep?۟?Sпw'sG zQ@ď/(B?wP#= r?>$ЧO1]n|HOBܟbω)_;W}EyƱuW\N`<;:pw^O|>ޭ<G$4avU9Ҿ/|)| 4мA79ԓ18EXa}NIF ||'q L<^kK+{Aa>%`JcmV?뾾Ze{xCEaqcLr9e? _J~Oi,m ᜨzs_/OJN{wiNWt3"\ipOQq[xng/~k_kMD65y!`U̡Ip't/4ϥl~>-z IⱍG|%S33I!Z߉4iwoRo7k'4-㍚E2(A!azg |x[IۄbivF h p8e|5>xxSVˋM=[L}Q0HVwa)8Ic_MUŠ(0((((:ŕϩ"ɧGm#ܣ&%^gz\zZ*i[H,ʹ.yWQ%ox/?.?5m_WQ%ox/?.?'WS볯(7{ȼM(ږOx%{2N2yMtx7PoEqx7Q ?vW ?x7PoEqx7Q ?vW ?x7PoEqx7Q ?vW ?x7PoEqx7Q ?vW ?x7PoEqx7Q ?vW ?x7PoEqx7Q ?vW ?x7PoEqx7Q ?vW ?x7Po^+sWw ?p>& |FyZ;7^IУlT(=vIķËbxrs I ?0;z+o{CljjV5夢?KsXQ[/ #2/rykt<@}u.wa]qj~~Z {/D|lqKm9#\\1A!́hRǁޅ{ eڟV~SQ9(]5/.sӒ5ڷE$S}'CO.-`3^4j/$1@cH)Yb)s0|SUn,G!_^i͛ΚtW2:O6o6>^=3H^c,!7!x7]OJχ#-m]N$LdGil`DB s__x 766pgeO1 6R=P0(((((((::}čݴHT2W;ĚI;iQ JWvW8]jڸI[ ˏkcUD?J_~\?KOg^g~|95}j4WI׳A t/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFk|{"on*YFExWm͌{ot-$IGc_ApGQRaҊį !*(Շ?OYF`tW5 34G,ӣ|_U0:Z+tog?iѿ>/*:Z+tog?iѿ>/*:Z+tog?iѿ>/*:Z+tog?iѿ>/*9cJ`m?G}f^]鷶E~}"n^###q[,5_'K9K[yg.U! c T3ៅ#񎣯Mi-Υ}<#&ߥ>bw1"G&E~َX$(Yܓ&վ`AHaڸ ]|{#~?BkMI^4pc Q؀0ˇ;w5 2rOSi'ύ>#YjVhTӵwе[k;I23m7R+mm$ll`nd x0Եm* *Kk+;rBF$H{o1e}k+4X!gʮ5P U]3r}x7P𝾋9ƯO]!?#|ەf%L; Ư7i!_KKyfKxD?m~v'~g_ZS&negR>mpEh~w4WZ^&jPإh"tWIUؒw`q薟I@sOž Ŗ67-ot5FLڦU$>Q/g^ o #ip[{x!2Hۣ$*~xZ]Aq^_Y[WU($ s{;?_&~iO33ů|C~K.hmCM 7F$H\WßS&'mHMKY\{VBcW]7n(C +:ĚEg\jPk|c{$d` gyM&źgeo亷medhɔ)H ( ( ( 5֧ZO&Xq]2#G"7noV%.gvc<}u<BNO?%ox/?.?kjyVZ]i:cjJd%v _N+oH]W;D?+|w#zG?F:hL F: t Ѣ3?e7.+FoH]Qޑ@/O(;#v_G#zG?´h F: t Ѣ3?e7.+FoH]Qޑ@/O(;#v_G#zG?´h F: t Ѣ3?e7.+FoH]Qޑ@/O(;#v_G#zG?´h F: t Ѣ3?e5?d,C`3ӒO=M{xo_ERG4t ??e:QL F: t Ѣ t ??eEg7.(oH]Vޑ@/O#v_Z4Pw#zG?F:h@c}i^hc*z h=cJ`m?Gʖ.{{D K2)[jms:rd|+ƟZk޽΅g-jϛ'ɪ) *0 _Zi:BLolmʻg5GT;Rݟm\SBƉrѐg^_;j4E/rwΌ#ݴNX.ڏ3d~/6\n"-_˜|ğ߉_SֹVd15v!x.\<|;}]W zNm:*>S#Kqߥ U>oT՟,|C/ dB}ZZ[;f Rvس3N9M_GmX[WT:(nY l,) GؔP^o:V/ ~̚^xOUZO Ny#_Jq ]cC-k!,:rkMJmz+{2)UíPcs5;_`Z+ CIn*0 8 {EwUx[ZOj2ԵX|=s=י#5н*^\k@c )uo+hmSZܿu4K_AuUS0gq Dp83>#ZAEWS&M{7~!$Ss*~<|G[Iyj1WV\ndL|L<Dkk^ޝ)šx!(.5K=9|Zb1pik6ІBb($zu'5-Re;;zibZn6|#k&S\_2}#Gk5hQRH7l_2E<3 yboCk,?dGn̻*p<mo%[[t5kgǞ0O.xVaWz{C6e,6 --(?ֺ#I՞lzƚoVԪT ,@X5wBoO|W=G-O; T4np*l̓KԿ\>!5z֥j=zRep̰lX)a噉?gJ/%mo>`)QEkۭ7z݌~uT%F'$iVw5cxwU!3YZr#'!lgq@ w!]_L?J_~\?X7g:%yVZ]OmCԵv[,dyU'YD?+| ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5MC.-ef奴*@~$<5=?A&o?/C)_j#^~DVMcB\%blFō[ٔ>Mދx}oMִ;Mʖi%#I$7PjX3?"7~'Miԗwş[{K?s =0Wu>_.tY-u6cz*Syv?>6XO,[*v+NT緗l&POtœp>[ݿ:ğc??I?V?.Ϥw.K{mlSlzl_fRu}s[izmN2ُW͟$Q OȟEv}-|oR1osk$b_2LR|qH"Un26=C۲G3?"7~'@]K'gۿ󳞸>qE[{;[8SH逖{| OȟE$PgW9$[MPkN)qubE=Adlu{v^}{b^7dw^vsgٟ+fՏD(o'XO">qibK=쭜]O$\UOi4:rH6bkN)| OȟE$Pgzo˫dPGpN{yv}&|Qdk͟k8k،=kg՗D+~2MWg.BgCW6$yn5Giʫ F|nJ 1m=2u?k"4Zʺ21#rRb='WS볯(MKGh4v6\Mr-YZG Ԛx-[zm^G?rտPm`H.6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJ//>y%i E*Șǔy8 >~>6_j?W/ 5U~ '7_J ^~>6_j?W/ 5U~ '7_J ^~>6_j?W/ 5U~ '{wInu-:!wDR8Mjc<_|l1I^k =ĢPJ) H'22qT~ 'ןύ?U_z_ > 'ןύ?U_z_ 䪂WE04v/,c3qy ?W/ 5Gύ?}Bu =:H YTDBLCC?oK<@_|l1~>6_j?O/%Q?oK<@_|l1~>6_j?O/%Q?oK<@_|l1hJ k[[Gh&xd{wRX+gߠ((((((((((((((((((((((((((t};9t/u]Eqײ>6M}ȗCKx,ԧ-6YrʋB0n w: OxNtӦz[E%xnQ1GAî+uW>X̒^7J VWNeMkkp YLUfc9&;_|Qk%u/ ]Gc_|Kk OPVխy%ԩUy<omM;D_GfJ}+e 3aRxXSx>,|{ *k4-J>VXv]$To1M%~7_Nk?hvΓ^Κo2\ mv1PHlx^(GTGխ-di<*y-8wWͥn-R-Tjǯ|C4_ TG4W:Kh/ {]ddOĞƽćevO:זj|+(^m,u6 t>V'.ǎv]O O:u{/ xa|E?uiԖ&]efFۓ0NQhcZ$Ů]ZڮfX4%nNtz!xj kziv}JB wy fKDdvI|մ_ZzNE>Mfk4ߘ W-t0P=fꕻ[ڧasy\}Ltx m9 O# G^wz7-?ZN͂Xf2a[%:y~ 6=cԵ8Ey.!heR:A%FҭEkiw( cUio@ΓVm.?y^m"|ƚ.B)3K)6,K98^'CImrCz:xY_[XoPqsQԾ}wljx;GmbC/}pZt!*V"e|hwZx9-?OŇ.fTH1gslOʢ3egW:4.tpai^#0ԲH<G aOYG//mVoKr˃{o/z?:.۽w@oBEK$QdHmG̀MsVfbmgn?! ?2#wo–F)aizu͵cķ7{y&VNmi u2l<_hTW~ݣH<$i$C C;;_/6Do? {ǃ5;+洒[I S+<X q^˨cqns2*;2{~tn[jQEHŠ((((((((((((((((((((((((( E\$NSa̠.:08n@g+cC6?[gEq mhlf;o3]¶?3k!"wVZ =l@3vPcI[{Y%bI mt klf;o3]¶?3k[vtP ͭvfi }OQ[zCty{BBFQں(+6#nIYC#}+[vtP ͭvfVmo5@g+cC6?[gErWoẗWԵܑ]<[R5]EPfotoxx-20.08/images/batch-add-geotags.jpg000066400000000000000000000600761362435004500202720ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 203 374 0 C     C   Q" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&KdUU,G$iVSyo"g(kRA"|ć#zW^7 }EΞRe4gւͷ^;0R}>ShԔm>-u{2>~lK{$d[t\), g5?_o~VKv`)޽n[Jjbfy8q-Gy+ X*V8e]NSFN3C*m`B=4XbupqI#88`W6w7'N"3$Fcr0Kcq\G_ˠZ%L յT!kepP#2\`c5sͱj*-$G:H# yxǃG0X E|.D`Jb_ej/seIb6{0Dȣ6?W%|,TTkFLn- 3~MAhZI&1hXyR>x|>o #e֩䭿wn<.U'j:΂[Xl._z3 z }o:[._KԹiv/6?Q(aλU 6tڬݒV>rbWn>cƞ>"i_#md4or0!O*A_( rG< 'Y,t%]]Or]GA)%_ vzv~ɭDw{qt"j,=1^( r0Gh XҬ;yy#vf2p3ڳCţ0Nvfrc/*0ɯ] }x|9sY}Dvhm9g+5%0p[s` ?fvm]hrZ-wWѺ'"BpWx|>o~l0RE He FFGjA%GaMRI)n!bYȿ+GfƣiWZ">oH刃Ժmr7oc/ OhfhXV Az:ՀOu~6j:/ak-2iFbDAr3jįj5.f=.uLq]Kنeô=K`e{6?tl Qx24iw1a "\H?P? uY]mcķ"YӦ+7RIGv⸴ev,Xd >t;ߟVw:*(Ume+"1VS9O6RWv4诎/<{x>-uS[qkkzXm GQfFy>Z4^CB]F{˘ML!FmI!z t3մiz֣my>?ٯ#%cq+JgW|OS^౑#g4=A۸avy5|B~9u[dG ewLŅC0TVbw9Tm#+㻏^.e+[- 5{b)ňd69_|E?&}.g*n=J5M5nW7&8@q buK#j3 {j/rhIuDѳYĎ(Tr-b^ m5oGd^g !P F~z!u}ROOnfTA==*$_w"}E:DԾ"xy#ZYx z@XGbd9%ʆߐeXTω5mbPI-tbٖ;gSi*f935-/ZP])f8P2M|Ꮘܞ)? 4֗ܤtyr3u ŸN4[{wFmO2ġ8 f߻_-Ϫ? 7z%éiw ts!{Z7|w-?glOtZ>"?,+*HϬCORNx;Wum6H[dڈyK( @֎>K){s=3[[ /nD.ə@-cGUWu:Gi-[qiy.4Mx8)D-#q&X"z WE}s?bZj.kqmZְ k$i ?/2|?]+zyѵ }JH7u Zue^xƩ꺑$}_ cJ$QEHŠ(((((ǧhgAoR])OY,J( /5h1_?j?c@mZɠ!k(lm`Gʤ /=H#!!@5 /5h1_?j?cL +~/#=[d YB@ Qx;@m4=6 1*Eh I2aw\ Z' aOR_ 7m. I7z=n$+l07lP` /ßGYtTN)-7EA k +A$=O,բ*Y~D?U1Z涏i[-+O,p,Q¨π|0m$Im}-THYXB@'Zng%݅֯J#` ժe7Gb֛FY<jLn=^4fX]Zu6 kb|iڕoysiBxW{Sº$zh ̻$VïpOjբSxW0*X aDV WqҬ]OC5}ZFu[a Qz+zh j74[Ud}@Lo ga+¹8ݜdKyj:Eu:] x"##jk"CI#*F3RMsWT|U:RgMltx ;{̆;h5,y-js!CV?eC0TQ\,բ*Y~D?UtW7 /5h1_?j?c@% Z' aOPIEs!CV?eC0TQ\,բ*4h HbR1%*=HRq@QEitY.lŧ,<-ǥOj72:"u\jk;k7Žŷ>KxieWla$> >q|V{[Bz-?? 5mIٯo 26HX+p "S7ljm*}@g-r 0/3־v5^ZگËsGn+Y$VKmH,vBg9|5]wY?m|mئh1/͜{$]jj{v@h>6֋6>kHR˪e2]?fmrEzLńf[=o7%vr@5jr=U uM,h<^lX9 |{ROSP5KK+覕*$` ?׃|*mMKAO>4^nȸqfV9Xu$' 75x7w5M%EK/eo>^ xCcxԝy݀ޡ?imK+Xt G_Xn\ (I`/_!|LzJݎ AMVudn0+CT9{m~0n";mg#`a5fsd[oו~-m>޺;Ksj,JegTY-F]C uk64<)h2+׉c DM L;#xD_}(&M|cJO$ӴϢ\ o\sIlF8`@_u/<+3ip&yaDe cU=__ +Z zR^]~RF7$aGXȪ 8xW˥:Wt߇ԡIQX Q2@]'Í{G-w6|IM7VwhcfXZ7/tNr oWo(QEQEzO%yUOWQ%_63\eg%Gj:1k~Ծ#<>fD̡ :)BxcQ_m4fXu %vf ȥ9 IT$+  y}K_tZ|(M,<[y3Bo5UR.ܷG#q$.P_ƞKk=wL]QY U +f^Onߋt;[,mJvcll H Fudž|E?u.mkhZ|vjkkTb@Xdf č43ӵB[uK_!{^I> rѰ wC+=vUσ .QЃiIXPd#s >!V=ŝ ."RH #UN>~-q OH#"M s»'◆=[Z~2b `!m,Jȱ)Y&5AtqWIwk%Iٶ'>.Ŵ[jG,LJw~wl+k;8`m..f8r7;ɬV5C*%SX_k>? -4]CK,$BrOc x:um}E_ :nj=<HF5#i<&'*Frax×MZΜq]Ӟm;Ooh:Dž|%/Wj>& [O e |&H{ͧMXj>.dm,[[;dn Τ%ooKo{|[SWKKǞcyxdW(YR:Vl+'Q[:cTOQo㡯|C^W|9h _iyh/~ \`瓒k|OKŖz/u[o~ciO 2дGq(daJWSJ߁3F~V_sz w+x'ſٿ~156,H'[BE#p\+>*uOj>xoSF@-#6B`ϴ/%@odž5>61kGn; t>צYNrI %Gsӂ+npٿ>4oiBu+n딸F* :8G̱) YrOrMwMYؘQHŧ+m8vϛFG]p-Ot6:LQE Rtx~\5u]"1$ [ٔ€5誚eiVvV[j"VJAj3X n^w eı Ѱpj`t4QYZ+^tK`ua3X9J] V{y_i[\ifɹIXQǘ+JOmۃҪ7ᾓxCCei_jw;FM|[@ 5/ j^1JЮ͎{?.g2~H玵=/ u x:KHb^+P8T7 22< ?M|[@ 5/ j|/:AEtTBł7ߵr _RjᯈMn/~⫗>9njC:'%_jWp]vwm 9åΧ_EG&-y§п綵;G*} {k_<jM|[@ 5/ j B//Ѩ?5o4_LιYX6ϻs+d1}o>=^|w57us+q>m$r%%I9QHo~Ÿey^Kk-*XI q<1θ$+8}FF"^.biq`ӡFN1 #]êg>*>qx~^]Ư+ 69S1BBO1#G@gP ji:/;M=Vh_C ˖I&F]mo đ[J$IcT5 cPp/4jҖdUYE X ݘ+ MQ26j7gM |in%ˋ[>lZi$K"~u-ij,Ƨ! ZTVݵ'ʵxEWPwKχm۝HfNmԄ^Q|;dQ/6e^84|7am$7>τlw\p|^"jGº{fЮ/}+nI󥹹Ge K*Hpp8^!Wo#ᾣ<8K̸<6UX2 /ܱ1jzi7İ\^DFڎg('&h*}ּm+:c^&w_m~%wrVKjůZi^/th,%P~-1䍣.*I(!rF hIo/ DfYSŜ]-Ֆ{ˁ^FAb bKios.ͼtaiq!Oc'y42Nm[ K }IQ/eitP88ݜd։^wϗG߅ۿe֡o[\ˤ<] CmsKyZgؠX$񞔘5_ @S v8e`ҵ 5h#0~3'=85/Nכ+}>L5YX5|>t?i^Xr5ږs誑r}|4_Z,VG/˧ z%:lٰmょ s_Z>^-jn ~ʕ~쉺a؎EG oy^o|ϳǶh\ ZZI߁W w?|=o CP3[(kOb;:N<+`O5'1\Y|k thE*%H=#`oE(o.V{H0'fS:JxsQ-mOK̎+!tqE÷`kG'ۻ=8YǨj n|ˈb-+,˜9 K/'Z:(&0<|S^q; Q^SvPWc.nmu*lM l,pNYA͗ų[GQgLsqy>Y0a* ~loW.xL1z,҇}}b #mpRE鵸 ]ƋGNG—zg5=jAW6+qmb7ln#$_Bym.Uh{¿5 __iW{;Q 4J;\'9 +9_׆fA:{j 0@+2 k+ļIQh沷K}qtH- ZCs3y` HlW3ڧ&x)'\iZ :;Y» )PsL-_J>|yѓ֥{;{-5g[C424Rn3s\GgxbU`:n2vaz .UEݸfs7@ sç^u &U+Ym tl  .ܷ>_WWFg]ZxfOAuf1`6LHdc2P 'L/ iCZ״{;]"Xiya3ccׅ[~>& 6{$6۷fBsTz_%$1W 7xn֒߼\G .Ű܃5y| _g[k2i5S RC\$7]mZh!?=Dž=|7/xTt??NmY&7k rs׎R\+y/5I[-nKs-$O$eUO ,`jsh&uO,y+FT]$(lqo~_Bdҵi.m෺)!UP0洧MԜiH"_;+>"|H Ze}KRglNvzF]mHRKyI"ȐN03]OU$αTߎQ^acK],省̏#DAǑBhdfv>0o,en5 !)oXޑ>P m @*~8j+n+傻+˴j~F4a>pA, /1*Kg}ŅK-q29 0w$]⣪ތ+-0M;~E|o}ˍhN\?β(cp(((C)[Gޫ"u8xim"0pFo!A`+ :(<_@/(EީicQ8h-ြS*n'rsl~/ %(m5g7 9gOKyWNly.[m4jwAIh"gTWmT~Z-<_YY:6fm\dDH}dž럳nLj"vĹmu {`FF=>|6[p|D.nto^O>Ӻ!QG׫I%ߑx6Ν 6[5i*>^oĝw7vZmޛq!I.MWֽ*mo\i[c<-:x_Ie$: kjBI `8 1O?{mkZ-U|CZV;s#`,i*k/8>E Vh b#뺕,۸Ks,Y'mvbWy1q6Yj3x|H27j:E 4$=ȓKۉ ]Ecc̼?nJwڌl`6!HΡQAcߚf[=Ze;ukkh++r0A8*yKK^|:?m:ڴ79sp߇#"bYhyU]NUSmRCTMfMbyD,C#Z1A+VBO/z'];OOҙetHiP,*{/UaQ3|2;+y-u[yD[ܸ$7 AEk;Y|Qyu+-^]nAkh˝yѬ̪FG1 *m,r <7iƦuZfk&y>$ o^qy<;im^FNcO|Ţq(!V ꊨKJ]yX%%׈Uu[kcORŜq\5Ʌ?sʴO^=Y>:N& ,g"d`++c$G#o͟57PH5_KZj K 22P@$*MF=0񞗡F$xcPۆV_,H FHQVrJw#"J緖Κ[u> o^ѭu]- M;&1#}#Ԓ B%?u1(^8 W!OZ-ڕF>[ D5 `@\@݌X:P?DiP_+ ( ( (+[Ir#ЃaIVW_ uEfe]fuA7ZTPoUo?W_ uEfe]fuA7ZTPoUo?W_ uEfe]fuA7ZTPoUo?W_ uEfe]fuA7ZTPoUo?W_ t3h/[9pܨXg^Y)3E^jw1NuBR{3{|AʺN(/bIͺL1~y_isݟ^z|?3{|AʺM0*@eS(Uo?W_ u>5ߺEnFLA0=Tx'=-s]7Q@0}iuA7GUo?Qo iHMMT u+-Cԭ!C<㣩U#PUo?W_ uOg쌒K C#0C& [ # N1c\xoOuBKcI_X;UYU@۽tW_ te]f>xsV luпxL}q=\SK=L W_ te]f?E4:)T쫯 ?3{|A"_O^} BfiFⴄu uA7GUo?Sf֬̒K C#0E& [F98YiO5 K5[QK(b" 73{|Aʺ /újw~Y>SWz.e]fuA7TQSWz.e]fՋ;9m݂0eڋXrzPw}}z0Կе^se ׿x)A[)UFI&6W#hw6wZ^jWv"f^F6|go^T|Dc[k^5[]"Ran%mmIW TrNv$㏇=lj"YF;>(m륻hBݳ|&3_pSX!ua9&6ls$QKe߳?,Lge[YB |M)?Oa5hvĖ*ĭmpr[6dm9zᆩOWi?Eφ5o_nsŹhٷy(+}Ϣ=;R&(UrM%o%UXq|H6Au--H-{^Ox=tjn<]h'7z |v^.:D=`̪ .oWVkC^q*bwo\w(ok ѵ{!5}rI{+ᲆwo2,F?? $jG,-t5DgGE>ae$p{ <c9?#qj;Ƶpw|_FEy gHEkecc2En~g8]>?7mܘ!/C/t`DeIsjmzG>"%<8|N jWZ|+ײ[U"*$1aRx:WӒL]Ni%ϓ,sv%'z Oj:LՔ7s#q$A,2ƒe N݊'71WKln>f6fvy\y`mrNvg.52"PމVl(((((((((aK? #I'tY"I;h -G+ з_MP/ %NC-@|؉~XEnqLQEb? %ݎK5D|_x[#"hWoH?_x[#"h6m,షK{h#(*Y pzu;YܑE+ з_M-Bޑ4Q@+ з_M-Bޑ4Q@+ з_M-Bޑ4Q@6X[o ,Q UQ/<F.-6w9ifُ#&(WoH?_x[#"hWoH?_x[#"hWoHڶ8*PQEfotoxx-20.08/images/batch-convert.jpg000066400000000000000000001311541362435004500175670ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 414 528 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((t" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?.M hBSWS=iV{i] >gyOʽVz<Ɩ6{.nm#osoy>lzJ5YX7Piq5fu^UFTs{Q`93>VzHtV 杀`X 9XK[K 原N3MHW^Q  ~*M^^ JgA3"h[hދVl/ geߴ]VϦEo :*";rbqŨ]ࣉ7h߉9 Lr/ގD+FLJ;Wtl]IШLǐq_wRo>/( tŽ?7]8ϞZ7ZwX?Gxku|Ѽп5? ;_֍־?nQcG=o>o>/( tŽ?7EyyGxkuwX,[ϭϭ} ;_5?`>z}h}k_QcG?nF_BŽ?7G( tXZ7ZwX?Gxku|Ѽп5? ;_֕&due8e E} ;_5?`#1(A[{?( tŽ?7Er=%5)mS {ӎMa1mx?ޜz׷Ž?7G( tX֠43 ?qf^3uvH˺OFD9@r {_( tŽ?7EKR]B_햨#UESQQ|o5[e deU$(wX?Gxkus֍־?nQcG=o>o>/( tŽ?7EyyGxkuwX,[ϭϭ} ;_5?`>z}h}k_QcG?nF_BŽ?7G( tXZ7ZwX?Gxku|Ѽп5? ;_[-/2)R#M8u?nQcG=3"ȎDۃ^&}:՜&$9?kGxkuwX,ϗď$a:+jijC :JoS~?5? ;_e[ GN[ '*N<TfdS g7ؼ>_n8PWU_ QⰖBA$؊Iq[omvLJQK-D&͜\0CwWƚOA=k6|m Vx˚{C\_oVA;78 (zh$WOc^|B]YcG+zЬ5]?G|F*d2#Q\'VSZv. ʛeU8ץAmO4˫K 'd FE.!r3uEGQXtci) Cpyr=q^}xZ UKkodF_,̹;H-!XMج//&$G3Hs3@V˧%Zޞ {A񭾯ɪ6HKw#y'PhV771]XMwb\ة8S44= n-V}" F[cXtorx@?&kѴOXuh8VrA<*7ڌ-l.iOoU.Pd$`;Z+_]%ݤ%IB)c»2 ⲿ9Ct-FT/u {naXRO!I4E(((((((((((((q]6+ ?$|9zGX4_TFOP{!s[˭UiKnf F52bM/Pt]YA- o+Ĺ_/&9 u+9d(L i:|*A8ՙ4-JY4}f F%K*3ƻC8"./u~hĹc/KnQQokSI.VM M>x[[oi%woe;gmbb\^EsA{@׆0q{>'soCPRk-Jylt߲Mxצ;)eYs*˼pI8%Itؗ?Q4b\^Eд \OKmVL 8.M( H$!'ivx/[_/&i&dk bl$uwWEP8M+z^fIK&qԀO %^KL_9DCQJ$s^E`q_W-W:|vJ!%YQYvY ^EFXmc2n І] xM[\]3XC KX]J##=gi |.^2!K2"Mrx.+Q@Š((((((((((((:*Fy2iUu!+ ! S@AR= | h&KemR;{Vz^qpJ_l Z̳+NO;iS3ڋԅ8B<꺮HFKZIιyHBeuOV^VӮ,m1t6e.*;sJZ [I.4ĺ׈ HH$h8!Gá=$ulaՖ+|SoJ?%O'ҋ_w:+ǵjo> =2HB ةWzZd` w^焼[mN:yւE`&86hp?0T?ٖ?l'ڶyOFq3\wo]*MZY.%av4q5ư>Yk=eA\HpW vW`?WG ?{k~'6/`B<+T6G=~ ѯd][z·}lv>s+,FFw{pcnQcvw!2t ښ~bڤ2c 8,2@xv\x3Ae![;خSS.6<8rr{gۻE,6128=<_ׯ=9/ŵIH̩nnG @$sN"j70DYS|F`2{ ܐ+<% tHlu hmMx.Uh(fpu@r:zVVVD<ʗzͷi"}&T$zႆx_C={=. {ic ۽JS|t0WI%͖mB_ͫ7/(C! 0:u_7օ5sB(hPCrz4=7 Zmg`?WG ?{^&k[Cqߵ_/- ,LnK<!ZV2 B$#nĝ? ~Z@`\rܛivĂBo;xUY6'Os$i5ToAB\Wj .5n Ҡ!$g0W؆ĈvONW|E X8*3Z֯Xxk:jS%."`D*#+\oF?0Q^y_RE[V/lO.,'Ua"(G ?{`?WZPG ?{`?WZPG ?{`?WZPG ?{`?WZPG ?{`?WZPG ?{`?WZPG ?{`?WZPG ?{`?WZPG ?{`?WZPG ?{`?WZP7aCstgD! <*HgYyR}̻P .f`8 ժ|Ai)oj}ÌW)0h%66IDsZsc{wvĐ-dLC*縭!*AGz(ŵr8´ rZSgloVo3 TP[x{E{)GӚy<٠6RG.0O5kCʳl\>zӢ)˥iҭahx1r"q||z}O.q^# "*˫{+Y.og1"RO#m汕yQ lBXt=V]>}͞$ {knYYSyA4|@~m6seGՕ{Wk,W([K;\Z?7ZUj{q殙$sҶWf@na{gNm>s摳\7g{:ixI4fv1%2Jbi73_40#/7 |Fjikc4K"Yk'1ncڨ隥N}f#dϦE] i|Vy#4v@8TU4^]'Ddth(7)*dw (g:6caqi |WxOQB朗o ,ywPh, pø$wb/5[yarD;H?-͎o^[jmf]% 9>o=FP.- },D1*i'#K$[h%(fUx* ]3 zƭqjA´bFAɿ`aZio# $QZOfE@Fbƞ߸BJq86?t`M >VúmaD}LV0Rtg`O❭V֚\۵ʀ&SGbا^k+&[64NK)9Z^xHK=&,m[XHTP\ei5ӈ 9ڠd.]_6mũ2mMUĸ"<;NyzU ?i^h}IҶHf R&fB86 8v: ,m,\FXd}s<.eJ뵞K0  [^=sn5R;acw_.?_&\4MeWJV=RLl}U͟D2n?i`Mlq4^W۴M6?umԠFI#9ćbORhO ;zO4AI_&H _i ;zO5V ='i? ӿ /Uh _i ;zO5V ='i? ӿ /Uh _i ;zO5V ='i? ӿ /Uh _i ;zO5VX-oc3 rNJ⥢+|cqh牵mS÷sImJ#H9;06ױT/ko$dDyUkgRTD a.َA6n nj?M6vPܖ D@GL8t^503Cp 8ΕmiVwB4UAZ}eOo$=ztwnVh9y5GSf_%ҭ^{^k-ţMR&mӚM:ܠ[İ9P1O-DDxP>=ĶGEq%V+`128< Ci鐾gqi5No`p6^!"4 vGko"N†@B=(5mJ]wpK }"kj"q Yvۊ.3/u|)9\>+ 4E#SD Hx* w-pcs{/mD[nݰg>ۙ Oo밚&7ms"F ;ݼb\G&-2`GRdcC m=(Zz%irqe->~aێk|}+oSү.H2.u!8KP]X7%Q1xL}s kDDᶜハlQz7]Z%Hz2GjZuI{.'Ε#jzsW4y/z|:YyXn -\`t@?]Ci+ z´5+ LPK+ġUGsqgվhג3Hzn5s^.euAcڸSjOK:k^mEty>rmf-no G# 9o-'k|GA./][{մ{u @8g> _#G&!Ԣ:wx6}ߎ{RN ImZ?ߥҷi=8Hhr[8늷*ú4-KqZ#/37C,l1g\E?-'֟jooMJImTPG^V&]O>"kOӴ"rHe̲ r8=OjBOѼp"osCv-CiLdH1gI S-oRTYm8 22dj 6iUߛwM\4H7U|8V|# 0`/io|96xl_ ImZ֟š#ši4r07,KCӢ&G#GmwŃ\qgo6;xsݗ&I eדEW/]Ʒ;o4F3(`3WY6-xt AmOv:t4>,=YBherKt!`AxuU?nLB%l1:a~Чf[]?WSRMR?²*Fi6ctnT*k|&iM#6 i/n v"tlD0*U|78x  PHDg*F9 T7=E޹C6VSg˚8*qǨ#ZOm?KAaeinזqx+X[d6>bs;{UX/`O^A<% 2kF.ttC>d%HZ,]C[mR&KVyDJȂOf8\gW=;i~W(ߕ CkC֤t=FOnhϐz1(PcHգ.5A Ò,`OG,?W&uw_OXSpuV-ʗ #m06mƟu`֡=fMFMv pFq)W;bA$T\VF~@UڡNj5!C[}?2 c ǭMLYiZ% 2dq@_IӞ,ѬaSحP +aL8Kt cՈIPiXgf v$te_jK<։K<g4@TO4q8]~+%rM* ր ҴK IRK$*0:mqTg3y1@ۺO?JfMwo4P)i]$N*!ḱ]N'UkXU 8= PC*yuU'VjGsªI5 2yUG@A+O1[jZ&(.cO.aF۔EwAkUG&4wwIZ?t ZժUWTfK 3#p7) q@ۺO?JC>g &Vk~<^`]qL ?ۺO?JAeeǖ,>PA+On?YW7ֶ\\E,Vv dRA+On?T ۺO?JR.n?GA+Oy}kd`@g`pdFzze.тY~JۺO?*(A+On?T7T,X^_Wx*:  'Vh%iV5o|E)85n+5ԶG4b)G~G\tve<nUAk85 9#1äq*9&i&Xxۨeb]BN{Td2O ~5b9&մfaimXʂ `b9ihZQCeǣqYEr6hen$TԴ;Fchl`-1s]}##"c!?EWUu!洎/;,3cN[~e} ;yc20|s@)<'}'LX,.⽔߱vcsVNqj\D1GbF]v'yt ]2=:]'O}>3kd1/~OjzZAhmq$0dlC*<j&]*Q6Mǟr޿wxImkƟk q2hɍV<(?xgMzeE=D+J@,G$Jm-ݛxMڡLPoNJ대qG`yCfb1b8ڣ9RX~#Ե9/a,Wkwն e+ ziz{9v.f+(>z2<ڒ2:|魐C? ?OH|MiZ+k}) $gJ ϧ¾4V&wDJ2H%+E{[2V +,5)$d9Ap=)_ǹխ:.qqS::6KB$Z5C.ȔF;s^xbW׬uZ,?e7M]hUޟ֙c5Xo$Ѧ:aH#7/7#K" n¨,ȫ&i@hsDzzw3W,l-Rkt""T)VIZ8|6ƹOX~s>=|rUc[]+Uܜ%}=*G;ثuM{v&>dIG$ :t=$C 6!t|VNèċ#`9$q\/u4OjoKQI+G3T*=5 xJ+MB J-T65xLTln8 gZ֞ ԛR_ږtJʄ )v_"<;.ev]DX3EDo/˸k+(b8ׯzm5coZ۱k_0lVF3?<]5k-Rv_]Z30?qeRQ@8X1֟ʀY2A`1Ԝ}u(:hg+o!w8qVI-b,1O%<w~4} #R'b-EJȧ:PqQ.:x϶c<ׯ5IGŢ[Oi .AG"JҒ\yrzj'k{;f7`-1)s@WkrxoM6am=Ȩ9v᜔t:cZ9b٩>ݹA'չuirwXO-F1)#J`JH`ZXCo ^h$HYc&xuQA>jx{SUޭ]-;eE)eńro$p vЭ㸎E"K ʖ<ˍ6k+i2D +dr9<(lVؾ;֯=WW#. ],8O" S8|;AC*4@tخ1,0O>J=G#<'DNIBm3mEC}EPM ӫFwo9ۯz??+fӴ`O4K -7,5 // MztM?DZmVfH#yǵ =K}iW3Og]M 1e\2޺etVF 29Wh66qwWע+T+0ܹ]Nz5ڽF"ѭm&M@DIaqC=ΩfZ˛Y-19:޼Bqʒd (%$֕xt3mꋥMug[DN$XI&6n>6O+o2nrw;3}Hf8=+. KR_;K-3eU W5(}:$'E>GtkUH(P-/'QadB%P@%O# s:x֚KΑ#I|S{*Ʃr%ҭy- c(KkmP,^S @<5gOnN }@:;&z?5îh|򚐋/4?|W߷0wtz::\_kCJc=!+:,SHk ?+};( ɯ#-=e P >),?9gEXkui"c`B IBԯ~¶]ONbqw=4NT[󽴿)dC^@^B=ĭWv#xM!l"0aH# OS+kgi5+#t{f}ɉvxP{t_?=j1;bn$u{:z+ϦpEO --ca n*Wv\[P}6=īH7c+? 563]Cq,p"; s"("<;? 6K9/<-:Mx~ll{ˆ|rjlmi7/L] Ǩ }!eW=Fh.5%#\1\&{W;)<{k?t˝KÃNdD{hJ`#֠擭 7Z4cn ,.q.[Ex>ڏct~u65̳yDGyN'&l?k}|K5{|Ź# 8dt7 [][F簰~j%/noy$0*18ː1CȪ~6-72iRj ЄFiL6;uI_wEbx"٭<%@W7e P&#۔Abkn4QEQEQEQEQEQEQEQEQEQ\=XBqq: +ź^mJ y?8dS-7H>iq ۈ$E$ۜ dvLӦK[+{ { F$%A3ڨ|F>%m$:4 0hM`zu;ʬЬGh  cS-"SԖᦛKU'^5PCc[Eco% w:|`a$W{dMc @ !#uǭOEPnP+C4 p{TzE'+vm*kMwDjBg33OD|Tt^:L)B\}(;p15k?OG}X?д?ׯ aܛe׬Οj[ۋ'*V(3Iv xt7E 9ozڊľ&I㷲{;(!<zST>3xnK$÷:6EoXqg`Fp3k(1X&e=̗"ݡZ/z1ռ3xkƶn@Oio@۔=k:}Q7_Ļfд_חxrUS6;6vO b?Ƨ]ECUgql3+, u==(H<_@׬Οh^?:??:]:/E=Ac~t;P/{ˋVH\X %J.NXMN iw0K8PŠ` 퐍VEso {.bTv +hV;qk.A`Y/[Z=x{C>UأH * *C9{%bƗs0~Y!2MZ<7uihOnnO=Upz ۟kШ+_סU+LMYbK_=btA3-tNkWzjA&KһZ(as:Nqiy="ڭ[&F 6浼%Z]mci*@JyJJ(z^ [E-kR+9V[hJϸ~e` ֪~?<3i7eI-l'#28=ׯZ^7>0~UTb2=#ÖiծM$BIn(aEPEPEPEPEPEPEPXY#aԡ[ĎNqǯ+f^\[^K 0= . !yxP#:6}k׼G {}ҳ!ʹ!H'n[k6: $`VN{oն)esdx60OZjk Ax"Oԙd"|H pCu.J;kUO\K`rː sE𮧩)ཊ$ l;IÈdXEqrG>$nZ)x,vmcqzψdls^Gei p `J m$7n7>UpsTt~2xKiuo:]Zܠ bUx##ޗD>E=+ĺVu. 7ƞGJXdӥR>:mcAQu6%Wo8FP)8'Բޭs?U&o}[Z}.f$U,=tK[t 6ݻ$!YM|cu{++c-qDłp-gW-4ߛmpPAH5eQb=1s"ӵ5]HmRk[Y/L-9D,$.y{2 7gD~.#_]h$ՉPGP_-:uڡGCɳnXǍ9[iw\"3\ j}k7ZzV-MāEȻߨ c d]JԕעֱU7'y['*Uӧ]&9-1TUe2y?r:Vׁ37Մ6ӛƻ om$YUB64N W lʹxg$fS\7kV3XnO5QDfFR;G$|/F/[~F}#CL֊tƙ㐖PpsVGm3Z ff-2m&AS؜Om1<]Cjifk(VV؂esx?H,}G%:sz?)$rG!N3*uovCu-1rF$} tQ<)kOoIJ̠0ŷ!^[uSMKHIVg2<:VƭhGgݼˈ+v[z@xP+ k;5MImH)<aR'N85z!.+M2Bi2b10PBUPyEax#Z%d㑣bT(⟈fmyu4.XMޔ4W'u BĚWvbI~`ߗje|@7ז:j؝Utݤ2<1@qދka_KEy~0ַ[j?>4Ъ /|pj&Akoi }tofb}({_ca3^S\|FQ/mfp媓 qCvVԿ/R?o.\C[νy%ׇ.57Yn\>o9kχ|u ֬2I[~|oe,l~ChCϼߐ2K]B;R{%e.؟z 8.95Zj7ot'?% ޶7?Ə7ZewzՄn>SnMP\֩{=D\Oii, qi?2JΧ+Cϼߐ>7?ƀ9 -> ;J[tl> rr:օ/y\]XA%rO9vus.qy!4}oTmɥhLwHϦX֕Ay!4}oOYPh6qxZ!彺;s!$%>}oho@V^ l{]F .W7p6#θԁؓJӓM[%ـjK.O$w#n}oho@xL]R;j;3n30&388M2ޢh&-`"3thoG}?Nm:v!$"^|1dqSi>ҵBVV[C 0ywo7 >~Chޓm 3Xƞt D$kß YVuK-]dvڅ&8뚹JO7Z` sQgi;M47~ChG𵶙MBkuy. S[xquyk Ք(Iʣj0xM/oxZ?M7OSXAeّ006*#j焼xoTSKSR^[Fx"58ox NCh+;M4ox\ֹ֥7~ChCϼߐ >~ChCϼߐ >~ChCϼߐ >~ChCϼߐ >~ChCϼߐ >~ChCϼߐ >~ChCϼߐ&,y2{1Z*ߵlsՊ5}Xh Pz2 sۀOҀ5u+"O@'O29wahw"O _s׏]kX4OZK3^͹fx:zei7l.t]24WJn,-^!:ÛW ghB *moƗ:R tmKOX3q9@Eo:Ȓ* V^)gwio5\yT) qPQ\Uu%Ԯ-5m:ĶuKi)0RiYZ8ZVPJ!%vH9#W'm*hqSB u,Vin5TWsɝ!s@3@XO֚5͵1qx :Twα.UӾc;:ڜӰt {j.{Ρ[R=Ojl3 K67Pv 8B?Zm|ico&jS@sI6Rv4?kť꺵bʑN<#e}=u?畇?:t}:M#PMRԑE %ޯnTq}맕 S,.RT<9=OK Ev9;zbt߀Қ\o0&.2Qxr E.KW x _ڭ`s?kӵa-ŨW8AZ̾IIL\@܁"}kmږK إͤ2vz1,κSfя^o? C:PQ2X}k؋k3O:tPM=;_=GR770@F YrsڲΛ|zO|єRG*4rH*$>5sdԮJ;͒7J޹mXBjǼ㵙 xCxPH%UfI95֞MYo Ǽx˵zҵ/ֆ2A$D(Bx۟H+;'ź7<'8ۜ^'oK?7-qfq4;$g15,ѯZ9L,p}F%XP:aӢx4F](vǩkGO涙c ;Ԉv^Jw<1@qmY; ~U'/.p90IT3ּghik\2y%q.ϴJ#cAilkUk}NEZ6"MҞ tX*DukOKfo5!OE%C3?RDoMJڳ $Սs!ΉB;-gTE*W$g#ʺ?;С7n/4dSKn83R#O͎ൾҧi%Yvjv b+b[(d@$WOf^J:;ʹ:U/:OѮS^.{M7k!KXݣB8AATԴ{K۝"i!< #od٘P9lsg}D7KycRΟ|}nC[f\ՇI=5$)[$7;qyw|Sm#[A_.').T!N8'CijWtdУ[ÃQo4'I!hI*ݬ}-У ,72HL2zH#qmDX8ѝ0 rxѦ'CYwRG?1䁤 ÓM^*6^Z> `KxKT0KiF,p9j=QLaEPEPEPEPEPEPWSy6 RD$6{s46g?Oﯭdke%)XA {(]œH6`fߟ_}9=PAs` As/AZ{sӟ޸mwwx[2Y5߉7#Hq|؁7UTzV{+UOj&$:vܐ7miW5̗?2 = yd?l|\ڮi/.WT%Jfhbd @]6܍ZӍرbv'vnwm۞Z:>/ Cߎ|JV5n)c>έx^]^򴲬AnFܚʸV֖ukp]NmSZif ̔ 0~TϮ=+=lO%ﱬ+p%K89#xͭuGȻm!ПJydvWfo-$V 9ϥ48%=qӏg럧ej~/Z[xsQ-F)'$1/pO=kBűvFΖO$;pz8%=q/ӏgǙtœq$&y<o^ukA$5oKiwx!O&&;p/ *P8|3kW_֬.4{;1{B+ ߼-.0Fq MBDtŴ `r1(G_H;j?OIx-;X].LԵ;ⲍ =waQsAAeop4$_1 H8KB_\xsHtC?X?럯f|APm3V{K6iqvVo^x[:3mwcY.De[*̤POJy:zBݻ-1Uw%?矧?׏ڹAMw@"b ;B}@U[jR_i6nkkQd za8<H Eh#P:Y4CWڮ:j&(/UH0 >՛oZZzc,g',:nQYى7PCLyTh#?E]lմEX7+g@K5Úuk?Co$k &Oph6QUgW;m#Z:E·X-ݕҽ>|8 yRό46mKK[n yq(s4-lC3|UkO&i^S=:q+~&kv:vе?ˑ+.*ȲH@ zn$]yV Up8ۘcG}N9ܬamcMMsA{]??Lbb\^EdYy?Ȁ9\ E뺞qv4ц Gs)ttۋ_Sk骭5?|ey[xފ-]ey}~X\lHe9psW_}o}݌vRۨGTPCpvP(zDxzϧߝNJd2 ܋.drs ##]WeńA)MvX>X.kEqs8f.; ux%X\=Un#y# ;[r";-7_A+}~o jzC]X13ȯ\`9%Oqg= PXՊ][C׳KIH( ͙?fZjvOe{q2  9-9eH`68 >[nn'"zw@G|9 7W+E8NW9Ƨn98۟΀9Kvsgԭ ĺM,]ko5;ZEJv[mK2NF v5ޙ_jve~qێ¾wyh9qOjy ڸܤ-Gs`/ŭ3=^>Mv{(~pǑ]'oӎ4kN߿^J(xA\{4muܛ# ;̿_iiyp7] n|/5'o|=}y(_vcןҀ9/ַ:}.&v2GAOjmome4K5CHCzS cןҏ5'oW4_ocO:n7#7Uv`.IUOm&GQ[ٞuo(9?*]v}9J%~3۞¾y}nso/4{ {"%`?'ZZ6~'."{EzH#T9۷<֨ΧnyW~(Y\c:g^}{; _xohmI'ŻE _[I4}a"mK81BG0'tصa- ,q8gvI tWZ۝N~P%~3۞9~Rr7(ȑ,+|z:MxL7bAީ;Irs]pO/ӊ<O/ӊ7׈מԴtӧem"/#jӭ'|q{A o:e(^As]IO/}}8S"}8qM<5ʶe"\{Mݼgy-Ԑi֦E㟯4sNuȼs m/AմJK+P_"rQTʎkcd jv9ȼsz|2DfFU ϯZ^o=|K`F[UӤ"IH1B]C}>m?b8<q+^n{ěO j LWQtԣ'D9GJ~m?b?ا~8 G5xӯMմ7cc:Cs8#FyC f" 6sVQoZ?Rz?{ox2^MŸGf j.ojYNqlƫ_qvm?b?ؠ K\c?<]gwW=6$qEm &di's"u2E;>x'RzOCro/&9?|6֬únXO$nq)(񠑉ۅY49;ek6w>c P2!(Yq+Cro/&?&h ~6W;xvaYoG18CgAɿ憎w!?%1"ϟQ7i?5iZsj :E-C `%sޜhڍsλ8 %t?ht?j; kOd4BEurPMwLh;+aʑ\+`u i?>i?ZӵF"XN:ޝ$a]$?@s5<24BH2m+iv2vt.`]E*pO?8v:m[PNC<O&u= ךl5UG`=W]izŶy1ڌۜgq C<O&C<O&^,dS3.SS>fk=BaS?xhOC<O&C<O&J{]Vx Y#FX ~5KSUc$zke`/\ {gfXQ 3]BJ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@g_jև֢-紃 r3{c[dz֫i^ݠCe#WS~-,ťޢMèF tn@y_Un@y_U,nqiWIńIW@,me qi죉bMwƒrv$svn@y_Un@y_U IX.u=;V ^%M;lˍrx >K\jz~X 117 239 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((k" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?6*"E[ v}, j[I\oKVd/(}eZ)?[?nYwO%)C]Ţ%)C?[?neum0H̪JTCTKo()#?քQQE1Q@Q@Q@Q@Q@Q@Q@C1cU5TmK܆ QU/KfqU·C'$ zʩ#Li,CDHCp)޲xY|?z2-&'o{ ApȓLD3#SwmI"?V"< duČ>f.ۇLr:L[xܬ_[ T bP']֑/rlG; /.bI;>\hJKI${u)?е mT?ؼvXvGSHvNiF϶L $(7~aVZd:ndI#uv$S;Gמ^ Yi-_kHSzD rN8ۿBOFuO]!.Me3ZFѧF%pM7t@&Gx|ͅidB#z*M%Ji6z@*^寄X^+u Җ~CE([ A$mߧkv[e-±{98la²}FMn'!oMjPDjjqrgpbIU8x8.3\\C$s(^R}~ S&zzlvpWȫu{i<H _ЅN3j#fC6zg4+O6گ+/ oeB@bw180\;xi{E)s Nr)] @8Z$l=i ]oxp}.eD\hP@r{8%vsCGG') I.,`'2R+?3nfcv'SZmdUvE&4y4=WPV1$X\51;7aȐgCT4)f[)[e&vr*)}viLF =3Mm𖱧 {vKX̣+lpO^tx$Co2]Dd A$@]i8N=),ygƘ%UpgTF|!/lAM)޿2{z~^閚_$z=fj |ذFSI[^B8MW}:|(Xngo/瑌{guuأHb(Qc5Tt]ǛZ~nkQEfaEPEPEP0cX4ݲA|P6=ףl?z}ͲA|'^E3l?z6=ק@ '^A|P6=ףl?z}Ew7nGs;l?z6=ץ6:햑v|~;?A _o9@?mzG5Z#>BPO?d CrjFmzF?-5ay -TdYTЌ6=ףl?z+xPԟ`?!k('^A|A _o9G5Z#6=ףl?z>BP,/r+˂=6/'43!U-QEQEQEQEQESTekVꞬqgIc?Gčjm)4q=6؛X*95"V{O,0"VNŨ_i"`p}j[(uNI_γh;0(9m+ 5k)ѯn\s"6(2i{5'WOjliPRxN kiqH۲sԏi-.M.]Vm(G[0U6Ϡ:cj}Qiqm۹(iB8Zڄ+oaciwvm~$i 2Q9Ut mMwPrR6t9W]9=qNBĀJpAV`AROj뮶9Ԟ! KDU$^_яItz/TғCV{.Yfp|>1X[F?@"K%uBpOi,-u6#{kag٭ rH9PvǥKᨬoPIm5D=5|(g4K^Ȑ"Je>+k&LwVG8NV)U&<͋= ޴eya5ݍmŵߘʙvP9)$mvyw5żvŌhmQsV W\Cs4p$ovtu9sz RkX궚Mw8IAƛrq$& xYg6mˆA"G è5<Siqz| &7}ҹU?UYibSP-My@mێ:yؽНڕƬ0(((((!K$LpTԔPg/6mvHeT}R+NAH?Q-Kl:(3Z Gص/ A? nFSfaDH7+IT*PaKEfq?6%!$q'ص/ A? ϱj_ebԿ)Ӣ3>ũR}QkNej̒Y$m$e1 b(fotoxx-20.08/images/batch-photo-date.jpg000066400000000000000000001627651362435004500201670ustar00rootroot00000000000000JFIFExifMM*V^(if%0231#0100Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 600 1000 0 C     C    " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?.ioF|ZUd *lSzmKɺ[y\/W8ă@ZƋQ,mm0I>4=8z+c#˲ڙG;$d}SӠxD?ąT۝TY@\_!AVEU+zcv2WFqڽiK(H!Zpmsʨ,KЁO .2}UNWO:;6ڼm%)5 %Čv2@\Mp+W[:݀ lqqT<]ѤO> 6ߝAepqȰr(']Ǚw:,wtato4rUK?2~KfǺGA1{^j^i2I,o-`o69?w]~=͢]KFǦJR3G0X//QO¾|/PhE޽k$BYe|#w{V/_[_Դ\71ZEw\\ʢ0foq\?|};OK?2~KVjݧ/ hӟ>J`vd2^I^$/Z{*ZFx|:98+;`G˒pp)?~}__R̟|tվ|jwYx]i#ʳ)9  ߏ_u|(&KM,;%2O9=))_R5j~d(T'_h?oj!X3ɝGZѶNn^W#_[RG-ɋG__R̟|uUO{pO^ Zq q(!8*qEj蟴F|.SmR 鍝P,0`EbF3|~ZB__R̟y_0|H]^M*Ώ8HXa#2*A=-~ڏ籱𽦻XXxLr@EnMn'=2}hR恫_>T'Gڥ?% ?i{P݅w}i4sH2Je@Tq\K\kT'Gڥ?% ƋCZԴMV}{k%g *H#^Me*}cJt&kxK`q:4*u&3m\7R̟j~d+/pּ3.T֑nRhؒ'f ٌt.l[ٯ<o&v%0ؾ6\Sqҗ7/>T'Gڥ?% 7s߇>u,Z|:-F,69fߍ |ּoV+)o!3ḐUuTpNy(rnG__R̟x&SĶ~IqbtwLR jfH17[UP]mo@M~K=3Sx$Mۣ$,1,8+is诵K?2~K}__ u}o+\>!k[ؙBIdfeÅ$zsj B/V1񧇯X+4@7;AA]sR̟j~d+Ǿ|{>/jm+UH,gܬ8=b#gb6~//QO±}Mk>3 ?j~d(T'Xk>SG3 ?j~d) G+ghaccqy%Fj(?SԤ`lFZ gU'\m#O 3FoKmKR_9\z] ;&E >^A9=qJftGfϪ؊u]5vut>FwtE,@TYjcCzh+⹻^{jJYȲ^SMϖ$/A.@`T69мczznLѽΙqncrI$jFk>^qO³|Wa}vmm{3_s\U`ıBfٙdhpx>Եm CM#Ilny-Ķp!.Fpg Qǵ__R̟sZv-F )UuϸW\ Zo}kBaɨBĨx={t4rOK?2~K}__~~$%׋ OŢ驨G}1+ Ry8#ݷc7_xsM::'&mGfL_FAaZ}__R̟xNB_x{FоmmSOq `XFZB|͊sW ^'Ə j#Dt/<# HYS#–UvǶhr֗^v>T'Gڥ?% 򻿊^/u_ Cqɨص{7Ywa0ϭxo{ThN+ d~3`9[bڥ?% >//Woǻ|1ŭK2ݴnۗ QkUz>t/]GnFt vW.pJq(>//QO¾bIG/}Ehه8//QO¾Z?i燼/CR4=ψLԴaHSƒ7Iִ=B{;"ںU@ 1$!yq[;R̟j~d+|OG>մyu J]jGGj(.3ҧs[hmv-2NmY&}2{YG$r =%+R̟j~d+Oomsgľ|3gaqkzFf|f9|UMߊ>x/__N&qd+8sl˥7>T'Gڥ?% H֭^ῈQYtkcŋv`lgkW\7žlN_ZI{ke{g(yD g&`>T'Gڥ?% շ~KO g縶J//Wڇ]i[o<'G/5ኄ0ӝjKu^.VSK9#wpJ1M*:ڒnm{T'Gڥ?%  [A&Sij?qZmq學M¹$/Wh+]h]2O l7OK?2~KhY4s1XT'Gڥ?% Y4}9XT'Gڥ?% Y4}9XT'K%n[naeO.9r۱:sұ~}Mkx~c!8jZֹu뺂礣@Z?fA2:ctu'}1{)LT`q옿礟 `ዏ%O'j=?S> š/!EPY8$kԤY!ᑏ-PO?R}xɋzI&/'?Œ68XrS70k=vDddQa8U|Ieqo0], t۷tee;x8 z옿礟 ?b~c+G4NA&#KXmvPE##:\\H&( vq]X=IOOGL_Ohqcċ _i$ h}os\Ppr<GZmOenb82@#kݿb~c(ɋzIK!GOٵ`{}~^z:\`1(w7__ƴΣi +=6Ѣ5=j"DvVpR6,뎕1I?1d=$Q/ߖ_|\O]ggi_%U]%Q!|1.G\s|Oy/OәaQuᱎX1I?1d=$Q!ϵ߅-]{z-{N#e!\8\|Rnα *+k,|&q!߹>n9O옿礟 ?b~c(7:$xmU ol(=}i|<;xS[ k =2}-*?| ;+?b~c(ɋzIi_稹}moQ.|O ndP.0*(XTmQpYSa[ "} ?V. SH+b~c(ɋzI%]>hxV5ǡZCO?K39|\$a䞀gQa^06ЏB{%_0ʗv)oɋzI&/'?ŽWsS{᷊xO>2կWH`Ьy< wP q5koƝ<6ȅrQln1I?1d=$S吹>$ ^k7ԛ·~M$%cPWUwsU}j r7LQjJ/ 9],1'lps1I?1d=$R啭u7^O_q~!du;McI|&Uup=BmI7OĞӼ[:zI%k PȠmL`t+ɋzI&/'?‡=I-#gK _|CmOz=dWVDg>RO=}kξյcUvXAj7w~o34o?$&/'?옿礟 9d<_.K?Nʹbsґ&鐂͒fMe8H<xWР4Uζ]-bw?^GL_O>Y4O:Mx?|a4{[# 0 8^1I?1d=$RsDg?ڣSb~c(ɋzIAljOɋzI&/'?ŽY4F}>?&/'?옿礟 9d:R?AX/gџ~J}$Uvpf-c vH D9Lj屚k|aQ ;{u5ʆIypxw<@u-N k0]dB| Xk߿b~c(ɋzIyU 7},G XkY;jdl͹h%mN`Ŝ~ң&V6~c|ҵb~c(ɋzIWGD͑3JsIOk~x{ÒiGLҵxF]xRjPKr!*ÀxLɋzI&/'?ŽY4JnmbmϾYnc`+GГV:3n~ֶvS)`3r=w?1I?1d=$Q!Dٴx;U]cLW E 9q1G{gx_IyqewTdkYL|s1Ǿd=$QG,'k]|F>3Х6~$mbc G$(B ޤ}m'_ԼdeửYV6fȪpb1I?1d=$Q'[]O;G~C&֘*U\s8=3Ŀ  +i:Qmڽ$)$rĞy옿礟 ?b~c)xgټ7o~IY`4[C~|٦~Wo}3±JCLmɋzI&/'?ŽY^_Zx{>}RDk@j\S]T%|Y}l٦m5դ6mnUHǓ޽7&/'?옿礟 \9_gG~1կX nB@x6U9U~6#;_aB n kPYY JTb&/'?옿礟 9d.hQarI>-@׮XHnO9dx*us\Bmi> \h#T %7FA _Ad=$QG,|}KX5:󢻖O5:eX_wKwg_& h@FЈ:;,Ksҽ&/'?옿礟 9X!©3i|%#Lہ/fGcgG/$^^׼1mqg <2d*G;zgL_O1I?1d>tK~>mb;ȬGfvryk|17aMڧ9r.t[Khe@qOA^GL_O 2A/|ּu/#[]*[>UbJ.,|==Uͷ$]y5}6)*'}r9>GL_O%+[ 4~i1Eo\\Kï0%#>oW߆ l,޲ͬg--Sv x1I?1d=$Q C>ljGL_OhgT}L_O1I?1r93Q1I?1d=$Q ϶]Z'X1I?1Bmۂ 601Zd& OW'vu`ޛ˺aAɖ7_`:[Qq-83UbǠ9owZ?.?h+E,a4}?WEͨw kLN ~lO"ʑ0a_%Al ëѿh/ŪֺP0Ú,)pvu  1_x] 5kۨ-I %Z'p 223Lmxc0--/dk *Y!R$y{MlW X|y xJeɠ0c#0q*(2+ּwxw&x_OѓSUa5֢.$Pt%eʴ 6@H'xvֶVϣ(<;ͨx^]z; \}N9Hb2T=U]RuUl0HVpcFc  OK[_*ߙ=~9AKy-,7\ͦ5jw=6莙w%v%ƚxO`15- r৑|!Nv9}S(DZM SoCP-yimym4j({$a ǕII>Ohz 7'} &qu-Ր6\4oD9Y-ZM UB[VX{{.0V7o./ΛŢ$66+/fy]^u1lDtW &o1#hO˦hnN%էYnƢ}U[rRp9? h/xV6mFCյ18 e_[2`2!ϯhV |Z ' 99X[GyUn@2y O׊xz 4F[#pbY&0ˍP9Pp Y[_KU_>xs_\>Ѭ4^\X4F[HR3Ggp"uMX`zz)PXm&Xe2Fod\# \(@4K)~r{QRPQEQEQEQEkA]kC_o3U'Z_Ӵn-3' ~wZ?]oF qh:ڍ8.,07,i'_|( j*~=_ͨXYI}:}(Zsnʥ܇W̋vd ѮV?G#?4k"k_>. KmɦZ@@|D9 ճTxzl6[O3E&Eu.w!2Pp[%k_w5ii7vP:xVB;h8`#c^pZ/&%Zx_X㷝]G0nW,+[ Wh5OAԮl6"5x+sgs pA`dc;0gJgB0]Cc",?FWj{{Ӽagm7P۬f؞38ʕp 2Y0x:<=fM"%ж(#)P;N\W563ܨmKߏd4m3M#N+m}m$7X>Z|`᛿|1iZuEv F!bSiC(I[ L.~!'AKGW:֎"l'8R%{msĂ+>O3z|]sK=Τ:ro!k.ͶYF*9[;+ M*:6gN低zĩq|cےpdWx-Bjz}jZtk[kcgwYJ2݈~bѰr* MZֶĚiiΊ_C]Kl];Mz&s:4v>K4ʀW:֛4n x4D+m:Wɷx˕ʅzvv6OM;BKsPү5kBSxŏ?b}R;2'Ni,PXݘǝ+=k5ap) (((+s?[RB <,xel{[Qs4vX佷2*:Q"YE RzGӑo\[0.6lq ͥ 3$[nÖ%=k?^h?7M AY2o.جyd|>eeCmcMq \ZwQ|7Սog|Av]i) h,!V/:`mTABsWt¼o vG1OJn$IcT(uT($u3RxCz_t$MS޸S!2IObN;f?Wt¼o 3GKi,&)"dz'b낧Ii,ausw%wVmfhё\V`|NJWt¼o P8/ZOhԠK.gV29,u=z`xEF;I6P`6D#) ?<)nn4  `Y ˚E@~kzzjZjb݊#gvTǍ#K}jí+VrY_*g+'“iVssgi eZ9w[~^>jЦM*[NGl#K?&wBlRRo>>z7_jm:,g!Ғ|Ky\6ˏ=dEE Akռ Dy>uCQ(n;.&@d rk4#u rG~Ej19;G눵"5V)L[id(Hhq^E+;#~xOO$zڏjisR33ʖ),0@*|5^m^45I"Bc#`1J) u!!#P8#WKQt&8m*O3ךq5:{;HVK6!1LȻrtqֽVbmx岼x-nt+  YUQ@ȫ: kƵ\iGy]%,I<%1J>".G:x=4[XFkwow-j 4#5}"8ÍvqKrsM~s4Fı<.ERQEQEQEQEkA]kC_o3U;3>/ѥ<cx2[k> xCZ W}E̗nskora’Ãz_Bb;<卷2 &^_7ԁoxInlo-llu .BFB@r"ԾSUI]N o,IIUIXFO8C{Ϭ7G+ }fwbw|uGiiZ=}7Th]˺L 31I7mi?;xZ Oc#ʌN̜'^_7 BY.o.h=kŒ̷Oz^bb5*A\fxR]ծ$s%ٸh z?^_7 BY.o.1`[}N\Z!&Ioup;1 ȇ GdfkMV}p` DU;;OdBW;rKvo_++[̵dh"b(ZlL9J+k%m՛[VZ;-Bwq9Wu 48|GhxoHXاIhR2Q#oSWoxZ]Z;M5`%I/1%W8z ]G΁\P7wz"-,^u^@'wtSnw\ڕ}qw$W-!';0aһJ(QE(((+s?[7l;Vgq? ~RB&~n?^QH M/߿X+7Gq? ~b߿M/Պ(q? ~y7V( M/߿X+7Gq? ~b߿M/Պ(q? ~y7V*ɨ?cӬbq-ǔ݌}Ӛɸz<_קe0te0tgq? ~y7O?a?aM/߿?/k/ky7Gq? ~߿M/O@{??O@{?gq? ~y7O?a?aM/߿?/k/ky7Gq? ~߿M/O@{??O@{?gq? ~?@?IkjnneVEs.@3v{CgxzNb895t?V??|Crʓ\M/Պ*@߿M/Պ(q? ~y7V( M/߿X+7Gq? ~b߿M/Պ(q? ~y7V( M/߿X+7Gq? ~b߿M/WwQ]XZZyjp{!:V?O@{?`3ɸz<_קe0te0t&~n?^<_ףɸz_gG_g@ n?^&~~'= ?]Rմ>I㳶VT ƥ2QY\z ߿M/O@{??O@{?gq? ~y7O?a?aM/߿?/k/ky7Gq? ~߿M/O@{??O@{?rAʃ\x\d\>%'{@8nx"u ?JVx)h@CRBOicEi^kKإ 2Cqq]^!I 5֞K[H.!ߜ~tS`ǁ5A 7KZ+( 񆣥|$2]6m>D1%0/B" z/xPeƣY;/qO"m/×ڝrxmq~onu aQQ-cSK_xcQ-E^ZIHȑ,)+ \K7b\ /eAY]I U7t*O Êey{j~.]ص=/@Vx'%"F[ I%Vp [Ey#^ ]`,D-V0؅XBʼn$ǖ.C泷oc"1 xY" d)&It@NsIL{Ftsg'Č̡+)K7qxsĶmqgjzH!)3F^HȞTd 8q ?5%[6 T$ ^5+<<|dBTԿi{ Ǭ75}-.{AVmyPqU<Wx{ZAeTMvlSIH$Lu Ek3xQm|Meog-2Zܭ7 a17sY o.sW{y-+-,[[i GenNY$ջko_īĸ,l<9} m"$2/^~C;jViUfoIdxR,ҭtm$[[SQҫ9.Ĥm9w >0PxNM7U r8Iarʐ,p@^35Tr/? /4=VXK\gl.nn#d8g~w _7lwOϥ谽奰0UwDUAbrww%yz>/DXLwz w7qi qp eo(*Ht7uGX]k'ZӞ][]C29|CMW&_֫ O+Z}otKJuA<[4kgu.Hʶ"88hC!Hjvk%ۯbl,׺$Ѹ!X U5 C{ۣ{oi4֋ -[l?XwNFn|]|2ǥO"g9fFNGM %k~.ki7VoZ{ ˨uvi妁6%2D)2)3ǝw= A~! ɦ޶f4\`۬eVhCq[zf#~&|F,I6[i#6jwNJO||=-5 WYii2o+ A,(+iEܧ}moz5e$1Ld / ǭt5x+J_ Yx]O1D[19,H>R8<=w?aԣ*((((((((+?,]Jެȱu_5(dž!Y'f<5 ?\4x 4{hnQGcWS  yǎ>X>/,.2G-iFt8^++&Dэq9 š_ |?:xTn6V-a[lR[#Srǿr/rF[vIbCe&/xJV Z}IRȠ@*{|i}o Zj+RD֒ ,dcU\rŀ\(Ej+cmsm%9Ys4fp1s7% jXӿ4kk#:}#oiQR8杺_!_3S;_Ե+{4٬.-d)$L}F+пj;@}O@ηͩj3" <\Q3?f%s?jZؽ7isYIm$ "&UғaY~kcKح-u[+k:(wesA2V*lfM[hwFi[*>GϏ#G`Z?lxƿ۶<=:RYM0ڣ Ơn.k3<y5I<3iv2Rr \o;@5 0[|Ǟ?{o7O;~6sG xm.xO֡.goI$qIIVYɸgzuG]:9Uզ$(R)O#י>jv2^3:fumE\qQG)aIˮow=#\:lVV.cl*[ su5/M^iHx~umno*w:# 2H0"忇/M\?ľ.Ğ?gVmè9C3K$%QVIYɋ_Og~&?|A?e>h:mԺi47ףOO:y@Te(cc=xFxOKj/\" ܉"ߖҶҳ|O$(9#KOY[x ]@՞(M-XUbqFv}[T\<]J=?Úu4i Kcwns}KiuFC)VnA`H85eK^ӼMk^&{oo|,6曩>mۙ+,yt?[GC>&`j'MDvCC`yw6QAi Ԗﵟt-=}KETQEQEQEQEVw\_A5|2j- ?Mr ZK?5{[QQER(((((((imTYop|\ZPW#O['֭uwhv? :^4sHRϤi:Enc#>Sʫǘ ۜ ˟\[{K_]xKHD:"¶p4O, 1hy]Xv? :;]jc&uj VQ(|ҀbY>Q5KZ[<>3#O~ZMYuGTg lN;]?zxDxYéý5WZ.ZM?') wp_4o|&@I/9mA¿lr1š]Jz#O['c DQuF|Ie}ˍy~c}i, !za/b$hQxuy7ae&hl xMo ;Tz#O['׋x{y~$%P GiLPmRN*޵@xOw4^!O rq5Ĥ,MQvEv2nL &_[[ _A=oWvGc;^ |#jnh/^[)a>r$>jbqxAiBt+ zM&QEHQ@Q@Q@Q@Q@Q@Q@Q@=" Tۙ's$RZ*~d`z1zuwkVv? :;]ծ5ӴGN-JMa<ׁ<Hݷv? :;]KZ_ ]_&˦Vf7Ni> {r#jfKk6M]mGF]a˦W c#Jv;zoXIcٿzA=oWv~^ΝFdE̐$f0"<2:i:Mm=B \ 5ė1@G@ pdG*_,o]ZsؿzA=oWvz/ ŒAM4=~]ka&l,#oMY$ Gn)u]FO ^5, y]Z$9hSm ' E(/^;]?zy֫յsBi_;;A_Yv["DX,Ie>s/ɢ>&ggsj!k[x\/0[y1pe WA=oWvGc;^[<[꺝ֱqy%5/c|I=2}+Mk-M%n"Y.<Ȍ:)|V|=/#ry_#zA=oWv_ |OW'&}n{kߘ(IJYAB\ghxNq ?R\xdk VZ2vn +??iMEgSvn?T;O o7@Vϝ7?iMOtEgSvn?T;O o7@Vϝ7?iMOtEgSvn?T;O o7Fy?뿗Hÿ fmu(b Eb1km~xCLe:}9K'3IMkSdj:=#7WS\<;W5~>vn?T;O o7J9? %ƳA5k|> !ȥ >a׀Bo>]cW^궚[ABʹ"vFnxS|?)~>vn M e;7Qvn?T;O o7Oߑڿý[<9hNM>TԮ?K(HDk?$ y=6_D}6c9WP2G-Ư۽[h5>~>vn?T;O o7Kah]GrCmKCf-|&%35h3M0R|BXMn~<-]F\.|C)kq,28Qx1;JJEw~>vnjE5\E˧}ot$+-?v{Cg?C+;$o(`NM>2h?T;O o7Gϝ7BOtySТS|?)~>vn4(?T;O o7Gϝ7 +??iMBOtySТS|?)~>vn4(?T;O o7Gϝ7 +??iMBOtySЯ<|Xo x^@},_(p-Ē4ѳÅReb`~>vn?T;O o7GTFO|<_Ó飷DR$rD8́pe;AUc@'W~ UդX|<֯q4(X|i  {ϝ7?޿molyO_ ,tU%'9s"Gn1Rl5"ɬ [?NUbMl,q.W9<}iMOtOBN"Zt?nZk\G~ϻnȃpsU}=iSZnuj)$XϽdڃ >ێ9?iMv@O{G״j4y dhB FdyCN,mSCtY>[vif 1x[Zy@tl8zߟϝ7?3/xjWNt.HΓw. D3Rj?5/5@]Gy"X>MJNrWoiMOtHUkmf:Q_jwZݐLh"&VvG2I~ *o}:Cxv&h\G Nx?iM[K]E~.X𝾿 MOÖ>&XxF c/4SZ:L7evmp$JX_^iMOt++r<\9xp~ xHԸXG26mDIv A$\죦IGxV:FQP2+VHXC#4 A6{ϝ7? oKOĽCTuMZ]6-Fie+ {mȚP>hH {džkOu\  j{ $)k?5{[Qr OK{2;Gp*FO.Ht3nZ_&wxZl$-zIBw%OBԟ#]j x] [˙G[k=,bZI{H(z+Z#ר }CHE4-i_%Zi*-kk5l+Q5{Bn$̸-pHäh÷e du^GQ_:|^]ihmkFzϖX+;}ƿOjzfFj]o!1Xð^H|;Ey̟4#~55(M8]p1O? [JC/EZ t,7\nB>򌌖atzdbGɹCmq3 (((((((((5 ?\5t?V??Exy"m"i [NW?-q 0Tb2#mJ\j:JI{ȠE>W,*9AŖԤԼc{E 7Ⱥ%2of# tw~jI[7K\w'Y2O]#W9{h, *Iq\>Ԥ[ ]CKEPNXYiA4_xDZ.auZiʹ>^長e@0%TsBM聴=>!l5[_@'QE7B. JbmSeF-Xii,ʠE\1AeRXÜ _oݴՏZ+e֣j2^e_^ӴOK`@&ݑw gc_.7YuīO6wl#EA4Mfg"_5WϾi#U^h,Eңi#BWgܒ56 imF"fn潰XI3o#, hrU5go*IEyDuKKX4Go$]R3!Xrʤ?U,9$ﴯ$ }Hw5(C ( ( ( ( ( ( ( ( ai(?KķGҵ![h$JmiC!PXp;[RBصH9}t;o-#lݕ$׫:5ψ6vZ[@J-42. # z)i=U-5Go쿭ʯ薉q.o5<בV2Pr #tSXs!ZE->I XD4h=~OD]sk>/t۫m òh@X~\pbPU99@l)7UiJlpU *dq^E>!,a-Q2Z G[  p0kC/G6XQxH5Kv[I!$8 _%UfM.*pG%v_Н_bΞ&l|M`ޙwOlE#˷PBĀr=6~3(ӭu%D|>ilW?jK;b_GUq^ ]*tWe> j+w<[=_~1 g>iQL5֙Ir:w@HyK/fgM&_CX^o>n߰063 wd}O]񺿧jPj};G"H6VV*U Gj;y1%[<fxyiwMw:3K 3++08n/AԬbFhQx~Vt?pLUs2ȇo\g?S@}KE.'˽2-F٥dGl!բ17Sx{ηM7zSY~`)$\d8WQN*/٧VOï xK[45};P9 ct ̂s?MC]ѭx4iOBIK4 SնsZ)^_֣ȷhݷ{P^ç[4XJc@ 9$+7/?Uu ٢+}SW_nJTU +/?UuxNIfMBcR$eʢ($ ( ( (Zo8R4؀XQI ڀ.XϾ7G%v_ ]*tWe> lY0x{-C$ VmC:kZ (: =BO!hxkA]j"=o41I?5+%,8cvugWeI*@|/skEɦYG%ճ"Krp9ǯx'xT$ھɪi62Yiy\҉7,YyOq+?~PAX,t$[ʹbrH(9 *7-K]SKl?.s I-nb\fcTn^hE/ߑ>7oa  Q}4V`HAV_c#goYetL}x}-4fIh" e Ȩ(by'QcžɥqfeG*!q)-6gbVrvjHKFDr[42 [cssĂ%Ė |;2ʌX#>Q$^[_#S <BH]AAj$O9~7ګj7QO[Z={L74 o4rF v5xׅ4J{!nma]'MO)%IGi rG$^E'yzl5>?߉X ōc:E %]=òr@ea zpR^#DYG$>jz)wVѯ/B/faIcd' .b ǫŠX-Ԗ*Iܠn@5LU'E,]XŖ$!ԙHe#/G%v_EcWe> Ͼ7@4V:͘mxi7\:[ kIDַ1,HPEX((ZkezF6F33DP $MP}O]٢+}SW_nJTU +/?Uu+$.t۔PIe`HQ@nxg^?ְs?u OURBY: ϥé%YV>l Tpz⵫?i-7\׶%Å{ $EvՍ+SO4_ ڵl!`5F-HM&zXR^۰px K Fけּ7Q86tKjE-ɿIQ(]YKf3 /i'AʺEo(mLjXǜ;(sM_2SOm}-fI6m\˃+ǠO)G9x;G~ fV6p1adr=ExׇOEԼ3m-湥[7b˲)`im G 3Ȯ7cSY[t X K+r)F D+n IrSm&K__E%ŦmjI+\2qMs 7_ݗ|o;? Mx2xCZ~$$?gJZlatޱH* A## u1w|WH׎7B+]Uk6фYdTrV3RI { I?2[ɭwf}8T} )"FX!PYA 5uK^WԦHTv\Mo$/<&,2=%eowo;^ Ե hޝhڞoum3D$ڻѰ[hʰ rG|fvZwc2Zy>b9H5$pXds^UwAuu_ <]`W+_IuA[Gu467ur#zdZ+iEoujz~ܾTWfs >2R +K௉"α<,t2iu·!I*>[v 8'x>#G|m{?ިq_[%1AAj }ߴÔOaWݚB7<|Ȏ+k`'9iH#I_1x4 FŲhV:NQD|e"H.8˜_K4z][awn_(G'm je3ȱ[¢7/+%?6^NNP' x/C6Ŭ5{(`'(a>+?7ľ!O-Qi N_Gju2\{2- . c _UA1g"}L3chĊq.ֲ,9|Zm?ξ(g#g*կ!}djwi\څc t*QYA.+~/`Ox4[=YO`l/&{{yMYPe((dwo~[9֒94]^Cxp c =;?<6Uݏ[[۫CLj HLiXH-9ٍk| KjZKkz aj s5ިyoXbGFBsmc qO?WgxOuɦĖDgmZ0@.r@ܹ7`r9ډb{ck'ox7ĺ:iW厚Z]ާla>Fs?&8*6od6;%|ǡ]GÎԺuѼ!ۻ=Q^5H  (]OE~ |Hkτ>Nmk|1X"am _*%%&&Yp>}=:_~ĦQRQ2XXS?u__,S*@5}ZLfeopm&]9#@?x3Otg wT3E q^c|+-^(heP<uee??@/?/'ÿx/os,_![ila3YWqIVϱQkmm|-ώ^zp{Y]xQ;V;⸏~~j%i-m4SN`Q'9$e'I |@9Vk与X/_…N1\n_Zw=MQvCS{m;/ZV0hG8oK޿U\o;F&L\`3kSI ZcXL퐮Sx +)&}f ͤh0[Gln{[%h%T/lN2Gg{BfE⶜5itKPx%ӚO[ևIV ;+63(:nb{}KF"Ԣdy{<̲ȡ#fNkz?' CuK8[" D!Q"n_|STW|Ze+RW-ү. P\dO&&A2 ~eG`7w%\rR\ǟ21ZE@䍤cIkj7_ijqhY9&ȁd2 \0hEzyo!z@,- v Wdc[oo$ o絿7^mַ9Ch>}=mn.0Y陌ry TTU̜~ډ4ݑ?{[#}5CIj:Mim}o%Cu*Jp@8kzV)Z%j]ݔݛbKMͅHI'4uB-涾ѯ-KE`%lX+H Oa\Oc%-Ybv 3?xOZ@_c')/:[LխnFC$Q[NJ#bd-*H2z%SBNk75OS&Rc]iӪH$$rk?,]JVc_o3Z{Cg?C:ET^T+YF:8*x5,q$1qƊ0@+ ]}.;iu LhаV pq[#2iZާsd5Ү틤D$^@Q [I4-tzm7ǿm/⸺#Jy$H8݃ <5 :ŝm0w+Q+Ɓ"%ɣa9 j--wo@X䁴 ƋtK̑ΣXGǯ._YoIu=3I]Gi=U z7qyYw4.0-Fh I:geVN ?,@_c'ھC򈊆_MZ%hl<k$o&K|m=j(4mFTԗctUSW #l<ڶ!oAfRş<j:F^]i &w-r˖i#qA XrL֢ןWֺ+C:aO-[/坲1pryzwIT[5KLW}+{xb #4%# Ǎ'KiV,?nDH%L 쑊o@M3Ԫswg< #8q޼"Y.XhwMNcKMr\bХяiCJ,a]ZF,mTZs=rcbʅ]`coʦ IOEy^IKZ  6K2s-PZsJ!hpw SZjފ3-Fh IV<k$o&Kt#/ŝqVR[ %D,Rq^\?~s`Q\>9?ѵԾuu2;$rO$QB SiYƗ^$n$E5p:0pGYp[ū廾{x]*i#"vg`{ե<,멵;(X^wP;4(^xdC`ǁtw7LJK?%mO>0ۻk@'W$j(,kkɴ_H~ 3L<=j0[~ 6ȵe^KRBoBGFp|G-4wod2p\p9+Tå~xVhwXo+gҡdbc,#p~[= VԵ..=+~e|DcJ]sd?ſ|g-RSCk<-,Bqtr gx#6k0im4mn}Vh$#Fq^+ݨ_{uM WR *ȠGy?Oٖ+7[Gzb5s#wX(v;~͵M5C4_-ն9jAXƖD1!`X|=&>LIcqEzpښcE?5 4q%mGN/!?M{"Pmo5K>q\-č C@=q|-d:|v-) "L-p/J(Z;T4UofS߈|exx_EukY^Ot۳argnF WUc׊iiGHGhwr(kN+퉖-H'׼QBoqNlLR}p{tQ][YҢA4[Z_|EO<uh^^Eukprr$22_<}8ꖱF$ .4RǦP V?_n~:Fsku]%n$E QLA kk׿+/V?_|s"ԬM{"V_WmTS 23PN 1Eu{Cg?C+?r (#_e'R4 8ȅAf  y^&񗊛ZeNћOK6{c}.UX 8hhM]Y[ƷG<@:>*4t;fpk;g ޵<oKkQhisaY!# UK6vs|[O>~w?h_&{^Meu82W?*2"HrHq|#?Ckbѡ12 %خGq9xI໋_&WPih[7 <3Եjkh׿+/N˗/!uoݿͳ?M{"ښcE.1!oAfUUmV[ϴDAfl?T j֌VNJiV"G_+OוmEKy4^e弚a-WS,"0ZXr8ACm.5N[Mso ciO'W5 4EIZ6+_gyEpښcE?5 4\gy\?~s`jkjw:๖afV'pQp.WZYXZ-ӭwa`Koc~V7)Xgڊ>hg?x4tmmiRYc;" Y՗ylxgWht'xbZMmKF$HaO[ffdmFl\S/| z~Ѽ'K{ [6``_!G|9Et>>,\ҠM[OK6I{a|nLrq ۼA9qϽQE|͋Z? ~$x_%X;D3F]x-dv>DG8 Jꡯi1J8mw^J?:Wwd>j z{H+LH h" ȷ1-8+V?_- i;7?M{"ښcE.3w\_A5|2j- =q6WRUǭk&tX" ǩ [1u ђG̘zO؟.?&u OU@T;'ˏwtR؟.?&?\M[*}h;պ(؟.?&?\M[*}h;պ(؟.?&?\M[*}h;պ(؟.?&?\M[*}h;պ(؟.?&?\M[*}h;պ(؟.?&?\M[*}h;պ(؟.?&?\M[*}h;պ(؟.?&?\M[*}h;պ(؟.?&?\M[*}iE\TPOt?VwWuꌕ@du+?rt;'ˏwtTSO?G؟.?&@>q4}jSO?G؟.?&@>q4}jSO?G؟.?&@>q4}jSO?G؟.?&@>q4}jSO?G؟.?&@>q4}jSO?G؟.?&@>q4}jSO?G؟.?&@>q4}jSO?G؟.?&@>q4}jSO?G؟.?&@>q4}jSO?]7j AJ4oBGFӼGhfeLWq!dt9W93W]RBs:6 ױ[K -.`_D uh7~)voij^^ah(vsHأ̖F1m=?5B7Ȯ$g>~Լ?=ֿkqnsj3&7>麾m'ς^]jA:z+;Hqܪܱi_  ?k %PK lc7#8Ez~'- S'C3;\Hʟ( $ )5otitK$-⣮xXn,4y$n8^#ޙ-&],"aʬ4dUYr  OQ]~vy4xxZxw_.}Kr-k[ 'PDd:8y SGK7,k_j;\Myio%[%XXzkSI?ǬsX3 ktkXkc v>]x*?zq譩MjsF=WBQ8^H޳iKsXXKʛD9# 3q8RuoE2h~eIKoq̚P:b"E1 a߅>i wqkwLҤi[&5|Fyۧu]J|%G x V1K[TPr Yؓ _iř2"#Ċ_8V-i;knz?k 3Pb. ԵKNݖG"$pd,Q0 [x7>Qբa c,+!4&2@9SЂs~,uҼft R5+K)L%MF\mfYwp/o~$&M,҅¥ۺZPJ lI$f[[Z^d;1|-ЬLVuKxaVe]2L4-gψ+/ Z#BFK> !L(fF3^_ֱ4ҼEo%?,5=)5+ `& ++q\gf4𷅼I|z׆í$yKw)Q\(L2C4P[?Ž~FcV3I:nlH-Hh˕S8e)M 2hWkLI4,XK˃]0}}jIxxOo>&-PJaɘRU=U_xV}MojA,&Hm4|h!'ƿz*k?_֗+*MsdySq/f/|Kg۹G5k:~*\}PHRd)\Rx'uv:BEZͪh"`Zux=" 8[m䬸y7t C6]}/m><^ImCqiOql6ʳ)CCjj%7[F8i|ͅNR@ۃEK_x^\5kmKI*c{]@e_4FupQrn| ;[˩-Σp!m5af( p9#u8h[mo~E/ӓBnjrs}-I72[? Vo2NvҸK[m.e-u+;i-"Y*_jy 5>s3i0%s_Mt`b ā&%Oxi.Ú]?冗Dt&Y!gm)ee qX(+6u+tw;=/j[h5+Kr#YhLkGUP L_l|W^1iew$FY; W,񵶥⨮;{]B K) 3[qx괟k0[w]jnx#HHY+%C,&0pO#J_;]X<}+t]SQLZLڡ_Ȑf7/,A^7MxsW5Ho}43yҼ#4L' K٧WM7 \ phCN V l8ykZO^($^&݅p'~d<Ź€zו[C+7 N=//>nbfΜK`gBVMKW5,tMjIm딎gD @bE9U!wkm|%JwK#˧GdYkH\h\ϭk|Bm.ᰱ2YV0 *L犏FŭZZe ht̸,TlN+z.j -W[M^k$6qyMjda*ɽ6if-bmzsU֧<\ۤYaQbTn q4+u_o}?[*0ھz lwk7yRynC;|l6t i|Nxf#ԧT2ƅJ6bFq`~ʗ)Xi.PӠ:ݼHY)<#ܒIQ M1mF_Gk4oGkf6dv,qΛ@-[~O6> xZڗ4 iھc6-0>{T_ihV"P4|gktTs*Z[KFޘnX㴓AzelzV3-q4 ī21h~_IP%c4v7cd`i"AfE nM{MxW?fC[xO2jDW ,P3|A,T#j3'4ݭ勤QEIAEPEP[:4oBGFx6Vt~sA[7@Vmnٷ?!tТn+w|C͹ +?6?n۟ Тn+w|C͹ +?6?n۟ Тn+w|C͹ +?6?n۟ Тn+w|C͹ +?6?n۟ Тn+w|C͹ +?6?n۟ Тn+w|C͹ +?6?n۟ Тn+w|C͹ +?6?n۟ Тn+w|C͹ +?6?n۟ Тn+w|C͹ +?6?n}º0%JEPkt?V?? =BR ^ T95]ɢn+w|C͹B͹6?n4(۟ ?n+w|CB͹6?n4(۟ ?n+w|CB͹6?n4(۟ ?n+w|CB͹6?n4(۟ ?n+w|CB͹6?n4(۟ ?n+w|CB͹6?n4(۟ ?n+w|CB͹6?n4(۟ ?n+w|CB͹6?n4(۟ ?n+w|CB͹6?n4(۟ ?n+w|CB<3/?\mnu~VX4-oBGFk?4QE((((((((((+[pӴ%@rAF03OoW9+ld[vI)]4F5*#zw?ksCD?4xWHm{|blj9"̻I>C2"/_1gbV~%Ιk6aT8,cIIvoՉ {#zw?ksޝ=OuzO{{($1MĎK< 6Jrkkj:GtNj:3K#s$Nhf(\].(݅v{n{#zw?ksd m$F3j:wV_$kךO_X&MApbpB8*3~>Zem?ڮxJ _"gO#l[?>)o k._@h`gop])rprpqo_iGkkeopmpy$I,gZƱFHW[3^8_.h>m*]I,ɦhpܭNv&g ~XoNڧ n~q⏃zߊocfۍc_nl&GH [p0 ?/?zƩm_RԴ ۝c(Ɉ|黕'TG#zw?ks׃Ǿ9o|A[G6{[Xd%?0eP>ݜi>'F_÷6 "qV҂ZK +qo3{.gPN\BҭayagM^U@I/{d,繖 ۄO8 .Cs_"PߺVj̛VS+?rև!Y'fQEHQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@z,Ica#9C`nyⴵcSGΡ+W_ <IP_iif`>h`}B<90Oyj?]/#z?ksV&›}WC۷t߇uSuKnX@ŽFNu2=`]>q5{,wqVo=?{j5oOڧ n?Ɵ!t/ji kooiS1u lKߊt<*3j~ ]:d[=˫ėJJ Pez[^TY:E^Nom\F%q8! W[_q-Mk%1j[ш}řzqdVoOڧ nWºOÿ뷺dZ<6?>hRG)eʅb6qFy6>{}&yixE"c)4xP93W}n] 9bH[o ?$h?-h+­aqdtciq{4&G"KY)O͒F}ʌ眖dyڈC ;Fv6NXNy&Q#AmG( БQYso[[K[)%eScQrns]~5kZmPc: 459ِ̟VC Ѩλ_o3Zz֒G4M 2G#Sl.o/ :,@oi{<?ƏK4E^ĽG%?E^ĽG%?E^ĽG%?E^ĽG%?E^ĽG%?E^ĽG%?E^ĽG%?E^ĽG%?E^ĽG%?E^ĽG%?E^ĽG%?W=F꺍v5#;V  Ehֺ}nQ$CVȬ/R>'BuFrJFj-㴻ط ));v1Z+ج<;E{o;\Is'BuFp7uXiwM6|5( +) ?7|Ԗ#N|?ivGT;+ALrHUs qk#t/[5wJW7BLƙ\51>>QXLiiHVhcX }~SWx;ŗ{O/$ɒ@UTdWj Z:\.IU+ Iп]o3WuGkNP}6Dv) >FxqU>7K=O]*{x+)}ycl?Hg M|3BN&@f.uh5I?ͨ! r)B[; PxGai&}iv1nIп]o G?$_.h,r:Nx{O=++(=n[Hq}<3/?\J.ӥgLn1δ'ǦT]veqh 4-!HbF\O΀?fotoxx-20.08/images/batch-raw.jpg000066400000000000000000001153561362435004500167060ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0230Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 343 500 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?GL]";{o" N SV?5K紟ѣSi?QQ@~?4}'j*(_O=j{I}Si?GڧF%TO紟Ѩ ~?4}'j*(_O=j{I} ^|K'?.n֯K+-geP  +ɡ<+ߍ+mx.vVj}R_-cu`a9=T?Ddo9}'hTO' |+kY_j5^*2+Rr3޼zM~!AcAL{#{V;@'w,*.i_O6s>?4}'k~,xwnx4MR 4S 䑦VXrke~([xgF/,[lK7l֞ }oxFkm?ž?4}'k ߆_:gC},IymKpS2 vx[4mGl|i[wZXlZݬ.&}o17_x&~HK/O=j{I}+쟡;}'hTO) x?Z]oHogDs>[ȣĽ|+WZѯx4B &dqp%?-hqIGu>}'hTO׺~_ ➯3]if'8\W1;Ww ߽3s.*+T߇:Y紟ѣSi?_B| cۿ~<5 oo$Nm&BzM/f-o/]Q0ܕ!*owM ٕ{;~)~Gj{I}>?5dm;umbHLpbTO紟ѯn"G½:O >qhZ֡US9̰1d|Lpg$,yRImV<(+@–;GsW;.WI?j{I}>?5şزេKïj^0jO\YDn x$=v4Aw⫭O_`sjȲ&a.R]&?5/ڧFO=EEK紟ѣSi?^ Ÿ?k?ۿ?6}~׻wy~{W"_O=*Os#adbj$C#%}-b[L{)d=|\2Xӝm&HwRsӦ4{_m4|^nYxjjtI*}E}OA=KNQd`vȜO|7?5]aI.VשϝeR4dkяu|fn(u|fn(>~̔W ҿe ҿesJ+wWo_2wWo_2%;7tGG;7tG@\oW W .|E}7@+Q@+Q>du|fn(u|fn( Bxׁ3?+o]%d24XNnٮB?՗_S[GgҤbX=d*/{ u|fn(u|fn(wI%dVoO׵?>$׌u85g?^ݭ'BلdFӭbZ~Ѿ~^[oQhWk5S|7#;~讓W W _w[2>|#υAԶ0 ϻ:yo/Y_i˺ y7^@+Q@+QmM//O__Mcúï6}E/%2v&#!G'5oߵFriqa]G_k _z%,qu_:?_:It%{[1<[W'U>eo?7vǽgxw+Au. 3jѺe+u|fn(u|fn(loOo_r_SMծMs>MVķ0Z|ZͫYd^rx5@+Q@+Qнe`xGQ I{kKMux-L#*2~b{+]m}kT־^$ּ]˩[Y{mwA .-ӧ5cW W io+~]Г_>?VekډM29ou뷷J+چKkqI<_?mc&rJģ9]?;7tGG;7tGM{El? Rr{?̿/P\x[PҵOe'{&AmdlM5W/W9๼;Cu/uٵ%mD*I9u|fn(u|fn(ZM4Rvi>du|fn(u|fn(sJ+wWo_2wWo_2󦃯^xoQ[.J5sYJ_.7}_졷g>R.:W W (_:?_:7yZؾh#;3oW W _N|=×671$WRr" A<)?_:?_:u,1UN;5y|r$Zv||t-_]][McMxdt ҿe6O'OftGpX*X^Ξwmvafj޽Z$D~W-_kzc{ \GJQYC_ FN]M|/ᖫMW#_ e?C5 %fUv*vp3=OhZ250QUYVb5 mw6w8D<~+|y?χ^Yo{/\!CQP2Ju4o5KŸӼscog^2iېe|,ˀOM+M~MԟdIT}?M'%WȺ'_fHu[tMb}:6巗}8P :f>^>־] ]bF['1 t܀c9dp ]]_'c١]j")8M!?|{A״7|]NIkUK>7<`1Y_ƋŨ.On$!x "!D)$sOo/׌uHԼajZܒE[>.@ `֝jGO2"tf0 xp<}K߅i{ӏܖ ;'i[Zwo 7?-͍unGly5Y⶟sN]ʣ'uwo+>'?7OJSu}>+xխݣeBY|/Eqie?_҉M0R3)vBz~'W[mU{uk[1hD2PfGL,=jΨ2{S~;|8Ӿ Z#wO^IZiWi3`3Ӄ֐I09+.é./D9ѶGQ^EZ|34y ]yI!!'z2d7A\W O)Ez7]f}]E|ߠJĶ_;jK.$yFuڊsE?_KxXЛQm i3y;XiZգX$Jρ +l|s>3w2?5Y`FIxQYHI_UZɝ )84.ËRs{I&VH2u+ ƿ\/ ;NtU6/0m=U7=+ƗZxLtlK2HjvN&w@\i8"2I>ռgZ_[jǛ W()n>]گIƗԞ5MnuYcJ[QfM@RYy*|WtJ6H#.m(,(((((((+}U<=ǥ(j~MʎCc ھ>R tm.ɦ^7rz ~B ' 0*#[ZxRyI ݡ~G~b$_[iFhNq:13=x$zuj:FۇXtGusp1^c}&o|IP;{uGZ>iJ$B8֯Z;4H.-S2rc :w~o! w+uXAq.mr@\L`t嶷-{m-ƛ$ TrJnO^>>iÃqi#^$vLk_l`WY+n|z~i~Я':$,WZ%$ۤ}+hp ":iiIUcM~x}̍WgcֻJ))O$/,tFP{KQIsypBW5/uxolಽPHT_'3gvrs~ZDj$RhV70Vh+kY7V>kS$wTկ'Ŀ-ITWb7Ŀ~?4?3j6Z56{((nPUG?f#qG΁JBgbg'wFe*T {Wy>%jO / Rl?U]޷~|Kuς4W.S 򂈃汴OvLTfŗ777Ri е'Q?6*ԥ։j oMROE%"Y$va ګy I@K8 A\G/ Rl?UOZaݱYi#O5xŗLs%ZPUUDq!:~;ToxڛY4HfR61$ |е'Q?6*qQ̚<z%ևu=CK78lɭVMSP{ky#?0nWftP>%jO / Rl?Uq%m0u>Zy+m˸d3oڹgg  &xyy16aLeO8R|KBԟG_Zlx+֞ <%~mDR+YY о%i#%jO / Rl?U/k78N<>GII"*Ȉ>s^Tе'Q?6*m$عES|KBԟG_C.QTе'Q?6*.QTе'Q?6*.QT4JMF;5[\wS9]e/-l$cWTf%]GH J+WX d_G@ȿƀ6_]c-ں["ڢuE4jo hj|Iy{ dE f985^A_ڟ]6q:j,R|Π>_7O7!{qccr̬G s=}ñ>#GY:1<=ySDŽuS _U%PSyϨ]Ε;hRRCM9⸵CJ6<sxc_ >چ=wz] qfL+8&m{~[v/$%]cStiC5;<"oYPKn0kKŚ/XNgq %dN(%BaUe I#֔u[}KMWsLDt]SUtFM2K2+7>)E>dwlm`Uk4 O|W;ƀ-з-q~n,Ck>z\G]HDRJ߽LݷRE}h }#S߈CqA{5Ir#3gN >a8LĨF8Ey_?asx/6wVw6u\YkpvAp1X//3xu}*W;cK:dqxn)2@B:5ڢǴZBB]°89Dwbv_M)'kZ5si\ͧj(t9 $|@ 57Ԭbִ}NAoKXs$qˀAu=3^C/4u+O惭iz], K2xo*O}C>uDnBzҡuд+=]ڏ;]>_қZV:uγMiov@q؀Hҽ~c͛1]xwVjyXnl6=E}9Z%ga"]w҉+WvJ$aEPEPEPEPEPEPEPEPEPEPEPi_&kxBo۸=lҿ' M?BYyhdTk| wvܨۙ\E|hg˿jvhXA,2#,bHG{ st6C ͎<֐420 $_--fc@=G5]7ĽJK];S{\8W H#kYZ˿^N'_ـjf`ֿ&7;iqսcfv>:~6`sH>xOI&h-ջf7=`oxnD%eݛX_}Z,W6Λ6g-AQ-6jox#SԬk`:v5"%p,?( p-~=|WXexsJY5*k&q!DK"#ּG]HH5}RB:]1G Xg,y=_7{ź#ZkZ^oyjZ)D&\Dʍ³30<濯N\h~6h_hֺ\Y\4-*M.kkxR66uOxcW]k8C@c<| vjW (ծsN~xĩɧ\\YJe]HNs(^|}}g'}"m>; 4繷1 OvZ|E🄵{ s6r5QUv8@#.ƟAi6a anl}kfxPetxUt5IʸzוWZ]?+Nk WXԯ,dUR|󳏝yB@nڞEcx@6D2Xyq(bd+ϊ 0u˶C"麜 NFOu4W;zW#Я|QY \j%nD[q8*@W;_C-vm RB99' !97;mOn P6 HO2@t`z=5 Ջc5jVw/u{(,u O#x^yWvGQ^E=|IĽ.\ 'fOq%\ܖ<(P1ʿ'ifJi/G>'جM',F.4]yX\(8݌USCL\_ާOt9ݵ-J3ʼ:Iʧ5.joAVyI~2PIcwA[{qY/o 2{X;՗"Ljt4Bz-J[inyk^4v *x+ڪi?4WR}lu8&}"?/~8P4Okj^&ƞeϖ.$~1nF~׺ 2O)gour?k9F_6!4*[-+u2 wAMs cɔ׺ 2;/Bk#N!ƙ.b > IφASVݷ$z#Nwu{aqarܒ[}6Y#Ⱥ"mx f䪩O?e _|eŞ Ӯ淊LՑl #,R?cAM3U^1*+5v3[1I^0s៉x{z&typq1;szQ^1*SCLY$~HoNd-'ԮFor>J_5 [OhVWW_bQx?#j4?Tmx f|eiP7d4G7qes2dBj?j~'&kgV]Hcg:=͒yqBb@[,t5 k\麔:m少7\Zi`T:7y~<^2_jxQ4K-m/-vegD3s+Ѵ M6z/t-beg JE_UQ w#b_Mɔ_Mɕ"ҼANR$ms;ɕ~o3ʞEhRϿ /? /(Ͽ /? /(}kĺc6L4x`,##;Mz%P_+-sq|WNj<,-xE(.K, < _TQB\O_>7IsD5,R!R8W{|7`o I>%Uh'$|,H6\wN{4{M]BMU-4 Kc( 줒 g'5_t?ǭM?R5ͤIG#WՕ O~LU$MLuKy(̬Ғ擓mC-c~1u9"-ьw'١ վ#[% nyv}௄Zg/ږsncPheکJ ;o=KӾzMӭ,Ki-&+Z쨢mr9ߎ,-yKˋ=FJ:RNx6,ZJ'䌪]O"yjT-%q]ƒ :3J`(%cZa:YDc\M14)<xU\:ͮh%( \:0@F#W^ueOv7^#{w 0U|䞃uGK?fQ[tC ~/=+hֲm$*JY@80w 'UѵS߇ݴY"i<3[wks7q P23s^LxO fjsxgp4%&g$7@;Z]NkV! *־p)In34tk>;ۭ1"h݋*MF7! ñ יͺ!uzVj\7Jۿ|^  u|\ޟRm5=kxoM_ xRi2(!W'x'w,s/`{omaomj}j{ZʲyVZYu '9nO֚ލ{a֘14k{bʳHX0 ;d\>XkpxR?<_C,2x9eǛ*FnAxQsgfs7GhR4JC:KYb^!X+̙#?QjXxUa0\ZWs4_ tǽDxkK e&o1qֳqRV}t~ї+¯x{iKƟ}oI(ERuNy2N8߰Fh?_]]&ټ;7HGdX>RN )/º)uM8=kPtmT 5 pF~\A+[ +B~!-|Okx_>S{kQh\yh9(vҾ|'_W:߇+7z YGC޹|4< Fk|̄K*w>by?.X=oni^#wX~7cFXZ sET @=0k>).gF_, %F Ǡ5o |MOë"KtI.9A!^}^^sj?^!I6)6"c9WyXQysνx h3[YZ5ݝC"m91RAn=3\dbO((eUV^6Wu|;qcXMOS%7+gʛUp'ӌW$J7G$ɦjRO7+YO5-Deg(yݴq\5/}ZZ|fcV9&Ef g_T'oЦs⿈wZ_m>:|+o/uWha&Pƺ,g|!7Ѽ̺kt*"&-Z_{Ozj#wEcz|{t!uf/; Ү[Cckͮ07T̬8aҽzT"PjZnI4 (Š((`q ^c5]KPP\?1G^_3::5Y5=n}oJ|?I~`#:gڿᯇ&m s4:;7o^UCt.|Exd#`;ʅy#?O!Oنd١۾.Fvܝݳ+oz>.\7l2>38Zz_çh|d|<𞝡V,`ڢȱ,aӊzB mF@5CD>h1ScH4*.sF3^ =m? :)FgKqs1a5//?|Gy]_d֛Mq19fڳ ]3=z]{kgSvevY2s=3X?ixŞ.ƒwV{u`ƁyŜ:~ ß:/mX}5~uaVS&(0.Fr#=+Cĺ>Я5IdM^V$Ef s\_6 Ǫl[hǁv~xz_xWP->u*9ʌ6# ;־$: ⵗuk]9mc.qr3f$6"@ȮH|EfmPD.$2yxǟ4~+猭UtVgV/>?j i6n]FkW$, 0\\ŏIoNnm~mipN,n2"RFB{p#~$m7.q 6Ho;ᄂ o!Cq|+7 Nc;LY4}"[wE:g ֦:V/%tn|YiZGsj79YUf8zOzˤjD_i4{-`hB'*Rb+ݏN pg7!Dg3Ɏ-Oc<PmAmhkelpk2oOwAxg\amn$~f0;'ڷ<|+]?LUJ<`I+y6(`p 8_%Пv3y8XC3a[ʂ:%5|!+LwzBnv[ c#㟂<;Xj 8 kcv)S|eCzeoS ]ݵ!XFU+3+&EZkę]}_M_z7/Ti*,;);0'4j|R_ E֮ng[/+e6qKI:瞂O#?3kk)4 gwn [x?=0j_Kϣ~)k}^8gcߖeBslOA]W;^$-ݍŞir\ZMʣjm }' (Š(((((((((+5jOK=5}#W3[jzų,R+ˇ#c)$P\|#xZvj$w:Z 7࿼#UzĿi%H.5c,ia'} <{i/]isNߏ9BycBxRv_FZXjO.2[b8qHg]Es~\[xsZ.qn7Mr āG=sE`OIqifY$I=k_ Kqojoɖ66B 必@v0n5/N_O-9L$Qn3G#5K Yf"0PdhznAEy=OUѵ-K41̀k/a?t_ j3m[QI1i.np$ ]T<m/?貴fqV +~Q\ޡ꺟_Bvj& Zv)Gmv0$yK 4OKmQӭ 'OH4Q\'oZ.Ѽf]2[x+Ip; |qu|KXt=#21aE-íGj=!6nV4z>L#G%OMWKpxº>jgHx$sTկ+o3Z+5WS ^V$׷ Urv. FQ\_Y/$ӵ=.e]GN{co+pHI>V<##?ǂ{;Fq \I6"`+?q8*+4:og9?=GW$~>xþҵgᦩ+j7z+kUe.$ʃ,Hb`2jjM3^+5WS ^V$׷ Urv. FQ\_Y/$ӵ=.e]GN{co+pHI>V<##/Ɵ'hꖺs";m, b@*,3Cqs(O_m5w5_[J/pq$/\dי3tkYiklJڍކbYK 4c2SjOS( xwW1F^}E&Io |EʷoŸZ|0_ aqy 7l2E x ^VHnn-o]CHS O: -v3+A8pQVV|X_Bє;U"\vW*}I6Z:,h~* 3O-8F(_xS+V[rI~_?|K𞩤x M3YxAc#WIdY eTgs`"ŢLc|@M ]G7B3$4O]}%8CMkvWMmx ,ZA!06qӾn:G-5yH8Dۂ(oP9~xKO+n 3fQv'v0O0֎րGyIU[.[xjJRxe !x@9mn^>!ԗLFc*V$pxɯAM]t ^? mq,2g[YO-\}eCt6k*`G3μG{O cbS۽ oQJc]j?7~<xb\IE=6x&$@KL2z⾂H1P S7CGu~Dlr#Z%0uVp>Gڛ >¾4׵'Sf-]sn+ױMoyntӭ#0fuw8;Ӧ:]C{-Cׄ.겄!5[~7:%:[06E&<΅ؠ E*`GH1P R?{_p cMR͖cN+;7/|-ͩW3Kv*NoK9#ױھJ_eqw>mong3(ٻy'kG~1=WkEmpxKó}zеiQgj~ԒAz~uu]Do Ⱥ겄!5 0@#^#1)Q (Y1t.d7t_,t]Y sz2F*bJK5ܨVw񯅾9>j{ineIӭIg$pps#v{Sjo*4 ^tNݚawKjcLrS^/]2mgUMvOŒlݼ ݌Lus|Vٜ%Z\xLM_m>?XG! aWa+һ(ƕ(0((j_-徙_[[IL\Y, %T,FFpkax"鑑q|xX?j" `hB LRRpqlӼK~ ';/ :P<v֏X0:^5g?+ {{Z`ژm{Y2N`2B0IZ- z4 _vy/u)5 E }AqgJy>77xgUjgmx UxOKx᫯c_լ,__Ae1ij#G~'$ڿi;z~W>1׉|mßm_%׊}&(/7Bf]vAg||igak F5iZvRn8ʅ87ooFS Ş[yKW5_ Iokyz C |oz]|OӾZ$|Oq;;-;R{sKpX`qBQ7mK#+^ko~&};D]:[%Cq]UA_p6`{ XŠ+~;Qִǽ++Y;_p#X+(,`F 3ШkE[n4Ytf[sp }(TLp1s|:,?M*/ nD$O%G2 rsIwipJW>=wۏ8|3&}{:Cw:nє"@;G"];",kΞu#*LÜF=T1_Fo_>:滧gH5k4: \8H_ROJ㟌Gc';kjzZ!{)A)vs|\| ~K{=XG{y0RHRx erI~:aY^Awm*myj 5f8=+5oB+lG%uչqS8t-"cQ$ѭj:L;t؂ZN|j݁/Lڝ쐓#q]5|\|iq[ICP1+@+^<>8[zƅqiwys ci7ɒH=(JO V=okuOzF -χ+(. I—U GU?z_]|^'H$ol.bIr%|f 3CNB..k`БaҺjb_S#>(m7tOv+miI$ӶÏ tj~§_ hQ722q)1r_/ RQwM=n"6!–5c7~?S.ffsoX/ ߒ+Qj jkᏉM><˸FRpλܩ_k/~.Sn4JNޝ$q$α*FY9_cJ>y)%;>uOr]Z7v;C/AOL]%<;7zj3kS~?k?j"g ]!:^^핆4|r2p9 /Aj.d(L=9ߊ}}  Rhz)֟_Rk4bկ5m*mM,0A1+3;W~6omVo-9+Hgf9m?u ӖK /͓k_=~?Z ??W4l;5 +Unb)R :)Y G5z'ȗu]:̩`QE ( u?:6] qOvd3[U }.,ZO:D.@qZλ>\ mN%+`;?Bl 3Nꖚ\j?7v#8'zZƯiҪ֚ղ>* HUWYm{w{s]>\Vngy i;[zzW ZցiPlgX]|vO9 z?ٓޯ[YONmwC,SG=vq$]]eX?4չe%Q'_;81@cϛ?/vv|޻x:N9 ')4K&w)<ns>2qָ~ϑ|E{/<:4X%.-<{Xrԑ9\[Mp/ͩؖX8g[z?#u{4K#23l(݈bGc$keo{?_VK"taA%*0u$9棲|a:YMcqs\rB }vWgmc;~ۿ8ϛo^qژL6b[`g ;_6H\K&pxO7oyۙ_EE'KQ-h_(ukmkͧLB5{ˋ %-}0!r6={j$5ǽVtgiuϐ3lZ>XXI[ :w`n}5o^%)a.neT62kVk eH˶n>Ԕ+wE^X40Xx]NUІRpEtu݂"1/ OUѵ-><3s\NcIqrs TZoMծ4Sizޯj~1 urKLn$rxIY#ud2݊ǞD ொ:ioo,o';u, RYXjVVe.x}ldnaʒm}:KBY xkfm/LGRI奬dlc-ch \^Exy-1{toeq!SX{k㷅z+Qج;x ػt'I\ʲ2\Ųk/ÿk7w/>Ū]Z"U$dzWQ@-Ua!Z8'k; -2N31q_>;?oCƵZKi`|?KqNd}I 8UJ/:X+O)z׌>#k:ksk#T vcf߃_> xzKѦ65䚅iw^>7I+ ] ڭLKo*3ПJTԭ$e/ .S!?tM[fZ:K3YR^7ΙvK Inد]86$DrHWh (Q@!uǿ x\4-Y_jYpt $, i?Y軁kmezO%g( e8=}i7?=xVZ7 R$v}^DeU w\=/G"x>>)<6ҝ}rsr5wi> :%\CgeOS֍ÍF/7>;mBKc w.s8akka鶿~m/5[ _ϥxwGy"gˑDJ0 P QFjaiiOm:C/*9HWĮY699ǿ|GGYzun{al;d8S56xC4MlIsujl5y[;Hvz'7oy%~#Y״_^w-C+ ᵶY+Y^!85\fω5j}m+ . #Pwm:W|K[mKO< Xh"}.b-.KPUYYX#9\~ʺέXj: ME<i nH$0w3LӋIk{&IM~4ZŦxxx1NRp++Xg*~3,o5 譭 Qomo."MwF dV^2y,uVB$/qHK0q^3jƏjkCMm ՛Hbvm,zV涂xJڷke??D#Lz}FGkcM˂9]/>0?O>˭V'"* suQ7jƾ5|9},3i!4K9. `,$/`xCyu'J/ԯ4n'6VDQv ץ{m6$QEHϞ|wSE/84NזI.c>{mqa_⶧W~I7-nn~ES4Iz I-Ӄ\k4>隇u:ZDn:ÿ^!F׵xv?W6K,ͼʫ4336J! ہ"w Nt:j7,o=+G -qawmx\R'US5i~/mGAִYt,5Ee."O@;"ꟳ/x}_Ė7>35 +oba >AlgyԵ]&hZt.|55v6syN  =9Low4|-}MKź.jQNA vڎ -mnXkt]v¿ 5A;7Dwpb~⼟>g {@ !ES'$a#zWi/MPW6W2Y\*6lD_a6$|fkW 5Ƨ\1O6/,(Vm5?h-|?6rkipѾ Mp@`N m_>$E7t[~<7J+)]Y;_tth|UN>I5Y &7dIQK Xix춻?i^8Z4j>S#9ԧ͂N>njŏ$^C𥮧:tzV$(#mX/?Gj~}S-6I% U3t^+kmgƒx/Z,/`?ںvg4'0,nw.3`u'']MjK;/H<)so h1L[Ol~a.{ž Ӵ x--3TkVDV?.A<^-w2:)x{}k>&? 4^%gK F;W3޴6wL_75#6>/?=ܒKyE#5wU[x5g'i^eԓ Eˈc!@ψ{MkG׬i5^xYn,ʦPz p:~:ׅd,akxJ+ssÉ-F!P9PB_onvm"|h~|A57KRnt$}#P]Bx(RPXl`dz>"_|+mM`կ-Lkػ± {O(g_뷾;<7Iko1n@u~MԴ]r`34rU7p;y#xxT_<IixI-[Tko 7 zoOCCԞKm^5M8'@" ~῏~?ڶxR:|Y24@28S}{? jMSSMc_5)5MF(VBTUQx:_#?/>wҼ%.>('|OegMjw آKLy1@Bߕ&<-Z4 *[#L-O.BRǞrƽc 5}?t+)n6v11N+^5Oh)WZQk]N=\7̦"`y${W㏊8?XIMXɪjRZbqH[KD.u0N9<wP07m D N!G]ēԜ{|B4_y<_ /tk iR߬YoߥЈ6z8s]z 3]P lvI'BUHqA÷z E}vt~N:qL_ Zm:'P?>"v?}SOӼS6{oY\\D8f&((,W?^jZݽmօJY8qtnu0FF8<w?Os=oht~N:q\|m)4FmiW^xgcRD2G }g2f8ȣp;EŪs9g'G] 6θ7:#BpyS\?Ïkzů^3J4o%[elx\ZZG0|Bs╴Ӭ͑ҭF#Hҳ|Hߍt ;E] 4:i=6qTWkCMuԍf+&YP_`94W w~)^xg]MBCk%7猀@$GjUݫύ>R|9 y@n;Z~?o j>3x D|?BּBEtOZ6̌x=5'𮧦Ǯ|Gܼ֠2J,1Fs|>5|[qhϯZ[\?]Mt4\7{6>;$'ƥEM461C \>_s/L<{i5M+^oU-m ]ż򇍝e/ Ac'E?Ȗl5=Ĉ}E4N;MYfEFM`qǦ9cZ~&`Fq. V/cW` ]V}R|z&s~MO.5zBCZ{&ZBvd2`89oº&;\n#T6Z&o\I$ĠȪ M}EWK>p'fi7ieiJimqk:Ԏ*>P>^7}E7pJERC^$Y#HtWP$V0sTrx'>4>F>xN/ic \78 {|^'UC=e t# sҳO V'o?BX]CPoV1|%RO;1k-lth@l,Ak🋾%xíYͅ,n@" p [_av+-?obz^|a{=KMCixEnog:qm8C$m"NlM}OࣺRo[A|SCU W -m7P&cB&Rr߽Ģk$xBx7λwŭGIb2 3 ZXk W]\x_ViӼa(x+`-q+%-k5jI|Žں$I~Ymݿ6oGFJѦ#ǾC~Z^jZ^aIg%\>E'c8x-Iχmgka 7rw'gT%h_1>~ xwL\&+Nڋ m]To\bþ, }NjXom4r 4A3UL 5+Eo$ޮ/X\xQyS|D˭xKG#zxr8l<*Kv"#LJzxGW6 MA ª̹=+:w-_4Z5p@ b#捕#֔W*;I|T|=ok4*_I)q*n%0ͻvQ;R5:eeGq$ mwh#?@aWMikto|oxPҾ!6 :ݛ,`|Vp]SmFӵmC_՞1K;ɧba#j`t۴o ?FO%yI|MK؁/$e̝84O˯|2$S wB_pGѿ*d]iC Y%I!vJK] ZfaktI \Fл=+Ҽ[q=BeDQQpaɣ?: (Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@3~P^Rkkm隭YK١p/3(5b~xZ|cs#hOyh$sQĎ럫@ˀC tȟ86L&0J]?2όj:~'΍ny0度Q!_5hWZ/tfuKY6X$I b^q6"z_o'ZIVBIyBEkOs&oggk%رf>[GqP8'E[3P[ՔO, B {?9 &_ g=r'y|so_E!5-KM,f,D:$\?kcN}O<#y{'B/a&sHm$1hTo][@'OӉ%TV_*6>f.χ>KVƥp&bRFNNzN$Ԝtk+:Kn^溟 h:&MrHJTǢ:TK[%monlEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEfotoxx-20.08/images/batch-rename-tags.jpg000066400000000000000000001324371362435004500203170ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 380 609 0 C     C   V$" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?& KdTEXI1ҬEfv)-tb'Hr1ץ|c!QsF Iqh #{W>O1IJэS -RṔ׿#QKIDq EŸB@϶s^GֺZs$ef =.$Y[izW|/(H6W rŷ1'z^tUTspO/'{~- ďfTЎ:E$QL` @$l̝^>;ō"R#i61ܼalO.~e:+KG".e3]<o, ZNt:'Wbw#xMdr mjx̽3Gzp'Wy|sO; *w1ǣq5G|Y$X r EeAx?:7u\i)u]I7KDH?)c;R_+[]?/O(Bh:վڇ SSo8IHC k| ǿimo({e}͹H9<Տߴ_mIڵf dX,p[cTO=JRqks9?O+oڗz]?+ҵ xaMe;eIgEq3/i^5&ӆZ+Z(VUG0X G0X ݾx|>oo`aWCm=&Y8 ]E]&\ԺxTF$VS^( r>λW| .rZڶ^?u)̒Ғ}71E?^,Ri-WO;Xht0O6?Q(}Ý\v`t2Wݼlj|ǘ?F!ӭd#uklQ|Jdzmȣ6?QÝa}OmwIpv]yw `pe@>xF:)":>&dNx]ԊIty`0Crrsa1"G+[0OtyvC@O} }GO/^IMF13 ɦ|hYX/.$w1otg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tb]~}(F"\~v 6A m,7EE$8~\oo M 8Fgr@`1?N#.񎃧iZ]ťSNQ" pHk7œRL_,X,dWLaeՠݒp_8B0Vej|? zsj}3jl$b6nNIo}b*J|g5? |J.%K/ fRIyNP>)߆=(ZYYeɥK:m4ggº2HUf4˟:Cӵk?j as ZƱo s.$%bQJI+E)᱃_ֶ1)'o_ҊO|.~"XS.k|w2C RK:U2&rNkmzxtˏAe4qz㽻f[9/ (QCد3Mw^Ӽ1Ojװ||ۛH_>+u+ߑx67wwd!ZO#p|:.:`W9xQ_գ4Yi:gw64Ѱ-;M33Vrܿ~yZ՜;cJK ]~9W[ -i \YlxJּ+ĺt ]O*rĢvbH{DO/MZI® Z_Q -i _!W8~ϪMv-|$6dպ@x<dž⎟ ^]^\|4 2g/=M ;,z B]>{w[b[5G!B֗® Z_W~7_d Ǘ=ÜZ rUKc{Ik躷fLd_w?Z?UkKT (Q® Z_Q -i ]E® Z_Q -i ]E® Z_Q -i ]E® Z_Tw? %K- 9*;Aw; bOrcZ8L&T$HKmo#b t5w%x-k?_`*I\eXޝ>Ċ&$@@Ge8\qBxMP[v۷9,l SbNMwśvpt+gҔ֬ |Cv [|_y4 Y CJRX(a؀Z s|4}:)`$Fc*UP7%m#A/56( 9l~8ԚhW t8-&]B 2ɵ fI[dHfUmG=>RoDH -@.)FRFU`pF>񧂴RiRh$"0dtde` _K )lԟU7z3=F- edtwW?O]<^-o_KţAd4^-fF !pL*w< [↣mctl'K. 2ĭ)k|';xC$;atxO[b+Xe@ i#xU;@(7yjm5)6(Y#|q 34jAE40h'–ɢ˟âY7:E퍏MxӽyąBq :_<}xhQާL23b)|Wn2gfF߻9ey0%ծKK5~$G 366Tm`A5zOFh5FKƊklX4*U730f=KJk|u<"_36kqeYL.w`e&mhi6nÚgkm. !<.1SPYh.ik%܀}HLi`cY~ m3k}Y5۹0*&琜# eM^_ Wo'P᫟%ǃ/ خ-66 y/xZ@i kitt pk@*)‘׭?<=Kⷼ7 4v_^ȃmb<qͿ %mzI5}vNo~ǎe>{?^C.tjWLdXCy>86xΛ*yg$d Yu6v@6r:2|yXk0٣*XK;wI;da`8Z_MNӣhRF^!8[__| -zqBFq4`]kpz>~4Os~%Mm{i<$Dꨥ>h63VشF (^zW~x>MT2iqZMpx9cgƣ'h}mh[ W=mWJWkSҠ `]12G/#!)[h/ŧADk)*iFdj@pBv:G_XK.uVqh&9$v1H s_#-wÚisqXR: ᔜ3.~cWu/l$q?uß3mhEuk(D,eAMa}Inhv& E,%2n%B\36P/~xoVcyCu4v6h] 3Ycpn<*+_V{Eyn/6&k?5۸&ta_jom|px_.Yj{o=x),l -IݑG Ö3ZM>+K{s_}gY#e,Bi!p 9!D׺mmkZ̐\\1%6X$φ.bk9u7Ct7\g9<`}D}yAx_K5߆5-+ZQHү, yfPw!3MIΉ_ڤ66i0{o .cG 0 qӊXQ*((zVlJOK* ( ( .66ɨ[D +>2@'-k6 f '5W]#_@:gXir().Dc_<M뷱[Mkl.mmmi[Ecyw~?%?+e 4֚st쬡lQVdA(n1g=i|8{;[FZ#GokrcrPp2\WM .A[/O]#_H _TMIo yB>sףNLh Ϭ]>Xs};S6$һ_It BҢ%?+e 4KVhZE:U6I-ݼv<E[9zIzkC*mi[UQ|bH' .A[/O_ &e!&/nB4|B䀣W>A%d{3! nXz1V?%?+e 4KVh[EXi[ g!`Nc9梓~-1m\oYR'%q9$T?%?+e 46kZlvem̻%dёqЊt{CO2("62)?%?+e 46F,lԋKqs"m8f HR*![⏈/8:b)3y!XcqZ_?%?+e 4'mC nx/]k"-繼גQme-ŽXsY:i? uxZZk֣C$]Jf $!eERvKVhH'![xcJmJ-u(HB+Wǂoпc\$Gl!?ƏIt B6ՒGI c?7_1ǂoпc\$Gl!?ƏIt B+?lx' 0TO  a⫛H' .A[/Ot<C~?lx' 0UKVhH'p:O[ B?U<C~%?+e 4KVh'-ߡ*mEvY?zIt B.o #O|L-7iܱEyo̰!\mVvT{?%?+e 4KVi1ڛk)4[T. Ƅ$( '\֋/^-ͭmfqa#ql bA=IIt B~k#u-B+;fT, clckAH\̱HZUd2=x5-''oG"RAOO|3Cg /#ѵzZMd[;6sFqf<w>kik}aM[kY [ c ('KƏO|3Ct>x[ش]wzĶ1vA G'K\/5;ľ4K-R Ebb{4 h?0iֵW$I.=:Y="rRa[o S@:xk HfFOn6 `2? d]!guh> &umC 棗 yu z܏yjLssF;\N ;1\= _G4{4 kmt]+{ >TG,PFC#o|=2mGIuM>u$(Hpι0sEqg /#?= _G5^+ԴO]gNf+?ɞqt?xG xrmox KheWnDp* j~W {kmRyy{ݹvN<O|3Cg /#G4:i? t V= _G4{4 kҿe9.(k `2? d^@/O'v_E_O|3Cg /#G4:i? t ,{  '^"0eaMãi? qfB`+LJªX)'tW__z]晭챸Ԣfe6#qĩ" 2%-Rxm"^[BU Bs__Ey/o K oPԵD-t{Ⱦȳ1yޡw3gJ_Uӭn;[-t{kd3J}ihh$iikovzVsya- Q&B8n8v_gzyo4H/..,1qcqº3^9;RM7A,yh4M տ1VzC* ?fbxg U?ޏ?ހ* ?fbxg U?ޏ?ހ* ?fbxg Uo:汩Z'3IpDq9y8'kKƚ柣h>*XVqF]#$f]p nyZ231Y릤EPEPEPEPEPXV1^FkJ߆?hCzRx>j\ ѝ{f|vzO_ ZWv;3 M77`MqsVO- ؼSi#7 `Nܦ%ۂ߻w ? ~~2׃,}4d! "_=aٰ@RE%:]Jov$B|}=|bmFdֿE˖Y2G2B03Af^ 𗌟RWv,-q(O"+Jȑp}~.G𮡦c[gTil/qڷ|x1t ,8qji}-K&cPNiӯ5vT-UH\ч¿7$dxV'`O7OЯf,Agi76":|H#ZLU =BҼMgy/7G[_薂(5- 3)xtI>dv >2xKFxKT::}X^#$/mp|#8WK [M{xgǀ5K}gB /u0,h 8G_ xi7`̚eyl&F-hՕq*}O'K2M']IAY23@4ݱ3Gn:HH}D%՜8MOݒvE|/U"V4J#@ mm$tmH+be;A(Q?r[||ͨGk5;@a ѡ;r?yM5&O"u' XG:W4TJ*I]YMt; ]~?/354o2X4+9*Ws`t,~#džl4^-&-sT]tO v%{V WՔUmܖ[(0((($ſ A5J=ap*I'7jzU, h@#T+| 4:ޥO on4MuqoyTTܿ!nx$1ψtE61JTLW, k׿e~we^mF\j y%am*k~%tZ}4i.nS upC1<)C֏YON[W½O|Q iwqqc"VX(еm4iZuJіAp#5R<1互Fr>ܾYxcOtk? .=(rg?%R<1SzpA:_mLJo^ȄG$H )S]v}o`vcc^Skz]owE݋J䖚LǦ:a 3Ÿ0ihYOG, fxS ?<)Cր:j+O4 Z騮gg?` 3Ÿ0ihYOG, c/;GvLE8뎝E6J߆?hO^κ0mei5K߱QQIٗ#[l|JVu u$kW$}W*B*}~vyW_ KZa%KӭAie H(@vKN@Եmhh.܁{ʎF ߅i֞0.oi.\sBYߌ?"B|6 m?"-t[_.JQ+^r6xGV7UZ^^p]4.ǖ5dػf*aKuFiY_}1 ih' XzSl@Q]v&im/lnYmruaAs_/7a߆_/7ߋ^3ook:e8=$B_3#迆Z>xcG?Qmnm4e#Ue(a1N9Soo W;yRLXCoP$s*I< w{4/jv>u K{crc*vWQJ>/⮉c_CMjNu-r$X)tq0 |_D:͵k~;k-Ph".6%6y0үe_߅e;]] K5+a*cQ$G2x6z4(Mfs&?@gOu]f{wkSP&9^x4Ŷ^*ҭexWMc-e&C٢)NعWIZ!fO+*uLuf!O^*~!C4{};:M嵞v`+u{Z1#G+l83R)n|G>+g^)#}7k{%e K+V.F|9G|AaGK*u#cQk~ xQWË4xMjVIYnZB0‰qlZ|wvaɪ^BKkxC@cfIFmnKHֲIG vU!o !F9XXH;A#P׾?(3/ЬྱҵRZ2(9=|Wx|iiJ|'ksIi.ԬcTN$#1cph>YNЭm!hkq^_?.Q?)g' QSG.MwoOK RkEGσ;yMW&e fUxG*v ?t_R\mKr"iwKAqqğg}zL׼w(-.-#m)!#qSһ}dO ; ڝk]A&h1%1qs[ O yoߢ=+Ǿ gw:qH-|U!s{F 1C?JU涚9fscb*̣=q'/x,jΗ=ג4p!Pʬ =*%~Vq2R~񏋼?AF5o v-Tfo=YTysR]~"O^{L/E;ZL[3`$۟?h*𞏢C%ڽڌ%ty/ڞ2HFI]gψ߇^Tgx맍7adg T6HZ?/#8{ᾝ׉4xt&2yq]~v0rk|7 gv:Ɨ>Dwv]3 n+x_nt_x5 ,nl^ Kߍ v#ma>ٍi$ʉ2{J9$o`>?[ϼ_Ii |Smm_^_ i0kz֜jZJ4\*\vݎqҼ ?s7:-m|y &wvse[C|MkAK"cR;3br^85|U?~l)&0Nx?eMn߉|;kV:qKKkF8ctkH"xnJ r|twGG֠~ jܖrFfn"h VڲHoYo̝T[O_iGѰ~+>SYEs4DcB|SF:]>kX6WW֜ھI6jP4q>|mGgῇtkIwe0=g^֮RD@$a$%sjXZX.=r3GȬ"e)sBZoFГ|}WmmO 3`U Ѡ !H'h#_$l4]Dk&mA"9.#l8>`%fh/ğx/ZWs_hl7)՘gIN 2vy?gxrJ|95j^Ț?K͓d7}fm? b$bz\ީK9Ѿ2Q D2 {̰<w`zc?\!MWNM_A|0 BJi H>?ƶ_4:CjM̑lIL Bo2|fayk4M{xмRt(_BU67j[x_&eγܱW,1ctE2wr+jJ߆?hFjTQE!EPEPEPEPEPEPEPEPEPEPEPEPI'7jzUw_O6o+_XEx?eikahOB#RW]E`)c0P˸c$q^טi?O|pefi™ ڭT`4u2C8j_.ے|q—0?ci:tpgi|ǀJzW{yY&}kN//"Q䥪a00]e÷qר>BzNx*zhzGo+-"rC~Fp ?d^ io/2rRGc l'%#ug&[ɯ/'틮\|1'u׆˫i$UESH\ <=Mkah/YYNc-n/>m.Fzb~ҵXMIm,#13̣p@Yd xs:g욼vI,&{dfVVrЂۂNzowm~?74IOx~^m&V-3zR^aQR" znx=nOi2._"K!6A'hGL1[!կ5VOK}V6c>YpkƟYIe,:e&$u)lL1!q܎A"\ܶ~wes_?;Y~4;I*K-vC Cٛp),ppK̗z=WF}sXat}ZMOQ;Y'|)]H0pPI KpYkoVEtHG`,9,Ns❿}?^KH.6 S4֛M{5_<+aof-5?iΣǮ(ԣT@אb`2$tWmG? 9Kյ/ kWϥ]slo*fS[\a g [kпe/c^}*^mo,ȓWçr eJ.0+q[Oo~w}&ir(CK<1?scUPeM{qO+ڻRkx.Kiiɹ g{x{YIݜqyWZ'lj-;r-~tq?yɹ d:Vo?xVKo7mtuԍ:f0<76)#s#nPr8j!WѤKdfT2Oǻ d#u*n7ÿ jӮ8/oVE\\~p2RKxb>ģj:mЕվx- 1x閺,N'gTHȓvf$ x> v>$o [a1x?.Ij R  hc:.ekei;F(<YG1Eݰ uc^!O&m6ZkwVzZ{l#XhAN"L8TF_EK?[O&+z ZCkZĉBeF$; P%|{M|Bc=CK¿ \m^w-3[HF|澗Í#.ɿL.n&"Hy7־%}FOj>t?iF8v۰`1Tݯnߍk{/GX~6]BO+|S8Z1-?fhnOk/=h< :ׅl4Q총Y!k[FɰDl`=kWB /jZw|Ax?ZKXEci͹f` _>#Ɨ.?k5eѵWͧ;>bpA ⓾[ |3>;xg\ֶ%B|$z` |g?:FU9P38trGC tGѬ-<#/L7MY s]͹*s11$;~BWGo~VEּdKy$B1k'y5oI WO1Zuf큀yz~x_>0_jI|ڪAx'$K*7M&AAa\t߱y]#IE-4QVE!f-9 qPmwM~1ڳWs =|g??~?nKGnKيSrpA9?jW{/mmx4HڎMZoA;Hn;KZ_+kzՆvmSuf,F8P +9oٓ^8mt6څ.4YE8 V|rYRIogo%}/o<u׈O^x<6鼒%HS6ц. 1\nm7-J躅֟I|Wkax; q4bkkWaܟQ٢X FO}>0N''<kox^e֖uk^"]! Dt(l Fy]m#{+2>;]ߏ iZ|?Z&ou#Ys"chDr^>;x=>1wyrZvN>y'أG"9|%~#׭foVZmӵXL` .DIIei߳֓*/ Zx0Ism"}R3’; WKdfTˏv@m9'6G~ -oh.|TǨ^5+8vV*?q_A %^glO|!?,m既E$Hz7 d9m%wڶ{CZxR+mĺ'RIuC\hlvl ;ܼxdG;vZP}dJ.^YIdWqIZW mIc c0$BQ%~kcn|sŚtskᗖ("9!fM]J߆?h▨{O{'-V8YdRP1zJ߆?h+rݯQEX((((((((((((+_O6o+_ƿ$-l VT%)?|=xK gu Zm1)$;q#ǁ#/K]uX<=s_pKM3 B".2qU'vA G|)>"\F׾1j]ނmq&Cw*@;z?<;|ftOkve&\Ml/[]I* Q€&>9'?iw>/‘xE0ˢ& Dhoe=׆ό> ovxwPCGB_eo/7yJV&r~;=^%׋>O A;U3vhǏ^>8\ivფ+)n+pʷ?x?'ijO>׊vd'ImŒI&!fl*1#,@EWD_>"x_lu{XgH~Vw}-gk$'?,Q.<}}ehV)F-v6Km$6Љ$־?(v~&k"QZe/By~wl i1xS݇<.EO ItB~%: k47P A?VPrrq_F'O#^8njfC&Sڊ2rNM d7ݿO~_M-G@d##5oTfuq~gw|@\;*"]}+焼_.axs:re Ҳye|);Nzg5%U[QWPҮm@\EZNI'R1lF@~Z]޿Dž>+Evl>#/,5.HdD3$#qav! s|xg]$EkcPuH!@)H:F%}Q/ҥZDbǙyܥ)w ΰuO=WԼu?Jm/{xp9¾8=h[[v^;tZ^i:em&]FUL|#">#xwI I`&o-ՍF,y.ѷ#9#G]!@|y#]Ʊiwż7.dV&pv~\{Vmڟ5kEVV@~G,ɲf19^Q}=9K,nPnp 5ÿl/}{kO ]Vl9PqY [şs^Cռg/Zu6TU1$_QY0Ҵ{7@5-;[iv6k}(~|:%$u~z&O~̟nj_x[DGMAGAWU4 }yRهp7n$߲w5=3[4^hkL=m丑Q!F*BMƗ񾟬^>W\k2P,s!\֫|9?o5 ;6e$ĚեķӗTy7Fs!:7{VЕ](Ko:XnC ֮4=2PfNv ;>%m}xRMS Yv ĻN_id y<{Y׼ ?zwV9Yp>^= j?{ |3Ho{jz5ݎ}; ©,3+ݯ~;|5un!RYYtݲ\ eosZ؋'&kxZ?xc2Ѓc8*ZN"^ww<]\^.u Nj i)XO ;`Wq |{e?Ƿ{x^hw`m5h9XDNN9 CKlhNv~5p>qk4Gv'$xK3 |zV CF%ms&ixw F4V ɭ5;3Q1NQvyhCr|N#MV_Ĉ=Clb{9ϴy~\+EGNR=;GYYm+fcg[hL6rp} 6r{)̕9WeskԚΕxɭ|Ex>צAAݳ*11@VQpsW?h?6k{i Cx^Uu Xu+d*щ$Y aq iּDxs]uck١xQoyjc* #S&Ig?ߵocZR\x쮺Pns.TaUC@RKmDG/~5:ߊ<{4T> o4 O`~r;#)R k<{oiaiU?2,+Stm*rFyJlgh▛֋sOiZZ5~<F?ո w^CP^je4QmQ@SvK ktө-;[/ gzheC[0XC)׌9)V1^FwT׼5sgso6݋Y;"8bnR36[V1^F_Zt=Z((((((((((((($ſ A5J=ap*I'7jzU, h@`k=Kk3g/MGIšxGzd];E4@J谖M˨@>w] M-,,yhU1G#3Đ9$Y ~(Gk94+ Ι}K$X4D gtVܴ[ş:ҿ ~&|A%·Q/nt>kh泷KL\3@A\my7|AԼkSI 4? [q[ |L;ON+~3aOOl:fxBn|=Xդx̔(|6?+˿||:_!F{Sk7Hn" v9']%KI&kYx🉬oy_mmn8;ndBc'mK1wZxj3A58y;󏗵8+5ov|2іmFV$LC%*-ON3Sn.O[Ϸ@9g\PWjZ)F]o͉Q$ytߍ^?'ѭ;Kׂ`>V2ź%#X |]xC⧁5o֯5R5mQjt Xyat? iO-մ}\KnHZAaȮ^2 p2#ڮ\_ m},S,qA+8GOk>$w]߷;঍gm}{ .$Q8`!ѥ˹ڻ8ߍ>xs^淩YGusb֌wۀ; G" uHyt+UsSX*22H'3^VwJo~ 6 n/y.];H$,\2ǿ |6'﮵ _hb\!e \W|M#wW^,mB^rƱ1Ǵwdi#|5Tj>\)mty`;XZ5g Y*VIM49&YOY/|$`ѼW֣nP]FnL[~y^~l 텦_Jl-Ğ,ᘼEhl) 2~ƽt>;|ӮToMciI*7yQlv$~ #΋"ȣ)?J^]ܽ֟toʾ|4|;5-2ZxMM,AcL1*9Ko |L|OFmov~cf> Kt :9w\mcqWF'aۜgg|ճ/ kmWI_0WvӚ*[5+~xWCcx%qv `wcGa:URΩ-~?M/iv )2\ ]G5ڴ>Z|H1i$3M%XRcVA*3doXN>.DKIoi4lX\|9ӛU!ֿ Ӟw*P7'מ%cv?.7"0+7xiwR[}Ě}0c>l*3H3RXU){&+ݏ.k.q.OmOQ\,0!C=j_&j^3Sx{Rᶱu33}崚W ) jk[|1|9-OċXCmq$3!YYnݫ'Τw wj #Z4KY4&7a[hZ-f2q cM[}Oy/Z5{;-)5[xdȭ6ѱ;#OM3tYF+i^9V647]2ɦ>X ^(d+b?jk8o.|/ǧw)RYDCk&LH[,_ ?ᵇi+4֯6k-GX孤)ڨI#G.}{7Ģ{i[22}o:g4+|{ MSPIbiYQYr+%]_ī]Dx4KktˋU#+jRhD;L jV)aIsӏ|Ykxi>#U[_4kei7pr_?|!4YΩ}3kq\"` j IXW~"օ˯i|Ut `doFFDϼi`w\/Mmϯ[GyѤBt<Ü[$ piun?s_p^ҿ[ex/tM: 2iqj KhvyBf[=k|ujyt>9`{+fK: rą셬iO3ZݗAj̍:X}4zIvOd$y^F'^wY^>uG/*.2J|(y7?nߊl zcm2Y1.ɻw eF|Q_ÚEźchM}r.6F7^v"^c@#c❮O#7EK}qa-q~qW5;/%Z\BzZ6\Dvڹ P&3;B-?y&t;F~آc!F:_~%x➏cO&ύ.OLiC13+iFk xB񌺼Z&mɤ^~\FGѯiMv> V3wx-p#:!O.5`31:W#мE\^H{ u?W|7G A 9OMc4: )Le'U!]@ 1ӗe&x~ ]'OBɧU>e @e,;_[GZiB/tiV)N FJR߉ Z$4]KU mO˾ёdOR/#k_&e{-v k kAx6t\͵tw[ +١xlƣZ^d>9P(# > rFY1rb\  }UA|K{Omӱ-;n^G([~~a+]cWׯv rmHDopȈe XvC_֑äNHDӤ:r8 "IoR7__IE/؊)*8R2s_3x_nj|C|IoG[xtVj6/>Q~rp F%6/nV?f*k5 9ePF!hmg7_/\xz^'ἶ=4aHDdk3XÃ*|b?3PMMistj7Iej^xKvŗE$'dE+}/zīfo&os u䎻d=6ƁrKs6cQ &7FFA5>$Giy5/:nXۖґq@b퉃_!jCksA$V!| 5~hA_.RxI)~뿒}߮?/]?~Dvym5Cow<dj+ ͂>|l|vn [9ؓա hZ2w;m _U|o%|=zއiMo/=nVgK)wP_ex:, -bY Htl6j>7=eEi >; hTĩh7sJ(o,|\xS׮\[{Bfl[ϝhwһ"ǃ&4-bMM-ɍYnm_o3MI>E++X}ny>B|ᵛB8"uUpX9LՉ$Gi φ;.-f}[\.ʞ{WSj~X0 ׅѫ~#6_ɼ]Z?УV1^FgQE@QEQEQEQEQEQEQEQEQEQEQEQEq_x&ZǬ?W~5$ofMRXʥ-QH WGF_&" h/pi62ĩmqjj0U# \_Fk&{Zymb38:(t~ 5t/!)Z6Itfp< @]hּI5^-s#yX$R5PWEE EeV_p=]^NmZg 7CxjLJtLwK%37Ţ#'cW1ZP{x{J{vm] Ȝġ bM턮I+ 5Z[[iq<2,}+0ZMm?u-kQgF#i/p:</֏i|}j:o˦7ka)$jƀf@)/?kk0Ge_ 4">U3]HipzskĚlSQj729wE"NO!U#U tTQ@Q@Q@Q@g+ylӾHFs?%Ƿήq׽U5kFGH;ٺBm%ojzQEPQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Wƿ$-l VUߍI<[`ٿT`KERUI[$x?eikQL(((((((((((((((((3dذZ 7nڪ[x_;GfϲZ?{}1Tc/OP=Z((((((((((((($ſ A5J=ap*I'7jzU, h@`>R5sĶQFtr͜xjTc/OP=Z((((((((((((($ſ A5J=ap*I'7jzU, h@`#4& qUmc/OP=Z((((((((((((($ſ A5J=ap*I'7jzU, h@`mD6T\[m|Q6>m⨸Ey' 'm|Qp=nOAOo'G 'zhO*AOo'E+??ͿUhO*tW ϛ???ͿU$D6T ϛ?.^I6>mD6T\[m|Q6>m⨸Ey' 'm|Qp=nOAOo'G 't_ ?ٳ?5J=ap*×:. _{uEf`Z[z|]iQ@?ZTPo?-֕k=o>. _{uEf`Z[z|]iQ@?ZTPo?-֕k=o>. _{uEf`Z[z|]iQ@?ZTPo?-֕k=o>. _{uEf`Z[z|]iQ@?ZTPo?-֕k=o>. _{uEf`Z[z|]iQ@?ZTPo?-֕k=o>. _{uEf`Z[z|]iQ@?ZTPo?-֕k=o>. _{uX:kv bD ];OG'8h?-חOIOc%鬥[X:֍ /C4]/&K{z|]!sG|_Mk4k=o>. _{uG й/&Džl4pĆGs~i`Z[z|]r7:|/ 6/4V$O"27F m_Yj&m`)#1"+33^{cGz|]ϵ|Fu-Ib{y 6qZDŽ<2Ћ^a^?G\_G!sG|_M^?J0a-A7ӑo]_Ev^jZZYO%[cA,&MvdbI0;!@R vQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEtPppƏk ⾹mn/ĶZ=)roiEbngTe +CBnb>k ث[+#@zR}h~c-6njy%qMSK~"<{Pi^!u OKYc[L-RHi9r$I?/9q(-++f!co\Tk/(ÏMwu_XXu:u"G.nR8c7cN7@2ɻe{7/vBKH)K9a(cFϜW \ۥ4AHe5L)cږ>ѨhI^q+);5owG߃/[0@>c^rJiM\·"qmrSs"ڕy>iskWŧu 5SK+1#=R|;ʒ<3Ÿ<O>3Ѽe.. ֭2 ye`˙PO]|j}~% w7:vEK) ED+IFMܱ>.%O<&~yѦaΔϴyGCJw^!u6aѼiip0KYŇ;eGI +o5+jqxoj2xIfkwH!kUPQNrXJx9&LRg[XI W*A -].- p!)o~$mB5;gΘ%6_aH)xLWxNeF#Ce9}Nk$.VvfsCֹ֝qosympR5U՚[Tz QRPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEfotoxx-20.08/images/batch-report-metadata.jpg000066400000000000000000000344341362435004500212030ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 145 274 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&se䚣y ._-Va+3ZԤn1!^%xRsC,F٤8vAƼ,vaGw/|(ьJVmukC@߁#$[#8uE!O$lw/O5 еjN!Q]6 OB}]+_o/_=Uh.X4LO'WO r|=ak/|GMWJ1 {loݰ 8Fz*57NHSGvC߹YwF(Rj׿C/:<ru!_%EuNOK]1ᔖK짌Cn&*v =F+/5ɼ?Ú摬KlKIV1%H B;~a|Dru!_j?Au=#JaA{4"}BRl/S\|ޘvwY~rMX.|HĞ_>^r xOB_> ]Rf*ya/L=:?+_k'Ğ&4+[x7O|YdT!G9oSw3x?X}]Eޗ֛%ī,FQ _iR܌1W/_Q'WO< xoK}k÷>4BxQ YsbA-UC <;Kmyb! BopH b)Q+⟕sǔ| EEo]zF&fUm$˗-+71Eh^3<;jֲjk:Hb 2#~0vb)Q+⟕s˓R6{u{s485.DOތ)1xy^UV0ϮjTԮ"7w[qU 9[OʎGy:<߁|1z/BN8y̎^0wd`cToo ?4/b$rA8;;Emn $ד 9KV?*>ox 5cU^mqi|lɚ9o RZ,.d4.n[pۄef08kվoo׬OiޗJmSS>% qs#!Vs*qoOkgְ^Ȫ0hR|g?Y/ev*,!$|οԯ$^&G-TIHgcKxv)/!. jCR)KHAt5y'O&.$H/ݒ(G QNO` 7"!Qw=gwayrLzT ~!{{Fyxm,wqhf4WqLdKNKBN?=&隍r+o.`Ȩc5;ь4\da#O.״PMwijqwq.BysRƺDž?76wŪ5~axhYnbDcRT6X0 2IM˥Uv '61WIRa\7_~~$ŏ}V aӬf 'ؐ{p2}>Vv);#. X5+]#J]̱Cgb=h$J}DX۵ѵAeS#U 2x޻땅=Q,09r^)v-vMt0[Az`Y6)'Tٰ0hZu94>t~[anxzRFj-/Myjڕ$ 5D#t#._ȵ*\i纺ᧀ` bJʡ>lP8>lOKj<_{an5&mB{k˽*uyV!nG ؞ek Z&-yeu4RpǗ>:%)fuEPmkJ9GZȡVqISsZCk/CZwͨGiW|q(D*2DvzEvT]䗝C:|: WR^9s9rXᝁ :+t7FVu1Ga&YL6 `mD98~&@?|AxM/J\W -vAhV?0kzkM `Q_66n<_gxĚ{hQhm/W@,rl{A~z'kˏT~ LЯmvPgui,k8vvS&|$5_bM槩aY7W8cPK;1$.-!'H]H`{k[x[k:neO6#[[wg׹[X` #*$ k4]OJQӮ̂@ʾÂ+#-$E}/kZU^NKtrOy'gK>=p Z f1Ҫ"o_O=2((+ny#^խMFwbm羆'AEUOUCnM\qBs@7Bgin"%1M.]6ծr)lqzux5`0j'.|@$9's{'H?? GEP7O&O.n4,{sMo 1g9W`fU.54乙vJ$0I\.H`s;٘ISzUasalvo_̍d6Ayȫ?<w *[![f%pc1J1pWVWG-յlLW ;(E ӆAeYXXPxRaL+\eFi kU:>e^8]׷W/wBY#bF5R+þu/SwP^i-c(a-D (I滛]LїHӭ-[UGln81W{+la/2BwIPW1 /$$S<]ko])KR+(e P`{ ۢ/x >;W}>(v9vEI랴^oC$15scsn[P;ov}5d=>7&!xl'֤^쵙9ˬX"&@ w(#[S?v/$4ɫ-b׏˷y3Vm_]3XOtoKfR2.Fp:YE 0oM+Sm9Y(6=viiec i#lvѬqU­QL(\ ^;}]a5nZ3$U(尠p9O,߳d1o&潗Wsd Ԥ4=ÿĺ:,ҌK7;toSWSJZ͆q{_Y^p% yx}$е-?Wx]-3Lt{kx@J1lm봕yi>'4=rH|#&#Y&2Gp$ 6յD'gl<]oZ,ӽs5ba :8FgѬسxbrI97hZ-EeZZڸw@[<Ѷz6oцzAE'4];Ik"F溷NYDUEw>3[.u5F[Ҭм L]̸ E}m>޿}aԞH⺼628 2xkx})uM7PWZōV"ys3XH"Ϙϕ_C|qLY9 է7[$y0m$ld`1BzdSVȔxsĚ-棭lVEE$A&Jᗂ߉ o2aOٹ.씴z k?h:ɭ~ n6 5;RQ.^EV]){y_>?5]MtNU| go[d77nV$T-w+3ΜxO`cۥo;h[*oyqyfs;ko}/tu.tk:ƷlH.HwH*_(_kFtM+A,c%Ŭ K~ֱ&`0Y;qi8txIcigxz-GK:s,fX\c?|ȡ/0Oou{7ӼuoϦǦw:L=)!^NWp8=_+x@`7𥵻 '`@gV&[joUmtD>1Eki/xKX57W+ w#!\;k)>-ũVmcx?홢'F!np2sָgLcUկ^ 8fDlyQbOk:S[Z엱oyh3Y[Ӵ|]qymP0|j;ZR0n45gݴW‹lOϦj7'O@TJޣk o4^LvoP٤G ᶁ^|h:VqhF0A$;MBT.p&*̿13{3+6,>57w*38T/!Bm<"8Ԓ.OF.Ӡ3k3mQm<"8wtqcҵTWҮrd ?+Ь0Y}T@۶g(ݶW?@4Vw?mPm<"o{3Egn+vy^ EVtx[Sz& ᙢfCRϱDJ9 m<"o{3Egn+vy^ EhY۶g(ݶW?@4Vw?mPm<"o{3Egn+vy^ EhY۶g(ݶW?@4Vw?mPm<"o{3ECmrqy*6L&(((((((f3n(T* 3w%ժ?7IЩUۚ?5_O |iQ|#m!t˽FKL6yP|dҬOi-JY1`]w<1)Bdk_ߘ=?+Gnj)a4nj)a5YpeZWվl@4&NS3 @|?Iis_| /L̂9`ٶPOЧU$ЧU$-|xGfKMqTY=wm5R[#myTU HY~e瑖Z}=։jY{K=*87ƒ"arBU;'_ԣRx[UHeOd]pX'ڮϭC&k"#b2o Qܱe+^46Ҵ{gGxfd1xPR2!iĸWO4 CЩUۚ?5_O ifź_5Z_v^Oq;Gok\%* zt0AsjdԼ  c;e NsUOTIsUOTIB/鶷WcN 4ۤ7Q<KT36KuAxv̒Yjmk' |0m8nj)a41y|-ڗ}~>ÖdzKf5 w}}o.,>#^(-չ$G5M"A:h;xđJFR2 š(EkQEQEQEQEQEQEQEVHA?F;_Wg&;^6qsQj?// k΃:jqG9IxJ#"`Xsc5Q2i,t+B.Ra2BE3ǃ'9/{G?E V \D~ L>ēkC\הv?}?N{㫟i~m|;i7LXImмQ<)M!,kԿ_>/Mhh7eA=xWӯm4)kcj%4$4k$mb",0U-pFܛMtZ5wV_RZoä\;,1Бϵ{g?E/m5|ͻʭcxLӴl!L!&r~`A2_)a|^)F7_o(g]% &nZX^N@hʔ-Q8[oi-𝾟]Gi%5" F)0wugR}VQVU4?PH8 537 500 0 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?x#;ԛhc{}:moΛE;{}:7@o΍P{}:moΛE;{}:7@o΍м=\YkMJ_y+" #*=w0Yi77vqӄh3; ;~to]-.at=IڃI؎)ǃoluahbv:ށ~to]%l,}Ir=_𶡣^ȐJW 17{}:𞆚 knN SxñZ6ayo^Չp{~tv~to]ޛӴ=vXyho&SӮ;xM-I(Wv>F~uh㋫k$IE'i4 Z8Hb%<{Zq{ѽߝug8t7+$"tgC jΰN%P}(ZߝN5$V#vތ{jo k b7nOzo΍vzť坴l\2ýWLzbUےόz oη[Z؛ YX<ПKFm$h3{:].gF~uþӯIQѹ$ie_/,z7xOTmaмP{y?N;3{}:7m$ζE |OREVZJjD ddUQԪ4ߝhoF~t(ѽߝ6v~toMߝjͅɂ'Fr6*I'( oί]i[Y5ZD,mɸ0b3-V};{}:7'i>m+4ț0aZlaenn;{}:F%ԫogM`#޸0qEѪѠej !bC QWP((((((((/[x4K < LG_Үj:t{%$}+Ψ$ze)4Ν(i&|F8@=ꕽxݵ#bRwz :/|Wg} % 8'=zX 7R2ǸSJ䨥neWFм)wwwΰ?1#S s]G;;JZ4#9yWHa>-[vݽjK _Ct rmi,Lf#?)\zיIhS/ުK ;cO^43Ex٣ Z"y8++ye"OUZ]=2^Ѭ|m]zI[szU[r5- K[+ 4z=hio![s^[%?3Z2xFoHoq>Ssc6a_SO Ynu+"FY-hv߻EyNr;{U2tRm 05sV4MAe>4mh1UвG)+=ڰQuB(Eݽ۵ȓʒ "c+=jqsţedR<JZh@ pǮҲN ;סiV5* wǥyK 0N+V;4v1Jԕ;_Rc_H&a=c5$3+9tۻa*QV(8 ȣ ڬtmg(@>\1PHȣ ]TG{ m31>QvO}=Bd=9?ȣ kݬW me@0Rx/L C5@;(;_"K(%3`qKsen+02~?ȣ 5 ʽB>Irx=OEgDP9 ʘKwc~Exkv?V,}Ӵ {A3? ? ȭg!رGdpJ@kv_Qoe~Eh_\k%6 ep>٩an`%r=+ ? ȭ 7RլB 1BP/!ߑ[TP/!ߑ[TP/!ߑSj>#a<*?;AP28h Hy,qZbC|5@;/(7_"3 ? ȭ( ? ȫf.WFc rt}h ? ȭ fgApSnǥeߑG!HN>oe~Ekv_U+TW]H.Ӑpxoe~Ekv_Voe~Ekv_Voe~Ekv_Voe~Ekv_Voe~Ekv_Voe~Ekv_Voe~Ekv_Voe~Ekv_Voe~Ekv_Voe~Ekv_Voe~EEk-ƭkk-YK*r OJN_jW3[4rI;1=G^xDM!@"܏OգGJbb4 Y]Z-R5b:|Zi~xPic5m@SRW4kFHL[1؝01sȧ-dEon,g}W_QFO=kF]SY[5DR$He|;>ۣT9lIYޑ2~}ZG `$[o<,͙&9f>ǰtS,r52#FqVZ F$vYr3Ni=UZ;ma-g-lX59[giK6瑥ffI4Ӎ_C|\zݶ^"?n zGUrmgI|MYkO$;Jar {[?2ws]S:4 -O3ˊ{7S0 I3;O'jUՅYJv|'ժC9k!m 1inao1F@>z^'L! 5xz[hdgdcpQڬ]z{8.uK'Ð Zf2:{Q% '$wCf,ċ?ux[L8t fC=sV%Ь7m?қuRklE$v52 >•u-WW_X-qqT9g5q 2Hu)w"q֩jw,t"dVYn9P?<ѠY:ޫX{=)etR@3jxuصu)t7,*HcAt[Y4Ρla$֎=gk.H{xV1O+{n n=>i紷3nq]:Uƨre\/N*氖ci$JC,;5uWM{Sյ}I~/mSpPA}<۵K^PO֯qXX=``)1t7E˵R GO-A\Mz˱P.X!wx9X^V&Dy cE|~u\_ٺuK-E;O8\`(?zu Qxbgx#SGI:kؘdy|)wR쫘6lm^TI0;N5>7&RHHG3mϸ䌜q]GL̟OvY A'/ R +,uok~i:U`V$1ɫ ) (((((((((() RKTJr4.bUf 5ݡ2~`_J}mQnUA<8 /Ҕ@#_J6/:nbTŴقʠ~Uf}(ھ(_c}chл +.0杫K_|ځ1w,&d ?ͻ<}/zF+˧,2:g.q&9'Gz]^D]D8.5| 8`8"O\_2yw>6yx-Őkea(NApj#G@Q}c\:ӯ4=v~4ryr'vjCS,D[ֶ L't$HoOj-.W6<$I#r*t}9r&$y7wHq4|=v5+֝?i2/(NTv`Zs_gY¡_,>+]g?pLE*(}X~P*(}X~P*(}X~P*(}X~P*(}X~P*(}X~P*(}X~P*(}X~PϷ?o?શ$OVBq!(!+p*tSOn%ɴ=ѯ>7v!]đ)*J/xkm-/lcgK`+|sdǡx/&}SD܏en?5QH ?iw,v0Im'T+Ӿqj20mAYÛZctA#]qn8>@$ԯ4%#@jøm@p_ r[Ous%(d[ta`o7jE4 tD{ސ**G{[ߖݨkoU1;m~[v=TzQ7j?@;m~[v=TzQ7j?@;m~[v=TzQ7j?@;m~[v=TzQ7j?@;m~[v=TzQ7j?@;m~[v=TzQ7j?@;m~[v=TzQ7j?@;m~[v=TzQ7j?@>>QKxdzJC'(`yeAf|񩨠qs4b'jj(\~/_?ߋ?F.Ʀ!Oы|񩨠qs4b'jj(\~/_?ߋ?F.ƛ~URgPqs4b'i~gPqs4b'i~gPqs4b'i~gPqs4b'i~gPqs4b'i~gPqs4b'i~ ^\BeyUrFs@Oы|"W+O".UX0R}Ps4b'j bCM,a(/0'94.f@'hO4K>/ Ў]qs4b'jj(\~/_?ߋ?F.Ʀ!Oы|񩨠qs4b'jj(\~/_?ߋ?F.Ʀ!Oы|񩨠qs4b'jj(\~/_?ߋ?NA6TRQ@Q@y-$d/d}b>Huh~*"xm5=4D<}9=[AsrAcYf01ދCYp '˒94q+*dnxi]CTF p_ʒڬwz}m6b_:"{=fY6zE%SJTFm.eӮ|ڙ?zF<jXh!Ԟhw墐TZKz5Pǵ{;X,+hGFfxm@ވIqմ2l< MQS^QΈt+냊Hk^Ae+ hU'v=^1qo'W~6MOHx--t4y?vZ&sךk;.e6gɰ) ӢL6H 7h)6=SUݤK@k&a,WFi71%5׉tU&E9Qݎ Kxr#X\ۺ n`qT^֟X7aYnd]Y> Oᶹ/.Ѿ[re5otrm0LdD d#Rk/CHf.14I brtuv^ OC]US@[QH(((( @Nb꺛i^/,3F7yy6s-[ķ!ɀ) \T&#R}>_Zhf]HEW׵_M}C󼭿& *-B"T|ghkH 2..A_c@袊V3H;1Rl̄1z+V yiDc9EPs·<,HWR jrRܽBƧU>?_POߑQA?آߑQA?آ1?#y~GGo?+b@Eh#(7}F"( ߑR=N4{tֵ )EEusnX$g੦O ߑV`mO ߑQ?'{~G[P5۳9ydn Ncߦ Ѣ~(}´h ~(}´h ~(}´h ~(}´h ~(}´h ~(}´h ~(}´h ~(}´h ~(}´h ~(}´h ~*XgKD pr~r) EP^aa=ƕaF94gc{^U,m,-8aO.([R/5«2X%<ѤA-^h`[Xt\=ss]eM5RI,^K(%9}=HŞoJDy;2E+Y区?/Vh~ KB³:SҺ|=,!Ice<}2/ PG,qi6*pF~{V9:C-$p]h;efF'閚K$5`-9>Mi &GC2=WDzUR,vp& 2=;+_q4I[u~#r_8<־caayXqb1;H ,y'ڷ/8Ӯ-4 \_ bzO5KA΍1Hm(zsO_̏ǧ-Vmu-s=kk3)K ;vvVtV 0j KeYol j Ҏ[PsRQl'Cp|@  u.4X\.)Pݜ:Ixi/}>-!<*00HLeŕzS*n 8`Q}m1_ZA`WJ :G\J,EGciv6[BaO()iU?,(Ա tUOKJ?,(SR~RKJETԱR~R-U?,(Ա tUOKJ?,(SR~RKJETԱR~R-U?,(Ա tUOKJ?,(SR~RKJETԱR~R-U?,(Ա tUOKJ?,(SR~RKJETԱR~R-U?,(Ա tUOKJ?,(SR~RKJY{]/Ag"K-ܑeOȢ-QE1ݐ it/SQ@ϴt/SQ@ϴt/SQ@ϴt/SQ@ϴt/SQ@ϴt/SQ@ϴt/SQ@ϴt/SQ@ϴt/SQ@ϴt/SQ@ϴt/SQ@ϴt/SUQsk4yϴ3̾{Ee(t/G'K<Q_Ͻo"I>k4yϴ3̾{Ee(t/G'K<Q_Ͻo"I>k4yϴ3̾{Ee(t/G'K<Q_Ͻo"I>k4yϴ3̾{Ee(t/G'K<Q_Ͻo"I>k4yϴ3̾{Ee(t/G'K<Q_Ͻo"I>k4yϴ3̾{Ee(t/G'K<Q_Ͻo"I>k4yϴ3̾{Ee(t/G'K<Q_Ͻo"I>k4ٰat5 My{hHR[XHD9VP+Uyi52ȍčgѵ˭+N%ԗS4#v^.·iU-p2IǥQ<_l WEL'w[nN㷽`\xT`u!I&#y[p;К,1giqkeMmgo.@Ӛi\͍/[[6oq2m'$>6u,Ͷ5h7@Y@5tYhm;ƶc%8ϯX|Bk}B7h-07zJט=?#^t^Ss̉kEvsZ)_=3޹6]VTd#%ȞlQptQW|ii]Oer|A`v6zźxvzrػX]¹DN|4to,Պ>"\mU{:C-fsFh٣4њvh74f3MfsFh٣4њvh74f3MfsFh٣4њvh74f3MfsFh٣4њvj޷XKjVc;&PլG|9cK]rez}(FRU C0K. ]2-,XaA?Sio!@F`Y I'U?Ѩ65}@8(??ֆ(a?Ə+5> ̒i& p2=J ҰG8t +:fICoy QYn- _ҰG`z b3?D/.亞;9%L;z=3^[8JKa?ƴ0=3?a?Ʀk6yldc꧸X+4iX_ AYrkkh[ܬcs' Hf\'V(Ĉb6@o\`z b3?mMRMRh-1-שSeu'`Nd5/V1-у*=ދ+-r {%dK|dZ?ݶUmk]Rjm-hoZA(jm-hoZA(jm-hoZA(jm-hoZA(jm-hoZA(jm-hoZA(jm-hoZA(jm-hoZA(jm-hoZA(jm-hoZA(jm-hoZA(jm-hoZA(jm-hoZA(jm-hoZA(Mm iܩZvfA*Hj*|q%X( ۯ&42efMıBv!k65tH3@  [ZK;:|BJT8RN@}Oz<8-njVR$"ҹTs+E`C6"`ćw-h-TG?iQXۊ `l/K;IɩG1hc׵ Ka!AJ Oc4x=/h[O6,Qn8b{ZCu>r\iѤO&@f@kKv6ZNAc+N* "G#e򰚿x_OFZ@"y<e W:Σ[lIPv6s6>Ax$jt 3K9>!j̖W2${㡭+]-qjlנչ[̈\r.ӀF;bԴ^w +y{G#8##ۥ?! .m񈌥gn\M7Bp:0eaA" E2Yc6i4^YƜ` #-Q@ƚ$"yQd;> ( *!unghPe7("֏n+u B SҀ'^IDb0%QEQU?t;еwm.c=hQ@Q@Q@Q@Q@gwVgwP(ڏn#\ߌ5}nzQخ]dy!Y:0ƺMGA_O9Ksq4lۇp:":>#ډPM]VE8-Ҧ>1~A#KݠY~?bh링k۷{vs|mmZ,70w\d#xFtRBH}[,f&0@#Q! Wu;OA'n-U<.zI.Gx|c<'$#jv̫#2) Pzg([^y2ime?Emo)jxk_KIKmOW[+$^&؂h]û;2{χKIQbҬW%AKUGϵ[kn,`!=5F}'W}=[)#y޵==Oٺ'Sx5[h$T;+TltQ}+cXдv⿀6X!ÑT4acs"Id>r(L} [iH F`!*qJf(|'lshֱdx]:;䷊OyYNNzVm44-c9JzޟZމ̂Se@i^s]p6)u<²4 iM^Z)4)fqAZl 6{h0p]M*^L.sC]Rlﯬn#-=HxRХs:֣ţi7צh,üvp39>X2x-X(,/ Z z&[wvzg8;O5X#A61Y}@" a n#5顀|AE!Ns6xhVO #/A鶚"{:{o*꤬+xz<)]-ȖoHXT(z<u݊ۻExA܁'HD|/>2^ޭi֭ioWVr\\O5*Ȇ ˊ٠44;~,1̳'p㎔<1=n[B;a Q.%17 ,e{a"Oacl=bKHPႜn?G{SpdtL s#%ɹ3&̀z(=PvѦ ״?@Y.dǨ3",wýzP4k- ܛ8>1][.zzR[>x^Rlt뻈KU%h`?;c]XR+Z6+P#*CtQ֡='S{%bFg{iw[˧7o3s,N@ Pwc֎]Ӽ?oO5ۡ-ʅU#h۵%Ewyn8UZm={Vx.k_}[\M $9=;*\_M,q'u-SZ\u[>t=\1P7q 85̻]sȬF]ÖMx<& cךg掖 9}$۽v#Z((+B(QHemGA_&ú\7qǖdbcYu\_ +HA]EpIM۶zRemKC.n&Xm!Kq3I`;k8M2x/lv=| *牼9ȱk0#$bD݂0zi>?i{&3Z6x8'4tbCkۣ/n%{nŦ'}j5}*[Kd7hMλd` "𦠚 q%c{z]kN:w#sy۞%Hqz3۝ZUKO_Eiq$nr.![=UbxZKT^rX!ǚ5RZ"[\$ .Ht+ƫk^ /oI :9?*ηkZe}e v GfnWiGQX> N$ ;\!TARrs HaEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPY{]Y{]b(2 ۯ:NJt_#կէ wc@jmGA_7R\ Kif/&e] Pߌ{ B1Υq ⵭5Sֺޭ$V$ ,d߽r| h.lWx|v#_]izLfh"6NH?uΟHfMH̑J_|qkX>\HUFdcqZGuټI\\Z_ ֐֫=Ha#8_ [kE6-V#7d`P9?Z:^`o[xzik0=ԏf 4|ouC>d=`1ƼG^l~꒛KNu3WĶolJu3<9OOkU> *r',^RH]0Һ57E}i;6:TUc.WmNm QN rDKrJ崻mf@k3xinb#<-GxJ-.nhHeh;v^6Z;Fp Qְ RSqnqSmF,X=hw=X|`!h؃BzQvBIidEx4ȪV'ijqĎcAfϱk:|?$PO @fh$d~OGީkjRŧܤ"\D Rsޚ{[xé;BچYų1@!8 [nkzú߂;L7 YId2) WKkJmy/rH1FJ۬[6r#)Jֵq^65=JoX¾ .@hC%*?eq##ץZ\!7mڧ~ߴ2;sǥrW:.wwQH"cEę*69At:Uߊo ct@8[:m?ĚN. bCep~p=?j+ٴRI3(˽GRٮjHci>Ei,./:2G\DbBSiQGPu1H/T{0({:w GoԊDͼ`?EskM4yBڢO=H>+>>,QEVu\_ GHu[u!G(;n8*ƣ ۯ{Hԧ.-V@[B uψ4(ma3tϠ^ٯ$2xm![nP-fVdtukQt2uYYtH/#EIvT'vFs-f]f%nl  [ |J|? D{e/g1jts`$<\5܌JWg^,MInںEdg#;ִuK?GUAO-h qU~_/=]wk׵ >I'Q.>kF>>gVQtY`YmNzuHa(aQ*q>9m-QC6K'icw5dbM"nmgb@A?ÒZi\OCꍶZ{ޥҪTmqŸ^{Mwqo&myHٶ'8~^ÿ`v88)-Pތ4]n]{hC)t^)k1u2-عۦ}xkR/(x'GP@F/Tr0:ĚB6ObF3+Gmg&rFdɌqݏZ?໎-2,f]|jW6X-DŏDy =@M%tW%ޟ/dbA$VaJo8t=ټ">];Er - zx_B%H<93Qo ']YpHSjCkwaoou#[$vJZў`x'eE*!xIҢ5ӭbR(d  vҾ:swm%YבMjW'Vv>6m.ck! &84#𶩫[27oP9OYlrqK{+`{_R-D[kbFd2px~?V$V[QO{/ 1;pkt fpP4բǩRw [ҙ46__vma5ŶkItb@b cښ\Z^k[=šcJˏN\ckOEU{:$’}j<;k5nP YU7|m)-AyEPZ֮4ղ, zsWSF@Moh'(< t0jsip`Ɖ w~#2`7gy݇c5MVG[6"W's֮ד WmGTK`M:odLGS ֗ALѮtG#~RD+ OOo0E K6*[_kUi:3,Sݪ9WxNcM&k.N.W+0AWAYkWDlI=9h袊C Ϸ? Ϸ? QE!FW+(`GB3T5FM3Eӯ]E$O嬏7!"2!31}JQH..X;Pvr3Jt+Zm_^o56켣3qJvO[)}NpqSx@ E. x;/g*l4kRsO5."M;[v۹J0;s׉Z4b{.oj:[M B;}{WIV)=}k!ׂjn\};D#bK7NM>ױ80f;4grѣfEb )xUִFYo.(Ko$~%)r)! ր%xf (#HdQRW5 %^w5G 3Jp8LbF0iR9HO7%AE!݁E]{ Kֵ[xWQkO(19xS5k=NFP}3WMAu IZ56,-u lnbo2tV+鑂*Kkhlb%hEMm/KIQpC緥 xjkyqw?gAJe õ+_רnwԁUs@\ i-'ƕikuPI;qu&FeDr } (ôjXt$r(1F_yK{h܊s# ntV#4(Zm p9 CB3N#h"h^Fx?JMll [Pqy'ժ(`o1?((1oc]|u:cEtj鑜QŌyi1E>ou DHSڻ`g)(5sO)i667W./4$:QE7O_iFL8@ f͋CM4 y۴bE5bA J*Pĭc@ޡFjcc{uymnO!bKө{*QEV}}^q%hV}}^q%X) 64S@ӵ9#=pAj? /dŷ:i;jjE1p#t@^6xf3&CՉI>B] ~&pדz|7%7MvRJ2?=qS $ӵ]*k ok欅 4+^e:mBIH1Fy#MSI/R'Y# kdPzi^&/`{ Kc3K Y_iA>uEG0,;[hs67W6^/ᶒ)D~ԝ79:T5Mu)hyN5_~+muqpHySI5s"-ê8* .,-Lg%h<~?(5_ia=j,$d>bHto- 1$3!GC&-Ak -sx U'}#%$xÀA CIvҭtt wgL[tX\<5istm "gΡͩQ 2Y~5̄:1ȵoGgk41_ Y~;:( +֥~cmL9 "o=ڥ՜ח>\7q#92-@f?7zik !l ˸8pߌvuh?8Grdb=S`h?W6VzlFhiY7Zx5httҤ#'m=ڏ :j+񌖺#O8H Y>Y#=eݎwN:sGW7KU7usڙysEi1CK}R@Ӵ语^k-|ed6=:c4mj4i-)mDqx SVյ tJAd-m t=}ma<֌BL6s{~MtdkW6\$9 ( ( ( ( ( Ϸ? Ϸ? QE!F ɫEՅLu65xt_MHdY[bEHtQh[ȵ'CY5b/};[W ͸HB>\.OJoisxz]jOl8FÌ29Nj[ r <5{qBXlǶ}Fh/5{+VFl#) y;*L4E;VM.6cFdq;>IŪYU=0 vT<15d ΕT}eMO%>],Ao-R׺͎v[=bB 3nx}CCm5-n*I260/5QWG(pðr6cIL$Yn][/ʧw [Hu-:Zk{gG\Gq[uo&+aV[4>QE+n B+f/ 6(Tz 1n5-kOխ.{%t <j0lsϋmMI^}RK.<όgֺAhBb,@AI4k존dƋ6i0]B۔u猝8RxDԣJ ri6Zc(8 }jZYueYX][ʈtzVbcVխ.Pv37K z/dhvIA9C(q 6Z6 Bv^'OV\\%G˴9|qjA%{8mKm̑1nZ]_J5{! ].8A O,v_ވTgwh4/hڄ,i$Ob0r2(nڕ.*xjanWw=I#r՚ V tUQ$^:t:%ZJd6fqd5n09 ?w^unm'IN-Hn2wYzMxIF 6d/qdK(P0Iޤqv~wy#,x$ l~d[hXHFB ǭ/|Ie6r1YbT3`rQև>ln ^+^;E[m,r'<jW7juO5gLms:4W7iXӳ:ddm+HM3Iu ̠4q૛5sq%1Ǖ<yuHu%lcg\pO=uY`ٶƚ͵묖0zH4ٻY^&ћҖqMa]yLZkg=X ܤZ:豣B0E }A'QW[Ф,`&FM˕㑑Xx=fKZ6fC"cX(./uo[py#=Qƚ㓹 xjjmKQ`4-^{qO_ ЮtXdL,|Ys㚟MfR'Wڧ`2=Ƿt-VY_y.bu.\`oG7~6uewkh]Fc>c8CgCK$3Em7JOzKF񵶧ؼ1WSrĮټc/͌, G(N/M >xFnZTPα$靊qzRCFE-Q@Q@Q@Q@gwVgwP(ڏn#Yڦyq+c%Kɸa ?h? /-į@ϘE'~rRҴm~#\ɩj72E,TuX^zRVx 4}CE# MH8;*(ZG)85u[϶:_RI ǯGm[NF -b4l23:W_EyKdRi/岦97Mzƣ-[[h)7QC_YOL`Y `3U4c'vIk7\E8翳 ȀNr^Տ-^ו/V-x#L˸סRՆ5=EO[bmxoǑSsYI%VMc$o*H@n2T{E>[w_ iZ]kA MF9m:*=wMu'K>MY $k#ʜ{kE;Vk{syq K8®ӂp*WpR-RƳ:==k-;WA|>.[ܤc> Eo᛻]qu4o.dˆyz먧<7~  Ak5wa)0\u)}hRiä**0BqyvtRZ_pzVisi],G"mAHQE1Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@gwVgwP(ڏn#Z GҳF-QLAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPY{]Y{]b(2 ۯւ28*@Rr? r"r"r"Ed6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴEd6ܹȴy6ܹȴY{]g˟S[#B.VOҐQEQEQEQEQEQEQEQEQEQEQEQEQEɦH#.玀O`)R}v?TqUoQ}Inb69ĸ}2(|RjqqW\~(֠7Zn9fB2tX|S\jGNPLxn qa\?TqUBzz,H&9jNN:։UjK˩Blw1wRRϕ9Q?⪅ϊ;;eqFnݰzҀ.O?T HTEiq%Q%G1VfO w-Pe`~Mpm33*(5ڤ+sO"ŨD=^k5tk#F{b0x=TqT}O?͛z5رmgb;)4k}GC`xPI>WGڤ+si&=1IP/tF5(N@&/Ou%r 2S\ַX4htMA(]d'A[8jvtQE!Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@T]i]Fu xi,c9_"9ʤ2{VnK֭.^P Ad9'׿xhpsS=V[{F!IeX= &y5g{[s+Fۘˎx5j>]V0/cܷG|vYC c+`\,PϩFVo6@ye WIZQ2O/U yF1|$ $?')W9:M] hsdoo.+D\}>դpw3p:,[8ֺoM36mJ8mYt :IxY1,jwϠVt+Go/ ;[KL]?L獷o&oM xk[ SI՛U/$͕;ǵ[,ӼC51o!u&GL獷o&X|;]Om.+x`.9}ha ԤuYP`#[36X໚dk)Q౒r}5(>3[ZJ\މ?4OL+uJdzWYw4 Tt C?m[8MjTT W؆F lNJ޳u Mġ3xϯ=+w&GL獷o&X-o cq$l0!pH?Zkkjpj[.fQoW}+?m[cI4F5; gnOP ]̈́qq$,0<Ɠ&SA${䝕8t4QE!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@fotoxx-20.08/images/batch-upright.jpg000066400000000000000000000203351362435004500175670ustar00rootroot00000000000000JFIFExifMM*V^(1fixgnome-screenshot0230%0100Fotoxx:resize|retouch_combo|sharpen| 5http://ns.adobe.com/xap/1.0/ 117 239 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((m" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?6*c, ׀#R݊JQb(o ^Q]pGSJYw (o ^Qe7(.pGSJYw (ːr=d~8PZG&Q6БQpTMES$(((((((*=%._E%IU,_mƢoTKfqvyWvsw"Ć$h RsKYXIf('[*; BO9Mf)4 &rO9&mSX]ÐKuiv-:I K 'pwbn\Ը"P6 .q|$w5y.|J,Kb"4` ܚ";T twh̆3&c=wJe$I&*FǮih5.I+sE缁cgxuMB5M!{# ær+BX`eIֺ MłOBGRMaF}ƢӼ7ֵղX8-~Ҟ]?;7q;Cb,x붷]OI$e3pVEp4tg^_L9ߗ܌곲 )Jkv,0}NII>PXMuw*o Fu&okss-#$ ?Zj) ɽfʾc~WwGtZ3UNpN ]3YS)6 )X((((((({7OoDʊX8Ia??jl5'so?b?̚8_\=2%_sG2HXtP?ejE-<ؤ%Wdm Vn"#MBE>&{ROFDx,=+:Vu:m^F<֕9Zmkt2t<`c(Z^jyj?kĖrb/ѷS=~JQ,4Cmjʯ-Z@#YB9M9݌צ[cl8*k 996{;K3iqim5ОD+GtH(篪jӬ5_Rg-p%uV2vI.to}Z[ȭ"JS6qvM461V 6e9;-zlvJI--exdxgϝDe]oƗ ykG ŵPݹ6Y=s dMw~Qwln 3BLGtgm-#mƍnUT @((aEPEPEPEPEP>6?SM̿܏?OŒ(g(̿܏?OŒ(g(̿܏?OŒ(g(̿܏?QC%b[;PB݃+SKo hٗg(̿܏?M?Ə+SKo hٗg(̿܏?M?Ə+SKo hٗg(̿܏?M! {\4A7ң#G,Mѻq}f_G}2r?2IX (dӭ/ϥ4f_G}Z[OcGϥ4f_G}Z[OcGϥ4>`9$)0]'zbPEPEPEPEPEPAho~{ɢxu(YQ> ~U%v~EWK)JH `v\լ%<nѬ:tq)R5JQjM;DYXȌr')ѻޗh.aԣuO6ek93yy8CVn,![Hֶ*B`0ۚͪShXK{tέ2R?)#9tmWaqݘZSu+,LlT*+i f>1cn&<9EKpxjm4Ă7DX %rk|mGӴkEdYEp-ۺvVShog`[YfݬC1ncsidv[Hof&pp[ڧqsu%ׅ"o-Rсnj}ѭ3g(BT(8Twbc{+)QEQEQEQEQEdFB"?6Y)h*?Z(+OG4}SIƵh j ?O'բ2L(UgFk1,|( EP.V#yo9QPSVGuEe}SIƏj ?֭O'>ѩ$TuEe ͱore8c֒fotoxx-20.08/images/batch.png000066400000000000000000000106051362435004500161120ustar00rootroot00000000000000PNG  IHDR@@iqoIDATx^yOuȒ\QTԠˆ& b.pz1>.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-20.08/images/blackball.png000066400000000000000000000203061362435004500167370ustar00rootroot00000000000000PNG  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-20.08/images/blank-image.jpg000066400000000000000000000342441362435004500172010ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0230Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 149 268 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?*PfobIg^xKy#fJK]X2$9Ҿ1tBH!y>}Onc8+~9Bc:mVZDZ]m<#H$.b}^/THӘDhfHOL:,(_yL^_[CrŲğnz^tUTsp//'{~=̏ȝ- `CGA"q.  v$?o,Ʊ R#i6x'͎p^$m]Z#mKF蹖(+3]<#حe[ga($# +ѭ6H<^5/#:v$2@צpHR7^sci_| oچ;Լ s[¾tx/F,c換 *-{<_2Wk:s},9wE$ ^l))sZm{Sꟴci_| |@t5 [[m.wj#y. aR@9',ci߃5/ˠ y-@ !I #*d~m{C/K>|E߀Z׌5 FrjfW\Z[$}ɴs^'Mnt,m.k(w"GmV G0CϴcT}ORO{E jYG|OPU_%Ǎ^x{]6ZAH!Cʪs 1|5u o&Vxvy.,ׂ#؞ܿ_m~63cCwX[Ksr[ĆI%j($w9OA4JO zM6^ YK:sެI[ƾ(53x&WMxB ?{cewoLjv>խ5+H%wG4;]z85?WX>$k>0m$ x{Jm&{FJP.R.6֦o}M՞}M{;؉dH#' }y\#̗~?i_| >ҿݏFB|a,n τO63`~a$Z?iivxbMEݶi27 wsp*FyF3hrQ+GWAGK|.4;;ujvO5ܓEYj2xk;+ºD7w76L[&;"um1'6}u+\Αɨ6WsA%’ v#\_ck+GW~G _i_| `c _+/~ z9DEi}ފ\TS"H pA1^[|Ե.lIEnxh"\TQQۄq9Kt9̓Hrj"p!j~o<#Ea"AR4H櫈y]ldMҺ/Gmȫo:s@*=ġe#>Z }xG|=|*wċnSuߥͽ ;r*3g)zlkM1ߝM%QXA$Z/4mhout]wW5w[^4KEKGjǁ>/ឯ}xkFm2q2 ITN6ֻo4mhoٰIukz$ڥЍfWq *i(Lqۃ9hR~X!{}4@A)VbWRP v?Z/[dh 'G#-3rme-}-5Kͫm0ı#+M x.R ,F5ao#Zn4mhou_և!|-𧇵3ZӴu=:4']BA `3Zv=zMSQ_H5+խXȨC~ur5mhokE,M48 (Eo£U݋g1p*;8 yRMHRͬc#˖}iZ/[dh 'Ga·}?_ջKx;&7!M@4{6`?_Ro^v{{g=ԫ I> ßlj|uZ^E `3IrR[ԶF> c\J788_ C N· }kiom٢Rg>v )|n8cko|Y7m?by|\<*&O5#r><Ќ*{7Sh/ twlf2E[Sf r\ yO[=|9z\;KuyQq  $cwJTE֗Hgi". #P־jObJb__/<Faiҵ"sYZYMsf"[ZY: XsTm{&M?o߆o|E4Y4;+gs~/ʊb2#'?x@G"? DƏVY%%Re2Gdw,־%k>'u:iZ:}ƥ ҥH$.byĦ0 ϩ~Oiw:-֥%ᲽE ege0oE~:goZw-cVP1Y!,#`>'k@ԋ ( ( ( ( ( (<W<1`=OJ,i o>x`;$2:oxczX,~hZڛthVE?7 4^fel_\.x-lSyߍ.o XuB-͎wRXL?('OS_3爴6Ǥk-S{Kw"I$Ei9{S)~"fXxGJ徙ey[d#,D%i);6OO| l5hI靧[Z-t= dFH^ ?Sxgytnl򣺊Mlh̃ 95|MsΉ4u 2 /SN;kеlJtF2pp29$fL_μ|)=W*<* QAz717mQE (^|; _4b<fj6\y\nUeUEyƳM5"B5SE]-&e?gF09~l̘ t1՟tkKɢTy!iXP̠g~bM=W$($&7ohzE޴s ,QF30`Kʛ@SdnH!9Kuq' bA=W]'"ލ›fdH>2|#i=R难NbM^"G0Hm[ȥh6޹եִnCQ==yꮬ_ &>;,>j_,^ۦn efyko D|(ᾩ^:?,sѸg#G 1 ]Vu}zg$2׆Y7%T7V .9zV>ѼM:E֏E{i3i4R(eʲ# Er??x+_!*w>:S?%Q {3U^3lGx /R|5u\C:Efhq!X0\V:_O)=*<?Ǟ C躯#?eYLt51nW][< sF\_ &?%5ɿ*K/ ~  wo7MƅnLڌ@I7yX1#(rx4η]BҼOizVo(bpzp}/w`յ/Ykz֯N,n,[$.YLǐMQav:G4MAs<ܠ d ? >*xDu_icZ4m싄2 %$:5Oz 3Mӄd,[qv3gXWOc_^-e(w3\Q!7>ݜ`Uɼ/kf]&e} WXj~.ּ?o. wxB tPY`ݻ>`@Wnȇ?06⁤\C_Wb7z#g9.6dddcCǰte<;cnv_ cſ2h!+çҹ|C#xVk{4G$&!g\(H=8h`M3ehԗvJm h2HI'88 _k]Udkx8>Ds(`AdށQ@|/wMy?D-uRZLsgr>dVp?MJڧ]AY|7]G6q3[ʷ'LQtf 7 ơBO-#Ц+0HkQ('n ~ע;4_.7[G h׏<+_4=>&i} 寬џt+@co+=XK|,eHĪ1ȡG5 m-,ѲE(u1y9?IPŔej ~? ux,t7]O5k2DN $7bDVhPo U]U?U~oO S/Zx¿|9þ6wkEͽ+UL?hEܻJFnO^25xM/Ŷz<zk˪sm FiX+&#Q @8Z$5{m[kYZIrwm?JA$_ ]&#W1E߀7vmFU{ZNrr}B1劏czgí_ZiuI#_" 5ݞTb4y M}1Efi޷ S. kZӬtkHĶW} ShXI A|ax?Ug~*ZM/Rݭ QKG¸%K]cL>>64V?`KonL$WHV,WY/i>!мC}#Z/5M.k[8]6,\y&GS?ɸ&#Q @8Qq]m+"w Vf3oAcLn>΂UQ3=|½{_W'`-[J4U?!Ǻ>qa{h@3ں`~brrmF*://XokyfL廂 #Y4[;ȀV$^ |L# k@Om%%Ţɥis0hĞ[m#?'둞>xk7zo.'{)%aq,{VوV(|`WS֢*y[I~(7ŷ_|ĺz՝$ mH%YdB0aOcRo\::4o_5E"%קK)PX<,$#+#iW⾛G&D,Ks ̗'Bbq)a+ W+& 펃oo'urJAll&<L7 Kڍi4 n. RIlѝ%͹U_OCm-5vWO[EX)NpdXoƣ1iɻi*px IM~r{X[Y~3\u=|+Csq֯g5_7A]e N9ouPxVqyhRYpl625qhԞ\_I|?evpOE>((((((((((K+gbʒ2B5Q~*͵"{WXFyOEPEPEPEPEPEPEPEPEPX=[GbMlW+_ϨʖSE\JvtBWw@pҀ%BƉ;LQ$hH+va&_x2:;ۉ>W`d,k6ִ&>>ޅBYZhVַ&ݑ}s#"" y y~Z;O]?#9ſ$K,IIMs}k|&}R9gtK Ϸ#v1< xR~ɩsk! c[+XK {#\G ğ t#H,w0:r* <zI(ГZ˫C7j scj3l"R w6Hw,4jYO|?HMwg ƒDb K[[U}+XӬo-4iٱIsIipE\$K&EO?<cvSIxim\y%esf^If* ,G~@KګOҎi0Y\YN>9y6ce 9/jmCSyu%ҡ[-yRgd՘A|q;o24.[ev7Mstb<>yS*|O_j˪XOqF]ZMpYev'3n߂-?/++A]@].]GBsk:[97d>qE'ۮ_4Q@ۮ_4Q@;Y.B*Q@ 4}~%P4}~%P53;bYRNMPhfotoxx-20.08/images/blueball.png000066400000000000000000000133631362435004500166170ustar00rootroot00000000000000PNG  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-20.08/images/blur-background.jpg000066400000000000000000000411721362435004500201110ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 191 300 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DP.܆"=ʼ,YU^I<-:<cs/ڇ7w aƥ iI=(ګX6pz$|?Z߁< q^K<@.4xJr& '$!1j$c?[|EF_[h>KFEHc08P!Xu)'y'Z:m /xHFv><~;7p9'hXfb:eF8\_? {׷o'?DV?ڽW`2B,S-Ŏuyj|P=Wqc٬Zie(ck؛BW,DBca4ytZmvrD2~-MJ8HOb sV(E+XFnHp,<~&mny.,Id_2/̑sc=1E\廞et|pM:+{˹spZ~#A7UmesvBhcD-#]­F=֯o=4RV%E8 5QQ/V{y>Z\OIcu{-$ $OAIBA \B]DJp~S:Wz7杬YV[۴Ki2(*s+/\IQ 0y`@?ϗc-2mvu-Tҟ4'{_G֞fh8_?_Z=?gZ={Ki>}G;YVG;YVG+ i>}G;YVG;YVG+ i>}G;YVG;YVG+ i>}G;YVG;YVG+ ;g~i3~F7$k [d74NTto8:\l@6v?? z??? zo'm5>8<'?/E%=YKk=4'܍1yG`_ORD V(@vĊ:g??? z??? zn;h4#'+-K;i1y6f%3,O>U1^ H 9.,mnrI+ ?gZ=?gZ=-A;l|Ia:_n|%mʾKy4mRgDi#g[$ԇ5CW^W|kq$В!Ǘ"N@ɯ?? z??? zi;OUc_Rxvx[]J-WOMp ˼Oni|]Z_[|e<?? z??? z;)_֧h񵿋T~$!o%GntPǰwn=?TԵ-C\77ږ4ɾl}bP'_?gZ=?gZ=O0xO׬_OoYos0e"(k' 4/5?;h2}DP".c=_søşkøşk[Ic'֏Zq8?*q8?*as,) U#q{6P>L|}qAU#qAU#f??bWE\ؑ*O>W[oۂ4;YVG;YVE\DeN<ւ >a ό/,4[K[w8}q;YVG;YVXWtu15_.Y.:[FH,mMv=Č }O$\W#UxQqAU#q_Y_Gp8(8Puf9/5bH \yGzo`Z8-o&/-X4<'_%dgj=+($KPf4T,{54>3H5i2xOڤvǵ1aq~F ;0 _şJխ Լ'kvM\:B-o$2% r[[[]Ʌկt4{?x/on4KxGAI{{.#MkGao+y$0|+GUT&u=SRbcWoc wzI_`ݝ{hcfWC{i!VU[(.MLYd`%6U`M=<_wih\9Ԁx5Z[{R[CimYK.q$w q@C:_|D>@Ku珠徭m 0i][Ax`HfPŀsp};sZ5/6~a}H$;Zp4%<ښIО掣:?q@CI jƜ][GisͤC1C;JU$yQֵ+_gTn((((((MfY5^×"lmi [͑L1z>'(iT$4G8 /“|/ׇ5'T# KOe :oVS  ޽g۸gխ 5П -^# 0FT".ӓG: xY׫C*;[~ќs#=Ԅc8B:[q3)݊c˾ Qbg#[7RYTeW9ry{kæxjRME.R"3giJ*( /HgpYUp1to1ۋ{CȪV 9#*16z gF &{HχQ7S@$lW + 05tTm ~ `SX|=xuVW&+7toI8QDnY\Z ˞~fMx\o'čhÞ[63F@Ob0?'vz >E_oWsCPc+TҖS4^i*5\.i-DZm 9YPn5k i ixaԡN" ;7I V|A'/Meѵ62*DrOD$Dx''j:O^t~ JvR[.!Thc&a$ ܃/[/?!7e+>⧂Ѡ sJL8_p veOsV~+(`4i5V%>.| %|yaag$txXR1so$;g+E??C˱񕎮[H;IPn'xoK_ϤtcX a="\]ZeR\ۯA(r@Kok0O7Cx4UW+85~oڽ٘e '>C\-޽%-OVΗrp"K$6$w*?n|A} TFO5M?Vʳ#Yu&436ͅA8 Ҧ{oK͍̓Ik_Ejka54Gcw%8;7&s xWڧMw>/m|J h#N TE$r2RK/]/xkg"- ##ݓl]{y@E#Sa ~)dak8Pȗcp\qjMGs iGH0D\،|no٫.7(XA"(6p| eBIÓW~;#ԼoKNҮ/<3=-Au.ٴ̶H?yN3gU=^o~!m~Z`S56Ϭ[۶d], K& ~ +Z\FivEM,]۶H q^uO4t޵Zsjʹ)^YBUc@d >n#uwp|=B'Pd..ѩަ)}z 6ݶ};yiL &K٦T#%˓$6_4wktVVqpp!t#qx^ ?fhɨjB7́ky';F$N r_MgQ֍uisrH;KyDr0c\@ekw~={O|)k%5J%l^'g#g8 v9ς.]3K: w6)@G"@۝YyAr+ |3񥗀u]"ڝZG.}8_A*K %ݻ/eD$Ė&|wxY.c6mkVZeyobn&W@T]A$KݛğkhV)n5 \6հ8e`y|}gU?ĚERMi]P1)+|5VҼ)F|74MZKF+ďtIck_>%~>lVZ6>Ģ [P xW;o{~'φwg5߆AJfx_/>8g57ĺxW'l2ai QJffc}ҩw (aEPEPEPEPY4?Ƴj_mKVq>8H+LʶverJѦP9 粟J?E ׭'`˕6]bm+ߧ7'Uvv@OGSiPYU/m״}D@N|$aW_VdG]w|"k#Pȑ^5ڥ" OVZTm?:ȩQE((((((+WM_ -> kne(Z!<ֻ*(pc绿~/G}+95rVf=\! λ 6> "GWeݮDhV wZ\˜hi{]Di=w]i }KVLEFȸo7z}[ed#$Dž}iZvx b.-8ĐRц*V}񶙯]U4լAc`sB.#[yexd v?uR^_uo|Y{ ׉ou Ny-E]=w2eٸ I-%xޱ`hv{QO[^c$q,%HUg3E?/k~By mmaK-UԭbTm\?Fv 2ruoJG_zڕF2ΓB8q:)wv X_7cZe_Ȏ'9Z|ǩ㛟QUe0H hK2]g̛yٵּ_ ψH>i*[W?Ը{&&ʾT4?O -A`ӣO >k(W?ϧ-lRH~#ȩ7|Lu\3 X/4[c'HP\V@q hj_-Λucqiei 9H+"c4h@QEQEQEQEQEQEx|~4=5|=xYybHH /*4eCBA#=r^%}p^A-ķD$dE#pNNAϵC8 ?NOfmu=yappI<; ֫K}߉t? O\%eoip!LT7R,2<åk elڼֶiowpRYG@5 >2|yoy5$Yc/ h.}ޜחxo_W_ЧMj]n5M&[I!7X<>7׮E ?}Nߎ (ԵMúVc?Eēm#s*$1*L+g |:ԯϣZXZqo1jK1Q ;sē|uY趞/ Mdm\݅I٦S+!s<Hmo \i2J7%&&~A b'#G ,+NV[[RhDiIgH5uǫWۭj6^-kë-Ze ؑ*9F+ kh(ߩkRib=J(I/q_|Min MVs|L1-̇htY$;PU žc]еhZKԮ+KVFO<2ːO ;;Rşa/l<9x4V˩m y<e]ʑ)knﳷcM,Wue>>>,]x'Z헉ރe}k`mGS<#5ux~5wPcm-~2~J¼FV?!Q.ƿHmjfQREPEPEPEPEPEP{BOUkm @Oj$Gl!?ƴ It BҢ3%?+e 4KVkJH' .A[/O*(7]#_G$Gl!?ƴ It BҢ3%?+e 4KVkJaVRiI+`س ޤ-?tUCQ֭tDϒW+ky'X1@?EcU?%Q uk&TOiƩ^y+k\^L'{;1B_KX5??Myƨ$Gl!?ƏIt B?N֭uGtώT+y T1Q_x]O:],)m;]#_G$Gl!?ƫ]g> ?YϦk5@%?+e 4KVj%R=uqtk?UXH' .A[/Oq_[fi2b`8]s_X8 yvg]#_G$Gl!?ƫ]g> ?YϦk5@%?+e 4KVj%Q uk&Tg]#_W;XdIbat`Tb+*?<l]i0F 8d٠(((((((B286gO[^M^> CŒ&9[ώV_,5u:͵[aDgMO|= <6mΒEȩ%pE|Ũ|>xkX+koom|5.(Kf1E^Z_(}{{ϋZ>M[P oBؼp4ކX*0:*:u6^bOO!yɦeު 6bv[8:= 4I}g[鶺ͽ{xđ@BU3q >tZJu=`R Apey5hvc7P5=6gn&.XLTKiG@;MJߕ2m7\NtIi A Yzi!pvl-38֌Ŗ~1°YeYJ6cW9Ӽ5j /A-ͼB%X,؅Fq^ uK555= kyivPG$ ;NG~'x3[ФuD8-ESl]ͷ>-tyj~/5wdoVK;8W/#O$1.I.}!Օ_^ZƳOom{Bd3bT'@⨥D֬5}=N BppG^otAKᾣ4qdف]"~`G,Mw3XS.,5 C7!KfeYO.FPU9 QGin|Ek'5!֍YhiYXB.e7k/> :~ng:>/,i@C d.1c:ũZMg-α\G.<$_X0" (Š((((((_Pj){ϩ !RƌX~vPA{ߵ[ GLu N$y9kg)@xPv3i]6-$7vڲbI=&D.5~)k!q*yתQ@u)4DpcYcv} ?#0mfW5B{&mRDD6eHw^уj(=_U_=GEBZ*(~/o*P.FHV{\*(Ouໝ&vMwm4r]p[si"fLwVI̠zk kǨ_U_=^EyEBZ>/o*WQ@za`l͔3O=QOS8*(fotoxx-20.08/images/blur-background2.jpg000066400000000000000000001270541362435004500201770ustar00rootroot00000000000000JFIFrExifII*   (12i%ssPanasonicDMC-FZ1000Ver.2.12015:06:14 11:13:16PrintIM0250d  ' ''''^''''$Zb"'}00230j~  g|ro4884884880100`@sv  ' 2015:06:14 11:13:162015:06:14 11:13:16 +Fotoxx:resize|cartoon|retouch_combo| Fotoxx:resize| Fotoxx:blur_background|resize| Fotoxx:trim_rotate|Panasonic1< !` "$%j&0408'()*+,-./0123j456789:;<=>?@ACDEFGHIKLMjN*jOUX^a kce@kf@kg@lhi^ljkHfllm lnolpq8mrtuvwxyz|HmHnHHnHnHn oxhoo ooo`o@sP Ts \s0148<q  psDVEPDB AFrA < K  1z۲1%zz7d۶\\#DD`#bd fvhjl;nprvEtvxz|+~VaUaU"$&x(Z8 0' 24T6> ۶ ۸ ۺ ,XXzAEhjlnr$$  Z$$2       *8V$>. "( 0:&@>8H@{6 "$&*>.Z(,02<4FVZXDBDU|~JUFHNP^X@Bb xzK \dbfLTK U:p -2- WB6q  f `d>e@B DF4  hj0@l$ &  " 4(*,.702HJprxz |~ tv  XZ8:^> Lh~U\         q 6        <FD` b @ B D F H J L N P R T V X Z \ ^ dST " Z $ ( * p. 0 2 4 6 8 : < > @  N P R T ` d h b f j p z | v V & X l n `bfhdjxlnz|~vr ES:      YCB   0 (           " $ & ( ^ * d, w. xg0 E#2 X Z \ ^ ` b d @@f @@h j l n p " $ & ( * , . 0 2 4 6               xnATB< " $ & ( * , . 0 2 4 6 8 : > IA      A   :     DSFV4 P  IS`bdrtvx{z|~~~~~~     (.{RCN@B2D+FHIY "$ & (*,.#0QCM  $0p U#DSCP/yJYpSwWtuzUWߖ]H=__}utW1V!UVw5UqS]UDISV8888888888FFFFFFFFFFQPOOMJKIEE;==;;;9;;98XVUVSQSPNKvsrsqprnkh,++)+)**+)453442200/pdvevfuhtgtgugufvgtfu@f@f?f?f>g=hOP:SpMLaq4\\d0A:49N5.19-;&B36!.25" #,3%(31+.1'0 1248-7 %  %) OISG~~{~~:2.5~~~~~85/&~~~~~  ~~~~~'75~~~~~.+#~~~~~~~~~~~~+4~~~~~-#~~~~~~~~~~~#3:=~~~~~<4%~ ~~~~~~~~ ~~~~~~~~!#~~~~~% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|~~~~ ~~~~~ ~~ ~~~~~~~~~ ~~~~~~~ ~~~~  ~~~~~~~~~~~~~~~~~~~ ~ ~~~~~~ ~ ~~~~ ~ ~~~~~ ~~~~-:@~ AEBM#'>(435&%$.1> $1rtz7dgjjkIfnqskWglu~s?LhluNJ<]Jqvsu|PRSTOHa1]Suu>՛V՜YUO=VWuW]uUB-u@E:U6u?daUTM}qwtVjM6GURIU\xpQ_UФ7a$qTEg(Qy9O7it:r_fTV.Hye}UG_P]_SU~yVwElE9QQURGTU]DQTEUSEUU\&QLUEUVUUUQ\UqWI1UEUZ_'U|]yqlu4]%Qt5qu[SHw)OEw1BK՝wy{UPv=A]_E^SUqWmu.n)~Sy1cwZ`AwuW\YaWGayu~urEʹ|7G'/Qqq7՚TPtݝ'_W[\ΘV~\QuNv6V$M5E55}UTU$QY}eYW/Ϸfq("Bk+")F>qI$*;2=.!&7F**b),!tyYAf . 4?! ix$,=8=pC.A`)gjc)"(A) [71#-44PL67KC&%#sP W&{/$-:6, '>.A9.X8.4eLEf'$+8F:\:*(.6@.3]8?*!hJ< EE#=.Cz?> -I9E6]TbB<B=/DmUDMA''4/\W'b)$5O`.(M%>+.%;!"!m(!HD<n909I/UJ//3PVKzD2 $-}&v/'.h:L-#vXFI 7>$&A1* f=~}eKGDc,(-`I|2Z>`L]k>6 +()/0',"'-5:0jP#K}$(B3$Cj8<2e0+/t0I5GSNf:/ $&bkR%&+k*:-R`aB>= .6(4c1O14ٲhT%O1b`2"#5##^9X0`jD+92):7()##"!% ' $Z$"-D 6   1 WEq2#Mb($#>.N)x+1DAC.) "RZ?!"(V -'d1PS07c. +n.&!F/C؃AG@.ZD-#$%$&\:W4Xb**/#!,"!&'$%)!)(*%(H )1j~#   )I`y0<P'#"/:''%;54)( #T_@#$$E#&S+O.2H ' )i/- f$i3@rRfmsUUUW\]E?]G U7NP7NUW5r;q+~WU ѯuMW;WftU% EUyWPcEUW0wGM75W5tR]T]Ux-%^T5u] rXVyB][Wwu_}]4?G_G33EP UzUN-euUUUEUMKUTUUT]TaTUPTPVTMs5UUPUU]USUMUEyV[UUUQeAPU$K QUT5_OZT58y[S{u%VYe.BWQmuqxDquqU=yoW]eO;^y=-5RQMxpU}uQ[C73kNUOUToQQ!u7|]=EOuuCǖU%DEuɑZs]ݖp/pXVԇs]e=ui8ZctsuSU5y'w]M]Spa5kcDOmW]՟ _%{0EVEETfO]OIWܗ5]|u%uS5o]уU'Q55SW^M[F>_UU}QMq%tNA]7YTM]UůV/}lI7[ADŽձi&-19v]uRykUy|{444GO؏UUM_V[W^dQQ05UgcY ZGPG_[eywyyAD}uYEU_TUECTEIDUUQUuU\YTUIGEUQEUVVUTUETuQTUEQEpU-w1WmkqO1E=iO}g=/5'}u 5]}Օ_?3PAMչYe_^OnV8_qU^Ds{5ՖdwWC[ wuU^SK5CRZeՓ4VA1M&Y-R"| LQUT$TEQ ~P͑\uo=5V]|MI_IGUU]xgg_TUU-WYѕU]sUWegs!_nYPL=HP]y]u$]XU{glTYQ ey'_GU}WOG_UV}LQUu$gwUpq}P]Uu_WIU4UsEY'Pߢ_u0Q=XgFϟvQtIQUPc1H=%qD_6_uuABP45U)ĥ5 UإU'+T^ST_ۧSxwU@H`IU[ UQAucU\ NWLIPUBUTVATUUEET-q\I_gU/PUWx\}~DV1WtATlUuQUuUTP?jA=uSq3}DT_TSmE[TwUSG-dgC\SveQ0uU`-QUS1XG 9UDD]UqumV VGeYA%5UEsSIdQPFovWOu9{SuRe\AE]߰ETta=SwgT_ __aѓ^__L{QDvV5`rP߁|]VݕUZ]U 5PU5ztU-?_mREߚu5%Q;qP%YQ~QFXTgR}}DYhp]EWtpME]}t^E]xՅ s]=IQBW]PUUUuU]՗eTUUeUG9M}aU5PtQUGUTU5UqAUs]Yoo]wP'[Atu\40UWPEgwE#Vsr^Tz|TqUQ_YUv!Gv-'_yPzMWF% MH]1O_Mw>PS0%A]%LUC_ Ou=uaA5tu|Su.^FOQ]KEUw5~ڝebQa}gAE_HJuU_@1Zv`YsK{e"aܛ,V:-V>v44} Uu6]u[Uvw@UqE]Iu_Tg5sq]&tx5UNtWGW_OQ_5KYUQqҠ@\Wԇ u w]WUQ2USouW3V_UUue5M5]\}wU4}USPW$*vuVWG }U\yD&g]L=w=Uu|CPqqTX,VZwU\PU]D?@uSWq|5QT%D'Eo@UGq4UWr5Q$WQ%QA5MW}AMWSuU?quWcE[_]MmStz'=e[?EEuV ~pS7WlWueUqU41epS1[e]~E EtW$ߧuuq\ӽA%TG66\W}wGt]AzUAwpT}Umn/G7Vw|0Qsj˸A,@WCI[7SWCU|dws}yYg_K_uGU@qT|AwQy`]QYZ GVA EEZ,Yu|uu}yF]m]]DqQQCSuWVw|yv=te۽UcP]T8Y1uVQ6eUWUDVQ E1T]UUUaT]ЕTQ&TPMWQ_ߝE4tTUuY!!T4UQ)Y}7ůUE]\qqqqxUGO&KL7]P[]z=eWQWEcP{u=EtMUIU-Wڗ}[w5T}G}TUacSuuYe1JO'U59OTEV f2uQjpQuQp9J W^x4-r9V3uX:Q^=E=w?^HEw_cuzEy}Z.e257u-]DC ERwq@qy^ ]=vGq5Q_Ywo_Y}OG_=wSt}UZwwDt_m QWY]7QYF\&]ɝUd]UOU|z_U_UJB5_WTt][ ]7EuDud!WBCZ |y0&M1q3fGB2#  XQ3gXyCG(i1\&n87((X?jv#J'q'#[W  1T  C15 vd   3): E< N }  Q 8 d n \ r [- = O # ,1 tm?37# + :K[<$hV!j8 + * OYQ#Pr a k( 6 6=>A<-BJu O A*$J#F&AyUB !{G D&/ dA_i5 o$'3#U!P%I1P9j"W'AZcmSB<SP|KiI/R.,/$..lR_$=&$ ")UN)9& &. Zo  7S3%a(_)vt                 GAMC w-'"  w-'"  WtuEF9615031000869999:99:99 00:00:009999:99:99 00:00:009999:99:99 00:00:009999:99:99 00:00:00GermanyFreisingddq q 2015:06:14 10:13:16d9999:99:99 00:00:00NsEs0H ,LtTt(\tHH  """"&&&&"""""""""""""""""""""""""""""""""""""""""""""""""""""""x! }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?@i'VtDg9¥+ [/Qw/R?5oj?ĿG>/Qrҟ_xF/Qr'N@C(N9tmaM+R?H?҇V_xl\?տG@][M/CB`S0h}'S?H qEV?z I?5):oހm]~ /Rdj yr\g -&Z ԿܿMsC(s >0;RNpb~Dw*),GZ,RE4 Za}MFH;tk{M a\2#eW{^uGy [g &?LUr}8UGwx˻5;R!#; ׳_AlCQ%¶[!7mGUIݞkYO-Bd[agi-w*-r*Ybʀ;rkLo:~Ua!=O57擒ԛ$)ɦ֥=H*e^0%dt#IZi3`QUԼ{Pwg%Eg%h?ިؒFCM ]D 21jzq&ѐ:ӚEH×sՋi"P(arsJA9KH gh1YGA*I闚f֚3+#yR;E=e/#E v\?JFُD?>R@w6rTtAT' I'*C1#|Z-^4)(RR7r:((3uO# dһ2PКpZ zHD{diR+Zl]M.k+c^v652WEZALĠ3"8kϫI+7pzzǀ<uSb3ڽR4nw=z!ai] Z2p;m$,Yn(=A5dNh)z~z-\tKVeHL ç^G|u5ᾙsN%d`&1,kOF5a56_t-@4(t X$h x9^3I G"i%^*0bhy9GWV9RbfQف(⹓O ?ZNi+6;z pqM$F.*!# c=k4YHj0!SrŽɐrGխďaiw5Ȏp3~jxFI۱4/ h^j/E!- ]zzNא/+mx^5M49 }^$9$f0'V eyʞk Za ]gT>]gZoc.bofcyiŏm}"R7mǁϵtc,*I-mo8Y3屩[7&OSROϩ{N /w)MاXWZј2gtӽ5*F G=zJ ׸֠86C9=1xvS44A%=_b/J\ܷ"H.\!`0 ss[rq*%,B.S8uǥfK9H!Cٝ^QE%$ٝMy!yFb qTM5K+8K`=3tخ,*B~OVFlIp8.x>[+-uqqŌ6LʨN@/ϷjE%sĶ^6Q5 dBS(V5$\4v/89w@441.~SCSދj6l+) #z2JXOJCEI7w"/4۸,/ ?R?.QF };)M*U砯3;oE&&G\ؚHl_x>(5;˙ 柕G+7בa&f=YqR!CBsM4sމ~яZ~c%d2|rkQNO?nSN-zj1[Z]ϯi >u߆Z|O E#UU/SMkw"7dN(8%1A=H+ӛxMr\Y+7~%y-[>Vc:m|ԌZW+t{D'uhPJ5_X/ \$**BA+NR80p:u%mL-8ݓɩRB9fHwnɤ3KoqX2jp4ZhFdAYT"W$}M>;ɵeɠ.IUm4^ T-50ҧK7bzM_a *J}ϭ۳*EKXlr1b\\Ȫ(smbl2*99#1RFʹǥ?#ؚ"S!$!"lj7ylMh)N Xِ*AֹFIp =tFUJJRz!IW9JC\T%$zhdެI'|R 1B9E-ĥcS7pdw  ז#p&r:WVWzrTݻhlF"t|J5LQ Zt{U̬>ˍ$&:LmI,ӱxulg-;9$ Č<5$4n|FdJG9%z6fT ca뚈] 1 376E)GkOOֻ2yrV-N:6NPr[6OA֒,2GjA?$:/xdnOZ@pQKƧMsZmWz,[N9-`?"u?y?w?SLPhotoshop 3.08BIM/gardens, art, fotoxx, ZFreising http://ns.adobe.com/xap/1.0/ Germany 0 C   %# , #&')*)-0-(0%()(C   ((((((((((((((((((((((((((((((((((((((((((((((((((( " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?TԹs ZzTe 5V)Zd9MMf;c\qNEiiԙ;Uyhj UGq"qQ9e s\SH1 A<-DA+S]x5)QMZC"LV<$5 \ I0BQ3R5B@%A˃҆bMb9^ +PQJ#5jRo%cXŒձb03XZd5"6VgQqCU` GJcl.QIR޵"jݪ@Y8 XZxڙOL)wTޥB}j QDGD1֚Px!dWwUN s7-סBgSwA<+!c֦^kۣG5+9▌Rn`ms[#\Ҝ^Wl7oVNf =h8ls^eZ2Y~BO5!R4bZn@+ ]{UgҝYEKX&+c5+&O*z jValBRG^~V+qa ~a1]8qֽVri׾ͻDs-۷8%T`ddEcHA 19=~x+A9wgZ\~d8 \Y^3f'tߥOx$EIh W\{&bqmwi-f&<8#Â1_CW:#| j&ڼc>r1^ gxZZ;o#H5MVUѡ Y\i "G( Gt(,$tq)`9DPBҹP:{滓M>Cf{D$`;#Hnq%\aqǽz6g6̗WB$"Jz/ĹVhEsX+T9p RSWǪxWmI4!i+#(5UwÌ5Rׁk2SrDG TJ' |Ԫ!4Uj7";cdE1SnMfeݠe=럾$WX` wa)+*dV O88WFJ.J -hsХ74q\e ջ{;vi5h ΂z.9s+ nN+Ϟr#pΧ ^zOOftJk{t9Q6-]x;R<6̈́۴m!@0Ld`W30Nc.*DF ;F=Hz7+T**J[G"} R|9'$Hյ:3ȗO Ȯ 0r0q-2M"ᰘ *?RHMa[]7p[äHN'3yndle)HF4bNSO1ӵFVqwUzEcn{_-aMosCC = t'Zm> ᾽Bbvbr[;-W.wQ3`Cy ,d-i&t(X}_I9KaR㜎zӽgnZIt"Z$dYǩ @z@'𗎗B,MAKI!hz08o5QGR*Fi bn\<=eNK4y^0xhuHtM$?$|BpUOԉrn~"˥oĨgXps:qZ> چG kLcyӏ6)dSjWjsK m7*ăP_3RSl7ijD{Wy5剫z!PɩW,@J(ؒ@:seMZϚWe, ɃgywV潚48_An7Q9lHjx5k BiA4(\҆JdShsYRDԊIV84Q?AVwjj:sA 2U8?j覢ڻ&!ki:)Oq^ῇZjlq \nYG"P9={ψW턳0x[*JqǬ}#i k'ưCe⥸ n_[!< YpT9R}xC]͠d^'6/ ňx; [9:`bZ'E@YSG’q6CR{y:*#$pSN_F6ym1KKw?) pT8{Qʥ5K. VHUKW0zpWoh 5 ;Wf\"CH#kߴWtZtQLm8 erT+Tv$dG"o7,#b9A1 9t=NosŦXRog#ef0I'l`Jxk im-Y"IfXAzZ_?lm !Ag 8^+L6[4-[7zdt9'30ġrxxEbKib䁵#$uB;V׍omm-pypĬp2(X qtuj-ZtvϹQ6͂7#X9Fh!W[&:1,x~l@ wۮ^Vn $ZݍKM~QJ˸S;p,;k)9m-E 0|:9 944Mėm\K{#n[jhctϯjq۲XcTm:OͫyX~`G5Y}^=Ցq[֘,{ jWC7VCV=M"D96LQ)`%.j iwQ6Zz@R"ia5bfu H日1Fơv/ZH$+IDX#;WQx ڊn071crD->mZuf&Y"]3vaTzBV:ӭW\F$({ܰ|`5gOxPB*/,ʣn| dGFO$%\ !Ve7X2A2c .!f20`>v cAkV^AOQq4!xY%' cZi6@ŵTr d 9aUxgmcV$;eA}qS oZO%1 e r;?Ea(e鸣eتpx|Hu)Vw6̹ۃNx{ 嵊Kli6z)蠎7m\Ĝq"}vKX[VO 0B|یqHj3Wb)'(f T!ҢU8ք6^MVRek]6 2L] 3 bHmm K·Q 2ՀI#񭥄 C0Ac1@]Nfo:r0eU9k"0pNHZ*٤e\Iq';q^I_> HXIPcHfϸFߺ68=^,\F2h7]j72^V,Aު2w!4eLJ(Ӏr&(RFjAa46mLfSm p6h=jɩҲqHT)`SbƅaBP9(5&"d.kO/|G! (n#+Q¸k|i:|mu4vU;r6P`d)u4&}?Ȓ o1H-etTLmD2%6nrp1ǠO⿄.6M}k=Q j~-[I7#:( \{ou:;4CxRAe\s<1FsI`z^xL}T|x6 {^eH: ,@`q대;g{.Kalm~i$fԝ܌װx֝eR:*rb}{rH@5O py כ[(`8`dc=' V @ݒjϊ>2XٱЖ[| $r}#,+/{p2Kup2`3RI51ZF4r2H8$ Qt6;^=11k4^O_[BgX1`3TkFK{aXb , wfI7ietYRwRp~8s} 8$JW^i&a4W>ia rr~ZHKCŋsM'SA~TsI)j.i HKf\ԀԶi# x[.ң2>9\jfc#91֤iTsZ\i(KÐ&-HDJ)Sܑ?bZfʱ0(`SX8ҧ"qQ'mXzе-Y" +ί 394 348 0 C     C   b9" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?:4{k >(V47$Q_V8sKz55}}/+#^}ދȣGYj^X EEj>|Wjk/(Q_VGڽW_GGڏ">Gڽj>|>}z>EQ_QȬYȆB_[k &ԯ<"VOnAv(sCVooO~pnTasϥ/ڏ"Mm-GGm5 M-U z5b⛿vWS_<-Պ`vjm'76B[QW}8I_Pnu~]GmmJY)dYlURcߴ<-~3kLͥŬ |k_ X*0S+uOñ}K$e{{otJƊPNEl\^σ|Wx|"Lc{s4M:W|Jm>J).}+nDe{VogG|ddu=Bij>||)j#yf$AnУGlg<'~sX|W-WDKXOޜ 1 |>}Mnnǽ;^#_GGڏ">Gڽj>|>}z>EQ_QȬ{.`6MȶpD{i^i7 Rv >:<xNXynv731^z*5wQh#.pv^O ֝yE];mH=2M\u.m-?}$#jFvMD&\/L@985ϩ\Z$ݼ3Cr|=Tw.y^BQP ` ٷ|?f@"EB}=|gb~$Gi&lh~U`'? {:(M96yP5lYm4UFkp33W\Uw_/鴏 _{khom%Y$A?1^Q ߘmmb{qʸ{ױ,EֆGf9eY;_3E援Z q|0=u`~?Ώwx_]n~y_/ ,wx_G+ i>}g;k /C5!Oi>_Aq|/aetrGO~ø{ƿ0:?_ =_Y.~y_/ ,wx_G+ i>}g;k /C5!OH'0kП] x-1-Ţ2A2B$8_ =_Y/ ,=;;K?*ӬK_^[3Ζ6[nHĮ W s:>}♧մs!vE*">e?}_Aq|/aetYCMϛ=ROwo2\E0U%dV 0cq?7i4\Dmu9VǗ`y=q|/aetø{ƿ0:9 Sq狴oc7%vSoof"Y bxu|Sڷof$76gr|{З _ x+gQü%ό|1⻛KZqs|e& y[ȍ zT7xz R-7.q-%Iwb,}ICOkaȔü%>_.~?.xrrMi> ICOkaȔü%.~}}_/4Vx8 ,V?<}hIwx_G;k /C?Z>}k35!_Aas}W:4fkbłBzwIUke_?͖qA_zø{ƿ0:?_ =_YasO%fw$Ȼ%NRE?Z ⴈmn@ LUr5;k /C5!ŞqCwo1K_YO$|? oi{x>־q|/aetø{ƿ0:Ynԫy]c8J[&TkZ֡- ¢UZq|/aet_O[=wB+$yjδVo>vjZaoh#yi!Ct4}L&S3#=1rZo- վe=q+hÒ]9#zReȵh1X>H`_[}UMѕz0FF \ˢiwvծd̚_$s"`SJk52doKoiFHPj Dj22ƅt]RŬ"m] =0_8ؚ?T3K,PҌV>ZDo<}](O5ICC=p(`e)8`VOwaGhz\NnX[kfI!R` c5Lm3ŷ~𶷯ZGyd&v``SJjܶ{m_QKZJ*(Dc|C8LOijγ$Ts9$ jW 6m(ǚ̄eTn3M&/WoE}[_{>T$)XC#VAE> V$D#5 `@>g5[M*WOPMӼ1tw FyϥVěMcZ4GKƧ},@ve,A> G-.nt)mΦMX ןx{Đif;U%XmM tT\҅$wspFE5Z&eɦ6i #r޹'^jזV/i4|D _.4r99xV_N kzmݤ?9ĒNO қe}kڥ͕ϐ[J!\2+u-Z},h66b .Ď9%À@>Q_hK7g}=nǖ;>^ (n|ZBvaMM)dEu 2>Vs 3_/iד1BltQS)uK~S|/j\/I-au;8M)d,v3yjCzW>-#_~%xs_|emWTǚFaq/HLf՛ 3>ûwp1JpMt>~BzJ޿̏:}`c(δX? EP~+O k, )"0V_koyOڹֿ`6((((((m_6s|.:gfyaP{") P9m2JboSGa]%EU PTX QUKEEU PTX QUKEEU PTX QUKEEU PTX QUKEEU PI5+;#c,<\pBoKuk߇o*O⩁ n@? 3G%l5c|7F'?T\ Kuk?-?gk~1?]5GTolFܤgW8@s CU PTX QU %߇o*OZ*,O߇o*%߇o*OZ*,O߇o*%@NB;mR?U>,WY鷚[Y^%s$G[7q\kQ䭽0+]quj0[7I){g؄uh[V Χ{nd(ip3%|7 EyƘI!f2>5 _ʍKKvvML}:-е3] $]WJyl@?Zԯu=k]<`Ŷ}NG D6Mprb@]s/57ޣi$TTOF~r[B|F hV֩~5[Z_gQkzee"};]]+r1Fzw|"k-!..%XԻT\,W> E:mR^fbi$t*`) bVx^^^iv( &ɨŻqڧo8jun_N|O} h>u{JbDV#HS# ֖ZemycWwS} k褎iq-Xlk;8>-4j"Pί3iqcyƙsuo;[֗6WhB$RK.eoGԔW: ]>xK&{[붍H8+&JYxVꚴks5:*Pq_%I;yEVeQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@`_:.ntpr$ g o^[_Yc)+$bEsE=˗~ QZʥ%;Fe<2+OuV@Db`0I)q[<yOm5Nԩ 2? JږM4"Fc$Aزo)<B|5/F_6.a GG[ú=橨&Y @5OA%uK]?ΚM9IɌ"YW+ Z[xG@=`$~LZ|*dHB AŒY Úvϲx[Fتip&Ю$P0{z֬C/VmڦkYAg7q,&xKGT1Eh:nL^8;U THtӼ'֐Zi[m,"8 j ޝGS5g^O۶=&mo}m{Ogg?7O߉>[4kUy$H.{yFUՁWE@];# w9Tմ=/ĬvZ#Y2'~HR7`!U-1l SRB W|;'t-zYZGnᜡ6]l\EچU PxfSq*<~U# iyccsiV).MдuXem@H'p]N4;Mz֠> h$%J1s?o_yyOW⏌ !75\Ut]H2qhG^>Wk漧~TyOKB.#KC%Ӻt)Xg2AEUҿc^R(!|q#X[ۻ mN9UmXD#^+j|xc[._iѧ{l v҈-/nA&M7Z| Oö>+⼾wʹV=eM\ |7}j;c5 $R  Svȿ-?;ˡF kS^Yzjv=ʽcCg2(9rp1t |@44gHإ566F1_@x_߇|{%a$o/n.8A"#iq?Zvz}3PJIk 9I뵿uz]xgNhh%hP`ֹ9n)hi^'K}.vxaHج`|%MCkR^4,as,6M}!s3A;r@⳴xto^X|{XJ wM, 27/*LM[IRoɯGo>xMmoRֵ,dAqs4+FV#lWLt}*y *4Vi, @ *VIl_$Jm?4k7^mGu}a=LNwɯ7>#|5JDr隅mfciR6A$^J]LԮ ]'=d^E4nOkl:40hiJ̬JmS\WCqO𵅭O J7UF!Vi6Wq{?_ɯ[S/'s},k#ÂAY?fdme͸ 'p8q?ȴ]nWwqHȤN@ֽOoΚI=NkG;QT{[_ջGmBK u Q _Bv-]eoXC>|={m֖HR"  9q2qAbz~ D}~}@# dV+7u/,򎙧" E+Դ]~]"ŭ[NbD04g U)˙kiy; ~̟ |E&/KO%DiQcy o66vxXе_^>FU/`&ݠ8Ed:@y@G&r*EU_O#hJV}GLtz7%o-5 JR.SqܓU d Td Um~ J%nI֋5MecĐO'QP3ȴO_F7CNKG3ysJc{"KSJZ'$7sp2+$a. ~Іž>мW:ɥ]iO}i6m,EEF sW? cm[F?Cm~kθ e2,*Î3_ee'n?_U@M;G׽oYs?m^6״j뢼^4i^"!--̅)/' dI_wM&tXػU[3W/*'Mrkk_{-6Om5d T 0MH#t_UE9+AV?*Ac[Z(!@5=HQ@ r {b?f?MKT#K X KY&H `yFgqIuSFZYUzWSjVZ]{{me[4$ (L<38j85++T{W`g7n \"*Tė`oX->,:Ŀ -5X/-$סCrч٥NNm-ΧcjXg~jG;R𽝔Ti6qįo_4h>;EG3 ~ɾmKRV%'Oӭaef :F?9s`t Gvb^T`Ėb559xn?hֳ?7;9fih֞վo>xkǞ9fxbU֢y`x#H 3\x[ZNkïWEcM ,Ihpl1[Դ+-RiL^Q?x7=jYV~K!:yd#[ Ƿ8AF84O_i _|{Kb[mZ6IY$x,*;Ůx-GTY $żXʯC3J"]|96ɚA3ېt'v"1-voipipI H䜜[[I/i]c?w:w¿Z4>Wof"P ^J}*^ŷ6"Z(dEMQmǜcJ45{ [ۤuzqZ: +nX.&2ĒyI3H<JSӵZmW w|*񯋼{iR\kV1{M.3%ӵ`DjNascšG6emy*F!wz.Wi5MPM-_o~QKBv5%Q@Q@Q@Pƀ )vCFh(} Oi4m>(((((w'5jzv]Es7xmC u&~R-]|Ea73Q-ݴRt)w,$xu1]xXK+!YM"ڄpH#44_y'7~(OIn5 jl-ZT y$cDuA<2|gצ$-dTG\FTgr_mhjgiG"k_?d7TPU'a-ɯ$n3⹇45&hֳ\+\$;ȔƄrxKI_+ O6qF6ʾY<_coXhn5;hkfhlҼ˔$'e*_?<-_Pmi&ẍ Y ۔1m/K)rx柬5iE c+(DS\[߆zK^]O{>iTOyȑ]#pbf]2@# o>oSPܵ Ite 戮ir[9rGٿTllnxlw0vս?U͐vκ?5-)·ٕo2bIA<=t#WPop!9ďE!va*SN_roncla'c< 93LN)v*POEx"v~'֬|-ua#9@X1L`@trOiZ]^imզΞV` \<qNk7L;\ VUeTdh-ޭFa]GME| \i7-,R+&ջ,| Wa5бDU,ĜrOSVvDe{_#q㿅:*ܭMF\[ss$LT"/o o6+Cgii *.T.c$dW궐> lXKQ6wMug[tlFݤ]?3|!qĺ>GIV79iaݢʐ$Wm\ߎNeQEHQ@Q@Q@Q@LVyUG` #:?0n3͟}h\̧"WF45̯һgՍUgql? #ۑ7VwKoAm4G L2)Dg͟}yϸ-}ۼƑeqcU|G?(vvsbԜԺ/o2ʏBJø5O͟}yϸ&7P#]sl?͟}\htm5?.6Q> .A ? ?uoRD&ViŒh j?(gqj \yMƫyϸ6RйzJ h7S0!Lϸ6Pq*4u3'Աl?͟}q[+x[Fխmm@&8vN+sJu[^9EhyI,;8l?͟}6G&7P#gql?t?C@Cj]sl?͟}\huqC ^]0@@lcTG?(QPϸ6Rj*6Q> ͟}yϸ&gql? F1;iU+fWit %*LSg.<ACaI&rm&Ed~& /5?o kX ?^Ao A3BgTgKrAF'H;=FHd&1LE ./s4[)om.$C0|8\](Ωgh^[iWO@"Mؒy;@idV{RcAHAS0} x~ <{uGiN* 34YJH,aUVGj4oZxKIk; #`$1?7+`U5fORR[k+ւ ^/c+K3VӮJ[Qѱ 71ʪxp-+žALP#*Cx[o)c >#X٘]s6Nᔻ-Y{y7WUNh^]|riXYn4ek },@1}iwĖj%p$*H,656ј܋">h/=^~O7\ܖejvѴWPK#HTcb2ĞOFQд+ ^o[pDQog $ē= IrG/u[NV.b[TgT-[ EG<,HC4H I'M $ C:C)B >T3[oŽ8l;]:[G foELo {dR]u W%Y N,l̪BQV *麻ĶW\#oalu3ZF?}ZInd4f8`gƕݑ m/ƑucJ:7m.RM],VǀWnЀ dxcwZB[k}7BTh`q'|ƒ_mcƊL$a0#E6Qf49ʯ99Ӧq$KB]-c)++FwLW*]-/%/O-5g;둅$M$WZ*L&$kujB\-oC\U]#0nt-.5K.cp-nS#>i.|d66yɕy[*+.R{uSc<7^g0 -F=fhf8i5߳֗GGh$X?LH੐78$Լ1..~6mK6%0Y^I$rMMkc Vpȭ1#8{/# IJ\OnK\cz.?c6_)!Lt^\ԗ+{jw1 &Ĥ=A;R`f۟ k0餍FK)ة gn=?~[OUbk8Э Js38\uQl:jqQlkԶNs2Ã[>q9CP6}ymxrKBNŖ f"9jπվV;*%01qh|EY}p&Kdʾe5"ڢ«in1 #  @86i_oO󷂵'C|Y Þar<-_t\ X( gY><%L>1_KT>$.ӄ|Αe7 rH M>O-YyHrx5matb/.;(v7\GzS曝 6o=V w޸dҼM嗺J^ݙR;#RGa: W.5;m>jkLH,@i6QYŤY ;tcIGU 5o:jM"ɏ֑ϳ=vSt泺co!};/'ڗ|I=w̾I U*LjIn/ǡowvdYܲ?xzfT{ŶGv]qƁ6e31SS6t ($<}ׁQwS_2޷5 =WR%7AK )[ٜ6'xŽ{[Q;;mMmڣK! CGJaKmA"۠hC}\Z׈,l4 {;YT2fg!<sRyμ|j6 o|]GW%TYe34?0P9x;}aoAw̺d_k`\/leX(=]' 3GN$mir`Uh;`:UiHI[k=&XcRz5owwv,S(_Lt^G&:_?,ڮ;?3\ם=kcz.?Mr4,R@ 8 ?:LQE QEQEQE5ؠR8Ȧy>iTJ[twd%Fzሠ sϼTy>iUQO"K#*%LIP)'}9_?<y4Rz>OjO*9_? 8@y>iQ?SaH)(/9_?<y4Z(/9_?<y4Z(/9_?<y4Z(/9_?<y4Z(/9_?<y4Z(/9_?<y4Z(/9_?<y4Z(/9_?<y4Z(/9_?<y4Z(/9_?<y4Z(/9_?<y4Z(/9_?<y4Z(/9_?<y4Z(/9_?<y4Z(OyEȹ/P Yx$ h4\OhڞTZp܈D 9V@_nȦcFDlGx[ƱWP]k0qi]47m;C,,:Wk{U>nbhL|0 u_ RR]L5^[(2*F=ڿu.#Osg%MOup< vX++  uOJiXZJM >&Q|"LÝ7_x ԤL:͹goA։^ J֑S<7^n?7-Y[)<}<6#Y&E'́v8<z1N/k7N4ĕ~Bo:-jk?8k'xn]Տl<7ck7w%H^9V S8y쮋ڔ t5ϋ'x15eI˦C ᵲ{37dXEۼ`^ /\爡 wqj6s&؜#|pF@9&ݚO{I!%.HЪI1;^x=/JʅvP#P/g߂5(SI ar4NFʰ^Q>v^(B>{X5Vzx'k||gKҾ̧OKzV04ZHDhJA ?ȿ'^G nr'A3`\{bvIؔsEA;/7K\_u:gD7h/1 eu8KQGRTTWwwVAsv3GʻT-gI$Xk})٠ gR hn" DBFI%1ǹ9-~nmy*)+jHS߳GG`djƫG{"4Ug}W6O|Tw_|%|ksmu]M>&Ie~QNs[iohHS߳E@_xvzl4{N0e[s tQԩ 8gG_?4ibτ.iZ<5c;~em7;NWAq]CTf y9;QTIYkbh_߳UrltMt__\؟>ҸΟ]W4אqnd2\.1S~clI#IxE7R2*++m. K+x`PADQiohHS߳H hf6giۼ,IwˁK4L= LCb+gY׎$9kC#yM~ioi?4 z=nm LY5-#k `!ԗZuVZ\I[ṕݘ}IiohPoh_[Kp:l,_)x_xwFe+f4kbh_߳@V}MkX[ cQ/{ ?~+_߳G؟woV?&//]W1'f?4m#'%܌79uk0sm3OtHmmg02$hp5skbh_߳NtMt_¹?4}٥pi2\$FwG%á2g DTě'qԊ} тs F@ERGjzXkJ%yYAod ƘzpfROᚵң/uVM69gGKdVVH A㖻qAkz] ܺn@3Th{~:7+=ǚ5˽2ZD6^"YEY<<)uWo gF}QɆv|ۼtݞqbVRmu#([-|iu:nmn׫q!v*xßy 5|UOVb r4!pdecX*[+{̭uCZ+"oox4-SHN(SW{ym?YP1d '2kzz,5Vb-D꨾K4OaAZG/u7h{u[𭆻/}?D,my>B qʂqYkX__Huѫ>)傶9Ir_cvzu O]?èyc0QFWhgʌ;@4돍PG:$eẔ[|$w8yrG^H>?I_xsiP^hͬQ[.;y \28<0M^%7oGQE ( ( ( ( ( ( ( ( ( ( ( }CSеMLE\vsrK>Tq`?xbaԊg/Usjuݔ^ {^,7)\O*xCuHK{$dVajy4}<?w7Wڶ:͔\,HvZJUr U-XBlo-b}7LK=^\|R!2I1К9_?<y4⨲]ƛZMö4RI%YU6p7HvM70io3Dv88K}O*6Vw鷚ljOgwDıٹ,7-WeC Eȫ\H^sϼTy>iP+y=׆b]^-^++Kml[s.JHBFj 5 ^[u.5n!]m,,2BxV}O*HM_6pkN]J2ھ Zh[%1rCm߷{32k.ODӭsiu! iQ?@Qy>iQ?@Qy>iQ?@Qy>iQ?@Qy>iQ?@Qy>iQ?@Qy>iQ?@Qy>iQ?@Qy>iQ?@Qy>iQ?@LG.Ncx+M> ( ( ( ( ( ( (#-m1^L'# s@ןכ]y_CNWi/EΥG"QȠꠒu7]+_ o7Fk-<%u+Y7XZhr`@Snƽj n3Ad> 'F'F91m1'sз]+_ o7Hvon粭@?ZS^G5smww䭄n2 @ ߉ >J$ZXd{"l)]t^)-Ii~mf1O:-~$1F"Yv C`aOb?:vk-V֡5+kzKx؁Wju?G\6)ВI oEs]+_ o7Fk-냃.r6I2[!7F.3vvF\qU>m$6A H sxŚOlnm( Gn1[C{Uʴ.N|lޖE6YOk1d2DpfdD+ue `V!ڀgarX>k~ t? _yR75E%M!%iK'jx隕ڽޥw=̑ ɅLy>Y=Cq~AӋw_֖).oͿ՞9/>$krS-Κa*Cʸ=<ug\Gh^S{oʺ/C1ȿӣm:]f#|q.;a~勵_˰Y^E~fޗ_JJ-SAU-ڎ:CE; 8 2010 2150 2 2 0 C    ' .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQROC&&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO>" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?}A%NzTTRTQI@9R521F)h45TliP`T"sFhfTBfƞzS) *SOZrT`ZcB(S*E1OJ M+sS⪀sS)ⷄzǵpArsm{*"!+tkGTvok:N@zRTE%+3LǵH“i#";QN#5BI&jb *Xelj3֮ .Td`R(fKNo"L" Z hο14}1X 5aItdʊ%74w- &'֙i(E=>9qqR.xCSyk_?VD+bJ֢~+ߥUqG~zcQ3sR5BH ڤH@M:FML*[) SՇGEFgH٩7Z& Ȫ֡ORE+Majk5# S#dUr9֜8*niEl`m 7jZejY+ O,UGHi"!sPIU[ &#jRƓfQRi84xaj+gxֲ WL-M1Np985n5UG5J̝*@x+6+5'Z54|)h(Zv*u"2hǭy{#](ǩOTvk:J%u]˓Leb2i!;Gޗn@v3V܇ r=FIf$U}gU!pjf$al^փ#>ö~VGmd 䎕uaڬ[)dm&5.e,GNR.Ǡk@].fqO5d4&[WbַV GjϞxY^ĸJPW}SQSS@Pv/R!T 2^sVb\{ȟp\wW+]W +c5=?6?UXͽK^!+&N OBdTWQJmҀ+=Gޥq֙hM0#sP9[aD 8mcZgIJJsi6ߔc&c1&*޼SYdC}}i6g+/|V^[ +P4ֶrqVYڬ< #eu#r+>-2+ F[rYj[%BCm?Z.;8`s'k mx=pA+ϫ|֊:a%kM>(ɑXAq6 +.mOͻҭ6e"PqxÃC޹[ސ~VI(GOc{H3:3 ӛaLCۗ*|WfA gܯQW5V6LR.CZI[3QT.pcs\2N̞ KLzS°-֜AbҔ fCy'!}Q'9!5kO;'ZJ**}7rp͸f^M塮nRԷ.Th92=k!gzuЂ ͽ [Y~ j>Pq? TSVݷPEMoJ8r1,{ >VQ\aG'=sxEzP1"3 ÍnG*cm2I}'e \(4;MBEٰ ȩswB)Mnc⟰L7pOcRK S88yPis0_(G_S\fWH>^~Rk:VW' 3ƫ IULަ.9su4$4.Aq] 7iCc<:L3$Eo3 \ęR{VVmX eШvLr rT9oNi}uP2liѭᏖGqg d692bHp8>6]Iw.%Fp~kCâh1ҕWM)!i1*JnxO-q+ԏj̝tq3+VfS [Z ܌mۏʫrV5I*X);j_*WfD}%ylO7xĩ*;mhDn-ӒhƤo1I:& Ҵ{6KrW+FimU6w˻8{sU3ҽJ8\8#q+qU"~UZ[L6~J8"07dLToHIF=Μ.H Ef[F1B} G&,o7[>@pզtA- (XQڙS+MZJ'ӕЪE5˸$!2G5rO;Vn o*ucһ([k* 5x mU:nV6|?kwIu@f|u 7k3;u:q7Nb ⠊mCWLyQ)HϨe< [pҰ5 w( vJwH}˛p}*osX%F Ҝo` $=kp-'qU}!vqMe;YΖ͊FP3#j׉Q)Hi;9$Cusްa-eTd =*h,5jldbM)[v2¹y[Ҷ, ]6i,{E<dCs~f@ :ϼ8\КC6ZFU8.YmҜA]wq(I-|Iq z|;Gh7io>O?RhS( rJΗXި9 cq}El+1nǡxp> AIm#(G ݼ1^N;ݛFZXzxVEROnj"Tv,@^Eryv8c%ą1cfb$\wre_$Hb:tX8yfKؓ\1oE :6yca׸5v\̈$\2= FBW7 :T gsZ=^nƲ#9'Һosl6I=둉Hf0T@oD$T1P7$2!ֲ|A9?Zְ֝|]1D.Q+&xǙwu Q9Fk9ɧG; 1޽ׄV9e1vrʹMH"A ޘ..`!FyVNrGسuK{ G֯*vFԣa@Il2ÚV?4G\2lJù2usis]{۩G5yj#S5ml=k3bNJ/-^*VAb#(y[VN=zƍ˸m,54MI.Ƕ.lWˉ'BIXR3V" WMd \>W?sr0xQ]D*IqjpWl \%jH\_'otpF2~:+؆0cD쌡 gOou;cp24kyeyJ q?**ƒK`8]f%`$) *ijc2>GB=k/TkEP>Dݴc޶nQش1]Lb*7va$ s4NsB9"jn>+\Ϟӏ=6;c%4q1\,cv*,!KfeWsN?,v $z*n=7V֦zt% &cvu-ǰlˉHLʌ0 xC7C\k*7Z`IjԨdj:dw|֡IR9=;h͕T\^oJ%(YS"ylx.+Fٔ䖅!+1:#giMs0kHœqi,WɍC<JƹeDGR\TF}{^CKSmE8Ui3*CƺMn@߻*GQqCZt? ^bXV<p4:ke źY85_إEfsd~` yȫ[b` /L֗ r*3>Uܵ7P ҔOLp+0@VM;''C[n*>oxkBj9ͲYM#Iqobï]S 21qW]AV^tA-NW\ @s4ZX=.LojhNV28 ;xZ8IGz{,X.:_"^EF095}|s3͵dL"pr9lZL,:9kR6.EYE5*QRE]E$qel5OVk˳/%Q*-ﻀsdbER[$B$595-ĤujѴ[5 gvYvph hٜkѕ;F)R;8k]>VpkNգA1]ipDW]J&`5-%VL+M[,GS]9nc(vfu?yЅKf& =GUyhT[HdgiC8)|@ް(e447z rJrpqcz֩\aJθ>J+PZܔe~SRq7I[ң@j4^"eօeAYqIIKSUDVSTfsa\u(tG[CRm],l 랿d$9i,jI8O *uvkmv+zwYP(>Êݵ9gph2ˏP+Zͧ#v@ޣu^Ȣbɫi`(Pg71 v͜PFMBHt &yDY<6bwcei(&/)NA~d_BAgKFOBKbS8c[pf2 os}88iZ]Aih#syX,nmR+mh0GCPi]$S7'T1vC-$v6*g#B@ae皤85.r q tQ]8-IqԊy.Ddy z-#FT$sD:}*kI'zM$q&Ix)~F9QXRDž3'dkQ׭cx N27*JDkVr$6rspTuQ֫C0+X s7jZȮEaZku+09+$.Ԡ[1*  OO78P3EeJ5n$VWX{ABƇnHo"fmn ~Z (᱊TrF͚sF[Zkq\jV4bln|(` 6tV;gt]1E#`Gf5+ڭ(՛D)\֒ %'ɫMlV. LKUsנ1ܵn`BV Ac^4gw\UB+ek-sTṴiOE :<j+E8[4q?#PZΪWJƺTYFR9VLg ,! `VpҺ%.XgԲCgjA+Ae܉KSYw>HW3tٳ-8$.qM's>q֩HԞ3Q-^"eE[fpXU F2'1O(o"(9LΟæ?)dќlE9W_mpeEpKN8Ήd)3n(0j7:dUa6\ , q5rAsMk:míuы&[djo$ӣ(VPxA_OR,m0[z}8 ۴J\[`xEQ#m*֢5GdpJ#aNjj̑&ЃUl}OT#)[CQb+VTܹU$nrYmf#z/ 6dbZ$\!goz_%:ǒXd>Ư`4(Tۂ:)&э[ݍ&yȭ{!->Cm `kbMC)c+Eg$sZIZ1+GHC2-MQ^l2d&VN?WY}F ⡐mB'oRh@2Rpr$?5[sXZLޛ|Ia5l:7~5^6xե'ʙk b&]ҐF@ԌYG8¢yQw@riz>[mJP|bAjby<hTlVU֍X/)(\ RG4>v<R))lC~?Ji#R mq #LpUul5hHh|bn.|pOJE*r֒D e֞Q0Wm&r9Шl/b\k>N\ʱ?*~ ZHsЧT^/1[Hh܄(qW%}=iU$`Z|]s<$ocZk$%2db0MKn0)v&onFVt=̅x tLR2OhnXa;ӝߐ*6eccßj7F噓MqJ&mK2KϧVvq2TNS#έie9_Է3(F#,T)6 ָlQk2v6] p(&] F)2J@r:(ٞbxUM5kY"o%A9BS$Ӝg&X߉"Yg5ѐA5dkXt PpNڨKtm%O;ְ˫"r:w=֫d!5,XsjK HAۃLi,A`V@~V=iX䲽O@Dd"=qWu+| Uk:/Cy),]JZ&kcBf &HcVM{2AoJ`1ﭛ&?o"p[~ylmqXԳ MI#"H` VEyO+$s6'; 0bbw.iDR+k2nZt}E*qݹ!2@~Bn$sU$RD=8Y&F#u9b qP^hrw̞5u35iJVeR\hFR[4 bEۚXyodЊr20)-wɮ)UoS6Asj;50)+)UrlϒT +M-ښ-IJ3#XVb3RY:ǑRjmjdӉWmf6fC<8r+\`@1V.d9^HGEk4)ƫ@-? ֲ+WUXlGV2k:+p5K`>hsư]mZ/h3rPz8C⋻[b;+y_-ۚY`oɹJͻ{WG+}*kԲ.n4'lVv.¸uɈ &5KnoYԭkk\:vn"dV + qG7偗,so ) zUYs(= o0$g{:Ԗ:5WDw'~u4{Kin l@Hbc)[3RVXs~*oM[MIgj௽X4u_Vm"7n89ϭ`#6Q*z+Ef}ݳBFj i099}|ՍjRɧJ 63V4-JKu"Nt7b#d?˕$v=MuBuV)93R[=.dX#+?GKi+q[;cFIW]% I7&/B?:#Rx ~Cj7C:s$8K^%Dx3'@ڕHE;9aV!זQʣs娂ay1'B6PjͶeb:g]&0MўYQT7J;KTo%TjQ9hnl oU˗MBEũܜ?lRH$wi1S#(cSRF._\mܫ}ErXԵ+rБhTm{vQ!ǼMiYʤw3,$-'oƜ44FHQ޺Er0 G ѱי,eNmv8]I\Y-\dS`OAS*0Uqs7ff$>jM1|橳̱\Um#T sT,l76GRNyhUgy !P7WYVB[9oJtؗf7XKO[[֌8=jv/E; CY5H=\%vԱ=k9=HE…qCQ!>aUICR}k]4Ev 0F~F=RI[$u"Hy'~,R%P=Ҳ2vEnƽ=QN+o7\Fױ@s^~"ٜM2%F^جE I57'dsOLzfd]3ܚӕԜFe晥2[ѣfX;:&OSLYRMp:Һ ՉWҹ繽 e._Uta36"kR^Z) +b$HEPRAZre[u T6f;E7M)'8cg>Ȯ-ʁv|q GnkyemU* v&IW 4rn՗kFґu)*8"`$YFTFQP:u) 5, 5'gZWr6"A,9mt3J[ Vꦬ|-QnGjYMnzJ*NFbrq|JJtN75c0zRA ;T\֍{Vr7B@In1iyoW{Z^ʦ{q_;p' 尦_A<²]͞ޅ2{u7zS$:,l vf=W9?"94+DZ!]HM)#c΢Ԧm pp hYS5a{'a̮c%sjAY=W@F{W=8w* \pj^E{ r8\<յ^2j ($c\ǷR$Lv T9&zV'=Lb+߅ F{ ,g5|.\jW:qz˻XEt4@\vs:Я.X1mU6*aV۷OEa_lD y۔d셭DTX8W7O(UҩPI H{^¥SszUj߹+=8Tuk. s?L\6֭i7ro֞E^/Qb 儢97Ff>aZ|!5SO BK<Zlt5QZVf2CjrEi=j;a{ִ+r6\em:ʢqa'煪c-ox&4q%BSJծΩ /tҌf)6l&NO&17p6~%ۤyd+fSm;I#w (DwP<քmb927,"'yA3>k?Z"8̬82#\i:})u{&qҢ6+RE:nW[ '"kJ3XsY]NC] Z$^ErC Mr֏+穣9J%ZZiVSX@時EO&ZfrI8Q5A),r;g&jrnv:ȹ^:"[ Oʈkb;xK\3,Iǵk&Lֶ'7% 5sL]*ֽ<:䍍dfíBW[PDC]Gf&f[hq֭+8G; ~|?_\˾<~ [hS5p S2xHВ+c^w/?:g2ؚ+Kb*dչ*bN+.hmǭ2d!I0@Z?0Xt͏&+Qa^VљIÝ0۽icl^lKYjӺ+r֔`f>1)g*kj1W"f9ϥoih1I Q6qA\aǭY_q^|F-C!e3F;K!eڃBFiG<6Mi=POPj7W Q w6<ψэUgtfVTp= {V$ܚg֮nu QSu@;VFM6GW) ⻄O-LnK\lUB%I/z*nv^ E*p/@K. zV'8$xzl#]9^mk6b * ,x|f»pש-AR!Vmu8 k!zcgKm9ȹ8/OQ^@X #t=_l*b/ieMZ i{ļHL0G8T]RPMrc{y?.? Ֆ 7k%:۲յM"ks ,lʒl:{w75K)KbvfzB3,Gk#Fp-TNig`mZ_3P|_缣A&QLY|IMe"(yJMDͫ#ֆZ[| TOmPfg=)ZFٱҩ\ZFW-;E\2UC)Rtf2xqn[PRAұB5k.jm`oS\Zj+qoe6[QqOjdC]8ؤ-w?U]}hR4n%esw-8i\ *׉ɒ{3 ? %}nQ~gpJ,P}8T΅Jo|zTuK-.[IE`&׺}εZ,qRZ,Ź>uϡ_'յ:؝+O"VWL; Z}K =K!4PZk[{ify2sT= ]0?hn2jcvI(x;sX'kNy֨:`֦ Lf Aih;Y;۹@X1=*M澅\[TKa#0iVVV2<]$"n^LikOZ% ?qU;;Jkh3IzvUo䷙㷂+BÏb6&𕤱4muriz~U4Wt% @ɜz+FmO^H/8E%A˔ wg8 ȹ:^?md:.T+"*# tkFo 6I'H^\ҳm5@%.dlmD8qI OQ֯&&V?e.%qQ\X{T+|v G|#o,0HDն)FGR2گo0HMi/nEaU#ܧͼ`ʹM+*ha0P3Ij$giPQGÚ/Κu8uYeB]]^ .Է TP3hǦo|(ٷsޥ?/ΐzX}@@ۧivf䑧m1aH^:xΤTtzi"-.U7ʶ/to?@eu}9yHFm%EWW?γ \lW9fnQlOS`}A4~?cG-s!1A-NvO|Oн8#Gf=Erz0{"xETګ^s]KGf dEv:OCWS=jpA= 4py@2"d٭ 7(nGB)Sl.&ydluC_?ΐzX}@k+P{fy8 y6-֓5ĒkjmT+ARoi#_?A}P|[X?#\3~sҽP%6~p*1zG`*% $yqV N+Z| E C)Pd$`8GQcO2@ X{^ԇAt.c^>3kqLNTXjgo$)5zוa:}O vt?| plir[HsÚ';4/ʫ\ʯi?؞{dy]qTZ ! pEflwF!`@:֜ڍDqQliTQ r,m-*H9>Uy<5ٵkk2Mǩzu4um@<^i{E@=8I]ZK Vա<|q*?TO}P+oojWRY0h H Y`j7I̻pgO 9uu#r? +bdp+o5=ݭjJ|\ 5͓j[[%d ws^S9U $ {4pz:[]^ds1X,dMH[Gl@I8|.7V 3JvǩcQH2x;Cϝs+?>G?U[ Y[YS U1Hw?d]RC%Z )EMn g/ms?&=LV9cPSZ jJ )TL$n){QAZcOcP`FTt;sBsECL5(-QN:Tdhȍ9zZrɂ,J*$EB|QHM1kCPH$D85|:U]Þmo_ktbz3T%3T%uJj7@sQR=6,ID'# }9qT'8 hC s)ԑNx*0Wg+"cBRTRf:jEDfS=*6Lqƣ|sRmNjM]o ]+ ]ÿ ][#O_ OBMkCSQS@3RDci -!5FةjL5#MMKTWTZiOe!QEIRK bb+DC6):ԵPLʔgnqT$4R0'$XP7Z$pچƢ1~f>Pqzxs+֞fBylxj6cG?.JQvZXKs:.UO KEwL8 KHUWeI (9^I>g8PI=W^FTm j5ȵ2SQ+kb]g5욚>j cDF 1IW=o_kl֞m&w8޳v,%IxخuAj.Mk(]]杌TǑJTF 1JEj>_]Ï ]+&K~$+cğd52^z¡q@0S1ӸW4c>!$f=&{U`j$R=Em%GJSkKsE ,VتSAt֌{Uw$b(#k1ؐQLZRJ;IU7>U:o|7VnvFMXH&i2rW48&W)DCe{-aGʠ T) Cg#d 4m|<ڨu}ky*)][EUzSUrކ %5+a̍n: QGq5͉0+]-^lrQO;1F0c{t01n+C=)Z܊Ydn;-up6X$#0x=>BFvpڪj 9?[0A9;ldiF<&fҕV>^;+ 1qlYHe 'QͣHB&AQYm6a]n/(Pw _J ϵsRW*խ)F1IclзL}"w;XTnlwtajMnG䱮U$Б,5|5[{#LHN75FJgfҁXsOkc j;&t)b eNL2pqgE#/UyuHb\*`jf,711㨬}2cr@N~\fCmT1ILS7ܱrL::m=C9^rޫJ{Oͻ[+g,r*ӍɌy27m3FOkvfq$[vňu$Ukzc^@軜 *q䏭gjLx>vs+$RrV^ִ 2QաlSmZ Sl,~ξNC5c%rkLT d UI]5|8qɮ.%jEkxq֋ZZ SE&)b1`*7fF<ʥqcAkU:GCJ4i*H\k` 2gY-H˚R$G!V:Vɋh+':F` 54\5*\PP/5*-H":鉓*bdDU87UcV4o?Ě6q'>ndֳPY9t5nax\՚l)-ȓSE-fp' #MvMѫQMye\e}n&(T'\ nv~Ƣp{80< *T\,ȅ Ŗܨ<~4. K6)rJu 0ܽ(nx{ ['*mV;{tWbr{'w6G\chi2H'ȚY0]eƉ<he. j*ClNeQ#n7ibsY.֮''P1HҚQS=S$P٪-d.A,1lR7>}+)]TάE-@s<[^rA;9$V!A*,U?C^ \j;u8RqFG;Ww=o_k{zZʂd1oo[[iʅ 6qUz =3co^H#zJͷb"9bDnӯ#k(C .)!A9sMIMS-8wa\ve% jCWohe $y9-kv6@(&HčF1IvEC Y9j\Q"<.*`چCӹ.Rv38sS(X\5"\j"LAfm 7 k"OSC<7ZFR'RqRxJF +rf j99 QTCfLjɐ5*kxM)B_&5wfE0+ctʮ*RA]wV4QUȫZpHo.M.tdmtH2DmrɬMVc[ہm Y6+u'b.ÓnW:e\#U_TPĩxns iYnG $m{VJΜHn0#Ң̍8vȮFIdkEd+\;*5aIݬ]nSXOoJb9gW15be\sU^@2zddiknbd%A=kfKڸkF}{Q1;dAq`3+Hv9cYm.$xv.{U6d1 Um-Oq]r0FlG'Y VgrA UG(_ld2  '[MI`ہeoQo䅌r1tIaX#Qp5hF}SWE0\*zW62 gE ZHy׊ K;W.ak=W*~W=Tǥ+s#9hǭMnp[zp8"ׅC.IjG5X_RNGZ;z.M9)#J[aum$$'UhqZp )8^isG) h=Py!#%؜`i%o:KA0Ƿ5;bz^TӹJ N̼4{zG%rEs"eaA=2E0f ե:NVø.e⼏\PZx!?"bKS:4Z`*VN&I"dU;No@fdjW` S0Mnbؐ˷cPgRjDpM$ koN8\L=zRW!YmiCG .z7qsQ-=k>Re{t`tQϞr3\NkV\56SI ۶@%W,f*qV)L*U$סJ8JZ*9zxw!kڰԺu$b>^ji[yel3Jk{B($6,̭u*ldD.O,.A[rzV$2]0砭wԋ4m Qc<J|Io ᄬz}wM191NQʸwʼzZg bMA<4<,R95 r;+\̆&KQG}z*Vr $H*dzcY 7 (KntMEMqXXcm=1zR6m4Ϊd>xƸy;J[ZgyG$w-qgy&T=%(XW4]kqjfβ 9 \7QF+WUxpXV]*s(\[$& Fvv; aW~n3|gB;GT,YoyokTZ+Q)V3LA\chyAܻ0 Gzl1rg$kҧ7qEjeEs@ ]߁ f ̹Wtjw~X/d,څߌQtnCwؠG)d?fY&c=z[zƝkm\\\\vϷ-{ڵͥЅ\9T0: יy.4}%#dNӊaڇKvX&!|%砭]f@H| 0Z]S;4tQyiy(=v+[I&q pZLC*&~s[Ԍp9\ E=N~9/.)LNf#:{$6CJhQqdĠcs*R,HFszkʻGʄY4ČX)+j#7HTyi`<9oϸxV֊ǐCVkO[ѓNcP+V M2^̋$dd0ak\. `RlͥI\@іx%\%'fq[^*7.T lt4-ہZ0ÊzZ:O.3Y9jžқ1w9*5I(,۪B]$C Uv\L ѴĨ3ȭm4):kӚ>Ƹ+ֆЃF%EgZpz&3/vB݁9BKO\՞^ Bj4s6 a[6rjдjRv-_Vk>ݱ[2POB)*v\Du5r sVUlVWFɫk3+sRCЪkJ+qlcqާF2C++IXNHB%ޥ$KiC2=ytdg` W')od!ةT); oC; ߴ ? PEM =fvcb26Z24d7UI1,?SmVHkI3+Golv~B(1攖9+w<W_h?NH6Cҥ3; ՠg\pxZtu#Prq$Ta,NZ:Z-FH$tEڣ&Udc25ueRWqy 04d`*5EtBAwz ÂKNr9EJ[U^)\woYڒ\S/󌐣Uv<~J#q%sIWa3H߳\M9X6?γ "&79C=d\bI*sSht݂GTf)En֤r]]jʑYذjj 7@I>֢pSRvG_ce99jǏ$rqkG}MU%׆,ۏ#7RɄt9W aU1F\j7NO3^®LW+f!V3׭t~&>;zB%Rc`M,a9n>Ұ5+7uxKvXN g(b\yqWD`Qً M)xqڕ=4I&WKx|Be+Bw( )y8Lxhi؂;:S^q)c^;tYт Ȩ2ocU´fOs?Qxj6s+kR~{uo_õ6+ƖA*};R^5 [ {uǑ:dW@uOddbbIWz "+n~EW:Xa,\J@9?bxsPV|s+M{棃r>JF #=XdO\MdјF7W4Zr5VV#if̖FvcV A⚰#dt+\W&`Wr}( d㚽ofYq`1⺍/Fyݛؙrw㊞FΌ% 6[{yc "qںVU݇X&8 dT:o!UԈ|dW9J.t1G0Tr>e<,Gcںw{+$$oUF8׈էBRqZ"U՘ o &#<֍莡nc\_72N-k1!k>kQ*+ˡ2QYAYJ?һv wֲq C:j[tSamUᵵ{>rS2G~+{-GIk0Ԑx Ac"%7h瞕^Z{XDP/Ncp.O&`#TT?9sn?_RY:ɢơf Zmhב,`^p9]V_gٜ m 8UVPU-ڟk1Zn)*2̄*czWLGYV\,[MS9`=^Әhi+D$~ i`+Y&͵Hk s yqZD}c=f;wUG-oSSTX`^bOLm"xzfR?+p!$ᐟd!7\=y9+j*fu,iiAPX_Z3oa}WIj!ZTuQf[H/A#LCp 9Q4,l9/$=QeS%y5_REzfJ5G*3v@IޠU:C(KV\I1R.2hASժ85-S&m[VZFVY? >j!nPsƳq]HҌTp⭦ W_?PԠa[$1Ȭl0d z6yϡn̅w|#:rNQs=jޅ \\ x,-|vwn. pBg®m:_Hߑ:$nj{i'AaP]VVQ[ʰ[\NA˔䕈bbw؞dLFV =QAGUqs[zm-+oX1ɋXq Kj[% ѐپk6˨3u\MwnpLB-C՛1[U.FH( lfvlKpW5cxռ9o>1^Mxccڜcg"]#ʠ0'y$ՄS,tgIxO&IV+]^KcpFm03]åo[UA:)zxէEKM[kHT"m+77vM!c*}P2eMЂܫr1ڤdKʸ"E5TȎ=if@b cAy'G#vKnln#+hC ܞs\i ryՠFHfYOXZ^C}/z1LY q "wo0籫Ze,3Zs@b?JR\DJ)ϩmd#S *8")n1'c˩5M%ApuQ,Ċ۩ aZ6t* ĆΊ+jXkҴK9H4y]@sRKs5s9$Q-Hvy5KSY5 SZc7Vp_|7S4CѿH&AI6E3% 5췺IRT֩g&;"*:$wEFiD Pܬo2GOx{vw063 vlCGLO D1]I!B5Flxɨ&.k/xbHJ=@uJTgy2ٔm_Zf-pmyx8&ƂJF}XzEYDpI8Zg/; 36p=QDNIFőJ͞4~W495>fLrCg5rNxW$M'ʂT:o$d˫i-g]q[,1IF*:tmm-\HI_B9e+h{.*[Gԟ)ZTZ+yWk䑓i|EW(;zL.,I}p9?JE%hrF$^@RHpv֔6rj:>nC#ǩW|30kXcf=B E[͛EƧ⻫[rb.~cnuҊ1r 3s:C 5\n~0wl7R\ $VU4zFj(ji9>oBUo!TnyFe!J~uJ TvJww*EPR.EZ-P+s$Wg54biÁI-KlF\e^  'dc?]0r@:7w5Hg#`VϛĻ͎ۻI W\gҭBUz&3*L(G4F$Y ds2j?\62INF[@j8ܹ-'O2ij>Pr֊/cԭoʩTZfF#Κ%bP`,<7ۂ}1#%bNJ-sI:U+ Gz i\&k̒1۹VmۘhcVIпh ;O )Vy #iw)aKy:TjDN=qQTJ}&k䐩Bq|Z0=r+X结ɋ~9 kV<>cf͢^;E6[:U?1Oݜq"ũC}kFI%i6RImQDiqK9ME<ӴC%Τ)RNiMI6eD$LD<Ř0?tko*P *Pϭw#(s#2 [̲0J$~k@Ds}잭Eܬq[h^sR۬#zidP^2rQ\yn$)33E.YJ8È'%*W8ke⩐Yiy 5 X1rA`R:y5` {3jOfkl)Jq-fէu vVJ2$z,js$Uƶ DÚ! ȫZ7*ޚ@o?LH2G mFé? y uѝ-#O|8s w5ü$)l1ڳ%=NѾIvyh1ZrsZIKHsV}yiVnWF)htevg1AZWeC]BӞBu'Rݷ<ַU{,$C R>!T*xcɏkGLr4ɹFHJ{3^V-zV,KJK}+J +O'v4e,8cgc(tMx!؀O_XO.5ibTQJojrN9U^#F_-0GM:y}+U%-L\*+ˋ{ xvLdbubJ̞fRD*ڴ[Eb1)0S=WO,!}JrA1ֺ^ZGDY/5 nיCkK*ޕw8%:;+nw5 v]q޼_˕k{MA!T1Bg&e&M0q]_Em9AjʭI^3ZM XԀO6 g>sZ;qOUYE)Eo3Q=z8 i$-<2qUo/R V+\~].e5{$MBB稭TfNAXTHβKj]&̒yH]]bؤ#mE#ve+dT'S\*U{vD΋%տ*` rjˈBl}t7V>ue=^;eKKQ d'[x0`=R_jv57\dtIIrs7VH =*Ul V+zҭ',ш>#-le8s, w={m͏ʵi?r7r7VU̞֍C)a.壎`k-m^vB5еojV:]ipS,,H~Nsj+wwf0$נ|6B?ڝ.g( Es[3].{Eqw:|Sh}Iw>/ F``y6Kuⴴ`!ZGwva Ս7+ ԫr*2,&E#tRkhղc8F!iB(9Ԫ*+ T5}RR_i\7"RM\c8#SXzv 1[J}S sԓ^W"{V*Eթ%r3=QR3YwsUN3mJ=]F8q5w["Qưb{`t5H}֣$Ѕ>rϐ '4 6)PkE[Bopje'e^8#1@_fJd8B}kxYw!Z|"!VtϨC0A.]wg8NrZ.}܆ ַӴb'I̷/.=STMȔFUX cMA(vw!I,iֱѐO|eq0H>5UwmqEMYXr{7RHNMծ49X>`"у(qTO#4sܮo"tgJiXKuK5o,s%T=ְ-[ɂ0z[Mac9OQI"[jaw"cs*JȹV iH̏ݘ?WJ6iFnǵoe;_#C~EyOSVq9FeЧ;|F 5q"hg5d^zY~\ӥgۏ\,L@O) %IUIYrr`1M[Y߆zVux$$c!GqN5 H|"6I <,~lsZq۬M8!rOֺM6ucd$sLUQS$DҤpY*\ДR+)[D]Й-ENǺ¡Vs*{7[݌ŚDPXp9Wm%Q+]ǽrٺáB 9+MwXӿ:1KMԭ _ hdʌUXXSD4\ t"Z'sz摕˷}c!ҹҮlzB9K,^JKCIJV܊ÐG/G҅}+f4ו[&}X䴛kvyq!=ER\x4#PxVSi2[Eۆ+_G2-\ABK&>Vʇǁ۠Gڷ{ BۦG8#[2i8TKt%D)H<:LřebXReJMoX-՚9#9HJrNA\rJp3K)R^}z^~Lw*\YIay#Q[rVsnp+fi$]Xx]1W74xC\Ŀ za; m⤵g(G(ӚG9?ި#61fL0OJԋ2^ҺŎ(V%q,c]X~ޗvEo\nֶyڬKa6MLcI2 n")]rZ>HO͆\RGPݺBǩ9[F&c)^)Xc)vea0E{☱9aJF M 7?Fw,uVg{۵z/X~q4t8XIMI35tdxxg'+ɮ=g/+tމn90jM[J"D|pM5&%4$)hVkې3֬2&j|iJL׵Oj-.J݊a$b+Sqw9戮q5;wMcH KZZ (n])8޴&Fe50$Z#5E淤`SMLmf$9˹I*jۚ!浉hEnjU~j)9cezҟG+64br? |JϾ\[+ϿJ_m;b)%$Rj5Oٱ,trjV`cimd6ٲ?;֤+ϝ+33&&w r*]:,m & ҪK4q[BZe'<轅S.`[|c8CW-V:L5!FbI$>B*^5 q9c5JuwE%PeHFtu:ǥI[օ8YJvVD^+΢ʸ+Ttn}*M+9NuMI#7XoVFi$Em,[:rv_pq[|ƨM)E -4݃[Y& ;JMU*J pK㪜[KX|҅mX(_s۬mb#*ڵs?$V4\ iJz Ս*Iguo5-n[~a\' jLc4^lMw`aW_25|Ym_r9Ά1Ìg-X;GFEd u95>x"H)γq2,rV!"6:V$ ųf6xnET-A{S^p=-:R4{VP>xqV'+䕤bg[% A^ҧq\y $?کhpCFW? ֝x'JZXyIrU3ME}ri,^J|J'[놸@kv>EFndiM&G&dZH#U'`TmbIt* `)cFl]3IVX``T^0[4  H*2@Y9%XYt^|6 ~[bbaںfT<2Â?QMbZzŠF@Et`rQHuθ eQ$̋tnUkPa׀+Duܔnie'5<x>-#m^kZхEiZD j.Nֲ֎kaְSH@fSɧ2qҵRIع} zI>\qE t8a,#rYsXԢJFyo,w`{EqzKG 8W8a"H#S3b4`kRI[PwLNHU=kiooi$IH 8#q^8- w:Tf1mӃvP3jŁe[;P ;qs2)2;uɭ+ej欩k8G;D W[b_kiw& vU)^!Oj=̄gLM"tvjZgGTuu1˓gڦIB]̫GH f cڿ{h^vιiXNk8!;#G+]8U,xcGzt#FOLtW>"6wF5UёR$R rk ϭkA-(?qpk$]?\YL);,w5+1c+~k&So%\WcqGh}|kxI,ͺg.ǹ5Z*W&Qgueu F[gܣ Ր7"7R6VsĪwH==kKԅx[\5؇ xUM`kD,pk_WkXDqZ-%YG):Nah+ŤKaMLvT"('*ih5c<,JT^V 93v[#l̸n!W_0F1ަ^I}HI!k}!KkeEZpa&fUoMycMHZD]j,DRO ln:gߥfݭsi06 퍫GLtM/!дQ٤^t b$ǚl6Nr If*}A1@/C,4Tfeg?+=fߖa?t374Ѫf%o1.=4}sRfdR̋& * c+ 𗇇M.EX鏅#N?MO箛Ʋ# !''=QPivάרeXbp~Y|'CvB9-&5Lҳ4;huW-:AFy$*G\$ro& 4+v\ʪ00AKJuAךFHb*DQY"(M$cqVvP/)[;4 Tww \ӹHz >xG-vJ&լ>TRiqT2QQE&p 21O6ޢY<::,i'tCy.7m- ءoo4Z76iЁ : g4QJ'էإL ~sFGKUeڊ+)A9]4jhKIm\tָK{fbIv-V$4$ [q"$2v75>T1d/ldm̉w SiXG<2",23T8;Hn*(E +yןcF׈tcIJE*R:e–my,Xv=C`-Br팓ۥYLbU 4X[~yeΏau4 ! A%zby"qnY$9t ~4Q@tv)F*vwj&,ڽ2zEx4n|玣5o6pHr3֊+i;\ɔQ-d$kM~%\"H!GaEJA$\K2I @PHZƯpW>(TRؤ8>ѢX ;6Bp(Q Gn'; 皩Z.jM͢d3E\xwJ{P/2kc$tϽ[Ob)(χKP =TGojgcka=ŶLQ@9|3gH94Q\ǩn2EVQ&H)(!QEZWtQVfotoxx-20.08/images/bookmarks1.jpg000066400000000000000000002071661362435004500171100ustar00rootroot00000000000000JFIFExifMM*V^(1fixgnome-screenshot0230-0100Fotoxx:retouch_combo|resize| Fotoxx:sharpen| 5http://ns.adobe.com/xap/1.0/ 308 676 C     C   d" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?=)?3\ƹ=>SZ)Yk9e@T;uW|uzE姇:}"]&X'XFW1E艻ڶ4ռFuOYZٗwgȝHOH56G&x^j[3CzU`yq]?XӼ{n}kߞְ>-BǓJeVhrzߎ~OQti:V&ngWL3"_woPyњw~|>>}k\FkGwx0mJx;{O͝95}vILi"k j`zvhhdI#7ua]|֝&{;H4f{;G Oocsh,?A4ג>GMAt*QS(&<# R@Y#3K."04V]Z#;N'yǽFʭ|WIa.7.l 3 $67!_';P>c&3o꺦hsks!q,_~(fL9IVV-3Z5 k"yk.-,r:7?uuy?Zz%qhkP{K6;gyQ\7" nx멲ռ/xHl&TeC֠4`Hd?Ͻ 3\.ss{[yGyoZD0Io('֯[V'4;KHs}m$'f%dq܊jx@ԴKŷ?vC[]7";Ʊ:Ց#YWfmC 0ٔ>7:3\ũڭwro- ƙD|6yj?~W::[+u3ve[g((ȮjO9m*4ԯW]ZK8M/}W)aZkk 4_پN5^5[e؍K}q֫COӢΣ{ n.!hmk3ZhR?)ӭzhy֧C; ;Vƒ[mpc˘y ǿuh֞8𽞛XONɪN0߽13^YmGI>o<+XkPͯH7]!Q?؉ƻ?<5MŅ͓ӹ')"Yv#3Ś^UuqFI]]vR H{o.1x6K#{G#@Ȭ ;?j.nS->ơ}m1캽WF;ucj߅{[5五Y'}6P>c&:|S|gs?K{O~=#ilWhzi뗷Yk7wZͨu \BvdW}ցis\tA2jQȊiva?f\]s?RMN\8rGszʰ& ;w˷@4fo}6/wA."Zzy/s'C΍}Im/Ơח<";.!= ;y5=c4=rQAo>s<4!UTUK9ti5K5X)d~]t@55-khql_ȓNMɳzWǏti`iPPheH#-iq߾24WWź7ÍSLm=G]ԯ-# zԶMd{i'1f?i=4;V[Œ3X1(zG] Cy5 Ѡx mgC0i:i6f9u?9 @߾9B3Kⵟ -Uny#ePwDO.ftL!m ]USKv-e-%荼2I(\g| OҼ={aoa&{m4F%fP]zg߳< ӭ=uA=Uy&tI.ݦ(yS܁|cS⎈+;PׅSRӬm ~eG8vوZOORWOG4,nޘZ7 =76Lp~?C=+/%ϋ}wN0 c1q~Լi7u;{5͏mv Yy޹_LW|y6ǭ 5ܺnm]IC?J|Q=Z/t[{JҵI-hDEyD|po,i2MzN?7uԳJ("NU P9_һ KyESZhzW|EiL\*yV@3\>$x_^1Gl<5{irY]Hظ7ۡy# %O#`t_:]G%cΣkv=ݼ0 0[ r"rzo/ j~ Nk:l^O,9 #K˱Yc{ÿ$]1NJ쵥*C>(|Ukki׶vBRZWv~b~˞)nu+S~}u[M4+bf9뎟MQP>mӿfOoDU4}V;Q`}[q3<)NΕi_z]5Y5cgGf_zZmBKM9fj& OeAu*YYYg$ѿ'ߣ- `tY~j)㯀ڕms5؛Sm{DO1Kk'{zKH්x$1(vU]hO1F()i]i7 fܶ+Vc%c,VZ?Pڕuo|soC!mGIIm@kZ/^+ԯ=;ZCxVkRX!Ay-Pp5\JzkhiSyuIB4}/ gAWwMKHh_SѮu-_U +f%/:e'ѹLJkߏ5 B Njv\:#}4<0$0u6wfoHմm6mf=>Uf? kq\h3hјI4q?m6Prڏ_F4w],R}Jͤ7aVtp Da"0NI&OxQ?_cӵ j0+ 6bmsCT .O8 kأH]|۳jnwqU/A]Xשּׂ|@K[P|Md-8F';9u4{ɬ;t 1GEvүh4?-6O'W \u-ЬLX)ǭAF_ķo  =QR)I,lΈ豪Č6mzngVWլMu}b#6Iٿ^<_?l&-JPաU;׳ #hC ?$fKo*IzGt|59 ז'+N;'ng ~T|Ei/n dd, <7 pas/*Ŀlu,5o\jzD7PeAfet ^|SyouVʖ2^W5\lM 34zq%M~^%fѵOx?^XΖU;_f͞ѯ7ط~ϺԖ>@Nn`mcO5%q溫47$~yZ|@ֵ@w_Ql?٬SǻI%r6k/':<-%1U³y.;Y]kA ɢզaR'MIߊ]h::]k+íjI -iI[U!w%rwoxX?-Oi>!9$.mA`sϷ]O<7UuiEyX[+e}GvcGMkToPwN|@ȬY#UN%v̛:{W/ŵ;Jt#$'ޖ| 4k^ ~]WLڥuy?oetjG"3:uiyu>K>O'K9,{w23X*|E:޽jfPN}Rd$$^NVfqR'q .2m s:gu}~xφK=CŞ$HoPE~e1e5:_=ςw-S]6g,u>"o$ucJKvv!aj~,Sqs`e*_)b? ^úeZYM Oң ŨLA̿?Z w_ֿO_о.O=Ig[Zi$i.;#5;uF=3LHWԮ0" |!㎸='Ykeq۝TQSAlJ£lzN:\V62B;n_+Z>'7ĚU#\v-x*{HKs^ۤMm:K3  m95Qk(m~OmĘnTl[gk_~͡gD̬ Ȭm&oqR#Ⱦ4|!:AўKni- ߾kh]3kǨxREb4F6٠RհwxO}^9϶=u-fѴcIlᱵUOy0B'OZ?j~M Yu{sZJ[4&]_o^<Ԥ@{Xg?xzm:VIr(GW7WELmeVvzy`֩(@o~t[-:4VeL+H_s%Y$_=^Ŷ.iitaYKhRCAK}?}~W'cH&dKk;7*ǟ,Jzܟ{em[Y㸄.r| Vq_(x?{-VӯgMYVx{م'$az#xG{OjzQ[_ljHn)L)dra (J4Oɩ~i&10ꍾDƷω:T^ ׼Aqeg d:MՅ&(c!J[-{ͳ^eY#ZUh1REYsEjjNъ-HŠ(b(X>Yu%4B3/Jo4?xW5M;GNL*M|yc/)eXFWoU ^x/uoxFf-ͮKm { We¿./-Jc%&2E*9G?ݩ.x9-HhoDpl1^kz\x:zu༚7:9'A2v;+#ZZ5OA MvlXC@y7dH g|ln5?znyP?5O)cx[Y<1k}m3@ VqlsfWvGj^"#7=_[&UGQmWi+C4|o&$vZ!x㳐eS2?Ȼg3~9bnan`>BܥÕ)m+m:ƹS|sصll=.C$1[Da*³!3 >>oS[o4MWVF[,=ͪ]\I|ha4O㔳5 _Z:&Yk-6f4DRV?:kܖz|7ZɥLcb.A)̬- w1ZZzXZj^^K5l'#t@0G^Qok>k]K|bPe,&(@Oهm{KxP焼a/}jS<#?Io#!2LL lc\+ȧz#Ƈ{^3< 4z+]Ӯbjv҄ f`1Y5Sm:M?c:U:W(Rܟ~1}L/eu]BFɑL,:|GIW<4xŗ2\è ~v$D]cFkFG<e=Ɵ 65;E_7[n~}}#:/nO}Bk&{t+v(#5" 5=յx|JHT[x- xo3M'}A&|z=xw|Q 2] 7O3PgɁ?@Q(O 2KoVtH~8K$/vSnBԚosxEKa3K%ė VCĜK1IWwxzg|1i{x}7e}G2ofGs$q7{.jH־x7m2d8U'#4ßV,kairsjſcyDlGj|?znǩϯ_^ bK+/uHGbTk:$ײ\4 Y-Α wQ#]%ãmoD?|c_-+B5=7|ˏ-"g!2j%_qxFm jw_jW:ߠa7y\"ڝ]'U7MOM[J RC*Ȳ+9'+F]k [V-}%c[:ZXKi8H;7:b^ Se.cxė5sy)KB6y?DC/f7Ɩ[wD6KHu&߇^ uT/sQY),RG*K3>Z* N+hzo+c K׉d۪ۣLY=EVUg_gKÿ6=Zxxl D6~ gO +;{BhgP]etu]gSxH5ƊM֛浩Mq+I2<$k\W[B 6R Z[W]ĺx‹ɦՃoVH>`p0/~YṼY նE=F)D[xaa?&|vfᯁ|5 rOa-et^\OͲfv[x(\*q\΅mKĿ59W~IkgskhLg+ w#zrz/>5;TկU7Z_=lvo? MS%;{? #L&ӡ.ͽޣsvA5ANb |" JWn S"7U-*`ӭx4O|45jZJx˨^Wl7~y[o({+~\j 4W֤R_:)d粬bO.W_P H2mc%cً+fJM5|.Aw>VJo=TB)v Dko@k%Yd̹i$iWuھ5-WPl:}ӏ:G>Tck?J{yO_[꭪iŅՄs/y!),a&_q?ŗVZYX=z['Kk!v3~,)?Ğ#5sj_麕nR&Xyc>sʟ1=g|aZźt]a|96mUkig$ss lI?ZZN1JD- KABbR@ Z(Ÿ 9'[PǭPXwK5/>L$!FVi={ſt 2[{Nuh]J]=Dh6?)~E& ?zuՖZ}Γg^_B mnxiwI -߻;+?L-t=R{i/t-QZ^,6߻o7F?J|m=3cOXihcy ' uFW??:G M?ƻO!.Um*CS$_<'&lfo>|V񿉵Pvk" :͔cͽĸ`unj>;Ҽwx^Kk{Y]_Ep|Ks}6z̕z^k{kZfnefdXcxH5+#{|5kvz[ UoǴKxܹlqbgKGdO7yrRxoZ?Ce=U"O5w_|[?ܙn7ֶr堂HVI7o%.qxwW5łvЕ>|2]+}k|Whh g-ԯyro%Y̌Ċ>qTֶ!=.t^|qjrx\*#ڿ|yg]xO FGlh7y_yBfštaDSDZ6z_5XtkZUu ?!F"?ʮH>ga7: Co"lX5;QX) at$^'AikUӴ5E /X[Kkob!ˆ'{pxhjsa).mgnU甁VDbDWEm/NԼyz[.MMoJG/8>er?+yEԴ-3NK7wFTf  +;COxS&ዽZ<Ӷuǝ_2UlxM>GIgCwZC;)/t1/fvxK Fa7qf~z$3⻯^)mu.o-oRYgfC"V7O5_VkMӬu?_Yk)-Eۉi#̲Dcʟˠ>4ڞ໘<䭅q4vxrEL|- plO3~f?ֱ,_ƚ߄OTծ,7yh谢Ek(kx=9w>'uT4Өɨ #4a'<1ͳuQ/!~@ǀ_xխ,-Kg_ b/'¿x;PtO[LEy_u4 d,^Gy$;W|`rZ@ k@]N0\%C$ #7Aᯇ'hL]Q|bŌ텍8 N '3B} {lDsYαBi;w.>!t1gO鍲"Gm1ooE,cMߑ+ox O [=š֭E5Ŏ+\s͚|xZjQ+N9ld ~gcGS-?|Ye{wxOԠsz=OioD}^մ2SX䵽76<*K >?|;Tkk%6[a?ѿ埥Aៅ4|7|// {fn$"9٥*Į"95Q%sy6zEljaaIci hM{x3Jh'B"m7:U{o~<Ķ_\iQ(5xmKI7UKVMyOY밽Ccs8g[vWYȯEٳ^ǐ|EmW63`wd%t#z3 o^jWR-R؉t)SKbIϸN\l_o~~g.XK7yl)nLmnQdB~n K*<'W՚-RV+GS䉾7!"֗~&]r] UѮ# i5 9C!~C~<֌e{隝#g֑bdw8$*_?#x79>2_tfVk8p=)._֥y#h"-R 2L9 7L*hoO;Yd.mUt5 F}֫|iI<047EH֓SM׮峷A4{|ᙕ)~>'Ҵ>X)LcYYY܉!g>cđ&:Qgj|% vDE|RV+lQ*|r.{YMӠ|QD9d'5_ _Aη'ٓO`fi8`Y"U2'5?/KxsמW&Oټ&|I介v4̑G[!/>?m74˛pd;d!v+5ivwvd6t]r-sezUHC{5k\ZٮaBeB>jO>' H%iYWB9~?is|.4MmSm5"+,&c@wSׅe;?h764[Ŷm$I"}Wm?_G]5V}5I_GBvDݿ6.𷍴xU]?^#jMi!Zo%=?oˠb/w,(%N|߭K\f-JRmuۣ4o1ݢB<+;x>]Nk..vEL~+^-Vw]𲋍]t|vv|D vΩ xJ{Ecu<'γFxuVۉ.",oDWϿ <oPM.?MvIn&3@$W|CA䵵޴ WXJ"mTdd>[~j6"iou}RQ[ݬDcȈߡ>~ K xc\ ͝䘲M2_<Y#*6L'OZu=JhV^֎4Mi>֌ImQ2K P}ҖI$n' QEQEx 9'[PǭPXwSŷ7g#__I56I87V`qm1k\þ7?u ?/URvn8?Γ~,E~f// :ak=Gم;9s]'sZ-ހD݇Լ7ǩ@H$/7? f~|3𯂮n/4翸#ukx#;v _%xXc\м,uY,}lϞе9Fcpxb|$GGZ?Rnuu5ln`-2&,`'W|F CVݧտgBCj&ݕ=ɥ=;-N&]V!ԛ2H6d$(SPE{mwJ-wPcu劯}~^5JU-e$MrG}~ӵ+]]ΟiogW%bUc!t6vºuZJԡk{Cx.JzT|ϙ4*Ko6kGsOc\[ !m|GFW|r-Ͷ4FkuD]̖шfσ\k v7PO& _w~~KZY[[sZ|TgU\]ZrL̊O<~@KO77<&t3MӮ-`5Y-o/|mfIgc9p ~ ~7^ VMKV5!d7f"Rշ~T;y%nO柪NkC%-gȥY<a t++8\y|=s]XIswIZ2c@䟻]M)S^iJZ ( ( (s/6x~?iz&gh/yIǤ ׾V3jZEѱSq~q<xkSJn4ٮc oQ4c9,7V-UmB_0Ea,1Jc1y _)8c\uM8o7Yh م&Y|%+5>֘Z\_o9k B?vS).ḁb`4&x ~/0gO:O.<1e}-3R H-!O, h|:T[mk\1ȪwduOCq[bK8uiKM,Hw}ù5𖟠hφdk)oO\2 @U Ɠ[MZJj`-qy@MF45h?/Q!xfUVFmwgد?䮯_wkQk7=庬1è]s'OYV6oʪ-c0Q!2*!> c~9[u_ {k4A+ 1iyvm|]b:|I,3L.$[=ۤ9J|[W)= x9,쵍d;\].*b,8m~ x_L5 ._%Ha_oY;GχPЬw:.kI,ɰ$gvmS(+OWo ZCX_xh2-mC~Ti\2NDs&xR?<?4%hXui#Yl7yLvxcT]K>millJoΑ6ʹ7O_M_Út5]3/hWw|zԾ \*ʺEmvd_, #oxwh擨LPk nhBXq#=VbZ=|YCk-NqCI%os^;Tkާm3$[F+؜'ʫ'|>~",e򣺹!IKP;lN:7zl,++ԭn J҉!p~Z~9NJ|?GP^+7kj'"a ;w><ڻ9; ϚeRM[OTMƓga'5KPg-DC"G(6}pS?123jFе:%3"W34n+96:N 6ԖRʊ;;XM\KDoxET}ѽx(WBܕ:_zeچ E&Kk*㑃nmS?i>"ٴiui-M<[04/q_ ~/^Mz}ލ<" J~}yݳo)ᙼNu$b[KȢHVf8g Tx%uڊeTqŸ0/ 5Ƨ⻏iދhW\Ga|׶ Y%1BO XO_A[;YcMC 'ͩ %՝z?BuFKftr$I"Vgx=j헞|@ P[o 76[H_k/fߤd)}Jun~ؑq2;oκ xb/Gw-E AxNq}~u]mu5e!y *DӶoG5б|Cm׈u[I4rLrsD}I[ݷn}CE49.};]}kPyn`6{sZ嗇tXҟ~q0MCnj*nt OGhB9(ll?|Sk=V`3tY㿋MR&}Ne+xeb&b[? rhC&s]'߮\5R6 ϕq$Ixx|s|!دm|;5QS.1@ţxOE庴B꺑*y_kԾhzŝW kMJ=Xl.Vڋ[59uɾK) _A ƝkC68oj3kAbElnfZ[8=?q׆`Mu-6Knw:8.']v ^iZ6څ(,m]*T/(ٲYH HM,$ yx}@MY!mjZUj~27!>gJ߉Zzt,ԭa6"w»! ߿Ֆ=+spZͼ_( M۾)ynR|s^mc&ahvδcxᑣXA(GucǯtcNӟO0%9`>ᯎ k|AcxuZV׷;b7}aoj<k7-;^.]N'%M=ۓ<>~̛X:8xzFI}o}ְ>q%vy>'k~U}yt]KM."Q6\lAiuk Bz#x~t͟7'Os>xv2qh7UmbZKߵS6475O} ԭ\}ЦE*O:8]wr17_ RҠkb""Po$uX*?hjzƽi6nXZjZޤAD7).SWw^Elj9nl EYe= u\V/LYaGyJm#VO:F͙̍xp'eīh^(ү&.;T:9}H>-OI6o*)5m&٤ +t_u՞i [Ů&n[w'(  BAoj~(ҿu ]jO[CSὮx5O Jy6_dm,?&{Zt 'O^`9%hd$Gӥo|G5OZxoOt뻍99m#k&1 n>vMϻG,_k SmcI'7Of-oVwZ_m'{#:7nc2q# |wjZ^,ѦִNQZF3mѯO(Ӧyaa!a"6o-}Ǿ ~ tLЖQnos43q.q_KR FsD6 *FX"" 2lF7$iVwWPlMl͐pkM~4x P_xp&3hy[ft"[^g˚gſ_?csI\Dڳjc U ?Kq+Wm'Z:.u4zZlo*'R̛|̓;s_L };ǺDi˩\i:M=#X>x/T4߈:.R5 In(y%ʇV#_?|At;?muӈ] AZ VC>ئm~CT߯Kdx'mg=.>TV oWj>5/h:ytsAۇ{5oh40*wpj7̈>-UAn|ĺ>>֡Ч]j f=,6ѱ\IZ/Q|O{yㆰ^;xzSIc㏊^"">=\- ZXKh8.sqk6ך-2actd VcݙGT7&< i^g{Mo`͞vYcOw?Qlsx7^񇈼!/ ^jZ~9ozL7жI2'A (|Td[ PWf]if4 /?>8sOd[rM_^$е5lh}^J`8I6* T $~#G+QJ:)W.=W%RF}ZB:9K Z%L^[Lyd=4os߉.ni^罐%h[>`~Һzi773W3Xh1Zy$WlP^ \"{^wZmyuh3H#'t\c䯈L欄 LjHZĽ*,o?Z#<?xwkmBUѡK7HoywJ#y)txI֬+ڂʑH6?xL48ڪZ+ O5w7b4) Dx-ğh &֣ߋ5T ӣKO,D=I1>-<)n+קO%_)%?wg,|5k>sYI^Ef/淐!8}~N+ؿ9:W=UG~[V2;9#},OC}ھk^ 5)-b@*/ɶ)x|Px+ f|Jcmj}8@wn#+~5!i^.IJM杣Y/tV;#:7`ݰꞤ-5_xQɣFmGNKn3*:\.;|&͟Vω>*͵8.?m957ͱ x/jO[Ok]5vo4+]^Zkt{ bX.mYb?xPQzl~5^YtU'~ƶُ*8r^ExMa}>(u6%Ԥ&btH K#{wl/$<[[mA)ff>kS~! v6ڼq#n@e {ĨnrkէglF#uH"MIt`~˿\MɇVEҡsn&\},?&O!r>׊gwnX]G_2Mx]|͙?OѴ*uáYx+cq61[c|Q_si].|0-oF77jG1>9.#P:=1^|Q?"ΡvjMRTtIG˽6=h]?ElĩkkSK_[[X@z*ᥦpiz/l4E,%HHnjNm 3'4{/ I{9&k\-L/ڑ#|ր W>+XKRbR@Q@pC %wux 1E?xJvMmwG5ΠS}nXC' Iֻ.xU}׷zx_Pcu|O l MtM7J#16rɣ_oߊϋږv|9/uԉ1yf|}m4'Z^iKι7%CȫfsI+4>>_rS߇Fan,Ii'-~V/~ ?5o亦oXZe̳э=ӗA>Og5B|4 ا4\9gUXfsU߃"|ic]RVu[)Cɽ{/Z M'iT,tImv>_2I4ݎKĿ2HO?WUzabHBYwmt޷|?_楡3!$+!ʷ5*>|ҽ!_x8Ia0<ͻSV].t>{i56(}|,6lR/~Uuڽg?$%.%3]"b`B ٸoR|HYOx~(kk+ ZP.4=hƆ ZU: ~&x^As}wDP D6$w3#/Di6x_k/ }_@<>u(S3Yym#'B6lQ^:5x3NИx~E59$y#<)$lQmS<ʇ`}/#r,$L]N]$I##C" 1O-1P{.ZBI% wV]­fLtZgŮgn׿mՆY]"\>zW>S~%Qn֚:7̋$ȈkXmo7uߋNw[M?CVT[g@[dͽ r]>gm/hyiso9`<#sn?_XF*zX>yW#nNjտ\FLf;g"0#~՗~^#Ӽi3Oɨ]x2é2v]]M'S>YY?'ܯ3֗?i\j{KWn}mQh\[l,wZk_|vkiPh:_d4PYUQo*zcĶҭu2Fq<";H@O @J? 4;Rk0ߢEkalz~.,|swͩ6RG #fFěe cUv-Itk{qݮ.@ۥdTw\O?->.[uMwN~YdJšO*8 h7㊵LmGƙiK-t58.Im̑6K!Rɰ9vO5; o3x\״ : Ri&kDa{DUGԹNqGO >/Ahm[^8Ṏ@vq@? ~΃ h^gѮ.F Gڙ$H~͏7Y7__ Xj λYiJ_%vBkZ`U;5hg?@,low:ڲAޒdzptG)*%;xhhv'&y2!/evIPk_mZ.m4_Yiˡܙq1B]Gx8|iqᙢnos-SqxX1W`y3ֆZ>2f|1{;emԷ("h|dt5%}<-A HL55 # :$(|`WuēUJn'PHIlŚFա5I!M-d9^;wlb>t85ߴ_.;'eethC$&ruNp=S]񗈴%me丌[ f/5W OhZO_Vm X3Cd[_y.4F^9=kԼGccuv:YhztɘD1o6Slk +|Gxw]Ƃ-M~,Vp>w?o>gC5{;i6JЫ2Cm1d5CS~$_;74X81ڪytv8OڏLԥRYXX>'y5- wYn.%P;0K'6'Wt_ie߈G%y2I$FTv'Zߩ*7z޽q z6Ty.2imm<ұOSqChV]e~pd<EcźZzC&\MmYfxXw_h> ~ Sz~4tqեdlؘ"y`;Sɭ|m=gš K]ՖxT;]6&HwQ>bjcåQwq\`G5Fg&wr|ּIggxsZ[+ƾњ&fp'>_|u[Ǎn9{mrM:}FKٚN<,{'4O1lt6J\]&űhI:O^\HOշ/xC]> Rxk[{Yyg6VGį5KEƛ ϬjH:啍o#?=k7KSAxP<jRK;FX1IxM9u_/w? ⟆ }-"}A5)fZr{rsqyk=4oIMVMNMXg'e}_'z2ԼyOU9ơ{iGG̈ahot9E^t3z>'j7æcj6%Ģ#) 帟p|䍿S$#_J]zt!{uf$ OF~rk7H#z~`[i"tޔB\BO]W4V/o "xCLt |V6l.eydٸ'OD/|+uxSy3TH2~oW_o<<9>X=M)h[rDf6OY^x5'O𦓠/.#Y%yXo*R>"tsyZl\Bh`P˳Ūi~5o"FHAzu5I5/%ot&WŚG h!֢K{#9G"1W1d$OXxe<7k7z ֣i'I[L[C1DbS+{uq6=6^\K bTVpwkMGy? |*λk`Z3J㻒x^NwS_zok_fFtE!|ryP2Excw յ]?ZkjQjn, a>䉲VcWze̺7ltu0Y]l.Vc(b)~P6ڧ+]/VHsN/|_-֟nZc:'Z '_Ibuw\P{ #0ꉝ#E+GFw1寮|O;5̫%AE[ [ǖOa\i^kQ¤s"M co=#wRn,~iW2ڻ.@զ0je[~`7hz485WG5wDM:?QO zffKD@ 糋LM|y> \'jGKiu=K:ٺ[I#˩s{FAiyvֱs)"󦫷zz>׾ :Nj[n-Jv<~Sx|4idT2#fXXDq<)+w Zi|j.tZO5^GhBۜܟݔLo![Qt5/_h FH7<}èc{}mgO[iEϪ\_0al#rv~~k_MK^5f$MPk|V6Ȏ~WB6c>M]sþ(\vN2\d%ПP[Hi9.&~]{W?};smxoNSGSE%7E&?57GUO)AZ^kii, llVi*4 :ZོB EQD@~!ڳ~К؟iB3FV$Yv>Ik[ n,t[3\DsF6HN~y1b>JgxRxK$z֫˹]ot9G_v˯ ִ='Na?> qט}kß<%ym2 KΓiu3s# w!Ad{Qg/kug≤muH5O6Z;cHcH jVp=+[\k[Ŧep(۽%ir>m Fmj5𙰳e[넸~ÔAߥav6>'hxPt ڄo2ݴi I u:Qw[Z"5ïE Q{MK#ȏ+JH(95]iP @ jn/1Zyu嚽ׄ?|zR eZ é\\uI4YZG:D"<? (|¾3U ڎa#/J-QEQEQEx 9'[PǭPXwMmYh_hthZ=^6PU+k{Q<(+Llgp~S,o= YYG_o[?agФU +|q>kxjMt-7H[yuRh* Tm H2sֻt}^ѡ}FO8Jbݸ.FZ/:TӼujzJiiq=fm1HΩ*w.cz;͏»k{KC5+YUHYs-r|h^)e5Vxlt_]ڋEw{NW`?w#WPӣo^e~D߹}7kˋ;I쭴)Ԓ`*( %x?xP4mJ-kό/Hu*ɿ{8cK}g ku(+t2ެZѴHltOLhNk>֖ڪˡ-弱8/ yUlX/tڗ/icU'n,cM]]>E]?mc4ЀJ:4R{؀_05_h"F|?sԖ홆{I$G N7?ZgcjQ%%7RY$XȎ#>buO,rjƙZt[/x]C\IQXXmޱ,;fI"tl)K׭;ZRg)Z&?ey]x@k;LD6 ~_޼ixvMKӗA4+`i)aGL|eO;O<=>yGۮ&)gTݸ ^iGIgCϭ_hxLVOwcfkx"gr/֪j>c{xL[F]JHxhlQ'nMHy~'μ>0M:Ǐ/ӮE,mIQ o!JkO J@n F)! W';}1>/Kپ̀ 35}shJ\x˅>S"EOs3P77|S[xTx>Qҵm8ͪ_66 >k]2wXܑ~9'x?o-Z^_ P/4]؅E#Z Bx5(eHb#*ԖtB,DeO?wQ^1>^.o[g'Ho/xIk; [j16=/ 3Dgm;72jD$ᘮ4ĚLsk iѵ{WʸjZvkdj_jZX[o-%|z"-y/xľ ծ-=*YEyeN2˃'޻ hơOwJ@",Z[h.ſACSu˛wHEI_XLU,I ~Dx{H^$Z{1uvӧtW֮IsZf,j/Ϛn(r㝟ii5 YM{XKMXiVFϲoJq{>, iVWPZ.d$8Hե˗tN~+m/VɊۉny $ 7&_O,4GMdڄos^4U(3O_Wɤ^x@pֶ9Ok!C_JIiKieNVq*ayF'͟5w? g5s42D,+|-ĺ,(oabNuDw;K9t$ (OWo:>mtta%?̢pgAZvuW4;>58FY$Rt3 m<ũ[Oqgs{hcP +۾~`GGm;íykycqFm.7{湯]zm"U5RFXۤQ$xFc< J);m¯ ]kje3\ɥZoysvҹKWLj.|=wӥ[-'WXcidl}'t<-㯄8ޘ#4K Q#C,:!-j|jvSjzl:qѼ?è[ ؁<ߙL|iW>եӵ R+u\>t" cuI4kM~kk \1=*/ jmw<:&hʜH<#\~30 >xGHВ+MZYnLh#D22Dw=/'xͣVVt9WqK݆}1]|- M[o>!՗Udk5nHfGKR Z@QEQEQEx 9'[PǭPXw{]ԼE8: Ð{{iF-Ŀq"]z5)SšOzLLo^5͜ϦiCxwKu}*II6ʮFr|0+lw;ڗ,|:塷+6mgN~TN|U^j#f6pi[FBj;ɏ'Y'X+oM>t)OkF`DJnw^{U?*k<[(;;=M,HfG"I>_fؠ!T58]}_X@S,șskZAk"׵I5}Z {NnۼB:#4u?8jo_oƛztKHhV s4-v$sYE [u7:=jHx!?>σw WRֵ=VO82aPI0uuAԅޟkw3β7~\Q멃?ĭkB񅟂[>, iQjw Ti-'g^PU>xj:~+x$I)K }9~ (No,#Z"G0wn#unvwn-^Rڼ;YUU@9P~zI֫.wa=ob쉤I=}>0xOsh>Ѵ%kGv"wm~nm7{'|L\li~|Jó>K{cw(}":tkXBk-^ɠ{;'Y _+hv*`bўYJ(iy"pd˝Ww34@Aw3g$n?qX E-c5ŔO3ew$U{~u,^|(Z^>$qammJObwvV_K<*4o jEa 𵤖J*pV >x|S;g/Q5N!GFXG:lT/koO \xjk)/ W[ī#NnJ7t]*oxRH5k׳nuiBeg3J.3,> ;J\_FzborW"4;}C~$5>ᮬ|At whZ/)fe([BMwi F#E%̱T#HRRQE _?x#įi$ҴQܤwLBe0w1ZM:|߼,O*oXW 1{},6Z=՝] V&9@ﻻ_=l p oE?஫Y4}JK={^u)//K5mϛ (޾XÊd}͔QKYa&[T&|g  T?h?&օZ3=+Q) ݞ7CѦ|sxTѵ-7ΛEXek#"hl4;T1a:GNiVZwa4Cfc-9yHdz(c?6+?E_UwV4:+m'm& z>MGk߂o-s\Mhm㳊k ɘ#ĮS.$? C"[8lc÷3Z5Sm_[+BsVϋh4%}s}QDb*#\K-^4ְ}yk渐ߕ?ph%z(Zt.e\Hd(ʐ*XJ>4Z7^SZ(>j]z=-t{F}s]osxni6v1䅄B|WTŽ(Ěgk؅mF-uQbTt&OxO>?4ۭ?PZ@4G%g~hwǜ5DC utNu:$<\ğro ΡkxYŹL$.-,gTR|֩uK}uss:oዦt^_Is~Z5,u- 6KUfE3_|эƢ=j>5mk=}/ )I7콡hz vzhi~$vL,B)X3毡k7~'Ե+WkvZ67Z6dKb&@'~?SfsHo%ohw6\$vöo)4l{q]'#^7czGM#P%yvo| 񦑥:C>kECnHe">֫x~&,|gX`M -8~|C6jbk[;@nWF :}|a9|Q)-- $B8zU+h tk䁭㾉e%h,kwL쵏C▁L3#t -]#ة _&Td=kGЕr_~O jewmW6VW5 )nexYG'rן)dLTծn`ccdbC_%aYm=n;y^X~|9m4׉emByr[÷ʶ"zc GlU%Qj-|Sx{_ Kִ.xm#ʚ -4cw6~pu/h :w,w4x@&l=qӴ\xJUK=f.,."JQ 3MUrcxDĶ6חZ5p]\^4N!Hij5|D<;ʹsx!+qeĭ͑Տ/|x}xS÷:ޮ[{˱)&rx~"+'Rur]fn*zpթ|0A]$]joϪSj<2/*$"8IKyk<K~"׼D)$i WCw`>OSYZ;[+A׏n.Vŋԉ ż*#͍w\|-O@~%dK&FPO&CR]8xoJլtO{7ټ_ɇ7~oWY{ǧ6_\o:f=I.h׾_rG;8ؿq?Eּr|G=RZ$3F,/PcwDZ1DGxgv>J[n]OpX Evlho]wNpjlYQM e;?hmB?W)ѧ:,-WvyqK2Gu,EХ4mE5Koi>ozW77൏ $\t7OH}Hпcо}u]7Mc&yj>MZ.+qr&#d%-qo<$dfq!/o0uWh2'j?$uH[J'T`O,NmVFU6zMIkanʢbaOVw%'{}ƯƯx k·:U@D6s]"u`Ǿ,eecꖗ6R;}gtʉIJWa/_/:ާޮɮğʘn&}76M:o|7E@=-ou=TJ~6MMkj6k,G2itpw=HxF5I !`)ݯޗ>SӦN|?jtdgE pTU_;}Om/>.Q6R)ql,[ɔel_χ^27Oeev;|Ȅ!oּ?4egY>׳붷udg!/)6G`4tʦt !>V?W_#|(5iūj7EīTcz>;&|E=/,w1ٯ88ȏdֽŸ|;IgNOVVWL6GݪiP:ᖲ$Lʕox=]o^97{od:ƍq.ZZ$vk)iXGe]?I^WK.M>%XMUe"*fOM2 H{{䯄m2/?k_yܼv0l@rl2csOoK7tH-ZM #K7OByǾHtMf/PCi$r"+tܫҭiVwckugz$+*UVI] ~/xv_w!Zfv4 C-8fi%Tcb RO"s?-ʩKsClyoۖնFڈiCP7d|y+NWѵ;Ėos^lnemʲuy\^'3}/En49tA/`LϺDDFu_ 7%+|%M/Fl,!i%HCn%䑤v,~;M_{ bamr+*zՙGi/oj:l"hfsM2"г+ف[|UxRff[eoxi>*f)Q7ش/Q-O{|3/+SL4˛Ȅ14RN~|OMcMmmhn#8S-|@<㼷е/K;F'^e$+mlfGy '3\ƯS,'JH4sɠ&ndV4eF}>HM vw7lzл.>5gxK?otݚz\jsCgmr7I7*Yiݒ DmKYu/>;[܆ж9|v"|G_ xMUMJ[C5XEVGd>nS_Ndx-񯡊ٌ2=Ǘߔ*zU/%2|MÏ1ʾ+CJw9Hq^:ቴ7Mfmt4ۛ=*,` .4 N=2j=+VeiBs q]:6"J۶4^hoz 4}] |mAtoUdmm,pfF$sw>c y)woKAAYEErݬhm Sy+ kiW~``$|ī|_3ziڅ໿5&h'1GȉG؈KS<!{M= j}.{˙Ȧx$ fW>!]Y.YFR]V{wpܯF_ ˤKp,SI"aT.5 TLbwͱcy~/M$(TzG RYlQ[k.G:zmRto'g߹>ttL~ 6)Ͷm:ψtkR, ,`|ѕ==͵ jW/l h4NW\L7ךUƗ3Yjڬqe/GqZɊ{5`0MK}x-kIK"0MsZ׃-4jڮft&(z^ ΎSXѯk761_QoËBX_vҾ/|5Ay46+n5 gy*N ų’L>j6R~'M/%A9!o#XO4Vcc*qZ^yo_\DYx'ziJ]IؔlOa5'k>Яt$}JH7*y_-GLޕ^-j5i"֯Hؿq*{7 |3CG,^V$qq3Rj>ͽ@C)75M7W:"^\]OƲ\] iϘ)oWLW_G敧|3uO+Ce2dg`9?.?N_ Ook5Imt{l2_;aʍgw~ ״/ZH9tff02+m"DX߈?OEo,MJKw!nf5}G'ӨxR߈ah%H'K/vhҵZD0O[Z=?l%uVo'lZ7z|API:\:v:n麂Kqs5X[BGZQ(s^<O߇.#.ta4E(ܲ1TA*L~ /_ <[^(]bJti,!Ƶ{W#._+c<@<]@.ӯ?JctF~7-S&v"-WAqsEYnܙ7vJmHn LI^G/zLj|xNxI#"~a;A5Qf˾2^^1/}Y_/io,v_iBgTr|2+:scPx?ėn.6 >l.79 y5,hҖMjZ&XHfKwe۾7M"*-%-PEPEPEPEPEP~Gi7E,kN›} ^%/MS_ 4}GN_Ke]rwUqH|#H ++<$WGX55q%z;OX"1{i"˕}d]~Rxsĺmr+QhlFWIO6c ב, ľƯ/5gTӧ/-ss3 (?j&g]󊷣X<3hYsogq5G+L#6K2֞,otLh!]?Q-yE?4&X/Yi|qo "X<,PϮI )X9$}ߟ?}:sGO"WžƝhz]ڄ/$Wq9Pq|SᯈnwȺ'5r 0y-t:C>|jkZs~rQ#x|`t봟'?1[Z֚V\IVa/'y*]p& >e-CZ]pjW2-#?Yh>(<)/|A4M\ms?$nm/,§%uĀ>p--zM= =4Q[2qB.};>@5xTFMIM2}WR [FI>OPǟෛ̯=𦱫 l0xC|j:ω5jjvM۬,pGwql|g|YaCWWӮ>Tc.o>xt]MjcM%Cy<>\:Nvs֭y[|@Ox@cjUJŖH""^o?:F[⾣px:/Wyk#YWŭc_WA<2Ymqw돷+0ԑmr|_0n kt :!OO+7u0xx^O:Fvٺ^]jKuO5rO/%ډ+8(~|7}*]{]5 F]J0]4;H@Iu@7>b<Cr^M3T4-M9KKľEr+Ҿ xU@mgiDjfh#et>^qw%~!˥ |Oݍݍ-F\ܬI*mcHtuSᇀu}/֟~Uu'GxYBf7fsT?mi!m\iէ}xh܉ pҔ|~g]{zơsz[kw~Սp=-o4,-)TTM7/5W\xƺd:Nܘ|Cw͜)KDe@1?>1n$5-b=.YyQOJ1ھ]NNC95mmY#y+b2n'jQיᇌ5Zvmᤴl$[>L4bG%iL|W>RoQ%[ۈc_hmoUmٯ~$|3=F|1kI۹gI$];K[}maHA\Xɪx.KRHbDhdHEq1O?ԟ?9<+j:-KZV yP6vTD{-G?zW?]] OdȲe?ud2x7_<u=yeɭzsޛf[1ϔsڢ5_ß< 2uM_Pmm2 27t?ECJ@#Oxs"}}+?zW ig%d-<^蹙GW},?j7:G5/qz6,{溿|-u]֞Z5bmKݽ:5XDA*r<<ֹ~c n59Z]G2K.2;- -n4YKh'?n^}NˢI>_IѼ w5WWM63VWS“ ewqSW'Ey>`I%ӭ!; O^W.ž2mv:v cS?ɘ?_?ܥ7*NM}sXAϧP±4{ʇk).MzmkZt ͣ_jwf#"K:o=zwj2 um,(%cfـ݈Wbb(((((((֒M&4#RxA$*7WmNs \_LvwPQγ%ܟz:7ȫ_,5Ox2i5Ś%.K^A0呦S_=L~Z\߾xUuYnu)^+F uoTeڤ3i{Y kyZA٥Zwu=~CG}Io -kY$r3Ҥ/5iK}kx|M>ֺl9eV213s#\\~$Z *>-/e4ح4t;oal:qVx^7񮞚؅97s\B2$NG2 k0C6ݽŴQDT.ԙf^ޫXzoI4oSTZU֖0ۛw+%|5/,.q.͍?؎?K_VD|76\x?T5Њ7bP5Z_x[]_T֡$1ZaZD\`oFֹtO~1kw7|(-S m݀]=0*_|m{(4H#A TgTPS;_ |wh7).; 6C$ʄwk*Dծu]M. S uF#Ps|".i#P;uq$R#aM$Wy6uA'm-uy-PmY.$8V3_CҵI5[oke ˏ69Q\'6S9'^n]k>kxmiP(Ʋec _#siu/Pl,u ^S1|L'5rtKv~%!ꯧ]M]&{BS'cq-8YR_ -/K/ >KOݭ1C6[Ec߹j7hx[E>ck x"2*2uj> C犯Nx }HťCe[{YKg7E6: j,;x (m#APWZbW)?^xT"X[5Tvf<;w[~{so5޼}jM/knq>^; xX{[xoIү-em~[ߔl{a]ŭPK}5f9Q%YQlUxңTghxNźj.!{{h"̷{1dT9p~Oϰn(驢 V i%E sy2c&]"~kC^W_5\tg>%gG1!VooKJ>_^j>3m[I2  <ixC֝z?|9jZE|<2wrݔGt]#[msCg|3l&uI)e,gd¶$E:_cO^x=2mzOSV{".7G}8/ִ\[t:=?Yưu}FN5 _]$~7 lr)Ø=*DžX+uX[,)!Bی1J".$Wm/N>9h5|Saym˩Nm4%'}7c̓? ?KGykz5~sz<*\HTohTC̷G|I%ĿjKCֲ*#?\ψ> ih4_wNٍ6y~\lPҳ>6^Y7?^5O$xp6y'|;o ;kY[gk[Ο3+X$nytVYh-c0)(CWc gNY4uIɼ[_ijk1aE{u!l^o9ym񭅝υ1 :M"*?<(>xkڗbkQeɨ4>I7;qS]σ|&h|6߈Mӵ]V{Ox.CYheTd?3g~/si?޽jE&0 n]N;o| Qrgqcmm"-y`W$ eξ&gO ZַP-R!T]f*|=_3sqMa6\٤G&ْCFW/2:MbKK쥒69Dyo$VD,|e+%] 4cJ?+k.?-Q%iRBO*=?+^'}ޝĶ3Z_G D[U#Z_2^-OJO:r;CW?.*>YԯƷipoahEJ7Ldq^E.brAREPEP\!? Im]pC %LzAE\MOi^ t_irXڸmV++YY@;'\4ou;LGG/ 4R aF_qUa|A&'t)ut56ݹ2N>tt<t=7.I&~ʊ>ui(sIyUjE 2Αfz[0&]mD1eJZ(wK V-+ht;#ʁq)#h:}uepn2~C̿>E02gƏr>c<$KXٟ;Koy؞B?~<1}~$ЉnćYoϻֺZ)@ym j Z5g۲HI U35go "J#H]=QEQEQEQEQEQEGV*K[]LX.T0hee%R4HՒ1/Zx#Þ ZN\߳]* mZs"Ydln%r$ѹ0QyU?Z&uK[S&6DmoӠia+s-Ywĺ_45Ph(I#H"US4h5O]Iwv$:%v-sƩc%{)+iIv)OJ/>÷z ^bosN-_&|R 'o/>6钛X.1vF'C/t[͵s4O,ę $?[R~xÖڕpjQ7Yi0Dq%TO1Ծ?X,4GzV:l ?w.`r<#:4K]Roi{sAnrZ>֟ ,|$k m!ݏ$ֽ?5?Ў,[63rk]-6KK7la6R ~l tZ4ox;Uzt y]5I$G_V]Zx'Þ掶z4E|0pq)/ֺ (ҾKEgt ],Yi eUqlUxOxC.:Dp VJ0sW5RPfvvkԺķa'xD78>Jaᾩjk=ΥYky\ nm9g|LQZ((((sO$C!A?ڀ&=hT|Sgrvw6zuf_qS| Z!/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/ΣDei&r@D͟}E%QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEA{-2M,) {+Gr Ou_ P_؏4U՚iIBM>ϯ?6*>ϯ?6*ߊt^ƺmxMim%frȥ3Χ<]n1EO# 2X}{Mrwgga|c{:geS-tk Q;d/ v^5 zZUH9ru85)M̨w_gg` ]=ڄPa)uo#oOXF5 GooJk\=m#d'G'\޽Dx{WeyxΒ5ҥyK$_9Nt\Cc4BY25?@j_"lT}_"lU<9@z,s>b%k vX+1dRVJ[@'Mi.nUտ굗Ž7S燭,5{.V,֖ӔIL2M/HﺹKv_iw_> Z(,ZJS֒ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (3+:#;^oB*i iO9J\ݦ{ַjC/S~DOנKYtmY!|b:OtgW]&V nD w?K6Z(=hzQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE-%PE4Q@hPFMP3EJ(?fotoxx-20.08/images/bookmarks2.jpg000066400000000000000000002175471362435004500171150ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 430 521 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?g_ |}xkZU[ۢ]$J%(,N^'Wxt{<Ɩ5|Ty+^}ގ`'WOY_j^s_1?bu*Wj,jVWڽWcWOG_ʲGڽ bu*G0XQ'W򬯵{z9Ư_ʏ1?e}ޏ{5|Ty+^}ގ`'WOY_j^s_1?bu*kھOS6_]x[ͶWOf` %+˗cy}=9{] zR =W>&_!nttoCbѳ.lVe5K_j \xRK3JUxSߎs[16mȟmyW|q}vwZY)kh&T1C׌73ZgO@}F;~^=̨F;ήMMugؾbu*G(љ*'_3xsUeLj˻kGWL$Qwl$@9Wiri)nl͵ ;d!=F9I](ݯ붿 5<.2FC)Rٳ⦱YcigsH(MԤ3^@psT?hX2MM,Q#rRɱ(z_ʐJhZwB4G%2wsZŏxKgKc-ŹWYW,R->-o[I G G0XQ'W򬯵{z9Ư_ʏ1?e}ޏ{5|Ty+^}ގ`'WOY_j^s_1?bu*Wj,jqHTdf}ު]܉.s\Б?cWV27-\V!0t=M&g'TcސҦe")b`G㱼ڤ%b61jK{.w:e"#B 53Y;mB`>,B-uV\X$ڑɒgs𬯇u+kC'ٗ;+z|ƻ/ϻҞUy>%um@cI#ӣXJ2`rˆ('/ OE}W3>ӞWG P|uhv?MO$l>w4;_&I4J_l>}Gak?9$)}W{G#y5hsDG]v?M=Al>w4;_&I4J_l>}Gak?9$)}W{G#y5hsDG]v?M=Al>w4;_&I4J_l>}Gak?9$)}םE0kgWD=@}ʺ\v7o^z#y5hv?MNC6/sE8YiUD?f}ӵ 4t%}`wήr=?{G#y5h䕭Z:gQ֓xY9yh'ZkVcÜ氼+ Hyb0-yg̀}z#y5hv?M>I]X<1 {Khr zbO*=1w{sh0w+do;Dz#y5hv?M.I28籚IY vco;bo~[5I,ӠisN_}??{CwHMW[ { dl6X̮ f&4h--K!Db=@Ͻu=4$8 3WtYltm.Ek"\͹Ig_3𯀯дgw:yo3,Ϸ9 ;_&Gak ܥG]v?M= K֏Z??{G$%/Z>}j#y5hv?Mh}ha=4rH9Ra֮;_&Gak K֏Z??{G$%/Z>}j#y5hv?Mh}jBk)ʷ?Gak?9$9aUT,n$m oAZ=4$8OVZϟuY˴ xh #֎åCP1 I8$5;_&GaksD m,[[8eʹ ^4r3Fw$Q(oR{ײ;_&Gakך<}aǷ7Gky۵yusEj?-8_.I*MEn #]t>&^&(m ʬ],G;W'kǭ5]GO syк]zH*Yy{pk˵ZǶ#:姊bhe7D *FR>M;'5ߧG.Zx[Vӥm`7.Yռ/-̢E\d3w&[^VtoX8&p;gBMAmBi${M0<1bEz_vNF;]?*N[#)M+^m6eg;A+J ^LaA_<~5ᶺSh/4t]PkyxY>`qzoEoO-kVAvԀ6hPjq{ɯO;o>MoNɯY>V$i>7lik%_@AUvwqG^x<'\t k7cS<{/ F[k2okׇd_ivGpixż|9r)/_vҺol5g,-l0sPRcrQhM P G#q_77/^xOSjvMǧwObIp H= aqxAW^X5,o,^BP!jmko/x7:)RhHdU q~&I]RV{}&8ѾvA88PI8T~ ։ fku d݄1\|BsY3^1}F .{CtJPplmQ6tύzFGca+p4iG aV#q]?]ϩj[)1[BXċ$\G#}+;X}+]ћZTϭ,$Yv22'D9~g}Cg|c|ƍe1Xddv˫Xzr^v *y_ 5/[SX ΥB@˜?C^u+Aֵk7j/-IOvO/27*<7Iis;O>3]\Em3Q.AnɨG1EPsW|g7ּyz W3}L+D|zS 8qhCmKW־"gP Z "Ef=-w#Z|9&e[`c"v<IJV[dk!YTR3_,> E查uX"mݮm]Ydf< bG }n-Z.l$1[n+\+3Ş6|ውPQtl`u%{Szbx]d{Ȅa<؎Z#w7NmTZ@$>l ^@c'4䒾;onjqx{;JbmDvد0\8UaqVsr߃ǵ|qYxXZ)9Z%UBqYkh7Ou=oo/ Qo$/FH9*^Oþ~>$U]dM|'3YiHAu)VfP!ݒt^!௃|8!ncԄqEڳd}vsH|kF|Ik\i|c[co0܇y{%tgX$fmL`R^bm19|ϧ|#WetI|L&Ӿrc2H;kpwO3Rе|ocuGssm|tI2*oyGyj{燾 x3ewiy 7nƕ*ǜOЎSt_ZFxNHno&5 68 Bk>x[X[w>]YBPI[T˩Z;" @eua#޲U; hj;-&V*4ҬJ$ $z:i7wZ5=x^*MN&p,$d}+~5x6OS\kQil,2KW0ORI=4~k[+w=95:K7[VCL5p)etlKh(8|!=׋,|?4heU3VUv#m'u6֩R}y%M4֛1j2B`c+3G~}u]="+OGYm#ӚA$ʶf=^Z7"y{ O -b^e(Š((((^ ŖWմ o+JvFb Owz|oڇ]^x^`֛m nCN5-(]tZҎw_$2Ȯ+UwLmwNn0Mt4ڄ4=axf/_~9D4up;2ᲀJl |Q>#wCq[`"L;sZ[-oo_V=_u&2C5;2g] J ,&6_ź" YD7k %mM+K)挈-%tw0NW,3W)o]0#iRj^yLbid'q OˡWկ?>j?|'[X_xIw=h~a>67E.e{Rލ-.f$fLQx| |okw](~%w:ËcF[. ^yyY@)yH# 8\x_0ma-54jobK-8 rS5"-C[/WLO4]Hj/}JY-9NO#Wh G>!Լ9kw%)>ƿeH)+KTuMJwZf&4Qg(]2PpXqTKdחRR_cߍ4kiZ׽F1՘pp9 rk6Il׈"Jy@]2>a^'o{Y⵾/xǔ[J}{VVxMXGcKw34V& t9۷3K_0]/jSǂ(ź+_4|܎=ꏇ/{X֯{BL`)θُP;X04ck`)Vܣ'鎕},1Mo\K3V]=;.nbYʻh!xz&ե OQ^AhΥo^f 65ROuO˧Xu_ϪA{CWUz >x]I!}|5s;MkNZ7 "m~n@|:ODcЭlմmI+@| )U!Kq8KTum7Oţ]f8]?#Mwݼn}{<-'y[dwy>pO_?xᏋkԴ ꊺhWIt,%art=I%OCoWj͊:J$YRld}z_K; =imE=m b^B1V…?tX4s4fფ}7AqEhN^$¶NCv`.x/S2O,mn" !i :D@C>1zeMQm>!_JKivy9vNrAqK V{-.RMwK`6Iz+^,}WNi!iE+1R=klS[Y_x{"Rv3.0[ٟ7Ucnݹ9U:B\'7Kx׺%M {\uҹ_t_Zeю}MtR;_,m Kl88= yxH^NVq~ڴ?dpB/K0p ^Oqv=Bf4u8|=黢$?w{/dgjšM#nf~zh|7 ;6ci"G6>fˏ|UEuhzlfUoBTkľ$u:h6a8񿑞qV k3Z/QҚ SUyCP7`rIq,?2NVWvAƱt59K4̌U\ v YD𶡨^XKV{x#gWPrq|/m=FuOLٙɁ]B鰯q+׎.o9=kp-v #ld O$gv'i _VuY402u?7hj<ޓYmkGWY#΁nI"nOz)⼱ѵ-Iuk;(6[۔}|)RAv{c]n7vGZt i6]e0OA7ď %cG"7lts]vJz6v%ЀBH6P̩q$ Ac}k@+[CS󮧂l ?-S`~QܔCn?|-i7^"m'5K7U'O/ŷc{ ~:q'xW^[-A%#|teܑڬxڗ?d]?AtS=hOݖ=3K=gno>-R$Ki!襳xRI--//dkέY9`{^%5׼s+=n_..t@pH0F+A ;'қ⟄ύZ>llx{xR|YA{+%L˨ͳ@Xc&A?k >$\y/  9#P6oIO:;-w<̥S gS߮Ö?&[sK)u8Kɧ4KE,Wvqq 6_~CZJi;潌B:|j#.{麝΁} -]C^Auv_<*yT4+_/ >ҭ-nc %-׃S'O @SO|u;76 A|eSF?? 4Tx>_!N6gaөtr -75;tGϑ%4sո[E@c1Xi:xd59Ql/ٵ;t0O|u;76 @S-suwtf<5uxmZd麄a U%y Z)P}>^Z_Nl3@6ڍ+ 2RA8F;Jl یҤ>,?|'kZ.oI x1)M-?Q'vV]Cg<<5? i^eiHG7-) <^@Ӧk+-ўݜ9\=/fK]~%-՜ X(qXm&d]U Vߙ 򱝾%_3WOMA_ &%b8Y c>:HH~ /Q[^{/tMZ%ulźV$zyQ`]Rm-(o$Q"#xU> ռ_?r]WebK.m,g#W(ƒݷjG^񮽪ؾ1Kzlj?r#Z!AR|/=[$|"W 6 'zWuw\t{;WKaԡI AlǦY¬@0Xߞ*~ל^~|WY~ [^-KC{JWdAKs$HrvFƕ\rmd֠+d0XrsQ xcq (;Z-aMDs5 3Q~1)+4~5OGVo=>N֮PIJ Hܧ%J SěխX_i:Inr1љg_ZOW14sFQ0?-(U5PXJ>q7.߲_tSxG&N0qn+2Cr-$־$F:*P:VUЅ@#sM?ˡk:3K}Fdę1$ A p9*Z%$Bm}{l(C ( ( (ȭgf͓H1wE#@J}jSenēDA9`V| f_Sυ$v9Yikװ^nZie_0Gi<Dn&^NXÏ??/iZf\iVl=oY$X#imVsrm㙊 ?|Q}[5K%win'ʾ8Ink{+6ٵklxڇ~:jM׋>O{ktH "<|$ך>,oå͠Ipoo$3Gyf;5Z"R(aNI$qcVZKdeӣ_012J{gds4qK!fD d sߏZ%^ַ[Zt|MƗ O53S2]Zb̞e±xضH`9j_ ӣY3e0xI=~_{c(9U.w+Mڮmsi`-l#Y5aЎ5u7XNV#+I].[vdCQVv~GͶ+G/sKx@TqMKne?>.FKOkſ|PQ+4֮>N$AFJ;(}Ꮓv(}S1 A9sb(c-BkM`O7 ^9V_z;OgͿ~0YkᇎkZ]Xo$ݵđ.Ŷr&p^ Ӣk7T5kPKyٜr¶F `W4]fM.ΩeC| Av'{P:u\S($0=qD_AGݷoϏà|՟[a^&h\[̂HK7eoqL=Z(5]xsym}~ƫ]#,ʛcۊKSim&| g'Vir[ k@F0U~voF_Q%k{>@[7>.hoiΗix'Oԥ5Wp fH[0ulpkٵ46U .rJaS]^%G#ECڰxk\ٴ5E9ww&rX@aBѯ^>X~_sg>)}+4{ ^.gKPxe"&Y0xo]oQ֑v[7e}F 6!J;sZkCYir'5ERÅb1MYx^4[ӮK=xaX8b[0R$ֲ-?ɫ-uF\f=',׮u[˟hտX̖)45X-OAƟcq)9y^X݉[} ks{kz-4Ry IYOf> ?^]haop$ƢEMu\`ճmT+dI'Qh?tG$~ǴkhOtG$~Ǵk s((+? =oQ%bm`ig{v daYNG.ou46]JYIs,jXە>g!>f&^j tˈ <ƃ $%xSLGuo}y_3@<ĩe%ij3ϵ)8_0H:ۜsҍO.}^mN{-cvvSO<^|Ċ]v`q)M%oo.Wm_}Zo_͚04i67i"DU1`2?vk ˨O*M]-U"kQf oσtH'uƤݿҬH2JPqwʳ’.v8>գo_W_3;mZ' 'Nm[Y =]R''G\ZGu]G_څαz)i YdJP#-¿xbMo%ѹ]٣T?gI|( \qMA(R"KJNֲn|ygƟv/R,tp2"Gp'+@w&bK y?$Q= 䴯ռGa2{z}жPHtuD@da+W@|o-@ΏQYRY($bQʧlW=حHgF]A8\[4zmzŽoʂw`gHjd+Q_ܿwWO=נ74t*9dbCl=a~Uom ϔ/ϗb>}F̌##Ķڟl-n [b 3`r=$%!㿓UKԺk9 ҧ)_ޭv}ȇn9[񟀾|2 ɿ|wRBy+c=+S7|#]g=W1HLve0]Hde"_Դ Xj>-L[fP 䌱 梓VW r>*((oXكq''riw;_.)E'm5C&515;1ӈP?f4joڦegWzei],3yOwv7U20s9>"87)ysJ wM$P1uiViZ1Z-\Ku]ɫ[|SS&~fGvxN9|+'Oφ;93<5YfAnzcOo5M;PQI leC >'Ŏº}VeԱg%уryO~ u:űp=4SH+<]ocAo07I9W +?If; `+t%o%Y+y7^W|Hn&ĚO6RMRt?"qe5mŵڅ3IZ_A9o²u}s SkZD{:d($<ioWv__$6=ſAGL{#db0"U?1$?C+tͯմYt ;2 =s_b|%x|(7Klb+I)< a^vKXϸ&&+,UR%IqNot'F=3Zoenui<m5&Jk)lwKB.kS Au{76VKtʛ.#I#V1pKp+ؼ3Czv}iIok [\6v΂E8 OǾ ; ɭ}5]:[S+", i6S(_WП8FI_R)iI_-~П8FI_R)iISEPPW)ψW~jZGW2 i%P.Q0KmZ:?'|%qnCa[nn7fw52ب|.OZ]乵֣Y5Y.. (P[HdU  -oOO%k5ojVlKu {$|p2kihHt}~mZMU?FV1- S !p͛H!/?i '4 ONҵrYOio1"GU<6]ݭ~Kny ^u=hz燼M7SLSa%Ҩ? ol|}x5[opk1Bs0Aſ'ix]DBaC{1 zsz_JX&[Ra "PvFy5ݚdWK6m>p]VhTmqbfe[+e[˅NMq^w<+ixL٣ťIݕQ=vGu)-<70XqxVQJx]3.a֬%k,\2΁ļQ}*!-}4TOKU,Tub_~Q񖄗+OluȄY2]0wg9__~Եĺ{i?DqnHx̲r6YXTjwܮ5zo} w4BSz&r#t:o)3Ak:vk8F$vڹ815w,~Ɲ/̺L>W޳yL1ڢ6$|\WѺ'Ŀ xmI6ɘBm/ceۃ˻23\?i1,}a{c]FLe  _Gz駡qvtO~kV߉@WN՚0 Qw}sG~߿ _?=;W_OH"T$6<_е6R>a}k+k719l4Qp:xoº_ۣI D{g+KMw$4eGzk訮VkHz W6|SM^&fӆcp i_Ջz+Es^jZ'^NMe{oI [DoqO4=OPRHĮV$yQQ\߈%Kk&tf"k{{^Hw[qݱN:U;ƞm`G70PG(,#z4U-#ZAa{oYHHKYppAv ( ( (<"E.Oca푶1 W#X0ڞk lt9xG xMֵ0Xۼ2`nlç7_K%ҴjX/e6Vqbc%+rʹ~OvOWvծkZVsWͤM6xEI7Kbt|C7\Zޱo% _GfۚAYH|nٯt?Upl쮯ck N[>՞<#@Man6L Oc9?9~ߙwzv f#usk۝:<-|!`|Mc^0<1.csmhV6HwI$idj ׸A·^(pxKPy4#kd;J)Lυ5 zլV8!]lded=*O\۔ymπ5_|uk{ƚ厱wi/v껇MUh&M[zW govgBLhFcAOQΓ&m vgoXic9S_n?60/4Ȗ[8|CI52Qqv_.e}yoj!>7u/ X'<=s\[\ QJgUb H#{^x [Ʒ}V_C?ܾ_;YpR`@Git 5k;H.-c0מpj 7iv vKo3s򤅶jҧ)_߫V miu)&ռ7Z~"CbV·ZU_9f?I F@ 90Mc⺶g4,8c#IR$񦨞 |Q!]k+ \uZJm#:> {K[`30qҪ`/G[xk ɢ} \ϡQXGϵwN8ɧ,'閚\k`}畉M}!xc%_xY Zh|zMޱqWBq!r烔n1I{Owoi˰٭/>u\x+YčuZX~ŠH\m8kѾ$#^0x\E`,Oc|,lE)fğ'xR\[] +}b!v֍%bTY  no;QK]$ #uK)o#_Q%]o_.|< j:ixCPȼzK-Lob28:43|A5e|m+v#QAx@j?uM3M2 sZo,Qy%`Y`HQ 'K}=_Ϡ=WO^NJk=kMO]-)%;YdeFhV-n%xjSjHHLw*NH#z$ %Tݴ>F?qS׭Z?qS׭򒧰-(WŚ׉5S?7$좞1yJh,v+ Sþt]_j2]KGpd ~bbå7k_~^BWOw{fEC٭V[\2pf@$.Avq_Cpx6WY[Zuȧ.̗qIn̐0`Tc<V^Ѭ5+utd]0v85;_5]II|!Z.VKwyL^oi! p3<9ww1$Pćs6;PH>mZ%|Kn k8w<;u4mFX! (E* `Fqo`Zi ~#z+?6yGį޿CZO~(V-U[ahnDp3ljB&g3qqIap.NFpky9pI:B22)?hK+ +YmMM˗k8PfxvQKuoQe^[{4z~ WƻE\%H-D]4>vw]f!X IdzӞé|Dקwg_ Gwqd5׾&HXf n YH9?ύV?q[Z67ԒKc8 (sTr]2,g{?Y~+.5|$r+s{r৔Dk)r #~~"񇇡E)#>PW{7}X^9mĀ>R[??ЈK}/# W-G-u~;{ٴSi3R!+Y?~6J݇Y2Ꮧ*I:=Z]Ovp2^ Yb #WYе[Bo#.}ҥܕFCnьc-2hkV/7X<Ac#5\.>E{o&XZx .!G+,aA8e]ptO"ѵ t=:k{幖UcPK6c 91:3ʶs-+ BrV:&һZ+((*ow x tgnȬ +7@dc`w} s6[PII[9\ŁrӖ(cʅ{ױV~jX[ĭʌzW~:O mu{Q~QY&¤cp+ua|FyhWY'Cʍayd$8w[?^ ϸp8s֢7Q}4ܻC_> ޚn<]}na' C *c95ᯋ>iqc5$Ϩ^H~d\Fn=k㎁xT/i~c}i'K3%4+)泫ivqo]#XܬNҬ L\]ݷ8&}?<0վ|6 úŦE! bd8-˒%|AZ|5U\Y/syw $ N3ȯL|/>EޛR[j2 5ɸϘ>#F bPV5gCEv]xSZԖ ,;q ytrY /OWJ+YB crC88 ?4_ 8Ɛ̺-{cj4Cܳ2j>-?>ыO[IeS~K8+ʗh$m$Ĭ[:x~kTssq4wjVsȤo:|/^𾩩iz _]_4b$o4=G~? -O;.kX_[˽_IhȬw7_S^ZM>k {x_XhLA Ƹo-"mNNӮI$#FڙY9n0 ]mK,|g 6>5W_7*E ̷k 8w;r|L(ٍ IA{5ӦhHeBB@ی+ሼo]RKKa8VYDZ.g 5⧅mg,-myB](j76022FiݦKoIYUx_//i ~5{=H\۬z#DYe V^6_r)+kTMs Vs׀oZ~3=v:V%ŞxV/eoRvn y(8&߳uwGxsPVR{kdJB;U9=ZhԴ_A۽WLZ)w 1U$|Vj/Y}hWz/t[Iզ$[[|`2TnPI#dy,s顢mJ{ϧ9ρ_|WO>4CaQ]24 t_WGZ^L_{ QmLD*էO!𞡠MkG4T`FnSV'*'(gn'Ii"+*3r85+m}uc(+Z$7 #`d/Ÿm #`d/Ÿm=nwQEr~+5$׈$/N!XS3o,pzYY\x·SnTT Mb:+Ͷрhډ20-s<*^oGƱxZk-[LX,Z;[QK2POlqYN:Q\j_ĿH ºn o<.+F6h~o6y.bN~ ]$snP9|*׈ _9!<9g&a@M#9{:T?;S7)짼BCq$Ab;i/~_&#Ş+|WhZK9VQ M8|W'm(xE_cL-֮l$Ӣ[Kpnaetb<,5>ӦMr뗅,̳hd,/ !ʄ', <|m=:nlr YZ_q7{75z#ςZ_;] N//R[_ '27V>x8mV&5}HHCtwoɯr;wKxXYAxnq|zOT ӛº,S&dM A4S4NX!Cgl(ii >pO>.[Evnn/uM7Re:f(T)%l]Wot_x\w:ܫasmZ]lYǞ_pGݵq ?++[iwi#7 A1c:(W3+՟ןq6o-Hm? ?^{ffpJsU[kr;g5OZawo'{ 4iM,mLq0D Rx9ZѶ7[ܛ+[-/xQI.:n̾"KkkfG[%"&q=8ԔQR"/~`3oſ(QEy-QέO Ci+hWYb $?~ |+Ikq0$ c9E-< -W^?;/v pܜƼsV|].:4W>]rK{e9vjϭE|d~]I?ͯ?w]݇t2P) SoFj-oiu ";amBu' zhgTyoa[_41O%qd ɺ e SX[nŜ#OcnHK$=6]+qgK;Y֮{Mj_^qZGR, P .0IZ "^!7|}yy-fhtQnP&Z+Kxv7u-+=cStz溒Q{F[)7Bkvy<'Sō>.ޟMa.i6~;7 V`Qv`Q_Go[/[ܻ`|׮ [\'αmdQ9i#Dk_!ûbxt-ZFN;/%,TN]Úit[[E2%6W_ßqwQ,,mYв!a+# L\[[krZacoZO Abc~W'sc [~3g_,=>:m΅6;Ym,!+ɥi_ 7bK[ۘфsJEKsI8i>wyk{GM9l<xUd0sZի?o OMZ7V Deм/nuN.omnIOq}'?jm]'÷W3\KZBL@t+Hkk΁IVV C,N9zOxfK+lLg[<6)ʌpM__ !ik~[{xψgju v %u >kpZ?>)sYkvU7F2Bgʌ;s9]a}?ŷ=anr\gռ䄶b$jOmº3h᛫[MH-o/l;[K2&y;A=@8N]-eno_>cFb4vṵ}\U7@Kd9z_XhZX./t%onA3mrg&+oX5ΣwbAt;jGxCP[ہiU'Th ŠΈYr@ysU{jr񖞽z=?]?353 |Y7۞XCQ:TN9d{9+jU xS"[-llX^xK-3T#1gvw'+ּ_ڟC->IԵ}!-`7.;.މku//_aGO<1VZޯ Jв*r[c zm> {Dhh֢ۇ9VH US 02xl(!bI-Κ}A V3FJ~5xΏKM.mWW#h4"NaM*1e'fmI1+4gJ=9lttYY^OvUr]m\w桭_ּKZj.. N?`D|ODOZ߮`ɚ9&8ܷ}Vߎ> IxKCQ5(Fڢr@<(΢kz/MZO>-[Fx¶3ijYi7jafA2Ɂ灀8d ilA*OЯtU,ZY3,?ggG {-ŏo~xÚus ݍMp/M@A8$\_kԣYzItlBɶ<-<: e[{ss]ou/-_K&?#[ו57e9__)׌oto 䱎ºtw1DGEMLKuՎq; uaK SoUWxrSKf!yr`snD+3%wߊ_jrVG_y>l,<# :u&՜Hm-/F,mhc-Ԫ(POh-~/cǬ]ǕXm-] {n ׶xCQxš> f(H69*cRm}}n[Xd⼿D+W|[x^"_ Z躟-D [WIq,*]5&/kzoş xrg5N}ljMȑ;~buO~$WL>+{y$|ĭ)U<5o Ǘs}6my湉\b1!ո ֢jm=O jNӥ(,L#lrH1m@M{GҼ-_d>,4;a{62Ltq. F#x\`kK{ wFw\k} EyLJ|{^On-uDl u0N<[n Ɍc8u|}{})&E I!Y&vi0w(یVIػ;/W=~Ծ+  -IkU_*F>Gc5 8Zhr$A؇`X>VCx/K3hŹ:89 ȡ{+K^G@fPQƲ5kjXd@͹\Bv ?.{s]Ϡj]."'J3_pJ+f-tM{E`9uGNЭU\m]ҾwqE|~e]yy~VZ+j_[Lմ-3YD6ܷ))6,N 9]zSucZ^[OHeUﭮ~^`a"?Ӻw3Ajckkyqi\Ѵ3|1ɬ[Em΍'uoowg`6Ev/UA+|yy^ѹ19j|Rk?~˱6/m=:~;y~ jF× B9njY"s&?>įK!u+l2HT/{Ŀ-c=k/B/nua grp<1;x yz/߇/a5,Qۛ;F0 oA'ףwK_쿯jtFiP|÷}0Uema>'">?Zn5}ʉ ;.F$P1Rp+kLއ7&jjp;wfG3 t-'AGL.nkgI4.GPV qYcޛ+I}}mo>']_"Ӿ^yO[Rl짂T@eUWڿu )s+~7 ukI|]/9.1:@~6 d8M5cwZ6yVzD-,ZQA *?מ4rk+Ҽ[ 1[\Zњ-tX|(I)--oO+U>v8 ;owW'Dž}:䶶\s1l N qmDׯ4 ;F,n4/n?%$$8һ~> CԴ-rKopDi]dN"a`> ZOhVck)4M=&[4!d/+e1RyWk~{Rqn^i^[|hꚆC[Z[Z$J伅pɬ}/ொ~OŚ$泵忕1F +3,VΟ>(>'/ JM&)i"HN oƝg Xk> O,V󉑇$NW?{񾽓Џz?Y~i[Ot2JO{O^JkOt2JO{O^J];(OS_Sׇ|H_3R{&|{X8 x:BW:w3I&q?K+< zYJir7_uG^Ӯ{i +RsԞo@qMsT]B.eRk荺d QG) 7# Ět)|;xZQGSKyFK$2d^ÚWM_{k;U_m% i^%b^kSY5;~6aI|S.O3DP\$rGi}{^xKEeZ y6a򪺳9@<+cjOMt&\o+~xN˨Ι}oT^977c*ۙ'^k~ $ҎwB9K6Ҹ\1dKωVӬn4[e{ϴD-51Z6PP i!V ߎtO|=Dnt^[`HC@f>a`8k?u]_ݥ4__~4?YSowo>'PS͙f_5'[/j[44/g>Z"wNQĿ}cS~ Xtَ7D2 ʐW 9@dڗ]W6ghooC,|0ރr.c]ONudK<|<&O&1M5`YUkhj]~qʗ:7 ևM.=ZH]mkéAu JF R)w!SQF.w6xO[K FU7 By8a"6Ǒ_j{gۼSa_ϳI9cN9(\o ~I ݿͰ(QE|EN>vڞow^&Rcee@9k7~55OM8y~e˼n\s4-.Nt};o'5$5O [ߓv9σl5!x=BMMj:3+pd\m88 ׾7zeu:=gú-cbekyU's6!ȉW^+þ"0x2Hƿ2,A.fKf1ۄ};I Le+ij\t/~x/\.oqyIDu'X6L3Z';r?YYxHѵEwZOos|f@e*byyݜT?f> xXnwđZ[Xi|ʛ{pPpbϳ{x_Ɓ/=🃬%٥#ͼ% ΃dǼ |i>G[Xxvk$y`xd*1 IU|C/O7n#xt"eaóPkOZBN5}D\Gg,%6gfA.#4dDo_WX[X."6iH];! v8glNkhڼ%M]mC {sZ)İ_4!Z;)Yy5AI>O :`@yj ;O lx{K{ u.㍤&{aJUA fVqgoMﯭcZLj|[imV}f:u ǎ  ˄JWq^o"muV.0fZ\։ZK9ɢxORkGoI[v7`Q-.I}Elm_/W~|/KӠ5FԮ5 KD@ \dj^ мWxN|geI^ݍ 尼wY0-|l`CzpxqtӵKxu֊kuلL$n@WD xn?]RXcmmji| vwT'8bJOT~n%k>%S?K]0iW-Gi$DO5Dmltx7|UZn oȲgFw$+xCR^ }CJ͵ω.9,1X#EE;-L+񿍵&ׂ+;FSj3:tC,0z4ȀP[WK^k~ 5j8ѵRg-eܑk[Gqª"IQ!TR>QO4j#~5hzL)f4:]!h),~m$1\/9_R֢ecN]3] `D[ Ėt(?{>Kw~m;[K^5MxZve~~{l"T Tdx#^&`5ƣo`4{K(7BX[i @o||g(>&hZωc5}[}6V|q*6Vyɤ-Mo5;j jڶն1`آAY4NTZ~'Zy|B:?%}K=[_%|B:?%}K=[_%>ĭ(_mCQkCsw*!s)*UAt< ~ķ>+"xg:E^i{{;RH m+@<תX=M{P! `}hf4 [kO}ZhctM9ceoUgpTM{o[F:A#Hvjk~~yhƘS$%4WN#}{jDŽ Tv Nc?৉w1m"EXHM{Mnw8@V};3a᭚izۭŋHPꡜp+n33j:S.xohލeoH$[O?\1 #j㯍$xwĶnjihyWrR`8HəJ.o俀⚗//[5|'wRmxg*sVE6VԾ6-O-1Xn64{6m;VcS~3t.|AtF)$s4IvU ǮO]WZ)kxzZ;,c1g~3F'<⵩kow3-v:~;y^#|SI YKJײ\z#ʯuڧU<=;Хym^H) ;q;W;h??j_*tizt0\9a+UY3蕧 SY߅TC{wnfg.s#a JNGACU GOwCծu 5K$D| >itٚN&~}pk4,_Ki[NO#{6e .U{o&\&KxVDT wMu> ?,Y44'-BlOPz)ȬoXIfZOY;$ ds-w|>м@,-K7ՙl,,0&Z.s-.Y~Elן;~CVuz-vaݕ\L`'5 [:6M>Mn]ۻ+rDPǍȬu5!|5-.LR[Y#ǴᕰN5;X~!^}.>.uw4D6"D3;;9E=n^ Jo_ L>1/%.ig[k0y{5EK6B284oW7z"q/=cZ{gci7O--nu:iٕЂ >lu=BJD2n'Xxÿ#`nou&ckm>ԡ0mڠPI,?<_o*mWVaqwq %h͓DW!SRQ-/u_G _U$u3\ڨƑP,cr8\?0<=iR-GQ}EmG'^]zͯ j3nҼ@llqoB]j?z~?7"/-n-[ na"kHUlrCc F+ʟGo.ׯ3WϚ?hOtG$~ǴkhOtG$~Ǵk؅E 4ϋ?4=ZQ:ls+$ yR8gEn:{,g*$w B0Fvd6I rMx/G]5; B|+^%y`@i=BPDېZٮ}oG| ѼE⸵OWׯ⾋Q]m@<\F+YUې+3_ o,6x&aiayAgb`$+# 95k ,.?j7N:n?seܭJN( F6Һ^&xR,3B8XqVGS&+8mkoO¥q̿4Ozw|oxQ]Xm$(_7a\'~/5gҵ H2f\Nmo,44K2vbAb9' F .enS~tXX=UxSO5on5O2ExVԣ--Z=Rr xz|.Rrz VϪ(uZ]Ŏ+nFW^ll:.w*lk?gN=?Ӣƽ5Uw~h'crw ޚWmv%ݒ}=j(0((((((((WGmDuk1ݘ+~bN8]^m7+?'*uRKw^hmcf~qnIj-ml,m [1[B p~q{4zy`e"8㏞2;יx<ۨk ]?&.XYFk M" y噷wsvkh2".6=NsSkoZ5޷$}n;fTi6c'O>GωNWf^-= !WwR9/C6z֌Xеy+P$;8@+ngʤ;s٪IZn0zޟ/^~yޯAW2뷖k:0p%PhO\U㟄(tTcha[i6baJ>`>(W>c5~3xw7n4Ӽ egq[3I$F6/ ]Nj|T4+K_k2[Jѱr-ة52@xrK'*Gz4ۖ/K5ݯ<Ot2JO{O^JkOt2JO{O^J}[QAAXW tO|Qk=2Cs+ [x/ UoƓxV ehevUL!;@CITk|TjQ^3xd8 SM?xOmqlt RI dN+x-u;]OJGI|_4ɀIEH  7~$麎jVulZ2lhD_Mm|8\W.+?t/)Ҽ?blg44rI,#29Wksmg@"@U=̻NkjqzBjKOQE%Q@Q@Q@Q@Q@Q@Q@Q@οKOxzм-(A3zj[/}nIm>x,bW`aOsk㯊<=amo\Zmoq[sDy k>|N㯈Qj29O >yioQ(C1Ž m|wExNk ⺒D/ _cC,FN*"]-[|mž鯾֧|/ퟁ4.ne UbTN }NsDymޙm=[.YL*R 3+5ok<[;Eեfk= BHӒay$VBev*~PǏixL׼qyurcչڦvs-K8Ey/ߟɢ|u,nuO Zַy}ky |P:jm[-uqi$x.i2$J!5 6VD^oZtx+6}}u⧱kzK$FqZT\ͯ__6=;IQ.8t$fl2>ԟD5㟄 }_^"v(,B0 &W%sכ]|E־'fxMI]-#]ܨR]W o3^X6%‚; dI:?(pG&M=z?/} =. <΢Wd}J +5r3(H0|%mb]Fxm֭qS[\~A,y*Yi7_uW~w:啝^MyqV9gF\* ]O>xw jޚj^0"|:#۵y_ߋi>׊kR1MB n/t6DE88$לR>I/M)-Zm &;Ld`cڃ%_#ۼa#ާlo4hA},ϰn:hlGsS.HVK+/EjަL'ul C`d O^1Z.n5M B:u%>lXhݔ$PwuϦ|H'u?&nuu+{ky9@hG@aKcn iU/{ҭ:]OYjZ7Vttg\%B2P=+kο-egQiBKĚohZ/4G 4߱[:k*s)e.D,o^*7 f=yZ)kE-mґBnZ_]Ě=g]K{2j m$g{Na9]#]tM lu E%H9X1,DAA#5uJ:!*a|/C+Wxswä..].Yacok)])u.׎޷ *۰|UO_OKVuv4+?KquDTڸ?.3 * t|we<ZkP"ՠ4FUm&N6 k۩/ :kIle*^"T|T$Uj]M3kTօ"x/ !??]%ǀ<7w|M6a'a)P6qG_ x_aĽ;7?m/'v|ݚ촏Z烴 5|e\iq-㶹xuお-{5/hї%i5i} 5]N]$rK[kڗ,xVOjf2}7 ^W|YMWO5 )մv4"|k8lU1m9R_ ,S/>"뗾!iKMrʷȧf=1^դHu][M.x/n컕Xv%H4-R}EPEPEPEPEPEP/WN+$,sEi1fP+ݶ ޵ROCCYmiu $ 3\ǿ]xJúD5].dWa2H÷^3KDsikz"Æ3 ٽ,5I붿e'zݢ%jym&KDymS*8SOwRM$%yFkT%Hx.U|?!׵4Hi?4#Z-v5v~4uT"N`oRG.iCzZ=L9?_q",|~*o/td-PNr0Ngx[;eH[+c$8 (x|@Gx/iu;4hbQcQĂc[x&]F4{ آ4Tm;"Q³I'XRJ4_ mŻnoxSv5OӠ[i.8i*>_./I<%gKDчiHaFvǫ޼VԼGĿ- &:ōui!htH(Õ_XC^x{v&qi.%н^a+t1 C8?1s3rmbQ[i5>G:tQL-ح[N_f1/n­6/xOH,ma-40YTr:yy9)Kgg˧k3J36,yO Zӵz/nkE|<弰pkZ줻;}ẓZ xbZg].~U7v/5ah<;j)^[$R2l W'ω~x6y_^RLd_.1Һ?Η "GKi,ԌvJwc'f6Iu~Jvޘ)%ZK r/lѣYd*PgV:΃hү48kTh||˫qST|KXj|P$?+dJϊ~)Y}Pׅu\1j:VAxƢ#pFB❵K#~"N}[=OUWsTu-CUw3#=^T!#;zT7_:ΦWzsqw0̒8Ԝ``=k_>4oƳTӵtKI2?Qz3^:ģ&~U gy>j=>&EoouC_ÿ ~i:4=9{(bZυ<x[4[X䓓d׊X?K_Tʖ?_| xzv0D`vN8>[ |?mh[TX24P< 78U^/Qhk~-nڍijTVeR8$_>i_b|?xvBMn4Z#1K&ϘHz-/%!4֋w&5VA]O?JVA]O?J@vQE0 ɟV໷i\K,f&]r kW㏉^QQ⻎EH$$s(na9xQj$TUݍȼEb&t-m-^)(O)x'×LKy k#nsz.fo^j..xA<ɍrqwfO1$ x5ۍ obB2tSVw4]erV[][]xQ<'YI|7@]\/$tHUJP:b;G> |=>"x4s̚B8j.7}knt/rm𻾸Jo~WčZ6sjI*%r/Um<<3ǩItYlb#;סu?_|<7tnR[.tq&<)&e?,Qm~C8P`~-|L׵Y5;+Xlb-1ǮA~M;z5]Z_{U?RPҬ[u $d&ZYanGmglQ U^sk⟂>7g]ͩ[ڶzEɧ>]ԒHL*bA/Iv.]fm24i>^E^ޗZ4o߂˹[|aӞٴ LDucGm&vPZ"EMh 0}hH ڎgYiibDaivv}VְGmmP""5kH֮`4+seI3`H)ZiwVyWxO1SP{x{JmYuSYMWh6Ջ}>{ආ4GVXIՊ(((((((Ssxk* F/@ }3Yw%ǦjZ֕x[#!L1|s?{Ln)Ymcd[}̿E7>zO n~'j}Cr`,.$d,\pW_2=uEZo%h}7?t{;,Il!#+S\m3XI%{W^:-[X< dT h,f,$ cX񶷭&({7$T0 $`5^Fw9?OI֌5S*K7|Vu'YѴmN4"03]LT$ +4٦ߒD[[Ktoϡx<+,mb=+- B;(*>cӥaqf"$Rݧ6dg줻;}˻Ј>e5쏤?ϋnOҶԂX^xU'=fZu[EQt^WȲM[c2eBXxx{xZܶEmA&n<s֗Ox+_'©.M躄S]C.[5H"B $rp՝_ ]v?ǽt-L;ۧ9!oUGtXu&c,Pg ݿ zk6Z^*WXo&b]kOg .s\I06OWrZZᲚvV7}n.<)*ȡ$ben֛zϬ>nKNi^%K^jPW Viͧ_˴om2= |s⥧x4R/.{#P M ׮9_#)Q1ꚞsaq$/=[I3HI AD_!G޷}?ߌt_^l/lpWWr!1r@J՟:֗oMm$ |Ihk!};QPDkRHBl%H@7׋j~$5?5̶ieN\"V}vXpLʊ ਼VFدi;\p`0O;sî)5:t^k Vq彤bqXљ0x;x8/7p tJ$zu|=s ]\@P dLƋ#|xWiWCV77UA IM Oj]SG-8]'ZW=OO+nF2T(8-`~4eP*r+"D?(+5]ZF$7 l6ǹTN ]{jZu Xj_Rl[asn~VhM8-aZwÍFTZ׭|Yo^Iqo1]Z hLe1rG:s]|ICGN4 8lyg%(cjM'-wj;GZxC]1ǨXGpiJ&[Fv9Zw0ZMpJ!rI8==G9#xR'|?NJ_.IM^-^,±9vX>Sbܢ6sv8uFXl#oq`,E#;\dGqV"1!X`5l1K_٣ᕗ#֋X[BGA$h"BB 8"|F_]'5{>o goUp \9Itv/컲!(ߪG:ܚH[,]jNeB_i#'h,7B=\x@3 iG@ WϚx? k):n5pŤOO!'kg[qv˳S$hn!_1 pNHigo6&+_OG_j~:=9,1"T0+:>u mR]E)Gwlx寇n]Z_jjbܰƖL'# PHVi1j)n~ƥkI!Wxs$KS'M$F8^ '|=CfSod^QG%pw T=O? 1q&K NRn}8;t-lymc- PҴE[C5ٮuQ`A$KFqzZRM*GX,q_¶>W .;]hV{`$H_g|'ˤx'#|t76k!js{Go-ZOc:x#^xRYoƐOeq s)5R#1/[tmz;%_ʉ"dhdd 2 ^UiD5Mĺ5繶Rױqrcd{1yA6T{g/MgB_;Jơ46X[K}JeʖSRkzOBvc#5*֤CehupyMvԍsc4G22O21i7Z{yꅑ.b35~9hvK_[DL[Rͽ_ H_g!Bxnlq|lQn?|"FFәv^bk[.|~F+s\+sHҊ(\ŧM?6WG4-4܉<*X^M F1#"\Ԙl{W/m1|:5vѵnFF n۞~Wiv}z6"6)0ȥw5'|IuV&3 jV6|Gk*8qs߀xz2Cp%,2WWş|Ity' Ұsw|Iѥv\, lG'FkFT0>m?o:0.~hG*W 2aCω??^nO>$Pz,_4X+>z]q#EHUYd8mqm| ğ/_7G'c#E.tlm|H^wX{\ӝw'|ItX9m~$h:iti] ?|! ğ/_7G'c$rQ!\9ϧho:0 ~?M'|ItX9>$ʫ̄ruᶖF_"g\??ω??^nO>$Pz,3HRhY2\cAڗ|Ity' `>F3=(#>F(o:`# vOω??^n1}ĝWa5 p;g?:3\kHA8sCω??^nO>$Pz,tyRhQnDm8, Yv5-KK!t8AN|`|W ğ/_7G'+1č EϞW\㴳HRhY2\c_y' ğ/_7N}~$hJf7Z'# Rң1խɺ^. ω??^nφ+Ωx`գOeo5-DA8sRs\|UѤZU!"By@1ަ_G3<Bȫ>wדQIUuKi,a) ;DqN*IхEV#qm"|F4מ5@!Tg'gіkK}Gf N ^iEz4TZ;Cv~$hek=Pc-?uksO.Uե*,"R@ں_,륀+(`s?~/06xL0GtER$paYCPC8 ^[EzvcO[(T*q1F I*ĭ9Ep!6בQ@u#E/tUP>&hr@R" %J 4H<ŠώzVͤMn wPa1'ו.Io5KiKMk"BMun L^wE ;Ηhq:tnʚ|n.@ T2²~-_;v緐0kv;8d\Es4L"à㬗SO?&wQE -M2R'OS]%TP3hl&Ev|vTb ag?EY[j }o3R ?(o8TfS>0??"@,M@j>7 ?(o8TfS>0??"@,M@j>7 ?(o8TfS>0??"@,M@j>7 ?(o8TfS>0??"@,M@j>7 ?(o8TfS>0??"@,M@j>7 ?(o8TfS>0??"@,M@j>7 ?(o8TfS>0??"@,M@j>7 ?(o8TfS>0??"@,M@j>7 ?(o8TfS>0??"@,  ?(o8y~Q> ?(?o8ۋTH9g8_62LLK?wxbíp)(o8ƶ(( !_W_Q%f֗?J$ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (9-WF梍WF梀8?x# n/I;w`>?J,~*xT>!͆eI]o4F"j֏-2 ^g.a6p K17>i̷Y ;`O,6jo@}>z ݭCXȶDL1/c=i3wo<:唐؀.&k>@[YY(|$gnIiu5xZnfZlystI Y\㚶v_o="?>LMzyGf 'o/ Z|r3=<{WiG'Hlm5څā#ѷlntFA}K|5i5HS!߂By迯WW]s{i" eYH${ͼ7 6XadGB@O8Ii&mQEQE:=[-&E$<#8޴%X,[Tlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ O2V+} r>HI$|~UbdݗQ/KU5{y\ y#`|3]#G+`|3GN5?~_(dݗQ`8Ϟh/fGlߗ ,_ӿ9t4ُ3Jeu*@vGlߗ ,vy-'<:e/}?~_(/fϞkdݗQ/q>%=;_ӿ9vlߗ >e/XSG5+yo㴂[.Fc .:eP}?~_(dݗPTlߗ >e/@r((((((((((((((((((((((((((M*A#1j]+arT 1оGI/?@5@񦱠j^Ӵ[[+Z}#qBA'.?$}G̴7Zѯ_2F$!cqmkQ/xQtkӬG(C#J-pp?P#ԓ?_] 1[iðUwFOAp<+mYjm֗8w}*O\kmfM.m' "3u.0APEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEgxEOe yWM;Z|/hPj,MrnV xkVىU61k>8 egqIp#gЛ[[?­rST&dck;mj}<2S^xgÚc'X$2I" HǓԚg 񺵦xcWf8;9\1EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEfotoxx-20.08/images/bright-ramp1.png000066400000000000000000000572721362435004500173410ustar00rootroot00000000000000PNG  IHDR,ܜ IDATx]`.˥B#% ]@D#" v, lJQ$tH 'f %@ \nowv~;äbk+<40wo?Wڈ#[DV&W!.%GuuJ"|X4Z6Z%"8jj77'EIIKVhz'Ȋݣh n?9E V6R{]>L`?g-Ui5{+ύ#TSqS E߶)WV*m ZlJ̔aQ g@A!PfC[ BR vQOȉH{omw0 #q<7Ea^J樺"-++kjŊ)ui͠NB# BFV 9KajSf|7=3cЭO?XȤ+ݧ$ vk\-9싘8yd\mq.4[%U!EQ HU\X+MEb,%f,wM`N*ئ=\*ݽk砠jh6Lb#DZ6|DV\֠bZ8.W}ski;cd(=qd( `ki Ec\_NوWԄY<ų-+^8!Fm,'K^(ԔYkMRN@ȧDFUz}b[/; GL-Lկ1d>UkX)7# "ۿlk]+/7СHCE?Kvk /)Z]=YLTTc,yn3]IF/=ef܍LyJIac.=CxAcB<[ vvv̡)mʌ8ȑ[RIp jHM BϯN DzRlnv8vيU+Ǩ8T8JCtN"I5kmٜQ =5؊"x> g ()$*k%^VGB6.(:5ID8(>sۮ1Q[A)Y@DE#"z)`MBwljF D!sp B\/m:G`'8YWcgHyyK + b5R&_Oˁ8Auk>v ?졖ۆjrp)bRrЪe(@E`cF7*(ZT$afcu.̚څH7y>Bx陷p9xm)h,Nh9ϦPjI_Ҧ ct?? 8MMyc+醍;AR,;m:k-B.} Dg.Cpq_İQѪqa=aw5p$B|w۩fiG-R,Lqï jBڴŦثupG,.gQcwmfvLWo=DzL8Wh%q{vぎRti yEm0lddfc>u6'1dHwAGV}xLKׁhQcc⏌bIMo [;w,\:f-ݕG_="(r p-.afaՄH 6 ;`R 8'&-aT8v.-#}ҦqX|WWdddV1 #XYiJo!#1v^C5 1L4btw+܌> m3K p-9P'#j&%gT!hʇ (Mnpy˦ax`!-JÚ`EIX,5j oQSZ0B{Chhbٯ?8F!t+E4"C5j$)p(-zO^↡Cc?caSbXDXgc 7Qx*bN\9w;]t'8ޞ&̥_6}j>4<ћ4a P?_ i:(&W|l\fꂨ+9|<}78?՝bD8 ex.yYm5GYj?,TA)5`9[@׊r[nRu[蚄԰Xy䡰 2B1Sy:8=jl (@qvYgLE̬, ⩔gE>k蚃]s14ꦥYEJZDJVV8Efp0-KHVq,BS{=:w#=czQH(aw&>+ۈh u(8 ;ka;0+d`w%z30}xCk@͹H<!LooZ&+r͔4W4PLH(4ƃ\-[?)Ƞe7SQSbAdV \Lɠi 3ѹ+E͑Myq ckĠx{e ̷A}Xg,ߪD?MF jX:yzAR*tlmgp65>> \y}5,-?dBl"CWKՆ<{ ;3ChjDiyxx}XJ?0VvvvtSqrZ:zO4,_ز;Oz7!W %2zehZ4×$gؘ.9DyTn_fxj$6Tjצ+M{[/6ypӇ!12Oϛ &^OTVr Sk `0+ WJ˚lBh Z !԰%r͖CjUn [=cpSN HM[Gp6a1_LBɜD ?{_dCOĿ/g߾ѱ-ၗ(q/_HT{4OP4~™S{Ќj͞j!lqkY0ԜEcۣ;-lqd2"`^w(eӻ@fv.ܨ ςa% w@NO!, HLzI1s(t%\=_LwVs`SIOPiRH gdX9Bsd8Vo"KҥK8tL&\QguhDh(B[x3-#YW::%sDt؎jZEѼuW i>_1%tͷE;xοW?EkF8"=Σ_ĤgV׆#jƍr ҺͩGm|6χѴ17i}GkfIJ> c>KMI1M(՘k(!CV Hlيh&)֫-i beli"erMKj'Þfm B vA|<ދ];‘ɞC? t L{g|d KC.Ok (%&i[KFZP~YEZ6RYgE d1~I4q4&m C]ԁ0KS!u TH!QDR J6N\K V4Xii*(>@XFM]7ai{V4:h R`S MeI+ipVJvUҮ *Dz:[ƒ PAX70¢\Yd͂.֒ K qn*b2JwȚy{8P>=]Z{&!+ykF} p=&:i_=KKOݻz(Rvnۻ;n]CrFܤxMo|vU/wU08G#PFX4Y8ҙZ n`Nd4/JIȬF\Ɩ4 j"?ôN̅|V9yӺslK spudOMvcM, C>!PAX/2t }5-C9w}]߲8a8˘u%%ci5ִtݭn46sSô&.#\LXKhp9Ɔ@Y ƢJ#G#И< '6fl#P\Ԃs8f',1#G#`6p2SqA9NX p8f'Fd4^oI=,㮜N50\EV_9sw$|S!7l_I׬rĦK9Z& Qq,[]q4aG z|t8´q-< ܐ#PJ^8 ReNУ'BMjmؘl̲6wƦ=qT1&cn/[( 3+ ߉h\q~ '(OmIx}UhgK:*mf:Gػ$IaŇcpFq mBļ0-xx{m9Xl̲w&]b[g@Klز g7χ*;S5~=SΤݕi`3gb'ֶ xMBdhk$|i04E^@LWN8u-b7kfѮ45욯߆+ON$cy( 'iOÈ^B%׽m3t< լby&ǎ_bh;k͞OEMӌJMHh?)iHwxRamߥ+4GG 5)eHR7Tuz;COvcOohG56Evl<κ200PmǞx0|`_!EkG2FcggMǔѢU[{zW8_4.d3Ĥ*8Y&OX:MX(-Aܶ8{1KFA JOO?B&L_Wln͆\Cp;)Y^Eu)viδ3.88*7uѪ|ϞzbzeP+OoF}Mn&QA)'=gBB^.n_8TR)R$,_BGg}>Lo?&>Y -E|HOoi$m&T9olžWfu]?]c{{{jQrH@ER;EqnPSͯe>O?a9aYaNMlRKЩ+[Qڨ.ۯ6dڿ//^khk/mǾ#1l, ]ةK_2B+UкUh%T;WfkJdʹ nXf9F EH+/H[vrSaO)9ËoVNh&> >x=?\X ڌT%myoꦻF~ nH#-lPr|ceaΰ& +%`Z]cpJ+', ,{ <}igdp3*&>GCBQRRV/#l+yhK+%ukqy|&<~@׎ΐQs1xSXM;U5W)&8 %!2e:Z{K$-]3e]261KMjڿp<6EYر0*?,-~t/*yQm3Z -u4.;9yYip*# t}=f`F^6~ݼvHGR0upaۏDS)3gC2 n: dZ9kix}>;a{j7{aEM7D&m_<ۺc55>\\ݞY?6|."X;D p9=9^=˦tì>.1־2ɩR)9m'qx|3X|~{^!}]csp~B||<[xȢݭs$0不pG3dfBl Z!}#ƍ;> yx+S|BN5B-FYbwMP3u֬]!̞_t,[: gV%5=>j,HOC<̣S413Cm4,^brI <¬ZRlv"+8<, 4(W!`KS-2sl5(L< 9?c-6cdM7'KՔkgEmłg^FD(RA6})'z΍'5,^8AIaBJR~m{hqy@H#'2Xyyea¼oi `mV?;k":nSo<֢sܵ|n~ŹG 0-o~ʛE`gFXۓ˘Uyhl_Ő㋍ !6o/끳76?/ tÑ-<x7/uq?*2=3}H`u lW '2g| z3/yZCc :ش~Au<=oR:++<9vv-:8SKӴ Ky~O›+WcOYaiSFWxC(% z^~UUg&H&`/+y7#M6i1#XtuD3ꕡj}jQOaفTaNjr& ]wi"Ħ#vi+7AD+w6 5#j=goO 1rAQkF ;{&{PWmn~a0ΓdL,o/6^ȣƱ0]"Tg&ݤ K2%;y½]<?<~tV6J jgRd (}a"ctyU逞?!Z_}p*Jh=j!>+pgX$~|!Fz9O٭ZS]yX`M瑁Sy8u&r۞!jA+ܺWye`UW5z*O_w\G=ydL>jmyۂN[5{Ky߿QgbP25N/E6i²nAn"|-MA:TɆnVл_{oԁtRZV5yL05G%9BM΀U1tr({ j[VOȣ,%S=Cm:ץiB+#rTjף $1sP]CGς ;CKz\NۡmA>ӷk-v NyxvڏݠJ߯z[ۥMvڱӶ9\[Vi=^nڱc6o=[ n]++-{έ4Rn-,׮X1-ݴNЦeW.xϟk;:9:C⮍5D{Yr?]:OMZJ|sږAZ++Cf~ڗ6h v/գ'%mpQt._n[Up %KۣCk>0FK6R;mZm*WXW'mZ0`1c:;h»jc*BkJy O(?ց*e2о Z,$gSM%t/TA5!S YfývɣIw7$Qn uhz%fx f|UG!Ф;\G0$dsN/_(C6< G#P;> / LE2hpɒ%h߶- ^!]7G# h?;wxW`Ig dU,~#DE5Qb@BG#p`ܓj4[e4&?pNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpqϳp88 ǎ8kXhcGHKK5o^ê"#/Z-q5] nܸggg\zbqtT{X J r%o&r9rwwGxx8ۇ>[Vкu+4k bMqª~# "Dgacc#͛ GRR4j5VZ%#*GGGI ?OϟVH G@]3Sll,^y5 _ ܼ;"''&ľY;<=[9a#PCpª~L(--:KKe-CII) 9T)QBXJtm˖߄a$Ⱦ}q9Q pe$NN8q4cqzO>nt' 'I6 `&719a5;:2`² Pf B8f5F#aqY`5F,I_/_D[ )`:X}z)sj0aA?# vR-lm>#FJ%B H*Ÿ?!H)e`ODNl}gDeoo/Y#=KKV#[Bjf|" .{'ŽkFJC$ytaY20BR0aM. dȀc4Ka9:$pqXp:T& %2a‚'5a0udHhI 0Z G%Dlɇ5-u*_Ȅ]2vfdAXԹsf̘)[XX@)ZTjC(Lc5[%5Mҩ,1` ujvD"Hlvj)b" GY0`DOp1,O&}izPs$::;w*-똵#a}B'q8v$j9O#heik 6!5d4^f-uh.L<"""묖Ě*/^wt &KIb#Fn±93{]ԉF72¤ο9INN&ꥤb%B n۶ QЇȆ@5ֿӳgOsMD<3)9YX4AA˖7RF0l^@@3a}3s>K$Z:Y4<fMXl+!@4 h^07R5 t a~9A Ft#\ o .G#Pؐ=[/Ȉ.y&Ħ~"RgXRsZCuZ0Śh Dmaly P+a>}?plܸ=&MT% 6۶mñcDŽ5Z:a1'f[f6nێ 6 22e~#hx]tQ2uj cDDlϔlbb)%w?s@$FגoM ̠22a{Xg6!PZ2*6φٖ=/+B;bY?SHH*Ր>x))|ZE߽TwOdku'T&ad5j`?dC$Gڻ]Q *Z)=CMߝ,ϛVAAƌG@L?*jO>}mV8;W,Kh΃|$!jˏӶ$*䄒'^>dϏk*a;.^^ձoqX`p+c6ފp "-HբH{狱bv_/Ny'/`̸ ÈoX*oEm$GawmϽb 2 {W={Fz(o.z??̘9(Y1dt$ⰸꂭYd|FIDAT_̓\"}萞՗+q(thE#6 JJ<~69Alc '»vαk,i {%t}l!/{§U}qwuƋ?^VOޚe5+oj1*VŌ )up&''GRWA"Kˑsg5~merԗr%hxHK Ok_NCgx X-tMG>oCXZ׿^Ipth Î9vIm X_Xt G{ׯ~ň~=SDh۞KwV1NNT];"(( CDH\ML2٢YJĶp'B;a!hTr >9yib>&7'J}id ɓOB.u ,݅aG77=:wn~m'(UUP jAJhXSKxBBBBD0p6> &MiU N e,`Avgȋ&5m6p&/) 옝cXv+/;qnyH&+rJC:@~eQOJ"̍?!Q,W(5 1-1& J ٱY`< 6XE:$ţA ˚Hȝ5غæ9XQ#hЛı:7n a192#5J"PyX"7{@@.]^z#(:N8lQIpSymWNń p~|c篻0C2#&e'ye H /h=GG1q8F -JX=욄_ ֪q=(3݇ SF7-k+"'c؈XI+߶q#`6`,GGV%DM:olʊ.o]2q R,Of‘_`( Vb|T$^CE XBJ/g-))f!_YY20hZߦ#p5,VyOv>cXF*F?wiji;`Gg$\޶'sKiּ-|\ 5MM-z39\v\2Ӱ/[ Cߋ\l2l W`Kohl SʉKL{(NA,#h>V‡s{qK<>v4 ~ nly9dTpp]w:Q kZxyѥiwXY-I!$H'@B񯯁DdS؇u߻XDJRrJƵLuSj@^ >c`ذa$Q6:gY`o7o IǏH-)7n\ęAj${^wW࡮]{ cٔn޽nP{KN8{ޝjyDvm{-_Ag7$Pw9:/ª/Bv 6͂/eڷ-A`p4Eva}9m\/8Iⰸƃ_]Ƹ @5;Apw4dܻhף˒QGc5H<222ZξA>jMN S='OmsC/ofV>=B*ߑ~GxNk<{"[N*7{gbދO`嗕m>cz~dh(˅M[(**'~4! vvvK M 6sKVC(r 'aQ8(DC"} tn9nJv4'B;&,&4mPSM5 Y?;oe%qhC(9ˮX]Z }4募iʡٱ)NtºGT {Mp8NXr!G#ppº]}=G=g.n9~G#p0:qt\!G#GW_)i U '8Ɗc\j4V\"PTTѤQ2|s8MT,X,\E@SAVS$׃#`p²#s9MNXMŒ\ U4Nkh*q=Fc)kýNX5]IЩ)Rev6u'CذaOl'xB8oo4L0c ڵk]@aw瞝+)`А:5%]j]}ʗԆ6.L2boXxIVkL:5%]j+w+aZ-;+`;… ]#ضFwn`XQB; MVcb5v.?W]}]=~lGdMU դPQ L8Y.ƬFSSҥ&#kuԕVm554Zw_c䳢6Y[`qNMI 3p mНJS]*`}2Lh u/ņV)SPmR.4:+6%]åy:ƨLѕٓVu`7g ـ`_Oo'O}>XkTa7ZRgGW4%IR3yk#,tzϘa4( kn;ԥL cE.0g$L4!i5LyWR; qۙ++IPZ؃FߨUdžp"~v x9h3 =˦=Tx.0^yqVon#ȢN:&gm҇tn^d:q=0N3>t)O5B 1{b$ZyKK/tJˉX^(uѕIkXw^M"JxaQBVH)U:q+ǝ ;+|}(_J"1wcx 눨煹S),ϷX^m]sq7_L"]NrJO"uÖ]boDmgAz qr`;@v{{xu(5_jnW}lUwɫѥ?일~cPT}c1lS{E oQm?}]ɏѩO#Qڟ|Te+{ jucǕST^5Yuv+ @YH)(F~U[Uh&lܫ{H :_O^wZTvl~ٹ]9an)X>lNx{78qW za}@c&E3G{؆'C 8`'aC%Z{=Υ1Vjala@cj(oSetKbc[AqrxhP:!Cxyd =~kײA?Uyt9x4$ %e^}=؋԰u ǺoPu;"L|z,8;-PriO cx mJrۡ >Wue6 =+|ZYfՋ?s}lauz F~B=*lݏbRK_?dRr_cxU%EDHla%vy=~!Y'X:_DBFjtb,?g"=Ocg®tԛš*&&4#X j%D7_+XdPeoޘ_*cM;եo.$Y%к .ۏ-ǷӋa#Lv56* ݝMAJ z҅]Z6ޛlgzg{s=3dia IڵVn|ww<^ߵߤ ?Āp'AnbI@.Ih~ ج8_teAY-v_8juz,yg^_Wۑ5c [1iP6<%&lY_ela&qfຓfMkϞAJ&ލ;L1S֫0&$#S<–ʝ'+)NPfUNl4  ,7Y@0SfĘ&aw=LYKO7) ̭d&+"DQkp'PV1"t 1Y4@ڃ.hH'id+WWd1Ci1>-Vϯ)uT0)oL}$\b":ZuRd]!̈́]m>۷]SUS31RTWUfs>@װ`Njb8XVn aܜe:7ǎg#8oF8cҤh!Ͽi`D>Ěue'`EX!'gzaӬؓ9E{[ȗ&T`-*TtgV`\Xɘ-qD_kF7 d~949ANJ__J#Y76:f01>7;]lQ(J,{нG\vU3+<& |Vr.ԓ|729B(5"ag:A穇>-|t),~Y g KHz'\$9e9)HVT}gȔ~j)V>_ZX6'4Y]x.8hhxک#y:{mM*]H.GPC'h|(*ĤX,A ZIPcR%*9 /X?iΞ_}* mAh V(1@8D[dēo– K(OZRPXaE6L K8~%n 8W,F-bDv],q-vG.+Pŕ8@"ť`dvPj=p㡕X/e KmD˺zTXtRaw profile type APP1xeQ[ =`qhBJX;~£s竅BL!q ٶiİs 278 300 j]LIENDB`fotoxx-20.08/images/bright-ramp2.png000066400000000000000000000277061362435004500173410ustar00rootroot00000000000000PNG  IHDR< IDATx}iǒɡ$wɰ{0^06 0`%ionRy&)%R]}*#"3""O8q2o]M6,1ˮiu@P @PNv;_-#{:7|C&B((6H`ʱ qgXPƢ_HA+Yk? tDd1EUK=xdd }ih ~ẗֿ́i-.xw|}BL@D 3x9FsGB"Ҏb}43Edf_]kj*Rc'_`"ap c5c '7k*>mb#tv4P;%ŦEIu@=]z#*uEi}Qygv{_= yjv 2jl%3u0VXERSК[F"*Yzmk 5`S &0-p{gKb'k|;_<1O!:z&mV#0@-o.@8x!w.\zǗ?-^:yVW9tsʓ'Oߌ2⎔%%`&Y.":Ĥ(R%BA1Ul+A"l۶Y?Ν;͵to+W}_7߾o޺`mW/~go 5#1Wae$۝d 3i1v܎4U\^/,73g\|õKܹsuS7nX^Y?6єj.lޭqQ5U æ1-̋s MŌ'(*U"2tKD= XW4 Dшj\sXo^`h96gK]$(*U"2tK5cGGRAL/ Lpo4=wg<O =\=t⣛ݹrumsCDePcI%#~G|Dj= X#Ycj:`UR̛D W/]OK'Ϝ<~/{ٷ~Y^rqJ\E( 3` Jࡧ"ǪĠ?9A:v܎4Î r%(֛;7޾z?trɼIǗk֎8tsOV}<('ϴusJPTD=e8V%8Hb9vY< E40aE?ٙ$_|o3KK.^>}[y0m~w ,^OoMQ@=UjTւc .oE⎔%%;~^Os{mΕ[_|/?2MGj2D$W =tcؒWDR'u;Xk=ľ~?$x W?x4?۷}qO-/AODPHz 8V%0qb0O@HGX{H4*بfqn/žϟ`?z?px6"C>&EJSFn@*͎qb095N8c cxGܽ4: pH5"lM0^ݷϛE|N>+α#PV43h~ʕWN=qڟ1乓oܺ(b u@WMhE .fͤ#ڪoaWawQ{h}CӈAy4~ 23~Emb|I`DxX8rG[J`p'&̓k3>x=w/ܼg?c5c*COEa`4{ ƉP2A:P" {1pYbR'޵=/^3OͫVw>y|WTm^`GTTz( p.ax0N |& 9X `01`q_KϾnOWz S'̃>~|zdž}nek.T #g"*ďlY?Mi{71ƀŽ L+cg$w8h޼qKݫ_x̱ξ{s:S7m#31^`1YqGJIt^p~oW/NgKhTF2&CrzJ<@ß_v鍈5mDHGkI\*`Ctc */hM@P"O:,$d`q*A ʮ :0 Aџ8nh9ֽ̜@ϩ4վ({Z 슭Y3=@F@VeQr;>U/T^uAB a. x+ <^[jrNZKX<$ O:Xy_<#/P`wJEq JN. Ñ홛iRZ׉sTqrWL^e,-n?iGF3&!.݉t&`'j"td/›tosxfu& &:,d|9<,NDidVEU$upT0X|38ȭ h%ˡ(RT3ewtx8B"tyv*m]mDHZ6PԁI:06(rLi=l}](g N%jڂO@!0u ܇߽Ն W](#],Z$ZG#ە(kuv,8\k% ,fZ&#؊V}A~:' A%Ǽ.l ` DGI+vRĜP=V޾٥h%:(iюc2UWч)8= }H >a!ƉR0YmUUXvnUl O^k' !P:;^֕@v )miƗ*/?86߈ڄvښRP_lM}Mxg*cx~00 (c>j:wz !1;< @`!`$Z?}4G'B˙v)yH͘ ,U@InVdlERE& m I1`qLL[836DGꋮF^;y>}j+VPel`)spX}T]j_=(m# ÑXE ]=_c=r{XŖ|tHD$4UJ0():vJV10A^cq 1x I18[VQ+*[lNm&Yx^g^Gh셌^e,ͶmH@Fڑ!;~A5 hՁ8'+d]ڎ,h_L3SNT[C}w7{X_`zt$fz!ǔʼn0 ̪ȱ #>Q*2>lQ]܉Yb[m#sIWHDaxV*BӠ:Lʠ`K!צTi!8[@`qԀ*6BW b {At/pL$Nn!).累ձH*џB=/0T, BZZ-Q@TxqdsrşJuZ*N|'e3f*ci=b#H>N`#5Qʁ+?dv56lMV{K3@g7DB@4g0"$DY a9VXR%,N?|z-ٙA ])b*Ou~P-22eo.BnSUR[R9u?6u[ h3Tj' bO#%JP1' 1}jj\(wz5 HU䘀Y`;InKR<]Δ׵OUE HͅȠXTJ;`+RE~GNnG7eH b XIc 'w&&}\}H;"څs2+ET: @Ɨ]|aKm7Q)ԥ)zxH?t$f"0 eZHN |[c٥8V%,N?|z-'R`~3|gUє Qn.Br R[\EBЎd)j:*h,ݦ?A@ۦ5&yGQՁp(ܫ^ Ry1iM@ tX8'%0M[ǶpM)|G FɠHDDX$5ҦqLIQ{ɅlDБPƀ19prgbo mҷ]!) 2525T:PicL,.M' QvTT.Z'"!2)}jyu@ wz }Đ' >ˮnx\5%Od,@Rd6cA,"V`FF%NJٜ\D94hpVg'^GLiH;g/KݦWՁl${Q$+d[iLe4 D륡P.kӶ& >fȲ6c"UDHu~>À}K`~3|PՁJ"ڋ5jn _j15m#cMzw N@:!FG18V*Hx0ݪWJ7?܇߽ W]L&`p6_ Hv{r:t;XY.wz2dJ.ZT+RE$9pr'*Ņt88 pMtd NBiG(C1&`a^Z^_}vH;RiJyM@A3DHZeZHN |[c٥8VK̳'llf z'0GK;@?Wtd;1RH~<ҙL5F m+jKSV{h[6mb1v7Bs͗5'~ztSkGv qZ~EBIA/ A* "T>A/LjuX8'%1R<]Δ׵OUEf w`IX4HUIcH)Dxq8^c XIc 'w&&}rk{#nFbrh:T@)fD@gW,nit]_K95dD IDAT,ʐe"96 mEeXu,g " CX@,ËWy&ô{>CL7& S"3=f{sɶ|؋ #W̕-ݘ]^G7mj&rL@h7Odp(ܫ^RyJdAU\40l7?D=a^3Vd3"BҞ;h(A"c"UDH&{cNo;[8:}`1vbhmX#yeu@&#L'=;\.=&N8^BT!9т&`>5wkMEKks}ݫ^ Ry1N 4+An5 &Ghx|%]d,@R imY[〈"$QSx7Tb`, ]K=<.ѷ,;~9*#74ȁd/ DX/ o`ζckMl K$Zɲ6c"UDHuyY{hM@  Xh`犎 c'F*O& HW^3*m6W&#AD(n.x-NVIQ Δhb}rOb7F8"U`?0jtt, vɥxz0%g+2?E!BaH*r0i"BR"J8prŇN@/] ` DG)˙H@FuRBmk"&v֘jeSL1L@~|srDH(6Qfu[٥8VK) JL@hY`UeG)R<=qz3,Em#ЃXA6ZmY{HD0;b`쬍HD ~0tv: Dݦfw۾-=`$WE^Aʫ.!cꙀ&HiM/\ިmpž/& >$B6d /+RE˜O8,& 1`qP)cDG 6h$Zz \ \f)4J̥(* sE\5 'jRéu u&<pq3)6|R۠Ds}ݫ^}}X*J"7RR< +r")t-Ag `=C0AZ ԮdǠ|WH,b/D. 9i#Ln.`K:2,ݦf?,cu*U:PWd?X!,.X.%uxj^x7ߞ~T6; hdafHHx;E^lf0,D ԁ|:R,ER!GA\6nЏimg D(Mz'^ X]eәh?6aE;>U` OM@>]@ŀ.)Z)fŨDcʼn5à ܼ[^|B'\/<Tb`,).슣 L#HPQ'`QΪu`^q ڰmSnv5`Z)/H!ox$B&6QV!o%"/KѶeޅ5x-gK) D3t R& l[1+K%{!"m7V -oL`$KQU u]5Bu qzU )bB1N@G%n˩xcqfz/0 F.Hl`>iv \l|K%^x0m#7^6DVqP&湶s)0C ]:햚8Ҟbo~ ]ԎHAxB' BDzTXtRaw profile type APP1xeO g``qHCHU[eJP!g|{|Tn0 *>%- %2x,tt=]ߢ±7€Y;μW!&Aʛtsg$ $"$ b~J+/)Y#AMԉfǾR:yASB]:diTXtXML:com.adobe.xmp 259 427 (IENDB`fotoxx-20.08/images/brightness-steps.png000066400000000000000000000023101362435004500203270ustar00rootroot00000000000000PNG  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-20.08/images/broken.png000066400000000000000000000036401362435004500163120ustar00rootroot00000000000000PNG  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-20.08/images/burn_DVD.jpg000066400000000000000000000222521362435004500164710ustar00rootroot00000000000000JFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:tonemap| 5http://ns.adobe.com/xap/1.0/ 122 300 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((z," }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?^*\Iz-l=?1䔝3NqR݊JRTco;@?wv]Bx6=LYw'Temh?_veܱ1Uɪ}T&w,⊫<:e[H&lvsS"sFsO&&CEQERg֜&qE&((4PE;֒ZN(PEPEPEPEPEP:''޿fBNٮ㽴iƓf%߉o*~ݤEA,W sWOZ\m/ft$S6, 0}6u.ٯdظ˓l\泬|[ jWWfIL0I!y3Ƿwqd~5ID\ۼX -BTHK*1zIx.-fYY-$j#+HIl<_h?-:OaE,C3)eSV9݁"]{HMm3I$lb!Bsm98AUEivzLHVR7?1ڴ%u'5mFfٓd2m;OZያ-CE]M%c"[m˴om$:ߊQCn ( ( ( ( ( e,Gqit(7lSק j%&4wFY O#-uqNy?\җ"+?/eK;V;?vE6}6vZ_t;ZfE6}6O'}W7~iG"hdKwWZI V[z͏g޵cE5DU@P`)hBroq)ÁUɴPJ*Ab3ϥ;λULiWw!|]Ҕ]*C]*CIޫ ty =(u_λUλU #ޓWw!w!u?]u?]X ty tb]*C]*C*w@Gw@@(u?]u?]Xן ty %@!LA`c8 zO@ Tu=_M5;3'e<ָ\YzgZj:)bySIi=݇cVVEu`A d%]mbZ_חl= teI),9v ~Q.xd_<忶wl~Ƨ>jdV||qi؛_+p:Tbx^X=vpqy5|Z޺(Xy~\Oucd2yEs[;zA=yߊ/,u Z]ZX 6\f# SW])'6f+vgC"@q 7ǭǭOF@}AzѼzPoo>7ǭǭOF@}AzѼzʊTA{ǭG3ePDЅ[/J$xsRc]͢;RX~Y,}N3M%ڏҍҪbZmGĵڏҍҪbZmGĵڏҍҪbZmGĵڏҍҪbZmGĵڏҍҪbZmGĵڏҍҪbZmGĵڏҍҪbZmGĵڏҍҪbZmGĵڏҍҪbZmGĵڏ҂ĕ+U?-綣tbZmGᑽ (v5K{j??Ht;CmC[Mi16l?:MTm,,H #w#б$έ]C#4ϳ<EKEExȣ(R@}(-Q"<EKEExȣȇyG|ȇyG|_"*J(?"(!1"#!1""*J(?"(!1"#!1""*J(?"(!Q"GhGeXYlgyG|>[qxݾ):=MPާQEQEQEQEQEQEQEQEQEQEQESTQ}AR-aXb)X*ȬO W񭶧weh4:H5ghdSd/$C/eM\\ũf$# ]fh,FѢe8C#֨/tEA7-ī/46څ'gxWF;[xgRA&`Lh~ln5WZ5_ygeZG BSqa 8 FybzU$9YRwLmrXm^H5y:m6ѧ2ۋB!~ |9~r,]f5cRGj+24H1-Ybr{v 3_%X3?ZJKƬu&٥{ǒ h(ɏš,1kd FBh!U$|Q}MuHoxe{Y Hwu Zڇ-5u[IPdDh>y 8<ҧ:J$[+HR#}2O Z/:Zdb {Fzb[z1\iq}{raIౙ5X ;{sYyEQh+؃U$TPAs<ĺ%إXUi.yeu zS5XNĵ?QҢ3Fk={j??ZTPt{I|%U*}FqV3Ѭf$R?h&xSzAm|]iQڀ3m綣tbmGG3i=/zZb@YekD 1}z(fotoxx-20.08/images/cancel.png000066400000000000000000000137441362435004500162650ustar00rootroot00000000000000PNG  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-20.08/images/caption.jpg000066400000000000000000000244221362435004500164640ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 200 200 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?5/_t/:$.ngK[K+ vGH ;{_w|PM:G *|?k֖6$檁!ǭG[[\7/ oX:|Befw'Yf -j[Sŋs{qmwyoe#xk[ p7z?R/wOC_k2GҦMN;HmJp*1^*'~"ӟQ66ZEދqu(MGjWa<c( Er &4;OEkŭ]۵դSDdpN8a\Z~> ڹ\&v+xӚ-O_og|)MoIttq&j©Ueg#1OLJmQ4Qea5փvݗT%Q>^9ℯkush}\-B]lM:đHC X^{ x piS6R[czR`+WZjV zPLDMOϛJR;N9>] -V ;Uң- ,RïqL (Š((#ڷ[ MkW-XSBиIX qNbou/+PR-0W=p=f[-鴂c[$$Hqg̶`|࿃xKfi^-_J'#v9e <߈Z}+M^.EDž֩slD5mtnN?Lmпoomw/'[{oa}|>gtVk>v(,_S6ơ⛝:8|'o,-k9]H^RrW%_OAm[=Cj^-^(xKn%Ox^ݔȳnY`0@4xsrxi=WW{=E,$`;:t/qG.ǙmN?{#Hw:7cwonSsV:\=8[g;ssǦG4nFqIoFþ@EVGrFv)%Z@:-ET54╄3[r: 9z(F>7d鶻dgwtۍZO.mjN0se8> !#XH?LF7Wx3ċkS5ҫ1?de߀HGN@]ni|㿈+yjpG%mb&DjV.7r{[5,$zm4Qm UIu#׷?LI?I5u7(>>a878|N_)*SH<%]xTA_xT[;#u{rL$p¹`8dz?_>:Ɖ$Tsiqmy Ask:U/|ϵbxWw57NO.x}hXpWYl,̤Z~^_;;:&qs5=3 yvKFa=ߍWg24WHG |8SŸ%7)WPn`6[6*`<`m@pODu_חm%of"t?N+{[#bB$ +7¬{WI1ĚډIm>_#z0MqH*GQ^wᯄ57->G~'TAÒ)Er^7|__!M&=N{_~#^g!nbe1H7` s[S]o>mGD^l^jVkwi(K Jt`k+(<GSYפ5Q!ݻ|dtTbWQE!Q@Q@&?`~ܹ9?fOh:?Wh?~-'{tB),U%;ч>14$} \u^ /Ub?k[߅:'eKs.2Y!]'j di\MN$ķ*)t"/c}lI{5.WmF?)'߀,־!z׊4YcӉXeI(;Duϊ^{k3{᫃Ob'RFe&)2[f^qKò=o~g;|lu}>NzW@SۊIg ^xBfLg$SH/r;U_%|9]OYuSmlg>AUasWߴ(7|M2ml1vmmcJ獤w=\oc{-Om1dm>yhn|Ci~[m[ ; TtukؗHQϗm hˣ]uAs:}R4;иR'n7yv3HoL ~-۶1;Ԣ2zW@|lhS Iͣ]5AFs%: }w:PG%? r3sŒd:-Gm1h u z^ZLKu hScix'R?|ڱ3Wכ_Vg^Վyoh3i"K +!e"]ݓ_ Ŷ3q%V1Mmt%K) t[ >[>ǦxY_ƚvZB4XhjRFuc0[j~񕾿W_'#;P EJXn$Ԓ}}-lmZ 5 gÚg xj\īQraEx[ }k(/-ŠtgQu;+ (]{+ZQH(((s/z`¾>%j7P cԞ4Eą9mNX~c:Jp$M^ӽC?fZOʹ&yI!Yn>d@W?]6? {T6oMGNOxKZLs ,V * v:_Jm6}۳=\ ᾁiZЬH7Cc| {Bv?biG>-յO7 {Ǟ!Kokwuy^ر&X 6dcS >.ZxI&DN)!Ia;ʰ;w`zGH-=Zh:y٢Cq 7ZxjIG`tXTH0(oOT4?__qJݿ?Ȟ~:uxHWMcGsα ~k~&ޏW /k.RWd1rFԍg}3ᗄ[Kw- EjYRX5&Uogih6vvc-G7vv'`d5sT Zyzk}ŞKv3 `M2#QsgϬx{BsTnj5GWĚ%ͥ=݌=•0F 8++^ҼV'viJ4Ī[p ?᷅tHmY]}+uXqo1W SZ&3qvj֧~ɾҭ5 b?jڥ+2=D]ȴ-rr{ѵ_ |xw4/^3=Ʃ-ĥg'=목藑vQHaEPEPEPggд d"Bz d|U_ǔ&m~1YAU3]sC#Sƭc~Kw g3J QʬP󯅿~S7%c^YXcԑ%xexH!SZ/ 蚖ݹmWi~ XRClyx~g&v>:5^HI0l#*b__ӟ (E?@5(tjS4n^0HA*E6ve 2ےJɈEx9oxcUGTfsn"鼹m joٗn=[>8T:^5t1&B2'51vbʯ}5EU((((((wX70ۖY隭F֍@kPYnG$__(AJ A+?W i4AJ($_i5~ A+?G$__(AJ A+?W i4AJ՛tYnn$Cw=f vdy dBG0a  A+?G$_V?[+h[ QA/o AܕVb>7q `&x0? 1000 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ??_(n渕#ˤX3s_l>|au^jl}'?DV?ڽWc?}'?±Gڽl>|?zG*A ,(aOz/t/1xKKDmϪ`Y3)8x<I MN{7z5毧թ[C-˰ ayEo kʻHOQesC#&j&/MtᶐrqsOt55\y4,H"˃c ;- >}^ <* kaچjMy 3cn9S)^ hͮFo[4 y!SyҦ?{l>|Ms $Ot?<-3[ƍ-ZoǨ5"@s|Oւ_spen4a|ST]5$bE1T=kcJ"&DT| e 5ŕn#WCFAqgqkj6Y%@TM囹8QI{ %7RmH}O,憗mjËhWjBͅ9uM!U={kC x/[WwY.SZD^6WQ' }+)[#P3Lp p}j͛S7.]b` \wwr^lv0C*zsVTbBef H ?\u,uѕ^FaS<2uP `8~{B Y 0TxȶjC wܤv:eV5n NX'f?4_ WZ[`Ln=o^ΑǍ߇^rjj\_0)sJFaM+  eYոf$,g=2p}+З:x-yTG8s8ھGm&ʼR,) a)Q<޺cӡEwPrHGL+1٦6~'c"oo-ZY?)>'ksmmżO|Lqּ7C+$B0N(8=tĺfmo%c) pA @x>'nvz,I]'6{K)Fr(Og.jT2ehi95ox]x,#joD@#-:b_,RL*ܰBĞ31޾?$RkRT6@:rkƩˢv gգo> 96DǓ'tpF8o^[@Ю|t$¢~o8s^5]]Y٥W`wAkQW bكq %9C!^3}Cr:'¯IᏇK4X|<q_00+|!a+/<xxE S?!y5q7?$KU@e#9W&Y$a]Pƀ.T*A#w[q+ 8{߂~| iC/i%[c+oOE;'_}Tpvmל*!Zj>>PUNG/|HJi.BN+X2Ȥl)7?{]Dxv)%c` mǮ}ksSi;i.n QeH?"Y: ?i]cq:UG#& 0{¿0xɷ%|*CZ649,sV44F=#r| 5jƻs8 v5|1Rl= S>Znx[Xس$ClC")9$.3Ҿ]mj<6PZPijr d͜b 5 Du{dYU6Wi |F>LFkTQr~},Vrj\9!y#z3_$o'»)c 5=ėK9L888+fVu 2)o~(.7mKV9n-~jTgkIJ\n7Zkw%9}PBOI韴w/\e++kD!D;+PA}5|CmͼUyj S}ȹEM(xu5]L.φsjB8A-`2[ub•$2HXvum*e^y6|+(PImt¾CMwyi:Ok6a\;ɐ"r6H;}ϋx\3Ma<1'Y?,x@8E g$ִi2IdxʒeDGs ArA2Tjcu%u4GSR.c_Au/<>M t/m,!I@Qo h 𕾡o4wh֌ѐSzqvxVլ"mSP` a]EnTIWAjCtPT)EMώ~7Xjs\VX?.fHdf 8*Tco.<ђg?*+v_<7|g{ ž-gӍI"i[hC+j=|-xf?V l;`襰 ~J.½'Lu͆K-_<BAЏ$uq,lVuݩ6ȨjG x$M<>!^ϳkM蟴fxfiv͘wnyh Q'H/ 4p/˝ pΝN+pn$i px'SƜBpzVM>g8 {1 yw>*Ӯ^{FO.0Dx'Q qa+/-D/\֡ث 9WCkQUiI@JlqF?=InoYL2dWC#9<;$𯞻thA CeJA{R٣fciijMhMte2KgsؾC%b!n8Q=;]x^XͲ,ɺHIǨ$u4)KTOܒ6nJQ8UoE/C1 ?xz3. *H%r22>U+_X˩~RZ--V7Y0Ñ^U@llK{y|U,YN>nrGC4sX)IKG!R{4_? oi(uM.Mۛn\}>\&3F_%av,m{ +~1_ios)Y0AhYLHP$psڶ!OFz}k8)ۛl‚QGBs_5ޔ]ߎgK/&;E׼y: ᭈ],^iV}4X␦c}[''5jQ5 xJYfj*3,k4 #43YGAUu?m~:Դm.U -2Ϲ,䐰vg<.ۺO=6Z/_G[hq^fBɦDd;ط+`b?tu_G+teε%hOB9 e&S@v[b. V, s^fqK Ulz/s3᷈Yx#iڞic9Q^ʵ? 援Z q7?.r|8?.ls'֏Z r|8?.r|8?. i>}O;YvG;YvE\I'ֿBܟ?? j?ܟ?? j,^>!xCIJ|UqoV],cC4NTrxj 㗎<;}&;ymݬ-%dQ#Y^U v}qA]#QqA]#P{ߨ'mO uyc K㍭"Z>{q3uRYV#qo,ıw3I$_Og5Og5מxiy'oRxapztZ:h$ܿr5=Ӂ_ߕ_:u=gcmd/!#YZƩcn{cgH/`IsmRAON{W_oK]i Ff~+KmB-ۤYV+4e{b8ힽõźְ<=7e_2I)[rO$ 4W!$uf+#.Yio_:;r$t\q*[_ Zhrڢ%K0=?yJsB25qk>Rږef$w]皜:BAl%8$HHP@^xiNWSziYo-m.4GnrO~[xRHnff nb>ly^U 䖒ȷh?,!An93s[|@ 6KǾLc.iTOW.5tnǩk[̱LoHh@0y 15z:o\[(vTpScռ}m$=3?Gw"[6MYg?.^^McwQlUkgVs\9s>qR RBڥAW@16kʌ#EekJOMi֝ ď+K:}GM+_-_m^ZH r\cn 7CѓX[H[#g#O1a^]|(>״y `6:$8U-kMI'SV( spy5XRRT@Ⱥmhxc{ww#ǃAqw|^Ee , U1\ 9`N&9qI\tH>ƴ(h͊Q1Td 8n7h~7*&6F2IZS<b3 fȓf9gyV6>ZnY^,p J59#I+ƒ%'6]+~5kkN>JM5/t՘wpv)reޛbVĴ}ov5$[39&8a:"\\+'(q+Y< P Qhb1~`IvKr8Z;-;7S^2Ln.pH \V-ԚѴeؘ `n83u.'ġEDNXO=WeGޒHwį?4.2X\Q^yj\9g,ξY\c WȾ69C\_=ˈW  ^/ [\߉dյ3yv|{Xgd9LV#[6+Y/ ;ZBč̻vwG\ך*ϱC u ]i"kM2iD; Bv21ZLi*_g{ eC`q^EmCKaw\ID%r:;/ Q9Y (qǕ;cӭ!y rw`y3=#6,Vnߔ \]Ֆ9#@Cl,sVזjјéĞJY!Z+Ǟi5+_ wq$RZ^yq0Wb5k?\ם/ Jd6O:Z+o%F@PNKiFI<_oϪ[-jҸa' 8d.@0tt*>W y%Fo*L̝HPtucW%#ysK4gpY 80:yPwSMrc!@ ;GoSW=e]d4^P@6V bL_9,>.YG1-."dFDA?08>Wjs\06sV<5/2.CG͸$F{c⳯FKƪM6nG Z?6~&{m $,Jq|!r2j>t$v1 6 @G9qekj&ͧ=K9 d֟]Mֺ\LQF/: z:tڧ%ك+^;vZ!K8_V6X~@8]I"h$`-VXb0n1^ Iw26H`q< m!f,YT#y˜]Iw6jVVLk9l76:ҮZUVg[(Ix#E0ۉ:vɩ| qx*=džYUZ2@8W5s{!8\ڴ{o~&)K]Q-M.so-vHc2>|r2G8Ocykhup+r[b7e$x 5ڈ<%%wS3B#^X*AᯉzՓUi25H9$z kV}fuK5^c?g2/s'|P᎛{KMcZ,m %G`9P7F2x,~3|R ,>)_ͨFGK3fnXhJ`y')~ imΣ^hV#UUUA$ED̷Tuo|C߄u˪j0Od"-$r8>ޝ)A}3냊&/|\e5mz܋+b%+X O/ .&<^{) ui"YN/ZJEa;4E;s76|CRqk[  !o o&~\suu&~=zy7?R֫Բ('t?W+j ȸ8m۱?BS_Zu/v7[;Ec y%2@ʹsl qoq&XxS=H"S`bDX a~Tc#= mFuW%ʔq\QTVy=+ռ7>Oˑn[O!ӊNotHfy'kc?ϯzo&9v-S^\-,l92YkSW$2[ep7 ^.|E-(ؤtbF m_éf5ffܛ[vSOUc>5ȧ"<~ɜ(r@xmK0l҄fXrefNzRx[E:T%l-tq:{Exp\is)]S VGiѼ`빔w (rL^zf{u{}z8q *猱8Oö;I+8?98}߇Lky2ʘC8UzpG ]2H;^G!\mO1wieR(:u}JuKu=%rfe`3K2s߈ nt297!{I} p,M42ZD,OxeGFRmOls3í"]յX4};OF]opIr킊CGZ|D nj5}6W9_*=>q!_u z澂I?[[L QNο+Ն )N[s18S覹+UjGs!cS$UZVOOhַ[.'&;I6o݅ yu7x-X286iߣɫ[\+$ےq9׌A=)G[M>Ndv9-FDxp3';]5/#XA|4~βlx7=Hϙ ی*eR]uax8n'@\+4|IG6gl^Nj_6D6q鷆e۝Xs/3^J(BO}]O[ɧm",%yKrNʏᯀ5ދ;E;B*1=q} IY¨@ 1 WD՗cR?#~/7?m Z]er9<(cڼU}Kkas1.d1Yd߂^O=a>, cYcdfBAE| K"bI$Or$.I'kCe'oOC$GZ5oC5:FRUexj k,$3Ⓚm>sύ_W/Ze͇Vթ4kD8PO2ĀX^)v˨fg : 5ЕGOU7_?Og ܬe\mFF92?+,Kt*_HtYha8q\F:Zݤpl /?/\kI:c 6=k*ɿHZ:}^?GaxZD<3fA?$q1Xl=}-4N5wШ^1Xk[{]F!R"Yٳ74aAS; YcmG"|Eْ3Xs:u{٣ d]Eb2GkO Ow ǁ "[NM4wxVhn VAOݗ]~8{;/=O4 'Av,_j.1Uo3\G⍀0}ieX?j Hϰ'bOm^+Yƾd. aKs '鞂ΞsZ)na:ik݂ 28 h_Z/ɧZ 듵^IXb%]?öC5W~"ūaӤyY6>p%_ܬNy>O_SDi$n`v.ȋ5ϊf8AQ{Q\F T`LӐ?DER~p Yv4mNүxmGLTn4PEc2p `uEΛs-cP}:ͭ Z8mvis򑼥e->>mH irZ$v;5Ū1u1 Vb[!p͐*|ߍu湻O05ʺ,8"k~;ѮRK{c>`-\d;d׶i6 Ȯ:I+6Y? cV:i3c6V`a`a<=jKFeg:eGu$19,Ǣ+<ƺi`TDk~Zgtk U!x\ޮ9v_)ac߰/Z\ꖚ} 8Pkѯ,^H c޼ x+~@f壛{ _zi \ρix ot.X*L'q@M<}ﯸPiaM{ۀ҈DKdehGL^&ᣕdT;Z&e>ş%鿉/<{k0ZN }5~u\x=|aoW~j[KLc A0#y]uŷ%HYbiJ\lRd0($<Îw\D˨gfڲLZúnNВ Ŧ gTL}*9犗RU~[.Iz)F0V%>p>ɶc4zQR|OÉbޚ! k? eEUWU6ӧXmgޫF^.#;^Gbiw6z5,|0n.zDz]XZX$rhᕗ"'c~-Z *IK)OϾKxZRFx@rv@&J+7[oVڴVik V(xdoJ`U>hԂ9u3[%<`d,.DwN>剁8O\i>,o'WV6^vZO K;?^:_,OYih!bh]=9c?>/]^Wn/V*x`"]ool'н)59FZW$eP냒zG9SԯY+%;p8 GDhi ƝgKNG8Tu=A}\}V3WrJ7y͓zeq^oiGF lӖ,,GG~5Pkˬ1H@}+Ξ8xγ^W}F$2,Jֻ*5cYV#J䠈 NZ6cs9kkHiųgQbF[=+'y`E RO^ӥpAӌ{q]V"mftz{ҍKEh7f5H* 9'۠CDr ^E =yJ="L# }? I'4vp+nn9V̋ :V64q‚@U_C9<סFz|㨞:Ni@.0duM _MAKIy _e ˔kjoV~jV&^$3ˌǷt*3m;I4%i-q֮ǖ23x}~nl9;`Fe8+c¾pBLdzףW/{I */Kk-v~ʒD{p{=;·w9l\\cK,`fz<%[lT6S$n!p<,UNH>'Xy BVc㓟+:nuR%yhX>9x h#kQJ2 znE9 O?ͿzՇ!2Jַ~$E y }q_N Q ִ%F X,I$M{,:r+xR{+{HH^7RжXx>YamWQ̺v@y19!H== |GE,rCcw[$HNc K]eza=|;^6>0ֵu~в6h%16ጓ^3B*pο' PMO>2|E8dP4{a"qBqxzg, hFq[.2qgEKOsk U%VC${C+aX9o|E41=O pЧs-hz]!RpkN2 sTx$w#ڳG~~44;yX5Jq»y }MwPwvR:jvZAsL :V#ҿ6n؎ ^|@yGu H~P$Ҿ ^|5WZgXmN d::q¼R͡+hCsc!WR@5WS@~[p9Z#[ek㶥 Z]^#7ge$ofRv ^we%dc8>Z*Sk^o]2(,T Vs޸Yk/A$?S%fѶ;Ue'i_ .CGx񳽨\1;r@x21dF>b%R,zo%"^5mj嵝v LH@Vچ`GjBѼ|D's6Eߝp;z_7hz;E]Z0dEĊ{SQp<LoxLrtb!:vλz.4bX$HIyKs_u?2хѽ9 ;2wos:qӥx_d Me0 @8\D}𸯮cH"(TQQ3Ta1Cޣsfj1]m [־RS]GC$zg@*+`z/n6Sa.s;Y(FWkH/#"s+xI\2)ׇ&nC&oi:dve=U#Ұ.he7|'ky exu Zz/ECuiڵ'^G Jc.ꟳ=u R!"6ˑE~Y{W.b=5[jZ=ӮerTW85ѮFnRI[7k:D\ Evp$qsVr彎qiٳ;암8D!>++| tV@|9?E}\>yrݝ}QZyk5m/G:M Ȗ-ct?9:wG#sC?6\񽅔KtfVpj_%׵i4 8/ +=*70#WQGo/?+-|_~Km+Ne6",U@Tn~w)#>^h)̈́X,Đ|.p:ײC~ i ~uMZJduURU>SIYWHռ[^Ӯ4E$xȍc+ tǿMk߯XIr?~ i#HR<ݤT9ʟZ *tKi/ IHqVmJ)[ *J6>ȅע*6˟4xVX(5{fuqy+*1ɵʓ<#wRΛui]KLYil Bђ@rk_?Cտ U-@V֍~M?>ZF,xT=1FT^ÒVi0Ii4r~ITO?1 s_7kRWPFrwT~J SskU"/xV39ehl B7`{ou?gnl77ri# T YӤN\b7@>c4P|9I~{kO`zUøO_!kGk`Ă;vZFTo7? 9Ӛ4:WŌݻ<J>!~ӗ=lN|cwOlw$gӰW|bd>P8H<#f/~{`޵OQCwFxS+O//ܽնM,TpA-g8ꉜRT?^ɉ1'ϥo~ |osE6ss֕[HeԍN:]4:=Et$7 n"9Fq߯Xu?u覩2:ݟ <\.6#W kE»*+kN$_p||,Ķ2aC~%ow݂OخfZ2JɘS>B,5Pi:e܌I6nbC\Yp=> `7Ǹ,C6{{X+* A:*&Lt$˯x{[^ė=>L7 Uo,#Qt?=XN0IGpQׇp"l4hF2$޼u*K ]J)ug/";bD0|( Xӥs5>an$!mLH_Q]]ҺvGW۬gwktsc&,jGjE>f0{t85;\zΏ2\` q]>- ?q7kۨ279$r@xםAfY ^]Ӓ;}B-9"di*BAT-0'!zWBJOCkTy7k?~Y.K)`c-?o|I4$ gE.h$WE.X^G?h13f9IW;jdZ ]v 95c,,-QQAi2MkHIeufrByϯ|ycxMcC5(ehNb,J0H gf~c|k7O_OcZfA% ]H+@Z~vo^|K;c&r WsI 1]<_}icY꿷>.|-[Ҥ1x=ż>.?RGo 5~Mʪ 7F˟q5=A_hf8KTPvf< xVilK4c2rW#W/c'kDʛ"Yn8݆:NlW!% |Ekr~ ޺.NQ6 `iVޚ[V^k 8RGʕU#q_I>JKa Κ-r>q! W4\o=[inǟ u.?u./k,ћQ{gF_^sMYӌisGS-c|%?5ʾ6QIn-mm3WXM,*J$B$R9}V{Gb+oqbv΀򍃆+#&56Uծ4 ڀp&4 ~=#TW6ό?>fyd]Mga3LQO٤s5o0Xs-,eQ+Ǟ*F}w0faIp%#3 qֻ_wl Y5mI*4?zFoF{^6" zԠ#;|RK.X*|n!`^dget[k+x>X vܣo}+%^lr:;mBq\>TbC_5-$z B+5Qq^w3[d3(D3طc-6uڮrmt_KE|y/m\\^%NmlUmX<ǯbbֵ]DI (ѼŌeqӊ=B=(]¨nJXh'M^oLe Tzs~ WoUK\1:ȜI# c (e?ztV: 8$Q؊_~3C ֠׳cʳӗϕӅjڏZ%I\0i ,Y.XH=\uW5 ' qt|"mo¯A'Yy$PDŽ3```t??W+:V?ymC꬧ЁSV+#7G*H*A8 56V2| ⨭xzdsۯcz\eXv5YǞ 'inKxbqu ;@pΠg'wN.2!Lr ̓Ӟg=-_!ng5Q{; &G8aG|GQ\Jis}di[??gφ>QC"^ŒYqHE'%>lM0ʬdp랔WGdxos* #ƾ7/ kOwӛ m{9Ec|/Co[5ku?LR?)B eTz:/Ƿ5QMfk x#L{Fi%}N 7`c$TdOeH6 Eݽ!7.de^ʍgpk>4_@XmvZ]ncWFcu1rJ O,p074 [=*^[jkқŵk_9bafS,Ani_ͧ_2QN֖G1|2Wѭ<[uA{(rP~f^m7BA*8k{0 m,ibMx:?eխ}PT1\rʬ|]Ny|[c9'u2kiR$P3E sI~~ tώu qɦG{â:= X=ٕ||玕z]_OdtKgPodE;m*yItbO 6{+]ou4k7L\ b`줫3*M7wm5$ \jWS qnw|( _$$گ>!|K񮃩}@O'Q)o52vHD0IQ[sx_ajz]B~˥\sF r5+r)~̿OFtQo6"ȑΡivsx+ƁjGEmJqqyq{}=PZY܅UUUU]ku!$QEw:bzEz>Euj%rm2#APIr1\à&)e 7e[{ayn> 4L`ryjXK=#ܓufFN_!W}/QäZ1ww-dhYORUCkgh^x L,wd/jii ̪)$q#W14MbE$3ϡaZg+zuSPHߴBzG}^ Cj9EھzXuym-aSby4}~'+$H ~C߸u#/v7_sᭊ]\\Na5lDD #z󟃗׆݉$`߅}ԴERWqhJUlwWsLZHN.?Fy:^?S~%K/>UI`f,V4 ھ!O'|Fu,̍8@IU9=b}? H[}sѯIt<ڐug~mSikh|#a >Rv Ϯ~qFlRIc=þ }(Y_Nk&0BGʥI3¹]1:Q_hvNMUH&|ptk7#+|qpk^;B*v?k]6oáiY3820I=S۩St>i-ZM ౽E|t^kao)זTk %|zW+c,R4"erZInk..4츦EkS+ƥG{Y䙖M.~ڬv>fFi?U?R'Ҿ#y~_s wms@b /:1sSkNE?b !c[zk %052.H+*Ngtcړz ݲivv n+8HتJ2,qq׊P};VԤ׷WS6'bz0=+ۿoº}٤X#{n#

LBٞ͐$?V8bjZj/cR2k=-!f#$rz$1k;"Ȳ!z?S]Vs&QYaX3c͸EH @ OU]2+ fX$s$=v+30P;`1XҒꨚ5xnI0kyx#/皟sw<~:ϩ5`DڴAa%N@ 1x?.G[O]ϩhn>tl?^?s;t -=JO:+NHkf<.10vǽtr U&57Oh26Щ`{¾mGwnSҾ<\:{Q ?Şq85KXol3dƼ>WByJJ>txO iwaVCxӯ|]4d&i2#= oLjI;}Kz}ƹ[=[I&Gzr /+.aizݫ`:\sӲ?{ }{+ -9QFNO9>}YԸ%󴒤UN~'^]moH 8w"ŽA9湏i g|Fϩ=>婇mkn5?hMxv9dLmةpr~-1*RI 6EV6E ʃϿ/Qּa}{ hfZU9- HW.,<IhӵK{{14m]8\csY`®2C(kΣ.?jn : rz<|MY|q|S|4Љ%su&0#j)p ^#$.ٛSf(C̄) ;ds}ySm)ɏ[GiC$wgjR=b ʩI? "Zֶq.UI$|p9xT Ձ3)YfĜ, ^dm)&ÞI?ue>$[>ȯ{W_x  dݍI=`y":Eck29U wIkq%V67;POY!Ւ$cMh[r=&h5u渍JGsyamAl9;@ڹ-SǺ|RlMcN`h=)5QW R@ٱ.+<*5j}kìëLllDq$2ZZE{ wRb+"]F_hYZ􃴐J q׸i'!T@G־4Ÿ +)<{s%yJv;}Mk6e/"0tVk7>1m/:g5/&m2]iV=K;%W;2Htg?7'XK@$D"y9#v9E%){˱<g٭3AX^Nx4T/?{kfZl1k۞E}dvGgi\̟tH<[vkKcծT+ VN׏q]5|KO>.5I,.-|/hBR;H.IRL]Tꗯ]Ϡ|K4b&Ki$Ub7$ ޯ-h׍ +[+N𶯦MhrA=6UNd% 1 y?ƹ`KӢ%ׅ4{VTe&s5̋5Enfi,zdK]kAgֵbrYcMhfӄRK e*X p7d֍Cwg <'ZnG<4͇.ve6OǞkԭ>+Zeł闺Ŝzb^YeI$lም4AELn\ |9-nAsu[ocoo$]ۡ {js;^W>ѵ/:s{\\ےXC+A}dž5x_MҼWy>l|+ftM.Jg2Hڼ7QF'C /;s[:#[ ֵq*UOFlʱɉ4&LHH׼ׯa_EW>vH{/nu{ [>wFY0Hnk_iơZF"~#_Kh̊\i;jѭ[0YxXInP$ qIj_1gowGuo?¡hn)5/ anXHϧ8hŘ Nx=ަXaۃ[TZQ'$=OҼ⎍>'Ypyhlm?G^q]9P=ldPן{Jxu8˅j5\Y^~FTh9=q*2:k//0I4ƇL{3!k{/x9͢kI?y: '}R~ 팁kV]vp]<4>4^ۛ·W7^[[F6Cn[Rb@R2q^1e/ED blOC3+>NĶRh6muf.#(ۆO;_|f»wqs]]GV>mua$J|HXn(Lnu=OSbcPmCgx81 A @΍hvP$,͈a 0uO.+byl296|HmЍZ*>SתSDS5/7ZLRO-n&L^GC-JْWܑs^wWuژT m lq:O$AffT%*8ZtCQh~DGt>^4O4ClyR08 )q= pD7DPFCںf_-\~aYw:kP1j]ZnrJ9Mm$y c>W?CӼ+g IBYcvvv+jXy!8ni_QPOJURwd2ma~^x5?Vy⿂>4 ۭ686coLUf*q!H(էCjǂv2l,4N5Ki[-ͥ \]a a=Q۰=lR,u}^HA(h`,ۏ 8㹓zWf_#l2,8 =G{t{sKAoZhhqkYRIaed2y-yr3_y&)5> *92<= MAy&g=Lnoy&1tdm<[+412 ahXVKuwF9 uߴEOTxHJԲ^>i l9 6)xijp uuK ط[ݔQf _|!cfX Y ~)|>+Y˹1s[Q8eGOAg{_e^cm/&E9cYcz۴{_J 6n\͜Ǚq“}v|o`y|E[L.nlťѲ]k,>FB*ė^(hKd̀s_3|E*UjU!,2$Q:r=k> kkH}9Or@I%od%ZO+FpGE $2 ]89'8UX{jKu~7񖡯Symlwk+S}'I |- *<'VϠM^zN[["RF韁;)U.G<ʲa*z׎|RkmbC-m?|'N]uBO%Q FJ_de4\ nI0iSݬS\jg4YP1Fe\W?|'G'hS4ed)xc90. >1cN*/SOg8%p$9?+kJ .B 0곴lgj0&P,Q n>nHG&Qy9`9g#򯜟%YϡxVl[d ;eIϡZ&X_bom m:Ncn:=1z5O ^ t"3gX)B5#kv/S4-A!eb,}+?ŚHѲ Uki$ xOl{T:$/όƊ趹t0e ?Qnp|]z[cIcc020+v+ŀU&K^]J3 3Ԝֲ8)]eU&kXސI$ я1v8LV$oqZӾͻWSң, hW``YO^d-ڬ;# 7ƺ4~b,;|àXLo+k*',ATd> >^ߝf2VvrȀ wxI`|ԒAE82yRbYWG,꯫s={cx|5H̐.\𜎹u@nDk|6cilxkUլi/ec[Ԑ٘`d w>\3+C`Ց_W씼*{Eg\(F\Ʊe6qD[C%o1v#,NA,zR ѦlwD />_ yzka)&? HyaIq~ok0 | znWR5UaZ9ݗ@f|M?b;IQ$2J-s7wnPҲ|L$(@Ns mWE{{oo4f+UCA(9^8Pn*ha+?NOs!;V5_=yŝMjm7Jjhiq rF:_>_ /,4߭`3nΛ1dv`Fð?g/7~ NǶz]%[DY6(lr氡rU۷kVif7V8Ѣլ7F,r9#vHe# x EnC+sc;~x1i-&.$"GY(,Abj 1^Oֹ]0G5WR2+`NѸŶa ;U1388on>s~#|.hy8!#3 %BErXKfa{@g܏Y{{i7*M_(ӟ,=/^,ֺ͵$Hʊ7c N}Eu`1>'fX~st|&y}s$vڬ9,&AX`_?IRQ㤻Ijdy!0JZu15E8I:QM?8MV/ݹ0#񯪡Kv+N8kw9~7^/(MӴV4JXyH5&6<8";_P~?[<>gR׵KW&ȑ"ߵ"݂089˚/SXӭ.^wTVfU'K7(CJ>]X:֣fu'jR]bx>NQ\:u>еCߡܷ+\C2F_Qw&#@P>׳TkT]FjqR75K` zWAObLB3Tq߭z._RC10 dU׾UvG?.hL\ <9" ఑y;y+ oveWwQ…W $υ> V׶5yImx As[_~0xxXj`5ifE28A(iPs2kU+[P9<_={ij:'~ݍȭX'!r98 TAw<#|V]wA>ɩjSmIR춠7J7}8l6ed%3`}xJ;(Jq]\et<b;ל.E8Lv%h{Hj*RgOs$eټ6g'8ɿz^>Q>zqE{cES$((((/vׅV'ϱ: }+U-m%Kw^kFӼCC冩johJ֗}{^Q _~ ;Uҵ V)IF-ng7?: ) 30_>!Yzd]^'HM]ҡ$A-!eN;Q~K _&C yHooidzќGCSEKkwڣizmt 2ix'КO>/YK{a=՜ B@fHgclpP ^S‡T#֒ ݠ?޻ b2'#Ws?AhCix!H4|g@1\^1š cY -" R}mBxiһPW7cW~ݗ-W&xg~"t2AⲗN7 ;n 5v0$n`Oo_k^ M.M3P(jr_呌F9\IW=TgrI?U.&i"uM6pA)ƾTԾ(xƿ u h_qãb(Q@U+W ;[='t?WO{߸v5lĄ?_ʽ't?W+ _7琨9cxO" >b$2pQ+1KJ x!"{K4:}U6F0crj+0>%@sϯk/nV^91 G0t=B𭆍𼗺Dqxg\Pۘ:Y(p޸wOUkFꌡ,Hu#^WW/l*`ٽ^3j|GMhZuރhLs_߿WԠM)RzzVT_ݩ߉~!iz-K[; 1?^uh[?-՗*Tc*?~y:>%_[Eq8ʱ!=c_h|l'Nq 72nOSj<=Ǒ w,*5S{q-[#0~ SI:Gs_A2Er?VXw޿e =a~3+oYgL.'B.~uʃ@|gƄxVmo,FADfp;>f? ~FR.,5m 7cp')[bT1ٗ~y1FH77|9dB/յskxhڅ2XJcs!!j%(<|'hW Ѽ~ڄwKDX2ȭ@>j[$*H"\OInH$d%[;?o 0nt=޻5boBΧIa, ZoZ+M\Ep"Wq<sk#h)SVc_Lo|OIV)~[l!O9pi<֓Bt~X@aAv} 5.[b0[=VF']5GpToVA#y֥/xMWM˽[4X\0}}לX0kw!ůmJ6oQ'̊x+*G8lo[j 5뇏?4n>Ƽd!/QwcOuEM.Q+˯k<DŽs3Y~0q; r ztR'2:p1kQxwŜ] >9 ?ι+IHnZaȀӌ Ӭ,Kg>@~Pz'8UƽhiveO̟e^u} %Ac5ϟR|%XeQb2'2ğXGľPzxa]ds$p0}+?g|EWzjWK}#=r3)iy;4[$V~@7 w'{p+OL (^GjR1,m ן!t<]Uԣ`=}8Wɿțdbφ> oe"N2k{qWioD3#b;檧-|pveU|Gӌ~HǏw-~M~UiЋ$!mbW~ O?c.pԚoH{oƩ -{E"%wA1޲5-YlI4̿1VoMA ۋ{ЉmďEeqk:x'!{;Ee6)1I{_~~'q]wM.d]X,; @8 ^ũƺw&5_62+;t+qq]]S#n~9]  u#2|с!V tcv յKRvyut2`2;»M[R.!e4%Qh'zH|KbdY&l茝RnHE{+g~& 8O4'O xeVԬcCnHGΠ#VֿK*M HBB g#,,5 ˋo[W]4]f dc#E"٘%[Y~z}txįYxWEuNqmB+s@A*'4]5Wfc >P5Ե]n2$t.LPm`:_cQEAt<H>s2QO /*+[QW?ݿJ?ݿJ諟ߥߥScϻ~cϻ~N{>Q{>P:*=G=@&s FYEnLW2O8ea_|?j6K}^1s=q$I#(컰: zK췚;N..t$ϩfRIog< -4߄ Bp_#]K4iG(TsZ~GW7*M&&q2"Xdy_Cƛ46gǾ3F;_ nj!\^`\@w#r8>A-,m 8mڹRdԼQ$~M3NZ8CH2dݞ?ji~rC! ,{qx$r e\TQ8=OX܎$(i vzv+9TZk<l[9-ҏQFm=Nt|;ិO:YHS&" ¬#t1Re%$.rz gھqq+o>&sy8 m'8^:4!};h+;h]rPeH>f7vS=F8QK{akugS@W(\Ȏ,s_ҼI5*ŽN N߷xp_𗊇d6Wsnkoq+)?.w$5A5XVoB/>W^sޫٳ>8zI,m9qtK" 'p6>?`֭{H.cmR"gkFN$yVSw;be;-Z`Iih|ȋ7&Fdײc>]T[6З1-&46LB$0.E`cJ:-kAv-Y>dc=IWa#~_xcK׭x-JM2DIY 6w.:0k>7GuKzc6>xQ+dR\\e:iԔ]|-g\Gw Pţ`Nt#Gjn5b$@ect5C Z]xSҟWM=ҭ%3*uʈc}o]DIʹG_=*VPwL]m-sVdIbdc=A )spkoZ"/u K(mgqslC7w ddWĚÈ>Rd-?^ wdsm չuakϯB <SV>hZPSRWaH;~7Z/VxwºeS2@">J,fsk?~&/tVh}ͻ1*N?.!lvM'E%9f:fZQkZWGV9l޳u&}vҺ2Bׅ?߅mK&jF4{Qkp9e]˹5P%Gz~ :wv3elgWnno/巄A3~X߹5 ?gcS'a"6ߋhl{0 lN27Wɟ~Msi{xrO&k p7t6J(yuD.l |Ɗn8ܤx\4yuvHS;h;,2[ƹV?0SKťkw㍴NˍV<.BU[H20e9+ Gѭͩj:,? 4n˵`lxQӣ-b6}*Rhbnh8bpܜҫ.Oy(RH].O,OakZZ ֑{6T<{׌W݋;xc`Ñ w#i^5Y4q2= pA9˞ZNԪW/ I+۰λ'?u=q~W~2kO[Ug%v!󝿈NOQyA$:1)=irZ {q9=:2Ŀw5-kت|GO<8Tk4dKnÔ9#jU 2t"[F3^lq}aEyΓ; `ren(9I澱1w5h:سo[:B}?;G>*RӼPto-ÒA]͕%@8-_e+a&90cM=-3HۀFAJ-_"yI8cr֮JESPاz-]=J[kH/+}I$ w&%71J'~s#`cBwHq3{ O>$Mmtb>h&i@,r*φ X:%I aKG $}'&cci|Oo%v|yM؍/D8!/2kmಷ:F9$n Wؗ%!˱CHoF+rbU-C3c'}ޜ^rЌZ埿QogK+cXʜ? h6_ ijQ%H{_F8YQEY!EPEPEPEPEPEPEPEPEPEP\>-Z&2N9]y޳f+`d惞ȆRcxK&ެ}²%C1}KVԲ,':gO~FnZ8l\tkk=n#˴l>Q(mݭӮk VRvlLƽfX|PT1` J rA=ҳw$gǓ9ʂrqGE4}7T]NPx NXH$aUxV eH&c''i+U Pw)MT5_ }#xdfEd8]/ {컘R6 t5Դ9^Z2a<.9{yG<2g&MXw];OS b=u4|"mс=+twi)tl_PfFn. 1ح9a mi:cIhQFѩm<&pyVVt獵|ugo5ks%F!'y K3k3T<JċuK8t7( !ǖ!rNR~?֓5) Vc1vkɆ-znbKHEIN\t&0ﴘOn=9"3oɪE8m|].q6#61[GzQ5-[#xϋ5;iZ]gig8UTr njQnk?wS5_O{nA噱,qn#FjA9h|64f[[r~5c sYүt Gppj;V7zu$EO*rz`WԮzuꐾ-̶V) (B8@^$:wM'E,}8CH,n 귾S !20R3\d}kċ!$Akjd*G%ٰ? `}=dɊrMGF4n<׈_ d<4NYr?Úmowk2\[LHUyt~B'3.>[q̑_ɪx9Nxip,{:dFPq_`IP6с^3[IѵMvxċa$fP Ifs,p8UiI^ztm*Po!5#Q$a~5F۟ &<MzOfkKO(͇%ID3'0ağזSjU炴(}F[˘9ͽ_'y˂v_@QtgU'}7rwY,#tUKEmIv3>!>x|UifooE\9̓Tē;V vH-HI d6h5?n xĥIK$%NF9eRjdc7W7k5m9;|[]aoyA P17;qgYnuMǧOJ'(;,O'<]Û[?浫+V48X]0M7F`T(u-=<Ium4ai!~JU٪sV4caʟQ^>)((((((((((+>oi,Z[$%̄'$h4BFG4[E v0C~,@υMD8Ua_!5+W <N?J(NwS-6Dr^=n4={{ mxA+bL hU>jyat %̑(GQ/qĀE@QGn^UoAsIܕ>|7x'E^{O_В с=qhE/aK܇. =)~Z -g@|E C_i1cI4uǤb:5B-1E^—'i4|tiHuֱ邢)ʾ]޹&Uwew;o2;QMEGdC-69I䆙' >}wċr0T,wR"㪆(˒=Oį j׭.n4Jos7B̙~FA$W :YPkeYOEHFqѬ=χA Kc˯*Ej9̨/?`|RzA058$OW2E}#ny0-g.cH$op-NsڬNRxV6frgNxcEZi;oC0c\dF{G}tQ\VFE$0nQt][\mGC%čͨ{ }ȥV?~ x;C ,tx][x7IWnmh[S_rM&N@4us˟񢊵F\ϹSRNZoq )TysgЩq^ Yo&7\.v%.Xd p(N,R{'{VXӡ,*Wk ?$sy5|B2-_ oe<…)یgVSB: Sᶇ/GrZkHw*ư<#b0wE j'!FiXēE)QV0Oq([idN‚Qd޸n'..R| (*d 팚(J1Oc~ iPAụu[@dɚKCxn䘘cT%Jg4Q\~•Ⱦt˹;!<54P.xr@FO&w/o1:n@(!Urovn+n$5uN>VL!^ո?cK@tQXN); Qю0>.Icnǡ(C}'4-fOZ?!'yl>kL@gEJkCuj[y4ѿƧ^U5_r2r  K|WO ]]3:$+&7RFy QEiMA}țiO5Շl䵌]=w( I|fo1gŋn+`X{f+/tImx'º7ti!(tHVfXbFrHUEfotoxx-20.08/images/choosemap.png000066400000000000000000000150331362435004500170070ustar00rootroot00000000000000PNG  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-20.08/images/chromatic1.jpg000066400000000000000000000472061362435004500170660ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:sharpen| Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 210 369 0 C     C   L" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?CGM8-8f=5wyPAdm{}>?5 7gf=CEM缿٣_4Plfϗ>t >j|OJ/[c1 ?J^|7|.5?hlEKzFs r;1r3}_i__Gt}"]t}˨! 1/x^x?w?OPamm4ӧ\I_Gjc_7r;:vI<_˃-z?dψ>?MZm.O[:ֿ{;JDq!u5[Ÿ|]~;;-/Q£jUKxUUۍsӥ{S缿٣_׿\~-P56j4p .?xq\/٣yM۩61:v8#{ #~q=lfU#߇]#ԢӢ̻2HJX]D VX!Sj^^¾?4lfkυ>"ZmM%mwwC/gN˘)іqJ@g<gʼi{K}O=z?w SJWR]: [m7e[<[6~$Auk]24Yu ]?Q{Dai"r<ҽq+%ˏ/hl_Dc4m{}>?5 1yKˏ/i5P`ҽ"6e/ jT7\9$()ʱ94m{}>q=CJ:%m4}{K}?e`]|=/-x&?~ח(X+I'궒Ez)ω^?gjS^Hwk.*$iFkM~',^ ֤CG YJ{Oyo5Ob]8\r([z~w>|d6?:N.70fh]c޻|zn5 t ^xMbxztInf.N}HŘ6˿ܫ{=ܚ?|Suߌ|3gRI'W0H4ls\>%xK/4(53/K]Y ܭ{efPGSӓ־F4%ky/V>4?]_V5yb×1/#8 ђFhٮakm{ZZv;m%>C4`H/KΈ)=m~M^?Ӿ Dg8#o1g!p3߇ >>j&n0c?w.8'W8S%ʇב?ok] hzn Ư տۉ x:r>|K)(4}SM*-a%> 7 Wha;WF9cJ/>QGƟُ|3䲽YnJ92zx=+|)Kx'ɮKy&O`IKŒ0Glz]}ɯ,}k M7OQk^+xՀÐrg޻mgC];>KrR#4"be/Х˙y~wjkuw׮{ r?vT6LTMqFڠ;FaF"b3n*.6"vR+:8c|omCWXX4t$g'j;zWm%h>xH3i![2[B$2IOl>3~/}f8.a< R8m8ԁ#h5Ɵ:k3̽$Լc V<ZjZڱ~ QX.Ui+Eew/|K>&}nw[F3MrƗbJ8zq/,]y*o&~(|kOC_ zڬ<C_ ?o+1E?o(P9D^su^d$1 0Q5(drOz/y?2& ?>5'Wc񢋁?o(P9D^h(|kOC_ z'P9DG2& JdmOO9Dß^ jwvyW1B jC?#?6'Q s?½GT5PjB'mO?o z/" W mW m u s?dM7 5_5Otj<?o _ wzbi)˭J]JncyZGX|zԞ*ӭM=Q^dF 0#8cP3O8?xo(O9D^W mƩ45W<?o ?xo+ѿjko7@iko7@\dM7 'mOG?S5_5P?xo)?5'WnxvKĽln^7 SI#6ɂn?kO?o Wy/$Ьl^oG;X%8#/j~CU[dM7 'mOE>O_5?TW m.y2& ?6'W jy(OW m.y2& ?6%z?! W m?S7?4ßG2& +uus. ^[̊9 GA[ Q_9|c[˭"^z(]-N[ȯkuτǟ uX۫ӌ$6>mTJMw)ZG[^7[CZ|'N`?| $A~WI^ Z[) e_ *:$q[/ &ԶSLQqoV5VjK`e r{+O~""|9hMi{&Hxj1_י&BNNEsSg oy[sE}P 7`WG'xƝ~"ا4Y-&Y#I7(;q_Qy/3qy{wcq⪥A &τ~f>Έxk6ZӍ9Qn)DႧ8?Ϋ6մ;mR; [}V8epn3) y{?? >j>SR[b.$fRO#w\ׅ埏t_A,s"hK;#q6\Rbލzf'>%x?|?.[j/'̸#HH+&?H#>lRh\:prw錎eD'>x#ڕ 3!ܥ~P8qW|Oƺ7#ŚmltoM2ArYC֫%ݣ/sxU_|77<}q\˫ηS[(k.4Qf*y݁5 /A:ͮM*,y߾)m.^͎}^ih)&9Cs <)_~EׇBLgoKF99_yihoھmbs85{g);H[{)5[qf@ݱs@S5J(U &2 tT!$>x]ŝƯ+!bC6|6H9Ү|-K ?|cK7o[C$ajcd1+nITIB%{v?/;߇^ͨx/BD>4Q8أ_>Ŗ-ϊuM#źP@ޮr>&x4|[_ƚN9ÒMmbF8_o/kFCq.ji d 9( |秥LnNMCQE@qDc/Ǧa[(J&`wP=4 Dut3~1|7da> iM8;lc|989gC|J9|c}^Ěk5+5@Y6t`n{vg*x6iqZEHS_^IkqSnRb?W|-{O>𶉧ڬz=;9'b[Klo{sꚋ2ZiUoC(QG$<^Ek?)h|K'}V'on[$G @+wĿ~3hs93 5ǟIAfu(Y|8w5<~;$Gc/_jn{Vy2Q "rmPnT} (W Gv GVԘj3xOKdF  [F_ IqX?g |LU5y T#Xn+r{hvh^ ?OWWKX;c3^(L~,|uyFVCRk|T}_Ǟ#f|9>4wo {+C`Ӄ9S__/ \:rk: wgxd%\{+[C?l'|<.twN.C떨a8@WU G{MV]?}OSkFCn#;$׈Pqi!/57Ot}GKiq~u\2=k+hhyo1A]huO?NsgjES77owNحyҩ+n~4Oᧈ/_U :*mXN` >a8RN{K^>06jxPK 6ry׋<[yᏀ?K]x}KQKQiK5r椫(v=w85iٷ܇|_OH#s^8R.-]xsMK?LI]AHe}H|ei>$յOi)m2$JA9=+s(7+B3xន'dz/='{Fkh>ݲc &|`a5ECxZ|AZVi?ǩ=Xʏ$c*@ws__\Q5oݟx+~$ONǎ{OJ̼͑ }Q_7ō.'i2O6ّɉH_IF}y++򧏼c _޿xKKKtZ 6yr+|{W&ƾ_ x{:]%ߒ q=}ŜO{Gx{_:KHΜn汹XYU!*OW2--|G-xUƕu1gn! ^?(4_V+X3XvU_ -K K5fB; (83c^ S: .f4,1#OJsUY0n GiiGZw-mok:?~jy2[39/jg_?ekTVum}&*-nϳ9Lt(jB]H{ujt~ "GDʍʷ61=IminK[Oᕧghg}6m6|^q%Ty&c.q7r 䜚￵q&n)?\5YV^: rj+\Uź+\U+b)\M_V?:gzi?Q7Jо>iE-q#1f?) ޯRN6p}x/-< LpHk<+'Ѿ x:vȚҠc>ɢF9^B/y\Y]'5g72(iQzs'־ꅸ?[vj-CY M ܎G庪-'r$V;Z?.տW??.տW?*WErj+\Uj+\U Y83G?QQX>.Տ?U|;^뷷f^+Ao |$u1u|'_%x/i?l_?Zç>kFY-.|՝U)۹P빽|cw =艄w1[*'9]/#ah |.{c7|]nh'Uc hDv潈k@F Bi7bVTY,c$قlhKa[v<2Ku'$E=놫?j+\USi$']ЯupTje{ rTyҩkn)kukM><חW&$q0J"DоNĞ${-tP*4o{>(,+?=$ UTBރw4~zCA\5'ZޠI,A 0+C A; A.,g:]α `FDq߱։}1@tRV':7&iogM'v񎽳O _:ݬsuaXϙ w'ҋQu: (ւ1i-GtQR❂LQ:_xOuj"M-ajY$*oF⎕|/|<#Z׆+Cq:v`cQY~)6j:)MkU"2 V֙gYiw \BC)yPеIJP1(:P944i T7xbJxx~`{,"4,lHH% [:\#bZ]퍾3m~*|+sy+͵́q~HQё+]Psml7quqB|,h71A͵͎W(=h7 KIOu=vK}6K EL1=9_CKgzKxob]C p9 sԩMr Ioo!DU 78(((ۖ_#_`GCx^-Ÿ_k^?hHGdXW:/^# y*޳i!.tJ;2/&kiiҷRn~ ږsskoKdH6[bIܚo 6oq%5BRI@'_^ -NgZ6#M򗟻I c4l adܷw5i+[I]ρ3)x_\nn-G9b8^`W;k 9.#BozÏ3LԬk#"Hpp8Vl2Nma5$B0T%+`Q>VO h8&GuBqju vHbdc8þ͞<5-[Yݝ^@1-2!U9__;WOxWºvOvIyoSÚNo7-W_wUi12w?ħ&+[/8|+xUhloA"InRte `cڹy?xq=k#w+KxzV]O:8g:kw;}^//@H۞TO[;/]7JK={đxg\nKfnd?,',sbugŸZ_HM'#N퀳>%τ{kiIYB[^2HlnP}Y_ mInx|HM^6+z<=O&o1kKAO?WmW{k{|Yu <[OĚxCֵ5Y6b]# b1_YxOzo]nX|隥&>bnUV01cm>xJھg${s̎v`O$_ʆӵ_b=|?ᗏ~ xN5O]mRX|Vè 1ǵzq};_ciRId`)< wڗjE nme9ik'UMeeF*~iMr|_^;/~)A4kt;x QHKo;, 㑜歾 xLM ůK,<==cNuV’~vQTAM;~3, iY B)8S><=hZ]UT[ySPzd0GBj1^ӴskvF@#-|:4KI'18CxcSZk h|n-<2\{VbDAk€j.]XX{k:/c.MFdPWGa^=_GҼ/acm!ckGYe_:ši{t81*3=;j4/:5I/>IJY *T` ޗk3K֧QMJeIzP?DltK(PXN0t^ޙIe #9~#%*!R5ݘnW؟cߊP(j xGV/ jvfŲR+s I.m?Q+h,HE,=nx3czV ĸ[n,Ywp@8N=-OsϊuNwbh1xfU4KyiN_H 4֥_ZUkq{Ē-;O1{.[8<>?żS;[k&ͻX$.qxM><+ ҆V%Vl3㷿雷 [~:m㳵 .[x;nQ¦T.U~~kz]K,D3uc  Ovgm[;ỲxL.yӷ/vbm-Eȩq(=x~wS_3;w֥ ݵ܀N+4CMm7Zn5WUgexBc-@+4-/M+^Z,J% mn㷿# ͎`-[hr,\-}gD. q׃idw mj5qV^>f3᝜ח/Reh$gvgXQEQEQEQE-%PIE.h4RQ@ Fi()sIE (IERQ@ IEQE2+Q?(4%٣pTPgGRRUB N ( ( ( ( * :QU[hnUN@UeEg3*(gGUPΏ@/OV_@4Vw#:??FtY:hY t ?eEg3*(gGUPΏ@/OV_@4Vw#:??FtY:hY t ?eEg3*(gGUPΏ@/OV_@4Vw#:??FtY:hY t ?eEbҢ^ivh?3Yo7uTW)_ ?< =?}@]hx{_ h("3Nc.h,1.# nԟA\ ?JA'C"#:;ֿْwN{v~$.o><a[k^*5Nk0FT9wX>_,;SLEy4JKAZ|%!m 5-1,.@0]4qkm?F~'x+KmţiA}p6 G8SDuN"b.D2X`tަj1OOV~ KǙ9]7m'jK4jzS(mf]3# ?ߋtY[{kJ"i& 86ϢA^7ž"g'W|-(ofn"C)*-q .[6?B|*P+]݀'ŏ# 7fKw]36}|o+$Y*M|v~ A%6>#8إ ,AێNk2s_~k UHDEFrѻP u" cHآuJXg*DZ Jx\м?kOk#EiHB\R#`R+|H|C[x[{M'FfL#*]dp'"~Յ}.~/Εs%Y⸺6R}A9u=*-5uM=dl"*qߜ`s<~Կ.5}O!l+w Y[x}Юlfo#%߀l^Y7In _'%Ci^)R~O\Oaiۮ餷91HҢ.mxE\[m#: ?/<|O4PT AH]v@ u Nе WVzu];LR^4X>hkR RxIӻ=g >$h> {i0藚VA/4BmG^{W_s#Q{~2=C\_Frѻ ;D) ((((((((((((5&{5ƙ>c $@}Y΁e:Ooy*2I 623M(ϯ9&'btQ(}ɋP{GLcr(D@pkRo ɤj:MQUb2I`8^8*a@9״=6o6×r[Rmȏ1JiA@r@5;Q}pNIFՒy*ӆ|tٓ#> A}EF{ݘ+ж/E@_hȖmAXt90 SQZZ eomiP22#k6/b ?{YSn.g6ݕ H듎fotoxx-20.08/images/chromatic1A.jpg000066400000000000000000001147171362435004500171710ustar00rootroot00000000000000JFIFHHExifMM*V^(if%HH02310100Fotoxx:trim/rotate|NE0Photoshop 3.08BIMfotoxx,  1100 1600 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Np@,?Ҟp*6ԏOӐ!`T-_Ҝ|s?As( ^<rcC}),r1ZVmDِA@?H9i&;g#n l:cXi \$q?I{ dI,;Th8퟼8Ƹe~o8)ib4m@ymPqǥJI%FO_ 'TI,$]9#Sc`t+ 2L<X2Uӎ{O)B0X)Dm4Xh`Lۜ 0Xxۏz{>bHw{s >44O9#ӚM6`:II8%GMOo+ `C~cI̿9p) q1N 'Ұ4){p8fi PՓ*# 9Q`iw \ԑX֚\ y8G68tFx矻j;x۞{Y}jjd7׌Qq&;<r;JFlSU,x- Ih<۲0F1J w]Z]ٓ Hs&r1v)kx~꟫/_KzSNݴ=ϯҕP;)Dl6cWX9ǰ@8!An9p.70, cH|ܭց0 OzbL iINq1qvzjqCGN9bЌG6ߺ#4ʜqS:ǵ&ӂzQJ¹_ WsH˒g$U$ uTL>\/ܪ#h5h eBSLj ֦ ecK $20 N[r}@3}ZM98:f24 <Rg硣gBAM`;)h#8Ͽ?Z#u*g$6z,5!%yק򯺾|=q^kfBI'"v1R/i3DDeWp”(r^;1l V Xʓ#j; sZfN~`~nF޸6X/,x$S*?ߩ|^Y x|:LԞ xRHqALUJۈ79"!'wp:RFIԛz=M+wDE'8Ѵ#1ROʗ}Xw\4÷hQIT̸V֟)W#FzJvOhJF^28R:zPĦU1@'OJc&2`jdSiFv=ԖܦWs`?wiKמE!MBt;\F@<ԭ&Zki .}HǽJsir6eoo_"( 0}4rlGz@yI]H# 1ޚvG(s(n\ܪ͂Xxe( ܫ6v~?ϐ#8 })'^GDּXjgui\#=:Uˈ(XÙ?`hڟ=3Nd$j2;ʟ|IJ`g#=ĀeXG?Z-g3|7"m=xԿ>/p|Ge;G{ln6O*φse(ki%$g^4c}rوݾ??zm崐So@}~x8+XyxcJn_C>(#w*Vv4<]fp~1S׶ID<>^/gṯ0꽇Oֺ0\a^qH;xTwuZPLۑ" d }6^7In4eyFo^P[Y`ܕG aWf|~iX UJQrch;r_|42 I`nL%<ƘS m 澺2wG瓌Y-GAp1? k&&N}:Ҁr61ÑT߹ ILqרqsM#o9$Kīs8Ϯ ].dW*ʥH+chah]㓻Njvn@G9N9 p=)vy {`tB0Qqc֨12NrSE'# x_(Uf# 1sQb!s9 yG_Jhy?o֥R<ߘNx班5JFp#Ӱ21+n'H 9>+ c8lY*ʼt^ZVcBpڇ}v#L k!@#oxs_r;襯uj4_~/ j >E\6Fwg'G_־A,-b$1p)#x)I`1P\]yFp"#t{v 'KJxSZldC +@+"$7هO;޹Jێ8g5/ L֭%|yx|ώΫr֪O =8%fyꞇS$p՟L]6)L};y[xJmm+㝳)uAV݂^m%[4:aA fzGV{.W֦$gkvUo")h,J Ƽ/Q1]Wv]4y%U1FּZYQ-ϭx}R9k$ɓNArW-7ÈE e+T#lfG9q Q#1ҧ=UW 0<<z I^^ԣF[5^;zΙ3WX`l8Y8Ҿ aZsYn^/>Lt':8aXS6 ^i*-xq12d` ڔr}sS'qޚZBF[s8J\V܆~x^ͪg5M }:odq$,>#O#>$lf<1M-yO'>[p42AO|np9=rztt=Py鞼SF=*E5P٤MwgqRc>S 1MHfar(2psE&_2$uh,2e=WEn$apd?y+x[u";7nOƺn!#)<@_sUfi'.ZNg& Yst n9 Qm<G5.:4F 15^ilQ,ig\r\,PCn־j7^7,* }M-R w<:*\})KPcRӁ-A<~enϥwk!4ǐO yۚkbZqKҔǎ46 鞙ɭ-.-Y .ҤҶ,/=BYZ3k}Of}# Hˌ%劊Օœ=i(E(<\Ǎ޽{^վAqk+E*0\9onMJlȇ&/,3=yisuMI!a|*c n [Leg¬&:Rya`󪓷; ꧃rw /}%gzɦpE|Yp9˹{_]چ_2vQÞ1W㆟ ͧQ1x]TCJ{q^O=yqɯvd#!@ OjphɵJ4jTp0rޛ}|dGrʨw1 =?:%-rM2#6-T:ݼ Ȑ_`bFFG t5{ᵾKJkWRϰr{$#__ j^I;kTnڣ#)p}>zE-^-2[Fy5U%Jh9+ 3qy3k鮇B_r(F=MHKx}s^&Ei9lWN*hNt,ՙ" ŒLT9/z)fz'뱝d֢.X=jWH 8E"܆}Vat5Xm=OnC YyNrz :'qKLZ v-79~ x#LK_ϫAξl/ï :iVo%k8IҦ~U֌ڊϋ#sgo}+˼E-퐐/črgh%}n}*]. 'fJA?\8Ӎpl\ī3o692'{xPCxbC ֓P`5V9{U ZKm aE|LYh~Cp4%o]Z%}-p9ZmԹKgDP}+GDVo67~sǚ=ݫDOzϮ |/6%i`%JfygK,Wײ3*)rI?Zxf(點k%z@ +<{ٰvBX98_ҲXB'w<9Z]ʾ͝f6b1V :k7nYB]~#A = ֋i+I%|yTḡn+μ]gk Asc9犡VMAatqMxOw>4 ݻ[$zw߄4;{}䯜ΏGrt/mF;SR]qϭv~񁼺VyR221ҾtӴEk7klQFo%1d EzRɕS-O#$ƺ]cI{ Ry2翵x*c4=89Փn7.w wV?U5{O\F[8ldz >֯}wy唕#?ֶo<1$W"=t#~9riFa&%d|! ]υ|*4I3(`Q#?pI~yeb̶@SH ӭ:hOz4ymR oܸ<3:WA;={ŷ$)q#<* Y$q}+)|NK[wy|V >+O—:*3EXt)B o[-ocp$ۇ6Zn}U:vNemxo+m,Ϋ7P>V$VPk3\2Z;'=l%m(| g۹[׃YZX0YDSXϖ0V<>8>%Kn! 61OrqJ_&'-u,cWx^9[\J\S6&|=GhtKeQrOƹԛi/S+:եy;͟@|k˩I/`|:2J৙?_ x^ԵrmJ8c@9? ږm.nWK`c^gsW><[q;PfR5%8D1|Mh nZs ŕRwe]΃~CaI6 9{}7ZڭVyPL#nuG89+I8ɣ8+xTW]ߡo&\}0S';W˪k:#a]ӄp3ե>!zuYFoçsÚsMu۵xU?:$?wG^&6u&<}U`Fع v0}+ӼXbEE!g }>NöWۢVp1Mg%Ozߧj"~G G .ijxxXh W^i~ݺ7푊<~/ZX1-v3wld0۹ (R~RlV$i-Ԗo\ץji..R-WWʗ7sRf qy^QI8jY~rkS磚%US>#10qSɭgs^aݹyOӬY1xʬ0a? װў_ Mě+gB.ҒW*9WZK,w":ѳ+nN{7=Socar +yχtX5b;0ke%G!Zm+ie{Ҵ@l5 IPw?Zu1G#`)Jѧ65Np&^Z䨒RGMZ1ї|+D-}*C!N*@wMncr3V56\$W_௄K]c;2@)V抛:h`\SOꚇ|;Db& 6I 2Hm k\>M!cp$گ]cI&-r x"E:m5;<.?bN4] ɝsv>]#QN.)FGePk{ǎc&W4o^kO<0p-6 ^x{#E1z/ RTNɅ:JMI9hy8 (Ggn獷"V# Ny8^KV}q?RG?Zfsfc# Q1gyb0nU wRIy-4/ Fwx1޼_$&t*GAV·Ѓ+tyK$WIl瞕'2u,֖>oK٧W >Pq+ϵڢU[Jɍk8 .Nk$OmĎY{W--5H,VḲpQEÞ7HzU^OWVբCr.o^+^|A  <WhꊲUp{W&L2#!U“ӌU:6%=7ƙ\yw89t:]ޟڈYäӎ8 4Fu t#xsSy,I&YrWAǩIǏ^ƚ:x/ ,l֡4R? ~%[p nX?%MSھktht]&ɺ,A8>N Z@I"ь>^kzux^˖WY>U3]|Hz+hZ[*:p1ONҾD5Yw@}ƽ-4l ?Ƹ <Wd5{-_C*Hx n%$,'f;ƶN&8I Lt"M-7 7Q^uzu%(Zi7(`22vwCb&PC[wƍx.nYW;v\u50^1Ш)c^eEwy8t~ D7W9 d0`^%Dv,ːߏnƎ6 eR5 1.eNKgkkJ_sonhӭEPqH\d;l1 p}FO^$IFdg<;ƹ8/^޹9HFJ1WWU|U|D)7R6k0po/׶[ҭx0LW:^z9#SkKwJTg<;#b5GC >Dh.Y}+?FWMمIC'ktQًnKut>c,gO<ze~]8֭|42$XF2;?WSP9?OX6-+=xG{Pg#GF}: ;|ȷ~!q{q'nu&y]wWOu3_ @KrN<9ʲJ|Χ(.;ɧe wkKa(x jЮI~S\?d| 9*z!Wmo, Sʻӌ?*Bnzp4ckwvwlB.3Ir6Oʭ=]-x%9ҮMdC(o_yFFV8EiS_t4gr0ci^ X|xOɦ, C(>I*H}6_F&?5`)ӓ2Mz:~] 'X l3ڰ#]jy-? i.pSax#,@H?z!ge m2G'9WK.NKDqm#m:gP2DbfF8zrOJ]#wݽ[G#\u[l۬x=GkGCцU΢<^/^!ʨMKJPᶰ 4:ʯr9.|0I,$~ar8sƔ߹>[x&}&쯤lL QQk9.LӀ!#> QW'Wi#'COּj7w1}{Ewm숟$92ux-Uc*[@dcns]SK\s9=*sI<÷ZJIuo4ˌ7L{?Z_ O@ dg~a< V˛'v6^!mf2B ?Jewqyt> [Ep%Rx"$Iomow 7.==?xxW&MV]19 ^\VwGtdp,?^K[['u av۫)ꚕ(EӹcRIEyO ȉǡH+1v0eZ=n|(FRέ$ ouz9,N.s9?FVj pko|7p[tnBrYwii}& @jq/^^%~:oˋJXVn T}}Z}_xcDKh(w=V{\ƵxPSxKvt tɖMT[' jc`͟A5iSl8s^E6n߁tIr%7cN M8+qoJ1#.v1Һ񂾍vMtyNϥv&#ߵM4mC,Qh}r\4:.2|^,ﮢ2ʂwuW=5Ŷc(]#T4 KdmL2GsF {cf7~}ETOB[__>ϦvHĪA8_CGqM05IɯV䒹^a5[sl9$}^??>!v%X>\8ܠ$gpeCZoqM,O)¦g{isbϗ }}:α,~SWğiq:r {_39h⽎RTq^$mY}Yq^>j}O?ZR "x~Fy3( qDqkchOF>okmlt+eH3\m w@&fB0F|w+G=[Q5VymT+펆L1π^yYaV:tV8rG$Wx[VLkRH6p "0_:檬{l[uW3>"Aokut|>$vxW:I#UQ_Gx3_Jb/ -[9Q\O|*Wɂ8F|xp;wuPᇋ>g-f)z.s"M MSitJ uY_#N+ܼ7:1ONVR095xOjӴ`u;QQUYz+eV"|UhߧS %o/˹Jۭwέykv|;<}Jˑ݌.N3GҫxO0^-;Hdɵ.1^Kt<`a_Q}#MD"H$J侱BǾH*?s54eլHA?Zjr>_3@S$#U+^(Q3bcxFc(i+*c#k{CбFB~Y&`>ht 7C8}+{$hXc3!H?epbW ]Ν7ᧉ5l,- vbUqz?>iOwOgJxþ M΋Su4km6#Ȉۜ Ӥ<^oԬ҅s|axG...~efgZ N~ p:g}ovl\ɒ*o_/4:_َ#c3hOZ*>>ii~ ?aݮ$thm+]}KF {ps_C 0eIe+,U?|'zbpj9cWWz% hYiϙn]@4XSQ ,p%R=:{ewt`]8^Xa@m(: f6*ٵ =7RnG5p׺\ Ic&YIz{X`IWߴgo}|5KK êiT#'\Fb13{)C$s0pjdWxwɓ\ݽʐ9+HɥcIIQ)Pڮ:5s~.31bG'&ogѰZ: &zsʌwrf&?<8EF\W[w\wKKIU^\k4Ѿ]t;״[¿$%=\2)Z6oھ%S^3xe9Ix@my7g"E0NzVqi( s*3'7<< 08Aw^jLUvdԞid.G.q?Kke8n {\mt=qJ X-QRsDf L޴Q ^#|wҭ'cg8XO[?>}?Ԛ9NvzWXd7!mqtΧ%I9FΚ-q[z7=WԩΪ8ut]>ƖC\($t&-n3g I5-`*JG=Ā)$gyVЩۊ8,=MѫwQbX7~b',c`}*Ň,Anȷܫ/#8u-Ht-UnPI+q޺X>!i@.Dip vz}4\OF;Y7ROa&`ZmRda^"M Xʯ-H5K%9 EߺvWs<}HYF揠MwVxďMIQ7 X>u6->prxZ.PvGC]ϥgRS z S^E<w\(V[I$ }OCU&?{ⷆ癠tՔ|0C^%8x_m'-Wy7gk+1~5W =|gNkghTj=Orieֻ;Qۥy&8%jteF+snr kdѰc9✶pݥJVOGj*=QKKsw1h;p=AU ٍX$Ibf ?k>hdf[fms?j|5ԣ-o~DJO9[Kt{E &\M#'Nk֤8Ԥ1bc]>KUu>U~A?θ?x,H?R6J"ix.,1f’Nx|o[5(ALd~U6]X]mMjܨnG8Y6%>S8Y] =@%>#sn?.H+)\tϽuYrA޳]ā_c\~MEH)=̭6tgXdSztIҵ]e ۑrN+,CC(ac[j7jb zVһHFvܞo Gvg7$>koe'ȋz1_vtm=F6O\տ*STHlO_J?u%i0D1(=I8p[$,$2r^"9r|c%d'Ii19/Ozb'{ {NiIzmsĞ8"ajzVijcw{3_WX/UKtxf0HTTuf<]j]c!+4\޺-icMqW$k\+&XeM;-e !5tBٶFº=wO`qҧ>դn1~T7Vgi2ɭ' 9ۻާF $yҫA$U N|>fY^1&EσW={!{3j'A,:ݭb>nޔ8Yh_:z{{i 6-wFGPFSw;"kyb.s;S-iW6r&"Kw7XG8%h49xk=1D}?: |Qɑsy=3ZҒSa9YE\7ksy?.=kWZ$QL /F=#-f$p3G埽&`Ϩł#~5]EH!^<,%d.Ǯ5Đ.vU4%+$aeHO_*]"8II6P=#jݢ^][Cܒd1W d.-獊W^0}^u9$iw)+7R6{]JtABV-ZVQ mR8"WA#d8>~%߿ǘ Q V{.-wLm`oj؊rmFLwHJ20BXG8JU9 S_B`}sn^2\C2:1_A>oGvWŽ d.cB񭯜}W;cX5MU,'Fu涞 ۔9riq(0#[j~%Gh:]׮Vܛ;]"5ЍWR[{J._9-Y)^{-xXk T_ P:RIHA "L;' *[?ju( KEt>*-IVH1X{ WSgh1I+:zFTZhԷX" s+ZX!.!~+w[y cckT Ra ~M~tWs_PbTşCKOT|-Mm[tZ"=8{YNu 1W޸Z[fdl"M M C񯖪V eMJ*{KvҰg`FNӀxzߕdJ sj]y,YY dقʼ+8vyݖ.`m'8_ Yݐ ]Gfa匆5ջCtH·˹GsnօGKKL'"[O$(O޲t]κEaFSwr,{,[zҳ1M ]}Mk[KQ9^g?,zz@V85ʢԍgnphs,rHLx8$V5ͽݼMIu$RQӜT:宭c)d8+&Ҷiug9^ê.fy'vNǽ~ϡXJMnzSmSiz+ -59A'\|&esnS̑$m>8]Θ.t%#?7LsYF-J;x793n?)*:`V/|kwcxJ @` +'kFJ0d?ڗXKQN$% `}+Ҥܮzx Jқvv>&%!]w|13\Nc\K6y sV=%6ʲX6VyTqŒ޼%[]ʱqFNO_?![O~1^~){GσoZmGJ9)$0m:ɚy&FtvPQ&YT|gR8=6(f#$zkiI n[pMqX^CK$ B>~P4Lkk;nXqD21y57-n0FxKNZuSwy bl0X#a,%kBC6dz+YP?cx` +Ii'=&RM9/jqe!i C6Ckџ-8M$c=p 'AahHP`*i)3xyB2guh.Qd ICyƷ5Ɲgj0]W[EpK3sav4ME|9/SUM&12tj]kxi Nf'3]'.>=zWewsYZ>–koݹ!pF J\t:uuƾ$3ۇKUNP5 餐[>vNZ2H=5mJ^Ei<Bn=י^X3UVGгoη[e?wʺ}?~Pi`aE-o|Ӿ9 KWw^F}r/N-g֯$.eX}y{ySy#;α!:1F6I I`ùc+W(4RiPWm(k|Ah~:Ӧe¼X~u'˩(Ajωz3ƶ7,T{V3zm ps?*,bi7'5^a}k%ռoq%\ TgT{Mv5m4{eOcm.?O1g~8>mSĶVin@YֹMZH<Й*\zWBjdd>*UuF9!̀yJ,ukxs];[?lcQ{g&5Lҡ ڻ-1vm# OZIXoQi_(󰱰֨zƟj&)c;`QCm+'Ed-#:]>\HBVW4{(uBDaiQOA]Me, dykjLXha8_hI`nVK6rY5[ZYMg_{0ӭtV>H!$B2uHǰ Ib8 E)ij3."Oyq Jd'iuirЙ1vn2:s\M7vd?x{=B Hܽ^S*9oo/YY-Z}fÃ߯CY3. m\'1^| Rʕg@\ tj;"mBmr}떭Z磇.2%Nk&|uiRg*<ѴcpkSVf}ko߳աmSKB,zn#ib jaRmOcpU0e sF~6HsxUΛF:w{W_KdvL}a뇕_儔oxoĖ[n-`htc3[{UhBv|xq4;gLm&9WIiLk -*c 8+{Vb1zTjooq :mAEn6` !F qx^?R|Mux0ָ_[,n8b[Ҋ2*GجM٪Z !$$~bI~'կ59ks`V1SjQbS<*1ni,UJs?eXo.Vw9ax+VyWē[]nm؈Xnt&'VvH:5u1*+c!O62tJPf)X~:ԤDrR¶S9S]!r}|Aۮ%\! Q?GN95(6]xX'doq\ æmnU{kl-NW&HK-F%<.2{[M6Rnc#V4aH,\wB"*<>T<2uC#,k,dӑ?SJa7x`1?~:v =?0n҇B5tYέ5){mVЕl'^]>k|P6WL ^Y/[~6m?g8G-Zf?[?:Q<[mZ']xNq]sY7f&d9Y9%vEL7LDn{\NLZvI_sݮ~۩ѤQP9yt6[be$@Hۊ!Vwީ6IBOďy4Qrdpe*RW{kgg-Xk}$7G8qڈi>Y|xQC]Sw5\KHHyZXOSZGi \ t_ҽ9?w[ٿ[r2;t TZ\$JaF*ǟjcoi-s,~ҕxFiԏi,&x'z5IR\ᾟ_ғA֑zr7(هӵud8c\L%\pב\&\)VmֻeA~WP/,?,c1ֻnT lӺLW ÜIcyM~ȓ8OH<$I"!Qy+an3z%AjٌͥCb4{gnְg%9ⷚZ28%~ckIB, Vy>նKUnΙ )` 7~*lbOn+/Q+>T:'Zn7KE/dl9٬5OF#\I9M&TfIoSU-b LHĆqڳ+Jemsw[1y~?_OVjݦь=k GU-_zЕ&&r߈toCzקFE}F -/H'hWA96@>9X]X\?v03نkCGMLH+;Pr'iZj|c +c>ƯMȮc" }Ɂ; 6dܛv>qRk ^W2p:9*CA ]m}aHɄ>N VXhYe7%ϮIG )U( QkVlԬ'm1Tf+su3Et7Ұ^~̌6'fZI?_^~)Y#+6~6xwƷ%۴ ۻ{WɊEYFھkX3ۈ֞}lL8q^*^cr8ܣiw>7Hd U\'u*NY2r ៊zc%Y|~תYhv`u7dɜ^ ϞXXdEtTVt;mҘ7d_-MMsG%u 7 9+3Hh/Ygʼ[m47vGTUJG>!m82t/:%.< |qԼmA*`i-G{Vս_yQOm"\+mWF\G>}-)_ʾ[@X^&`- OOSӡGt=GeK/ 63=\D*5M6%R"9WPGw2~%dRz+ڰd)#~KJ'9uܾaPwf7$(q|+XfH{/0т09_Nt]#<|p1\;2quS{IeέNOT.Ih oפ][A|-`\~-,³ly:D}R8:]{^xlt`np15-0 0f'^]iR2ɧjE'}Mm)$Dvi+a ˛ތ~$r`c i^xv{ %r5R|H[[smna[oH4:(pPgZHTTqH+t?0F}Ꞷ8 :qkWN q[6 /ʽ[LJ&"c=}k4 j%|^\߉vmVNV]Ɂ.0+\;˂3~4ɵeaxw#"݉+.P[bzhd-˲͌j!jku[g#ⲭ{i%BIKqhn1&ߊЇZ$0}: o% ,nߝB^^3sY_mNU@Z!il+{iHh~*/Uo|9sd| A*!h7;;P,ȻXqןNk|9umcx{yEyʣ־I# s<qߠ xJ{iJ㸬 7vW3ٲ`sӰ*{ 9ؽq>fL ZKgij>eIq]w>(-ld㩯uׂ8x#cMxB͊V{Ylt+fUy|'{| |lbWSY^n$aqjWkGF>Vʶ+%k|;@Ӥ}JCƜצ}|/+#y^Z7]Y8ܘE5IcKI:L%[ƽ^὆+eN,}֝:T%;')O R*vgxkeҼ? -؃֩?k?ckݶ|wO=?νN rgU̪E"s~:g "!I?Qj+?vsϓ8x^Y{mB;.N?~ַ[ _5ɷLםT%tvBjj{[([pE 8I>Urw:Qx~67|@2q\ |\y9)^>aȥvF<*5Hc%@j˟AH #_:Ӿɦ NX6sF?hcaL n.dr6zslJ݆hKK1czzn'e'*'u[H#wVp=YxGep8ֱh$G`溯+ҡZ+,};׶\HoƷKO9TSJ*_yiKQM$|i?X^~,G=^ 9com4;!W>^5ǪNGݛ`ܿxf[HI;8 S"\qbkkn*>Y~͗ve,и)'EBVCc]}jٍޏqidcְFB x*ýx)..5Fm=kW6q67J5z<1d>օnf1 XeAWߡ#NK ؀+-w^EaarDefcƺ[ 4b9OAYh20 ~oִ|dciuŵ!Ex -ID18R:2MuZ-a|/'NGY/ّpYݭ$ccРVHnG5Ō0]=Ȫ7y7Z6ռQ0ep1»-{[oLn/!Hd|)cluIo&m&ܯ&p[JO#Em Hx8݀F6=)>^q=3RX"lv`qMRF09R2+bHy !VSj^*rTmorOz~>=nmDѱ=J6k>6R g`Y1K*K夛W#V~b9ʊ)<%PNCb9P)3:eP <ȥ3έG%Ջ:3 O#ֳZ:ƠѮTzўJʧ1kOKx%*c<ZKW6.>FUisUm$tڭ4C(A#2ڶ$ݙы] NH|ذqi,0/vrA=>`fI'`.Onֳ5 o#n5{ňlm#P,[i6l_bkt/Ɖj?P!Cs#~KPDW1_BHeH9WHJFsk Ԟ'*l2NnRҿjHKOyzK0ՙG.̩IS} [0zcҼ2v5 nx]ƄZ\:7G3e$u5wvHFX Μ*ӮGV2/ Mէx-̭bCpTw>d%WtdONԅ,6wʑξ{RՌdC #W7>!pQZ6<}٥|QGiJGۙ!i vI?a׼/xƃ&#h]WUFcou=Z*.MS3JQjtE9$TV\,~gIX-~i, @߷,ޓ$zϒ#[>cj+2\iH?LgXCi+q^9|2LQm7izsiJ0 |WZfTcR7=t?N+To%؊ct7+~] 2Cvb0ГmhZlMٟJhWRGT$&$gh^2Co(g2Mx)lϫʳ%fcY{[L#al#zO߆jֲW@-;n23\z?7^xo7~mU'B}:U)I]%UP碮C/A H$7V>(\e?v+V>-ʊ+?%3 (v3_nB/(Z}UѿWkcQEEcۥ(A/?J_EHǻIEtx溏?߮BZ(ῃC@J(r_OԟZ.J/'ߛQ^]/'x귩E6>޷_T?g~+|(nL}W3ouQXK ?j/(_v 251 382 0 C     C   X" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?GH]!Q 72p=j.?缿AE{}ˏ/PQ@{}ˏ/PQ@{}ˏ/PQ@{}og&y@}Ơ%qGQGQ7erqursٸ˅u% >xI=o7⾫ZhZfkV  39=į:+MC[a#q[_vYN[#|}ƽCƿG< xgO_Wz}]Zܹ8ڲN?*O;Ѭn`uCV:m-\Wo˻8ᚔj⣢<νg}d?Jna]ƽ[OkZ՝`[9qmp]lϨKq;%$O=ZB{xXeer &;tx.2.Omh>_Oƽ;ſW wQ#غeݵir"@v29}oZ?|)w:Ú&$4{!i#W5Hyqlwyq޾oխKYWtT$2˷kfğl|E~䳗GS_ز( p:Yy{8gh,:Z4M[R}7^??_Vx/pQx-lڎ| >"V=f_|K< ZAiq<ʖ9cwwk˩+lm4 O)6z=kx9$qC;e/1cZk$%D![<qm>pqy񯦾1xcOt:aGT4l$@8pzW NOxDm`/et{z^ vJmWoSyHs4}{K}שa;m~id: u5Eml @M]^+%&0uʲޒI˩KVs_lm4}{}!K?Ͻ'#9\y>q=jιu˷/]7EՆhwZ]syo~M-O&g$(e0+gkTM6ϵ#u#R5'y.ǂj֩ڼYHG`𢾊SjOX(*IߠQ1ZܐQ.E\1F(/8ړEg>Wߍf^3_jN͵I 0ʑ/GĿEk.Vs}佼bU?6@=rHkvx'~ln rO[i͸AE^xGt߂4 WBLڜ2^KHY[flUƿ 3|wO Iܰd1矔1<Ӂ_,p0BG#9Ǯ=) ݴczVI$]sC|B|o_X[u$•<נk_~~&j>+n4 Wѭ]%õ``u_ns락hO lUϭt4gͣRլt>!@G,gy#9-|__Y^;S s9'MY5t}yIV; ]O+x?N^6f𫌟Z>G?mxUm7-5;M)(qG  982)FG$@J.׹Nh[qaO>u&dStndc Fq^Cj?G<-vOѴBTūC43L2r@11~I^׾iKT]|R]xN/ėq_.$N۪ (Ά+WU#?5/ bїZтͰpe}>^$z#3CZ>䥪ϋ>4OƟhH;{K(P,%*n1lJlw^,tmk{/ YInłL Wd$}q^q;cNiN->Kl{Yl8m)2^5/ ]i2#1 U/CС`nO;xb{>bQp;-W~%XY;mBXn 2"Ϛ}jğhZݷ%5R8Gfљ@.s1E+aX^nd¹\RAk*ԡ^OfuaqWx+>K[=_M3kAFRųr÷A^3:Vỽ&4"VH^H7I9sל3zދnXvcf ><~}q\4ss+}瑲䕩Jӄ]տSgFQq=z|3rH<Qy8W]TOKxwdM;K^gS\5kx k:2&Sѕd[i  Yg' o hK7DÿGiἆ OÚ 䉤=0!๫~(}5j~`n2p3q\}( /o o kwM-zWѼWDw)t#,pF=s]/Wxzv3Pn-dxnk(a#=(Nc 6-xw]\y۴{z `S0(Z%mZ? 6-z1'9?3M2^y9?4l7%mZ? 6-zH$~cҨk%<=jwO2[Ē7E2ǰ4_[%mZ? 6-v?4iE(d[JQOA<`s]r2zdF/׶)\W</o o kդDNߔg{~5~ Ǐ:u .>>cNN쎇) C+/o o kռG$p=s"c' "GR\(?xqxwdM;K^mGL8?6x$9Zv? 6-xwd61<斐K szϒP4O+C^z\+iZc֥`?LĬG$D?sqb.sw=dL BO7o|`Vʭ_C7_ɿwkMѣu[^[]&3H<ǜl`?#<iriI 6-xwhůS^%mZ? 6-z_dM;KG2_&%Z</o o k֨%K7Dÿ%mZ( 6-xwj_dM;KEz0?X {LbV(?yէ|eCax ]*D6Oyn=@'ه~@;-1}k g嶜wlKGOk{WaraCbo|:7}_I4/Xż7}E!f\ y|:!xh"j~_ qhVKTHY 0gx|IgxetMd.xlc'p!{UwkZr-r>rHÌ{ӵ5ּuo-mxWO<G2sGK⏉>xOGXNivKwǝr | `c6 =xĶ1Kowc.m mH!6lV} Z3>Kys~#k++ul3imAC4!i!%#t@OlTR0H> j^H#UdPȁL,2O>&K2ͮiZǣImq-}t?6 b=ؚn+Jh/uWi9; ޏjHRMfs^+5φ<MCW@oSQHxQ^/0XpV|N𗅾"k_5ڦE+X`K4vEݥ9^=+LY:}[\[ɩ^%|08 `(s-_#鶚̫bvwm(saz#eOVAc7:o洺[C-ű;.#^W[-x#_Bn4 8"eh _d|ñ^h>"ѮR2t H<}+3 kdon3yGq5ws,K-|{x7H:ebFhDWgRK: s\ zߕ6q>nFHP9sҽOqKuMVov-Q7q8V w^[2 i6@b&Nlry5P QY/-=qeq4+wd<^xOKZφ7,/Ɵ}e*v_aXw 5\~^ԮDnBRiHNXٴK(}1^5Ѵo_xٮWV,a<߹? DG=5W,ΪEf1S#T~^hXk.n(?LO5}Zlv647t b[+I+H8x6Q3z *lsfW^##x#?TFEBU^֝#%+CП&/#^"?M~֟vqҾW}#~!;_L-|Kfu% Fhic4Vƣ@ 4yXNQw>=h43{QHAEPEPEPEPEPHR;mF=03\GS N,5+x3Q2s(,BOzqjM? \k]]Gh񥙆HqZ~*|xwxSO5Mj+9K/FH? =Įkctz{r=;jm4 >./TNA}D'_E<tuEmPjoxP!\c׷֡H++ҼMW)*1X9xc7BXܕ!+8f}SсSa8߅&|AU/170Ӯm>^-o,ב̘raGb=i Y#jg}6=B_QiHbKԡ3T1lg5{='MFwYZ+ !!Nr19%4X6Z.+]W/Sڍuᤛ\Қx̍t!܆4=cOoğ<yx=Jn-4뫨 0`;O Ksa$sx5Q;4ŽYC Inqڴ2Jƙ'O".$>Z\YC;xq;la$g>mS/~ |P_v:7S[O}<\;i򝑗`^c Tt4El?:Jo6{h'r3Voa9=A C}'K||kw-3M ڽhFVM\wt7V?˪6ω/,/\D.KFāp+˝*mm60BdcztEB- zq1I%7}ŭ焴1t&CmeX5eJ8qqϕx?W|/e֭>b҅f%Pz*ߡoA$V&>;:M26f;1)^LE7wrT-ZxB/uC;ºtH*X$峊-[Ŀٍ=i+4wVe#خ72[,ǦN~EdEdxHT9)ghx2B~F{qZ+^#7=i^&o4u)rq+k[Sm_u]#\KlYm3W-tA/$.D1.<:TĬ vAaǷ8ekWԟ |]i/|Ma;epw*8+i>~+u+O^}߭Izod0- G\m#Ou+x * >^ɤؽɸ{8^`q@;[KF]OĽ7JkS|-Uʃcy;k߆ZZx j]At[/j7M*1`[-#9ܢ&iJ%#a}b=2;~ :M,6vMH88:¶>u|+_btjOܫj/mP/rg#x,s~ڏJm3f]`~A*5%UIS'jW)ֶ̈́;u >')HqYTՑQwg$t<ҌC^?b߈_4>\_b5{>'0} {1woxo\?ዾ"C{9Dk}a5?b<7W_}oᇅm4vͯ<3,xTd FQz7Q]V (Š((((( =FMz 3OkxDn$2.UC'"LP>9WKAo 08I08+|}ǥx|LS>6ž1ӡkI#|f ghk2?U ?8#s _aD2A/cp~q_/Xt2M6w:,0hô ܁s`mCvvM+%"\'3VLicH?e'p:rIsKyko ZE&Cb(o-2Oڛ-[A+c#H9PyݴeK. +lG5L&'V[~?7O6>%9ίƙ(?8xk˴ڻC41_ .l+Db0٤+T x_ѵthOMVk/u{B#|ʻI O8\u6ӼHiC9dMqbWUN[Kh줃6@k4/:wIa4-b1G!Fal۸?/>?i7^)㧮ID9.Y]1T9y!dMcPhBMlp81 ,O!WjXvWXi6q~ q | ~߳kӦA:ׂCْ0J n%/¿u/Z.lOKs\1A:%x%8>W{ιn{#5])G%3+8A7:RO$1kR$ tۑ 瑒Aq~_5 gpizedMu;mlH\Ι/N\Uue ޏqķHq aعe][;޶=jks"e 4\ ݑdǫiHr<2F* p3s9>[/Cѵgm|?15]OIleQUy&19qBrxu^k]6IUtHW$t9簽 ;5m(_hL0< wn?. a^%b@'!{ WsybkP}[Ŷ.E/4ՏNrͶ6_~%v0q/3c7mwzWYj+·֐=k.69ێtz<^" =WKRKMm$p |nS^~*A^eqGmp-cb`y&B.)ng5衷N58(𐃀{ "€_L\? ?1DmgPb={WK~'| 8wJ J-&y^4$EBIPfԬo=:8'сO/ [p*['1sP Em .qg:gژ <-G> %;.nl&yi^D@ g`S~8xgƒ|q *OZm[YIbqߢX5aDZ2 ^o(e,Bh18ǮODy)vvG~|T_-;_׊tج="+DWP\Db<#㟇<_'Y[7"򱄹H2xZOÝ=#~=9\ar[2\I$w/xpփagXN7Fnd\!Pr5kO>,xc>+jI}}%(| Kg'o,'n|qF6}N:|_F6{~ 'm/^Q-|2O +VmEh$6L6?Uh>\h4>녗dԗ{}r@C:6=j,9/=6㟗M75Ф`j-$1qѸ|=Уwc~Y <||Wg}ъ9uLVe} H<%|G'/>ºIXE7+q0R^o@I΍d!X >_ 0;&ĒlLS~˒xd`sP!A3* ,4ije>G`mȲN' >#k/4+Z'4Qjw%h-JYbJIJ>D&uѬJMAp߁ݜer{;5r{]0O~\|ȃ[7Kொ m+Ѽ__V:ͽپڠUs~jҬ4(P!@Dw8' h)ꚖltZ]:姊FDTb Ix{-cE<vvcj^ K.bqR+m]vq@Lq;N+̵c׺3ꮺMKfd[* RH# Ұ.I-Kb{An :腌Z!"pƂsӣm1O){uI\R ӯoZr8<gZ@i4+= b9_^A .p1y_ #e~-d̗RُQE ((((((6I sMr>&ܭ Ch|'Qav`H=u͝'־U//ۯxF+7\tÏ/"{?Z6V3尚H$ʨw㌎(,> Ě)ö  m8s6*Jض7jV\BC*,꧑Vvx9־`TCEǂ_ hc4r_s 8 Sįx ͮmr>5XgJ c~#yVI|مF€g~}k tgk"Ww{E(Ar?Sī:s^iiyyIvTc;W.ϲit{WTjV/;(. މ#EϯiɫǤIl59#iUX󸯸ux * mSo/~"|;{ho mYPX99Fq[o-׃4_z F4/4HX@RH9{ >#KۛR&«۴8l}B8zo1X/$5S |\YjZL Nf1J2!bBޕ sIn)Jϔhps߭ PJ_χ[Sƺf>A\] m.㌏Eoaiq j:.Vdf!UD6za}5M1кO';,yU$sHޝxEݾn"6 oђ7)R|m9𽯎bVpid7IcO\;^+`t}QɳmQp?bqx.I+ϘFs8s?5˦Z|Fէ,x.T6@2Q_&Ï٫ h>_[ `F;[tJ{r«b_Ct! ܒ>`qVcuB_j Fy#"<~\}=uX͡O-ՖgH+39>c1,tً /Z!յ6rJAU B'Ic,NfH=hjkjyf]]W'5Mú%"B 1jmc9ONca jUXn1t2Y3ԋ׽ǯtҾv|~PHuPyJƆMn.w'"AR@pp{Wgۓ@eb0ALVwDi}u7^jq[xVxn,c4@s;'r{s d3fRٟV^ K!wu OkjRY䊠 $UwYq՟wd)2=h tǷ~/TtشǙCoRF2_%zqV>|.Ҿx~G漞[ɯIduG26F7;MvxՑ[;[v=9RmĴ8;GŗhNsa;ݜY^O港 ~z/ :M3ƾ>4u `ND">h9qgU8,3FdgԲ F~={wu#cc4FĞ)SOZUʍBmw']փ<zz{sT=&[[#889=26;^7ί٥̑#' 7/$dg=-3V,OEm- bt +RxX`iX~ Ǽ#^i#)oc4])GHQMFq_KS$qC?/xHœ>d?m/S'N)w F~Ztɵ/Mt}E<4`a6`|OARqdnDvc~N6~yeZsXv48`\E8H*``T$iMǪs[4f77NS[xúu}:m2?w|>5Ө#: iQE ( ( ( ( (nj#umGƿUIضy)8BF+<` Ƕ<uߎ>ώ;>NKq~׭,/l`]B]f>٥1G#Y@~׶ƚ -7Xͤiz_E4M:9lG]AIg<g5ƛ&>xHde;N9~Ȟ.մo\ZHWV~F-V0>b['nU;GſW_E.mF*.H<`y- %ϕi&5}e60Hcq^ÿ[<[sy!%H[%B$v@ g!Um;g6.M7_Y?Fj֋,6v1+w۞sVi_IAsY<1K{cqL6 Qp@8M㏄:0Pc5?ī9lZ9#hy 99E3Og|D?%)Nng>Ì$T􋟋Q|g|Gd-,"`~Ju߁?_ Vzg]e^/`Jp3<Z|]'<]bKKEǗoiZ=Aʺ@L(fzU:Bku̗+G٘vnϗt֖!s(gK?Xo|.t;+k^TKVxC#o,0: v~ė ㋸][Qv_2hڟp1N8>cWG ?TЇVj ፼Dn\9mFvJGs;+OQWoq38`1ǩV; ݴx=zi]_xvo IzAZ.5 F/<SK.BVKhfd`\ ?xi<<E).nZVPmٴ~GM^XI@b#g $*JUN[ p3k*wQW6šx㯍4 k@~Ϊ..mkETg5czQ!i exlqֺ? Y|HߏxKM^IMŴ1|:p7C,Lڷ QKY40Tg,F%tl_m⏀>#v]_zujoWD >aIɪ6Ci9]]&kזHGRa9o= {{PӟI|*I swHېx$;ω=ծvWjpP-S;[L?UҾ5>K.}a (d{ľ-~.|=~ѫju9im,{6%;goU ÞGM{kQ,%s $ NrW9POeސ֞ntՅ%XwFH /'r(<#YzsM,n䬛Fr[~6y O鋬-=%1o )߹M\3'8-_ ־']> j_j8I'K'J$!\5¾{Ɨ y[DnЮṉ1$'%hԗu\xA-b5okx¶&]K(͔0a潚lE ~"|E[# OAYjgVx!Haڳ~,k Co$ Ʒ7 zs<⯉ ~9+^:^;<]or!FnUu|nV;_~ |~/tK|=ƝAIG{Ḙ̇h 689!|=௉~83<4o\k8UD$c`PsVS+h}::ө-"HZfRq]kݻAe) ((((((rڄ1tXV=~w^*o :+3^s@[ohNG^γ }-?v!%ໟH $j&A ?*/8׹-k[Ӣ? }-?v4{;Go :+3^s@[ohN׹-k[Ӣ? }-?v4{;Go :+3^s@[ohΥkm}k `7F eOd{{Sl`mR$X@?v{;@hGjWj6$j$o_nC_֣:6A1[~ʬk[? }-* W)cۏNMZxmY׹-㴿?v~VWk[o7GVC]bs@[ohc?RƧXe{{f]F-r~ьd5s@[oh׹-Efk[? }-iY?v{;@tVgo^s@[oh׹-Efk[? }-iUGi,@36,\4PQaIE.}PQaIE.}PQaIE.}Pֵ4+ .NYw$1E\y'&VgD|z~l&g]VQ3j\}@ȑN/m[/ᶪFc]p"6ۆ`UL<+'5iᏀOKmK; HFsbOӚ[_i3o3G Ϳy~;_K+~* ^ծ4..9dשkӿk_R׈H 5hY_ܥFEHuANG_bEiv j{IGp>餵I=kEYis Ҳ89u=PĺLN ŎYR%(7˜ VgԼPX / J/x\u_VqjVĥnF$1~nPs|K@m7mh/= gFI7RU{EUpaW$F ׎PoKۯu8ۭHJagH!'uq_>% IѴu?x=k?~)}u{FKRtB<Ϛ>W. C|99-ٕ{z߈qc\dd2u|K@m7mkX1A/Km& ūEm ez=z3xwεc>)D$tK4KЀJch,ki-U3ݿ|K@m7mh/iB][±=kaw71MwG<.XCP7F \@$Q]_i3o3Qju qYO".FoB3p;i;?v=Z͕8sg >0:A<1MTLúEʺA*\c]m,m[D e=*j^&dS & ,w򼻀I8@3t_nt>"j]JyѴ& ;evPT.T1R۵ϣ|K@m7mh/3fg.wKYKX{4NBB:'sԱx :5 -oW1[kx$J>mJM0nڞ_i3o3G Ϳyh[K>ψ|xboYMuMu+B zб5jz/֯m쎼dhF ʤ0ϷVM5s?|K@m7mij$mTj㲆A'?yDߏk xʹ".,-)É3>ҼcОjO1DK_u-?TS3L .ɢ%\dmkS+QW9kXA4V??_@.1x:kS((((((x?Vx"=sNVtI7!)Yw>x}x.喩lFOlWO6E M<]a ž[lt=3<+%mHԃ>)l_ZHKeԬÍ[kg^hzk4a\+F!ړN=ծ՞sa6xG $iwmq%̑ mPkʾi_S~usS=VĖ$Q+cFAϯ'#P>6zEZ4C F]%,d '&JVV8m6/ 6¾M휲 pFNNGJ[F  j#+ 'lu Fq^GU5{|=5/]&Kɵ_ s-H(rfp~~;ŘDΑw'ӴvR? Ns36ys~gߊ?airݍG_"[_j5 pfP0gr?WĚ7þ#ƩqA{y~H$W@#b~gߊ?ajQIXğ^|Nfx>,S?x~ӨT߹I;Vn8$ת9GgC"6 8+pY=Jޅ$<>iVGZD! $$4Rօ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (?fotoxx-20.08/images/chromatic2A.jpg000066400000000000000000001243251362435004500171660ustar00rootroot00000000000000JFIFExifMM*V^(if%02310100Fotoxx:resize| Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 8 463 1000 2 2 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?;*HB.APqB4@~[~? xLae{&f@BMZHQ'Hb <85ge CmMe2P|?h?{d#dOZ'R/c^ oF?\n|y{IxT}h4l#4!tӻ<>*.Wz߅^%֥p2N: kZjZ>J]^bRIgw%v\=%Hu%;c5eNmzͧG@w@:!oJI<^izݵeLJR'k|VOږ}?~UftzYQjm\o%^>{֏ .TPTU-cq>Ϡ]jpKorj&*%kC>#tIr|tֶ'k[dXzI0+Sķ2*!p23Yb( ЪF-e3xQKX"!V׃.FR9etu -)1פ|+O x cj1/ψs%r1:g>4{(y ?3YK/^Z )P ),v}CL>^췖8#UTʌJioF*{ZUIgdid!ǧix88^ƿ?~QOjvow p`g5naD> ȯ1 Goc6!z#}knbzXycAj7MYSR|aa|Hx>j2`]NǦ'n$ wY{BxK )TFD_lm]xG?{c\4ݟOi0#*/k$*_٦P?=M]ol6o+{'Y46ee,7c>Il {rN*O2%:(ʻ]E.V2\ԗ)Փ$CsZk N+7ǫx?3ߝsygl:񍻂X7һi2W"&_AWd\f&:2z$f8$gp+*Gml!e H>SY|X&vE@NMqҺH¬ڒhOy+[#)%PxZNqk1cdaZcoHy`X H#_ B,TVHT"HsϠ}dJJQ> ơG)^mI5SxKQ%.l&Ͽ֦܄zCyW/Dӵ !v~I~ k3[A־K7l|&`s+*=6ŵ> ,y!$=k<3_YO,鸅5'&w7-,%IV· 5e\ q^G C.;#Gj&j2Oҹiw!u%m 00Kz>x/hN7+Nė@ O5mmGS\[?3x6熛Q29a`Ԟ{&O ^ArN-/r@2)_z>:ѵwTEC1${W*y-Nͭe>{zWwpx-+d} P@G#ҳs#yFt>Lh\<uE+l#x߆~+jSee3FŚD)162gUV:&䢓:yZL%ҩW@K ;ut .-'v[TuQjZfֵ+_xŸ5? N5b1n͏ƽ[OQ<{ʾD|2Ƽ}?I]tsh|EիcbW%e[%R C?Wsª1(qxޞnIr'ףLhѠ !R6Ռn]m{eys{W=' y5떟_ylMvs[~e>Ziqkc1,>6/m.1wqk{+o&B=7uY9RjZ ~?3๽wDa澤m4X"mb04'b崷^WKtF0KaW*թtBTy+0LJxcAF(i{J񾩬xަ/.QT=k㮱Iqq!Ry}*žd[K2A\n)茯q̥luw8Cwi{{ OgN|G.#1lJKkܽŨVt&2ƹܮBv8)Mdq5/[rF_o¦% _bQO0{lQ|SԮt(J+YHe}uRN4 IKK:E=k樮!5;-nUUbTܓ^ QbөTֹga WZ-:Y!>yB̚oyz61<GZF́e+c&ne&z6+m5ba<hFzQ*wޞ7oëϥi5R(~N7x. <7%-G2ynvއ=kڧMԉ?n,H)|Ys3IX#_WhlOՠܶS/rWȏʾ(dW4ۭQY%aXgZ.^6z_Ǩ-ۈE5v:oIq(F>YǡUOFx6M28SHp8?\KϷxahK8ZdQp|sk IC\[JrU׊ ^$6r%ZCwςhi,4'?E}Qup 1s{D= Kk'?g\{kGUMOQc^[Q0ӱ@ssKjfN OzO9dҀ$EqzIo9yY.ڎm#1{Twn3nH[s8#r"ϐcקW-5bgYS\]8ڀݨƶY,Iw8cڼĭdv7SCAzsZ:έqAuH~Pu IU̗I5lsһM?Z-'@,ZS;3Լ_6|3:0khxw m#n[T|} 9\rXx>ѢjV>.[[Hb@WQ8^5_^'"B~Qd{\IP@y5g%%r;cV4Piߘ5mA?i8I"%Mx}?Z%;tCgMFQl]9 SՄbA>j|/ú Kdt5X<^+`϶-jSpY+ciJ W9vN#Z ]Ko"UxyO &na\h28^R/DŽ&|+ 5>JM,DR~| > |HCxikFp<^aXbcLp}T;KMF4N!%@v{Wu +¬^^:x;Pm]ZyaF?;imzѴR4dMpzt?ZS?A2oۙH.gڽGl4TgQ$4:W8k=C 4F8~15 \(PL3kwEsI'G\e>*N Wakk_cV%f8qmrNdz< jx*Ϗ!4uA ê0]iH1ԓODB[ּ?+DDF~m֖Ȳ$`oLjz:q2E{(Uc(%zr)M^{X2]7]<@bҒB ر pjik>4Nuu[I։c p׬`iJċۍGW[5fI~P3]ޯ_`7[ J Vd$YYQ8N힍.]OO]EiW!0khmՑWoZ݆/SY '7;\p v1rNc)9V;Wj%H¶xQg![' VEG ?kk>`Gl=̵ g5RWG㥼>]|PZ4{|ՒyNT@'X$k|$+}!uS kMTwtkpY[1|_Y]@+,N#+lN˷S]ykiZ<`.ᇥ)Uig/&y$44z.e1R^R*K~((M>%e[q_=x%v"m[4*} 21ѵ =Nu G$d[ [Pҋw;&aݮxzuFihT :c_b|]K|#c[aW|} ",;. /]-K'|_G G u P-)?*Ҿ4Yb"_WS̞$rz.XGQՕW*zz׭R˩xZNxغZZx$VDi 1/_KA,;ՍFLVZLd9vPf,t'd"Km^{|CO<ªHLzѿzNN|;e]t9kycM= ך揤M8h-w1MYAR{Ut$f7WZ>8uvǝxoV%xw*M h0CKLG+WE,#ٱ} o޸xy_հ"Q GAtmq K_ɨkvN[]8/Z戀x eI4y]d'Hw$ZNz!^TdgL~-q&E1}OJx^GxA q \^kz6#F*#Wóyk4 ڮ*i{n26CҌ9ۻ:Ad!I8y;E(,vt&~evݜ+<43C9 j_]BkͰgb(RPZVrg Y?_Gl+ ޼\=gK̑׹[MEA=XcV^O5=Cˈ }݃( -T4 N=]P1`KgbږO L6-W, )5(RJjéuFA8ⲅqadoxGڬ՟P$&X[njIIdg=gD}2C uЩ'RGD3S[ixC*-0H1 <Ҽm[\ި/slq>xR_.#|=- d I#~"|@06ov+n[!wўgNZ9l?tt,ӧ>|7II/~8oһڧN>k^G*a5\œt>%};[mh2^?z,$M^A%5fo5ޗt`.A}kxǙ)'\V385熭<o-bEiVdێV5:F-7_vz|F FK|^{M>(#q,hmvGDa1\oug’ v67`$CgxL@iI[T`ݎ7ְ:8u%0;WCm 2m"nYQHזRٯۡJF+oYDXS=)ScVz3ֿ#xn A=?־.ITV_Eq-o_[xTk'+Կm\,7 ddm+|}}oֶ(DԮ_ݥHFzY*AO+񖮾 ҵSMIxodnko#IdN%TI1XI;^O 0pqI"3y'r3X^ Ԭn57ܣ18ivjW7ܺdrzV+oEl@qAn*.wB+SRқj^eRA=:מi=}DC3X7G mm(j ]WmgĒL(W-{[9ovw&$;ԗუ#rc/]lgnsO E-c\joreD,۹L3˳gQ +cjojG@VtO6%Qajp&z]VC2'?zXzIhٽ$vᯆZ5i.J_q| γ6Jl/۴kȼ+ O ~Wt; %:>0?\^($bVV3'0sG=WEyu1U% { -i>;Ba\yUztrI4{ @Ү̗ y⾗5muW:.`^y/g5x}P7V'Abq:iV/}2\$z;3xo@SSG]*z40C!1_AWח>%Zr֙i>讜jd9(}uF~dbce<nY--%92;?g:[N;yDh Q]$9lshUw)#b85|3/.NӣSswgrz[;aww1Ck/~#xy7dyVD<_mN=/kvfj)[:0(8.3p@C޼cXx2T$I1ѰG0)2uJkKc1䫡4W C)]<=i/ 3UYXn<{)ێ;F~-50?h9qGκ]9-B!Μ˞w= Sh߳o 8^nZO2izs?S[@[CZ᝜,>M&ݍCQ\lL =}Vċud*dN;+"X7aXs,'9ƕՏBjps޴tpF8 [yX+aNz_D_$qqrEFGiA<R՘ U%߈lnaunQx1_?MiDM}1nSεa?/*OcecJ&FG\a('4zvuxXϵ'9TJ=iVQ>?ҵ؆yo < v =rq_`&| '\V8e |}ltn]r̞IꛏޡFtJϫd}{{ OH~>|e$RZ_P%5>5xvP.(pzq_F~2|JuOkkmI#I)N1LkNKZ|33^Y>%4wh{Ն8Iʰϯ"98|^G;4]aNps5qJ&u%NOMji"wO 2RpM~]u.)%ЏUzIM.-bTY/e5^'?g˦jLڞ.clXn;MğzƵ#AlFץ{o4ڵE|S}mCŦ>on|&EߐA5wޔn4|9m:xѱPTc̃Zno )^;o^̀n /G <_9 ڴJJ7fԝϺ|Gσr2y|eQUGy߅,IDo*VTn O[cg=_)ּ;gecyq}zWɞ9!lۯSפ|"&𮏩Ii]=FAnÏw*|S%ړqzۍN{zNʰ !O{.Oersk Ec, G8Z4}fݮ~K{^").dyjk]NލO2N'SF폧J> VV6/iV-BUp1_M|d> jzf-]m+_G`ˑm+?k4p? }bqv\NTEԟJ5ҵO^\V@A>⯋6\O%ْ`݀8WZb 2*fwftsRk[ntȻQ^e}gk 0\!q\(u-*`pȨxQ^mcpWh8GJO]rbxײjŲ!Z5K񿊼c Ku[ѣmsө|~tt?,Q7S_b,1ȡ)S=hVHY0]ڌh%Fʵ3IR2<hOi"yɐ_Yx崻KR,-@Gڢ6w#[ x'[ۅYF~Υ%:u/KOvzlpWʓ </μ7㭭ݏĉe@7n$\}cH6H*{oƔuCfYZIlĥs5݆a0Qg׍S⛆&(T _Ěf_IV~0i#^>)#nU\2c[z½-V풂v} _úy_wQ_cFOOsW_Jʈ3uo\~5PxQUV2/ %ƣ,S ּ%hS*jSn)/@k6!`bhv-cx"Ou_/5Es#kjlx#ݶc(޾Rk"ţ\/6`'8k{iv-fRsi $vI`E=Wln.U-i[?s>ǟI7ET+^-Z7b[>ʼ+4wv j:G-x*#ƗVxMENo\}-*&tqz#O=͵)%T|=?.L#YQ8]G%TgH#$t:mO>!)7턨pgxȞhA NzϚڞ_h _ -~i{'t{M$AKd EV\˞Exobc)s?8$^^'&V[.脂N{֪\G+>MuT ou6GpX>]LS><Ėu jTQ<-⹟+HMMū_!@ q_pih|bf_1/O'*f(s/_¾j7CokTPAine c5]5J'eyMRC,%RV5lJZKk;VHv1\WO׌;bY/ewi.u~ \Vh:>UlLjb)R[0|7h]gH!@7U#?־tto/$: s+ʾ!]EkDge^;ӵv`BL_^"VQinzB*kZh1,̹+γ~(Lm& gwփX#/ۮX(&YU3*IXC<8_.IL{VãikݙS;5~Z? $vAR~ҽc~d$9Yrߝq%TjGIM6/t@meU{M;jQ@컑׍W%tǞk6_Zќ骉,| Up2Gz )|, +|17[Qegm鿳<:ͥ ngߺ3K RsI3ׇq8?SK*U?uI=g<wUx*} ])$Pt+jpjQ]BZ%fFk^} NÁ=Ir8׹ę&ǟּڪױ'6eKȠgtj:F<%lQos\ކ$ri"˨C-V399Jzq~m}XoY7JM~~5;{,tu'&.ag /5y/+V"~qy}揑/3?^`kt ,Ay$qHqQ{_o跤 L? ]JlY"15u2*8?|AwOA[Ð?I;ᥗ?}咮ۥ`^^on&1#;)CӤu2" :giiBܗUר]jo]3:ėμc7$+y\1 'w?ιxKPXʤc÷mAb'+8\zD򢦻jtVHEJL\1Ҽ :|Vz Ѱ :V.P0`] rVWy0A㹡;B6 ۙgWO`+Ӿ)|fg G8׉GC|eҹ^} £BN-d~x;@šlmEfR 0/-gntvd'u5!?h1expy9Cc7N*Kc2pD,>yIdӼAkqlXyhp{X$$H YԓV}KW2ʢ8ԓ_h/ksHLa<Rkk1ͻⰴ?|u(@v=ժQ1Hd`JmgQ>*$u{[W>:>dIW[x[O iFE8ֳs$vSQrf?o|;WGCxY:Og'#՚y%hIP[_O$rj?S]<[Y@p hMEE>"-k5\Jd1[FwKyf|d6ydAsxNh#68|5g4w.x$5)hvރfŸCՖ ifr5#>6lf3闠fANG)ϧOu}y V0M{Ck@5!f_'Ⱏƥf>՚٥sOj+ngpފ˒d{:npd!`cIڹ_zjH`JWÓ}  -o>,OH:וNOI3{[)ƻaS&m'.+ov&6? |=-}+Y_{c4 سB"x־Uj%еQg}fSp^F)I =Ӛ9q>u\+b#6/wu\ImsH&:~NMb x⻳Sqx*_\o+3ǩE{\#ԓP _Ny,枭 vHOcKMЮEլLjoy+x5F{F:0{WTk̓Ugf>Ff6SKonBZƾX[1h> u(fQC7urODץk o3̈z0_6|G׭t 5;O_5|cxnq6$|<<+I+oҵ>-(Ҽ'mEd^ymysf沆.nLHF>W} RE.m9?O/.}8_Mƥ,(0 NE~}5_:<Š߱X'd8}Hm6x*%*8R;텽nYWi=+>>KqKXczWNHqЀk̼nc8 \Uk[j^oV{I@b9}W(E2|W|I\#l9xP%Xl\yϯ)10Βh!~#k^gM/mW$3CnEzGغ:>B_)uz˽Z@ =g7$d<+wt։p$j"1^kG7 pG_һ][ҢS5_ j) -ץkGݭ4//s/"U$m q7/kưfK Ivq գw93`9kӫӯz0RWrcZrn}c,iDnמ мPl.ˍx 5/KDҼ1ef$@dH5ONq;?~ >2x6}N)F2ʏZ͵WUiLz]'%sYffbHϧ?n_m}>a^eI{DžF-n~)hM3DZ\;=k; gT~[8,Ol׵xCףGe&ps$-nAs&JWFR=#Ok~Oý nj\\Xuaz-<`\4SzFӧzL%EYZ#HDi)v3f"^XW-xwƶKi-3"Aߝ~_##hV1Z)J>UcssV3FA=5? ,~hWN+|a Vi zG‹'́g^ZsFTA{/-cq />կ|yeMt))*yj& S}4ҳu?\ /fzQ2k7 $nB2=.߃GoMedt3Ԛd\<RD3C]9,ᵕ$.:R=+󭾯 %CZÞ(:d>\ПR)7z@͞dlF'e_L<$`F3ƼK)<&~ڶal|=՘H`}w}변PѼ=*mRy@xQ,ƤՎ:FIMY}>7NBlaYmdGM|[c>"{X5,iWv^ RI#ʌ*%e- %yES 5xTOA1m'#4W2PF *ǩ HM=T&_os'`(\7@31y@t[q85*Q=WjK/FaGug|O}>{h#aO`F|Q/yI>GV^4tJN?O헧ymqe#+va_oi4u;iVKvH:W/= >(XLH&{ j'O~imP7>{g+)BQoSQ(}L|oqw1(@+ɮG Pî+´_uCOon_X>#֭u:hj]A:p.pTTp^Ŷ_ |[Ԯ(\޲k9M '#:ݔr)ʑھXm|q!4mmcvGdd2z%#o/g8FTL9`S_'>O[i ȵ|UM}Qkvr#@c JP33<;m^rzh.M~xxw+6bG ##$q榋R' ;1e` <1q-`ˆV|*l4jJg1p2sڪ| σwyO{Gi&^ ~iE̶&W ~`Wx{V>a$0@x\ѥN&mtFxאj7ߊ7b=^,o-^X\麳M'ð$c\ )[jˑt8oWZ/Hw\$ aҳ|'Gdx]Tl TcG]o6|cM> D2>=iGZ J:׮s-kVxHk 7g5xӮ"lêZ5 xNKK%pRKF3)41XQї¹/ۏ ]or }+í%XO;?GҼG[9 <zy\Gy\45rd,8=AY~*#X'6C}k1NPFkCK*jI(͊[%')3#Z&ѮIaߠk?G|6mUJȌP(7f69mV7e-7=6lڤr}W'd|LDҴ:o׳Kqe[^--?ε?/5矷bo7rIaOjm3Xl?c׊VY` 3UJ3,jNdןz |0+פh3:\rbם*q-MM H&u_ n3fATErZ|]$,JY]x;BGC^_AՕ_٫WGWLH>i`!0BuB:Z%$f k}OF" WM 9 VzJ=AO EJʘcּGᔒirϑQྜG_mԖ2~΅>C=ᶾ;ycZN.i4iF\KkMj©ANv.R#}Ex^]x\fQk#BN2RVeRRƏ/fS`e@ڮ39 2auNpqM{w=+Ï].3;ݰ[A=y߃_BOZ[--m5S#U' {RNYFvޓ-XA"n:V P;ρL-\^2=zW4f&g=7}eLZTP~tK:1ע|!I3\-öw ]Vo}r>&*WYs˧=XGouZ nfUKk>UG9)wΥvjK[+px>Z-1b>9?`3&:+jr~;4Q5<2N?mi6mo4J>I',yodF!Tts]&H XW.Lڳ<qy;IWg}`KQ\OYB0w=1UrHa[#9ݠ_EpIж.c}h>"\ڻ׆U9@j׍$DiPĽ=i5guaS(8Zꃒ5sO—v >m?iZ'McP5Ql1'ۜVWnpCHӧ5g[Uɭh'䴕[>Brkʥ\"pѣK@%Hڬl`ۂѯ|^=յM:CnpꭌbLѥqبӊ(zIjT|'ӭt*9t9p.Zh=֜|̋Z/X.t2v<򟂾 -%Uo[x#kiCܗwa})V eB=*aMݓLCΖ̦A8U_ΡL6d$Ωdc WN*U[[P+7do4bݝA '^98U])vׂFNUoođdw0½cTb"˒5k}VI#Է+F9kBjP[O#yb93|S{ssV,*W z!$BO h+d}%nJ\ז GwmƐֽoCȚLv^Gtado<d?lխtbOc@8ֽJ`hht3eZ_>n5~-&[fT.kVmYu&7E6+I1JA ,I0t'׵%r)'Pwz|*U-貔C_ uKH}^cL}W> S5'> *ǨsD_jd+ =+*Mԛ+-cZ lyϽ}/utب<㗺NRD`vZ4Ϙ@.ΫYir22e<I'h)ioWWgė"fo䜜 WxJ YWшMmg'bq>]o|1L)j0= ׻K¦E77b8-%M D5k,rUӆ^s*MR7/I"0=ֲ'Uk 7{5? IBrml?Kk|!ⱨ~ݘч݇V=Oxan9|%xUvZ䪨ёSMJ(khw6WokaLd1^&C{2+5;kWD&\OQzbx`.2tGfc(`T B0Bal^J6wk=F/ʹcI6s+.^8{*'W;:}E,jo3$(nHDx0yϏ/ul,k_ZiXdV&^[?Qԯb!qjR NhoRMyV3Œl[ cI"Ce.I.uy[1pkuwнˌ1URײ 8nt/+r.FGCw1ޭ~ruku|C^Gw 4 B"HPwa&\ۚ-%hwcau˳;s+B."OkyIJ_ȣF p$Ju՚1O^it71BQ B:rbgGΠUWo_✣?3%D ,kFcSv 8}+CCy&'ǚ&Imc^QtXg9s o'fn% vzHAo+qb*cZ<X"ȵk5 GYS5}sX{f:Ŏ%׆,wxO5c+2tW{Z1$h\I3kJ ~s^\ffTs*X@*rkZ NV(y[%$җOPճE}*|7w*?U:$hk֟aK<yڗzG>2yy`^(-Frt_]t?|y=m&n)Ff Y)`=3+S>\}tܱ=x u| 6]=y񌽣TTTWKtP'G(`rn4{BzsZO:am'>%8pɷy/dꞮvoZֻ]Z%`wn8B ]6iʋt$'F$ѴIMQ3>nle|9lgEO5]7Txd0غ030RͭA5ydy5Gs šPKF0Q?V63^߆63<|G$xU~UHU kXUvfʥ_acwzH.Iے3Wwmn&6UaJӻtO\X\Š2kCUCxKR`+8u)Q"޼'s 뗓KxZUjۚ[#C᧊-i?WD ĎǜWyħѡ%XzM]'"Ҫ趸R.3ּM;V/P7c=+ZA{"):oC}FVA ĉ"^[=E'}w,xMVTdd,u:x&7r>uRMC0[ݠqtWK?2NgEf|F,~E& J@OAڳ% Z1o:WhZ,Zyimi1)jr84ү<$@sһֱQyw?Ga jͺI\jO?.)ăJQC;xk8|G4UӘMq+3޽gE n[E`rZƝ?"=.AoWXZٳi|+b`ɤtt7OG? T57ؖe%W!y=?_uDwR9'<*. #B@(UhWZe? 1$P[yG^$0d<ѥ>#?*ijƧ%1&\ȮoǾ6Lj)F+eOx=<1[Ff/oNSm\;50#uE_mpEyh44?"L5?!>|M\c4 ^PFď²,R;-P;}ZP$sh2ڴAdQzU[TUj銋Q&V9/KKh ,s\v2¤vw'M ؑטh*[CA'XM/}˟е ylE$ך_ cyK݉dpx3E_ٮ6Y%^Z%Ė,zOjV7gs\. YX+m;t6Ͻq:nlo GGj_%οxf+qyXG$Wn.nQ1 eCj'zWZť9Y8c{j-KntJ};z|-?GҮ/N xݐWVњ'Od9au-սLRHZ#wfi糍~YcEo),)-==f7h9tcHyhNO]p$xyqzsЯʺZI ).wgNj5=igYq3wTi>ޣ5`1ΊRgo[M.{sv8⾂..-, wf 0$cZes_={gZm8;&WhOy;Tԥq\.FFhE֭*dm^QU]> |'N|O*LMhfѮYYb`6+I]* @?:Il1 $_8W7yxdv2fmB9Ve2 8JWyj%끚[ x"\\m n-|/[65ƾl'o3u'U%E|AO(iTKk[}GE02zW-Q5hA<҇\ ) ?O[$r*:j 6zn-\/R iPXyX =?*>TY:dӴCu{C2c5+ WtXOu+;L3N:Wm%(>tguJrXlό,MSž8ܶkؗkK_&"c6-MxI.+d:Ceh$ kZjtt_uojيRTnH<ȞV?`֒K%$#"Un0 W^u8@Lrt\lD-PVXj ۏ:cɞ -gX[(< ;ѷW|9h OIn8{Ï hjȧ޶Њ*SOm+,͎@$ԚAtk(SinE׽xT׽cϡ O%yp/z׈ A5뫨U(.<)qXF7*Blݳ[VgeY(1}OӾ˪kW =ѭPɾ2=VVz|aɲCu5-ooCxiܨ "{!4 zMcCmNIJ<*JnsZ o8$p8$J=8 =!#LWxŷ>-Xe$o|Wh&KWluGcTa SukFŞ]ŀy\?oY pb?xN(br/+&`*Jޥ(5'Wjѻf6jV+ %۲qFd'9:Ct}+м3%/ ?5~ i,p;\'JV=NYu7wvdZZ|)w,gnX(|L-|E<3\*kϖst ANh>=;F=l*8 \em+)aKA^[w"f@[z b<Ȁyl|u SwQܫGpÕ>f g:P*2g[%6<3|I&i *秥Skn$K}? GŒ^'ϕfHYqf'ѢtBQ%tn-<I?P ׮|$`f70h$5-ZV w¾}.4=:`G|V-1, yx%Z:eC毅6<,WP3[4!@$_cRZk"tLd #ni0Nא0!\3z:@0?s%.h vG>կ`Ӯ`!`ڼNKYEoc0x5wN޻&L-^/o&Hg6e1 {#NTi⦛jZOq![psצxZAq4f7w8\j+mZmn G}y-Qѝ$=w3R=gKUtr0i~"he@z.l4?zTdTPJF#tώiZfĉѸ<֊-rcf= <{kxZH-r:]%)c1Z5=gI?.-Yl^A$NMoxW.R2:QVd)~?$W{ױ\;6XZ\43*l$jO xV/QkۯlPߪDcִt<s -\ڽqTW8QWΎOkKUL}K|u`ScbZEET|'-ђ"Пζ]f;#]ŀ'UO!fZ žby @k@[Y9Ul0k-:(ÞE?'IndkYG*^3enm3~}+?7m6wy\+CZ&֩kUspԮhs֩('i>-m{VOYqlktVPyXrUG8[oI  ÜANhk\ڣ+jsQPs*t<ߞo]ľ.Կi~D-ZA͑E}78EJ O>t;6Z-,ȣ.'a/ZKE+g= UiEhw8)iMtkhHT}5 =|XqQ$\e U|4 i1WNjuY4xic$Y5k .1_i~#K X4>ÝZw@<XKB}E AA>SҤex贓k4X Dj># :׶_C]?˜P+'9ige8ZLhEΟ}?»[ L񣳕ݴy:^ [xy6NdoʵuzD5]#ڸN̺N3[4q/><ψ~u$K',a~"ZG-~0Wew(յ_(#8S9\Z"^,&K@UpI?ysY[+utdSm$ҽK7ӴhKȨA 'j?~ӼgF>n ˌ{<*$eVj1_^^0q P;UK p/˞Th6*|㳹=sPQ/O*xM+ST] ]em[ ;/M^[h[F"Db۹a.mK;n?i*']t"C0Y1·ZVtV@ywo%x;^gSzk2Moָ=%8xgLNFc'<]n`0I/N:b|5Nbo:w3\s%&t$s/ k+팮6W[I׾ϭhS6 0F {O8ۉbB.dH԰0rFyYk4xScffOxsVė1(l@HW־'%muDF =+{͂)ۑq^lpCu$s?=ZNӸ+(7i Nu+nx{ Mâ y+ٴ[̶o#= }%=CZӾ,X=ń1mn?75φ<}jzRc V63riIu{7>)ۥFy\ t+K]YEȧk mŤ&23/'ް&$Dmae;~7Ш8_YxbL^q/Zſ5,(]7E%,eO޲9><xZIb 4ⴲ\?qM!F gOcOZzŜ:GƼsƟ`t$Ֆ+ȵ(ʂ5^M+OuiiH&7ci >kŞ-!eĩHfWsWNkKN݀l.'RM]6/K(I8&_Im_ᇆn;h+2s5\}Q $W#OCNLu^[jN$$5;29|)(K=_L /~$%P7;x1ß ezԐ$:|0~ǗPx&N$Dj&NHx\_SG25j)3gjOv#X $g6i4 t<O4h>7SyHc8>_Jk(#-s:_eua l%j}[vǚnm[Owr$r j iYڨ _0~? o1|/ 6"R#k] ((rp9_ ǎ0 U04#*`0Rw<=~ xźԆb:չ4+h5+yusܟJ@\"yqI%Ӊ^J{N+>{]v{ɳr_Rj:ZCy觠ھq4/ƺJ$ʂ {⾍/52bCXVRQG-BүgIc =u}A^x"ݬt.K)?txe냲6p=H\ >kSvdVǩHSg^!+g! >^.Ga>lۺbCЗVHY.1ֺ(ͩ(ޣm|_-/laZY6\~o^3mVJ4RxKRȽӵ k[ Csm*HCSnKUylnHڼ f{^Fhou6ђpz{Wx?S7$r >.M;Օ068ROW# $-x }˨_ýԷֶ Bﲿ+F#[_oj.<ּ-Mv%!$c9 .dNWu  at'hzYW36×f˦KvF<׏dž~$jR-Vql  n 3UA9F:8({Gn7wYJq M<%xK,a5_5]3PԼU2iJ`cW;v ȋf$;MB1յJ%/\P~;2ck?xZ-:*#Ym2y.\u.ř fkr'Z߇o [z  =qº_ƫi(Yx4[:t+MLi[w\g lmc|_s^V[0W$.L=y?_*m AbD`$d׊C᷄|9KX(aOܞ/hl5 -{LN3xR)cGNzT 29i>gfhj~6$.a(pN'}Z1kO."_KoyqaHUNS5|c~CkH41J~/:c|:<`W =4Y<#C|WmF}WTh|ʱч5-<bӺ._O4-e!eb{u{mD!6ֻvIjo8cx≺V Yh@r I^ڤ~"I=5',[j)kܜ#=W] VSFUю'Z/c=BC4a˓ovTfY@1 ֱ ǣEoi=ɭG֞ddS$vk_]x}._HT}3_U['clu?,:9h珨e"tm3 jx&U(R+i0HH*G[kO1t' λwHƧoR'!r\ѧX=ۍLmֺ%%ZC]>١ ;{(@?$fuZ/ޢsHR| jx/$N]]N*)Yu_[3Ð\+$WwV =  {ZN&%!ZĐ7╆ ON @0=)F 4Ϥ<=~!t˩34k9?|Suo&V@3O"ZM_^T]7\npw~S=Lol3/{4,dq] ^?~#Ave=;I?JWOum#qAV76hrX:"8C"0Fzψ]OMhdYvx҉2>7d$rEsP]HX3EatrDMo-2|{~umnTsWUYYmbk}EtM<e%epXw-C೶["+ _+_{ׄy Y^IhU=J/yQoryCEAݻM&w;XූV^?λٓ×_%jZI*|ǗZV&G7z6PP /ⶏwx¨ΐ}/ #4QZSK"G`x^ϵ|9PêxTmHP{E ;p\_ŗ$P=ڠ'Y}OJ(tΚl~ʿ /~x;FԮ&I UFE}#mz0.ߛ$s(ݜ>%.> _ } RFq6%y*TG-Tv۪FPyfkX<~_4QSԑd"``yw+8ۓA(eu>ijWR'zCNw: `{QEy1TjA#ҭ'ZB*\EC3\%ލuu]g"Hy QYOTm 9BϱH,g0_ :V'Ҙ[k`ޒ>QEs,eA.UZƾ&5(W$#3/Iu2Y-gҊ+ڍ8{Sa}ڈ5&c֊+'2}DŽmJ6|^>zMi2M4`;QE|3UB7x\d[2ml )zB+ETHqهLhB/ ֧Ly8G3co( ub4Q^}Gk\&4YHu=No8Ɍn@fxcZ=ƣ&#a#Sަ^Wz j8u|b;-*qI@c'EUMhP K1>%nn 1؜u9:i+;uvw/C񖝦C:dQq(4442%m=y]4Kz;z=(9-.B#+=5czle%^B{8Z$a$uv(fg6 5[S& }(6?.YX+2m$N l/-r`ߥV/czNw><>~R\[׎'֢$iQe`7sފ*b:͞/ Ckg;yA }5-#E=tI+u7 255 363 0 C     C   "" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?U4,t#UF\'5+])~\56l?B~GX.m} >b`(ag>E?V/] _l?B!XktX.uеjA.6qn*x =$j Ү4[-f5aX{W|#u~>:|SVZywSe%Co$1 nuk:m#FmN i#yRcFPHTgok$oc|/GrijjF@epw1a}*3+$3ǚ6Eg_- XLxvvNYi_ iEmt+Տ-ϸ}`v9&[/|Ϛ P~/ xŞC|44'2*LG*3NMK]KWeϛ~2+L Cᗀ7|<ҭ57OԴg7ڤWy !бT,[_j!hW-yMF׏<`r$ O4]|opJ}W>&Ϟׁ5 g7#onl2: (;=RNvcf7 nS3mI!5daZ]3} >aػ֝v _l?B!Xkt6l?B~Mo`NsѴ}_(i^t(L:OR=Eᴾk[7peR8"+ė6 \ܼDv8?zԗF.RW* ȡvUIÚ,+6]:΁Xc=HwEᴾk[7p0pEnZJ 1m[p|zp.C&IJdIc%'%B3*9S,<72cEsoKu,丆6$es܀GZPԮL_hb1=Wm5(ԌRm~xMJ*7l~$3__WG)pcA.MMԮe3Y3RⷫM1iwvwY"t,WNxT"}GJ4M7Mk;H}0  ƼԼKmWxw7ZA%$9Q&9o׶_ivӋ9d!Xe=+|;x~; ?h7k ORKK_0D] Ƅ<Ŧͧk/O&~-xs{GH'6zU#XmRt`FN]@$j4Luޅ >yϷ3y),L"s׎+P%}r=V 8.%MaX&Tm%f#;xlu? {}r{^|u-ST8|TEHUFp$jz?VGxo[PxϏP:Dѷ#??b7L+~$x~xBޛ%^!4/5W hax"Jy…q1g7i&w;U0ٍcz^/;MY}WO8=Wsx?>5?&{cg VĬM.c+n0\b|}7:Wum5ZFF[v&=N+~/-R;Ia.5>.4ӍJop3V4:붰x5ˋ۫ MDr$UݕMͿIy/oOvwgg6$ a1cl ]ÿ)i7Z >AD ?60X:ROZ>!1׌"eO;IKh] " <s&~V[Ɲ(m|'Q =.(.-1ȑJbc1,2PH ҳ4/b(b37KsR"*dSW[mxf.:3j2Il:^51xiE^u/G{R]9U/xvo>WB$ {kO;>Vx:5 .!_ -NFp6*7Fm`˼#BSZ/L5#[j:\A6 b~uɪx/RD/X =m7OÊ Vx@t >wԝliooI∧zZԷٛ~ة3KvN7屌9&_u֟?W ]<',;6mrX1?ݒibXXtIx?~_]@7ōx)X!{ ƒ|Ă w!]<5㗲u.w&+fF(7)*I8=/ZFx\x#L|6t`" Mn7j_;zQǼRTw?/T ?hoxNĖuX Vgxq@l 1Ocbx^-HH6zki2)zPC /A+k9|Y'k[@wx)e1y|DT`Z |& xPomeX[myx;<4`qUoO?Km箛x\na>*9 y<[Z}l}EnbՌk7c0a&EPDs˦.[jZLILwZ b cV5:|}#:uX-Ah1Vn9˪xgWcOm:2d$YWry0L{#$+i'}ڃIc>48|I/[_'7bFmM.ܐThxc1-Wi|/>ϳۿ=?5޻xCN "F"vgyvK.=:ooW_"go]X;𶟢\^/#Vkk cbHybni ?0_]Binnಋ̸8#]Qd|5=%sh= <"ȌG?1GÁk|Qu-R]/KwL(Ygve>^\m߼>P1^˿ewOA^kľsk㫻OFf(-#D>gVFU=GACƚw x=gQ5xE#khVxԕYUddR; xFއjj\:2 EZ-ǧKܲ#/qH`2Tq_)_Ÿ^񅷉. OD#xuFtSIRv3| 0E|i߄t-[35:519F1y@QB;pv\MKީ__Hխ.uYb{8)<շ@<̨Xp }4ly &sKڱ-3h=ƓD&H3"dvy<'Qisx/^]=jRB¨EYNJc>>"Rq`/,HF sS]>czg>$.ƻGFE{kI55ŕ\ ":9#m^\LmWᦙGOɩErίޟdar1@Կ_ӿ?㯙/5 x≰Mgw|D:EbFY($e(C/i T֞l'Y+y+3_45[v/xCݵ4+=>7 M ,K2}4og)Jm.hCcyH ؃a< Svm+_OUx#÷vM~m+02HPN9^<~Mx@U[\iƄxK8*Ǎ{~ŏ+a{=NK(n3F̱b6_*Aܘo_֗[Z 挓IdI"B2/% 7į~;tEGo?OeGl.ALn"ǦºƯqiZ~ii59H UO(U5m[TBw XkqjHs*$8պM6H^GS>UNum.kp0e0r+xӵH%y'V*$eYH8$q4㾶: (Š((f 2HxJK닭7zfŭ2W/؜ w]62,na#Yr}{,hmմN4^F p81@Omu/Eo|ɂu~b0I=3V|= xc>"54jI/+,AU؄P⡹ hzkM3cٸGUiX1&m'vO8j%wWW7`[+{9. >\9ڤM7oxnZXX 6c ݎ#Пέ|;ᦰKԝu[A狉%iUT!5? x#7z-͢q\\I]$2fo3*@}NvI鶄ma v9O@?!Kk9\s}m'm4%4"4Ď"aG$s'/F46ry6ݺ,HB!1kc'4:6v9)|<$MxNp\Gnߓ!c98] [ҴWF{[HSʰgg?f9|9|k(׏/.=vwi~\ ȷ~ၖic3Ԛ鯾#x~{s> ʍHMFb###" 0G'֧9~B~ o5˭+Ije!K$ $|Q5pL/j5>xfnKlRVZj(7) lI@OZg hF?E7s C xL971$YxM[&W,U1Eÿ<ZC}=kۙAI xK#[N{!_TvffÖ61UxK&>}$K=Ut%X$8BF㌞qkϵzs*c>)|;M~Kt4ί0 cqEmt2OR%LŘ*$xI}sڜ >j~s:?Ϛ/qX3'°x|ZO:Sǫ\IKo!:q]<=C: `d2lQbXzO5:}HjoXyqn{mX`da`vW?W=f~gSLi-XnI2qy:爴_$2xVOB0:10"+N9-z皧FUT>:mgv6z.k4D18 zΕ=4W p%l}#JKcׅnRGC6;Sma[Fxߌ'xG_ z74KG\RZkR $38 汤½MemrM/ޫhǩm pGVqʠn<'89D،yp~):-1m ~Q.3ϻr7 GuZ]ܵ-~m%v} +ռ[i_yjn7i>qI$hu-2Np9ZKC>9!GHVEl3*7Ğ;9M]RKE5+x-b nwgOğ>_n|]Ïx'\S5u Οe0G&NN$v>Ng:gux55=oľ$Xg;i(3H|&>sak2ڽׯl1A'$Inqۓ w([?Dnj`_7K߷J]?OnS7oc֭x_o}"XQ+i#ʹ0^:i3UcEle~#=ٳ5㟆;[k|Qh++DQ_{ vmvڌqE"BjS ]mW$W|?_x" Ѩ]݄6PkڧoZլd/R񶬐imXu H,Y NH_jN}R+{%"9mB2ʜc5oѴ3úe:|W?9q 3%Íz6>H߉4׀]3wWKcoi/%x [tٴ( N-~/@u+6?'֡KⷢSo g9}[_ ݥ(8ase%toq6-<΃󸎽lf鰭ЯJw sx# pr8s&}~,yK }kJYK&-3Z $ *OC_iGrb$O %c~13y ׊dw.|'vntZ ෛz%ie6wx٫~kgOCw~2kJľԚGn!k0 8B wU[~!c6i}ͦO6J%T&08$Kț4w͛)hVsy7s;Kr} Q\xBxP9Vт 2/5+{w{>E%/k>!i-FJ8;0l+VFKKj{BXF{q#WUV}F6m2G7m广mMR ݒdOrE H.6(C ( ( -}曆R/vIqZi/Ӽ7>g}Iw'UbHcEylC_ ,߉4{; 庰Ru̐SȡnOfGƟ4GËU K٢2)b'*xa@ѧG8$hZJ=JK Rpr+.x |Q|KXi͝7KD?y@$םC%д_~%~h:[|V]T8-OMV mWφ_K6G[S.iጰ;B?9I|a0N)7 8p LWrc5-[\J2]Np$> koůiVwU$]Q!Я5[N{ -ͱrcvgGxU>x^W5x"ZY,)):ׯ||xcX$[;WKMMen-Y$AbՒMuM@m>4NaxGo, {lV$m i &_<+Jl&otp3#n9k;gB`uii͎3yqgx%9r} |xOGK8-'Qhz6K iV1;ߦ+ Ki|Wco}<73-c<q<J=1~JE'Kj/ucVҴNP]"mrs#־{wG/^Wf_JZ83#C*W?i<h3ZMǯ\I~oNs9l;JBB|6owxXi"tż+/ a;sO;7u}7I,|W^KY쌈oZ׎D+}Vx_&G4vwi1]Y}42~jXdx]L?&>At(}^_isOYE$0"V;C1WM,%qvw}w3–!k7mYgPX6 9\?3xEևj҇nI0##=ǭyn7#xiN]ZL6ޒ5cR3S?3SHk+9ͦ Te$CXt"*؈ߕ\(EQ@Q@Q@!Pz~|}/CAZ )%D,H8$c޼wJ-BAX7ƅ`ğ,I"'Rr94֮=(AK^A77yTo i(Hnoo7H8C#x|*jRϨ!-9 zѸEySxH8!IMzk;wid!“ӚoǿxıZ]Π=կ۴X!MIcUF9BFOB(=m3F}K:erB&?)mllv5c@ 髬\s5?KdE1>cnuk=jy_Ζ6p,qf> igm+9"ߡa;Ey4io;i_kzg2El.lm⻳t!APj-E9ZizMF"b@  vҿ ?᥾}^?=6/io;i_ vҿ _Wa2M_ izw ;wTut Noo2ʇa|UF3l>+Ox;OEC7m-.D" P9Άógou xT$`?,!H z+#tYB΀5謏K4C fA:ע?,!G%?!^G?h z*c;sYPM? zJ y[FY ]+W;ǟ{cx3IjZٳ0HLbMp$gk~\^|WNi'OQ ]~H}.vMկ+|[Hx%]Bn{m.ĺ_Om_+)`Nfid-N/hZ>e"YS+qbIOa|SVi6GRR.67cIcS"VÏzHnfТՖ9H2\s!gـi9O(|cegsewcck~,/g\[ C MА7n/i6r ^Kn;ۛ{l^wFޙN<[<^$$I|g̷f&/'/^x/|4n"^ӵ ) 4de78h[Xm]#䯆?αG<ֺ6k;tqG'OwP2*m9?68joִӍ/UA,8dlc#WUw|Z5.ty-n`b@'RH~ҵ@5@h)֩JM_htS~f_ Կ?~/Rhf_UG0:^C z?^E[_r</~f_ ԏ?^E/?}Z//5/vcú1m;`9+b9\$c% _C7Ha[F` dw( 9HU*Ofؠi"+[v>+{XbH`bE@M~˟ )~˟ 3?7[TP/!g.2o.C\d]mQ@ܹɿ??suEbr&?LMhVZT%n8^I^CL5S񿃵]-u[)#ȅO)5uf4omOyR]ymiMHj'=ᾷ L5=fx]HnPqU#+c-m2R>q׾|@)xWCx{mrU}oR 2l(vn0εŚ:,sMe вq8GEO+ M1c>&yi#×Z}5LfRi2cB5 t'lu;v-txn-E pAQU}Op_(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQPA$v\I???aES | U?MaES | U?MaES | U?MaES | U?MaES | U?MaES | U?MaES | U?MaES | U?MaES |ⵚ9YTuFTEPEPEPEPEPEPY*H6(dǝG cZ/)?t>x_BLt ؆ز_2mZX5i0^شWs{xR2X6>[AgB;Win-DI70C\vhk&sxñjw */5H0dvKT_&?} ?dȲU6xVElҢh)|LPYHSŞ9ZûkV -cB<ல®ȑ*7|A_}WÚO4}Cel<34# mHZ\?QZΨ]:2N#N=Y-?=UTӵDSqUg,0'#$A_Ix 8c%ޒEZOu H|9Q+^K>&TU LɒV ŒOcZ >"kR-ə\2`)m `"ĿK&5Gȱ`[˻>^[_⿃x(ux^|P}ݡF2)9Luv+T}xY?>gh+F$I71m\,uiLa"&d'bh)#$yffg4\iB o,%BĞ8hjҷ=/s<_uI4M3d05}xY?< ,3N-#ak H<4L^(^y:>8i6zmk Ztokq\ʒF̑;dnql} ?dȲU5~+s޽XOU@rGH̥Rڼg9}xo5x Ɨzn48<דM$BDdq f$ci({7ooIݥ;~յ쥹NW2:*`2q)<C ?L-m泶ԴY 2@`8|-ks9wD~fҬmu$st,2 Uzயgx#VPZoh:688#򰯷CѨ(((((|Ea.M Lऱdp%qzҢ<@,7]k4yF 8PncӆƜ,Km$qԚ(TTR,A9!vDu;qo{>`nTVB+<1}&M*M=8K714+BG+Ɣ2go)+hm-HF0F8]5>͡XjK+^"y'% RPppyQе]^P dʰ dAJ(umR"Ú}ϕw,s&}pFj<1}64<+ɦw9# z-ZnwW N0bvxWcגGr_e*"Qԃ-XS]bޟ&;9#I!P C^/z{_[32f=^EyNH Ow^4En1F>l1Nm 4xFآ1JPBP5]2Y.౅"Xa=V}֕]^Emyi$Hwb {@sk{ h%dLa* P {Q mnYX<0<(FWzx/";,~jGWِqMҭedI\d3D 141 250 0 C     C   ~" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? { x|Oik/(Y_VG}O_gG?Ȭעg|=όbң-R5 :33B$0 ZݎGgPo1hϰhW>6HЬ?-)w:?=xIxGQltvQ}e?gVuZAkkI瘉Tێ0j>9ҼQ'%m^oxrNmB+fUHaӞW~^h\WɞG([7 χ U:aԥ_ @g^uRQ;*FAOO/zNjVfg5Ig# ۪A_NgK_ɿ'ܧ?|.RًNX}pz~&$xo@o:z?4[m^T :EP';{֯ut`ֲt^ 6sZSq!k@voIC!TKq 3 D6%rSNW$4gH77=՝_vNqTGv(K1n4_F j]<v6/5G;xbU\h>};xbTü>6/5G+ h>};xbTü>6/5G+ h>xNj>xc[G7Ǘ RISFO#:_vü>6/5G;xbTYhχ7xN|Mwo0ޢ{GPp N;ŷ'e[x16/5BsJ_\kǏWZq]W~=ƺ1u&hHՉ@''kG? ?˥SnǎM?GMw{-c@1%K IYOG xg< F$QKdOڿ? Qa?#ؿ;0}Dexm\eEY%rw ?E[:uW5#Grf2JȬy_wƨx|0l_j5/ڵOjօZRV.6,j0>`s[Ex^/|Uk~jqY6"Ldzkwƨx|0l_jN6MD>& ٮ5EԤldHkL) 3{je-)䵷 U1FH'+wƨx|0l_jBb~~0յ#GMƟZ"ۣYAU?1=)Gnh(' ؞q__? Qa?#ؿW+πu_jnez{ a˦#E4R5$^fC? ?G,?CYh}ۈVL =zβ!O,X9H?Zx|0l_jwƪe4%t˅IS?33,=̈́Wk:֡-܁j x|0l_jwƫel?3Xdz+cG+gsBI̼)@h~6ԬaO]iw<d|IR7r+[7q־Yfxw^wᏈѵ6fiK#$D;FOkr>>ק.-6%ݸ99'~.wfi:HxJ{Ͽ<)&mwked!Bg7B\::YZMs)'Q|7__U𾡣zuMfmuI(??q* ~Ah7_ p<>xv].u2YN5=6[qϰd#y}/g'RZGE$p0>G Ҹ<GԼS?&x(#0[Bdݺ&#}:K[]>~(iڅߍPAY[Pwڄ 1_vp͊ObMafVtJInX$rjg> /:4,%32W~zUsM2Z-S3CD!;5+[z/1/??V6햯qi)ӬxFQwῶR{_ E,^U2r6Tヒ+|yqx%~>i7w٣LyI"n$hZgѼw _VņB.%[(?x4NVK3[h2Uv*Hdc5k/Sx'ŖNAE\XY!Vi 05 ~#>֬|eg|^gubO0<+dsm~1KK>.熡kQx2qܱ.!im^iSYv~,E&bvNU2}>=k|97>)${uI e]D, bxFu۴-^ZS:}OnBF}-3*~xZ"SNj ,'LNjP@8WJ(Š((*}wv_ERӞFUwe!3u*_BAA7RExo ,TTxo ? IX IjZONCj)2>z{P(Aiu \ZE,2eaHGvz&kYXİAm(aU@U((((((((((wmlqOf>J65O_:) Υo<hǺASsLks>~ԺϏj6- μ`ցd2Y*19>|.|Oe=֥@w.5D'y|pMzAOU"z_o4o4 \{ Oo5k8otk |2o~Ӗ|CE#֊~hC0?N)-Z_YiԭwqS`h O>+|% ùⳋU/hr)%Fx]jjsuvѿ#66i 74`h Ov7/^rKA^;q%GyF쨱o?v8z7VVhݿs;qHaE4)ta 8 586 1000 2 2 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((M" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? n8]$:Uw]Ł?*)*l9eƻ?t/$8Zk6[Jܟ}3c|dRiv4r&@7Js] `a֨G}T%ʜ/qih崻 3o71$gç4S$AA 3}²KVRMlەA秿W6ek=u&uPFTF?LL)$yZtW7.Ǯd{Qۚ%yAW?ϭi\ޤ-Q0?jk VFZ4kfe~SH-?jgj5,.2Gg[ޥ#>H^ټ.@;|ԗpD@3ҫ P ԚtoonKqӿֲqe PݒlNkҫg߷x^E@{k+0i1LͱDxJIIc0 Lt.#qRdV|Ia:k邪<L+2BOV'cj~!ӭ$bsҾ/o^˶[ "J[!=+-~uZi? ^Y]6y;{t'n73nuWEYX$X@9pakR(#͞g?+sIJ18 1g ֯63u.z(S5 cI?+` snIqN ?^^qo6 s9?+Zͥݥ0!ϷB}:,r֡ u3,}:8ڰ&sֳnKI"($o~:Ų:F@]͸{ҩ]|(G~W-$fyҚ4bIX,1ۘcx#Xr&_5 _Ziy6ǒ'T9|C.E{/,{5W~2G'qO#`{8}#VX7r9Lo|NHS?RWzH81YR2F uֵd`zm䡗 .c H=n⼛_%խymkgg oQU}O mX=EtltS1Tc1RH#0q++g2Z24ҽb7s8BGgw`=GJБ8GP2uD7uޮ.%%G}>|AymD3#'Ay{ST&L6b`8s\d xUH1 N}sqڼEk˖[ ,;nB63+ņKH-_4q#sXM]jۓsOwMmF2d 4ԑ:ZQ:gJ1N7u\Kk.zYShQ\Ĩn 3ӯֽÖ2-N\qV+,SA©;k뉷Dn3[HH ҴTF_N}K:g}~U$4iWNUcgy7a)!O=NF#oK3_F,F%@I5 Hb^'(R(& pF9'9sԔvT"0%ՈR[*D>EDd."@\' yɯ*"x%xn"0 Wu 9hee1׏sԣ6iՌ]/_[@]̉ۓzj!e>aǧj/[Q8\y-LAVa@#9#\,kʵ7dtTTgw1#D$u`8=@_It$lE+ p~E±8f?1(d(`I^Nrq5jOјz߬PM!n lb;dhXicn/>3'nb%g{s֢E76sQ%9ɉQj=,(  Ip`p?ƐO\W+e778ŠN71~"jg<1%}N\6fy^KyUbӱP֒HXHor}}+ǭ)WwQ.Ji]>]~Jgmڠ d"s݊`u?wC:kX!^.pÓxY+7\,%P9ku.WtEV>e.xO3Zʻ&H[A?α`-M\J$\աdp|cĎz=*h>Z 0}<#(V#}k)r5k3j[B@*$Cp;֧]>I,|l 9= [wOpQ[nt79 E$%!S8^k~]߆/q^8ץy\W5r^7բt_c &O~X=ԤԴۉ+cGl(c}iVƛ[΃{ցɩ\j7W2Wug[hyP;|܌F{aůJG|:MοBmm0Tv=.C~ֳ\ Y8@9dߥpնtGDjFp:ը.מe,pOf$ct3*)6ɁV/;kRTj(3,V#E8׬c-.H~}+έ+Rj^!ws-َzaı@chK]c)i4m#E$g#m%_l$ t$6:kZSbq mwq T8?5j˨\\2@ܠ^;:τ_n}:ZW( qϩ;'tiډFqqc扚`t N1ϳ,$JZ\EX W4B.Da'2ƾԼ0o؞}cJ-cy=>-;—k[m;ckѤ$O5a%I8f"їy?_ڼLnc sO[Z>P:^s~*y+%nI,nGB15~*>$ MBy^ZMUkSjrQ9b%X'@Xzgү.FO6P %K5 OtʱyTry}Mh͠^F$yp;wF&n+gM޴,ϳ7 7hi0O9^EUՃ/FyuZ k p~ *A: oW<]Fzdr,*9K4v:j2Zn6A\摯iuGF8XmOSF1|ySw)C,e1PD ljsgT;cA->\{04*o=2^2"~=+<$l Fk΋I/VEȬiI ]pF3CE\g:l$g2U}ώ?.\Z:Sgd7n=jnm S>5<'*Qtӄmi^5u fF?!uջ㍥sGxޥyopۣ|fnQS_.࿴F6ڀCېyl^q( +f8Tl͆U`A?5=ViC `\KCpI8!x>9\Q,*a$ԧdzѕ= Z͏|bu+I9CB3 Gd{Tv>vyG1@s^-\;Z|f>C356Ϻh>ul>_FM)LLpZoxӣi--s؎Iߘ5DrL "1:/3lUڼ릖9m 9#5VY#\sJRqgl20|%8T Qڷu[x˹MԒe)q',noF[;rR1VG-j}qLcr=oƭh>'dEP=I{mEd s^?gw am^f%7*5\)FyלUzm"'H<=c]Ґ9*ԢݼYyb)8H<m @UUn:gGWi0dzp/z>:?u)Je%w^}9o#'F+Ϳg| ʹN}v'^@=k*'Ym#;լpFHZc 3[@2\y +O=q?TѡV|ic+t9d3?x>8![;Xm%nG.",H =vz,ܹ9?S%Sܕ]s|1^QKhp}GҽYB\3jAHJxͼPJ ŒZNo[¬ZƊD%^zڕP,"WoQʟxyZpD$ ۞BN+,tJ=F ߊjFN-Ў3Dy'?JnYn"V1r Gs:(8IX?w <I1dHҹ+6eL\vVHPHY_up- [#thĮs'e6 ݮun@<G&,UBg;I^XR:]Υ'&g>uNSP/-})Td#ڳoYapyk(񯋊"e wIkF$nT= 5|QknCy:*c+v rH#Kހ~NcvmA2g BV㊇W{k,md` }4{arG2Gke^us)f V pD j F;8j S9=kM "9tbp\wI;ņGrH-kx~Lml9gMotlw=uŗO'9` 8E̶LLQ{pǂ8\QǘM >AYIEdFbSpZB4krnh@ 9).$N}8SWfp8K|o`U ex\Q=YFKr~}xߏ<:ZFn<4y%RW0@FU3=+ O-c#^}mv8)֕*A<+aDӭџR4^ն~1Wٞ\CRҽ|שMSIs׏w C=޽vźb`MŤe#89$qU]G9Q>WcFu5Ë y;Ⱦ[@}3eؼܞZ[3 = ]Xc2՝^ذb =52P]D@ힸ漡/ p#$|sX_GŹ*C=pZq09_0 [Z±4jq%vebkT54ۀh7)RhV$N@lum'Y-"Tpk< mIҽ!K4FRpһ1 ѫttWN;2IwB;|@.k+,'⺟ j7Z\m ~y+Ps{;+M3ҲQw q`5 Lpwz5–u*E')8{Y<0 Яo2'J#^NdFv}:׌[^o:2:3؟LoFoZkc %_SV~o'څdaOo_ιMBMz&򪗍A_W56mF74ȥ'<*lMX48=kkxzp+ ŚJOiY6lp c'+Z%h]&UITO?KK$fe}{~U&qbT d\1X M+'weնZ7$c5XPw 9`m,Yô2H7)5h)Qb55J]+c8?OJBSD6`ʕ:2F?ueJj$l<F9{\A,q+D1/yuNlc<]h즸 zJ]PrG,.N:UI s8an;Y[!9/u cU_J}yIrz|"zM.+ ԗ2;sBd\kq";qzW"Ao?Rzf+{da^SO6W;F7xn췲 q`35ri~\mJ} `TC$h kirn}c+jd\jq\iI}Ph7:覽)=JLJ!y׊]W,oHc"#$=}zWVk7v W=h̓iA;ᕔ ωZ3;GMq(ĸe-㸻ZxF3r:b?vwk#4p baxo\ ,xcvvTqF9>s=T ΎT,]KV[s6v}֚6ghVY48 Z9=h.Y]3+MOAb1桼n-&AV 1 ճ_DhൎwTGOr;בxA\ͨ[aЀfQN}.Pʬ B(0G 5J)7sr W2Ó^i6+ja$Uө#)\v:i\D˸YXsb4[c9ϖDZֱ-$Nxa^Y0r^'[Y0HN*sϕt*O8$:<7ܠA1^;{dƈ(@'_C]xV摹^N=+F8M yq5R7h6n [W;p@O]셷eV}HF#4Mݵ̐1N z}cf9L(Wy%XYUtR%5tr $)z@l3DN?}BWnwcd_UG,q<1# f<7_iIhB5/iq46̜lU6,zzZ%nl?^I^D?J腦(>IhGn=4R[ D7{q_+"-V1&B2K5 t-b֨߆dr8<4r@ B]8^g\{4N-C5([XPg_@uֶP 1^3iIΝXypB>UQ*L%zPJ@Qai.C-t76{ʗ:V!%g+X$ 01Ts+lb i-;æ0.\'^2=>;[(|/Ҵ ȇ-59 ԐU'u߅=ί3& ur2 !+P{yNR!U^:Qψఴt t:1>ۗ݉:E  W'O|˂c]ڻWWJjpRFRM;0JC&bUPXA^ ]՝rIZ>lv¼+W_$B G\ } yXٷR1]49qz|#lxO/BoJֹT.OJ}4r}<ׇf81ܴեKg,"Y!aʞ=sYԯ, oY*mq">Fpx\<ׅ8^ӔC&@ۓ1k4BP͵0Ah2ʪYh|dqYh+Ռc>p=:h5gșJ@z׬\$$Fx5zN7͘%SϧY6hg1OyS; ҽ=aeSH(x%G2yPr[6S֛sXYgIz8ڕĎzii"HV-cGڣY%5ڪ0IVd׍oFzPw_$iJed?h=I-<m̻P(_ ?nTm  ͟jtIrZ;Cwx HIDk XF 鑑F+5ҕ3囹WMOح1 Lr:qOQIthW!O#ҭΆ {Iʔ_E5T qIq&F{\.F(8dCwoW%uvI$CmbNI{c]F' w.Ov@J1}Z?-䐋'6լb+>Bgr׬B֔dr0ɶf`Hlr95QФ>j̨'kϞ}5voZYYA c%[PNIQ-^VWkj+S'\8TCjo*HP8C PI棊!AVŜ+ g=kiOQJN8{b'9ZV`Zp Zˡi+$tg'w6ed'l?di" {GbŐFzUBmMF_D]ˀ0=B2pS8cq5НfSG@ھoTE" e2A@L^WYM77E\#_\8q% z:Mee=tGM߬/3;fLgZ}jfxq7CS?Z"ܡiq|1t\#`<\ei[:Ur-q$e),&6w 3\t+D:;KI4*f{q\YPx?y1>2-+;SjZk2)kkXěwqN{SNSL6סDzR $Z7-(cA9V OZIK!>Pv DRVsњb)ȴO*`yP:Uhա_K2<}sR5, <{^(V8NTK~V%}HXpR]\L 'wn8Zܾ4T8b# 8'ּ?pJtX`{U#:a;[W>+K J<OqNx==>Zm9L@:tqS'QNs.BsyIny匝QuSX[7jW-oĤִ䷄nsL4Q^T>#,j W4jAćQY'_toQEk9,)VNsZi#TvU2rx#?Wl?D~#忈 A}(3Ѣ} EAXZI;Z<{~(N gZs03ڷmݑ#bPW|3=ڊ+Ǝbxq$z=EfG<1#<4`:.d(//~9Qֵv+ۖ'Of䓓!S21ֺࢊpbE9_ p$xkaZ(WɎv l,FI_AJ{QEyhO,p@82zzt)+s=gWgfotoxx-20.08/images/color-mode.jpg000066400000000000000000000505441362435004500170730ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0230Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 319 368 0 C     C   &" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?GL]";{o" N SV?5K紟ѣSi?QQ@~?4}'j*(_O=j{I}Si?GڧF%TO紟Ѩ ~?4}'j*7wេ~)-/tmiYUY\p ;{4moTSO=j{I}'; B>xg;kk<ƈZ`LZOM7^DŽG7fO muxRJf sO=j{I}?_~,o c Y;&km%#MnZZG왧??/Zxk$z[ (XbDmi;^˽?g_j{I}>?5/i_w%hw`Y"SA d5e^,o;ynqkRAǃВ{R|%CO=j{I}=<aOx$hm+]|[_vgUi}4]<ݝNXnU$F;D,rC{˕ZܙI(>bTO紟ѯ|Q-IW/Ɓ?h𦓢KLӐEvFK#G]c:iψ(d`#*AYFWQoٴڧFO=EEY$j{I}>?5/ڧFO=EEK紟ѣSi?[>xƺ%Xǩ^Ej)b wpϨKxWN[mKGYe0u/#Tu@+Si?GڧFcW6bV uڪXk*cFhО*(S^Kջ#ge,$Ԇfv|g^(v|g^( 3Q_Lû>3/Ktû>3/Ktϙݟ:?ݟ:?h>xc]#ekƑwp'A9UXp;vû>3/Ktû>3/KtӰOsH<;?^xovWS$uɨ8!v$JPmq5M<xZ?%asxq/q}$)9?ό eό e$o/?Oڏú6o? 3/Ktû>3/Kuwy6_K͗O#`<7]_n9^5_YchZ>AYt/IMjMe-7ޅǧ5ݟ:?ݟ:W4yl'm;2o[|Qm3\M`vFJڼ37Ǟ3Եȭn,ap\I{$JL|өtv|g^(v|g^(l+3j+wg2wg2W>f Vs.Fq*6:{;?GG;?G@:kz߈u)o&0$+aUGtFv|g^(v|g^( ~!u5 2:V.V֍ivwR4N<l⽫z_ z_ W !]n(ORakOwg2wg2#RWOFkJTZnҋM>jߴo.,򭫩\Qę9"/jqEdtb̬F1RaWû>3/Ktû>3/Kua2\>ɽM>GK4-vcEycqM[rOc싻4IieV ,j#WmGM@E=E|nn?-ּ/xoP:NJDaZBrH 〼'۽ǃu)UkvaIAiD[QXuO]߯5n?J(((((((((((((((ao6Wq nbhe:0!k5I^%,q"D]MpYխSs\gj"c5cG P0_ګ 3@l)=7ח5s4b=hxk7Fc=1$ 1$kh"Pԯ.}'R)i#4RLR uk7K_>坕֟ ɼ $ c1{3_uE{Ա b%g"2+ ~~ 𼺴KXTm2N{6rу+xJ|c,nmBQ\v?<tK]6ickʉWi#cc#ںm͌gqW' ůY]i<@  ;I%OzʰͦoH7S:h6jpa+$[Im7 A)QEQEp a[qfwxxse(r`I, J[Ğ9}xqt-S[YͰhcn `cu4(@QEQEQEQEQEQEQEQEr?? j'D=wV^An-㺂H&ER0A%~ A*"{:=)op.>.x ]KV?|m~\|IKX>j*}8ςrUKG| N2qz/٫|Cx'Jբ#Xe$}J]onljxzWc-V=BckZooޛK[uXH1ޙҸoI|kLK}oLmGAiMte[.R7}}{/n_JxH :pUyWxGJ#¶zvC}jsgjbG1eb$| ¾xR{er܇t Fm-p=bZ?e-W5K~:]5߻%pK[$Y>BǼ,Ik|784<0^nKM cy*]SX$uM^Ij1D%8ddô;HRW_]y/-J`=4>J@3,yσZ5|:o뗗z$ծ|Gwӗe'X1Ҿg=㱦iڰ/#J.$_pitx{F֮!9:]W Y]?$|mO 85fWzmŻjSIe%$rW# qկXOQO[P$ "G mьu>u/ZxzB@H]Fwl\s5_Q%]_A4K XiZԯ-eB5eеt5XVtaa3=w3 ׼G3V UD/aH A< tѫo{6k 3?5?G$Wti˻ky)_9^=.ፄBu!Ũj3h|6FTW-*NUuy澏[mK~ukkuY9%$(OJؓᇅ&Ԯdl]GO]&tyY1%&Gk0ݯ/z0c-gv) iN_p1$Ae r3^y~Ο 5cxr"O}-$U?Q< gj^GoUUFI&-&T):8L0# .jR뚖XZj6V]!)URBh$A"O]jM]cuqmOyR2nݵz'XK}[⿊%;Fw*~r8N=|9&OA/t92}4zB),ۆ.Cf}x960^{X\o7&h+l'һ]IV pk_>9мߏ6za&薣O. O>Vc}t6OV./se* KPg@ɧodJJ7>" gékVTAO \WGKɻv=?:t)-"o'fBt--`[rw$@Q8䍾Əh< /|@]fя,~ Ԟ(|/.`R[̤'ޜUHmR#ddPCA=|/KZmǁbU'҆:;diBy`aN+ٿ×^Am\_kLn̑#( cVhݰJe@KoL_o^Ï~7> Ag.h̚dpUm>=Z~%?#'' G^[X3~XڻC){]NVv$g ↕ΫFM|3N??h{sOJB6lɶ6ѻۤbNۑίū x< cAQ~%t~".~,K #ק \-Ҍ,@/Ax[Cm@#$@ckGJߐӾW>.d $ ʰas_|B|}Žz?Ih"rdLG#);w6~ַxs^óA6NK#W$\6p ^?+ip}9ETQEQEQEQEQEQEQE#(u*F=> ?4fbrYc$<[/+Mt) @ญ lA|rѫI!k {m]\ `\|;xn:MsEk Ƥ |<𮏨B:=v][XE;t=CV>kWX=:LӦI!vya. m |Iq[a_i ݻ8 W5+OȚiQX$dUN(ts@=3^m,NQ`>!xW[֥%I:8lƬX`+_ni;%~^ۿjֹ cRӵiNrAi}}2 mQ8N(xCBO4]9,`\EjE\`6I9OX]ɡew7.-3 O< gqiz'3ho3jy#ZveEomBB[Bi5ǀ3wO&m62Cg%M lsT+y<޴tEO,q<*Z>n;F#>ٵ!h~kn >0S]CĞcw+ i$ f@_h󴎹'z:z}hrX.a[XQJ "E\`6I;95O kԯ;_j#a6QI0r9R~Sv:U>g,-q1xi:fM6+$X#r3~,~z}kP{ZWCb|1m5VdDvޤ|0nZ8̚&^hlƪX3^A ǀ|1wacc?4F--\/ኻc'L7FK7` '߁`9Z4P4=6J:\:}ZaF٤*!(z1WAvf]MU !g d[S)QEQEQEQEQEQEQERQc[hk{ȢY١p?4s""˖Q#)9YP8Px'=-Ft:o-|M*z T:nGsZ_©$6_-u _6LoQB@ 2c־Ў$'h>w><%č/Y}Q""8k WzO.YEwHu%)X5ȣO6X't w&9GUs{_O5 O |>-̶MmisWeaTh6@jĝ.[u:?6J1NI- F{]n'e:-*qȌcL $ g@&a(yp媩ۂ~bq^qW.~7t{mQSAzkkxZNm;O[g2InW;7?U|ԵkV/?o]I @ÎHlwm?3yxac[7bhl-l<*}x)xJUwAn .x ֹ=KQ~so-Ь='Nk: G2F+ޥO .uR>[M5Dm X>~G=j~FI׻?uJ/)tբ:j<CnSu"c$¾Q)Ko_ǁx6]*XF5l06\r\Y{_k~6>9F FV70J4E~w/]_w9L-j82YiaONn焓Won.:VKORxjuX>gr~c&z'b*2ONٮ𖿫i7u kyuVIUwĨ=M.cc@ĉ'EEK)~o"X|FA8=j -}nnIj~ĄלzT_4=Z/SGS)bSf\1  +Vr|3UKc%}_324Ʌ#-Sӱk}WoOm<%c-wo_0߅|a'~n.ujΥSnF3ֻ4?oipMmiD\Ap& UE|⯄,ĿW:_ ^=%{-M1LFʉ3쿳 K[ˡF)À_RrCsȩi- kW5kMMxޑ3.Gq*t]*ɯxi:X8RϠSbt8$shQ_XCXEFtJ 8$ߴo]+ᖿ.kxSANn!vHcA bvcϬhoo_< GnuOu-O^iR3Hq T'oOk-u[i+m) 82V Oc;[JwW|OOMO =2=D> Y-٤h1rHv wv$Ma~Mi [RKh_0Hk'|4y> m>$<)+m.v:'=Q{c^$|/ϥhɬxByB*X`pzMr? + ]C7vMiW|pdvCT[ښO[+]S+5_;Ox?֕%V]o&fdDU9bORZ?\bkaI[vK dE_[QE!Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@?g'-tX) `׳Kw'_Y|4oZ-n8Knkp1O.G/?,<;sxC wxQyi]\C ꎐ)u>xB4U:lrNM3ƺ)5 5)6@O5n"A/Ko1-Hg>?aCEX^+ȩ#x˸2Ƿq h~'iմY-(%,E¶uV#V"rcq xWxVf.|-|7έ:~gkwDZdԖ9N–-u\xO[tH躾"^B+MȬx=:h-=[ |+6o$3%A)]T\ZWvz *=ąL-?x#sӥqǏ1|N=j]L.ݝRG~9_֨{][٠x- n4}ĺbgӤ☺>hc͸t 4C?SqO}Ml\ѳ) 1^i࿎u뿉iIiz֧]Z۾ h,J蹌88grO|?k i"[{ ;Q7?UH8qJ:{֧QEQEQEQEQEQEQEQEQEQEQEQEQEQEQE]BщOD,7~U-| /[X:Ŗ8m08U 3%4ҸssDeA) wNIRFeWVe8`H>g_MB˕փ\`& ;: qGM0xCwS2> rg\84+ ?U4ZvmXrNju/ =^jZK%vYG:E|i=(M0ship+zT%Y76gRiY4ߢo>VIЋҏZo O-+ThQ3ehi"%?'B.SJ>SGpY2#+oZo O-(IЋҏThQ0?_ ['B.SJ?i"%?U??>/>VIЋҏZo O-(Oehi"%?'B.SJ>SGpY2#+oZo O-(IЋҏThQ0?_ ['B.SJ?i"%?U??>/>VIЋҏZo O-(Oehi"%?'B.SJ>SGpY2#+oZo O-(IЋҏThQ0?_ sYl?Oj o:I$XkonnF}TKq W &lH?Jχ>0⏉ZDrqeXy{ČՉ;TrgֹjSt4tjyjߙ6~%7Vlc3C2Eȇa}B+@J#FK%A~lie<^hKx)()9߳pW|/x]?ٵMO^45-^:ysNP>EU@V;_3Nӫu bYiWAMpW^O_~&鱷'Z_Tռiѐ pcFjυ4X*2Ko քTKeyA &1#M3?h_&o'O7y*rJNiuYO˿崛볮ϪiWQ:XsW6W> U֮;}VMBIdG;? jm_U袊O_|+ײ>"ѵ{5 _*ie=l.\%v{|ŕgT/'~5տ9`axᮕxgT%x~ml@-nIbI8m[;jyMfx?xMOVM{Ly UGGA2t>$z^{[[\Lm!RB?xjyMVi1k~#ҴԴ^_3,8X(f8'=y(xZķwxtKLkKTY\ųv w q+?o<o&0|IJg5=J[K->%t)c,9q E-kM_Z[(aGI$En Wo<o&ws4-6 TpN,) ȀE(((((((((((j\Pm'A Jw1O \ۯMD艨o=gi?O OR |[Z?o4:{BG Bb T81o?lj2@/ ^Kf[,vg*TP.k/uٟ@i?O OOY"jxKk_*Ƈ״QEQEQEQEQEQEQEQEQEQEQExCF'J?tuOV , tq nh' xAWt+:Υ%3_y2dU`#?Ng 7M=v(5 qn^!blp$HjM?dM?dZZG^x?đ_~$5F-;Xբr\ FXwjzQizttM̗ 1e 睤wbM?dM?d⩭4_֖W~(m}|֑Y40MK;qvr@zng$sEFZ>xMXhp9l5]\;Fbq#v [c;ᗍj ?ME:ꍥxiB;}S{5ɫ7U~ھ3i:[KYs!~A9'~M?dM?d /ឣgjjm=8cG&&C!b|_o ,4t DjOcwDr(Eefnaz^ Q Us=Z]|T~0s[vk$V%瘀UxPy'Qm_Z62]Zāx|pG¦TqѢ%N@i[o/4UۣXKx:(8QHgfotoxx-20.08/images/color-mode2.jpg000066400000000000000000000635661362435004500171650ustar00rootroot00000000000000JFIFExifMM*V^(if%02310100Fotoxx:resize| Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 8 315 1000 2 2 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((z" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?((HN*)%qq6)"p2\ޢ7BSlϝ#P85.OR=M=WIZYtũ]\&Y]c5 d\ֺQR񦉎xtuΊ;z {/猟֯[}ZtM#Xcڦis֔/{u|Ț(((4 ( ( j')Uw@r: +<܏_ցr3 ZR[㚝$ sP.gY3u$U^ި1$V5:\pE` jT:His\3ukFn4M%26 2 > ( (PhJ( ( ;ѐ*&i +I<5ҸҌvp=jXte&I=i8ZK.3SߥCq7YLy#E]{Eh֧>Q\ƹj3pjq75%f˞ Ѱ" X}v((QEQH LVm;UY7NyQL+ U^MC+eF,79\榎ɬ4A\\ ]Es\sK&h$?\f#\fJs'$}i ʃ;zgڷQi)$,6b : ~LV<++]H+]8 Cd$񓾇\p>sl7K ;4ȉk[q{v@NGJMӠ?)֎#i#='5m!o1ZҦtdͅ ZG5-q5ڞERQEPEglw ( PnMIrޕE84#L}cU$8]G^N*zɱ."g IE\nsņ@ܫ=j/i&W9utx nrx4-f`"BǿPjk=̋&>\9=+k!Ʊ%EBtK ֫cqnUvҬ pkwUnGF#Ҵ-Uksop>%r(npz̶nݠQEQE@QEPiiR>QnMO;c9ۆYIdoG'SU(2H\Cv5H&4ss4z+d ʐ@IHlA7I;d΅UrWȱN9nn|Yivi1&I5RصӾ"Vo|3WF L1m^"G_l] kt8#'1M)4sOo*F"Yp{WRWn6].F#VXgo-Ow_5d?bҬ! WVq1 B Kkۿ+ŝYӡOZGitN23]nBGhVrF, }8?#bf̻ct(kthR yRW9TbBZQ9+F+ӛocnJzV2v.* &FdAǩW?5NM#y!5sNܫ`w >6&U9;kRmHulN‘ I ni~4^DP+ݓ\{K\E,naN+'l[Fh-P8\Ӛq$ǠDC(e q~kbmB?yGvsWLetsIYȼmVyc/HFzg&R+>7lhG* ĝïOIu;1w>֫.½#5בW``sRrs/ sH٦=Gͯʷzmzl,̑] XK{)B҉S;5 VV1O,V[[a<tmNRu#,#-TsoEC֘:%QLAEPFjqg`=RٮW\P|3ӦM4L 2싒kSCyy&Yms?QJ F`A?_ m*+ >H=uo<߷%\Km6;U9_|W Wrd|'zǃR+[H3ZƬQ߅AmGkD9z;ZVMdx>~|QJH彫[ ~R1JN<Ě!n,H#?s+ȯg+(Z_Vi=Is8?*O(3iB A{p w(O'qJֶZՖLc'9qt5|#jxO/./cPFrz%R2VJ\f7$?QYj*@;.ޤ*uM;v*"r @8"ⱺbGnMy{ qxSMӵ.Gkt˥?) Lv](Ԯ;z*on`!9 )sqwq\%VQ܈Jr`qZ"zgt}O6'WKXj>0ZhL,E ▂Š(4 Bzk*I(F:e^]FCUYzn t>`#6y>q:7@qӎwְךD8\9#  I,b(B <}ExjzdS@tѯ.F@`F{f~!vvzdh+^p^ m -!?J]^/i[[( =?kj7JY_@YX8#plqNT9&4|֥acpc^GAM" +PIy'hl.xuݐ}GZqڼOோ}HW(XA{d+Ӈ-1|CJV}q^?٧9Ϡ5_|]7PK-D|;Mx5l69(=;9&EeATu=M}#Ca>\c3^%e{v6pÉ_HRPF7@xt2Ζ*s#TEe0X7ʤQ g008ׁWP\'jo'Ejܢxj Z(PhJ(B([l'%ɅN@5RU 3БֳM!bQ2F/!kk(\ܯ;VkCO[ŨB) j+hT_SѼ} V:tklde iTkA,ǿJ$DSL-B~&sQID4i6cqj][jY!*cu=ik <0qMr-:\w\Y>iŕߟOjx$Y4Okoö:]w2m $k߮xWZH29(!pqߗ{嗼xnj$f񕇞\:c5kkixTlW֡O+}j0iݤyTm;NFEHQLCP-AKJ<`;b# +$lA+.vAIhZ# #Z,V7WW}Ic*k Vkխ⵵b#8N8?AIRw6񍟃5}J?Zɷ*vgR%ӵi T*YOqȯO!ҮLitd :]W_Dim"&ΆxG#ܞIW:O}bkd6¸#HJZ۽\GsA8k&Bo ֳh3r?Wuϊ6_K=5X¯Lt\^WH;8W䖶JoEX)ހm8ܸ]qru̷,vmsc+u H;(_jҩMg3ķ%-\e3y= M9%F`sz j}>rp3Wk[g0;r}kl>ۚLpQv$UF/bM{%+yڼƮbGv^ kJ @ؙ$r{UՃBwYσu h`h2rq:/nn..fkyFXJ˴sQ6~{~Pݓw4-*\} xxN& #}V;nAvvyg Ӯ}Kha4 # J?;c VQkU .8yJs@HҲ՘;+ҵrF>KC۰'nұ(Ha\0+ٖEzVK}kiqȻD-QT1 %)$fV85QGҀ<ƛp>㺴ZF*l?9 ~ b݀N[Gs@x}oZ&1ɅDbx5/Y;Ky;hXfݬ5%\O=z i35域yrJ3'HgGyPO`eB\zV-̒l$l+ eX, bh4w6P{glZ/Yu][[ \Zܴx(iv@QepG=:`yīOQ#3t`YǨw>)doSh0{w E,WXA#6#e=ͣiG瑆N8 ԯFUD`zZN{cq3y~|YCGe;~ktPG\מxOZm*c۹%^(]رv+>rb@ 4{׸SU,Q"RTt$kijpZFfb6sİ\xgQ Ȅ ?hk-a|QL.WnO˃8S5ITf7m1ON,ЬaӅиHUzW-G(xf`.Fh/ׯ%4nF>bF}{SN3کkV::s*Yzdϧ5LC[~ڀ=sHnkHּC.Uֻ&V~E"K@ (4 %QIZlqּƦHXs1^wpF~HkR5>S;m5*c7=s\_X2uko+$69]I5F~dvzףx~GΌMwM܌Hhϧ5hr>rdJ'(jt UV޻`rIYQVH;QLq@\+"V5޷hC9H?TDn#k2;1/ pڽYօk^{bwX'\E|gjSjZyiY$洺B=ÌmeMG#1޵,f) IFck1] K^O%;*qv͸H/` @'aǧS^3ESjۢ;BJmeO>Յok 4Zgkc↧mj͵T\ga #5 JwYJms5,w7Z3ڥ`Gc^cn1ڼr,(с,s;W:^a.m-PewF8rb*sU=\kIoiBGs|ivgzGjk#tfN]MѢ8"Q2 ^#W__$ KAv#k{=fѡf"lן|6WF23^kMYuNI"N^հ4٬T f1󖿤XW:qCrs7נ6r{}j)kf%x W\\帷R.Ҙ[`#DWYL_-nN>g`p?:$5mn]\ծf&ުyR+i;|n{MA-n*|q(ez98 :&1EΧsy c rcx#R1q7:扎hDUbV~Ě:29mt?ΎNO_j]<Ӎ@4Rc4oʳqӥj[xFr~[[ոhE▀8E((4i*9ԑ u[Ú} LE`O/u(t2I%$m΄;nji=9ɉ/xJơs22ǎ_+̞y nϩ'57}⻝4y]$gGC޶<3;]+e!ʌpPxzP#gnuW ,شBkb8$~Пj` ;Es gBzX3xM 㶼qlsۚqi15t|6w@|ΫSl~5W๼5KIrW =I0#Zm2bܳg3F#ml],Gt"#yXk /R6nA܌&z0fH5:ԩ'[ه@}SWHH1El\\$$-T2kaJ*['yl w 9RNH L\xka'qWt~]8渪\RTl|PCK ]+Ɵ-ao&hν[ޛkuk{˸v11W:{pbYi{cu4t7GK(C(đExnQmqIJFwNt|F-3t4L)eS=\EarG$s1VM,sd?ʪ^5F//(Q sBҽ{mzk=-c^<2KFpS5cT X .$Vc9QW&g5{iq`n+;InK[;};ץQ]JmRX|%'ݺsU>9Ɍb+ ./C|=K8z-$.$^yv &$h.RԌCIJi(gyϊdam5sy{q VޏNHCDNr9'WV)Ff: A_JM>[y`$`܀#>Z| )\aAހ>f?zc3G"lP?Z ʟ2IdP}jGvAH1؃^u"q 4 4d;.TRf&9*G;6; Cy9+|c]̓[:K!qx~⫋ٰ9'=[4O_<5i<o nwe+(aZiڅkǶFxޙom:ww!N9^sHehgQ?S^Aֹwg[Y\p1]_Km|c4 a5Q(ʪM`^=y>θ|O;B]Q6`F=kM.s3dq77eZKE2Hji>,vX#G+bSמ4<ݹ$uї-g1:7ӥz׆g w9*zf8ڭAPh*HqڮTsd(RzZ;S⻩ae8湋S۶# #U*ǩz橡蚆DccdX&x-/C8S\suFH]]*8?y&OvfW|LY.'@j&2z-$tq]F.6䣍Ixm5l/9k[Xp=F uSc每𵇌#6]2LPA2*V"dqq 1=Wn:%onMyG|mZLe5Aڴ]JmYE]i aDQ _B";\LI?.<JIgYM9 ܤ xM;޼ږ6pJ9Wx@*H}:43a#EE  %)nWjy]]kSPvҫ.G\z+< M$oq0gX5iH)>g`#moYE/T9@ kESsU\Ҽ Ȯp7o&l1zCtebfg=)4Xő.Eǂ5'j cT5l'PU$ |V=:QoK,c}>au[:E6#"8i7Poio0x#(͢pzg&in{x8l'VSx-~yNm1Wen@(4Ze5  ) +T\J|O-u?q\sm3)4y7ƓsqXO;⩫4VW#ey%@'py*Kn@^՛Z ׾ҜG!ۊx>Ppkhvq@nRWE4g=Uws?i}@Gi%#P2_9 dzmVYaKZYD"ߒ}J ZUYUz^遣YHnbr\HDW"nlHұ-tP<] ck &85=,PڅkT*|YLY}Nki#_["A |J:xH̱0bƹhv"{kBӞx޲LjpihDd Rlm[cOFy$6VF.ϵ?@9 tzio06_x:+lm.e`Bt#I=#ٽסX>QMy$h?qӱG$ 賚5(Q]& uG/<ԕ IfqVl/Tc拌u땶/'ZDHq$`Oko*mU$F ?jƽgO;ƱZ>n. EF=N;WZVL%2JFFqzMB4XLoC^ \MhN $njtҹ[]1Qrì|gp0ˎ׍•aFG/g~9F;Z,:TCIJh\t7-rg륵5)Ł]V$\eY|#WS9nYKګp*yT *#@)VBMHÜ@ I=ڬ. az⼺8̒)b^υ:I-vvǒr"=᷋oeM8>mBzUuuJ)8< 7Vm$Ҁ$Slv'^g񐵷x:n?3S$G98TKRř5#]jLL1d|BAe}ԇT3 EktkZH*[9'5YL;t/MJAJ0Z0i>!&c:25ŧ~=Kty熵=_ZHe6g%֫fXd}Ov^)iqcӱ vZ"dirsA^Vq-*iuh& uN7#(IQi6-OCC>fZ]*/cۚ?~, g,Fr۾﹯{^U{G ݇K༒sSr$|kj̖spȠIsz~wK,?ZH:ʩ"1][_Èxi+p>5/h#LmFON )=u5rXH1$5?h3"QY!>2kH:VZ35SSt=)A0[D|gE<}k|?\sW躄*aQ=MZ8ۓ'B;! \55i?Qq`}kW!zۙ>iGR}K.w'}x{/=aZi1&{@μ5EaݽHlD  jIc-i72?Jm<8TdΉ)%FarR3ЕI\עh=+t$@7É&8Q:qR! ҟLBN{RC@o+8a޺Aes3ɯ6U|qE ?p? xqݬ&jgDJhkiș]7 ^#GJ#^r1^sTxI rvl ỂxVk->D'xx^\ch ZuE V%TӞ= $- M%bP:+YF)DaJ.VeTbqހ;)Eq' ׊SonO3 f)ims$.5:n%Zdo"翘|cN8H-ҵKkG-7v?td&Ek;!h25Y9]ڮ;|gyN~fbq([Z+z4TI$,ksCU%v\r=+OGTs@!溘Fr~Y.I]ty @ YqnFžTMTV)̀~޺=fC\qMGdj1i׃Sֽⶦ:v+ g`Wr)XLpi7|a- jR̒i,Ձ$79=Em[8To֔{5+h41!<3]`| %VWV%KC^g,qRH~!j_n#I#e!xDV]RcXgi"hgV&v.=rVs,@,#N3I^Ͻ fy3W ѢI#25F(猱jМܛJ*;.++Ѵ4kʴXePkԼ8mL[Q2uTd ZZHc/R5YzœPq ~f`/=6>v#Υ5fxW\=#+ZVؒAk`6@ Ոd[<~au=_K(GAv͵>sh D`m#[D+g$G|WQ 5 ڢWep"$6}ִTYlgG]]^j!8A]VYleaqX6855{6M*楴ekymZiVr~.4#\Y,GCh01X%u5ǵBEKECMjqej}+s]x8\N&9[ƾn#7uI8*{|6L9^k_[1`Jh8ҵsTY Qար5 d y4#Ii38F ԑh3$cxD%*1#;/lWc03hXoQY(j7`8qc^ %';zuEtZB#*epD@8jQEJW"ѮaXTV1ʸXR!o&2sƚH;V g 횕-T?9+ndOóxQI?,qq -AX(W.+|N@!T틆;CF;c FϭA?^D+?^@?u~gO-j{Me?8åuEΤ5䒓~U^@99KP`Im2>Z:(=sАExrm詞Oʹ$[lVG~R=G8^Iմe{8]޵OJP&Gq]Vk@~E\Rv/xsGDXb+O-JlEWS@+,qՖ袊1VMjTCIW7:iu9+RXZ>-)Xn\lEwUQ:sʼє??*"[)<}26~at洸邹E.A#\u*7]  Qd?h#e|Ee_Fx MWKRbt<^y5Q{ךX<8qtCZPeNjᵐrjOk珊l ôoq_Cj|+*7rf@U 42*?U*QO'. &.D[N9 L";+q\PO:G XVT|ة|Lg=(6>4++A MK[5&oܴH@䃞&g2~EE'wڽ []1,H#`@30 IdOL-y=̑<1R$~xV;f6&y=@4} + dv9ɮþ&diJLʓGjĶF9AG?a4g3I>$:mEn֬c ps2gy_jU>*[VG?Z! ќ1]bR e^Qr?wz2=+ѕVV~ckWj8@%P I}>պ+WCJ\^u?qZ'9\]n{ Q6`$*r7Ҳ#a#t?tkE8 8\ջ'ie3Mpbt9ar%${yJw57qh+H!k7VmڄҔFL4r$P@ [,xf;IJ%o4E#N}Q!Ŀ&a)hZOB r=ֹBW#tOP.Nq,dG|nc^wq +浖Hhܣlcqi\vGah´G_A-law<ӿZr+rmyc ~ʷ .R@qL!dHn8P8׭vfvR 3zy2o-bR%r꥿ҖІ*.wnAuHs$g?#muzI I9eBkWFp>1W3/Zi}W+ethj"l45"5n!v+PwhpV%QVH;6~qEni$ƸMV'b!E&D^#1u Ȁ]z8cuotH6*Rݴ6MsS/H~Q@ES`P KL/{kid$]LE#xc$ns k [ۚ(Zg^pRT/Ѐo֔mVbbc>0p3|jY^,Ɣla-6,@;~Oyʉg1ӜhlKiM=z 3!8+5=dS q(h3C%QImϓpoBUOZ(w׼nx~2ҩjw:(5;m2=3ZQHaEPJ( ODfI0:ȿ (SQ7ƺP4uPfQ9^|ɦI$k Ӯj(>Xc<*en7Ku{=OW&HQ7~PMj[? C?w!(ZT ;nsQEl_XvcV&E4Q@ޗog.Q@袊QAN1JռICEبp݌ey> pzOXrA+|iOFs<Wt-eOE1eȮIzbdob8?CU=FHGT.'HĮA|qJ(OQYdpk~^-ͻ0c#Ep>22qW+KUԒUS?v+f8Gx_>kX9ͽmI$P'#[-/F2ueL㿥tZFZX\G E'#WPvNہבV52 z+(F'+-hg3Sl`zU(lp(fotoxx-20.08/images/color-sat1.jpg000066400000000000000000000244501362435004500170140ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 139 250 0 C     C   }" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? { x}.޹ MEEj>|Wjk/(Q_VGڽW_GGڏ">^t>!gI0׭)xg #/=iz|:Si:b[ME40Yv78ACVZzwROQ_QȯRCOtK{~O {!HŠ6=37Þ~[4MVY慭'WRAv$o8Ubqp9'U |7Wu|"/&5B/ap$0]Xgn6vĝ/Q_T_jIa5`,pkw⇆  OfOcuq$r\[I6Vd\q=7-f?xN A٭I,pK6HIY]8w܋k9,#0 Pz!k{>0ǹv#֠Ӯ5[WN, 15=OJ4mu؝N~bwBW5tG.1S5ÚVwL=J6xET>\F8׾iu{S,; ^xe'ƚ"g1«;MyX jS",U G[[_O/]H8____[=__[=}w?=~}hIЯw/ox-w/ox-0GO~ù~smù~smY_Z [ >4|G"ӕ%$V\#yܿ 9 z?ܿ 9 z,д>-^xij[W%Ve>^_q3j#?!mCp5;ln9ʐPB ܿ 9 z?ܿ 9 z\s⋯9o"w&ÿg[Cd-gW-qӊ'c&|o,8;SU2y9>;8E^;Zl,Z# S=}mAe#mAe#wPNַC+>2ዏ]Mvl2m7`VA@JX&3|oRۣ l@_;ɂU}mAe#mAe#spfkNHդ(3i~4NGbb16˓Wys=̞m]ߦOAܿ 9 z?ܿ 9 z|nl=l;f1T/]:TוK=B NϨWE(`ZtBza,C/Q־o-_í ƷތY}Gaz3%X3yH;+Zz#3=%uUf5&A1d|W=_z?SpoWHB+n gھqU_kz,n>!x^ūKv/Aڊb095r> u KlZWkUHVF$ pN1BϿko>|7X䈮M띭AD+sKJt+x # ?|m^љ<FwŎ{fAӾhOewq} '+cHƫ. d*2:I7OovB~28W)k6\YD 0֞7/5oKhTF[IqyM|5 |o^?]oč<̠ȫ,rW~lk:]/E֮4O䶬o|0=Τ~5E]C =p o˷_}ah>){+د}uf|(b9Mq/2}b:onA'zeV?k~᥿-7,P="uM2*_xڿ5K][y<~P IW9)c8]Mz֜h3$G4{֔$RG:t]FMCWunnR28#_+]4[Q&hiX$[_>;@\ 5^6~V|2^8ۆ|zrnK:}*C) dS!]fVB +2Jpy sk%fТ (((((((((((((((ּN4tfd2PV0qHBƙ,y<OEs7Z߉HիY~t7k6 wټVg լ=90=OEPEPEP\ů^ uk^џ_m"ViIuRv'TڼTm\M]Y&ڏ9ky(D cWtW_yU?g5O&Nkbj?Mo??}gE\@aZ_/&QTky(D cWtQ_U?g5O&Nkbj?Mo??}gE\@aZ_/&QTky(D cWtQ_U?g5O&Nkbj?Mo??}gE\@aZ_/&QTky(D cWtQ_U?g5O&Nkbj?Mo??}gE\@aZ_/&QTky+g]v &jl|A_Kw1.8vJ`3OJּ++k.i{-LDѓbA^:(RQٽ3ەG/[~z=7\uim\i1EB$wn;U׉km/w}|UtHhf-N+^_ hjfk$k4r{F'&Z%"LoK7?EvRVdsQ @֊j:UջZ %e*G$##jPECqg< HغwZߕ ERӿQυ_ U/];|-غwZߕ ERӿQυ_ U/];|-غwZߕ ERӿQυ_ U/];|-غwZߕ Ef]Xh0nmm^TEQ?ѿTQ\FQ?ѿTQ\FTjeM*y[Dbf? +6N,i-lz$h>FPEEs>EGFPEEs>EGFPEEsP8:6Uimmom$|6bP{ր'(((((((W}# g(ι_~~{$RM 7&T*{WMނW/Ľ ǯ-C/c<-*t>ҼCkiچybXn p"H<5;Ö 4 _sˮ_âKo+ŧ=ْ+D,T|! .^^wG-uUw+df͊\HfwVz{_Ʊar_÷De$#Ǘ76zĐxLKFg1*N Fs~_Ə7|Ym'Qoow İǨ%b@A?6K_XEOxfTXa &(\3<,K+H& r NTS8AAgUQU8{Z6֡y0ݮL $k< ޛV;^FQR0((((((Oj7Z\^,## 9=;W86EںO}>?(WWG.q ?&׹Į~(w ۆ鑥ϟwzhF32\( %ڽ2qiaw e;hɴ鐣ås1"Jh.qץ@j 8 315 1000 2 2 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?((HN*)%qq6)"p2\ޢ7BSlϝ#P85.OR=M=WIZYtũ]\&Y]c5 d\ֺQR񦉎xtuΊ;z {/猟֯[}ZtM#Xcڦis֔/{u|Ț(((((/jc0^^I}֪1"RH(+2[걾jI{dukorzOUA;G5 S>^b.iuTתUX`Ot5P]mLEoQ[q>5'`Xӹ[݌kJ p3\]ZӶ;MsUĝrH*^[+kZjSgN|蒊(((P((F8lg"nf qڬܷɺstҊg5YXdZn:j[-$4gP'$Wt`qŸ54wMaf"`jr+ÞH݂OjY7GǥcA!1Έxk2S^9&&KH$nT>ռZnsM9I!awlVndJ_ZGXx?z a&':ㅍ8aXQ9DOc]Zܳ۲pr8W?jmO+ִqI֡=9i |׵n5ˣ&l) P9kN (Š(@ ARreK.5v|<0zI!1'?2W<ʭ=(жsKlxM6)H!;kpM1^7 :g1MPy>)W^#K45p=+8a[x\ؘݝXiE2.W=ζEQE E-Q@icij ZVbֲEh\7QYW1ZHOvSj'$gGfIo_Oּ$DQ]/}r Svcu ڐy55뵤MhQOs#cPJQKk:ɻ9힢ik$[SLni ׫k>nRRd2{w KOsXh_m"C q#M.o99 =;|ɏJ#QM\JT݈e e* R6|3m Q^;u)tm?6K$\FJKCAѧ4;nϪ/["_T>k~ťXCAr?%Ԭb89u I %:W:oCߧBSڴ#vjӴTew!f;?ZƎЬ凜Yxq_=~+Gn<͙v_XQh6Ѩkl=FsG`qڵ-㊣sVͧOo fE .HV5߇<\Sw={}+˾7hWZkVY2?(9]mN=m>HF&iAʀr1ҴHZ=)r;8kֱܐ@ EdX[z45 %ϊK9}5S.{w:U9۾+`3㫖!(:(qq^"[ Z2t ӣH%R2'4ԅUdUR</6PnEh=H,_՛10xOv"o|g~0Oy>sn'& Cm+9=+کw'=yveVy'ֶ]'bO k\mQdl$ױR]/RG }n j)D}iWefԠ BN']X?'u\7P *ƃ+' "fe̻A>kwOux1Iq>^]xBҭa{q mE'ߠ`;OGc4`ggp}:ڝ˻٤FOм\;Dcf?Pεo5 ^FZrǞyW44+-YI#+P,W$ck4L=z]+ h[95r4 Gֶ 싺4BEC (< .GҼI Hq$볹Uwr˜mAR(4M6W&2BSkBc_e^.m \Wk=:Lo"fBqfաͧ?Zti2^ =.CG08k F5ci3մi Ma9]{$6''ꦑ wE%|UZ(%tPoֹqQ\56fROnk/K 3ۓ^a] 8략ىpQ6#XSV}n1ץtl(v#m\"Augi BUfCuE{W4k T>j68m-n7)B 3](7wgYkD-lĭpO_z<93y]%n9F-@q/męG4ɧSixsY' stl~Xpl]n.ckk䶺J`T2yF@[v[pɍLqgiB1 ;Ioi$K9 %Tn Wzn 81u?$pCv ǿεn2V"7Q1" Z,Q0*ǩyOWPmnQYLrc8UņPei"[{o/1ҫ#}*e ERP(.%Ԝf!=kW*`"ӭK4q &l~U c.HTff7'G|CS[+l鼃vGt| u{]ݰFRs|mvc' =kW$=b-0Jo)/\Z~jcDV!yB$\7B9 -Į_j۴6[j^Xr9ϽcS ^}iiokwa"c~q_4RbapSsk5o jiMrX@O]?VGHq6;Nag+U5f$?ݭ_ ZICpx䃏ֵd=F@PuM#:v-bq$n99Vy*F]3ddi$MvDСtH AOWyDW;Ux㒧UⳑaETAEZtq6d{W; -MpGq9ss^iZEaq zlOۏ8/`ӋI/̞u^0:LW3d񯢿h\gKwi_:I֓i97kgz4}Tb;IpEp]=Vztrd})3у4@G`IΥO?ܐ::JBAG\+b$a$-1nq[ QR>?` ߦ;iʒr@|gb\+ ;88ӣױ5S:⒥cڂ\X]ǟ2@ݿ1X4|Iosy4wG~u[ [] sm<"~?5 ۃγHFf9uм3Y:YF/_E$p+ŻtG l~V#Le{XT^qzJYFx<{ai!u' lW5X}a#Ez(6GzT7Lր<Ěֳ_¹K]nvKab';^xwEݑ_|;Hm/L{CuJ1d->/ݗpYqc5ݻ>W|~%RiPmՕ`ϯy`±jңRd44˩#iGvnrf8Iq[U IVUAAqgmx7ߎc[^h8eF$'=GKwFF4Ajm}A{ֱ$(ݛ_ g61ʒ\_`[6lF{־p4Z|fb:}# me^`}kګgQ/c|ZIwY?¬Cs];=ҸE`?ê …x# Ea-?x:oֽ}y"{fiv L0)?TJbX<`(xƍJ;D٤g8V8.|Iw<й!BWM9gMG-oc-qe:ҡӮCLqjnqԯV6mw<5lqOq+31!2}5'1kFq֥k]B^f<# rz5,Pp+ Qd[R8X#js>c/Ś֯kn7ؤd&?jGRb9b 9=1uotʲF|䁼m.`3(Op^6=CyVc*mRc=W/"m $p+x^HaenØuA8ՔX+g#ֹۘn:gHjKc$YLknC_"Y~I H|5{wú0h`cspZyd5B574)eGu#޽N)qȱ ``{Enɓ}lUԣddziv*|㰪I9s“\zm%b4VDI ugO;gxi#'F\#QZ@GF|q+4q^]<#9Hdjs Z5Rs\kMq1Ms3#^x.̄[CJqTbDqI 99Pb+W8jJ&ezoyXbksH8Ͻ\l`&GVbOW9?vY(yA.ϷN 0Y$cktDsާQX@{+H ݶu+ Ͻ7V5%Hv =9UQVxW< 㓁UriuoC71$9Sk.V|E2\ZA18my 8jtI7 8nEyu\/M(?u6cǥy߆}vʻ =(9 (j9~椨g8ZL{VnW[ZfjF;hg^[m2yդLO+L$f񲬾RIj0N0H|2cN$H@r:Tb$eҤW s*7*fUdQkMd`GE) {Ïދ[ w9_=?ᦧʍ-@9?3082Mu ēr{{RXW)]\6+06025zppXcѫK@ߧzbmcQ斣 $%0 wV Tl){j6<]nu.3~2$ 9ZWT__8͂H5xnЙȳ?xAb~W =4:TsM` 1_SG`㞕&֒^^8Nm=Lqia+~JO Wom+>UXk#'oŵ_.,?I/X{LMGoݖ?3z(ϚFsȑWȐKLkkJnu9˥WcqA$Mw4ȿD}piK?Y]~Xɫq ^_ZE'Ps){TxW=깬G;yZﰤ [zV=vy.Ab#7d\ヌkïkr5{ҸxRj^ kcK9gMbH)#pMj*DFmܑ\W'?Pz1"Ex9^]#cvp~R?ʕ4>Eӕq pz+dyV g=.Ic]Ǟ"6NG$R (j[ h`sWɸ>k mßZL#E> H@?;׏>b㞰4y= ^G݇nդ6"^Ei$ഛ6Iy*K{UlgDdGV8sd, |GմX!c@'VJl-@d$~RǶ4a.dye פKwwPLhOE5 W*푀HdeyqԜ5hg&q[FnVXgEt GkO}Z.qV-V-2os եA+=_j%6G$XD!\t^Sf׷ҪANǠ9MvwncYi>7d>&34\6vo\„q1iUcSҴ.'=>Q'%<71iaiFƺσ*跺x<ׁZk/w=Crxcz:}K TyX஥Qwќ =YUuDӽÏ0-w!UVF@-ǝ#;IXAVВ氍9GqUnJ4EDL s^ējd`Vŝ}ة 6B})Ƙ= e.B֡XڛpE&+LIjo3Z5f\nt jZg+̸Cs5@yIVM1I;q C%c=9kMY4e} SA4Q\G5 IͥX;yM.XlɃp9-ZbE%D;es[8kc0+^ +yfg'<I])ѵ[2db$lQ~"+is mઅ;3g9cJƻ!E r[CˬItP@V :3uc^TNJYK|9q5F+xIY9;yT[X`Y$cz4VЯJlLti`'vkEYOAn'?*aY oKN8ZA VP(|FBNA@q?:!pJXIw[#[ R-PP(K8<֥eG qI+͚5rk L4|g:,4c<~&Xp{ la_ǭpkR(FsZbJ=YV!Iop֬},u6`t2#ֳTpmF֡n[Z^>߭D$/p:Ƈjo _-¼+/_Q걷<g'\VYؕʣm^WUf{ExD€XW0 K|U?*dwZ3`fm\a dϓ]V홉fcԒrsTU\Z~=95rV`*1SZ*~lb$pE[kgc|/ӯi!\cǽgk$En@e\۵{:I^5]3XKxs}u5p>-gڑɂ{W\" 郚C vlĆ420 #TMۍZT!{7|V/8*3@n( ˊH (+g6\*q֥H VIलRr>I9'y5Hw^xsr򴺦 ~d#@3Yȍ"alRkؾxrM2+-]7$<*z 4NzգM\~i ,ʢIzzF?چQ2#NӐEsbi{_hk鰗p3jw*UI֪QsWܱ6:>fucOúTq\kP}Fz֊( (5}Z18<ⵒrjMÎk]NkԿ7֠/d rV$۩DW!fr=q]m-`cC΁#xc;e B& Od@9≮ n?$y.:`}QhPH"@WzčWdCuc=*{WV# / 9hl雡g0$OlW:1[D!%͟OZ53&s;c*{ 4oĊcĹèN1#y UUh&E]@QO֣ 2>kF~)>W`ʙ 1$!^T:@{VY:B" ) -!5t5 `éSJ@q<vf5_ֹ V`|H$3hʸ_֭1#bj|W8ѫ]W3zgs]}y[Vwl=7uEu0-a -1Q޽'Z]kki,` <ZB7U&oe[A<9eFt=+Uc9~GDs'ۊcKH3|(s֌G27U<kQ>obQJ>U 8տêpnm_3 G SݽJoCXKy"f d+"Z Oc9EzyTodFl~ks5ئ 4QSJL&w.P$zMa"9u: QZb>)F-*vLs#ڃ5E(CYSoQE/qKc֬m}z\5_T)osI7*j(O$YO_^(Kl~~S|J(#^b|W~co81ˊ(4l2,9<{V$V}DXā}=b(O@+鄎&ڤHQ^v+Gj= t"3$8(\>n|Mt<Gg#Tp܊?HهA zurjgK&ֵtzg&E'^.BQEi_[ݶu'pί1yz?R~,7vOv48"+őc]) (AEPfotoxx-20.08/images/color.png000066400000000000000000000163261362435004500161550ustar00rootroot00000000000000PNG  IHDR@@iqzTXtRaw profile type exifxڝa$ )|GUlOÙQ4I!~}q)VwUz2x>ju=^|g}ύ>g^ڏkqX|g?X˽c|Bj[El>mMf-:xYaloXW^R=A}Xwg(I/~o?rOy6?gSf^a}H~-ݽs_+(Aðg4-7#r<@vnO]9>ʕgloy>[s"~ۿ|ex`{Yu9BoAwKnZneWN?{q>Z;#_쾎hr*@ƄhYĥR9?\ n6cBVz]:vrHK:SiVbe2LJit6Sce[u`ڗX'tfaMXQyUi쑢-1ndֶZ-]Ur8F 1|cϾƔ;`k;1i#jLetd4tZvoߣ yC糳;t@gKE7˳-d^4}Vq\z}h\޴2F26OeB-O2W Գ)[2=vF7`lY?.sF=΀v94jHǺx3ExUMŊ*Cgk՞1PDuanN=&tq,Ƴqa\CR, \At6j艼YpBnc{ޥZg8QVy\zN\PK8TIpY9I9lETÙ҉gICO9lgLeC_3 [ï;Yb8Q]!5!+I[NLCo9[%dYoKbv?K3psZjEPZRcxP_!,SQܩځ>YM9d>cIQ;ezc<ϭmYIxQ™z2Fc8OVm9i 1I¨셞60Q :s}b`g)W0C+uK7$FRH$u"y~!*ߍRU#@Pj獂 E8)0͇jzCSЂ 0Ջ.L.nSeUkqzՠiGtDu$<@dtc(y\`6}6Bv9vC;b:?bxtUS y&hJ8Tk#`7шBeYýg\͍"ݣ1|"{(gH' } VÍQsI!VdcNktQ@ؠ] z&hߧF:l8 _BhP/Oi MON|?QflЄ l5XHpHYyBW9gi^sBIT|dIDATxy_u?~l$ErDJ D"M%WjX J4@Q@&v&nlN*$0)TZ,Œp[r?FJq:߼w={{y5v.T2V <eVWynj= ?k]y_ٌf@?Ja!zpDq-ZHWp+hT6cG(/@w#0C*?ln޸~Ͷ׭V=R,/>{^NCccSSSϋ35~',wͫoV[i^lsqdoeϞ:5ќzم<=h]ŵ__ve^nzV,'I.EHLLx)sߟ:;6x_9K3mmR]jCw={?q~;KV PZړ :M~3V?'`S4z|q޻ӿmI`:A*CS "HZak7iE5杔 zwn߻گwV=s&Ոj5s|Uw3I/Ÿ? 镞4_X>6=l\x+Td`:2GsG+43T+gal`UٽKyO35`슖(DO~ jd.\ ƶZvV$"+6l`kdȑ7CW+YcKK<<% ]h1ٱ !uZ!QH@j1)Pص} 4GqyR#}䑍RKΜ!40OS\b:):I¦]DÇ6֞o -pXwq~Q񔶁*2ɜ"rAf!3-:Yf%iaNMz:{ncM(y'>&)!YT\Yæӡ3>s]83r>WNbIoޱ_! T\'H}\!& J|SU!x1>CCܲ}#cW\XJd4_| }^ b@ ISr" 8d431[/;:~NAU9@7pC{ޭc3e+7b8F+.o+'" `2ρ72v=wߡFF7aOom{3/,_j5H*Y+f·Hɾb؊sk~.>ͮ|@<+w]pշOlݲeI?9J}`%Óe D(%A{7b" cݽ7.9n?[;\iZ=@{hP Gѥ,̂ |W6qahlغo>awaz>q϶(Yn-/9Kc pM HAR""@E/s) } q;dž8q =RZ߷fu(ťW_w.8&hQ鰺B T(=G &*_zU7[Gܑ{|DlOO`#-'D _uS DuK 2Hp*`pfrf" qg.NzO`fnksL b:N1 $=1Ġ"QiA*q3Gsd!Qz7lgYn߽R6z{&Dqj놵5XӈD7 /1B.16nVUq\h&YsVFQo?Q59!Zq+/E4Q2)J &`d-"JCklFvgA|\ Ν1MQ鄉vtRrTCvFsO('zKr!BG*<DDI:FVbj(ՉRZ+HumtFݓ5XX/ uvHf]q:1V3Qn@;TMEH#%tHhԡ>P:@ .5PBEt\ڮ*8+ (T9a*reqI1 AAKyO7F9LuV @6' *)i_nÜRMTt@)\o q>Yy"8 N0 JYFOYE&=1WۄCN*|tyt ʿʕl;s6-e0>`Ö6;JnBt ڂ)"qP.-}c Y)(PLC^4  /<nK9χ#hD̦^E1|lEt eS]~|Yxr A3 rՕN}Z m ET>/ӜD3T(.he/Z[)t^YAe ws[@{,Y(=Pֺ Յ_B,b 0"GlV.PL!ݕϧh`CoϮl$wR|(dwOXB^ƙ1WOZkG geRU-l͋ʟS0Ӭ̽Y*Q>nĨ炧f8 |Q}a'ߙJco߅{Ѝ6\4 h} &q?C>b$b9È]Kg6CXs3Ny5$S@>Hz薁JW5|pJ`7v["  EC_Uj70B yԝ;dp dNw)Ck=tVe_?LO=pK +ydW|;b=ḅH}.tT*"a u/Ht~'>?zg0B TG R$JE5"1rq'$O'[FΣ$ܪa.*) .40ݻcdu|iI9c/w$;>ԍnU^<\M-1@W䙓sd='3iQ=oyߞgڕkԹ W(4-^?W3<:'X#/ZMo[wa9J6ICJ228,`_lFjR)){\:9-y_n^3hN+W>X)a%'\rr%# )H]:&x駯W;>r|L9wI]V]_KCxӬ_USdQ F#!% Li N{ȅeirqZ(%/[~ :-.K>yZkM=JID.R lXq(û8 O +ܶSy8 ?|_ _a [8:~T%auڣ"Q@Gg,#y\J۶i/x癜dm^71C^8l_߲i˒cHu =%Sj'&]$DA'A Pf4MStc 80GLfM-'ޛ, (&I:LWJX/Z[ &Qԣ$tkp eO1L:F"@batzhn{ZT6 ^2.Xˉ2q? {t~KvTZU_F!EC5PJd@PAeDZ 8U`U"t]SKj 3re]GDQG ,d15%$@a3n@)JB5Wؘ.*H 79o}A-Ѥzo ^.wXɭRXGn-;!C@¬.k@(P\+-/UU0֑KZZ'4:EAZr[w- 0Nk1D̸8׊q 3'I&@f Q@a  7k=qz;C86Z;_zFD0QcR ֑V ?#cAG:}78^XIwb#R"Z(Ɛ -Ȓ3QcꋕxǿŰkȶ{oeb!P8G`\Pel+;F8_jLs~4񼃏D9 kɌ%+ YnrB.87yŘpa5' ؁&qwg=xlE(W}|N.~"gXau3;96$1_p p+HZe{\) [iv38o!wzާiQǹJwIENDB`fotoxx-20.08/images/colorwheel.jpg000066400000000000000000000655331362435004500172020ustar00rootroot00000000000000JFIFExifMM*V^(if%02310100Fotoxx:resize| NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 768 768 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?((((((((((?廋= .>uNpXz㡣bM](W5]/é3C`RV*!!Daشٿ' M|Kqȸoi!)aHmGQqUPH<Ν=K[9nbK\GQ)gv'9$KKBn5d]dUԌ WV<3h־!m#܅%,7 us5ۖgwbkȫ(y& ڲbR-h7n-~' :Km!\y Gkc/ Z5o@='M6O#I^;5{(+ȩd}\y+~VeK(eRb/E=ڹm['2ÚHňIi,Js}$)kIĔVe_sei>J#dC.2Yw :pN +y[k$ QpFgJҵs:7(MC;)c:tZSg|) #/f/i<';hHw~o*b VQ['|Q6ށ\\ṣL!k0e}YgymR{_M׉]Gm]P%>W`hL*a3qf M](IYES$(((((((((((((((((1F*kKIobI&qqD؜rI'ï[Z~ eEm7!P9Yy;0C c*8h9I>eN0:~1ψ\唩mn`; c|5w»ൻ^d@c G0# 8rN^F.ǩck^U?]|:DfU9o.:]rhj MWXD$60d2}>s]Miewil(cŎPe<􌙨:ksݟ&[FZ)>Td -\d+ɜl}4dSe*P =*mp TLi֡u92JiZaYFE(Zv.3X2 ZpZp+E O PfmU9Sb܄ )1Yd^/ldYVk)JlQfMnV~͟ |sN-yHpm#m|cZ0S]lKO 3t.oZ@߆[GM8&!0Ŭ'~cYa=Ke#9|BV26[}dž)ޮWS,'LqF+迈tEӏiFŝl1KŌ?ʣ% z_uCKiFJKo*8n]((((((((((((((o5ψ:2V s f EI`d׹cZf]5mH| s;# }OH‹/0|=fh 1 ?|F;]ZWݵB*U@cE#cm_!/~|%gY<Gm('zTp A`v,:Y7ŝMK/53C,iXX(SLuh֏j@ey*EB^eHe'x@^ltFE'CPZ5'TdSu@VjZ󦎘*&eB\3ZQevF*R+F鑑vJ5qi` zE"dٓ`pZp*fM%8-=Vlj)NilȌ%<f א_Z}ƛ1SUPF <A ^ {Az6׏-Jv]?t9B[U4qMRp#_/n=gV/7[ѵ>JmX,HRHA5@Z~NNJ(Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@(RhQs{GIV+şθ|ia1%Q]'QEQEQEQEQEQEQEQERܿOڟ R~3m/F_vy W?>kN˖'+<م7/k<]J+{ |7}%IPl>n67wί{-qq)HrIR\Iu3+93jDB4ce|%5y=W* YA[GѩUҧUZ4R.ihUTVE!JniJW oZ:lU [=0J_ ^GR4Y)%<%F}cA\\ۯu^19er&P:xu ƂA?ɟY__,,>"6kCh4(((((()v3Be]YH|;g&2A{ʍq#o8liNIF;Xl5L]XѢ&uOegX1eƇOFC=dg*Q , ђ\u33Y䚩->*Tn~唲 5eZEVh"ue*fFsɢԋ)LN* ţU"D*1ZţE 4bghȈFVJSs^uHQMUJ"WVT$gș.hȕVTNHΑjѕqU%Z "UF** T$T3DW&v&7o*F)+E\rJ:ٌ)T3\g4Ԩ*ȵ#RET9 T)Ux2%ss TR2GX9 H#X3Y\":SOX6dD#,~85"NJyU`G͌ܳwL)`|(¨t#jQ4g?y/v.n3Tc5n6Ab]ըSըKFEۥYHZVMZCU YhHԪ1ҡՅh!T)/J7搯^*]q\U"tEb^u"b:"U%Jёj+@VJ*)9OF**OB nNjjhvĎ=Eq(J&UI=G2 QN*eZ&="#Z\9&"蔑jksI$|T*%N4) TQ)*+iLbREw0s"XY*U.D HT"X6dWXA5ab,RYb5"ÚS:W2s)^iZviZƟF`%]+Yɹl9A AZZ(QEQEQE(\9~xmkRHQkx%pHrHTAG~"x]SR[>1C,XVI*Cޫ뺥Ρy)̎$ױὤcxs.Ujc:6[>&VnyqgD*OR~67Ui)j{UEVTjɣE"f'5V6ՃFETj`sҰ6zRi@Y錈T2%Z)QםVd&Q:*b\YRj;#.TMjRkѧ#*dr2c5JU5GNF{5-[y+ƨ0*{ӱzke/5:/Jjza) b5ՄJ9%!ȢS}jk8!Q*-$i$)%N1V:81< c"Tș)HeڤH*eGR^*GSy49,u*SU*DqH́bUac?jW2u *x+*pڭ,T5 ȿR'\&TtV*j^=hЙ2UeNSGּ =r2e^jZ%Q> |xzfH9~XUY+ªF2 ۊWD>e-NH53rڬFX;)|iY>i%Z:8!#Z(:u6q` NXDi;RԎ,t ksJc>*tf$}(92$HjdHw2$(LqS0b+ H{P`U ,Y*2!":pQz{]ǿz+_hSK-€^2%E)Qv NE(ᗆ vS/Kxu_3Q.fk9ne!sʥ]`yňUX=72F0|82s-i\.&21%se*S0+(kۨy&qqD؜P9$1_z< ģDLhH%睋 v26H?;~ɾ]KǗ^,}>_1o]1oO̜""+r3<63^YF#{ M\Qk>7q}R4#zޭF͔ըQ=Z67QAVz̎JhjFj9+29xqɓPjFoYJ͎LUfѢUz΍ެՄ~3*Ga sIFE"LCR'2"dBW6Ҽʰ;a36XSGZG4]kiƚ:7(HHэrbcYI)Q5j90f$ &1AqYQV)V eG.{ը#E#V)jrVDRr)k6jr'dKY4hj&jmY?[+Q5R4#5a*-V9Ix̔ ) dSTr+NT)IVXUV,ׅ^NHEqyj^De@rm B0O 95WbMl~Xх{쩣owHkig*Ld[9\ Fy+^ M}mLO | 𥯔b.& [^ SC/q1OqfU$5aeާGHaE5b7a$FISztce#E$Y-XIh6R4㒬JƊFRը嬘)6(Kޱ75 )PW"azk6R6a%b-hA-`Ѣ沠~ d,jhGȫ UXr:MP f/jF5ׁ 2eڨkvH3TgӥTORH}rh*W!cէT ԫ\{TlhR e 5f8+ɪI+Zj*v漺 ꕣGXެo^d:x1b;z*VcG)Z9'T5f8}pUꕖ !)jtڥWHjeڬ5(YT+$^*Ú{T# T+,TZXqODTYӄX ԱXENȊlrڝU/Q59W˥^_4d~UV~|eмmbi&lu͢ p\Ml8U_Lƾ"?)+ouy }JPOI-QPgV0X` c;)ROT_O~$zZ{u /qq3%,*$ ѵѢZIk[æF;:05B_J3߳"XEmp坶=`Ns޽/&s˓M~ӻC()jU4oΧFJEjTѪtje| fFuG*2=;)LTPGHLI1SsSQcU3I%1Yi&X^E+sI9qMc5j9sPѢ 9XPWhsv zsZ`i[͊ɢysӷzjK)LڷlЄMZvM#PRc<մLיV'DjI+aȪ_=z43[5QoMj_'Se9RumϥL~M]bvj;oj*VG"QUȭqVc*8gXX۞v;ojpJ'j=*ve-Q8Xb;|vioV5G$ڧH=CS$VmUX8jT59TԂ/jԂ :U"VV/jEH*xgKH呣ByZmuoCg \[[F ōRU q@ E~*_Fz?3FEbm-hoe#S,&c1 DFO|g59,٨jVVVיRI0>' 8P_9T-Bٶ]ҿvZSVʬ)fDzZVZez#TiHRdUjv5S-#S4z= )RN:ITQUzV4S/$b99a%+)K-XjIx 'J= zk)qjR桡Cvjѷڰ o6+k[MZk[M9dNm'JfYzW,?hoۚЋd[Iԁ^}H@hh*Xy|+X˒ު5$=+qPSfXۚҭGm_1Z&TڭkW!\ּ*rvn+_j[N':1*Zڬ%p'kS+E-}UYDrToTo\G;RX*Uڮ*EhUnTX=E TXi"6dD4Z RU6#"ȋڏ.Q* ]d󕼺_.ledWFʱ;V,9 Q)I;1_g5ai ?[H =sC/,3Yj=WO¿7?౞QAe&0< nH$cgk*xߚUVZ=[Lm U5тZŅ$h~޶k[Kҹy9m/5{Sڴ\9IWsӷjDl'+iWjoiZYWR'x5-=tEN+q9]s9mEmҜ-9#X[z@E]^$dꔄt}SjKTZRu%̪"i_5g˥Y</^jǗ>%̭҈}yub *m4ldØ&ڛ`gdØm&Zm!J1BHGa"J|Glڏ,kw? 1k)džc}ƾn\%m7gs+WgXwMz_ᇃ']w9t5ٌ9-Gk=+qCN%52*fSФX^*ZS؝[5B(ojv-L5H UU S*T٪SJi HUPԡSbiO54mUSFؤнyD՟sVlPҁeB*."ط~m-bۿJҷ|bq'ۛYy[k[IҰIxfZjIt֓I뗵k͊kMثOz³En\-erc|2u+[=n qQi WAkhvq2;{JJ Xjj#XzUcּ:*)VR&G+i|*ъLWڕ>إLs\rr+GSf;)ivX ]ҟږ9oIia7m&u/Y0ʹ ²cRHF :I}+;"z+ 0͟QU%}F+ZSyzO2ŏnY_!|~?1J^" xcJo3Y+S.|Mj?f(ĆG~~Z~p>v2qkht52*e@lV ?4jv}(-< ♺ |CwQcRRwRn;sH'5)HZBqMȤ&cy4BԇŎⓚnbqLW0א?onY[^7e6_R{˄9!2I\a^D՗~Vvci/=#˘_H$`XFG͎%Tssg{@Xe_Cr>S!teVs ݝk_n*Wٞ:7㹫 sXQs^%C5  6с\ML*U.}km8/`|iHmVMKDi 3gi 9vGErS]g͆f%JńRTqS)H9ɒ TjPjұԫPZMRD 5t(Ji 0utH&"Z3O&C*%^XbUڪ1uq 1hsFZ6 fBqV-Ҭk[lc۷JԶnkg4VZұ-XdVp+*haZ"d^MVsJk'J׷`[IZ F*!^z\nwҨn?Zޱ#zՔ/٥TKTr=kgNuJ.m%L>2בQq5"{:{s5&t)sR ޥYLHYA6+1f$VjųE#DL)JT-erԙtIN*%l"ߙK8$Qr!값IW/97ѼVMW1>B(ߚͲwQ) MjDSs›ڹ|ā_XoៅX WwZ<]eSAMׅ>tç%xC_?Vj64}ceK$m~x8*@xK|1o1/v' H%xbefKkwP̲9 z_Om"6W+־YYGYj nb^qT\ӦO:nژHxPCS!Hs&ZyTԀfL^ ԫִHPx8#PjEQRb)5ypj*׊HNsʩ2jUWަ)ҨLb3UERYU,׭\Z*O -4~͚ ["6;!{Ǘ0f@ϫe[7'v~S`}w2U2ԫPS'4)-2)Ka L WCS!uLzTP!JeW@^9d=*UҦAғg;d*xJGQPل2N(0$EXߥBak&i2dPjtZ>*c5yH:VU4)n:Qkl唋qSqWكeJqQybfF٪Q*7"EQ=RCSWX.MF;ȸS+UDjzOJh`6j52xzT[GUTGUūЄThH^\vEŐԪMZVD댋RJJfԋ'RJb5R.VJT-dѪxKx5D==dExINTJiD$bN)D+$`ST2>8{UTTK=ԝ|&l ~v`05sT0i VGRo2xDt}l쑩VGzڥ[̒K+gbrX$I I{VOM++Q] ( (=3w| i1𮸋mIp ,=].9]i :R8 #}-SPhڝė>(` i,*>d'z$b8*{ v[zvQT'6bˑVP!9(5n3*m^UBq*xڪ#T+ǬtDSTժd5Glq52* x5Aղ*TjOWOBaU|DbjP^-X$ZVWUVח8m^W檣uEqR+@StFEY*S3FEL?&+&TJx *EhbĕDIOMX"ӄ5O̧ 3PW/#Iy$PY䑂(Oȸy|'4Ľ<'>c~}_}Ѱ|;t+ ^|NʶFc?svFsӂOxߞQ\(4 ( ( ( 爵 k:ƕpm5+){y†Ђz͢P>:ƕM:QY3-֖L'11 nq@?Fk G<= t唡p2RT=<%Irã^M wB6wqRx2A_9:_|fct%(jty,ASsSi\f>MNUJ$YN*tjtle LTȬ9ҬWU&i#5U6a:UF2xQZVV*+Ī2ʚMUVUŭ,bUԀבV'|$ZSRR+דR'ddYRUVy}zx(1A|S,l[35PKNsX4j\b$ޜ$FEŖ$~e4 Q/&\=ApA8 >+/[L/,< yaGAbdoc~vɂƙy5ms )uW|1t|3 <,^f!jž\YiqSj9ZCZxyY!y9YJFjsY6rȰa* ,XXN1X6rȰZ*q1eFjU=+LŖPNZ3VJ~xA9ް uBlϭJ4+5 ( ( ( ( ( ( ( (^𱲳ǎ$ռF+ "WK پPrV 3[R:3SѕZP kjZ4lWk$weB{B#?PAH AU |F[CkC}!Rxb<C+m:5ַɵM:+^OOHQ2r8в 4|a^t581*f|ԋ՘JVM-jU59$[՘Rըڱ9d[CV#5R3VQLeXCUcn*t<2ej TC&ZFոjej ԨjLi[+TכPԨU[[˨oʵHUP:JPTGLYm_pzڈ,1USWQeNjpjM"Ȓ8+hH֪I#e"ztQMwwwo[V(a\,@:HxIxG]E6bgDdW&8Pu՘8f.2hZ"]IU'uNFFTdgDHʍ 0AWSNyVOlZՔjsVc=)yr.f35$]CXhxyi3\CV4< ̚-tDl _\dXS+UTz+ԪdjZfƑi[Z+qR+s^uBl5=[X==^ʆȴRUSR+{חPd5<=UR6feT?4GDYi_]n>h2p_u(ҸfTޠM0URz3\/ -~(TkyV{ QX+!n%C QVϒnj%J7gu֯wq< ^| x*\>Ӭ]H'pz!Uw?.|ck\axoiu I(ϊ췆Pj+ޗnϵei|w|Lƽ}ZYէ^ **s٢DIES(((((((((((((SԺ敧gOܗwwcTOn^7`^'EiN>h;3i׏%Xpx'ľD~~ר TAx3j^r?vGp23m!X)?J+q`I}&1slZY `D#ynk%U3q5XY[L(xcß۶\HKeV5ہH+Am5#nн `A IV#Vuχ#]}qYFQEXғt#N6AEVEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPx_o;^hl6RԂ`8#)ݡ47ßnxw-wbKbws,"*c-^П `znvk}Rʲ@I8%q3]0Ն19.Z5O]&; ÜcYg2@èJֿ8`~<0OjW)-Ll̻XNL X$|~֍[zlTkG*==5=^U>ivMUw}oa] c*F':wNj"Մ8LX>6rztǽqʅIlyc=?asN _:7M^6}WDcK﷎3яuK_|Wy^12]HrR8\V]V ??>nlQTZ/ -׈u Yvak\>-xn:W^_R?nݩ'$䓟),OU=-Z?ڧ-. -犧ȒGfQYV%9wt9> «=Jd@1I&FS> S_FIi<.YL yyW=)(I+ )(((((((((fotoxx-20.08/images/combine.png000066400000000000000000000254221362435004500164500ustar00rootroot00000000000000PNG  IHDR@@iqIDATx{wt}7}7 @ MQliFbƖl%~#[/%/'st(^g;ȊDKlHb Hb: T+-vgw^ MmV |Aod㭁_v5vUUk0O}.~O~4n߷~ۇ4L%ogۗ>_oyڏB!}]1*]TdO#cY~*E]R|CSvgz}~s70F 6 OT-0Kc#~|?<5ݢ~۹M0hb%dYH1+]i"k_UC!G“*rܠIK4AdBƋe8 &ic۟X}"8^[`0T}jFI=5季'1:1uV[rŤ}( qg`0>PK BH5045i;v12Bֵ'g.h/ [Y- n͉ F5wb3H\ I3j5Bg { iR(V3^c͸zhQ<)?isv 9(a^el_TдܸkyyP798!2Bɛ!IkGN@:;|(ovyj'.78q7Ȫ23-q3E/ 4 fOT]J壀B9J4C'9/{4^ TlOj gCS5O{o&$[3I՟ʻ0rSdФTg>wۛ'.'5Bi|5$mcsy꣘}T) m $!Rq HFlotsUc}  9@?=/] F`4:AiI[*/5^LS:4F73ҠʹTw>fHok2q*|AUxtrix%3&['S-F`IF6m;ni!炏IVLy8xBf.xPdRh,Uj`f,5/w,DŽF'&l6a{$%'ܝ6w #ڇ+(_\ΑIF= )<5łgGZ/Zi>ĈյvMR7.OwY ֦%9{}pQǜdb m݌{#8Uh|8:RWDNz ~QZa2 WQ2x ڏW |ӎ50&sx׻|&hx W_i2u"*/~{#.lvttdР(!/ûW2a1(Oǯä1>E(Fa Ew{p~MJWZe aA iԐR2k8C0bhL~u()āqN'<^?N閱?ܻCx+LVnފ%S7q_bb n,w|>A1l?_}j%ũkmEX {߀~IA~>a;@ӉCC8 Dbbcq oo>T!/N}4vgBU\-,Tjk <_kxB1*wl$ߞq>QzA& I Ix~&*;qP<ɊUKK'0Lʇ7xY{\ *'[P2 DiR(~xk8գi{ǗPZw.4z@b+ /+wWo8P9ހ?سŹxmԶ$~J$ b GDTCۂ@ɸk×Q%9[dpaZՏ~m KGD5pHܖzܥED9HQY+ qV޾F1Ȩ2٠A)dw)$:ɞ-Fiyt{W3; 'KMPVL\㇓+ܮ~{CtI}K[G`?kPU'[MSMA!VXXMؒ Ta@(<|p܅^,JO79YP{$tyP/n :~aƧ9A%(+b{ݑhO~b_nj`=^.δ Śl-AUT(/3 0UEHUK;qjr'Ʀ#=X6ՅOy~/2 Uؾz5]luyƴDU]F a^`U~nb>,N'2 #0Qľ"9x`=-+!goFC 1Ks|;Q>R!58)XVooF@[Lxa2)į/tbv1@«gZ0:$_GA Mhy|U !R@?s{dڤ,o>Zb߯kv/LJ؀$8Iʌtc M:_tm&:L7F,  T7X6FsT^/3|$Y6jg޷= 49RdGjH ̸I1`eRYSV_N<ڇeY+WNиͮf30飥/PA|5d-b=|bJ=c5J@5~{f*s_04J'owWe xZטL. haB-d~[FgbpuUp?5v@ c31jc5^j eG ߙ^N&߫kz1K@BJھIN;~l07L~{;\'?~ D@WNUW0^=NL1ɑ'Q"9RP*3čvqbbm"畣W=.ME_ lZRq]%9%ֈpқfPQ/z~^###&XQH5S^IDyybo i ^yyn"hP(G/`0{b+} naqk rώH]i t;@o] KCXMPJHt`w{1L i+攢9 Tm0=0_ W{bdzL8x>sSQV]o!i: Cܪ8q$GA Rb EF@5XIѳI )#gB-ikIN ̓ntF@+/AN$/ ZH` 'GBms4-x~zF<"'AzG2*Y!׷bxb OTd#VBPED(I3@_n3`cTyJ@RmոyH%e2)Jן*ֲPs PN0Aֽ e_ f?F {1' ӄiOl#h`tEQ"wNlֿV腎!lB>w7Gwq/$@4aE)=B 'y%G? =Rj"rYeٽ;PW\ISǵT=2p@lsMpF3֕a;z~4~T ى֣8Վ.Ook+o~8j0* vqE\pt&f6&H&] 9"arn$d WD1$3KJڻF QAfw"aS3aT8#ÄwGPtC#oT)ɰgs?^YIN3]UƳ98s Zep$7C~~3|Ig:MzgE2#g8ǘ}I$qU]eaaAub)1ݙt>W{Å>a\ D1 xuIe>J*‹K$wLbLPzS[hN1 tOMZLxOBӁUl Ƿxa)` M2wM;bi},מ:S&V+,cAe RdO$*~~ p ;}jU'ؑiI l)ˆ(JA3sHW^zK˨;-uo-UQ1awO"X{`ΰ2Ϗ~Ғyy=b Zx.ґQ"00jݳ)ܖ@*]n*"BZa!t93>?܇%n>b,'ҭƨ%9qZL,[nYV¸?/}АA @05nҬ\`1lT> 4k9[ڎ[8qR( Vd/5ҭ4lF3nb4v|ח {eߨi?HCۧ@*yCpWop25KM]X qU!]"% 0&xRûR 6aMd;pS&B#_x VzG8]R楫 XɬL>}*9$n](]X frUZݤP3-c*[^_|b+fbNlKH7pz z+$51t<<^\H(i)^fu ~̓ع$st&||epDgJWekx4'`5L Nk5$]ZQxp6]+f%s^]<[@Bc(,T&sp3Pط:OW!/rG`mrN!'g ϛn|zHei0 OO|^'6/$HlKDs(7O'TP6o~$ iEbI^R]*CsP5a c 83PV&\!VL%)gzCywod8#A[UM1 I;YBJK>ŚV6hh5W_&kgL^LS04jx2mQFͨ{yFsAIJ0@:pgVviJV1 MK Qœ,*s5Zs>vS*9Aa߼1 qaeӱ(ǥ>s+s}kZ]dN ;_*^XZW5kc#9m5IϾO*6,["Y,i?- JsӫKHT*ΓTY\ JmS~߇ VI_YD< _sXT%Į&+ p~zG_YTO{?.1.3Ia+f5&rგvjQĦ7}b2>e%)MLӇ|4ԇFz1)βDX 6щ] P:siA+&(7|[2%$t27-AMCxYhRT6"4L~ٰδiZ ⨊PGwIHtQ`J3%$04ibqP#}ZLtIME#g zTXtRaw profile type APP1x[ DkI`9|:&a;V_i$y&nzL7^+kԚxYXH狧ϫ|9r*Ӿ k0q@*{M?>u-þXV0V.~IvY{+7)>ǯEsʑ<H$O֐7V[Io%_/4͵4RiЬOe\_J~)}Ϸ**Y o?xndG~[|Q(S޷쓦h_CS j?sN' {/58ZίDD͹}-3/݉7{c fs?99 uR1joEAt[.ٛO؋-ro%k!ۙ,.R\,fĥ\m~k巋윿Vr_ݞn)D`T@L.ί4$'=J]N۩8\eMifNky~)"BC͞%p`F/j^Md HH1y!ww4ͦy=z7UPRNJ@y&f mб*=*W|[rD;R?w;=z!Fv ݄'lx&Rz}a]QU4 0mR `9Z=bO25S(tѷ&BD:WCf*W׃kGbŜPA>e h5FJ1G?k }/h 0ie]#gK6 )*a#mfO.0ݖOH *c5I<3:r4qCX˩n9o.Fؘ=꽴1@/e:]ZZrW[-3Bo!6G66D/գBUe] -/3;l@PSP:l!g>z[47pW`}ڱ:*3RV0uv-t!8D]BR#041IL.@9n%Sőީ噻뀭[vu-Kp80h *~m[iE.uCZhLN fv5q9jpr)Өk*\ՆM(FEkow`'zi !hfHh95hע&CUAFq -`:0pB X' v\WO:AZ,9ވ…-F$˄#4>PƂ,`##BHO+N-JGOQ}.=-C 3QlBd#lC)άTwrt#&g )ceo!:.+Awk.AO3 mJo 7~7[R-d 7s֧E7k"il\Ddy ~!Z%a}đػ:AC5W]Jkނa% z#aKDŷ@`,Uv"zXhlp  S e;:lՂRrY2p=`K?Q` b0HPtA1rR\E<"eϝ!M[gZ Àq%C'Foh ptAܡH'04h:I T2ڔU nĀNx"_I/ւ 6 rI##$n@ *†Pt܄R,x6$ ɎW+i8 ]܅~$ɄXBv(y=jJH`V*s'K4بPTLO {BIyHV < 4J&´`|**iTXtXML:com.adobe.xmp 1 2 3 0 6 64 64 1 b&$IENDB`fotoxx-20.08/images/copy-move.jpg000066400000000000000000000676311362435004500167560ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0230Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 246 489 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?GK]";{oܪ N SV?5K紟٣si?QQ@~?4}'j*(_=k{I}si?Gf%\O紟٨ ~?4}'j*(_=k{I}H{>||K~#k5I?ӯ xT*߳߇>&YZVE­Ҽ9In.@ٻraA!w砦Mo%d_k{I}>?5'/u/g]MA5I3pDBpqwsy[r>a}irGpozsSu_4 þ?4}'k>*| ;_:VwRn}<-u\Ao=*6FR@)!r%?ml|紟٣si?^7ӗǩL-uW0M{Wf¸y^<-2}d@k[^!pܑ"X0QUm/jM֞i=k{I}|[k|.H5Oy>.[Y]'nyx\=죠k> {2X.4OEo |ѶۿԶoߏOwk{I}>?5/챨kG>ͭ*~&-UUfYZ]O˳Ӛ??\dE6$S(2,L8qs$һk{I}>?5 ?g]/K_[^6 &1ִa jrifڔIJ. 2q ?wm]/DOL;si?Gf;'C^Q[7_-53z{!$=Lj# rA?f 7z6}U_X~vɨ? /o s+owƏi3hx|h68ƀM;G4ü>4M_@\[o ?? /.|E}-@m7qg>ix|h68ƏwƏi3h 4_Kü>4M_G;G4_?o|BogBF._Q{)B ǹQ)*q<7A 5H^&rCm߱vj;G4ü>4M_MJI+_֦hO#|FWk,տKdz2@7+6H;Zh}7V⟌0&F5 kG}Fy1~3w_>"⑤bdO*0^1ǽw7l4M_Mٮ^b٧^%©|5D;,zb<:Ə^Ơڵ7u)feiJo ?? /w~3"ڿ~2;O<<Om.9|^GbHTǚX/ :,_DR)XHOug@m7qM~WO~qhvo uuqr5nQfѿjm"/'"O/#2tgOofߘ?cyEkü>4M_G;G5|wS!-rO٫/ێ? v}:Ρ /e 7R;"I$tŏ ~0/ҴOAk73jZs4RֲB9s\7;G4ü>4M_R[vE=wO@?uCWIkij#DRNppy<כ ;xMMhs5+*R 8Ax|h68ƏwƏi3hZY[f1rs}uͼzz\Ww?ں<= o ܲXr϶>dFü>4M_G;G4ӵ߇8;;~:6?, AI>7m5Υ >qHƠF2y^/_Kü>4M_G;G5)%۹M;G4ü>4M_T+4ֿWNYl$67ڬx}5g@m7q|?_;Dۙ1e 4M_G;G5l )6ɽx,pJ1%=tWNߍRBʺ6Q8ƊCYExVoSSѯْ^\ eG\-dnH[7fQ_k_~2xoI7tMC ح͓h!,Rc!dsџIiy-4]5+<,`yr$Q:`<}5E|g?ǿ_i^֬<ihv}-Ζo亚:TA< ;íihGT?!V],3@j+KygTU I;ח0/sJz[iߓyirnSz,2IA"rqfpZ_#G[Hu]ުM,Ci6H!A-ۙZMbxךՎm֭{k SȤdϯoWi 5]l"FvZ(_N/kԖmcF$M%mҖHȤK# P ݞ+Gi'~I4W7oeke퍼,@BN1Ǟ: x?msyuu}[n#wE@]UDg'dx,+|TIi~,Ş%{-g^[-'cXa6@==m'X*Njn4QVw^#IBΨI]9'"N=S`ڲO>f}~ 7425L.,cx|V%#n+mC&kzկtH$Y>t,qQ59.aҿa7kϴ诜>,|HÍW]:[Up 7m`UTt|o;x6 /Zkwdt܋o&Xre}p'u^ xgl"XgI {_cqA<L/=(0(((((((((((5}q_1mP/J(:XSOu/ZCvaͪa?ǥgxn˭n}F=>7T.J(t'תQO}n?gO: ]/IHKBe q11$ tCx5ޢ5 \] Z6an_ר[ -7RP<el䴬Ksҩoᾋ?px'MIР{&E⺚)=o5ǚϞޤLk&7C2'8aڡ.il￶/.g9`VueAA<}E'Z[~Xx5+8䍎-Su[>xOC'Gz:,?>vףUryUv/ٵ +)Ec}]?&kӿW'$mww >ͮ_N\@4V7ٵ +j正,77q ̑c 3t4QEÏ(P]S)CoUHTC2` C.-y>?hmvu?ڴڛZz"SP0qq%ߪ.7m?ǕCWQm5t|iʻI;.ڥI$޻ Qp Ԟ=R;͍DB#FpH汴D'Mx撷\t'͝>x&߄7>ԼCco MwIђmEx+̻q迯O$k|K_-Oþ$/%⨼1obס I"\,x žkg~n-\Z۠RȠgAMs4 x:i`@$wVy>\x'Mc6cC,<5w5/q}%y!1 f=Oo_e.س[׀ x{Cx^ yĴO*i^V (8$Ƹg_]CȵGcjbknq11\~_@o> s\tj¶k50wR8r_Bm'|@1umg>Kw$/axMQ;F#Ou_ڷ}Góq[* q]gTλJB2ry]gn*!U$z-l vn.|f1j zlAךNy+q%y@T/^.?|>5G?4*1x +{k𽽏|Dk_ķ .eWxɤhxLkO9xf\G{/K{[eLS޷'1[FоX/홫xgþ2 s᛭: 6SGq췕.`cvC{߱Z_M%׍4N-5Y,d0GHC)D`NS; sKUC^!_isϮ" <ȠMNrzϣ߈OF5/CԼs\| +N]_ZDioBV9r:{WFoZ%xCK[[Z? yC+HXعNq]O?gxhzn[b).UPmf Oc⏅o-\ofڤvʦգdo9 9j]6Zvo_~އռgu _j7 K9 N$m9w Z?oxGA'y<%e=Wu(دMPl"$jbm]Og{ǿB&#Zzmö7~!ͼK{mTų+g_nῌ[|3T6;3_doq5k߽$וnSlZ.~˫h0vJ>d%c+KsxGĚ'4_N״ᩔּUkM e¢J6^"xo1k#o ,D&-3k_L'>SDkh~Ӿb49B2 F9/k/{kߌu5Χ ,y2>Xe/;Iˏ/xKR>?(t}S,]6nܜҹz4_ =uifyU$1ɽ1<կ_׊>_[RG1Xn-7TMNm-/=-k7Í^Ӽ#ou_ Oe7`O/46hʔy-1]GK߳+d:7exMTdv368+>8_exYkT_Wm_yQ/;u6~—fWwo-Χs#,p$"'i?um_Ի^z~!5O~< xG]v:ֻsG,VY9RG/'{wDu𖑨hךȹu)"1j^r<A'|K>)k᭏v:͌3D H4 1?:&.M#J]{^"|6- ַ *4A3*>oJauKK|s>;(uG[|<ЮHG܁k!~֟ ux{M/-Qmc2 Ggr95z->4K}3> iAMm,7HQA1`_IqxFZeگc,a(a0BTZrY3?˷?ƹmk:Ï?sH#<|B'Ԗ|&WZetZsg4l|ZS T eHvw++)^@;ĞòZKYvQEDH08_NMMu4ד%񕏌QYYE~=z5xX/""2 bKz~SV^}I bբ|Kb#~3 b|>#iXޟ=ˢ3ۏ273~`>Pzg c–^6Ow_=KG&I|٣ #Q0<[?O~S¾.n|8]{#C{/ 0OO-C9!~iQo49oE7c+H1B[sQO>0O}ޛ+a -}ȆCݸ u<OWwo h1mR:Ʃ%^UP1$iR5|YFx#ZZlZ濨5a!k9R#ޔUI9vz݄|:^ZtPZ$aj8|KԾo/8f~`m\|4O"x,@B8RN2x&Y}V4O0 r.qgiU-^O* <I񧇤]L<;mߜg4>*`'%Z=EiSx^tͯn ԮW3gg|TV[i^/o/qZp_ßx jv@eBzrȢ`/kn?Zj^|T<.!&.MB٘GW* 'ho]x%PxP%m,CM;1&87E5_Ckoǽ;=$xw^<> 6q̅q廩R7oh[Q>(xZitr0s!WQQE3t_#P<~?ƽ7~<׌|?-+\k|{9qSZm:-ޮPABnqF4y/k{UXt_#P<~?ƽ,/kn?Zj^E|~?Ə7E5jn?ZjGֿ׵QE_7E5_Ckڨxֿ3茤xPkj:Ɛt- ߭sx/xCsUX?m ~xC'.}* GOZ/| o(Ԍʡ"o*L8=k|a#֓`۟yw~(| ]=FQT[i3Fe^Y䎵/@,'VQ瞳ZT3XƳoZHPŷx9aw ~&R<$PIg`PH @_+y?UoH[I],7ݲo+ҷ ƅuok<:4s}X#e\bp9~&,φ o%S,@DAe##z)Wzw Z-g]Z1uE bd)_ZQJZugh~|> aS\մ ^~IhgQ{Ig.)f+OR K]@x_G{L3}EU[ogž u>[DIcV&Q=1[z[~+޾?h/eoWPCne@nKIp9^E++[Rώ}0^o>-_]~NWGYFFS2A~s;/ Zӭ]J-E]ZlY~l1,A@p++G+fd5#x|!Uuoh:ƭax~?=CsΊ B>LsҾ7Jbj/&Ig(ʏ3PY~گ:Z]oTtSΝB:OA){*=Ŀ/_8hĿ/_8kJrP5MH__?ZxSTԬ_ź[ZxZ.ï "eD/ф0pO0dz&%JĿG&%JĿQy>ݿ;Z&x#DŽ/6tX\kLJ%I_-0demܿC]|OмVWsnUIN$s_Zk_Kȴk_Kȴ]+a>ۿ#t?Z{:g5k/K (JA]{A| ža^_IHI}@:r} _ҿq/" _ҿq/"W-[~3&'.| [Hޏ+[Qyщ^H^*IpW??<_{CiCbFPHgtQyUk_Kȴk_Kȴܮ!F<+>~<Ұ1/#W|hv |?ݬrZ-`ԓGZKkM~_/޿ ď_xFŖ.&̶{48s5.#`u7Ey=H|&@''!־?t}I֦9nݻ͞g97KxwᾇcMwrksH,?ZrSoje%h_п_E_п_EgwEp+-+-OM|KBM|KB\'&%JĿG&%JĿE_п_E_п_EwtW _ҿq/" _ҿq/"p;+Ŀ/_8hĿ/_8hk_Kȴk_Kȴ\ZOnlSk? ea˭"YBfkGf *#kߦյ[:v۪[^=rd0~t{}2謌/o$tpa0)ޥ [:]Q[}ebuh1K^=ʼU]7?gMM?A{Q-)H*CNko+@Gw3?)?&6#AX_csRjk6!<>:&dɷڹUI5|.+Un_ƞ$[+wp VкAYM=Ν03%ImuM;KmBY;8L9*KR@(U=WWd.Lnf =U6ڇ. (ey]&bh>4uϱ_+]-FF^=m;kuyjN>`>t_aO_w Gz3>97!yek>&-ju:.o9@`S`nڳר P Aq/w#| YN)} Ƥ\l߀HpN;P+#+><M\xo0xw_,Gsmu'TduV9^;Ǻ{_ntH7o-fEr(/ _4^JmJc{Tpddݰkci~"5 kP-S).Q𬛼öm{@w`_^ݮ#ϳ|`"(\zdm|gk:̺UkyEmȆO&G[s[<7\zV}ovڑ5Ηz i 5ypy@U聜p; W=wh!Ibu'Pr SdmBM%跷4sropFǁ*+٩hEkԂNx],TzzV^MTMĠ3(vnw׌W^:҉m ȡq3܀sڼ+Ov|=i֍sox/lW 7oT<ӰPQ^+k^0|'h76-R{?3rZ}OOhyWA<Ea-E_pA)ٶs-O@L >) ZWO}KLk漚&0:EU-Vkx{Wm6뼍Bɱs>9V]jOMIT_%^|t/;Ew&ZzMAy+G+0(XNp+>ĺ)9ځ.K(~mn(e|ɟN_I|H֫/ZwXϦ;4 /CmW n\sώ5 \x7^_nu}O X^Wv=HBcW=Z6biyn-D21_/x_ďx·zƏyQ"`JD?[|0?ZQmGi>*j亝2Gs\y NM~_]II=Yk6w}j䅞U kǿtse׈I:OM*I0d6:(ް>wGVBښ Zu O|mlUItZ6R7%l p |O -_ĖO4彲d]Gˉev)"󻍧ָ#~>/l1-dgX]$ؐ2~(%ߋ_SoC״i:U-:6׆~϶Ÿ>12vh~oAHcĆ]z:Mm((|S Y~>YkkF,DTd+zu?z-Ǒk:}\$m\10m~\_[\xzZ%HC̨>b;f 4h՚MeiWiwl%B;z }r($>'Ql;]c/^:2h_Cj7f>)`^ Ŵk_ͨ@{o[r[(Q2q3 k~oϲy}gogxON[{ b]"{Tm$.kH@dE9Ur:W5o8ˮrHh]UxI=6ׇ,'*˥[1]?]yh|nmy4G᫋9.FgPUς? 9?uwS֛OO6que l`p18r(C>|8м2.>5[9cI!'}kmI[C+^%qN4.%K c>^4C{XśE3`GCŠC9|>Ҽu>.n躄z193v:O-i5o|w iN>^AiX7<φ|uGM}hڟ؆=ޅMc%aVc؞!W7Z&m>V&b9eas^E<7|KִfmSI5M Pc5=2N@ޠO ympƿCĖڏ7jAM#ݺI28P}>Rw'PCz5i4k.Sn;==3ׇtۋ¶Rp3q]&pi?x+xLĖv:63v d؝|Î"kou+96c_4[ppNʪn WuE!U; u//szƛSXM8`~$|2>)q\ek{I#Q]e֟-tWLweoNRX0nm@ڽj|OIg,}X:42[Jd , !#j(k/uS^g 'LMkB4TH/$X,<Q?u-ꟳLj-Cx^D|:~nUHs< u2>(|;Y|Wirkv) ܐ̳Fc9q$N X~Ѿ6~{A-5_p-{U%J~(>o#xgPϾK [YF$3Hl|?gxYWmG: ڇ[@j޽?KOvZ5[V֯PԵ]Dq)PTU:Q}<;x:~ɠjOk{$FE/Ht>2c7o oxRKC" +q=pi~bֶmEs&^,I᫬ Y.{΍]hTÉXV 8j^RTmu~kL7O>a izaԼUut-4mU C77>⤿ >jAjveiH$qy~76sr>=?Xն3ik"M P#A]~h^*ggO,nW/*?Ԩ9$ӓ/FqMEw|?Uv/4\17"e[f#{OS^s> hh t4 {MB*m(+HAne$+袊@QEQEQEQEQEQEQEQEQEQEQEQEQEAt)/]l1+fڟZSEP?tyڟZSEP?tyڟZSEP?tyڟZSEP?tyڟZSEP?tyڟZSEP?tyڟZSEP?tyڟZSEP?tyڟZSEP?tyڟZSEP?tyڟZSEP?tyڟZSEP?tyڟZSEP?tyڟZSEP?tyڟZSEP?tyڟZSEP?tyڟZSEP?tyڟZSXwuٮM9P[x,ڟZStM4[ JMѝyċD<8{k|4QG2jM>M1wXn y ƋC5[n o^Hs甉Ub~S&|/+[x{V$.5[ϴiEs8C'ɓp*<|M˭S^ =źf<bYQOI$v5Z|/qk<+hh]ҫcrGrILм;O/> 0-`Կ)[3:HZWL׳/?[J_M#ú-֛x\kVɧRq \!2 +?_跚dڌshW5e[mۈa9'φ@Ӵy;:{=CNrXv~~~3Xx4?C\Էqw89K=TOxj~)I׌zW0<-HPCOZ7&k-U5}R=IvIpM!OL Vއo/7c%]K'fWVIt n{gX?S=dF{$hQEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPfotoxx-20.08/images/copy-pixels-1.jpg000066400000000000000000000620021362435004500174350ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231%Ơ0100Fotoxx:resize|sharpen| Fotoxx:paint|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 274 312 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?6R##E? ȎiwڏcicWOEjz/++G֏Z9Ư <_VWڏj>s_5=y运Z>}h jz/(S!Y_j>k㶭g<(4^}>Oŧ";~b{-ץ.k/o <_WhxoXh0 x [O0_n]H;u0xk4t?|3Yj^[JsE4 #`shSOP{Xy运OE|/Z tݍa!-܅% Tqsj_R>xY5}6(.<=zbj z;hxK&#kE!d15|YƄ.4)oMF&eA8 MK6hV$rӆh$;*jܒmGQ|h{75Ř%͞: ?2vbctc湿1oß٫rx+KO{vfO37a\4sw<¿AyK,-*^߉쳪SOGTeaxTKca̟MEc-lZb}Uν X7N6$gh.{x/:ō[+Tv&W ,@98A ǺakX^)"mX$.=3#lT%,wIIQ⧈.4m.n%#+ ϊڤ%:j^4vR@&X9#<T][d?_JVWs;\͹FBA#)%~(մئBc]b>IUSx7Nehȍ9)l>g^\ 1Mm#%oZ>}j#9u4rH9Va֬1MG?_G$%oZ>}j#9u4rH9Va6Ǻ֡xn+˰Ḛ\m0D/F#ۯshΏ:_\Xռ=ƞkK  Cn7ng;_YOi~EdmwW "ctcj0Az{#9u4$O:~.x-h+&t?j[yp!O0azֳf7V6qw2ݤpבmP0QIϭ1MG?_G$ΎNhrx Odk#d!+xsW+]Kᗇ<'k#h]Z=7 fe3˜+n&F#ۯs+\ѵW >#_ N򡺚!pg eۭךZj`I{Ih_| z_#9u4(IlOx;ö #%;G>}z#19?K NT Uĺu'{tQXxIpxUk>ec@ /˻w rtW⿋>Kꋬ\^۝6;ig˩ 8V?.6]HȭxHhNxmav2gUVh9>Ph (+w4 OrjvFY!YXb3F0 0 nX1~/ _k[vJi!wP8՝,7~345/. vU"B6Ln0HsܠBvy8f/[nZr3ui-*s9jX$ԟIiOO3Z8ub.T&=3DhjQ-Y"+RU)\a֢(((O`(#iʍmdTc@ =3`K}rXLrӢ^8KbЈ`W vQEVT>(.MZ/${mJ>#c ׎3>%AUEaHEs*]D$^@ylվ.񽟃ntnYM2 ##/#'ڶ ,.& bp ?jɿ_/O-j~&Ԣo x ExΖ&u=};A+Ŀl4;֠4{bIh]]c|i|sLŕwwzZ o[2<e3(fX.rxآ3/zU7c4? x ƿ~ҕbN2E$re~s~ԗč&o ͯ?eVy3idU @8潣?m>"%~Zi1_^[۰i$ޮ^⽕V3'%mKWmD_$El𤻉ls#;/_Ȼ?5j ! /[baw% _r)~B@cϿ@>1|c,5Hپ,Ҵ%1#pd.j+NnJws÷ > u?V}/j_4p%>jR`*EUZ<OxZ׈=^"ho.Ʃqg q捔gܪπrH,&6{S߼^ŷ6 ZKM^UWKHڬK g헯hʶ:ޟ('.-.ҥ7°)$ 8};EW+;[cd[j/ xEܒVo~ ivmRC4ikwo}K,S['~+CTkh #KcB@cL5CUR<ֲ745#"d[ FqR.Hak71Cϫ[7e3gtR7c.)b1GK43f[?~0' "? isicY%Ou1cS1~&W_!m85 Zu,+T`D, |@ռaU]2tg_(Co$ьq.NNr=*—$7pkzmVaup֒VV7>HP'/ukPk7z[O.š2.!?+roeoi|^.Դo?n`}Z;dhA?QO}t%|sz¢z[ϨܿWEKFc0?T6SI]!.*I8ydأr s_\D}js㿏Yu ?Wӵh2߼+;pX\,/Y56cƣ6$ḕrXck*)-F?AA<~$+wT֒?.Um--| S ŰyxFg^ |[^˟Lܺ~ڛS.OPS9^}X>:m >f?*gpm<=)Z;R]K^]k^ <9XxT Fؑ핤,$(_@xFWmv'UAw+F>DAX1̨5x6(Ե; L~{ fO130\8>$ian4]b$l[;Fh*@ p+D_w͓kY$w4V?!÷/ }yy,cG$V״$Tm4ؤ,XY'so*xFOtm2+xI;vd9Q`f5>s~Þmbl#\)D2afRrF94ƅC+Vx6Rx_Z4[X {uQ]i/ ]7eu{^ZGltkF-:N:-Fx&rP:Vx2xn 7jͤǫFwقmŶ/CU4;eק^RM5NnDO xj-f; ˛H]D@`23FfMZZ|(m'č_mopk$+Yp˂GZ.z.iwzm+5IdBw7 qm4iA]0j3MoG,[Y[Å*_s> [+oTUS{ƒXb̠/RH-+WVt }op@zIh{o4?> <&x{A ݨF|RIũ<1Fm_wN?I#mA,$BRȿ*kHgzmXKai[jKE&x`qMB+XV}vݕC37qYQwPObĵ[_M/B}+Mc! _Z,WXYOc%gTp AW_xNK],xe\ٮ FVc<{q}u_Xͥe3e-k0-Q$9MZ\cu@]e31.ޟOkZž,- ހ,\$Lt?wȱ($3 I![q>n|s Pn|;*-uhbq AQ0JB_Vtkzޔc֢.-/Viv4cqY1N+3Ao|?ujVQ_hlf+>`WUveMsF N+QcHǐjU$f^eៃ=5?/x=VK=ϝ`n(I\57ʒf6$0Ə kόXZ ܭ4װX*%)zǞmEçDw%Ke`ܹ [ -?lj4MÚ| v=o[{e}R;xbM@|f:? uKSX/ᵥqq\rk$DS9]k0~-EYYwHn}Ly#W GMcT]oƒEOsX,t7+H'H.P$-&F+WWEω.Χ]}-|1}e Qm *dFmHm?Bҵ&aN, ,[ɹݑL$2S_xz[xol$edU6>$ iSڶ_ 䲻`#uPyehޝmo>^g[hjn9z=ToZ4ήT\Hb(@ }t;u(t%I[K-jGQ(Cc ՟4Lt`=$X~])îYkgta:YƲH,/PXs_ v[| P'B#0^a12)}澛5kkuΓ[ދu&.,9+*pAXj6WWP̻㸂A"8= (W~'=LJ1}H^+{F|+yXm,$Z˵[rRV热/<6JTkhΘ>$hݤ ȪFq_nQM;[߃.;>]O7ğZ5oo]ދ[/#60I$OnVRT~\k_&^xZ;ZiuH_[T3Kd('(o+_y_jB+BM%>h"yOǿ d|e6u:?A= l'!7 (b}3^Qxr |mG2j3M&8 r|1A $H3G85ms3{Ꮘ|V>(4Ydpj62c`pqRZml &>Vh [a)-hm^]hLG)uφ5xZχu;f>Ҥ{71\4J1;B4 Vaoi KXqDGdT-[%{wS>o|wgǩ|97~!l/XG`=w`t2Yœq H4}-gn!YPo*JS%~)Þz4(KY4nx :!&xN&~敧xn_ xئ"u\G]z lے(##ͩ@ Y$1[Gs[*:+ӀsVwVw _ƿmY>_i&=͌(5fY^c%9 t"?Ix2(u6 :vq1f.рw8|ciilxrj/&&jňZ27&W5%=+—WʺK٬p]cN~`G]¯? hROe\9yNO=yǗJW>A> xsS\jZvMu4vI$W18;'FuNՍ]. ךVvapyEI  @Ѩ/>ZOQ%z"o/o6>'u>d^Cy.r<8o |+PZW^%5k6&M-Q^TfYc@dAr[>CgxOӼNu[CAaIq;K־V>ҡмF|[^Zihun@d4J&qJA}kGu WE. 5#S F] ah0dI8Oa}dx:'yti6z֙)/Yb:o(:o|Ezi4kKn6dA$dYToiA{ =kk:G7wQi,1W+Q;Kvf=4hZ]m, Xc z˲_ u~QE ( ( ( +Ⱦ5? ơ>ImGu 6BOJi-W>2Ѽ#iie7M"C5) ~;@'FIT`zo\Z+!χ+mɮh2Ih\]ae ino:Ůiz:q s@-߇A #`[I>B09"Ki+ϫ(-K4oA@|?z6nĒ$\dA|o_׉*[OoNͨh#5KIm#t b61qCv?c+5$όkZ3^M2\2 # 'u|R񆗢hͱӖ^*lnA2 յ*Jɟ@QE)kWU&I jаVjy@/B|I ;U>9|R? =iߊExFc#Yn]KrQ e.\J⩼Aswbk#ȑGظVhezVy@/B5c^:ԯ8^+)Pcீ> nOWW M/#=$T)Qm* q^E+ zx4g> 7.k4EUݕdDe\'5#mmGT񎫭jӑ/nO&G1ƦBI8N}ڊ}n&ќ< [uos- U G$8*gص(ukz3igyl"|MK Q!ڮp3 )Y=>Cx?lV i#ZV; ȡ\pq:yg-6|ߗ5n?“60g?Oq֟on-,aYbnP$wxyF?߽s)ެI%|Nm藯6W_Ÿ_S}Q^W~፸s3z<q~MLl\tPEP^- DyrEh%w*譃 {MqW_{&IArpO=I<`y_^x^wb}>J^$3&. @ӜS|'Z|5q,VI9Y Ag}ΡC4 uHZVC5)w'wZXk tR uiyFO_?/]%dA!CY̑v6c?^!JXM?T5-V}e^ճFƩ DA j.M=zd_:{s7^`By36u6l í/Am$oita=emȬ$ГW:g{5mg7Fu?w!9keܰȰ{wA;QқG8:K{Zh&F-ɚ;#J$bʬ͊ʴ5sOG{Ь m|kE2*Dev 2 s >k~լ ƉkxzRu=䓘D.Iéiϓmf CkHe庇Vk/PgYZܼG@@4u=~ƅqK{{[ {`x&uf$:oU%wg"Zei47HeT`pz |ZW"Ϭ _Vs4Ku+U0 qAO $sSXIOޡ0ܾ$4a#ĎwnA\c7?oW|Yi5 ͽ4f-"e :,Eÿ<)OLӵEU{9R(3v8EUT`:U#WkW5;I?w^Z'6gnKQV?AN)QEV>wF 5w[tmNC$v7-Rl9@$p1I;_!7o SQ> (~?jI-_PRyKY2хa$VB9O|7lDwދMSIma -#n ' N1O]A_[> ?5Ooa\׊keo%`6VR^^C*a\׋xw[׎/-<} g6TG<g)0Nӕ]ϣ5OoaKivos,/<^\EuBӷz>xU5[UG 6k D<'hp.l.Isҷ}s_Jz'}ʊ(0((+{Xm .V5 xO5-PQ5/:N ڲz֥J~?ltG7ݥVR5P|@mQ@ 3[o"\)4QEKYRmam6ԮF1U~se;PLJoCzX$]KCiVZAufX4,j9`$ _;ɔ}_[`ӴV-KH,k;C.J29=谮/uAL:嗌Aj]οҴZ$º%I 2 u?BiCo[úe2E,V"b2#9fꍪ9 c4~@Q@ywE'^^q|:7ۧm#ĺ]4%ĈYnd5 Nxjv0Xx\F%HCa pY Ix/RږZt6DŽs}01@3ky''?^x :uxSg $:uINz Χq{(fVB[Y^sK?qmSpbxHn}>b `y''?^x :V/O7S>_iͪjrq43grˀO9rz4:M64kA_n"=N7sHRw_W9@rNo?++/Ky8ufuR3jB4[r0݃:GKL=v鷃+'o? t]E^U405biI$:QEQEl[hQ=H,>^:.sk(/5-#}W|)o&9VKeyEAOob/Okynu<3&Km(q%A7D">Wy?/ٸn4~\P;ѱe+639VI}⵿?~$MBIRI.R8yWIp6Z~%gĚmsNZǏe ᏇfUOh勨g#|GwonSc*Ϡ,@ȩeЍ/]C-Cw>">*xkW:5R+q%&;f27|SԿ ~1@ImF6mK߶5vʙ.ۦD,FwtZ+'bpk$I=K> 7\KvF`  u^xZSuwK:+9`c4q4!`rp(W;+͛r2c2 5Y.U ܒNw5>7Xx ogqu)L.Ob,&⻗ Eyz,+$woolIn8uCm&FwRn@x~!EComH^hy~jbcݻctzW7ZKOtye9,$K< :)_~_T}yx{K a8UHf0y}03h4i>o)Qgm-..#[K- >TׄڼWriIn]n.6y-*I9aVuhj~ k!oas#K<̰ċid fMpZp'#lr9R5ZIK;?o~-/Oioics R౎0I, 1M:w|]6za{/B5DݷpA&+/ǁ!-x>nm4mZ+=Gsw}O6Y~Dcdjs{xAQhƗp<1 a0g+%gs<_ףi=Wml],$24@+S' xfnU$cCuҴR-Xom2KEѯ%n5mzs ;b7Tuvr ߅įm xP#7YKC}tD-'xښ*p(zlƕ>9׿gߊ^!ݬW߂w9&GDsGx*3ף|X5.,м+a)nS~[[]́f(VAY@.g$~"Ŀ\_i[ߥҳ "h#lyϫQN_1Yiz.Oj:1j*mQ.[&y@X5]RPľ%OKd[ԾW;6[gSykgڒXewpau`x!ZZKH~֧?<}hZBk>'M,f}Bo2U Q vl{O?hvmc h(9(Fch+ m!`Y$5}sEAbݮa:\o\MlY$ 0Qv)wpJERQEQEQEQE^KQքBOrUʺ*(7*n쫯 ^Ң31{|Aʺ*(7*n쫯 ^Ң31{|Aʺ*(7*n쫯 ^Ң31{|Aʺ*(7*n쫯 ^Ң31{|Aʺ*(7*n쫯 ^Ң31{|Aʺ*(7*n쫯 ^ԩ.d }ypH+FA;+ct:ͽʐ|Mx q~t_uA7GU/?a/P>FAGSMڿos*n쫯 ^SMڿoXѣ^\k5RK37!Fq9p5?1{|AʺlD"A; ĵ;[!RHq-+rsz&k([׼^Z|GzdUE1{|Aʺ?E4Q=.U/?W_ u=.w ڌw3bAz@:_?'E,f2_OvY0j-Z? H[4Kق'˾-Ǔd2I)QEQEQEQEQEQEQEQEQEQEQE|$}5W+_xs!Khzfzde h Xqڽ{3@b(5-瀡A,>Wk ^}W[֓S-,0~l=:r}MC:WZ.xWBx𞍣[__ic7۩3>G u^OI~5Wͤx2iC*r,H Pc8{_xUލoa"_@ڧ𧄯``IxuDc*۪X>Pmj$OcMKOziGm-> +%N$a.۴_'?BeUxW;kq)$*esЕ`H ]}97Y 6>ӝqSBI9ozvNEBfI- (ӭw9NmᇁC%pJzjn&wLu{63gk.O_69\}ݏL5,|/Zk *w2V99 ɤo '?k6oƑje&FnE%97D.28 2]ׄ]Wĺπ@֓ 2F@NjgWf?'tKY M lTiV :|Mn7,Vrl Iˏ tRaC> / x_A iIi[bS#i|<Mcğ<i b6qW$ w%kdϭъ* "X_F]NCaN?#𮖙aEPEPEPEPEPEPEPEPEPEPEPVv]@<3 uo<y_?^xW"hy_?^xW"kOL4ak%maXpQECxgGiV:R, Ry_?^xW"hy_?^xW"hy_ܶ;hTa"B`8%Pfotoxx-20.08/images/copy-pixels-1b.jpg000066400000000000000000001232361362435004500176060ustar00rootroot00000000000000JFIF-ExifII*  (2i%F p CanonCanon PowerShot A3100 IS2015:06:04 18:51:24#'}0221   |  0100       ! 0. 2015:06:04 18:51:242015:06:04 18:51:24%PFotoxx:zonal-flatten|adjust_RGB|adjust_HSL|retouch_combo|tonemap|brightdist| Fotoxx:painting| Fotoxx:tonemap|retouch_combo| Fotoxx:color-saturation|cartoon| Fotoxx:clone pixels| Fotoxx:paint| Fotoxx:copy_pixels| Fotoxx:copy_pixels| Fotoxx:resize|0*"   0&1> F F Ef " # ' ( -. / `@`8\x8Dy?;IMG:PowerShot A3100 IS JPEGFirmware Version 1.00Trevor and Emese Stokes-- ?%:V'k,F-7,F IzPPzCzd-$k'-6)-,.*+)*5+85:--@ #}b  dd  @Gb "II*ASCII@B=@B=ffb64762c0ed9fe79a2019a0b100adfeTrevor and Emese StokesNE  (  HH    !#"! $)4,$'1'-=-167:::"*?D>8B3796    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOx!  }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?e^_ ӷsm̒*8YT浅2J$Raf4=>-< (׬jWvFӏ8 Z.0>QZSALf ɧ=j$v/&4" Uǰ@ A%&%杴U\ J8`1.+bƉэNjJ'G5*c,5[]C.o,4 ˸QN):14īT TaE?m KQpӸybS$ĉQSD > v"i sA@a|19.LMӁGqN}ivm-_>c$@':{a7y΃h܋ir:¸2UaRZ0=hQN_R~t9GҘ<%_z:KSNW'+9q3pzmq~gyie%ر'4^-"C^cDqS, f5?Φzz@Z&I:OSԊ'Iߜ 9 +L٦kqCe\[34QHUwGie+HcܜJu¥YH58\{=M; ⋁i~3a\p4sT G"i#]eomhTBáqW;d57#/@[h&b0g>ÏST2qRPNzq#(%Ԉ:PY<7qu+j?3;+k;+s)ks+zs/;)+Thek{;{j);+{[;ukukqk[+uy++u?s/s)a+Y+3Wuk+:}+ߵ+)s+;{c?+us??y/;+{??3;+o*3+q?#kus/+q?+c+q{;;y*++q+Q+?Ty +v+{+s+ u)cszC?{u9/?{+ Ww+)y+/s+?)k{7?y+q+*)ܕ7?y k+\kyoKt .uw/; p+{+9 ;+qk}[+c?sc/Q{;y;+e{k)+"3+;kkq+|Y+Q?+q+U{+1++qo+{{=s//iu+.3q+;qyWqkjs+Tq;+tqk;;-w+tuy;/)qk k?[+#+T{?+q/cykk}+{{?++q)';o-q+u=??q#kkq/+swqj9+;s/u*o?;?;w)j{;q[ukw;/q/qq+*s+oq;q/ti+q;+RgjNp*Q?s'{4ky/})Vsikj3qus+;?/;w룖)q/; +u/5?#u+t.y++y+T}s#'s7+u+ky;7s#_s++q/;;//{{\ukΆ;?ytk+Wu++y+q?;;*q#s+tdy/;9?'k+q{kY//xzԱ+#u}/+;;k;]}**˯si+1;@s*;{/u//:{;w/q/++q+uk+kk;;c q/os:u#oqk+u+;X+#kia+ ;q2+uK)q+++;si;ky+;p{cGoU 9?jw+/q/){;TyU/ksk];Q#is-]+q+q/qk+;++s/Oa*y+x:Qo si+x/;soq[U+q;;+ks*Q3;oqo{+ysm+(]/+:sk/wkؼ:+;w/q+ky;+sokuk+/;q#u/ ;U#o/t;+y;q+kwo+q ;uk+)k:Tq3;9ooq+]k*{uo+ky;q/sOy+q;kq /{++\+;a+Q/s+}+eny++q::s-+w/+s;qkqkq:++{kr)Vw;qk:s+3mk_y;+{+Tkks+Kq;:;Vuo//~q*T5;(a'si;U;-;/9kckcok\y;/yuio+;q+u/-51'u-/wo/s+x-+so/p+uqkos++qqj/e+q+q+eoouoq.+OykO+?/+qq?q+-pq:+Qo/Sim8;qO+-(q.qu+oc//+qkkwoi+ݱU/-s+/y;qukkw/_q+u+ow+ \q;3oqk;+)%)u*z{*Ok3/#U9;Tq?UO/u+OT+kj:ykm)+t#1.so(u{x;q+;s+sm+:s+/q+*yo;q+>u+j;3;+k;+s)kq+zs/;)+Thec{;{j);+yK;ukukqk[+ u9++u?q/s)a+Y+3Wuk+:}+ߵ+)s+;{c?+uks??y/;+z??s;+o*3?+q?+kus/+q;+k+q{;;y*+q+Q+?Ty ++{+s+ u)csxc?{u9??{+ Ww+)9++s+/)k{7?y+q+*)ܕ7?y k+\kyo u+.qw/;p{+9 ;+qk}[+c?scQ{;y;+e{k)+"3+;kkq+|Y+q?+q+U{+1++qo+{{-s//tiu+.3q+;qyWqkjs+Tq;+tqk;;=w+tuy;/;)kK{7[+#+T{?+q/cy{ku+{?{++q)';qo-q+u=?q#kkq/+swq9+;s'uo?;?;w)j{;qukw;/q/qq+*s+/q?q*ti+q+/RfjNp*Q?s'{4k{'})Vsikn3q/usk;?/;w룖)q/; ;u/5?#u+t.y+/y+s#'s3?+u+ky+7q#_s++q/;;//{{\k;?ytk+Wu)+y+q?;;*#s+t䩫y/;;?/k+u{KY//xzԱ+#u}/+;;9k;/]y**˯si+1;@s*;{/Ou//:{;w/q/++q+uk/kk;;cq/os:u+oqkku+;X+#kia+ ;q2+uK-qk++;si;ky+;p{cGoU 9?jw+/q/){;TU/ksK];Qis/+]+q+qi/qK+;++s/Oe*y+x:Qosi+x;soq[U+q;;kks*q3oqo{;+ys}+)]/+:sk/wkؼ:+;w/q+ky;+sokuk+/;q#u/ ;U#n/t;+y;;q+kwo+q ;u++u)k:Tq3;9ooq+]k+{uo+ikWy;q/sOq+1;kq /{+\+;`+Q/s+{+eny++q::s-+w/+sq;qkqkq:++{kr)Vw+qk:s+3ik_y;+{+Tkks+Ky;:;Vuo//~q*T5;(a's i;U;-;/9kckgok\y:+yuiok;q+uo-59'u-/7o/s+x-+wo/p+u;qko++qqj/e+q+q+e/ouoq.+O}o*O+?/+qq?q+-pq:+Qo/Si-8:qo+-(q.qu)oc//+qkkwoi*ݱU/-s+/y;qukkw/_q+u#ow+ \q;3oqk;1+)%/)u*z{*Ok3/#U9;Tq?UO/u+OT+kj:ykm)+t"q.so(u{x;q++w+sm+:s//q+*yo;q+>u+j;3;+k;+s)ky+zs/;)+The/c{;{j);+y[;uku+qo[+u9++u~?s/s)a+Y+3Wuk+;}+ߵ+)s+;{c?+us??y/;+z??3;+k*3+q?+kus/+q?+k+q{;;y*++q+Q+?Ty +v+i{+s+ u)csxC?{uy/?{+ Ww+)9+/s+?)k{7?y+q+*)ܕ7?y k+\kyoKu .qw+; p{+9 ;+qk}[+csc/Q{;y;+ukk)+&3+;kkq+|y+q?+q+U{+1++qo+{{/s/+iu+.3q+;6Photoshop 3.08BIM fotoxx, art,  http://ns.adobe.com/xap/1.0/ 5 C     C   J" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?L2+}, Gonk9l7[* xceZZmeYnZdV-ȹ.O澕MjϚsKVzH4^&tf@Kg{:KH]y1uI(fWaH}m/h%CӨ}$u_uqƤ q$`+|ASi"գ&}cN|LѥUBW c9^x+J }mlLHSz$n1XrrzV5i"dNyc[CD(:ԷUH.g{iP.ӎH8́ǽWOGךddGҽn4g# @b3PZ*Tu셂1?.PFpO8V(<݂c~upf!OlS\Zf*:u_"~VnM} K;<$F& u+8X'ga'Xd rp^ "NCYWҼ(v=OjqafuRΪ<{b I=IW_x#_Z$|#mxpFgh8'Ǧx}#UYEms'k f\pi|R #w_zM=ɹ  AG DdxnlKfJ#{isN-"V =$qUnzqgzqRZ#e5ry< t↏ H"Te˷$T|;nЛ0l% cl\ioZLi `Wt((;uMSh*8j$AF(،4īk0jKhr7xޮy ~j{(2HW שO2=jFp9G\(t*Ge~* {SKkhvӡ>t]>0?z^\殎=QK1 dp>7vKh߮:~5Rxjrf g3kKmlTֶA `():׏)/s[,2I[DѴOQA8#du'Y0t/o;F9w 0^8o}v2 n9 qP4XOC{ V,HY$"VHIJ 8q\o TՕMX菋5 GI]2E[UKQ6[C4Ou vJ˓սG~гkDuLV)BVs-yF}D3(G~2,=>kgU&Abrr')ݲSAN2G++7&IY ƑIAI1>h[<봑ؐ }6H?fRV0lrFR sscFd:g]݇޴^@[Du䰔ۉqdrz ŹN6 q'c!G"XeCoj988Z’ Ծ\13lg"X;ьi,/ݖ!A eFyz'& Ouc<ջ[ aC1NVA[VT(ƤݒAeƶWvZ\;l}wH-f^icاj#+{jEȂ˂ygޯ~IrvO-`Î;;}Ƽ=ߵXuRn/w-Owk[<$prJ_:sxwQѠ1$zy$Sk6XӌdJNyi/iFAf z-`hAw;2H8?vmM< kadW=F=?*¥|DRJ}3W\!$QiU0 'j޻Lh I2|AI\>tO%K3ʲ #y`r7Xkͦ"i@8LCsBgjZRPW,Q-sxz">eɸg(v'8'CXjme2B֡4f:~5+ 6тU}NЧ@p_΀:GNy|9"xLBXb1ԓ|3|?{{~gjS&2 dw9k=^Ψ2UzV~ sDBv#5oc2XtOzCll;x`BJ?,⵴cQ) bV[O&Ov;<]2em~o6Y0cSW MH891i6 (rz{֝DSe' M* 6exUon*xRWLiJfG39Gdu#gO+Wwgcoah['d50g>^W9;SN̲8Asi V/0|tPgFq֪iȪ[tzYK;)a~[2 hNPAf }k[Ur0zֺ̑E1 >m[dD#jcܚa/+DѬ|5N j9HrBgj[ &D7l#@qZR#n"r-4xHHߟor(Ka^ ѵ WِѾXPc*>(^$Y \;!e8BT}I{ j, y 7dҥĶ`!aQGRKIAtR$D}zڝxA[ecR7_Bͼ Yؒj\nuZ7ufu =]ILK(N`}pLsUMUƬmLEp|~kgg=,\Fܤ%T OS֭hwiam@W=vg9syWUsI"raÅiNchS2}z/Enەl t3QȬC_ז-H$ЇPpJv=\h-ƙ{yeܬ>wZߊ5b$J3$!NH vG/й5{k y1綎f_ $QHPC>ϛUGIh`P[0 Î5%G,7 |^.i6sk,s0'[$*sA+3]b=bdEQ *R['>*jH\iȂd`2Om@ŽGgKn0a>\e&&p{ K#B\tfL`mҦ~ py#=7OӤ}bd] 9-#~ RZxSjRI3$K(,s& 4Ș7D{IV##,}Iި>]\GxTb'+eC-<wI҈Hr2A:fSƊȊS@ _[8[7oq}!pdvQ9F;֊^ Cl*pH## g殏)<'ux8G1y{( !!seƯUS*HM\>b1ǧZ9tUk$xRQfLzUWv\e i=ܮU3Υ`[gr䝤)GnqT]}H/qw]6Eӭ Ԍ< ǽi3ShSU-H%p9[b{iFRrrAr6K W%jN!<#ܩۙbӡy"4hkj,Rg ? uX$H]č$x q9n-1$DRHҸgs\YiZcjo Kc%b@t"_G{f}dGf%PJONݫB}O*WJGtrnLY<;!䁒yrqu>,GQ?LcmurSp d)Rbm%s tYF>D׶xx\pk>'޳ۺEPf<`;㎘=k#_~ڵΛ:_8\vT$aAWc/i} +Ics'+Q񖅤VHc\ WqDG,;T`Agdiifʹ~Ja/m~К]aM]B]>L2H$0OnĔ3ܖdﹹh1դZW2 F]8قJXnr5.X,)^_Ktwql"Isu}z:5/KIJvnz2{sm^I*u?kn"]o 7cַXJqiK2Z獵;$wK%fYxYӕS9%B,gܩV$WJ6OԽl\Hog)rpТA]2*%@-Z?ʰ>``ޚ^'j|Iu [%T<zzY[*nMcٴI*n5c TgםQ>M7bCz8 h zʹ`Xj ^r[?Zwx ==i aҌdfe\Ujĺ1ޯ&JkwjzIYAn \%p:5JI @*dq5M5!Q55v*>&Uqe{_ )mC &p=h/ 5kExET޳T9ԑ\ŎM5b7Aפ:6d}j`*r(ǵcB+snjfBǀ8#`g޳b10g=>c?2N#=r?*S r޾Jpe /# #T b'zq<{F$\iumk+Ux{Zo]Jfv ,Z\)\֯^+(y[Œ;}NGOkw–pm92s38IO~汫&Nv1OŰH-#,7 m8 V|y Oxm*'bbv?gqxGf"r~j79VG{,=zub*=CT_j-dj$8C~V,c͎݅m. teIT6幅rp;՘쀋,OqVݦb[Wntn"T%E6`{T!dR*yݚt4yj @zc ƵGӝ '@OZxeL*sϰZ_8$0Fd#}sz.Zsa1v tk*4c1 9ӧn_7)6$ʃ^߉WNJ߄Ӫ*Y)ag%n:gh'>+{ [btaÌ3G*476|UY 8NcO-B {yjM{I=ob!K K\A b|?ˍ}uqmol2:Yq=scba=O#ziQ4< yWi'98㩯va)FAGWuE`$W T΃s*( plzSWgC}NmUF{W8_Yd<ҭQ(STz"2\ާk̈́qSȅ{Ҝ~ŏFs^Yw-`/Ojm)%>f-ysל ն35.̗|*')c%\rFu~4ffvglf'NUN¿*4n}2/횖͒kr7E+'q+(4Qv9_[Xf |&~Vi74+!eu $EE{Iw]{'6Fk#gy>jiZU,pIA rYY{48k 8nFcd}r{Y ,{}\,N mڣb+mKU'#dc*؇OR՞q[][G`;Zsu Z5aߌ  %ѕS&80}M2O;ZO A]An:c*gy$@>S\ռ:MR*g"iHW$-^pOJubs\֢BE8#u,,ඏaA@AMhZxWBl"'F J%󛀭pw_aBv1 UpCvf=ihq3uɴ M)1[\#8'mty?4gf>)ždl7应`ǾC7J2pkI v5ZS(@hf3"}>lٳ]}*d}k>Qi.?No%bH>VN쯯ifesv+xUݺ'sN׵U3~$Ժ& Λd}zR6u(z0ށ`,vkOcMhѐ+);1or;6 2~aO#Ύn_Rk(y0TdeOr@;Bq~P_k'<}4 ͔~@,࣓^4k^uHwC%$bt"SSO_jӤu;mP@ĕ'q_Gږ7XuxAhH@yr<rJzucU~'t+PTIKxW|z.HјZn%e ʛ@A9$q=k di jet~"fIp ;C^m ouw׵Sq8^g@J|3n *ܜaOG6j{T4=M|WÚieie1OpT1bqkH.&μ\Q_geNp*Ք=h\~~uK ֭9S Ӛ-'qWjfNVG@ qR p1U J1#pmH' S5Rԭf*3^)2` WjHPҠkYplL1\ĽKZOoKaQ+!s ޺\`aJ.~ȚčZ\H-@Lw^jvKJ渞ChccO5[cW:ziy+f$H#2d |1>)J>;|5-_HY[Ҷje"AQ 瀼w,Aun[ά5UbB52OޠCd^w(~ϐ)lxy3EY (}JV`xWlemmن rz;?ٚMtn IBn6h 7A|G˫K&YӞ?bT=ES%+ރ-,y&yPR[i :G4Cj +C;-!FEr2Z$6AԶGsR rdu z^t P݅Rþ*H {W99f[\8   _G<֊Vr{gKs|W_ӥg$ dp>bIثu ^ǎzTqĪ>mZ)B,VR~aUsYȥiӴVѨ8P:c52"&΃VŴ1L_#Z-y-$Au|J]VB?"̔?ԯmd˰qkk.Wc̶.ppőXKJPN?/}o^총:oo4Fp00${ls>g%{he$$II'޽3U!VtϘ9zWj$e6ԛ,f*Ѱe$3_EYfmn,0~Q3us &@&UscǯZKC]аөGq0jTw` ĎvUpgknExbE~pcp~g\@#+7tHo_(GS~=~)|H {4i*(#g ֟Wi:ouYs癣lXW¾({khaٶۜ6]UA5^2-gyx_F5O^{FV8Q."4yI⼿]CzLwi41Ti}ھ3<@]nrc[udc8dyahzVMqvcJd0쭏A*JgS,ے>tYXb֯n<\(H5)||~x&Cvͻ2s<˷vwAx /]Yq898m#VQc!Pr+!nV >bOjbi1>eIr $0G?ªPϷ5WQcif>Tj 3@zfO4NJ!@#ނjA& [od(8NI/H$C/FnfXre]3R7)_^h6'4ֶy/ϴ0Bv8t/.OŅQn7}!aR#&OR~`x 2T;$ʫMFk\"*{uQH?ثW "CW(]⧋RBuYv<z-ZuSVRjs]_??&k{ap Փ|rG9WqQ)b%cFgRn ml,NrsGW!A$SC6G\`=yYP3T`"?V]0qWX_ԄTUy:02#;J4]3Ɍ kgk0T|mUF8Z(ZLA*V6~橭X\R?Vl u[?Pʨ}zYɹ֜2l5+XIԓքU=GUt%W9U5ɰyaO<طHR$q?1@PgT:`"tU5ܟ.HR5_%Au/xLm9Abk5n==ot3'fڹw$.91 Vx ? SyՅ=vx3銑nFQKFp ʹC?GNsGE$zI<ʥTj3+#eI,lʈ05:O R"9עuح rf;t 1E]7ܭ<1O\}RzF*I5Ԟ2>Ͽz0?;8 sJ`BIֳ2$}*Au(|bu˔%HHiizrSXS)XUāՈ/%VFwʨRߟZmvgÞTշ`CT ]ͼ'8[D(OT͏ fG:]k)zvB0#NpTa)R?"ج9ؠux5^fq,JC ?-vGJ伊7FIPqzW:7S?aLl O'}kbV Jm' />!tH;N{BiYH= alF@gӑ!j ^ ^7$E](ʂ}s^WI]GY%%$k<rqZ'i\4v>G]<xUs~s*B ypK*Jec#O p0Xqq_ZZB2#væ9fְmE6ѥJo'".|,| 꼞x|&)\,jȯjߗv;+9uՅׯNާ[#J^Ӂ"y7OfqYZ"X8(T?TnZGFhɐ˟'Swp)aO :wȪ2iMqIy/PH'2ϿX;a c׃WE{ m[r w׿ X6/β5 Iq>Չ}PZ*'mJ >w*Xo7Jr˦HIM9'ֺ;派Yq4jF9 nt@8g°ڙ%F95px=Eu䋇sqE|VwFu?xж;y3Iu^f9%yp1ߞ)-ZϏn%N̽tuP̪q: sE}fsӀkQfuݙ`Lv <9=9 ]zE0q2R;#XU:d\W;|+Rmʿ;N=@T|uNa/LY,n26:,m]n- _FlUTz֏96|Ɠbv{PXU4` 縢Y|\Y5e}XY]dCʃ.hej`$¹uH2 f`p#OH)H è氿$ѸHAp3?K$271=ֲD vu“U{б g{VmEy?zړNյ L de?M5fB/>1VlxW ZO7R\fmY%UgWTQ^KiYmK4ޡ% ~ueH洳!%@UWh"AM9'~2z -fÁ,ҥw*Qi\ߋMӧZ-?%HVX[ԷUn>`I8 5Pu3Dn|}kVR5,[m47a'?)5e#dakԵhgĄNO^ҫ!@l8q TMmncc݀ ~&N׬6> 1)zZG'?Bou3WNCDo@Vm N/-ㅒYvXpOSPV?#W}P7P;+ gn9G<̗"'#!NM>OTZ$mbDnw )$ 1kW@E͚1GiA,tP 28#UF;NkRi1F1Kxݮ̭z#)rP&mWjV#6O@Rc'̝bC\ęJɑ~d>֊Ȋ%h 'qfb%ܱ*8\A5]PRXQskl9!|WYYiUqйb0ty|3I4kC˖gRg?CVЅ;wAq[P_1Pd<-$ep-~dGҬ Gcӧ-Lʈ%6:}:=]0]vc24M>@Pd ÑҪ)_RG%fiQZEڳ34p3v'~zMOQ}kO6*v`2NOoZdXNt?fvN/4dvZpJ -恼 Qʸ+I53_bEV[=9 'gץuwSf5H? bg'sg(s"ΠIli#+yY_'hvTI\ݺ3Nq֮\x~i^ M#m\FJgbN}].randg&X\qU{ 0ap&uYrrD7Sk֑{i`B>JN$Kn NzоcK \yn0$gqZz]ͭn#pϿ$_UC@"3"9$@dQTCd|iHF8O^3q+Oӵ3wN_4_0&—y|9ݻ54jݮt,<x<69һ CݑlcyJ,xk66HbW12qx> q: JmX87D( ²$ 'zU j*o:q4vcx<^$RN {n-PwcHӡV ִ8$u2HQ+8=:K]JCνܖ62!PCp̫C'Mj(X:J܈mXB[ZjIu xgduϜXl[j# v01hw6ڌ7*Izֲ^I  ' M޸S-W:m䊿4lpzs1:W!!FH1|:j1_rqg>aF;Sk?wYJ2؆E?UPVZF&LA T_n yO~іW]cwF͖<}XtyVha!) ?*+=utJOثy$J(\1힞r?i]ņ]瞄\ENJ*wH U2: #>5F.֤.KW lr:N *xS#nu-ױ8c{c>r Tb7H}=Ey:xC7Kב]UXVmΑ۵lM&'FGoiC)p5r}QYcpJ&iQw  }"59nmկuio Fہ|7Э_Kܛ֮R͘2o+8gnt=+D7,mO+2FAۦsM!-.PO BzVNjZ{Τ6|щ6=FG9 ܫw,4^"; grGJEFQzt#;uTg_mI?T#6wRNeX1Gy6xJ?n?Ƌ\O\qq"O݆?׍ӠIu]M5"dghb3Tm>TBfm-8֗Je !Ex\zežSiAp7(#q]FuPq;#a!:Jt\\#^IĪy1#ܭ.FJ%r30@+ c s*Sr ۡpIK,{CF|bpAs5F-qZ[._1x{E_ӼYlt,dxgyGՇ$F3ԊQYX-#V<1r˚:4i߆tZ | ||< Jih' Y!q*믤bI;d/C`GX$v'wۖ9;=I/$0InLb<;Y><ͿV8Ϡݿ$B_L7uqOs޳ eG^2qn3}IZC.ǜ\Vs,19݃Q_@ rz[m[&C'r1 vQۚmƑ+85/#DW?q˹MP봎3Vf|+[Sr6Hg`PX~:G{i#-G}I]{O]&8C)Lx8aַn 7[["-ok<%ТRN2qvQMnyB]6׾}M-/邳8\9W4xW?Ƅp xcM}kKm4]gMXfZY"o$A#o^ҵ}"]+@)n6ĖUSok faqm3,q}'*[[ڑ[kviu(7w=q42A$@| =A iu}}lRov@@^}9^!9nm pp) Hu|6=H54<ae吒I?{\5[j woyo+G2*p ]@q_6\kR]7#fݻFla%^)%D.v A~:x;xaVmo=q۶9woh{?,#`z;W_3n#XpQW\d<}[/oDbrbH58S QT}4=FhqpT[qӿЩެxROLt=+̮bt4f#tYoVw=ϯ^^*mnXE-X4k&ߛ zҹyCpBRNܜy9NK0 1|ׁqҪ?JR-UmҬn<?*M0Φ0Hrx^+XӭLOŤH1_sBn'[=15f'uL2vO^k)Vq`E9B?P 8c?[))t%]+k֖QI>Y0>^w`zxRY` ^hyqn $M`̒slҧ!r#M䒣#`8#_;tGW.!Lɗtj3<]>/fmJmYGk=yNDfcG@t]).d2\_4ae*X:9?_L;c,mݻ"ظsziDrsz9U-I2їbv=֑I?l3T{)ӡZUF_,ʨnH2ā ;kZeͽ}څܛDv'tHHF8#irsB#Q+ZiP݋* ARzgϾ O5GW1В qcY_hw=Ib:%mC`GG8f wr?uNN@8 wQn{l=iJRZ]+k;+ӑ܎svvf1qi<} G y팺N-縶uTrXzzw1SSC[KkV < zyt'f4>OkjJ*EWyl~uxg=C-$N(V$UzqM|'{d\ `hBF{~Z?Uk-Pgș2^2?{WW/M5{`-5Y98nz5GP u}aF~^}@^?$_\i{\NMƻM7f𛛔ӷse8ϟ*N:a8s]:k6&Xĭ4Nm^A?G4Gb1娕q{f5kIw;}9>kf?K (' $rsӸTsՏM[{0nN9z$뵒hn.G!:zUBmA*;H=tsJ[#}.EL{QI%z+[Ry=Ǿ+вv Gd{g0{ybUVA2uJmh'ZC ~F3D#)uČ8X?,=e]c9Q7)~C`qtq*Kݖ3ͱ6\[O%yͪI G̤p;i55|gc9\@C3D$!|͙diT0IA|&;mQâ@dQ6 s9 8fH8% VxUGpeU iK&qpqUq9 m89x?߿Jumof6+gqg1FY0ܰ'z`%S7% dՈT/˿qadg8ĺE[W\Iwܼ зu* |;ipF:`Ͼup;ԑxB/5&\ Ζ6 ŋ ccׯjT4餵Of)k@Hxmz}Vdz|D5;+Id 㸎s構Dܑ&vc jͤi}PsNyBʨjze- *pѪغ;` i%3̂Pq`gA+C*°I#N3^emhp,\WB|ƬXZN.$ONmn;Vk u "I.Rb팝zg>k|kpCDryfI.Y& K%y>޵=̗\*][D Bx$_wcjV).b1Azo8'b_|.LŎmJRpćv .mORQ_*{pʹᏮORi4JH:" I0۴y]I=\gFzEp~+uv7-d.&* LpGenuϿRzQE6#BȐ$@ D`\}\ Zi3ٍ|nV#QX΅)%xNHݳ3[mQǹK m&?Ő:W}0v+eVhL=q+c &: шcrr@נZ׬`wy+ S&+Q~mg4l˷t0X(q`ΤKK 3Ą diW;gRh%-n/I$@ zrV1^-Msi c1$ cs:(i6|RhJ糖mKHHH p=3ұC[GuʹsXYEA-L'.;Kvsk*+9=ko 2g"ihm#KpXwaI$1'=J;ho|7{u.-ěmƝI߇ޠx]19(/\@ʚVPZXW8ު#,d m(mFKU&>{)Vy݄aIECFfŵWpncܱC,gl_t:MY}J!"dHebO\7#8'PaȒ5t[]:ᮣ$bVh/kwmOU6ffotoxx-20.08/images/copy-pixels-2.jpg000066400000000000000000000656551362435004500174570ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231%Ơ0100Fotoxx:resize|sharpen| Fotoxx:paint|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 302 306 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?8B6#<zUo5=ӟ<Ɩ5_Q槢B~G} jz/(S!Y?i>so5=y运z9Ʒ <_Wuc|',E,m!BC@>J>}Ϥ_Q槢BO _Jĺu g$f3Fx9 xėEyAyqdtkV !9Hr6F7 tk-t? c}-jGMq.,FI)8NNi9n Fg6=y运|+ ~|/FCJ;IV]7Ųn%g# s}ƷtmfoZI~DFiA,"P!\+$svv}ĥus:mF]>;g[+%zV_WnXwh:PRxCIC+1Xv9Ľoz>"լou{IQf.4l o.TcVy)Ș,OS!_h0iƍ3Ǻuo qI-.ng w5!Fp&^ O=]>;Oq%[lُ>͹"?ߨlo4l2OE|!/<RhZb_i:0 p6..C^f^(ѭ4kxN]jJ 31ݹ+>S!G Aki2g#q6.ILj Hvu+@о;=|UInZivR@Ia-#ar(>S!QmG#1XՈ kd}[4e4k洂y]ZF,@u`Ӟ#4Լgƺx(X>ɤϧ`.8嶕&f$p}m[K[my运FڊIVtX#see]jSZ{lVWwf"kz֖?r&Թ] 9|y_ֵmkPZCiN8 㢥˕]sEu6/\Tҫ88!VYy֒+9+/+_Uty-[&I 4tG/qmy}Bg[-Kqlp2pnOj6vVPqnL_ <>UT|!\T ֊~*+9(ϖﵟZ#ur` q_sS"TYg֭3i_sG$%OZ>}j#1M?w?4rH9TYg֭3i_sG$'^%m_`LdC.xGJ7*:9{M+:N "_z +~4hf/ G&G'D;.-^Xvq`I@ 94xީoI4 kKn9w~l׷3i_sO[20>?&'hȋnRN;k?AFE2[_n 'Ueg2pCdvu?w?43i'{U+_^4֯Myq(K%)CNy5_?ïZët_ktI佹IVIDtUf/O&In<_VRt5F3#Khsmq0xs2som^N[fi&p0 _z&mWPƵ5C748U;c&>~1xē76ضX¥ewf,OlRƘ4尬sO#1M?w?4r\*}whDf۴чo:f/O&I&8;#ݻ ne$boI:\Z?w?43i!sD> n.. {ifm2i],JdI$%%Hs1jf/O&I N(|7ZMCX09 ڻ_|M)G=~4hf/2YB_k/c٭1ۏf=OEk NM>O?g#扳Eaxƚ_bԞdXM6C˺vVph;FbX rkZ-66F8Ueswbz+7ſC-"[:O]V8o,f4HVEn(\`왶6 8ޛahC/m&48'$nu~bbWOFy6zWyWu hdCn𤤷@C\E|CG7(QEV7F>\RVfTwF_s>2Nkr (Ѯ5;n緃n{NX#@X:Pmȭ\((((s7/. v9s; Ri_yyoAqqov,X6/ g*pH P[EPEPEPEPEPEPi7 ŐS{ 2y.B? uk6xL#VyJ <$W~3n;osc{z椚\FDR2;{,mN jj^"4J#t^{K$d`6I;}U>gxgÿ<$kÝ-CH2'me$u՝NI_1_?GĶ_ť<|$Bc7vװ|=פCMԴ}BG\KMDۗ[i%xaEY})rԭZhBo(?3U;wlge[ P4H:օGk r9 }2}wźd_ u{k&<.m]I)B$e(0?~/Jѵm7NҠՖRX ",D|#@eH7_4oh~Xn..2U֫ ’mf~tg,4 5۟}~ѵğ`O;v-ŎJ7J=a{Ft{?6{M"Xw*dsZvkpn`z3If.M@  1j׿8|ψbY>im˯E8K.D7lJ@WPMpI/PEijOcwi..a>cÖ@/mŭW01 K RA 8? dz"]Č) 9⟟ +_5\;r]^>vЈ cok"9oɝy2ps=5Y Fe\HPM;W?ךt_i- ӣ l \y7u ]^ ЬkK?HhUer𞙡xCNI :}Ci0$ďw$jտ_C/.uY&uHkn ܫR|@;Fj1{9cK'6dxeencE*Xd) >6xói5ڶ7<"MO3)-2v;3۲ܦ S qԥegV\[?ˬh֍y~,n2lూb/d.`hh<ֱZW!]kO7/+_x{J&CP'M;Tx ڮ3ƵST)eK8$q*犧[%/>9~jPx2TOSjT\ܢh5 <.Ì~ml6yE.H -O^.!mƻ,$rI*3Fc+?^oÚ/a$],G4l B̲Tc0MV<׃+_³K4-6tQ-ia i\'8N*K~/|9X l`zsV3BI+z~V>ekɡ x3Y4{ xf-gLm:MQ[ZAI ̤ w]׍uexum&`ݐH^W!Y'eu}eIo ]E=چn5v숶0C\o>o sm9Y[RՑ\`8G|tEW5-\SKXKtI\wEvw4_B[,4ib-?WWd/ϏH áxwsjZt!4د"nAv9p K^,| 0 5}W%=5-C!ê}F}A^.ϫi4Gi548Ոʪu8ıkPityV(tұ33-i?ּ+NiffY]|6f7Jz.X6rsDw_`҇f_i|;x>k3PKNfiG ]e$[xǃ5ixc஽hvNZMKeR1hB|Ǒ" >mK/f J}Rt9[pf$1Z6>:ީyoh+#ClJ'<9G^oOv>5'Zח t KkL'i꺧][Okj)DEKL?v>B}qx]GdP֢/|0)G!NHӚ<]|,|K~b6 jD,RFrII.Ҳ]U;J1ᮕe>FGծ[ l#6LE*1vl1/݊ –v^Լ[Uok.fF^XC) RǩLt˛n?RG Ti11+> GŗVskpL4:/y't!;w_JWxgLVcQ>0RkOU`K[]G ZXY:l:{B$S$vqFvV"7P]خƿ9=;XX_-Zy*jQ oaO{[{žm}6Xe@XRl`Mwe|wៃ|#_Z..o@<gzCIzn PA8B{Q}#_ w >3^pk\ۇUݍWi1g\_ׅ dICm*P\_*2Qkk_uoxwKTgU&=OW;Y+ZXWьv .{KOxxh.4b|E}"HiH5i~w>ՇռPioc%P7E Ư 7?Z,k^׼=j+[{vfMV*?|3 }S^ Y^&ZFM!|$2]G\8$Y"߰ ~7qo}r]\kpͼ\%UJ2mj)v(Ec7/ PhVV~$#a0W1 @q^GiZ~9xcXMԺ^p^t~nʈLmo*fV@#Ux{ǚgo3Zvdլ㺋pvHg[ [/~5/^1)'.5hO$Gۅ+xXymcQ/:-КⲼi@%r0QXt=+Iдm?MӴdAkiH:*@*5G^i|8OY6zsslb`\LFyyOQ^\AM<<_K=;馷R\*!I  }EoYQN~1'o,]E|lo,uA5rەn21ϰF7_ٷYgOq4El`ž6e<+t)[N^h_|߇(&6݃Wsٙ r!ʎp` Gg5~.*BkSOT w[E< AQqkԨm-(|{/և_k-7mI&#*A;B xTw e:nit, kvwwTp&pP[jSf-u%_Ξ)Dj$ap9Bl3(:xcWx4mfC|u6|'c⛟[x_F%Z|Ky(㆘.:º*,yMK>^] ZH|;&ŕĆݷm>S+nU cS; uV}Bo Z-*_ty~}2I1Fկ(믝Ģ/m'ǟrx.Z'?˿]-a,ˉ!AÒ^_!iK6x%Lobmb[eE<FAoM3|n%Vޣx5м+jO!6wR܈cFx*\BHZ ;Qo7M<|vgU"=DӼ;UۨHm,XbG@=[+/$+]݅QH((3pm3#O{ÿhtx/("jV3\PA4c2dȁ8WgtW'i^+o kA ekNKWiHbV2+[(TPߴ4 i~+pIu|iP߉_笌߅} y6㿋/+w5kheI. 03$E@[uh]o  :O;׊-$Au+0 'dq0ʎ H5oD$ψ`)An"Ҫ6p{`?~^2o}}c"{#jsܤ7im-m-ʫp>PeZ*Tͮ &Ohm൸F{xNAʀzkOګSӵofaMSx#Qm,H]`IOI6 Gď 藗~!_j:# Y6sH,"jFpu Whkzij&;}.[!H__K]Ϫ*+LH,XgKBzdž͋[^|GyaUr$-8ϵjs\m5&ìtrrzcWۗ83V?O^kL뺖[EҵK}wY4'Դ^ ec 0F8"_bBGskz6{ZG=Α f$R]??oK&MwUYonH h]"B8PI5m?zśRw7:xQS-وe䇙Ch3k9O-o{i?'tmY57F56? סו|+c蔠gQEPEPEPEPEPEPEPw%C&JH0y+eo.Z|Mx,{Jz80IHգJ8g}<4uQuj!.,7PB#jXۤaa*!y+ nXx_VkG,R͋!1c(UE&?_T.|;=jt,B Y40͸ V>V^ ɴ> j%OgVhTDI0d 5-6󫿁-玓rjkQkx,Zנ51rDFbH3|{ ) gG⟂MWy+G\,6@٘!&(Է$ױך(?/ Sʾ1JU@<3BlYAkm[@QFUF (((((((g51% - gie@88'*| uƅ<#>&-7.m@]6# '!?Y7,t2f4ۧg$! ŀPyM3ʫ&g Fj(H ʹ䎙/~4|q= ZMXxlE%})b2ddpy-U7 Ɨkv:'5mB$TKPP)ʪ5+?jz|]m%ԾMM"L?3`!ye<[ h$;FUKo` `9ּǾ&ž !m[-?/{6"ˉGp8_Ǎso\L?oր6h+湹5 1^X(y˟q]smux# :> 4DPƟȕ KHl:ω<Üc瓩1U&O i4 +Ku;9KI.~ΤI'(]/ޯ _sHC"QOJW/|A7[54K? b->̍o@"oIe 1eceV="O .iƦ"\g3 Y~G"2k?q%:> 4D:~? X{??|WO\n𥎏&%֕}ܺ*]lDB?*iִ֎\iB1MV7T05V[sګ f4mZ6F3er*@Vo';Xb((((WOX}[>ҵI8er(? LGymuY4[9-uY5H=ݲĐx?5EtTR@sIf}PCslmT FBQ֗x?S͢zJ{v:0Nyt0IR *1Ǡ(!  bgXxA+fh`cbK)Sǽa|wciNn|PC >T糚(g?] zEթ-Ǔ$$ld?)E#1t >iXxkE-$2H͎tIA(8> }si|d@YLPrbj+:7uBقZX-0p3Wү&;g8, hJ(H`VLWeYׇtEZWw"3௃EYvh$BQk#JuĖcE|l.Dwv.~ umJ' iȢR$RSS~'B֓ռ?%[*[vS%>v3]`o [Dݪۋ[2nCfY 沼/iOxH:t!gn)yWcx븚Q: f3Ku? mw_7Řf$"Pg,c9A|yq(tZ/ [MƘE{d"2a;+|55HR{2V,ø]H`5wcsu{ $(!Pc9 *F)-7z[S|I>n=+ONB]BIwdHh8*1p񷍼.yW3[xcH#~t?Ut$t?|(|ek,jڞ0H6 ghqھtn ~~C]/?xz1)2I ]x#H|燥ޑ43X}6;sml@ h{~w>lj~?-> |.aԮ=Y"X[ٰҐ 8ETQWW% w:wt']]J?&hkZw -Ȟ43#'_xf`mQEV&y;=xv$V`nm16׌2:L ~yC~maP6n h2Cq5ctd\eHb ys͡_ZHo/Ŵ %˪pK jo ޟ=70uoo9F5+H>ń:|j0J0ugz[g 4 n N 2Zt[>+ke ,JT;Eiz.I+]yC~maP6n4Wzw>\$SRN ׀|dc">45%qm,'f~|ϵv& E_aP6nxZ.uxlm6I)b233޼7yҭkw+ f|qd!oxz?)mkEC ( ( ()׉5񦅮x{ /u}=:۩I#-mb3O =^sọbI@b@y,G&ͣ`}ݏ;~3g_j5_hfcť5[,~k17Qw{rF?Q\Gx4ju LZ:CyL7`~p#:ndmOZEP\wd0OnbHo=B$($.FS K6@29::0dtu +A+O^ƨޡ˥I}-q ßS?瞧=O]ex_Zg-kF_>s<38W>\ ʰF陥n߿|?瞧=G*?=O7z8&⨼E-2+5דd"#>;7qV*?=O7z[+GLJ][yQڴA@̋1d;9S7 gk:6X9F*"A*6biԧI} D$'&e$4D',rs㏀jx&M5].\ϫ]Zg& RIUrn[RjkGtdw6>𾟠@YZpv*>h?I="aYIMQ%I]Xc*8ch$]/-R0X׶m{Os -⸎a %/'r"wϣ&[Q}V ;-›cnfxlqҽw #>оh>O2<ǖIeo$9gۻ$4ψ?|Q`R |BeՇVu893>| մO 궢xY.C*Yw9bA>WOJ`x__ t7%{u"x\UT~Pqqs z a⪯_ʏ-?0%G2LlLz"f`W(((((((((ޗj)y<8yU a!=}m28/-X(DA?[x3Ŗj:=3Fא˺ެw"XX1A=/a&W{ߌ~ ӼO|MìK$PK-%Uh UI0*:z6O۩yDE@Y1P@^A/o|@[+JmffԭdO-_MʏnHKOx?oiZVciWW:ůOaB1_€\ ě=COﭼSc5gw:B1מ)gL$fWK+6Co0k=wwoYwQ>_S"ǶmcPzoU*|/RMu-nAM]ֆ-. ii~*ywcs +nȒ}Ǫj<_Og>^&f p7?,w?xz5|m!I1 c Gg,|Z𾝡[V𵮋5t 4EE828 9xw_ĉu 1Io DO#mM@ԆZywdmix?eK+]e#bϛl`mmF9:V,[i^bB36x6 ^^/|)*TII tqCz❬n',a|Q{o 8he$E! aU!Kc$`WC_ZFiCKe-Ֆk{Z{r)BC mktMjmxoNҥլudK 8` T!k|ՃA<)j $;8KDKca=64QE!Q@Q@Q@Q@Eqk f9I?[pGs-LaNju.o#WmZ6խ٘0#WιGu.IIHƀ* Su.?)u|CP*u.?)u|CP*u.?)u|CP*u.?)u|CP*u.?)u|CP=Am a;xWQg\R:_?E_g\R:_?E_g\R:_?E_g\R:_?E_g\R:_?E_g\R:_?E_g\R:_?E_g\R:_?E_g\R:_?E_g\REc*`E0<^G;jw7xitW="[}u&.Ŭjj(!NS8"#V_G+ Э&~{KS}Bx$bܲ9Apx'ַuct@Dˆ!29z+ Э&V[E|?M`y_v6.Bxw 600 1000 0 C     C   s" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?KD{UE O]BHg;}2cw8Mh3E*nhl"Vmcȓ$8kϵ MڴCbہ$My *X%/5H: Do;UG=k%}=J ]MM1QZp骪\s]TJRIY/k"V OVʊIOjQ^+{Ư֜pA8Z^5pGW-!+aץ>;D '55mN,-'zZkƥQԚU,AGrfS@Jw(`ZZ}OqQ2$;Gv?ZV4JWb 8N>0<*ѢK`Tu?WBkmtinvl,x<&uf?*O`*WqT$z}z4{ZgSKC:Y@@>J.vJ|TA;qnҨ Qʓӡ8ip>аFKܶ.1U: |Hf5YyGbO 8*PU[/R[;|V+ v9u˴jTw0R< LB+c"%?/P:WSkO]’wsB+/3WT9$(Ҝ*w"tBu۷Z{D٣ X'$H#WTccnCXTNp挮kR sֽl5:#㺍ŵڮ «{ۊoq> [Ҽ< IAw_eԧ D[]W+w-f'vwu܏{\ ƾU=kS*+߭>H.3 Y"P~KgR݈5p=ܓ[2ƫIַLXne2+ڪD [gNlZy]V2犫5'[2Vl4\ZAZHjH+>yUiFHګʧqS.zUY_#YyW'jVeREBlpsPd@4ƓN\R77E{BCHzܜgH.!e1xpF~tx#R4EqZkm0wM e,rٝrçGr呯&!>ڮiimV9U728. ++愌DQX0`955ƌ:?Kg1HR~fZ}Ƒ%"ޭ, ]R`?#AgL|ѦXtb)t%vZ='p єԈvT s&rԜӳV!=b^`h~Wojč,ɲ[SI;?PM$؀qJQ'ía2$|Yg[bڄl_A?NrDEx^Si?CT΋Dbxu3\xm'bNN^vK+OZܖkbIjyFٵsYװ;[bWtRAգ4 &S\oژُ=1\vuQrzF̍٭W8jDH"Ua <E$vMvM1Kɉ8g ccm&}-RYNl,y?08iS˕;}I)ZkglUR4j8>bPea\<PH%h uڍX׶^Cyo K$l(뎸o|2ɳ˕Y-b@ ǧO 7 r0Vu=JHMSVH*2':1 jgu SQiNtZ,+x2 `bH|)[Mf.m_"l#u<6!Z6-FF} /+J02C#_\X9>X= rEsMjpŌmST^'P'E:KIጻ5s]m֕e72[']]0,D+7eFt*PȺt)BikxWX #! 1䑀:B|OM4Zm. GzVΑX_Ckwn)l!h@Nҹ_ٍY x dd9V[;W CV~VrҺߥ_GrקҶ,4jiK0_@pN@+&ef 2yc>_y4g~;4x?#kF6Ͻt-_8y'2Kp*6LFd5=ZoG@ Zfh+9犀(=)R(ՆTK3ҩhAGpn+b}]=rFo=M%p3Y9qӧZ.? -XI-(b9Bf+ŝl/EPucO:׵ /J?Rd/ĩǽZ{ cӥh^ٶ6M&? Zor-KңR$qQr;,ʌ4ߖ}NJƶY` cVufAWlI >DwN:0'H ոJ#I4,d60BH,\j^[}k%f5s*X8U[ +њ+9٢+K <ڪXn;EK :y$.DBG=2ݯ9cf_yذ ̏|ѳƧk2F̠*pRNb<CڷxmF[KSE ܶ4m!CHN=*xo%ir`-EU`OpG4-b$Duk! zmT-#֤zy v?2b @ϸEgy~u赀cX#>a= vlWOiE{һGAC14VٻA}kJ`Y,6א95|imGOW9k}{=FeAsx cp\x\N:R4 $#=zxx_SH-~zʏ\#o.͓'$\('~j-<1#8suеN1mi/ެeWѡWdϕ+gbb  qM4OAZ#6|FӭO zғO3ia;ZwEϽRg8킎EQq\V|x5BIʱɮK'w݁BxeCqb -XcPEĒ*8Va6Kt~sFǽVu9%RbfC!MW+Z`TI+dC3 \ZH$ NI&=j&9KeIcwZ( ^hOjShF[Lx:\F|9LX {֋B 桒<jWbɧ_ҀXj9j-DGn* 56'HǵP|9'!@#DPc;BMJK(5y0nlE_8}J2 d,F *E]U(e?vH ˜w GVp;҇^8DjvJQHǐGlB}Eb*Mx3)냌u:+`r3w.IY+Ԇ}*:+!6rHNިjkr5̡,;?ϵ53CWQ,`0 OP s~>! ֝Hwr]ۜXjIge2ɌrftyBj+duA~ІU I@;WD:DV5fj~^1lP.` m=jDxIcݺUjQڋ }r茩ϖb$5̛H6v8YV% xr:Ү8P O;,f`AۊdGs=- M5FJzEqg*1;wdžL1@},c=YO?kBV q ;7{G)ɸY渖bvwcXl/.x2 c'ۊoSdNg9v]PHb>ݑ։HƧmXD*Ѿ]GbOZkM&Itq]:( O\ҷ/>%X֬ybp0 #֢͉F$ϵSc^~r'yԇ@׎]`HTf:gWW=݋[^ raV(AJIv3OCl!-PX-18@H(; ZԯKrid.LמlqҲobʰG`vqPm^.HJOxeg Gy[v%-_u='axEj"on>+Ek2oVeWv:ԮhvӯZ }×M*J0G9ۜv|uB2.[o޾uxoXmBݨxeNJkBq- Q!A7_s酁ČvQg6Tx6ۅiQ&섂 ǒeIj@^}kAط"q3C$VheVvlF o?5grr6 sYFE8oq8 = Pcjԉh.,rT뤶HkE+sknzV%$UddMO$)m$@eUiR܆rkmȪR`/] lփ9[܃ZZJxpN c(m}jkPݱGTv拊iNknHکMp)d^*ΌIr@xqsPIZ'qؠ$xrzP AL LjJo19c F8m)'#;SL<-+k`;Tm: ?84 , 3 Gdm8UOI^ɇ $pr9lIBD814^fgs1Eiwf)AaЊ<] [TV5P.L H@Up*A$*PFQ,P0@Hȫ298>< yeJ$gev+eSRT[ @=yq 9=$z-qclSBې4Xtn$udZvc@Ujnai ~Wiv=N~h *ެrFg9V6ppi$+yf7pyҩYE_6L W 1|d䯿ZXw͍{gT7Xn(r9#9VDX&=rj;ùc^;au -~Ҙ/~&XZj:erՄ[ц3ЎSl yF +ק{_7r>9 YԠw`#Uj;63dWҺ :>a ܼSN}C%-3[w(^P7U\J/fȣք|6U F29c025l A'0P*WbF9[/1>LnyjguvQ) Z'`i-,0yEGC;PpjwL=MJGO^y[jjA*D +*RER'zwԏI}-cV6E{5=&.X>+!~64ӭn2{SՃҹéQ?s\:ӅāH֊7̖!=ԙP Vvpk||Ïnֽǰyus H ȍBaр(|JMWx37~Gv2;s=X7 ,4rHb$pzcK&o (s\ZWb;NQqOݘfݒ:ᮇ$.95@ w=ZᙢqsVEƠTzIn]_A֦Z/ m8.E1$e)0o2W8%i QC}[tX'3 q֑ I2evg(ɻOYKmÌ J|@ۼga ˑf'A1 e͋mo,ދC1 g'" hgiXˋm?ýXq& `8VXݞ{}*tr rM'Gξ==z+MJSkUl"ژ,ҏ1@sgp:>(Hq5,>wWЎ SѾd^.@'Y]m#r:u=iA),6c˱?RE*N]P L^2=T_XRw}Gn*׊4[hWYfv&5t$01˾+&y@yUPm*~i:-k \*9RZYם$G)]$g5FxK=Iτ 29ǧY[kמTǶ< 1:f?e.,K,2z?f7\[{lmi4nU^YIv<ڞ!'$Ç ըPNd-⊗J7hZ~ibԐqnx<Ȣb3@ϯZ8J=IR4TEXզ$nJ4C1c[W:bzU`۾c׎`@4=kE7NrFk1Ѽf:vhj+rQɭd1+V̵cX=IQW[zXfڹwex\bg@e(i|32idxo.-l3r~kuOҵ#V/ګ4SmW3zzcVJҮdFhaySRH+w◉|G=䚤1R8`=jb'r)I}zzG ^O(tcPk22_r9rGq"*[*y9ӽ?G5JTizL0QVg\Ƶ*ڢ#e,C jddc銐 ym¨QjZԓM5N7Fv1ǵ^T;W-Zێ9*~7n#εT}ϵ2F9?2@5;QJ+JƵ10 cR fab"T~?D] I kFUqO"NIS̪JH䊴!ld>T>9VF=B ]ipU~ɪBhC2ޘhY$ܫfPH>1Kk;]8Z6*ѐxE<ݷn:2v9ǥml`g-Ŕ3K<~|wd+%;k|3\`5kjsSMMTS+{] < w҆@EBx \ &1TށWA$-^*"ULLΙrū.0KVԱvk6 *!m)H lc$s~Em:) lngVxxIb#*/0GZs܂5HT^,FvRA/ָkViIeDZ($(*8 s[)J6zR،PAfueO?C] _CQ[WӓXyqq|ki3HGu'sk4W@6+~|fòZKQnA?&⵱'K= Gm aPJWMX7I{{s#"ƍ;ZҺr|s]{hq=YJ(Zt-b d;[d [Yȧ fF VymtU.n2 G&|#똞9^˓Ǿ5$޽] Xnܹ$Me958Ok9_[9&3jKcJ$𬦺\ԪÚVLsY[,C;so_5`4CMvꎕ4TG\˸{VP8j_1Ib^6֕ Rm;UzO.$iaűoj7w %ۡc%F*2}I6wRO.W:sxBn?f/MonnGo9#aQ;Gիo4ŸGۓ"C8ǧc_rxscWJ$}@*acK36x?hվ:[T)m#.^Y4Q^D P>,8WōSS~38*F]N9>hj)S^? jZN9c]s?eu\7WTP2qوVo\nxc,|-Cڤz%Ҭ &KM9u #^ψ/ .aǮ?5wVN.WJt=#Ώӕ7&`~nG9R Mϱn",*ȵ˚qմu9'pGAJ?x#]D 3X+zw.>Vs!PD?Ƨ2zXC ڢ(qJT׺'xMg{M`q99!m$ou8y/KyXevƬ[<@htٷ=瞕|3KSv(Ӡ2cd,׆6*aŒ䓀qjE+G$n77| & }-B-I2`ʳƼEкNju ̚ϩO[tfhF9p?<1ٯoI0H =o)M,7K t~\Rn8YyGx d{ G¹ ~l`Uj*1 mcC9lcW]ʹ> ݨ7q;+ČqVA$?˥sM]T{RgQ]sDfVR@qQIp6Rg5W'!?F5OԜBߞ.ih퓊1_yny$:~S/?+> _\_h\J2 F#ӭcU~4Ws}]ZGV?w@^?S%稤TzOlXMK;$n>?[lEgkmwx&b8މKV@eF&OXRj 3\F?iITe-Ę5?u]:rd$xq^Goد0>>prqYT3UMjTE_"L:;SR氵ŲZl_9Qzv?/UH龜dGUh1ENZ1 qxZوD #[ףߥᐯ^^a[Y_$?T6TCa="#cҷ嶂!Y4js -r~è#SyG`*c8֥Nrp1_DxZme@+.3<Ђ>g_BiAmc`m՛[TqǑG\sޛOh&̹O]u|<*WxYmt9>hFF?;kdBĂ)gˀ@#xZ%y=Ii^=3OTYB&^ L`AZږT4`s0V=B2Ua9qRͧ@cV`*5`Z>ChUUO͎lk]ը-ťs3|jq' Uv\̾Ty$t$v02 YW F*7m- D#*s YM#Fр,;:QN2F3?J9@d%W j8JBJҍGJ}A6[T2ik3:M{ w*8*MrSGKbg1{⺿%V7llY;'<Ϯ+эhFMjxc/m+= DeB{WzZI\ >AgK}R X@<׹Ηh]uayR7X֕* ҫ77/R8pIӞdk*Pu(ʗPFO^ɝ(F<\8}Bv&vѱ cٮյHA}*VTUpH\ pֽ>%~V;rbqgst=3ulqR:-n Q4Rr75s5~B xaf)}jU<}05`9bf[t?OdĤ{lE1@A(5mr8gѶJKןjZ?uI.[$Vnh/4aK`7iZƗg9ק Tozay+$:/I$wpIb% 6X`zu i:sZKgI <[;ֱ/>l'WusnKCmcډ+^E5YYu)(,zϵ}3)K6}11Ē?qc.(Zu^ܜw⽓zд ?4eI{In qB}k4S9}/ʼnFTa?ͿOh$yFvɩAl5ԕ#]gt >kaYi&+ƤGۏƨEv0sץ:pQrt YA c1nPIp=:_,[&}_#@MZ{U;+ ڽ{Y-Bңb%` ֤? c@PESq5z\ /Zb+~8~c5{/n5HZA!23о ZG.]759ofwG+~|Zܝ89&fF^[ȧG\/ڣ0kH(G|Wle@@O5(k, Gz?1u]ZߴZ|fٯZe$^C`~%10o2n ٩Nk?K?dV8`֡ij%[V;2gv7½_02$Ҽc|K>e璧Pi̦}q6iCWA5( tf͍ oi>yeG$}Jekym-?1U9w:%Z32X8O?-EbGwXa +^QÑƄgbŰXG2-t$km~,I/MI&Q5kwk;eP_1szՖ@YΊ{34{p[ܯ%:  @q*ںyOаּ]b)=h%ϔ i~;N81:C.2?kVݜX}ދEm*}O&V zUl¤#J҂(n[RM f;s<}TxޥZ'f2@X;=Dgk7RSv 璻5:ğZ+6+Hyvvcs0Gyubg?Ԑk8EdqW.X#rWbR՞P&" Clz1Zr䑷> WF3<~¤  hZ^?|n>)z3ƮĨ,+ ocT دZdӚrxnA?μl,s^U ׍tVEiE q׈o=Jla>ңL WcÓᗈqguɐ\{$ڼ38C)mm8H &0~~U~ %naSֆ}.ɜƪL 6q#>zM OGnId䏠㞹iiC}0caɴlt$+EÕghRTpq^kq{:3NKKy]q$H)sq Cۂٯ,Zq!@>׸~̾wWFc ='>z֪HtdIyOlt9Mxb'rMgќFBs֠[ (<~#!5#ږ_J!r£k{1Rp0)vgMEsxE z&S֥jBhy^&h؀Mec`9*wpɒ4؎3k$O pm~RYA רZ`\W1@k[mˏ 9|HឞsA#>eN)6XrJVXdӳ::>&uK})ҍCk} hqN%cbjULr~?x/@',^GN|]Kh W>R H&ơ=vN?Jc;XVKa@2?Ag(JMR1 R})>pOEw{]0 } 5Jd毑]WlC} } 45r,ï WV.LkRJď_lHKм$pjjO0kͥTwi%~ ?&sP;דh$'5C5e22+kQ{cqX #>.ң-ťk#;9&,9l}[/xWG(~io.1/W~R=eyxZl^502KWxG\c7ŁfiSHI9lr~\z}g>?"J}0H#^S}1fo̱\*#TExf #; Hl,ve#}#ȫ$bnBlr{qV4g4UBV'N@5ִ97*UbhE@ 8yŋ "O PFvօW>\I,?N?* cl|[VLoV.\n|ʝxW_+K#瞝1[zO{<;]&[KQfash<6;{cut:j"ovsR\BOEnAujZ\VG8u`AZ?^!DCiph-$∑!H=>W獬hEY˒{7%Щt= n ϭso6rEb6:v3S+&ﺧ30N+63csx3D`VGl ӰcЊȨ 2~U=%Ƈ1'IΟ{tP2?EWALz=/'V {Yeƒ$cUzSN^OzG6V6# &B я ֺ30 /ι+(s,Hyc#8zeql::}Ah^H= Y2Hp9''',c *˲@{fVdQr͸B18ε2|ͅ{կZOx'W`~XK 9M|ۣI,['=OҾ)M*Ts *dbAC{p8ˉ`Yd3ye,qӵs:'Q"[lA[V%;ԁ_bc}FmB$jlY7 $v__eu*6Q230>iַ&l~hxIuFi]J^G99saZJr0[v+^0Fw7cW9v608z>%`xpy־jFIGb;s |c1\NU=M}? j/KO\:Zev'i,޴㹲[>N4܃1ELmm]㍤*En6(0tҮ 1G|@֤|q ,`*jseLwt2[vtZ|$Sܪ*Ҽ_Zl/+Hґ$JGx&,w=8#Ҩ]ũi;[`r/,(\0W63$]]Y60psUt.kytl㓨k鶟!dj~2d6TF\+.XI< FVGRҤ啜9l!~]oyhhǖ˗+th.y Đ?Z'dВT[]r35wZ 5꺂KkV{5wrw[˞|>+AM[o "ٮ&dy=XLuȭQA7r ALeIU rfdvmSK,^(tMYh iym5`-w%u N4 Ei5ƿhr }[>h'Yf#4 Gܸ1_ֳt?Ū=鬸 b@)n)Dp=) j'0&-8q|׭[Mwm$y$ H93־k?[w\<ϥ^d?_PtO$Vzݎ~Pr+<Q$in[qböpCk;=#rDv7$XT : 0|_>4h,r]i䛠o{ OPxfL+:IH #/9^:o^"]hmT[+#ަ|q}qMX!p+2Iڧ#+;=?&-sœdNu'һtYHfX%vdh7F3 zW6% F;+[yszz [L_e[w(8r*H#JQMEZǥ|Lzz[&Y 8vX\`%c_)5Y,sU&.n$}a@ /|9Iisap0ICJ&KMMc]şFmRWq) @ kߴ hxgYۗƬzq_xY/u;Tށq2/K־Kmqkwao%V0y029Z46p#&7Wz !rqO:<8K?6Ug*f7ށF{̟3~lQۉWp=coLSZM<֮dިR1_ZM|[èv$+rTlˇúv,VbQM8[378XڕtecmSRKƻI_H'M{u^Hv=kM2ado8U]El.'\lWOMGB  ͆C|-zG<5 ErƃF?pZ{xFjn\+WQ\| Wghf\[VxfcרV(pC5 Q]Y*X+]ۖ= 06E(߲.:ΣM.Lqy>Y {œd敐\j^Y7.ɀz)_`XڜcxG?t#ϸ_FC5 ȣ0/ .zS>S)oh6w+7ʍ-8)V; j#䳝H*Q7qѣ$q).5KxΠ&@"3i`ONj&]'KfӘCC}NdWq=jZ(mZ|FpF_Vݶ12e<{C"Y7񷟕n@ @kJ8dn$v,O34qi=$7VL63޲c =3f~Bc<*[w6 HkXHRq-۴1,eG8,{b^l$% /'Fm<`(B>yRm=IgYB'7TRbk=WÑ_Z˾K{n,i4M"l_@L`9/,d'ag졀q@&{}Zi10 sQ^Nz#i`TX#.޿Zm[XbZ#Fl1ǹvsUa*LDO$Y(G@qT;E \l`# !|vn@u2\{WDjN{ɗ!n'AK k"&wvo|Imj[k93IW?ox=J5Qw5gޤ\xRݫ1fxsܶH\U/8z4y,\2ޢiU`{O/4b=j DUI"e.pwדM{̱aO'ys˂rp$Vz͵j}Sî,BeRy=>j7s1W®.<%m3+Py=$Y5HS)I; k3 C%ǎ|3-Ėe8dmtz?ZWI>{skk%ZY(FKN2sA>m3.u]yV%C<*x`2:v9h늧e-.]6uH8Utcj!piԤ%|R8ׁLk{;+mG;B}O} ƏbHȒn89NI]YYVO4'RK V!9_ cKc"R.rjZư^,oLf>Whi?ޢ~tVvfTSBiJ[;UEpF~3k3ȈXz/) ErpX+[͋LMc9\Unei;ҥظ-PjK/TX7xs&eGV5J|8-ecCF&1*_YC RjTm0YV$;]o@|@<;`p|zC)+HWB ?i=s.p9래_/ I]1x8o6L}xEf N5SR{ ñ#q«7&${2蚝 Sje##nI-T{2%U՗Ey{mi!q +Nϵz|LX#1" \ggQP/$9*#i+Y t1}O*\ՏZWdVSi$(P^@)N,bv:?PUflVJsI)iZݹ;n'ocMԦWyiqk3U up8~Y(ZԿgWJQ8 ƶtM~P5,&;]Yw}r+26R/@'=:sLc^ VU䁎hV­݇&GԱ]}B-VRrwf^$?idiV̲P'#= 5pYHe7A ,Wǀ۔+@;qF}M}{\DŽH)a]ٯ2m[3]׉B-rň, lvz3/[,l $rG{uck&"DFN[?μ3Tlg9~u:zf%af=I9]_]ܱH S"w9+'EkƆmѩ}zhKg-<뢏!1ԊX$A `1<J)II0ѣӒ|zznڵfCNk#M֞k !o&xגc*]tṡʫ\F#8\(||YN\2u?*7ZeeiS {kۭ'¼W HLx HLVOyk؀ ^-ʏ>w><9rRII !o\K跋m"& L0BF+g͑$.0<|Jέlf-6>Эo|ѧU>8m_'PӧN޴o.XaMҪɴ7Ǩl_?u=m) 1`‚`@ u\G#ֲ9.wQ :}Mei:i-GǾ6ʻgnM36W\4j1tⵊNDXLC iJ/ۏzct /dgsDcp:R @??a !Nd|ų袖F̷"*j_@ &Hv⥣K\ptU1|HVsi)[ \ze=支T. /GZMPtu4Vf"`qgS^@4*=OZukYWdavKsx@; U[MЖid\SL[)2o@x?ֶΖ0mIO\\*N'S֒W/ nX>kVw )WIw>qd9#4=gÖ4  gkѼ4DZ~1$X4QxDC~r8`r}xFl[iVM~LV,dv'';XM.DXuAYxr[ _h 89#ێZ$֣iyި'7rX ":⫝Jfx xS2 _ KAi+pcSc>[pJ&C78@^'}EOersѯc~q\{FGbIaXDIkq^guijv̍]ڂN;/$;ۮΈ$ruM@?Z򅒥k#HϿ^`+jw iщ>U[Ԁq5#G?bXM3;:ia[C6{qM14wk{m2'=OZƹ46fRJIH*^Yk(F?λd׼em.7*ьG'u Yyt %3-.Uy8R5ҮdeA$Fbi2HW,;p]ZcU]%W{YTglj[aǦyֲcBOpsVЅ݉N@J̃svJr`\n\ڳodӱ=$P]<ӧzҹotHHܸdsV-Cd Fryǧ֫-l2!tloBqRCkYzI(r"? =)ho,%~j[6v٘xE +$I}N9W}]Q"@܌|LU%T2 sһ ;"^$đt`; We3IX 12q{yiIy`O+gלkzM,yramקeYP~M's??085c(e$4McNHN^4$[i˫S(֬ hKAsxPvϚ|!sڌ)$gRB湞RNб;וi~.-) *xYv6 Fq=Jds2=%ŬE)lTb^ݻFK#,(>Nh>.̟k[C?JW %vicv k(yFOT+X g=ps:W cq`wʰa"*OWSgQ1¯tCgۍ)8kpH; VnOLm'&&ad :{{TWY.0js(h| ϏªNq?ALBw`7^y /~)BB"{12;WZJ!`ẽGjJP(0wl_hTTiIs376rASXH*In*1:t%<~n$v=ʭk㦹W %rsmi8sH")B9 m[1T5ntYи °Nc7Yqr;½XMpjθS'or}qPK{_JMyOγq-EPQcKP MEoo#[A堓k/][ V1αO= rsp#wۉnnHRUW:py6OPXk,?$~RQhY8״ [N&Wh-#Ddgbǿ&,q= E$o :qyM]>8*H$z]֟{){  .vA=;:w5ع3ܿ 7SpiYx9#9#j$QAM*9=+us,(W.LA9b_<*;=}2KskIF9Qֲ|#Z$ģeb9t+ĥe/]*T0 ^1׿MX]|y#}=+hOi .,8`˗ApXd`GP1滩դ.\~S'mh#ZFn;34ܕ$0ש8.' F\q=+] o$`׏ZO$D #=O$`5Z5ݤHl<[޹˟_\ܶv=6#kST08!Fy˽6ʽڔ>LjN-m0:tfT/KLtd{eWSN}몞5EYP{S͕ǨBu'{ڹ{wxmCq$Dw60 ]DZou QJ<nRH;3 hU@a3oU'^c8SC{j1i*3n7s=;g"1 R)_"}B]3]G|;sqEhUY閤,A:ΐ?QL\/ַG3 \ߌg:jhR`xl^xՏ$qG71ܴvPdɎ:O;Oh5aq+Kp3وU@*iy 2Kvl}AR?緼6$+ 9sޯX# >Uѓ4i]W=+Ҽ}eYB?% ,쭹?}89exVXd~G#'޴S%79}hSDJ_C[=s(i0b11Vm5ɤhTas'<$.ЀvDqxk)}B0 UҺ=bm'rX'VTUr2qpzkI mS6BA=OqO?,̈́\iӬ|=0s5QĪ2|Rk\9e'<`LVEV79ϦpZ'аFy1 ?uMGmPAnp2=GI.r֌&lXb7W~7xbwh:<6Aظx""=1=K `D&ɍ?J+4UGt.k{6Lx_K~2Y'߭y=.bƠ8WԱ_rܶ~A$jE٨5?1Lkܞ6u%aяNi#sTNڲm$fctc>KC6;Uw iC['=~l(,yG1Wry<'=(&\|#c?dnGkgH9= t9Z goA^1دUV>,$ex'!4@cO5ft@5|C)|]^H jHp 5SɃMj؎ĝLLqp]FGײ_Jd-׎+bぎ9Gly>T_@zW=Fk9,w" g~;j; |h:dQnmU0$p|wf]#S1>@ &{p2|<3ko _"`IqkCΖ&lqJJU:G֚:r]iJ< cBe1J)89O5N sFe3XerעΫ)zd7r!*as5XxS&PPz{TsUčK+s Hx&q:Se 4x@EtivmPAB2HxVp0y'׎=iwg;!?łH1T W猫| A޿=NG7$a0@$(, o 2F=9댚ڜ'{hCЫ% fY:g!e%p[w#]TRyMBON,!U6n=99wCSE'?(E"MeFRaGSktM9@1:׼ RCnȉne*# 9#c'DrOG /wm"0 x/Oc]Z[ouE(2 Ap;KOEY%pc O")WesGxG6%7028ߝrqA-}I$ q`Cws,O37|}1 u-ͤw]p~Sg9]Q/]N^+eb#_Y-2&LprqCb:P.SG9E2͐?Nu-6

qw:j<I|0mo>o ϮD݃ f*WLrq\^ɦ>c}'Sa!.OiT`0s=gҮlʴ'~dKVny!z29# s-WCxrY/aVcFV%FpVgn-o! ;31# =3OcsgH8ӿ4h]\YFD': N8'f5+u{q.khR4Ў70'-q`L]6ܼdQђ ,6K#%P==y=?*O>Hu{lP3 ~rp9>at 4n ǡx5+YE|_Zj0X䍷|!1=0=~+/![!7G> |?LOBߚ n!y kU'+k̟l'\Ѝ&M&|F;H҅h=R #;F}M^3)1`cڑ+ VGdzJN~oΐH"ⲃ {+隵$8RI$w@feޘClf6˻uq?-)aX41fإr~|?d$g_jS=>Zvfz7%JqV׈\-d>Q+QC6R2D䚕G"yf-#c[5X\8˻L"c f&@HiA &枳I 46sfotoxx-20.08/images/custom-kernel.jpg000066400000000000000000000644621362435004500176270ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 315 345 0 C     C   7" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽӾiDSE<HIBd@FQӑxe~}G+:Wk[G<#M#ŚX4&θ NkKGռ5Ii>!̚ r\  3J9v"8/DEl>|Soo _u}.M[W3ؾ RÿgJRx _MVΛ_kK/.m|\{!|#"]g]XFӮmͤg^ct|\x87 hZ]gTJF-kE%ų7^0#);/5sl>|?W E,ޑv<0m"ڌgd7*GB.r[9f_Wgk~)'լe|hfV ǵW#)r?qE%1l>|?Zז#mrZAwnk/f*yjx':|{n \F*H#n 9$2y㻭/q ]`4[2(aOcޏ{ӰDQ Wjl}'?DV?ڽ+ȐHiળ$}&XhjΝj`#f&a6W_#C^`R; [5xK=^Ti?Aۍd+0^]}Kdob=Ku5ޛp27[0'ֿ%þQӥԥ{ۋE2#)U:.*֧$g_5-VTM: oJdqyYl7iilqEvU$SK4+U4+>ѹD!ETݱ[na}^B4H"yD(P;c |P-c0TԪ_%u<|q5VNFp}TL;;c$C^g(>'FKx\hIowÿ|U6G; >*_G.~|_?0mwÿ|U6E\I'ֿAaW=?0m0GO~ø z?aW=as'֏Zq;_kø z,Oi> wC&q;_kYϟZS>|PCVV]x0"q;_kø zkahύo~?xQ>)I'֝ZEim'$~ZDrH9K@x}quufҬ^1j\90yxܪ~xq_dø z?aW=|m~?tsU-دaW=?0m [m. Zi2G+HXpǓvO^mz5=2JZ+]Xum ǵpH+wÿ|U6G; >*_G`G _1xn1*ʶ6X0 w99[>#,mng @jҨ$&*R@_sø z?aW=ϋS7Ӭu}-.lf_!"WvƹxXqjDR"#>сq=_xø z?aW=.PGO~ø z?aW=;0GO~ø z?aW=as'֛$Xa+#M#wC& Yx#tT`Jo=[7Lq)>; >*_GM#f>ՖZYè+)ssuƯA5o8s*g vfq;_kø z,~E׆o,"8Yt8>kKmO=ݢYH{}wC&q;_k𸚪X^_=}{A xl=[A'kfl1g.K37V$E~ø=DxWwgՄU5Ӗ_ڛnqgx_7? 閐>h'Rw+%l KD8!|qWvKvW@29/3|@Ei=f:N[xI.0(<,IO|J<1o:U#_-Ŀ CH'x)j6KuOUa7<G\{˖"kW',f[]WB;hTd8S-nh w$0eao^.Xo᛽fM"F~Li2QG#''x GK:iUcS,yEդc@z a o\Ʃaaqc<9ڹƯ{c"I+&V[+/?x^Y?w0mv;>dF!%m?)z>xkW:]ز6LvI8ﰞ3/:z?Yߛ/GVo)!N,d[diɆgQ&2G')o*C8ҩanv{ᗆ_ (|9#K B-Y$eV_ ѿ~/.F %Fc߇+;pۧe}ۗ?xVsSK/KOK6D[hٚ8i3k[/?hbL60'o.j5~63nzċnwm*IiM:A:~wmP$J|W!T`f /?hb=RFF37c(? `zsKo8uK*&|jd`9k]֭Ziv|Q淋 [tG,5ݱߖ j/?hb9x{'zk^X[_˪ĭ$! Fl|Nq]ݿ?[0Pġ4 wJt7/?hbf- s:x{u4o1G_ ѿ*g'FTZjI/-XP /Yj˦ivsk?̗3lGqcV?_@]3(W/?hbLEUt7/?hb-QYmbUt[Qs׆ll( ƸϘ1iuM8EIJ\AmlyeH- x=)EUt7/?hb~/.Feg P*_ ѿ~/.Fj'T1 e6$d88OjjkPKm.K %x6tEUt7/?hb~/.Feg P*_ ѿS[&WKFKk敕2˒%שER|?4 |7u}BJ-5V[ٖaAqwb@&S++KѤ>GPB4[⿊cJ_rd{RST>iĿ7w^7.9xrxR[+dKX6X4I3HC?*uOwMy?_-NMk<*a bn-/mmAES~/OW C?|a LQE xo:ew{vo_(|M@ ԫ#q՛"|%Qu4Z\5,_+YW>$]y?TХ%4VHcRp02zR_O5?@-R`Ek]O^$WW֞cQ\Chsω?笿Wj]?/^G}М~BվxR~9$֗uraDְEf/ӎޟ0z+g¹' O(sO/ڟWEW>$]|I=e»S z(?o?kFI5\EvL03y7U$ЄOo-1^5ky.*F n(+w?/t_W>$]|I=e»S ygÏޫ3\7ڍtַ2mmfL<{UbTBkkg5]SjZjzS=vRG1dis~$7!\zv¹' O+yG+YW>$]zX)U =nh{?ۘs@ukP}9Z!:ז[\߭OOO_U-u>_ 9_W>$]|I=e»S xxg.?<}O[(ڬ}d6PS_։MN/yG+YW>$]z¹' O+7Pu ˪+5/o<>pF+z5PiETUWTB-oj%Ӯo4> RT,J]"劖8Qh;Oơwݼ25b1ʒ b:rP<$@r/Ut?WkiW®3=& ]Λ@\Wk&тWkj_o^o.-nx9ШN;FO L43* Ey_供GI|uvXϕ#n:6ppíO ]ΛI-^!MwZXBRH$]ey*w:om }ɴnz} }ɴ®3=&eZ \^Nrѯ [W+ [,H!;c5;Fy9(xB)H g]],|2qh^$2C2$}!2rIx?S&\Y]XVHo&#c\z( }ɴӶEy*w:om }ɴW®3=& ]Λ@^5B[ <]  ]GO-ŕmpRH#WkE;p' ǎ5H潳L̈s!YIX0 r)C-īkZ[KB#,:: #/,maV~uI'd\4,Hp Wkh]gM{M^a ]ΛG*w:omz} }ɴ®3=&>LZ<!o <*`#F%&u,3H$/K2Y]y>Z1+i2qz#Gt'ǰk^"muxZXO%B"d!BXp1 }ɴ®3=&ӻOvA76Ut~(4AyY_ }ɵx[Qol5uQ]+d}dse@=QR\G#xIkSXnN=ޓޑg74F;>WC$ʲ=A=d*-)&A~|c-M{XOZ gUא-ڙI0w/m}G^e{lZҬo>l,p6UI',Ws[kZE4QE%q^)F kOv0R`c^O |g-þ5cKF5] 7g՛ q [Ğ2xi/z}GLﯵVӠGCkYͼ|Y9Vؽʕ85 7E'd_d~>,s5;ֵ.t܇-p˔I[n׮V?% šGKag6Z[OjFO'9mJM('Š(,lkȁZ襯\S y<3`_ϻ 5_`.NZҼI⻯K][,)orIzQŸ(_6F~]y|Aa/Fih|Gxn/$յ;Kt0$vS !$GLRי?_k_E@QEq^5B[ <]  ]t~5B[ <]  ]K[>>l<9h_j _e%AX].eB5,چ{k@_ \mM$$Y7>JKziWO4լ 1V*A Vÿ7XoAXC#Y$Y$n]ݙKiKZk6%QL()'*G[~/ m /Aߋ@@|#<+]}|M|G4{>Ud]ݴvjXDr) K~߯$f/=M'SZF: y)8vYIqҚ_oY-4AES~/OW C?|a LQE xcvK ^L]F62pKfrrBdܚu_ GYӬGug,c-qvw  R|rTW3H?Bt_xo Ƽ4~XJI Ğ !KI,R[J@o7' W~?k2?yw(]z.@ =רSb. ;OC5 -<kKM.-jć;JzS#]y7M[F-a0[H lՏP/7?OŸi3xBIxo^3Uſ-F2Fh!#Gmw<9r?oZ7SBt_xo ƳcsQ'PnF^I8Qdby%Cy9cY8"2抗s˿Bt_xo ƽFycU;y?/C4]hx?Bϰ_* P:^/? xg)i0ᮝ]GY7+{)`!$WBlF[M+œd^;y$ξT;3 zǩs M.Yu#n$`0(ޟk}% Ewh ;OC54S] Ewh ;OC54Q`<| cX- -%cEO 9ŸDI5;;V4DIsdP+:zPW9_M)?(]z.@ =7C~}SÑ\G rn~z!KDi۪54+~7 z[Bt_xo ƽF,…'?BzX(ZC6qnB!AnqMl^@c_DJRN=S˿Bt_xo ƽF,…'>5sii%ڙ-4xA[2 e~/O(o>?/1ͨ zZW\jsx͟QCoibQ;\f?* ē{lZqk}U/ @`Jk^{lY-*0I;Cӿ/Vy7lm$1bۃj*vU=;jӿ/_;;ժȴ§o_ӿ֝?ƹ_[^xE>#p̜gCLN5j-PzO :Kldѕ;ɉ35R Jk*y$* ~|^Ow`EmwaOa:G*?2TbʒA*2%,#^y/.kCz++qrAfy-2J®Tp8tofHml@cl$SSZ {;o]ۜMP$g5u=V_kN_\w*vU=;hN5j-v?Zw?5§o_ӿTCVzw"[mw AqZݒ#p/0M6ɘ^I>ة1/|KuEsmp̗VE P3'Ctxco$Vhul@ue @v,5h^[\—~Qxar0aw:>XZ@ְ .(3rI'r7 (diDP2Yw4~ RX[dtӊ<y?4mikkN5j-WEkN_G֝?ƸTCVzw" jNZҦe!YNAR=t>m-5Mi'- $N6u #e\2" ;$" 3{T~:o41eH 𷄛 xΧi-n?p|3qtZw zm-@]c 8,$sV?S ZLkN_G֝?ƸTCVzw" jNZ5G<0ˬ;>v3SS ZX~ 1*kw^j/k$7Vj6L`wFdw (ڦ~5 X$֏Y\wGҽzW[Eziyy8 }9yR=A拏rd?߇xLW%ڜ:j81\M|f9>cYבx˟ ]xOBUi-/aRȤA $(,wܭh_'|߇<:%kg 7-n m{B'y$%"'N3}?OoZ 3MG0Bq'oxZDŽjfmcoɧ? 󾾂oEr<C~(?lx' PTutW) c?7_2ǂoпe@|k#N^xA xA74t?Ux.erA'g-TONYd`f'R<g5gH:~wvk2Yv&y#o `k\׉|kt=_ޗ@6ڱ 2Wdqrg+{KQN&¨-T K^_+6#O[ B?U<C~(SǂoпeG-ߡ*9O 2ҩkGu+t42$yw$rFC8 "?xu}oN呢dKˤlh>y.Sj~}%npU~Eg$^luυM׼<|L'L{ű},o7zu[ B?UR*=N]%]O  /CPW\/_H/*-ߡ*oþ%<mknr+ X$}&QRE2iV^WDR̀Iګu'3qnv-:"-MF44Wov7׶\ZYhxyWT9l9 "o ۟c>7K 5FkXney 2F<ج7y\-N"E,$ [+_k@=h z!O?,k'AҼh2;K_P1Vq%I H=k芫[M }|A_ 3^Eɿ/o?4_k@=khy7% fK'zX9u}_Nδ'LS#Yk &Dt VoICD5t27u;mHq~W!a}i5Ix76D ľL_Bݞ3@ExƃKxzX|mk隅kek}O0d2@u~%!aeOci5ĘppwU/#77_}V->里t\B.w[Ec3d" Ղ(}oo̎Ed&+K񧂾)6!=ݬڧ5 I$y\f௎SzƴT&C&Yj 5 `[%eܥ@g5-=Ubk'EgkVՎ٠:[YH?*n \O-x~xN.=f{8] NHzz*o}?G!0Ӽc&]1}zP\B P%q2xŊ={JHthd/,v Yc ;T5։y*QSqn-57!O΋Y_Of^_ y9=J=m?g-]%Gc $#b^^m_ h'OT.fT=&ookDa#n~$ʌ"wP;HM+=5{1}lnw5bg%s1\炾*x6iec5Ķ^l$B Fr29G:+ƞ3 WķeӴWd8R'޸嶰<2~{kmZs=8fBl3v>\sG+(Cmyoq4'l*!`[2'l@7G0 Qg?pW ssk}.vGdU>_s<{ŧ@fKXGY&"Ě`Gg?ۖߓR\xSeW$ BN U׼ik.kayIdڥǹ$@}Va rJR=GV뇶Pfm!.MQZႾFzg'S'SjIR Qg?qĘ~|<ַڥZXv= GrK0IR+%:&gL9,5>SDdԕ#$-FuۖߓQg?6Y\G ŪA*N\L DSN9MZ4ILr,C 4X խn%XR23W+'XklH;+Z (Q@Q@Q@Q@Q@Q@Q@Q@Q@ scھG𞧪xs~?Ŗ$K G@F/3O: uTSOZj ~wz$.˾\\󁂠G5A^+Jޙ&^C48V,O2~3roܛZ*=_It: :OKme-NSΏ uvUWִQM˘~|?{]3TՍq4>-lgqJ@H1m>!x:Sx_HtҵO ]$lhNRQXpH;K9I+m-<j g[խ͈P(`$F8**o?R/Чx ^SGl5?]Ǧut:֛k2ۥR2inῈ|K5߂,5$io 13;fqErk.±fQfQ,$dBeMh;ݴWx~&4> tӖ /7WI Su$6u+4 \i}' x>I #}r"{}ZFpuKN-jwᔮ#>@h "Pq^o' 77Nt|_4+_;'­ <_WpZ$:O{ec4 .A[/O]#_RZŶVy 4D;8?ϖK5@%?+e 4KVj%Q mk_$Tg]#_G$Gl!?ƫ[i>Z /Z]Jcku,#٘ dT2:zKVhH'ӵvLb\3 UԬj'Ҫ[i>Z /YH' .A[/OϖK5G%PIt B mk_$Ue{n$TA@?%?+e 4KVju'ʇR@p%tpM'%PIt B mk_$T[i>Z /YH' .A[/OϖK5A}[갠夛Hz4@MjA}meU$w+?A7z[l/m%S .&br_=|0K}$I=:{bǶ"- As:Lϸsl^o6SӮJv6E9XH `NA~_ojNSr@:]ux㕇>%x[3OrPUiX_jl2ƩVSJ?H5IxV'f?%ׂX/wOk׺6_,1 ӱ31 $qB x nۍ4϶G3Ea :e=*Sq<ꁲW Wk n h<3N!E-ge|o`Swhq~æZ]^mYmPI!Ks)I]k/%~D7mnDUL嘌Oo&_׿<1xIT XK=HdQ-3sU]NjEK7:@&P9* s_2ᗈu{Fjz0<<4jL$lr#F}k燵o +S6M Vo//*ס@yЫ}Vv Z[iא{ig28'V6zeoح#X+p*287ڼEBZ:(>//*Щ}VvϵxK y#ynsBqZ}O&%>J*fotoxx-20.08/images/custom-widgets.png000066400000000000000000001013641362435004500200120ustar00rootroot00000000000000PNG  IHDRͿ IDATx VSmPJ24+$FEB^zIݾ[9#jժRBm䡇JFݣGiժ[.jFs12993:$x>L9TUMuQ?RN;WX!{5kJ5+믿^sOYv-;/rɒ%r۱aN\p~'L9#;찃|V2zG?azѢEr)H*UVZ˗/?K.}Zl)jptȍGt/[EN:IhO2rz'Wo!ݻws瞓)SH۶m-N7.hN*I stkKi;{騠B+]J}}0+0|曍J ̝wi̛7no?]Xbvnf7mڴ17tQ&%W2͚53{5jх_uǹҺO=mY% joIi67|3(>ڜqZseq]x]ԩ=z9riܸx?lwe+(h^Ϛ>Ӽysf͚hSMt| ꪫ{g=c3|'|ǍsD)3ʠY[\֭}Nˌ3(ceom;s|c8`.ڬdF?\cquiRSAfFNMǎ͵C_~f]vJqh?#5ܤSO=Վ}gy@5{젯ĵ\H|#_M<fvaFx_J?6K_駟(xva ƑE `:  JLU f +tܹsu TD_4n_}vჱS&}΄Qߠ1O a^Y{zIԁ2S"F1X}va~@իW$cI_7'Xs?ly_7re`B)_Jl6*6m\ۙW|L69̌#Сen(3]TaUXNŪ: &$ TT'pTѪ13-3aE7-Juz/]`zK.㔌RՄ l~x2N8'nNp7'ń1S ϚCuL{T-`lٹ< 7N:UTi-Pa;s*h2p{)O/\0W{Giq0wp? j\ƙ'(\H|ܵ%[B|<BA=xy#EĖ:M:Xf+Wx~袋bB[ͪS oR[ǰՌbctҠJuńg8>R~}>TlU.f00I`B䘣+jhL>ejy]9(g3Ě&~6le*W=% gIy [6]wur7Z c2am՝̝01Of 0}/"M4X80[`.#W?,$lODGROv3Mu=.9}v+&a!tf!(PW>=xF@e]fGQFȨvPއ8'a\[lsl ׁw)[P.[zM[JGqʌYt 64YAIruOK̅~?l)ⶋE6$vcQ뱮6yoFaO`cx|'MdoŢjN̙&K7\:wk\tzӶe-vv;%W>Gqs{I/H.]=%x#S6GGlC5 b5_G#(`S%A_c<ʷ&%^QsiRj(s(n!S}aֶnnd@u2Hz!$GVFH+"\N(7!x*aܽDtkG#8bhT+=;vi*PՖt+ CgKz|Gυ yP%j8EcDX oG#(xb>xG#x<g [_G#x<gKgP_qk<* C'*el#$];V<H;XمL+U: P<< ~ĥGSG|f@aJ]>ecBQ,.#(|7vq"2ED?݌ZF) FN̋2 GTOH]ԙ)yZ}ɼ>ǖgKQ@ pwpf!O]$fήPBi#10%?O{_s#%/# @Sѹd0V6븆K.2,QʦP?G}$ztQNCɇ~:#ƫS=rH۸Gf ej!ւo^&Nh2m=7p#M#7ްq5<4jH{92emu]eܸq.U)7U;82dm#0a#5ܙŤf͚vh f> ѓt}$mi¸&R3>3p¸?ꨣlg;OWXac83f5j԰s )~UI{kH?\y3A%z\+ִ3`!)Eyw 5î!S h?V5#I,[G( ziye]Xk /^юCcZ&{0zWEkРCr 뮓YfY) f\dG-K/ٍYsϕ#GZOP[  6m|EB3cM7$?e`(?%EcI]lVUqOTd>{ Q]kδ?DI_rO?6ɇuw~i[m6K/g7m~ V7{b3k^|yUqkvq_. (Pn?|pM$ӡC/d/SBR 'mv{5nq53(gϞF&{4gu1 /`O%$F%6Fp[KG }Ј${.e4Ǐ7G}46F~f&rS¯*Q'57xN<َu;дiƨ$ר5V[J v#JFg _6aaTl2Au 1HI^1c2U*l4oܖ灛ʠz'jasř+7=W_|aO*}5;vNyKx0朻8m.C"%|gd6aa۰2[Kv*KDerzŭd UBjde*uYk7|fKQ 9B*%3f̰6wO>ҽCN8Fp_GHa_`kN頋1#g$ҥK vmg%Hۢ :ntDIN H|$VūLU \ҧD9e-H[(}rDt:Ӗ҂$0U3x+OVN1ERM~xXӝ;w~9aNwGqCqOTOs=@ܵtXgggU}ZlrH>c^qͦvgoت$5WRQDn8pZkѢGsKDƔHdBv|q=rDH=NvLu6:u" Շ2}0# |A8m *,F'[$ȗ4Ke{ɏy˪.[2gsdT])]Y9aY.߼:Rxcn^rh3W&Y;C>|~ybeAk>'egGl'9pyd¿춵enWȭ=vِ~2)2kZ+Wc}h[~HF~^\)SEv= 9,~niTDu=Y[.6H`[.(ZhPyHH:dJU. b-5p.\49N[Jv )N@WG08.!9^!$Q،fJ nj8eח)iᥗ^j< uk.죱Ʊ M.,w\Kl^K6LӷMc\+C^#'7#~2wC+: 5u̮~\qSㆋW INz4Vlrks7or-9R%rsj6KP~'ML"yVɂˤl&@?͞$~ w`DRkzTo|2 )U3o_7Q:bñD*l#TRpt紇w Gԁ_vr٫4Hyc!{TʘGAXjO>jQG08ӖҀ`tRw2&09G0l=7Ͳ6ry{4O6]RlƖ/sNY8=nfaqҤ(ǒ(cA<`/M4qN9xMᮏj !xG]$^l9/;VaԨQΆ/}5Up[r/L߶d^x<D;žvO^ aA^+Q_ lA;6u^0xGnGsdMer4 piqp'iJrj(˧L{ϐe'ԓ*Ͻl&be^{5{ː{JZYޫxen.)ksCCkX wS?Y(W^sU#vSڵb}Z_O/̘;A7H)Y|TDg)qidX/_Zy\oSųkΓDl-AkѼF:Y$%5K{-D:,m mfjSw?#8g96lH6U^s5Rn]뱍*1aI.gAX{ OWGHu}c Ν=N[Jv,;ӕ9ۯ_?'DŤD< ƶ r)#|򆑃L=\l1b/aػzl Rq`bCWO#"e' E]dLPxHy@߮ 0 p@%":f`y\ XF-*?˒*^&WJUmwMHΆ2{" +oY JûHf5?ʏ9/G=2nzv IDATݺN.Х}\+ ' _XVTkWjVs*-[Wƅl.g={[=bӼAlUFg^[A>!A0<<׃ 9ڴRq 7srɶ&I>N1Ǿ 4*h!oi*.B-g]vPq]:w'[\T}$͖L:59dy2{U"e.#v4G}|ӵ!}lLj};;T֕sMoa^d8s2:6?o'ZvԩqQ3?kdnUٜ쟛lnR}z,Q\+,Rv~Iɤl-_Fdi=%DحuQGHVU*h _BC "+_2e/GDHw|Yj}']=<[|5D:ʗ:H 8ݜʦ!I%.?z Xl*)B˴L<&/#*0D͍ZgF;'erMhR+9[#;Ҵ,y]ŶmjDص{,Rmc/*NZ͏j^T*Gri%ݏYyn_D9eۼd߮dͿ#6rKݜdICJ工+Vj)'H=X9I1O~ z~EK̕gnW* FHk4N. 9gr兛v9W>z~3i-~fT˖tj%}>@qE|ԝ.+ p(*QIShB/j-=="؂GN$q(칇#RQ$=QAl1žIًm]ITN]+/#V.8rTi9rFǍ6ts\+?<(}[WZN;l0`\gҫ2l2Yl|XQvZe|{ |qPMvhxbKd#[rrRޚm%MzAU>%_Z&kn#j7$+Q R&O])+CJPQ]﹍4xrH/麤48y[&{Pqd lpɆ;{ٔxJ*hx~bct+#PTNU9`~Q0VP5!y>|5bg"0b,p 40:Sh}E#u;e6"xK&Y PXdc_XtHG#(&D7#(B؜ϺD-b_UAPFcmu0yISD I6.&8 S 1sI yZVawm){<=v>G#")zFNecZRQ,K&vzD(m7J[󳿞QO4}YRnRom5O7/RŒ6DBI(O"c^KQ,[Yv0 UVRAW^yE? {wE6889B]Gع>Hze7GI,Tv |"JcW3<3݄ *;6uκ;-ӇdĆ08qMO87擽ZՅudZO9pWJ8։'0Z#Nxl ɃC ;8)ո)CcD>v`{0vp &8݄Asme< /;\ush7z(Õ?K8OR\ >ͅ|M5aHl$,,nND i\&1\;SyJgcxJ5,0R;\|sHD2jԨ [do8bׁp,Z^_5ke*=?bGbڢG&v*¨e]fc$>vWIDd,+2ilF>#lITwk+L u%GQ/DrX0&ăf>6r#>ո $°8-hÔ)S)0491s!ʥN> qIDY˶hl'aGIY9.'׆ Nj+ 8; I7]w΢ eQJJ[ |px qBbB(\5o2y܃i@S‚!QeРA  &̃H%{Æ t3fN5 /L a^D"P G$H4nؖdU5!c3G?`t '>$lHIW$R,r'3n!?GI{;`4$ڴiS+'d0Wonef.],*M66KtdEoiu'mcD(ǙGGTsA){&n6ז1: (7P{f15Z^#PjAA%^0^ T"Q؁I! ܇Y` ΕDiԫ,6p]ۯ_?+'8=jLsZlxӮ.77˽?ƾ`s}F`Bf],X!ͅt׉]?bKIĦ.B/RD670m2qTwUO\~Sw4<9J Kj+^UFqcL;udE–"=Dʇ )!u!iuD[ӸĜ¡@ hDT}oL5o?r0Upg<d5P9~dWʖ˫ܱ= jB\fJ0,ؚw${H%qnq1YJ08,RkŽ",Hܐv| CnyYi_~P~Փ,lH v@aQ=`5p.\gH[H6d 4b׊9['tXH*1'?k'뮻sϵ|CRw ۷$a{K|G3ip V+ha v[bU}:E6b E'|D"+& {-G0DO>hF݊&kPikJ*" (/hʅʦ8sm=z}ف0wߵ6!FUv;nǘȂD"y!`~ž9s`G ]v]|Q *8!a;`/1( )C!z?7ǜsrs;n}$Or4:<@V F.LA*4ʔ44$ՎΨmQO;QuGe8.FIӰaC^FN1gqFT1ٺՉè!#U<4pڸ j Q&ɨڨHVUFzip-za̯QmC{Tl1SINV[Ԁ2!LToԎѨ6Hn\6$cv^ d.qmN (coT*gcݒǎcoL',7Y]ݧp[9\P;\̳QQiڟnUjL9@jj`d|ܟK~sLE SGӻ3t(&:2iOIMC÷w#(P;!2Dd(T{Tis5dl0q iY4^[N}OJ"$oG!oy%6sؓГG!^lV;6 |Q,~c[(qg!? Sl;ឍq4pΔ-T<@+*VxFpyHYH$w}gۺ!ٖyJxu:xM{'bq G#x</Q,Fx<G8!4-G#xQ,F(M;V亠wEA`ƌAV4}뭷2W֏5D?$4tO'ީڰ:_b;S(E7E1cH9*<"fĞD|ϟ$.%܇D}Lpp! [_G#P `bv9mbBoa4N׻%WX0-n}y=Q^Xp=% E&-i?PG}$z1jQ}!D=Cj;N "'N '_}Up B\XÖ%N.:0j6.;5_e~v>ö=Zj6rxtu8}r S΅^kI g1׬YS4,m_/bPZo:ظ\jI"w;B$ҋS=mk*\y鰤te80АEû˟5~sz„ B|r 'QLw J7\Q`cn h㚻T ۸{ALa]PUW]eūL2,L_x}1W\qlD)dtE2]ԓ(F_|T%aJ4*6ǘW2Σ)<ƩU 'O}Ĉ-k>2e㙍19:$N$MS/c믛[n2||93gtSt[Xx&"׃:(x7J7|_2}Nt75z+z< TXʖ-2^=4#U'O.CT2rE:F틊6P*!O?4P{U`ިGeԩV';]t 5k^S\8BQ)C`g_TP?rP]*&#Qve[ah"ۿx@'.jkܨ'7ްl}6lgG-:qڒnɮLVlax9!e`)6{aa`ƍ icXXDT+L扖j\e`NaQ6MT&VŒ[P'TEfb "@cL^).nyqLտ8Te$.H| 65U%Z'MڤqnXpa<܀ a M\qG RrIՖY=\(8>H jgh%`/.X mYtmI7wu9zb|G 6HWPe]tE"#T!UC]wUz-_!Vuw2F>~YLGK)#ځ_vRo]›{G'|VŅ 7lHR-N`qIWF"^a-[Zf~Q!Q[\ \2r a20'pGn;99Yt$̃t|qp Lro^DEW`=+Μm|<%$ΏF% IDATan!ߌe"Ӗt7D%<P 8V7sLAdk׮VEŋ~T|c_x5׈z Z;G"t`n 5Tb~aPgkk35>[؆> f%jp.y&ѪU?qmUƴ19NT:azǬ='-N`|FI 8WlʔІzL&"$X{I&v w RG>n6a;Tw${>޹̏1WDH:1ܤ920Au]» \w9?Ν0C->/!OG +cݻ0ȏ7~UZ/Az@_veJlߦcjҨMT/D'*޼ԅ''(+i*oHRgh Qi--nDUyF 7NKVf8 V' [yI4XXLTnxU^tEk{,)'] DP2\`[8b|Wf3reI1XO^5aٰ7a+7o\}S8=L<&|kaJW.iS=}ىA}#V9C=xK3+G}g &z{ݧCBG# 8bvX`dFhZkx0@Kx(2ؓVSLF$oG# Jm>aĶ(x{&1şx "?mU:s5*6/#dD&4jB%T͕iq>G#Gl OG# &r8H~Qa(; Ǝ+l͎_ծDpwߕ;nΦDk}܂±G!^Zhag̘l($lAt2d.NgyFtsqf-n8xn1cP|/Wcqdh>IKD \7FQt:rqW_}(yk m-$nmWmēG ~{dG F␻z&#$LDx.6߅{W^a■Ђ01ő }p BHDc;Js‚On݄prw<Õh 2.f`hH=y!P6Wɪ XIc]b0jjO783{lkC"3q+ #lDVmgGXʋI^EEF&8Apr-.-\P4th%| MFa"#b֪UK~v10_e&9ݾ}{;(ߕoppag vMz\g!"Ϛc8Si_u[Xy┻%pǍG# 3Hb6J.V2&FmlTeJi2<69J|ڻ˨JtƢ}mW1OIO\G4 0j'eʀFa͛gc/Yl]etLvW4ȨJ(d#Ygm[zF˛]RA\r1y[nmm25Öfg9N>bxlNU6vopqIGUɕݦMsM7:Z>Uot:; oh4ZMy-.];r9}:ths[ThjԨa|A;3ۨɁq>eT{݌VL*UtUu&s2})wK%՟xճ-{< vQٲeA"戺XYL= c]GPIRѣGp5/J2t?~UtDTimPc&"Ԑ8sdGT"jmh+* 1kKE%jү_?Bo۶mP\>HX$2+ad2\*( iӦr_nî)q*% 4YiLx>TrtYD%p&ؾDY0>}$TP纾3}ñgѢE<0C G>癋SK0x$@;PG VMUU( 4avyM=FE4jӇ v,aV [5/'Y˕ #]`/_.*ʕf6fhc &K/4`ҵ!WYP)߆ ^4007x 8w|q aa& '9agL9/uqƂy8` Y^=8H}S %|\@q8t\8[`?gDx"R2mڴ23j\ +, g(#C~:] `:`_Xfjb1IUVK/YthN)H~ c'00W5wp2~m g[$RfI8]g<|0SL5<_jkiI%g-~}=E+VXUk jg}z?,+Wj5w&ʆ"v><Ë02a„  "ډ#'#aDR1n(]8avo^pA zolBvӆc=&bDM/H5`p\B2/KqOu]te8)@mHƾ׷ 5w>Æ8\rK:.c̍@ry<K[05@bUDbAB&%HzmӳE 鱝KļS}k^r#Bʄ5\#u `$ *>aTlV>%mD~!ECʔ]wur7Z c>pȠRKorC5{'la6Y3gC>2`G1~E53LBѰaìO`g,7on@όHFyF;si駟n|M`4kb +Gq8/8<1~yx<Y!nUW;> ]TiTdѓ&M qiKB.ۍF%6.K#^ƺh탔2j7f4Qt'6Q[2=W-ݎzSQo{2F? F؞-tOwɨ͟GxF):ѹҳ=v [nF1*n,V頝x} Q~Lj-{ely>Ԟ4tzǶFḁ*ו\(p);G#P@mId$aORG#A 'rx<ҁH5D\:nb{G#@>A4ݫ[7OG!U x<)Ggyf}%K!$@(G#x<6gG#x<!<ßz<G#lB3gG#x<!<ßz<G#lB3gG#x<!<ßz<G#lB3gG#x<!<ßz<G#lB3gG#x<!<ßz<G#lB3gG#x<!ʅi F`%Ԯ]'#x/Q,}G#x?Q,c[x<GHb+x<G#Pb#BG#x<Ewf)}E@rFEQ3ׯE&/y<@qAKHvx<G#(fxF oG ?0ϓG#x@`ǎ+O<c\y)l?*+ՏlIU?DQfիWK:uG _.p_fm䭷׺ p# p}qݚ7}~cvY/(;3bAh Duk%J{ >VߧP {1jbA=QnA2eHvdw _.K4(exgR6]F\l[VC._8oP79zR(̟2Q2jo}e݀QhoE)D_|w#H@(wq2d8qTXQ+dww_yW1B*W,_~ep{Zjٳ%QA'˖-?_j֬)UTN:I=4JʑG)UV *n&=P"OTO<*ѽI7WfJJ/ǽ O=|۪MҲMު)+o%eI\/+LNj]g}f y|ynZjrm[9[G}$zիofMy}w%Ss1rg#g!"*U4Gu>v)P=+V޽{wf5+믿^sOYv-;/>_d~X]pXq?ؐ&[N9a믿ISqL?]L5k)b ھ|9\r%V[K˖-W_ snp{q֬\S)sLG99^qҧ+/|=8tPP~nڴ}?re3<# 4O_٦M6F3w\ӹsg'i\ 7O9fst=o~0s%H3o8zԣÎc0W=\W͔W579Ӂ!CƘw|p:n:f3)9Zל1w^a;٫%m!0ߛfƍ`.?fsSz2]di\}yf7W{}W`ӦU 2͆2\{/^u_GU35fKKm:Rl]s?Yk[s^LL*w;\2b i(go.27cTG[oQwYdl떙i.8١ZSQ;'ͤ5~-欃w7kmhtvp2,Gɋ+g78m.PȨ߾ܻz8sYgՄLUI\JPƓL%[^]W%U7etga$bR :ޥ*]3jhٳ`L}/3&(S%\^zyF$G=X?l8c*ޞ:L^us-e{$S毿jq}l>թS'C[!vք:1v-6m?oUaٴ[5{֬|[QPGIH>ȋ+ɯo+\xv ?u\v$r{Rҟ22Kb'CJ {/ ) _'ww| &Ckn)+!}J^QO떮%%.3.G^9zm ʈ1}ll{ -]@"T`H_Pin:fR./q(Ny}wi.bo(,]tGRa65ddG' .9aPH9nbFb+TfQ\m6HL{j1WN! &Joeeԩvm@ k '`{q,Wg~pMG. G7 (.^|`&^$<}ȱt7?]]!eBLIDAT20\LJzp7[7&P2aa`~k/oح|ڙrh{x!.vp&ˆ<"p`͉PWxYhO~cҕG;0%1Dx7nnV`bosiaBE 3[pS1luPa8j`T-j'_SLp8 'qyTMN K=\IH 㖉$DHOQt`W\ig͂# 508CveF^5+W G}D=,fnj{^J9rs=h0P11jH^x!HÍ#P` U/4c6yee:܋)WȀ[#n)Ygڑ-؂srXi珖-;a?RZNo`ِC&3<+HYPRedb4O 2mfQw 1mҥA͛'0|IuT8!e#&4LޯͲaN\'e;6ևk~l٬Y3!U{4qѢu78~4cZgl׈8kVPi>eO>χ&" !QdR]- v"|aׁDnݺV=BN.Dr[g ٪U_|-[F4iza5^"QFfΜ)HjRڵfiذa<0;KkYɊ;IͭUeVʊeeZ;Hmh׹dY3Y_R U\BY|kiкoԫNTIIred5f]ʩ\UU$2gYSMUѯoIoٶi-^ |Q~ҢtY}~b 2iIҼJ 45|)j";n[i|ewj*kϿ^, k^U͘) ̶GRD}"\&Lhx੃![>p֭}q[[E{mPCzXx9c3翕j/}L%WfrMeʰޑ>"=?LF7W۷Zln&Rd12ዟeaNeYi44K1G=gKL]I߿)>>T&MF~8DkKݛKmf/>-D&y-j~SjLU1E8;v߸w?I3z\=iC{)#?Uzatc큺8O.s ;=?d+cWow/#^zUFy2lٿESٮ Mq>uiwA8*7.]0a!tHnA{I'fCJ Q$sa4vw¸GÉט#Gtkk vPT5lnUˬ,ؾv_XW $l N?Pu&sZB4k%"E G\kvW5 O {?+s92H// Y(ϼ:^B CNԭ _!?}2V^~}x|Y)e'ʙǴ+Uʛrԫy[{'yrP?7_N 9A䥩+^r;YgnٲTH}:1G.V]k9ǫ]G9rE׆RvӴwd3H˿. h[l:\Vn{2JKޔo{8b$ƼG+X0<bdQ v  a/YRLcja.!7nXwOPq9_9iٟFŒj@$@~أG$@$@$@$PJ P,b .f# p>0ɂ (dfA$@$@$@Hb2u&   b @C 3    HF4֨3 X  $#(&cQg   (42    d$@C1k: @1X  $#XkԙHHH b,HHHH  PLZ$@$@$@$P h(dfA$@$@$@H\2*M \Г>RR[eD  (-أXZj$   ( P @i!@C4I$@$@$@Q%0F'   BbiiHHH$UQK蹹brY'ٿU?xIMM&M +h iܹ/H~dRV-2et9nZo۶-nrM}˔)#)gA:u[oŁu͚5ߗ+lի]V,Y"?sc V$%Q IȆ}re۷Wz+'|ʹ{n9M6Jc)ot?JuLnl⸥_Dxb#IS_4D/U^xqwB믿ʇ~(u֕^{-|O$A @(OPߪީK6tݺmRlYٻwq۵ H_GVΝ"em܈mDL(1ʕSʕW^Yb:D&;vN8!R40%\̾˗6%A\|\-]eȚ6't̙3G:9SOK(pBJ*rAyG0mŊy2cƌ|`i߾x≪Ë֭ G \}2fYh +W` AtMRR%\ 4H`Lh]q-[WLUڵKE)fnݺaq9mРA}wJ$Zh!3g ,XO=TUn-_ B'6!{ =cHW^Qmسg Cj^ Xywy'44UU ^~ꡋ^^zI;eH!Op%7n_;Gb_GJ?آK.t`7v0u˨DsIF}+QKh SW^ .GNwמԋNvc\Tu%?p3=ڣHa:nHGg\\3֜|xy,i^,e.,W.# C>Dvf]"37Ij246{Inj&ȶQe>C(juV|=CC ,B/-ܢ1l^I8pz.-m$&02b)ڋI$Q`Qyq&CVPA1//?7ݼػQԵYaSX%ZƱޛ-[z_LC:]=J:B|o4G S Qx`Vik0X40`Νw|`\L4IQ7Ts6mڤ! S&p/nFgg$N&{FX)+1l gϞN# +b,F7%_]s%8 g*۸ԕ|8^ƅ)oQJLeŧ0Δ'+n<|<~(cb|"0j==O̶JMM%';xpX)Jק91mUQxT~J3IZũZ9,qkZqW}@ĜkV͏\S,@s9G-qj Cs0nСn`*SdjXYXamH wk?`~9=dW+e8h&X E[(  6L5jZ,f3Ek c&V myX<9r}QLsLͯnp L]׫&LP2b+)t<8^F!rذtKjk?3g1㹅;q3܌s3WNjyLlQ4.=`>n qr6ntbxѼnܸq CC +#19~|ڷt,ܑ*m:5igJ𧾴mTdbQˉ-Uiuh)_dFOM7%S{T 66ZHXm²TC+!B4zp:=G(^$ xE:HפI5\ࠋq,WN戩&O~+k9bDGn 6K$c+ϤKmR,nd i)yS2f~!7eO)qpz. +P4'))գ>$"g zvxQ!i6Jpf N$6Xae(N>%4gaԒ%C=vIqh(&k%١So*ӧOW#(zd׽{wd h(::Q Y ="Pv?{ m?t$@$@$@$`Kf6  ŀW0G$@$@$@+9#   bW0G$@$@$@`b䘎HHHNb+#   X Pӑ @ P xx$@$@$@$+c:   8`HHHb%`5>eXIfMM*V^(if%HH0231<0100Fotoxx:add_text|paste area|trim/rotate| Fotoxx:paint_image|NE|b`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }Zǀ2iTXtXML:com.adobe.xmp 600 1000 0 i;JIENDB`fotoxx-20.08/images/dead-pixels.jpg000066400000000000000000001331731362435004500172320ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0230Ơ0100Fotoxx:sharpen|retouch|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 236 966 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?⿉_|\x43TICqHcT`d=y־ۏ^4ۘ^-Ic=>ס">.& C\ĪhrhQ)6k_oZn?{wD+xEQಾHmto+劰أ|ӠEωRM)'%̱A! lr8sZ]|5ҵ{_{c8f`I1V-{ú_0x緸tu#!_x/ĚĂ]V}UtfBz̟rk؏<d ):y*GMyUqV?|)ዑyP-PH_U?#}]gKNv xM'&6ڇuu'@~(.,m=1PP<[w|'K EC9O& { GG?q~)khǩCA?o4c˼MyuW}DZC:xQ|PO&|}@CQQ=|]y|'@(ks>'Kn4qK?kij_DO&__] וM6?ӿK.4j/cMy]6帹Wcԗ<[v? P|O-,(;z(.yO&/o8/,Z|=]Nzry)D zC\GṀߴ۳O&~ӟW.?g?ƿ tMfMis3c-Gm5tGL7ic$> pr)lJ1QTIl'53mM?3Knkմ/~)k=\tk6RI&yČJa|.?ݝ[xq!ye3Pq 9B8+6I'1GCAzxO&iŷgMz-xXu_\xMpi6>M`0Ox⼢eV706{?c؟d[D 9i~pYAOҥ:cV{[?j/cM >'Kn5./@'` jV_ZY{^]ۧߚ otu>)'c{?tn<_Yp#P M;Yۮ]<s[wj ŷcMbP;#.4Mf ߔ񐮒ylv0'Ns\5+ܦ/i6)ҏڇO&,z~??CO|N#khJ)lU_ړ⊂q?Oj??7] וAGPPF?_j?#f?(z=O 4:xO&mz5M#~x,Y?ifۿ?QPP<[w|'^YEcgnO& nbb[*<pf=׾h~[MWHl!VoI4t.hCuMy]lCr6dO噛|gj*cF.ݒճj4'T&oc/!MH_T9_M{N6idPhi{ɲܰ_,ȅG8 B֎_Oc rQUqȯ V*2{]oO|-˰9j6wqw5 A+hжʬ *i$OHs墮yN9!qp69!Α:OM=E= Z^h Ox-g<3"N7`㯡ύZͭwzZȜٿ3W?Y杢iu{lBI+: 8asҿm.ǟIOOỴ5$V:Ԗ*ƨUDЅnhE{> G J?G:C4X}ny!5^|*elG*nbrn0p9ֺ ? Dڽch)?y}ZW ???W u_I`'c4:D۷3÷Vv^IZ#v[MwՕU7\׺ï> J?G:C4'`o|hQѴh4fy?h o{%į-{H9N,jJ#}OJm&T9# nsz/:C4ï> J?Iف> 4~y0Iݍw^)>E? }Ҹkx8uܡA =w:C4ï> J?K@>m" Z/$vś,C֚] QB1X_Mï> J?G:C4\~~."͜#I_I K?_s| .࿆|##tv8(c# )8sܧ?,gKF~xğnt :͖4і ܪFGּK"Э8+5oK"ušB7QqQ k%E@ušB7QqQ k%E@ušB7QqQ k%E@ušB7QqQ k%E@ušB7QqQ k%E@ušB7QqQ k%E@ušB7QqQ k%E@ušB7QqQ k%E@ušB7QqQ k%E@Ǟ!>0EWVSE}c&,< DxZi:k]KS^Msn]Koɜks5I}x"kgNԬtk &e(r;g5)#xE Ѝ_\+V][ޮϥq=ދ^5v--TW{2K6=;߀:!Mۭ ڝݔ\U+01 I^U k%EG)#xE rGK|kw]9L\0Wĝ~QԼ-}ZܼF\I"-s?:~1|8'5#!>ÏᦃWDmq&;] i `P.ϞKg5šB7QqQ k%EPnEv? Ѝ_\šB7QqU㨮K"S_?F/ .?㨮K"S_?F/ .?㨮K"S_?F/ .?㨮K"S_?F/ .?㫠濣ǩxb]rquqAnhšB7QqQ k%E@kWԧLr ZE#ȱ9,yީc k%EG)#xEgx7~qfĖL!yX)u*~x#i{sj]A**XI$G Ѝ_\šB7QqP]7Ï?Y_ B;+ u Ѝ_\šB7QqV5F9RV3 1(ת#_쭼5;K{ PRڪ;zW8񎶞>nn6R{L~8w`x\ \__M߅^'-7_VX0%kCA{G%@}_ rW35xni~c#Y5k9!wa;~b=/hK R|?{Ǻ͌zvɟ*IYF98<`RְS ʜ`Gb!ŏS.7}SJ(n-c[elǒȼuSz+ͼ{x ^xP۹Ӈ;LP !7{f=Z3㿇+F"ivԷ*$ 10kR?6#qZi?e*/R//T1]{g`Zoƭ_Rm>) -MH"vy~V6owsFԵf5DeQ.d,E;ka_WjV:npWLc26&@q^Aߴfhu*k=AV<ɑ!'N!E|K x:uŗ-TKk}oZk9ᙧ>s vpf|<[`v׋5K]ltVEK #G,a\>|IǦjZ^zjҀ ̧ $kůOSY^>tM+º隞CC?|H6>wW12;DJs=Y%=+Qs6KHFBӭ7-O >+h_5cu ytsTD ßx;/GQຸ2Oq;K=Gŏo9\mT>~_8}BE^ss-坄sܺX5r.1nFmeT?7Y[p Pvo恴o;oF!8O ~߅4FkkgyltNs ޵nj1 (cc~W)o[9@ѣ m|4g̒A;FM-/Լ-o>\ώb%z q֚JM%oBz&K~.ߙ1 (cc~Wu^aObk7WjIjoal%1cj1 (cc~W=~~xc$HtǏO]DJ9ߵo4 A[xnu:wS߈op;ZZ_xlu?x~,?X߆Y~!c8oY ,%KkjE<$o'{Ԟ3<Ny}--$Y,eq*@s;-Ɠ{x~,?X߆>hk_k~l5[-&dI,H|_[X_/GH |x8G# rFG 75+Ky9a@1 U@rrqȕnoL[3cc~QLj?7N_Z2xֺwȚ]*J%Àk:jn5ռCmUch<&fhQWr zSº;FqeĈcrPM+-WoLŏo9G# rOq|D񖇥 .ÖV6~&/uFpy<}?=b>ѵ[:]"dw@U$M]Ccc~QLj?7_τ .i^j)]DB/N{o|?ṾxZ[lFQ?Se[w ys@V<f/xM 2[Ag d5$p15=SWZΐ4NX>.V@bjGK (@U#w}sʽɷUڮ1rvF;nl?_^Wkh#١G:G߈g5:|!h䉔2*@9S+bFڥB_~W}is}gY{5S3 P TI$6ֳ&mz+x֟x<]Zo7NҴˠͧ]Z^gr6qי7ş|z<]3H̚kY)g?|Ueᯇqs c庶abw̠g y 潣]W+u.Xӥ g[#(Z:R7_Sw\_m/_Ճ/_=Ŗj>83/t*윶v||yYF~r<<1⛏_kmᴊ}ARBL|,+~V_n¾S<> x{KC*8T{oß Jd} ݵxf?jIRc#n3ԺŽOuRRgfb q6{M?䴭'lz@$_)|q~QϋZ e,zε.>%(o0S g5us^)cuO ~ vE6rI³ deo_X]C?~38.4^e&AAxwߴ/j~=e zeO^ .HݒfUdqDXQ"(Uz uo)'%}A(l_ϐu?5(֧Ҵj4t^P InagkKNd5ߴG|miOAEۮjLeegV8`gÚOtoK4pdI|s-6S}[ <'ro~0폄,24]>R[.]Ib#'9] /\xC ‚mnE[P'<0@ԵvV3;]s_| [ύ<)4 چZ>9֡h#ϛ F,@r095y=x;|5QVv( s#("0v67c;MMEtg쵪|]O=#^izr^o1_2iLw(sSP:J|W|Ax4ңn;w5&'TG#+'=w|yE?Io e)fdGy#IRE6\6G8y> > Kº1ċ+JŋlVnJ!GqX:i)ͫOwi?ۉe77YaIhIF`WxsZo?Z"[k[rI jwo3IioD+wy|Vu=?*8xQtvWŸއ-M2BI_e[2\,dX1$+۾*|iîC]ͨF翙j +b鋫h7V%6!Y,Ǣo__Zq6IiZiWi%Bod)ڤfaĿpW~h /&յ;XY mcr cSTkK'⟋.1ɪk~ϧ4!"Y|rW8zM*OeMOǟ,,ݧceωJNe-m`dLU2`1~4_t? z|ϬE⛍Ick%q$[bd`B`=F9_ş|࿉7%!'Vv( s#("0v67c;O`MTx9+5#w?G+:6MnZkZ?ȯ"R%uv #\^;/~-l<)7ѭoTKa/eUTxP8SZ~ p5]bc6tk9~Xom<ߕ͹&ҿi~ ׼EcrRhRF0A;pFM7}o^=j/ ~4"8|EhX/iܶ!Y,Ǣǂ~)xdnn\K&U\4tzW׃/>ԯD+;{wc s#("0v c;O/!ExG߄b5i.{-{Xg!NeB0w_g࿄ƭa*D|;w5&ń}ml{E7ц6=֩nc770aIhKDpv0xO׉4xN4kȵ +P.mal"Rn*+[\VQiG' dJbxut9~ ٶofHgQ^K <x5ЮPғF:ۛ]f9n1ک/O]oL-V4 yVܶ*fc(8$ A{5iO|M}Dž05T|ĭ!2Jtj%?ﵮOnf|k87CZ|19Ѕ5.-eS+v-2ثWd;=JUkfT$ }:D)$\Ut&98 OyY̮ dm0#5*ՋR0DL98cCuo\IߓDa#< V7/,S;OmJ9ErD|n[w<> 9g E, 6NJ\;)ƜOrQ)T$esȬ#vsT[52!:+_e+-MԕZm`pIXD# gbeQV-i )K]zZVVX3r2dn@&imI[.k|?7׷YZ3HY,O@$j9~/^i/D_?|unAݷ.)/x^g>$5iz^itxZx$o< FFʩϿGmk'kt}6hOMȵE8//DPh]VV/m"A1 \rG?)7ZL J*rQ|[O(u|\GVK]KM"HY#!Il4W>آ+\o|fD?ᆂK"d1#eVT\Ǒyz-5n5 Kk o3cr:.p0>~m^3.SDV쮼Afzy̫$APc .N+cX>>:fEM*=NK;TY%^TSU2A+1ދjg_+OxFŗjz$ )2'Ok?lRxcC<:mM$?3r@c% NioazƱewou!A /v>6ksiW[N.4ۭn]n!sYK)W , )E}MZ46$qnWqR _ƭcs=KDuxj[][_S)^a*L>rw/ qH`2FM4GTQE!Q@Q@nlyկ Wt׏xjwXa S~9Z|t[`?O_#~#1-vOG͠_7~vx>em=5[?IS uvqriJ-kAo)[6q_Ö^Ğ2}=ݑ~m4x& qOC~ 1kUտ<\op5NhCq} ݬC,d::e#ToF۶̡T{$/?6|M'Ŀ ~ԧam]~SkGhb+ĺVF8⾆n<:oIgAY-r`omB5*Nvbi:֕iwi1[IC >vL[[NT?ZEy&x%I]Dp0$OAw< yi¸<sDκrǜb/s}+eƏ5;f&7:]VEvk5Iio>-/tC}{otoÉ^{=. ѡ,`NA?7g zKƿ 92 @PQH0'{ _vz{ucy5-dGh$%)[y-0bz t)+4hrxǾ&"ɨɨ߯$nmLrFi3u/6OagwzJE5F17`z@_y {ha~6𷉵'M}[U,.lmo"{rkoEbɆxZ8ۥb^vCA<_~cP^xy&DV'Aـn3cLWjug^5DvJp-HٕʑVsM<#Ho|4~ɿLJ0u[N8xT{k"R I?oτZ½Fu˟EFF,󸕌3_\'g<xgIL"Y[{B'Ʋ.P"Vbb[?:/#~:.VhCCO4of"1;Jsk+<-?F5WK.8J'8%M~yIfq\G?_~.[k{8 6H1Gj.~ߒ_tFvvSW? dGJ>?o-?qǝש:+þ0׼[x+%yN|[rÈPp;WۥJ֞gjMn|8dtC+7tλkZ]jƚo/.] NH ͸$Q0^Kw#xxr{Ѫ e$w>BW" h4X6Ѹ gԥdomGߵ7uoxwMԢeMR+7:~YRl|/p {iC߳4R]6,Li2xwPGckd9qp?Z[diMs-mMrf, as&>Hz M|^(͠Ax|Qx-sk<UKpAS_tI sc̍_7qH-PD%vIjj=ϐ?dj\~+xnL5;3",7I9n;*Ӵφh/4?Kv`&+XC6־Xj yhSfٌm**;vws/ Pn.5m M"DoUP IVVD΋k?ߋkV4׼gwyq#]Cof(O;_~xk]._n5ؤ_iڥuxRG-B8 _}+,HM#@wmWޟOj[~"x~i|Qz5=uUZbIQq#Lt 0=EyƟ? jo'M'SqeʚVrM?[|QeAGYlp0`4yV.OuFyZ;Q<7{gB핋噑D$bЄ("@F 9cT]W*%"ם3ٻ:v)eu1=]trbĆ\ֹh?xPn.5iqqrGyP}'}͛Wf1SRrDުe"^]E8] H@qW^x@\mjzn' MpHu?0Q?Eb )-$4A8l| @᷆j_ }\E杪]:\ެ h?vPĎxgoɟWkM.L;jc+o}4vњ~,h3jRK6v|KĚOڛZSsHԼ7OeEʚVr?B>;E{e_gcc.v,ci2xIS{4.7:nZ+]̖__'~^ͼEz%D2ydnVcΏ?$tH'Mmh]jHcdjAm A< 0i'mVW~LJ֯_E$v`&+T8>\־XjU< hn8Koms/~&ηa7^ FQ*f^7|E}L$jOZ$zǑ|m]k\Džamktkzyk\A |:qMJ#YDhWUA QUPh)˨#p9e91Еz g߉b'"o?14gؖ[0Dit5|L?| 0ir\$t2}+)CO|cԺO mBVfm<[:DV8=di#@*3p?:dW5iٮaoO7msu*?+&IK&|.\4Esn :?.q;0 q]Zxŝ|*0x!Io 0GڹNv}Z8UL~8zޛ\F2 U;vkG;M=6jT#uG,L+n9O" Uk+8lxh UK+EtF0i3"sdW;|/?Wwziy4 :9E i'/I@ c@X8UIw5ٞ2gx7]񏃿h\^6aoF.-R5 6sW5R>4>9վ_ۉ`tkտ/6,*"$P- E,UK?owhQ[W)g?: YΫ{;N=2G?)7ZK?owj[i46]̌efcԖbI?SH ymfHwLWh){6Z%c© å`{:c %!<֛ě’4Ip;?|C|Wi55[麍 4k : ĉmm r{zl?S"m6yQ#%!2~#]xwǾ8o{m8̉A忙d\WyE >dgѮx'ko-/Lj[AgB@Xt|M^cMu}+_[amUF,sS.z­{&ZQ$Iufw3 kOM>>oPsr*LKmwmSP`:rl0kyb2|.⮭:.<#&suG\y@N#]H#Ň|( ܀s^0cC'ď'?u]vU]bku{M/SPW$;])yilz@t^kd '2\l$~!<+/Gf}kŖc&^vY2NJRm-'/<_O~g3'MbZE=<-YLݺدNԴ?cs^ޝ Ċ'i`$d5ǟ>bմ}#R4=NXlQ Ag5| %ᯁ^a៷WGP{{;rNXǞI'L][lʒ?KsO|(xR\'KDs* a='2׉5 u/e[i٭f] AtˆRX9kϊ?Gji5۫TԞ<1o-uϮjzkWڼ7r"gɍrk43ooȶ*K+쿨kFo45t&si HV'!@s㯉_o>_]x#'zI͐ h1յ}GL?$VieZȒ$b3)Up{~!i67:m߃c]ܲLly;BmW}?潗} -xM^wh'/3EO0YAs^g韶õ\xI0Dds&Y9cy;C7W5Ohv|u=/]KKooU;6rW9w dcҺ?9K\mޕt/4OJk[I1c>e 8#kyrJګK~1qvu|{O-yw=ZdZKWޏ?/7iџh2n !͜ ҽ~> mMNQ{QYYn@Upi| S<`,{YiɗvC: A\JotEWoE źŏa [!hHRDPI>n7ԼsKU񶑦iI4Z;i>`$퐏8X >xk~w˫jݜv뚌n07rJ oAW/d j7 8YZi`=NҾWxN}|; -RXVQm5p,[3_tA^cx"iZGܥ垗v̀n9( $q*IħgSi~{|'ei[Xyⰷo:Nd 2Bs(h v}_xBv Kk/_^*LR(X"3vNk;Xwbf+pBI` #+*x?5?>hpjQGq$b9HrlO7g+<Mcm ƕkz-߃-?xK67l6ű}/=xLK~#Ѵ6!*/ݓe x١ZlVqiI5lcHQq7¾Ҽ3@Ӵ6-mmbƣ 9O'm}>Rw{K<7xA50K2`ds99bZ'ctHχ|Cs໿jOGrï攵#~ n瓌~3|7kݑЮiڏ8FCI.Za>1 w-w>XC8Н?zMҼAz>}g|B./QIW>DŽgo x#:kpˮ8PfT n j¯C{jTھiWzKy$iBqEE7U[Vo|A_Zh^M̱[BC];1rXƻcnnȮC gѴ6{p49y$]yZ.-.>_ONu= -SxXVQm5p,c#j;9|0-º7u<#|UT-z?_ |4 /4F\e$u'#q\oiXx^/4/"xr+q|˗f.K,x<W? c SjEuڔF))$(,Nn>F}Fyonyg/#$iF&;5oO :(Q@Q@Q@Q@Q@A?7v\VI\q]ݡ_xCZc"mt<2ywLK/8E%b̲[lFAu}zz-/o2gZ௎G=kޫ ,FhU)S8b%ß@+.T(t)ۼdyWdoBjcDz#O?M4Vv8q]T ;s%%PZBI&(I9ֽ8 M8Ժ{ڒ(>432|0`nc R0[k٭?D}=Sq7+?]5ŵ@յ GgfFcșQҼWO+G^0pUQMʶsR-ͣGt\q֪\ɅzQ-0 ۗm~AZ_R[f+p(֞F ǹ4V-.tGkm\ fAi畂!K1;Au [?WZhS{M2K"+sXle=¾&񟆴{Ku+h 3FIS j65x'[i6Vφ,"4|UT|.w⋿|Cx1n mOR͖&WHl9#qVՙ #*4k~ ҴBK[hU$hĀF`r=kokgOmGG5][Y+XėMҫ(8l4]Rr Cg{U`xLyMvˌmNX`^5h6ğ5o ֋agooo%g5^2 u:bGĝ⮣7:(v{wmh\ZJWrNFA ꗷj'S=][*]E+m89_ e'P7akVIX p#mQہiW>WtC^|mHѿ?yH!A$I5B\ᗆlWPryc[1ݵK}Gu?|1/5Gm% ܠk ΡT+dFke׊l44-]̒3)rA9}+xw\SxY?>@)L$İx_TYVm,_[MbCY g;sq/Z4khtfN7E_l=Iեjφg[B%=],F92i-]GմQEQEQEQEUcƊ˪:gS.ן´1I[?r:iڡ|%zGӟ~#,IhD$26I&Op?go|Y<x^ fKu:XO,,#+*ul' S|y搴ZuHGK_ xf?/WBLJDg/$jB'iqZ~Җz/ x+~;ҵWXK[YDdžaOol7d)EW>u-?0([I# *g ;ƿ_Wë?B'~h\I2'TF+zf{0R%ZxZ_ xcTtm^ԼEyqaeXZ'suwTO,_?GčoⅇMSX|Uuiea@iТmPc(ņr"ᇄ >xN_OZd2P, VQ$ ׮ ,a~pMoJTy+jig$:T5jr `$"o8V!a)K_qOXhO7^4~imLY6̓c,qi~-o^i Zׅ`6$^Z x±\[QTzoN^F'ǯ__KOi~ Dž4z]5֥ʒUR99 u|#};_l#- ]G|J0طô /p|cϜ'6믬:I ѬV&XNQwbOgsӿh~.Eۓ[?o=8Q fj>74?&}9'Q$HrS)/OuO'-\H]\vMt Ȓ(@3Ju-x2QY0|1C!3[[#&y ]5S ;_DhK[xJTG:dD(Ws9YdE}_8{>?gu4۫ > %̑ǺS*Bv._GTv-O|AǾ|9}xPՒ6{6wn]F9՟~О9о7:o|i/'i8C{f}ޫf.GC]>kgOeti:ͦm9A$ay± KzjQ㆝x'R6 Fyw.E:HQ@'wQ՝$mo~.~_|aqCDu )>#3$y$deH#rNkC/CU4xB\ћSMj\*!Y5p.X 0W~i᧋-5ºm\ -BaV>r@({i;?yJ;ZxO_E6][ 2H$a njus¾mkJƟ;K6Ku 0CTD#^Z>I_|5zƗlRko#;;Sw1ǵKQ>$,[xVy@]◸He *Ǐ9*vv&~#EGS,&VVKg[5k@. dev3h~AZǍLqokX @5-O3gq[x_S߉\Aنڭl2QK'>xS_u5 ڦ9$DjY8c$+J[_O5k^5g2 A4ʽ5?'ľ<֖~ Ӯ4|,K4i|QL]&w?/>) (((((O*[D\Ggve;\l G'oӽ\c6N$I7J[US\VM\Zҷ@=IMzȌj{KB:b!z\~kf.2-RɎY*$dO4P8a˚=՝& 8;5xMsF;Ozz;Ѐy~=p:MƖpஙu8,qnϴ[J۵c,x[Vy t#5Bԓk}~sڒ_Q&1XiY[ƱCm4QUQ0)G2ҠX\oqb5SVlܯ"HO̼ ?jQFop3WMh*mJƐNNk UۨQUEQiIGRM87u;E%Uxr <ע:mIYC~55}O[ =3S)m/`jB RrTzw@q/4/4YkkS.,}p¶ymi>\I>ҷ?h|ޜWH! ֔RFե$mf%;K$`9Ko:d_ROL$vC%q;Cd_S]UUe=AF9_܍EtEy]qtPEPEPEPEP[k<^黏W^/=[U-'@V/Eơyneԓ̙˶վTd v+hƇl?_][گ.(QEQE6Hd+"+((( XcTPҊ($I2V(((((((((((((((((((Ⱦ6\@s[W;Z~4P kp(ӗ}L5p֮J ?\UKKە#$7G5ɧuIV\ѲgҬZaW9ޣ&PqWxWIIZ,DJKc5~IF9n\xzUc2f2yyzqvM˕-QI%:e61֙;#EmnR<p2jd =v>=kϕ:Mͦg7-N|1 M5#% GriVry Bѓ7M1G:qED$WdLT7Aֿ?>!|U~& l~k-w i m1ϵiIx Sƿ>.M,-?'RAnH l p7̷=_OZlbI 0(pG^+Ox2CޅMYNAZ\|K?|,dVܗv O?_j:Nk$~ )l/ Ki|Jde# pg_Íibf6|Q٣ti5" G?1z |'w-A +L)\r;ČX=^'z xx.eo]@TE$7 #)B1H W|Fq$0|bYm#-;I<,?5Q,E"nd $8  5Y>!C{&UΞ:pH܍ Mxg4Z׊< O;w_`nP̠ ddM}t~x+T%HH lf e8F4^ i+в*ͱ\l^FFx7o6/_zw9t+&3yqW O/CҥuŠ(AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPh:b?#.xJ~8#3B2K\OdU%L{Щ{#V<6 Ջ8DJ}+Y0珘榻\kFrB3Gz dNeOj9O#ƉQ{Wq%pzU2犩N9Olp[5NzX*N.YJ '桖">YɅw0DJ1MM"hNf`ǥg-3s)Rwo1ϵk9=NZJcnlSV&~-{Q\\Ɯ-%tC!Vk>ٮt%! 'o?jZBɓ74dA?@hQY2FO&?#y}'@VL&OI'o?hɓ74EgdA??dMhQY2FO&?#y}'@VL&OI'o?hɓ74EgdA??dMhQY2FO&?#y}'@VL&OI'o?hɓ74EgdA??dMMwMxK.~G7`>wq0T 5.ڹGk3>L@V Թ'5xgqXcq&>}'Kz6=Z~Z~>OZ>{A~Vk?OΏk?OμW?hOZ.kY~tkY~ub+Gد"p=Z~Z~>OZ>{A~V?yد"+\AֳֳϾ{A~V^Dߕz'G'^}+}'?:??:^DߕW?hg? g? ן}'b+Ek?OΏk?OμW?hOZ.kY~tkY~ub+Gد"p=Z~Z~>OZ>{A~V?yد"+\AֳֳϾ{A~V^Dߕz'G'^}+}'?:??:^DߕW?hg? g? ן}'b+Ek?OΏk?OμW?hOZ.kY~tkY~ub+Gد"p=Z~Z~>OZ>{A~V?yد"+\AֳֳϾ{A~V^Dߕz'G'^}+}'?:??:^DߕW?hg? g? ן}'b+Ek?OΏk?OμW?hOZ.oiߺ0e2Au+QI9AnG#]/xp̮EیvNcS(Tn*Z<}\fSo6F@Hs'm'hub^4lx_r~nY;H^=sR9j3n@BiGsKS 7;9nQxp uW=$E2EdfF3?Ln\sWU-{w5Hq:$I5B@ZΟ̼>" 8Iٚ0Q7VW$+*GYpi8  NZTSaďv0H㱢)SKZo!Vjsڑ_*qkhQEQEQEQE-CQlncY.A HҺ*`}Kh3MM-^'mB]8hsZ3BRy\v@.Y#JԤxY<th44$i$sCq*w+i4_ëvAR[b.#DiF з?tK ٽgKd7^~Q=EhhV?[ x셕 vy՘(sJ~$KƵ &hMt~Qi'v>I m~iZDֶ7.=C.YM2`FgiCxHZd:֡= %ϑc% 8P; /xO-[V,asBpJȒ:1|Mm;&ÚI-VS4ip9ڍ٥M|?w}u<)o=BkX$Q@r.wmÚC=F.m4_D.>QԺ0I6g^kaЕ7Sxk-W@{_)ňӞW$;CDc5|iυ>)HiZh-.QdF(nJ?}:yLVm#.JZ|gx+OegK6a=Ҭ1A,&H.`8׈xYE>AصXml\Ҵ/' er2w+oxz?Xh э.C4 |{Q-GӞXx൷m*md++^ڿq/MsA]'oi,V/"4Ohn!9W+v_YQSnB控m̞뵮+f?'?֗%e`(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEkZ|ΖrZ:[txrC` ǥynV[>\ŧ]ϪOi)NPmѿ3x-t;cG],(# 7E]ky.K{icieSr+_xg='u˿\6=^t*]IX߀>)д$xwmfu9IO3$yz v_V{7i|ܳ<.u'8#ֶ~ .`|KXqqQfЦ7GɹbPc nrth`uF27갏]H6r38]o {Mni-: GrI5mٔ"q֩ɏ͌P㜐*H72@(?NՕ|Dhpa.w&|#"'-"}z;#6z?*YVJT' *s@6v뎕NU}ßz颪42P0ű79H@֦_j^b#k)5ټ%M!6QUIf2s8[-bCT՚ G+ZsXӔζ;+oG?Q-MŜ:BU>#+_ӮӮD/se3J\9DpA`G sxWn"=<4n$e2DqLJWm7]4jI8]R@2zt_Br}_XL߿B.˟ŷ>„ݑЉF$w& ڒe 75 ֥Z y-~|]̋Mz2ĿW(8SYAsr_ m#N9'ſf%VCH:ϴy^~sh~͚0n!#z@|\JKŮ]H$ú&3mS4#yF4f3&W=p~s0Yib<֒j,CyӰ%%tnܦxTxCC&#cj^Zv6*6K2/$xj:i4mYsJ4幩gcY[XidW&!g!Bu ntúh =ѹn/?Ν']!H]lJƜpkp4$F]E8jbhnݻ$ݏL`a\$.Ԗa${}j[iK k8& -F5MG8w/Rw}qmXZ~o'RH#n;GZ:=7jZYEnrUF21ڸ/BNzq+Oj7,34j;+YzKc*'nɔ~#cxDž|I޺>~1ΡٷNyj 4H4cjV&Exu$pM9iƐ|vi}7${\+dhM4uԟaOs++S㾦Ao|uuJQps|TWGTc~ƞ50p4OͿƆ (No5<:ob!R5>h?u10OͿƐ|vm?6NIExsg:-5/OqٖZ{j+?|jYmNO6ozn6i\tԃiM4Sĺom'a)&{=ٖRZZM5R#=H;r6~-47}MIٶoI>Wq+d2oc멺iϻCN;gf{MS^uH6uS/u]r\+MHm?MOh?֟ZjEEx|qí?6=LZ~m4(msڟ'/]LFiqks'8=ZM4>;jhhM4(g^(0fM4GSſơ)l{]RAT?A+'m|.[ S-v{uISf6ӟiSQOͿƟ+-jI>=M0OƤ6ojI7ʏiY:JGKmX:jR6?mGoYN+ś㮦?iǍMhM4[[{ecΦA?ٶH56:uCqں;Ziwj:n.ФIoo,q62nRZ֦=GRVW[]&iՔH, c1OoZ`??6CSiZYo{}5uGMe Fipj+U.d> Rhu[" ȳ49UqҪԃtSjƭBMN_5Nvgk:ʼ#8!ֱ<9dD9ǵSlj~<BŐAbq#+*'Йi"嵤 ,U?ޔW]/eg y35"]e3қ4+]3#}הu"~6tw}ol:}(#N1ZHfotoxx-20.08/images/delete-trash.jpg000066400000000000000000000324031362435004500174060ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0230Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 128 300 0 C     C   s" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?*PfobIg^xKy#foRA"|ć#zW>75:|!hdi4gւ͎>JهbܿCf1IJэS+-^Ṕu򌏭E.l[Y}$G x} 1>xZ %ivF]!#=2{{׮k7X}i1y~ԑll2-?ֺ𘥋.yaoS⹑0'@ }$Sn5]a5$RNHsV}we"6c>lv+埁gG%0_^:.e3]<#حUkWa($# + ִ%_mma$28I Fގ`+_)A7ǣS[F% qM185ho:Fov?~+ǭE}#ėwߛf%B `U~"xƂ%m,{kC2,;0V9)_o>HIc6_c|Y?wڞZ&?dK[IQPYDǵU@/Cxž'5_ jOӬmn{MpJN =@k_ >Jv?QG⇁,^X{K]Z҄Kj>8m/>,n%/gFQ4H )|TݿFMEixҬbm8jc|=#_MzKG>ռ}sc}[8ii8}dw+*5rc$ ,=Rֿi_| >ҿݏHxz7t}7zީos{m l"/06w{59㻏xE OkZk-ZlAVFRs x'0Oҿݏ}?+>8c%-d}XOmh JX5i oY 6)o[twTuH慥28Q1S99}.a\up (@:N=W㯂2e / kVFJ(ݳFKF|Cϋt$4KM 8_ZwԔҿݏ}+^}ޗ0m}(Jv?V/_ck+X^(cա&F𝻁?~"c\+ WWk5-FK+,RF.cps{@DȣV?W5|,LTkE4&c 7+Nm7I#4,ƴ h}`}=WK+(: T!G5\DԪ'g=m$i]sG l]M6h\L[9hes=+}xG|fs7MPXar:뎵z}xG|=.!_x{E}CTh).]v$1o<#E͇:<D|5:5 t[qe2SR_1xñf+[{t Ȫ>b"[G}Ýf~x O$-#ع#cvhClJ*x kÑǁ4;-\ZYMwryfEAӵ{oحȣV?Qݬnp_ 'Ï(cD9I8dO2o__Lm3^KB3oeeea'1_ EzحȣV?SpSH/|_^[[oE2_\A (^919gI=8Wԧ]YGm "{뗌,wFez9k׾o<#Eb"#]E̻['OsI2x3"\>P<]wZ=ηi-Eؾ1Oaш?Ѳ9Ύ+ |X752Wy%'q)<+Ytoglvvb"[G#7o(#xZWGdٜi O嗶1I oRZwx&#B$V}xG|=glO[GحȣٰF_=gj}xG|>o<#E͇:2P~d[Hl-sg(osz~v/!<%MV_xsC3kg}%jp eݞo2|K,zG- ~0|C62 8V&䞛Y խuu;{ow1]u -a[WKEdC-8"ኖ]ΑŃiooŅwozZveGn$vW~~;%Zn7k7eGX$Lh!"o,OÝGZI4Xm61t v᜖1pΎ_3W~[~Ӿ'F־ѧ^çMv/̐%a8_rkо!i6e$JBX$x%:a_/h 5~"gRú*OIiQΉqmY F` ŸoioO5CI$j+0 l}Ф+o~.zq *^ 𾽩x6Zͤ\ GSK)z*߃?hwZlj쯴ҟOof{|#0 3*l q8&>| Ÿw'񞕦5-byC  Am.Mu3fEaWmu-CdPƤΌ`A<+}7r뉥K%ƍ4MtX4O Yxqw qlE" ʁXl=G;OxEACxIG|啍,JpqCw鎜QKZ]?ʶ-'d6io^6"H/Șbȹ +œ[mi\꺂=hwS[²4leyF5?+dRoNz΢KB »dlg_ 5- O jZƗs[j7w>Lۛ%D.nl]z,?%'Ӵ%[F0?{@6$KaZbRjks5y.r3\&O)yr :^c~Zƃ6axe/ƣ5:$qyOGa# 0Xo+y=KǟR--Xt+ji}iKAX{skiw2E+2)؁=vj>5ƷrC6if]I#8,BrTM6_Wv (aEPEPEPEPEPEz6xE/,dG$ 1tTVM֗soO'/5Kv۸2zu{nj/iZ%Ιvlv˞u 7dJ>=ԯ$[J/%sa/r0]HV98dCҵXKu9.!o./]Mq4c6YN @=Ow i[ŧhג=3YDSo|ox~?Ha@&G_",Cv ^^%oBĚl-z'Nt[UE@rdpJc-iwt=ź\jAu |ek9G4-븮coPDŽ|!mOTB#m<I;q,qƬ͑U$yFMîUxnY&,o-^w2L0H#v2_6$4G֥,صkxu}.)EQ"y8']MǏ(t.dm*ӳߟn~ z vCĖZu-tFE@rO QebxG>M9>mgaqy$vpeu7(cX>hwsq %Aw=Şwuo;&&! f0*qgb'֟q+ ,ly1Kv#wܸWxd4Wi'rIe%bwknב4$yGnCte'h\~AEPEPEPEPEPEPEP^qYxLֵ;M"꺍Gz漚td;pr:^RѮԕ}(Z,JU1]G,bfcU^gdvswGNo Ũ.㸹xZ:Ib@/_+YFGqMGIMc 麝u˙4dn^JXe oڮ2MgM5xNA ]k1N-CDĺ[nfX[=!Ӧk7+,Fr\ ʞ+u| cx,Uyh-dJ0V( WUwk[+;[%?jY/vomk0|Aixo-ʬP; %~ϟ-"=Kvxcuh0f]ta_$D~6y S>N|i[[?%x-oHZ)dF){`v772|Wq'-_S[Rl[3 ¾aYJ6JFxJ*Ug$i.l$3ךS>(~-MGphv5h^fYP b9^xZ(z??XI$^_7 sJ/n5{ CfKl|D=7rj͗졯h0xr+]3Z_]IjZJD #ئ!ϐ ;8ʝaE4|*/O<E3+qaGė_Lm..nR; :|(\~Y^EP0((((((9TD$\~u>f]ϲu'U(u>y_Vh u'Q]ϲY+y_Gu>f]ϲu'U(u>y_Vh u'Q]ϲQUo5{+km%;Ycz I'Ұ136Nx^Ԃ GV؄r9u^u'Q]ϲ-ņksC j]IQ@$ޠ~ݯiڎzrX..XCѕ@y_Gu> n'Y MM#4dgޠNTieRH)fW4gκdjy_Y֗ZJ|C3k{_K/P wwzܿME]ϲu'UmF{"U,\\ms.\`] {T^'<#K\wrI ʍK9\ N(u>y_UYthQ$nF* XީxPx;o7]'ky_Gu>}STFOU\cZΡjYBJ['ڍb$vϐc[ xö&sn<2+9eIi ;J$`}Mmķ>"ҲOƌR QkEU/jm'/?Xtߴ[km?vp*Ron _|'oϥzOiVO ih" &D,ͼ[~&Vז =QCf!"3;Jg$k=zV]FW!f{LlYZ ]OcM+W>t xcBX 4d9Hq9oBM2yV;z^[BKd.vDWD}Aldq^ijV KѴJBq5UV֋]b)8otIT+[r*VioTty|]iYR/5v~%h_k{Kjvx m1FDv[\'ټ']]ͣV[>q;+<7cm^Oxj7,:D$ŧ@"}OyZ{ƾާ{rA>c2;Srگ S|_)5_6kWX쭯n)Eq"8ɮB=xZEG-nXaXB2 -&e9W%k-ƍiu7Nm+g2d#@27zb=Oͯ?'r+ͭ|N)}R苩` -9^3zψ4GH%u6B@+#r3ҹ_6lO[X|pj -HfUc4i}ῄ;OY$[r]A%@E>!L6uRF זs%>1Waƒa~xDӴmW^4eBe`kB@W}Ku뙙mLIJڃB3 M>լ/OSüw~%/rKchj7d@BHv[xWv]n,O[ioIfR~lۿM:D7>s4ܶKv!cw8\sY>ZV~ |mO^ -uK[ZԲ0x 'k[L;k2e{p麮WIu 4'4nrsNzWsCw*ߏQE"((((((((((((((((fotoxx-20.08/images/denoise.jpg000066400000000000000000002144441362435004500164620ustar00rootroot00000000000000JFIFHH ExifMM*V^(if%HH023120100Fotoxx:trim/rotate|sharpen|resize| Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((9" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?o\gq ܰFTW![<_ѭXT (((((((hzމht[h5"h5QwF}[5iw7Mۮk-aUPv;~'^.I [%WiH=sZǀ,4Yq\^\²n0PkzWvjbcGL9e|mMkϰ6xR5xQծ¾WE{ύkx[L6Z~Ku!ڰWXCɦ\]>Y]S3Y* t{E ]/K餌(V>k>X[j"Kۂn]ۚM_UEzXnmx0@ƻoh#jIi?Ʌ S}sUmma_K\z+u/isjޱmN[ki5il<#.$t᎙]-J;]- %dYTRގǒ]?_ M&O`a\ aESQE.%Q@YBPԅӑp*:99UjI%̉_@T O@)+sۺ빷 \M͝$(\w0 yvBB  }M{@Opj\]mWk։cV}a:pl֧^aG է.hnڟj]w+3BAw9$`?@) }Fh#*EI7ƮW:zVM>ZvTUg$zwԺź'iPe'koc[&3+J6%v24Wv$}}~H3R{*cFCD8+^cu╶ᡵKf<©uC$X LdU(A;$LhQd%K Y:kl|P/'[r0sjui4PI n8+/PsmQ#+ >i+(OS5+6˼ Ǡ:VLֹ4i1vm#nyɬcm)IKV{( OZCoH~ZY|I7!6/V0GnmO}J ͉ey02ձ)/_]~Ny^Jdu;k|\VtQ+^(it-ҵ+ Y-mQcd?LQh)Iwdeέu qP>P8hf إoevht!ңݻR7#zV|mȻsVlg77/O1{{qTɂ1}Ԫ0l/qDF[w=R9Bŏ]PX*+w 2w>՗r̎u[”6:ޱxK{Gң[ES$>ƬclR% srHJSzz"H;e! ^OmŋJ.Bpj;Jdk Pdζ@3/5R'ږ LOUshr{xY,3,GSuBp3?ю 8Z{NH=JGu0s|yewo3j"=!qJNFҾY  Ψ]Cq/yS%N@J~*g[;or1,턫O5ɵ@O}'QjT7Utgj)`pP)ڤ'dyR6}xi4k3nx?+7W?x~4gƛOPQ^  ?៼W?o?@=E{3M(~_ix?+7W?x~4gƛOP!wRQq\hЬ121;֯пojڕƟgqk([rѸ9]3M(~_iBסϿIkejXClT21+K O5!Xgip ?x~4€9OT-5]` vp / &snA1]3M(~_iñ'ĭQ,%ak]?øEgt'{rwj?៼W?o?G3M(P|R4mBh}ơ]C򲜂GLԗ/n{YtZ9#ҵ៼W?o?G3M)y\|@Ð隶\[{{Ľ*ٮ.[ 5=<NjX<[+7Xyt BNrDuaY5y䓑?x~4gƛOR|miWsp:[n #05^  ?៼W?o?L?x~4€8[n ;![y]q)lgƛOQ  :1\e} $cW {VKd{{*+z ^Ҵmg+\/Dֽ<2g3{uV6D$I̺fHKgo32sr<@mg[-ƙeYmeR2~ u\w7ӧ3$eehǝ'/ Beۏ(|iβ糧Fݦ\>f8ۊdz)cy+IA$8M3tHnl_: c_269?fV]bK\M7o.+1B.{_:槓BS9 CX]*; аϘ8u6MaI5ͥ9vVI+CVe,Av#Mij L/ ^j'6% M45Brσ մ˻nM\n1LbJZԺ7.I :֤Yr69g In. I&[^>\nOJF|ްXk$槛MnlT=5u]{u'(OZ͖o?!ɝrw AM}^WLgGZSS+re4!9z7Kۥ G`k"Ud qvHϰҜj)=E$rhm#',1RKgcק:;I$JP*4 vM7f7OXL2F% !TI;{Ry/+ޭ3$Z$C澏Ʒyfq$ⶭf*yۈ[c),G\HtCrjX(KXLmV;Tr3OQN@Oj_!n gQk{+P\3p)!m=[kphn8*& rr2kWwF6v+IPA۽Ks퐾̓ 'dI,@rqҘ ՁHsi8',t$iY`u:'Ц.] ^8#jKnn-_A)#TwGwU,u㵔H&}~DW\S],KB8  [GŞV;m`,)'=AJ`7Do*Z)) kWjwr]0xMm:w$-湫+ƋO4v[G&13š0W#bFA 9oc*:2a#X~c-}Kݰqe;w4gS"9kč3GrbM/>P1a_KLD'V )ff8I5x]+=%$ŴV+;,]V sv>ծ|)|ud?5U@>V* }.5+)UPjy>:,lu!}MkcsڤxsQ55R{iUMTWXQ}w$!H'[⣛]t|A R*X*B%)1qiWQ'ou_;r-+th`FsJ i 0 &ѹsN:Q㾶%(ꖼo_u:\8} T^#wQ W?h~1&p¬br~_9JCʶ@뚬.pRhݑW_Q$Pp4$ qNB$4_O-7[g1(ҢB#9qRIF9 z"F Zi]M{*@ܻW=j28>R z[ȑ̡.AN./@fޠ.6 f.dJRZ1s;rǭ]]3jq?!\Oc +z7dѬ-v't f`O SZr9.}FI+eiS\nr1Y(_,XfdiHa_w֐?mBⴍջ% $苳֫r+\qTV]M\_7sKϑOv'Q f3Tk=M&H ܞj S:Ϙc2Yy?ү^(HryE%ʔi7jw&"2nU )*`'Gb'XxCpFQ"P%t^Z]\IJݖ4uB,I= u,pv)zB=+n\F4)Y1>dhO S}B@oܷ Ԟ5F=h;jțk1;MH$dnoZtq|S䊏qpF{1]2ƫIeNUpzR| #I&CQɛu%8 1=x5 ;NrIOFb:0(mm] &1\cEb'7>PRsUnk=Q58 .7y3 5J@>4Ya?5"Wsؿ3Fnhiw:qȚ8gT(: x<oxQuEI |.rAJsؿ3FnhT;oxSɵ.# U1x3ÿ61ogkڛ`=+o7?݋4f~fZG39 ұ<i4gbcq5ØSp ]nhb+_@9?x @Ρg;J0Y ք^ aՎn0d3eJg3sؿ3FniZEa.^{yh9!zOh{yqV/&ssؿ3FnhMva (.g?%⸝M3IN5ͺܼ& iv/ћ| gihmÖ3%q>rx һkHoML7_U#7?݋4f~f S-Vd-r\M7mu+wCq>Z_7?݋4YOg}m$2.ԤHA0= .u 0\(񧁯VI,崶iȲm ׌{3sؿ3@E>M|A%7Jn-gO-3fn zv/ћ}یsyY_7?݋4ycLO'V'j vz}G!V CIϯ53sؿ3@X< j0A˜e3E$cԅ:{}w\OiocA'h'Eb_x;IPu2y1!`AZVg  f!`X pJ3sؿ3@(_!pޱ&4T^${c4좲y,MFPw!#3k[yDא!uqW/\U)rzklPҮ6o+#5RF;d!msӿS94eINC6Ȭ- # b$t+t[o7x_jP,UQMKʶ#HTN-nj7V.s޺ j~Yּnl~p$ mAn۶QqGJqJQ%і#0sҹ}\[]Hn Z7 tՎZ+7bgnSpВz6$0![[[$>c.B8Axϥ,JVzdV"v'jB(eKZg._R2(g楌 `Q+n'nB+:UJOKt%R4Q*eNEW59\WcR4O\I}kĴ k7Ƙ[-;$@n8'=J,{iuz^m .lfXlGmNz:q]Wˆ4]൒]FY!1|!HIouA}sERQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQErK^-(Rnw50Z|uӇm= +4@qJsg5]flW7["bڶXsq]ys$'qavZαX«e 995vi7F1rwxaJQ<0Z0[٭`wg7c(8kCMAiɅ8\꣩;O匩>V2Dn3um87̱1գE<ҷDYFRG*T wUfM-|e%I'$Tֲ& . h=qQ(sQ"I,b;x)K&@m GQ5yZE%PGMw@BxYEn\5$M,%@:҆iiM̌ʸ+G" 3>aǭI8$n=})%GI@y'NI0Q\I:.:E[ pِ1J$a)6mdʲ(#=fFtZBq ,1ڊ8's5]%k~p{x55gqB8+A]B{#{VM=-ZM{^ݬv@Ϝ죏‹t Eq(4]:t"mc2t9? MRK F+<,F08w: J+3F42 ;yy$@Ĝ5(-l5KcP wP.Fq\Y/k[ ZM*Mu0D$I`@#@wTW^3)u#Kvf5++}KkȮ-biZ`#~=Hj_s|vITDG}aq]z$un!,v7byuB0.wW%xjYIMλ]\?ARKt]cfB8!ƀQY>a{qygb"'# J+2P Vc2w |Du]B8|=!}C#_#FOTW#xh}MѓP71`h?Z'Fw\\j=iB>Msx|?u}yf^Z[Fr&0N_F+bS|?kO,a'Nƀ;j+˼A kMR % S/kqh((((c^ d/0~s]$w#QBUzL98ALK^~!6ӱѢY-ŵߎ{W@!b>MnV<yɤ|O \Txƻ>U#'>G6em^i b//B pMmvBo\M#8'w(qTT{zPfQz\mֳYP!ֱWcjjڲ&Xe?1lv'u,tݞdve o.TpGyf[9'$[e+IM2[ն3TadX8Cut brj3 #5$6O0~R\ yFt5nl|cK#k_a׭-u;utc(WWPpH3\PKek<-$>JFQqY/!RXO46-}bȪ(Ln:浼5^wڮy֣t#)qp$(㰕hld`j`F*D:k ۉjzFMŽ%аܤAuG] I@%u B{jLq]w͌9EOi&eM Szc ,ꐟrncB|o.#-mgb]= ]QԖISQm> j]C,"+sQ@aS>v1G# I@'y_io53[i<Sv)'—KU͜ZB=--B6WuE^K5{D`c卂*`zEwSP䷼Gn#/Z ?05Q9Zhڼ^Xy{yfFхmqP BF=RQ@X(((((|W'U[%?1TB{&L$mA'#4s¤Donj? Sl⹓;yow$,5ʞ2h L\,|m>us_'aH]NmOL5kp|vZ:e[כMp]37tҦC,M_dWSn-OL8=9W^,rG8fgh;+D9]K{"_N0)\O5"$ ^dM8$̔} %.n_;)X;jp!22=zTksf\ppiʝ&׵/B]˿qOʡf.q[)Jy8Pd@ z{YnM$L273>4Ka SB>wn?/Lhg1 Tߥ2"YV\H1U三 AŽ-B_^~#:aojʌfG(Ce$ˍT7Y#YR*xI洖׎פּXzJ|~4juЎa+۞_/[y :W,Vo.,'zzW5kdH,Z"!$dk/uhxu8.5pD< R~o#K-Q?c[WR 'h~gi~:k[.=&K9]]%H _Z: OXn'ڃ_iƻ:ڏQmm'm%u%˶MB(d8Vf -];06 +ķ7t 7EwwqyibBrHb\x&=vM Vu|=u9/x-Kz֫$K68A-Z:ĵ vڜ'خ<77#ڷ5/\g6, (ڠA|%y%决%vbé _s74֬`I,.8,pW qV,Y{v q1UM2Z#ox}e,k`qY:?çOwcڵ+`\[yws-v-֘jl }.)n-V,Y'=_tAo2Dfb$HdAmif8K;ءr6p'.!vo$:QnH  +%HXI #F*hkN;l۝:t9oKu >[[{iS}kT8'? ji:Uݞ S9I]K|o -K?.l`bA`k: k7-[^Ipq6r1Qok-7k_LX 4n :^~]b5v`axj+SoaG#qq:u5G|cڸǵ=/tb7&d]Aqrm{CغcGrP^Lj_\&yXʻXP1~5oMMHXp_kcA]KonKhЌ~7y?uQE ( ( ( (.AkHIЍ{ꖼ+=LEh5䢕Ό30%psڞ Q3L7m*U.$npFMq7vEytUOlE9>q0p1kQy7A;nz̫{Ż'0Fk *}G^.M̋S|%@=8W֛}ȗ;[9$+.WD qRK;8œ*'4n B9B@'ccizU-dy<"|zƔ`?,H ;Zֳҧ t=/qk6U8:87"㊽ožZ8b6Biʙibev9gb+)ږTKzA䎢=θRTD<6[I$ssӻT!RINiE뱣Wԣ{c GQɑt5N[jo=8)ֶʥ%8nv*8J;nf'8tlWǯzYd`HZޕNe7IDžSvI_Z%^#avǭzW5r MvOc$՞btE(q+N7(;GNEcKqu_8Vիc$/N+EZܝU$EIݜ2hǹ#ZJW>c;AFwQ9mp3Ҹ\+|T-ڋ*e^)R[fX.- < nYI\zvW^qYo5- /cdL95x}/[*V׳&aA%)2{z}.zm>:H/.,]^DˉrqtPkxww2mii* b@SwVK~[ۙYT3qqG/O֐g{6IDÝ=pZ/s\ {>?Q|(~_jBJ>:JVG(ʁsިxX|qh7z,Q\G3>D*\v{>?S&(G;QKގj ̍\ Ӫ+_Z@QEQEQEQEQEQE\R.+MSo .\}>c_@k2YB5!]$ua>&Zm]=~V.8Ϩ p^&48W-f\g4w4J:<ď` CJQePaNAK"<ƐٰKNOL|*Q|ҼZ Ҫ/ oq:V!". '\ 5@ֺRѼ$>Q#h'D"t~9lԳ\PӎET0Ks<Z-ʶd FnUfe' ƜV&69ǥH l.W f+>%u ;Ue5G{r sGqM &x5mA3+o9~oƤwW, d0n䍆v. uݞ{nz {p>zmp$ޢ )>nbP: ތnIf<]Ji+4r@ɖ9ETi#U0H(_iˢGoy[-&;X`% <;c$mlWo%cǕx:j7":I\d *'umsZw:K}b*cp7)F1wQmۯ!FO;AxsQjDڏ{AZKm2P#סG}z|/~"Q-:Xv$~?A]_d_X$!3+7ys:('\׼_j7zl&ifIUF́:קV7|?χz]TUmnlsAWATχz]TU#i0r2q~REP׭$/ *% tRk7N^-L1$&ӊ쨠_x;Pē__RNv"m#G|-4'Q3@!A2S~4PujZec=q,w9u\c+xW>%ѼMieupz;Z\Y+T+ɟ2"o :㏭Ix&Q[+xw,$8k(,q:յϰAd֊-ؒ䲝]}6P/j6(Xִ{˯xSZE:Źr+j/lZ'][S&l`nfKQѲ:UmKA_KR6V{`u.XhʥP0((((((<{9zl,w(?ѯTYǚFT[UΕKaˈ棹 JA>՗u>^Cw I!jcFﱤbԲr_bOcQ]:[Cӎi/!)%jVCp8+n_/ ›k Kf+,Kgzfc_3r$G u4|k>eg{6TPJgS8*=:ap$e,˼6jiXJI'fny A@8;!r)n=}߽6uddžZ$$XF8`L6:cVGR=G&ѵkQsϴN0*~PyS&WQH犲@8ziy%"y7y+F7Jl7j L'+l7=.Njݺ۔G[:nҩ sNՔI4a< t[mHW#ƶ'YnuSuvy1iv9)Ajޡtn35jmҀO`GJq7ӊЋJfu,=WTQjVS$MxkN0k-n{⽢PF (%V#՛T fvx@qNzk+Eam[RI$nqh~l`x䁚0=+i`s4]/?A%Ƒl-K3*.OjOGIpH?`g95Wcĭ#HTi4r0lv6`+>|) _xM"5M:Kh6!U#\z1Iht!wUxneP!cjR}‹lʤ`y${3am;{X#¤hURsm1R[ң ۞OLԡ|*>nxcn-jTv^ʨq%m2HCtoPԜQ m`q@WKX1PIbɝ%9VS wD~tcU8]{Ȭ;mY@jь)ڦԒj,yr=8;%۸=izQR#`T7x̰S1֤ (9'o 9qڹMHf`Tv{oP.<[J ;.k޸q0z㚽9?N@nەxZDaI$8z-DzqQԚy8,*QeIpzTcO9otg~kLwm*qP1|J߽T׹9z浟/-6B27;mf]"C[ÕfoL+6aF!\Ql8MKR!;W4Rs Wy{UŜrWrWW N 6&Sƚö^v}hT5QjX~[QzCz;UT9$ rk}1whJQsZ,7~Rx8MT:jqc ]"TRs{Y;{LenI|e*}K7Ev^`Acw&'&wZ-VV0yuhir;U$ E_h$`QWroVo#&j0wdts?Ha"ڳ{/%+*nO6ER:yWsٮ| sfs=c?e}i+6+z6\XT5KJXi wmVWs=c?lz5ON忞-ӿvۏj}??F˟׫P}??F˟׫P}??F˟׫P}??F˟׫P}??F˟׫P}??F˟׫P}??F˟׫P}??F˟׫P}??F˟׫P}??F˟׫P}??F˟׫P}??F˟׫P}??F˟׫VI٢2u3h.^??QdʸrX!nƮ20#v\X.^W/'7@7>4ː~0eѲz|_eѲz|_eӑf 7ȅ}⦢.AkZ>!#k?-|ĚNzkd [lX^a ns^Ǫ,Q4F&Kד`HaUے{#Fb u#ivSN, V,y_RqrJ/rɺL95i#ұ4H /#޺m=UPzw,DێA&zD,5a#m-@M^۸+;W"lюE$(x5[k䎃!=2xH$?)歮X3=:qI4Gk (vB=JOGev=jBEV`.Aޥ}Us>k-ɿvX@ќ*2\, ;Rai\+)cSWo'7T$isUZP ݐP:ܬ-˹lƑ .Tiے ըyڮݐ+BJc1j݉#95텊;b-2Nl]r%IYc>M{ꪤI滝iv'\֯#yѪ>y uwº:vqXBx⵵Hy# 1ɬAp~9i.cF:Qi&d-OI}Bx";޽cN,sF#ִfX/4NW4lTbj0dh̒?J_t,ϥo K̺ FH<J lW\uv\dqVkv.f+;f.uI+'\7u] m?LE7Xٮ}jl][ފhépwn$WkD3-8֒ BO XOvZVHٜD Q(9#z~b=#NK9kuARml->uJ"C1z?~![C?ZtchW&fxa3+E '"$0r:vkKxR̟'#ڄIin{v0#ǦcX}nx Ϋ\@E\4Lsd'=kKiie~ZIRt.FHNT@Ez5+H'E!pk ƭWdL+OS){tPikyaZ%T^Pqj^ 'U'[IA'~5RxsDOK4m5P[vCPM}{hm &(2ь€|+ʹљܼc>Nd{Y3#oFE*F*=Ӿ BcM|BCU?ǻ_|BC_15Q,UwʿCG#_bkhX(>g5 !}&F!?תG'P}V=*k CM|BCUOc{U/??C^E\AX{<_145 !}&V>?yW#_bhk CMz}rcF!?/?Z(? ǻ_|BCiMk'ԣrg9ߞ[uRq,R43׸>^UUK$iN2J@P2I0c-ܳ/)ym,`b~0bjx,)MKL$`ag 8%G&TScg ǂ}zf>%![!#݌4M*Lq痷F2쏛O3{Ko?ͼ;ֵ^yu?q7ڟ?g{^|rI xd׮_z~j- )WADmm2s$I*ǫ()4yW#GT >em 999Jӵߕu:jU-,-nnD9SEo/WMOntMA{MhlfkTrdp(>[]nV/N)l [ ۹-#xbGzҐFk1=^5cmC"うF+RrK^+=T ߿kߠT&T&ANY$pylՆ70^1[^%Y5 ysK.l)֝%._BԚIh8 v(@ v]IJs+GqֶI g~m*}r(wPP4϶:TE]IA8a#X(NwzS߭!ت'튱`; m}*WԱG`(Z5Ŭl-Aj+8pB f۠E8#c=UISKL'zc޺{m>|+$#$WAdĂGj Ԥf6U qRPԑU}XG.[V1MKsB >zuwC2_5b̀{zT02`>r[Ќ!-;eB*~n*BP299.$*֩GOMd+1=jXb@<ނmPʧOji6+zd L;z~I(#;T]]ۨ޺&h<$g9˥ݸrҫo65[\(8j9o')?zHO9HF+52 N1XJIF1H Q#`6;MufEt%[8`>EdqyWD$yۄː#(z58+Xg QV%:@N3SwOM(Oq`sZ+t y 5+db2?CJ.Iݓxە'%&t9s$ Jrvj;hSN],7ǚ**SwZJfEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP?-|hY29\ɟZxVl8»$nKvx8V-zk-2Z;g;s~UϜd] -*Tilzt~&뚶aמ֩y^jjQ%XgNJQSytWuVV5V n2:`VL(Ay=+۳XrKqd!$g+|׭fʻܠm'kjoܦv'=0)42r@L>dJOzTsPYkt֕Fɝ,:|n(LV#x>fMac/'<Ӯ{j8F6I?,s[ֵt"Y^A `4SıbT}hN-ݿNM]}i)_֒(((((((((((((((((((( ZR& d9hPZoꠀd>#dK8?˷Kpۃ`W;Fs^Zl6כݩ(s$GAPBJkeqp MGPyiӢ@XqNڳjYm_DxWP=16t8$עxB2,`8C}&_c%fGei7|ڶXw-|TU0  2ZswVfa2K8TɐɨLF7qQrFW,f:yt2 O^i*TlzUF9TpJeH\֍5%6W5kٸN+F\^EqO4MboqOV( kߦ >4MboqOV+.]Wk+y%Cр8?4sqOG³_3[?o3\_7Qoo&9)hM?gl>?o_ޙXqM-M?U63zUR7Q@ \_7Qow>c>w>cqOG£E(E(OM?kߦ S'qlc yJݎGs@ \_7Qow>c>w>cqOG£E(E(OM?kߦ ]J747 yA@R 7qOG£E(E(OM?kߦ ?? >4M}/i~)"s"𘙾ƖkY4M}/i~*?]Ϙ]Ϙ$\_7Qow>c>w>cqONK݂TaCT\.XRc8' c@ZƋ8'k8?-|%\*LOhxlBҸ+28ei[ד]6ԯrj1;tbTJ5<bITN㹯iЊ(=碯s^-<s=kм}To1V54ojG_דZK3W\4|g{C(=֭=\UHOy*MWCqsөPrR 5pִQP%M^k+-pڗsUFXj@ZkvnEI_8Zϓ27Z) QsW"*cϥPL AA/֨NMY= ghIWT".FFx혱 I<тlڲJާL5Vd0yaS%  W`4,2Wq"8 nA=)?z~};RUB7գ#*>S>d?NJ3o9SM.M_ROǔ{Ůȓ$Wq\y|Qj:U3,rJ76MtpJ\i5 'gxA-nJ/9F&<e:ܑ^2!rw69;qX& ŹN[;P2ޕN3 l%!3}V,zH~ Ȫwyb\|01Fh9FTg~ 9e4O009&0<ơ@MgC"HH9Oz@Ael֓#[ؽN0GQ p$9 @$=X 6snܫkUi'h!mBW<ڏ0Q:̚dϸ>6 vɢU8 ]pZcTa>ZJW4 ( ( (5:<=u-Cb= # ݞ'5ǡ;]";^}Fku[&xl܈3ۂ)u?tCde,nЇVߢ-ذڥi9F3X~-4x,uH'{Ks<7ːWP0? xZHQc͢4w hUf'r:M S*htCKKZ(.Ir2FjEݽ得vmJjܻXc9;LV2Ok3q8 G~r<l3,W c1..Fsg]M:4"A-3P?OE6>.{JJPrMbVo%m<($)P\|?B)ХWn}޵ٮ/iDl3|ÚǓvNDio%0F 9ިXiqŽ|eVWb)8#4u^S|pZUvqG5m! Ggaԭ{9tCv oQm?\v2A֖k`8F t~XO[iOA<2*yiy] sk{wk=|F8>?m]16DI/.W;0sT4_ MjL̒[X=3'[D4 Erdbp}Z:_qkk]Ar P9BgZLj:mՅյ*dcuY,.txFCI-r ]*I=1;ubo-w *VU't y4BI&MM) 9 g8YFo $m|܁~ZJu]5,ѼL vqLKOc^Y}WL.'#5wqxzYye{Ė?/$?ͧo6$i·Es=8zuaX_^kX-gAO=( t;?-|Fjʡ@k?-|75k{ėrID'RjDV]ȮJU5rW7C5̾@h?9X)l蜒vsȭk}RhG|8z ?.$xsb\Ir1 t{ܕ.ӥC,ghl:6H7u[ u!]23|\]CKs7spӲ)ګҖvUSOs^xrrHAgcq ]%&~\wWwN~ ldkYQK͜ qֳ/_,8Ja~&sPk@Ku#>94@jWC3 P&}*O +?N\[MF0q~ɭ=1uty#yY_dz!5t@lI-BOE8hFU=MYPSmnr5$&GFT+ &Km@$b߆:Tr:BG8mdOUPyO^MIFBQm>c&B秭fHw]΂>w>³f4d ''G7T\ߵh[߼$~2N /qube nZef뻚m﭅E\O I<^ceU9|rZU[*MhU[Hyrqj5mS"ߍ!;VW[tMy747玡~ /߱4XXϢ,cL}pWCxKx:ES@F ^w/Mv*_*_Pch?,;mO/G#RU:G47玡~`=o{? <??᡼%bE[r:G47玡~`=^\Uzο᡼% fE[ˋgym=<#/ hznumF"Ju7$o`UG}3Sw|ۃm-ⵯ|ώFg`"k[cyBY@cjh|8Fy_m7w<i`5 0]\2+8ⲵ?˫xvPOҦ.|G~>\ix~̱l9|l-en4c˷.sJt.tȠ]Қ&͒M#ă[>5ִ^չY7@@`} -ŵ--F$[~֣ysolm-(E$c,p?*<}Xϩ7eazrI͌?QI}2=Z+mF+c6.,z ڻbZj;~ݿ/;UuBQXܨȅ0ø ._iLVO!ixa"2,6g|'VBI9,ԓpK+iȖ7/[߭(RGN ֮G]"-rZ4tK3-HWБY>+uMK}[2I6{s,17ZḧiEI! z}*M774WM﵉aò@Ҁ372[޿_Q_cpag uW]{MuVbW ЩabB;[:iooid`3Ҁ8(2ˍA\op<{Z R%##y5i:d;۞: #q88fl}}W >4S0ة{¼jYԏ5/\^MB7{'%b }_~V,5,(׮aoyyp@^+^}D1Du+ou5L99M8Ld Lڍw?j` *BO`M P#NMFo~O} b\)9SWljWR RTINJRy|O}MYu7k (^*ͰTy:K[ۖOyrI}ʴ<*쏄Ⱂܘj3qUo#sιuc IڸѝrN4̊: Ҫ. (;#YWuY00s\w,{n?hOKV7V͹U9#֦YA*VIzRS4-z9Y<^20F' ںh/\yLVْOMyjC+)jrsQNsOݵjCW3M4@sڅ.cQJk)SW*E8收A(*ӹ\͞EW|J_ \Ʈn{W5 Ffp=3׆Q(G3׆Q(zj?|E}Q  ?ូ3?ڏ@+_T=xgg }/PW^G_?KG3׆Q(zj?|U) KV5;yB3rTtJk?K=xg€>m_k %\.vA}KuzQV#w/ѿ^G_?K\.Wzj?^G_K,H^G_?K($A?3׆Q(zj?x;{ul5kw:fƿuA_J=xgg }/W40iϞ1kSSq8Q#DhqJhrOZ$V=;SNy㐜I'N*u"e'H4<Ӹ|L9֨ h*ʨ =)= uFDNxr&@Zޕ/ ohqذ5,s唛vewtM=!2;3y5F[S.' ~Qs =Y`Ld$Trr~2#'G+pqI*jJ xZXL\E''ƵRVMXץtzUl]ڼj1C% u<>:VjzqY7}*j _Dbg. " ֛^fvkA6`S`v0L1IK]Ija=jE#=yqH ֩vв*0Ɗ9P+BENOӤXZm!^]X۴$FN'K[;csys YNO]n&Ow6A:χ?tdA$rgk'GR=x9[O[/&JfMۛ#4t+=cRIi"+/# ;Ex^,-;.HLaImÕ |[!Cg&hv;2c#ԵKF컷,̌j_x1esw2ũ\@PO0P U5]FIӧgX- ]J^+fM^MӴ Jk-ZҭSSĘ3^+ļDPj'=gkm3*4Q(kF{\"\3=R6bSWT Hؙ+k\#uTPFD^̗hsIFI;V{dHSR/5&қ]MTJ袊D'j}Feb=)`#i Rm54/2 ^ڟ{cm}kqmu zoR1G4^ߑ4}bo 2ͺ,[;E$qjZg RCg"Kn:cJ _?Ə^ߑ4SD][OyY2>=7)nnFۋ`1TqAo`#h=no9.xpSҩ~5KkH/ئp2w=^ߑ4} S.Y^нy?~GUυt[q5P(Ae g4+tè?.zsl}f";(- x'71d$sOUQ**!r  60:y?~G _?ƀ ,例$BwT_ i+jiRݪi9-󎕥 _?Ə^ߑ4y}o9E묗vQn;Vm߂ [[¦_o_ֺۘo^ߑ4}r ,:F /Qb5V1$cB𖇠=Ɠ`Һ%dr8V/?>y?~G- =W~uӖK*]GMt,zյo %Q3'xQp*^ߑ4}f;gW,X2џ6MKQd**V/?>y?~G}Jt4u;#RH9=yjC _?Ə^ߑ4j`UViw.2jSSČ?3^+üF|EFj+3 NJ{vhBu,s5#0nmd\mbrNyRI4x-W7rWgsWzVo+w~ S\sJVweW⫄=HRmBq4T-#;_& 0QF.~\4e3\ěWc+ ;<)Sh>Uňز#Lj2e$xŲ[E9Wg6␕bUd3IʒMbeqkhȾnTN7=N9ydZl[/IY;@>p1jCf)+He#"ZNVw0iUO"GMH]xjNGҡ|>gOawD%mƆN3ǃHfJRrIt4m<jշR#{'#63j+=C&BV_&k*BV_&h((a..f%g i7袊@QEQHJZ+\V!bD(qԩZGgs%m`,өeckŻѨMnzUbow:a~&KvZ)(5iKaZTH[Q[뢏Cld?NIt5fhEQEgh׷Wq ,96(v c v( *C5bR7yh\| o2\ARE ph@vBJ(Q@Q@G֥eOZŲ~!Ԇ3^+oJdNn\ƟtӱQI&fYMJ9 u#LWͩޕ!zS4G" QZɭk̖I2V}D$Kvq\~ {TvtjGި ff,GR8I^G<Dk3: I%n-HTnNE+ơxMl4KȩQSe ֭>AR5&e5g$66f}J20GҤ#IZ zڝZ&b4i #< "dqU홊m=i^ J(E$U{w5#>NI4䃃D_VKnusԒH͒;#*wޡ/K$RUBڝn*։vgSJJP52;gM n.Z("o|1& W'> MMirֳr5ҤkkpH y5i[$ұt+ռ+eQ[Q)vw0i̻(Z@1[i[DTa=`4Rd3ިO@֥0X2d܅'CxiLX~];V0v9x٢?9Ͻ;}?qϵ(,\EXcp{Mϳ9FS@ [Ǹ {@t%x٤EFe ?#Zo-@߇iHSjxw4[oǿcp2k&} SG8ռ!("88#rxM.eEO[6=}*mh#CF2}]6@j%3*]C,b +w>{216n61_4cje e7mp%n46KHna[pT8cӤ%*@l'bchͬ% ૒X} '5E-U-ތ,.@iʃ=N +uص.]FHZa( @AlZ*n" 6n<V'49t8a絆,^Mˍ䳌ǜRrQ&m&"!ѓ;Frx>Sw^zjF1EPij&*9ݝx٢d?w_$泣Eԯ468輲0sq4]^]Nul /&Y)Agwa۵;F8S<'gi=bWdǙa<hDGd!WcȪ3- izkwnnA8^*F:|%e]k` = )gc0}8Xrz+VVS#ZK~GTsgQߐ3_(O۸gvƘ5#.1bUmo"gI̓7 @ãgo烏ЀEX%Ґm$dTip[[ҕbXec=3Ԓ{kVuі9_F׭ {uX̪]Aܹ9VtA<[`4 PnLvX>je֏۫W?|$Ń+$gT/R)Kz qq6(mϮp3WGX qn#UbY*@=b%(|R FW?ѯ|uωuSyB5et7f SSq޹5aT&nnr*+<=v*l\k 85ܳRxu~#U)n⸦w~dqbvz.HjJH$^I%%f$I)01*3eegj'8$5RLX`*/43ҙ]͜x%tԫ%Rt`$«0MԮKo"dT bA⡎,snqڳOH}.JKg9yh9JxfBJRLrvhVCLMjV)АAnT1$OCkϣ[˯+qy~ZDl93Z4#bn4[F @%OB2{zB"ӼUk[ 1lY6d'B2zUhJdxz|CsV]\Y6%) ik;Ao[g</R=ꮛ Y&6R[)#‘JsJ},J䶷7ڼ67;RN:9{2_!s)0pyXY^B)cD܀T)|quY٘cI36"MOqE>I5jMfލRѦ-ի+;0#=GT&jRhe@͎cRN RērIp&3ǸoĿbsM!̖~GQs[zFE^_SJ]RJE,pHR1#t>ך!ꖚnNλkmq;xb2=qjdEri)$zj_gk>`$l`GN}= zJ8EԱ]dw$ku-Ry-]Ospquvwo^m;۱<];oKm2[# (b8,nm ;~t*qֱ ] XŽfuZ ٷ@&W8m)=QƜuI]k6s]Es r`uJhB|[km$KƧqA85izz~H]F~('xWn4eCi޲p)u!V4)$]\hAd3ڶu_yGώUmM m,on|v#kcW1-?F}. sMXwnTHYT2.pAR嫧`WxvUc,t ŲXSQ*,/ln g|vdp `hSB*~l1Q:隤&5oJs9{'t-V~eoǦK5 &[\-3;hZ5@F8`M2cevTO$,fwM}+GwZ}.1Zg+M.eIh>V{"[-@iw77dh#+HFtiMEkC6$bICcq\/+oYsʲbk g?·u1s&}>^dwI&Z/tmrY"u.Uc<${PRE6ܻTܑW3ޭ#nbs8~v溏juP{{2)|? X*UTiyVt{R[?;[./m4r;IFszާy,4m'bc,~TC}nc# R]U0J)sŤկ~0tE6җ^x&]d}ϖsc֦]Nzfd*x'֯$%x681a4Q!ϵc{I{V%Y1Zya*if O֭':Gs?!^àwRjU`(Ѱz-FmڧXK.⿥]2z -;.>\rVBR՜4ao᧔Z$m T VaҰft-qKҴ#ظTt8zVm~T`%8R#ҡ!YwcehNjGq)ٕˠ9+l'jԆsz8⸖[S)NKp tܥ($"*z kFD 1=Ȣ E\iDȐ05IDrV{dX֝@ұ,<(L@kJeq]-%*hh̻QEr-'ԣ;{{@Mmx9$W5> 2vzŴW[\)hevGPd9m ~ jq!G>9;/+O]N`Ѵ!†fBG<8>Ɵ-ǶF-CApcy`"/ mȹnKFFN:Vߊ/g8l_ %V3 ̕ᄁ'hN^oL1nKj6_ikcn7`" >]ZPR7dO,qݒʣ{^kKi`Iy.W%s8O_;o[?"T-~\<],63kIi$̋ԑ+;/u',mNueX1czlZmA%j6Hc 0pTpEx^ MPE*4S 0G]oOc8?$߽A87{uy7˸4V7O1!*c[cܗZ}bI9?8=,Rf[BT>l OcftHM8 ˶y#+T<_.m5wZ5d]lxao72YmXjܳq$g gsS7ZMkdmeJYQ^Gx7]}d`r7s3׹&]L;Z&G^wpk&eoY /Ώ)X ,z`9̞6ԌQ asWZ |ÃUԥ>+me[R־$[X\M[+;9ה\k׺uqGm!i`7M֒\>[8ʃ+m4[Y>Wg(X@v?/'2N߯uuU^iiZ۪kfqk5 \֝ 6[$.T ]ZS..rόqV1xLI%>ZgEv hiip |w9<xUZ\국VP.In߯WCekjOe׋1[=qqnkJY$]%Xa~R.EloM9N+l &NIaut pJ1'8鍘Qa/t>ēM9W<7zV<7NjIw;s]WK}3Vn. + @92xj{k$]68(a+FqjuZɯM8 ThºV(7f^cpin9b{z}3HC.`e{&D7O>V3#IEeؐ7p[: S5ޗ:ES_n|,6e*P9=J¶iڵ2K$PJ+nb3邒Rwb7,+3‹}<+߈ceƹ+H\RwTΤ|Y wB{5ιV-$kϵs-q&yv;LͱNSU*wU*\uZNbMx6Aޤ0rO[jdK#\` omչ E &Ȗ"2\}+6V,*HZVVHrhi r4<p+3qh,rwj^v5YJ ZAN qByḿ4NE0bpk9kVWaM2ybl4דg4˒%Esi^ 4AA;$uGe+kE iZx8-Qie8A)JM:@ԡ $T$iBU>Q'mp1Qޟ05JV;&CHڢÞp* V$  CMЧ9#H"dqBYb6kv04fKo.%Y%̜^zӵ8&d@$}##p<"Vb\ en*O>ĈNsg5Sya;EA`查Oss-nXݴG^Ln!s,cp|ÐzS7sgEXgwndxP ;n~ ?omjp_. G5ml<0u,~a9,4{}.kws[v?JZk9FIkm QȬRN@#JyΤl,4]6+-;ǖEiK$&Ե+m:K%# 0AӖ= l:mbA5l"=0X:Օl).\oAI5>k Y:Ҵ,ɁgRq-5)oLks# q]k V<3k"Z"H䓑۩\1ۻ.NiEX|5][۫)/$rŲ랕MN*u͏޸翧_ʲn$mb՞+hl{89# jj1n1{[di]\Cio$2,P7;QkѴ8źl崚SXf}#%H;BUl AkJӬb[-BA"WɄ>\E&].ZZD!o2B A 7F9Ju95]8[޴-o3Fø`AI,76n]@0('$e9EZ9{x#ƱZdVQd1'm)y>$}aJ_S֝i{Jrwbcd~HexZ2 튣Eė#Wf2@~Lq+t-&X?~d_Qש]?3&0O^ý"+EќǏtJOG緷 қwkZ.%Eql W q]W@MZēYH-l:v:#94nzHm g Ҷ-`֗!'Zm׍`٤ \ݒsu1yđcHmURqp]L\8/=nt@ urQ?煵?Q&D[iohL!P*yI>|Y!ӓEUf|;s늵m%^XCwqA=iJ6z~w5Nun7I􋋅c(;rF$8Ϡv$Hfu.~^F wV~L6` '7~ /\Ek2r'ӄ4gRI/Ekۼ~k F[I+@0zVNm!;!E|mHu$WnxJ/k9%-p*lȸ<)qryRp(, "z$$~&d摜H9枊mrG|0O_IlT8MҕwWDަXqM$F-HUi(< Qկr{"RH*3G 1o8-z;hgHT>aҶ~ƌJzZc9IT\AYyִlZVqmZ%UcJtw)hk&TϠNUUR:TKrbq+Uݝ(oLuzSA`j来D0[w*edb\kPJ)=Ezt/WXɪĬAbNr B+N#jhF*#%݆cV8De7޵h9_QPImMq5vĻۮݼ[tSRqف}^c}jZ_i&Hvʣh;xoBvif7Xn<{k?^M#i&up`ֺ:)?y]i:&7 @߼܃y{&/'qydր8Ǔ_rMe[+J_ՔN\/HJRWMETg(_X%m2ן(&,;=?ˊw%ܱ*)7={bHOzFIOqOI[M,f@rKloHigF~cc$رA8-[:,׳1'_[ [ƼZ`s(QQ61+-V(pәWrJgRn2sT^3ISpOLXdg +NVfZ'lQ@ϭ2L\˗DJKqR;dP*!ꥹjݜ;* C;]`$s6w`hYڣ|?4j?G;hYڣ|?4j?G;hYڣ|?4j?G;hYڣ|?4j?G;hYڣ|?4j?G;hYڣ|?4j?G;hYڣ|?4j?G;hYڣ|?4j?G;h8%)p}+?Tϝ?ƏQ>wሧ b\\,Pwjܬyixɝ¸Z/pW˞5]cw'k1_8>%nd?˕ uqdAyŃ#^,d^eȹrLGldԈ\R3Hmm=&pA#9xͪO90kUM &Ҕ Y?(/Ȣ;+LI9by=맲+ΫNMs{ p *RFW$1tk}1{ȞkG.GJysځ8U- jPOJ8֑3W, u7JDocuk2T&cH5~G횆EsX_k}N^}A1|WJm*QG>iX&G3Wn-u@nVk(eԘ^LNke^돸Xa$֗S$P2=軸2YGzel>RW'֖3"S5ZmP X;U>]I>ì tޗ j=1ԧ6ܵ$SNB);I\9w'IҴZZilnUyf9b nօ+Eoe/0K*q[u|2%f(Va]] ( ( ( ( ( ( ( ( ( ( ( ( jezvьV^-z[\$uvimISS60nAukQ@E3 k1}۳`(m Qդ[&1ꖚqC6}:;X[6r˜@~4ohZ̖^ ı0vHzH>^ij0\\F (#{ם[]?|Ywr&3/tt'M[JΟ ?)R$[VTQ_>'z|?k!_?xT>#d\b{l`(GvOP`8s^r#|ǁNT)c֗2=i Rmt*VDMڽ8.NM6Ҧv&090{4\+#4=J <7ҹ# kkF\BJ޲FƖ1k Ձ6-Z;{^E5cS:V1v4o6 ޕqSiCFU>lww=iMW4!<=k/wlmTnNWQд7Oj/c,*I-cUsh)ڟ%:tw֑y5-żKn ^mkC{mK%,b2*}໲7u%~6zw1y0[Eo@?SKiwm{GéEq?&p~^x[y`_zEhF<7hJWѰvk6nP80ikd& FKpbI԰?@s\]4×P6Os$+ߖ!>as'.|5S|n[;{tUmmt1^_>4XΥwu&nl#A99mw#hig"zP?U/u2EQYT$zMr_4{K{<%lfe"̃#}j_'hdկ;+YdY dI/4hUm[O\e \=G<[.ZS<[ml,m$Gai,nj<=z-h0ǹ>Gq4ɥI#FYiW |%}=̷ [DαG3"1e{ 압2FA uT?g\S CVbNmsZ<[`儑 "I'𪦣NaJrJ{ҌcQ`=)Jr9N0%6ݢ.(O aOJn5z<*QFmb*Bq5e`Ȭ$z ~F웽sjTs\$;mXFgC n*LsG0lx^9 JsU :GKQ@ 5隤/ȭImѸ] M;htVjwdɧI5errkE(T$ըk09W%Z7Sңk)B-"[4^@:WC+ݏ/N28y=@(*䭩Ux#uN3Pܥfbyۈ_LSrZ=jd8Y6ZUyϰh4)tM0((yV$Ǡ *ƕgq[j2Z"}m ׌Ko:mt]y[g_U洏Q VF("e2)!ob9.\L6V?g9I9=OcMl;0JLsȈ?`EG-gMrVcÙbp2C.xSLG"uG qfO0g*W<@oŪGEdqh@N~`ڭ?ͭXs_5vzqҦkZ-!XfMV2xLmE8fȗ8 >y t-TZAm2kCCXVMH>$^KOBZ񶙤[s,H= j3]jR6\A?x8 +4 4Yzm1~xd\4a$ir\f,NzzTVi}weZG48yt9C{ a-52%DKر4VQ˻>!/`z ҤG a}jVXEj\(AtZk_[IG R_iVwY=EA4yX gs9yYKiiukLQm\FIb O7[#X 9n;GKe##J'#kx5D>wiO:kN]v*9&Eh (((KVT(|cI2F_+m`sI;$;r^"v63 A+@E7vȾLI|S?5*rwIf+QVBB9TcFF3SIm4lZJe˫&Vr+OO桲'hN3#'=qS3}cEx9 ¯ZXuSǃE1Nqqme a5 gz;:'n*ˑP} %A#*D#ED-W'ҥTRc(i3umA`zv1Fj;\/diB kuWWPHZ[r!4y\H՛fr`\G>s)lתFeb]N!R]TSHn;sW g;k*{{.RsRZi )EM:n!Ti#ѮGZ(ǰT"tK.O5w'ҩțMY EZ`Zt%gԗ<2PrɓNu$TD¯Zkc'UM O$n2_͟}G?,QUyϿ>(W屹0 l IyϿ>(g|RjNڜVJqM$VY:x.< h-g.*+8<צyϿ>(g|S&X{ ꯥ}I t]i0[Tc&:JvZDK{xM%ۂF'Fg|Q>,a_Iӵܪnlӥs_ZhX[X_:zW}>͟}Epn5}5Zi*pw r)xVemeb.)C8͟}G?‱wZ]s$1Cwzlm*(Pw/s+{C5* % 6{oMgGa]?6=s_GkQ#IÑnyϿ>(g|P3thvwsm 9>K8b9 zcgNүa{; !)?嚱Gg|Q> /h:.t/ZNR fGmGMj-Y)0`]?6 \  麞[>geQ>k>WJjWSrI!{ Q]G?6Wq%ec<.H⹺}>+1 y,_w@]PyϿ>(g|Q35©mgki%]%"i $:?xGY+>j1Yl^>͟}@s;Y-Ycwm"+61L0} Tz f͟}G?XEWg|Q>e*?6XϿ>(g|PVf`]vxj1 +`U`}OҢh':?#\؎{~mں9C!kмAzm-I|錥ǚ)dMմ$Ӭ(#zn1FI0\:m9i[bX.A^vc)]BVhcq\K^ũI$q\ z^3Vu.:~([[yh_Tڵd@P+j1)h㸀J+"U7cҳqrvb.9 &#`pEnn\unELR+ ) X8QaAޫL :R<eE%hICV"jsj mG%nL@[S^=j3Z.քU-+T3}sEWANOӤXZm!Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Eu0nԵGZtUЅT:IDca8?:_7Vq~\Vo'5;{ 2(b Yɸu'|A7mⴱ[xfA~dRp;7WQt<_yrڇ5ZC+ѻ'1NO_zz- }@c%^'b-[#kemq^sg޹Ro,Ƥm~q!I1PO8=k2Yi ,, @}oC֬d~.E]aPy9ȮMUH֎xz{Vz]/u$Iɚ+}el^;彿"oZd _+fU{yX\:kKOX ' nGT xVi7 ®kvI ҾjGB^GeLO8/J+gO;% A\nInvH(zijC} !+T*v3qZVAMYZ{e! 9CAzmGk\Q ǨЧuZO],%q$jtnO&T /ʴXROdg)t#ڍ֫%Sm d`'n`J+h۠Z(%Bņ*_7&ǥTTfsQɥ wP''^y8Sm2EUM+]f7Z)$BXE]#,}uEWQe9?7֛N֛HaEPEPEPEPEPEPEPEPEPEPEPEPEPEPU5X[ FO պFhͯl,^V34YYUGNF30%sΙ8㰮{j*@aŤ[j]X%1ׯHizL:fqkuH.1ܕ ]M7wkk4QxVDLɐpөm/u >5Yܶ,Cjŗ|-mb*kFJMQ_KǚӦ}+ɿ?΀{ʼ䚈rx lʖm^OZ?0_d r&Q>h,k=p+tް$?-jy82v;|!aXy`:|_NMjjҎ6r_X>`qfzvhBHuZ\ki4#T<8&=j3Z{?3'RT| V2chE'G*崿0!ea_HHյil|N:WkN5>e~kE dWf+EBIu.7`ND` &ja5_K]Ɲ%A){ݧݫB7}nx/@暪pE{*M! Q޾4Qq>qP4WѢQ fotoxx-20.08/images/dither.jpg000066400000000000000000000464551362435004500163200ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0230Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 198 345 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?GL]";{o" N SV?5K紟ѣSi?QQ@~?4}'j*(_O=j{I}Si?GڧF%TO紟Ѩ_p>--nKo=H+HF.rQ]IQf'hTOz߆>:[9~$#Eԫ4NyΉ2z+wCů iz4VR.l*)\69i+۩/ڧFO=}k3?_ţQ6$~*B=Y7@d' Sb/t)=>R}cJѦ+-nZ\Ww'_>tڧFO=}.o[xAMSO v~ȥHRI;bq?|L4/@ԮlnY^!P =i7dߓw0֮i}3矵O=j{I}[7qOW59~ &xNXn%2>Nzq\fGվOa㋍k~4k- WGn*fjkZj'hTOYg>&7:zIg ue rymdkj7o*QFe.?z@w>uTO紟ѯc8Mg@ֿm6E44eH@ <^/I4Չ~?4}'j*)/ڧFO=EEK紟ѣSi?]?)֩{61Kd/4q sJuKRVMZKIUPGP#TO紟Ѩ~x:ZK<6!B*3  b*mc2.~Wok@ z1'紟ѯuvvѺ/;C8ϙ0rO3_M>uՕ~@%?¬3¼7WB>IZ[A0Эa~V\lx[W񢾈_t|e #Iú>2φ$ s*+wG_?dG;/i2O.|E};>G (t|e #IϘH?€;/i2O|4'P>btMoODbciqn}>G (t|e #IԜZkRVeL"/<+~<בwlo% HWuyx#+Ͻ$j1l;/i2O|4'TGoczq3cᯉ>߀?ѼeZie]]f ̹hrGˍv_ŗ}HxZQZ-i2A++Z&<`?|4'Q>G )eeooHM&W9KNamᛨ|]Zi[K!3n2H,[W O]5m.\&/yXq;8ϨQt|e #Iú>2φ$ +5[}o䌏?yo&/o?ꏗoYjFm[cϷ}dI{UQh]'^ouɵp7$Tv onu~#~z KLԯ|45W ŶV ",t|e #Iú>2φ$ [Z;<ƫ/+Ehod Lq( :jw|4'Q>G (I-nUi_|EiXOdЮi0,і?qt|e #Iú>2φ$ b>bt|e #Iú>2φ$ Kמ[Z7Ud2)-F66V|-n!i,s^>G (t|e #Iw? <~R3]YvrP`;?{';/i2O|4'W6'OJTjŝ,ml VQz^kCտi.K-BiԺ({4?1#$dc|gy|_+ND9ڃ+/wG_?dG;/i2O7RrmXڔhJwk]Nr4W7NH F1 'Q^ꗊ&Ц Rq"#=c֬4 "S5T&eyUȻ/w_3xQ־'?ר?i^ީ}ЋoIA?ڧI49j'~<_Oi!uӵ +][UxkLLU!`n$K~(_bwokKk *c&3PyrCkQ7.4@Ma'j--#Rѵu;;[ߴ*6AY44o^<_I5+NJ$uj[t@#Qf`'C2O|oc>ӢJ`px ^#^49XQHGP2:zԾ6}:͑biRx7!# ].:&w"FlRά_xk⟉Ӿڞx~EyWhz>Bx R_>ݢUm|?_x?5^];K{  hEiS2z9L|Gt 1ȯ j3`H8G_J7TgYk^_iϵ:@r2pqҩh7Gkz6wz n @+tJEV.7."<o4di}l!ϝkOWzkSϻbMF d,9Ҽ=].O~%eM]Npq*\ŧ%">ԼMkVzۻ]j\UU> #= gBiWZN$Og$2ݘ8 Q9ǷLwߎOxᯄ>-֗4]7#10(HN_3ƃCYfrpY@Iq׿;K5YAio6 iB6!vవѴv,^Re͍ٷep>V破;Il㑂:)dPӻ~aET((u34T ulxۊ'>:R&յ·W[ew A#W؟G'\,VЂGY1K:Ư.Hr/o ogi6WvGm69خ(mʽՄ. 1K6m/m:.bXTM뺨U?^?^M/~_Q[Ky *#Wf]'h/þүֵK}4wyNB{>}fis4qè[ڑ7lW^vlzf4>𮛪i?Ӧ 0*\"gbqױGxzX/kDѼ8κVȟyD3͵/LVSmm.bdFVtt=’{GdX1I$֗ g [dk:SYƑ{Dె b-V NwZ.:ZtD=򌵻21ToxWTon+;fmu7 6Cpq]P3MA.:%Qѕs;ZC<zoJH]BLK]r==+W'/m"]M_^#ss3M‚O 3ֺ1!yQ7p6}) I 08 mjSQei.#%*Ry k$uC!eA xM+BҺ[B+:{$(o,dWstpl|'~cxxt0,̟gqzo_h.YxbLӯikd$7#Z_GtH?>|N3kCA)ڞ5/amU60Hyuα]F塂IHUI1C|%}_x#ĚtLoxdB 9#j>[Mc^mQP]@ 9=+CᗎS=}}J.' 8VOZޑgWq_ه.FC99ҫ۪&6c%6Ki}0N}2^[ªO+ gRc9◑B\*APWK$W ~ѣvwbtY i4o RN-|Vu|"ѡ!c+ʱ5\d<^]gY'N~hm-K FU+ݎ0l<5> Nmkra1͎ȵIOm55C濈 @NX_mYK)Hr+ yj4 _~1~ eu!g>hז,ωىJss ּ\ W6:Jq֖-;K/| | dž;M.5= 6Udoo(VC Dx5?nM~Ijޏ&kzE)7o#iV=ORxZ__?ڭ<1Sq{k=BdP z8%M/K&iz tO^mft$HWxJ`ȯ7Cdw>pL<ƺUݔk7U2WmO [xq<;kK]7Txԥiar };CE[!ȕA 0[moo|\V@lJm*摃f^V ٷϋuojmV5M{ėV -@r+0'k#Z^Pt|yu-]YDiu@b҈1VFk.J߇o>-/fnlnbp=S Pc0kJƗ|InmO\G&&cI 1 r H񆡡jq.w5:"ʹfPp6qn=Vۧϔ>5x.ox> ^6y~#B w3HI{ k<[qmB;B!e7lFxavTpr8}EL5H[|!κttcYuk#D,O=4|ME?K]XyHERy pq_wZ97'.QJ*=R] ƸU/<=a[\/m N,,)2Fϻ2}+A/Okؤsw0~c5T֎~G.)?_sXxw_MYGQr2:x%M,4֗1],"?jyS.璹hj&|9-UxGT> YZkpnaCuT!b($0 z쁯X^M/NΉe-xn{{UG 6\I䏜t}E+ERbGן|\%N'`=-V Ѹ7R1Fz\M_;k6:W'u[{}F[|VR@#p##l̯gn[FOY>PGgc)x8HA!, 3֟ON=5Ɨq#ItX\!IWM;Ś|YZϨ7=o;P[8b@̘݌py^M+Mu{]w4W:ۡDo2@c)8SkoU;3sto/vH˽uxZW8cp2j7뺵mC]j.+!FPNA ȯs>?%Ogm{sjyh+n@0xڷ_ƞx|]#źNn ږBͱJHByo#g To8Yo.mFm2+`Q]qCHȦ|3|HןNYx$x˓zd6E`e>=<oxvP-zLc\ Jw8 Ǹ._F_Zi\p||n9,rNIIlfi\;\3@Э5]g^d3 # ӎ+OhcC~ 6]ѹ m*l W?8~#X^iz݅/$uu~I9.?l%aWT?ĭ|IEmtŌc$b1{f:Jr[=.kI/iZ1̳C}xT#t~Ph 1pN+ͮj>M-ž^2H Cy-w C~ ᱓7T{fki-%fyR2=+ƯxR|)Nj_XԬgqp?$lP7cڸhk߃Џ٫)w:X!1?09jմ X5IDe۶[FL©>Q]_v&kat+Jya~s?2A9m|%VZ.I[YW)j0nv'?|^|>* Vڭr hohv'q,M|1|> >p\EٔKrL?>;TWt[_{hX1>iZZ}$3LdDZB>+Y$,xCmr0TVz֯b~/ cD>p"Zco1mk |eq71>/gi ޙ] >V~w___i_Z%Bf$DPY9MCKg#ZEͽLj"ewMjdA f:?K࿈? Wo|zo5]bE8#GV >YnAGY%jD#~3x: rS_em~C|:T] i$%\S? b1'f^Qa#İ|p{-1 &* m_4# KKxO^!:-A# vsB }?SῄK}j%K=RH⹉.!g{#5 {-cy,"xCk<[=~J&Xޟ4gh1G!♿ezt>,H|*fH(;iuKyEx_^Nz-$qĹ{ŵddf֗w~#i:m:̰tʪ#>RcA_㯊F  MX7s}4 7e`r;žx&^ӼK;M7[`+IPGqKȓvǴxǞqɭxUG#e\p b}&3x')& Z~s5>&(©$oσ><kR:Εj(Iy+(pW_!|`Ծ) ˨\zmXs$щ"vHcHJUݟ#F~xEu[[e·G&1e8 ~>Nx_YXԴ2]ItcE`JH8  ƵSZQTaԺ.O;>ؐUyoS[xwu^6?crV,~S(Iݎc!ZQm_fi67W:G<7bm{rnwd&Hje&_QEAf?xv.'P@dXO+x+tB/I_>:Ҿ!h𦩯 x/{[`_`ݔ'`=~M64;/Bҵo ]}46e,F쀆\NysSԮ:BӾ2cIUw5';¶ \kuo[@^5.]'[ c^U:֥i4Af!Jʥyr0bVs_S({Ge5o mGET0N#v(vȇ 8o|mcGVl޲=&=6(?Mx:/]:M{kIl&"Ef[ ~RsF/|eg]ӚNkZ $ƨN82Yо%=78x , O! Tx7:>|a$rЈLȎ@`ڸskgJI*G îkw_ >uO^j5Ү,@>=J+|dz#_hZ*[ ֋-\1tX緫» !?]cMz}oס:v5[XL&yI."L1I='?]c\|'hWwaV@Bʂ-˞̕MdIt0>-1׼Y xšݦO5+f)VD.#U<5_fZ͍մTw!)w?.8W>zs7Ӽ-{75Q<Fmȥ峜`񿃭<y%дI4KZrH҉a$VʜKr1)w?.8j>.NkxU[mJYdLβ.`W[ki/<#D1dVi1.Ā>Zҟ_]S2ߓb44(>j^MVA:V'iVI}j='7|)wح-Lw="|ld3z}kٿeG ꜜTcʼ 'E]B=gD5>SԵ MnIۉ6,Y,IÜx7jhwk}Ao%HP(Rr:dq^?.8?eR;=|5oz/uO i[g$:P.'m.nvEW6ƍ7u˫Y-%Spz@WJ]qtiO@˿/. [*J|Oh3Xih%͖(925ydS ? 5ꚥgസDRcs}c)w?.8m}IQKDy, > xG~&< ] +WqX|617dqӥ{ ҟ_]"Wz,3VSӊ[;\Y_^,)q'E|񷎏?C^_@UZCwؾA`׸iO@˿/.)wz?K~Z $g|QS?xcúqx?Q:l /W'9'b/ uuA}[fԮI/3dy8oiO@˿/.)wm $] U)w?.8S2J]qt~?.8?e@袊(((((((((((((+TXN̂;dfΪ(oֶ>:|EMo-\zYLm\A 5gW/WxlI~*ݲ_ƚl2B"W0ikxfZOnlma3!?, n8lsBwW{v|A~re}h>#|-x_T֛Gi'33kF&?4 aKͩ^.nл%$+ gjK_ݿyYu[ar} V6r1h܃1ۑ_?x[oRN&ηn}N4y Q%*?7540O T%G4QE!Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@yMTub_JX.epkg H.rxȣp>}o<Mk aG 0=49`? Eo>k3K2VHg¹`I}+@}?S(SD>+#vOLQuCq+'Ri ~#t? \x[B򻖟lb,x9#{"'OMRZ_aOWcyykk|P{o4fk;c6V௚`c/ XWZk뫫G2! f*:8^@}?S*{=Mӥmt[iq¨q@fotoxx-20.08/images/dither1.png000066400000000000000000001164671362435004500164060ustar00rootroot00000000000000PNG  IHDR,xQb IDATxgt\G&CZ$|{@$Ao$r+U]ս}̙?v939ݽݽm˨$%At!{/Q, x7nDܸqTTb1%%%NWT&^"ps  :}>݉zoa-)(>&(NhD8%jz/ ckMF㺄M[fQEyLPav!Fy_^yͷZw<%M&/}*舠 C"" n OAg~ggOÒx3| ã4 -7__vן1G҄!GFS$Gx-އ͖ CM+3*\;<1 ! ŷHbD`Ȗ'V?KXtxgfq01҇:9[0bA3#6΂(XDPl]OG08'="`C &o GZ}=>HO!՛ >lҋTP,F!D.5i1y?_z|oRk`X`0x n8dš4?g-8xlD]1s Q&ھLd@IE]bTj=ӑ1qazrv١NdELQ%gLMM*FgFu^ScXD^rjb!JF 1)^ъX |1|B<Ьgtv^)g4y8E&M Jޣ1 Sc&xx!~͂T˻SG bՏ sHXE1 **DcvJ*86h]9d%" -bkog*w aԥAVAAu!!=y%g0ރ V|G#b@(NW *bsUȤqk˱jXvƣ(;(n{3% #&GaZd7=#׆Q~.̀ɡA8UVU$CkFOSn~WP sʊO:dbh0f#Gࡉ!'J:6}iQR'蘂:xhJlBXQå=q>m9n+X+ >˥a88SAl>HwInt`~O} c`On Q[?ӺMؽc#Rъ/8e,W !V` >G'hO2 hzu>9݌܃ޖG fw(.)B 0Z_&{;ioan:.Q{6&bSp6RSV-&2qnTĠSfnw~p6<[ r:=W2e@;^H!,sdezKGEq&|  mKV+RX+I?)=jU,Ac}hilD?m4M -ރE9!:ٲ:I{Q0[e~h"1+mDa~7fN'q;{"߁-#M]!=14R9ʟZ1OiNtxg!Ƣ8DCNlwŽN< xŎJW MuEAڄTX(T~;00:rbD hsj`v*rWn0PN gH*J 3bL?tܓri7^N)jH;!|+_jNYPS3Z㐒U`7'0< ^O{pp8PFq u *\ST=zYMvڴ:WSϺ_qIN!7s,1836vCzu`o\,^\G㹕vD.QVV-LpN!8FćXg۟jCq`LO%;-.7dW$ &W~ _aoNVNv&П wb9臦!{30C[Єfr (1@y_MͺTv~[d4c]K-aqrno͇ *~/qw|] 5pwA U4_8ώA /&f;} @k%>#-7)H?On{t/}vgo.~#0\i0ްKʷA)(].\!rb5ZVW~XJ j.vѾpNkޏg̪@8 F n dcOH܉Nxe[vaNWH[V\)T4p -ُA2)Phq0#&q8z3,F^i-L1:<a4'?W96oCL9sVoߌFvg'9وB0چ9jŇ5Y5x-{1RC:Z\2L[W?.{a%hX6#{${EQqgeq~U"Z̺=|EBnD#pkը'[Y"pFTS"DVD87ڍ7~c@n"6)}TWذ)}(O/#OiLJs"HHp 'XgT֓]Nl8--#FR<XՆV596 -fE!ag?EzpDkttzr5 xmc"xXQÒ}YNXopaGI;]Yc0:/ٲ,'"7VXB͌qA>pbK}a k{Y"8$;T9B/AtsR0 ňF!V$1 8}} ~8 cc0E=?a␜KdHX&' [Yɲg1ubSkЂb .]\BlStg~f}cK^,uY1p臸[/FΒ_@Kciw AQ4㒈cF C"(\1|wD GQu~ʜ8F\<#wpX~2__ƓNt?:P8Z)IMCKد8 y) uCm!쿅CBWvm+5? uMC8~Xm®$ř[l;ZkoUv/|!x9~snX&¤KGk >¾~s!wIg)JQ1D ވ[ʑMZDoo66>:CKa! jQozg _nŔO$wvęrRN/O9o}:ӑ2|&jv~Jg {܌;^4Ó$^Y8?X& =r68֌O?%>A [ {$UKS1mqW׭կ#ڠP| vmńƹ58|,8bm5Dw?g .vۗN0e"{ф$-k]EC@O)vR838AH/|Dqq7l|Olv/"oG#8C5#֠^&1͸`g~*CxA?W;qecS%Z5s 08J*\c6)zI zZ0g1㣍ifV)/anD ʋr`af|yU׳[k2>[vLzIr*eP\1xyEɓ>%9!Dlz}"l݌M^g rҠ#,G[uؔK#h(Rnul'%!6H+eM 70bj}Q0ClKF`"=3kcC*҂tO@aa>-Т9m|vM_é+w8%~DrʱĤdt/UtA۷a]v2ӣhy@ l,R:Go#022U-調@ ڵ[J!^nw>(Jfp!We[FJS٭GqhW`BgڋI7dW`R$gp8ig,}V+ tc4`/,^:\S'.7 hcZ[wBTbmٲxq1twc@lz6mބx T5chD'bj0vnFpj$fb]&Cӥ\"%8z8TxC560~ډsf &m`>94\nt;׏8=TrFÍm`%+6f5x_]^<> F|vyH?8i*둛`^Uh|Nm[3R&W Sr=S0v8>r{IFA kp|N9G{[Q{45Ð먅Npk‰Pb7Ӹ=DZn2iOS)~T$XaMD l\5NIiwG68$| >;ߌ)/6F ,seb 4K/fP*BQ@Yb7H{E; ÿ+y6Z0\I.w1.* ܾ;Z)Cձ9%mMoRkh: /C:v? +|}c\l!h28Zg"L5cMY7O.6&!JP~">RQgj vVQtsh6Q@Mxu ~Ee꩏0-8ܝA-("]ĕ~$m|/o/E9q {wS!W: qi%عc+)f&S0ϸکLX IDATx{J)LIO; TQ0U{~5c3ŨXێ-oL~\jʩQH7FS7bK+,wY?=8cimH*ߏ{i# L9D%bjd8.-7}{tp\A6C:}зTV(XR(EnZ2Qb"ٖ@[5'S-]fҝ}7 bS"݄#ȩ|oju&GYEZ^Et͞ -$¥{d  z79˩%n@9Ovة^h(DNr Ɣ"NG-h8-SmZpi:߸ g|)6OͿwwaEHR,*a첰$>$Rp@P pKKjC"tO}4=%j7t6v^bӐɕTضTZXcEч=(ħ#'&ns٬|j0~ POBC00=(eqs8VX(jٹHWզx$s+9E 4WqV ه= _9%.'m\lp:I,.V8 >M[$=\ kg#v#GSIaKx"{ 9A/K'P#Q3c =H)"jP6CA%R 3li1.@@̂D<PüyyхDvEI#=cRZHZÖ+pwQe?sԎ!t\m#NCT j`4kVGhn@k 7VxrO"b)Fa~Z1KW٤ }j(8ݤ}C&9 &ʵ"2Ö]ҙY-jQ]׿g[щ ʹwEŎ\,G,MPhC Y*5Vf̥Ib!P=^+`E x8UR22 *X _%+Ljvٛ5bQ@4eQ)<.[JN'tb7+1ڃ7OJHLsN!)8vxH1,+ާ =b]&Nl.=bD񣦄#C?d"rr270"+4 Y}h/0!C8|*sv!:cb RXPz'?.T#v̈bݖX;IޫdwV}ާҙkډ=t*f:W**64TY&D:j9 kI3]#L3(ݶee"TOtI*ֿBG8+RbN$b!f@ƨuy@EK/z2Tqt波Q(k 2[„r(?vglI\=5re&f&Nԏ %s!m.:\Ό` rSjsN q*N'p7Ed\GZ~ J3zqu_AZMWRW(JTX f[KC"2p.݀~$J޴sb:cWmɹct@g6s?JYPV*a 7ή.jsEz^1S ͢ƚ6dG4mF_Jw [֢p3+K!Y9K,)?\HHG^Zo]E]l貏sւEV4\=6:rw”Cl d7P{v [ g !w_Ԝ  $>!ўcPoc"?n7z-۹bJF_ȖD5ܚQkZqa{7)]M08jmFط u\ev~h?4k?^yaOʠ6Oų"#gH>.vKYAD##W< TQ ض~sgGUغ9Lcs*tAE^ ISr yƌ(+̃F{%;3RhwM26Rk,L}K8q'?L5E KRv=ƭ_0j .+,g4LY˧Oޢ)[7x8gpcYԆTnE21T~ l?9І o`6L#E$ToD-D* J" %O ?r^ܼ>g:胤fT5!o329u 19(em#2 t? vQUl}NMeoU%aۋȡR)\H&zfm*Eصo/lϗAG:GU=8ؒi45rQiJyc_O7C# \eiV:vީVĐ]#D1׈]3Y#'8w~;ު2y6l*A1e:/d}\rga9z̟[dZ0. _ăѾnt-Ł2)%<8E]-(X9>ЅQ5r5ډ&G3Y_OC >8zz!qm<& < (Q> te@VV&,!gUh@NC_o/F'aw{!-- )s>ny@'֮4%,A-0z06Q[mW(p~{ -=v$Qs_l޲f|ݽ3XH .@ePaj\ݘD<R(陷sbJrZXFigjG+vrU5&@yo9>@^"+N2JR h V虯o@w/PoLvE<"xJ9PTv.ϕY EI9X=נeI޷iұ<)VzhEv'HHGey9ڍ5'P[ 9.\e i򽑼#xZ8L`)KTE4IϽc4'{)LjQt(甔S\UGH<X6%bM'q霆56RzeJt;T Ѭ\.'Z>aQw^Xjz_~_k"F8r`%-Kqq2|\ӔDhH4hgYGg6lmr9ˁ5,)n0NY{ XA'/,:ak+urr`^Ec] `cDf:тjJv*?#%v1Z6LFcag;q?7EEYQebcfk>(Ω܂^(t>|V4%{L~8_.;b'Z T˨3Ҋ# 'B-W+I%n¿~y0F0n /mqT zu,Nb *#]#~gtC/pΡ_nF] E䞷+l'J|ja.nNFA:va~~PcNĦ?~cpcWO~N{O[=k |n >==2 k3Bb3J!8*1(eqZ1ٯL5KW:Den'Y*Q G[6r-w z)l; pཌab,aA'L3Rnڢ|ZIKFRv5{>.<\\/ p)~& ҉Dq))gA&1ck80 L cdpRMZfi"o B.xIx'Q 0?#VM's^|5'B_Ԟj1ڋ#hp%.Z b.Lencg5fo}?Y0yk1m!2Ц5)x1fc)q_lY\e[cYD[ cNOM00m,H{̱ X'9kvx2A"4,hně"pTgLAw.6)Xs2_ e EاV]ͭJU_ǘYA ]VDslvd$|a%?%"4qP3q6l;0Hv&:z60Ĵgz||g_q"0ۿDw G=~VL`l1᱊!t+Qb6qg*"\l19J8sJ.3a$jk2Q{N;&Y'nBđ*%UL|,-zӢX~<۳%⁸7dP.Kwt8z 6#gM5DlǞoy Gkϐ1'> 8K-ڊW7'aK.Q*cAK⹪|VYrbΟ[u={zۻң1u"~G B1h^!pad!,_=s_;"B% ~M;ĠxNؾ)V?CG~w!KٖC_[ck9 sac1 6"?< 1Ha-I٨޽*ɏ{ z |H"{?)Η5;4񰀂Ndq6p{ y6z\7ҥ#&"Llz J\l@x. &u4X$jဦql hŨ#,Lc 6f )H sbpi߫~rE  wPCřt3:*ۂlC 1. 3~ G8; ?P+^[xZ{QUQ䲓?ӗ1H'lxBxNtz'NEˆg)RL Qpf۔GiD'*^Kиz? w)?hu wv%IЫ=ۃb:vk{3~B|85V믠N7hpd,|oHM={7 N,cmF !8`Nl$\G=K 4` Ja/ak- <(D-g,:o][o&;a%`g<< M#LoJノK12b-G}ŬI,ڎWw4Տgxn 9B=2ۃ xi?9?dόV/D'3Mp7䱳o@[!Ja U[Ab&qf3'K %Qs5[' ' DP+<ڳ ,S٣8tL+چ,v]S`d'arD! Jn;e9Um&d4Z!J a z/-LAaNĞ d>b~z0 3.)TԎzzFiJ@^nrHb;F%Ɠ̡8 3pQ/$f=}&M RڇwxG!DLu̗p#B:MbBH8eI3,c'cZ=ptų8J_JP%0@U(ܖ@; 0ӈӧ g F$ns:f$ַ*x^.j{9ZyueHa]^|e76ac,`C P":p&;*m~ 0ظ+UB##m8&0YEU54\%ݫ; `Trc8Z|~g3b'OćB)K@zjٱ:r% ڹS)!$r $^'2L':pi$J@eE5v!o֦1&S8w.++! "^/E?Wnq[؆O~k r+_ќ p47Q' 3,YDٹ{ЌE>s6]vַM z 2!T?Z֥pw6a5ހK!b /8T~[-.+6V Y]\ =y1u#':Yhu flkpXPE-y pSaI$,eahS٣f7EΖ>w s#g~u.N%qا]8e-:9v2gB^f_Шzc:e4/I-3 s`>F_v؂uɫF XR}aFDIkZ[xBeH 1c R5RNtQsSǍn+@iǂD$sWk>|"}DY J2EGG`G"\TGꞌ\9rõY|kbatI bp9Q.P3ҥ ,W%6ji4bDd FerH2q <@ZI^,HRZ* ~h۶Tp-D)_}rG+7+7gR0 fܥ&*N"-Mbs7N(-qU쯉߬"WBjegSXo@"(-%ގi7rawf0ـ up vrvC sE;n  NzyFUK6ambe'Ck ;(oS80 `C uW]׉!bNoigtbKNb_yNkԒ?H ߌctdFbb@1&WAwxE]+hTfl7щ A&?mē g$S \HqDö`奝{IRɖQn4m9MC%ArsI%-y~F(f!xon浉4,+nRC|gB0FsU^G12R{ZS*"0lbgcNJ"úBu%}"t~ׅnZvgCI,㖇Sq9WɌBVJrZ'm>ڔ&)Q 'iOeUbኼħyWunڶ(6&epy\/ޢDEzxљ Cޚ(w/K/_ a֛T ҩ%2lr>%/5e)~myj+PnJѥmțbU!AG3IpCΜn56# 4}C Mzx|C|iP9&ExPe. w7כ9U\-O#+ V?\rM!5|~yt;ڐHYWjLMYEW#f BD]ץ~PyxIƦ` fvh #}X#Q*9.yFa+) %RNj8z :J2]$|BRb:옩tp ?,[ByFQ#h0u5OOF L(y1)|Ll0D%̇=Hߒ h]\irh=f^hpmn!::U*uX[TT BM h5;%[wT!َsoaMC9~Nql)\p&EDCUEd)X y)у|N% \ `FAC/1ی^'xY])ypUc\:sM{zcrQb{0|g49R(JrRЌEqc0%!+Y6 ̻,ʋ?V"z{%j(?xvE( 2R&PRӐl&zy4L'b]k| nRD!&S }`)ap)7*2@HRd qBۦT̶e("J~Qm Rm1>}۠)Iʩ0Qҥ䙑3Tl9F/5WIAC}wOg"fKK0]Rgܘ.$۰{ylEɎu/PLWS.P @D-jSܬoB?0/bm*"eRDX\Wqb R|YT)앃_.D"sU%wp!|jE.Lp`5̊ =(/f㦡CCAN] +DW-!'\5wם=(W72_A*oWdR7 s z2 hmygT &[՟/Eb˜E6k?xҳJrc[Uv4"_J!cg\:k*~NDwppH H c8aDj1s HNP{[wRrP-Dcm g(%2t+TM>sCxQWH[/* W`D WW x!%.+Sry֖Q4BC;n깼@\uO^/GBG!Hc Gy|e&gBf*J1rK 8`NB+>-xTx/pZJMvi6;oR|a¶p*b __m8~?JRedom5:)F N*YXbn\n>k ^ioHƦ%na)Ξ9g?i.kiMavG\(EfW&7EOVwi^{\*x^BȉZBtx($VO ݬ }yWGK"jL=0=_BКwSllBVOKDt~ q3±qbv2 e9 GRsz L H"i&Mm} ږ]ϿJ?1Wu?v=xT]h@QRPս_mڀdLDFV9YHl5B{h,baYEh_dl,&?_[H=S r-s M/[arECrv:K";5\廰KwoSj} vg@e$o7~7 Ѧ&oɦ=808wDtJ8v&Q{x E_hY?ѪD+X:NV9+U>+(\c ɽyvdʑl//>NŠX6b Y?ӊM*A$&zLezBhKxHRnT^P~d:Ch/]ixKďpFl> -_Ǽ!Z"<^;ܷv|Xz+#ZtftvRrV~dеfr.T/VG^>sY|l._ &?} )T+{ YKۋܷ%/syl^++,[R7WLY䭜_9Ja)b$s1xNӉdQ41D%ay.}~&^r1dy/_-)W;-iH~8εߵ)y~hX\^k<ݿ= 9<}+G)sEavyse 7p߲2߿毯x$X|Gs+*TE%J@OMWqV~œV L-KC_emMoMBMW]s E.(S>}szj¥Cy\JTU*X Ųx\<ͦ಻"'"p1p` K)#+OԤ"Br1_ %">B"YG8Xaɝa㚹J$j駽IȞ$JTUjrx{ڄTc|9%L-KVdPY2oz8&YC@@>嶺"Er`E Kxtfvh-'sGx{l10xlD84r`U%Z$Jpcsjz et Z>8RoVXIhU)VmxF '(մMў&HIXQeRC֊ځ@%a~ '&ᦳ骉]tgN:Nbn EXy(D'`}cS'_6#;^9WͶvNd fۯ"AzGrldmr xgh{w-:`R'& .? Ea #5 !396ЎmѸOĘڱ){$ɮۗpˏ (Kc#V46`8t2^in ^*@PBnZfHۉI8Z:F¥rw޲ʝ=-Lnn|fذUHe~w%drVU51Msܟe$jJaq} .:v3]\JƳ0~ւVU~8 q@vٍ|0䲇qԌ .x\rPXf4 Dc߾]!:GO?:/Sz-lJGc"P={HLֺhaH-[t .ps?C{i.]u-vn,>TZ&Jf}l䍵K`rd^qYND"@D"aޒ$IQ(dJ*uu~]=1|z1=_zRr @x d9̈́MP*D7$0ݽq#ND8qڠ&On ,I,;LUjm978W(8 ZRjUVo =윗9{QqKl:t@aW~?ƚ/g!.AB r/,)yx~Z54zQÓ}D9(zKE؟ 7[^GwCB!X DgT0U=7kx+)^.EJ|sDKćW2kG8(It!‡V#Wf֟pI(-Y;PGmm3F*m%x8ЬU3^X qF0Vvoo@MC;sLBQeLX xP׊dDX}/ 9󊎋c볿ŧ9UAAAU!FVQN$p8˜E/?GuX5w.{=Ix38H٘ 'Xl!j?jc_T20c}t VVHZaj6ނqĥd~=˓gjU25?B|"N }ٌpX9I"j8!e\#`]5 Eئ!ܿva9XD6qHzpqp8%bUO}|XNx'`Fr)%LL7pkcKHɯyHT"0BpV}D%HW+fWKKd* ,G*ZKQXYi;,u~sO(넦I?_9{t>l׆x272I5G暭W huĜ[ş2䧐 (m՞´]aMEIAe`,:jM.ABⴶپ58P훔S!Kħ~`,QZ $LDD_dޏ/?sд ̞qZ 0nNKVYM!Pb<ρmT/y,s5$X~.Vfps|@ef~}o2_~.9ȧ4śY0&xY@õ߲ݺC:¸L *oˆI!c֨Gz;?WRDZGݨ!' 7Og\$g+A #my棜D$q uhhmAن'A<فz4b U[xQ+N'Kr80Z|zQe>ZÛ!y3T>UO!&8ڇp^/^(>ȉ) #H/F ş2lFFBFb߻UNY׈l}8WtX^w=B 4b:]cxHWNԅ;7_sL?s1O]߯1N 73k+/)ZFqgJ~Gh۝p?U0'}[j+rYI)iHNՎ"&5Q^mO `LEV9詿_7wa?LLXbgaZ&&7=,V fd Z{Mǜ{}G\Ί-Kyzɵ$t' ?Tvi~_G# 3qVJ_O_.AʥQ\x!muv/Z,"R/Cau޼b"8@[ ڦ*kCBPsG1~%#BED`EZSOo.N^iX2pt%< =0LS\u_Sq{pD^ B-nOo[Dͥ?fL=W51zcƾ4hCX9*윆&s?i΍ŏPWEܫNjI5#}3GOl9h}3p~rqeBݓ 'fqQྟ.J#A1c_ '?2/,(6of1U|> _?#Iqt? [tuC+nV#Gy}o9 ]O IDAT\q$~ (}=Crr͘#^hsL. 1ؾ;l\ۧi2ƭW+y ;v{o`@]}B#w,, BRbtR\qI0?h!q"22 xMk0og|+\ QE6D3]8&teGd1_2}ǏRª<궝E3֍3ս0T)JQ+Bq UFl/~snV*McYw?Lno?HDAI)\ZI9``-'yk|U:`̯Bq:Q^;L.IfLˮI. b8Lp"; U۷$7}:@ Ѧ-辛/<+.ȟ.ϔIW:*m E#jV) WX:diqthB?an78 ,(Hm\>~~W9Ce/`?Mhr"m>PMpWco08>0Ew"Cݘ%ߊOD^?8w<ٔ/ KAQ.gr| ѷw ?2X$(M(2C) y(WR{m2ճa6g?#8F2̹I?bXL9ԳC(4wA Z:f?A_NЖ+'R"b8Q 9[YZ&6'u-+MlPxͶKHFR\-IpџAvLhVZøg33 22pC45)oAXpK/d!<"PΰmCWf:a#'v\^Ias-Up[8ѫ~{BB<IlZs67 #(Α"%R2: Dj_^}e¢sy~aQhB=Cr.)dH%pNr#6bэ nNr}r*l^ iL{wdߑ3绘X*`]B~{uӶb(A D=#yr, - ~:m3\ND_ţ'iǩ,P[߁L$*>QO]a*4F^Jǔ^%St$'I_b Թ%LҌ<2>5P>Iĥ@2"ʘ#xGd@eY/×v~e(,j_e<|TF20myź:/u~FtY/..я51ϰ^q*>=կ$ !§@Mg2^zm.ŒBNOOuœ tfr>2ꘔuAҶ;d/ym,xRO?.$6vS0[FW}|x$3$$`$|Kc#,A@d*%;H#drץt&wRd&!qF8mWm!>:p!,Ycc-l|55jBja=Dn]QЧ]6\$ u$eYc7q}vЗ˰ݜ]݅<nvmppbqѻj@xz#.MҶUU}Xs1 by8>4c!sƩ%RCM<+4ˎN_XG\+lⓣ#X0 N|f$GanXtS G~ __#~a~w D do'r9v$eGPF!tr%ՙęAYĐlNuۈ!!# +"et|ff)Ƽt;l-<C^Tr]pp%~Xb٩G+JHH HHCagD҂b^iJP2\ԜHCDqb~3/ANHC!g pT~(xog#FZ Q٦OA2BcH=&Ħ.ёFYb$n*1;Vؕy]C?J+pY>2|cb2TD)cBX.]胊:9/Y a}e % lgSzQPYx"I "HIseufCBMZd.)F#[$!mGn\!"5V }LQ*C]e̕/mbIr!?7]Q$Q$iŒl#i3brp 1M,Hc^y$UI,hD52I )F&$s3+3/+'٩\*Q)R EN:7%x$0}B?IBH3Vqr|,GIE% 3 ZRn^A‚l89D}⭱ 1)lu #FoI wvmhp&^\dv,$Odr'"Օ#8x;?nҿRsqly.c!E2$|L']+Jf2AHM&[eo ]&<%hq5;ß iW!_2{v^'d;UIW/dV)y˵'H7 A/RXTa$7 xAW~qI$E*h1 6Hu~[7\&qM\93[3kHQ'Qth_Ӗ7juI؍$? `VY%~3_!YpqdsyX2rXpqnj-5 H$2EF%2*-/'mG7[ɉH7UlJm6˴9?]@ݏp_ȢWTۖr 49 0 l܁HWȎP {qXj'I8gȘ2;BW2^ɟ@ۛ'e¸t&k/k$+@믛#P)7[#ezB_d,n$.hӍqzx8N!}N -KMϓa޾&7tz7$`ȁ"W;Qx~QɗhdWK+g HJA^oU~$9ı3m_<>۶b4A*g/RV67~_.Ҷ2 "VƵ|ƥ>3'r¼eհ^7w*DYԔo6=$ύxa.? ]Rxjzaյ0fCY~̀n7岥.[/R!oK~6lE &׉ TKݪ)~um~zw^U--ͥL-1Zb]I %IE,/k/irZ&h^=[^@P K`ZL5,KDݥ9_]F3#݂xzzr  Fa_gIF#hy|} h<oY%~MquUP^'QU!4haURU? i>~_`U} y/dH҂*۩XЈ8WTLyBSqCCl3/TfKP{wI1D\vr ]N8#VFZpk7eF(@Їh%v5S +#؟A `| {vlf8%ZSź$,5ٌ:jIE6y*QEUeyp 3;ևjw ֆrbqĉ#6V_a ?!rpD6Ngπn*$b:^TYoH9YNz5Po١('Di KSx|j;—gб*3ُlA$E*CǍ=y.P gG ^'/O(b.%@J.ΩeABRF.GV SZR|"mȷˏsP >}]=XI!0'7\a& {_zG3}d!S4&,,zlYʇ3q(Hd]Cx#{%{WJ`BlmD+©Ȭ#1dMeVQZB2Թau"֔/H:\1 \X R'bh7;K71{\@XSݸEZq$i|$Rih&վd-ю~~={/G‰0uO̍ۧNNw[Wfqr? 0SKpݙM"d#p-B<2_m Z 4Gw{JWV J]>ȷp$ &MxNL$7эKX!3DZOn_•IزQKK}b$V l+䡲Dg"@Rl3OUt' ICF%k2:'[FAk矒GEklK M'x $A}L=Dd{{'2s`i弄ѹ5ئ@j9>=XģпGG`%ZW3S D(x1A'W_/k]`X9o1O!pwmDFel"*"^\J$B+54vzB,'2XH1$hsD K0@%>˙.Դ!"%>}@=I&3z gh9hǐ]ȅ}d"K,Gr&Y$b `a5g`IDAT0g[O"ȜZY$HQ?%}#v$"=1.4m!la}GDEaYRq.ߑͤXONQXlvH$۳ȵy!w˛:C 1.er!RkC8oKK0;X8Q1;]Ęڦ7?y{)W>0eHkZb{5O+=kW7 h? ޙ"F 8pbL_Y~!#ۻ0y\cK/_4y"QT.t#١0fUcěA4LZCto7w.1G7Jꬪl8"PDӍh_FE+d[a4P8ssr"y9|~bQ]@bOM4u3B}dH1!3.U\ݻ~%#kuAT}RoYQ nASCǓEZ}D_/AN+iA󜜉U2UHRBfn^!"DAUd+ @BUh(&MȄ,<2c;|NPmlJn'%˫weWeФtldD9Gl6!FbI)bAUeBҐ97?ACsH#w1: q3dyI(SwAAW̌bxbL[%E,"e"(iɛV/⍪\DHwzѯdD$Y n>p:K&cp`QS!\7(Âv`6TP*ko<}y凐/l-ʺ1NrD2dIn]E}:qF1$$[X< 7 lA26warѬCcX!*})P/I".?JRM*>IAUܾ$`2sjeY(MQ4Un#N2*2-0,0F2 ]-h5:qc9.665)9k5r (LEj*Edmm7ѵKns nc1>N]]hjnGOK@部>>(s\>?K N@*m3<~TfP n J~w`$IB=VgV yV13T.Y[a[%iG -¬BI|IaE`6v-y8YYlWD)n^7\GI]F%1CtVc"C7ȜEh웆hS LH& ZWn9eIlD#Q8]$7e|Fk[+7:$=ҧfEl 9&ّE`|(ŋ,f"FƧVRX.Y0 ?{ (Jd8VȌmKwNrwQN\}؆Д\'4KJSa-\ºzi#4.ͦ+O0DD0EV+O KƽR&T:D2){j|Qlopy>@Z8TM-ܢ׭ܡʩ2n LcTBDhN ̌1 _ ɑ^{[wx2ḓB`G3)?D_KSxPH%z[{1~x$J(S@=6~|7ྲྀ{T0Ė=r,8 (qhξ ndǓZyg^(E"VT-Xܣ"j,ٵ-Nnl9"w,*lbc=;flI&3q).TQ_L 8[0.d˿*>Ԃ Hp╗c߉Wp'AC W-Akc wE:/c 298t qo~0ZKgG%7>N̂9ZxdQ}ޔʗ+wml(d_r ,k"Eb=}Q2J Qh ғ?H 2Hi#k D$O20P0F(dV| #Xgk<)DQq:Ng%CS I,e_u.ӊ\NxZ"Žg01I6d!B ?8qyksD6ZRWLl+€+T"dbFW3^Xs$byo[" )?Kr$*\bqUCP."*1IcüKQX`Huq}xrAM%s%9!I92>E$ M 2Z]8|ٓ3o͍~bf5$LJ1$G#.rd'#I+ RRp|Ě~"[w1رP>n$%pt/ "f$n-!GB!ҊFj,X#<ãNX. <mrvqjZk eȦA$&FY%6cqlљ+lB 3S}NhؖvV,ٞ#6X䒍 2{ ghJ7?Pk k 4f;ae O%Ƞ_[b ޢ<*A.WwF81 3\^rRVZzE$?qC{S|*r?Iǖߙ7 C]%Y{'mI}:Y 8D+,C 6h=֫ݐKBVpQa7+/ZkEuښw@i~Pm?}~x:mGE}*Sb%fȀ,W]߶v(%%K.Keml\!`'1^Db4ҭ `|%NaVv)4_azXBgcfܕF !zIt˒JXs$I^JS:h**9U/`]Y$’JrߎŗpK& XwQe 2p%E^չ*'uޫK`/H`W%e8r0x>믺t <آD)p*g%]fbZ!JKOt 6% hFge"t %,J@?,^&]J@WXAŢK@(]a=I.tz.]{Jrf{0 +Tt A i7[YaҳSI nv7RWX%G% j+KYѦq<#L%2qLOs&.H+?c%۽DeF,X#Ăa) 1Wp`nzZt:^]%WDX3jP7<-<" vQw{]t]"g]{P^C "9 fS_rRYR2F* Wv'K zz/~!BHN!Ea|V؀;Zߤ4g%H@ƩٙFZP5X\4$` ]FOMNzD~'[2JIգ[namIޕ(څ *!O*#?j|\t'7]i҆Y-5yH /aՃ'x4A3 N LI\t+,*"RM C 7A1 -BwQn~+%6 _3pg.n#}VjGJJnCoDY \Atw=dC/;I\W5cxSM$)GjqKh6k5!n߾O+Tvk]a&{]{PJYizK-Dw)&$ed"ۂκ}fPz^Х1<1޿v6G2^/ǚwKk(L-ƹ3ň u ݖQ<9i8ؗ xPoc>Z|V`vIk_k+-ye} {$!6[Xa !مrdݏ {9! pDeX?EKoԀMDEB5P!C(.c6GXܼKڸg u EٺZ:h |gKxl)"6xrmDt_Y dqw;][,+viLML`h Fⳋ/' +2] LtcӘGKK qHIN)?"BDnӃ]n+l*7t kw؃B+a cs"M%.g`p쉣ȵjCn!_`&F&T\dD$^^_i6[ZQ8q 7p@ 6xa Ne]a.'Jw0* ^\B(*BH 6dOFFf:R*0$&4`\ցF;]%cC X3` # d9t >>WQfNB[ qcA)6#P{öve~vQ.7$2@ˈF-I>Y\Y33'rQkxgM q-,M!b>1("%X(Ƴdi}SƠTMl4/)V=+$W]{VTP 9ɾJFM)R&AѾ { ߋ]UdΑ V>]a)1؛Ao(%2FƑ"bW^25ɏzt  P<_MOcyMPX0}UW KN6;Gy:HitD(W4+ '=mJLKF[= u5Ah2m2ؒP%|ZK/.o@[(JjJY=C K=QP!]`JD[X@GKY(QTu,YWT5W]am^@@^k ć}ʭW.Pz].~/KE_stEXtSoftwaregnome-screenshot>eXIfMM*bj(1ri%HHgnome-screenshot0231%Ơ0100Fotoxx:resize| Fotoxx:resize|resize|NE@g`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }Zǀ1iTXtXML:com.adobe.xmp 198 370 0 BfIENDB`fotoxx-20.08/images/dither2.png000066400000000000000000000513761362435004500164040ustar00rootroot00000000000000PNG  IHDRa IDATx}|Tǵ]z#!@10`ccpq{{yIOo/ǽc0`MK]Vfj;̙3s r\{BL)9וd6764.+/_rl6S _^72q'7d2@\\\,dSݨL92,4nc]gN S@L&`Fں_j">}gpRQ@0\& )/WgN tLPʥNN X 1#䬂s}cYz p3rqI+ؤx]x -&Hחܯ)]q%W0) ި?s `#1Ec{?kW4;Xk~sP[߈|PhLwXK\S@~p?71G-ďh16֡8'mgĥd7_zzRr+bDvO1 5)S#(܃ewF~Tbk^0XiF6bJj+ٷ (Qg2&PM }]܏ 'b`ԨIMLBGoE9WO'iB j~W~WFL(Z2E]+fMeT/p|}cmJ7\KfpKFLW71;Zmy.6w,0s)ÌGǏmfDaOCsݻfK yC5[y_1g*w'pַ3L7,%.æ]Pnw(̿2.1pjݫ[gS1XZ:HHګc/ VA6RTXOG[אo2>6Rd3<5?#$-s.| Θ*V[6CSfDcAwkV3Mlɖ9ҩm"h4llAuY)J;܅/{?ݾ^'-\Rƚ#?/*,, jWBVUxCUQBXLp"q ~*Z4r.D53S2@aH0 w1 y݋24z˥mPzE#gdZhń|5"ľoaB»@@Tqdsp+DZj^Fꖍ-?ƻd ʋb#)r]0 =OPUOK`.FUc,s0 ða p_ `՛+p ytk@EA>J7P}K>tL_3j`w GY(*ъ-'?8i%Ww˗_]z}??n>͏#+y9ctm0]SXx'2O*th0 SONێ(?_ħeUb;}.=_vnjz3Mw>Kdm' 8Bl|hYs&"id&&TQ!'N\yy9ou"Tf`>{PtgfO}]M tB2h}2ȹtG"3'뱑Yz5i4FrISfQFN@.bP*L#wWW(yffAхg +y* *Jd3T**5vrNuA#O*Hs$dCǂ <웍,ZJ%,kn c$wVs@ dzqߗ'CP TLdzEk\puV1mUq~߬ȦPN4GP{;Bɐ jsE+\O11 7N|+w"3f/8o\) We]UrpwR:Pɘׁ,NƼz:sp2u 3˫-r8spRS@֢q]㜝9)pp.WANn}]u%|:쑾2[[vY]Qz'c^.?B{k_~NL8Sˤ1/` >c (>kCuKZ҃%(>]k ` uƻl }4Qrr>ߑHVGDI+?Uk"9Хg+6|6VgwxHz>x'd#)VuO.MlTDe ޕsƪ*E!+&sGL\BU0ZQYRfZ4MpY6ZP^Tz-TgvQ?( BYCIqj`qqpA#ԡ{T[eok]):,OPҚǾ吩gu"՛[ǥ嗢& ~,?",n7C^~nh@}b;݅s.dF,Iz}Yf]߬fU⫗ϫ,h!~s`loun'FD@x/ !^dokG•*$XoSMԡ=+h'vuf ų[}S6y۹j(܉ւvE0/ skGpdkXg>s7bfTځ8%z3ܴȻp m0vd1pz R|X[?~[NâqFxfL., o ;Ư5*VzóZIؼ,{Q~*/w.lyy+&ǯ{>+;x۱ꏻ-x20cJ4BqxU泟5G1<<:J3ŗ!x`vmbrZYQrI`]+rL]n;zD=qწbggȠ iӰhڦ܃M"mpvyscl$F[/scFӎL;f \Û>Ļ[dLr.]!7&cGoOr RƔ?)32 oOhqn1eL4i-'PHp L%]&# [s,J2'CNy  S1wVbB[n!uqO~iMBW*WOx"M-_8y,;qNƌ vGr!D&bܘLD{7RRZK)> {Wu35}ӑ޾>p#tg4KqϠAlPvۃx#&iL )+%VY(O qte0 F.:'F"L7`ro^dpj0 ߇D AVjgg0Ř8:=3 S&Q&stuoXg[^nDXƍGaA}`^>WgILt+ANVGZK < a&YtL D7}CvˠL20hAKP$؎6 \+ ᳗{0cd(6i#ڴMh Nk!^91" \ J.3(-)C2r,f~7_KU!J*ȃ1< N|wmve>ܹ+{Ԑz6M) qaY3 TȠk}y ZWur>\3GWyxܾx{cٲexPXs +<5U6㲓_UH~,:{~;)h-GjNK+s9n#4PEd".ʗ\EK7K/!wl ciDMZ!2_g˔1?ÿ!n>1}ʱouܸьy=ې!"7/yݷW+V7ge%<땡?ndp27@}%!}]½AϵKgNאNәյ1-9]C 8әյ7?]~͝Jr ᓤytIH "[܌*SDJE1/o*շ#}ˋQkAL?J10c4HWA(&4Y- ָ[ٳ8G{ 5h{  jТ(<.Tjmj{!"&B QX#yI` SS'Zm~B{:%k2X(#1v'Y\!cҁݍK=5i_0cWXk\lǾXtbL nPNt/č;B:-"Ndf>vo\_砦?R]"+^ۋr+.8]Q?]Lu<1hDt4";uC3vM8{딈-hTF/1))v vF]lY>ӰpBL߳%5WN9vXNȄ4_ 62Į硴3;VpOYh)BH[P²ē=z?ǑP5x0p6yWl2c)Z[ↈX*W[릵eHRq4E ['䜡 8Vlj*N(46QSIBϸrwZTNƆ@f/Ɔ<8)Jb! ol>ۀ[#2Qbđ6OtY82˴̯GNI׋u ߗV.$k4DB٥AIR ~gv t%arW$ Flݸe-FFsADHƍ8Iy6o}- ܬQ~s<+)aHmбOpJ /Yԍ,flAq%TLN0o-rKn/Ex>lWҞ}_\lAo*Hȍs=N,Ŝܾ0۵\'fb;[PBRq;PJ5|*7$;C3v]lj,E#(%9 '"ކ^`+[ۦЇqz:iȌ,={|pŬhn~N`\PS ?&$]S}Qse.ǑazT_ ˘N|2|y,$^EJ)9TCM4Oɸy +ѓ0qAZGߏhP#?Mp`jli<L!ɓ}S#HrI%WN2o.5^Zo ubDpIw _|y~e3 r c28%S%OY^X՗xer?RމGOGocy\c]}1~C7`U:i`s IDAT`&Y $I[CGLդ0GBa&l{(xg~f"`D>rj%gahxП&OΦcm7[OL":V#, Th&a$Sjy$\3(QzI*OO_J1S.**4sjuul>eL`uTi>*bYxŧ8z,1XnC o[?Ub@uAr5/a4>}[3P|™rhQg!#ڷ+nGQܠs {+ˀ{ f4wa 7a[x4d!Y7Oi40#wKx:<ţ%]&3`c˗ݵϔBY bpz+Epy8k*7TAfo0 _NQD̹nLKy|B3h0F,WbY+bxop⹷/فXv+ZC`x ^^wq9T0n<,e;,ǡ |?>4pB{Ȥoƅ=}[ )E,S{CtT{Ja,ž]ٸpeEBV/^54SbVAS̎c[\k?.-ٶ)wQP2Iq]:qńKap1:!/616vdt;fi),'y[MN F/`|W'ߋ"뾟ӣ]rqiM3g_ng7<~8Z[.~p,F/h[9"n:Ksj7LKlƓ8Y 1t*X,ZQN^gÃPqSPT33(C1?Po>;ڊ;~k,MF}1}=:x1m bFw8Ů={;566Fb@&' 3 +5m9orώg9|B"sK3efLZ)Ҟ'Kyڣף nQRLoeh<@?=tv}ڴv#tvsَ5 XGp+\̯ADK.*Dl8*p Zhߧ^$1([/3mMW_*DmS3Nn~[%b1 ʚZ& kPM!cf;"ƀPK߉29v q1x(OҸ3@p&UFV2s}hnd]E|Z⎌)r@91mzv-oXZQ] :dZZD-AHwґ .'ԱNC=((A"xD݁x_#G[˜WAwҷmmp DrB콻&&a=L)ѽ%Slhʋ68~Yu$l) ji& M۽++ )*XڸQQEPniL,>gLXEs t%prvHRh;-H/G]&Ggpl#^Y:0;Ut>( $Djv4" teWS{%*KsRж>0PʜQ60kI<2J)m\^PX\9<1W ;Sqax{ר'_%& #}ZuŦ'I8$jOt)r滥[ 7qt$v<g>|5:~@ uoQEN9wcNzjGL@@T:fO=ջ>ߏxƒ"E`B|Pañ`Y~FZE}=Qoa^5n`e{Ǻ S|#Khâh-Lhxf6{>?bOlw^f.ŲYxчɚ-\c(ąHW?FVҦ B*wm?ߛ&=is.K1uuEȯ|=P6#!|oMrJBN,Z.ID|WOxB:rǞjsUlB3MEmډi$h~A b]tM(,/Qs Kƣx?ĜXn=i$giQXV\o% \dVϯclP͙+GPPa5FODH@bfCS j'(D2\S:KG08B'Vo~S4[00b[[1I(prRjA{˖ChT]:e.7zbBGmvm@)>k$>N \ 13g<38)p=(0(ƴ/Ɍp^_/ \zU˙wNsm1h|׫d: ;svwZޕ9"s*{WJ~SЗ5az&R88!ܵp N \-|#fzڐk.+#`:2#8,ҡQ_i} *OGc`ACUV̽>:/p5Ϣ0%^Ҽ8u0*4H„q.*6X*Ob\f8rJhz12򬍺f=*pvЎa:g"1)D3OzFU$ IR}Fؘp/;S1IS`A8zZx!yhd$Aq,r{iK L'X7!1 +Jj #sc>EUD*\=2iQpgMcNCPlO.uj[j=9E:I38r*-&!-##zȮl,Ķ]`38~m "Ԭk_sQ1k/4y',UWތw`_{B3W{=Iplaê}P@/`PF/pX d_j\[ Iٮ4ʿ'ѦrFaD]yJj-NK/G1_ฬɈp3+͈MP5i5c{]8f"}= 崝!~l۟ ϤSR}>vЇgN@ka!dsώO|w+mO,h7a؄9xTx sPXS [+c8ܙhݛɵ>? G˄8$΄|1bx+uͿDF4״u{2[Rx{e~V$<̭wh74dA[rk}<kkko3eHq R4>i?Ť@ }ON<{2ieB[W|&)gwF?^ܴXLfG9賫GŨ Ί-&ξ3c-ݜ-nKy|F߉_>4Ф֙rz7R)ϙmu d̮_QM8zYR|qK~$&D؁'JȔ6 \>=h@?ZGhuYG+u5MWhGm/bAl]3o3j$A!` F/6\ ejb|;zhТ7\FZqXhϕQO;>ĪO/4SIIFj, [vFW=oYA"V r 0o}|#ef%E2.$ҙ(os'c+pa-G[{;J/wu 1"ٟ鏬)0փEh ֪Ûp9bB~-F(:RM8yun龹Θ ^~̞\[ԤS@v|DJl543fÉ ͧx,%FK;}7f1icqyCxHI.pAbx|d NaCsTkrTW<+̀-}i_.yqz\ef,mmhWK^<~ĩ>)4q ]'@) ?+k|WވK%φ]iigKޙWF_YTN \6y$s&&(do2.NƼl9|p27AegM^E]*g3W@߿[JqQ7r8  8zt1cPɘC'F^nO;~('6 ,EOgsb(vBN(5 aWmt #R{DUjWIuPgHo%=҇R)E=%Q}1]ht;Mi'5&rT⍺Gef꥕Hh;dN٭U|BtMZiR!&z=}, D~=Q->hfC]18b^)Rxܭdщ7C"֒&pk/45bxuUT(v8nl-wR/CMթNh(!HpPoh/Q@K%5T0#THiYhX0뛮DKDQО1H%|a!7q/v*] ^s]ً1EGQNyN;. W¢-ÁO;JB~^2aG|xC1Z A{3 E=j*y Z -x.0qdt¦_,Jby#&1cf[Ɠ2YvL͔pH6NK`[}2Ǧ!з}0ÓiǯƎb!ceNZ8%NEB0ttE&W1[X9{ ">(F@02[z/aq خ%ZQڵ +>KQ浌Y [@- Q.X7?ڂBҒ:ۉ'ǯ?tC'X81 `k{]`LZJX]_瘻1? ;rpcRō >!($j+sqj,"䭶p&^ V*_K#w:"kP[Xz:EӔT$ UG<_ f޾ /̨ :?CVm&nPahx"$n;5ZQԬD5hj7BE!A]%衡U"3CҐV!Tz?koXVsa;^]'n'MkH,}OBh3v<74Hk5r$|)ys%jaqqE@H|4v16th YӋ2M+ =>7U @H͈;;ɜ'cJ$AJZt14ɱqMGOPgɓnAȵfji%wDR~ֽr~ܔ&tV p/AxY4/,&do/ _8K\H$Sxe GqxoP=?Ħ.N @Ls%AvlU;d -uܚ!ٻ 5yǰ~W8YWF~(䁥QQ>H3[7#>1v%ј>{$Rk`~-ajwзs^"S.7k=y"Јv=UWy hmAk]vNHzQ23Zia06cl92g/± pgnอ}2naxanAN)ɜ Z {yݷyb>x'T{> Y"@my܉{܀ͻ\IDAT+֚_A aH:?!~!-`Sꬭz bёՕb&N DvX; }cc\4p7vZ>I~ 6Z3Ҋ[>{[w cd&EyC_M!`/%`UY$g#ql-' 66RI-&.^HC)!.)8sw}}'cJ('A_ߢ{rT`?5؞IPt88Ŕ`Hhdz45-[bœ+ޏqp# `|1t苞-sz"ra¼xH"N'Ϣo\:72%;cE@UV), #3Ԍ;NE`(j=FbzxO| "ny[/=Tz.фvb+L 8JD]CD,7ALy |..V Dw SZ|2ܬ(8#̘8x͍H)mrDN؜Wy> l+lG$EߛIQ '/ g"rùʥGƒ!qHoGiIjc>1wfb) F$hix%82NFg%oʱNqsƔJ57)؂R*-9mN=鞈B Btt?Q-,"A '6yK#A\(&cƈI+0c{,fMnÁdJ[KYIϭͶ~Kl\D$~i䷇ѡqlgDQY%Yrt$  [A_lTM\*ɖ-l ZX{{ b"T kҳ!"?|%͈WW:Jok#{vq}_i ("?[<+~J8enwSwdu䎌A*)k YǏc4P>]9[:;Go~9gO'|I oK\ 1 2]@~Ҳ,V qkV^޵-Tɷ vLBat> qO F4Ê g]ǎ 𕅀^lq)@!ǐ䉉aDwg+y20Ͻ8SY)Rlٸ 0axDqۇWƍ‚ʺK0,J}3 r8S#AܝkptZoap*C[9< .bA;nJ_'WyaplgxLE FnާaBAލ}_l#fBuis 2$ֲV+'c "[x4RTE1Fq K 2qO[8F OU^sی*s&&QdΘG&=n#|xj{\6pQɐsR6]`&f:FLJKZHhȥ0 t2KwL[:3&@rxJ_n؂C<h{"caL5#D#9֏DU5U3&!oT-Ƙ?hl?6# <sn֤۶eΘ81ws#͙{|D鉕a +•oQ͟D2SXR?s磱8o;tw"6 ?E63_{W>\yEb9:dLNT17 dG1SWFGНlW޷{o-r=1}铘hb9{ +4m {.1SI86T[r{l|>r>`.>7i55OƴEzGڳC=7{{Һ>Á2{3\?X:~ɜp23+%tEXtSoftwaregnome-screenshot>eXIfMM*bj(1ri%HHgnome-screenshot0231Ơ0100Fotoxx:resize|NE4I1iTXtXML:com.adobe.xmp 202 207 0 EE`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-20.08/images/dither3.jpg000066400000000000000000003350411362435004500163730ustar00rootroot00000000000000JFIFHHExifMM*V^(if%HH0230<0100Fotoxx:blur|retouch| Fotoxx:retouch|retouch| Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 600 1000 0 C     C   /" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?@=j%zڕ>VO.'*URlVcr-`cD[\/nkN] F)0iNxcgDTkbZ.5&e*^p}3্dhn-c:$0{4<^o&3} ׶Zл&D)o=vU[]U?k<)+Majݓo#Hߐ$r|ϋ~:/m'-*g0r8mx+'d;|uu:69A(`Nq,]愚彽V +?5j_ZE;.F7w>[nHom(ƍoo|oq-PfLsI^{m,zPO4)Zdg:}毨H vp">1)|1W~;>>4].De\HHZo/d村wK>9i  xv}KTU,sgb_1÷@z]׈8xz]#[LO JNfm,p#@ _rNr'A/5Ohu>s|ZPu<2Fѷ>:>៉6b~ ]?O[H[Ix&!mYأ,dό<b'Rԣ}Cڢf;@_8 &޳i0|OǞ6'|i`Zkg7]yU3,q _^i~+E2CuU߇BxĿ$__ k:/ iiMݕ"rĒs9X'_V]W7Zgsy} ,_g'(Sdr1yےw6Ilj4ԫZngI4MFrB?b.5T"8.LS;F`wpNi ,g|Eg{6P"ږ"Gp J_[JREk9(A"R>a\wxm4ϧ6c2Gp1bXr@''b0WQJu))CGhUi7f캮+GHќ۲~zؽsxF:%XZ^zCByV4 lm5 z:֏!ɞbyϮuZޑsj ;,$tR7 ֶoxKS|sgwRX+'T!qxa#0Թa n-nUx}mg8 ,RTfS\ܯN]y inmlZkCWf[rDs 0{u~ x^t*ɖ8W )PJ |Z>&x;^'-Cz eBkuP~`#8ŚGYt 7GEܭO$sw.v<{WTxZNy$qi&VWѴVS]K(5JPtzgbε|$MK{]B^"A pI1f/|<о^6zu˛;.n!O:u!m.K^c⻟6]?:ztQQW"[ 9x&;Mb5Y$vk01 Nqeӓ%tQJxvC*&EFJNefawy+#🆼5'<9wtIk"Q18`O5|Pe{{{KKK8vB1q 2O8z.D2AAa(x6:g9+I+;?Gn.:y!u-YTP"6jdLt%(tJj+9Z1pdX& ooWpBZ㫟_xzLZwJݒU6T2]o CWmIm<3hwtVH2d`$9,Jw~:Wm8uEw2ǬB lǼ. J|Üw32xBi+fi֙nnm4ag$xrmO XihEqunQ ,"Z+ ]{?%]@_. KO8[dOZ7.0+ѭb||ir$+~%DL9 kz>",r# r*cxp.+_'¯exG~Omw8kbPrx偳| > OCIXGe.|LNdc$lbpxྉ}H^xdC;gzyїp/B g|><)?EՎyVleqEoBw׏~x.φt_" mm)YXm#0<1bNrh+ VXk>*MXaytڽNsfyWSp"V mk/Oմx~+(SQ6w!{u "s=jMcR]'MtgHJ?r: ci_5\e>Pq ԴNg\I"N99 IJ j;)Pn+sWB>m+O@\a ^ XXKpZ7DB:URl(y&IUT([V]J(TJMM;+ &vP0f# 9elW9Ԇow- #>Q}>nWkX2s] ԆoשtiT*zjLyX&kIozϰt3}s!"%iÒ *|GcK&^wV(E{'9CگNT箺K.=gVOJ.tֶs *p5v1819?JDWq5hV#f2K[&oɔOW6mͣʲ( l2}//{InwJN{fWU@ BX۽l /yU~ߓ$˿XAgMo++>1hE7Iy.`L~{b:K9̨\[ {Uo?Gk,vuBr,e`\(-]a(ӊݽUZԸVn3Vo Z&~57j} z1a})]ɦCe,&sn 0 vS%{pn3`|9 s KmU,<=ZHnפ!oH$-0-L~am*+]Jo_)m?#wX;]%%Gh{*VW!OF;JkNV\Zk *iI!}RbĘX f\>Jk|@5-+ui{U_L4WfR\~> boj;oYžj){hDrYr[kQUۢ0{;t~'|+72Y"PkM_Q,/n5>>%6A P#X:m"|okr6McK&.y^MnfyX|<^ndekt5Mce@V;9nMo$uMNP%79'Nk^nwԖh8Y[<7$mD^nH)gemoMN Vtvv[Xv#N %YnD{>'$ޭ*+k>Hʚ匨I r_G+ȯ2|$z62ܲE#m?$x YMgO!MR5zZ93|a6>.x"^n6X'Z0|l.j? }QΆ_ٌ>^ s^5ٿgY^'-)Z<ఐ;H#;xOρ׉{jz-hA0o#ߚ귙c 뗯;knF(o%x\g;5_v߳zVwij6+a&#bCݟ+|\urxoL5=QnWuz Ol.E;W?09s;z_ӿf(~0xKA#3Y-rt˜Z\_o^>\ дPծgĹq7>3gd!uWW4Q}JJ9LvUHS8^,e"KrJ$0.qɨnlv;fi>=i|2E{ҿΔO$7Xd :=Cľ1nDx1ukW[+EJ}NON|S/>h_"'k9#$vp8?{.>F y'S>>3|;'l-񱈨 Ox>{Zn:/G ūEխk`%G])nz{1i>#x[Uγca];ɉGIq\>:zi/jP"1R2yIќ |Qmcfmjh%MLhI8mKQ[5>z{Cխ.<;e-yL-M$۷ p8 Maj/ٻQ>"귾!o-NBWlI}0sX}uͮx; m>ͧ_jZ{F#A|/_~.^>~ kޟ^srlsy8FFSZ[Um ©c6FhMS͚%S9ZGb2I+tRMm|7OZ 2,+!?Pw|}jguOڗ2]q6P8f)<6q޺}+ƾ7~ [j ſ[ >b1ހa띤xxlVJXEFY[w[k[;$}>R_ Ehs#V JNKw[{_Wu+/&?X u.y)nmhy#7qAk έ{i6h/WHRx!]W b⏏AV:d֖HֱqaLbOsDž<kdI`ӮL"΅\ôl[8L&[<%)cqV|ڜ&ogk}zCȡxUVڊږ8׹v3&K/ GDYM' * C *w^xkŞ:-u oѵ 6YR3$,q+H#n# 5UE?1T:GwRqIs7; \X0@q?<%%k|Au?#>:[MRY*}pb0 )ŗldlބq]u7eMWfR𖛨wY]s bd `džֿ'i |k.⯄藰jΥumes9.!RR *"A:|m+|],5 VyCgQ(aGYbIU+.ppXW'ECÞ5? }W_p.cK2"ʐ"~c3`O?t/xK=tHS jnȏI?+*&oⶁjַT+]AtS͜Y7IK &d܌)*W ˯O Ax meŞcku,++܅Wk9Gb?izRCb $[ C2~EHk}8pրxZXƀ}w ywFk{MEviPFk(nI-q!ņk8w]^'1o$u&A5fmƒ18j/5j^0T[XE{֠0Ƞ~"r^׆oBk ׆ M2هdb[!|x;τ( i- 8#f|BA>h iǗonۚ)d[dBw E%)?~o~m6K/öWЧV4esr9r~' [AӮWCx%25/|ȑkTyd5jYa]Ԭ]Ln.,'K)\)8| |$3{]Pɡ eM_Z-^h,#*ʩd)^{'?.F ?E]SfvH$B(aHo <^!lKnHHJH76 y/DoUZ.s7QYv얺zף˙Cy@xj[xm"j%l_gk6{'ٵ8?Xp]]@B#`zvIڲJQy7=;=*s,K8Ǣmttw7B%Vdɳ&0v|gҸ[ėW$2G|T~Ӯ5B])mDG#`H>l+r pG3_cJxz1ewwq}I,*@>|nX(I8v-^g?nm4i[vRRXg5e}$4#pN3ֱ7(ž[滳j5?qW]-z»iS_ ^\~~Ӷ~8ϗo6z<{TEeSm[$~~󶍩^^r)&V.sZ:hLrB8j}wwG MIPwm $:5g&ԜãH@o;>n9 Si>'? .MwiC!*\yyc=: V!}#֍+;itbOmO:Oo hWېZrnY+ѵ7ku )zQ5) J~w]{+O_!|IxrQ4)mmFk;º=\VǙ!A`cIA9'-3VZ]Zϧb7/=VxÖ1˾Jl`[i7!cfyk" bgG8mWiY˜tцۜ׵yl\u4 >nL2y’d`H>sY֖׺Y?iKo@=a1UqiTՖdk7{_pX|Eԝx8iy+-;T1a@ejݯ?L 5BlݻWះ]ukY[x%WMf0Rcڀp`Y5Bʰ2K Ϻzu4Թۢʽgv9a{WK]TG?#𦩩bK+ܭ %|9*kێ֞0۲w\qҼzŷ"Zb^%O^a#y"Hcvƭ0_hxXB>[q^V[WVOT$/-DIF:{Ǵ隽.G/#L6YE㓑4N/q5Yg[_,`1I:c?5M׾7_~Tm]~z#Rvg?:4|CiFM+䋍W+ZqsыMu^:-__elPE!}hfGU.ukk9Lr9 $m&*F;j;s$L=:AH1An{.cg.gz i)3֥.JQn%]^G0uEOֳ5/n(84ٚ#OmI' u'Pa&?DXnDW|ckEiQO=~8XV"Ol;[^1wuRƨzC3@|J7lUd|WID*rWE8;&z>& Wr_]KVtN[>_|ןZw屸4`p6j\YI$5QiJ1]\=RI⤚<+*9D 7ɻ==2ܔIxKX;nB¨*z*E~(1B_4$ BYS=s9xRj]Wq(h`)#{m_U`F6[<*`aI-G)wۧn=xº'(m|#m6&,/4ms{/:n_p.{{2߫|7im1xI/455 [I`.y18!pjžD|[Fn<)b5%YLG~߁w/ċS=f;1G)Ht$_I|Q߇4/qIdE&Yb&Kvq?$cۧn\J|"^p(m'Mmq!#1]ME|)&ׂ)\޽Izl@ p{相kw >4/ n-"xZWQ{y--*mA3>v0ϣ7)'~HaY= G6zVgmq Ii|c;ӒW[7'm<|: 'o9!dRpVz=|qݪCV&kq۸V,ss] XTg~!EH/x_O<}ƍSGx6oX#ø-r] -?ߌޛkb_F.@Tuxeaj_[x2Q4}B 0]6f0pq )|C5þ(coxKKpizy=FgE%>AjKu?l|AsCQ5H!vsO5x6oxRS]v\X-<`bUGJ3Wׯ|g>+f,kq $䢩?:p:W7-pbӲQRtxTeyWWQ[/-&kdO b0]M%ED@J:<) c\o+ѓ~6<]nSd-6,KUsnsW>7|=Ӵ?^ֵ~f !-hgwdP د:kPxjs/.,]4^H\Ļ@lv*HPz0uy*9:q{;]h=|fIaSsR3J2Fz |[⿈~"OlQ8‘ cecɻ'8'Z:C?v-hzyOL"Cx؉QaNp5ouo*#YI-Y|> 'eh΢Kdoq:`c9rvO9+7m|;_H ]e&,dFeU9(u:CxSm x]Io`J[3׈gaO浪0h ԰3Y\8ZxyNjSWMm=j,%JSFHG*.-6qJn+H5ϋڳ5K=fx5=rA"Xc BR9XYC'tovRGnƨZqB!P5:6^%Žցn%k9a5nvB*'kxR]xmaObZʥ Ļ Hs\tjі jQ\6RvV4t_ RQڿ"vTԖj2oƯ'ὶ 5c#\$## I c~o~ mj4-CFfhY^߷w/+KBMihCfk}5u$y$o%g*J8B*oÍk$ѵ CPѴ 촛;h-0I^9#v?eoW 9>3xx"h㷋̖eZGyDvխ K]߉Yno^ZY[vo8ŵ@ٗ/O Ë/ǟe]wmt%%CJ51#l' :?`SHxoLׯo了]@N LJIߗ\'~dҾi ~-ꖚNaZO6I]Xz0UEaEJ!oe-+.gZ^O[PRҾsEB"1u_ 4~~/|Eo TRi`&8^T.Ps5'/x>·R֕b dqLj>|Uߴ5> ~Z} ZqJXI'VXPV19~g~*Ԅv:%ȡ72a@2A졗|A׉weN5ayY2xyF?xw ?>t/zVXx|6M2͢ y5,jc( 8 -p9R}8U!5 ;~k,OyX&EF ۷)'=zIV-5b>;s[>-6JGGI$7Xϕ>\zGH$,z[sáͧ^\]JV&\HaxsMA:C~gNBeS>[9}j?~ 媯Ah:L>!gϳ䏽W":?g6~ߛqҙ?ه?Snq4IV61$qЊ^3z8~S̝IT|ϷX'jO؅˷ftQ&du 7{vb?!a6kuޭB|yiA=>P@㍹=H8/Viߚ=R~~{ Q^뱉l2Ml\NVOM|%`:~:Áі^Z۴F V9<{W޴zv7~ +i>c$]>EI·-L,$ߡS*]~ﴭWFt 7vvuOBCMX0Rp9&'MxzUvɯ_E#+{:u kmZ[xX/NÁsỐT&H_+*Zmsxo&(__& 89l#7P8k՜NS۽ޏ EEm ؍OG>q;F,uR\/vs kmci?k8F@}߼޴4TH'I|<W74da"epOC]riv v&]2l H)t˴nsȢR=WmKi{#Ε䓲Z5ֶmixWա񾳨B{xZlA+-@hZ>n9<3xCŷ52Fih^^!> a |5H!.s/z,>Xcz|un5zgXI2s"WϮ3YS8rSNwtߡu*.v۳g ^lKO;{K6$m@ 0j>ף/o[R{ja[qү|9մ]V&EoF7Q|YьHoǯZZ׈͖>Ku Û!zV(Q'kY[ᷟNܷLmkZ7qntǩgCqkmvXQ^Oxi<@}Og~eNb y H?e"%@J5(O+xjg`2Ā XhI\iLWs;YxJfDL%ͱ۹9翴2/y?طXz=ڊ7 a|pO; '!\*qY_QͿg#e{JNpn8}᧖Fs|ۈ4|0=oY62x ]>.F/Č>I)$kҵ?H2E{*^\@mp¶ObGE|e~*\P> ]'H >a>atJ;\m[ӡƱ/\:.ay"n`.M0?.3#'g?%SᷛOh!$m\Ldޭx_7?__iji[d_o' /k_ u_6 n#Cj wZw]OUsϥ]')$g9Ll$wͷS|-/.xEm2Jup~S#g<]Axž,XKus4npPpa>Ҡk獾KOo%`5`XPN\ԗʴӢ3_4ަtYcqOĖ8'/Ὲ?fOU'үEqqm3mJ)"eȍ,\Nx A:>koj$@ȑ7Ͽ#!`:'+x^𝵏tH~:MOcm!qHcR{pOk$9F=;ܿj_д=2[юaqĹC[¨$ğŏ7^Ү7%>MʠSF+/!@AQc`APzWƋ|!X|+=h2>ܖf0cñڣ\=y3Q-91,VechOb Գcka#~?3[>&KOqkHMk!v3甌)u$m۹GCӌx+_}?֛u.χ"k *rI:?L߳N`]2Z;O E2ҵdU,OFz|e__o<I51{.:, ^1jܠ&R>_w}⟉|O6ծcu'๰:Z\g~u vѫqݪkIEYq/uGa7[Lucgl=:=>)Զ;kFւk]zkOBU*Uik^]Mlf;˨=~kiKG˼B;GaZςz$w:֗\IqnPű%8 +#|%t[6 ;upN\g8 h~$;2|1=0:vy)nӴՓRk;w* 4I8FZJ;nѻZߖC~ |K"H;vB_`F:c#&M .o NUԢ$mC\)hsV-xwD>.ׯ4<8VuFNNsiu|6𯏬m֡մ!eu^GRHDe@V#{aU)S*/Y;'Ik޻&,F-f}Ǖ5v}Aĺė'ual# ؑ $)Sq*/ 4%=^Y_ l* V &AbB䍽9uuNwSUEKr;Uʝs(etZxJ.|U_%9;ϔ1`se=18?9{ޔ}~H9loU(Ud߲ Z]-w_ Fue_[ϪLR1mÌ a{U楦SP;kVX/~Nc0kf Ϫxj{_{I$CW&]u/v7 u{ ߘ\s8BČpTzyN4Zԕ:Jxu==Jq9?~PJ)r.[^B R|+qLWRXD u ewy] ei^]ŧ(-5it5o[}S/p`x,ccӡ+ֽ#Ÿ~/CR:uX)fb~JuuOwox?R̒^ܾ<`TJ^'˨WM&ɶڼM]{V70fK MJR.PimugO>(hzƩF=ŢjGonm4xSGXĈ7`c?=x/=;ÚgۦgKK[2XdXm\ _/?j_ "a,,5MOR6Jtx&K|~(_#LoOVIȜFw< (Urc}Ꮝ6z >&/Œ{OៈM: &ӘL`j;UHUH>7ݮ?+s|e+O6|_A]jI 0H#&>!>~xJ-Ri#vBBdb>R ;mV];NdD v˰_;\`3Q \ƺ_Jo j5 k i/{+{uMqM#r ɂIʖn dC>eyo}]' m}YFiԐ$?>KwdRX.\]Wu bbw ' U=b<$Qv\|#3bV7%﷢_ ^L1:tW,G Yq$p ⪽ܒ[-NM2OZ ok*%8zV+*E'&0rrzӝnIF 7߷E%ݼa9{sVUlj~2Eqv\ u_W_r[Y;P03jjh<1Hвs8^|'R0mAFg^>N4]{'Ę&FgT#mY1慷N+^$%1@tW| yGZgwh,xdng#1]Q))/h_:Ϟ)I=zjn^H4LzcikHo0 ۺv;ҵM*Kb@$m:$j8WDeHլl9T7pX1j5#BI/WgJoOx']ң[rr9JXR #q̺],pA-`qU-EDZG 9 )놫e>ӧcju_'ʧ#kɬMvv8޼Wu_AxFKx@ Izmi5l}z>jԼ#kL;)zhznrxPz07tٴ;\Ӝ_&_<^ҽ͜(DU]9dƼ||zTzܗ>&tkVX1&q1nTeTS|=; [?_QQT+1;UCѸֲNeڞ2tI FPSda98v^)KlZ_pOSF Y8Flw<7?x[/ iviEԿ$c nĀﻰjskzz5kUҋ |մ^~&ϮAi:o-t|ҳ-cBZ\o$-;a#wu S4ӮM?YHHPM# q³.m&ՓSc+_L dJt~Th]ve+Z-Wm T$͗% Y'mtHF=R@{@[wz" ַ</H^^qK,ҫnk0SӮuo'od8׵\Uwin7k飾3'+(it՝ƕ⻘5Q7ѵ. E@?+A]ٵmWǷZtχ (:Q3 U۸ 0F;v^oc/B𝥾. < ߹یJtߴ=mm5&i*%ym]HeۆhroeH== șq7k̡+<]\-X=)+blBOZvt+p皓8u[6y5Czl& ^O7W$qO)6N$sԨ$"V6oVw_h_k:;+GpF#'¹iRiDz ?//Jފw|g6*y6\08=uoagembMԪ@&Mo?x7_[sYO&]udGqw>"O ' |s K59/|Y:QWD $;+!?{hq}-uukO /[WtWk>$+QPX'54-6mE$  (yOWo|!'<_+zҼ?kq1 n}Ea|1ymڸ#ic}wuhA5|cمSY6rݦ kvbve95|a񗉵^ZUoMw Xڂ IrX)|8?&>$j_oR+2k$ʹ.'\? t?Wִ^M{Qk6Jwp%z%=+%IImh>,iV~(H5nM*O \c®(ם{_ #|:-!~%VG(a3cuF|/f~%68&{ cx봢xݗ};u񟈵+:k"z(>ޑq潓~X:Ěo7ǂLwvβT¨n_ܡ~^; u_xEpM`-$ERsg*1\fosៈt߈̾(o-a֭K[G!n<>2:ש꿳}υ|J;XfU-j3J c+&[.k.}:t8o_x3i1ҡ'-kP*v=1Wȗ|@}5cyB24F#, +' |[is)u76֚5:DtdAx'<Se2[[؋XpFqFNHC*dq*}?+\1U\*$⦴iIks?CMkVgS!no)3FT7djm.m4 Zuscio"!*$2c;瞧>%x7o{ͯ5n[v!ib] U$y'jڗSh0Ge89sN:UUq3ׂ悇;ogvKWt<Ɩ* t)ʥDU'*Kvm-ԛƚ^7#-nmdqξ^H$/r ORּx#R1y!va!dg7Kﮝk[N֓HpaKV&RHΥ5jؐBe$qj𳌨ӊi^M{K.ck[7595 G+=nk^OEt}裇YkQC@߯q^{ 74; Z湫jvoG$6]\*Adl.Y^*o xTmVU՞p@Gf$#؞>3KI^[AX"[ut>Kxd&V!H s8ךdlpg'.7$?YIRܟΣ>k8ƒ ŊXݗ,3BGTku:` Ÿ-+{륝'HAQ [q[Hp>Iak>6:ςl$1iyhmۗ8r/5W?*-"+7 5߀4NቃD[â)t1%,W߲޿.|]!}!-*|۩'bw7Gi:ʭn{g2I5dcmo{v:4},*++m7<#xX|?mxZ-1FVE\.i'c_φIx{]W7po"KkMѬc\$bG!յ}6Q=,mm%* f`s:['5=s^Mկ;Rc4"F! <ޑ0߶|?a(]Y\xnL BA,rDEϖ8$xi$a׼%}ec{_:Hɨy*ZEF`'|Eҭj |$՝|\g{-wQ ?)mo oMǟ,~gyexxA4KHu4 HfPy$>ko:Ú'|`g kzcw+d6bsLh.隽ռ>/!udD}LR'# ` |u I&+WԵi+5;^"`,xS3f`񵕾>%4$b! h|vW~_|wG<3h:56?1$h!y /W˾ >7m*-DxcS#XG bYbT!K) :א7|cZk浯&/L+/`C&DD S8~\x]2i?Gōᇊ_M0YOլַҹ'bU *Iڼ+|J<o HL 5Nio}' r|Mj&ap}(EK1cWC2XzWZm`U ;UƄiJ7]FkASsvo<5>%XMA,q5!ïGq$]H3 K#ЊCg:ihU8JQ^_UFҿxk ֑R.;-t;Ts}=l"tiKv Y+soBg 0JOϩ&Jjp3HД_Gj/,4[w',޿JeyB(;u3們[PF^f7QcʧW:"KWR{iag~%bGcc}+Դ-JOе tۭ_C5[ hA¿//`&[9v={𞑯~դDUU]ǐ1"sFըNӶvn&INQ:[_Ӽ0n+uCO.@gB89:7}+l>"*ţSl[]K :%19/px!o_äxƗٚwuFۊFqߚZ%;]t{;mJO^N*hxFo >=Je^(]x$CJwZ)l_\ɰ?誄s^/Qjzoutx>+Izj|9,F{884Ik8HKMe289F<~~/_]F1Hm0¼gX71moi#>*RI_Fk}uɏė)b+qWi>:K.xr~˕Qqӯ\bO+"0^qaYj[[˻qS:n1}ڶ5Kp;jf5$)M(oO 6ވ?Sw[_(.WNhAso$f_\&/a,|ryqך4xu=+E+,Kv_b'l=rPh_.}n*nȴX OjVܕܿZn^FT'p ] @A+;]0SUZo m)85x}dHynp0jYw[5і~0k%Dhc!0G wtdLU ɨ(o U(k(KN0Hsɪ:Gם4̥C] \SY2NX邹È'l촹90Ef\1_QxOPӊGxuτ[FHhry[Ipn9۲%ҹ\IFǶ4GJbNOnNcŞ(aǵy~O`0<J&E }y0m1чڳ\+FJ3qa*qԥo{6otcyvnةֶ.,#pw~rʨ.+Cy?]~bX.ڴaG٪m(r_t-~٥CM%ո%]FUiɮWv{σ1t>Ө_s[9-#!U$~jbdk'xOx+B+떞 gee@F#?7Q].σ^xf;Kkm'^f;E a2&WA> |~ Ʃ][ZqZ)]nfr Oj:m<Z^kFU ,|vadg~bZtyN|'<Mz:ܗ:v.'ȂHۈI\rD '?yN°]x1Ɉ`"i$éQ:+5{/6_ ~ }WZ/ӚqvIvA-g͌WhZO| SYL>DiFQ<|jg5Oټ JM^5|([wV|03G~/Ts){o~TK$9QC"IVc7Mׯ܏k:ο~j|fYF@#, @3/~ݯ9+Q$ X$smL339'8~ђ3',e#r˻i%KRvr1^/ڇMKᦔFk Gm%9#Vb7sRs"4?w]/:7][ AiOs]bJં̓/^^X7ޏ\x|qwk&M5L#5%׌4xm!dZvsw=Y-( _n3+c>6_ ,`|NasqNWlj9?|ݭxL ]մȵfpL ayet|nZ:^^ Nr:s-KS[xZBH~%HMӸǙs|S/kG]r"SKRTb0J0JmJ)J7y4{[M>l |.3 C J8Fn-.t_Mw>im<C]fE#>х3υ׿ ,c\:UYVa7.NO @N$SSE:6vDb9#+w,D  !$]ّHrxOJ:IIn E8{[OpIЩUpwrNIӳ"?-S\ImEjnoxoǎ=?^â`;yt]ݨ\nRI2PrVnJ6{u[SLuZ#R+O'e{rK] :|^D'#w+*9`z)-75i!t6wXcifPr)?:p0Ef> c 0u$FBʱ W܀GQ],|:ҭoxR4 VqgO5n ЌkԡV 2i/'C5Pz1RR|Ԕ'k Mk>$[qOn k- =aX؀~r:#2}lU|*v6: Sg7ӼMZ*Z%nU+=o_[oZmpO,7i 0PWG<ת.֏T/-p|lGpp=7^%6<9k4\C}w}2<79${b{`/~#Z<5ơoAuZ䗴ˑNJᙲHaէQR;Bjt'٫8zi0λp4{I}]J↱LoMvg=ԷT$H9+⨯>%xCwdž(tPyA,̲<mUS< &KŬDt};L}7zv_0k=l- vj7_׀nmGZҋ_\Q۬iH$ڻS IKo=C[HKz[$Yf%8`W?㇋C:FmLO >q@/{HHE7t;H=.45˪xg&-tQ清_#qĿ(6x? NYp&I^W@ D dX-k aMoQ4 O5F=5כ $pუ.pM~߳K_i::k{÷b)(Z5^|hO}?]k)i/]_:M֑sSz۵ldxqxKGyN4I0!-;H`qh/|RiSNIb{-:j7v%T^ BO\ׂ|~dgjMm᝗-f'N@EP iO7׋,-/>hZs4z #c4yL )%O[+ؿg_ ,-J94;i.y n'V^|O'q&-2Kh^, n0|ٱn6:̾ A3]yLN"O^hn~Dn[c .%υ]Cox4]fM2M>u-R`d\o?e? ,t2Z\HLy{$9&+Jzߏ2~~0|F_ioūZ]><J+#Y4~|S|ךtC5 kS%0>[4bܩ'ш< ]I@i;UE.gc}nJi%;yrn27hyK1?y2I$/~_$> WX7VǛvd7ۂ0XXs[MMs^eSgmkWq!ù0:/B{yir= DgTh<1i ڳoBNXN]i݂ulڴ{l#hXIF/-N4&--MvF$F=cWkXd?1Z3*bOG#ߟ*mncP3i5iX&rʊ vdEQ%̌#cBqW#*V}H"2>n7ǩz}ClXD}};?JVVL6A\wڵnv,OVeU_SMfמT)9?z{,VȜb}+H(Ճʍ19@d[yn캧dkz}]sa~v]m@$C9^GӕLV} B#N|@5b`^U*:Fo~9t!fumSzV }&H}JǵJ>Nb 8|q]}sDfu{Re .F tתȷ<6J|=7m5Ud!܀sۚor}ej֕MvBg^ҜA\-snz1y/:OC[g[}6[EG3nҼMQ&(BZKR熮')wIk\Z;-f:17KӴx;FE_'OHdX.J3D2IҬ6\V*W|lfh2e#{#GO úU߇5^GUWU gk|^ыKTw=Sb)_/5-CG߃uhguO#4H>%ό|eahz{M)&ݮ$w\w*K1jZn_xSG٭tϓkIpp6stOS+7GEyݰrWsk*7 {U/5km{mhcH j+[Q>˛]gH` s}i|k<~yayR~2MR:*KsO6qBF+|sL4 kUlY|Gl݃rj5`0ÖHn#MܳvKKtZ'vOmң)vkt_] <#t٦tVeўJ1ב<k[` aeF Iq3⡓WЭmM}-;h`Ĩ^ 9?*#zoWN=Ke"'wۈ׭Tq8Ri2ZVtF2ԟ馔MTߧ~]Ƌ 6Wf%yG\ez'5 do#YӟE^ӵY*/S<0ly- MM3gw-!rWmwZ_Mx#֏E>A3pRFqȩd$}rHV: %5dtY^j֌cK V*9L>"[ܥƐt-0v-Gro!A V.ts][᫴փOa h6D_r1dp8ʞ+5e( I?4g*rXr&_6tQeg#8PzՁGLq:Q[ecfTլl#\6+~Λ]6?pkпQcDOvrV-ۈ# ;k)Ls!I)%m _й")\ jҝXIZ-dlg\V 12-_fYLoD]#5Ÿgx(8vңF[Nani:V5!ßݺ9co*NNnչ p`˜dz֊nYR$g][;M\HG+EvffQ1Z溳3QVV#` c+4x 9X&855XMQpvgʘ>^U9 dU)kLeAҡH[=HeԙWi:x^;}kZ05}i!N >*(}cJ/Ձ.,- ƎnsX~{xv VElae[:S"r=I\C1].o#(5eF3Z~1튛4.d($>|o={:q%힗<P?VEXf_ZFyV&{-KSP!_‹ʬfp /?|;⅞m.JʷëU*PEU&S?;~2+X>IlJTGa2vHgkc>׆[!F/ݴCQ!ʌ ȭH|/Th^Zqɧhz*mZ; 3cK )-mQ%{[|^ kH\݂ȶΡQP8#7J=pj >14Zn/t>~쑫og <⹝'G%u_j%+]jQY KP[s&2$9=sF /Χa}FWaAX 6PrM.y_xsⶫtSO6 9fbm9P/߉Ƒ?uAchi6) yoeHRBr8^#o xY|DY|=o<#7ͱ_B$[]SŏhnEJr+X_$PWū{?'H|Q|Q%KQc-,nK x?&h^$4ᆭ,'g-,,m[hXNu,/?CfsekYQYʍbݝ6meMUςgM7ßΗym]Jkriv̭] iqy}'T+ⳍFR#H1" Ł穬9|J?٥u_j 5}x&f:菓Z|ghIEk)gK%dn9KgJ^iIyᏋ>|6rx˦qͦha[,IW'<]/~ 1Oj}Lq@ǘ[xN8f7K].9,59?bo!};G?~s{ˏXťS[}ku~56lJj߈ xu zM#OӴ봵5[U+2 s:ddGJ<5iZ>"4)WF!hdr6sX2'/ xRvLJ:o5IwߜXRJ ǑgA_{O"_4:d6!|.} KT%4HrwNܯ]4aU䦩RSa6ԽX&$axCG.r[uH l#$Ğ6#ol/%Ll_l RD%L{YO=1MovM6+vɲpYCK#7!GߜaiNjkSN4ۗJszrs^7Zj|Y-<B*.*rrI=cefoG>#ZРFpҬxc!PNbFlQe]Xݴgcwyxbzp?'F T#h0898dèZ]+dP̱H.c;u\<7Y NӒ99Z;'6^/RWON^OO5|ae.h,{(n-'xIsώ&:Wkٌq麦#IZ8r >s^B_t">RX\G^I2*'@7`7>2]%X'3c,w`6n7R>c/zm͸֮ݻy\5,:W|%9sIlM;^ xckUGRFmErGKvSToe)ԏ(+=ԚWKs<{^7hڿc[lJe/"LAT/>"]EhPn\FqU7[O|-\+6#ʌ;t>9CP~y7l%XX=nfwtj7smIks p؟b(>e-tWKxWѾ%+Ũ7e'1 dH8!H'# v~Vw 7A!F]ŜyzN5B|0č^y229@xxNAZl3Y^jtPGEp׭qbԝzu(k(-֝S9jF9&u(է{hKS|sொn&k: x4B$cXgprsڽAj?:V\G3,Zu=[d2,"Kڼpkz*\oٛF9>?\WџS^þqQ_R >I#1^my屸7akҥW(Z F*髦O]]׏`0y:nPJNўGK6IJ|-ѿk=ZMY(--ͅv-$q1-KcIw<+;>񥞷ZZ[*K{K9r.AR  8##~1>P𖳯hV&yo^$ <ȏQ$3.T?"gHឡo~5=zPh.[(Ī\΀wiӼDSKH:oMm4Ʈ ZbX/s1I<{WºN,Zլ!XA4hT܏4.@(1߉G7'xF=4~ΓWIT4~a3$xsI#kW]Ү Z塔I,`_F0R@\,|4AE;۟k5GүAsC2 19|AoZŝcV_Y|QoUD٭[Vgݐ?G ՐK͠lzwm_O:w>%fqKkSw,Wtq1$m 8-|.~xe׈5Wèɧy-[wFЛ?|F ^/, _ޭiImAOh8$I&wW=$ůP1>5|9iXh-onGȍ۴_4cRV?"V} %DV.q7yl3uz>&>4çlS0:DIke0;h>~*_w^-|'^pьp*yH܊YFx(VNx 𦿭O+ڍr`cO°2S`Qo|Y6ŒAk7~6"MV2G{9 (M%=FQ)8G>Kk+? ;]I40=j;?ZڳkPI3vg@y~V^oV5#J);=)J4 >Y]+1.{7ΘXͱӭuXƅ`tK)owy>__o\J4 SE~S|wm) 5T#g{_9[KHt7r|o,3".*!A+}߻<ֻmN,Eonیqڱ):"pSԃWێˈEzh&jYxkeihl$G=kaf򳐽R_E7@ 4|JF`̼2UJRyw<7N^7?jZ\Zx m]mqrxQWJ.5 ^5Di~U<`p\&AB=k~ .7è\6J(2/cW|W>X-A*9^NJVF+]3jI_7iku/,R+Ѹ4]~R o˹>xRӮ$9H]GQȭkmgGB[^NjbĩV|=axWȳ !,F2|Q~SʚXjW~e/u+G-҆ioTdFѯh:-_x4VhJ~1Ywy@> \uݚ`J0e̟+3q0:_ \x[]O c- 7\-OÚְMRX:1Knb?JyTu9-RJOUrSߖ;-.څOaޯ![k0D1Wc`ϯ5q{ xr]Ro꒝>6,w|}ܣcq^l/4eT]ۣ( yω4ß<nY,-\+Iw2+ Tr+m INy]j& 5 eɫۻIipKyQ0*k{k[<1-)|̯?6O9$ עx>vU[=M~ݮ$cJ1Vlg'J߉gƚ]]lJG1C2g\b>88JRDڔiY ili} }[ uži>hv2y~Gnǚe"M:ƧQi>u -|<ۯj <ǁ'j~۱#S>~@A^G=Tx_ ƚ]=:Lʿd+ gk?g*Q9.i5۫BEr79 @axLԇE3?L߀$xSм9GKk~!ҵ`[DM`|Ue ǞՏ x>#K0pdq' $g|DQ=ׇV7$s<rPiTNml_/ži)'Pyc))ps4s޺II:䨢i=Yzb9snF9˰͚|!Լ_R[v~5G—(~w_4\ۍ/QEIZ-TSI:i׷UM ^iw +Ciu+O+W͚-lª|y*;F0k'bҒ?{z/_oj^|oo:̞` UYwRqs[~/VSZȮ&#M3 `1`e8kk7J߇fѭ_7 &Qg?o-i{qkf,tM&h+iD%ǀKMmBx}~x"F̗뢏;3+9أ2cpaj/Oz/_W--&?M*IF4'w; yg $[+o:~|MV_/v?80NzՔǯ:5M2_/kAH*F:r>7U^wEEnONA1΅>ӻq7R+c'^;Þ9m[G/Nr.3.ԍ",5i_76yŦ ܂G5W #Iվ;x+ZOgյ7$:C. \(9,y*9KvwNVg|O^lmPDl ۡݽNf-'Em[^k4_FїKE]ֿ㇉O/5M q(9*` YT3rxSZ|SDM6D0],P 7E#3pP_@@>@.uڈҮJ0I`)drH܏His=<\AOw%XmIEQ N2F{2|fc_ QjN%h-쏖N :1x%}9^JMSxGMi“^O>Яq!1Po3X:Ň/xHEhWI%"ߕ+rI_][TbAI7g d&I18y'2)j7yOA޾W a8jIɶ^ [>21Ҏ&*6isj.vvNw6g|XZNj(h#wh۴+Kƅ|@lt:խXG Hݧ'ҧNJ7q_ktڽ}0Fwarݲ8k#,456-NZnF[vb$uz1Vڝ*5\wR}.ߩqV)iJvVsΔ^׽௅WkzRtK0l]vx)6_ Зڭp%29ゼ8Ƕ|pi^^Mbb㜌&nƢǺX e"CA+#d ֯qpR 䖗v>sէĸXrS4]^Z;(]{M+scDrT hTȪ| zT;BBaa 9U'hw'۾t~{m2C;[l5-m9>dXr y㟜H9''E6wybh^RT$ 8WףSa)Ὤ_,ltQNM]znONj-$~득\k ľ7Z1ϪZR2 2{~4ޙT107z0TSRREwmI4C5s>eԩ+F -9^ҔdlV+[X4/n唼' g?(>,F`/jޝyc,VŮ,7 vC(@Pz`𯎮sutjZUرi#}d'$v5]ZZYGo_K6nnu NI_vIjb!;[򌕚rQ-_žAYUpKHnfNN4xeaZluMITȣ$`1%'P|T?hO[OtK4K7 't+#T)CCnmX|@QkU5VaWv_6$Z5Biq6.5SIWo̱xpxʺn 5\Xb1yBNI{qWwm5ѻgQP2l6'V4۩69Wk%C[/}F 0h%#=΢I*X`qK[:Ȋ6Of*uP}^:ӧn8b}^DJFFȡI@/]/IԵvlm.,q ξdrI.IVGot}¯ jV5$X-c1hBp]ۆЭz3$=׃ }]<_uoiDRem;͂@߶Oi?[K-,&_IqX[,6fDB\yquݗ~Ϳ ǁ 7]lVk[}8bFfF 33|q|Xu=GZnH\Uc\j1޺]G-ۑKJ{_!S'ZŹ1#c_*__]V[I÷eַ3J<- cu~rxg {ׇ L]L]JF"Ċ1, 6p}eC狾$f{=SvWDy#ľעl#qoNwi")`vB[`/ѿj7Dm&O(隗-^ji B̑:dQ PV +oSgB)f7l>ip93W? zg i:c]iخ.cRc+T}"C#;~ڧ4OW*- $4Ku\Bi> uO7osG> >h4gaW SasKv)/{5Km[MLCjCj;ce KG*8 $$ѓC5I5[;dT"F㽸ZvA]7㥝c^_Z׷cJE\66d$ϊ? ߃~|2-UU||ҾIԒI=KKRoSm2VZqu<;VO5 io~7C]-;XYJ 6ܕ2;I&[i$%ГO{rǑCıP13[mI~~> j~*ĚV2꓋e,h$Y|dGwK|Wk*jXMw\|Y @5⏇>$/o,Yz|KizVl2I01?>2@- )< gh_t/V'ugTZԒ ؆-Đ瞸fV7I\5)s}V_R+⤹_\|vL2ljrғ3.ߵd Dq?&JISw9SjΣK%R 1vYzj-[=1V˓c9U [x_ܳ[ }Inc-Q,|"ns#:QJhv]Og4LѡX{ 돳xn0{ dG\ Mo j:ouFo'ab,mRXD!(眜/iC⾁ź2\^I~D>P.n?t$$vŤZQi7n:2n[/?5i=BY_Ic$èCy,$o/`\ʍē/uF4lO'_gOX 5o ƣBԬ Mf+}2_q$fV8D}/>R|/98nq&6jTWUM~M^] KYZd3n9'5/]ŶWBnllf(ʕ_Ix#C¾+~Kc1u5# * cn]]{c]Z]g(#0=jsn63ߵ__}̅*A tG(#u-ʹY:Q-kBU,8,`Fkϩ|aQS%^C"HM&_m^VHՎ;מO] 6c0ۼI֪kNNcX|5^e8-9݊i9P9aN4p4:"TLg5h4$le1XjJ|ӵٖEwm$qiM˴'=~d8 p`= :`18o9\,ti 㮙xLK/4ɭOeco0+i߳03݌3RYԊPm9}<B{e%74Fp)k.>E|ރ[ M{ſ| {wPmYj}EL|v|p5g߳'N,$w ?KPqڵ'{ *GSҧS#o.lnl# uZo~"ީޒ Y!$=`e]I_6G:ŘCGmu,$f8"0mxh־ϡ y%{Ui 790~ت?ۧk>=7{Ξ6o g.FVR3ğ>c|Ma[id%g0K8 ~W(?_H7 o 3G|S:W d0dKk?$7oݝk w_oc֥ٛ'Ļr`Ԥ]V['U?{> ?V4^˩x k-"UL#tb=;>4~m 8Onn107ğ{s޽/^տf ?1ak,._<}qҢ]m'N';OFZD?]]z]s;/?o.V[/1g pHv0>,[oCM-wߴa|S|-~2|"fqO{/XM3sn:j-j bie]`۞h|M'[[_3:O<;~5m˩Mz-o0H`0g_!B219l׉}V,-@$xnEdh[nܹ8l@6x?iVKUؘ872˕;_quIK+dFmc|F9ЌWfH &1U̹b+77K:*U)s${{ofziuhOv$ݽ$}D]vcòkKO[]xj"#fԐnI6u^W|xaeup:j01P8pFzW~|wS}b!`d % {Ȭ]lz9FJ;*ײw\20q)Ns撚N]Q>^kz> E.#Dy$fu Prq!I+-=%֒Ѱ$u\ӓ].S']N6VG Jfn"fC, D~M:%\`InHTn3N"R+Z*UnWm7sWӧĸь([ZmA/rxT%s6Xffnm X"N5V/3PJ6^֩]o}s'r7Smc~kZ2跑JgլeeK?9 %Gȷ ;ׯ)izPy7 ` zZ-rg ĩs注hw4eye${V}&zAouY]jєԠN9yݾw7;5?WĐ_nBdJs93e>;ּ;oqZqWӷ2\],})Q+O_xC:3▽6T%\:(6N@՟ۯGŗZ-4<.j=K:GSe-3K,jIq27圶In1~3Cϭx ]>Sv{X Kfѕۑ@9?g x[¿WO|{LM%Wg㕕a#7) ' +"/x)l'-5]"EԬV6s0W#iaW _騺 - f!t0x*z>;;_ iKmx𦾿gcvV?=H nf;}WMj65/:W`4M-ZTK4g7PEGŮ'ᶴ vytQUúW;  WCհ*&جj%˨Rd BC ܞCG?6vyCp g<5StV뫆|M܍+s}"Nw om$^8J' QCVޣɴ2 >*q"SN<߶;kW:9A/j[mVM: tD Nq\q %zsmXQ(#N3T*2&OG\VĒE6*@.N=t{k-HIVR.-n[ʐ#`9+&[1Ƴ<G8 pGI?7]f ʝvy眚FkWBT[\MfNYݽ1[An9">7^?+-o,7&`ItW_Ns~XߗM5UOwʖ^g~eHxj+! )˛Áב4oOׇD^x+[ﵐFTSZo;wxwHun_9gu'{KkfzPȜ27C8'v' J?+v$~$65uxō?o]eX2 2,Gzm'9^Yvu_mqɬhPjGmLc ̾nzU4i>>xL~*tHe\+5%; q>Mu+m:N''.m7]ߘڄ}s_-n5|m%>U8q}Zzԏm8y_`i3ǭtJ񯋛WWӚ6o%S]Ìy&oiwI%ߚ>_$?TPR5+M[|Z?2rMNߍi_|2j!OOƛĸRzj7==j1/?m45P,>ZpIݙv<\ji@<7sm%][v;dj|E<{a};P]FO۷ ѱsƬ)T\QvU'𷦏~oz1o.u=Ʒwĉ VOd4Q<?.ϔӊK |U:nMMp=|k&uMwÆ).t2uq_c8»?";o _~}]SɌ]u$ך^V_3~(/uAuץ[|So9dM:ޛbH??^p8@;E-dcs-ID!&7?oSBs,7~œz{W>[xZIԺ0pn>P 9hLcXJJ9^/g~n*_<[>kҜ|u)vd_ƼLhܸs\l.ÁmN=-eq(mHȞ26?SY5V"4\WQDTnyq\<4[g(\>a 8u@Y j$W=}9gypj?{p)s[rhQ#1s9qj*ÀEu:Α6>My޿1|^jynslvGgB>5ڊjTSp+ȴ.#=5izZ2SU'Z&4h=nXwJy=+Sʑ:9vq/!,X 8oIMKݬ= rY\5ZX$F9bMO*+8:}:BGwJ\ZicGnP5c)ܓ쪉5J̼dZ7#Jm@:[C5#E+҃'\zFg^{Y-sXX郹gM`9lWK߶s+l nZMGZlqjևP͓\,Ppjվ۱t_\/ǽ]- gX} 'T=J~7I&oV?'8~65) {\'3Sd+^=FqNVYO>.|R9}k[}7I;ck%u<mOᆇ῁չpڨ̺wJgH8N6 'G,Oßk6Vͺyh[$V=smeoW9ko x_~ O Σi aswFӑ}þҾ q:WI ``2gvN3i~=q/e{u +lL@ŮpwA-#GLpD|1yw6Kg5bK nT tZbSj**Ni{m2=s[~=#FHZM9ڌxWϊQGuID6Bfy#a|L5qo}^,.eqrᷓ0EGt]Tz߉t|G}6ʹCm!]RY`weWaqɪDso[ooƖ 5g7Iؾ5Ñ9mC|xP_|U-N>ϬчW"%*[ Wu/ tjl!pH\1=85_RҾ=|5ѭ>fq?.nbdؘ1ޕ ;7oEO<;KM? &$ ? G* _'2+G} rzJS98 k-;^2g} KdA9\g\moWC2_ = h5v@\fSz{o6мus,Pٖ#w+Ҽ]þhxO ̚孼i[ DC9,N ~¿iöb^l&3%^FF`vK))'gkEj >&Zk[rAswZXodյG/:c(Xy`k JxBi-4z%VNu"qiɷ۵sZuv:%Qkg'm< :*M7T\YK%<`˓Abgv]J+sPS! {78f9R]EIRGo_ 4?|UecE,W0J~;Tn8G5/imL / #+Î_ hVNk@ImL mi}^~ -+NL+, ylpn=y^l#um]6{k}68VeFS򪎟:\MW{կ-o^[ %YDC{3[%Iڤ7k 9u=D-؝T62Ssj='msuM~OYI+c<_F փM da$v$#vp"IMMݧWۙ{ >ҥzM/z*RIk*&i}ĺ 欶3ea2h8 WIݏ%淆XKNNDs cy5ؗ-Ϋr췛|#e W qK߈uv-m- vXd@: ļ2+N+KI\H}Nl}<^?xRW\/*i_͢O~:I/<Y]uq# cK MsTmhz}|SIC2vi-Jd}Tҹ/|`IyᏰj[c ''Nx+BLJ}GVxU.caH[`s8⺾haI4IW]FB҄3wI\iǴ:{:+]Z&mN^-_i?`E#w~:x3U5>%MSJmDߙit4 D @; ﶻ~޿~2`_i@t~m2tCMrpz]x1'dB?1>)a. `p:w?]^ok'H{oCm<\ꡙQ}kgbL> 'G%3i3xwX~Oqo~u=FI1'7fru9?`7,~$Rb $1hteBėR *yKYfύrYmZ{y&Q<̨ﰐs @~2,!h4m?Ԭ|?l=խ.&.IYT4YpLo'톚uXiB^_(jhFČJpyB~bh_SGR-մ+FCX-qC!&<Č5^).鿴~w+YYS1OYltg R 5O:w>>#'z=PV )a { '╇,eOĚ4Ky'm՞Glyf,0ܧ<yόUy,nMjJƤvoI Pv+B^~\Z^mD=vnZ?3jצca[H$QHGO6͸|s:tIr_#ޒ7^4}a/ϐd]6-r,Lf3қiȖv2Ā3uHfҢ9_ Ւ|uIJ|#,ȦK؝O7: o@^Ur8=tfKu{+WK\cʝxC[BZ'|ңR*J䛲nEz>֚k$еTt{hpX#Ui0V_v -6=2!*UNy'Vە|$ҤS\ֵRKKkuƿahxJAst"|#]JuaP+ CZlw .>@g[9C%.U (4J/{zݍ:oAasׇj+՚F7W{Ds}Mz=vm5)&(^W={ĺmf|Aڼ]Ji&޷я5YT+EԫwX_ko`ݹ_QMu;]؟]y\5tpIɾa9}c]~Ծ&{êJ;@&^rcpMzSRQ}wp6լտsm%qw)}kBGi 4^բPM ._~Ϙ`m5"ּO=(ӠRYi|ٗ=Q,<}(>Mܺ:YѠ_9iQW݃Y[vM+##-5y:e|߃4om} *޳W [xG[c7d1)99xFd_ K+',nsp5CJЮ!6x.L!c32G`d$ sߊtU`ڗڻdSn: /@mv/ԑk`)=&v4|P|A`fþKC0*-??BŶzi̚i3$w||{}++<5 #IV>Lff1o^pN:E4VM]:)֔&w<~ R2AX/-~Jߊ;UT!nWQo6VlʧkW7^ơ.B5%/Ccn\:k:X׼,nu kND-tu}e{oMx5hmHF| ݽk|k;!k2dR:=xǗsk8MzI<``FZu?w+u(%WwK?#+:9-^uOIlSW*rsn{WN^Ȟ{hg+#YyvK~А-4s?F,S<[FxMu#tmBH 01+:I^;}-.SJrODm//2ōD"H*%ąefgm.Y r1Z[hֹcI{*i<-30ġ@'Vz+V,G$]ohš!+47vUiN <2 Ek9]1c)nL?UIDc(F[j9⹈*^RQv-[Lc=RI<]6tA$OM^FQ=gī"^2*r^GAo<,6\]â819f#Zγ'a>1թ{7cЕ:x6v^FdT\VՏN<̟¸8g{iVHky?թu.9q~wL tV}kBp+buV$06 hB=Egv*SLMYI皫)li SCE"f\u޼v$ mKNMd5gڒx#Ԅ:Ԛ1tTp `͓y0; SvTd 0'Nj͔k|w`1W tܡW4fE8,~`? ӏČӒ>sowޭgÛG}DnbE9/ .A]/>xDGYu`ҭy:~x#mV_м 8F槈Jt*Bp |*k:UjhuH֭8h0zgK_CČSя|?ᖝ\MY&#yKH_\U|}g~~5cwQF1Tlpqכ?_t߄?sq}.m>$n'&2> t^Ě=Ճ+].E8:@0l7)鯯ꟳn7k|N+[mH߆Pi'| _ B\N̠0liUyk}YGdmdISǀm~0_&ëV]kݴp@ NMccᯃsx5ߍxU kkH)?T2pz^i?gwsxQdPcm#{275ԇEm(djtvB_iL(ޖց K F>2Nh_`yd$$cU$WC&[MIPDYY@Q)c99[//z#-ќiP2)Pe\qް<#I%C=٭4}Fy@Dɒ#.vWn>1 gw]. đ=Ò\d)pxQr4/sş <]_Nu?]^}X/ s@(F3I)'r;&'ڑFo1evQqx޷_qp-"Dyx9W'' NjurӃ{G]'t@>#io7FE\y˴aC|HǭEZ\^kh5ɚaQr~'=sYV>1~9j~#4L>ytY a R9]:3-[֓6POj׳rJ~bnL{8'n]/BӅh9w/&<-Z~kncT" oi [%4mෂ X85Uh@j+ۿmcA,:^F O | fAM"I"HWgq`ץW ah9Riki+V'ݟ[XRy씓mMhp [_8OkdH;#qm-M|cuiׂ7Cm NNs]<sV|^ Eu:,-4@ &y{:l`-is<pJ4r)XKMӒ[O:?QSsSVѷͮ86྽b]_Ik}kJMl2уs>+xhuoXI3lsDHg>awRѤ4?YrC~,PYg:ׁӮ8{]&y>jq 50u^J+ӝqRi[NjU[όzs[M#'qDe }:W/Ay_e#dsnH(p1xGl4ۯhp_^ꚉ]I &ǎIx+=q[>#Mї>3i@؜q9EMsГu8:x(y*sछnݧm^]B<yvq=i~__ jַuw6vVu[ U18# uNMӾ PB(xpH#zo]OK~0Nn#ʈ|(pj=3'~th'i #E,Gݖ}˻9"@Q(a#^&uPrj? R-}EZ||q-(5uI.Dk(s".s=ͫZZApg @P0*zzuOni-PN-Q oWow 9o x:4]EYf aun8xN}˭!H]`d\"!w'<rh0ӥ {:n+[/yiteϚy*ciV?+iWI-wxfr]tTqEᯍ?2-4Nc{ΰv8- O=X;bYxCº~5 B#4ĮPH+c_iZ֡{x M"bbi9t'bp­JW#4t붶r<5,jҲMgqMߚnyj_ckק^h h.`.6Rsdf  ^h"KKh4m"$%;\#{|ՏlGƚG.ղ`7ھ5ƹ@־"s)Vc'LaۂÒkX`Q\{oyjՑ<~eӞ%h[UnUn㧆tۿៈY4˥Z5¹t,1| S9>7kwqS6Yu{BjKt$|)HKτ6h.[۫okF0" M/U_ڪxcúEڦd2J6mVA+XNn &Ub|-xKNiyrTE5<$b8v$g?7ޯ^+&\FugӢ^Ĵ- !S"W/왣|ZGᛍ"-oR3A Jh6@N"~ xcNuxrUDq<7 `rg13agψZNo= Ğuދfk|f&H̉J商/k~!׾Yxwᶍ]3Ij:Iԭ9Ȋ[}Е&7X}5+KYݵak5O7HЅd`7>.iPmž+.%}3uoicyѼJѯv,3 G/xkD2zzשF\ $KUٳG5^> m&t[ɧ[$m!DܷpKpDꠎsIŏjuox k nSXaXkvoI'8Î9~6ixCp?]4IjGݘbG̃a ~)'w'H/4o>ֲq ʎC+0%xaQl}cǟgth$Tm\ƠfX͜,s<v ~xƾ5я$iv0M OK.o75’(EwJhٶOO..>'^}ƹ'AN4sX(|\I7]d+A,s3I.4BAn!eRc--mjl_MƟ@2M&HtHfk2+V;Fwn8ARv{-h5(Rasq3WPAF['_jųeƤg_.)DfTl.(8‽Ko]m;dӿ5Snlph$Yv=#Ҩxu% nA/OܨnUʢ,}s׊O?K%b6Z(TU>ׯԠA+_A8G] Oѷ }w_K}mO4Ge8ҧ?BzНCUVxlDP龂iFKXvEי Wg׵rW$wSG#9$c'Ohڲ,c<*ֱZ[hO,(XdsHx*ϧ/CR۲8؝@nSӥqЫ(Fi6"[!jVZ܁Sq]lpg1kޙxğenFrB\GgA 8m|WqyeXc6- 6pMuM-v]Ibɦ-hQ2C x<׫;{>ut[vNkVK%['ougX|6¥V' 8Ec\7>pjYls'|};_Νhon`n&Tee;qf4McٴvrI^XrJ]Ԡ&*_&7+KgB&ӾQrɢVɥhΨ^cq4ip1Mg)]Os_kI{z]l8iZ'Q'#v#N>9x~z,7lY[;|p Fq*aM8[vvJQmiwOzJ%촲h$]cy[Y{{cA])%p?MR_Q]/v.0*#[`zw }h]]mI ?Jg0~m_U^M8SQMGnЫj@x Xk<!FӸP{-z^كgZ~#Mեݬ>\W +fH/X㯰:\f>|} _9]8}oKCW8NmY|#|]H%cf4@"Acj_O5"#UWw3s=^Լ4"u狒d`nlmSB-׆6c\i`|q(#ʪb(.2]VV_c!R\FeHa+Fc%<zZM |{VJMn9\8[>O\Wqw$Hzg?2x94 j&\r*Yiy9` +㍿1GyK(q]A_r<_&|6Ygo|?n[N-#'91lqW鷟s8,K{d1@h| }'/\h?Ι6gxWˈ?ڼ6޽eNk~ =hoKbP^I{烸4$1+g=r,SѼK۟]iO;) ~\*!g0y fH<þ-Om ʹ[OJmU< g x=IZƍºO~33J_zv5r-7hB pߍIS^>_6m&<{M/QpQ7CӜZyҷ7N_4I>(BVֽRڙ,V!s E<7e&kCKu ;Kv';gm<+|B-?m-4}#UHGrp(Qs[[kZrkZ%Οu4E'`<-qzwV' *;Z鮷okc a)T9]βҷš:Okz~(-mCvp<| &?ּ7~2iEtYbߕa; /ן^?ឧ+xLxHTK~\縬_w~km=^Ƣ\F,a@#Z,^eGBn:4y^SzFz72tkj*qr~יi{6VO5Om.3-ŬpJHVχ'ox7>tlJȱpxxҾx_]~"1\ 9 N9'b,漽:O$,ҩ_oo,+.B`}xJ\ekܮxpBJ ˙k&oG2| sjz~)ZH`In^_+v,x3(ҡė~8[yKxI"p|Tr9#L'gUë4djOBֳi:Q bx)8${޽:G%mZMm[XJaGٹҜegէf}.(iPE{Kw<1z>x;Q;Dul5 [dVK21,l qpi*-;a#8UC_ I+qt! :=3 Z2ҩiUEtV[[SˡNn&:'(oviI(6]6]֒6v<;B2N@[ΓLE9dlM. 8 Ik{ºƓi~aYiemxl*kGhXN $%QwI]oTۧM60Q*N"49rJܟMgH~6 le h""p wb^5]GP:ѯ},vىRX(vx%G*Z4wVF4Kf W|%znP($[Qm}Ӕ-_i _ 麆,cQT.E+%#aT5gJxFyi[Zf]BWrb0 Z_>;mT泧=J?ٝrLD 6HLn9򜏃YދKEeݫƳ^!pBdq͇:qo^nge/SW:Fgߌߵ4/G|-mΟ[\uo04`-1w%Kz>7xJ_okolU%#ϔߒ/`z{ e[dkNmNXԴ-&)mdI! WcP#v>cFʯُבEl\2e20' '?cz_6O"kzV"+L8gy*#| ď(gǟ |@xJմbm4Ym5Um'c%<70P#_/N]J<,R{?+%nv8|goUoo$1ެ‘1GA/C'G6JӴ-&GӍ;CǽuBUnp1+ǭg[k6CUƝi$'tߞ{MuoE~[;oo5 $Z%`IU0#y.fK[^t}6¢MB24 y%\=zͽޅ]&k8t9SfX|x?bF? ke}cVA#x];sxZt˥E IZ 0IU1o3(-Wyo)-~̾*i vp⏄_jڧtj+#<񭠍Ydcn gs_?~3|TW^쬮 HMfv67dN-x3D|Aw|Yg1%2I4VZH0?6s\Wxc'u89<=6W[Sx/>+x>[Nu/mݫIr o|đʜl/RcŽGju{U`)i7co~#-vNgn8կ SFN|'9y5!(r]fv,n8coen$\NčX^V<xÖu}MJSFSrOCڴMknG?{>Ÿmo>c:د on,g|A]6e{hmFV9 9Ӄe8(A-oz_V{Im#Hkaa0c߽x.뚃\Z\ 2aUB&_ 4夐p;5rR-K9jRғ_Fq~_I.6x?J˩Yc}Y?wm:jj= &݂3מ԰#4B;_F]GWߪ4-g>˭/7I9i\q=?9M"k,awIcgsZj /Qdl>SXUe%QF*w %}]GR _JBa12 Agn>ԭ%R&T瓎+ЅJVz ҕh>h-^0o?Zմ\]AkhIE v *6a%d~c28㞹>+9>[JRwivw{k:%ŷ[˃X̗v&FpAsֻF>!]YvZY[&&{W <5{]n'dO<*H$a,#lcxY5siٳoJf~OUImVyR Ww{_iM7>%x?[$:}%%qlPsܶxi:~V c'V33¡&bn,Hʫ7d}J7(ƍXj "ْS|9% xZM8y=\{$֋ի>FN:/Fq톉e7t Ȼ,ג~9J|#{#ݛe)"xv,zՊFNϣ%K==t7 2I?.+ W:rkd$P]~ŷ}<6ӚXZs*xzTw;n}C#F@^%A(fo3 (*Ry|xT Im]5iX#-RWgqcA*ɳ'ttxs^{m\ t'==BLleCDa%{7,}~1@bjf(>eYa? ykj$rK ܏KV?y?(Ewom|w<ϛN_kA8=B1JҺ=Huw7p"VT :|-va$ں FPG';dy!-ӝ0 hڈ%F+i>wN+QEdzIVjG+R8.\CE5Ug96bLgnc90ˌV$eIFې΂8#,|_zj~4O#jt+%TҺ=cg'?UEdiQӧ|_|KiŢ :ݩS/G|'xĺ]Xs*t)_G fT!8qAߖ>]߆mCC5xׇ16_ joOOWonx'­?b.GV3x wl|ۏQA]< ?wĿt/|I'H֙xY"υ;'_oR;V:5\)>cߺRQg+k._躅n5KhRCH~ Uzï|<~ kxAWSC+9,}(. |է|Zq4Zǃ7gqk8'm<)7 Si=S޺z'?O<)}s[[I[F{啗V)"f7T 7Qs^qgBúͷ!oASs#/!jZ΢X^ZDQ vyc\dž|/{QҼ@ڳȍ>_рE5R|4nF++m}W~}7Aה[qN1%kkWzP"x{#W:oO@8Xb>c͏l[Jm2le ¬6r;V7_9t}'b9YdfU\Z>/|&4b䭔;{قHΤ[c1sJJՔխQ`sӪS囵Vk/v0w}oŏ_o ^DCm䱘#p `q*'C_RY&; +]%XzA4OEΥj ;imDv+|ySr"~?>o`D{B 36r2-#F~"^ W啍X;lVHT66|܎ xOcl5͚n5&P #e܅d' ע]ZB-|? xYMMѭW0߁r1UP`焔j{FҒ^~KGn}aO &Q7>vսtwJS|wĞ$Ae|$рͼ+粩$Nq^k5Mu|XnHc$%ʹ|[̘Uב|Kqӵjٚik&15y5{fZua ͨ]}e@bOqq5jcJQI/wymտX .pN\'CExǙ;İО<,;%Ѵ{ZKWw$u3ƾ嶁u^u j[NoUs_n2sOÝkW4iZ֜709i/= s _x:Em /xR5b`zsXb}zK(J0v}ZS|)` 6E*RZh$ێdKw]CJף埉-%'XyNQ`898+Mu5/|?Ѽ#/3q@2d a!FHRH;h5_yJs@4lp 2s 7oKZ/xM-`ږDS2£_ .)8]XMKڻZh8Nrv%ڿCw૟ h DԞ[0>coc`$: SA xF5тs,/nN5u(]?S焴{IA)x]!I!++\~~q&¾23wAȘx8VbCs: &M_/솦<*4J! I9uK_|<:#-/uƼ$bXKG m " E~f7>x6:~,`k9$s})'+"ÂJ`潟ĿZ~:5; %zkHm/TPGCFg}qri˪k04VO0 $ p_}ǁGGFbj4m(cX#2PBT$+kW]YuM35݅UӬ/$m@S[|>0ͩ뚇7y} [ӶKkndR;P.>XpMr)eIg %?foI3x& FkD㹻VMaZ?i|T|5/Ē/k~@ 87" kwKC]ȑ[F7)~|i߈6]Qd64K)vHic@@%s1HPNJ+sNơakus͍JeLV vc\6~S'OgG^~Z~L~9FYio>2!^$=ոLWß|H3/x'RԴov7t!qo2f%gwtbz@u fsfZ|*E_,D[3}2~heg)߲V [CNu߇@N"ךk;nT i%o_0 pvߴnxo_M|iLJ.]bfڀp!pc… V=?~U|K-cMj hcᜮ.܅lc|n- {{ro jjkm%k| #Glld·ΰ[ڇUޣ]oß M: <%w{p~&2I |usQY{g `GpYpq߿ZӍ)Sq}KF3o-yQxWQhI%s1VlmⳲIBqztnY6\$oa5:[az`q]oA^g @υF9?6}װ[YoK7=Wrm?R{ pZN.u|KXN]φ!{]&O.KT! ݐU 4v%RPm.@0.dWhmlzIþM\\Z]ɗNEh$/,vxٴ$-IF.￧M:4x~^iz,@;_G"$'pc-9\0+{U>7zY߻U-Zͷx T|Y)5C#JuO*@ # n\خ;ZҿWKIY^:n+۽$[ޫj~,zЖߑdIUY]W_Uf7!=ƫS[{(+rIx:SRiJO1ٻ+|tdjWW9m ë/}np=y5vR[.j[eۅ|'S]揦sN}-[ټ)$Wc=W1w~]NG0^W1V,m__=}/y'>|HZ4]X,E$qk>$9| sڳ[^1T8_DB:JaKyaY,`Gcb9$rA>_u?ƛt_Zj:K±&?tX$w7m _+t;㇆uh:֫5 [MnȆ%nNXTu?_l7k&[-D @;|O|%4k}zn˙/f6.0y.hoh;3'u+haQo !8b A_SN57-/[۔ՅQ%3}ޣ$ZW^W2ϫW5no^.oh7,lt{]&;bY;`cq|^O~ּAovS>. 2 ùpߚ?~(7 ֺb1Xd+Do|uk㏈>"uB9ww`sN[{Dx-7Pz}2RtFh n#^yxE𷆼id?GI20OfeB)OxnŔ4Sƛ.u&8⹓|"'DW +6š{Eyؒ V(YȨ OF{gBHI&ZNIrεgReYeI{ieMiOJdok]Gvxu+Cq%zm 2@ZᗃiMv7$m0dڄQ0zp}9oZMcTtVwygV4[}OcĺRNl5C!`|>z5<"xx҆^|ۤI->O]v;%hchܫ_;Iu5mmCăϊ5I?uG,Bp0ۻ߁1[MjLԬ<7oj7gkp:Z5oxV]{yRRௗ;>^gEhto.]O]a[[O9P$;Ww^;E*\ O٩Z)SW5v^ZЄgB:4ݛ'tqŗ? %˫>KMRxbwq\@56Í žk]gQy"՛g8e sz//?Fmզ|&]s7@ ${W žӼ?uq(ҧ-'':)WUьh8JU#x(]$SWi+? 0ǹFy%庺kV[:ÿQϫo\!;{o7?<gM]=RWt}Vblq-!ی9t_:3ƺ_k3^\ȅ7 U9+5O-kwwݵPK:`cBې[leޡpn@Q-?hh^yG-ḿVmNI6\n$^)'Ueu_ m(ʩmz߇ֳOCע/45Uo1r;S'iF2MQjk{X|Ml+qTJqMK䒕fܗMrXO~ܽ7;V+m8:P}UOUc 'WIy;DyrjWԥM^'$!u"#>;5|C6j(Rȝ. rX(ܼ*ȸ/ے gi4:S*bm&y '9?W ǚ5ׇ_iZcyO1II爱VUi>Pfr e(~:\oROA-յ\,򘆚j f S\&NOZ5gH59$]j ( 2$(,{ ^k??o xMӵ^+tHtYQ Ns:r{O(_<(o(R^uX!.gp 5gK6Z6xkpM-sjbX- Fɤ _iȯ/~x#Ǘ}Q$@Ct#dg] ߐW]|+Go< MgZqy\xNfH[{uGtd\.w#du__'&úփM?Eu[$l"N} 3Z%~$վ L]F:xEDf9N] =q^w.~/x;Ou]|-ZE{g[ H TQiHՐ2-h~$|"Җ1 ^ǂ$`d!?2@[㟅27zčG&X>u O{^9 yDkhdfh?&ڇ~1 A-|[fW1$6Irw#IpP=±K/ /kA-@|Vd;''\pω'~_׉uV}n%-m8`yxQuBJm5b;iWsy,OE6uw#<\4`x{-_YD'_ aZޟU.x~CWZlE~">QϗsS໯ tirŔ+NUpO,0MgB^jzWAj$EJ˹b W%OV>cVZ~K5&M~ayR퐫S,IkͩQ[y[-ύoGm5Ay 3/q[Wֺ'^XRSdv#6|b|)q> nBז󪓦H͜gjx_Ku Kb?ėiR+`KJr9Z*eI=+y['Zʴj{ܫVXžI nA[;–\/tI))Nm=:j)t>iw'CQ1Os,q' [*x}9?ڴ:NJ Z7ٵ-dO5I/nt i.Gmt$21^էZOUEI'8[+X@!7p7|檣pUdtכj/[ށ/ 9QR\7[ >ҼUi߈g ܍V6>8 {yۭv:x5-)|+FҚMvXZu+1$X W‘MۚZjU0 g簨M+>gtޚ)>ttSK{tkYgĞQMiݱ;;qZ·762ז >З2eǂz3ջ Rڰz-`choׁT4 [ѢyqhvD|h- 98Rq0v],Om9GS{З:T7ulMgI}\#)1خ9Z͜1R*ɓӉL WW!ITD6wy>ekN>ٛ0ɏiأK0|#mMu\]5qԒ2O&>_gъRLdiIbs%PQE0|Ch`@؈氏Z?]|a~gŗŮwi\hf8W&p1pG^c?['qxxF*i"#}lXbGQ+b3F{0Y_dglݤ~jNٲ_1Bs;(R}*~WOZ'xH.q_cz͔ZYλ+$rUaEZVqo?K`k|Op5(`9~}}?yG4O΋6˞ңZu?RfϬ? _V5Y~bs9? Mkm\Ia2@8ۏZz̵=makӤ" 5 kg{Y\eOum +xZVKf+*hS[WCo_™{ iu+;ۥNm0>d ^M퐬j~]kWZVj͂v| =gItҷ?%o~kEVZ>|&+^wdס9MĀq3[? gw25xo 3i-Yve\3c5Zvzg9^?ܷY5TUUtyelg$K{qFlJpNvvlgv^?Zj CoswzYiڎm$AW$s׶1?( cz^̟[J-N|-msިx+ k^ EnoG|6ڍS+;Utn5{%ac\$SWa?k~D~F5hzm{clwa C#Kl:C_g3y;~mH=RS<{{s2qS?o&9u]&}E-BXxVMPJ(f Bj(6|/עգ-a;mKUW^Tt 6}꼚My. s3 Q|޿f ď'V=. '' jaP KywU Δ+a'(55 ru鵎V#ZUR]߸_齶?~;'t]VF64tDnR6g{*pkOI>/SE/2dYSaq!Zc_¤9?]xdX.T1֭q_yq!"N6ǏSTiKI)-ҵ]ze 5GRNy{ٵ}9=;7t ʖ]PĺsK_&TV.ZQ9cw+/{}zh|6'Wc0B.؅"BmMsޖ֗ZHǙv&C #lv.;]!񞌲GZdʊmq *pp01Y)m.T[}B oL!@VҵhѵgŦinu[R+OVN޶~nW?(5{?o i~9v.QXԳ St+Ͷ/6lN2>fg{dH'zT҅I^  1N-߽}=GG^,cbY,6)B@#\|A^JY6=>Am1LrD< Q8M~]?_ cۤ%BSZd?y*dse]>aBgHjaN'-Isi%W~yRa'd+{N?~7\/]C\}l/[NwFDL۶ۆLf'c-gƾ0SO:s,,*AP@YHQbR?Q |?kϹt'wW>G/qsi}:+VV1w=_Zt7!s梥˵3[3p: i]iMy[i 3ْnTό?1+j_fJi]B0їEI |P@88<ifDG}woٜIyygYw{s O D{bpׁS7|};yzwZh"ؙKHZR)›KSY)h6w:)hkxQȓ "*2B+ `0ȲE"IXz:*sq@k_}Ʒ_f6K;I1↚*qda cs1Wys{olJMs W>#2$0~@̊{b,rI8jYjiwawA"> ~tk_ >*|KYi?g Zλ=욺XjDg ʪf"OQ(۵#.t6v>[hibMg nhvAv>+BOMwq7s$rXr@Qjz]iڅ7p6)pUє2$z@1#k>j6i޵n.$iy+BlH[z?)h4[ChFMbTBy m$& fb bHkZvB46H, y\[CN|#᷅t_𷌧5-B^&iZⴐ">҇$9sSĿOkCE徣3;#EbM6U[瓞Z /1sk;_?ZTZ梫qrS{!mPq |e pxz-CizA-!{կS,yJ1}">3Ğ ץLzjMi>V8h&%W9&_'Guu{lLjI 6* 0[O>FmdkMO |#+v0ދ'5?+ oK{ ysrտ{1}{vee#|7ԺB%ϴ-hD3vۡ5b߄îp.jQs(ai:rϓDJP**֙s3)0![6|'Ьլu(ZKA{^CyO-N~xʰ^+jË]?LecXkKj|lnbOV$ӥEFWڮiFlgƿqm1$tY0jaMˇ`zqږ~i6("c9`e%$.<᫸͡i lJ+ZZ3xRZntoQCla3]M54 I)Tv־ς]}VI#$J̀eA=pkG2-['W 0=F''v5զyՂa/zvzwa›i0X}$kcnM+Ç['S VL1kXR4KXFwcn8Z.ky1q>־חþJm?GF${#F@b2:Uo' ޝ R[Ge0V> #*;YPN8*E}' k|&T GePogdrgI>V%y\z߇â^, =s_< ] d 4/6e6QG©f QU=V?? h2yD%_2~eSFŤJAll$E_wۓH$7˷dၓ~ ;M}`^* 5S ^mu_G^,[7o_>A+i5ͅ=ֽkn.;u8ſ mG񏅼AuvjXFJ$ ys2dC _JQDv :,| :}n-wZ.߲y`n*i`'NNڵ-yc(Yu]ϔ]?W֯/|'su]@SN.-H4J[\FevK_]xωm5Mcw͟lm2͝?-]7z [\y]֏ FKn*Itll+(,e8xR2XpAc=J+*֒jen8fVV.|eV DQ,kTÖT(\p|Mo'x-55ɬѷ9*AЎ'TZK7G34>t?K +r5_ çI9 {7'hk|FnyAv}J>z݀9~'DZYUV\sg D' jrSI[7/+צxRhzK8%<7'Xz R[Y+n-LYr-O6Jļ$ ( JQЗw4O=W <2)5" "֯k_g *;[y&8Ի`2kiD]#Y]V#5brʚ'{oE_3k_g 0[ZE?R/"+ܮ3G ~&&~ZҹCu0=K$  :G?+}Y.U>"s宁H+'S🉭~^S@J&; 6ڴT ^g9K#"Ǐx??5O\Yǫimt CG!ŷdl`WDW~(|S`7|Qh,c,~GNEu9-ϊ>.|'^7Ӽ_[ *=;1䴪mV.6ěs_O /itGjڅh7'WC|/7mWǾ?QYOV{ʆl`)WiW5]Y-u4y~v0Hf#wgC'Kux<]sYO4ty`9Tv U/k|aL+h:7+(&yfANǕg fCVoïWz^WڻB.Jȡm\=k{~$?kf7>u2@95xg?_ur~n8ӵDn#q h |񗂭? ᏉziEai?\ ha_V]C|/gznk^&DX*U kg?kQ^_ͧG,%7ȑv`W!7_;>%-lu.!%l|X+c##4?}]xWѬ7|I?h/r^Tٙ#.64`?"~`8C_p|_ۻ<<*-} kcyY]኶ 1ieK_|/@}(LfjCpbeߟֺCٗßluIxdk`.9v0A aw|CcǾ)SW +x,< *ntϭr~-ƿ-?xvAT5VwGmHYTnfa>9š.7VT]'ʩ3f~zSj%owsmE˛' y^hǃ :Ρ 7a&{qs> ȁ 8jZ_CFi_Qؾm٠7cWLj ={Ŀ~xGQѴdGIW}J4ry)mgvm3I#7Z,LXA'SŞ%9}#K?xf dCܬggʹ+pp#9MSďZƯk~Þ W#߇/%rM lϾ\üH(YAi <{sx?YxFIC.$E$+&d8CF!kB-|ukV>,ᴺ]5d1f\a v@=MmzV¾mnSQFa.u$V]gbSTn|aj4h.pZi6Kp":4*X㯵5G^r5SKBmlHS!dx㿘?/zt{-G/ԣ&STI%o>iT"HRp_>+k[sK:6(`JF ukqzxiү<_\EmVƢP@g 3>2.wGђ2;?Þ'#(ͮ+MNP],&)I9)5oxo⯂ht' 5g3O >лb r0Em|qEgŎi%Uay$-1:dW~Yq'֐ֶ[&F'$rNq^W|k֏%i70@e!qxOkڹ<5cK ۸g, ҳ C`(' >:|Hѥ/ x6 LJ\T[6V[q~2+p2 C^6>Zd\o{mM8LfN~C6lW%:޹U 7QsYn=<2X`Wp;JP=uD׼eYi1jAYe#Iuqeo7|[;χ8uy!O4G]Lk33d y9h<upqE~{ʾ5 ~{1%~Y1;F ׮_g?Cд>+ӯ-8dMaFIpdk7o75VRY[[A3|bӾ-Ҵ;j$35݁ 00j?هþ({+W:4k͉n`h ÀT;wO moiִK[໋˝#! ݴGrVXxsƒkׇuiYouc ,,RO_ V%Mo+tb%eRe'q6^eG|nşس󥶟OoUH5ek" Y.1{sMɩBmVv+lp6R qxq~_c/58nU˸Jpqc#zQdu{fĸӴh<-;S&-ćrl/+fߋ)|kN q vB|=w{y{ز_C݄,2d_~%x'○~xTƳo7<==8 %OrxJh+_\~ 6eBZ:[_g@Ld ar0MhįSҼ7xbo Z\Cۺ,FgN8y4h j|-cR֬WZ^yn"p1 Z ْ}3$ZVY˖j?g^!A iy7֟xc}$k"0:ķsk%"Uh.o}Wί?uFX𾝧Aly`F"$ u GUѾ&1[ksR̅dx EJ95 :VlYnchfUс +n;+_ 7? n/Haްy=8 ITկC6ڜpC"$Nb6|HGc5j{ek./l^\F6I"Gg~x'~:͆orv)*0YO8V߇eY~qۧ.lWVT4r*KW]3| Fᮘ5MZٽ e$u Ϳ4hd4u6#l#5"A{wA6A!lmݿ#{Wi?|1Ѽ'5o _ 0$0^q"6(o 7|[<J/x:Ƌu}-Z[pb8)6n"`xZȗaX'9#5/j&&ԴZWXH-a( Drk*R$^~'xTO?pу(t1Z<3>-X՛B-~a rm5MɎ 'ŸGgn&|KQm1099'̚Vk_,qI<Q|!c,O˜y:W5ŽA,t&RׯW}[hZMe\P)0|zQE)@I¾ <ii6-<&vF/#՘}P J(#QEQEQEQE`Š(6z6(A974JEHgBӓ{9xGA]",t!4P(J((^QEQEQEAk׷L}"9-A!]w PSR]֗h1EcӊF@ߞhFb)yU@zA(c4QEQE4 P'hJ(91` E.v(+ER4mQ@ьsIۨER2EA@PQOQEh2 P/_z- 200 313 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?;X@>_Y4KQfk}:<~uhKQfk}:<~uhKQfek A~-4ٮbrBQ9;y_עX)u}7EMQncx-ib_aA'zuVisԤ>RȻ 1-piu'oΏ5ߝzL ]KpZ-ƚHϒ? ]o.-40xn!yoΏ5ߝzO5C=v3mwO\Sߞlv~LӬl5}V[yMk@';pp {S{'/]$_Y>eNıj] jeȨ_dW240k/8"|~ty,њGo΢ |~ty,ֿ,QPK!%$iXj_]fOU+Bv ̍wƶ_xAѴvg11I@=nf4F\:lq1fy6h#ۊ ;?ki4ŽZZ Gt] %5;}"-;M:uI.5re8W.M5߈H[dDY~Nsn(υ ;?ki4k&ƥ7FPhDI( {gl8t)ŤYiFk!]s8+\?Gx|--?ƝdRռw.glt;MӃwmWW&.-1As,(_x֗(υ ;?ki4wyhz_(υ ;?ki4EX[-[v ˢqֳdvy%I>+whLս3S&K)D2+\ys>G(υ—#$;*D('K?Gx|--?ƀ8mU}&L|l6Sy!qAOl o(υ ;?ki5_J{4Wj2USz_(υRuF*QRU$7vϯhdTlm#Ъ kHkVZı/7pGZ᧼taTm](>mBWs䌀}y+^&{%)յŧvV[3cw\UC Rt2L3i ]\5۱ <ڏ="5j7 Ns4rj?ca *PN3\֗y㋫ۿlRad JcxӚv¹TW~Ҭl;u׷}}s׊}u}iseD΂0; c=J4W^xsXxKa T1[ ^+p]do` 4XO}iJ>dkQ}w {t敂Q^r5=_UAIuw,bDbF_iֳ4ho-γY.,a.(a9Nsjvh.#r8s 5Y5msEn-n׉"HS'i=ϵq֡xkjviWtb;2HJKVމh9ɵZs$FXv-`d~&<ɪǨI]+]Oib0?=rjZMqO"Ĭ52|NQucŒ=PBV.x|\54/$.vw#>MZg<$ [k8nn#i_+kc3Ӹƌ7~)jk>[kijD#&~a;RtW-f4,1ldQw׭z5p((((('Ě+vDq,o7Xrұiwn(+^kw/7T|-o`iO)[NY tCRuG`}GDi >PD_~?nknٞszιxqŠ}:-OO0xuk5[K{$^7d+0b,;q\uM+JP\RcTS 2' gvk .x<;{;\h&IRsZg>eݦN^:xNW%hoO0|P\ٛB8~ԮM ]2ekk4C4W.#9aI.<>:2[i&sfwnq5-ܥkЬY&UlD8#{Qg^ĺ itDjvnϗ uBAn?Vj3eKYY…_hrilO&f.}x=eh>ڭߴZ^TvS>6 _F=nYbA(밴GQ趶Z)\\Z27ڦiY'k}I܏d G9N uG/ŢhZ/m*ۤq*I 5OFԯ,m-ͅ\^Cwyqs)G ; xsNi)k<٥VI3Nc]9no&ЬӋ DQ>" =6j _6Qx^Z _iWkWpǵ;;_A][zx_LkPRտj!΅ @nѶ{9Fj|6iKg[Ө, zV߉ ic |&S6WC F q5mO6s隄ᮍ 8X|_nu4?jZoy}UKY-nW捏t+K-N٠jJKi$P8cCP|?lX]Ag{E+1%AKc2TPq Bzٯ=> ]e-]Ra ٙ嗌.T18Ic.ty4r[?]`g"~6#Gm2_ TM`X/#t>jZXww|'[=q' PBһ WOǂVciR\},]%۹/,i?ad)M&337M;i_6i;jg$*yB P?ԢӵNGPK_7:cn;{'@֡ӴKPk5 chJ?Uŧ=No-nPe83g j ~ÚM&9C<]ӯN89WxI1y5X5"Q, D 9#(nMՎOhm$++#lV!K_[\_^fl,ALGS+;Yhs_ kmYRdPcny#[m.OCum'+h6.t3 76Bi5J&{M=mЎͼk'G_Św#K?ZGƬ*e$m 4Oi:>}j[ޣ|yk:I-zb0E;#!ִ-k&m$v0Hf%e5{kbK.ƻUYN35/iz6w[ci4#!A=ᴰ no >| gn>Q2) 3MzYam7r9'oJLѵOG,ȬXӒ{k4vI〼d`c> ^aG :IAV-9#e0gN\g=ΟSy.m#Ӕ=]к29=OVwoP}X-'1gX ;ҏ (i?lI::kb-V6P4~5n0[xT^ImUؔ$#%5 ]_@BىSG<]ZY+Y v1` 7<{4Y,H-_MA*Un#s ׾':΋$k1fB|ŷ\`R47Rivla^@6e|@խrޝN٤$pςhz=F} [Sӣ3w5&Y[|[)bxixI߶zg @B9_s ;ּn.)˕\KxEӾȲby]b(ShItY|?% L2FNU ޙmUM+Ptk VkKđHOCV Q@Q@M.m[MFӑeo%'< ]{J֊iok4pYW807`WQBvW<@neg sZBt++2涵k+KX&ԴY#^p&#(@|:z\ÕI(Gy cpG"zf]>q mo)2Ǒ'ӡzG;[eu+]5,bW1̼qxB[QԒ2~Jj)-?ާXaOF;_3T֬ZhB/S֑i"IN9^E+6 Gº<S+{+UfYq 6s]O+]bPt]y%Y FߩiVG\Rut2kf~o̧V{_/qDW VuOZF_k7/w^ZARNq}726"Hm0<. (s6߈nˣjW; KG|)^2tM^]@;GrYzz(|GKkڝ_p=?:x:JYZ[cluhTkOdҾەc!zbh5޹~ZNgwt7GEEYΛJ^ۻ x:o&u8o."R=k~"ͣMQpឮPk^c3G i FAk/QZ-xm_/W2봿{o% FAk/?d>?Z[lC__>+RX$Y"ae9onc.3aEyNO{4=ֱ=+#Ո ܂w-|{{\ipkV~f0I F V =>2.WK @{ ֕֫ciY\\$ws&ѓ2Sxu[H(K+}*<[ꚽaŚ6] 9bQXW=x2P2<0=ȪVi5Y--6 qnU ʜR@-=ȥ[CX@wZYҶ'G7y?$Oxz ^+ ffts(860{s[+VhҶ'[TRe[]r\D͹12#=8 zU1z@!@ԭ = '3!5ZfIˣ@xf[Y6L3Zyzn`3Q~0/ktĺX z_ZQO +x-KIm =W}82ZoaFAۆ4ZxbR\ 4aG(fgW~ZT]ny-5$/Mqf;i oFjgjw>Κ=C"crw@n?֟+7mĬ'GOoq?Z?VuAsZ-n;ˑF.˝݌=?*̰F7ovm7<=7fgQ^'Rk/ }I5'|7x?y5}+?I4~*AMoX2CM<6)\m+c1WKYZJi]v1yqt*ۭ?+"?IGi6Αo6r_J4Ro"@snJ.'cn/-z`ש}~ZVV?SSºZLjovN5{iD"(p Rb_^N[SII! A}v9֟}~6xH越ښ˿%FϟSV}/\!n%ٱ2h'|z_ZQO wfNt+%uvYO;ʑJ6=I^A3!`cO+i?PGۭ?)_Kc \N]0AiPK,ڭ6wL?z}-\|y6;]2+HҒH-;b0'jy;׮{Z揣z}<, ȑ#Af`5/7v]sl/#% D18ϝEPn(Wx_s\e^^MDs G\x_fIF i2w"IoAhtS7zj߿RR~šgrW6ڞaeY/*O*y ܨ'~3y;ף|?^G.%K{{gDN傃T4Kx4c{ۭ̫P21iΏ|?^ I0EzVD]VM%#HY@H*#vF~7>!KK5"T36ʲA.o\ݤ$gdkZ|?^|KC}kVF-ljp]UA ~5KE<7z安 L(66㯷Jy;׮zǚE7Vcr0Ow\QZגZȖuA?.5ݶtv=ET}0?1@ݵ\P$OaV+κlJz3ubqɮVvwW (Š((((( `6d 88.sj֓Wo--RYdT0 Y;IBCڽr;)FTgc׿H/|M{M"QL[-"k'p:u?hFco][x|G&n%NFsڽ_}G?}?Z[_֢oaG+jH,ڞfض;H qP|3c-Fn5%Ҧ. N8ڽ;Y 06qU^C<º]SL >jf ɒcr~9Gu)u?I$Q$6\w.$2+{cjwu1r_w? >;XojnF{Ʃ-ot(-nep e e~@z/Q?o;{;dG$I/`g gzꥍv{3ysCwfotoxx-20.08/images/draw-line.jpg000066400000000000000000000565471362435004500167260ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0232Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 346 356 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?_wMoh$D3*Ҷ9v#5s^SY5_ܞE}e:#ƫȟ3<~ty4ڶ>j[S$l Ѻ1G]&/i$vsH\lGlzoΏ5ߝy&$ NTxiTIvۇ*yxzYhmZ&WԂ+;:p=)&i_+𶅪ZifKY#!H}ґ:`a~uuqi=oqfUaCp)_דZ|[y-o×1r1jFI,N-)>j5 :ØӬokﶂUd/gB=|~ty~ x—Lw]Q-a%ƫMPm>-D H9#p4'pz^q|@5bO$Zݟ/fpGQ)^_\FXjN{kJ_v:4\U_+~,}'R](۶,Lqhb r69+gCwn`V eYN88^/<GoμFyqmkxj}-lct.1QxK޳0>6HlOC.3<~tyͼ]ze67Q]lBk&ӓӜ*>-jZi˪Fب'~Im#>ԮDZy_ךz^nxdRKUL<-v Wc|OmB ~ 涻i %ZdOw>o}Ѷ|Ot+3->KOK<9k{߳(`2nA>'kϴzׄyy/S st;n6ϛi4]2].MGV`kuX4+ Nk}]?Ǧ8M6wzcZϛi4m7hV@_ LOww'D^^zc:t@as)|I>46fc*Ǧ w`A^|OmFfy.Ŭ<ڲmC]'e*ZBbj] p淶ϛi4m7iik[#Zlj䲲Av` cӥZg=SpJWx䏿m7hw>o}qXI|Qg 6sZ"_;L1ۯ54:e߄W4DSm'$cAw>o}Ѷ|Ojy\i%ϥOF. S>2¨~Ao:m{=^o Fĩu^Ŷ|OmOAAχWςo-6qHi)NEw>LJ_^XOpWjY Btn6ϛi4] wVKQ+n 4a=3ߓIaagaiojw2'Ԁ*]n.Ĺ5ۿ?ƍAc6eBb@!*ѡFU#8kw>o}Ѷ|O,ȫ,oz^YxUMuOyD2H#sn6ϛi5͈S$ӱۂ6Ocwl/56;{OrEvYϛi4ne?ִJc,N"*EyKTѴo [3g#n5ֺ~Ha !r1k~_dݏ\S֡mWK`Ol㼆,2S$qyĚgl9er_{S&Aҝ\ǪU;J#, dl}2?:ko:lW^eK#⬏ 't$; K{{9⸅ISs@5t_hvSjR];(x ߽O{zVPjM GlY@MgB}̏\߇ƵǪǮ)or\a'+<&tu ć0[mxy<|u TW ૿Ωyy[e_ZـTO>+Ş/ArFf ̊Xe7Qm\:+Ά;xXu{{}/G`bFy[ϸczVe ~y;MlBwb@opwh=bG%цk{}gu6qi4Qۈeb2;JU'5--% r4'b?+~-AEq~8#5z~ M # H}kGҒVq4׽H\7O 34[^ǫ^ok c Ɠiri*oOzĞ(:fi}N\%\מ KɇV7#R,[p/_=2'lQdo)NyhJ #Q2 ,ԓM+kicW+PGZmW獦]^ܢDJvn<ۑګE^+ AˣiO- $` սqys3x%o#QB{gWنVq& )Q=9<ǗAfcfLҭ"TK(7EvzdR\moLI9-J /A'׵hFGJ}]u =<\͞yeDd~235~s-)Δ+gAN)`ݥIshUgO1r\/|uy`5 ;x*"F\ԼQ/ MZW6z̐/[ *76FX=;EyNV+5b` mXQEc~#QaNo1}k.z]֭raKmt n'9|F|=od-[Z,=A#QִRG gP#VN8(Zow%խi t -^MOHPF8AS%#Cl z;ya_3ʌ||Œg_], .iw`B$U`"8c_pti:y m+[n1>)EM9Wmz\LHcÕzzK_j׾*t-9,4!MrZ|D7VkhY=Qv7 G4_K} K{[yk8,Ii@⼊+Ln"k6yܱghqQuׇg媆 P/ z^$M݆m;jڜv񛕁c0\`rI8[/P4[Mo {=bj_mBG"TIܲ4STFϵIyԁ[.fiecd:zTv$FVKF,IP'>λ~,IlʹBgSIFG r犫6mor^,Ep k;-?xF_>Ook4fx;*e\*ů[k+KhHӳI$؜;om:QUwHd,@$G|^kL!ӦkudR=+|_w7DѮuMNOsEq~eXvι/ӚMrKH4ZBYW-qvW;Iͧ_ظ/o>.& d#hx@,u8WYc3ڹ_W::/-tl]$EIPU {R֨:pIssʐp^H(.,5 &'6%>r#B3!#vW1YsSF!R2? 16WpN20OqYmfC5g4c"Ybc?vtPW<F}SHy7c1s~M~ H|Wi;Xa.@X-V0]kRs [{ CNUU><ԚFLf񭥱9}}GQqX;*IC E<޽bIo+kxtt"O[wu`9ݼǧSEi$xo.E2GHxTM#o?(1]k,&9A z=عw݌G`sNqҷjS$̞[lfHph~L>Ϊcßmw>ێ}s5ǂK[5ԂV,`G>w;hX!suë 5S9|ʼn*e#8w[|C&wjvsiEIW>{~M%5sԼvuBFխm"$Y`Ӓܥmb9&,ztS$v # 76GQ´PXthD@8|)׈.u{Y|"Cc"ݎpӒDhgڬdRn'^q>H{ep.pIcY_u-. 6MVnb$Y~6<ףQMɱ(xwZUgs1ۉT] :~+>޽cr*# #8]pCZ$6zdtk5& anTw5Mb{SV=w O'O%q*tvdPc ^^Ү!B>5?/ud.lGD}2dHþU$ z[RFsmpc\}VVbH,b#w^h@vD# 쨻M ڹf#!LxӞf"ʜM G"?]g'#޹;? ~V:ha]b;SSR gÞ?յz{˫=y( X|'+n/}rlUεPμ=AճЅ.5SXVt5ۤJrAxQ>5$EKyvHثÆkcc-BXehn4Ѕ$|NZ|-kkiى-Dvg'4.Κ(eֵd!?' 4QE!Q@U cOԭ4bϑ 7&:P( ( B#"nF}=nc8fӃZ*v#EusGgvG2ͧČHsJ{r48QI8~4Cw"$AGoP)j=Qt(fecvr ?r?? ]2TO'nDçacFw!UFI= riU-܉J{U蚜qAO=Ɠ11l'Q` ( *ټXYdq2 tU?]_>GmOP[K4%dy}ScjxtNǴ,p(j*B[j-*SN']!FǸ7n.=SN-Ŏ;#RN,4=6ec }Hf0iqn P78>pt|m]R1IXgx'Eq1ֺsqzg(73^ugmvb7vaq$~jã >⡓Iӥ[OqtC\OGB|_y]Y~KKPV+mm^ 'Gw-,uk,_kſp? (6cHӅq!c>tM*,,cxl}J(+o :KKr&1I&0XbԺfiBAiBB xV=t3Vn㺷xgPѸސo̮zwh DjI=z5~LS"K3b0w׎9%uagsmo-2mb-UZC" lO#q^UIs Vh ntҽZ\Yherk2$^ѡY40E@;qNky9&egkuwfn%'q8bZCG^=w3$=Y4>M=l${ vL`.1RZ[X[%6靱BrrpĕOER([F?LV~c "9Y)r+4xn6O_ǏឮV\L gڰ]F麝7أf"6\%Ocziv2ia=ـ?u2Sy!S`+I &T85 Ś6{ 73\jE ExƗmeokz}VhUUk{e=Eem`/UH.vs/-ĉH2TzzR"MK :WS WZ]OHrgF ;m_P|;ug0ګDrXMleFך:{N6L,3 0%T3RZK};Gm0RǓ5~-k:|(u_-UCo4dA"_RMNmj;k-MNFV9BĂ=zvbZ,W2}6^`y4NoiL^R/tOSfW{8sl6ARuoz7o4tXu x 8fnR{ XּKx[NTLKN{tBI]p g|\ŝXs~ΐ(Bw9#^=B֧q%ѸI 8t[5ä0h }ݒkJ>`eN=(((((C ( mRqVV7WK'L_Y8a?_EAEPEP]UԫEs~𵖫i[{Iqߦ+w6@֦9 `^RA4O P& h4 O˞A8_qLNM٭$7F# OV+Oɭ|UxMo&: Vs#Luz淫cOɭ|U}{/7i%ĭ#yb{1\m&ŵ՚Nfȏ9ێMP>Л@m6o9$AAϽKgmYxZP"+DipKo.%[ygF}MC HvB1=T-.1ԉ,dsIr=],^D\6b)SZغ_lغ_lp]Kt![.b ukA p0% ㎅rzzE6'Q6'PpcxcFTO8Gy/󙳸{z4Az8<'z~v:|{XT89h/_LCI ,Nkj^ JKK2 t#<Vbޑ_dY9آ M;JӞ!rxI'4Fnخ"Xu k?z<55Z2\bYPX=:r(>,<ϲX$BKo6b;9=95CYZ{YZYX)G-Hë zd޻H"hHee 6'S~b9M NJ_fHgWʔbWQ#qQ6ogKmOKmOQT4ȣK#nTEzW ((J4K;Y̪dP^C_Y] *k{p7+ ܩ4GBvG3K4OΐgRnBg"8H2\w4&lm٩ϕ2;/ Vꥄs!cWS&ܰu zzUC ( |)mfmB 若8y}.ioj+ >I<|1 WtZ- L'ۜӮ; I X&|mIUYp0 o|n#kszdB-I#{bwaXHs@UQܒ@񅷉.ņ; ֗ѪHk0 ]|0ee#dfw8Ӿ'^ݶlmk31 gޜuCh(п ׼GB&^"RQEQEQEQEQEQEQEQE`ŚOfRcq8f yP=hzv}&FSk>R7 ax%q%ռiwˣ(}Tr+vb;+g}Է)NV|fm9:V-Gm59; )YUXd1;spOX.tھgXIe@ɍѱ _CyJHr &:eq߹:])lLJy%/3x~r}k[F,.PfY.tn;i 楥ֱpYNeÜ.Fs 5?ou?tѯ_:W΄b as\+ZRMI켟sOZ޹~G-_o\OJ?&_B?zj]krI WɕG_t8y-NXU^ےZ.w>4AhO(;p1~g~˿.~ xK`< ]g/?'ZSYj(ѭ4Pa÷zRj^3v+Qt2>M *5i^Y-d8WxR{gϽ3zVq-c#dnvDBYF9$qZZfuqMȊ9"GXijYļqkZN翱/?"?/?"h{--hEkUϋ OJQ+@nB ~Z` 5 m`qOAV/?"C64Dm,WoBEVkļqhļqiޢI-翱/?"?/?"Ii?v;[K{o%$XY3Kr+g~2_캟߆.?6EW.?6>˩͟o.,QUleCp3$2x'֭PEP\muhZJPy&Ai5$k6|`HށNHZkǿV{]#~jqV^t97ne64,N Te?WźˡWO%9%=ߒ(WO_3/¼g.nk菀,{5x/>@~o<>I7,T̄p5屒$23}|6Iap˻2MFsLUYƳ˫$VZ,Y]N UfO't` )0ʁYXyLvVkZu=Z")q bI{~NOSO_G?ֿƀ.QT44jikh@/[hg3U5*Cd!K, v]֏q #MHts\<;'\u b#Q}nTx=)!.Yޠ!.aI>ZYePE{hơyI_vROSO_G?ֿƐ{/sU׈unɤãip(n 9`p&8ޮΟ3or)5ɨwUeg+Ւ5: dg>:O/h,v$ axut AKcoe@ʹ!@<8hjikj݉vW.QT44jiki eZ|jQdI'WF8e9^zŠ(2y)zr GxV( 4o0Պ(?M5bxF?MX*o&P=ϵ?MgkES?[Ӕܯ݂mV g[_6>nlkVfڛxEЅ%P8S=@6>m}m)gKGm3)i6>RF滽DЩy$x;IbKXD 46"ϭ?E%3 ZHHcp# 㱨-Mw+t!IT:"2O|Pϭ?Eg[_g{o!APMϭ?E3i Rc㐯Pg[_6>ߗ- Q`@lug!Z~"ϭ?E3h=ZMʌ-ҟ&}&=Z>w?~&w<`;FcFk jJ$ZWrٲ-@ht(h(((((((Fd??{(uYnTj r:e~kՏC\Ue.y R;E>`%5  @q~?4KM.ٵH5A\\2lH S\[#״汗U{6!"6w+&xa/|9j t- Yd0Fʩ!@ T^ ?/"O _\JGf qU񮯬i"=G{.m1`,9j-OZȑ?Aw NdbB:dW<=`[>44?JʸRYQ]Eδi%dfP1  \baz%ߕv}qx#UѴo 3۶))2x?)ﴒf{F1W#^-^j70ek; ;x+ҎKk&^+MiY-7dKI.';o֞_x{jRnҟQRcjӷ|+yX̸fwY;;Hy3 zԗy.eavQu".E_۶&0c(~c֩x#>%5 [ؾaeRHE vM[%Im͊dhPh6z'U"7s=+@*DΊG6ҸO w?j>(Yogԧݧ(ت1ZmBMDYWv8t/__G}iWN` ҒЦdx:c[f|lѵ7c8#RxĞ(W:mvZ/o-v A}OcVg𧆦2XE3]˶f@;r(ZwgZY..vVn˴m5k [u\4W,ԵYn-C.p i#tTVhX$j˩c[A_vW1@-as- ~;g֓Pt zer+Khi6scZ\,ּk$ITWfMo#7#*x  JmQE!Q@Q@Q@Q@Q@Q@Q@&( Qz (hm((=Pz 6AEmQE-&( Qz (hm(aKEfotoxx-20.08/images/draw-oval.jpg000066400000000000000000000415261362435004500167270ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0232Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 228 332 0 C   %# , #&')*)-0-(0%()(C   ((((((((((((((((((((((((((((((((((((((((((((((((((( " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?;X@>_Y4KQfk}:<~uhKQfk}:<~uhKQfk}:<~ubx{ᯇ5{ YjO;/FGQR nleUƕ,fSx#4ڳ;[5ߝk}:tohھuke;I_3x( &Kem^:M!ӥ-(;aˏƐoΏ5ߝv&#^ S tbQ>] ۚձм']E/d,( X+5pzm^xKM6yooΏ5ߝ{G<1II~kw\A,S0QUxhwk}:<~uhKQfk}:<~uk_wwI G93G7` ڀ3|~tySZM7Oc.Th&YH)B/]ޝ47oΧk{Ջh}w8Ys,(o%AU=2{׬hǣW|a)3w`'pqҥQgc^V|K̥\S7 Svl:+}vZ@TCXUX)*eJ[a5|u@?.?*R>:\Zo3^ G_ #qT5|u@?./*R>:\@o3^ G_ #qT5|u@?./*R>:\@o3^ G_ #qTWMk:dXX4HK܁yH~&7:) s[Z@JQ G_ z M^xsE-!oZ@c]-tsh 6 &MY:\G). iw|WA]?PMW/W$9$W9:WSI^L,=۶x?R>:\G). פ‘?Hp\_Uqv~#mXm;bW''IrY,NI'kHp\_U|u@?./*<5sK.e{7E2 xEel`GUSӵw|u@?./*R>:\@.jbxC)zp*k?Hp\_U|u@?./*8ͤywk+ǖθL4]vr:)Ҡ#qT‘䯂^\~GkafpƠ2URzG). #\]PTc8*TI9ݳ mZ\ZG%ũQ4jrc,23s

    Ə\gCQlu"{"i,{ݏ'w_Iuv0m5ȓ?>AEV+p!14=n_Sg5}=7 5>L\SvŞ{%Ůs"C&{gJvZtᑏ}z754TVch`ǟ4 ,B߀:Ưi}H GUEk2$b8˶#9?P;+<]--RajZ71EdA$@ǿ::WR[QXڤpM=(@Vn=E=Q)1F$­גi޻yQ(cP#1؎< +d$QE!%QYqXC8zXi+i{Z&yeBќs)bŖR\}ΰ"坸=c_4K]\ɣY AXⅿ&v:,5Io-.lgr+=/ Pgn )ܽ?*WԬK4!t9-sdb0 ,}QFx{SQw[/yܫR~f z|0tA_5y/ҡ djݏ,9:TO 3`ϯU53漐iRVXx| ?h]C5pKxyp+M M6 @42]CsGy&zy5Ήh&md~ֱ 1ךm|WJ V$S5&ɻ;}K0*+xaUke8 >џM[_3PYVVn] aU- ]j7U_EvS=yڵŴG)` @'$zVw :C]XiVotLW߃_ I)0׷vqpMہ9Ys|>&d ;«3~#I0P>_ uX?>3c XcX<."^lZA̙?6Ih:~ 2_xiK}ryb{水F}eqms3[ڽ¡ ^+Z]j6t4c ໌0%C~BT{`t^&Dfgi:Lڶqy)2B n:1> O$ЉLg ">#?uZrO}z58WY@ cYOQm%&k-fde0AsӭVĺ,Z=O,mx$\fZ.#btVfflY޿1ֵ|M[[y<.-heבFUMwԢIbGu=^Mg1mj9KYi&3IFX[= Y9pe\'ֺ) aj ]=3IlRy:h (0|aZrY˧,l߿K1r.:m$ 5-.A&K;-#Ǟ=ѯ]^)c|v$hŀ*_i@qɭm|A&\*HJȬWp{Z\4OzlK{wa-d+  ?3XӼE71ŭh4s$mwdf0;pA񮇮^Ikcq2G HpXoQ23P^>YX$, }UI6Wiwpzi:~ 9LD Gz< \cUԴѩELk:sZ_zo\2FT` i|7 [;VWho"FN+dc9JIW1jѼJOبx^Oֳz6moiOyf2G*dv9ZxCm`kg؁?Q3^Ǿ'{͖HbFcÐ#w_bA fy+m{,:hĎۤPNH+K6eI6avNqY<7k1d=E8bB#qOi>ŵYCz`H䕜$`zѰhd\GPT LՊKr&ñ v8z?[ytKmO]F]^+_)E+r 3]rSiV ȭk$R@}yGdgj>XڼeW+h΂Cokr%9ݞ1ڲ di/Zϔz s$7ln68r15wHumB;;+ʮ3蒄b+ sb]T|?y9YoZ;m\G*c. }+]4u_iyK[dyK*%N8j^-N=?SY΍b9Qi?`sHX)F˜mo\R^öZuY`.cFh< @hb~Q<Zacuui͜ЌuF+oKAj:%B599 l}M;hz] }&Q{Ut[ILcǷwq)ñ? w7؅ȴ8%=Ƿv C:+}jW-l.L7ul9nL_o--ĶSLW A]M7g跴iUIW5]\c BM1#lOr#?Ƅ6vtV&\4}bmA2`w-K\ꍦYI~P۬_#zעuwJF%%PJqq}}[uuQ*9>p=ҭӦ^f;Œ5M>_]wC1׀h\تwڥ*[ۖ d Ωi(lZ C(fP= #燼ҷ{vGY՟ٗ> ۛI4O X?$?-ak*?某ʏy+C}_ &AkG$z'?|G<yQ4}o-r? WJ0Mu't|L&l]:^<ـ&&yL?tti0>QvnsҢkZdZ4+qop濘l[E;Hbi%`1Tձj>,eu9 0A-)akuugzn(lWpJX |ԊIc?)GO\zb6餱_#PE(rֵ)W1?l(ձ)Xw2m] FH*WoPEPwmnXsByneL{bN"Q_tPGh5ťÙҧKk9ui1OLqz kI '[ ]iFcq&ɖ;|w䨤y O>}O{O. FGڲ̖^)5 ꚞk vlP6Bwӿ5T^g~;I.Dy"]-ew,#2?'lo.V۽o!H!#(YOϹqbcMUWux[1n( e-qO]V5AU 6]e4__nS//7^g*ys{K_xe4__nsE/7G*yہ/|1WJmZ7@!t,8cXPGtVzD~uQIxv7ߓ>@YC023MKA pׯ:ywr0 ϥ{jV?ׯ_Oqk6:ɉ}6J9~PEw^(c[so>Htjf[>i,;݌/*{᫗罹ofKO5ozEv/*?e??eEƽ/*?e??e@oUHmY%/c}M[0(x]mZ-&oZF\cڽƼSY6]I ]di=  Ov+Ptq)i=V&[FSXZmE*^Jј-Jp;ϧJjioA\kXxF6W?Qf)h3G| `U }kɧOQY2:gW9:dž<=g[ǨE%6 >fpOG<]閖W6XEid^6)^Σo.~$dHa)mİt]Uh%i[D|rF=r+Q_C;T̷zuDydܣ#fG#:TV?ꮴek qo)fH,AF^֡"|V+R Zg0$;y1+"V8缶&[|N+bZp 8ecx^>/]~ {8|o/Y8Ub?,]GOVuK-.[-NAt5V yAHi}Dй1(ZUNXo c-F+>1S]Ok! _b=j:./^wq,Xs_Uax^MԬ{y$xnWQzЬ@#F-BX*HFgb?qvVh l߇ >gb?vV( l߇ >g]܋tOGmՏ?֠ ~g}?~*/>|j<_Gl߇ Ͼ8Z%(d ̒*=q@?~(dݟ?QutDb5}>QK?vQyQϜ@?~(dݟ?T^}Ա]'HnZL!#}qI?vLs&inlРϜ@?~(dݟ?T^}yPl߇ >g}>q}NMS閷# .TQc@és7t/^}guQ|G YOcEqz JgVQ{f7̊ao4&MY$+i ¬Ys=htS+᷒5:8ʂXIg9!-WDѯ595i.U3F ۈ ׵Ww+_#"RNsSxhڞEaiozu[¨`SE:obW+#Zh-?x4aio?}Ɗ(坜qQIsV(?fotoxx-20.08/images/draw-text.jpg000066400000000000000000000713651362435004500167560ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0232Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 402 463 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((Bs" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?_wMoh$D3*Ҷ9v#5sFj&_sFho5ߝk}:4f&_sFho5ߝk}:4f&_sFho5ߝk}:4f&_9ݫj?4٬{ i"H\i}A}O\[!RXFG̽G(Z5ߝk}:_ Y3eѧ\64g#g˞9Y|X0 ?݅ے;MQ.hz9+~k}:<~u7-n%.<+׭mÖ߇X ۅ*!fvWy_ׇxkZ?kXtٌqf>Q9?Һ#eީkrZ1˪A$ E%F;ER և^9⟈Z_>"u MȨWH'?ybogcO%.Ktk}:<~uZ/Y4y/|7sejܫl 6SP|YчJTm+i|$ec8?_Ώ =oGoμ毩iSLiwڋZ;YwA_杩Q4eŲ_bœq\ga_q^k}:<~u:? oG>)@|ݠ/=*Ŕ]7^մm*8g|i?T,Qp=WGoμJxuK?xtBM:2މLظ' 5wLv'P䵒=B;v]TN9@Pk}:<~uhMPk}:<~uhMY:zwhc¦U,JvqUueMi:=Z"`0rힾP湯zo%{VdVoT5ZFUƔwnՔsj,2[g.3nF қU2MXr cg+ ySMv[2lyœ¸0كQBq=|vO=V]9]j Og`ڱh<4WfxW3Q] Z9i6ϜAb\њm}Ѷ|h [nO?ƍ'EX4fw>ri4m9?.Ĺ5ۿOm}t%ϜnO?Ƌ.hE|hw>ri4]o7dxck$ֵ Td_!`HPO8m}Ѷ|hz-äzdž57JY&{aVr w k6u^&woIrx?]'FۿO@ 4>}m6Fi$ lR7}ŴN"2DѫmL6ϜnO?ƍ-aY ݗ$̲^,ǔ$|c4`е;Xu~71Nzxm}Ѷ|h,;? =/]m5y"HEC+W$czeOxXS-Snݠ=ϜnO?ƍS4_WzY5Ů|eSۿOm}ѧyS5'M[[lmAi d]3B .׶¶uذRGj'FۿOjx߂<#ޱ.i&$fsN0;WKே^-'l/Ɏ)F3${s]ۿOm}ӺsFj-'FۿO,K3Qm9?6ϜAb\ #LK}YhyW`=2GNMZw>ri4m9?.V6Q ڨTz:TϜnO?ƋH/ XNT  II<];s m9?6ϜAb\o4*k)qq,:|hw>ri44-pN:4yS;đ_@bdcHT=F;wxM0.%;q`=l9?6ϜrPR.x1y#g;%R\H\ YƊ<Q^k#SfjƯi3g.8QTWUJt}:3Iz>ݥ }sjN/i\QSГsڝ&za]_L2fbhƮ v1jY~*t %m%9)6D{2x#+^z5 gMLr!>|w8=+žؤ:|#Ok[kvRG_B>X.zu Io4K8nZ|Xcى7uʶ>j z( C"\Д\+lO_nM}xIJ:ej. q2»Qw; G:5 omuؽz+@? W/H:ya8AsJ9`:L]xji͸09lc՝}.z|@23gS"G6p7T @$ zfAKirFuhRx7>  Zjz0-b;oB4:{fQTуJ| U$$Ժ-EKW\#= ?N6Z^}#*c;k->>won79X5bBH;hj½ŠX7h{.k-$b^TwqzW[iK3q? :\wKEy NJՆͩ_X=VESP'p* _jw;ytVV,(?+FcaֶA[ON/Em#y98`P1SW^D/gQ\|Os:N6]|V4,]@+nF[kKx.Ѷ Ux KQzi6=#păڱ-|G1X5޻}fG "tڳ^Q\O [Vtm](TadV'ro*p=Tw^2֣JօMABAT_)lYV @` H\4}>VwXu4+ (]ܠrH^kˋ+_ .u)u'Z_2(RH.COcka]'̿2SISא\iI%0 }=F~Ӯ~"koI`LpKLB>_C;+o14H.ƒ8˅݌Lf6)XHpH qQExz-[F֡Ӧ{pY W[ErzO8|GgjW=p?xVE-+A\Hu=!15uu)BÜCج;:_]kvQ{[՟KIY…i3|>^k4ҵrdԦIV!+jrsz ޢ9-G\֋Gbnc+e U ߭fhn`OլliF $/8 (Zޥ}rǪK_FoJIwt-R=J\nرX͵,׏zJ(s^/Ƴs_in;3˵k)RGQYծd{+AVh9P)w9jg< YáˣkgGOk6WjTw&y5,Y wPyo|2XB+t8 Z6umd;{Xi I*lq1Y| o/_s:&ot K{_CCgqی9/IAU^c||tvޗQk q%cо:Ys-Fr0qۧLs>!4 {Э5uX#O-C;y! f m<ײ[\rpЃ;T,pWvΕCayvײp|9}Xӊ<).4kH#oK$Ÿ }\)<7 w^:.~ƒ0n#9\4}yu-z/A|UHsg0\P CM:HNxY'~9Ƴ4zGl6սO#؉tW,R)䎧zyE/r(楫Xj>4N7{a:_j=^Qlא{i"]o+ kШw~_-M𷿶+%C'Iox֚\ 9LsF1g<{EYMl q/h&]ļ9\8ϵSּچCjz> [td5Em,zϧ54zM6pz~ FRX0f һ:(zSZ:gZH&3^Zdr  Cҧ~AAԣNHntՎxsw4Ps+U\Zr\RbXm( R@Q@uKe],~kA #ݷqU'ubom3ٻAmgAݷg=+-aslN<%-k k)4,8uQ_sn67W>/v46ȠtaA?\-=Z-h.tw#c嵮fPŃֺW|:imxʭH8<}?<_r4˙CMc4Bxj\n/džy>wlb6bS%S}[:]>;b1V֭[M Zm.=>v+ؒp8=ymuR]6UM"͎U3LG@317F7,@LFz1z$"em-֔W/YraHu 3PK [ylt5pH?GЦRնF7Wwoa OozՅŅΨ6XxFFwc֋ i:>hښőt7KnNXӎ浮]ciR[}X*yNx+K7Kš۫s cR~cGbH54֥S ʒo0ĥ߯z] z]2M%EVN0# n{f8CX p{+5- 𶡣iҶ ]ݼ" +n)--1t]12!;mAsCM{9{iHfWWC'Vr KӬ⸆,V;y7GN8⺚QEQEQEQEQEQEQEQEQEQEQEQEdGִßzh^E`H*FA$t6yQfcIQLXdHe]A;vOZbH8اǴφo`Y!nE3:6*oC'EҬ I]Ɨa-A:|fφm|[渎?2f]z(rx!4M4loQ[nqR:Z( (?M=LJMMolrG l8I9vWm'JV %h,j($ [|}kKVKpvH 7^iga&UM(䪓xMݕ֧$+V0A  O2F.D,e*nN+?_QKqTyi{PE)twM OJ;:%լ\K#>Y!zP;O-?Zq*4?h䶶\CA#Ob}n+kze; B)d "TaX Ϋq ;(}J'b$?)^KJAo<{[NMi;sfU(8ldHռEDVjՄ;(A-":X,< GSݤOe5ީpʹt. -F| b1 @nx>hp~"״ KdfVp2F~==D񝏃[VŬ㿷V6A0%1B{׮E HK]QhEsdC_Ѽ_[+x帹ԦY{7#T{R icڝMO闚M`۪ $߆O~E(8o-|7o?̦1=99}kFAb ٿ?˽VC)/!VVFe? EdlAZ6_C kY/!V+@VFe? hAEdlAZ6_C kY/!V+@VFe? hAEdlAZ6_C kY/!V+@Fo%s𭵻_]Z5&eᢎYv 㜌W\tM C#J%9AA#~V1|iZV7zDouv76#+qLcSxWX}<6f2UZQąj- /n2OLf:|,mEXOmv84;:(e0\2@FYA6t}n~W3iyYVq_OIMƢxitQ$жOGb SVa-mZZD2*'X2p]7*sqWtRմ{OĶuyeieFi_l'1_=@I4kH (+sKmO+n4˯xrKvi # ~:ײUIXgb KW,-c)V :DGPuk;_zſJC$) (((c"Xkvxe(lm{KKK;x.-HdE] &(GC2g|rrrriR=! nl1ny]N(rW:mԓhqNed[؄w ZKj8 }E^ Zs\6=1ħ!叩梶]K}6F5zyX7\\sߊԢ0;:uվkK{(49\05 :'` m;$g& 8ϭttPM>{ O[c>zhLLc㛱yۭ--dcq72u?}xS%__hgL$CvCd__jɶnsyψ5o[kDUiaw;q]Gt4Oid,i dUN)mGDt+m6O4dFqSoi?;SY+FƅWv9-ӊ׈f +/,I8Ϡ5߅?DWWh﬙G,FxNz[SSԵWmi' ~izj[/S^tɭɼY| Uc5wj|5{d=݌ȗmfc (19wunkUHeuےۈvK4r[ 6 YpX vV-uI.sY_ii"4-*e 2GXF-5URH#X`DZsqX|ՑWq<[lѴQ,V[Izz+_z5WWhtmN%EH.4$KxqzOAQE ( ( ( ( ( ( ( +үy s oS`p:C[Oy$[q "e|bYaaݏRk]T%IprDU6/Iuc.Ȱy.Р7$ W`ݏ^g4Əj_Kk)GԾ#&}?[v]cmomfE,XHWUퟲz׾Q >ξiP"3LQOj;ҿL GĞ7״OJ4[ywI`>g}`@Y u{{:zIb1oĂj 4}jQH X~&_(H*VC^5Y0((7g ,WvۧE[_ҿL'/?! 5 C~=ԴOK4X g#(8-"E\iQ@5clȲ'_|,O# $ZhQdXڱO}EXDnp2 ǽ|' *jhU5o5"qgZAk| *jkf6&-B 9I&3DX"qgZgIGPGFKCU!Zos3m.REr-j$'6WgEqqr  ~c]Vk:yV9'! '8ʐzR[iM u w/3EFѴ{-2 s¨v:'ihFCc&ƴMSmXH_?~4lƴMQi w%[;Tc[]^HyfmLsRQEQEUFTMS4% dU(najV#.6wlxs0 ]&h:U­+ `8ZtQ},3gO:'iP?tOM:TR#7[E`(((((((( I+Ȑf1sbNSq>ƽιAm64䗖F_4[nsJW񶱤zvK q᜶Hjk~O+QdrWpǦ=k7/ǹI$(Dq#Tݎdvb~o^PgO?)"^pC$ 1$56Uso E^j~Ԋ:+cQ ?nG|l4b7gYOUԭZH-"#6r]gHf_&JiZVűxn!{Q8 V^DUs@%4 S:׍ww_Qq SD5qwd m pr)X /7_ 35c;XEn6I#񮶹mn+{k$h3*OHYrNbnQXC>?C>?!O!eКmؿ%#haT;?[7)%(㪳E!Ug?Vo?ƀ,Ug?Vo?ƀ,U =Vh((((((((((߈O>Womx \ImsrFX'bx *jZ\1MH8SּB> +!-ẺbSjvdIl+HJ,gKJXg]k˛pOʩ#[K.Ǐ@$eku{T:"}1[TwUyڦ^!^~[bQpFi[K]Bg^_~]tgKۙb[w:Kd׫|3{\P{TrN XjW6us&Da W&Y3_f?_בm #)W?6Oѣ͓z?iW{ʾl^>W˱hu<:uU_?i?:ɺ#j+oH*7gXY)%vۗ?4+}Xi1% vCZ{h5_*DEƪ:::A<ϩZ,PN-s %'Uof+,J @ #Y-|-2tO< ZW8K_OGξ.71(FkJؙVT:v-wz_"?ךQ^g)ϵs#/ |-u <;{(]F17GJ CqDٯ o UPHsܳG/9$+=Wf#B2z4ϖ^z =Sqe~ LsW|7<]㩴mb};JЮ->{L"}p+?'5oIڇ9´-R[luK"M 3IumM2D!8/st~ V-kVhuӶ# /G%kfVoKԶ1܉ {22pBGZk! WD@袊((((((+ /na.o_$6R:([ǎ <H+]U0mxfu1ukZG-2iC%drNQN_RE6և '%J2W |=]wQеZXK#j{wm$B>x{:1}_EzEO<&^qumh0p$_b(=/?$lQXgG)y|R:{/X5!NDqШbգi幚yBfMxڣ֛9i DdgwRzI1mO/զxHu=<2Yn,NbAg NztiR_b*JV6(R_b)\v,x@{i_&Jd3OE6v D2=8LvhHHdnjpGF=[j~ޥzmiic s#6F!h:tM2my˜v%~UR_b)eoM]6(R_b)\v6+B|/R[}&Y&W&2Nнv QEQEQEQEQEQEQEE4I(ˀ?#/5S6)5;xܒeGҹGƺUsh= pt5ar2G8|PM~i7XM7X~עo5+[{#UiWkч;)+qgӚ^§ 4no* yWtRG7qY +}OJ[vաk7*Jcbl&znqS7XM7Xzιi-x-#Kx>P#i.]٥Սm}j"ara?4U?&;6HxteJԞ^=2Z\/)T*s':PMQ~ig}/Q[u, x'+Vw5y7h45m;7uU2["IYOur?C$tih 1PzZmXyI vXg%;H:ٮXMU{6K+)G)YqG' }^ 􉭛ԑQ A;w~i7XM7Yˮim;s<5FpXy8@kA4շK1vg.ɦa?4U4`!3Xܟ78ך%Ⲏv]Ċ>@7XM7Nl`ܳ1,OH&ӍN2GҠk>M4,"h鵉A,;j )I`؍z復%Ed "UvgҀ7wXM7Fɦ⪦i:a`<*1VUI./=O_;[X~XvHշO1C)Nrx=}jѕ2(<Π-b uUxB5m7L@0HB X(.{ӭ pq9 T4no*MR6okH{ff-?Wjsm) UF=(66WLVztUMH?1ح5Lxz{12^N_YӭfI'ty *?SKN+ǖ}REU]+Ao9&F^FU|Sk3櫤O`aI*[ǥvg} 7h]''i<_zޛj^KǪky{ e)a.2#b_ZK.4Mnkee0F'f,rǩa5sk<=̀2'և ֚6k{k JݻJ`85.X6tv\ =2Ko)k^e^e;݊V 330 600 0 C     C   )" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽWc?}'?±Gڽl>|?z>EaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DREҽGxoB'gj?F\KǾ5p2A9uB1ՅKx7JAkĊ!fLD0 !{ܾ̎owYvk:ZUO#?_Bx;OUluC {X/-{gO*v%&R2>qjsɠ-V dMB mZT(Ǡ$R _l>|?O>C 5{Fd/GZ~|,<3/?c}iY]R`!v kpT՛] ;G}'?DWx-:֭i≢u"Fv.EeQ״x5:.I_ȑb@тKu$^W/vm_(aO{%׃-SOUu/xNƒo yg[ VvڋM -+U#a8vTWs>v ,mϦqR:EzOi׮_ZBmؖ * |?z>EaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjj˩s I13AZ}mLgku8FX~"A/dF7?; pƽ?4"l/HUNے[?CNW<5$bD1T=jk$5;sqc^Z>|PB=WqWl \FF8P 2}hsgqkj_ xmR O*&rzAsQw ׎HFR2֭vrNqK hWjB sY'׆udu{- ԛBFj?v$Q* fYy$'Js oԣUw-h}.IU%R\MWo_zzmYj62`gȺ)e{k_sE.o~΀I*d<~UOK4 TFy#g :%략_7;YfcuMK2JIei>oA]#QqA]#Wf|oZ>}k',_F,_F.~{}_?Og5Og5as'֏Z r|8?.r|8?. i>}O;YvG;YvE\I'ֿBܟ?? j?ܟ?? j,Oi>qA]#QqA]#Qf?=}hIПw'Ïw'Ï0GO~ù>şkù>şkYoZ>}k',_F,_F.~{}_?Og5Og5as'֏Z r|8?.r|8?. i>}O;YvG;YvE\şkù>şkY},|5}&꯮D_߸v㓑*?|Hu5ښQLFłqƨFbr%s,_F,_FO qDw̟#+C2"SMkkŖRiOmc\j;e[[fE= vr|8?.r|8?. !CgMо~&hNs}q؆vERbB~y/I6O}i%X6ul"r|8?.r|8?.rkzHGIfXFҳ&aQ `@؊O^7O /M>=݉^3(^|?ܟ?? j?ܟ?? jn-;l|G7/xKi:\Ocn"5sYySrx5g^^!2=6Gui|##I=_iù>şkù>şkYMu5"Ms] S 1ĻAݜejZƓG]CIiZkk+[aa J9_hù>şkù>şkYm[Sĭ[qXF"& Ur#^rY/ y!H̍T~WOg5Og5.PGO~ù>şkù>şkOi>qA]#QqA]#Qf?=}hIПw'Ïw'Ï0GO~ù>şkù>şkYoZ>}k',_F,_F.~{}_?Og5Og5as'֏Z r|8?.r|8?. i>}O;YvG;YvE\I'ֿBܟ?? j?ܟ?? j,զ>yճ^z2Oʷ_ ɋ k}YJ,_F,_F.~zs,LM˿fL }*VwN_vwwo8NFz_}ù>şkù>şkYϫFK#i816 $H?~C?nt (l-m.ܟ?? j?ܟ?? j`c!FUY򽻦>GO[V[y ޹9_Og5?{z>F6Fxv#0e'Y.( ( ( ( ( !ZisŚ֓_-$V#dzbp]y>$|Om/:KNaX] %˕9#ʜu_#O^Q׵+ q gKPx70}3<.x.=O?-"e׭-y\^$R^U*$Pj&uxuo\jڣ_smմ54H2ێB&/xO_MoMN6k}㶺iu,ǼUÚO.k;z~xǑv:Fm3d4Ad8G~wj>GRKM#L-eTyK ˤm3j>x;]u K.tCnQ\6N¼G ]nI02qXݫy"]t,\ tּS~|?)sksea1JAy#n-[_%%u{qẌF wH=BhC4w$:rBzkނ]τ\Zh;WDu0DBUpI A>0xj} ?aGeko+\G"ya@9%i+6o3z+fi/ bSaS9*ffp-ۍndQ!sPthۿ73|[Z7I|k#O|.O+hV|nrĎMzg<3U//dݤoq뫜[էqDU}/Ԗߗ{L3o!LUHQNcu%ίaoq7-+FFA9W[]J z"mkxi1mVI5;d#'yO~5ִ_}>*Ⱥl" T4H$m AK_ߗmy_#-㻂x浖1*OGB2092Qmŕ7 dqWw|,!Elz6Hc\CHmjpҼ#᧌GO|@w4+}2{WÃ]guhV8ݗIprI8lkm/Z+=5oH/|bpZfOT+%\@"(o+?VO Q!VSLL$Ȇ+$yaCzlk_pN7}Smo3E*U`He# 6|'l~&i_J<SMh$SǶ[k. p {~v1ji#[kQLrys#f8j?V̞O=&RUUv i'<[BNOws %DHr 2x$vc񜷿6 Ki{m漸< ZF>]j!Apal5 CR7<QGƋh/Ps lϺ0n{P FF85WJ׵&nnVf ,8G=j)I%h>4\!ԖfH|Q,mg2;/эd\=i>mB!+-n@R#WiY4{-{Hl'mT31Q܇þ'6.drjޕvo: !8$(`qҶk  _x 1G& b*\|0ߋ"< N$K.ց|Gq}]mb:E knݖmY0HzT?Z3?N?0g& ZUƫcUȼi q Y'__MY,۲`]b#zak@;(!vPG_5iu/4Xn[^6S3up;q:c5Zx]z iJW<~v `2ry?L-'oZ3?N >V𵅴H U {QK[Gw>)Dڭ3|(~U鎕  ]_w:.m]jGevB:F"](Ĝvrz_nm}53ijoS7wɸx;ן.m?[n<7xkSVW$+2 CG2"¼~:j7#LZno]Ii1LN!Ԫ>g ߇G(,ot1ǥĒ*A !!+Ói[Ϡ( ( ( ( ( ( ( ( ( ( ( Qi?W =GM|A>i?uGQGH?P!niiW@GM R,)&d@2I=;V? x/ OmM=KKKo# N@x'x~ z?,5wy;]UG y~y6qkG&aK&iZ"nbom]}xrx~ sC"E$5#m~æR:'[[%dz)ae^@}=xViЮW+p-dTEm2 #G߽c(Q@Q@Q@Q@Q@Q@Q@Q@A7ͿOP_Ǎso@|Jz.]sÚNA}<6EA\v$7w)G|T3jϥYy-=$*]b{xk@~#9b{y22S*:xN/>kzTךA؛>\+$9-sšUz%M3R`sjbڎR6`\UI}/AuoHtI7 FQB ǥXxK}R=2Oxt^WR`!I?Zu?]^tK..af AАH=yt Sw7_G`/4KCq4P8 amoZ+jƋ5/fZDA=݊O_Z=ap*$ M]Ok B-KS5a!tk'ce5|Do覬zW<- j>+4]CF$iHXٗrdqзKFz4Z,#k؍B;GG ,АZԓgm_z]kqy\  OhYѢaku-m +/g +үum:k z|6d&UUݸGl|x}ׇ|E4ֿaOLNMB-\~3|K<]>ҵ |m]`EAo/[؉4PD8y,dא7Gjޟ{ $mΚH)ԎPJccV?X:NYEݛ?SyKÑ9˿gG|ËKOhZ֥u KtnȐirK&-Z?;[ ŠNhkE9J?3Oc}+K+^_+՗{QRPQEQEQEQEQEQEQEQEQEU{@z{ITu?'h? {I>ȏc^&7'?'򎾀"=zA siCVFATdozl@Ѵɝ6m[]Y'j((ޑ-R>MBuDFVy e?1b6+F45_Qi>Fvn] j`nj {BA_GO>Ghlf@  WU?i/϶5, ?".X-^D]UlfGO>Gh _/Z??`^еyuW϶??i/P-j {BA_GO>Ghlf@  WU?i/϶5, ?".X-^D]UlfGO>Gh _/Z??`^еyuW϶??i/P-j {BA_GO>Ghlf@ O?G/zɸ#϶??i/P9=$MV )E0 l&VF-ޮ?w} H/?/Q_? _϶??i/`8/bEwQu12^{p^kt<,дY! )ku:;_:k/Ӽ83- ks RwqE!Rs?F>6ܧ KF@NA>/!i} tF~xr۶M_[~Vw!4O>O"n6?`X<w!qEQn|a$Mm-d&t͔=^ c{&j&αoGM>($ވ?0x ++Xw0Ii%y/[X"F\HBАHv۳{Ew]tn+|٫㖕/0{O<8ncxes^0|m~)Vxm[ֶכ#E8C V؃yb?*;G_ 6Z/;k㕸5}CO$ tV]FUc,W/??2s‘dž'bt=NpN/&rMZpNN)KwM[Gj>e6zK5a&{ќ\8Nyį|S1ǪG;[I,hUg#k|Gsz݇[bX:;_IC>e Lx#=֯ & ^D&s VG],q T2./ tQE ( ( ( ( ( ( ( ( Qi?W =GM|A>i?uGQGH?P!np|qgmL潬떱~IE$o!]"vpK j)a鶚&}ǬGQ`q&1+7^<񞳠75_"[-P1)vA8'5ʵ f 6W 0Icw8(?hB=/mQԯty,MiSEky_EQEQEQEQEQEQEQEQEQEQEQEQEywZj/EgR԰9OM`sO+x¾ ׆)VosxO~,mmm`$YH5 ĖS$ MU'5mŏ|FR^fs%w2KruxWǑEj |=ҭ~"Gq?ͥǪz5ꩥD嗚"pHAǀ577YȣPn89x*8,v#d\_/|f /}ƞ Út̚,fl[9Ad⁥Zjڂ/.IM0?=#7st}_wW)PҴm3X>{5KxfY'u@f,T;, 5/ˢ[Ov\[E1.h('1s:,rP@d?J%-1İ{qwa |UH|/Yiچwiͮ?kd/%8W}Υ7P+/M?'cm;O-&mSVJ}R+Y9~r#I o3eE;}E2|Rо$;<sYctt{kAѩw$$HQ%O犼!~E熯dkVc\r>\woxf>%іP5fyk&ExBɂC#9[D_/_C袊 ( ( ( ( ( ( ( ( ( Qi?W =GM|A>i?uGQGH?P!nzmQPXQEQEUyd{ T~@?-?'EPĴƏKOjC?-?'PKOhĴƯ@?-?'EPĴƏKOjC?-?'PKOhĴƯ@?-?'EPĴƏKOjC?-?'PKOhĴƯ@?-?'ES--8чgf!IOrU({'.E5b|Z ßx/L%W3G$o$I$.4rPo|Iu>.ߋ>&/WMkmCk)nˇDTtLeT˛N_?Q_/_BoG65F׉5)/|=8th]BTp*$l[܈ WYyFhK!Uŵuyu[,63L~Xb6=@'bSx?W~xË۴PG#[zm_J쵯^ j$=E,r*_k(Q8ݜo^oO?sAiºO|S?m7T4䱰UH"lWƎH2F 6>#+~'5QjW@mm!Y]͒]_H,#?+ZGk1Zc9V@]b&Acᾳ=CUX]]x]WUG "4?g~[k%@kf7-upϡYGugYGr#s$s&MD<xu;JX/എ{971t(S^e⯃4_Ʋ(ž4l+GZ -[["2k4pیug5<7jA[O]>Xy-C=IyH WF!c;||ZힷMŚuYi66^4es2^x#O|uۍ6 QPإB^Xę3dFF @nSҾJҼ ~GiW"[KhlC a!+rp8MR[? o-3~r^nwiecw09V+fI˖7l'}{0~!x7CF#ˤ6P"fT 8_ŭxOէ5[;H׭ L-o5;wFBFBN2IS _ۨn LBJD]A84+io袊c ( ( ( ( ( ( ( Qi?W =GM|)kúx^]>{ %`B컂`v{ Aip'u= sl?"٩t%nuQEIAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP=Iu.|@wFeyj|AnРDe,0^5IuP9}oož3xC3Zߵ5 2nRcB TrFA?>(ӗxK/ƔiCZO Ld S]3Ş. &V_-b9RAO3S@`Np3<{'k tEK/&lʶC+oE  W+~~*w/Z]{.m S!Yu1ɐ*0~j:k]ލ[o >$~^ *6vljѣ[[5&OWo_>K/~6|T|GmAuo{՟N'dK";y'z;0F9;?xsŶFu隶w}P4aL"wRJ095|էōsϏ-R^Lt_ lB.jEGZr.DBf8 v?h_j)| xYkL"uvdzɓ~ぷ9#4xBz)m6;AVc*)fyBkƤ/^#ß/5|QOWmY;\\kP#̖7*!x 팖 [#R+Ww WCOsᵟ|V|+ KN:sM,br` tunϿ@ǟhc6xz-|uH 0gx^w >HmgꟵn~[5k2ݙfD%dHOaumqGpxf[)Hcp8#qw)jIel|["Y|YlZ_ k|kKMZX$I'ZȌ"1,HqT5i_N𕦍okul\AXA+::}*޳5]x\t/xrkh5KO]=,i'`s$l2T^5roᡚNy"d|`g/L-5-b[f;'b-/)`oژ4=CP(dOFKmRqVYk.lZwyH좞m7DvƯyͽߞG\$2xR`IԵkϙ~xv-~7xƓk7_Pi@͂' $]&F}ă^{4Maigqm4:#)VGI1#A_ >>,m֛wۭB?[}儤K&P|یg%xenT|CS=:6.PybN[ wnRi]\t_K~֟4]9Β5I͵<n>*$SȫQ~ݚx~ٮC.6dތC;VOZs>ߌf꺜܏bӘf+e!cx,Fpk-?gK(u[񗊵m_Ju=2;FY%_2Vp2c # 'ϣ[ᾉxZ *#/$k@&G[9[vv,irQ|Յ |r  RV0"?7SV/~&uA{sN!ox]K#\S$ϿȨ_o;:q xvTյ7chFr,8]xđ~?%4ڂ,4ymh22;c $LQA;qiz|GM|%KlWEg(C`I Ou/Ǫ?ֵFҮRK8)d} dnn4x1|u']&V.ӮnԩhyLb皟_|CIi..`֥}o7K*Icm7mwfalF!DݎyxF֟TПQ.H̴rP,s"x!Ӹm|C v2q=v +lJsUZl\_~~<7lZ~GVX#MxXoH~c)*qLoCR+Nķ(J]x^i!=,*;i_w11Uߑ_Oz4M:i:ͬIseuyn.ķr䑻qL?<}7_ ŴvWim%"ibW*X?gxV_ɤ]X LҬ]~ė 2ȞK'$ixgGtH#r HP2ZvM(Q@Q@Q@Q@Q@Q@Q@Q׿״ G^^&>j a\Ax_\. [lj] GYETQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEs?w_)j ]xgI_3\.@[nxR1P$ M]Ʃj^*мQ X]@ g >'f:kqoiռ ݰk"bpN Ư0gxoX˧GE,Kyp6v%'&zǎoYHѣvr8zjoEs ?া#鍬%'6*yvpn΂*X:e֥um5ܫPƣ,@UI tWeo'ʱVАH`A ( Qi?W =GM|ա'+?]@?]׀Ժ((((((((((((((((((("?7SWжHt-WD[;Xd:o ޫ#i$6N0rzO'&]j_ L柣\܆bnl $ MY4ãGn~k>#ѼE*.5mɤZj$۫5Ey d,5/K'<6o7(FQq(l[6Ѱ.+/~ύa;ѡx o úBvom 4=*կj"wngzO?܏iϢxK !vќJʵ W蟵ƿjjm֟N=CI ˀa%Aǖކ)'3= 1 %-r9cXG<9j/oy~m/I-%x@G&9ֈUo +oH?g߉RJiE`[IIzO?mOixEH>m b)t_ZPB IPhLJ8,8GjӷѴFfe(LpPp:R[5߅;וbjڦ"o(t6Py[EJ9")%I,0H>?z=kMcI!;죚 I`Ti%͒bP󟷣lbETEW*Jaч}>+n~t9| fymxm̚Dāy`=)k}?Y[5+*$[3Vw&W7EA3sƱ@w0n@Z>"~+լ_Vo9 r=I(p|$?u?vmAG1;q:93D_1Ffg WK{ XEm=-mBvTNϻm-֝%{~oΫM͔3Cn{51I`A|B:~o4˯4몋h;y&enfAjJ+JPo<),h@ `tzog3]۳\rW_Q.k~kz~j־3 'G_Bk+x/L;cWUAP[ <76nc22c#kdiqkx68'&B" $ҵ/m|ϭ2~0~6X{h0]Z|Vuڼ,eC9;AYW@$vwZ=]KS^/:%%h7WN5n"O,~`BIKw[;uZ!c1Um8[XU#%Dc H 1Ҕ\❿8KZX>!>$x_L5.=ڹmGȗ<"?_5}&Rh,ִeկf^H!Wgie,z1^t2H0iZ0:z?Rm q Iv..=IR}knev[CW|CI|M`i;i-sJ @Mޫpvp|aqD?ִ跾φu)l-HwC&Gb$(gm}ɏvyF3MkKxtYeK_{~ /H?oYa )巕c S*giP%~xo |c+][ ]o)R-!\ I9 f,i|qb鶈k #.q>i|^V߉t}!A/|E໛2]ʓZ4t;>兆yl>ѼA5/ o_= BtV6N->PX9$B6_2X"#א;ӠIa)df֚=wI袊G^^&U{@z{IC-pOWu?~5pl?"٩t%eQRPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|Do覯>$4/xwıx]g4ktok<ij4HI#l?đܪܘu_ 7Ecծu;v +I&5;Yg>ֆ>(hW-_OhRxඟYY5[j"{edGRK&睟VQ߈uK zDx&x F6"yCFcN񵎩i6_&)4tR2oi^[_ZoY-|7rI֏cJ_ y8+dS?yY]_ |-_J|K >֛Vhs6 dn]]sl6cww\pFkwCD`\%e6SWן7CoW6͖h:Va,lL $GO'I) i# o> 3Z.xZvsCi ' i<ږpʛd{'#K NqF[vMkﯠ5w{ZxjOh>QjE$/_bƮ:~~~%i蚡4mVM> kqѵ$d^q..=ԗ2 f-UU)8xof[]Z:V:5Il<߭9Y?Í|% xs8K:~;+[{;;!KvV$9H"p2 89-h|0Oz)o|gKLzytܥnr!IPf?!_qvbMrCB(m85j:bI?m@qӕb`6O_s//*xľ jt~qZXm(ǜ%fBAD+%7]98&]kY-q">HmI}QAaqGl6cww\?~k_בzkAyh j&x8F&Aq*H ?"Vuo<$Wp6B̠ˆyEvGbBOhFΗ]m WlF$KY.}d ~&vxx/ѭdGՕHhT5j 1Q`NG 8in,㸑[$6$> U?l+cw-u/-#W?$1 U ;ZSWh㹄!U mo<$Wp6B̠€4hyEvGbBOh]m WlF$KY.}d ~&4j =GM2]8+kkY&QeRU|Ue^yKO;AǶzl?"٫Э:%`O2G;WmPRKp#l0#J:(((((((((((((((((("af&¶jύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?3kyf`JƅNkcQfMOLI0%)GҀ-c<_|l1U'5_I!֗&-yc<_|l1U'5_I!֗&-yc<_|l1U'5_Iuc5ԯ & Y˃xO?W/ 5Gύ?Q}OTb[g76;H*`bU'5_I ^~>6_j?W/ 5U~ i)h'5_I ^~>6_j?W/ 5UĬU ew=QQ-5X" BtVD c2qeƨUW!Η&xZ_ eeƨUW!֗&xZ_ eņ+0$Z5楧jVե^;Dmj`V0q5|nVѮ}9]@^n*` <qּk/֭1\^bf@\V@St> پ(K/O+ 6(fHHK97ɤ~i:2\Ԓ]GWX!-Q qC=iyy_>|e[:%*hHO58Q$k[WMteY ?ғ@^g ռCV.mh"S,,J 3zV?~+C:5d$ 5{˙aFD.ˏOPJѾt=?M|w g`/`FH1k@;A~b=F\+xGTg*0!*Aƻ ,Qb|?l$o#(y qc>·k5{jQE!Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ t#+ {|CD8XeQ DOMvtP Ѯ~vbVgs@W+3CF?Y5WkEq_ m+kþ3Bi@S-! Ҷk\<:Πz, EB!ѹG`=+?\vP Ѯ~vbVgs@Z3RG=nh6x0Pm-.M>E)nyg Ob Eq_ozf Ѯ~vbZ(fh?;o1G+3CFhwA\ZڟSԥ U>3Ԁ>=MtTQ@fotoxx-20.08/images/edit-funcs/000077500000000000000000000000001362435004500163625ustar00rootroot00000000000000fotoxx-20.08/images/edit-funcs/CMYK.png000066400000000000000000000030261362435004500176340ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/HDF.png000066400000000000000000000033221362435004500174710ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/HDR.png000066400000000000000000000010151362435004500175020ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/RGB.png000066400000000000000000000024011362435004500174770ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/blur.png000066400000000000000000000044151362435004500200400ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/bright distrib.png000066400000000000000000000013501362435004500217670ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/brigth color sat.png000066400000000000000000000033021362435004500222140ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/brigth ramp.png000066400000000000000000000053211362435004500212700ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/color depth.png000066400000000000000000000035231362435004500212760ustar00rootroot00000000000000PNG  IHDR00`nIDATXGϮG_IH؄;,X G x< ,"`pll'c3]s΍]SQ>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-20.08/images/edit-funcs/crop rotate.png000066400000000000000000000021231362435004500213100ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/dots.png000066400000000000000000000103171362435004500200430ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/drawing01.png000066400000000000000000000021201362435004500206570ustar00rootroot00000000000000PNG  IHDR00`nIDATXGKzYƏq%M{Iѫ1Im rQ(MVhӦ@ZPP1_Yj{,۽˙65gus<{^H*^"\H̑>VQ ͗ 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-20.08/images/edit-funcs/embossing.png000066400000000000000000000030361362435004500210600ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/expand bright.png000066400000000000000000000020711362435004500216070ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/fix fringes.png000066400000000000000000000004611362435004500212750ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/fix stuck pixels.png000066400000000000000000000024611362435004500222600ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/flatten bright.png000066400000000000000000000021001362435004500217560ustar00rootroot00000000000000PNG  IHDR00`nIDATXGKh\U9w&I:VR>jMhb[ *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-20.08/images/edit-funcs/flip.png000066400000000000000000000016231362435004500200240ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/gamma.png000066400000000000000000000023161362435004500201540ustar00rootroot00000000000000PNG  IHDR00`nIDATXG[hU9߸35ݚ&l]JXQ$VŶ ,TjQ - b"AmPMmclבƴΌs\63]f9 [pN0 jn3((DC.cEXp5 O-9W?#qfÁ/ߘsogB;~bC͟N==o/`D;zal"=9_&ӹd:טd"B{%65^x V}Q|(7m%'?Iټ'?W(M :iRRhIBb06MdoM6 ]PPx`8vq|%ЦeqԖ&r!"mc ^l`5cBB/|Td$Y?7 j! 1 cd$SAnj%Vhw=G ;)kVh0HdzM6T(?Dё]` PC!,ucGd:װ۲_DZxsqسf_sy5n5 xQ|!oMՠrJ[9'E4b[˰dS/9c3}Q6Ug k؉m&,ݭ[\I4[DK{TU22{{Nmp,FT@Sa~hVG"k_mT* }Rƽ_DjTE@fW] {gRmR+eM"7Few(VNy"r37]j#l;VA ~y,E Q[{?<NWaUp6Q&TdQtq>CdMݣ\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-20.08/images/edit-funcs/invert.png000066400000000000000000000011601362435004500203750ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/keystone.png000066400000000000000000000035141362435004500207340ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/landscape.png000066400000000000000000000016071362435004500210260ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/leverage edit.png000066400000000000000000000033531362435004500215740ustar00rootroot00000000000000PNG  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ɚvWfWp 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-20.08/images/edit-funcs/paint edits.png000066400000000000000000000044511362435004500213000ustar00rootroot00000000000000PNG  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 Utљ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-20.08/images/edit-funcs/painting.png000066400000000000000000000016251362435004500207050ustar00rootroot00000000000000PNG  IHDR00`n\IDATXG_HSQ޻??L [JF jȢЗ| L$zY [/y($$BLR lf 6en_q}w6>{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-20.08/images/edit-funcs/panorama.png000066400000000000000000000010501362435004500206620ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/pencil.png000066400000000000000000000024041362435004500203420ustar00rootroot00000000000000PNG  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&@^=6yc׺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-20.08/images/edit-funcs/reduce noise.png000066400000000000000000000057501362435004500214440ustar00rootroot00000000000000PNG  IHDR00`n IDATXGKu};Of`$H #@V.# S,RgEJ*쓅 W%UYdHcXrE" 03{u20\c䷸O9}?zwX@GbH^i5bUGe)dJ֦] -YZh$cI[e +D1w~pR @p|tgLZN]lڶ)ԩYsu@pHGuX؁9 &)OsoiX@leLpyweasFh  "m*!WLTw$ų{^lt09 S j6{T0Ir+8`eYWjZt=jER;] jO ʎGZ ~XuQ9abD,'KqM|I?gaY%x}xC# Xqu>d<'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<_=JtV^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-20.08/images/edit-funcs/resize.png000066400000000000000000000017431362435004500203760ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/sharpen.png000066400000000000000000000036771362435004500205450ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/shiftcolors.png000066400000000000000000000016111362435004500214260ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/smarterase.png000066400000000000000000000015671362435004500212470ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/stacknoise.png000066400000000000000000000057241362435004500212430ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/stackpaint.png000066400000000000000000000022311362435004500212270ustar00rootroot00000000000000PNG  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~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<}n֦[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-20.08/images/edit-funcs/tonemapping.png000066400000000000000000000075731362435004500214250ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/unbend.png000066400000000000000000000035071362435004500203500ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/vert panorama.png000066400000000000000000000012321362435004500216250ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/voodoo.png000066400000000000000000000062051362435004500204000ustar00rootroot00000000000000PNG  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-20.08/images/edit-funcs/warp affine.png000066400000000000000000000043121362435004500212520ustar00rootroot00000000000000PNG  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_;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-20.08/images/edit-funcs/warp curved.png000066400000000000000000000033551362435004500213200ustar00rootroot00000000000000PNG  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@P{ڙ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 ?ggV?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-20.08/images/edit-metadata.jpg000066400000000000000000002063671362435004500175440ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 589 440 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽWc?}'?±Gڽl>|?z>EaOl>|Z_xS|(oo#=|1^vA+ b)$-Vբu Z˗g} s c\QE9.Hdvݧi>ះ3nJϋJn qHd"%Bn\RhV7TWYZ|$ OiID`2Ns.Qn=oG1|>$xW(aOi|>SGÚ} >}A6I0 l|56ūGmSZ `]^K/#vđ=f]A~>gaOm9? |*Ju50 0b:\W)>2JQxQh u%֙=Ig֭+(!'}5h^xDӴ{kkZDex"E'..pGNͮ}aOP/ItW?y [Fi·j܈5#޳}]// h մ <9}%ij}Xj^#c?}'?±Gڽl>|?z>EaOl>|{z,(aOcޏ{`5eԄ  $U>6{ 35RzK..ĂDM( S 6䁖3Ӳ2M@I ld0U Z5MNXWϟ(UAa3U+7x7Q+3'ъw6wao-l.,Qǩd8x7px+e# jޗo/jĂڇ (>gS^%7Rm f$>;ƽj?v$Q* fY%E!KQه&4_,6O͏S_D]_|/O}nM3@Dl(g6zxuZ/}rVi٘9_]xR挒}lbsGO~ù>{kù>{şkY=s'֏Z r|7?.r|7?. i>}O;YvG;YvE\I'ֿBܟ = z?ܟ = z,Oi>oA]#oA]#f?=}hIПw'xw'x0GO~ù>{şkù>{şkYoZ>}k',_G,_G.~{}_?Og=Og=as'ֽ O#i>Эi2S㿏 OCz4$% |ܟ = z?ܟ = zvopN㷎]>I$3:r<2B3){DEu3XTjlsEmv,JJl K"sG;YvG;YvG+.|'Zo{şkù>{şkYoZ>}k',_G,_G.~{}_?Og=Og=as'֏Z r|7?.r|7?. V}wVdyz?*7&,%%g+w'xw'x0-̷LM˿fү>gt5gyv8dg?ܟ = z?ܟ = z,֣%QCx$?!WM៉7:6WmbVD^WOg=Og=qtutzY~e*l,^^i w^'-+g`ZFAlpy\ܜпw'xwc]YoGiiTcdgb3 ξ&\Ւ}U\_~+h;ChQ=ſ5II{vex' Wi_x_.(t.L$Ty`Z<F\n 06ۿpu_ZńG[EIៗI]Zs YA^| $;}b?hvE'̩y[fK;xSU}jl̃T69 ],D 13YJK+m ݯo}et{YSψH4rʂXs^aARPuiS EFT^CI E:lzťWFx %Ѻ/Vaʊe&[%7dϵ_ ]da.f d_ m'{}]\hzmiki8e0,I9$5bh>-tswUBln(tC,yeFU2G8۰_|Ӧ{b[ׄFEI{o3"?Ty?|GeiId-63K8bhkOɴעj:u<5vWiKŷsPɍFnsJ:omMϊ|7{F_q-ˈH ^5Oǟ/m]ɨؤ;H%3\r XXK?ꚕCkqp4ot)8\-p?..־A4ٴ=FkW j^mc \T2M p(_އnxJ]eMeOsF#޹???ot+[+=gʉv0*I cs*t_ \khImm$h.$H㌻%Gd+"> w|E$R3_ȏ>Ȑ [zlJR_\H#Xgk}ޭj1vԬ5H.K;[y^aIK|LՒF%q_A|I^ߧx_\5kFNt^Xb2D 9ȧe׷+KcOVj~/oaF(YcU۽%=ҚDvUƅx^ǣ[Dge#2!,Z979 q^yU[[Z5Bm{Ku:K[˘Q F1;NU2kyK_w:+LRSFS _FVGy-V/̫mn kKV\/u( KX|VUY۸ +Uu5u Vb rT\5={S>|? Lkz5S+i}43}c #ָ/z#xǗ(L%gUhVr ɫdDߗG it-j.K&e߼B9j|m:\;9xBkjYu^'`Pijz_[QE ( ( ( ( ( ( ( ( ( ( ( ([-?/ -tin.-;c xҀ:'i2i֟a#+ w) A hb:xH]dFAՃ 𷃴_Yhzt:uH8˱9,slh?nYi{t6dX d;(ohAxNmmM,76Ϝik]O_yxsg4u:!|Ft#S$\wY[-Yghcb>FNq͓.g5U׼k`]pGr$,)2(lBRQ]Z6?: hß?.?ϋ>i* 'iQܥcyXD lM7CSºzƇ-ݝaCun:IsLFr9rf5?uxsg4uר> O,Ȥ܌q^7:-K/x6TeST-<*{)< sT՛DR[3OLƏN9A3eǞ.y|7|; =o]oQjv ӜGk-ķw03*F쑎hvQ{_{eu ׇ??\\tv-3ß9|Sikk:HgLM3pS,Ϥx{σ|KYfoiM/sreH HX0^voYP7E9YІGSe "[[wV/,>Ay/CnAdv4PܙQ>~_ |Ԡڭ_OgcXfbL}ox C{cZIouq%̷3O+ K+3aTe(//iYȞwᕇ?tX(_[M{LI4HB]C@K ? Կ]]7>ODrU[_3 |9=}&sm4< anϵ?C@K ? Կ]CJQq}S_'oEԔ~6d\|6ԼWx|iM*_ZMJZ#';֝⎭yia|Ol @;rOc ? Կ]C@Kշwh~4xKZ<*No5[Pᕤ Y%p(ܐ 5y/G'+Rqt.ߡ][:z+j_?.O!V~=y/G'+Rqt\ ω ]*w&o^Bz\$U>qf?"ƯR`yk#u5 kkhWs+ۺI W:Ý-?}j#0g)$ʬg|vڡ[ }1E.wa>mG~ ~X#a&I + ufe;>kѹ>nex R~$kZ6q;WN;O =԰-w` #r+n|7"9llW+1fE,{:ּ;ïWs|of m,i +[p |"2.!*M{T7i>3 ղ01 JIn˜#~Uy/SdG"֋Nϑ|M?x~<HM{'+Rqty/D4y[>?|=o V,EީbK˸b CqHύuE<xI|5cmq`0OoڌXxCj_?.O!V~~͒m?~-G5gZ,"%Q4B:R>oSQx{QמBu彾JL$K0L33dz'+Rqty/Q S\o9.gwO!V~j_?.aJ?dv^oMMIֶ}'[dq>6_#5H!m$ r0!p'rb1עok&kէ{]3]?+io[]?kO[*:_f⸹wMs LeS*J#;@Z"|SwxŞмO=jMo2/ClvUSһ_wwkGu'U]̞_G[mGj?CY_FC=P1 T iTQE!8yMxgWдp#/(+- wK˒9qS|?]nW:>Ӡ!~;tڸ˖%rŗ U޽Kv-~lcr~,uIm5K׳d#'I,N)=[{kQMד/jW6ռ/PC<*n/[}ؕYv`p_VZO_^hãx4*kg[i[ $!WS+;z~v>D,|'iEů<-uipJ5ݶ23":ٮi!6VlH6Enʩ2 .HB>[^/,KWisi)̅CV"'AŚ|^/LeԞ_(1+cAa^s_.LUGAj1ϣMX;طbFV">Lҵx3]aI)1X "7ya* keu诂 4m J}/DbGPmǬ³$2L9'~.3DF->i!/ #3I$0Hw"؎m<_k^}: Y!'*=U_xxLQ҇4RWTqbAq5+h&QuA*5P['=$ #x/>]2x/ @l&:g=N, Ցf8UF'ӈ\;׈") ?5ī?&[f7jWbc avݶ 4 =zVዯݤEMpPKj}핕EĖa*Q69?|U=xNy[-T|ҫ z[mK]m=1羱r& #[OZ$6GRtxz]Esmo.qG[$C 7RּMl-\tZDn*1 {*}3Y]Bm3S"O7 #<̍3AFG %X *}(SnT`WJجSTT֙mmi.eA I03OҼyx1H'6Xo"<Тd%@w"8b0IuEqa]46Βϸ~Ag c7z.{d_Sj&hjџˊ <@pp0 vtoxM.$~7ı,m=c􀱨dž 7卨u;KpOޑYgO"/Jiwˤwq )'mt ]+ѭ740{׍c[|/H]Drmeu}:DIX] `1i^?~W-iwi{nOm2J:7  +gxZΩii5ɍvF 9$׬U5bSSU-m>k˧C'$($R@PnDz>l]^mFPq'Um1m/Z3-3΁w~6 cY ahz7%ͬ i ?+)pA tNJu7NEh'Ȱxr-# H5ZEӥ..!̬C4Q4 dwf4v 6enN}T{'$!|*yTCLOEsJ(QjZIl]8jb3:ӓS4B -nVbb,PTaIUjkkaꚅKdFYyjvmhֲ\js(gIecjLzȗWeMGMS.ƶO6}[G,z14_c? EQ۱v?𿑠 ՛wO%[ꭹveaʟqRmF_&DDw(I]J[=vY_c? EQ۱v?𿑠 "2:V*FA\$ל#6vl$®'9__c? LvqGݏ/h۱^mF_wxKeFպQ$߂t {$kuu3;hmF_*ݏ/h۱^چkYkyw6 ߚ~"=rY'[M8ّܫ()91YuxAm4ԍE7J{O/(|_j>Jγq;2M6Kt,,'$nvlob'ljs>i?D⟶[47%ʩ+IL$Taf)k?cA^0F|2.6Kn4MUhH ar5ѧ]u!Ki 4pɥj=Ņ®dJIPt xE}3^-B#-;g  x8LG?> ,ntzݥm4 (#`ī)!0O F\/G߅&5g4[VŲjʐs SkiIHҼ+Q t;wwi {_lB427sNxb]駙Zs[}|U))e m:]jaT[dOIp061 >$3i-Rqw%'n$*2ӑ\7x>yohiwu{W212L9'8{?x;T!%߅<.(پrGdͻdi_OK7|ז/oo_GP.*&id^OF<;ƭǚm\~ ml vmR$Y5gآ:z3+(=#x2o) *Cwh"&PIUc#oc#8?g?k?%5?*6:}4rAu:.<ŷm!~D[qW}ߕBI_#WWt?A=CZ%hI5fpLk$v2x&1|k>}YxXVO% fc4F6R+鯇n$xO-=jAOxŨ,Jg Yel2˭WbWgnUijOVα7gZXAދEYHq)|}f5KK}E E!d0* z_-վ'>)-7Qib+F.#d2R8O#4_uOxڡ[563 .UpzĄ3Mhh|F.i.$Ӿ8], ru x_&xz>:<(tj.XѼ:m$VK]>q/$fڲ6Fs>(CWk OCkۮIw=bܰF F^y`z+~ݾw=-O<[ ^|IhZ-Sqj0D ,@W7'IİiRΣ'$UV%+c\}/U'qjӵŨkwk- b?:/7aX\t+ \$ Ljm4"դ1iWd aa.6W~I*H'WX<{&gXY1H6w;v “p>:.oݒ]j1Fn!rJnQ( Вy ||c^ RgͤɦwMp4|XF<'WZ}2o j!f}6v۴2ݮ#{0h+9eoͧ_12OcRҮaj:l3\^M{=Q*4UdBIn _z-rF*[%~]?+6hR<W#_Mτ'i(WOOEV̸; ~z‘ZѰo_twRp^YG+r kkk^)/L熯{fu)KtNʃXRmmko^4QEQ@Q@Q@Q@Q@Q@Q@skWU[?D?QEyߴ axluSt,-XҌO(bRۊ>Q(C?B6EC+ # J6:6:meKqEU­ r3F?:@-(Pփ H!K)#Zğ]Ac9Mg:|?\$+G"?1Ezzğ]7}wzO9QC炼=m42MBXOs}wzO9G$̟r?]S"hwOto?$̟rIߙ?l» :7E Ÿ,@K2?'算~d㕱 Ÿ,G+ г_McO]/(]_V+ г_M)B΍4 >=t'}wzO9[)B΍4» :7E?$̟rIߙ?l» :7E Ÿ,@K2?'算~d㕱 Ÿ,G+ г_McO]/+>kbWƫd%_ kwOto߅?Yѿ/&1'算~dO]/+c߅?Yѿ/&W~gF]_Q >=t'W~gF?]S"h}wzO9G$̟r?]S"hwOto Iߙ?K2wOto߅?Yѿ/&1'算~dO]/+c߅?Yѿ/&W~gF]_Q >=t'W~gF?]S"h}wzO9G$̟r?]S"hwOto Iߙ?K2wOto߅?Yѿ/&1'算~dO]/+c߅?Yѿ/&W~gF]_Q >=t'W~gF?]S"h}wzO9G$̟r?]S"hwOtoc^u{Fc32:պo| Ҡ+vx%daЂ Zu&sK qI|ǚ߃b< j>9ltVm&D\}g4je󂛸(w|_ ZZMI|!CmRK+(ZEkW|'1:KNP1i"MΥ(2x#PI xjC'{CgK 7ZyJ9:w[_W~[꺗.|w5ܚNb+%v!⨸}yBCQ _Eh;n"*O?].cEq^лuT}yBCQp;+ۯn"*\w'ן.D?U^лuT\Ɗ>v!ۯv4W _G'ן.D?UO?]?>v!⨸}yBCQ _Eh;n"*O?].cEq^лuT}yBCQp;+ۯn"*\w'ן.D?U^лuT\Ɗ>v!ۯv4W _G'ן.D?UO?]?>v!⨸}yBCQ _EDMs^K驡\@QqUsX./#|L*RԿ[U(Կ[U(Կ[U(Կ[S4- !Zޓi4i 2ʩ+ }rp:և!*G$NR5/VyZ+ʻGdI/%GdI/%`)yZ+#[^Ej_k(5/V㔞$մ 2eU) †d= @P5/VyZ+ʿEP5/VyZ+ʿEP5/VyZ+ʿEP5/VyZ+ʷrH23M𧊵:?>$g,n3z?#R_o9GxM]xM]Կ[UBKm4wk$H%o*)U2gZ7Wi?v*sk[x_[{Xҥ, {Y4#t %ľ >si:U֡bw1_,A5ڿ".ddܻA9f\ZSl--r̖I$ so;k2jַ3wrM(͂C#H[F_9 σHr֗Qޥumtq΢hhJgnTfoț? wƿ9cBÃWmR~ԗw@\erʾOx~!Uӭ9X̑f4>tIT6Gu&dHwX]/<9e zZgl9vXP IM^Ys7<={|<ׄ>𯇾$?4w^o<+&ajZ4s?7vωy7HѴ-Լ;HjG-L9$Vς|;w[}CG!Lm^W2G<5QvMh'(g^'&WPt ?G2y4 ڴ3*lb9%0+~)Z"˨-͜ͅS]\$|0ʨ0.H\z/Gm]RR-Hn4lC$HѶCdU=WhaZZ=iv"QK&2 #rݯ_ѯ-_~|SCk OxƺVAmY0*|-^eI72 SZ'MNڧ%Тmm-m#:VFo,[ \µ_I)$tK\=\I%Hdw&!AZ@5{m1A.)oep kr%\ɒ RF?KO߅~$>6+xKQE8;#&Tۂ2k+fτ Ɨe.=J+:ƥxGUg&6b%w>k?0T^%__W_B_F~a_ E(6h?P*6bʓF~a-M0oh=("n^赭b~ zݭl"?ާv&Hv>k9^XsTsuv|K{ko3Ea!WX+(,8#<$iQXkH'{[-@2޹ou&f'A`0a|oZokqgP۝Tf]Eu +L'lʬeNO|g}cMŕ>#nc7UkHv [̌ wޟEMbKk55ԭ\<-w1F:V> xWWWzEʹvEX.~p0Ďep8⥧o.CO4֏[V>iIk>2Ym~Ѵ~qgn |s oG@.kK h[M'd%aĤGऱHu(lUqǩL+Ul0#֝⟆Z0?[-4߅{)yPH{Y a%Ř!Kwk&I?H/^)/xl~bI g8zi=zC.|9wi:՜)mnYQmT0@#Y?ʵ&|?Qh:6ziugrU2 ⣪v=]_)Ѽ#3|T𶩤xW7RɪYj1]kr."{ymZ=۔k3ß{ ɮ^R o<%H%O VdKs9Ѐd>keZFj&][RҮbmWWwjѣ XYX*c@p[$f߲'}TH/45ˋӬPChnp1U+WoYI6/n/JԇQ+ay4Q|Q,v /@&T|HX_XOxB?i xv{q imrᝮ aH̎ L^|%uR^~_ é x^Il5xF}WPQ .9vvI\ȕݿ6yGfmoQV-񬱿`7+ s\~.YxSқD<9Gv:PH)dB2ݹH ;Ѿ|?.Ht&B 3~B(ݘK3$ v9It uܒ9,L%rOiKi_V_ CLZwV4ӥ7C>9mv{S[oj7چ贽fO5R(xbUā7C#BaB7?HW/;~m:_$Q+tH*;|S-mgQ|[ۯ _zthB @+G$rp9 #la xFm5{;/뫋 pM5GF O'czomkM?%w?Η6J/+ů:o 5Ok?fGʊE! te 8$רӶϾ~}Nj6J/(m:_$WJ<[Ws|E6~uѧ/fEVUlyR>.q׎5xG6ދv@M7# ?? $[MkMj.ėv!X6ѕ؇{?1{+K /oG 2r9ZGW韴ׇIh>#_G,-5v&7+|Ռ8RT3K~-%2hRxQEK8%ĩ#IL x zqQ"EUT`:)#o6oEiֲԢD`@I˶"w F']u-7S5 Z;6qhbX,E`+cCۻ I<=[?;E-/@q)f۾6ڨڌA{Q,+kfKZ=_gHБ+QUvOxԯ D&h̤YP@*w#j?}otJ \GwX.Ό:]5xO?ⰱӼ]%,_/1$+wo9 wڤ>_3k![?oG'O`tjn:g{ϵ\_ ]+O_{St侱} s.TAV(pqSuAy8y@U 2 ƟU|9!$DtH hooxwWĚ'ieycmlʆYFytO~$5 -3Ǟq"Wz}sqn=Qgd|Tž*UY^֗_7=Ws|G#oD"> <4l|76lL^C-ŵӋWY'%@]/=wǏ|c{i1Zvg).lq.kȳ='Ws|G#oD",QK?%w?Η4Q`dxvH]:kӴۘakhq Լch&P42a7H4tku/r[hgJ]_rS'J߾+y&wpv=G9|Ʊ7Ke🇢"–-$Gr9| p3z~o@M֛V7ȖwȳDѸG[ 0zzW$PѰ' &:Nm_MV!HMQT`/1g147}+lyE}5 GI֞UhݡXഒ~ɕX6BvPs?<{?tSqbmOٚtE,TS︱o7NjNt*;:[K[]͜N'-ǺVaiMi7mZs<>㱉eP)=1Ok޾x6ዏ >#-%S*1 w vUwOUesimRA:0CFv0 uYō:l[y.$gbq€9MUi^&oRIepL3pZ)p* >0@VVOGS{we*ߑi{׶ھj֭_A 'y (!T' 9@M7Nj4WsV?Vjwm]V /2AEzp1Ge-cg 32rOX/z^T' جX zRxփ?%w2\p" ݰuۀOLW 7 "?4oER-kg}ZY-RO_q<ַ+>ŭ߬bk_7?ůrOZ k(C~|Z. / ȷפ_bE*,$Fde*흪XAm|Qc\QE(((+<{kvV:$nA uO47Ę)v[֗^:m| đ'9x~c62Nqz$4ms ݗ5֓Νvi]yu8Xl`HPR`qFߩ'CE^R_Hx.pA]:O$kد)m]-pYvxa'ˎowMqOly&<#eC}ß&'J:'}Zキuj*g}j>-Y"NQԵ Nay&70UUr2@Wa 3slֺ\%_yJE,'oISP=+ȼ'a%^}xWPCmo$1,.w|4]7÷ t8t6 X$~4[KW__Y51k~#ǩjv:dZڲ 6wmJR ϥ|;>0,KaEZ,壟HBT'})jzg1Ž-%ee=_z։oRo$~ ֟"2#- U@?`z{GDo>,_EM";{|w (đ|UܿomsZ+ItH1ivk{vOn NR[aMWlbxCHOo 唗R]ZK$]<BOȧ}I|M^xetRV;+_4n$7<h8 ]}|S\x:k iVMb6E"VV| d'ѯt LhRU@f6XJ_?@oWm~ؾ9KYT[ hy%`H8f'fKއOZEچYC uvFqD#_x[Ú_OgåM&)sskjʲ~U_eeka+ľL*1epؤdg evc]%u CŧA֛Ski_icfQdV\nmQE ܶh>錭%Fk10 :Bd_/Wu=D[7 hR"ej?{5vڑ+R/sƝbӦj3V߂n#QKf)?CZڑ+R/stռ9X@XW=2@uȦ~cjqGGjE<HP{ejx+Rl_mm3b/HQw [XajzfZ\Cm\A%^ݙ)F@8^~V_(ԋy\;CK95 d@q\կkf?ˊ?"W?G_(4LgZ^nOoٛjE<HPuk:V}s@M+IJ08?j_(ԋy\;W5zBYWlrr_!ҵ5okfjE<HPu{I:^IҝbN:lWew ?"W?@io]3T?& )9^ᤸQjE<HP_>sKE Zdu{[V0.%q yU;c >Qʧw ?"W?L o |;м%ZƝop5=bEk}ڪev؃sa 2ptOR/sjE<+ľ<]65 a ԰ cTj_(ԋy\;\'&$#ڑ+R/srw ?"W?@(ڑ+R/srw ?"W?@(ڑ+R/sr58Ǖqr (|mV *Dƚ\UMZr1He7Rm'gXvvg^'E<= :-=lqw#p&$@'F m,9<;]NеYגX]1YoơE|sr}_J9~"R A$whFȑv31+\?_~[LJx.Pã#dH(\.+x+R?_gqj> H1=-^RľJ:ڤ,`5EXc,`$gV^حem߱ m3g>%A(udW ,}E>ϮgXcyjJHEⷴO~14{M-)Z-J?%)*s:#zv{|p?KSyZ~aeu]^\X;Kpb2d/]Zcj^#ѼSk#GGr2%mc尥&SVv=:?q@CgKNo ƚY\X}TIr~FS HvG7u/iwsk0ͫ^.]>F+yr{jڿoEoGcG??Ouv_.|o [xYX㝤Z8r |®\ox&3d]-: CK? 0MWv3hS1]iсS8?u?!7)>'|8 OH/Wi"h1 $>qt]/Ah /o.<)g &P1eyugo.U Dtw[>GwZzW??OtcR7ƍQt Ej2\V^ גcS łg"~[5o4_ xOkfEϖsg\Z\q@C:jUVHMbՕnl[8[Qʔ,A9|/⧈ iHִ;o[0X|HӉc1ȔwokBW??Otc^E+ _:?*,cG??OuTQ`<:?q@CJ8?u?!SzUX5?:Ҩy??Otc^Eq@C:(kS8?u?!ץQE_:?*,cG??OuTQ`<:?q@CJ8?u?!SzUX*Փq^hm'Lϴu HlG\WjLao$M\<+6>ki=v?f3&]kNQ1aO=1^e˯x6^|Fm<9hV4kg D\%yl$&_Ȋmc X_ >@v7iI)pm-$_2'AGU?A_E]>ȵ)Y[ɯݍܬh"k=OKI PSү}/ЯWsB-A_EnJBE=A_E]>ȴ{"} >OW«_Uw? K"}/УDx*|%hWsB-{"} >OW«_Uw? K"}/УDx*|%hWsB-{"} >OW«_Uw? K"}/УDx*|%hWsB-{"} >OW«_Uw? K"}/УDx*|%hWsB-{"} >OW«_Uw? K"}/УDx*|%hWsB-{"} >OW«_Uw? K"|D$69|R7 L%(}s)0WĞ ^COƷ9U#Hc3vlQ1~%m*@^M#β02jbW"t&3@w}5uoSmߎ$Jͧ 4eFDe gh +oiqOڞ /쵩ʑ*%;rps۟ k";?%nu1[ۤ%yI {!|Ddg /=w^8ƛhl:X *ciN. [Wfc'q]t|&bzKcǫ_iF WI~9  H H S}^>Xx X&Yck3p`UԓSkZ WRk5%U|琖'7ğ5m{Vִxı46mWr.sA')+~%4~ľ3Ѽczh7acč c(W-wZ=>Q}S쬭ਤJƣqsmf8G{G_oxIN>kbYP` ǐx~E}4jv]"Z3Oj^2Oi o~OKC}xnmίa{ FF%d>jF8+Ӽ?gotoMzhQkyc7Iȸmѱ`W[.-6kINu/y*ۥArid< 7Z}a_W |wKMAa$r_ZIx7<4iO,`R|Ӵ5+oVv)A2α6w28YSz.{87:Wuo][Z[Q[Ȁ_EVQ O1A(>cC.[^xQ 01 M-$ck:Za;ק㿕Կ~Ԛ} Wux\to,s,틈LʅPڏVyZݖysgf/剤 JǿnNpqUohRkârٸY@U0+YC-Z5˭$%-?j0b0 ln9Ogo?᫝RUN#a?YPp3.+mj>M7дmPhθ#Cu n\_KV/Ė\@ɾBcwu1F]v;]ױK;WVmE|:o xR?׊Ij6 lYmm.;x"I8ؾt<#B5(,]N;{g3]=dITLKch/}kZuȳᴔjN7Ub#$>aל^71gĂ@iƛI4 R֗WG5׍-.PhV)Pzp8<+B֑15/wSֳZ_OZZ̰,HTa08" @<iWΏaKIYLQe w*%}nvgjTzL˧܋Y\9x*և*}?m5?ma :g§FͿG*}?m5TQ`<>B6~m?S#hZo OG6MzX%OЍߛ&T?k֨y/*}?m4§FͿ^ES#h>B6~m(K OG6Mo׭QE_T?hOЍߛ&j,§FͿG*}?m5TQ`<>B6~m?S#hZo OG6MzX%OЍߛ&toxSWoEtZ+WFnvy {ey/S֫¼ ZG ¿-iY#z/'#lu+ #NRIHʮQ?(+2xQ892PBqEZ?/&WkH_~ $H7NHzuԾwmtʳ؆}>l\#|!]|zݾ*X0!P^Rz r|ZLJ4? ?ƶW>'іٵ]c@^[]JƬ%YIl R^j .|M%xVId3(m[sydXU%ĀlmP1ױN #|$o~EV]n[U"iw#D@wtQZW~N%ҦKDs'` =yʊ. ?i߲ѺOb0 h$8Ž_6߫zjQ_֨%S~?M(Ey/ڟ}mWE+~6߫OSo .Q^K_jQp=j_?M(S~‹TWmWGڟ\ZOSo >6߫zy/S֫}mWKhmoaYɦ;3#H䒙$1i{7>!v:ck۴d[`ʼk#M^.N:d3 C5\m3XUH:{ψ? |9;ش0֏Zlg7pFClvo )c6󑅫~r-/T5>S*ot_j Q# {C5\m3XUFZX7gk?[k~ ouec ݎ-᳸L3Fw߸Y)v>^**%/l>&eh`EˏFn\w f{=S8dt+xGoԒ泚il?]t[vÝ>b_v:? w]SƗijZ4:z6{ske HH%OgFi63kIqk4]7FdU>V(Eprkɗk|!45Oo-beDPt̟3aτRu}VMNfFb# YS>^gȕ̒mfM;<׾8[5X/m&AoEO9NۖfS (lsZDxr]7xM>Gӯ; vdfٳfPpNK5WMƞ)AVe;7پB-$-#*T)hCxz ;wX^Ma&] Aq)6G UQM%dѯSP7nbBf;1Ck_4߉nz&ilMJTY#uG!XpFrxŽ>$_|Gͫ]O$[Yȷ.-yIHu8}5&tm^=c\qHUVvq/e6_FOUz.g-;;٣oA3>nFʱ #+yeo.zueo5\ƨ.C>ƫˏkQB5cǨk캧 캧 _.??=G͏ 3ߜj3ߜj|Fl\#\6?z6˪ϫ~p>˪ϫ~pyqЍq|.C>ƨ.C>ƫˏkQB5cǨk캧 캧 _.??=G͏ 3ߜj3ߜj|Fl\#\6?z6˪ϫ~p>˪ϫ~pyqЍq|.C>ƨ.C>ƫˏkQB5cǨVJʹla0kR]#RYY4--X#D #)'+r?> OF=xF{tRf̦77p3־op2qҾi߉?/Zl4 kr)0{295'>1x5W/\xBdxDv4uҼGԵ//{IWm0Wl Mexg4iz{i'YI:IQxR4d6p(?BM>_̶- j7-yQ=6?>qiG֟>=㧁u+nDhOیF%$BBB!T0a o va_nb/Ǿ#maahR(I,^K;'M ".k TK-X|eeqx/+\5iiQ2vg$k=+zotxɞKK!b_26{'}Wǚ<=84PV%9s3ֿY?4M&}OZ {+2WnX i>>#jpecmz"pYpx84ޖ~_k["?LZux.fI,Scy[nCm '&z/u;Gwx:{CY%9.%3LAUbv7t6O"𶛦^YxCQӘiG6˦Ukok/{t| s!ӵLX-̒B899Ff OX.wN"uT8MNҧpzV_-7/-3g{˨iڬs>o IsW;!H|K&* o1耬c -|N 袊((((o?xzꑟ6a|Tj1 ~9+Wעѭ5P]=SQ+زv56!t~}A9_|BK3m-/ψl#kk zmh6ɨdI7]n=Z5VgGHOQ+3 ??G϶_֏_ր4ҳ>=Z>=ZyJ`h`h+¾NMiZE Kǜobxw^qkFѕ.yǒ2/_3­ySwpnBz<ៅl|;.HH.3I'֞6$wk.o]Q?ֺZ<w;a]=!/Km9YɝlqEgus{KG[\c|3־% h@Oζn/m7#FP_ Q V_k^u٤ ܼ\Kn'het FWƯcꗱivǣ]FyzS<_3s+?K+?K+_ iO5j7ѧn*ahWNr:^o:\ѝȡԎv<Ȃ {)V-1J?U/~6QKq6i]FI湎600rY'Ӿ|q?n51]iw0G-¡2bcs=E%EgusEgus_HxOk{ HdZ23JQ8w#1ʌI=hliOiwL52V+TV@`-?o">%hmƝb0\|S*9Qk\k:m @CVREd?+Ow |%ҬjzKnHG`r`nxu೧j]֟o45ʖ2p Q U}[&㫯 ]jki}m]G\IekqqȎ[CfO20dp> =wu,%Io7lё2U@ qi_oo_x=72?H.o"H.o"_τ/kW/h_&B&)Kl獯~^Ϣݾ6E\bM(DN憬w$V~_7G$V~_7]go jvDdh:Ν5oUm#)A&<+?K+?K/5߇4𿅼7jleaHK[<{-@cu^|)[x[Z[{{16vim$V,x<ЕH.o"f;կ.o$Wʗ86sհj>/7zԧJ_GXC<ഥ6*:n'7k(Ե -6U//m-׵KFN{fgn/x}}ty%}'_w,Ig-ݥׅYJg#"G*~ao|3VtwOk5ΚϸtR끝ϭ;o y/_!x}}uuxC_i:5橬\ U%!ebYK$'x$H!rx}}u/C|>'ծuJj6mZilɞ[tPW-#sE?H|1X_ľ(u*]R=_Bѯ/D 8u! Ų~gIG_!qӾ&uq1iX*(}*{O?7'_ s|<@uɴg<Ɛ܈ e\M I+<Ͼ/3ĿC{:^>6mlgY<Ԭ Hۚ vG>gIG_!׵|Dxږ~X-a-q@HN+>~җ&/xStKo n& W72 JEw[r3ĿC/??kk~Qt{};Bk}4Xы`A0G MC~ؿ4;ms.viz%}dq0r29posgIG_!נ~ѾEjR_koe4fH0V)8a^^]r—,RE )H 4<%}'_gI_GQJ4񍮦olĝK3ڦYlXy4@׭ҮDzJUž+ k,^'2ɓ~]PoW7evklφt KIxz. wz!@-O#& |?kk"CI8tH6KQ^ᯏ _>"]Y-|W\HH r9_S f]5.Ӫ]fQegI!TzTc9>hgW~{咶Sc8oG&mtN qgd,[|Zo/·?YK/)mMJ(BIgUq'4ڢH;Xkt;76v헲\HnUm;)_e(]]~O&ٯV{csݜQܶR_KqI <7y_~Ǿ#bk(ԭ6~Sm,R2(sʝí},6L*"g?ⷆ+4[\z'x%hVF<]Vo/$.{ݟ)~Nj~7>7XSZhqiEֽ{sh3͒+t>H;@7Y6xZmgЄ~ Gh^Lc@ 8v6h\s?r:~*hv4YgvOܮ`KvڿĭY?i/ x?fiwZuv5Vqpn"T]!՜ӗS&|Jnt/ H]%ȸX4~f2޼Ǐ?ֲkZ-lJà2+B,7*JDio<'=Di] Z Hn/4p%o/n\G8~о%|M~Դ Z.`Z[%W[ov8h׏;H}m;SN;|ڷ~͞"uCN^_F? I^ţ_]^^4R4QU+If݅:gφھCk*[إvmYhe1w 4OMho~࣢mލ{3[E%,r]^ +mS u߄׏<5cu5GkU J#v)-ɯRL>#hmc ongk1&#?'SG5 *M|AmOhQ9iS ̱=q`dr+D_b8]_y <ogIn-OW&p(X -Ć &jJcWk ic|^.O xoKULjlu@J-mDx FHU@S+?lx--nl4K)o-B8ԳȤ>t_~2¶ v$- bImnL`Ky$!Y>3iIno/=͞ xfqw?b:lٷrΩ;[Ic~*h%n{y!]Cq+,#[~mvoόxG~34~R O]Bm B>U1˂Km,5u;Z졥ƫSmkWks#ݘ'Tp5Wmn_GE>6oď (Vėv:d:'iXA*4Į 2b 9#k>$|nZ-Zm ۹᲋+s,1/DwͅW#slƟ |2n/!lhkQR&rWЂۙiȊ*=?[:,?ɵsp>$A,l9ɀ8x|qYO#ZxH[Ufcj%0qg dKWֿi]Ğ xW(^Ώ[u1 OFXͅ; OJ4O x cA4"m m3nbhS,θ1m.[5?V;(5[yFNԼ- :0| X.W 2;|mi𾽨#4:YPF[ nkw{aYKqobƥ vE'ac ~_ _?[.çyw--yJ"BAU3|k~uVr]F[nt09$n !דPwm>+[<9)t#r߸2];]ycƥgg 7;39=AҲhoş紷_Po{d ʬF\8ݜVfFkVέhrGfח3i:x xz j[1vGjjI3m#+ F3iGO]K}n.n-ImAA 2w(|E>xmmD<Œ2X9nߌ;=%7ƟOx,fˋ ٌE,96;F/o3tZ.6XЕ[HK=w2mP_FRN3/uiow>sI#ǿ*|AI;xX X}_ɉ|#78`*,mhphe˟3 I G56J\ZUX )5{]i- / /1>7HAnS!?0"?*yqp,enInT%S9|3|VV}g[AVhX V`YH 2:5gV.2w]]w|(Pkw #C񽶞uX.!p0@UY ¾zIuwZ/udԬH-! [\,#"2G$GQ"r@ג?b"]:en]"itu;B(K;IAv> 4[~ eզZ+]>#]}iUSdgq;@&5kvW-;2oVOQlS>`KWck䴥v?D/3E[htH&Ѯ&eU~V ʜ܇ŏz5Xj~4 _ZүMiiund]?k@nj( Kbu_Oe:Vu UTj X`0An@qvإ_։Iө]|u<qxrl5;AVoDxe"2ddF9ړৈgHd4w[6M>_a 1wc' Aӈ\>Vv?Rgx'q^v:))4H6qwc#j+{[|0!6tEta* Aw {j?x?IѴF{}girXH#^a82 sz4w[$Mt%$|>?_-cs|WҞ/oͪN739lsœpxRTo CYX1< g/|ic.anw{u}5R|Ԍ ̂N#~ -t 3eLvlpBZY0wlś*iɷt*?s}t zuoy f20P0R3q\ًľ.Y%k"ӟVk<~":FG2[Nk1nzWuY|5ѭᰊO0gfTLS˓?,AJMk/ w~]-.Kx£\cLC-N⢏4>4;VJ69YE n#?̤9N/W]g|_<~6ͮiwra2SyPFq"AMO4`p1>,>(z޵x&X]M^{x H>D\ eGn+Կj5^Ѽ_m&-,P~s4,\0 F sO^$7.>SﯫV;n_y߂~ÍaI<'%+# [vG˷?1x yfծA:LWzlY]^K"e$X'¼MN5Ohfi5jG|"FUPf,0<4ܛ[XzY}:#ZK?56Kf#4BeED ˓|MW&6iw,fEMAs?~KN <'(x3im{$ieG0P:JAxǷ_ Zz>+Asx.$sPy{OW7+N_xS$מ/X551|e ́cwˌL|k5UoآY6s '|Uh$6rJ0`kzh&nz9]QiS[D"x3#UU"O3%gK]_g~uhw{=B]KΔn~é]^6 Z)ox72]cO3\ FN$ Vn}>meA?vŶc{ugȭ9IR'3~Cnݎ6|~x?]xI"AB_ '4q$'E>Z~]菚ً O-dOڔL,/kt.Hg"QW)p{ׁ#j,Ҥ|Ak^AIjm"33eQ'ھm>nM[loOC4-`> fRnW_|7׃$lpxBml,M%}* g(oί'McRm. ou+ GY0x[G$^eShڗ٢;0 V]v/R)!RX>&}/UK yHUb9rĝ^&`1)`$6YabxʿS'77^1S&y[<ÿuAXxf[\i dIYޱ4Ϸ q[ tį"dX-cr6`3nq^E Ee-?}ò{_g~ʿ i~; އs>%tkG6ېy7,:] -> jWS]2K$"D$7C(psͼcV־%h~3|XlmQ_#w01۴:ǃ8[/5^# c;gͧ<3$-2k2IU&0VC r8J]?u{]ZΖ,%>ǐlztj4KWEu\&oǚ=_M,4$.olI$]Z\I1=^#fIx? %j.m|YMSV4Pqz[r2uzaf6Rkߴt8u;oU4mjOI">P.0ps@3GGڼG@5qtM?V:Y]#n$SO᷂̿t 3NscUTg drk 5uϵZ_XIOad99RwxK7c_| 冩gDsJkǺ+~`~&׾.Z{ymKK,.}lea( C8{'2X੿>$'X7qX>x6σQu-OP~g2Jぜ d/6 _#+PXA+`q[#]"0%v*rʳ8OxvI\\YMg%ZMC#ǃzWP;|AC&0me$x /t?xK7c<8)um4.K4o Cksv Gfec![qo&յXViI} Q0&(S޺Bx%=qu^|!gQGWݥVؾne8kdeSCx{ujSj6槩jDHb+d >I'fc?,T~jםSEO]ķ7>!մKm:46Y?PͻI8]1}c>S+kfu12>ZW>ُ]OOX੿>k2 zp2Ccgw[M;+hhxwRcx7V>4|G sKӟT(u{9&!fCVpA;W+$$RZ^";{{c/0"UQ@af?-ֵֹ? HB[A*C`B* B]޾}@L=3sϹ9~3xfgӔ(? STz."M% `3drќ9fͦ?ׂ?׉g\]?vlR*:*زy -:&ŋJ$lܸ;jP7t^[} ڬ T/^[q 6yZhGxEH-- ͽ{sr6Tm{xpM I껿"B(:Lp=^]@Qg4׷Bss ֱaR0jlZ^HƜQmT`{x8D@QlxXڃMAq=}}4 14hbP P[q}2 ߙs&@mm+a))^_p7HII&]W o"aOܦM+776V_dYr&g'SMH8pZѠ9*~_}<`֬w/ڈ{ h°a!6,[YSSO~~{{rzDT`7Gؓn+Lt߆I,qK'طj&IWWW*5~UU(5}*Od6 Qaa'u+[@Il4hWuG@ѼLjI0>6!@00*9FJrBCFBiڏ:*P e,d|Jԧi=TgdxPcLLB:i ]Z:tq!8~AȽPy4pN~ ~ d^!f=oC0D~ \5kּL=SgFQd'ע;7w m \wZ6P9IDte^֜ZM.z9nZ5ɓ2ds <œ&rL>$k2C;>•-o_cN0o9i|D8umbФNەU%{V5Xϛ-6 W08]j7FN=Q[[`4G;A @dGՇG[Na W@p ^@x&jٚEt|cL!*J|+a(Yؐ.d.MMB$Dx,8S,@E!.Rܳ$3( W\%^fp-]Y8!.0:u|^@4 79sR$^JfO=e)T=_Ҟ%gp|7Lɸa#ׁtE)k(7vJr= pN1HF' bb4Elur -0mE:w߿x`\livT[nW[Jwg7S F&]I^9I"/ɓpWJ\)a ʛUIWgf7 H6Nh# @쬑E >zCe~"uE \т 6+F.t\,lqi1  s&JuS7abiU牪:DŽQ B}]Hnl1l9<@x69AI|^-'b7|LGy*/gt;-SzpCu^WɆw Qwط=D@/ uy`թۜx\ @9)y8Ñr~p%97(UbϮ{ rha NWp#="&Fgs /U߬Q`k\|p15|bmd!A,w|_B`X*jYY R7ѺW;I]0P&T2z۾4 R*`he-~"vF]zdGJv ~ xFS^Ao`3u"hne3hK ?ʬctbڬyxd=$ S zO>u ~"f}_h%=*'ca3=o7<0zA4zA}ƭ٬=G͟aFtPz:^fБ5htĿ:~ ';2#5^c4wC|fa/$ zp7"tEXtSoftwareAdobe ImageReadyqe< eXIfMM*bj(1ri%HHAdobe ImageReady0231/Ơ0100Fotoxx:resize| Fotoxx:retouch| Fotoxx:retouch|NEzfh`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }Zǀ1iTXtXML:com.adobe.xmp 256 256 0 waM>IENDB`fotoxx-20.08/images/effects.png000066400000000000000000000140331362435004500164470ustar00rootroot00000000000000PNG  IHDR@@iqIDATx[չ~g{aw)R\@D#Do4*&ĔNj`$Bnn $`SR\V:,m:;;3ghY4Ͻ_/{_9?qfjE{zuRw(z­2$Hwu!Hkrɮt۶s-ZqR3Bo6$!)p YltYv Ak`boh쌺= 2!N+ili!'BaJgdLL!ڼ?6O s Ajd@l7/I74ĽxGi/[CQu5K#fHGr?\?^x"ތ9'ڜ4#y *RѮ\2t8z<ԯͿwHrywNuQw`̴ #Ds"cR wj,[~ e '(}^u2L!L|mR:'q˱U[ߵEr@մϭ#ٞV4jćQal͌dԫ4!gL]!<|ߐv5 ^]:vgVnǨo $8H:*' 9Eb0~]ï7ގ]7@Q`?ȈcTd1?Fx?'Z5 D7y9!Kb6i`` (ZL{Ple)hH`%`iKwCTŮ$:k7{ʉl9k@a$؀?Sx2ꢗ@\JR 3k ƒAwJOEϝHM`@_@pn [xO *LX7 6 (%Y45Oˆvv(/ΤHpÉ!gtɧLGC(! i'_Ti@8.v;{Dzm߆eYaVڴ/Ou^q[9!H2ދ QLl~9;`v9AhyNhK/ũZz9 Kn|PLm0_s ,Aa/f vG/n]i{IGGGIc:snʗZ8{i M쉚LM&,$ Ɔ_!>ULcZg[q0nA.Lf{u3 oq*{s€j-:$BȌꁮyq:VLǞy%/!9pApy(8a`wё_S4sB ^V?pGf>VF^7 7wX[UlVyt4*YhLٟ4M'z6w,<'D_^w16 ɵ8&݁K+WbE(U4݆BS]9( ۃnX`9GZ]z@8Ff|Ib-V,7 [Z@/")ۄ_^t=޸鏣O1 dh)a}xSuK<@؟T~Oj} iіEWXDN #('1{q̑ 82vw~!s yh2hEED"Z57F1f۷,|F[ߌfɇL!]DOS!/gp, EQ#[G,1▂ D~{[؊&kh(#)co[.^`#V*!}0Tzx)'0fU(C]fgKv' RMt rҰ϶1وK_Zy͘}G/N5@)Ð0$_v%Й,9O3eOo#-x &J\k΂ZUQV˲lƑmI ]gqkUĺs1Vz`J,gs+顷pר'1@ tLTI5'eBG̥깯8nK%ȸ0&qEEx>_0a_,,#GI:eEO_wTw VHFisށ_2?K|B!,>kb{F֎]8}&V75*}li.{~v7M$bdLЎKNGȬBm.E"}y|-.X d%XJ],atl\:S/ :%G4T&y -)Xʰ#~6L׏e۱ʛQבN&ʖ m/+ˆfh4>G 3doɯ׿ıc,ՂXʗ/ɒՌHhq_5wb1Izs? 'rW(bvqK8|[8Yfw 6GG#ZK n]+~k!ߜy #y R[zb}h.bQY@P^=8OΙJAe/"N!fmamS]?kNA!j8@>㗠ezd'2i\y_$pKOM O-BƎKݾb?՝EU>uӲD'1]!gF_+DoQLsPI-uG[!̊*#᢮AsXAMKcG2‡ͼ0b\Ek=2UH5;`mʀxzHARa\^Ȣ0q7HIJ#E@%i@$kh6!lq-67iPjn&l/S \}gwWr@J[ޙzuao/IbDvFR < (p%i+~;(eP0GA-b{E57裗`>~_+Gz_;H*qٺȼ9?xfMz5W] [*-H뙌C2hqkd"F4k\kc!:4YqD,wxq5gLύ DPl]S+OX23ᗫ{cG`P/ɶBp'3\ڧGDucu}^/ߋa[f0sJ3*Vt"ޘL9L|x 68~ \S g@7'<6+/S9kZ"^VkDcKԹx,3zLz4YRPf*}U-F~E!EwpToy}U\0ԻwD҈Vmhj vSn]\6TqWDxb,#-⸩N9Ó&*%^Hv+VElHmhb7H\$. M= H$XYf)"R9VN`6[) F*  ~Z/1%vEV qy̌(P KR!P_|[{JbNj?@uCys:oiB:gO$f.weO A9q7+eP0܋_-9}޽dn@wvm."4l.48dwl%_+ 4c4c .{&ッ} GI2mGv'f,Gj/H\4UB^8NE*fOHqq6xU?j:N`k̔^FeXIfMM*V^(if%HH0231s0100Fotoxx:paste area|paint_transp| Fotoxx:trim_rotate|retouch_combo| Fotoxx:trim_rotate|retouch_combo| Fotoxx:resize|NE 256 256 0 waM>IENDB`fotoxx-20.08/images/emboss.jpg000066400000000000000000000273261362435004500163250ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 111 292 0 C     C   d" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ HeU]+U*Cmu𵿊-m$űXR/%hڲh`&4V6 WK/Z[u]RTE܌aEU;iFklPZ xOdEl>|H|!'ߊ-,g4DSYAHY">F7/ ߉Ot+:_ kz]aWi.JM,@#GPjz'mZ~O(aOz[ x3 INWexkty\ $7T0J/[.Lod܌b^_ɒiy4y'8GNдZ_7SkM`?_Ο:ĜW91|kO-hZ9ܢ_X mտ_?i >}^A%uk_,kʭ:S7_ 3{s+P֤񍆠ZA5 ]AmV1>`+9nSVWc#!Å JCR}'?½kH퇋~iKom(*HϜ~s(zjX)|[5-_6{g6v/ȱ'u~1aOl>|ↁQ];:2Nsđ1v]UR;g\ TXƈK"D)PItgAco:vq"H"]s fI{DצH4Xlnz~/jnyR[G?B}? vB ж @'R)nnPOD?цA>34־6Ν.<^YFW>L2#)U:.*֦]HԵ[R4k%XCE)CiAQd9|dqFim[T WM/\ЭTЯ4B3B=X;Gb $<>Jze? |)CP-Z%ڜRE,zG0[qyJau_o ZFR+ߡ>7) L w(:r3E:fh.Y"@8(>x)Gg:5%J[ŵBsGO~ø z?`_Z=m.~|}_??0xwß}?; +_GU#Oi>sC*q9krG4x͗ͅæZ\J&#gmpaD81Uo2Mi:[3]BHd*188?`_Z=??0x;ZZĒ_|W7/\\cYOۃ% >Z?x>SѼG-ɼ?eeHU W???0xwß8iϏ|W Ze[{v9A(.M}sC*q9kYuO),%8ZeYR0rNMdx.TQPt-`0I Eykwß1|f>/IKbI|V3 0#1m_lø z?`_Z=;;ZZwik{;ič#F 3SY?>)>;;;X5* M2M--K) !c QNKd}MoxOWZ|'Ok <8ֶQFYOLx 8$r:??0xwßpppk? -j~2խ+qƅ1` 5&EhN0\ۤ4@YA ykΙm_`>V [)g@ l{;2ֺnu<{^ÜyE[ -o=yg@oak_ᴋNueO'wq$ Qn;FUXIY]ߛ/GZzhaeK4-44@,}p7|>mV g=w@v?W˿|Ox_m-H{ ƱMʖ1f?vmRi[o>"C#F3RM|/K}/ ]kAxS7}˲Hi#V e2Onk2LG;_z ;2X;in?}2BmV汾'k?cZ]it7#k+=Ҭ)UfBf dg#ɾ _?g|=i |j~];QH捋0$W !H+DW3kR²[6_G++eEpy񎷏eỉ4n&kcxL3(zy_/~]DWWQY[Mqq"(dG8UP2I>V"2iy)|r}\G_|s߅߀'eR2\2@Tu=sJR!"՝F7wcp:dURN(..pi7}2y9x>iMZxf3N[kxGӵOMխ,#Y~`l-ĭC$X 1)u:\0` Њ k 2#h،t8 _4S|>m+ ?͛QDy(VXman8-~ nFn_I./^g5G9۽9"Wm.b[is+;Q :6Zt?PcFv)3mn(%\#::)wwmv (±i>6v jDQ#=1phjz]\lpi}U%?+e 4i-;]5m3FXn@Fx.?<  X2f>2HIP| v?%?+e 4u^^ݤ oYn m]Aq60tH( &цv! ҺIt B.+ qyhQQP[~MUxkz΃XEog43 YE+dVG"%?+e 4KVhq61h~꺞mcAu4;?jv I3@kW7 .A[/O]#_NtC8?mx-pUKVhH'Ҹ x{O'YcxT#9|.y EciM0dǘ t?%?+e 4ls|hZ}oqidࠆ)7p6$Et .A[/O]#_@cJ i㸋8) (((((((((( G]HD2ZZpzdjsU?.=gj[c`ay!x<;/o ZxZ[{V;yVSч86ƽu=ubM7h.ZI91RGr3FGHʟ4YϞ[5\t?}WIcckmghMs;Ɗ$ ܐ$i=߉%z o?YϞ[5Ttω^ִѨi' gVڄRDevoV#v8xG\<"I6F>qɻnrq YϞ[5G% x5HPrMF=Y0&C֛o\]IS XK=Hd"[SB|YB( !Yrr4XW:eu 2A+'?)h3^((((((((((JI<j[ŗ??G-v;Zkv{d,TFvxzu-?NSY8e?-`~vPAO~1Yj kǨӨ1'j8/.TV~21b]/eyBvcM?Js%ҵ kwQ%K-F8摆x2My+{'ڼEBZ>//*Q/yY[xk7=<:4sj0ٗmml%I2(Z ~;פ(WT_=]gt{~s<+ü>΀ ŽN\ۂV<|5Zij{F=?TR<Kl _ {FFBKE'[Yq,e B 5Vzgb`KnmȪ6H*oxK +A׵=KXK woVvp"[fh 8fێHKWѾ"k!_]j5dcሽO[19Um,9f#$W_*_U^"R;k^||6'GCcgiBO>;A(ebe.B}[߇o *S372xOUѲPHWYЩVz7se-nsBq쬆xUvcHppQVOiıE!h((((((((((((((((fotoxx-20.08/images/emboss2.jpg000066400000000000000000000722561362435004500164110ustar00rootroot00000000000000JFIFHHExifMM*V^(if%$HH 0220ؑ*쒆0100(2015:05:03 15:15:16Fotoxx:emboss|trim_rotate| Fotoxx:resize|ASCIIfotoxxNfE~>  Norway 8 1048 808 4 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((6b" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?8Inir{S2$<z[ҀK=*3Z31FSAёBv(g5#p Ϋ z&G,&[MaEo1`r vm)jz sҝM#=s@\ԝIc;u4ę<_>xjUڦl9nزoEZ[{y&͎kyF~QԆǸv=%FO4R=e#LDMRzg2إ#48@ccsOƞT]P!h'ڛyd4I4M)CQ).AL *I99ޗ#֚)xojZE(lr¢#4s@䚏nSP=hc1iH9)!RE05w0&r9SI4JԔ(#84Қ :tqh)=RgҀMaM4 Ss@9l@SukMH)V+Kh̒9K|oEL6MicP~JThO?4Fۯ$Oi <A,."Va8i6i>pQ[R|3ZÉ4\Gp3ƺ!B+hiXE,@I x∡0g#yH|0x^3U Z.2 P=O`ɯ'߉t-D LeiJc?JHǫ:f{I k 1K+8Nȯh)*js$)<H=񭶟mIt!v1WX1.h-uzѷAlSaN'5d1i[TE@00:Tj&Lar3H'dcBƃ ީ2l}s¾x0ӴY[yOߡ XjZS$FD׌Qq3 L5:ƴX[y[85,2GnsL)NE+rG811F[?51GZkpA4pǭ+E5!c41C*"4 "{T$R3eq@8G}h7`Khhi1p387i})A9@wfB3Ld LEk13QF{S>ځ3֔ҼŘ Ah47(2&Z>|oᱬY@d1rHB(u.ȨsN'Rߴ6x-GKmF N9^]núޫH#B{@=BԵ+kxd d;;gۓIfv68\EWb$zu? )u ~6A/ /eOLO)]PЊt gQtaxF_;LhQFFr͏z>89p} :ް񦡦4?:KuMOHۅ(4]y8vXc2I~;x04{6;~5mKa\DmH϶kҾ5߆€^K6(g5sXؼ9s9p[E,q[Y4 @<#z 1>c`~a9WDB#Tݙa z[#IM1s|e[Xխ[MJ/ܤu[ȟNYӸ# y&cX-Ϸip@%zkYsLBdCd5zOÝ^OQRD8#.&FiOϩ_ dsWlmkI&:_'A}3LҮ/ֵK N@Q=3ǮkQ>f_6I qۃṠ{8sJρ֢u';XHqTI>4S֡c= 4.Brr!M.)1np*0}Mf&EWE!F:Ji24n&c !?):SG&}xG_.d|{)$"yx8ԟO_L~yrZ+TL:ty'lWWn5xwözFwZqD Jئl3.$6?vK3T#lf&?{\i6 ߆P`Dnaԅ?N)$i6h iUjSKpU B"GaڼPCGsП`A*(d%5h[h|$Nsޫֳ8#>]9⦲p?146b:U~hme$ˎ@+:/%x' +R[Fl)v}1LV;+ۋeӠqςÃR0'm ldR$&f[9Dc Ԑ*"&hVfƶ$+Ԭ6AX+K<[wq#yB[u9<qWG"PgAecs6]FY 1''=1M13nt1//'Kʲ¤瞄}ijUr`gN 9,5I 󲭁}N\iX$n 00p˸q@!]3P_wޒ2ehWvvggQ3[4pr?Z~8'=$dȗ(#`3vǯ5`f=Fx?֡LI@N/Z iW@ e≌Scҷh:gsxr"\I޲Ex\-i\Z]Iy z`X5 iqKoIM?8j~ :%Smn.JPi~#:k)4P8gX–9mcӯf*zT\Nٖ[<_sYVW+w, %(w#ޝk8Y|+ lgE&߆'̗ w4Yf3S+H+F2p=^ψb]2!b|r(T}힔zK8s|ON vƼjŝ#FV$;R |ݫltgT֮|M(嶪$@?œd4{Kx~ C͍#pn9աĺiש0$@4,j;FM>imٻӭSJvѡR79NMg[GjI" .$[Z\#ak [3}H&Hv7~Mms'2y'=*68 uj DhsQ 4@S.L xqE1\RsKth42qҒ?v(kǗծKg=S~g/=%פrXyF]ZYO9ܸ[1ﺑ[{I +E8lō݂Aq[~ޒ>0>IG;pH^ۚ-sмB4{KXO 0,g@8Ngź3OR b4.>haW4Ma7x(: >uk7zez'Ӭ wrrGLd~AX`Ҿ07Q;gKvӜb!e$2 Wʞ6?5]bh2:J\M fBv^NsHGmZE=Z $db)8%B4S{QNⱷ0=A' 1 EDsǻ[=~7F/AIĒ)8.4솷0?g a+ĚЭ+}T{hD3HIp@]u[CPV\7  ,ð=3Y:<Op!l8W94\yXb=j_Dfcw"<+"4ySg+&6%sD'uҵkm3GKP(^kޘ9^oE5]o^cشf',9 eԮ[+-)tO%z J}׹ywsI"Zխ7S 1򨽐{K𵗅<=nj0UXya$#DOa+NCV.,;S .f,pR jzǸ sE3.<:4Vwk:1#n;w^FSqs85m`'f!O٦*~]B٘eJoТJ 37\wG-㼊16͝B'f$u?ySh&|-^f]FC}m܃޴&ǥM=frLO>i";Y8{z^o.{-Oqx^CdN+NWF#4;w,fIȏ,?v =W/P|eXZi$y 2N*bA:n_MGlzU+đV$,2 is_j> oa,7zzzVZǯ*-,Ld޿8Q; vm"bT!mY S©vcѱD4nƈ{gR<1#nװfy]sGN!$ -qvvuNz+*=vƼ@\ÿ \xUB2nAY+rb3ǵg|Rap+Jvor!>Oy ʞSMSRFG,P_!&"'i=kMr^o-dx`pf[xr,[i'8<+cYό/k%V7V,ݱֵsM[]WύżsIQrxn|B,BX .nNҽ0uZ. aĮ=ʐJA[Ii`Bb7ˌ'>Q[}rSx_.FtudAL`v|;yM<2`x1^*EXٰdOD.O$HF:V9ePQNfm7ANCޣqNM(vo֓8 ~8 Hz׍|PaV G_b:׊jN/4ȱXE&*'QWg;o&$.-{,~cטZY[%HX>b7Z⦴n-fd *؟]ޱ{JtkSf<Ĝ\j=|G96-np77N_c(a8,y,sWޯX+K]׵&A \!TWc_k3mc$Ԯw7#$z5}2-I4f)|y^f8)ݓa@rI/ Y-ȭq$WPtk_gԉ8<Ʀ0 ^^I4(=s[>.>֦ϑ2@?OjC? E[GAq4~h 5;KD5(/ GΟ*oVܻ2}\ֽi i <_^Kh=2)%z>tM'JtTE+ɮ.]2z⺦ *[uWs,0埔3~Z* ̌CS dKOo FG-<ױƽWWY'&VS&yE烵$xY"o>:Hf %l~UψO߶7˸.YĽ#Dp3d,=c#X60l~ۛG ٤E\ې;dК m 4{n"Y0P :r)˒91ד?K+ s}S"[encm2*/'=Oֲqst 1H`9 ;+]z2 `y`tf./%Q3 ,8 GKXXx3[hyP3Pk#Rէeudݺ7M9?(6oyOqduƭsm('6B>{{0IsDܴb5]>Ѵ.hf5ܼr:~i^ۮ(G%[aYأ?l $ZNxq20eq<Jg6et;I_x4:5(0Sӊ/,v=\ȲĿ.܌U#Ow1\hcKre1k_+|3e"(mL }J#Y4dfP +Teѹ#4p1x9j;-E2: Zw˻e!n[ N${7L3LlIQ'eC_J#+", =p+>"|GcF+y !$1f,&Zd(O9r`xROMծ ]7UZW1M koq$:kay$Î z> OTI5 dL <ѥ}Mѭ6k[w3[[.p}umKYҵs FVI':lIbH?k,3\׃i,Hy%'=uM#QdVA=窚ule "?Y? -v^]B%w^8n`;\5-4p8H;@ICqsEetV ckB#9[YqJZn$ ~FkD]K diY+u x<Ք1 O<y{.ya'HlFv09Nk}\O@T+@.Wއi:mt"hC+l>kjNfyˉ㍤Yx枌.^8 Vͧ\h\_C#b6:.:{Pjcu\,BTL!}u{kr$U! S^hMв܃} R) ѩֵl;inr"G+ߧyG|mrO,yJZ;x36cyrȈlt%W^RL֣s%棢j!6onW 沄Q7*G5]-w k  I8ڼNnCwߝDqA^o|3>nsr*mA=HWy5ռV)lѻRiQ4f1(bGy2ybѹR9UƨJcL=OI+[mGL4v7)a0#4WNJ°XOz r3Z,_[JcrdzV,U?4QLp^i 7~%1>IcR>5\UާpMکһU@n7p<5^ 3Ä}R?JuT2j89w42ݺDRRv7e8?jiޣ%tFOe)?$YUc%('j,j ?Zy]}%H%"9 t 8v.NXНrd ɩ]'m@Dž\@Ɍ1l0q?[~ܡ]k$zY _G4 $S'47P\˩@_̈lk>Ć dLH P@3o{XτfEX%+{] ; gyW4Pm**Ao)yx^k8+Q4&?9g ƹ-B=:8.RCaX1]/nhu=xc,QF=k[s,3IVy|obup9뿆,#ErT~oKK]CH(*1+Z$i19V5˻msOQȠL'vyQnl,j q*1cN:'֮'[N|Ú@lcq٢z(#pi򌞵,lU@ O]2;Z+knIu] Ҿ9'i厇[jIM,+\Q'f#;+n$մI:+@r)l?ql$R+b{gaZiZε{/G$yvUp:j^ 4[/"2D]vN5m7č6zWk ja9"0g|dblp!}>H44KO@)Ij=.OnJkwMk (fvJڞ#F-tlS,[hzWj5hR[x?rNN8s(C4X xAtN..nuu 3wc֗Gsv?R[vzG[Zm$q˗p1WivY:y}6_:l1 F+2Hec{ާaoVZԐsWRۇ曍8=Es# Ujtv X~d^3JjA}O+eA\Ffct2)]$gd">1EumK{U9ހ\t]f`@PT7fETC# fBr+ys`;at<l1UKo*#vB7fYh񴚄!3+/jz3i6q ZThL?03R+t8XNkiH/Զ`ˈdߴۗ5ķS#97 (y<[u$X>[\3cIVS8.!k`޻L ?Y9ݓ"qOb;9zqUټ­Λ]C|CUGqP=Z2ۯ!bo'ڷgM)ҭY3#f󻏽5o6"n9Ew$VSOqj >ixHQ~ 3I[6RRk x8c+Nkǣxiun;hDǝ+ʵk][R=喚@Ȅ2kI4Q-QiI* f686ǎр@{ZeL*Um"#5*\<@QqU当1%eCa;"~xAk;֮1DBLd8̉E:+6AaE ;Fq> "gˁ%Af7K=Hv(\Kzg.1Q`Fg]][;x U߹ڳB5'bKq$Tu\U5mKK[&.oHWb(~{u^5y־ψ "!dz$bx"368o:0cԌg)KԣG{m6sֹ/NY-?37:V' R}A0\MvħۜtR&w⮳=meLuSi/[Ki,Y?1,yxWpfvD xRi!pEdÁ Z7Pb /zȦ`ݎᔱZ Y KQY{X2HMvzjݙN15Q؁7 zkpr [챨RNy5Z}/tc;8U1i[q1LW-^ii~RqfX^Ir!F҉>$_kأ,dް,O򑵁9UrIq.!3YmF܃t䎝k09,\%ܫ ۽mGU٦ZBXѴ?nb!8'ҒJvzԞFQ$ =jS`=sUUoy9k+0  v*}dhYz K n`vȭMu{}RNrE,QW'㈯Rhp<c ڵ  Zڭ7BMmVU2gyg' S{.-6hfK@cTڮmcliwdW+]^MԶТw6[ʀ/ZbRPty|PEF9 ¼<$'UDZ}__Ą=E?k_hxQ&ĉ'\/Y㺓Om2F|8+tƲ]߼֗, +#_K`:#*9"hBC(ncfH_PR\l3.Frsߐò-}fuRe1HPr28湟;7u]?5sטb mTS{m#u]WH}vKH#5E%PϿu^"pʙ|'Cz-7^dZ/ l͏,aԷ_׏lq  CS[f[1:9܋&5cxd).?y}խf *$yoUZxyW>!#&p]N+<"<=iۃ&#s?7:r bI<)pH#B:K!tk0+Sk8!N+|@[|zm-8+`W폎?zZ@L{%ǜ4^TVK.ԞK/.I\%{IhkNKHYҥv_? g3\c^+ .5Wam6Uc[>i@ji2*-ZʳY#._y1(o@?+wI ̍c#m1簬]2H-nų6ИczZᵭ5,.Fԯq-P)"vڠrOj$DGfƦ\: j23Xé %빖bEs E rI+AJbCU_FqDQqP(Vy{EG1i06OOcTi3CeX贽lZ?a b?~ikR (KK~bx$F+Ȭsַ-| s?Z6&t)m&UTd鞃P\r-p0$N'nskBxf3Us*q(1=$hcc{jlJWkWgH׃^m^[kcapF oxs\y5% "$ g57ђWz sNQ#9N}hOdbRNނە2>vQ 7 [ȭ6ӜT0>f Uy{ ܸH"Y50[UgM/3Piv.tώ1ߨ?sv*7y`g2[XCw!6hb3QOHo)XiHY]w qT;AA$XpByl8ƶ8$)Z৑gk!RT)eR@^u"RxǨzxO'ܘY9F-y=p!$tTkR큵qS\햘l & UI"5P wr14 H`11}4X WzPDR>`{RVH _B mqL7ZS;f@[9Q>OPF0rv1L~2jw*֙uvMv'9IR@cqipē4@5,|!!Ɋz]枊8IF6Wy好̨v't=WvO,Lց3H) V$ Q0eF[*z`jԮdx\C0,zg 72u{M}fXRݬR…]V.Q%$sQr#6$; ]ATCIܐy?ҕA8#`i&8PyTĐLv!SZ\Lg~p*l0N1QwyQڢ1QuH'qӾkKeW cS c5F1V-w Ԟ޴lVcc.NjW\]oWiEbGV`N^:kCr++ *3Lwpq{.Nqּ ʮH'["p,yG?tb$S@}TO2l{,obld'7>̷9=k(lӜp{qZZo%A=ڨEF6W?o$l_c T-$;B^Z<+ ЌV֣̈́OT5(=դGAj5Y \|FA@lIqB2Xk&7(5!k*MFܷYcf è4ur'֛NH&Vbs}ic|z{yg܎dl[ HyQ<b(;Ur8$OHS -cԴBSY,>Gef98SbsO5PrBJ W=]#fEGI#; )s,PЊ2Xi ͒XUvbnLnA9#^-"șDzdQ+1riDwAM|躎i֋/][)0G\ǽ.P3J23Mm(1oqhfIj"U t5+Bb@bNE`Լ[]J1'>fi&Ŗzi}r$A*pM+K.%.yG 2]OZܗ: >*9'@uz`qX!2$3 2ƹ4ERf׭&lg:HK~ T6R$k,pNR}IY>i51?ZÚKˆ)*02J#=hl;hёAҳ?y6Qk +3^b$\dP\ugUF˥h ^dr+E/0?qhVdңƻH,ұtxjw%d+֑,-e7MrʸOUCnķElJ FJġ W'RYdۉG$~4J-стʳn< Z6vn.͍|K){RI}Yq v/!rrY1{ֵ"7diUY2<|!fHIY3􋳤$a"21-d%QrFOv9ePqǥkAy)`Z$ZCNli)WVlgZ F#ae$uVHd3p͈ԈբjRJ1P״S,'>cWu![G[u?X/׎d|1$㊦%a@7YlEe2+B".Yc[YፈBvu3ff夗ڍI|Z녚). u.qܟ[\KLG+׶E\cfrvI Yck)!2#O*Y"6jq9W~\<ҾDH'd2z}*+s)t*;9Yؿ`lyLIs–ʖŪKc׷,ke-;! |#komFRy.G5 _ۤvWnĹGʪ4#3FPQxc?jM27.Udrr<3ҝڋ1e_I5UܨQK@_ (:66pH$eOjMn`3׵H",J Ӷ'c[F) , g~+jWAb# OJ1eç@1AKt'e gB(Z1iK:,3Z/rNeXQ_iVs78 !y]Ifo+e$j_3n'SgktrNBoRM XsY>n17]\qiXM?6+{{rV\1a "XfG|=jK$E1$&~ii d㷥I 1G` #y&O\Ki+aD*z z Hߵ9RXI8c 5}E c0%׵V{|iloHd*Iѱ8΍9w7_(m.^lI9<+vVO,6zkjP,+$ UX63魥szRyrbQ۪$hc;YC ˃CebvqVII\i8aPbT,r0A&E }=jo(U~A`+NcM,+ݤY`rYC2qxڕIS˅Bv ϑ.!K6L|z$'9&7tcOYy 0J ;H뚑FGH^l_+d sY"B=&\*\nYyq6ыiY8`OsMFt5ʬ 2K&@}(?%H0 7 \|htVE dy6cdQ+Er$ra#|E%Vp$Ssa;VP9aympr<9>XF60xȣa%]ϣC磠$^Hնɧ4xJ :+K2U{Q3 裞 A!}dd*<,&NbQd!U'ҷ^ V8 i7b?ڲc\r2M:h9m;ՋN,>Y2$af7H [H[ %#<I:$ᕷ1$~5Q,LԖexHU'B3㊡*{e669UaT|p~u`iJ瑇rjIm.Xyc#ZY@m皯>\Co3]:TG( {o p̱&rqU-\$5ګ<眞+.! ﳀ6k[!WKw`x2GzMqn?v.9&3>=6YK.U;ťZu7Wq@c޳,٠a*vu3cP}-i2\ڢĪ8@6/[`ۻjb2~;s|r'8&ta I?ۜ%[%? Ŕ\*H?LU1/8R!܍Fj!6 'RҎߕ@BG$WnY̑Bp\ V97.hjx DW2ƚqhkBK_<{~"ngc[E֥+k[)8Z67 qC,b8cǭ!g'T 6dz)XDF,ş5N00q\{E"Qvjo@]T(b'+l 95$E@yB9+BXSBy}ꙎA"&2NhR&蔬ɜR@ jA N4QM%rȠԧ@F*7!ePJ(ҹ !7Ug= (Y$ybIM(>tjk`吾QHKbNE_#CآS|! *xCE]- 0 (@1H4Q@LD)8\ڊ*?!?U>s`QE*NZ$t0h`fotoxx-20.08/images/enhance.png000066400000000000000000000131241362435004500164310ustar00rootroot00000000000000PNG  IHDR22?DIDAThZ t\u2}IhFhl1B&C[ i6IKpHHNir&N `RJ,YX$˲47wh9G3xG烪Wev8| 0M99\P'f,YUU&$&''cfy8//H$rXX)UZj0x-6{TT3qi@tM2~'TUUZpKIquu։ɘbm3332ckjJ,UꬌDd낂|]CCߤZZlWm-TF7YLoeR&Ovq#VAXjj ysl63ݦ݃^i~OO {E \o::NGpn{h6e{X,EXO G 9sf|vvNX/^NO8˟vxo@"&҄ݫv8|ee3P\Mϊ1̙_WJJ>Tn#Ȉ(.&a- ((D,0],mp‰(t(U9H؀?#.xMMMmVvSIەfd jNdb#Nd6z0 ; 9 Fvjh֖ze A Ni=P>N0jih'ۼ('1jK2=Dd)tVNqbӦr@)6(B!K5@"RvB ugseDBedv?S=3gr*8ogOc&xXURvN#1bBţlt).f'nll@]vH7";)N?cRg0+!Pn+3WsłGzz4=v|N0Ia#Rv&A"ՉE$DO9uw"_} )WE eZ| &xb!)|bsss8{)#p8ΦHp'"juⓂ Zв‰Bb`9*mnиd:BQPٳ+ŋBz;*[`w31촒 A!+BK %K[$*5)5@Ve@ S—/))yf3?}vC`"bWVl#&V!J~EExNqz4{_ k gxΏ< .dalZ?}^0Q] ꇲt;Ǿ|(,NX!0w:]9:46vA2ox+c"K@5Q&ppbcffA۝f\d|ZvJքb3,*/̿^oME/㯐[+ښp"&}]/xtA1j,&^ %qfU1R"/NXy~i9Š%Ԝ0#`1 |pppp4.VA¾T')@JpOP]z7b)s!U-.1.dFo)BGMl> K퉀(K&0"<>ԨFd? yEP%'x9'&qML`,ޑIVIowj 4 _\q gPp- ęx8 lq"HbStҸK`?HTSg'˱sRiiɛX˽m8; x88_mkB---Z,v2sΝYWRR\9655y dŮIN5^+|n =]/~Zss~," ^r>X muJMM1ІSVH6 H״jiI jIü0ami2-܅pDCwҜ4\PPuz8$De@PM,h:inn^p2<&I]_){ŦspX|H`GSi p5v$e=v\e'Jz0Sb8uv}kl5@`,WD\4v^9{I83T Jipsڂ띖׉Dac:EF6&Q$l٠;AQ8Pb\E 7T46ZRXCcnvg g?r-c>o9{Ga1T&ᔚY)]z85TGW '1?@M;ҴV#-UE٩(nv6p~l.}G~?,*H1c® DGwF]rNdSZFRa8jX6,NHus';uMa1Q=ީ(͑vBBП3Sp9m8sY{©)6A G˳Szhm&&Ůlb^ni(/=ou5P٭_|ET{>:ZX;9L8gGd|BQ(&TV>D Z?mrbiccm`,ښV쒙A _@vKu 2wtqdT<Ğ$$`e)jKWWs燤Ih"&&뚸2a~S颩30;'o炵֑ ʤF8# OFx )qxFV3l¢BKWW w|WOaqDi} k/JmJ=@!lbb` 3;SF0S2%y3dL2=m(? LC8]yq"?? ;6b/2ֶx]F c/={':ʽg>5}fE4a0A ?aXqS +%̊&`tۖ:3gԾѲMA]4&Ki:ѹS|xFӎJ}d3}gf67FG.[mij߻cra }~G?芠C4ZnM O0W"^")U}^v8 99g ǍJ|=;vJ*1@-4AD **ڀxX|bg _*U 3 sLr|qÞsDG]Z(0@j&afůtini RDݼ:1>.C e'˟v9>ݛ'؟ӟOM:-=ɎXbl&Ye) 0 `L{|2/;䃉e\М|t=$&3i+@1XWo'Y y?s__k(/֚w\Ӡ>w{j߱TƦ)ܖ-M6'M6~~ {MN١gֱkr_V}4_X:Cɣ/pP)Z/V;%l# @d*w ¬%և;=Y'eW=փ,cI>{Nua)Y"ptЯ"eXIfMM*V^(if%HH0231O0100Fotoxx:trim/rotate|trim/rotate|resize| Fotoxx:trim/rotate| Fotoxx:paint_image|NE `zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }Zǀ1iTXtXML:com.adobe.xmp 512 512 0 vxIENDB`fotoxx-20.08/images/escher_spiral.png000066400000000000000000000704051362435004500176600ustar00rootroot00000000000000PNG  IHDR+v IDATx]|UE^wBл `GmUw^DQ:HUz/IHB^^^^^3%![;K޻wof99xH*  )@?ˏt\պza}}VG;GR###Vf9v>t|<<<HD"%h_VVd:&H$!QQQRZ-OR|j0A N"L.Md_]3f;( }E,v###p IKL6tP*څ8_HtH=܃TVu[ϟGGlg8_؞ $/n4Ї^pꂡ6d{X$@$vMCzJ뚫 G(T&Xjyqn!|R1|hLLjXD /` 툒B0vb2liqGIFDPD#X)LjXwA&uK\Pq z-2S(EP` :YD0X'Z;ٚ)lZAK+7.a7Ԡ0Jo׀mDXejl]{Z":ts5k99!k̍[UdKurLq2f:Pm*8 y= K@iq>_azQ^*rC BBH v8[ CA#pG.ݨEi^&dSPT~!AAShI-Vq-0$u(D4Օ (uu6QrB%.@S2g#6Mv]X4(/̧[QiBтH ChRVtEC;ݒcEm7PB?^L \tB?aBw.X(1ۅr(. Ѯf"37A!)+,Z61|02ՔX /a-devb4CuXL=}ZȪ yWCُ]P-&GD`Xzuϙ3璐3%(ipo HQ HH}jܸɠɰYˑ+Sdw`Ms#xjהWB|b |#R0qd_g(JYS'Ďۗᛟ6ю*4Ͱ^1QXqx9NGH*38:@6b1-mҼ=JPBKdwvAnDJ0 ^#!˜Lt,8wytIl\~FQߙÉ>s ; MKHj#SJ 1dKL^FyV Z,֚s/\~m"2}[#Vy ϘgvJ)/qJvشW>#w7Ė-[:w).26 ޗ$(U4ihr $E&+\Iv+մi G.&ƹ+'$$w>Vqt|/.nտAj*' 6RX׸ah4<;2J5`ƃ'li?ԏ {veF=pz;Ծ;*~]o7W\N؍@ mu! q,#H #%ONǐoHam=Hh)G" M"NBnS# Xp+97Z7R< eMAMx'cgK{伾?RE6K/.⢢v.L51ɇ.= `߼pKG]oAڷMVge{'6uщxO8UD\+~<@H1̜ik6pdfZͪ?Ǣ)AoN2#*w>R%=FqFT^4eNȦ6UR~ 1HwCdl0J ؤޭIs*g.HRBqI}0&x'I3"n; Ⱦd0 ض= >1 4i^{AdOGcYr+U}$?ǎ}slr A'Bo lAȾk'fpm]zI$QgF y"t'q Wݨވ?eJ_GJ+VridHWm uQg!,x5d0(pd!ɆT:"#p5eAK+hrznZ\YyT~zu݄@~J \^GH3K8φcv梖I%KKBTn{Qդخ[ۑ(||ظǪ,fRe\=$DD"\_ k!$-+ޡytZʱg<4F.Y9`wϑBd%0g/f2!K G[aTioI3iGivSi&/FT :\/V]WAjJg95a7UA@xqC|>]fJU)Y'mo'[%%IZc=IUeKVC~U`nK`]^J+#'P:vV*`BӃޜ$6˥.gjȘI?[Bdh:υP^ꤍg-#+:I]w P\{ϊʵSofd+DT!=ηbACG+Tۅta[R7duyxΊ(NJXFK|GvTSԉx57K#%8OULwt'{<<<W|<<OVU@':%#c?##p|<%! 0x:?<<W^ Y?W$O>kkZbgڎeoWlF=cN.WKlCFA9J,(՞3|K{s/E|P 0m)cJfF&vRHeۯQrr0 gi>rym5w! )TgGQV#@Ԁ{+FmQ,A9?N%yضe/|{.Vwǩ&:,Z8nAaF9ǜ0n~h$Ճ= 1KDxPbtnglct׋܌+{Y<[{䘕#.#-0  #c/cn(?oYw#wrK&+&!^ eH UC@ '4[+"oB~7tMDӯh'}a9~<)kS%7򷒻 0|NBTi*^ZNb\syps3YM*x}ԙdUU;BgmKJEo&Սoɑ6dׅF.OQVVz.MZGv×Ef No|KPq}wxy ֣Lv(||qصf JkP#X.Ꮁ<ţ?.&cWl&ʿ$;7,O=7 A 6r>y+n3CcT_?ťհ(‡_}HyOnŢ9TDay&çX/2b"6:o*Z|8v1D8xgN;](9>ԄeU]c&Nw|#>6?J@mS'rODZȨP<#i6o+|mF״p  4ُUEކBZ? :<w8ܗȜ_/&/Ra֭S0Of'bvU<&!FEI!.^*rՀ`ֽpJp΅k/=C ŘkXBBO,W1lU a_EAiq)X g_-Z!T}R [/$*":9Qga }?\}O Dv1nz  o"3y%?o U䬲J=l625㥗^ĺGj奄jubfI$󐙑EXnE;bҮB@SSJPed 4(d#g2Z|6|h3lv;u44Q-nO˗ t())G\3-., k·}ΝJ7*Jg7/7zBjR8w"=$)N BrJ:fֽV#'8bS!‚`2#;ŷ ExM^< q9oo/<HuCs<FCo?%hJ@\+Q {ߖ~9e`}CR Eȧǧ\*Aqz|JF 2A)LK~!~&k#P)r%$fg ȝx &F'Ɠ:G39z`zQ}hraD>H7Mم]K,%!$=OO6rj{4ފ]9`]=~xjME%XCo:^z SR&' ph^߮zȳ^c˜j6^; o~u99$ӠL f 1/08ӖU^1 UKj)bCw/>Gl7Rl2\NFpBx [_S  L a*Jk`4VZmKKFrO$Ô{!Q }!*o 6c֭ر?EU'!4HvT;ά7"+CT#㆓ УubIF\X4"?nFDXjQC(Q2CT,])#I!a&F`p”Kه3JOS.! 搐2%&D4q`q IDATҠtP^5lH>IB+ıFP-fÁu.T8SF!H19k nH/Gj "N"Үoȣ>͓+8ؓY9I _C8JPuRfx@X4 8TpN<|x-U2p0 f)Pި$RbJN bi }C i}][<d[6~*f< cGE%,@eq,IK*V+I'F7I FRHDbPD1i ZԬLH7Y?#T Iط^e_PɩT""wFlۺ Ͻ8H6!Qpж2"1l0'_v+ rg'aeGs Iصh6]6T_wA p#%8IR ::]tD\N;:yMy1/&-T~ouUZe!lai[lVRmpX%d#3m&tA嫆7In[iɊ AH #IFxBI@s0!/Qxim1Hd' v/mg(^KGLԑ@uu*Ƕ_}[#%;ڏfs Pb,=6C",/HC#WK2lMv RH!ȮfC^TYGotEm[ YNYHR"R{o s]-F :74zl-狀#XkXc2`CN"s)m.ԖFq:)=1 ap` ,tVO -هXoD ;l%alP>M$/7I.RTvATm-H -tGvW;/Y#9%=Ӣ|Z隄%hY4S HQ2wk [QЊ'vD%7`t23@Ȇ/ĻD \o8%KP`AgشZQ rJ @ 2sdK~a{ 0۾7^\Y]:]|1Sw{?JrN, ZB23^jq>RІMPd A)E H y9ՏS[& fXP2t}w΀@*M8N-$<eV\MB ~dvh ($"X/ ]i@DY *PHnԵ$mau!;T`o|İ;=Eg $6̈R%cX0 4+0[Vb 9bdE(<'MAgdn^tdіSJ-c0?u67v)Rԡ}A*I}y ҝl}<]NGj{j4VT@gƽGm&|GfLjXvĭU͆3m8ɖ "N)0#EfД RS1_4D1t/)@ͅ'i `|C+0q x"Mf%;92je /dĺ@3]dsy uXN{ CR/6I"l1~~98G􄦢 LуЯ^ bj|t*)AGc{lSVMf9h& &V<*z]H ^a%ɤف>1#-.Bl8TS\mݨ MXI}8htu5?Ve`p q-(C1^$Csnƺ97Ymf e~sLDA)bٌIMxp8lyT~ G::ºsI'juBOȮH}H#t#ErvOFԧ_}%_6xEj$TA1x|Ν󒣬7<Vgԣ~m׷:SQ"9H05~Gie5L4Ȩm=R3}a= 13IFZu( ?0>5ʲp-6jCu7dꌤU.$t KM9ݷ'şxmu J mu9'3),NR\TjDddq20<yO(z$>dWz#hS |՟a֬+ ue*dWAFv$+"uoˢٕTĐ6R#3q "(!:-Uq>7Zfܖ=r*vW`2|y)UP3b8BVn_/ᙿ?!4z`֎y C/LVښZho!hp^6N0j8`htz-|y.v>9fP^eDbGmv$nNI!#ujbJ& Wϖ-V)up;S 'xGOБlrKi V0U526>ܟ~()Qh.0RS"qmkݼ1l-(a'UYO+6\T/R+y悮MCHL2EKqx;Tc0G]!m.QAf |I'rڦA Ѯ.5q;3r!'Yk7^@DPWI)Tar&P]M+A6;(nC)N!_]SG!~ZYT$B`Qm#V>d`fDHDRʟ^#P:ˆiOecMaQw1F$*&&TFfخ"Q߳ټ̖BdMHd7a2"-2̄py1㮦lU\4f@la\QW]d/cc(:'jk3G;(;:yRON6]V)sEhCPG$a$5REO*% MQS˟ڃIU*zӨ {;<< oUY1 ,zk!T鈕ԮA4`) Nk+P=DB'nĻ|! v3|-L*j9U!l7*vj`Xpn.soUvoנQ.k 8K3$5 +ˉh >ESY(/.= lC56HBʍ !E,KJ$g/c}m#B,?FR6VBk \du B`/Ww1b0da hhU_|l\C'?|<_翍Kp\]H]m* aVɪ _"8a&GGG Ugi <<gD'3,dYZ/7yx: GGmV[&`.1;9,nwؽvoBpMO8Pjȩ&( JFmV-YeDwꟂXDjr|jtN'zKpS{ar9PC8qh$Drǀh0%!IyJ6GG!pFVfcӲ8wほnnuT_M6êńC牳@n.Lѣ8&NDxJOjaI D䔑PIF#-YCPQR\sX^z =HGAҳqk3$uCN=K;Kn5i vUIG"545J:X SN"[_x-+bq1$pj,YHNAzJ"&(琮ME* g6ҌJm A@*깃=7(vN J!gaZo%6GCgo BN"#ᑧ7{cd&O_ȑA N~4fek>k3WH".SPj (]VVH_v":m^b zF :jX_`#sf1`E$= 0 vȭU9fseE;tly8YN7tnv`d`Pbđart h2w)8< !!r|rC_bpJemlzj"(țW#wG}Ի;c9d7s]ֲ\  G(Z!JF#X_t<@.b3<lu%&ʊ=ݤCUE%`l:NvpxX*!6M&r@I.3ěQc MDy(^̙9&胬[h!șZGa8&l~B 7w |50ӡdg{N\Nz;LaxPyP܊o~2F2(|8Ɋ:7^Xt {7eh,>-3)h9m@BFވ+r#o8Ib=H"?"*ۧ !FCx o=STD,n4OYkɄ@!"'|Q*[ p7!fUPS ۖlxʿ0 Qn%v-%ՙ(iti>O=y5 0i7?+g%+aX7(M֖LT~ck@Jq }_u`O^ -)Nle'@AQDXgZb~NR9V+٨yJ`RZpD Nm`%rb+N?u/_WFoRk=]OI?V >jSdGzD̥=rp6To>źϓCS1T#@dq 9CtZ'CnI 3 ɱP:zֈKH.f<C(U29 NE' 5RT$ME!3O8aIPrg.UrG85#<'myϿ /e5a3aHz/8 3 qG#yES|iBu.nƞ]ɳp W~wCQS講7*'"-<033?|rʟBԢQ8tvp׌65ĺ\ mx(E)aY?]UN,&x" $9AB>ج&,DPDB":mQrP` 7E[9lQ?"I$yfx W㨬ӢLk)V u܊'A!IgZd ^:#?gAdm#=z$"3ykKCzO_ʊؑ}$7%x Et@Y>P*u7*џ\09dgvx'iܸ7lДTfnznVžN^c-@]gFn^>.2]\T kz>6r/"*C8N  SabX޾kmJ!δ!tJdv/8'u#w*T䈏 A " 40 UAzgG67@'t5I":dڬ-ڵcJ8]c ;V†N/Pvh d(+)A P\D񽡫2t⛬5|gdUv,vDQ=_i>|2fy#F]J!d*?wRPqA@n;/ѧ{Y axo 4G*NJ#gD2CxIa>ҦV{!2<$J Uց^_W mR7"=v%b1CuuaA_e1GIY<459&ufCXd8L 0;EW@i6A*>yEEh4Q AW dJ%2ZuŸC m,_{f?p fL'̡m`X\ԗk)F8 @8h󆛦?ノ)&`gMb#l\+cbsI\RIb*L|Ri3k]?)NO@I8R{}dr%"ET~`3#I)׻`d?T /o; QD@̈_ASzES6YΌ9UgV6DJ+63."GO9I##Щ%KvE( 6&}۟wN9;Mi;iAVm[tҝI v?#paduaO\axώGGpdu0x0xx0; C' ÍGG #ns系vh9bO:]xjFFCc隺:A2w2oKMz= tR'#Q+&K@G )/VǎÓU3*:z)]Xk\hɉk^Vt؄HD 7y*Eaq=ȧ]9a ~ IDAT }Vr];ePlJVK?v憟 Fr\Jޙ#0iixs!PG~VPn' %d>BKb ׷;Mue\|2<fp|PP>:Cia.s  aq F`h0cȶAoeG%/XjtMh j0xt| #gd<[9%ˆ|{_8K(k UFaJz(9-ф̬#o q7~Z+b/Y՝-*3gߎxq§8s2 񗔨X%l6>xX6 >2Z#w`T:Sx&r )D!#L,MZ-q0nZ -g9D9~McyskJHqs+W G<+Y N xS#zO _f/9 pӈ:ʕpx%&'9,>z7mFٱJ`;pPgxi _FbDvJV-eĦBzJ$FCWPk\C8;&"5)8Brح?J@?9+Prw޶sɡ_9g)U;ruh< ZYZhMBiD9f@VV\")o0g&Ftsp۞)0UX~Z%958H€=ȳDZX~d >g"iBRFY\>\ ڗ{LUWJV\ A߮ucXIY<+@DվaQIˬ1 %7V3)#7dyBEyEȟ+@&uGy~&,߄.i0WT%_HH  Q. fe.hh/?.10[<׃\0Sm^{3жM 56oی?xJ+"= `^"/?:pl`a!裢0٘4:D,[%l< -DAS߈[f$,;}*BeɪyYt>$Y jEnI"bPz Qw Nֈu_1p0^k2` zGAfȃw`]EuJjxQr,~xf݈D\},lDUe2lܴ%dП(RRbŽZ= $hVaW_A#M?Aq*zZ#~ܑ8,d8fdcoN3z Q WK6!) hSzF9FH#2*#yYkUG58վFh)/)9IV6K#=QU^_< 5ڍFx)$4 J!DDF^PlAJ}W=:58W }  H:&EppJGӡ݋?jЛ,x?ϡ[t%D m.8Lj&g(&^ \nF~<,f.9g:7)<&H ?v8W S{6x(t `Xdo$biK`brsIi.g fxRgm~ഢxꩧko畛=l:>] 1|RfF*X,%B v1w~PIh}.{L酈n9n"&p,|}T 0_1JዱCcg~H"P[^©+<>c7@g?}S ypm㍉lPjF}w&Fڋ dt!c{N n[-E 쨨Fdi8XcPDQZV‚]Kh4zlD.Ee5B̸1ty\yϴ%hoJ࣬d'}'! UQTb**{݊m}?mZ>KۧW_uU!b@ }Lf IHߣ0!߅ɷsνs=|M/oo@1'9Bc.dF.Gy7^~e=6C)ol!ԦpnC郏?R0Bhɛ\|"m౺dg;PG|;,VI>UVIYf|+ҒZzc+ON8 ҬjEKarGh/sᅗ"6"" k(.:=3Ie5o"'! Y @T,ZB"әO(O÷F txp˭Ia=4u&n-,6$3 eK.s闢!%=xߢmqR@]>[| Eʡv`8ǻQޘ0arS ē_KsO0q&Qا,T$ɹ a=рR@g|xB+7*;*o 0xmyocc fkP`RV 1 a`_épAS1|aU[WS uRA"5%eȆr + AC(`!0. W _[ƀˠA!0r +X W@ N txbjբ/qǑJя#{:q$t#\Bb"H5M7<  T-r3c!QMbcr:UekEB(.blVp \:Q> . . *M ՘2u* >:ΒGOJ8?4Jlt8'+9ǔeEv[FE Iw a^a+,4SZIkT$|nvpK[CWXlmgfezU"_JIP8 y Ry0f5\"< x2~wZַ|) °M,pbbq;x - J>YO:> c)xik~v'q$6ђYFqx_BIѓӇy%\q7{2hBG{bd  1S 8XՆ.'@;}ԾqUӮ{pe ?_; n~W/}w){=^e?쳐ȉ$Zk1E(\%DGV#e1<~&I]l86|y'L(^١A!$"gF-O\M)NjO֯ū[k DFR,*`r݈ A': *2i4'5xO>CaeL}W;ـp]KI,SAeS ? l Dl \ Qk:1$N1r*`iJ66`[;=yY8~7(upì\~ \C0'_anFɅ77>Ċ_/G#YJXȆQC`1?wy]'?Csy\!9#5(4Ǣc,ه'Vi df3"z3 c?u5᧰]}=[Xa068| ,Muxoq SO{(_ )4? atS9cݱ},v,>k.BW/iy3&2AG wLJ1;ǣO>DlLwn4Ζ2xh%Bㅣߋٸa BG!\,7- XS^ ޡ+"5xUnV#e<:nŃ{gPVYºEيVHΡ3[VuF]Uu^F.I_G~$q3jGB.7ӷ ҆"GH((UEݾweF9q8o<O˱`N'c#hRL o{ g ӎ~k I/i0qu=e*~bf0x}G ~xVT#h8z7> B^X:{YQ ^p ΝYVG45tX8 *Ȍ W偹]]}EaC+EK߇PJЈ@Nf9H,1]"b0-_};_밽A;$n6<ݨlCSO4H3BGX70ޔrls7R~@[혟MUXqSLk/>x4_{-[@ZbnjamX9;ŻΛ_2Z-"qK0^) DM1ʙtzV΄|,V/Μ7qǯBuchgNWu*ن °s3h#DDT"=߈Xf%?Qq99sg~ ~tݝ-h/FaI)P aZ128ίFj>TIz—‚+〓>M{PP܍\1>hqW,i(.e):&)S %IϊmdD5@8R}h-߃CsHZ}Ci*^Q E'5#7u!|<4<BTDGj]hc Tp/8>af`0gdEز1μ!Ef}֡z _K yOiyU͎v,Idz{ar#2ЎytBSPTR",0I5k pَ牱0A6Nر},T DDө4KHh tvS\jy]lt<^NSs+KnV: 6 > ǀ,HG@{9ae2NǾvs5ČO4\T覟VWjR΁rN،ȆR7@|kpe ^2N˸i IDATfL[ /f監&U^(rR;n9\խ)skzjyҰ輹9? ?ks)喙tЅLTwvSCDG1gϚ5؊.' 'ƠE5/ʶ$&jz : u a5TMI^mET\w9{Q^vIɈB*#@$ZxѴ-&΄=MפI~1jBqI\f'nOTGfT8]w^kܪWQ^]<y 8jR(NdHX{<UU /!QcMh_Ҝn NK"qf:#Ihk [9qU(\_å5xd"*_mᴵ0#_sW _ٔ P:six'=s"8."h,޻Vj3XᖛCN\p\|Z/c1zE_0T7peLJ>9؁i98Ɉ:JAvziܢwm_RHV#QeS<@ 6rI:YyL^hZ*^m/{p֢8ĆozT*S\WRT>1HJ7gش/D`w\(j J*qoW3ق0+şCzq7!%>y\K6~bj5"a$#K8;عaVWTL m:gL}6b-6W,9 } aKr(]:Q]>hĽ 裖EQ{+zW %su@sMi܀<,>؉U"42u] p[5.Rrf㢋C[a JѮfB0l i0rszZs&vp[z h倃2Ò9w-; oLiPԶ]N..dO7..p:uXHC.~:mSv^f%]\rEUhx4^;z?܀r0sm? -lL}v:{w'҄Շ*Br$\{m?|}҆fg8V 'bry$ 7RzQSd]+ 6=j]W9^E'wRka{QãEyLz)=tffgTDX5Uă? {܋p,\Mr2_&|z bJR"#eӏx-bZya{oz}%\$;~F&1BM+J57a5JPxHTߟ)Џ ^&E/ݪh CB|Y@EYm]D"lCL467ҎC㶉>Lܟ}bND I\Ic{nNRzXs8RW2?+UlsY5vt=55ۛP_vښat)MUU"+ !{#儙gjt aP}t0q3Q/"GG?/>E㚚V% xxdõ,CTs i 'Of9DDaH.G!-+ (u"kdLtYIޗd sD֤\JOˆF:i4bNgGwQdk$PRk*^:"_*ft$D8"GO$)Fq״:R9w$!ge(-i0M$%.M_1N IĒ)BX0$̘E$DV Yddd!W/m4f ;cj)vuF8tS!9 فxI7z??H_=$[W(̆!eu%PtʣuxH/4RGK\(#HMjqZ_;G'h5yG0^\H|P}Rƈi5wiNq~0uM!(4;SMg𝋦@N!$ ![rr͹tyR$n|l\<2=X9Ҵ)mIgG"&&ZiX ?Jښtvt"QtA I$ChkmAԩM@B;$SvbN}ϘvS!dhV)2Nc ig!$qNVЛH G.b9eL$bC0n"NK ^;PK/dn u55{.ŽZD;|-eLn= *(ǜqxNq~Q]FnI)-)V1_gdZ'hBV:J:%(-9j0NC+ )xb$~("v@#ڤL+H]5Lk%޼ĔO`$"6!Sd_- TߦA#|N2#^Qj0N2H[@=f E&:xP@C,:32~eXIfMM*bj(1ri%HHgnome-screenshot0231Ơ0100Fotoxx:sharpen|resize|NEx01iTXtXML:com.adobe.xmp 186 332 0 ` `zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-20.08/images/escher_spiralA.jpg000066400000000000000000000626031362435004500177560ustar00rootroot00000000000000JFIFExifMM*V^(if%02310100Fotoxx:resize| Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 8 492 1000 2 2 0 C     C   M" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?M(('^Qmf/$8TEK1z7E>%ߗF4!:X~kFԢ^6yoۧlԑwz&?B4N,{-"jd} m[o~W৿~$$^𽟆-_;%ʣdDg}*_SzR*ٝZs}ᯄV+x(Y9 3O_~'}_I'TQH\3C)n  ܭ&\v5K.}82mY7 93sQh{KP;*.v sЌVnЌ\K/K¶zI$K poY|"$мqAH59$W_ t_H>,7Xv=Sں Oi~7P"Hn3Rƚ~\~_dtG8r?`IuU{s^xrpv!=D:co}>{UR`0ap[$w#y9ut{{AÕ~u8h3z@ԼK~_ω#M ـFE~%xo~ 5>%5kp%HFXԝ 8c{;*ڀ=[#ۯǸռHA47;9"sڿ94jt }6`9b9? d᷻LhR& ɤ[suk狮ek;}2,KkɏmU~wבjϊG@=Fk RXAQ(a0_3l$9U#vyY:0HԿg~$^*ci&k˩'@Ms[}B)E湌F.I ~0麆'm CNc*.eFXi6I-ԭG2H\x;uҍ;=sv]UcOԤխ~oZ-cG̊Xt\ T8I6FR\"GÏ5x .Ӝ}!: VER7*0^ sŭR$q1'$d{~t$'t1[Lyz~QNQx~Җ͐v$ߑ"ú5cj90ˑ@GQ,szR9#òh^[y$qTF;pct&(j/,rm;[QH(__< }7ڄSFtCFi {zum%xdMXtod'=q;fVmg. Q{$+`ѫ1U#?hLtEfX̷FH0yQ5]9|xVs[iMra҈jӭ5]|=]~Y-#G%wg-o\v_ ~#h<%Eg}O%syqZphGtEfF-.e6]I&|Emo ԯgk›s*hOtu> ᴆaB1#F[p2-^_"DH%0 e }3k\k߆<$6vs\ 3eq {=)4[/,淟Q=)޷q>ξμa5h#j\O+T|ߧ>4L9z9?^ û/w1j6Y0#QWwGzg<J"݆GzJ%[+V?f^:%xKK/P( v ykI FKHh) -|qV_GMT cϐ; sƒ@߶ˡ͚C7?Yd 8^: +ÿ O3x⦡s8ll3v1y w½KԿ >eCimjY]+’ns|p'<8s̼[VO ]𗅿j;H^/,;W23l_ 4j^HvTQNhbsJʱ?Ǟ(E|-f} F2$rNGl!Wmm15 Cn,%pvW~ j>כ^V,youw8? iIkڇ&Į[(OD<'p7INȯ½+VԼ#{g[3il'rۿY>Spy\c<`]O[᥯$Ai֗A#er{Bi`{QL-g7*RVPCypG2%vO3Pĥw8 '~,~'QHjzmǃ ~0qs]cXl4;|mu_Z}{T̰yI$rs׭}K j-a5兤7wI夜ڼ6w͂\^I)[Y!2&1`4y q_Qxg3::\ HoA^FNF9S=K~i[i:ޕku2—|gd**N Ll3ǥ+1{[@{<$`W}/>+?ūEj|?Ȗ/j_29q i- 5ևfI[=θ7R5"D D)\|GzktӶڗ_Q+|G%kEc[F3$&JoX\kdi_jllWEHWvO-BnKUY"ai7.o}6RKp8?ٵ<> ^V5z\-41зt8$~5 Icc> giCen-$WqT͉ܤ15̄X`G >{ፇ4;Kq-qG_[ۘ5+>Vk+!doYC⹏.XA skm$٤}%, b>cˊRL+n7o_ !to6^Y0͏s_ k[w5Rw\oliisrp&Ia2M#vOWuQ_a+W&?g+B7ZZC֬~-|M>|?|UJ#Ӣ/<1́㞁>Q`%xI?ٗʽ cgI5CdCqָ/iYhR%(a#?{wp)rBgxGRj+Tgz4fּ7qxBݑ\K `<0Gl>Iѵ[-aqqe_R;0 8Drsc\wt`cA/?3;?+o`9sjZ2yPO0z&uiE;[;mzifƾYf> խ,·i:myGڼrx|Oku=xI$?>P95#+]{g%JLc=I(p:)=ēz#?:ܬgqDmpfZ٘G_|tM{2Z?㟰=ۜW̍OnG^·~mFnTF#0 Hu6u}&Uӝu,e#>xPrJp AaӼciz7]\YJmd9 "Y yuǞII43GQ$|J у8X$sQk[źKbkmBb,Ip9uzU|Ahhқk;&ʵF68 a<;['DPJ`9i>p+N:km$)=W ou?o|9?ŚW)yumqea g}CG>범ԴS%ޚB,$=~];2C>3֫5-ppD|O'+ٿ[ݮGT?lfdu7SE#9Ki0~l{|K5Z[[xоvCGYWWkI5<.2#8N$Uѭt9glu+;hbԄWzX}w}qIq,ʒǎ?^o`|'͗W:w4{uɾ z𜒲v]ڡ\aU^vS4r|y o v,-g*&rB8Cm_KSծJ~ Q\.yl{A gk:>\/ iZc sgt]FpH %[zS3ƍ HbмSq5FaM\X +uw3qk,.r0`GǞ FO{mւ0i iqP^Aup\)wsJ{NY:sho#@g5w;Ń?  ͝SW1̭q۳&f\6w"[];.a D nv7Ԯ!Pck:궷a4*̲mܻP+4CC}iwq4B/Ls\l]7Wn[\:&5e;ֽ;E$2 zݐ7<3gxTİXɖТ)]V H=kǓ y)Iיʻ':}έX]_II1-ظv^)7]xGi [o1X\I}j2{|[>!E io2!8'Hboxƾ5B e x9d@~T'xjR:fPyhD?3>( ksƾ(nnn m8wé藖ohe'ΎhC ҕh qFy ' 骤Ivw}MԖX&80 Nr:c Ԩo|x/O㹲셗tIxk焭u oR̷wəmcfiv9 FvrđJ_cwkH1mgmTeKA1M=QmT1i60zb+|'$^&+.vH6@2Ksr3W;oom3uu$On/_j`uND\{J1>oAL_̑~yed$ J+˥k֣d^^8?@?:9NRI7Uh7 K˅^)sz2O &%fAё 4HsHT#[[j鎾\t?|E&Jf9ac1ƺ z燋5fqZcqr着7Q_Bx:gm5m(E(2o#ɟu56׮'Q)#7A=}+ri_ڲ鳆Vn,@T P9*{=6ookm)0A mbN@H/l sS9 *_h A,RwҲx=p3ޯ\Լ r)[U{[Jr7*>SHGA\i5:`6Am0z`R[i CQ ,q(UQgx[>3ޝyЬ1#>L= xNR NQDOueju#gE?B6cm0'#Hup>Rs~hhW=t\G>6x+jhm;-Kp2XWBsmcjr}?< u /Gu_KYsX/NGk ")%1YOZD-2;Hy_I#ec[å$' h=nsiC!3==뭷!KEJOKǓM~#i^)h<%߬庞gC ILW3{s{C{2km(!ś'51k$6+*p99lsSۚx7T}%ث ȵ} > Jkgix䳕;zWКޏKQ-F,"\D{-Li7ō3 #Mm1< ?;k c^Zݞ>i;+ZmK?FF'٫oWUj&j ⍍>&)H %#*.^֢o-"!RH8'yu𝷇?jO jn($c5?o#4H೎TqV뗉XpR7^[{[Vm^863ssŬLmbjẠߨ vGo>(|6 F/8'Uss/[]!f]gy xFwd)??n[TyB340j c܊n풎Tԯ,絊JR$9cDxz 4UԼ1)B D}ot/7pd/`y-K [1;KΫ M.Y7KٛMœOOy#?Jb"rLGm[]gDѹ|m&gҒPkI(qoNz_9i_&YƱYM.o Y=t&Icon|;&o*YL@&S_b}澫Y>&l忸 0^iN:$e0s7''+|BON:'p?F?5瘕 mpzlbqYVwj5K h߃a7i=RIIo,\xϮA'ޱ-j#KQRP]B8JG:r;]z~R;-0/+I#o\>V? [}PZ{hl!QTB#wt9O7R/g44x@xzAKhz2M|gU׀ .qxnu+S3B 9< u؞ֹoMQ/` R\ܸD@=I?:_Gj\xoK Nholw3 {F [i[ڨ8VhPga\ϯ5V,V,j@{0)~߲.S׵Kh:1˫ 譃׸98oڋM)'_?:V撱j8& /o aQ1k|Cqb1²:0rG>%%y~Uu=vIDjkۗN>tAH[ Yfwծ&-:#,b2=q׭}'G$5FĖ14<׷7VLj`2im68xgF+I DHtRπ;cu>6J<`$pxk-# v[0ppO Nz7xX5/ow gfT +99<ǩ.OɌ?g #F̛xqO u5mj⹡\--ɰv]ѣeezSB}KıY\ObmB%G (\=s!"2s)ld޵f`'.ŦagKdW?2 ZHPay~,c!W8C O~sSpz#^hWqk}GR8? tbc>VkoĖivq[Y}s .ڤKdqzaVOv5hPDGl|x8^Cq4-9!]^Smr07Xߑno+.43bJ̐J,1c9A\qZ2/`cs|߇7R.uxa+n/p,@rHO(VgeZAp RJ7\_G lU;$nkoS]i4(tkSPKhubN}r’\Eƣk߂M9~ }(%1AmOl ^IŲFFU5Y.h:aWO׏uŸן|k˻XU~WELSx=#Лrm2vHC߃igmtZ2TM ז7HYW0t5x ۟ 4il $3Q9j #8hjqMݓAjƑgb8,qɦ a;OzoOoz cibM0R$QYZajCj@jya$GʾrB+CNuͺ7'~hj .%x gO Z<i>̐\jכy t+oïُ:Dw-%w|n~JvZ]:i= ;KXmDpĂ4AT|Vf?ZCj::hK1]WgɞcOB<\dup?n]A;+]@ xd(0 {W?dML~_ZKlz%4Zs600GS@MsTm:-ŵf>lg5s7߆z5Ѹk/HӵZjhԠ:+SF7vmvyd6%dcrs%҈[NVg^4l#մŤ.&d(bPFK?$zd>xG YxK3+]J?Z0V0J ]ݒ*=5 C&u:8 7mG~P^xvLKeM*q Y[K(> a~F3hlX(5 H냟j.o>}[r D;ӷ "uKx|!E˥}C!kyUR:6^y_Ԭ_]E,^)6[;<*/\h~w='GiwYǪxoU:}[BYg܀JaId(=ƒ6c#+≆˨[hYa:`_˓Ьe~]+.'ix'iXDTg/`*먕^xThfҢ|C-;4$0x`kv+4O5|r[y9;9ջBI"sm*FСҼ2zNk^]?P5t)fSrICH2rĜƓhjH_gg7w3ʈ8{S<9 ߌ9źyp9Pd)=[8*U^p.&;aGEC{cKAKҴm-tuWh٣ apぎ֝n},s%w|Eggo=+\Hͼkq$ ȟCp~C#ZO=y4(=X ^=}^P{/\oZ2Ers9SB\1= &Fݤ1ծ-8&]q*98'"nd\IxxF`O^6Bi0Ɓ6>/_ x[խZV21)'##iʜkFt5x:]xFkO S;0] nkH#25x/JD&[Ɵy|5M4|.O`[>3tG\8)jjѹ2w59-ľ !!խLI)4OuSWj^m{ūGhF UԞ@Fk; [&ߎ>>DΖ %H>ο!զ ;jn}ڼ2YIs쩏;z-~/x{6Z׉Ml_y;XTqvW^!V%1W 0i5 Zf6v[!T`rFK8uPA YZYqXAwkJѸt\6y$ {XۂGSLԭ>ՙI:%O ׸-|=[JKmeAG~f>:]5a-׈~+ͧ^C~rT`?V+DJ06IJO8#j@i9qKu b0zb9'z?>(k!Úh+w5JAubzcz9kgz:غ o.h $[$FQǾN?3N )-^5듨âh_fօQ76S1ۀpkuSĚ&{#ϦOOGb^K`` ׼I&T(|1Hʁ(FQq}\ 5Cyuls{·eAs`gFy6%⶝Z餹s,jFQԅ*?/xl/k30% "WϾ)5ލ‚%EogO-]N>Zo}m'ῂt_ h }+JK[tvO<}IqZUǝtUC$2xRс#&=kSw_~жAc#^62qN=A֋-? ruGm3[H褮 #"nՁ۱7^9+Osi{m!ì}7kŦL1@e7njYu^L| -N/O/z٠QGL7t fߏZ_^[_cJbC䮹1Z _-sKY)ok{ у@5Hac=KS'^>b=?? mV{ {IbaTOہ].%&}/Uj_t r=P4 TӯV^xcx6U*YUN*^][=R<]~^:ޙxc6-yz&1סQR|3wǑkXW>F+u)T6aPpӧҲFZ]R{{{t0G?69V+zU >BnJ w͜_gn:*+[kEQg%Wd:rIltEÕoً3W5 ݗ5:tǵxgo>:YI-Cpu=zp_GK߆-[I`k{i3#<'?f{jngu"IGLe|>N~" iw) b+뿏1B;mE'-#}c> ~Ho?a n|;3ԡG4tD dw7<'ןYb |Ufn>oLH#t7Hɻ=EeF(jp> )8u5 Vr@n002-#kB[wo3K$6ɆGg X qQ (nVxJOjm/d|E N7P@$v7W֭fK'ކ6!0q ϵ3^ŏ Z= \C) ?J'[ԗ V"uܛ=5ԭt;qsǞԲ1.`pҹ;_3ᐰ&ocer8 _߄s/k\D#j:eЌ6N+{Yw2tH6Ԧn*6֗B͹:F2i9f1 fl͎Fv~eEjW<{ãW@|>Eq1 ᏄX-ƭq~?&zǯZ;HҠGeñfYmWA!nO'm{? ç\uHI$a8oO=31g8X.rN2?WVo{`/5 L6{ .#\8j0ӟ<'Z2L&+φYhH~y[/o@2 } tM7 o$m+sd7SsV #uyY$S [:eD{1P-r?6_t'&Y P%8UkT]ԟQ>gOƾ&o>YI;7` =2ke^3D2 4*LByo~Ykk F4UQvEAX"QVH( 0Oxkdz0ʒv7Q h/_ş؇Zg_t_K%1qZ ?hS-fW(ʇ+ԓXF ~IoX`r?h&/ïsjsI2XǾWhrP\t6VTVz[E7p&9N>ĺe1C0{Ud}X===oC([ORwgQ͏|yCT o|;t>Wmxs]}3iyGo&9V/`8F:u5I~ RPӀImn7Q!+?tEs^_FL?&GuE k),ȼ(nX=Qc+CM6]!ӯgRV ?yA >!5oi5iE6R߽+/'XWHDqGcI<|uhjZ}>ųI]X{9Z.xV67-as 7>@d u\jK"v,#aqGj |2^ֹ ӡik͓;>}=8D9t0sIm< ڶgA{7`E1^{k_m毨\q䝇T1"^ ~7d<H&PvF=7σ7<z$g=5ޜ?\[/R#OrHJ@? kc:6OovBeA`GU'+oiN51m/,]<c8)={W~?29o5m0'a״G>qǘvAϡc7\WǾ0(.g;-%E7uϥAϩ6}|I<F*|ufEL5.{GhY`1`zϦk/ 1WI?@vL)%OQ8V_^Sϊ ,C]n?a(It'; y|?sua, FA3wXl u m(oq`~^YЯۿmW;pAs6yKkrtقۘ@C2 1e xN2 FNj'&umkm4ƃ1ht~T4ko$_!sHepRj^(ӴZY3<,HlN}>P@Hods6cS𽾗ܴyP. e9>a5 f{wRŗfr@Pf8zϚ{Gǂ {SX!@I]Gb/?uͽM{-Uloal;UAB[4q/nSg.KF@3nT85??V6?Ҷ H=T2q{o_7b0}Y>zmܾ1{Wp1]133#+x#HBGkQ@h(QEQEQE.7q^kpes+I'\\ڦ4Q@!|un6&5$k~~ H+?U/?`Ot0jsby.#E{捥hl6z}Clv,q Eh@QEQE֊((((|u-E5x ,وW?ˍONѵ/5ٔH`QE5>%w2+ w*3㨵777*Eo]|1!W5ݍmqM}&Emyqᛯ\6K"C`hGxG,<7Xhv|~!S~h(((fotoxx-20.08/images/export-file-list.jpg000066400000000000000000000303671362435004500202430ustar00rootroot00000000000000JFIFExifMM*V^(1fixgnome-screenshot0230Z0100Fotoxx:tonemap| Fotoxx:retouch_combo|resize|sharpen| Fotoxx:retouch_combo|resize|sharpen| 5http://ns.adobe.com/xap/1.0/ 126 431 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((s" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?6[ <#K/4[MCUKv M8P*q5#5vJ6ϱ':vsy0B~Rikqyhe}c|Tq/4l5xw@t=+# d"F5fkX@\Ѱwм9 3Bīsy) G4ͽ-*WQIJXf{i ZGȇ V?#H€#.޴ڣiZdv:l fV Q8EQ3Za6* ;KusÈ T0'_:MɶouU3>ҮXZ_Kurjȓ*c,@X8GlQKU/m-gHʁ#`|hR;SKg"c, u>RS8>WQl ZE=Fi((((((((((((zrzW7}ig|)NK^!y ǯ"H ұ)nt;}4o,֐[f |d=FG Z@yΥ ?[]nRF}[CR'YX#~㌨hռnƿd`-MC #Ƈ=IHm,\#Uq4[XZZ[8S8F֘y֏7 ׳-j{y-uw%eXEnJ 'vy RMJzWMfo(u$K3zשz\P8 wKd{2$B=C0?+ז:'y:DIǘObIfhu#B}.%ΟdѕYnJ8m CimaHS.1ҜІ٘"m( z)-,yTvy^)ԢCzրUL@3jk˝Z.ŮuMB 3oc JH;AʞNEztknVWcԐF 91@nu+f@FrsVMwñ^zA~׍FhIDVpZ56VqY޷Hc#@DK[+HQzp"o3<)kW2YMr\ Yg;_kk䅍F$"{/w$v7Q=*Ϗ>Z)$vҽoVbo*$όacNY+ }h)QZVvvQdZܖ,_t|<0o^-T֥[9 [ar?<'F~Qb]:Ha]6"( ǟ0? t6w6VL^XʯPO/v?{S9J(Q@Q@Q@Q@Q@Q@R((((RK&V>aMŲH@$վ+ މ41Op3HF($c-G#"kV$S# މ4/2އ5usu4cce"hc-Zq֐s@YYдZeLrk .*rd-{åiע cs=e}og A=i@Oud?ހ46o#cf2z?]V_L.immg=yCf~~wY=sKmk5|=xGzz%zF>+Gz?]V_@;hYwee?xzx]V_@\Fz{Y= x]v_@\hY^%zz?]V_H.immg=yzzA5eF>]V_A zFY]ٮ߫/GvY߫/G6~^Orz_G0%?띗#Kmk75e?wCf~ z?]V_@X撳Ɓy>,3K4oRӵ)㺒b;. #tҥggEޥ%3#HQ\]X^1@X>[Ҩb]ͬ߻O1N j2kzU/i)nfݧ.[ swC>~?$>i]\>\wccˍ=3= o_w\n:K@*NJڃ|/_l_Ij7y_wnyllAk> Кs#y&1rst:g' 3f<;=+A"21TIBXnޜ3}7Z-do^}5ZE՘9m^SH{纎KXVMQV_m~։d zR\8E%@98)" /x=ldx=Pk"5*K=>\׵hQ2yGI߾\qGH-Bo}m۸cӊ-Vgi<K -6EA[,V[iKo$b二^5i1xZ\[I /upČ Qӭ/5[ F6k0@ .T7_אI#jcm ouk0+=stcHL@ 0y(m+pz}.U[%[]]GK da"b9ۛ=xZvcjX YXOK$xK7pFGJsظVZM{&E8M1qu|'7;My 0H#)'g:2Ac""@M,< nvdq$ܟ (vG=ҤO.k\yԩ ʯ8݄׶w7I>#$NSqNiui"߈t?֮o-lEu;y9oJ~pQ??;DGs`y=p(7.oR[\-nd$G%r?2Zޣc\ӬxkUwkV(>`y'i9tM?QX/_PT2d7 ŲTxBLl^IP gM+󓑃CL:QX]*L/[bm3yTѱEaj^-h T#on,'77ڬF;$g1B.OMMnW<^T|%x% 6+Mu k VSkq\Vs\]+og;  Ӝ_(RQeXo;tu-"Px$2^w>%#t#G/xLC/1*5l@d>S'Zu+/<gwGCc$P% r 8.ݸA5>u }j dhlV@ qU/<^Iq2ݕ{h^zDʏ$(-=MF4mķ0ӤI3d/)bcbh#5k m46%&* vGRa'ro_\+LS:& (!R@t lo ŧbB8S ϝG:SŖ?OjBx-euaYVcGw5iMrgi2>ut. /mܞ)|Kemi%kn!p}r;Լ_G9mn%d~SN9Y%%g,Хe<ԴԟI,]..lg$$"XJP*w 7O!mڶ[E$BT@GR|)MEMhYvyY!ݺ\_h=ī=AR> 9mfv]FKrfQ Jef7 u -OL6v\n c\'Z͓DoonAJ?ȕP_Y!A(e-:X`"Px:1(uv/\Zn߯>f߯%~Zd?^d_z}͒d_z6K=S6K=/Oנ]Y[ݲ1?dƯ[%~l{'_?1(͓O"l{'Ѳ_Nbl?f[%~l{'Q86O k?_F߯ *fAc'Gk_XUd_z6K=酊ُA}_GQ?kd?^d_z_?1(͓O"l{'Ѳ_E_?1(͓O"l{G~lz~\,U͓O"?1*ɿFɿEa7jڳC2=hl{1fv2I;dݥ7Oפ}mwt.[ ֳ,8 *q^'Ѷ_F㽌Bmt؇:v?^X^\εtbgZ77 ?γctRS p=@ǃ޴=cz6z̟E\snqIbeFǗO/Q(`QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEpm,PTI5=PF9MH [s k'/9s(+OH_7b=Aqu M\9c_ZmV-OG$m"RmʎޘvAm'EI}6[";]+P^c"nhŸ-rO fTCKm[qqj~iMSpe?}(t?I?">Ok_ F-5H淐 FsV> I|ma_Q[j %2#W4Ύ[mrӯT;g,}l~"J6O񬑟U`?/z[U.IT"aUrNxR$Xj]ZMegku:>njO{O $ɽ@[biml f#gڦnozDlf_a*P~E݁hgkҿY'$Q[Jd\w,M7VvwV2ŧjs&FֵL+i}5Vߝ Yu!&X(\=iVQՊ*=d_VJCES(((((((((((((((LϥŹ?A2fwPGuk-ۼTm88]7 oAǕlƬ{ $@&i_̖ytP1VxQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE Z((EJ(((E%Q@fotoxx-20.08/images/export-files.jpg000066400000000000000000000466011362435004500174530ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 202 443 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{8:SGP1l:zg'Ώ6Ot߁Y.m7SoXpj<0@mUR<3,'=M)=͓z?]1t5kqu2$w*fUo^?J᭮m(NUMkyGdש{{XG=Đi@>&B};nMmCo*[fl.; 4<͓z?G'ν7⟅_x_Iޝ\^iP !fSk5淒_#̵fUW{Qv96Ol:}OdMBob'.:7q],,A rBkN;X,]*+S((((((((+ݬ#'K_ Oao"BK[=}kh},+ks<5r]n-ri( 'F ; v?شu.49d[Fy;\qӵx][: lo'<7*%_FOwO_O^ZŪl-]-LFyqU rOeaS\ױO|,!-G^EE=ĒG|Pu3GմѤhؑ$cT?unZ?'>֊5oXe.E-46h~"xwq^x? ֵ5hc)w +衻m=xzx3īC%aiX>:Fֹ([7ږ&}-̡\P$y JgY4Q@/ȝɳqs^cCi]wqc_<}B'TqX)ԩiJcfuo:YBۘɼTg*sH坋1I481njc/V kLG3D<1e+⻚48t5-KQ {ubG.GOQbslZޥ=ωta$ {dqRZ5;;fYBĀhS'?( ? O 1¶Egcizik%Jwv+jڕi-2xlVI\? O 1B|B_.u&w'ʈd]'sӨ浵xF_Dz1C0 "qI5OQbs>(k A}[˝R7˼(ݷ83Ua-f͍+\%'?( [p*~hS:qլ>6zns$zk+o}ξfv9!0;TѬxsKdaD#+е߁[z[@y*[73 ?[73 qhCVLKl/NNa8ⷼe}i%p&1!VBպ`}hZQo(o+B\mTh%Ep[ K.]]ܠ%uv}x V kLV kL-CqTI~RONN7⶷lj?]WV׷^HNEڙo'oz?j|yƏjtYWcNJ)+g=_×W~أ>ᣱuX `=s+񎩦? u{!ud-Hs]?5l><cItYWcIR<3i-9]!O"mh s-:}[n{ډl@y5^~gEi~ѫsƫ??O.]? PĺjY:SI#KVIWVo9woIy9ӟΊVo☨\n׮V?}4vECmMbև/kFZ?uF7y9}'֕A;@x{X ό%o/r;]vd&v_YWAL5BnL_NNN-4GS0g,#tm4[ᴶVCu5Yq2'?1}k ? W?_ _w k0ꚷnnl#V&kt>2OOלՏ]h%+O*C]qg%ͱv܅ F~=+ ocU^xtژ(OgƟVծE: vc}vpJ9jY.=|3MxTl:{yDHF}ꆍ=M@̺uwe۴esCҽkJOkJOöAs&όB;(c@oKjt.ciW qi!!dP@$=E^?7Ӱ?kc:xX,g򭢹l'!PŎs׮{oKkcܨ'C/Z4[m.8 .X|nOOW.JӁkMKCϞ]9&e}smZb]*}%\3` ݳ/O>}oNM~=j[b qg^xnUۢiɪh5 y1q\'9F7n kBD~rI'(sE+,/ok5cOPѦ:][dGn=G}ڜڬ:Eou;o6o#8ϵ7;?[w=(QEQE?תoXO='úlwzZCd`'+v%TڼmrxP$R\],^Y;hz@^nI%?{hn욎QeQc7r[⢴ m&35z'@ aԎ@h+3V^}BΖ_>ݷ|c<=sZtP3Oxkķyiq]*y~hb(8[Vmupؤ~Rڪ*|-}76T )zoZZދ H%v.]r|yuP9xÞ&}oLxbH մ设ؐ&[vIq]٠1"A}(X⭼oj2+o"$`5{A߇>xKTNN; =h-IhrIQ68e^(ta}hо=ЯӬ1Z*ъ+m-FKɌ4i^nZ|6Q%F IgfB*OB*ؼ5/n~"1eh@"H=QK_oCm+YP' Tv˱ {AMMm۬VC5}'?{䟻vaxGm/PxsV1j1ZFuK׈Fc֍C~ 4-2K+.-%L|c 'Q]m/Q_hV:Vt;ƖҴ#U0ĞIoZ*8Qg%Z {\<-ioXk.(sWԗ^}ْڽEn76+ߟ'P08&242(W nU9mgΩ]FtY]Pmޕi-ܖљM[n,6@ӭJخ U'Ou9|,5N]Wk>iϬB9cHxT6VhJ'̜р6q\8G/۝ GmչxdہֈkV1Ji&дM)(ٰ0HڒzsGYŚ   v H ִhz]z@dl8>j^(53vCyca#ynߕ#: ,[}w}SLn߼@6;Z]"u:=>o_pZϊ>2xnGU% E` !'6ON+|smf -JBev!@h9"n=<Zo-CMfE+{ɔwdvM=kF뚆ii[ݓGyFN61޵|GO떼2 ݓc.F:$uX/| s3NOS2*,adMڅ_}{jKχA z\mBM+P ڻzwiZܗ6-!bi,k߅t2 mEuJwʂ {W_~VUoe|xU8fOȝ6uo IUOLzOTpk~ŮnRvP <}5q+CO-ͻ!eJ8'xE5/xnVOyn+l =qڡ<;}xPj4'o;2wQc}h_?//4}*wZ6vb7Wh@ʃy~GU|;^[rO6V֦3fbN98uN6^ҬPRG/ `ọ귶O5K7)gB"wS_I5nWV&ѵ}t4. [՟!j=k7᷋K^ϱZ6w f LKx^]iK Yqm s) "#Nֺ4Bd2hø m>^Oպ~wumgOZ:B܎=^j^%˦#r"jYAߚOտ蘩=JGAԬkxH5 -m(;>n/5ؼoXQ>{K+yQ n-( ,[z{>&'a5bZח`k;{<ڱi?[ɥ>g[Ap 쌪yO'i9KO vݪ"*]R1"< LZKdlol. 2 8`ږ Z֓lMIX(P6+&yX\.c]K&q+@vs֛W騢F8R}+#F}KHQYnaIc*APjQ빃Α2L Ǡ'4? u5>Rώ*p29Ƞ +rRDڛ[l~݌= Rk D6[ڬgHx /8z{[Ea~,еM&S-J[`6 5WÞ;ω/DູT @268oχDn<={c EeB@x溯 xM-NҼD᠒-8è +,S TpJ{Vց _ҵ({2~_ӭh;m^;dՆB=SCo~S+XC]^G$[Y7=;zJw-Z࿿61Z˨6P0n: su O?&?²9nE4 H`8ӊ_ZfքIH[#s.:ǽ W`@u O?&?°dx˫&]v{]{oU?f{Iْ$U rq=hZv i;]C5nڦ5Q1Ul~ x^[ȶmGx<Й˒g uTQEQEQEUgqĖ FO-XW*O?&?'Pߔ ESu O?&?®Q@Co~S)sw79X*U=] ( ( ( ( ( ( ( (}+ ú֛k6PxmHU@ sVTMiny,тMfjڥ,-%Tm'+aiB 4`iP A+9ʐ~RWAcinආ6D՚  OWtKMM${߶j66g֢SmGF}*{9-C# OS^T/B%͋3۾] 8?"O4PoEh_߸?Ƭ@?"OQ~$EV_߸?ƏEj[Q~$>?"O4PoEh_߸?Ƭ@?"OQ~$EV_߸?ƏEj[Q~$>?"O4PoEh_߸?Ƭ@?"OQ~$EV_߸?ƏEj[Q~$>?"O4PoEh_߸?Ƭ@?"O_߸?#mv#wsLwVq& ulg}/DUc'X׭ZFͽXxUX; ?"O*tC( ?"O*tC( ?"O*Ek VKIci,09v &t]^ZcEJs}(آIQ8qUOϟR֌Z#ljTOֳ=wzt˒K`u8tc'GIT?:oZߡdk~Pc'GIT?:oZߡdk~Pc'SěT1cRJzkbvpnc@nWI:}ύFqFhQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ KkʼQ-Z^KMNZJdykica+>.TsFb|qqсtj~֫q~-m :3D DZլ_h^.=׭:P(_9^okIiE$ *PSr WG C!E$s)QqXu?Z}=fk泾P.>$ /8?^l<^i^]Iol^ʒyihUzV톗I$HG9_K|[C?ox^<0zoJ]c:1k`ksDy"ʐTCpOO,E]zKi$,/f2cr6?=kд_ho;Y\na'y k;S4M&udݵZ`A cS&X/5i~(5Rym{8}3:1uM@G/| %7 *+X ]ތ|WJ"Bc8[S>!Z,13cn]+B֡4MhWαVu{$co =|A֛R]IBx"&֠l٦b33\7^Z xGmb(0(((((((((((((((Sڊ(ؾl_J(bQ}( F6/(tBl_J(bQ}( F(Nfotoxx-20.08/images/favorites-edit.jpg000066400000000000000000001337771362435004500177720ustar00rootroot00000000000000JFIFHHExifMM*bj(1ri%PHHgnome-screenshot0230Ơ0100Fotoxx:retouch_combo|sharpen|retouch_combo| Fotoxx:resize|sharpen| Fotoxx:resize|sharpen| Fotoxx:copy_pixels_1|paste area|copy_pixels_1|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 403 586 0 C     C   j" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?iRɭKokgm )B3;-.n%q MuaO_M+O|'a-4WDY,mD+<^t㿒I'I͉g)H>ܒto MwF!ka=q(rTzRͥܗyyav_9$_no |Du[.8hv 'h=o9EROC>xſdK%Y%u Q[uol8o*X[iUu.Y v+{2DT mW̊0v{[4 ǒ RqԂAT6P%{A{/7F q7tmgFo)lrmHT'ɐzW!~hx]rM*=CQh8Q*.HLZGle8xIXږED4{Qu{V(%⸶!l: I9_=.^[]uMWG_Mi:|C4#Ǔ"( HG_nu~cP<1xkߊ_נf$R43C,_/$ |Qex.ouZ^ڵڬgTut= @% mh}=_nuk?+״y֢d,Gey!AV0O~?'x]R!Rrl@d`hdz q6^^Ѿ<ϸY q6^^Ѿs>d//{A{/7F7le8G3A_nto}ϸY q6^^Ѿs>d//{A{/7F7le8G3A_nto}ϸY q6^^Ѿs>d//{A{/7F7le8G3A_nto}ϸY q6^^Ѿs>d//{A{/7F7le8G3A_nW 5C1 yk$, .݇rpq+ڷ׃~ݭ#PzԪRwJ^ $|3^LwK4qr G?WS]f!EPEPEPEPEPEPEPX=8?TׇᣍwGNxDervetqPi.B Pƒ'$ /?¹%{e(mFݍ͖&guw-G4^F?A`5,H;G*O_NOZޗG5R>huE:.덿xkIo][kU+!#rl@Q]5uQ@Q@Q@Q@Q@Q@Q@buGЍ>8f-_IuY-r̄FT6E!O8:㿃~+ͫGF]@iq-ԆHA&v̠1$ 1dgjOxN+}WTn>w A9G0^k6xc )|2%ώt{:f[Ⱥk [r\v0<'ΤQWStK;ﶖ|b&v>|ce爓 4L3'yg͏Nkf 𖳬]?^ซTv6$lbsxks_($t^ҼVn/$҅|s!dc#*e NvvQe;S,'V?nq]¼^š<7w_$"9YkѾ6^;us{w((h{ 3x&f!䪶rFN3ͳ=,6 (պwO˳=ڊ(4(|gek0is[jS[ҋ;)g]ʧ%׃5]}h?~5X^9uUqGy<5{u$q̓lav8U++Þ:ƃ=+'kH)}hYr0L0-#+ןvca> 4K7P]|OM#~\H1 gQK@ixZ2Ph-ms%GQK uPFr0|/]?㄰hwD[j6 H'GBN  +cҴM>%u%E*0CJT<;"^QEQEU:ME'x%I##סUWk83ٌ~H9+g MмA]&:\+Yk1vʣɍ:( ( ( (/jhf,pI-# KVmrbQB$T-eJ$R,FmnJU,7q݅aj~x=zVI/)0HEr#NAٳ4BUUAz^7e%~m7<ZFeZb00^@((!?G8?xO(~ȩUW;EM;򮊀 ( ( ( ( ( ( (8#O'W}|h|H𧃼G3sRwm`6 Dr `9ٷ?$ZxWN`Ѵ [q$$aA̓~m~ɤj>Zubt_PI'Yh 1ݜ &մ|3ͩi c/N .yy,2FηR|r 0 +ͫIm{_Q{g{=??‰;5E7>*~}cB }{7>?x6_hgd:pu$/xE>[dMp?WVo i;3[{x6 pN2 ͟|!ysu=JdCe-K'Fd%_jRqi{鿑 $Jѳ_~Tdn*fxbg֓,Y-!f* @N HkN# ( eYsZA{nYXq "N 2[gP- úqOzMʌe6v#ƿho OV]>/"xDYGnFðZ/@3B15៵+CVcJEخ0LBs +?~H-bifyQ'GivV,bZ#$vcZiװ_Ochn ^3dX>:|9|wϳ|@n'5H_ˉY;aQܚß -Dit-kPtf[[ F) XIe(bj(!EP|Hh5]jRb@Ϙѷ[ӼaϥjHV( Ȯ-%'tegӞmWWT&L.Y=H*Џ+[Rƍ/|./5mO\.-"Ue{khF3e%3mLW'b'o# oڧ.v!HV0]qԃ _#u+kǚ_>tO6qE'Mʺa E@~7isa.oൊ9 l6<+F((FFȡVVל"RxoT]fmӧg[iTn<[*k{oͻLrA h1p,ZS/zi:.i/q<I,6fwm!R 3 NMZէ9| _ĝB.a Yn KUl\+&uekk<^ L@} T 7vr[߾kϿiC2HCS𕿈u;;gmaĈ9`;w>uӾ>Yo'SųhCY1DhGhLaO_s8?-%kO_;@x_f+J/gw6 Bo#_ံ+[),'-ry>򏆾:xc?|Mi:ܞkp&YV<^|KF[WWdxn"+$[|J">3m2(VWMI泓oλæW|?,pB`J)$IV_xK]-/ú9bED zMlPīVl@jۗkvfjI//"QPXW'+ޙóXL(fRGNT3u]ecktjzߵw->%I<ٕRNX,7^jvuL>'17<8hOIiIAөeP WEbx|)k}vYl4iWMi$`A*F1deic !/YW*YW{#[9>Aa @$/xWWXѼ'V7+.g\ꮪ u=(ӹg)V((?i]7E߆uzWjZ:UQG̖Kr 1$:~&Aiy?W=ίp$ZZ[d]m)?aXskվ3| u kwxJZ 3'eed;TTy˦j'1P'os> Ǟ*26뚕{4|]-?o~kk k-)5=Y#)%DbB8ߥtSX[=[^4jfLUʩ`}>[ӿ`M3FeƅeU.LSI F{噇 t68LI=6 ]:yuJ+mۓ3k']W7 ;hRQPPQEQEVNZak(%B *%`k?|5O5tI/*PSX$V9'uھ<#Z^\ζcl6hf,H]brI9/h_7i>7FϫZ魫)h4Ŷ݁Ct+tﴵӼu{t(ڠ9HyY*^{/jW~-Լ{ڞiuup@IZ%,2O 4GćJ {{n!@1ݱ_?ix]{x*Śn[-fI'0%#lRxKBO,鰋J$ͷP:vn^A>$4)eV:^xD ]fHH'b0F|- uO7:;|"_x#_u"M*!#81}>?/>~xG@/º.݇[JN+yC69'X5HmFw_|~{~ֱ!=WŲYL\`FFH2qQH(#GiivVniogg 1¢"@0+g/j* BSj:tXu+J$. 6C <'+zgq4&TU0 ~ErnZmrC _; j> Ogi?̈́@mg nLօ\?6 o+:<;sĚnu7JmtōDXgnzHglK'O//K%o\mA]ygSh;T_0;b? Nze/` N0O[K[Oaf/Yj:ԗ-\\LO,.ǒ$Z>-ß*kC?_?ǙqӚVL#XETQE|L1~1|e.5;v'~\am(;HC1goE{k_l|Dn)m̖hne q} .SQP\i: [I 7bHPWC0 ?K 57[5 r]ZP{봞MB[lj縸fL 6  X=_om㾟O9OxYar1xWɯ-vK ėR$1?۟dAb< ~?<1qKW1EjmLQrIۻ87*OmAa4M!1flބv,l,CW韲N~xZ^&-F͝VHvzU\?+o_/io=55F ȗ1LAۻVcḼ#tkT2 ky%"Fs*;^Ӧ|x|Sm'O-jbߵpȸ#ѻ)$YxŚ..jc[ҵ_3EØѕYIR2rkGoJW_xiṛcqy{ZyJ Z BE3q`4#sB}k:^A! Vd\!c !f$|a^;}/\gK:FlY%cut2.Č3MkTZV=BSj҄cc!rU%w$d4vV=((g_Yl2 =v ᷄oa,^ I. ,𴺕㬷74ӺugrbU'+oXæhg<5M?G<؞7 'kdG\^Zg]GشbaȎOJ.Wa=oq>j|5w)<]"}"-n2\V-<%mϷiXh!VHIdQ7ZT n8ZW]M?/??A> Gm'Tk'~švq#+턢g9=븯߃ޭ!־&xt_YH|MsPCpexPC(`@A⿋?k>.o xJҧ𞝧j[Ia:$m6Fs#+zw3^99|-b/|טGmdQF8dV(ˢW_6nRx;^oxrLe^CPt]yw&TVYŜRĭ[iT ?G~>mߋ/tOZYK[yΉa-E$;*`I'ϢYO QPPQEQ_.~ELЮlŻ:w-%uFx#'`*99*6m-o?{mkx2\]4b9\.wTu3<ͿN-8}E~wLRnmM'j|Cu$?([3Fo3:GsxQ^x~M{ ..%dD-!#.8N:=̞>^eWxu?ii6v71K rvۢs89  +iIRMQH(?Xռo ߎO;[ip᳉gA̋޾Fƿ^kNYe'W[H"c.|Q k>p&|$ =px>:so?c?d dE$׋nO069k.)ϰbN?2m5dnOCx{ J*fsq|?yl?~7E_hE1$ąE)F8[i&=Y5x];DֲĻN$WE!bǐs_G |[=gA#PF,W$SZ#|!y9sGo+kYx\[IY82$`3muyc_)y":%E}5{5ʒѫjјar\gJo<ϙYYEmgއ۔QESO΂(Qm <ؒLtޠ⤢"kX^EFx Td~4ܖ&A GFDߴ"ٴktJ2nf = lы,ِ>'~z__7?_xUѴA *ѳ uPeޠ+d]E}k C2 FTϵKI}P_Bm(RՐV 25=SI[`(W~џD 2MJ |4D@6#ωdE4 EcG%_Zkzmam2b r@p+oxKߍ|O4"I|a-+Y6Ÿ N9[s_g}R8|3P+۵ծ7]k2G"G |%+}c]Bnv#\:69W z~6?,7ONe[L '\ |C|] oAº-:5[;:Σ.3K W5ȟ mtKLӚ顿֯-݀>rp8o 5-c:\=ծT ;+#w-NsN:oh>[zMQH+Nj};,s1jh._{|9}6$3~u4ĺ =ƶ  .ۚg}+_2:T xwJ%fe2-x8bA|]ik#Ѵxn4]M OņI'1]su^ ~-RbqHnA0*Ǎa?M[fnW-o5brp9>$&ItU-Vk Rom㹋B2O J{#C]j[;kZh"&H` +4>:z(y$C?!^^qFxP?SN)wwqO]QEQEQEQEQEQEQEqFLN]B4fjObm]NH$FFȄ y,2iKV;hZȟemǒNKa'?^xO~&5Ni}'nԲƳDb8yx 7(9lxxTTidݍPQj }-|YOe)â-7Dm.I>+)-]K<_:>ilzU _ '2Fp`@aN(| ׮DaV nj0r=s^G~)|A:潢Kg АxuRH!dnDw{O8O]\N>Ic!]4-5׆aZB"Ep v7 {_SgYv:Wܐ!@8E>(((((((/|g焾:h>ADu9X.M0[ʂc p q^1ݿ5Z,tLZiFF$ 'L(Gt 0* >Z{\G4dBn0sx d;Fz 5[]#hZե5խW Vu7b2BNoCy>G?j_EJhZi.(I_5yPWs K>!h=oO ~ehޣ!Uy[$ x/~^4O鏮IIicb{ЫD u_AN'm_Zw]NFhm4*XjNl /u}?-~cĚ%-Ε`%-m>n Y^9-YFC_k2K:<7}i^}G anԐ A>?sƚocmsG,-4yeAx}pYbmͬZi&Dsw(|uȋ$ #oY}`??;ֵ/IKf薷}|-.:X5yOWj{jpEmsk׳bR5iPY$^Z;t%]noZo.{x/ggk#\I7IC!X_IQY{_O{|ʾpkDžeCa>MΩs$6֋"7ق3cf$foNV_­^ӣԵF7Zf|SI&Bh帼U%vNn֋}f­𮝤A&uY֢~|+\g*:IeFUِw~#jzK|sy-Oc'ѵmuhP?ݶd_tQmWq {;_Y]jWZ6i:E#N1Fd $Uݺ<5# ^^jw\.&B4d䯘aE=vÞ xO;Kٮ]:K2LEX8npE{U7DW*W~џDT.V[Op<:f°"zooVѭPoUwD i%xBG)e8W`Hq_;7:'\7 m$& yE)Ls4ֻtzQ@Š(fn2z-U`>|8k\ . j[-^dHn;~`NkHt㷳c&$DzW*Zt6GoV@ʤ0{g|ү:Pk*̬B*9\=k먿'U"ʵbF1qhY#C}0(Z+kwJ'㲱$iyS#M,x!WsF>QEs1vPxZ_-g*)[Gvr2q`evM ̷-Lz '0RfQ܊/ځ@wĦQj yey!d=6 ;׉~ uǖ ?'x&3l#9PbD1Ѓjb։7toᏴ?|Mˤ8DgiC$䪱8P >5>+kf૯@ӯO[[nȬ+Hą'|oPޞ մDtɣZX6jC3MxZK|!.OEh8ƱD&@f2w'?/֨~?|[#GУՠ%t# ,p90k)iOsšPM:[kV"|bxU,N'4?ٟ_CմW}Nu4&.tBxLhp76e`F:-[YΆ-o/g4;o_.`Q@D-Z@9*|hо6kjrmt+E7Ww.m@F@b@כ۾ 4{?Þ2Ԯe m6F&CpcPdUw`f`wOg jr2~4<6?j|\-6&nd)͹֟< |BO*Kv>RhI_zߺV2y_Ou?烼uXY[~"ctfZŜHagp_A pygC=ź]펛GF3GVapE[M]=(0jzw?5 K>$C‰eU;K scH-}O} '[kn<\"xa"#@C,gWz,{[I~FnP)ax~%|;[]^+BȤg޴4mNZzt6P1F $EI<v((((Si?[~uƺ Ѝ^T2#lˋqEC~Emʖ<XYIGULhψ+\}xwñjq#jsEzuΕq$P]4eYŬ .ʬG#zf~_lmDg~%īYD||:`aOz.i3eO%ōDZ5K ##z:vK&(!ŽEq_M.[uӢҘ~t ѾxԀJ(((((((!?G8?xO(~ȩUW;EM;򮊀 ( ( ( ( ( ( (8#O'W}|Xí{_ oሮ\ѮTvDkcp 9JQR~lVo,jZ8amKA'On1H׃ۺ Ÿ46+/۴%.;v;+džm:\=IyJ7\xL6LΏ`[i3LѢ$&=kGcB6P% HB@]MʕX(C aidCs!Sl|R1Ty~(O7oQީj'7QoH QO7oU?ž`/QTSy~(O7o UTsߦ ?Sy~(GU?Tsߦ EQO7oU?€/QTSy~(O7o UTsߦ ?Sy~(u__PxU]Ðj&կ ڣg]KH\rڵ>Y⿄ⱔPEm=b\ '$ɷ8ɯ\Vi$2>:$q\6O ͡L3:ӽ+_P3njoW\/G[] bB@%r?c}Ct+G{ķX# DZVBlr$W;?1iD&A H4WǗ/𽶩xcR"(y A%2>`7c]9X?ک<Mj7P*9ک<MzoG9^j7Qo@?ک<Mj7P*9ک<Mz!?U?¼k- [ؑAm$]@UI={P?SN)7zQWOYq e%B8WEo@VۖEgn[xQo@VۖEgn[xQo@VۖEgn[xQo@V,ͭJ}siFLN]B4((((`UBJ:m04U rI}sYﱕ}Tҳ{AhyCjK\֜9bޡN2T?ZIo[ͻWYFg۳˷v~ln8듇E귺MjV-:bULIɑ_Islv映[~3HӤ2Z< *23^EJ)[Iou .}zA>{=e $X kgc].SGT.e&Ǹvoq;bDz}xXl'4w~a 10vrOu~G6ipOj\M?X[5 Ndo(wϊ3e{D X;h΁PRzt9clDp͏7;D|΋nkm?ޣƼsa-<RưxnCjY[ڍ>"=a$1-;`z>w?kzn@ ,3IT_נϹhi.hxxƫwYjp3LʋM,q)r95uC⎭-Ӥaƹ_67ܤ»`gUb𯁸'JW|ןm?4}xǺǏG{6ދuq6cY8˜oF}|+u}S^ /Y!6h5$E9 qO8+]?l{Om?ޯ rruV_ $b+pӾҊ|c0 Wg֧_x_Ҽ[uy4QW7go#dqK9tzʻ@2# ﶟ;v_߳7G'K"ܛ9fF Xc\W{7?xmút{a @v<6NN޼ro_Ə߂w%o<_I\,QFUUp2BrOqqM;ͯ>k⏶{\ñGObQqG0XiƱ~(g,m}xXl}Q6hi_{>( _m?4}x/=ls>k⏶{9Om?5qG=ckƏg`GObQqG0XiƱ~(g,m}xXl}Q6hi_{>( _m?4}x/=ls>k⏶{9Oe& 5ʒ#ג+gS3ykCN]B4buaEPEPEPEP]K=kiH ;VHn8"6})DI/Q(X|uAYXXmc¾|bm9wcc]_ʏ)?pFjXn+g)5ZF]ɟP21ԶH%S4nj鼤TyIپ]bu-+Mopҷʷ%b7-r?κ)?Rq*=/hnr2u xO#:LS?)?Rq*=?hq t~uh#XV%FW#Ko1C*9EWɑb-]x ,ѶȦe])K ׅ5=v0 |=·6w3X )b7_4vM웙,61m4|FH:G'^֝h}.& < /S2xOG#ʺc#@!O,~3L:Ηht"S&^%1cuWQ ׅ5?:G#v9/U~>_7t4%"V9gG'ksZ)t L4vZ#1v2kG kN4u i?Əf?i xš=7(-'Ro8 K|1V.4])aUO/ƋֺN/A; ׅ59px;D/aXY 7I6>aAKU$K/؋Vj62^H$-TT/EɯOӿx_ZwOh[EUPTtwx_ZwO kN4>!nn3 kN4u i?Əf.?:>?:g'^֝hӿhwwx_ZwO kN4{7=?u i?ƏN/A;p{Ώ{Ι ׅ5?:G}.?:>?:g'^֝hӿhwwx_ZwO kN4{7=?u i?ƏN/A;p{Ώ{Ι ׅ5?:G}.?:>?:g'^֝hӿhwwx_ZwO kN4{7=?u i?Ʈ6g8:gf]nnmIQpqqߔ_ʏ)?hnnmIQp]O{ΰ;_sAIjt QNՎ:tFZQ@Q@Q@Q@5\6U Q mAҀ9=Wׂt-i4}KŚ69@B1EU۟ x/x~_NmfSRG6MC`3ExS}FmZ&d=3[CdE o) y:gxό▷/FSkhwW3,~k,aTcrH97{۾x=/@/iO˞({o_S㧇~,_5ݞNdGwsSvuT&SĖ~oK^*=u0p M;^9 Zh3N#m!n YH&A!E#nw\ _}k^%כP흵jəV9 hAՊH8$AwmOwO_ow>"Oh [죋Q}핌+tq[|uK6}.XdR,]x,8׍|_fG`񅆩hRۙti6*F>q\E ='|4ƍuNk>Xd6B;r0ǂ2:ŷFi/?ZCij:VV43TS,O@9.?hj|Cv-{&.u $ JxX?G4;k:~'T 4f $כ'K^<3iZW&м?Oat[dcYҲm}DZ/GiGN3ݑ1n62G zgt+QumDϐ|8qҾ3e߈Z>#S˧Xi[2޾4Ȳn`1FC?_Y[/b}bnuyI*V6 smhivCsS[nGO^Um3Tu=7= Mh_7i5x? E\ƃ`R|+?;R cZj6-=ݿ0YBh]4x#-y&xڷ='¶YeӦQbrмy 4[=U/*-OKtۏ 9LÃ}A,NGzW:߁~i6Ku ,HaKdftN^`!cMvGǰ4{Qc"XEy@%Uh'М%r:€ 1N(h((((((((((+o 5zUyׅq@ m[UKSHͻ\5żQ2 wSPOҸR{K=ݶq OoBu* 9`{yh`;r /C]6JT&X Ʋ|E/U#7 ־y~:|I_ iWZ[hVVj.wq"w(:3^-*D'4 }cWzO 5 3oI?&VϨB-9t"ȻK`W o7CQ}ڋ'w˷~hv+Oŗ[xZ:]1B4),0(.7;(\b0d+  ?%X#ke ny>^O3sj|J}{%eJ}kE]USss$s@n|RG'95Ua;.ψH5K>R3 \dZ?пߛ@||;G? jݵ 'OfL6IU$g _Uϗ`WWӫGЍ>_?i%Q@Q@Q@Q@5\6U Q mAҀ ( ( +'_~I4;G}Fl8 b9U>@W/Rk/งG9cb rЂ3W$nsa{Ե :FY'u-@'t?E=X]޷aasy^lY,dXO߉>*օx _6}wX7mU. PpH2(ZMеݿN|KB֟vz>u˭CN,"3]ͭ_l1՛__khn`C2,W*FAҀ#Ŀ-i7oGĿ-i7oG_7SWz<'zfU(%U{ W7$Ŀ-i7oGĿ-i7oG>ym+z揪Gygru]B?6'AZ/5O^~Z?x#~xviZFMHm&x\)<7SWzVĿ-i7oGеݿ¤?wTIΆ5O^ ωZnG'>%kO=ZGj:£5O^ ωZnG'>%kO=ZGj:£5O^ ωZnG'>%kO=ZGj:£5O^ ωZnG'>%kO=ZGj:£5O^ ωZnG'>%kO=ZGj:£5O^ ωZnG'>%kO=ZGj:£5O^ ωZnG'>%kO=ZGj:£5O^ ωZnG'>%kO=ZGj:£5O^ ωZnG'>%kO=ZGj:£5O^ ωZnG'>%kO=ZGj:£5O^ ωZnG'>%kO=ZGj:£5O^ ωZnG'>%kO=ZGj:£5O^ ωZnYxQjϟ,I$c`^£5O^?Qo@S}VHa6elH8 pF*/7@uGЍ> ( ( ( ((k mAH?.*sēBȋ$nYd0=Ai hϒ>j_i|(➻n 9a EƋ&=bMEn,Ing*&yOs=$54GbD `NBYן6=߁<3uog2{wK䂘-ˎyi5>xռu[ßR8ѥ,6Yy4KGyҌ7'>,/uwkBCڪC1I_@^>߆jk5N/j30+ͪJyچ>u\곲iP<2lCE<4-vyM.O_ moP'H-zYJ۾xpzt漻S%&e↓XYx*hW~\;HpG$c hjwMy$]NA)~VFG"?_c-I!ѭL(Y0@ =Wj[Ϸxoޡh9ԣ4 ijD{F;y$JӴ׊zUv,D%PXך_"Rj}#D]/-d]$[|.vrߊW׼Wc:M3M:oPH̙w20K9_9$Y4ES}S9i7+Q.ꄷ^澣♼A1[E9V4_ښ$Mm4HP[HXH2)!^>㻘~ W4ủۛ{v1ø:v$` }Ҿ6@tAC}[(榏h(0o]DK2>4.ߊ5i K;]g&~yj!' '$gzO~*Mk^沄%ё.pq@'@ ` :>j2s#GUƑn eǩ5tϕf)w;ï9. }O-K6lR3@ڠ6s}g'\Jj‘l(7''a_*[++3d7SK=?QSհ?8gS3\u????n~s_J?nU:+U3SgQEr3S_jTQ\' UuW/ 5????f~@Ek@O*O߁PQEr3S_jTQ\' UuW/ 5????f~@Ek@O*O߁PQEr3S_jTQ\' UuW/ 5????f~@Ek@O*O߁PQEr3S_jTQ\' UP$_ǟ\/7Z|H|MzE=uk *"|c!} ӫGЍ>C|Kcu?Ĝ~U9JyjL^eld*FvקJM]%7W'`d~xC6d@TRn07u}}SŞZ躍]hkwvyck%B}ağ$e4շiAQ^~uz=o^tɩk6ZAqk S+80ʠ,6F sN}KoာцI.gc /?}flN do&8^9Z= )\sK?A?| -bQ:%㉙A2yߴvExvY%kW/qȬ.)m,j?7n}SGoAyaZyz't`VE o]oD?YUr3 .7?jѿC/%"Y>Oa#N &`}TW5~^i&FM`Y5߬A 1 n5ڻ h7_գ_K5h!)?j?ßU|/tBM-.Ɨ"O}i9I?3 /Zi/keV6 [~ӗ$'F)^?IAaCV.AaCV.]%k miͧ}<\(Um" CWox us Z7e_A|aCV.%xGFÚ?_\-He@_3.sS}F+{>5h! Z7e<x x m6X%RSLʈcropT]:KkKV]H3$p $jd_u,_tx2]2]|(k8Ѽ;x/=WW|[j8޴(tYMAm86ߴ׋n7CƣUo j4sm\Aw5h!?5h!ڢ>.uWJO X%Z]7FʡN|N|{-LF>+ HG.[7G$ ((__>5h!5h!~^0LJ#I5 +y+ 6P O9倖|F۶>#jxwUi~Oa ŭ6L4p'R F1R]aDoA|aCV.A|aCV.Bu/ڭ>ҼOu-gIYo.b3zT~[?÷wxF_P-ΊW9n7Kó*ĊjI @ ot2]|Οt%5kO M__ŨHIYὈDF2dGgM3JfÅlPgxGI0Mp#4[._ 0F G 0F _&`^#nm(Hf0Or6b@'_Wd< K_ ɥ%R/"o8L/-wUm/?jѿC/%/?jѿC/%א9o*yn="mgT-u8,40[^LJUl,8OϾ(o/<֑֭nBDRI%oPLȫR'dkVs_otxգ_K#'ř"𵎵e&i,@dH /0ꫜ\7?h~;8|%?>+[5 13_;lrRcjr=?1|}Op5h!5h!i_)o ]P|6wokxdXI<O, AOvuGVՠ~U7zlfѮ2혔B @Ԧn_ס?x2]xR]z$r9:`LAaU:7R~?K/?jѿC/%ס A|aCV._գ_KCA?~ Oh/i7Ja&}Qv8?Jf&}WKkHU^if@Hms%CIKWvWOrOE<[CN.OƼiwy'ѥе#w~%xXn $qp?Ï,д}7>ujzE^*M(NJAacNZ^v/sOE<[CN.E<[CN.=Ů4^Öڡ֛p.Zfk#hn!`vՇ~.4q( Hj ely]Oا*~Rv_>=Oӣ_KOӣ_K*xN/<)i j!58GI pe?F&6se[xƺx^n[K/- Ksٕ#7,șqVw<=F[JQe[BФWaf ǥdxBIL#%_B~oGIo.ԊoؠKTP?by/GؠKTP?by/GؠKTP?by/GؠKTP?by/GؠKTPncnr Tb;X%#2 F@8댌u' zj6usŹB,̫;-ESw_I-qsΈ=R8ePOEg Ī%, Vn6뺄7]K4w0^<r+_hʭo_s˾. X\jcaojzKnVk !l)Հ(۳[Q}Α>}NMfiGeT'?|(-5Oíwh٭`Kh٣s#[eyS\F>c;k]z;Wc".ͨW{kqk*̠ TWP+(>Ru+WN'%ȐfP|Å rL5 ӿ.k&iH ̒xrI]Zm7ڢe tPĀvj?r]J\Ȳĸ"y8bG8*]2Gc ,Oڴ4MYҬQ"'E|2i/42Oŭ?bI5 ܭA$ۖGEWGobX.%2\,GGcf<; Yn.=/٠ЗbB+wȒk V*ʩ4#>ѷwF?[ЍLuiˉ.Գ[$HHL8,e?vM4w˨j73/n0< 9ϖGCWhGG|?Z~^syen<=wu]7l#K g;e]OqjD }~(-)%$2\)lcsgwFDt7$s}|uV_ynt!@ ' {?Os]ڮ]5Vp5wur&&VtDqF{֗":?4-6[8Z浬:m\sWgkV$FQIKOoͧWCF5ux5+lJp!6Jך@#G":?+$Z7CtMB'P}O3J[f,T I)C(`0r8T%nƶ:~1fm渐$@lFLg":?4V(K*|:\ڍZI{uuKvA%S"`qKk)6,z=ḇRMdZvm|mkc/.T9{FDtwb)鿲}Z-[%j7[2Hg(ĮX$ҩ"J;} >w2^B!B n+cFDt+X}oԹ|5{V Au+-[}JlE>`#8⼿捨,u~Ql[AB Ks! $ns4#>Z;edfo~ J͍ȶץ- e# lJ x¯3>oZޙUh:oXV;ئ-$p6?Dt?i-6zxfDt?iI1GG|?FQ0zEb}^e@#G":?m|s%CIOJ3QM]N{m5d2䍁) ElOWP3ēC"xP~/]_F$ 6Wykkp_F$ ?\G}(Z[3G+źq{_.^[_]Bݸ KH,R ;Lixf4:ICUR‡m, τWѿ ?W>_F$ 6/'x# -ր9xZipQGrcF0[t/=D5!ӭ]dm㟆h>Ьuܯ muweн-`-G^x Lt}QS#x{A|̮E\( »TkҾ::-u{;>%ԢuMwHC|$ /^xoĚ^6.Ӯfc N>g_|7k ?h ЍYK vt΀.wc<3_Oki\xGK"an 餋bqv>мM_|XVuIo\.0@R>NI/%I쿭t>M;xVM";ʑݑb{I7 {|'?i/xk[Ly`2l;}w~$Γ]/+Qn>{²m<[|n!H^ž.y.gO^?)үun ER͹mhR~[SN?j0kb/ZBì)HA+򒄯E{=q ?x{@:G>-5ۈ,~Y `+v%T{\A5'k^"?k< "Ooւ(9φV*.4K 2w _Ly5ā@i ej4O6YMfe;Sv;vk MOĐ9\S$߄|+l&T|[ByR#*eCUJd{ti~gV,">Nk ‰|ekWok6ֲ5ޔ9FVhfתTI|/FTn[DPχ n`o.u);,,+± @'/ڃQ𝼺n]3JlZ-!XtDs_!QCy.:q1hKoVqiNg}椷6iIy #sOwK;HMw{/vV D>ך?Nc>oC7=3Ym.=OP׼aig EI$R8F^#\l)lI4ǧ9🍴,_h \\emw2EcJTw sʶ~|+_xJKGot$ѴL XY^i6Bm cQ? OwvziM̹@Shs2'<{y"1?=?I’[xȣS(biQV6@Lc,=s>' kZn5;&{7RMnep-:bA |WOs|%#pj7w?NyrMAX <T4كLxi J{ oy-C@$M>b# cN;_OcwZǃ'gmG\iy_|2 ~SWkPռ?q6,֑LfX]ʩlg3+d-PGu⁵!X,.T" GAqhEQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@r1uuxBQEQEQEQEQETO@SI>3c(J*~(Y}OP*~(Yhqxz;p6;UY#5go?Sk}€%[yT/۵ u~(Mw_+n5?c_o?Sk뉵ۛyamlJ-#c}54}"Ȗ$ ՂPONc_ 4UoOQ75 EVc_ 4UoOQ75 EVc_ 4UoOQ75 EVc_ 4UoOQ75 EVc_ 4UoOQ75 EVc_ 4UoOQ75 EVc_ 4UoOQ75 EVc_ 4UoOQ75 EVc_ 4UoOQ75 EVc_ 4UoOQ75 \/7] b\>¹_B~o=((((( zI5' zQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEoM-Ƨq>$]( m*Eᇏ+~4izޗeQ$(¤r2__xcJ\/?vPf1 ̠. 9^9O^6?|2=o6ŧie-.P5IrR"‚w)^od=N~kx7{+1ܭjP8rvPp |5Ѽ7j>Կ//t3W/-:gcH]_?/nXgo!H"s3Ug|FOl[EU]! @d~U$⭫]3z5k6|B pm,+)s8bm;VR#?hk$YZ[Z򴮱rYyj߆hou O1ʾ$.cu B'{OIA~ͮDAm)=Zq5࿃gWZQ=ud$dmq0*ozC~,=Jk MͭBSUf]He k%tK J.٘Į],|ETcæ+в#M/Q6YBDrLSLeh/+wos6Νto]Lwuxp <+Oa<.Ym仿ӤZ‹3s. fs_:-֯o6?MG#iI (vAo0fjo|"u|'a+hvTRHKˉǟEhܯ$'Єh4Dgx񬱁*-j_#5j.\i_y{IjwQW,:E||?ar~3I3[hE)Vv!^2^3׾x#Óx'\C%klܭBVqT@~BTCy{k&{-n,A-+x9(|yM*.BWn:Yzjs;[orNt F{0qv޼w~zvχ$n4:tImrH^E3J$9,h̻A]~e}0&eS=qx'n$k/+VxVڽa$P\HAhM"YI+/+Ge'aF*QE%Q@Q@Q@Q@u!I>ֻK|hSZ.fl@@Ex_ʏ)?{}Rq*+u|I}O"O/G_ʀ=|"zg"h/uq#o'V͘n8-#hcPTz**O/G_ʀ=4ؤM%bVh}NgiD3 V#]hv",mRq*Ѵ[[}A<㌀ 漳O/G_ʀ=wZEVd[]Mk*B=pp8?Yh$לIP²=u)uOIKMyϔ_ʏ)?z7+-\—T?Yh$לIPi KsmԷ*Io+F>WHOŰ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-20.08/images/file-save-as.jpg000066400000000000000000003105241362435004500173040ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0230Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 571 851 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?GL]";{o" N SV?5K紟ѣSi?QQ@'hTOTPj{I}>?5/ڧFO=EEK紟ѣSi?QQ@'hTOTPj{I}>?5/ڧFO=EEK紟ѣSi?QQ@'hTOTPj{I}>?5/ڧFO=EEK紟ѣSi?QQ@'hTOU|3-h^UKd?sM''dKj*?4}'k xOjSOs$O/bݣ$NApqf_x/@O\Em 2YmIAB M/}'hTO׻'L?5B-mp2Y%VLd Kc d~|?4ωz汏VlI m=°XsM{jVS?4}'k>+i3u#Z+ۻ52; r~jZO i&Kx[Hfvtp懣IA&ڧFO=}5'Nc/ ?fy{^^hr[$IDVw?cjg+Rէ0iYcށ ue qurI]nCoO=j{I}w-<>(G 涽t-TۇpɯBn^#Ojzeۋd b-a~noG5|A紟ѣSi?_NO2;ÚOԮ$p-*vl ?:SM$GQla9'#5|v}j{I}>?5Wş'J}^ >w]Jm)fFG28u:~?5h.Ԇao\_xn]ApVRC?j{I}>?5^WFmRPhkڭh1n +>rzO-]x %Ú_Y_sOO=j{I}|9~ڶ$i P-z]߅?bѯ~톏U<552 3pOJI9v)}O?4}'k;|Ѽwxz[ /3ܽ2RK/U*-fzņgknm<3uڧFO=}>W Fڴ_&riZc:Jwxo"/asyoY]诶& [! S$6}'hTO bWzv{kSPxn5yG')#8a#~U<,MU{*,|rgƦwRcTO紟Ѩj{I}>?5/ڧFO=EEK紟ѣSi?QQ@'hTOTPj{I}>?5/ڧFO=EEK紟ѣSi?QQ@'hTOTPj{I}>?5/ڧFO=EEK紟ѣSi?QWwe ZGF53m?m],n%#{hTO紟ѭxe|xyn:ŀ1faenq'aPrI}W6JбJ}7O;[Gvc"_P $zע|[Cujά&Z?q|^ :b~n|,j:Tyl[~60I9¿um%lPAG_X2?;pL(?/g 4Q_Kû4#L_G;G44E;G44û4#L_@4Q_Kû4#L_G;G44E;G44û4#L_@4Q_Kû4#L_G;G44E;G44û4#L_@4Q_Kû4#L_G;G44E;G44û4#L_@4Q_Kû4#L_G;G44E;G44û4#L_@4Q_Kû4#L_G;G44E;G44û4#L_@4Q_Kû4#L_G;G44Emx7XtoiNK[PJ?g@3q4Ջ?GwZ=7VqZ:Էw tI@T(W7Vk}x~H^ͪkw3:q:կ@3q?gZ4MEd](uoBxrG5Xhwުv9 0Oz>:|ya%ė[Q$-*H# 5û4#L_G;G44U*j|3ۿ]jkKck:yXea+:Դ WIg0+YdwAk[g ??/߮(/i}>x/ѿtvh^W69pv62:ץim燼w?iHmto =?kPuQZ&&4#w|hF8ƏwwƏi3hn\,G/F-C7v>}k߷co+? { Y xOm{+|xۏҺwwƏi3hw|hF8ƪ?wD?]m3.t?]tolq^E?~0N+n#彐;L $qzWû4#L_G;G44Mt4+,O }Of|ϰyz~$~I>*d햩=22)eFTͣ8j@3q?gV_[u-k>7/7.b?f1>G͜ޥl xK}QxB[?mPH&&4#:@3q?g;7wWI+/o</zSiFj Vgf9P6`½3Ÿh_axZatK|!B!A8míG@3q?gZEmޭ6y3|I6x^K[מ ϵk_F(?/g >hw|hF8ƏwwƏi3h+wwƏi3hw|hF8ƀ>hw|hF8ƏwwƏi3h+wwƏi3hw|hF8ƀ>hw|hF8ƏwwƏi3h+wwƏi3hw|hF8ƀ>hw|hF8ƏwwƏi3h+wwƏi3hw|hF8ƀ>h-)Dr# ⾓w|hF8ƏwwƏi3h5ω:,/HAakr0Ĭ9r;G44û4#L_@4WaL4 -"mEmlF?5r6Ggw|hF8ƏwwƏi3hŞ#u{pc]d˶2NJͯ?/g W-řoțnH5B,bR]ju nd睠sY?g@3q|L Vwf}}-aè*Mj3#;I?g@3q}''s(3 ( ( ( +(ͬ#L muص+1c7ȪdVP ¶u-8/GrLKcOf]wG?Kex$H7>iv?u-8/G]z9#ߘsKwtW ˮhI=̺#Ú]#Of]wG?Kex$H72Z^:k9J:ex$H72Z%2x$Wy[2 AIe_˃wnj_]e]Njc$okpḿYv 8[ߵψ*/ԿX kWɹ^dgBbR] ?z÷~1M4QAǨGso#uDd\`_g?xO .%ߋ-ec'dFd!2{O⷏|I_.<<|)ʲG~j/$3[+tFg?j]OX-oæk:uh֖Z7taPv a ~xzu6Q<د$L8p)M6SiIό>뷾2}ź)F{8\ZL*a`~>0~/x[~%w%h$]a+< m# :W??>K=keY^niGUC7Q a˝CGĶAM宫&XԆTCwԴ}iV.GǾ Z7{{ K\zo\Xy2RVF ܃Z_=..Wo/0~9|T:]7:iO dh(9?*x7 DxDsm A.Vd6s5~ xX^W_{ żB R[0r:V_gOi!:ן4+X#x&Vpː9沓jRlߵEbeYcDf0p6|p~$֑/-ѩ6n+2cr9~'?i( ?h<|"+eUNÀO|]I{tҴkGFω`c@!UTpKv#; ŷtY im?eqzmL@4Cd_s2=9~, OzxK[],I` |f͢N7@6ƒ;vs6CDžvmXқ\G·M?F7'ZiC Xۆ NqBM]wwIK$iK_xC- m&;#r6)+Լe$ߋ<]់,P7v>k)1%EbQT8~'<'[7m&m!ҴxT&ЫBq?mz5n? i|Wh?zq^2x@𹌱 J~G>Կ&;K=Wkڵzf[1/!)06s\Χ9@oxTmUju} e`Svsk׈|IH 5#Ŀ7꺎Ceb/UpȽ9=ꕝcuo7uυ-8]CCm ;7?߱[Xx'I;oZ%λ]k 4f_*,! Zo?c–Dž:5gܶ,K|Cv19?_!]Ӽ/kfm~.>aŧ]Sewˮ45_>A-cb[hd "[_f ݌gW׃Z5s @HAo+]c -Š(4 ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ?x?wph!޵љTE+(9VUێ_?Seh˓:*\O֏?Seh˓IgKS]^C%4ܤj`G14J?hO+~ekU7d3pWm3?(?OP ?BK.O'G\O֏h!r(?OP ?BO.O'G\O֏h gQB֡G%'i}j_ry?Z?~{E/<x|4 ?(?OPR˓'y gQB֡G%'i}j_ry?Z?~{E'<x|4 ?(?OPJ -tϸ[B+LˏOF.?_ |A! ?BJ?hO+׿~g(bz9Zz#/\X3su/<x|4 ?(?OP^bzŽgQB֡G%'i}j\X31q=bQlJ?hO)ygv^?G#/A 'j_dPabzŽgOB֡K G <>Zz#/\X3su'<x|4 ?(?OP^bzŽgQB֡G%'i}j\X31q=bQlJ?hO( ?B{F.?_ 9͞C G <>Z~ПW1q=bQ?G?Pry(?OPx|4 F.?_ ?~g(O6y%'i}jQB֡^?G#/A! ?BJ?hO+׿~g(bz9Zz#/\X3su'<x|4 ?(?OP^bzŽgQB֡G%'i}j\X31q=bQlJ?hO( ?B{F.?_ 9͞C G <>Z~ПW1q=bQ?G?Pry(?OPx|4 F.?_ ?~g(O6y%'i}jQB֡^?G#/A! ?BJ?hO+׿~g(bz9Zz#/\X3su'<x|4 ?(?OP^bzŽgQB֡G%'i}j\X31q=bQlJ?hO( ?B{F.?_ 9͞C G <>Z~ПW1q=bQ?G?Pry(?OPx|4 F.?_ ?~g(O6y%'i}jQB֡^?G#/A! ?BJ?hO+׿~g(bz9<~ПTO@K}[kO F7&gs/;{7#7/\X3s;$_Vz*-+#0- CB544kZЬ#Z֬ݥԡ8ciF؀2px:WS\ w.~h<šeh|,_)Mz(6gY,no 0N:̧ *+3\xZ4H'D(Gkxn~orZg0wo~?gC[Þ / 92"P :1I[l}E|ߏ-㯇:? x~O6<-% *O̤ ϗ_l߀x)]]ż{BJyV]2$8۳BJmiᗆ~+[Ú/% M"DӯMoU}k+ߊ_5_W֮i.iJ%0#fOMϧ@QEQEQEQEdE%'GMՏPjI5B+{ƶ I4UU^~$O57:Ϋh/ KR?Zh~kW']754{g]xXyUW=Mz}08]&;D:Ë{dž<\Hw֏خ=Pk?KfI|>Ӽ#ޗ eh׷kSXT6FJ$!T*W?(5 CKUEr`#Rcg5X#Yrx"O ˍzPخ=Ph M|O.)|_<~aU[y䒥}䐣h3]}K /eFk uD%K5nJ`*skTO\{Y+k??C .K(1y$n_NT2t`~>:g֣_B|&elǽ!I}&\{Y׆g4 o\ܾ͢åmo:nnum9P89Lia|G8iusrɜH\2UЛj+_\{Yd?k3 c2FC3(? kUz0`IǓF_,?{io/V-D;wW5sAgGخ=Pk~6|[Oi7~ jU) !Fq~"Ռ.J'k>xo03(N1QI}&\{Yׁ?ůh5hmx\}} 2H@ #hbYN5v'ľ]|a;hӄZwWWۘDjcf:1\\{Ygh[XYUx2(?My5kw^=||m{4SbT]=:W}sN?_5h.k&\{Y̞g ++wt\!fiLDxm:M}&\{Y_NgUXlt _;^O#˕QmDx(np- enF>obCW?(54u Wv @},YWKG1v7FWzHdḵE_]2W+;FU%wR @ Jt{خ=Ph M|Dh^P_ csCmko=Q0YBxx=|5jesiڬ],DT?O mգֵeaǰ4H֧fAOTѷX>\?ůOmJG7|Apf+l%(wErpz1DfAOH׺[_ԀIm/4߈0Mh4FYI;QANy3XUÚƍ5巶)3#q58K,BֶElDDRx;OսZvK}'[|<%\;d`DAfx9}.>=횷?Mlտ=k4v |M=FE흝:]FHeXFH[3Ήmm1}>u);vw'=1Os$u[Y-Ymy29]?#?0_Ms_6޴>+x|;|Nom2fݷW= 43Ou _&Bk?\ΡjQљV󬿇=&xF:;Lvl>0J[v6Q@Q@Q@Q@Q@Q@Q@Q@Q@s:Ԯt 7Vy7) &Kq+"6k@خ]Y'CZ4^|hdK*yW77x۷6|a+Zx7}{Z~4<6ۖuy$.9z)%mc~>"~<{mtK4Vl;Wz[ض{_d531_ɩ/oH3|;qqھqVG7,|uPnC51˖[[P%C"O/B3G{<1wv3폌-0Zh ~08+(Z+!=]QE((((ȋJN`gU%R%'GM$%A4eh&Pņ&,Re("Ræ;68+)N@ χ?qqȕ;|ǂ<&t|zLBcȸ0[q+88^O ]lұx? ?ß8JǏ,ò.}ͫN#e= Ǿ;tcOzikl y _qvڼ?>~1um2GMH\6}k5FQ"1 1?@(xsE&#p˭>"|+.(aI ERgo8|!5W5jZzi֊LۺE2 s(?.[?\%@(j];I4kׇuzm P6cڳ۸Fu',`@c< xJծu<0w|ę11$_@(xsUv3uUchl rÏ4׵X(o[(?5YxsP/ÏG{%дy#X,Ay;w3;VrUäLIUڧ98ZƳe]KOEΑhdL}ǃz ?ß8J?>"RjùQz΍& J]"~cJ# )8sv~??Okm>($m+z/-GiǏ,9@ χ?qqȔ@*<ݠ{OĬAܺx (3Gz ׈o56˻Dws^.?@ χ?qqȔ4|:um.+fY 8K$Ñ\W~3~ Ix=ğtźb&^EѶQLJUל6C&mbxw–ޑ;ud2K3W_Q6}q$>"S'?=-;Bÿ fSӮcĄb'ep7_ _Aơlyot7<7vv8W.?@ χ?qqȔbnQpW~ԆZ|n(Ѽ1- xӯ+Y݉X_@A=Hu i繷j30Pg&/ۭ~kE@|?q?|OgLa܃pXq@:;fxOӭ|9ZA%B|x0-p=bϾƟߊ>$\5۽;/|!Ie6ڥVݛF: _>mu-+ Kk'Oa_F?>_1G '}#[F3Og߳f]:T#H$f6qxWe3RW›5niucW dum k'lfG8R?>_1O[VѶx j^CEα)HqY~ vèGiLKncmJe$O/^q7"C%t[i6^ki+#@0Aտkn)e4?Mȵsºm,UTgm$xSL(((((((((#5|cM :moqq XIUn v>~-gԬԯ/mQG=ϱ[J7)C` 3W=ΫWShf+( VC|yRSjOdwXl9#W? P|6m%H_-]NyY'ٗqp7@_m팫\)vCH ?k:%i-%Yr|2Ԁ먯a??_L<j4.p$2H^5Lޑeh:-X,yqH#P1Xx$ĺ>WS]g=[ӼE}_v2d%͜,mRA[+_;Z>Ok {^ |UI$ZFnW/ e M|]c>5?]iV֖ڐuۘ[!Lpz/qSU c^=onsqGI=y-hl2۪FX 75\o?_+g__Ə#JIF^dV>Umۃ)#/Ac㯁S~>Y\隥 ,S/lg?Q4 l}?ziב`OE=ȭ/?|9Z!Xt]g.P rls֩I k>Caw}bKO\\ߴsFԂchwbSپgn ?[COxCo*->&j^"ğX[mZDl- deps<=w}C,\›vLJ,ˮ5mYc8u[##k0N?gn E(e(?wU(Yۿ??^(e(?wU(Yۿ??^(e(?wU(Yۿ??^(e(?wU(Yۿ??^(e(?wU(Yۿ??^(e(?wU(Yۿ??^(e(?wU(Yۿ??^(e(?wUU}QI՞3k`$oزր,e+X4 zkֳlk*.NI'pm~1]|Ki 66V3Ds?hx:&㹴JY2N 3~] &Ưk^ht.  }+6%xݵ[}Fnθ_SEọ/',#xu? Y< s{g־-?_zܶJW6[ǭ?O!_}c,_.U}H<[k62[Mqs&m }!T=5ꟳTխ|Mvj2Ga7_ Ur9杺N?wQRG,GYۿ?¯Q@VHn,on1FO-ElUK?4o%s^<_:NFԴyLK۾JᲾG|YkFF5-A2$a_U#J[Cr$C賤C!qڏkos_&վ'زm+m|])oҒﰞ][Ye+?/eڵݤsGkYjp1_=x#S<):=Sķ,um60h"g{[1ҹgP_/xWu޲&6Iw-Y{cxml֟xï6s\C`#`Lv8+i<-㈴{VE;YUhl1pG`Ye.𭬑Ip eBp s|Cx/JӼKx^YHn#U! A~`GjeUwFygn {FGkBl`Ƙ́<)߈Ճ]7/?f_%=GGa:~m%$$E*Ǩ$RiH{-ObKEG;8u@ʌ-O_z<:Gj$\^|8|/*C:-D.w֗&%](7cƩ; W%͎x#(wgx7]ǁmwk'㵹[Dɳ(͈G+*H,I߉5u5k[xYqḰ#ZI7'_|,4{Ph5+Z("# <㡭!7vʌe6ve]7_m3E14# e_xOZI}>h|;tVŭ&' C9_z?xdSi~J'j5rsGztݦ*tKJkVРU,٥VX>TAG1YڝymȪq0$Һ/wj~ +LMo |p9asȬ 3c@IZLG/<<k]ڝ'-FUX)em0"aBg 6[[Y9 tʃ5k_7Ox/j^3lQ4 M1El )= A? Z4*ڥTw$ ZCm v~0o崶C2gL9.+pXWvEKO[Ln.ϙD3T9m䶖sA 郃#k#7/mƺMՖn &U$s<'~o >olluunW~f[<{;Ӵy-DC:`A-<_Kim1>dΘ89Pr0x^]{VdΘ89Pr0xO[Ln.ϙD3T91K|/C4?xM5\sOʀ;+O[Ln.ϙD3T9m䶖sA 郃#kO7fo\IZOgeCo}HA(( }hm䶖sA 郃#h~y-DC:`A Zrր0<_Kim1>dΘ89Pr0xO[Ln.ϙD3T9( }hm䶖sA 郃#hy-DC:`A Zrր0dΘ89Pr0xOi[Ln.ϙD3T9( }hm䶖sA 郃#hy-DC:`A Zrր0<_^im)>dΘ8;9m䶖қsA "`A Zrր0|a^Kim)>d&T9m䶖қsA "`A Zrր0|a^Kim)>d&T9}y5DCH8;9( }hm֖қsA "`Pr0x? m{xK Q]>e7a[rI ( }h Z >eiׇ4="éZ[Ȁ+!\N1 x[6oMZ9/k6mY2'}W5k6$&v8U'=W:V\kkۇ Gf=(#cX O _it4zrl;'c],{<9l-wK 3m{WӼ/^|֒~c-~HQ[%k^MeD4AUQaj֒}fyIra( }h Z<x#ph~`Zl` XwchM[\ӠVHϙ+q<{VǏ>#x[^ߋu/~b.d#d^d}+'_xZtiZڄsبfpWy xៅ|Qqy+U, |Nυ2=׆lu m3],Y]XeD`2$g=ki/MUnKG$8= SB6?1~jQrdd$ bxgZ|E$zlwZh,uTVi%>]P3}K<'_HC…ёy[qpާv>O{)4gZ@9EyU߇ ~*-sS*?Ɗq0lqM\|)ZMnK=NFPzgav y$( }k˼iM)sv'cM𵝽l/i hLRY;\sVKށX>Lz.,$ gs]~q^W?쬮\uyT11Bc_ a#[,-SQOcӾZ@pח;}ⶶRg\?n5}k~EX[X~rz=*_OS|h7R\y/3 t'o>j+^) ᧉUhVziZU6w&rx`|VDdnVT]L T;7\ G?jz ^!t_ZitkF׵Eqoܰf#.H jҷA^&xGw>Ե{m/"[ZLlkZ1EuMKĞ!NѴL2dQ$M~~xf_G3x7æA{{f>Y&0@^[H?f˽ {E6BVvĻ,m!qBB^} z{¯ -SL.!մԎk7TbqG( Px Vo@Ҿ,h{=WNTbFX95_j^?&վj~'<]Ky<͒ɷ9ٷf? ZO?h'eo~\> c4$Fs_r럳_? .Oh:0g>㺃c<_Ǟ5$Դ{-$,E8%H=@VvEW/(La2k H'T+)ÙɯƐ*߆ ?f?h>,4jj%-Ο,2G?kD>xkX*_P~ͺ?Ə% x iTX2 .ͽ>{1/S%Th/>8FeUf*:Yïk~*G=+hKcq!;dY9ڹ8/%-w=RV7*7|U8w_~m.uC7fxtw‹xaw#qsnB%K0f#'&qiE]߆ f_>,<_%-Ο,2%[L }Y_1ZtxE 5 kw^tV=F;8l /ލAih1FYlc*RXb׎߳[h.5=3]4?Gg\`ʗrNA>+uqo~9 >jik/?2yl85 |=c4W2%%,I9$ki/Y<e%ؖZ &JrmH+韆 xV -MӖ)EL=V/3u(XQEQEQEQEjxu$ԴI *8${z1VjZ/}(hwӼQ%u_`vg|ƿPsm1;O Yq[kc03 F|/=Zji5Cncw$|$M#]-٦[4mVb$9p% jhzmeck $QWki;%ʒ (G|lxO=JNKKcj3IvNV aHd־3XS[AtGS읛;N[c+sLqE}K>5x~ ƺ,ZGm4 YYU ;7Ɠi~]-;Gi'1+ OZKGt<(7Idoo䋈Ͽ;6J:]cmgWѣx""C h$Yc'_Z+zmL TBc|B|UgjİMtJCkknQEZo0__t swcѦc[ϗ|qU6bAc]3ß<{Ě Ĭimo#Ҳmq<⾴g|/x/JtUۦ^Xv! <+>1WM'G5-WFpncx22KK0k.Ɵ~,j4|co'5/sW6s:]]ZvݧbK `Gz7MG_#~'OkfTJ'i2LUvƙ~wgG<?Ks1%5x:937%?5mro$V *MNh x{Ú["[M3ᵍԎ~’?71n>z/J?6_Fe{Ne tPDҸGEi>Rs W/xFдx2S-W DX1\(+>xsY< m}S?g,#Rz1$H.|vaY`4ЉFg3͂|'wؚk<]Vx??Y%1sq׶x?<}' Ŗ%"m@quuh|4I $dnf$2I^:>'FOQs5 CHēȋ$n 0`z;̳!1K.HB֧_&i~K_ooWe`kߓGϭ45]kUլMyB7*ï ^ƍ[Cq\IgRGSִ}nɣ]k[Vo-R+T:Tx3bڧt-I#XmMSlV=v [>w~MG4{}z]r-&=jX[ xAȌɍA8 |3諒4= XR\it6:T($ַϭ4kߓ@{7tZM`uA9fڀ 8K/v o +4+T7!yr+_&u[i6 G/<3uI"{lw {t {^-MXu)0Lq0<V}nɣ]@S9{ypdivQ,n`8ix u`/gĚf]_*-0,2Hl++u'IRŷIZs>I[%ŝLy>V8gz4 ;4au]#OO1[ygr[jIrp } f1O}5Ωugw6wcds\c=|- ?~o^w@ZM 0Xy8⼓B JA|P>8mkvj?a@/s3u4gφ>OcмOkaYQH7,X$#Z:n YM6_XZ W \yX RItvtW ?<[\Eqcė]Ot+coZ"~xB7y\7H[țwn+'K;ّtGꍢxRckuVe(8}BR6(28־'c7.Y/\Z;kB!.6P0p92S?_u#׎ Ȱy}W~~1$ >xnI><{Eԇ;:h?exVF F AOWeߐew3;h:WMozm\3fڊd`B/RNN_O<40^ KTMD*j"}1~|~x{[<# _to j660HGn%+ ͌Oڞwu{G'ֻ˦<4i}SBi^D=foR#n5h/Ǣ7gQ+C~)[=[DLil<b|fR>"7VOo[ꉛX>YIÿco#ֺBմ_-A̰fT^_gK5t|Qj Եh~nR8neǮ@&3|Nl~.Q_ŮvVk!jPFweݕH1_  IxŽ=3]-um@=nl+!*9ּ5τZτb!Wd,N%h E!۞2@<\?SE~v|:m㏄[IBBI..Gu%+bF;Nk?g}oC)ooJ^"}![xf'?{` 0Ѥ;~(Igbdp?u5BP3qua5аYΡ%##'@Ex6IO,HV<3jekʋz hh.ggA𷂵EElm5ͬvH.Q\}.}d}umh߇a_g-k+@eXDy.H*8'57t};PZfg?5 ysdݾ1_~u#Hi"One)[#3?*读;m]G6'>O~#ѯ?!oj6^qC̈TpsҾHe1j úUFD1YNO;WտѭAx]|oᕏI -<6Q.)B#a6iRI ߈N{F.n9Ibu >ӯ5ُQblፑGVP܀Cs׳Vm.Q|6QEIGGLJ~x~ gķZ]Gco3O;V40YnݫI> OӼ.5;9mn,.PxGZ-tDYY#5ԇUKjw1|N񦧭~/w'_UcOF;8dž!-^coo~3kȵ{#N,e0?ܙcU3GVM!&KC|v\_,JW$ w_=WEҼ2j #_6t0Fι]` x4M/Ax4_?UҵƆ{;t bfWC:9gkuK+υiBqjP-jc 2b+f4#Pտi _em<5xc[^'ntQo$$,ԟ93/ kkRϥ[io)tIX 7y4xk_~W|D|/kI1 JH)LU2<ؾM'G>Rc^Yɥ$7&Xٮ"$Ӏp3KR|TҾ&R kOY-m@;=Ku>OAj7-CoM} P a1mou{ŝ>S"A;rdK_'?JÛjw-gȖ8>W8_<x]?ڕGc2pGjr]?Q}i7<_j#L0:<rOwzf͍cr9#w_<+HCzG+=Uvi>ūoL~RV?-6E,>u-+3DŽ_#\t'FQ_օggMQoiwZm|M흸Ͻ :+=a~ '7^XK-%Ԧ@FA9ς~#5kC_J[mI2̫$F%dW4^[Wgi^ןUֵKg4xmX rRt[QXҮ v7F ZMzy_ǿ +|E}4M^_m `3Iϵto îj \_ 6E}nS|i#ޖ=` 2wl. ջZvIpsȮҝ )QEQEQEQEǚ|yۭRL`py`smmvLvb댯?B+h^bJA#j1nVWEܓ7`_g1xe6kOf|ln2XՃRZ._?F?s|08?ۺNAݫ~c}Io.okhyI{A T;dVS[Ԗc0cQQ潯g|SOgע8ji2DDm.A N*w_7zѮuuA$$9$ܟZ u ^\x>Ζ2`8Sjs :.4u(1{ mrFsYvN2q+Ѿ]=WHm嵳+l eH"`i_6 6i}kof?uvhYH3b6}jցK/St{)X#.,@ []jχn y♁ČB?'$nki{߱i]eB*՜#U%?%]k>oWyټ-CxKÞgJjkh!)]|C. _o9[SgwkM)l~{*d/ɼ5zM}67S۩igd@NHxwmm, ^Iya1$u(Su]ߧ\橫/_>>)'5:Q4;2C= $F D̤#Wn|G"ă7J}}\H5itؚ0ivx|q6.|wOun"\At}5{¾kxv9$epHSxø(ԒNqRj*w~u= |lVqVZ7^ [nMA.z!Ki7.-#XVӢ4^6CuAx XE. (+䵾בMnd=H ^**#KkCMisifJTF00t`qCx{a{7o,#x.-lKdrKl ,āI9_~}zCKx 7Qv[<~Lu\8o 5uR_ Y*:8/ϻߊ [BO4;FQ-ͬ9. Arl # ~^nt\j1s0bʨ@''+]GEtrP/ʋZ=LW]TD>!E~?}qS=޿kϩRV46Ȓܪ{pk>9x{/ſi>xbxZťLYeC4vx~_q*uE#̍P+~ %|&ek*» ORA/߇5XME+{I~/# V~a}Wo/b?~?~{CUG/߇5XmE~K Xȿ?B~=o? [ ?%X_u> #i4|C?<ݛ:5n?_ z Y5EE"3.2N2|U2mgO}FPѴ@/u)R30 fN5_ umnmvr2F2I `Y]6w/-4_OJյ VS5]Ga[phn'{[kjz-o&9hRI<;}J[-'KӶP$) 8UI=O>}dX[tr*{iJ~U2 縵h|\&W4mRȺmbFYHemd=_Vv׵iMOH>彻Ѡi՝,O5ܿ 5_⛕OF$R?YG{fxv%aH/&4,{* g.mw>cX>*b V? W^ eQi[Kvס5)4_xJ%i^$KE) ʤdڥ8Tg?k:89jϺ~=o? l7F#gݾ-sdzn;=>9Di%K  "ag x?`!O.=/MnS@S_{BPO@My$Mn^|2cմ;; -a5MՌ)r@#o|+|?.IՒ/4y43 ǁ#Skܿ:iVij%o0k;/>#vlTṗh{NO_$hdzz{\5iHQd_c~C֗XMpmmԦe%P*89=__1O٫]hh4{}N}dŵv @q^[_~$ ^Ѽ1VMJo*'s.>bڳDCX+JK? Cԯ/d,/@ȭլKO3X1Iٮ>7L];iK- B"=+ []Ѽ j$y 7;X9=,nt놂[YE2a<1Qwb+4;OmfNҏY[C{5Yk^'=+O5h'rt,hB 0tsfˤ_k-,^D x2JD{sZ^^^Z~emy:rGA;91S? Ax[wv%N=׵eIzj"u~kί߃?-cHtD5A !P߆($vnh:+iۭJ}B"?*Sk:v6/˕'8U' K 4dH# q UZg\ΉڎfݑF0 BʟH~#_QLJhr3 G4,BWVGêVf-X|?{τ~7ڎY[FX(idtA%o^Iqe&9AH|88>_x⧋FisiVlڵ$C5Bs N2s^E|?w]._ XcPSTRz_ 쟳4;CZ|N]f[wE n]M2NOd$T:w=pU[{'S|k▛k5 kۦ,70͸m<X} *]?mu>{OY_~"kV.EOVf̠0}kJ(EPEPEPEPEP7<7_2/Ԯ4]V=)Y6;Mpõ}K7.+x{NKuw9y/.4&~b3J9"S@o k|RUFW{%e٬q)9YyUnx:u#F4>Xw|R Q?J(_R@o hS@o k08{/,~}A4;ŲK?df$WHI"ma('bUefZ﮻#%%A߭얝Oi_#aԣ"X\~ui?'$i,9  ciz ӏ =?հU^gvZTqt2,;SyI{y1fAcJm'ݵ=oa61ê[PIՊv UWcuOƐ~ʟTiưU[FN}%ѵ+}SJխ/=:{qp\I1\zWJ/oK7藖; SFF}k?v|:}MC~iZ0PZ5=t]vgqXʴb}7,x<ʚwxT:H˜mY^ uYxTHYrsqӊ?eoӭ:I Ͷ\}?oSDnWZvV2*+ػ%m%*|soįY].iNmpp;cҮC|M[8ieҴ guxN.'TGX˃@Ghpۆ>mk`v7F[W&п&獧hw+K ]m8iW82k-w{s-w YJ Rq޺+G9Νns6>ZC*E :G5цx;"-5mf52}IϺw~YjִK=Wؤ##>GW7-d|~XH'O5<St*^UAn/#;=z; {\E w;\dL+/x2B|XiMq02ɷj$3Ȯe񜮝n1j6xDb𾝭lZ_ xXB3]9U^:i5 ԣ-KxĚ o hcM2>, A"Qn.`<$)j3}ŗ!l|z-BSy$y۴9c[~ͿWOxl) B4G[x>SE.yw4I;/z1iM=8|PjWT8_$Ǡ2m~n -#C)*+9rXGO'-2?g/0(ԫ{界b*U)ɥe<e/ Əe/ ƽ?LގWs<e/ Əe/ ƏLރ?>3(_R@o hR@o h?__s<e/ Əe/ ƏLރ?>3(_R@o hR@o h?__s<e/ Əe/ ƏLރ?>1RσA"'Mb=J)3)~cPx L%i] Ǩ LT+|GLӭ> >#)mxʦJDm;]zuMMnm4S#@n|Wm&+A봰8_+U-#wևm"JFソy*H^=*ʿt`}EG2n[m_kEFX{]od^#b]>GF[[׿n—K5$,'m!&]خzgbFEnW0f`l@>"M6}/#x Շ.Fp:rOz~ƯFuG^&ip hd°.Rӵ?9x>+eNTYNяN?e i t|~?O[3k·ԣR3}vz%c-oڤNQ^ v vj`~x/7ÿMCDΈ癣M#Aڽ`=HEѤVմ;hvZv cybHUE~#!}EcҷNltgח1VFwQhJ2w[KX˾oTuZ[Z3)cjN@iNt<>ni1PIgG;5߲gu}o#?ք#(j}8Gkx"ê1Ekik{^{IQM+'{~ZSTW$Ѵ5qX*Ù=_i~VU+k+w6$AG_e4=H֐~ʟA-lS(Y]i{_98)_Mu{tKnWqxQ.$yz2 7мZd^#;k#wry&6dyt7[M'4Bkc&RF44kZЬ#Zְ~/x kα^{{l컂Hʎ+p먯M7B>(Ӝ:ώ46=ond|oKx_ ]ϧXj6E,wuǎI$!X18b=&ououkLͬ&QHF'۷m ~xO%&JWZ[my0)m?{~c}?E|>/u =~=čj}B]v/XF|/IP~Ϳ>tNߞ_F/ڿ]t-&U\^$nY% ;]aq>Xi,VZmϋ`Twy!b* |Ku?|-f5'Ȼ4P7c':s;iroZ(Š(( h uߴ;|񦋡XiAydד^N)yEE@I-@H uy&OC̺mdD"A*Jͱr =ҝxƿïC•LDc_ӡmCe#Y<5Hya uϿ5}% w6(Ai"3-}$"5Puf4}~mG Ejnǂ&m.|i i-_Ftupm 3ݗrI1o\k_hz u :4]?mr@Hw?x}*DžM\BХW#in92/~3ǭjݳ[hlpҴpQd6Y@9; jW.M}{|>4ms01A( Jl-"F[ w[g ^Cj#wgp""J2IFg$;AmwS3^7Qw.ui=ŵ*yh# Excη47|ϳ /319{~hq^(kQxz %b]*e.xj^k8ݤg>ioׂ?YxH1Nx㚂ڋUǂzxH/>|;~M_2F)&.6ݤA5|b.5xvv3Ke&-_"RG|#xv~ {-3$6m (yBzN/߳'9w (y\3\WWм+/FZ){>-sKљYôw9>!|A5Š~_ YjV=6 %)ýf'{g/[Zx>[!ho? jO w> +QL<79-7@sO>x5g۵Ք3Aڼ/дk>(s&xsj)#IR:?+`֏ߊğx?<k0Էזq(Ĥ$8t=S^ ;;K]~bHd1'W|;<_*}_<{|=)u3ʤhUvS9^'+oj~=啯 ooDԮH⺍[uTEx; :H,#H>YdRѿ7奾{yںZv~ PSm kĉ l\oʎxu<_xCKo_=-,[i', vWǎy]bUGc{.k}Q%HC+ύ98~64 gJ_Vt[XV衐yrwIn> Ibݮ/MOC _@ѭ5.51Ϝylm>#|s6OxM%R]KTueQG1_ :\gI#^ D[y* #*vX+]C?jOv? a[it-<UNpOLS~'4 ╧l>&6#O+E gpeSEQ!_xC^!mVz%pY}+.;sNz٧~П iKxd{ F$Mc Au?~nRoxDѮu7ӣvg(*ٝB [#⧂|߆gZ4 N]&IY6mi஁پsZ]kJz#+WQx^?|%|+_k:\yM-.U\FOp={xKJ!s1SP-is(S;kGƏڿEoBpxZOV[N$*`qG_3 |q_]voũ ȨΈ3 8<3/6?|*|ŪxK`g4|eݤ@]ܬqՇY|O 4hxXvX)'o=AM?K袊8^2=owV;TӭXL ,8GVZG>'^3}߆Zв"tfPɃ<*Vx'Y_lK< Ɂ:P_G[O?t^е?(;QYZ)hՔ~h@3Mi_OI44]0*񦷦Lk>!I6X<1{_jM' s#"c֋WF|F֮%PwS6T[`=u~>{?Ÿ4?^!VF,UaO?i/'գG}3Ñ$Xd ~5~:Ɵ?7xg#|ZIw4qqw 95??>O^G\j'C ͊2nXqOK [[OھyK:_[E4}P,} x?Oĭ'UF8n%XB9XhG<?xVi>=76Ym.$ {7 K oO'o}Bi&[i"$~[xoxNF}?ݴ[-y<˗\E`3$Om _SxKľΗiovqʪYOk濌2YO4}>oO9VOEfD7Pn;V"u\ky_oDg2##4ҽWu3]7/?fhi¯㿇(w hΗu-.18-#Zִ)6Ÿ[.i€G⫴Vn)NO֥|T;/! 4>'IBe⹴Xmn(TrS c'$W_KC+b]$n#c?&eҾIKo|ga^𵾟(4.ܛX i-#ʩ`F@- &k"Q!SVwWs5k+t((( uW7:μ?[ǚi )22d1M TT髶tPSQRZN_L7l{n1U=3YB$䝹q8ZFU+[+_bZ+/!~~">y|Yh ؝F@ڄfn ?1 s+uP|9$? u}4X6lLEq2m2Tk;.~s liZt{߅ΏKuu,zЍ.fHFlǜWUK-~&/ .mg W Lhi(s#Z+>0|O5K_c}j,BzW1?|_xxWQӍxZy┅+ 8*O+Csۨ_x] etQlKmoFiCJbQw$.Mg|<|L:μ*]OI,2ȁ\@M5j+i>0c_>UwWKkwط ؎Wkf&WQ֮CE;# hKnۚI!=՞ 6꺽C|'_:, 荄8Ui7,eI٢B1#4]+5ګ᷇7MpRv*Ѡl弹EvXR\px*_k>-4.7-!8Ct]Hdcg0ޛcJ+?ڇ߄.xC_s:T# 0uF*aq'c2l%rپEW 0WbeIFj_gDMM54+;Eqk`a Wdğohڭ,2t"-nϭ.w>ɢ7oֻꚞu>եl i#B}@/I@mTY@GF2w~ _|/f&sJ6Zc߿zNLml7Zƾ<ipůz&kIs"ƄM:Wk]X&ռ+kX$y*?y #p)1؂1MLvg:E+㹎jk: W?uxVI&Z9XD!uh` ;IgP~>|nu'Ԭ,K^X/LH"HG#h|wT,u^i#1TY?)WRH5h|)xk16>O=$O$k~e7_3u{Ǻt+o:E!Y$0Bf=>~ .>_F<+s3Be Ífxٌӽkhz_ hOa}A:Cզc"G)aTvVŠ(0((j;I"ڞcgKKM'Q'MgU(u5MpR~ԟߋ> |<׼5Y`ԤYi;(`6XxWc+~3~ӴG3xKҞ)cYNґFq ܖGx|f5j3hڌ Ỳ` Frt#R7[[+ui?n!SX$:ͼm3[̫op ӽwK |=U֧m/N BH v0{EQ_2|m-=[Wt4vK%u2)L#ln(N9> ў1W۟>*vاWMszv*' ĩӞmjQL=E)\_-K:oJֵٮƳxАwTwg־/| Ox{Fş @-.4Ϧ_H.3\dQñ⿳>'x>0χ.mVK7ufvd$ڹ~&\~ҭ:}O:Bc_$̊FGcJOvay]l}y|"_7^b{ 643Ui.?cr:ǘ~ sB͏&,gЩ-[LbF8ɯ(Wi C^ׯ I6[Yi5acveW Ms+I_tB&?ٯlmd $2*z/Zg|[WsxÐKR`yRƛc >s=75?hxǶ [m*YxQO41³mmTF?ֱ- !5٣Lkݕpgnq?% vo%+|UV WmMU `tJ6[Pȋ"2289 rƾ'7[L@-u8+u/h:ru smqݖ'P؂_$~кgGn|1-;? 2ucQz7"!@ݞձq'G&惣X<9KYlAFx}\~o䝇g{//"IWe2*J _/5ns?!FI~bqbO>*gkB4EhP>Se<~GZP-,Fn8 ~F6~665ъ͑w$ ~>z _Pt{wrsu: ư4o@` I_U_a7>KHDi# \횒;K xG 5}6IS^!brp־ģaD_Rt:ڗ׎|w;º4k+BdPLRUHnǂqI=k^xyzU)A⯈nB}FUXl18\Igq". <ZumrNXcy}|'nc߃,-6ɒuY=@׍_~+ho~x4&\kzEJ%t㪱b q>ysV>_~0|&A^~'Ne}գ;IGU Lմ}Y]b۽Z gR*8o|"هJz׆C֫n<B܆g灇AǨ߃t~iXs's_,n<7/<+g cRBCn`3!ݎAQ;_x4i|MukRXn)o_-]18lW]訶cڗHD~?-k`Dic|Ef1[k_tkl?HO\ש$L0 }J)^&/>OKn>~#4KږaGQӚK'tGu .gWO j7^$ab%HY &@3߽o/ } >.|MG@|PI<5|E0BY-2[o)"%>8OZ:ni1KڲU7n:(砭|s(?⟊/`m-5/ ^VX݂ 2rr= pb.|c_z?`[}"QH>ҏdps_i;Ե{y =KC>|J»]2%ǂabXġV$z8txB&Ui/ 5=Rwg#2NváNkvzu"T4P<N#/ſźR:Af6K$cz({l{k=8WX1xFTдh[MV%Nn,ۜac?t˛v@I8+ܖ8OanAERQEQE?gh>*§/ⴷX Oך%QH _xş4^Ga[V$)!qUz⾩uLڽz|E_[\Y N@&J> !נ'\wO M[mיE-~!6WԴRZKg?>(iGxl|Hޱy)Vb,WBepڧs8׾*|0χ4?QG4J}\:6Fݿ/>y/ZVT^mXQ~|%ys(<-xϯi1\.3rrMw ]j_!7 3J>xP<[[vdo-vgz)Z\XOUop)㟵W|{/?Í PK}wne n2\ٔ1UR$ ̹5<#cZ: G>k+)o5a׼ P^\XPqL[21jM^@yߊK z.w> &G_1|DR'>]Sk|{czΛOαٓky_vO?U|\C>v*N m1[#'8F*p8=k_x)hu~+}lcoQ\&HgWD|OO'Lj|^1tlv%[_=zѮKI'?_h?~ hW^7>toV q Psؠ.Oᯃl>,!Zi|qqܐI=&lFZ%?xE]G5Mkc44kZŞuMaq7*l jm#Zִ)6mkm#}5 MY;Z^yukz+ڬ5BCDHq)$WiEy޷WM>8otqqf|cfvGlW>,N >+ ׬fʿڥ,WGPOxC]<xGNI4}q*2qv:W|Kỏe3 hIM}E=(gW& /ó6̡fL1Z| xzG4 lGo 6Tϰz )=w|'Q!NSӈFX+Ⱦ~ȚMѾ1;;IK&+}+<7 ݜt}EU++Xg|G'3!W|+e$ie}p,X9PX+{O٫K-7ľ"ſghۛ;Zcg;Xb|(o [Ś=8jZӦ-PCFIO2Fo=;<l.223z۽hxM=o(𞩪Al̖R I"`0P ԿfG2麷C᛫xt(b0* Y"\IVWE-5G8EQ񧆼axUF:`=y*JdJ(DR4:㟇3gPkM:BiFQA8F$BG%#GM!g|QxkS! "fGIGR1C*1]{G"=~o~=VӐ,ڌli]#zVg~,`W85(L%o5m{UIpcLvWq }lExy^v;i$[^NG̿7|N7?xH|=[xj=jGW0ctX`dWx[{? X1s{8-YA$8d[c8'?|%.i$u [iVBt. 2);.~<_x{~+kM ^5XgqcXdFo@%-8Vש?i:_Bg]&km4(k-39Z%c##rFGR+'j.aMILV2m,ܙyHw7|c/)d.>UX0 8,3\?-o/|1{ᆍ&-|OgX mc 2y.v҇4BM4 hkF!Oh R?u i^Ҿ;]޾uPE1s۰̫ݓ߶Ou NVK-* /2!- >|bѾ)k3Ӵh.=ʂ)C8$Cj x.um4fV@upq[^_:ǝxVx75Wѵ-zK8UA,;{ >5i"|C GzW姉#v:?.G 7mrb7ĝKo=2m[FA{!axl8~~ /Ɨ ck_0_I0!o s2t2t']?CNXw<=YiZ|UQim죷l9QN xڲ_^ke_O9JJ] F+g's4gO~ CNj_.gbU[sdi~8]h 'HgZ^k-v3MŹ@dVWL,+-l࿅z_=bP7G~io)[m9CS9#| ރ/W͕ uro9< }ޤ2տf7-t-2 zycwvNj"1lw]SFGGJ[_IӼkVrS\$9qi~j>7CK{kom'Ԡnmv&|YQc_ 3.Hڎǩ47z\D̅p @ޓ}]Ǎu}SYoH2f8*y_*x'U/ׇ|MYti~Y]HPC &My~O+׀|e^[ kŲm+DZ2m*ZiM|Cn< jS6D,˙\pt9|._E柤]jI=io9v8>|O>.~ٖi:3ݗE:M"B88BIZ'/&ZE*(Q@Q@Q@Q@Q@2~>'|,/ú>a+ie8m0&c֦#⇌~< >j:W<<歫l[;X#k׿ |KkBwkHѼik7Y-<X K?_ [éi7XZ : zqRmF/_2X> :kXFkd|@]ğ}oX;[ov׳pzJO]MR\sxզiLeH$⾐n=^ axzk\Icn64K< ^Y d|)4e4/~O5sZi:ԵM8bꐣ cu}cGgçK%֓ ֗h H]$*FPd _o?!$־'Ƭs}Qm{ݎHs^e|־85t:=^; ZVlc-bG>^'CUkw~;~طkoSg[BӬ|MkkK%O)i>?w<}P_'~Ḵ?Ai\U.c {p|5ѭ>$6Ksf!bZp<@*xO#X]X~U;z߭뺆>2կ6R* #<_LfgJ_ Zk.Z3j@[q~T$m_wV_|GSǞ7n^w Cd4־~z [^ ?jz_ FHyq 6x!#x%_垉#K< ǫjik}fpcfR8q6g< ?tEka ufc+ƪ$6Y> x~ U\5W3xͯ*X<#8SoK[J,5xD7tk)X*_x?DwAUlt Z3됨Ky+받vV>?xE]G5MkeG?Y,/.Kk[xYh,ĞNj-\ $_:H jvZ~5&%XE峨q @UfX3sҀ;o>Ҿ[f.[5J p#W+1IgsiިY 0pk־ 2/uwFGmf %FJ\0@([67(QEQEQEQEQEQE7:λ2p=+JN`g_3>3Wš+I,leI3J˹R*ϭuT:py⪪Qݟe>WM sbVE9FJUհx#Ѹݚx/Ln<;:}L/;MKkyQ_(IkxCǞ!/"Z [[Q@b<|3^3kW;sSriia-mks&Dʾb8[w"-Ln$R=Eō̚<# ̺޵ p7K@oW{Кi Zcި6~^0'n'ψHX T(Vx957ƽ/ſ @uj `xcFF{(=F{SB>͢|.𵆵s?^S>.[G@$Q+EUB2[yG?D۟ Zt6 YhtYFrIWZuO |=CD'afKwILBB⺿ƞ2=xO;OBg1ʙt{ӳ]=+uyvo5M&JO;q$D}֟ޗ j^Wn|1w,qkʡUSш'}h5? hOxտ<]ݼ}wm0 ʑuHceFN1<{zv1_[ EP33|[O]NPIyA>Z((ZBǠ*mGuQ@ti3zޙg铀& Iy+;<'o C9 2h|V! gQ@Q@Q@Q@Q@Q@Q@ğ^Z>(Eo?= )42KVcG3??[|(k~"4Iu @[xQce7k0M$}E|~4o>+֓=ĺibf՘wHBOɴ?]#|J.u..H䴆\4P0EPYT}Gjvէ]s)g#c eOA__xKSim$̉k>LpCvB9f,xgw ";uw=xHG D&Y#J'}ZTQ_?j?',t3_TKZILrCjѝ%3|j׵Jx]񟅇ƚBZWk*ʉBRm/ފtQEP~/;:H1iv^  x[U;cΎSG'57}cOxŗ?tj~\dw6*ƥ@Ubn$ɫmk~-I۹ UqggoiJ##NGFlr)D j}E|~ٿb:uMo_<#ZGp-͖|ĝ8b\gRxKMYmf-nFTm; ?{OA/=oߴ?Qb-j Х׭gێX葮̜c獿zͯ[즖Y@I'dif  QPW{ފc/WM>o_E_iͶR*PI*9ی //ڛ\kg 7ľ8f ۘPJ20FGbj^_ gIO~0 Iqhk2" p0NS]gJ_m4^xgUmQ\m,.suة{3DŽ_#\t'F-SZu]X_[weu=˹%VV GQd39]'ҬmVmmxF8-vFR|W"T\+{ ?+FGKG]#K;]&?߶ ?߶ '3MnlgEF,-gy2)fn gW??Ž U??€/T?߶ ?߶ EP۳~(۳~(Cnzonzo U??€/T?߶ ?߶ EP۳~(۳~(_R4:_߳Mh^"LtGӭHL,ێT=1^e} ĝ'btH~oJINFԪΌJn&_~! >#kU?0Rg&=Q:sZ֯WӪc\F**Wlj'OI.B|5ojjY\.ͻ0b}f|OxV Ҽ?ki)U6o \Hpcm]_Ogr-q>:o"/z/>9WPYTI]^Si08, ˿ׂ5O {\wMT+":#k s梁[㟀Υ#^%ѵhu]Q#UKP#fi'V^ ;xE!imoK]+k[' 7ئFYKZhReg{qƾĀYèZsk2\[L$SDD#! $od7&Z0|3bTYcaeا[&G*4H~}뒹=ǟ1|D}BOxv\,/1_r6k;LtiO-oSopGT}ay#㏊w3ּQi )x@%Z[xkQHuw&o0+~ |/v/swm<%ΖJaV&@eHWu w (;S>ᯇjwq-ڕCq$wVrrKr)υ~!xç-| hz|x.k w|kdXMz-xO[AWծ.o.%wf OʲEÎwze%j?hMs~*>iVl:_S:ͅQWUf85iivg߰Nj4 ^=D$wn";ە,wΥ92|7Wſ'FBUȐdbc8dۂ""E ʦ}ŷڷUͫ}g€yT7.yR:wֻsh4o۳Mzօ>|I,/×¶FW g>3sFLv. JL2JQ|9#y?m[W[]¿GOxO 6 rl-nCp&ߴ/>(Þ񎙬q/ei)w zc@Cdw^K?.u_d83$^Wۻt_< {8<_7)x"+Ƞ^I4MhvW!^WiWe.R| B>8<x5'}'Ai6Vgv6ɚ"@:u}i:+߈?O|Nn_i&6V 9s]+Es7Z<7};+{kh$ܵ܌p 㲟\Qz'߈jRxgVl^}٧?;=+?ߴ߉zz'pƽ[ƿxIռk}Gӵ MK[9/*kl`O%5!vi{r.P<;4/PIT\ [Vg jϡxTh?7aûavwgfO"haymϥ\Y?:*a;B 9=EJN~>US|OSi |MC׼oWS$)/bu]@vQޮi/ m7F~)χ& - ŅLO"ncP޳i!W7"I'A3U#?E t8|cXC?{E4gӒ%wu8{i 1Ѿ5?ho,tk-z:ƎZ"DZz5vE{WIg<4,Q^Ss$ 01/>*HMgZK?Jԭ5 ;/G8|FPlm,Ƽx2=U_Tm-s2J0e@D[ۭK%%+k_;jx*O>4Dτ:&ӟHJBTiB~"Z-Ӵg'ʊy ,X_Awş11?'Ku>چ[iktrB63Az+ |آ(QEQEQEQEQEQEQEQEQEk ωt6޵4j@mN 1p}CA7w6xȓ\·P xWh|{վ&xwFOko\/@.nP)v,YA9|+i<]c\ZƈtHl>ޥL؀aG5Pk+|5{6gwz.=|M-KGxK(wyrs'ݧ>wZE&wG̿OxW/x~C5&x_R5ՊXeoU8/% ߏh _z拫IqfUҴmDyW$S_TSÿ |yñ]ֺjnݤt!)ۈ/<ZʟexE~*un-ıb9 yo#M[_#g?xWuGZZޏlZ\M  ++;N1Af.C.)<+6,P#cf.08^H~,/O%ZVNk\2yѩd\8,HCWKv, O#¼=VSמ/uH՟N-0ë2lW9W}VτZ7KYM/Ě70Z\ij3fW@̪v>1i@u~$KU}GK%v<Zr}]w_ Wj:Qlq8؈TǧZoj+=~^%] xƒjڗZu>1xM΁ǣZ}}ϰΙbPHVi/{tW|m-εZ`/n.VC0Q%x) W".Դ~1[kˇCti]$68en0ĜuZ7O}*m?7^tM-LMo4d+ f%,@b0Kc}EMb_}?ROǎt>A2x~‰mgor#tCssk`"N+:<52Z%Xx `k*f\|emѵfDKnʛ&kp o,+t]uFR<9/|)Ly5/ywqz$ w&%W:m&Ww_7?<> !{?lxzzLR3\$PR|Af|-jk7}F4i&v +3_U__븿w[e _?JXI*&"u_5{ՍWV?d뻟 ì: e <WTTY'~^M}#ٿwx!vKKt\x]~$h ua=Ư}P]7Q9QLH<:W^ʕ~#hѽ8a.,0D/,zrOt9~cxif6qDZkiOs?\c~EL /_N+tμ.xO{J[_|߂o闞(Ͽ %z7=EҮB,m'2y6t45fѥ;Sqψ1W_A>1v>5[X-_1)YTNwJ M|LxV]^!=q sY71CSpNpICB\_Պzn}h]^nqzT5ƛៈweCx6{<>f#rr#'X歽=w~ bg| ]h7 46ݧ+q >)k'a[/%jx1_%[|h^xHX? :# ӡsⷈnSc#D:E0.o~ey+c _밚 E~~_x^ȮwC^_k jve2c;n HW?M֍fX%uQ!{/)\0]sg޸ߴO<zeq)4em4eT\KvW-+og3k~:|D'u<%#PϢC ( ( ( ( ( ( ( ( (վ(ZkYhȱJdb@Nܜ_D~|l\PFz“ 𨾞[TF o7V?scq=/8_1^~о*?%\EZDEޣMb;#~zo돈:$6 Ħ](r!V;vsko_RkGzs+ψd}9;eyQBH\1'v`I$g'9UZc8dQE +ξ9'8{ .xY?,̌7Fsʓ@x5)$7W{UI%߼nyĬrܣ4z+UGq_3EZ2Vٸ1 "aIC֗S!F>fK]"YX@uDP7;;)8"_~6ϭx_SK,IH|%7u rqdWljo;is~ycϮ'C21e!L 9<5]ShT=#.umK $qpqq#`Xp|GiO=֏OVZ|[XEʭ3>Zjw6N3,381N~P:rMͥӵ &q iY_gù*|?_HU>&;,Ř/ʂXެx=/|]h_|M?苯iQJb-chVFBJ{=Ri{8g4NITvd!2sӓEuRෆ. e҆-վqMg>Ixg/ĺWM.bAF(%v5zͥӵ &q i,KKɮ{8g4NImV8$siRi.-̂@Fè,bMA |asľo4#4l6]Hx`m/&kL'?( 9&TgY3fpcL(#~#c}.8$$VU,m`W#2Sgd 2sd(mokL \'?( I٫=N>~̚Goi>*+۝3 gx6p2q'>|Q'VhWF-FMxy eLqVsk}5\:ɜA+dAiY^KQܸF=9&R0n-w;he*rx5rM> kGU/cźYLG7O !Ti 6kBKYU*M]cUaV٤Bchv##9zϏ!x*Lb]Fm؁܎M+[\\ZK{D˛pA 3  3D[w5K-Vm-Lq5ǽ7giz^D{sybpߚCෂ᫋=GÞ\yv2!:td;YtFu-JNpk"FkB:v-?ė0C{kVwuw2u7A?u;B2 M :8f$'Ycq]CPiZZx_9tK};Rӥ!;ic4,8Q 8D&:(߈t xfզ'R(]2pA)@N_?OG7|5g/! 3k3!UnK =,d8`@ mlqNoڏV|k?h:եM5d ̬>a9x?u Zi}ŝvs\\c^7?z/ÿi ]Nry‚s^Y۪o i?/?_=_xa;on@|!Ylm~n*y& "x_|l|3y||7>8n#gKcu9ޅ*!$+}+w-zJI./gYr: .yƉ.V8>k5oKH"e&ãPÃV+W <DŽtE^=VSgj÷!r $lSjxF|9n~" \lp1}/ms+|U~+x74 MGövq“*:x⿌W_%YG}_X-YN"|w,';HOLf/Wit67Ÿ "^]\x3^-=7>8W_ LbChqq a\l|Ui4? zL3&vyJpz;}UV}MսmE"Jp8S9۞3W>2⯉5M\̖_h/%1r;~SVeIu7,|%wDM{srċ*'>ӦhW|$⯋|IxrƝ4څ$\\HX qN>&[R?:[;I?B~8ՄS.:ea,r;֊u 2GC_ 챩I|$T skQPv81DGWrY@Hxb84_kZbZjُhҙCăWa0(ξze%yx=̫kbҾ'kxsj#P>nSZux;QC:a]ݠRx~sk>t+-ޝws_]/2r = ʊM_o<^X%彔rȰan$eaԷwi\OK"ROW|WyM_|E^.YiBeA+RI9K|Z:C?? ~[kPC,~k+VeNtɛoLv.ck- Rx%PWR2#RWɞ}uFkY-2]SK+dfPeH|p@5hݍ*ڷ; ;QEHŠ((((((((((.X]4mq"VlbuOq T 8{M?5}^Pm|q/iq%퓁ְ>,|<}?Vg==/9S9HޛiI"'e p\ =tyߊ?f cZOKk>0i&Mڰ! ێR@y_kvzeĭ1;f8[*I'iW|qv_OosxV"B<xG=MAjx7hWڦ߅Yv*lq$w'&\{o$O|'x]Yˣ%@V^3e-tgx[PT#>҄<fxLc cϥ5m+źIҎe[Red(1Xe'>̢VOsc? k~OKG,wm9gY#eݰ+ݤ&u E JǜA^( N,GW3h7 8`U袲IW].o\jZ=€F ] WEwmh7 H\ip><[nw>Wx ".m wGjɶBB'$TSoo$~񮱩\knVю?TLK"ıLK?ʬŇҾ_mro^j-5{"sk6҆e((Տ^w;F]]mvgĎRc,YHIns?fo|yדk_^-֙g$o%Һ( vn8_g o|QZĈ!Th3InnʌT>b.=[#Oÿ ?&~ៀ<==8ѬaE XܐI׍|Hex\IZg%ԯ#8Q gvxw_G֥lEmn$PD^s $ܯ~r|kxX7/m[Mگ/9z|+'95ÿCPCkT.m#S3N#mmx=3EwVoF!;Zi[S>{ۉWQs^>%?πcmF,u8ϷApmF7[Io-B ˷XcD`BgkW^5]cAx;MZtk"mF_/'#{;mmE~x k_0IФZ;y d Ie^+_ ~,4X-4P? +JmBk-t=CHαxѺa5~78 M-sǶÖ44S$?qZ> 7sYE"0ZT";o1¨޵U]+#kOw^mm&|r拮]jhCs5bPտeS~ ݮw&{itI'qG~Չ褴V[T)jSkf ?&kyBL5~PT5&ӺI3ĵ^%,Ӽ= mJSVxF$S*av"TWrH<ވnmgq8.7F+hhz;F\@P 3-%杪kwزҴhk6;UFI$RK~yM,]>VO rY+y4kk}dL@.6gmiiZHmK-)H/r gtr9"Ὂ9@"H 3Ma௉x3Hwk[1tA{+2˼d۹x{ :FYH#ҹ&A| qݮ6k<ȢnuH pr3nZI-',<1ˢF[jfw_FvA]'!W]^ɪj[@Vp]#>aXx"_'Û a5[m4i _(Iyų8\~+3|+gy6l6P][ vȯB- 5׊5#֭4 i "i6yV ~'д[~#ՇqSyRx#_CO_SIJPKD?-e( 2vrqSiw^vSS2> |E?/ěпnٻ]<7i=L ady\I$բ<"<{^MB)RXy%<&pcbO ~˺m+Z8Gs&}:Gn覝2⧄4TmGu%,r3׵sZ/tXu(t" +bA*WQJw*[>w>;ѵ[AwiC*P2q C$ dӨ{kQHaEPEPEPEPEPEPEPEPEPEPXt[༵,R,e u^[Kxzݴ$4. 3+/h&hUMʹ3N852NQiM7 | >XMMiـ2D09W|*fSox+&YkU[@gFA?g熮<-m&ۃwؙ';9]1{M_6Qx 1:T bnJVi+~w~6~{7|A2%[k/# zcFH_׬~1վZG<1GZ>o(3OR⾏&!KoWy|ʃ)w?Ƈ*%Dlzo̭u_σyh >>(l5pфŤ,䭦L? 럳<+T|Ew:=;>)YG`|p0F9PM{J<aM$(".Fàd@qn|>֩Cj7)IsK32;mtMktMsƿ?𕞜5 O]#<$edfOq{泵WFFo>-v_R/ZmȷO GWUw'!koS|]~&xštA^dF3b@dw5ş䵏䰒o0HCx@{e;pGۨ>6̯قjZVGxn~3M6;Zyi 帑 0N[M`x]^%/-<[Y~,]Jwngf<}coKhVϪ?$xi>Q۵e|@QM-;jzwG@vr2@`WZvmi|?>±\ ](eٷncRMkj<B5އ vu@lonM&Zߘm$~?b_|XxLX\"?^YW8ĤW?ٯᝯưxJu!T&n~rےIjo (xR"=+ĚE:}0%"lvVf47tk~;'ʞ5{Epo5xI]NK?ҳm<|:CxP r%) / .]Zh_qep8a NW=+s<ZivV7?ȲWy;KI=&FxO=łIi=v o 9qY; GJ(0)\SN)[X(|Wz/é ?b fH\D`aYsֽsTH;+yD$ j5gWʁIqxSIּ17olEh1crzKW$>^f ~>iT5QbX̎%F$|K}⦝xG2j|~&4r[,`As־vڟ@v+L3}=ϊ|au/ Zͯ &2!]0V`@䃜 95ot}|Lw{n͝yLtx}+]/~W gkij7P>!MͅͼofE_n!d]߇4~|wkmwjh 4P,Ʉ*6+4ٿ᷅|N ^9u}sspInlbK+,<# mvx11a27l潣_}B][M5YHIFXqbIlz֑AX%_oWbg Nuo߄w~+ZGKšD6bଶa!!PK|jKS ;>,Y~'mZ#o)Xޞ~? ?/-a׾$9/X&I$ִ|sID5ޫL]JHSoa[{%֞-&k7Ʒ^>uM/Tp7Bl(~HͯÑw6kCNmjZJ{[̽{u[c}O6:A|$|m}z]<'kPѵrk1y[ h >^3K2ukS4mu).r\y9s uj.|=/\h7˫8|3(iWfS*.pF~~jܶ>X6i/H=Ͻrz·i^qc+-(ً2ywv ^.cia[vUGvƿhkwՃxr0p|R5f-Bեl=oYt!"?48vֳ>6W'V6b]Mn |xĺj'a6sm,сR#-ƛ!?Ś޺]:kBͱM;n8=;_p_Y:їJ7yC2܀"Sj6V>弽(-[ RW^n>4|0M>otm$@ aA0L_mkb!䅃|-n>m|AO=5Hc)urE\>I9N4 ȳ}C`5l+״.[kMSG也qY%DTo1U“@=k:9_AX G=HYl@:=15B[ⵗ5]KzG*;|8 }|a%O$^o={c׎GoZ_A R$$mHx@nzAο|>l`gS'~nmv;U%wo렯ϬKA|8oeеiZ^*\aVL"5{А;F>Z5ڃ&OۭfOf[K#ݳ yֳZH$'tm>B펢"V%3yxES $88ŠZ?t/z궷ȷɺKb;gkcj'Sᅥϊ-p{&#VWK>4񅕏n-jɭ=AiI3)'F85{./%[H5Kr8˜.pBGSGa}NX>GMGZUyg\HXxI&VG[[Pŭ ߴZ[Ѽ;薖ZmՂev_-Y,ml|5. |j:H'P3y#ܨJ:\/Ӿ&vxMӴHnqc9Lu#\Wu}g{!MIi~͕޾r)eynjcgRtzlSiw#R|d(* 9^7ºNj~-g\0յKgckyXCvzZ{E%6^(<=믮ZǤ󥵍ӱ 7x zM߈|Wi6ڲ<Lܪ,P Kk^(t#Qqq1ׂ3_1] > -~hR\A {=ɘL#aʎҹW > xu/[xzJ1&%mg)q2"CٟƝ4>1Rxuzx[_V9U3-9xQOOj7wh&o-+8%x|ό5msƞ%֮ۛ}%ci3jh U<P՝5S|uUM7-4KL]͵k?9~Q_hZ+#ֿiͩ_v}ŷ ʼJ{ =}htivk-i+3+e2ޗÿ6}|6xyk)i >MW~k)ϸۛy(bRp@'𯋿g(Z4%LْH숄Pvv_i~:NԴc #:)4̿Î̯}Zζ!;|r~1PI"|ﴏ7={c׎^E>~GiXxMђX 1]n0U$E5Azn*[d_qxWo\Lln.lq/5Ꮜ 񦇨k:'4KKǗ\##mSI- n΢i̅W,+G^$&KYm|;q!ћL}J'mH;i #q=Wt7vc-f؝ڒm{[M"MY~.= 6>*xWwuS_]kV˙Br"kR@=?㏈m&cx|Ac:ZE żu/îRWӷ+HKx0xOiK7نn3jwB|6𵏉.^Ğ76ڒ^Q5~Ȱ1I)i._<_5 xGA4 \O ݷȥ,1J09`IBִ[u-*1-ݻnIP`}*M|: d[G{/AQ.a0W#y|Oòz%JNGBxڞM-L{9yh4 RdQ75:nwb;{}:m7N*D^TwUG8/ා|3w"OK-KK'gHm2' SO_h[Kk6٭,'ئ~e%Awk=yOPt"so̠b'gEoei:M͸+2Dz:_"j~1-4] >XGe5 u )$h:޳=7NomnBL*{GP &~&guA0$q_RsokŞ2 X躖ǥw6z2L.7Dv7Z~ŭC[]M7ò4~6}ğ G߆5n+kcKIO)+k埃/߉}KTϵ}Em1Ɔ(p퓃Š(0(((((((((((((((([Po4̳mrەrWzlmv)oC>P*@_G@_@h_zozo]a+a+v>Q>P*@_G@_@h_zozo]a+a+v>Q>P*@_G@_@h_zozo]sZķ<ڦ ba HJ''jk~W(k~W(KM}_M}_ U/=7=7€.Tߕ ?ߕ Y%Гr]'nۆ"Tzozor- x3Zժm o.k˶v3I3xgÖpNQ>P*@_G@_@h_zozo]a+a+v>Q>P~yY[ ȬmG$2F= ha+a+ F7>uǫk~W(k~W(hC/ތ7M}_M}_xk\j4; lg3ү'޳h+IfIrĒ^>Q>P = zs٬򥵔# 3Jrj >Q>P*@_G@_@h_zozo]a+a+v>Q>P*@_G@_@h_zozo]a+a+v>Q>P*@_G@_@h_zozo]/M25ե1 NI$ ϴEtW3[G?@5hg}*DZʪv PTFElQ\jWem_i*LvA)G?@5hg}(A-PMEs?>h??|3> QY9ҮϧGn|DC0y*((((((((((((((((((((((((((((((((((((#<6cƏ?hx?jwڣeY\ . yg%jW5O2i:fM5XA۸ :t?.u3U|sݷ4xI0:Ĥ*~n>׿asTme{p[Iq LlS&IsIko밯kOw~ 捩j1\ؼq[ٻ2H$1f o?ܬ` uơE=>z֧_oq(`ȌG 83߂aS-\KoŁ]ǜx3MOGc1 3q' 郵c ƞM[uP4+n vCDЇ9 6GĿ hx95-EKm@CX H?GoxZ_>9VTiubRO5m?Hx4tQ W^)S]2PM.xΝq\e#h6yy~~&jkYhJQ) Ȫ888Oi^-*HԭG5k,fVЃ[ǟ>,O,Za,iu$[#h9_O/|{Akjz췚BI *iI!V$`\w&Z)[z去̷oosn$pLS 8e݆85biVº喳%ق[E9#d|7* OڗĿ >'h^ uKW>8H_zBئoC k:;bc}k/ΤwWY\\Ak#YިY& JH 2y)Hq((((((((((((((((((((((((((((((((((((W[Ė} Cl9 ƹWZ  U@+k*?]k_7M1W{Ep_𮵯𯆟Ö{u+7<`(_bEŽ!#sv]ǒ'җֵCtwP ZoWZ  U@+k*?]k_7M1W{EaWO{=ěTT0QVPfotoxx-20.08/images/file-save.jpg000066400000000000000000000525521362435004500167070ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0230Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 219 439 0 C     C   _" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?GL]";{o" N SV?5K紟ѣSi?QQ@'hTOTPj{I}>?5/ڧFO=EEK紟ѣSi?QQ@'hTOTPj{I}>?5}ᯃu?z/r+d_'9*Fyu<7\ s⿵O=j{I}N)">%i lE>uXlaF9D BkxڄcKܷQqZ:ߏ8紟ѣSi?_N#$;uh]Al ܑƚTB-3\'?ˣ~ZX_A̡8#JJ+o.Oj{I}>?5OkEx|X|?izcީ.GxJ\8k'엧|9um7/]Am鉦nmB29saOEM{~62v?Ꮮ~?4}'kO*5*uX{k[ iȒ\3,2@~Y]CmQ ],#&@ e&~n#ZO~?4}'kֿ;RRNig0mܱ;V9',M?Q ܠXwl[$%y8Ib>vTO紟ѯtN/t !Rl.Ngz15v`=Op6<~~-G gr;GJ+uvdk˟j{I}>?5doYkZΛo.oY# c> m[R_O=j{I}b%TO紟Ѩ ~?4}'j*(_O=j{I}Si?GڧFNG"?k/w:Т[gxl!]_|^O@'i=̍$4xMQ/ޑK{Ѵ3jȊ&L5>oG\|L2XXӖ#y5BҵON F:-n˹afk+nyi4w7nבGKN l“vk{?S9g.e$t +w_o_2w_o_2S|E}5@+Q@+Q>euf^(uf^( 2_Mú3/Jtú3/Jtϙh:?:̴W_ ҿe ҿesZ+w_o_2w_o_2-}OhMRte<`$xBNV;7GG;7GEz[j=i5k;Z-4z]1$y ƈd ݫG}'BYxvYy6j\c*@f(wUկ@+Q@+Q5o+|/Qa/~[[,udcV М:kţᏍ%ezWNݘ1^@+Q@+Q-_hӏFr+i?ȉAⵐc`~Ϗ$oZ~8JxP?l$}ڈXqH7=v޵:?:+//HwվOWZ-Ofu`Z4{c^@nrk k^ DދX.4 _ Wl|+ҿ:?:no_DM~gI+55om=O!\3c~ԳŸ l1l5M/t;C-A`+uf^(uf^(깟<[X:~6_cikKB\<%V8Bƹuxm}5@+Q@+Q$ŷ}ϙh:?:\kzW zW .|x:hR)LoYUTk[` #=6qH:;7GG;7G@ϙh:?:sɦ٫S+B]B[ 5\v~;uf^(uf^(۬ݣhwe b2sN\>5?½J}-R&h'8`co~M*ڥFAȤ`sӚc>>?8o`q#X_^KgnAMMSC ^ >)<<2H1C/c>m.}HaA#>;xHX'@ oO&ż[\ydVM/Ŀc-;@vh_42΅Hhg{TCzhX:~8~ў#ּ;[܏/VzǖL.ߌ!,^{T|& ?3nO>"Y88,|\MKGT'ԟcX׍/.jpjk&w\\/}kO|FRxWvh_7ڔ$SF=o⧋="bnoEߝR#r Cȥw+?|=+^,ӵ Yk3XphB¾K2ls|Nuoxšè&O*m%of"3 vQ*Iގx #%Smo. kLDIm[A'utQE!Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@STcl%Y0'VbBR@k<#Ѯ$'z9A`cn0'> >-gKI#ՠx ;%I^9SC῀ZWt[Xk_oQ%¾#&vv\(]qOC½ݜtm_Ӟ7 B'}(ok$|}%h[xj"$h?dZψ^lmsi(LO/*A޵|u8]W~@}=t+= ?-fM{W.tg%Wt.:z[~z+>-VGuoι΅z1Ƕ=hKce.x+w:;^_BB$j *(}:5tχ&n|Ki?ٺX.#4~!TE@^jVhJa6RCaH#U<,ӾG1ayyy.M]Mv\K UڥT|@Oktυ!,x HxNKɼW+k:.ؓipIfާekGczωmcu36FHTT+3a*ͷ|5)Md-/n4{}BV^e.>ݹâKt9_/[/LHi3?v#c;T87O .KVDq}HP1݀Xtjh((z曢]̶e+}MncM^(((((( Ɵ/]zW=u;Ix]WP\KKs+KmDcלv}He}Jq<_'zD7^i"mjMaH%w6ǥ7^ ܀IaGzaxtЯ!/7&+Ԇ9 r8΅x{YwP{OL`ɂcŸgw~(7+33%EPS!\| qc㞿ki:\(tG$hݰYpNrs3BK(t?i''Rt+˹NpROz٤5}S3]3N{[˨:jk ͽ[%xdWxt?xmGJ𝶥+E96@K :sڹ|;Ὗ ϩX,$Iu,ѧ $ :k_xZ^KY{xYO @mmf]2r9ܴM$qr6J^Cwy|]}ƞ_ G∝U "s委@÷?x}/f>vA%r*_y5󂣌nA/诅bS|A~\WijMšYη|_.Yc ?.I'_KJlykzn+=K3a rNII T__Ե'.4X+n$઱ '|u=ƿâVM J`[x[ro&z ewgtg"|;stCs&pgdV-#9ds/k^.j:ޭ}fga τK_,c!'9N+|;>Z׌CmZ?IK'P+Uvy>n$cYi!Z=wR}N+9U\$(H^gOkĝAύkxK]}2 ^%xԲ))>,~Za]?No ,5n?5i m>`{]'$0X}YM~c3U_ӎo 6=o^7&kΧc6S\Sr9b_ lqڗOXi?ȴhfCkx Lȟ.ܝÊ}BjsDkm[­nM -\4bl޶~!~'xƗz~Ap+a$4߳͏x]WXxxʋ ` f,r>^2ZAk~'}5~/ j ]%-^1`|(r01MŽ:MBEcm7JL2$zm?Zdj4ZxLlO֤:2#`-[WJh|3[p0joe2 ˻c. )$<_xS|%+ Ƴ 7P^$k\"+xQA="-[BKԶCsl;2|LO?g>ב>GuO_֖t/ H.nX[p@Rp+qYk!@gЮ[k =Ci, ɹy _1+{hM1TgP hmgxsol}OHHۭ6m>΅ŔȲH=OqGG+~,_ Š60lY sx711F$!^Ou:Pm )[yg˾Vu8D>Ÿ0IoĊ%"1 䓎]|?Ak?3/h/sB j2K J8%ı>ZK`0~E_ ixn1>>t($1U_\"BѼu4rHKf4rg9> âG,/ߋiVKc,>Wg\g߇C/?Eu&[͏Mvur3Q (((( +}Fk[{iʲ5]kxш l3ZT ?HiO<L(]/?7G* ωɿ'f_z_E@M3hY|6V%C txXE W$q¢{TW%= -#"&> \?"ۉnsڀ::+_%}K]Ft L[g*a zI:ۢ0l3vgoQXmK/c6A&]pmY>{:͘sRx K^o j6~]Hk mUMž C}At2DGVP2W/t繷艉[˱۴s@Esğ;]Z&u*cOaVA_U: r^,FFh@2R}jωtu V u`ː;d 4_`fAϵb>74/pvm[ZqZRHN0N1Tu W? @ݿsij\ZkBhekim<TaFNv1NM?DZDEU?5-&|_#]hΫnH($KZQَzi(jď6a5vV~;[&ocZr8P׷ 'JS_ C&?By^iu .P1g-PdzJVieafHa~Q_O'%}CBhONN >@Q|m^K~ү5S'-<'~#ٟ/wq Lm$@Bj*穯{3HU1xkymWgrɩSOhQbUOFp?E14K} OIhνJx7ß}M@9umgؒ|{0>a:q3Oz憛ۈtAF~>o>) |۬7|G__E|sx6j^I$em${pk<A&wh+"IiL1<=MmS[M]X6_ &cl Sh#E_g <9\>E]J]:SKGזL??;n`@_lIt+^]τ5]cl-SΆ s5Q*y5wI]KƼ,u}RZR2YSczfQ;jϢ k6ZgŖxrK{eSbwnBỜWԿg: Z6acZ[j:YO07QtP}WfNDrZNøl~磶!kbɫ](D*$HItύ5EgO|'ky/bN.v.Ƽ/ӼM':sK\TӼ7&ϓ.IÀcA)5%fc >,N^[Sa9`ۇAlomm-Md }%ۿ*<~z{+Vnt [9cpYNN<O^#<w ǸӞKFd+Y$$dg"ԢX|5_'}WS-JfU"Aܣp^jF_-46&J,/(|HaXr#skhw />Ci7: KQuokP*n oֶz֥^T46>%ZBb4Ғe]JB?+^8.n.N֑[M$:`όM=Ovw n-x_ % 5GJԼ? A?d 'b}mw=^+ ֿ}3ObeGyXc[y5]w:w d׉76pI}AI@hVנϨ~ͯgNLGٵ )k񅿂4? ^æ:&5 9xtPY 5<kot5񗇾*_ ~Ěα֌.1$toJ r2:Wʷ]k[{{itH\:qW|#Ypq}a[O?~oÜU<S]r^Pm5>5awp^;+>9N<]/ 6 ^&ϯG30NNzӥyڗmOFiP׺U,!~@$'?V}jzn[kqu2J d{|c@E7k.WϪa"X)A:9s|@~+_%լ`Az^ojš#9;J 8SNڥW>c7x⽶  m1WIXH{-|c/_گm|/aI֥U[.R<{" u^;E|g_ۣSj1sgYNM >ko$fϔ`p@^➩Z_lm/Q}M415eʖi$CUVmq֝u蚮ciycn湺GkH}j[k/-"dT20(FCЂ9|_Ǟ-s׎mth|KI]^h,$%9<;:__֯>xf3ij%4¦8XHA;{1nֿi%bt}FUϗwc:ԐpAk⿂-5 cC}cI@W/\uٸ_Wf 8 ~n #?f?xB&HlMi[CH:/iga'tM}GT}6URkXnXT9=kNRqm/\~k;PQ$ɶ4^TUZjko k>b4#d;J1`mQfݽas+_Gњ~)MBMY9nXs b@=*|Ek5xBo hz$a>"ۃU=JՕ/w?YxZ߉֭fu%RXme1[.q[6W[VQ__SB.^_&kP+08 b@Ntbx_ hWMṮWּPV;ZpN-Q-Q< j+I5+n靈{`E1`Cj?a7e9E|w_a7 K鮼Gf[ZOI0G|GSKx_g/Úqo1mD-d1;8-F=Dj{|^[Q_||aSwi Yi7K/#\CLdO8'ڽ_5_fYo_j~M^Kbnda#RN>qfo#0E,*I M"+c7Ӥ*Ã'xgJf&zVCk(|AY,%㪳AJ Pvs{iXϸ}kO_[jVceppH<8_Ꮚ~ڧ躭e ehA\՛ֽay~k2c)bdVc?y/EE]OV.ê[R Kʇ+un<=؃_ /@-[M {OX5x.D.Ax3VcGVe+Ro.o|FԢbBI#21W׿r%h؃_ /@4 ? 9/?!nf[ R 9h\81׃X>)=Ŗ]~_6kc 4TEC4o&GܺI? K~$o7~!>%j^ԯR4>m`K,aU9>` ]i>~#մ_:RךfZgs6Agdx݆aМr%h؃_ /OaX/Z~*xNúNJఝF{-J`ҟ<1[k}m8'UrO1?u4lA˯h=ϗtA[|@{}N}WM-Ka#<ׁ>(ǂvx/u^{r* _?u4lA˯7۸ K[}Բ&8t-P}k~+x!Jit)J\2)9a?7c؃_ /\O Z1\yPώyXJ599wkre,%Vk'k;Y{YZwgk+.ڔۄ?| OA|&i fFo,0c+ pV6p3%'Km:]{>g RM;oȋƿ bߎⱪ_Ff-K\n6sX^/]|E񯂼m?5N+MStW^,Ydu hZ zA FAk؃_ /GܺIw Em7ϩX~0/U2mgqڋku"G  r7c9mPӵL&7r%h؃_ /M-4GxiqHռ84ﲩisHXlckd6|!x.(<ѡu#XʬY9yb]$Mr%h1YALm}XK1vYcG.HDŽ=kU< 1&l2A}fܣ vr%h؃_ /K/>.i!k2|[0a,vN͖Z_ o֦Zny2J_x-k? K._&C1k>[i7Um87f1'Szs-ڟ|?e ,k0`'?u4lA˯Wx/*x;Ǟ>*mv,[yI"6>g+;|@_Ήgq=V i~r%h؃_ /M+l sgO k_<߉4ٴ}_Ś m n]{EQ؃_ /VmR2$~6C0!EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEOUԓI{F 2TgYj@d ƟU6$c:RxYg<#u~(. #$(n1&M=_iO O%w|UWMs5ImC rN ?(/e$~]']ƥ}ȍK?1?=_:oWuxF?xauh];ʹBqOW' '<;MRIw*p8A]ΥD&B!Z:Vc J%\nAk}?UƏ?|emn-cYJ|H4f.u"]>!<%7Y¿zCZݍx_Ò貈#u3n2.z.xjQ?dQ?dZ'xOOе=]-[Vy,x&PC9^OoKמ 266 339 0 C     C   0" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? { x}.޹ MEEj>|Wjk/(Q_VGڽW_GGڏ">Gڽj>|>}z>E_"cxoxc51oSoeY潻trH>_Q6ڮKbN#P6SAo<#:lV13U8ٿ/?$o?K}u퇇RDcmGMҾϨZ9|2``ds |-v d˭},Ȣ&-.Sޠc9՛O\>}o||G $ ]018^>gV_Ex}N[9!6k##*[i2Cvcn` ZhfT6 ~u.VW#:]@/+oxEcV>Ɨz}ߊNN}Bⶖ6R]ۈc@9IE6_%+ȣG^oz~ɬfFj66"/ >yRK}k/>)G>)G.~z}__O[=_O[=s'֏Z r4ϊ2r4ϊ2 i>}_;|SG;|SG(\+T񦱭^}~:nV4Qb `sZWi:}ֱYiD֖}L$. kw/O -w/O -[ωm4(5ZkEks6d'޲|ewMJMZy"G$(TUP2w?ܿ ?3 z?ܿ ?3 zoV csM*}7Q3b!.X}ϩ>+h^#JFvKSko0B/2y!H~r4ϊ2r4ϊ2pNַC7!މq`$q)uu*],A&\ ;OӴhbAsK1\%A<_O[=_O[=VV~=7M5Ş,v !&BFvn$hϏ>=/'uOyG 3B%LAXč=N~r4ϊ2r4ϊ267SGϏ}KA.Wy-Y˅H{R݂7n9zFo Kh3 [A>)G>)G՗Oh2- Hc xOUԫ;w^1R_ǤIK+k0,&2Nv}iAe#iAe#};l|Q|o/cТ{HE2B~1+Fd }c+tLCv, o[6i(W0_9PB6=+ow/O -w/O -G4|5>8x<:b[ a@0Ž01Lվ5]xjS\hI0 118t@f#+w/O -w/O - c'֏Z r4ϊ2r4ϊ2 i>xI[b| a=9+_w/O -w/O -P6⛭j5Ǔl1L3=*K4M WvL2F~ù~g?où~g?oYρ_Qį[ҺC=@Ҡ & ,Cnn~ù~g?où~g?opfNHդ(3~0molS'Q{<ۉ{+>)G>)G.[6F3svj]Ne^u>O3FFHH3n}’|jHPBwtUN0;ϵ[!ۥg8XNⱵK={ķVUt;Ka,HHK*.GVְ,3_w>/M?M%e[}\2ۺk~*BgS-ifkL/73`rvz;>%X,<֌n&nq^}Q󅶯av_*ћdVy>摫 ǩ~vsg=#UY=5yeo3Rb(܌R/1Ny7;F&?h/{ X"?,;<Vjqk"XՁ<D#j7}'ZkǮ],rY ʲ (0 \ZEh%I$`Ic-Yxܖ6oc־ljع#)lo.Ʃ=̲B3fJc߽AG=|O>5|%eces(UC<3Jv+&1{m=ǜg`G8=;S'sR,O$ƀ;&\Gw?෻G`:Ƥw Gzӝ Zw#} o]qz߲ԭ".-]$g*l(jQeZiḎ8EHɑGHBGކ5_5~|'㯇\xQr$wk**zsQh<_|5a1A6z5 av2!8^)ݽ폦|7Dյ6/VqOb3P 26K'21Gߊ^.|]jG l#9˺:u9lbo~ >Aos-_ƓCž١ksl߹*(;) <] gk0jUD^0§,)eJs$IMT~պ3֪ k#4(s*8<x?x~&&CI2KѬ֗W FBʢ/xF/g<=3O8l푬Uwp"2Esbp08##=zGK񧈴=+~7:|EqqubAqqXɒNڇVO?-}_r:M[XO;t#BG{uB WKTW)_cJ+>RG{D8 :<\~db_l.>W_{QE!Q@Q@Q@Q@Q@^&o/GBvrE#0"<Y:U]i[elx+")]6ox[,Xq]# c_S:lXɤ\C#ǼȸܼZꨠ+\Ѽ _SSOR4LE W9>G¾mIl[͸` 7?xMEp{VvvWbD9R>}o>|0Fi1{g*m z]ot^ڕ yH ەP$db-:G^Q< mE*sc99E{C&q_Fd`ᔃ A )/koVh<;vm'ܭtݷk<;giWcֽ§GIz~ֱzsvy3hoL l'u+6ќ(p͐1 +O65վW5Yi(9E ;_ Xé] ViH |7A w`sMm۶ji3ib( DQX|:9{ x~OdX㶞6w#6Yry\UE'">j+ּw^̖RN]O1y䆆)??}^Qe?|Xѱu)| ɸ:AMh7|j? |QVZK ;XRIv + F\CIsA S4gI4OKRKv%YB Y v:gMC0/< -Yhmޅgsymʃ*Ig۝^^^=F;`(v~R|fb~=_ ynKNU;oxz2ud܉vJg5xL4 6',u.Tk]1e(-ouӬj^) fskjAhm R,' o\iZG:,o 0A&enz'H|Miu9ҵ- mjvKɰA۪qڼQtO/Ú$ׅ7PM1lUd6rOz[=GZ[kgYT1F+ (xߋ:G-y6Ao !gC!;)y꽩/]?ogu#<;þſj>uA$ް D]H;6B(gh?~2յ}[^s[[^Ou$WvDZaZs~-ѯX}?R{d][%T2.QوӇml\jɃPE<0j3C*H˖WR2"<Gz֦Z^O%=6W 6d0@QN1R3??TU?TU'so%?CQҭ%{2HC\(BquchO\OkĚ%iWB7U,DHBvYpi<\?Uu?UuOw 4~BI_,ցF\* 9 ;_ xH:Ư>d$ FT4Y]nzQHaEPEPEP^g?X>)]6\Ghq٠;~<+(=$Eh>qLio C6| *vsZuYug_uK졺KX!lgUUO5d9{MWwjMZ_/A[7-]mC}E@ |898,4_ !Drw ³%PKc⽢mH_g/T xgD]ѵkQcp1|9 o٣Rkw'.'k3<-6%m~A }?ߐx75xfK;FM9Xv+)j$I}(/P<2D"{@JnѸ84SnV4 '/ gemkLw([)\ 'vq|OR|kk&ɒ2;! ACYdYxQ,~_ig&mSO1]$ݎ2hEmՠzҪ(' Rvw<_ j6gyuj>M;L>\˾="VNj+:A2hFN āsqw(O?8Wכ36V~rI#g}k"ԿQ\!mF[iAgr49E #Ƚ>wu\dX5;Sr*p@<էH'>񶁮_$LK )g&y!A8"\R^$]Ѐzi6/GHC城 NS7{yT&q3\? λhzw8\FUHٱqC65HsOM֕(-'}NB:.|SXj^\8= )9o¾]ׁ5ikOj x[M$TΛi<^Og??[n Wo)xBFlKf\3@.ti<~Scj:I)mЬ䳾p[o)xBUhK'_.m%W@KgA+20T4ݾOkj96Hv I `O}N6?t;AP^Hm"ߍ(ʫ \s᷋!\jwVV>h6v6̑1oo0ٻ+m$/hs:ZΟ&lgK1DGPϜ)梗KS_nl7yC读 |X\ך#S\ [̒h]ycQցytbtO Zsitf%YeP~\1>GYQ]:0ʲ=EA YxMj{oH(!'UQGӽs_1zUռK$1|0@0WyVa61Sj͠N-z_?3|Cֵ SS,:u-M6ʲI9*~sY~(M7—WE^1GSQG*e9bߊt[ ;^SrH:18 yc Ώ`M6MRF1#rrX`G|kOÞ#úcs}w 5(㹆Q:9w,1} MKhژkYEm4Kv8 pP(#ikv% _6-zcpv—w)(b2~M!WTmUWsXrS;zWJо:kzNJK,,t%FYn,vox7Q֝(|Wo˭jōc6$ڽIO]u5WVf4ؾwKLwi}Ib}ն AWQE#0E,*I Z+ k;-3\tc>`Ko6^Ӵ+ydr) zdK kZf't58{3vmm,u?Y7b gpַDltaL KJ5jNnx]q q*!q2svOLG&"i?/&"i?MFM'M< s(jN3]kKÞSGԵEߢ!3@TفU>|-{\ Zv罸|CЍӚR}-ʿ^$}ijcUa.2ДU_WMK/NyVz_QZG/Y!Q" #DĞ2qԟՎG44^}6hЈgY#?ĦEopH$fKK]m]u3Os3B?ck$9 @5ko.HN9XNY7s|N>]﮵g/5[)ȴZY'F^K]O9%Kܯ[ku%YnoE_ x>_QM+zW>񏂼A1XxZ&r,4( ֗R2.a 1r@gz|)y\~&ڼv 'i{|;jgXk9n%ao/Q`' }A Ðhle ʟi{$#sLi-}En|#xv BfbeF "t2`{T'O6 c5MZX5jm0Kb6nAqwBK j4ou':7ZMXcW">#~hEz4$B#7y[]e-}m-u>=Yoh|`Eooj7w~YHX%!?xgxo;R xD'ծ+l 䑏s]+>7^0Ig|4a*-\E)'+Eg[5Xm,G]y,唰ނ,P ve[Y_KZEkͦ]%Fy: ^b["߯;iQR0(((((((((((((((((((((((((((((((((((((((((PKr; b(K3F3F U/A/"A/".T?n>?nER b( b(K3F3F U/A/"A/".T?n>?nER b( b(K3F3F U/A/"A/".T?n>?nER b( b(K3F3F U/A/"A/".T?n>?nER b( b(K3F3F U/A/"RK{c&(((((((((((]a}gMiehO&C1M Ð>rx#h\:xm5YoZKm31C߉4/GWxQ F1,B0ʃ,r _Wzu;mSփsz"t+L*y*bn G _5O6|/78|.tR5KKTm;Jλs]\\:Ԟ5_x^޿ xz.\k iUIVZ8Id ]~M0x[\mum[#Z"OТ + Dg&5Y|Χ]ѿfC)^timYU`H<5_LMd^\]wnV+!ETr2Ii[ovKSj Rl(m[y\si]&u%W1P$ȭ/.<'o GjZhl G<~Wwr۰rYr{W:6Dgukqg'77T Ѩ;eKkEs/./.u/<1cz4krk66I)U嬓ƈ0y[Σt 2_%嶭xֹ_*;i)Bd%ky\hzޑri,Nd;Xhxf[7̮K$dFkǾ^7[Wӵ-a9._2w`dAhW;@K_=a'sn(QEQEQEQEQEQEQEQEQEQEW1˘]&idXlɹNUmCMխͽbEЌPZ<8fM$a `#8&w䱸{}5 4f)Kt@|1B144a7&;C 04涄6EySP4;K>-糂H뚕!? WY?\?/&@|1B14j-gZNm@Z,#2)xnQmBm?HPbIm Lvߜ/nx]? >sH? GѨ)wM * {h! bG ?:϶mu^8[ɘ8e} vve[pYAUKU5 躽Gy#cA4YӮ.KIԥ̶$!Ap}i-t X[iz%4PZB[rv#9 й _Mc#bhv;㺷ncPIsH? G bKu}xU_aWCwR]u#V 0$Ћ܎455TEUT`:(fotoxx-20.08/images/first-startup.jpg000066400000000000000000001602441362435004500176610ustar00rootroot00000000000000JFIFHHExifMM*bj(1ri%HHgnome-screenshot0232Ơ0100Fotoxx:sharpen| Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 460 536 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?+8RmrG?ߝE3HdloΏ6OQfloΏ6OQfloΏ6OQfloΏ6OQfloΏ6OQfloΏ6OQfloΏ6OQfloΏ6OQfloΏ6OQfloΏ6O^p?^77BxmzIy&/elDO39jmYy}:'<9n,eNH$dpIGVq&dR|zr {VZ%$/>{XԧB7}AVX\gn:3 "B\z__#('!Goj+[4Wi ٣4њvh74f3MfsFh٣4њvh74f3MfsFh٣4њx|c&Iu,u/ôg=x*xY<_W6Whȹ׳c/}EmGZx<cgMƠc(;cU x{.Em{cYnזG+xh[z _Zx 7ReΣG A֏o~0vh2Xxz|њM]XkGs{ðx=< 6ͮ%Rs q;_T>"s^okr8=+{i75G[۝CLVeyg+t+~);iSV1 ЊњiosQЍhMMtdKfđ_J4{ j~GSYHXRܫ00a}sבzvk&]6J0N0sz1Eƹmwaq-銌%TU ğl~Usz2 ۫y-!m(;=[xh,+U-/XK8A1}kPNƖz%hD!Mz^33BvǰxD[-?VZƵ haVVE/gv E7,h4F&ʶ9E|Ӛj6y=ܨ5y :ԭeeia/g̪I#8Q'83M+h6;4f3LCFi4њnh;4f3@͌V')I.90>jf(')4s _ps3@l#Pc=_'k74fokoQa$$l W+JH>Xe+v;p`st՚=[ ]YDp@S0}jnjʂÁTIH35$)^L3?ARXm;4SsEwI+_г8k4gYA&hmmfcמ"_|BƕV cJq[γiPLLsήܻGo+uBWwL µ_ W?Z/+sZgywXI.|K4󢜢K)l }BU"kh#敗'bp;bXҿQ ,i_+#ſM%.Ti,%HV-Aۍ@kRos2Cu|)dz+_б8XҿT B;.H8emц9czܚ2D]CnFR@?Z/+|Bƕ❬BޏX^L.µEk,Zj1ߠ4xsQl%[ɐE)$$v1`hV cJqG+_б8Z ԒŮ.b6$s$M 8$*ZZEih8I:P}|BƕV cJqVh:L:|wrͶy%DwlU,#+OE֬5)5.qqh*r:dFhXҿQ ,i_+c\m)ͼ&Ja7K"vw">6n,-t ܆sµ_ W?Z/+I@注ea}#M>!4v;(V &֤ү8#1wvm' f+_б8XҿR]Hͭ3^ fH>w G=I_|ahwvַO2I3\ x ?|BƕV cJqU<7;uۋ[{f&Wa IYj.p*>?Us-*+ʲCh p}(?V cJqG+_б84MROK+hxcB2h_& "-N䶣Z _5x [yj?Z/+|Bƕ⭟x}|4_Qs3fnݞ64-zN[&)䅣Bp (+_б8XҿWKj7m7V GAzU?Z/+|Bƕ(k4µ_ WCXҿQ ,i_+9V cJqG+_б8?Z/+|Bƕ(k4µ_ WCXҿQ ,i_+9V cJqG+_б8?Z/+|Bƕ(k4µ_ WCXҿQ ,i_+9V cJqG+_б8?Z/+|Bƕ(k4µ_ WCXҿQ ,i_+9V gJ먠# 5(tddnf09+w7L;ۣH1#4>+q)7boj<x_Zڼ=kڮ"7WdR"iQUxV{ o:+-GϸmZyl |ojoQYlyo#Z"?,͆n[۩lO#9~*osE񕶽m˪E>CqIo"1n ON3oj<+z֥I䷵K{S'y-@Ry-G'y-@Ry-G'y-@Ry-G'y-@Ry-G'y-@Ry-G'y-@Ry-G'y-@Ry-G'ԏ(2( T?Jk7Kh̒GZ?s_vLR,B:WAgoYOg{ Ok:C) exGZ%ޝ]Pnlc$' |J^xGҭAhVSNjGY7OjЋǖ=k}w'kj$ ' Üj.V}żC#)R:az[we_ ]([Li|  R[+nߠ(j_ö77(Z8ؘ0 uvxGMo/Ÿ͒O3p2s]:@!Yծuo4&FR2;sZwZ74_Fլ{g< Nқu s̰Kbڄ72G>S^fG,b51k Z[ d+3(׵mMuKH-t ̗Ł*AjN6Ş't=#C$6v:tjm(wi|GsRw_~"NweI8ޣ؜W;}KöOstQjH~0,?CZ>owj.|W'wXċ"[߃OSB^[L1-֟O?%GZO YZfYe\+ oM{ʖK.%FFw2VR QYM6cf kμ%Xhl4x.쭵9n pbnt!~Usk^<<5}WEfs#lJI> ҵ.ՖawcfownIaPId1׭qGPik,h+<d`+2gZ|6#&y{FeΙVgGy4>fNY<5uwfTY#;Apsnkt,ىMʪǨRG\^wE:Syo&#w+8F$m#$9|9i+Еwf*:v=׳ZBm,fefvn'qҦތm^!7v97v-hmxѼ -|/X rhW~RjsurȞgk>'G5 UoO ^\YZbn]K|El>gojWde6V@@m}COUP= sxeo 6/o5Ή-4@9~\pyľMWBo|Qcee^2u ŕ뚔۷އ14QGwn,#ޣԵm;KTmN?7,{"ॆ75k[5S"*#ڵ*p0mcey|x4Ym!31{Z%`dPH;sDI[jAմ9.5 IcY',a]6F߉6ӣƾ[((H=~a^1R4?߉RvVZ<1ȉ)f 3Sp uF=3Mk`<94HMe*vjɍ迮_Nl<<5@kEc60vYw>nRi"@<6q:z}j:pl.R}Ozχڳ lTk`ss;uOԳg]:M&O70 U8 # uӳs}?{>^~nx⼋Ca|C5M(omܔP0^ s_%A &0e"TN|2#oplB[\wHڭ"zpNj]GVӴԵ KA' =Lq{x-L\Z"YFQYO%1gsJ[ 0yhm[QƓuc?mmiGspv=++{sX=$$5| 峊~jPkA^ VE p xjk 6:uq5ݻ[ |J{{W#SwHWٷ6 _ck?'-F 4װ‘|m%e֭d>VW8BQ Izf/ψY?Ty H$sEsNdtL<}kD=ݵ=1I3m™*}7ɖ_ ?*)<]sd:\W{5e֓]藱_E @px&Ei>{֠#3+Lk4=?\lS|m[Gl“[oI{0<pq^iַ_ICYnZM:UiKPJٵ8Z| 35Ժ'bYp# 8)iy;xO֗YRdlg9 o]ZP[ K#@=Isgt :F-b,UvUb>Fn<%hgpjݛ6 ',Ҵ.4u}/l} ɶ}[O[̆#r/r;RGYLQjs((HLgv3Ҽ:%ĭBm)F&wM%"v9\-࿅teؿpH)/ݯuZҮ,dS3'F`p:Ω_KcuoCaK*LqpּZдxYt.?$6\x|q>x×qGY0fExYxz gm.ìAwSEnO:י4gF*7>!pguoB+)ʭ"8B1cis$'3z+u joMuBd7 sP>cs+_ ϪEo6u )Q1 S'˸Ҿǿ^Ka|5/ځܽAr\@@c{ ϗZ>/,+-Bk{4!a b9ހ|^ۖOBTery!_܋}R;PFNyR>kcݨixK6}k0eD؆t^ | -i؃ʾ~$к#UB09V8L* { 9%HmC$9U$_k0|>/[/u 9oE5GuPP}^}_҄_MROڬ,%-%3C jz7\Ȳ*ף)SWxvRi::66 M:@0JM"*;FvV>RVmt.V,ooo--n͂\F&2F@?]NMK/h ,XN|'JZ׉@WQ:ޡe- Hi#m3x`cQr{eie%ww2yPGM/SC.~dHѫ[K>[ ^e{y]3j7M.⋍0\|IJ"ePo'gjnisCwkBtSѤwi-m(m$tVjN g$5^1s{kL`#ÿ=S/.2#v?/o|%"jWZ^}R ȩ#,{cvוڊ!֜W.u( 'Ȇ1*(%Fȭ5/T^lѬľlķzgJwCz_#تcy{wgkuV,)$dW[Ƴ|:.u˻Dn.q ?b{C-PQ/bgIF8fH隤q7c--{!մ: 2))yr3^3\Vx>,zR[ЙZ2,NrhQn>v߉ȠgtF]QhZHH+Ɩ2?`Ns҉&~%Zkmak5ߒZAn<Ԧm[Y~$uMB I@%lM_xᾛ{Hw*L>V+,t,6GyMh>vZwpld7n' d4ֲu#+MfŮmKA2 +pk.5?xOxW97C$RK)ӡz杶; ].{U-.n#{RdS# N+˭-O:tW+ٰ6;$ҏͩjXnu`b$~zj:Ŋiw1]Z*%I~U-/Z.|#}9u}N+\$UU zZEQ\kXGo4a"מSJnڞEytIh~5lۈ477hN&䎞՝N;bF >ݕ\nu*IS0%C/=^!BX.-. y^"ab:pW#x{ڞu.d,?ZP I@z'),-%3,pj ^e7)07ۧyrOOzn%3pf~U;&?3Zo[[EyusW_]լ]6 Xng7uKÞ.۫Tomo5($ΰ7OSRW\GP`Y#bVcRj|q-kkz{Av̏qA=vZ`>lM=K'?퓞(렯 +ȼg(%}fw—W\.}?M#?ݭGB:퍯_T5K++;,1JTexwum[E񾍦QKm}$y pz.;j~_U8>WKyEr q7:䷇ΒXDO޾ҵw(*95%G?ƀ+QEMmҧ-TVou)46]k JG gς?/!rPԴ%9jC4۽R=Fov_coxkF[  K V{X du=gu&QHJp>*ѥo4WW%lAI*p)h ׯ=ޛm3jWe>r T{KLm-3l8ʜ=i~Mƥak藐s #rw;g[:u+ 2+Ȯ\) }(֮DZUɧ6\8rG:?nLҭmڙޣN{{W%Zf?dVzҘ퍤 M$|汴ψ~+Oцoyq 84_ WuV+tV {Mm/-n;(-_́sz\"_:ޡgˤ֖r֦~!*V0F0[xmJoqU>$ 4)gIdk}1f''5j|YΣ;J΅oxֱ4}.qqSjwW=I)ƚ4gPqUO xbÏIm,O]FK *j_eBuKKh#{;kif=U9AOe4vғD]9I|xC~l/{Im^{^/R_[3y,W.e$Uil1y?s6n!]Z7Mu? n:b =gM揦\[a[ooH"vlVt*HHtXV1,כ[|Cկ"kܘfJg6p_K]SծngtK+nNf.1 ;= K%-E\O.m|WX,Qfbv2"pN{;z7rk8dk{;'쯐.>v@r>N? iVlzeZ',r0$ y.ö*6h s>*wuow;qS:_<9^?Ha22={U6amYao`=A_kzvtPNy0+s;󮾨[kkk; 'G&eAB\) ›>D]]&1m|Ci-}3> ,12)cEoQ@{xwvMrդu1z'=]#H PIr}+~m7][]b uuG2?QL@Ҡ$aMVHE]c\p?*Ӣ1ʶvB}&'{$Wl˫KXp('P$ҬURKXшdtR}g͢"]"'-|ݢ0ag"MKn#\Sh]bze[!%cL֮@xWBMRI4F}P%9`\xgE"'-dbEHY2Es 嶗>g,#H R0+JIKFŸaGz.~C>g ]He#ִ( ( IQ QE![}$t$Ro΀.QT7FXdž`n]Ķ񙢟|@8FuOx{VXўW I&û=ѹ:| /x+"יEy,m)*>9mUmjuof(F?(둜Y:77Bvr1|9Z&5 r]žɧ埈nA<0cOŷ.{W:77O*dCR5 Ute(Xr1 e/4C/QrnDW~L\cMusxtq}kV:1yHblI_Ewiͥt`ѿ2Ι]f?-S9e<ZHfxSǩxCѮ2VeAi2vc{sxt0kkQԴrMQaDmʀ*XeN Qcͧ]Kc-az6͎W]:77KmBX+4ޓuN̍AEKs ִռE}y;Eyf& ]~?4nȯSoq*KG,kÀ{sOq.NMFJtXuȬI?c>M܁Qn U*B?*|E[ w[й^sxtnoΖ;7gxVͯZ=g'zȧQU F,DP1`}cֺ ѹ:wLUj {p"0\H3{/|+HNN&k%۝vr:ҷ7FrlR!ka=}wk(\\̈́`rp :Noqy}CQ&% TEt[sxtz>&5 ^l|A{Fng 59?%t1>|f$n8䓁]f? A5s_6a]wS_.6yaoۻ{֍Í.Ǘ(ifs^r̖ҴxިJqKBT%izFB={1[Ow>cJ4/Q]x9$Ե1bݘ3!'dw|Jt[z~k r^ME0*N6+-D+ֶao$NMsϻ1ҹ۟%] Z\-O +G5]RcKX dU#U Ҿ[>}=~y?W;MүӮb^n[Vrx$z}*]=kHڰdnz6x#׊b;aϻ1[_aj7Ӵᾴ 9.BzS5hF%³Kv`F P33 |w+"h:^u4ZpEip$O Vc!hn#1u).Ř=qŘ=q^exZ k}3I-4XmD Et:_Z[/K)kq5$7 ;#}o6p[ȑns>|O^+"z$/]dBgbw)?zGw>cOr'6cO ͘-G׊ES0:Qj|fvӞr'6`t@فZǷjES0:Q@ف?nr'8͘3ǯjgz8rݜf ?sjgz8\)͘s=z=9P?>|O=~Ϙ=GӚES3:QONh30z~(Ov7bgoNhi`8;U(Ov7m@ϧn4cv0q~s۽\)cv0pN?z9Niqf G8;wP3=,;ww,-G*Lw,-G(3W( ~}zg׏֏>|P?>|nY#[,9 8#$grmxװ>ӚEP&^$|:nE,"2FSAsYnkyV9vX9q3~?]XlK÷i^kfxcľ4]mISg…W+5d☶zUlهEn;˻~?S,Lˢ*B6Z<þ7V0;C ^xzgHQ`lI'#8o?iNߏzӷumnkdp_uVƧæGbعYd⹽ZZ5ΐ2+Y< 0c"Zj=;˻~?@xէowo^҅X G'Y[ RAm3&-:1gw>yx [4կnZ((}MoMGZv;z@xէ;zEӳ&?ybY&;]"P#\> /Dmqy"/M&s봎+Ŧn?=&?V޿ $.BյBSP>gX>%%]gOwY)-8_tq<+>ɨէA>qpm:G̱?jVV~o FemIm#&UYmhwWZ\Wr?S 욈V`cvj<Ӿ?׏=J }V{^a>`BSE.RgSO k +!qk68v(68qvl3ƴjuhz Ȳnna2+R_ u B Y-9k|syuh:6qt5mqv8:9:5-tIrFĐ3sS|_,Q4YmrQZͻzj6_W|Uqem Sll4Iy/R9b]TJs\VAYeqijH@5QV3n߇j .;8?Zv/[4KY{mMpɨէwoN?==5.ޏj?i?N==5vן MGէ8 n;g9?_Zj=;˻zs~ZZj}QNwןjj<ӧw}yj88qv2>PT in\fݎ92>Rmvݎ9??5 yՠͻ{cs_jCiAoN??_jj<ӿuj<ӿӎ:TQVݽ8?^~{MG~??ǽOTk_קsRQOoOyqM{xH0Xۓs{ƀ,E5?JSKZt]&Rrd`35Dn5 dtqБ=못[kX%RSY'4m>AcT/0g=k'OK˥]n"R y7NU=#❎vkw+iF# 9` 1WEOQ冱m%A:%2JFco;1,w۴{lc=:N;+ZR~WtO]\]iw mk-` 03[(Rèi ^<Z,r2x+;Yj:gmE͔g\IsI񒝳 ĺY4x&uh 6Npp=5m>Bw5߉~$:ZGpx/-w߿fHm]𶳨_ź}V#ry8?oj48|3,ڕ].u$h 3c!x x~L񇉵K-ShZMp S2V$>;-t|֩lN儛ncުhQJ&ec88lw@r<[{[T[1kwĤ9K]<#m-fN%]1w4v'RJ_a;~?|P[-/ˡMR+t{xg9l3[34c:݊IHYcdm̠>dzO] ]M;_./&%i@0r;Ŀ5;kG,WIMh!I`j[9$`cޡmF~%i#M o౼{If5Qe2lnd8ҳoj:Cj660G;ZG/ԒgBF7k:oOázEԓ K34)uN$tT$>4YɓH =d6ԃUKcSPom붖z4S4űFnxZ->] >R5KoAocXp?xۙ@95;iv x=7P>'隞ڦjĭۆFq$M^ -KJc٭-ʇ.qPGMtpwgڈv//#iP~!n-a:Z\:/1` psn:_gnD[}Lc;qg,to|k &iNmv!9li7}*Kg68)krbh=KGF{$Wq+S qKNIn4Y6 cBՋ s\xK #6\Ly䞸)>{XoTymB9nehJ2qZl%i|G5k #XNDI`I_|G[m4}7UeoD!T^HrN_nkMPԘM774m TyjC'jx7^֤Լ5E֙,QA,`hF,cpzr)j3{&wOX꺅fyHD8opMY:ksI,F$ , ۞p \Y &Hե!w,Q9n2Xz7Շφ},;xuow/h6 #a'84=zw.!;+kcLҢIrW Ǐ%Y^q U[|Lv#k?NggLtYt m ԲwpX2 gJJsYZhO%݄skZ2b@q:S[Ogt3_Tagw-r/o`V_,8 xI{k"΁)1ָ+gRTNqqJ<עk ߇6[/Ǵ.3*u+No#~nn ty}:aT{l,o5WCnbe1_\A%F 8r:f91SE$7No>{]d5т=e>'+1=7Mh5A|rV/wGjݎ.X& ]]+b1aqTV_ۙ,b/ٝSۂ9PUkUFsaXO\W'+մtٴ ~=ș Avy͌jҷO? =(PQEQEQEQEQETsjJV) OP[}l#EfcU; Z:]eE2;^!at;.H.h]8`+MOkm缓 P_oAФuym -(wl Xc[V/UNJ,/.g:Zwj Hs\wK}YdžOwuS-v9g#8?%oOww:վXmb/}FŁbpyDmaIeXG,7Wm]d& 88Ԭq)r,yPH s}+.ZZk]YCK{W&uY'$qVfc6kK%yy8D>ja硧PH/K;43D_'_sSe/#3) ®b#ަ~^}֟kynӼFH Qld@5QM%oo=51⽑5 CLs;e_\keŽ︖+e}Xw7Fe#1=ln$){(ӧ.t|ލkq6Q#e__k_x{Rkm;\. (nQ(pAY^ώ/#XŰWn]cv؁no$Ac߾jY%%4MGItŻnfӐzgUhN[4kj{J As:Xd\+ö%tѮF (sڹ+[]M,H}I"789ϧQY:ׂu{÷ʿ3]Ai/#2 T?~1^e3/_ ״{DR"x'jTNTִ;QipNppk"[jǍӼDkqoieS $! F8Htx^/>؀cB8Qֈ븛|3#l2R0 :ޔ-ng_y˶rF@C#Ҽ@+~Jԣ.崺6Nm$G +Yo4z0ZM"ӥXСmm A*m_=f~:L71bx2y{Սg.i *\$ew5͉@YO)_8fޅvUcmAkwzmsU}xv}WPhϖ(ZU+FoGv_+5.]e[r9 Gj7_u{+[ˢ:sC_msA*:)`Pypkjemyi֗?&%?)'JŨ74m54IY4;ϒKtN{C:L |MV[!ʍfF@B0:)Gk;muvmݠ5E'q96k9IC$^(%sY۱uoa1#xc@JF8{Msg ,I" ,f6QTiRz(Š((((((((((_RTshQHdx*zOLAEfx B4oI -~~2|9wmLjp^L9x>Z#4q-ȧVdZ3Mi!!xXWx=F'!u {^h|W||mj{H5mɠJPM: ! (_wo;oZHm.V;"T?^FicgrX80ylF@;d_EZ/t2[~KxFѵ+m?P)}r7Ep+g$ ܯ)_yJNeQ66Oq\"_3wKKm;.tcw)Es%[O{;y`w*uR0EA֚FmЬv#%$Qsyi^ &hԸSf((((((((((_RTshQHdx*zOLAEfxK}chחVۯYq|e×w'u A"nx(ZӬdb${DTCCc L:ƉkIk:eQnPy_=tѝZ[HZitC [x[>ggywutw1? JMX|9g:6s mP":8\`0W-,nn.--ḹ`j),~序|GxSŲrk%$QT*[ uj>#-!iZkm;2kssEmpm#ϳ-_iٜ݌=6_xjkּ;ݳvlvyyU h![jW駥 Hj/! _Ǿ)ӤἊOGN+4ZNcړLIKUs CLqq=LJ4K.2GfaǗi%4IPWaO ͏I{ l408@³1!ь9X_e7 EΞupJ$x"OO~z&zldZt|.dլ1#n9<\4YOYcXdxU.0HWxǚ֗zW.$ΞЕD+LzŚNP IyMݳ+zI?Gi4%K4)&R5qSd}jc&vbq$6gO.6Uo 4I9I$ɮ#QF:- 4/Z+Y%9M.Q-UR|'NKK@.d]5Ŝr;cF*zJ v*dZ7BgSm^oZ~&Oc-VKpNՈ'deSֳٷw9bUULs,-WAt1,[N^Po0|݌cR2i2Z-jRЂ0ver=*:+JF-j˜ckzxGm~!kn`r8+j𬺮e$yEs sE ]PԭZQ-r2 T|=>iOIJZ$M'9O>+{ HcLQta`7?<wKx;kxpDfd|* Ri_Kg6ڐ[AQtlC߷9hzuf7,9Vk<*[#26 Ncf/&0߶2D. ao񌎵n6v*WM[\4Ku(G{[TG@J=*]3FKlʖ,bF=K'ךj? [xv7m/ds$A I*_ xYVZeӭ'(*G~*Rئw^͍76^ѭ!m HT8*zC0Ln"E@I%c" &{(ܐ꽁 zkGvķqdq[m>g1ڭْa!0=yGҸ'N-dе~L+|/8Q<%Fj. =rHH) `p(((((((((((*95%G?ƀ+QEMmҧTdER4d.(V~,q)[VF2xռ5ik<)q+a$E_4dw~ZMx;o%^dki#*S09TWk/\k}YGb*"lqy9z|SFO;W9iQ_GЖP6]wHdO1G.+z}$}x\6죱rs]&O'aʷ9}݆Yj3j֤CpGl( q$ý.?/Q+pnDbyLgn1t'ѓi\,rÛ-OP.u?^(ᾊhfѳ)@>ˤ|=-6O4aBsaF_`{WKh4WoHymSb<rx~kwh=i}j5cxs(($vnۓW2}M;q[Kֱ MGV 6kd[)Uy<:Ҵ 47\rX%tvKFNzcں|SFOBX=7ISٚ9nav$.0rAzSyou7p+dhT( O^3]VO'{;R|9kM/[kӒHD`#`gp#'b+iiFiy@nc!-eRrH _h4\,rzË]N~]Qro `ݫtmoF[czvO'a0?a{?7P1xzf2f,%~`J)+P^}kެŒNA|Ǔ[Y>SNⱋx.VuoZm*ŴUJg˒(@\dt=3d+*zu#"}M>&+hr4e֭I%ơ(iGPUT(֬xwr&c[{35,G& SFO(>յT'i!Hb˴x"]o>VZܵ͝,fV F[b=Wqh4h'|9VxtHK`+;H䚸OjzqEo46':Eãq]O'N±vh4;hY>SE*O'pvh4\,]d2}M hY>SE*O'pvh4\,]d2}M hY>SE*O'pvh4\,]WlSFOQER4)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֏)(O֑e#mQ@[}w^"h i#bytVx+j:xo2u_.1$gjku1=/>&xJ}UnLnUFORqמ _=٬յjd8gt[lj/⸸|5>&[>U.fBёBAXwB.5$zd$bX%G/84>/A6/[Kmβ*U*9$t-!^Jak`ʷY^ txpyחIHQii캙hčw #l? r-U|9^jQEwn8,z`a2rO$ /#&}iȉheV0?'{ \ >5hn DB2i*º֩-ń!bv;(W\uxĚwˠTKs*;K$k7זO ޕ T Bl&E m7c; +|=AU섲/wzt/X)u uҟQQ ^%2o\g*y_E Cv2.i =@g2n= w >e} +$Hف*pFGC$58!Έs|vG U4h߇|AgiO|u@uu$T)O 1uџRyF_ v'7uj=>Vs<%v3ӵy𶫦x <;dew@#xrG\S Oi~Q.]P8'^,̵\m,.!|gG|+oiڵm+FGȲǒAUuPBŮ.Fn>.ƙ}U"޳TcԾ'x?MV;IL7 ) >a8<pvWE&3唌 6NAAԛxwNorޙCz⩥͒?s!gVOXno&=0N$oXе )5;{]Z%#?4p[9 u5[S֩&x4v{tEyql?o.ӦZ-Bf[ ;6`qRX7< }:mu8^(e-K dR xÞ%kMQ],~w$2B7(uRf!kV4wI$s򈐗>+U񎙨z_@'At]XI~7y6mCDz1f| %֋=ZF01H1[xW[_[Rizeէܭ<߉w^OQX8aR?)ؾR|> &s(@b@Bk wBuH4VX`?hJ>FzWAJ>vi;Hw;[;|ZO7^B="Ĥ2Rqj࿉P5O!#2l Ȓ 1<JtzJ&tP{oRXryv. n'TV/3La6,&C {fJwpu_uItCX+YR_HǠyS#LCD Aw 𗈴0Qa)o7vg _J>nb.;U,Xm/'{aҳNך5伸lRGp؛}]nú֫ M 'kB'Ē.B<>&Ox[gN+ ?Y{⾆񄗣&d͕_v1EaSoPm$-`1"6FTyo~{/ hak M$v qڼ¶V,~>vt# l]M#IwvR ȳ6$ut??=F߈˨xciA+ݑ2[:G'_ hqŧ-Ԏ!i!2ZWhwM^xRbtʨxGS< s%lj *; @*<迭W#:+:7%Vs׍%<>49 jE/Bqq=jz}.{WH EhÅu=kmG -zR|KMUVI> l3޷oZu ^}C\d۷>ءmq}Ex.YVŧQyp,N~n}+[Ð0F0WQ&Ăh2y(SǷ9w{J.:?[s+ ˾,;J^_ lkʯ5{jIi%2,2awq\6? oOiS4+&,V$z#=BIm_wF_9xi|D֡i -ѿnb4N|g;_y1),2~aךׯ z3!IvRPdvּ+鋨|L/Z:, ~5sS5m" xb(v3F+#.#2#)woa7du=efuxb̡Ob*ƫ:ծm%m*̐x'΍ _ֳO-ޠU^n] +1$,}OZ-|^kiufG 8Ō,^tnz+zʞ{5Rʅ6~)1\ eoua̒3p# q, hַhƛ}^c2y@*  W`sAeFsT2}چmǷ=I1B,m47y2J]vo֩|q:G%Oɥ4~~ׇ+F{c5+tW2>#'^sᔞfܪGڄgޜW= ^hVc Ӯμl~cSrW|_.E[}kjGƈTC2?B,E} J}g0+?s$%~l 7ƧAe]/CM~Wi܌U5_4ZhHO{fYoϺ ]*sSHK]k6_\kqZKj60X_gi]zͲ} wPdg>KK]23x4\*+OZl$2F]|DN*߈#}t yv̶&'1nމ.[]w#Koh y  ڄg'Vx7=M:$JPrOTsߎk3@Ж~|5k!,ʿkS*@xΆov 2uzEMf-p-pA<+:^-/s]2y>^Q['jt,5ۋ}H$c3ދo~.n mNv&(osҚդ'lu Jnn<6q} )b\rOoEq)XDS WZf7;[-;_{kIk8Ez?:K<9) xjߎŗ^E8;(_-ó)%~Bmfv_.űW%jڧ| $v0c 0'90BPsJVO2j=H!KnGB o ƨFȽ ~~7׵[<5ks>|W%P"ƨ-t u]sPԠ \Hַ;-g+]D@ TPؽKxd $7 07(8-.l.Q䳚U1D1I>/K_ڱ K $B3H("YO@uۮhidXʠHymO9^'QåZB{M)/%@Nݨ:(ɫv%;݄_qQ\=&{b! >$|ekҤCO{1Ȫ@vu3u?{#U@,2\kWc t"Hbr8dP2>#ȥdUe=C )nGF}Oe lw21#@r0zqǃ^%Լ7-5}[PԖ_e'gړV$ms#KiYJ+ڦ[h (+cuOj_;5]GNʝACȩFp8 Ƶ56mKVԬt}mEȍ` ͉>cTIi RSr}k< /ƣW:kcOQ# zblr:hmKd%[DfG) '8k[MKGlDEOMPƌxʨEKq q0f =y:4=G)kGݰQKWazĈnX"X=ϥOi9ŒW wFkoc"hRQ \׊AM+ĚƵ:$7-iQ\4gA`I$g#-Wb{^zƊX]G4W2>UvS\/\Woum1y,A?Luִ>^[-es(#Ppi~Gj7J*,6jA2~09mzw:t_&cՓvsߥ\<]wkRĦ XVVdbc0toL^YO-6F?*I R Ftܠ⼋5Qu֙:jc ] Qj~zΩo*^<6d =A~s6b4>|Jdo-3Ees\ZhOe5hۚ4_^jAxiWzTVe۶ ֖Yw͸C(jB /Pih~|<]W¶ {}HP8PQ*94Z(2ko~ &V!MG+FVRo~ƒK+(5\h9y89#CM𮗧jP$} Yc;y3|? Kr|gŦx^iYMy ce|gsbm"'EKСX+4N R ~>wuƭYiZI_1X?H-Ei4+ig/;"o1lb c\Qηw >f~m~lcZoK7ÿ:ԷѼpg+6@>OHdi+ug^cEmjV$^3BYnmt衶 8.qn3Ğ"i֛6Or-2|$LBWi[\: zm5~sXJfmTxbkk!vH,>W\4]osr7DP#Of< O` K 6=6G:Le\Ko M##q)6v4x[oh&fH˳z}Σ; ZH-n^۵%Q1)h4ObLia^h-//$խO az8-7_2M#8#i㵝ncO1wB@?0ĞѼEj)#bO*FF ⼲.Yi4/FDFגּH!Q*:}uk)rK=_L$"I$I:m1-͠^ڝ.]Ht[}(f\_Mq6T{AȚV'eEaj5xžOYἻd#FksБb,|}s?5,-".lD0 w iV8(}Ե#tffxee6rst~#[YկK=An!8@k}z|u\jmr9e22(@+7;;_י^_MP5v1+c'y>jZt-OPM.HYZ8o..Gt)yyH@,utrRz-_n-m$.3+g^sk mEQK=ZaUTpz`2yOuaAGosa\$C<ǵ` 9_\_w7eY%d`Av8c?[0֑X^5jV5Ydv7 5|5ֵ.;u]Eܱ3̊7׺t/XiR kv.Vh9PJcTC[.emOn'[}U.YI茁XwKYo}Nm1lX$99 oz ZwAi,qbNK3I'5PO,໚+K3§j2=znlbfVkm^#Č)c+F]~X;.nW2$rrq^ k"b fsldzOxsk0նJ䍥sbFډa98M/⬷gLDpAx5Up#҅//`}ov uslVo]zb?i,kkk!s ̬ S0K]xu-t[md33!#)NG|WoG@ٔ4=*DB4<OW袐£_RTshQHdx+z6[EƎZhEJ1#Q[x*Uͅp!o"v1z❮+4T#Ui>-7)\jVak$3x|GuO-Tc֤=#6#D2HdF22p^>h~5u{> G  O7)t^ \H[i 5@HmܹI>)ѼO藫pm$Ѵo27#>w/tRM5_*Um呑YK©p9=E;_![]1ij~itu5o.$yT;w4m~/EҭuT俵>S{; r0ufkyx]{[Zƪ Tˎa; 9''xk\jKrF4hFwT*:[h\^(]Fl*>%[Ok0Y[vKr7ι}jNv#[$^led]|FӇ-SĒު[E$~Ef d#WYk6g!\ўm=A+nV{VuG[u}"icgp"mS~r:f[|,|נ\ZDأǾ\F: Qt2E%M ó/d{io/ݻvLU/NStwBӵ{(AF )\r9)awz>2CPAZz<= c_2Ffd!4`lGDYm=ZohγAuwryVPHF ng mR ڦ k}@#MWŝ) ﭯu(c)$r ʲm3=+|7ųu! ߗ4Q$ͱNч[]JᖭFMGG:Lqؘ8`7{S]Ba~4"E1g^_z_N6Ie|ml6g[ |dnBAnx-z-7RtHdUQ#1'gt~&o&u2NIm t`²d}x -pmUb2s)<z~$xWNԜ\y<.UU|ǏzKd7"]#jmn p+iX: , y@h\ntz|=E`ay3XՉU_^,>/oSm I$E5I&{{mEjdž<),| |ckguj{#{VwL#AW"<MK׵VTMeew~q]ŏM7LdYnme,m\0c=+KnmX\:pk-_Doc,-v[l#>~^t[] qipk:otۣa&CG_vj:O]jڄӋm ƩRMO|G]>Mn_ce."- Gؕ>V/ [i7E ˘WK)?,[7ӳBj15/ M˽rRI4e<ݐo >|_o7{Kz}YwI :T^.ZejM]TqkRzqbgoϱ)u>\bsq\cOG&⣪Y wE{g*mgZ=emyj- bDlu)s^wceokmRYVq4^bP+k]vqZCe=Ȋe`rb#OӴkv;aX\(5{?_o yj)|CX.컙 yv9Ro4+>x"B HylEy_oxjX;jOu殣L0௷5/uQn͉nԂ'^#/<3&k?,+[iNqzWy<tQZ=u;_ x6)?C0FyRZ ! נ|7O'`-f:{~]q](E xþ'}H"9&\cp ߝt𥷃;۸ӝR?A=m<tP>9ƿĔjvY0W/DŽo-[얖Gq*'r3](ExCxwt'OVVI^%.[vpc_爴T.5`t#e1Qz z(E-Շz|9 Xv66vד$;mS qp+|%]SKUQJRev6dV(E;+l| xSo-}oP$SrڒUf #`AӼ?D~$'T,_$(9y?qL}+E״-khޭtxd͂L).8=ޙi>u19G9"<tQm[w'D/.v6-]HV'^hޛcm:-XG'+ԼG)'ea~'|7ER-Rid:M2ϻWo~%Xl|=im:ϨG(h|/OI~pd%{[7h"X%9s i`+ҼG({]g:\UG<#q?A4cZwwv4 1l^Alǥ%=c6|أ6|د(=c]xSbУ+1q{v=G`ps^{qNxO@8u>iY]s֌{ k??`xd@u#vO1X۷?Ý)uijQi\2ġ54Zl4ֶj]d%Ӯuٴ bmk2#{WqjXgм򤄷͎ޗ-ocb}A{mGSܾW?s{sl(ns=kǴTկ~\___G-ΈIaxIo-AR0NN}k״ \,a$PEP0((((((((((((((((((((((M>s}獔}HTP0h"é/$lAӞ٥BQY,h☑}+ؾl_J57jm€I'Z~i]i MV:`=: 6/Ҁ87Vvll~z/qK&t-ӢmjsrMv{ҍ@\:oaҦbVg$|߁ϵ?L/Zwr76/&3lMv;Ҁ;Pm.D(YبvN?* k_XyJ6ȀWiI})_t NPWeùs3^>KNy,d3 C$_J6/08˽;W};% o"c o^I ەH#!snJؾҀ(fotoxx-20.08/images/fix-perspective.jpg000066400000000000000000001263001362435004500201420ustar00rootroot00000000000000JFIFHH ExifMM*V^(if%HH023120100Fotoxx:trim/rotate| Fotoxx:resize| Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1310 2256 0 C     C   $" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?1z})GjEp4֚1ޜ( WϛqªI23?}IkC~>7CM/'3ekFZ'$kCxv5B='w:Ϸcb):֗+W>o_NcrxP^Ϊ{JSW]9 }A=~,v ]}ϼxzYU7l}';'Z/?cWϮx?w!{s] _Rս5cOw $1~γF5k: F n [ȧhHr 6svˋxv֍IMjUZj/C'&J=Pdc_V8K[u1cUNj;FjRخZ_Z"8V62;0KJVNSUFo;)^|{yca'X,r_ߑ.:l_K>TlfFoXE?5 7ssh%搟Ef|_[Uups\HO8Avw!Ʊ%y-q=dmZzD&"y"յ#jZ jvl婏Wo>~?zyaq[da! Ҹ[arDv @?0Ҿ il9I-p#-, ~5sw-8ӭK `q㷽y|RI~8= :8z:I11#n]wu\o0su[RY7RFT4nB$mۇR;Qohٴn`Q (ls~^e%uLa^%.m ZZ˫u*rkVᦋ:ALE411ƫiZ[][FdG|^]90q]uY .kNAUW>0x*Z{;i˟ZyU$"ƹN$L#o滭cIRy oBYF"mVԭ:_UڇF-;OS7>P92p0U;Ol,' Rb݂=|@7m@?US;\QVU6qɹBC"nT@T#$׺|[keAOG5!+~Wecvy]ha@!I0Bz/jKBYpO`TVv.\n8LtQcQW{]tl[hy)R@'WG=EXR嶀?1I?s_ 1V%yr9bF?ֻ $ˈu+ls^Zh^m)³08fF]. H@wx\~֧,[49pYT x>XOa5\_Vm*+cm0|TXڦIJ[BXƮ[8'z&{/?k I%gXZI/gT(2-(s~RXO35{s޶#С޷ʨЮo_ohZ$\\F3fjQŪ|pBg 3Ttҵ!ZM>\ bQo ,{Q=qOŋ\yfK1~-IM7=],T|U%65cVD9l[9_Q]\ȭqpp~<~5}8;խ# ᘒ8B 8W?Kr(ewO5/6;k!2yy8mNZ$cdbu; gFB~4^؁kN" NE2R|[ ^B^.w޼^MsK_֧d}U)<]f\ owi$B`<$>gx5%XM`(אj3zUϚ+{'ڽ?O%gy|nZ5ygьjӎ߲uXho"܍(~bP2+=IFJA}Q;5҂~L};3iJCqtȓE$)Z|;̲9v?u$,IspG?~*51IF%@̣k7&,I:sz^k9]|'ܞ9ѹ+#ȏ>Qڸ(~l>?W&c+Gx@nߊkZ Z\=u_~uNm+<d%:tg$cڹ2*1:X`2=+͵Y%tYX9Qn澎lOݐd9 p1{3]\n*qlϏè˺@$9 Jo 8EO6X.$s\XEvNdWx*d:K̂z?vЊsH4ڮ KYB\,cnvY"Ww;7v(ݵcןYpE8D<Nyiv>a=nE k[=Ŭ\+l7  װ:iIYb`XVm OiHI\ӕ,*iZ)HL Dۧׯ~1GOx'PҰw n@&:G$򯕼sꮷķY$nbS/=?y]T*֋^y^Hֲ1WmlYJWu]5k"6a"˜\ ,E e˞ν q 0Vރ}5cW:՞iwtwj #UYq:%'*ŻȒlIJ魡F[+EYl!CbȲ7s *ϕe9e`1־N7OC%RwD?0TֶY}sHkC¿WX$j6X T&3Zq,T~j4~]!+O~*?OjBV?!Ui^'Cj?jG8;[^n<)adF~*mKog·Om5߉d1~5zV.aS'ٜrn}>x}?h;e {WŋmW?1 xfr[ozꠚ[eDLƣd"*HR)}YZ.qm# ћÜu\3ڷwLjKȝToU5_Ųـp⸟ڽnI`?xg#cנ#K!o؜W/vcU9k-o%[ʍ@zz}̶W}Ѷpz;VdnQ(cYihX:eܟҤQ ّ.D"wf""3~쉕bIz!}Ra>ҋ T~SiEEL^ZNR۸.I"Vn )R\ol"..0EPґ? ӫ$>t5koj\3b?bI},U.i >A ukN?z'&d˳^}U)S.|mu'6x*]KGIB=xkqkX`"[qe8$R=Վ,L(TɒgVK kfR|?F^V )u+qI+-h3\*xUN.{]AoliŹK%_iMrm#* S5 x0"OAמmc8&]+A_oB1_-N'6~fH2? Wf7(d?uk!hx mHK:O?Z"?;7y> VH=|@ɏ gJ +Zz |'>#oVA| Ρe~i=>Go Eʱ_Q:K⑍Od|d>^lO18#ҼZ"2xUZEe6Rd|>^9WJ4y4Ȃ)޾ 흼-h 3=8μgRMc +kMYj>s0$$?(GEԹiu kvI^,VO#>LHe?mMlZ2dPś%OS_VKܧa-} *O:4HiBU@VYF5nŷTVo-I9kFT0 T_Zu_RIyj/)uX N8k+ȥ9l+O>3N/дj2(Cg^7>:جL]|s\}_4/|9Z+;H?tQܞ½Gsbe{V@./ҽ φ,|);;<r>ڍh ڣyӄg;=OvJ0vL?Y4ymt U{av>\ǹ~ךBxULLM*̞W=1  =B~0X׉ڭٌj6?UW:x 8n|c74>&ZZw;ERW L+p>SxM| iA\j0Fu|DKi!Ug_+̦ۥsyP"tȁe$ rRǍW-rۃ+#]N|?0ηؤ2) qeIv7wM+39'~ ~]Ytq'T>wWk0]F'YL/ׄ=TjZz}w z|"Gus{Xv?)ВqљciFCh CÃǮqU1m*(B+鏧G]067"fz}_T׶VH05Sj׆/*Qd1~GOQ S=y2H#vޘsMGDkNWЫxqN_;n^Ke,*\nS+N5cu1Zɹda!+v5gQ on'HAT`gy9U)S,aeV²2XbxCzլnpcVxmpB8?ZMuyN@r 3mT{RrrV: hoTPV?)lOҺ[/0}Fw=H.Kk+Y'0 =Cc^F+{_ =ڢc>JQo4pEr*so<8EXQPf5幫vމq(qASZYtG]eqĻG;[~J>9K+4-FIВ3z'ۨfU?tmy/o.HEk]sm%dfXkjo4iΑ+$+pGA;ײh#u}Wy xʣi`A+ֵF}$בh2m+V?IJOZF3v R¡7@wNIW{64,v6~FɺOrO ^OwgndYvW^i^ ձx'͂ZJ[?U1_T=O?p>E­Q9>4tI0 @G E@D.G_uL =3J?yRWYOō'QWwrǾ0;WT9 G&F`=r#M{_sv4 d>s@ϭ)2  jx~D' ~@ꜹUU&kIPƮ0kzxs:SvlP?#W>`hؓ>}&?hԕc\*(E] 0rEY3G,r/ ZbmP*, J<3Ki&#=6 +SJ矄>EӦmjK<'${^>K4qзCu31bNOWUfK5?$\I8cdr/˛OpX,;T`7zS\m+uR>SE21]q̱`Rv=ƕ^=0O +zTȾg/:VqGnF#*?:~ o9œ峜\86{?q>HZ+>E iRA]CI+ >9F{7׋sU;iT[\ޯQ;xȲ_UUܾFa>VMc3k~3BC'ץ~.%Pڅ#%P^ rk# xZI5z5֥ @g?5k'sͪn-wǔ?yٚfYE`̍85[ڬ̻X  EzB*B)xeW6G|=zu, ? CY u-? "D.Dӏθt1xx^ծ0#jB4K͟}nZ1K}Ǘk< -k|(o]:kg ;y;G^eonnPNY{{m)TH[]Tf\^;>X5VMJRw\iZd/\GkN~o+wT^v$l109\\\!!carB} =B< \Am~YJ:$ճ +8\+@1 'zׂ F<[$xN 0]" {{tG>JK{kvSa. NslU*QֵIOVx Ȯv^M U2t(qƱ 8O8_G]6O)`l+ڣ7+|gXi.GtOK"SyG2E0ܞ]okpडOC-9-~z˗d}'[h3-WLOڭZZUb~:x y%Cs_#wL8ZGo}"S#Ow(i63+(Bvו9&tԋoAv眂?ƼiFIWxu5!$l6CcWVM.{.X嶌c=֦g:rhȎ1)Wҧ0\\54ȮgR񞜟/V;|vlW/\6#˺6%"T$*ٍ{-Lc4pl x /c,gN7{rݞ^ؤ >Xd<8U*Π{h7q/8[Zu]}lLjH# pT`842+0KB@H>w*Ǝi5xR@on3#;sϞ<{v V"Ӯ-RK+n1לgұ# yfIv'pOU>fa>iu\iB<4/j-7]?{u'YHe—:g9+Mn;eD>t<G> cG#y9q8U8=oV$k2 ։D"h)z8{C5{gx -Nk%nީ꧳} E9va]5tt BSq r۟2Ot 'dp5nsnƪC~^7Zm`IG,П}Jh<,/J*T8ɎoPzg>nɲr冷E+p _j~)fC_kS--2Y}^[hM<ͶE)Z|g6w!X`ozNk{H?פ'$j|NU>I=N;K{8o/4fIYۓ뎜5ޫjT#A gO 7=ᗑ?ЊM2 %]d\}sѼQ\ZA[*Gp{`]<1ӴmcpXj^[wr8"k3&bD{ OUU?cZ^!v*E+'<<;oJ5DOKDl~}C\_XH9$n>sn L՗{I(2%N0]q].\gJw(|[ZDD~V|uҙ1f%][P=M6:)Z M<4 =6T;1ךx69Cq?ȑ3x~]knVi n\?qnmdVhҽ|=qɽX)KcOn܁4M"0ޠNpk%b x1|AcUɪJ<܃7k;UܼR#n~ [zT.聈ĊGU yo5N2}ӵZﯷqLGpq ,R QT%~A%Zͩb}FUPkxXdUFOwJϡUZ4jn/T|7ụ5|~%͇Y;+c+۷ҡ񦘺n9jİ钹皳1G,'ߟk|\|\YWm8IwA\g{J# 0}$Ob9-11󁯨5ͼ:{A~->ȃ\"ꗩR8 eXLI5|D]?F?+]@TmʓJ?`MнرnrǓ+-~[:+FSö7W:Mf)29um߄#Pc%.*owLe qzQQ]C 1&C裚RsI]?x*Miyo"2B1Z? ]^Á}=q1_[۪!PpN9֥N eᦓRS!F^L~}MtO\v%b_b#Z[]9it :U[i~IDqO' %iN6WVξS@fl2Op:ojV0rd%zھmtÀQ ,V>1VQ=r[H1F2oRUGPtC/-+(,FN>&і9UוxP\g(=K=%['#;؁w^ùNDa&ܞIdf|AcDOJK 0X<^_(ZHU{g5nm8k}"S} ZjZp!T!l*<^>-96#yg/.duKLqFvc1=}kqjlRBs\U3j%3]^m6,56e,nϦ?UkZ.΁*x\=5v^ u[GȎP>:c\N4Ve 4дӤ|ST.Ԛr{s} qC_ \⇆݋D_`xAMk1%W=mVyU;c^GCj>A>xIǣž(P+>ǿf?o : Z4+ FZ7SգFuzKyc"d1/Vav*!ؠuᚷ `FY7u e#/;'F n/}t9/KҥQ|RkT]{\j宴}s'[s *~vCpw[7̻xi}9*pnJ~D+.qT}1W4;а >ݏ+=<\im/}=񾙤}Gm BۋuB#E.qyï\/Q;61$aAA>'mj)bX$\+qY j.tUU'v|>DŽ,!"U sk3媡~ggއ:"xZ0mV]j+H4W~~yDh;nWkFjn0f Xu=OOM5,) tӬ{8Jօvr_LlJ&pw|Ϡ76'JmFqktBgx˶֯1qt>ЊU%5t>$I/?S< ⳸Q*]5{dA }OzWj1]$7@[1bzJ˖\5SF'I=r}?t>NvoCu~ss|Ah{$ o%/n~E>mOUWTRv{VEu;[mʥy$'ԟ+ME.Q|\}¼7fQUG,}1^}8C;y_A#as}oWk{GXUX=+帾2d)|wwjcx?\Ucz5ڇ7F88G xn\P[7]&Y ;ҽ7g+}Q? x{k{U -x,q+4+{D[u0z}^}H7̪1= j6O%B˸znq}+ek` zct:[ .T=I׊xԷ=>8n%d!d?잧5*Ejz41 f47scG`K_H0Ht+]-LwQ+ ś342GCS))"K]O|O0!]\ƇW `?Fq l:Y/4Jj4 ~kGQWk-XfoM|@+-C+,JdR'c[eee yJ[n5+ wd9'zU3xYayP/$mc{VM:+?'pdo'YvB֬uFУܮ=7~]K+|ø?\ δhّ¼G zFTk[Zb3Ov%kNhǙdxĿۓMioo4M$qF9o:sE$ %zq.72cu_Ҿ Wes1u0"_Rmx޹}=2|8<1G/=D)䐩qھߙz7"N[zze\F4҂t$7?l:?W/:RgFkp'aqW=JrJՇ4'yykdӑH#RtlVt~I'y?^+U)W~yE ix0x^G >=j4V5%!=l@:^oq|v- l=> ǐq_+8yTיُoUoGvjUb?#vKc'ȿ[OSF鎕_x[[BJSm7¶- Iq 9?׉ӌ#!8«:{ QZ&;G`1֡fIDPI$8|(AF9]7xch۬-pBr0 ?{EDTTe[/^ϧ>rZ m_w[d)>?ʛ}qfO&w}\ yOZˮKX+X$;~Q}wUʿJUK~f8Zzs ˵nppN{v[r?*TQaD6۰ma¹ bd1J Iæ+|R}fuыTs']dAuxX,*kqjY2F9g' `+})]~pޡ L,=zk[ E XTrH* aO˔8W8T=xQz[B2iF;Uc-xͻӷR*.}P\S)czRUJK6X N1oζcX-6j%/!A܆מ^Iy{>Cfp{^^n~}C5%̆A#HP:t޾yjw^mNU%n8}kNU'ԞI\Cnv]J\+v+H[{9ܣ'ۏÎJ[}* :T5&uK˜eI?~WMY@'s]g|?qqs3vv² _7[yz֯t]'_8 0;gOYJhelw= *I.T8;Tx JWX2Oz_@J9q[GVS r#uZOHmyŴ]3x6w^&XL7&V9/bN9pS(c%r@ :{AHi]%I.>U̕1~V趒lDǠ5<]%@fIqgqޠ4HQW$;9]=?Nĺ^]Kna^QFle%P'Hßi^_ iT,&V˦Ajb>D$(*}[KʌpE}^*(?G[k:q^Y,O{B=3 >[}+][+p#djihCťw3(? w.~Q+ 5s,½8P~rO6Ri;oN\@kbG\7w"74Qƀ.&J8Gx޹-M$#Mv20G@¿l&o|Z*Y9|~}Q_TrF;>'x__$b݈% R.JN0ۅ} WV\=[Ub?#^??m]|^G|=tE C*ORG?h zi_kj3jk&ͪIǃ5dsc]5UJ%|lXMx~#`#ךMFg֯G#{EF;g]_J$\[$)緽b|%oTRUxMvݷٞ+1q\և׈m|H>k#BR]=zv!D3L_ƼWĖp:+ ^2OP{~+ּ^oجmK/Ȇ!>iV婱x8I-6Wez8#޽Qⷎ尯$(v hV]˜g_Bw0C|ne=0EFf~u|E\]g9%vPGzO/䙭bW匲N>[Rn?3cQ: dtYIrIyhUQ|J Yj3x!v eSw`c WwϹ#I92>z%0MoZaǩZ@1AUuE:N-c 7;:18]Yk SP:R=?A eA𜀠rX=s鯭^ƞIEV2D`G ^$Q\Bsco'^'~*֒*WCƽ.ZZβZ[Wu1X֓~WG])Gh߆<=ymQ1Fr9#*[=#O1:ḽz.\<ݸ!oqc=r1 RV,T88~5N$On|65i>plR^?W,Km}QO!sN*']&UU1F]W6a^VvFhݰ!;f;\di-,Ohkv;FEgi#n>±Y]p1!]?p\X G|6 }-9Aɯ?jHjǸo/=|D¯/WINu+!N^y4ֽ`#V7q>D*em=3P_P:Zƣlj)\w]Jʭ.xP{}"]U i5zNq."OֵZ>Syۺ$ ?ƹ߇"2_FE͕^|Q$Me))Wed}+bѢkZ?s^ 2{f¡`8$:ӭy-i+*I.Z+!R=?⎡hjtbNv]ø#pp o֒x^^d(vc~Nl-:UNL| Qٶq+Fg^+. EBzg,.:glq\׋g>Ρ|]Y[1n,'ۮ1_e P+>xR oa;"P7mQgNhT=d*N7nߍMRvPfI$?!^mʺub%\1%#Lʦ.o.UHtBO 0\Yܾ 4[A|䃸{ם:$IRPGG&.˙bWT9`VM *ug5G&#kc-@ 8]}=GK9Zhu:NY|\,W ?g+Y&[اfs-*g־[jwIEb~''6 Og~o aUj|Y@Hmdn~yHfپF_c}G m4NsIW.6FG?n:$ҟkwicʉ^n|/ Yq*Vf$ 2gny5qؘY>^KF|崫] K3:GxP[h5;8.v rtSǢܯD]>0R5ݪ]ZYL.23sV/,Z- YDw6la`n^zC_p*Ld b2qN |+.9#ufޑFg8WέiQmhMTi:olR^kR"?m@>+~&O8lDCwDO_ƽ :;YX[g77@C^W-63ݲ/ rz澞8jM*0JsWK}Hm";?xkծU`~@U:Ǖ\l5 Ge{~HZVcqaow[ڇ7.c8y^W=]&]L)@VzY`%&x]i4NU8fHWigcj 9'U aPzI,["IgN{q@|Z6XT@,ǷZNe8zqmMo=BG$uqWAJP*niNp85Sys em+e}9yC9c^iIǚ>iN-;^eb.2v U {w`+ָby'_s L$C<:Ӛvl[8|.)SlOYx?=$!]ݱͼO]6ǟ_s/9$WӼ($_F]_;߻м;!hOS4W.r6_FIQYu+wm]~ΞI*ዀxt{F4Rp(*[=k[MmdVBEkW1n:w<*XJ-w?ҽ_KJIr>cPǯ\°9Y\A,{#I `ѻB&GM׍Y'd+sjkm*+Rм7m]V+ʼ vsQ_]EMm 0ܨq~}oLUim_6$ezEZI]r=+R};UQ+jCFH=Ǿk ǾkϾ4#兜:&<4kbl/- /wS./dByd mOzuv6jZr~]r7dº+ZKwy"2~A>Um#Jܐ%cuXc <ڊ^N}oᇁ R@CImϢ%>ٮU5= iV(\霞zy©MR4V6Pw {M?bӭjwopKqs{W~3QGӍW+GS ^ lq8Ұ$\P$KӐZVϘ&FS{⾐/},d+!JIs> u4'Pә~rܞcӥy,51 T˧_w8 26Y|;=+˪Eo@Q9\zi:UvQ$rEsGNixZQY X d>玾C X+j=kRm>_1ѥ}\]־X qH"#G~aL}GfP:i>.kZͣUUIԬ'Ha#hswk]aڽls'n[s|׎:]-/lh+g?ȯa_7k9G?4L˼c1WcT](w93kzEEӵkW^p37 8޶ȌGFFM~X}z*i4ֲ3F+=IG 1w%-l۲; :ۛ]Sm3=5ws {ËWL!\vMv!Ӕ]NUG;->yd~ xnfx)V:m^V@BJA;w uJ1fc#t}A^c^V63<7?ѣ\k󩄊vfӦI`]Soƛk3 GH>Lc?j`ھjxfZC_!-Fs 0c||qh'XD^Ŏ O_HV֓qvga~.4,(_k´֮M{?(j)ƌgV0B $X~.X+<>*tӥC%gf~{s^Í/--V+>7Xv+Cź̗pD$erςAW$xg}M ʚT#w}#*0znIlOފw2 8#,,`pz kZ/.%b'#8\Nk# F EF4!ach;N^lthǖ[3rSkڍъq%ㆹI r"Nꠁ׮956 Eʟjt=\%=@n@5ZŶ19vbBB XgVާ"hmg2#N28|:=s5Y/hpi:zs+fa8󎛺S^mE;x~S<״]ZOCxڝ`~IˣLmk^Ky &:w#Ӯ+rU>^g+cЅx|7: 8c,;GQ®Wcd$Hr榺+.w?)#wIYK4Zfs\'WTWg_kzn&/rlE\WKy>'E$cn7ᅤz.֖q!# ;c]g&nm,&Ͷ;avc=YPKeq E}!7tVO:מ?=W1 #snjRpbF_Bݟ\U]ќu%D37,G+uDf9WE<3\_H; WGi!Py|d5TR|Wgcl`,)I銫}6lO y=9ֳ-'t)Z 1Z{4Y5;.=?U,NcvEMyi xdݹNXdJe}nGJ[·>"n̎X?fIAar3:wGc]Kǖv1Cq p+ּ#k>+hw0v +MI-nqNI5΅osUN造{uNX.l||#`r<IL:z֭VH?Mwڎe,wD01zW|BU]M_Ou9#?ݒp|ع#Z KGje}]c'''J7ey;X[raMz< zMdK#U#^0j!tkwȧ ~Q'U]FN6wqkKX/iOnťmL{&qT|֧ih66f+1Ԯ xf|+fj4vVgA0{rkf5Υl^;aL[!pGZͧnO~ݲ+a,rc}.Ba9&i鶪Ꚋj*^±eY}2=~9=nsæGg ÕhXv1nXs^|זw 3U.U }ӽudQ\,=8OXbFsg=3ڽbTba[=ChEG־ _ƀK'[*EL)?Z 94 r`U/A^eHK_8:p%mwJ/c{35378׷]^q)fby),=Ns]>[iFQ!{\<6ۺdգ&={UK- iy3Ao )'i}?Ɲ ฼#*5̢i}pzg*rd7sG^I2\(֕nKLoIԓLw\,HTϠUktZ5͌Qir|Y:ddSv_=<>%Gݒ&նmF8pJ9$gnA#'i,~omVUXrDEl`?spzV7;soķcJ#d0ӫ?f5캔|KI ].AMz`ufV K Yߐ9d@}+徱Ft`Uw4Z<Ɲ b~>3!k; OeO.!QՉVjwsto]Frc==?Hݰ[%X$Gmӡ4[q.zv;~eZP$k-qi^\}z 0=@RZLi0d#ֽWWI*zMclp&qg{ګxn/@Bھ{X\˦L\6Q Z~Y.$ܚ0u'GUsi{eA?^}.-zaOwwd] u L1+ޮ5?cqT?O#: [a%H+ҥ2&YǐHXQg6f0H ׊DkpZC.GPq?^Ջ_"wޕf(+yj\VrrM$_[%V`*s{]t;Uiuo<`$?w-[;h0W8^,(խ%gTksm@ռQ~%hs\0i>KMM"{߭S^Ee>.l=r暷Wq![b)JGO}GxI5aY(?w8uXMyJ; .?]6kꚭ 1ڨvRNzK UR/#0=^;5a9/rzj&E:]2UxRF0+| v;̒]]w=3ڸtr C^Q%uPcC ճ t)BA瞽ҋuQ\;\5->0K GϧAygp<iP|>>a^j6!2Gd=]\l%lAeaF;dƺ~V犳naG}@iBMk pџ}sݿ~h4Un>FLǰEʤsqxu/׺]@?}k١J$M^ov7QZr?esmZ-oh@$t v^kv6Mmq͕m-ҼZ3ZZ$V^@\}N?ATě 햱1ׇ_X8rh$x~e%c g C9LW:,yNy-#ZKLLCvk?vmdyl3Xu.t)we{vwӗ4kg-4YU20kٯK Ra}Ht4F4np H^cNT08FGjJsQsW=I-q{'ԗRiO'$z,<ymٳN9WXіKyhSb8^e0Ṓօy2FTҙaI2ԫx*>d% PN(RrT t ҂4vR{Ѽ9?qqy'Mf%ܫu|!~-ͬ<^uzzd}}? ;` }~gW~Q =oW]~$=dJƮΞcKlY;!$g2zO⵼il ;*m!d{89?һTNc3W85h31_bx#{ 9,0C\?]$k7 ъ5pܨIm|pA$5٦C0b}+alKMGf' UY4#[GԒ==ī%峌Q7a5y#V${+2+}+Ef-` O֣5T7R(?J5mnVȅr[S}z\5Z;9+ɝmafU\yVgܱSXCb"P+/ԓ 丿!ݥ\}*?\_7I*[Grlm$nǧ__|&u9Q|CkIyu `~FD ӷZ_j:jQ6 cYg;Pc.zԦӤKcӂ=k?Mѵim q7:DLWcՔO$g#xOOigM%C ]Utvkc~or_Ʀql1ClV"'ws{%fSX>aW ae+c}%+d}=ҝ *f[`eLazWBa!R6eV<]pܩ/ekf6H|+ZMV@A[ 8}xKm~+gA* v`h VLt}kQ$B]Z#9oҳ9shht_28yLUN˹|bjَ?,U+6mhiMwB7zza $9d/uW٦FYsG#\r*4 #rO])z8>M|~,6^}I4q??{]ny 8'oÿ:Zߔlkn+>1Ex#e'̊=H>5›q$wkx:})@;?"Y~C",}3IxR/ZqpdP ̖߼Mi"VLQ5qsj;96ekMbkk8$X~dkw&蛰Dv׫Il7~j޻ugp.srzY:ZosE9a#\rx{GseX|ѿFO^H!1ϻ9YzgwA q4C#VxE]ؤ{xH}Ssn( 0.b[ 컘CCi,]!f*]ƽ RhJQS6!`|(j0d~"#ԙ58$C&-eaȫ?l1˄ jñ 񞃟JqV|CM+V`Q%ߒCڷ],v\C@nt cdYgɒVX8׈'s'Um=KF# ̽V0'o%Q?e~'sxrU[Z)MBiѨ<5p^g:j8HI˫:*mjťc)봎9`$y\I{_Q8C-XSZ("#_<ζbvЅ8 _^n'AFikY#Xβit{jh&uL˦בڽ|[_? s%+ ΑiLK >5 g<|D;?qKRq=ηDEyW9|S5yÖ[ 3 [뎟=ZѭT@UX=O]UJvLK3Wv)_1p]{Vy2]oH6HU: #kU6Oi~o^~V#vH#V\_#BXQȬġ/RU=}kʯ/4w*uK"~;vV5涶Јíʧ }k|lu'{x$;?ҝZͫjI)-sWCԼ@ \#0=+|A{F"U}l >w5xϡ^BkM9mK<Ҹ=FE6!'YTH}6#J+oNEP!L8hͧG+Fm ;zEL0^l8w Cz)DxgZ^of/V¿\V'cNinI"$ݹr˴SFTpIZRIk6ējrC)H*eSkѴB=$"2$ {UBǁynu4BNdGZm1/$}-RN<BRxϵqT$tfxL|0$1]Π8 /t'z_݌'=j|M&zݖ8~']ֵotȜ2:lb*ѣs:Z^L%,n3=^Z@Z G 4Zs\wS0G^sAl^C8m־r)՝+\ⳛڪ#nfO_"im`㐢C6KK fStI{VEٹCA[Wz;r-MYK G 4YX]P62}+U'4Q#E=y!#J)OV9-k79dܑKxoUu]tb~VGY4 ѢtW@?v4]~UuLIp⭻rѤ[R2XWjm Lέ>S[Nœ[?7ҵ\6;B'RGl c: o# 7G@k*]>fe6]HCG \ k>+D&;@#׿u&ox?#\ƭ/h7@Pr?,VŜP{Au!5pgᶠ9k5?>-fX˚oe}zܓgZZ3QIly%ܓV}(K_vWC%yE RN=O7 sc_aڭ(w!< N3\rY]Eoy g'tŻ%<&#,q_R~b=jn6$nO+zhR!ܖ +.Gt $ϓHOÚq?+ 4 eH y}c5#}&ob}) 5VW3lj?Vɲ}*96^GCj>F7|2x?7_x~tPW?ξ i}ok=% B#OX mO@ѮՅucjJ65%$wvZnl!.F}I5q[(⋧ly!đw_qEvRo+53[* 2njku% k/&m5ȼ&4ll>yWZtŝB.zH\߃ohd]{[ƭ(F*CҠY7GMtZVoSwXM7u%ð-#lysvxWqsm Co63I>Eo0psWMҥtԹ1Er_*8]tڤ]H>չb_;jc3 d4h`Pj"CEhXܳ/:k40w*>*46 ٮ/4`rް|Ge,K<nUT 5ƍIZq!&k$0$BL+3Amka~v<V'4]jV8nirQx8ϾANu>!4 +xJHou=G2#j{4&rZBp>UKϛ#sPm_zznIE'6RF<|Oh|yLl5 ODs?cfZN5^,o_z5o??y߄O+mGUۄiUR@3W {T JOWmR E=dZ/JcA`?QU&]5Qڥ=Ԛ]"(cU$5;$}WfcY0} Y<u%k[Du1/WM0:qY}^ y>z?g"_B\3u/m\vW֍꒶?#_TOjO/am(پy숬+?f^薷p?*O }&yB3Ue圣淏\T*i=9ng?Y学#ʒ?X? ~!UqԤ5 ',8cjc(A:Z2踃Z<%Ɖ{i F]i%SH?~>>I̵wn--.qO,!С.q|r"6,1{g+O}χ>`x'Dw}VrҺ?=| :u%mAY?0QJT_@7_~TF>+ԙ*3)`}֤.p |<~wr*U<8_X.8GrGC؎kg+@<:~s 90F.o?K)崆w¿̇i||mP$ŗŞmFI\*f?{cSt ThVZV1Xy󕏈K+,A(GVPŅ|]_oyk8Ү5z@Y/]?Ճ.kj'%] ymk2?=O/ȭ0?*g]A'oDg53*|W!e[/񛟉I3&9 %_pcJp`?ݩ2JAͭ\5rtLq(Þ'̅bNI9L$(ǐSg}?f? ō3߲τn0_L{NUIte,<oK  ?kir2%vkeowg=s;Կ~tV0oshIgx|ԓVi:H~0!.>CײvOtj=zv7321N\Z4ꔓGx 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-20.08/images/flatten1.jpg000066400000000000000000000400501362435004500165400ustar00rootroot00000000000000JFIFExifMM*V^(1fixgnome-screenshot02310100Fotoxx:resize|sharpen| 5http://ns.adobe.com/xap/1.0/ 189 300 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xjb7cÏz(5ږBC.lqT{kۻvWɟM}a |NM YgJE[^p73+/~ AҼ7s;2]rA%{i| #(BP>x5?xZI֓IY[kFY7fE* 8"اdsξ}G+Ӽ=1h֣\k:흿-1k{^MnK`XvL}QͪGlxbRBFOa{W}/š(aV F$n4Cq$NdYQ99M7ƯxWONƨw6܏,NT}sTg/_\jl>|# `n#+W=;OAY*?.[+S#.uxKB|3 Z͞ou})-GG;mjK,k[I$/I':?x1Mnu_úU̒Oo,& 4gH 6t}6z[|3tS%ȸx"ȹRIfם6O$y(aOzwů-axOMNmRK$eIgHQCC'c$jMc?tijSo {6rݼpd0|i$Q2aOl>|t/D]2J[h! "8ȑ&t<`q\ڽUMX7 *K$5Ksqc^>|PB=Wqcو[M9VPz2[i;@?Z'xRD(h$/7xx+e# jKI/59 >mBTżJB{8P}F)ZW6{wF@B˸zknͳ:.ךeH+ ie";ߦ9#Y^[nݬW@gɸ+;0ӡ;[[g;-펧pEs-ރ8o5 c69\ƈ0ZGѱֺ/zz5^ 95X2U`xןGGYkq>Ú&]OW:c\B =$} O|>s -qAw(WeOkSO,X%T?bzW'9*3dFKb@|nQm(m;liե>hNM}k/+oG+oG.~z}_o_=o_=as'ֵii4;[Iwj">,TDZk_w7oxw7ox0'ĺдf{M&k5OREb#=El?|ykX_ NX,m-.?|P3Xs|6.s|6.G㧎ϋ?'EC{n$hB6N$4<𾭩z_f'U5M=)"hW+oG+oGw>wv0^jO/ݟ_d R񦳨Is4ܭ_yqI(Lq_zùsmùsm(f% n_ϋ<[;'ӵ}a<,ZZ`y&g->'ۗgu{x7M B^ o_=o_=.^>_h;? +׋+i"Vm2N^ d:9⟊tdqگ 9 z? 9 zn-_?'d|C/Em;C\2hs%vҬ-,xg R{c|J 'ZP Cqs.l`.zskw7oxw7ox=wg:?xW$V3so[T9FiG| +oG+oG+ቦUWLc+`kF\]OϊZ6ʻy@==+q 8C 9 zClz+oGp8(Puf9/5bH+ώxzEᯉZ$ >9~i%w1Fv%xPr@>[G—sh~-[]/\񅽥Cy$q΢EkcLd[hM;Pb|1(kRCoHcXJ&2m-Ä#pWW_xzz?+j֟_-ulֺFs˔|0S; 2G>H~#FBWowҨn2crQE[NIm(ą"+<)Vm@3%g< _p?Ǐ<x߇MۖAmUq asbJtȔcx:gJGqR?O> ?W Un<,g79M6 0;^M2 #xt6K^+].dbO\o,񌂟whl6ֵW}mZφkC?h6:i%&-kE%t p2 W7*EUe?3|S5[P|gx3T|ƍ$m*eQVRv mgLִ)I*AZښAEdS[2SNmnwu/+ S[2UmI]ZKKS0ɀm(lwPs]=exA_Y$?ihb)hd#ޤx>+hV[;4R \J 398\ <;ƾizOE/;%ŕW'f< $Լ[W>u%ޣ4tAYh Ċ|IG4Z ޽ i,Nm92ɖY:x&ƥC~nW /~dhDE @ֹ=i7wzީ +ڋ+}Q7uSAϩ~}X_j@ר-L6O8PGBI+ڋ+gKMټlWFKɞ9eQQ)@ G8cVi+^#ZUGgmgcuz͝mUԒ!i ^Ve++ oWNM\#6s"tCojgIW z?_?\_H_?\_GW_ _#z+/exe;+q=^/./G_?\_GW_ *|G)>_Y fmx0o'fw{UMq[g|?ŸxIOH m^-# EIsq%X Vo_If4ɴSSRȷI/Y 3$ WRWm@_ u Xh_[{[^D+-͖Pk[Q2?&Z[N,yˇmx!IxϴTM71(k5H PWK\(FyINF'x|1Ք\X~).rq[FNORM]̮_<,Go=σ [_w(wb u^Ե ]?#j7z@1\JI,GPr+sÚwB.c#v*Ð}Szcdj]WwƥI(u99xk48QaTG d%b t߄Vw#5=B{F ]@`%+֣|[?_'YErZmh|o3]e¾O j?ŷkQ-uP' ?5fWAG5Q@+֣|[?_'YErZmh|o3]ed'Y [fǾ1@?Zmh|o3Wk_Ge(ӮUi-IbVrZmh|o3]e¾O j?ŷkQ-uP6ᘴ n%K' X\GճEW:W5~,4V>if*An OXobJY xΓ'ľw\i\; G@6%ʹHGN1GOc,l; Qu]ZG]\M5̞=qϑŴ&1@~+AWVyo5co.dU-m72K#)h,,[nxs:-Fo]]!{HL;$eXצ~Lj>!ӴC>[ oz[n;Σtk-'pV7l|_Xk_۶lP2 >dw#`Fd|]7w~n"n|kvI0aR\f_78/`s#G|[~O''5z EuCVVH6UclʠO,A<hkK'mh.xF].ΐg?hh܈  ~Z}YkZ;֍;9 /jo4K [t^jz|v-n|(=ozR3TmaE4t>=ԥ_[^*ӬXdNKKBba*/7p-[G6$kx" NX͐_Zt}뺵sH? G0?_C_$x7Qa}޾G_c#bh.iGϹKLg?޾G_z[?'׿4 й _M>/D91Gz[~93^_)3‚ ㊠~]zA Dv44]jJq#+xB>@q sVoc]DtRUVYzc}"L!gR +uV0 y&myn{tYg )e7V(5  ԬwQ$ >Pwø߅,=#{xq$kg'/#q՘s\2?e.?e.m??O- .wQ .wWSi>߱fϬfwPxK⶞ &1!cҶ(mϕE Դ^KeSD @Gs s@{9Z4Pw?ڗ?㕣Egj\Qs@{9Z4Pw?ڗ?㕣Egj\Qs@{9Z4Pw?ڗ?㕣Egj\Qs@{9Z4Pw?ڗ?㕣Egj\Qs@{9Z4Pw?ڗ?㕣Egj\Qs@{9Z5&}w_[ivVEy.$1T,o[Թ=Rrğ k/#Q3G@.{})Rqѯ-j'4Ԣy4wƩ.'MBxEQNԹ=-bγ8hbY pU*!y6 ޷hޏI`ֲ[ԉVϖdRC/ ~К Υ v M7U%$G mk> Wž#smڙ՞G!l`b5;Kм5%ΗVRGq$o=_h}v9;X:fuzG)N8'ִ:D6wڅH,1 3F6;K.4_AԠ;j lQH R9#>W|ľjւOmm̞l5ʍEk@J]zAt -ηym I4 𷇢TX/)V'7mO4/'e !H?9ĿZ 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-20.08/images/folder.png000066400000000000000000000235221362435004500163060ustar00rootroot00000000000000PNG  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-20.08/images/fotoxx-views.jpg000066400000000000000000001726741362435004500175260ustar00rootroot00000000000000JFIFHHExifMM*V^(if%bHH02310100Fotoxx:add_text|add_text|add_text| Fotoxx:trim/rotate| Fotoxx:trim/rotate|trim/rotate| Fotoxx:trim/rotate|trim/rotate|resize| Fotoxx:resize| Fotoxx:resize| Fotoxx:resize| Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1310 2256 0 C    ' .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQROC&&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOZ" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{f9$I.^qcRݗiCkR@=+[Mb[dyk9}d(m}(Vk@zh v'º`ǝ},\d_9;?ͥ u^<8?>eew}(\_0V<;nXKG*1 Mo=:[vr&.rNX;̣̮?iLD$Sidufs eu%U$HmރD~dYHKt#s euGONdۻn:=jlEy)X74mu1eL|ќT2xMF9ү\zQ`90]c}29ÑF<9I Ick}.w1p[V ;Тshk/KУ/BY/.V"qWe±! ] ^?KnnJH";5V`9H-mC)_Wb#5Էb$ORZ4u n&rb9`zb}72>˅:d}zP<*$Ifv Y.n91M5llE.I$q?2zWQf{;?iQ-So`9\dg8?.vy/ 1]O!o=siF`9vӔtUKic:f]7!nsꞞEeI0hh.riʮWozN:tdnK?N/ :C4O}P]cn9g႖ r= U|;7re'JreeU2;0t@F&=y0+x7NvNN1ݩ9k5h˳+s}i@~C4-'XW9i,c$\ x8m5~k p{fC4}f='#kHb x8g)Ne񒁔]?!oϮ? 85V]<2 cOӥM pk3M ?7E-yv1SIHD.>^2?5i*? ?6H8b֑,<ۉ".@V#uiT`@Nxz,#sd`y)y IaGr]r?u_^_q9ۿ4?OQ`m±21P|u<Ra .?Xk3LoC4}\V݉` {R,0Iq:$sC4?G!zg?4)W9ilg(Xdw4Ey=]1fy>ޣ79>}wQ`_ZX@犣w^o3B |< ?7(/LХ`8O66B} ? ?7('G]!zgޛXͣͮ3BBM} , w^G!z_hk/LУ3ByyzoQ^E<< ?7(/LТpmmw_ޛޛXͣͮ3BBM}  ubsHYo5~oRiߛmZ6nxWZBIP* 1@"/E Q[9'((((((((((((((fҬO3Z-v@?Z{$ BIO[.1/)&R:嚓tWH#FkHw%6}6ε{[Gu{}۰nZъLw- rɴ*0h M bl+mȹvy2xnP|EHSO$H5+]/QJ5KR)F*h" h7'e;FTp*=Frpe RB)tUJ(`e\YqJc8+bZ~=E\hJ:cE/gm5U@j*0r>QSqv\m>*{8;/j_ғVzO{8yoG*{<׿ֽQ${<ڷZz~FQ")?o?TG${<ڷ]Rj$ GM#Q81ݮRo~Z38e1niN;^jW $Y[Œ=G%vG۵/5[YD3Ϊ緧ҝ]%Qm?T{e^]oڗ',[xJӲi4E*Gs%B\ޏnsn=VH|Jnx<1=c{n& q=h밽j: 2RkVο*O,MvW3]fc Eؽy=JO[U:JDZ.j_ҏkU*(q.j_ҏ[U*1G`wVzj_ҩQG`wZzk^OҩQGaȻy=J??TǰsȻy=?oT{8<ڷ(սRE=E[Qy=JE>HyoG*&3G${<׿սQ -=EѪ_ғVzJ^=EkQ{=JE=E[Qy=JE=E[Qy=JE>Hyo?G*%=E[Qy=JE=E[Qy=JE=E[Qy=?J=E[Qy=JE=E[RjOҨ▗`sVzBKB* p5A?Erbkd袊7 ( ( ( ( ( ( ( ( ( ( ( ( (ոP]PvG,eu#D`ݠ b,cb|EnK Ȅֳ̏[Ffn%Ajp9hQZtO)4UɰZ@94(QpQE0RqK@XJSE%RP!qF fSE} %-b( Bap,r*.} <wkqD /PH?I}\}"R-K&S#Rk @yVG$<֣US}3ew]D &==8/,..# }8,E]tJ^}{Օ쑋,s8%zb=FDCIU3Q>RJ=ġB*`)ltMOW%0vk/5B.18q[%PEQ.F(Q֒Q@-%bhQޒu%tR J)q@ ^=))i(E\RKIL4c")hW}Wyo?Erb:QEtQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@;eeqz̰)!ycUҧcRkCófW 5r5勀8+UMo̎jZ2Vqգ=!#(֏yø{)_?Q ݿ?\6:+|?‹:+|?‹:+|?‹:+|?‹:+|?‹:+|?‹:+|?‹:+|?‹:+||vW! [wg;?ҀEjű_Cj2e8 [hM ((YO=E\{d ٴ+G{.3[r1G7O+9u7)I<6+Uv%{NJ ;`z1=B͞3v-iVO9X9 ]%͜M]j~Tɱcz{UfWGrc+rc9&y`g94(͚HU bT[Tfw#885y{. R~afV/NkGJ+3JaZc*&y \ǹč!!;;x%T>|}kQ$׭Q-DS8Mjq}WE,P߅XZM@'ut0䴮r[۸?(s]D,` 5Yق8tN޼TII+{t0HqRiaI]Ear-W.f!piL<‘j֨OC>>s7?kM*qjYFWs)W<77hk9^^n$A$>sJCϜD] N=GZCwr C@y.sϤMYR41(f985\HQ`<77h |&H`8T"aRc u 4}| z9pyf@@s}:M|n.V _##}\Ϥ77kfaC͌`g_j7 1`s s~o| zmgrvȫy``9G/׬f>sj?k3Fh>s_ϜXg| `9^3@M 5z#"@rNsz^h<yk7{i3c@{b[APih\<ݞ.v$^-?zO+lPR7!vR18ɨ :ex!O9^KS t$$ҦbJ=ݵ|ɵU94X.y?54}P9^3Ej?h|њ_Ϝ?k3Fh O 4}| zhs>s_ϜXa_Ϝ?k3Fi'|>>sc4f<?h|њ_Ϝ?k3Fh>s_ϜX'|>>sc4f<?h|њ_Ϝ?k3Fh>s_ϜX'|Oj?ksFhL:A}j,>7kԳFiX/gs/2uE%NzXU"X>oDU7'p* (Et6%‚s{ji6T/?XH,~q޹ L(خ%ڨ*-ܤjD2s\\Gik̬Ӫ rJ湹4ցϔ*9QK $ uc]%tNF'_vѼ ݼuv,P4yNIrWQKFX`vdJz 3G.8b .yǵckB vaʏuK4c,X[&V,[%Q9P3ǿJ$֦28w ՝Ni`0j4Xc VTK|d*x9*7 na@΀T ӊoa'hS9ihA73h "~ZA#w=Iv`P3h_Z&w G4{^1+ Wyed0R,# YHg ƐèZnȥ7v$Xw"ݮq"ɽJp}#4`IpAlr)u fA8 Rְb>lgxD>O,^R>~77#ڹ,D v {.WցRh7T(h˭F~RjÀ{r92~n8ѼLHBtRާ Ӌr9RHNEs5Ψc!r8-!ݿ8@ڰ*O:)H+uœ~ NGhՕ@5Xp~^G RAW:!xO7_Zab7 $2@ 5$ aO>G9jѶKA8oҏ* u@Q|;A%0FAVpDe ߼)ǿҤG6,(R>yM"E7LiYʂI\V#[Krc`?$67) ͖2zJܛ8ہ;r-d&DK``p2 X(BCzT}fYprC!8MB} ̌$-E'͵1*T#x&b,Gj|Cm%AMý;/VW3ag^s)ZE|gu@忈t5BĿhĸNR*3/Br*6a18dGP|(c[xߩUGlh.pP^>F+837J2 h%lH5R}qJi)Ӷp}Z誐Š(((д&qںk-/X\2lS\ΓI+$vM%b3i eVrK]L6m6Ar g+fԂN%ޙ,qG;K!<&cTrml#u崳Z->TdZ+[ JŭGAʙ;MFv [;T}[J%1:`Ss伆 (A:TFCNO洡2A;@\u%X| MLDdx#lԸk^HnpL !{Uw+.#煇$I"S>nH%OҰoqu,k>+?cW̑d1i EBƳIs Ze$fa9 47q+)XIS4 4ѵ0:BI{ɵ;쨟fTHS勌+]D sՈ?K!0':{V|siMX u$lC5$JYZ!F}ws6yz#]N ۼ9 +g*o dÊo[rDZ$''Q!RFzqO?>8t ORzU܀8Zݔᐏ$ !@HJmN Ow=U7ٟo?A@SQꕩ"Yt"9O)>s=*Y-$OsУ7̃w?}>l`.)E)'}?J6Qv;_BXǜ*f]8ǰ]S0r8X B>*t֨ɦ;yϞ٠_ݎ2AQREl&=j4BNO$3Gn;R5zzSJ 'rԿn<\S6Rc^ʟasK'ۮsJ/ITDw#0=i{*}.oJ5 zh CN JWܹy$beo=j{9Yh!H 4.l$| 083I)7T5~RVݟlrҗ˷_'֞ҟDO-C#t ߥih2\NU .JHv̯R<vm; E{{%p)$硩C܏iy"j}gnM#Z5 *(YIm.kuܚ=>4%1$r\㨪zWzVz"QjJvr<ѹL+!o'?[C˜Ұ)ܰeaF ;Jm1Qd]"i/C=qFP#ޖTnָA~T6nӲǛFeZSB&S ngd*yQK!WtڨW]E$]uO(׃~{P#9#؎f4^]o)䜙9 O,STrCs0W?B')bYKg"I( •b/jf"杙?ަH|:VrQŦ>?bz*l/tU[Q@Š(nUcSUi;s#Ak;*rEY[Gi"lcB0SFR9dFJѥM eW<:Lrj:LWWlmɒnzW4jVO#( i;[V萓ýCOHʕId;bUI,t֐ri gM9pxmipX̻YBǮ*[DD Ah2L8 :]ķ6-N<2[.i7ePsϩ`{ cǐSVPg~EĻb0v UvaMOCx."$GnOE r0H<`;,U[L2HX9@"Xn/Nδ$*,k6j!qcڐ5Ǔr;Uoo]0;@ie0bH9T ۡ]81bsz}rOpSdpph`֟Yy'`en$z/:Na :TU qaj#I{'ΈFU9JTt;9U\-UOjֱ4LCEW#&Ps!P'pVR}=77=FH$tՋE O(n)#7{o0lfWrIyP #Ү7 rqU}>&8zsPB{|3[) &P1M:{ѲEqrL01S* D&c .[!;Z䤉g!xlRBլR뻓N68k֧{} MxX՘]Ws]4SwtlځnOAe4#6l~u ՓNAtgYDۑbVėq})kV@ {gQX;Լ\G"˓H7NV[8ó~][9>* TNP[]9$))ASRS>YlzUhu)g}kuQd=$Huj2c)wI߽Jft4f `y[maN1W-ZxZ6['4Vs#+g#VJN1W]_"GNEf992=)FYϘUK*(PLk1D#2M1YK2ֶWBcd ݨKd] CR&= #qVޕMa>!7hGn +xzw#7+ c>ru"$>#jU(#2o_z6q?#/E>Kѣ<&g` c=i/@ fa4v۹:\2i\tY#.>MX|? $nï|SRPDIAGp5.[*@4gֳԒ694-@ц\|z]N* ;zUKu7ڋb;h ԰Kn a]ͺvXC #8U D$2=꜓`AlEg O''R{YyyږGMwc=xtnȡ2F1Ҟ dgpiTj&#vQ==؊i#δBԋ ẹgSoؠXѰ/HíaPo w嬛\wQ {)ٻCt=7c89a{ם6C7E-Ԭ tJ'ŬXgs֛'4bZFfLtOt%]III B.i*WSlQE ( 8:Oj7en3pַ!\ҖƔ]*0GO7ZS4n,:bH"$O-J[%(5Iӌ`}z4 "&,68z?dv_%pb?0QKzc5 ]!##wgۙC,c8,OdUk;O1dǕ}Mf i^Ƥ.WfބN"eQXpJl14᎝+xtHP6<#Ϯ+sՕѴRQ ӹf|ıA\ ,Y_s'{ѨhkdLaLnOI4 m -*aݞ9V%DCޕvr]05mNig9U("貺jVm;Bzm 9 R}OP%OQ?B$OQ?BR } >OP}FV |S U>^P0N]:-XrBqh0aUEP3еlZ0DbEjBرEWuC}>i?PbUZ?آ]@?¯]}M=dG;QT}(h O4GCڲKT.0й*ťſi^T ^})<n]ݩ葠H8vO","/*O0&3M' 5,!+xFŏޢ9:WcgwE Xs9X_,0UF+VT3Ahh,~=&8$sWː@=*SדHN V8Jn3ULsJϐ1@:n$ީ+`@sQ_N1*ֹ?᫦m3'sH9&CZJXXzO!T?nMsk徆dž?߭s?zRh ZJ(i( ZJ(ЙaPX֒ĸ붻J8R'ʱ9cM0@h$V, 族JIrKctF6D@*"''- ׊)r38ks#|SGd?‡͍6٢ =O'Tt,7f5%ˡC#\B޴AV711c]-'NSjcEoec+,)].+JS9g5G'ۤUxv qvQxy"9[j'9$0Ϣ֍ZF6fc+FIAo$ziJ*Ji$V}ԴWIlV|%U]Bd:OR"QAޯ. d3?pW[ r1`V*Ikch}ݿK>}yϛG^у([v nſ_%yCIz%DOi%bsMO 蛶4'p7ޏ7޽-m %FNJ..S}}CK ajC-8&C<}|z3-84~@|<}zxGG>CFYxRͤ]٪ lDVBI7)FsGkO4oot<[9M=q4xm Ѩf1zYQ ?}oB_ 譐m0= 7;ޏ7ע#`٦ hv wc|sG^>}?ѿݿ@;}'^`Hn9|#ϻfwעǻfo wy٦ h`_@yעd٤KD'4~ookGS?3@ݿE7޴$f(H LWd’-84 h7?acMfEHg!pz찤4ݰq]OHSȷn:W Tvq׊z۬A|\ 8CWd<#ȷluO 3f$L,qb&pI`ZwH\ڻD4Ò`ǠiMǸsfooz" D4 hg984\=};ף!ϻf hႈ |g^~ ;FqҦǁpHgbqhˡ [KF,u<+>{XF毆?߭[%QH((("HO5@aB^,NVn3Fp>ƕ8\em)K4HA4$1$kkyi1'qi6R.e?$o VQe g2Dbgrzڋپy?Tc=W5+$gDSq+=[mUfHH3$D6ʊGK];6n>\Apoj2\k~x.gk$9D# Kb2l3W6.F(}}kG@/$/m$̏N2*{9crv!sSf$[x1?Z@n0~hQp9W`_ZA2Qҫ@#8KQ}>89p}hJLx,:LO GzTP)@1Y* "A)٨|b|'MԊl+}7۬bI%,#XRee'ւZԱCzh46@0HB}׽?; $Ӛq=i I@e1vSz9Rx( `3aQʁ(2S&VUdeg3zz9#֭c)t"^Pin_E֮e4c2oAzѦg3=*mfM31 U3k] -v]^(fP\P5"9r 9Tt^ݣgEuG?ud6I>HZbȫ_nzRZ4%7M3`goQZz$!Iw}=Mg ܬ0j՜k ,8zU.J 9A"|)#֐13⧊@N3UHi@hM1JWQ=i k;RDsb2$,IqjφU#zD\|sQ\,Aq6F& >``/…GD g֩#y1p=ItA\jvQʎ3)ajcisR@@&rd(*[±r=Zn0}5@?7SJ=9oOJL@F\T&;o)q:Љ*qWl #Rp~D0,W0 GJ&ҸOyV8ڜrz2+)t&棷n v27ʸ:`rO zP4:1~5#?xU!>ANqԒ_ZrIsnʁAj0\p8݁bQihɏ$J:=)i?ҁ2qҡAPs"T]D)9!\tGJxIp3.y玢>[Z1 3@#TzҌ .h):k%c ̬<ݜА6ΛS')H*$\Xd@ ؤF,N-WR)^%#;~*#đ¸CB(qO=*9rv8. =)Yw\g4yI@ |Pl0Fs֓~Wv[Sp Jt@*v'47vOOQymg#$/Rև~ =(q)LEKQv=i5A. JԍNj9Pay@b[4BC>~Uܪ/dhx~D]Ǐ4kT\cWcq&a~azޯ?k(%z6zces07 C\T5RlBʄT VUKGjZrHA zh*ʩWBwwd{*,L8 :vꡁXL2J ןz,q  W$RVcB);pO_\I]'4X 襢0#T< o"( p8ށ?w| Uh*1n(㓃Qf6dg֠fUzҪ>ZpRJXU ANeWσyi |!FNAUgPnpցC㠦Qh,UM9핌2'Z )kAUUv8y9Bƹ=%y4I 䢾NN1P+]DĘ6y:.ZБ * RsU;R299 t`@~낹^E؃O&"0,BۍBI'7ID{G_5st 3Ӽ؛i8GlgKx䇟oTpMOP;4vqN$Xh$D)#ޕjV 1R96lA Hb:wep"_jPv-қAcX?N_SJ9i7w#;֎c}P2\fR(8'Wk7q ϯ?e!Tv>Ʒ$zȘ q&^jxYGsBF;%@`J۽;ib 8#*)"@\ȶޜ8? R0OB>rOJ;|A>by1IpA@ s4YX^~St+2nx<-EGD|@n$ z]!ynuóRr<<NDrqiW=6cӵYV`Fs\wrVS3pt# ֪W!s;\}*N*J9([NcPi5W[K9frKw2E"~8F}"eIM`>;!r98 d֚J%%6QkM9"~vU6~o[>Fڲ/$~^T>V]9yu9+7C~t7Rt=ot~t7B{SjZ \~tt=7Ԅn7P𔮃R O n}*9m4 ٍ64IFY@uO#RmckJx>֟\Uh+gp\SəvʑN7Bq[o.ߥN3kQAfGaMd@.nZz2]щYd&VC#͑,ykg.9JuW3GB5F zƒ i9 e@RFVCY;Ɂ$TpPƧCv+>Xž0N[hGbK5K76?1y*7sT*uH桞x/ G5 Vs U,)s@$ BN?Zr[rdcz⭤B 0mnqZ:dW6AWMc-݄9`y95ַ;T'-* #ژe 8v t T`w) (ozAae3=脒}):4dR ʌzL,8%Td(rFߥ=F斐\G/̃mcZ{c)A+ Mh ~T)3S#@и⡻Uh#H(Q*80'.5ar"eIfF +.-"de nҸԒKSn\2wv׏k*0DBcҐ^- >޵>jܫ-]I^vF{PTuO}"ǴexcG)ByRt2B{bl!P,cOE1|pN;Ԁҭc3UUcv|WQ^dFKx~R}Yu$Q*2\#=k~TKr!$6V䑎1M1S*E ueTZm› ǥ`vЛ\ 5&1ڣ׭MtӺp#RO#䄌yOX\!IbۑxPp:TɏmO8ʒNy$aݛ5r?EdV)gq,6eYC7Wklf=:̈́9aO,5J^湝)@Z ,w5qZőqҶG-=E9TF!D|G/dܨTQCՂqEWYCc+Ҹ_46=ip]7c?Z4z\k$'_96ZGs5OTRBkSj}VfL7E$1<İֲ$i;0?3~tܐOP6W2+5xVy*&sW2J@ ^&C$r R,rd8Ho9E,1c {@H~GXe5`g*VRKG(^85 $ LSԥ-`c&юF~+`J<&mtz. FF{Mޚmeܡw5b1&;UQzzRH3<ӳ{@93*0$Fl=qx$|}# 9SަJFy )\LȟcPAS(랕RL7Qp @rM}N⨑*hd{-4>f94=rFі cS/!%"pvZ}¬&JMrZsAwy_pzxfX''# rK,Z(:yv#QČ*sMY<{Zũ܉KS$=}1` HMh6{d SQ.{rY)#ԎCң?#| <}aI~bIPՃz斡`DZ4`W>А쉱IQyzbkAe ;jv &-aW{.{9' ,'"qҚ%BpHV ?54TQ]W y0*AS4&lNX@B¦( vamyB.W! Bsjhj1;wAg)He{hD-巖 D2ZERς@Q!Tĉ7: Ix\f'@=*H OJ}- -I#=¤V$峀yA{=+xn_.bb*欥5`B}i6ȭ`.x9K ;zrimb]#y*fB(rAZBި$M?Ukk!bZ.o~|^qIP lA"e++[ZP$qZ&E!XW3jM,2ë{V%-ʨrW\V̤(ẽqEypb1] J+q׎.#{6X%9*z54( #< !l.C5W 4m6 dy AZٳ09w2Vr_s÷cw%Enp~jRlK&֑#=]b=cPlL㝾oM&dr K~UX[.}ރ>Vku&ccp0k&}GȞ%iA#V 篘Ѫ6\i'ͦ޺c rQݍC;>Cޗum~nH, Sc򨕆z]{,oO/6پ~*[-jF? fI+_OʚlPMzi>4#֡P{V’MojVyڪ8Ro)IAhrܤnIqf7`*>Wy[|֜ȋZn1ڞi gÜj?GҎd(IztWg'9$Q?REpKߕ.qeH ;Ǩ)'Mj @P EL*E4;'l vP9^ֺvg?hGH6x*m>l@8w(:s:c !~g=x6+nKMƜ'f@r#T*E`X[D6v:vSHE?hĠHJګZ؋i,F-9[ZWZy$8TZTFkv cK]@?Z|n Wyi-\߷?) Tҧ/tPQ=a3F ⣙N7*Q qEP9LJAҤr FLӥ@`TU9H(Ji288`I@ݞ)àUkG//˚BܴrBY2nҭąPyKw R.JGu\\8-^c7HTOJl: ؽUY.v$ثqӽ-!jQ p+ # Ү(|)ZF82Nq@@Z :H#5a$YFhg#p ɤc֝~(ztV>S,~jnNKXR>qJO(5i ([*x& 9kzV}S 6A=թU9fiSZq89ᗜ{{P)|v=(bˊ#mjrW9J3ƹƅp 6қc%ZNb,=MbJW}KyОy dRsV.6^]xH[`V,/r8 ¯ٍZSwcӒI8ǥJ); Y(1RT7R,qf7}()ВN=h:C&ND+$i&-#ހs@ҡܣoqH8ES€Zh$(쎵a#,iT1p$QHq @}* VWr3ʌЎEnG'Jz})zb2)14T?g?(.?EEFrpAQ;G do=LmR鄴1il̍P/ʌ.PLִu1W<|՚"EY[n9^ެUDN94(`'5+F=TO20ARKbOjHrqu4͛'RsRQf%,px'Iw}Faz4Ɍ>MD:(9cߥkM'lu6!kU}lzF3WH6HP@󚩴rG5ښHzsSA<>n;Qqc3YJ Pw6-6ӧNwZtmCF$>I^x./n߽#ܓs\d^鼄a#J oO+&G+(`QҰu VO 7ss"*)A:[TE834E#dҳ4f1,N*YD?$? &fjWH,k̅h$P*1Ηrȱ䌞V5@d7nحx|o9m'{*quEOsDP Hʒ½Ikiˈ1AskUIhGL[5O-޵2|~zؿuB*wsއ*/GF>76z]*붯Wjt~T{`g#W>bt~Tm_ʓS96c*a'U_ʍSg*2} v;bt~T{`f|ηϥWsWu($XT"158MXGQ䯥K}y$&=*2iyO\#BcsRsL4嵹f$U$ϵ1/ٮ?癦[i) i&rsS3Q=p"jtJ4E؅Wr*V%i h+qIuEb ڴ _΋)sB- }?ڭG6PߴM6#'QIOsNޒ@/ 0o}*ޯ9:gX^%-20'm7u%.(Vd\MLfkbK۹c1[;q/ }(EXqV:qE! uҗ2`Ф}: ejTPv wʤ}ңSFa3Q w=OJŸF JpLF6Jy#S$s4Jft>&$. iHLSMǙ?Ή!Fs֜t 9?^NOMTb=v zEAw2I.I麟 >\Is<9i"g4@R 8$ni:Soa);ɰ :S2:v,lLZm>0ϥB Zv1HRzU8Rp#5-Ef* R*QK@ V'֟M* @$p߁wH5, +ϥBy|hz;b6 [T5L! T\LWV8V/0ymv)+kgާ+8nưjLZ`Ȯu :1*"z1֗FhAEG?t@ Ҡɚ2Scq_9r(X%nk##qS !~`ҖRxn- K8 u"Iz{"=(LA"HjڊM@p?J);4;\aֆ1:P4#Ґ݂rqӧO䲇ENNASnO24;KGUC Ӂ>®q?zs$y .3KycN-v!XX+8ysTTBb.t^F E Lҹ ̾X8qk ͂Ay,w,-U`d*8U8,kV9{v7( BxoPj7Ђ~f_ڱ P @O}sRm 9@+%MU^Q&bzҦVv|ئzY6M C@, uijdHK6rr8`{Sr]u95Z3CaJ]Q,Y}指b+fϔ P|HMH]iJ*JO ?Zu5ԜB:b2H(4,ڪT~綅 Rҝ knB9BR8kGEJ8fBڪd~8R栆e}7R $u#֊ZǕi]֥3%`f<VnnDݒGeZ~k L1A]]yVŴ sCSUT; ?ʝs{SdQXSdUsLo\rjr#޵;nwb\/ZLt-K <-'3|ri :m5.!HqbH _PH0o|_JSm\{'X4gCI+UocdF|q:Nmrec$>cmm河FM%\08Өaĕz64,ǧc6Ʃ'tQK aN[pF$$Q\ky~Q^6mD]. G H _*"\qHL*nH@ zz1IdqCLb~nnqj STIT#w5(sGl$NY 3{; 摭Ȳ:8g:TަvxX3{T $эŹiNv#ZKݘ\qo5/PIYH]D\tnjK9߄S`|_jSKaF cI8b iIp85ԇ =+NF_F 3 B *$i.I4N0_A\R-ɠ|͑zSC0E\QEQK@ K )_=q촊s! 8M'S2>Rd.Y *9 D6.RfbnTd2d3ާ[`ߴ?iץ:"${W }i2j&gwqzh % p8朗.4ہq6("OU3V$<)UTz\dR ʔTzJ)h\{ɜ5M˟NkZU eu?oM9ꮠ(4JjD@:m<ӟN)ߛЀqڝpjH &.OqC*zb89=,5-Ƞ(D!n}4SnLyjQ*7.OΓsڤn ՠz)  Ԝ <2rNi7u!N=MKLshb*Q)Vl)MǀhWwfd dTX ?0={b"PmR@VOޔ$ g9CU%T9W4sy1HԇN0Z+\-n-L<AM0$ۅl] zVTeqc:tHwZБdB+Mccp=jY\0"Mu%:tq\! >BJ{ <҆r-H齺U9&+4e Kxqi@p@8hHb5BU2H˓O5-!"B!ݷқ- j|.ФqI=xWt 0%F355 yc?^›*oQœnHa;[t!%Q }`oS\ƴn%on'85o!k{̔oڰauR3Kr)+t-~WeʕwjKWe"FOMEpv?V,$ )#UFBpw;Kb5B9x  je`Z%s$2JA4ZǗG?SAn_;n+UU vK˂c1>F, j$PUV%e{]i 2/>qW)|&뎢W-46"KvDڄkҖ#ZevB20?P z^2&bH=B=֊K-cǵ#6]#pU@n_L윮D)>֮jsa`x NxW=[yddzԪh_OS,%72f_22n{F2Y%AuGAmtC/-YdTZ\cH륷J={Up;JTV9^0;u!=-݌dt"e2qXؘ?1n]ě{~ͻSff6UZ*pqCQƏ!{$tkdC.>+Eg  yė )}qyTN wŪh,>P>ԡp9l0P[z.d3ʖ4^[ROQȆ5eA*9NWZ0\8cry=ݶXD'Lg*yh?FA [W{)Ƨ/aZBymEd7Z$Xyҹ]Fkky!_׵t^FQmH+ֆKޓbWa.qLN東GaKRJZ)(((E֐ m/@xAzU9$Ӏ*x-L%Gn(CI+01U|TW =\U|51z%d )iz7hp}٘ 9p SctwiO)$) ;p fQz0V1 c VR7bV!=lR*(9 n6̑vH{Ue $Tw(};ّQ^%Av<ө3 (;Elr E-6Hwr -(wr (zn#N8) 6@=r) h*Ž6J]yKpN3N^GTTvEϝ.x'Հ%Y=AYA폡E) T8ƪ3Qߊ+޸;FxކQ>P^94Șl4*RHaEgm~v/)UԎ-?YK5[՘ʥȡOricLK1B]'JǡS8ĸ t1n ;V!@N{TGMlI.9" EN8PTX[͠/e rxJhdxmW?0S/Jޗf*.-O'[^iv1I^ݜJ(((jH'ZhĿ0ғ**ȠHԆ J eܮkR'#v+/ ךq?VJ\ϥ-  NiQ0Cԥtjۓ8+}~'1`SwbA׽b֦n@PZ+j@"c5M-ɋl?)LW3)v@g9؃NQ䁞" +#.n66F{M _sJHU$T3\G9>*4eROALelQ#X@uL=x#ڞa*7 rsҩGe}jSb~b9ѻu,cahZ;Fi6 |ܚlnrBssA&Ҝ9 ]d_:T؞)0lfB0AӍw`(`zRQ@8 Uř |UTzCK˄Y8m;=YcTb\BT Ph3"϶5?X?:*u4QE2JZX~QZPzˇ* Pst)w:jB$RIr8ANHyzBڝXgfӐiBJ#H0er +$vc *۠9|֎wxsޤhPcx.?pҒO֔FjBVV#{UJ9Ȼ ދ@3ӓޓE:(rsZ_/S{ OG~BihaȌoG _G~B(>v{ OG~B(*1΁ 95o `֝sĦ8cGaslwQ+"~qEy/˜'S[,銳ovzPtBzXfH #&;z$t5Q/{Fx̠+'bwMv9jbH:{S'ȝw]0Ԣ(}z qi4AE{/0,hmH>!C9qQpT(ǭ2XVQIKBl] X D@<xRC(8Z\I5fc:otTRҀs)*bYHA\nS)N 2ʜdqN[R >cOqzqKIE"EZ%-@qiƊwva#v]yLlcE4m4UJ*y\<Ҋ(;Tx1Ijqᚓk)I*`T!Y"!SL>Q_'8d<;9& ~>A.B*d]A yj\rWk<ԫ<T=YYnoP*\d&<, o@JV@Xo0sҩFb")b[jHFs*_]T=i99<ӷ.@PYTs1FaoH9o`)x7ÌqI9eךSxV*`;Tf$'#IzSTXsrO>Ѐ NicerT;E H' ZkGywG^其nƻYISږ{\?ekt3<6r"GxV?l>kbv4g(0JQN(QPҝE b2TtQRL*0FE!E8ʃju)QEQEQEQERQ@Ν&i$٬ᛜuQ@rWG~ ]QEs_ Zh֬b#kv5^< 9<\Ismsk21^}] t)ΙB9=QWrWf!1f%X<'3O\ͳ۶0qֺO DŽV-=HtobڛVB(((8b?&30 >U/["}֬;7?N{b+5]#9T=MkGv:)*(4PFF  `ʜcOT秭; [ S&pۻZ;YI'KXD}LVWFXN=Nu: j;cp0G֔T\CȦ4 ޞ\΁p=Rc9I=IMsGCs한hw&R1"nI 9mVBϭ2( 4=ދ<)& )1Frp:;W(i|/,:7U>G+݋F)hRJ((\ZME.hv<֊(s,r=ERRLj`{&=9Or;scϭGgsgKaHQ.f7.?*qcp8P@0G!LժhbDqҭĸ 1W"5'=`8$[NN<y*HP9 zc =*)Pׯ4"2ڭʧ'4onzTQڦù^jX1֗U`lJ`\08SpGZRXBW$gލz8IEqvg<_X~i2D@%PQEQEQEQEQE-%QEQEQEQKE%PQKI@EsvwpfcR+ֿq0A-ٹ)} T%!Sn_=+62k+wS Ղ&_ njJ]\EYݩQEQEQE7t@5~_FBE! zh"#e wD{T@r98,FG _JM.EIwG$' ->FxMXL()ƆHQEQEPh4ߺr8Ds+/QM8=iܻncObA~5eOe'ժ"G'+rzҰ弚 岹lvry)'bA^+.! Rמ 'Ӧ( e@V`O P܌wfclZfN#ar(WM3QWjeHۃQQ>h Nku=0(TdMQYQf)ЪM*!t#"? )X`ܒ{VdאHa#Jе-G- qR 1Ȱ_?)Z N- be}B-Ǧ)فqYA+t0J&d<<]y©$)VM-N:sڋޢC^lS&dP4Xv6Ea6h_+£h֥lDeԡq@JZB V2>v#">\uqgHAnzY(&nC8 o&9`WqcJ,)'~lben;wg8ҟ`%x N֍FPbrJ8*2 YaVOy'B Uzwz.ֱqZ#T߇[?_m1E)u$6>Q‘$-e)]d!F͸@N;U+]pGj!+;p)'Pa@R/oL0JSI@(()E硧S\{`;Qϭy ;Q)8CZ6y pk-Qwq4qҜ˷YK9HO%+"g+qzu$%IH=j]larӠuGfYԆg]Lep9; gw!-R%+&;4d簠W``ʗ- ৎ)UFYZ^T>2( GB[@9I=:UoKlҠ!Qڝţܡ,EPϸ+i0;jCNH@=y hcaآ78n /Z%Q@Q@QXEPEPEPEPEPEPEPEPEPEPX,rlV??\t1@Y@@O4WN rY?ޮM\Dɇ&((4٘ ?)[~h?kbŵJ;z;P dg\hG|} nmۤp&4:3E?!JOF\,IxZ:*A #;\g}Kv-T|>NCNt,kvMS*PC;}<غGL\G+~& g$h\q SبfcGw-'I屝մtAKl&U]2 ׵MѪȎat?_]#iga'B?ƧyaF7g';~41G˻i tlL6[vת6QXqoh!s:^G?j;W=[f:KHt})՘m\X>xpqc9`801N#ң'FqJx1\ `-[V,xcEsZso.. zh~n~aYS@ֺl юy#\)s$, !!t~fO3W/43g8 DŽVoC9+#6ڛIQ@Q@-%.(mœg<}*°>p E 9I#)g |ι׊[)rzՓW;V:$cԠ4Q+E<d2}*HIpxʙ WFQ%})a՛U9}99$IJi)QEQEQE(P`C;Q"Gj.12C\c&{Rm#ǚq8U嗒AsE]$*M>9Z9sTxℍv^~2aQ%,1KFPW=krƌ;hi]=Im%p}EQ>1֋+0 gګ O'ZɢeHTyuuaJHE@#5yE-%RQK(+((((((((((+ş.]?g((P(𛄳WA獧ʸ2[], d˃Oq4Hr;G5nKb|]d m,lWNGb =A,+pImm,lO3)+[A$Օ梷zg<=~YPvK 2Y :>n㡩i#D>qf5}-JБnB?4W q2@ɤ>?.ޜKX@I%Iu`SZo*ۄNwhٵ"i 9QG0j2s~%EgtHjTnqn[|'Iihd+oJ˽oQRPn-In&rxgbIs}t^  <"8kOOCP#Uܵ%y">? A4{E*=@ɜ}kѲYM;M)#%SSP.r4>L@(((((((((((ȹwb+ş.]?,(D9M oT"⧖7B%gMcpcls?*Ŷ]*zv2ʞօcwDޖ% ,hwe^1׷Wf2pvMXAIh7`Zp Ov]>-w89ذMt> X2ujΚ1ӳŷkOx"Wi?40֒!(/ M>{~bGh^Hԝ. ǢSMV9"1\?Jrm>GZ#{?֬je7DnRaզuTi \=1Ҩur?HXN Cd:79t ǹwMAķs*Kԝ$oE{fEގ=m&fa #ȓT=>]>#6.QҶck u ֜uzV$K/ҹ]|tlWUq}Z+FqSW-@WǙ\V8zmU+wJOHA)wڑT3ހgx9Y+îk 1v9S[!a4˒:lt;Hv u&~?I_}'`dv4z~mҴ&?fҷ3[{(A 2+_N׊幷IKJ ( ( QIK@4N/v @bԨ;d /#+!_5@Cp[=58/ +QJi($ZnZGLT-Ήs G zTcZ6rkGl'LT6ou4>ew`΍5߈p0TMyI JsXjv }j[4k,smDD%cWF(v8)nSN_3Ib JmBey s\E!W%Ux+G[]J[m$yi4Br=5 n#Ota|Os`]=mP `cDY va zf͚[ݙTKG;y-+{%X+8%֍=ơMMb68^:mP),ɧv D(QEQEQEQEQEQEQEQEQEQEQE.VcE˿GQEi=?UxY*e >w,8rp3KAXȪ9IYp+eC)c?x~?!1A Zw0}y\F3ʜTܣS]tX+'*iaCpP ̧-өTh:S W PSIW!>ea$H$E w})Ս t(+g!>+ P2+#mdg8CT9ze?ZRReh@# E;[,O9dbzeMÑVŲ,\'1CL6Pq.TY4UIYZmq8ǥjV+ rUT?%A 4o;1}*g^Jz:SɮGć&rʿκ/7O,S3R/JZu?q #G5?b#;vI cM=I*͝R=@{)]8O^ֵ[QOK.?jvo~1]&Ηps?ָOISBMb썠[J njUAXm]9B(((4{S' 5&TZ՝fG?:$wfoIh&v?;4/cY|T6#UMTw"OC=w޺^i)M%=UAAE \iܻ'2pU!KK-y$fC'iIŪtre)TZ?dQʃ?dFO-R;.hT4PBroqi ( ("Wҳy>P@:F:{Ӎ[3IE)i(Z)(ZbEZ %RQKI@ E-5@ )is҆,pF M/p4' 3SB zO׽U]BrLMHH.LrOֹe;`T >sև2FCs,kͦ~8檰55ϝxI~,U7 1# ;Z:Pi(%E%((訢 ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (2|S"ּW?.^kʨ_/tY/JnEMJZJ՜EPEPEPoFϯZu Pѓ FSk1+L-&>ҁ&X/ҍM8Ph$(QEQEQEQERJo9?w< }1ˏN{R'u94';(OjnhQO&ǭ$)z}(9)|ӳL$:4 r:#~86@H*ڢS6%5fwLV[ӵIrѨjLdxYIRV3 )d`jVb=)1Z(`Ih;ݩ3ޟ((((((((((((((((((((((((((((((('?.^kʫ|S"ּ <%2O\uTޤYQEQEQEPIEPQE-%PEPEPKIK@Sw?A@8&z)Ͻrr%O#v}=2sԂ}<0'}M2ANFQ֔pqHP#u\Ҟ;KI# I" >ϟik%<8?7I" IU4m<;E$lub%Bn`12v. Rw_'jm#$aAA(JI5$c׌V\KQDaF{dU29"%+WF9889ޞJ˸FV"9Q)p?qWTcu*rL`zԢcTU'2a:Tl(*JaEFu tɩr-!#"EJL]sL.͊#MÞ1UAfӂmL,nu*ҲRF&|U(jfaϺlg~5X +6rXTq{zVXɻ)QEQEQEQEQEtTQE`vQ@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@>)r^U^/TWY/Jx]?T7"%-9J)hPQKI@ )J)`ϥ4<ɠNM|8w>S~rEi=X:RI֛z6aaÊ)*qO;4J)h)1S $QVm|(ˑګ0#0=G45r+;۰6I'IFNI?I  `+'M{[fL0xaڦh-1@,q)_a^EnLŜ (0  vG,?J5q\ș,p;5:zP8OQ@QE=E(+7gƌÏQOAAh.*aa>lUUZGjub9jn(Q9PMYV$g Y8ot-%Ru܄S 10zT/*|Krry5 i%6%6fQEQEQEQEQEQEQEfotoxx-20.08/images/fotoxx.png000066400000000000000000000233271362435004500163650ustar00rootroot00000000000000PNG  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-20.08/images/gallery-select.jpg000066400000000000000000004223621362435004500177500ustar00rootroot00000000000000JFIFHH`ExifMM*bj(1ri%.HHgnome-screenshot0232gƠ0100Fotoxx:trim/rotate|trim/rotate|trim/rotate| Fotoxx:trim/rotate|trim/rotate|trim/rotate|sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 2160 3840 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((Z" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?K{h1 l@$57/Sc<"_@ !ߥ <ŸE3ȇxA~(!ߥ }"_ȇxA~)P<"_@ !ߥ <ŸE3ȇxA~(!ߥ }"_ȇxA~)P<"_@ !ߥ <ŸE3ȇxA~(!ߥ }"_ȇxA~)P<"_@ !ߥ <Ÿ]G4ME5FH!W(1ۀhJ9O"_ȇxA~+|;^FCqlg^܉OϑcTz?!]Kho4SʎFq(!ߥ <ºH5VSӤq5T@RH?H7d7{76܏?D?]D~ci3vVWp\#GxcTu}m*kɢ[8`ۚ!ߥ <¬ZJ*\3נBM]+yВ%O'PuD?G/WmsxFTTFh*[R\jO|T:|i?9+ӥ@Z//QC< KxnNo ږc-ZI YjmJy?hڍVnNW8"_ȇxA~+|EYr'[+˟yPj]CúKꚶY8ZXBH@/#57c|"_ iV׬yG'Y. ;Mue,) yy+C< Ky?t6yuusigTO&>xjdȿq,+(Q0-Pqc r`yg9C< Ky?t^5o:,ŒF8sIbk+K~e4aZ%׌~F9"_ȇxA~+<=kR7#5BC~5f66֣3K/;*j}@pD?G/Wii05k[[{o:y)痢7I1kIyo<218zP3!ߥ <ºH2dUӾj}"2O8O>[Sqnn¤rd y?D?]HUѩ؛Ŷ/9Ozh7zZth[Ĭ^z!ߥ <º-#ßp/XGzŔY%`~&RmF%h<3 "_ȇxA~+neEWv|wZ4I~ǭr 08<"_ÚTst5;Kk (J֪"\$QwXWq}?D?]\^Rܥ֝gkJ@23BjIl#FֱF-[&>pBS>g!C< Ky?w6:>4_Mji-}k-+pko#래zfZב/QC< Kѷt{ԬCDCl](PVMJ{pDd1^D?G/V>~^IhKº kL# 6 !9# ؚ|"_ֳM.et@N3ܓӶk#ŶPYKj=64y?D?]<=ٍ;KMbH%sQҳgq\&(~$J9_"_ȇxA~+EŮqh9jm3S^bZ{H|;9"_ȇxA~+7%qs#EnHAӬ4Is{X%ZsH:y?D?R0ddwy?D?OC< Ky?>g/QC< K(D?G/Sy?D?OC< Ky?>g/QC< K(D?G/Sy?D?OC< Ky?>g/QC< K(D?G/Vܺe[%\B6 J)b3=+C< K xOKxO_cB*@PP4E@da2|q@UC>ߥ "4$Xض 0z48w['T$>^!$Ӣ-Oʀ!-Oʀ!-\ƀ8+c_/%?Xƀ8+c_/%?Xƀ8+c_/%?Xwh/V>5rOV>5r_:XƗ\ƀ8+c_/%?Xƀ8+c_/%?Xƀ8+c_/\hV>5r_\hV>5r_\h/V>5r?Xwh Z~[$׶LF 09dkO\i?Xƀ2|9u?ݭP8SOiMӯ4$Ӭ~N%qHd'?\iXƁXZ-)4%: U'rsҦiYt̽C| Xwha_/dMzVԯZaȣD,|tG:eipʚ,,O#tZ_|kBG+й}/%nN* \f|a!~R^ թ ƿ._~K4± ߒ5P{kgO{P%%М*HME4Oi؉r{v± ߒ/+й}CxnTӡMat ~`)sPO+ +M \[\hc_/%5r_\h&M&/Wj@dD;tռA^]TQ XEk`W^3\hc_/%C"Ե& k}RG{ GLյ -Ե͐\s:PF\hc_/%bfO|DKۑ,m6-UT+gExGӿ.yW$daKME#@i+檟|kBG+й}/І.i FP]j1ݘo~[ ]BɟY}^n5r_\h ;\ZIX}.C$ + |Gk6Eqo:MhѰ(J\hc_/%d3R4K5٣VcKXwW'́&mb[y"($O!n7ګ± ߒ|kBJ(jwe uvI'a=j׉Aai+eip?ߔ2"$}}*_V>5r_\iAh`Ύ.[ːMz.[hTKR?ݧ>w\hc_/%RNsks.{h-%#ֱuJm3M4n#V cp+[\hc_/%z-ĚzVTUI4YLwWCQ3}^[\hc_/%IXa:"USʳ6B?)nj躎4 E_&P3V>5r_\hzV't5[udYa:~}>ɦWYHwFGԊ\hc_/%rSiT"vc_/%?Xƀ8+c_/%?Xƀ8+c_/%?Xƀ8+c_/%?Xƀ8+c_/%?Xƀ8+c_/%?Xƀ8+c_/%?Xƀ8+c_/%?Xƀ8+c_/%?Xƀ8+c_/%?Xƀ9Kk-́Aͳ+ڊ XƏV>5r_:XƏV>5r_ 5ys Eei1 ?5]+й}/ ƿ._~K4ǫ`è9-htm)JWbTÜgS\hc_/%byҺPHoSAI]5|kB\|]IU`ijcʀ=?Ɓaڌ/(۶Bݿ=>u}?3[$wbbq{T? n>kɻn#Nֱbݵ)9c,`nB0891tZNoCFoN>phfl~ HnL`4 fKIt(];œhsYxq11p~.$fr^^%ҋz!pi"C*Hdzh.|A5iFh/0W0x3a>6+\OڞC5w ySmƟ[+;)*ߘ.zoeߥ M=DR@qtڼM,IupϤQGi*nvvxcK?}?@jYҬ"zuĒ<ʤ07cN1RMxz :+9`Dlx[2y^q_AN%Z/gTbY*Y`[ VLJ.뵳DI\C-c\g%84nWgWRhLֆ| Hszk\ƙ LD!$>i:%Εꚲ]Ckmbx>!+O!˼J@4WQҬdGc'$Q"'+l/9=BFOs@ύ2d<*TRWUkq ݴW,ʡ0=ȴ MO՛X a8#tB5ƝaȐ$ Vr?q@MO&6 t_L^ٮ^NҴ/m]י؋ SVTE-ͩp  ⅸٖk~W(̰_^M&&nonf޽x,ɨ4M!;Ah a>6 Ҭl溹XbRD>d Y&y/ gHC~ǧ5t2Utemdumg Ăw^h MP^gZi{kKc`28@yi &c |=Xϫɩ\gP ->t[PfXύ_𪚅hsʱ&>X?Kd|9rlMCU?LpW^D"G y8mhBl+eϕ_Uz|Bb'-v۷Ұe"}v~^ΝP;o_b~d.@m^KIt},XՖBzs?Z|)xADIl֠2|mٖk~W+Wú (Nm:ˣœqk.PRۛLp $چ o+fXύ_7?6LیMu?ۜgިL#Oxqn^\]y7xPp޸J,+a>6™i#l>Z8AI,nJÚkZ-ݪO< "ʧ"H+^k+SiVs匶ܫtđE "n ֵK& $I qAmg}xjP) kچU( Nhb]"]VOK;ssi+ `+qjeߕ Ql1[eO0 }rEci˰1@}v~^Ν,+a>6+K FsI%/0Ab?u3F[Qٛfk}>v] YW̊9I:9s,?Qa>6¼,֚wt['Kg $jd.^^h.rpbyNsg6nKfF]bt(#5Z3hk]x\\ %JqlWE>EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^;R?)wR?-qMZ{UVA+״^0Ozs|2 xsO֮33ۤpm-?W֯S${?θ"*$G}7պZػE HNc,{+ʭyt=gQDwn|xXoo:I{;o㩮K:u h`=^|9j&:'yKfZ5'oCןz+DLnI 'MDƛ,E7s#n0_Ajy6sO ^IA_ϵ7I}m?!Շ}V$>E_XOj?ai?!k}ZC8/5eiemͷʰai?!Շ} h"`;yA(p:6̿k_o"ai?!Շ} k R1JĬ 7?'5ڰϴ[İA)#P*;;{I-oaI^7V?'5ڰϴXc+I&4(C-Ҽ#>Bquj>CQI @ Ճ# Qo{Znd_XOj?ai?!k}s[_o"k V/?'5ڰϴ_ϵ7I}m?!Շ}V5'Hd*{E`ۗwC?5I G?'5mok_o"k V/?'5ڰϴ?k Q}ACI G?'5miYی x8'@UF7Y }FVXOj/>Ecj>CQI @]ަ4Ks }P\iQZڬ1(7,O$'5I G?'5l[Oo"k i')8 2B2z5I G?'5kWVnK;8 rG s4k V/?'5ڰϴ}m!7=m!Շ}V6ϭ7F+VXOj/~zCok_o"ai?!Շ} i;xQe| 0s_ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPMEs?ڰϴj>CPM^;R?)X^(Ѐ+is>x u8K,kueʆecQ3VU ܂ T4,)t.,")ݥ!!ʪǥGOÒ7ppn\zTJ:INqWoi%qk0]W$ATnEdeYY¨TֲBrh+zNo4/F8j9(eFҥ_{i-/o;&i ]e@1g _^As󭴚ߒӘˆOk_$7 #1`݇C"W\4o(`dMjbu.mY[kžq&mb~.wː:sWא]h:|m&w4/0f8So y!e%zdMZͥ'[-)vxo]G id:^N+\ipnn- I gr'VzT7L{T)o@ܲ4esղI<пmm#`BvcG@`i%.M*>A2Bl!8Tt__Mk{}wnVUUr~4Km$je _1'RSLۺc7ܑ^PC);X.G1Aq-:[j*1 ; (~xؓŗFdJY4{{ wl~Q:=ի)lD "̈́cÏuׅ.$WP-n$;mzv4'^|Z=RFjPrcI#jwxZFLu_ qNdՅN,n,|@Ect4Rs^=+oSm+xR2CLj'I`:NZ4:~`kj~usi 1A*ۋ2R 2@5t(LCiIDY@;S`<7ڪL$#CQN[+b2 6w + O> .>.&΀LּhW~+\5òwm4q!%wqCP3zRwFEEqkMRKy6Z+τz墎H-I`x~+QEx}Om/ 9k1!O5r{k :.Zߑ!]w|㟭v:֑o\4Ѽ b-,QP;p7+{*l J3]xWᶸOcӗTm*f·01ӧºd60 bQ]x?L3uh,u;/nI<[tdYtK\Vmt;ٴF:=SHhEi~ʶu W&0H9,;U ]M(`0[y*HwP<sv5y| ;?;P6SF}9HU=)*[<%|ɦ80'h䌃6i֓ZA\y+u5q7 %7x7@y*y`zqM6NC} [[cCQՋ Y#$mrRE$rL̲yهBğ =?MgSl\ʧu<}u|?u{닋 uNRi"locjG^Mkڭdb$>|=[%Χ FG+Q6K$M-F?Z$/H1eQA#ۚLJ}H}j#8R MiaPM;+{uVb#(ƝpyJ< L.h#W+T.Hn] X-;RR{5>rʸ)-G[y շi^gY x'5*S%qzm kkZE 1ܴѴK 7>Ď:=s\a}TH-GAm޺oK[vI6% IیgJs:{KEPiZ2}b,q,܄t9=a-sp[ܞP~` cZabei,%8'9YSȻ&ZN[Qm,nT`F~^rsw nw!͞;Xdl䒃O_ZQ((u4mB-bB q:޹% uc(nw|i;cƗYmnC xJkxͦV3^J.H#$X(=! ojq(GYtYŴwPf/4b[C.V!nkA|-`,-X-2ŔOSQK4]`?mȝ2plg)5i93TnWk%uE6m77aa kׇ5;L -1G ^@cLc#<2Bk,ȍOdt5bïR{E]ݿwߘ1ôZJ̺w!ҭ ąd0OlVliz|wFnܛ6;JH%X tk"]wOgT+LCy-Ɨ2>u#>YTH'䏘QL(_Zd-Z?3RN>V~kEteC }CJrdC& 8x/ q~y38d= o:ݢQC$.|1n+í-G_$sK3Cy/;Oq0qڳ,mst}Fi!W`<+LK29{6fS`@Gc%zIfu,22|8T}Eb,z@:eޠ|wFܻ==W]\JJShaӠbKsrI$k]B((ݢV|v{GyjAf!z:uqU WՆr>1r |8kZMm7FT1J$.Qu9 t5s:}>\jC \nݽVlI^חXֿAJiw>h!A;q׎kk}VT\x-z ݫjvVwv[. lWn#:H|1bm]G'Θ  N1Q\h^Y溁5ۤT󜁻c=;Z"[Zz2]2@ #CK.l滏6dIw#$t8ҩ/tRE.VU\VhB_ךN3n[CuY%E#p3aҚ|rea$V]DO$j$9 `[1Z(mKn+{xfq<JV\A*r,/d `r] #C8o17.sԐ}*X3 i`?oA+8e=HgRa߹[Hmr ҅_rU;דjf/6jc?5x_N[0B̦Q@"gҡV [k*mA@pF RDq,{d*#05eܗVOKOۗvXǷjc%G=X#!a%OgTW-{ެQ۝dmSI `(?b ( (4s1^gB!o!O?딵 yi v뱇i] 7V-iq=Ğb,&͹ۜM ƢMr]6h-y/U \O'R-xIZDUhIsL E04Q1NukC7*!Լ=[xT弉e`I~aAjO Bľ8EVjHyQIi}gj67`4p2 ?V%Oi "EwL4n#qT`3y{2X12k~"^6U$ LU#@=|㢙Ϋ}dR'v,]FyھO<%'܍@fshUhe^h|3aC3Gl@28m+]ѡbtBEx-OwP2c1]VOIm,e55ڑs~jOH,gw)C^Ps]?MY}.uյvR2ǞLu;Nڧ v6zyKwovlAq4 6{۫+H3)w9氛k-D:vLᤌ;Wumlu! F1} 񊥬Zhvu74M9 y-\4r9WIUuI'SQr$h>{\YEMgyLNuspvz~ng'Q Wן ƮW"6T5Xo4^:d4CsOVhz#G]5VVgxUݻ?4g|Elty|Ik fu[x&mqHc9'fKxma/om c7>nq.*5KK ۷Htv2^~*UDTa'G96>6ͤ:Q[r!#<`Uc̞IGdV2*>u9b##^`v3܅ЄL˸Yʹ5> Cɼb񶧯E*1#;,"#ֵ&%j٦ ,ܲLǥzRZ%}*QsBMuQcNpܜ1Z|[В%p F'сi/M+VyV'T#PYr{}]~uj% EKqq]U 9hiU-n.|-6:nE&Im.T54|KhX_$2y@ԛ;+ovB :h~k%.X8$ILW-aA/-aUSyH8I#>.+Kg\\*݌`qE-r=]x;b=7<=,o/-#nM͝ ZMƕe?I\ZCͤ.V~#iqZ֬\GbY_]Nv ݹa4ԗpk(2Vaocr3 O ixc6!xIJmcޅ8fe %*IH8$13\$c{iyd5Ѵ$f\PB| oxDm.!YْVE\g%뚹/U@dPKJRm ݷDm8J[%c7mCINp3 Mkoj}91L"е=(cc=t1HW75"Zt?=+ۖB!{K 2Cl`V3ǽctftSN;{{o>+ 0RF|sB.Zzw. rxsYqH2'$=(ޡiW@XF[pzկ ꭨjڕA ciF;f;>C㶋o*ԃUL -G[ٝsN֥wΘ.*N0W{ ;W&k&a5i|Uh* {U?hO 'Wqt%z/.-Xk:z7iHB !1noho ?Z}|_h /Y"@{E^|E_NˋtK*C6 wx+q2`I)ќ;1$M(4u+=tַ$-w˪ߊ#xM-ڭݻ" uRAP =k1cҰki7:U+6qi.V.c(a mj[MG^{ռ\-m![ +c|0NNgҏ iZ_]1h𾧬>0jp21+9Yn۞:utۭ"Kc>Й.7ds9:V~sgˋ+w:?&UK|Qj Wqx3F|mmgg,Fsx h1f#{W2h_ V~֟4 V&4 lG^!-MgorvqK%џCӮ,.X  B 1#Gv%mNwJNy%C.}v40+{KC"ڜZ^K3m ~qW [ңe4u^f;WA 1/JEm0?ŋ_e=FRvHsKV{$P+׼qhZ5#Ks_0k_E ?rvOj %G5pzN}dnZQǙ,>. hC8j-.HaXoDr2`\Z}گ|aq֟Wՙg\i5F`]vAn+9mWʸy>O3ӝ]FpeI຤7 Gkjicr~Ev[tXVYpj|Y/brk~. F,VUS,?^O_>[5di& 3r?<Gh-[mAN#ֶXH亲GkWO{hXGwF$-"i lV} &UBD6ʍAKC"U*[lPdIaW0+Fnu˦Vi ,#q8kJ48A5_dQ].eٵ(h.ȉI$w']>H>Yf֫iż76!uJVc! v⋻ e7cxPah7L,O;q$R6k(S5& Ÿi.*vQ+]"+Ȥ$,lޣ<~.OGav"Եvп֧ĭ5CG ErA8#=ՏYRT JsVřL ;s)gvk%b;X6Ā g*lPu=GIEc$-NFȶjvB4G2aL~y4+=jVC% 7`81$_S+]hlGmho`K|BQ%FɔH;6jy!8i?P%w%Eڨ1lzS5]zY&&S=30\`pqRbe(F$\+Ba>\c|#dK'Q+l31br=+:XQM(Ey/7ed`rOQNiu[>L4=2);Gd@C/όJR+_}! _@@49EEY-[M]KN 9s$ .:n?wt/텓-9 e'$W:ޭ Kxt ̑/!={Λ%ܶRfȁ =nڢuoliFH=bzwg`*Ē2Dǐ Ĝ=&MRrDCt,4YIKaeXʴ8 Nx; mV2}f~gڬ 4QZy.do\'խ_RAeua,W(cAP+)dۃ._| hfgn]`fK[rncJJOSߎ~NIŦU|ϥ]}h@^C$9$q+3t\i$@nf V+J=:]YŨE+'8Xj6—(v2ܣ?tkt ,Ja^3yeiJYA'#LnKk$W2sqҀ9J [MeLJ8 p?:,͆0IJ73v{(seiCx[Ȇ@] ?0/ /5,7^! { ژ̭x9# :c~A_co[]_pMûrzc޻Iy ~<2Oosq?#L`mV^(}~\tz[|yk5̖QsFpÐj Ėzm "X6Gc^XiFc:RW1EV{!_&z+/X'ÿokCO QER` 'xd8`Fyd`2I=|MX{A$WO-i7`h^Auenc\p;Wi߆tF/ SĮϸcʞj{MnQ+P;c?1Vc;K{R9LDqI`˃tWqiFE[{XLoߌsڮCyqݕ֡i%`[gd vm#NOZĚ|ӥǓ+ CZʽ|oƐZ_d ծFNp:VI]_&&wytn5GS<{&8d\ӊ@^nupR|žhR^g#qBjH( Iۜ rYKm#,v)Y #={I;M=drּCdkI6KNqq^qs3G*25&[Y'u6A)xb POӽs?`ס#HhCm;hU%ю1gSew-捶Ơ (=Pψ?/ *Pψ?/ *ު~7;t7 Um?n77tëV|cGZm?F"6XK GAHdFppE{/-luQ%fG:kTqt\ JrWM[SK280q+rѦItY܀AS%ӎ\[yRĀD\CEw! Bs#yGCZI';xeQoNr>5xLPUOtBMp^2.X ۏJKKY=դw:T}Xj21%pV55I18Zts*HoN:tブūf?,_}2h!I*d<<dq~-)12v:̚Ѳ{r6XL<̑ܜVNZtΕ m IPv}qQxUEKEskԒ>c5JB+HK<}il!i>J澺P8mG]tэfW X1sZR}C-ͳZ~kFeSu/صo.FP0ǽo aes5K4B\h=qඖ!(d@h=[VeFYHv;5.ʹXbs;jqĭWöH= 5CBŽs+N !W]'+$N,IuA1r7 `dv"&B2N>bػ?#xծsg(}B">gTIrZ[\EqPM*,V3ǒB*%^g _sG|/nRf@.d`C~펎[a}.'G>xw%d]FȝԟJ-uipyC}3qn)Gb:סZ]l1`*ƤZ;+X_vqk ŭ$wO+{3mC+ltKuu~}3l<8 y:*q{ŜJHسv[gp‹q<*גK}9gIp\4~nQVf.n|Jm9bÑЕf:ɭ!m6!!%{Vs؆lPIqXE&C܁ֻ}*7\M)o-;(> &_BTF>fmΓ|;1ZC'\ew`pm\jSûCKs\ֱkZ&5TXݞ o\"YêhJv'$Hp:e]֥` ?ɕ' }TڐvRg@Ĺ/\OR~ZG=s+m?[ްM$)#3^xcdqĻ0 c+{+cݑeIie:tG*#EzaZ;X5iօ,vƮPdXD5]u+_CJ ,!UzpUX,V+(buHYddwǨ+Ey⑜$Pչry ۹`̎H-d2dC[GTy4oǞ Vl9cksna+ 8;XEdd3BULB=:Еz3Akml[Nh,-պLvjEiڵ棪KVW7a2Pg#9// jhk>ԏl[cpAZ,ƩGQ/7p܅9p6Z8$׭,,uH`OjڳVl28ypמf3I$nU~X.:~릆m#\FFyyw>vYٙ"Y3wmGlu#G8'׮|=H]c 1N nkɕW2kk &17Nzb |Syvq1sGNdp|2k4MKptܠ/gk|O-Kc0fR=ӊ. n4!mўEwrP.xn5ݴ]& Mswb08 ȥtԳ>o3kRM*. $]_{5Ki )~1$I5X;ß䕫&FOݤB+T0fg׌uA$h+}Z@!qDy@e5->.eXkX]>.`YbUE?2l y`u;oR| A56I+KfuUX#SǑV'E} B҇IjO:[|ا JW%+4wTխeEyy؀ΠNޯW[kwkykdnuzHy.!G<`UmO[%a/g d[cC=S;i] gmbTZQ1C' yR#CJmbTXqWOn{";PY+U;+;ZN3ɥȰa R A\s9#}4-]t[k 7g@#$mr8)rMrWԐ@ڟpijˑ>(><-[qj;-CJUxn7= ݑʳ#kmubwgܩ )4q,3&~˽(\)os<.ǁ,Umˋa.rs?C]n[ Mn[,-;vq]3UOacu n:i rIuRWKVt_8RP"+&H ?B"M^ ^'W_ [O a yiڽn3-̫Iut, ,$f1\xc욾wq1iP%0q B#p21+Y4pGu%2[GP#1hBzFI m~kɑ[}T``s ֵѩKK#FVs=m:`>5ZZx dnݍ,Fϸ \s-?hD:愖:-X6w6ֲo$Hx.zI8[J8URĒI`%7 ѤFs Cj7ZLjCJZ[Ek[gèIk 4Bu.R\Eֹ+Gm v-~}i:6H }+[Caqk/u n?/#Z.63h7r!YF nsUm!Kk28Jf*t5㴮 cY%&y EMF#{8p/L A\4Ӣ̗̄GNveRTs89UΒ\xKh*e(ol ڣsz o ,u##tss0Sk=#1_˪x*XqXJP>u{^/2o˃ۮ8,׬Z7O<W&RQT~{5}%_6{5}%@Q/՗DAjެ  з>[V(pW^5[fA;'^wJ89mNt+4q0R.ҋgjwr^K<}@rkɠt1$0qqyrh@Ic$}\›zplytrz;Nڅw-izh 4CPAWW)+.buE p~]+ǔbkh} S<Yn.ofyef$kSR/-:&^Y=&.h[ cJ׮)I%"u / HC)9&Q_^˩B 2k|_y(ps#Ezvf;rŽ$8;jBWv;Wس+Ssng`JL~qwQZGȻZ[Đ[;Dˏ>xPBȬ(N11ļAﰝ7tJ4rN3i0K-If {kfwup[plt(Uv |)^'@ wYѼ7 ZYwa8 z_k,ncy%@`jϞ5}rKB hP[V~kWN.%~kp:0mֽYmԾ<0F s}+񾕥+{95|_;✡?tqI$j9ՏiEd@Y>fbF ӟPW4W“,'0dt4q#Ҵtf-]ot[YԡiUJz]K[hh`0Ey$8Z$@4 jR=?h)t[&O5Wi@$cש,qHPSۄy,(o k#>a֐31q~~u%F})d g){S-g'MsQvRJVzT >ҧ&dnᩗKynR8dq8`~kޜvQ[";ȶs)편uq;k.5/+"W"g;IⳄ5P_y=Ƅ-l.PQ>Vco uU5UXV5ԝHp=k񾏣Om{ִhm*9 qW/g4^tKfUaR*9D/1BJ2DWIue5ngVKs²4gk2ysvYR2uڙ :[5hŒ{G\)٭< 5˥^jZ,$;F w7S>[! f|`9wksuqsI"UcA4穻#Iuf1#h(z>)e1X*ɮkM h0>nÊmnf!.7i1h۴6 9a=xZ:Au-Cqv}p6N].qaCqHauy,!qwV[AnnbU~!qes*2 zf5$MKJ{whOJ p8a)K٩EEsX4>_EHD#}V\Gh"9l wwOY\_d&גK*%Z<^$0 榅$m;Ȋ8gjGaՔ 뚸UMvxlm 1!"LdI>JauI廹B%dp'#zjM:9//˨% 7)K]=^H\E8 i)+5^4m3XO.2ʜrqkM|&3[sk7Mn>[ܭӶ?*xPIz_Ɨm.M/N^$ki .pȝFs!#v-/T4BxsߧzBO\DFx%_;m#NpcdW iq-ĖBS!bA:f}* $8\rWqV}eqTrG Tc:>?zJ7ȆHI26s\VjzifoF"B0VtuıG fHlîs@K&EauF2S #szn]J6X2akqOugA30y<+d u֥Mn+PU)@XN~l=BMIԆ+TmvDM=8/P"7wځhwHϹIo/#sq 9ȭ6uKI`Q1k6۶8|[#oOs\o~_9]gI[OI<5ԫ`3d\sV'c YP4QEwOHleۜ{ҽk涇Oԡ}HJɻ2 vݴיiU:pEyKA5k܄vS%GZjī`%z_ojXH)*?O]56.`x[1}wm5Ah\[L `?Voʺ _Ŭ\AmtB2OMxu9ohZmm$&6i , ԷZ8[Kc.ߑІ'>q6j2>̬(bֱ>"k/V$h4@RiڟJ7͔7omV-?nxwzu}Y-dH|7dt#TřŔ 06wqmZ궚,2jwGt',W9xZR4w-nep[_87K?5=2XZ[ 3Ecv`V_vz|H&Lρ}+tV/\CӴR09QҕK*[u}G.ubwVzҸ xt<Ü#gHőis0a1|csq!`h!?*E`FWD2[-LW^}$*|UFguW~pBjZ?B"MT#[O a y?% q^k[A,23F{R;\.?6Ukx}S}y ܂F0;}˥oM% a}3aW,O uݝ$,mXb@F#8-"oұiilzN?"2=@?^2זy.#?? V+;t(ŝKbEy'!+4 Ty8 \xҒ&GEVĞ(g|(gnxDV3[[Q/|Z8}Gp{4Iws.$m=l$!.%n+WBpp@vsވ ;; v~fB.t[a9'tRz^Q_U< |?GWo7SE8H*~s1e06n<q^s%},aRQùF2]]_&[Kgs$c;XW&ǘbN>"O1N?zVQOl &\95Ucַ'/`x52V5촫x\OØjnQVMJ%|z?Hawɜn?{MJfF uO\qbgPtW[_2YXztgW3~kF][ʌǭ)jG=j 4Ņ!LfSd)qq]&Isdgȁa(M7du !z8f}>'&t$.En ;1 V|GXX~3;EgghIՉLczAun-.o짷m+ot'z _JPMg@#Jr9.餳/?42o9z$ nOf=SR%.CGT\rG3[W7m[x'B;Kmlq,1$Rc@AS~z؜lWQuq% K4L`0Ō͐;b/=*ŢP]Fh18zF.Iy Ŕi[8fO$B?BmxnA{n ,@|r .-CvcWe͌+gvNyig@.K 20a7&"$_:Ywni4e܅`.;ROQZYڏK:Cs[}08,z U{ղ~ x4d*rs?fkki.& [u,䁌skn_vmqfz(s/ rLa ^8Gkqچ:jY p>l?|RYo6yIy 19OA+y ;Jv-?N|2h-R=)|=)0gG<.#ԃv;יx}yy*˪^:N1\+|H t ^J;K0YH؎<ܟ)۷#Ͽ9VQǣ[dWf!2q'OQUy-/4;O<1&ߕ97Lz+˙bw>ӭ>ZwWp{0q@ӧ06 `ǐ{`mEAg$wZ,1-=~M6I z^B썊U\㓌g5RHU9*Ey#C}mp1%pNxԺNo+VVx#r=*d[EsOg"*UmgkN r&ÎX?;<.k&HH2Hrz]GtMF ְyQr\?O:摞E([,A9\ǎj,v͉6޳.x%N(n&PRq!?w?jzo! 6=+"0.cx2JN{U>gIJBrGhmcCfmB\d:[j,ȰiaMY B֩\X]4Ri ֵ<$rݥߌsp9OpR@X?yGlX'Xn*-O<|q[. )YQZt涒[#fh'y,Oa mmoV- UcQHy ӠlΣ}]kQhvIۛ$cr=`K9e]!!L?!J"]M#|]O3?|Sk,l%~J8|p? ?Jڽj\ZyS\`}:kxs>q(~ N#-{d{F1̬^o/at-i'@:|}:\y+(5Քj$ qy۷Q͂ï|fc*xRKK2[+[}iPs3U<Oi}|tݵlcgRFuy [fzukHl#Y#i FaiI$0 Ǚx|9ceyf$Qz)xvn)FȻ Gd|`8=K\E\+_7|;]VoC.nW$pG Erۘm5)5ʾ`=ԜҀ=;4K4h\$}j? gsˋ(!K|φy'<*=>}+U0s4FF!vl:]a]Eo?ҼoUmǹr,nx/_Bg{Eiqߍ}WQVXSsk2,'z/MðjRtyx"6BKhZH_.FswzqrA}D)6fM.+In&RX^2 yR5l,DЬZ!2~@'ǻ{۲Ŵ:dAw@" { { 3s=5vSv=J|5edkV` zLriSCM+F<TfH&-m,.#ʏ]-i@ӵ@l4Po$;\ VqV0l% qޝ85gEG$k Vu)Ff0S掩 omw>Oz6Ͽaı+abz|uf}+ xm OUץm[v~׷`[0z%#Si}hv ]N>XdL*ƬQVuE#MHe})֦+${2}BjZ98;ﴗv'8Fea]F 7?]+zF[.Q\n>Oxܠ`VOt3V0G{,$kyն*u'|W?m P@ƞU>^\]˜ґ J3守G)u8ц=Xk (\K9cR>PT婐MqHehY'idy;er8VJĸme"Lf@G͚ G&F_iʂ'9nRN; ׊VLsAckRsP)Y Ӡ5PіY8CT-|H)(D .LH}bQ$E}}jܜʬ]`Sq^=VY±=z׫,6֑YV7M`r*TOuzNivMܘāS^Y[F7jS`]3ᚸIKd?XI^^7J7MuM)H5ȦXƞct?<gkJ2lW;aUG5WV[t[)mJֆ!S|5>exj^YU{==k664B@6D۽5S[tӴK%r$o-m pO,RBݱ}p+!LeOncb}WirySC,%]閎JV?zԖ++qLqS\()T7rqv}򤑫r]v%uz@iVhHv~ .>kqq Kyu1(cLwDD|wʬlZʴUZ4JİdT(A Oo*0#.FG*g\Ef~wzDvBRr +\zS'ȆnPO^j+F˸H88t,BܹUڮh̯R鴜\S]-վ B>J ca:ZuO,n^pm(ɹ=}V/?ŧx[.MX9^dgυڦ$L/9 -))sdǦ ](ºxNӤG`R t2FG$Q-i%4K8'!4X SOuiXƦhNTRrFx)is,v_dMŵ&c3OgWMD4WQ"o>V=4S[v'cmrˋ^U\ 3YtHIqèMoIѯR/tɕ:8 9\R.ҚCk^KJ 9u G]Y; yezNt.uSN(1ks}I(( G'5Mk-1;g,2Tm~U*Q!ڕeӧ2Z?pfg` |iWVxZsqm4)b/{ u5|$ k˽Rh(5Hlm'1ۭ`hs#B%qzU4q]2 #`փI SEx? YՂKsu$K}t{Gdզw#;שΛ,Cr|yhôxl2q|{Qf&=lm[[\ݵGKq|;^-hԽg6I5RH`}OPB$<@^G˼S¯jFf.lVrdW8@N2OZ'yF68WވtKj> 5th&Kx ی܏]?pvBedQu^mmBw xoΣg*8I#/r>An#@Exa^K4dtikc^뙞6UÃ6OaǭshcjZW~ -RNlr8AխQ] `@uL(Υ,?aqBn#;'MoHQPp T8}>mJMOˉuoJg8oҡH>z V5`:4>\i+cF VB :Uh/YUH#ؤq]<ֶmi2\]"Ĩy+z~ۥmQ^ l#b0O^ zBH>;C9FÃ@3_[N$`Q[Q"FX|CP(!xqVolq>e(T2#=sހ7oq(}I[Ŧ[`cUF⻉C8xۣpje%fK_;mCS[H]om,Se'O_E&-牭f:i#}#\(ѵ{mr[}r).#\絓G)ԓ\ǦIl2u$s$Is_ưQqSԳuxËP9 /+3|dtC{]4v0Q+'2HC0U?r~gG|W7$6ZD4J5R+Wi]9[{RNrs :gnVL6iv^bH0@skj4) U%.+hkwm;dž6t;.}kqpgk{m,(L"5ѢePKfW\tLhֺђ7kV W2sڗQ3.XAbT?URN.Rfronzm *h_>klQ&H@毫Ae;R-8m\d=MMjos/btD?2cՌrrzQEl^'W_ k޿d C5k~ wAZ?7}QEzK]GKֱop rc$@ WMZu, Yf߯J.M:;YO #oү 'V[+S[r@I<cPWùzVDrh:W1g:3g9̀c#S^xP 麵r5&#\3epi]]m?cfޒs/ԼShWdV@8(IBG(kME%桦j?g@[-ͺvHV[F4wNm L@Jy+ ZT⶗MՓnL<3gj9O`I6v|+<98'&PWoSU]6yn o[Z爓il|iF20 w'bmCų!GyeRl8'sZ5íϨZI%кͻW =*Ԝeʈj?k2iֶc̛m| m+CC!m+y,d"lG<6zZڲOm{ٵK-De܁ֹN( p qWs͆'qǵe(ݴk3#WAp 9=}9UӚdwD,uP2ΤiۙJJ;6 96hCk~qqf't/(ШOR;S-En+/;JmŐ"2-cPqma9Y"p _S.??WDg ? w*FzθJCC.@P;V/mF[*s׬?z.ͤ>׼QY^RgK}lc.rԒ*^[uy ׫blOQx9iLaUWg{Nu5B.)cSz5ekw$FO,| 8+X-ĺsrLM͜oy8ռlӣ'VDucݫaFfIjcs޹{oq2;I%˜vE%jɶcoGݤE(CS-%$yb,z@O־{+W?=LO &9;VK,68dGy!ˎr*Ki{~oj-t<{D= CY yyM !QopF{{F/t0Sv@'JvXOY_,`7 H%r7sW Q>pz?9'Xw9w?nj <-bJ^V2]͹??ҶTdf#v(%s.>74m"5)$9׳cy53Ӛvo:垻>q"@myc#p1]E5;r[m&T?G&`c;|~Va{[=`ſ!s'oJ r7C޾DžqZF?;=!hh<]Yc escտAtIDm%sZt&Sm3 %X,0[gV|uX,c,3(#h<[:rx;S| D=OT3 r8$r:sUu)ou[A%gm>ഫ&v16T &*K۵ӅǧJվ{ol>җ"}_Sj%kZ}SiWi#VIہX4?T&]U7P(7; ;+Z1{$E#0~yzsA宿Y.㌍3Y 6N4y9 t)L@V'F,mx, /.{]Y.-gW_M3+yܮ^_Vm֡ubv̳QWxYUkK?ȸ̍bylfKyKa#wCe)9dY-LQʆ`>UW. Gsh]d[5Ċͽ!HBqqWn|/|qmdBx5QRq=V+]_|C*nGpNz,[FR|#؎}GP׌7:H O'2 Rv՛AכNkaqhЅfe #d׿~ry5+=,r[Knu+5H. ݭ˞Oza:Yh%!U0 {J%ƠMi-'erX$rIS3:u^|6k{"dR1b6LzC.©(u-VG3`ckk&jldx2%ؗ"CXt)?5;FI$2 ;Aq[ѮB5Ac@I<yUZzgM _['|a~?^#K}n%~ck>,CAOI ff19A⟰n5=,#HDdFCn<޳5d#`o9$r+FYJȄU\cyzkBj9q i/ZKcwA o+>_|=1@Mwe]?I2Y"-Ҳ9P!~\eGE{m\iF TiS5O^"odx9vs:xZh"2yQ:HH O", $TF8qۚ/*.b4RI/گZλ$ }jݿO[I$zP|U'U 40ɳ+lh 7n\zc?4ŠԬm`n0Kyc˟½Z߈-g+) TFx~% F#|I)rN(]a-/&Ko_&d:_LN; kR-<饒iy<1^wė~ݦƑ`H$BH9^־xV4ŏY#')yoӌPfie-Aa;sVc YWgگARGInGQmM&-*y2(ڧҀ>BngKE+ٿ|QA-yG_/1Lj/<;GwfBXWh&|Ic Ŭ5x4_gKEURu=Љeu2\S\#aׯlđ(WC@lXЬ6w U(GB? )uB%ctaieL} u?<֞I${YN8pR[mܽZm,ljk!gؐO=*xK)%M-bؑu(O Xwy$:$UVKssuŵdi .o13)'!iIj)yE{X>(et\(Y mc>hB8]8q^נךlzl,fn2d#umiiK31wk9OʹW/oOXX}H&r,فMFDaVԧ"gӒ2IN3y_U';b50~T]gjz)>Y'3ϊ?%MY4Wua:]D݈ sGznxDW.J|?_N<g_3瘦Qo.8Q2:ױi'AWґ,̋ Q-ExSYfYbDl,NTGyO |7&WXq JhSCuY¬)u59>ŽcЫ; FMxTJg%IUXq9F*;yzZrԖsK*'{u B%|v#v'}D#W5_KJ T!PGSK_֨Z2/O6e/DG#=3_NE&ܳ?7$}.:UA2B0'-QTbQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEnxDW~k|?_iCJ?-!{巈&p 6AYە9ow٢Ay &qnXb02h5B3*igB:peܖ fKJ:\t ̌Oƽ#h((((((((((((((((((((((((((((((((((((((((((((((((((((((((s%zQk|?_iXJ?-!Ʃ"ɿ NͧߥWV7ǻ)NXͧߥPZ4v>j3ތ (>ˆSĹ(72K O_&H8 Lzdt+sXI[E67Ws x U.A?@]rGR\IXԢd񥫮y7̂% w)&[=3Z~&֗@俖(g[TVeP ,w1ǭ3?#Z`~lc +!|Iz6ınԎ@Q@Q@ k{ż[w |'$P&ړw7ً yw)&[h+r_Ieyyy.ʠX cZ. BM'R 3z GEe:^u+g۵HBK1 ui:~sx_FdK`@+Ҁ7hOJz$0|q]-{R3",1:Pk:텺Gooί"˸ q9[_Ťw"&M ( `担J+6eL"DKx4\, aԌV q=:OopUFpH$r1H<+iF+_^[q41Lb(|0<jqMkF޴%w%U㿥kEQEQEfkzOk+ir_=qj̪%HZ͟.$3z K[O?!na2lg@ր5y<_ 'O.n23>N*Nv̐ n P';E#)9e#v ucG7I:"Ev0;}ր69+L*-#rzk.?ڝk F\ V5ZB@@b9:@Maol Vh.,Y{k3A#cQA(jn-พ1Hߘ O<(oI% cHbnw[xL((S77}Qh3qs-m  '$MLVR&2\\}ȍ?ߴn}3@]-itM2K系HtUfU%HߊIҗRm+R8wE8`9tVU޻cc[^R)Yf qzmj^/Ѭm$قț 3^34_5PAЫ7 !N3Ҁ5KO藒*Zvһ!u '$>*fIc`1P;do8_7(6ڝ\=HWx}EW> ҄oMOFY54V%ړQV !q$r*ew9-0,EcYHx)gnlngۚQvck$nX,W~7}@i2V {2Ms늛]$hAn.'X/sfϗWib$(~cұW{hT_&6;sr(_}gI6J* 8(VVC$Os-o  ;+!mj-fv5$i.d9#hA M>Z;UVp0I<01YM+3ohD_\g >4Y7m(g M0SS^Jvһu 'ghnËZ,,z8olqŸ=it mpr9q@h (AѴѾe*UIhnQ;?}Uq$t *eyak4Ajtljmᑥ}F# ٢Cź5&/>ϱ=(V) {M̻p\: +]hA,fM^)KHC+b?}(B`pT-z4]6g˵7uy;dIR:^wM"rb+oYmK}28tc#gJ9W߈-,Jq~ڍM<\rC1P d]}L[nn- nIf9>s@֧jv+/ڡBe1&  늛]m|SYP_Nl?w.]JQKlK >I$MԮF::G5F˒ERd@Դt[. GNR<0@C2I6M[r-22?*(9-J|MyFvJcc,3n ȥmoZ[0ROizlqKkg^4@(ͻ+ҫ<gCwzArO*g*u}{To Ai:jOf/k fAzS.\ukK۟-iw &w8A]  (0|Q_Kfk>\$]K($ppzW='AoZCdTPmJ妞H.[bv&,Dڝ60`Ex0t%??nkQHi\Iizs#;Ur3 :㩤4;NVsoq+37(*;x/OoSih3_y:H5Xd$n9z׸'ٗOy?3=O\W?zGާ?_OƠt nveG 2XFQ2KKJ<Ղ&0dcoSh]n?#-ZmnJm-3̝p@dÖ;C`r:]i+L6QG HʃB1޼+oShbGΕ4J-/q&yT@TcdTߩ?*di>4`);TۥC{?OA8-Bq=k$|鼯-\Z[$qj뺺kRg{tFkR6)xroe62U 0+s@LQ[I(^xr#"x8umN?O*(c#X>2v=(]"šU7kpH Y)xÜETRº- kq zW2J ?05E |A.%wک ;x63Ӷ!FN=O Z֬:ڇRy&u Wl'-#t(#~𭵼E-12?^k\IyZbi4;,*~~4=$=]>H<_sPZ!@Qn;zPˤUR|K,V Kz >,N~bĜLO+Nnog#'#ҝ6+ Eӣ蒧*8Q`=);G:#ܯ[—wSKLkIDCE[H sRɭegٮncGU9 #T Mͤx_jZjEZշ}RIr@# :/jui+-uedR|J_!E<TмO ^趺X\\[^Ep#ޫ&1#1xOѮom/x汔2uEM@6![O8#wMaQi7WlHN>lAMlOv>4#eZ[\iYFBFk=+α-q kiqJy\clڷuZ1k%eT"n#nPt'վ-[jzOvZ3on7nojOfחcDv^5*un.yϟ,D2*/px]W%{E8/R&^3$og$/lיf`[!4њ~h34f3L3Fh4њ~h34f3L3Fh4њ~h34f3LZ殺[ۉq3@f՝KRQtkB@?3<]MLJKǿi Lb4O5⩦9.#9.yXd=idzCtncuuR{h5k{75LX@o$U& x9#ҹh։6o 5$گ^3}wd ~psΰu∵A,6!t]q䌚=:ʴt|g担ԭ{ /*+珅G*JO?(>$9+il?''?6EKHgC0yd`5?ØccĬIFcs\dHA ,w_˭kCJK޵y>HZڣq*;H?GUMw{+][G ãTKs}j*8]V=rtWTiwq\:4On6R8]|6Nv5j=ru.1*spp*X֭@2.-XfmGR-;!(*N ,Gܹ73CHeGΩ{osbhf@VȪC7/YgxNVpV8,\a=}/D\vjgH)t!" rr}g5~NqK^)K&{Lcuhs0ʕ 9v5u'繷ӣ?/#哢-סv.t;ɯ#~ק[,qf@@<9=+7÷wռzuV#[nB0$## s޳pe akZq/?hq/?kO!ECzonjZɣnjZɯ<y (W2kS&W2kS&Q7\xO?\xOoQGޢ>q/?hq/?kO!ECzonjZɣnjZɯ<y (W2kS&W2kS&Q7\xO?\xOoQGޢ>q/?hq/?kO!ECzonjZɣnjZɯ<y (W2kS&W2kS&Q7\xO?\xOoQGޢ>q/?hq/?kO!ECzonjZɣnjZɯ<y (W2kS&W2kS&Q7\xO?\xOoQGޢ>q/?hq/?kO!ECzonjZɣnjZɯ<y (W2kS&W2kS&Q7\xO?\xOoQT5[6]p-8W+еߓG+еߓ_@D|=^s({A (| _-j _-j_!yr'4?^E~q/?hq/?kh9?—#o?O| _-j _-j?#yr#yrW2kS&W2kS&yhG4OEnjZɣnjZɯuo?OyA(?\xO?\xOh9?”~_o?O\xO?\xO~_o?O|5FÓ\%~l$ '| x@γ[H4{T*?*+KewO_'_6EK^{D 3ZJ |tU[Oybu'ԟn'PXOh7j@a'aGB={yv2 R\S,F>cϳ~nss\3^$yp#۽y_,-{y`?Xv.CxӰXTza]40|ӭV-7{Eݿ&km6&C^L LQg|+o_F[kuJQGTQEdQ$< *^I*I+5iV֖Z2LE|sۏJ~UܬLNɋDRC!&C~C\ XK ,"4h_d6 aMo]7V[a,[`2$rx$dcP/#,|;eͨ0,L3=zD׆dռ|3 ֫]ԟ?^ luIELBQ$X4ĚΛqq'ʢ]ps#UHu[Izs+[M $8S֝[Jbq]pVa*?OnՄwhքZK iWr숂Dc<Sl]WƟƟ%:sB7LwX9߂tn RM"4I<!'n(sȪ:d:uφoniew¬jpM:whti1ϫ*J̡o*`H1YqVymL0? VoGCEV=LDr9$m{i6 sY;й:T||}%̕7HeRFXTuWyT(#Ʈ?o_>_gmhGUx{I_\*: [>y(T+a- IqZG(@znX Le"`c! H"yj\y_0SG͆Vl|ųۗ Z ajzm1vDy鞟<Yjg5;ٕV*fU[ ^}jo@䳇mIƓoP~)41ĞSD\mzջ}7uKE"}.+!U|p4gnU894QGi4Ւm*+@TqUX,5SQ3Z\6xSw?Jg-ͩ˙).j1ȩS @h2Pɦr;\4QtǰPOstfXpSJktamd} o oq0y_[ZYKce"' eQMKm9U.g_ĈʠIo8 }m]u];N[4MbI,.R)P̍yrLzn//'׬I>lq('kSCx[% &bv߷q+2~xGڇmqq mk#q](܏B*LԵ 5-Qz%z6؎g!&I%AuyYd9$ֳ->ks zɞ+5m=1}i%k%>}^-VWI,Ʀ%oSyoií}%4Cy#rg߃}oR&f]EėPh0@pEˣD,:Ƃ,IʱTpFwwqaC gyqz5ɧ[kP=Ɵ9I @BUޥu{s>ɏBa'UPE2/\w#ЊF]̈́۷wv[\ƅ-(PN/ x{ MvZYI䓜M۠o@^}JO3Fj.|#S9;5[Bu^{i4/VAoh=5c+.5'묩HmcL/ @;E ɩ] N*m~ώeI?SC/!GEkwae "x'9/E,*vA*k/W&DMSXj֓^q.w3߭?Jc Ɗךg-D79*9;p3X~F?4SGwY\Yw3&VDC$&1ޣ_ o T5.UI9!T>$y9'>~̾++kojbiI:m\8֗^m[k3F-Qv_.W4m5׮5ou8-ym@  j:XԵmEɚ172 ;Ebxc]-׌Kq4p:CH!_Y$po@k5yw9'>[[;NK2뒉'Fb<)js Bޥ{u=Zմdvm@:%Nx "oͯZYa]M wcyK7^+OÞ]#RҮ.5B &u@+(^eQK_vr^4{FC0\ Þj=Q<}u_/co%ƛ+ݫB|:%N玤׵4]x-#smKYAǻ9n}=._^M.K}:9R<[Pb`fծ{4RG{p\3d]XÎ۠&ǎ[i0#i g 8X󏜷ϵtpZmė1Z_\Ol9Բcxֹ =kicCD7bp@kBZchg9`2O9:g'ìxF'YLZދ+Gk4ֵA4 )dUIhl2ޛ߇)j~YA{k($EUdI2N E?}B(n$_Ul)kqckxV5K&.- 4q|8H=x\xQl|=dxnnN2/\w#Њ|%sڽޕ̖ڄݵƓ8p3W|+t_ dHoa#^Nq$c(A}j{kK.TibEoӡ7֚_> y&{ kk`va~\ C<ARFOS֨k❾iVm`bn)dWp"Hg<ҳ{$Kjƥ%EXhiubiO(7޵'z juo%;w0 lr5{k栳ptlŝ#Vc)/[Qm6жi-gi;_?J--Dc8iw@8T{D-Y!*9yHiI*9fܾZ4GHM[JOHr859CX^ $z OÊa֜[SW Rbf;[-3 L\]B2 Q>҄ڹeN0)pFj=Y!iWGqn'QNwƄ68\f-s$ut):5hfT"H3_9(kشO_G}SAZ_-s|TV_D\E1Oοil?'_'_6EKHgQEtQLnw*2O,8 7'&c"p|f.TBGJt+l_sF~VNkiq} QJ~Ze!bF SܚW$FnIe1#u(7.F!QfbSzu<Ҷk?_z5[(.VYmk'i\{u[YA o%hVE O w_Z>J65x*d%7lP,=^$]/6mBn9IЂp=ojs)^h32b&@D2WGj|s^ iP-0BG+95 Լ#=/{]Y\B.l0ƻ9;Z^%jz^t+!9pO^=k/Gկ&Qۏ%@;}zҌem4o◈a" K#4f8i2z䊡]kQl{6K=5MQ[7g ֳt&Z-2?1?|"ϯJWMFP!N0 ewg$*}3qں+;CƯx̋O`҇-n8zW^ ~ߙ|Ʋ2èjz姗!J b{9I)յ wϾk`H.e"FH 22x$OZ2C=vȳ;Wrd? RK"nϔ(=\o~?<5.ω?ܷrS?k~GA/szF/ʿ.kf}+m#v][orzytַt;7BMxOR:Fw#ЊOk|94u1|vh&cP6ddOo^;?x^= i7d|&wrO$y>X=k8$^Y-o4Ƭڹ IKgoX}W3,q dJH5Qj ݲyK7پРːqЎfDX`\9QْDN@᭜rM\οiK@g*q㹦<9>/vIdżX\Sڡ 7zݗĴkm$ڏ I68('kOC%% &bv߷q+2yg| x^9VėBA,.-c&i'̑OMR߉fuc#67 GnzPe֥X^|٭lҋ Wqgw=<6rI-R|&wy$g [n#.L]xѯKo q鏛I+_7}_uū+t뤏XSB)Ns4 Pm?sxux鷺F@Ho$r7cOu揭Z췫Fr9Qx qKi{V7m~w?.xoǗ[uiHdUUl: .}B pnHq܏B*iw6ne[IoEsܡA8ҷ<%G|6twM5o{\9% Ԟv 7xP|s>m&@EG@!O $} mkg{fJN?3`QV*[Ηhro$R{0U#^c%N@>^̾++mojbih$ ׇh*yS=i|3F.-ͬV`kXAE۵~p^?\,<%^eඖ嶉o/t'CKZϷE`gB "r=O k%.5:N$Y1+095x3@_ Msrg;IOCVē̺IѰ G='tм}w^0hem%ƙ+ݫyP8e S⪞?sk֖mjhCv]XDjRMă׊FiW ao2+(^eQKYӵ=fHu[ҵYOhhyFsS'].Q<}u_2X[qJPe S?m2Ny-<[Pb`fCum}w 67䷙(#V9hv5M]m&"Do##l qykN+KKԴG:Lu3ῇ~mu-:^lucb NttrhZcE|[۽mly&Ѯ,`K5Mь Hƚֽ}&66WX=̚Z2A`sޣÅ5?M^G۽ "l2N G?}F(n$_Ul)kqckxW5K&.-74qCpq qFziEnn[;Eii>um^JfKmBynXQ&qᱻgڮWɸ\j֐[KhPqFzoѵ=6MV *'N([d`Œ AUv_] 5*!@n mN8I蚎uۼD H$mQkmV]N;WfvP12}|}]ķmvo%JA21ڏӭQI-NN3ߋU2Hq 3{ZBIX:%ޯv'S73I'Q T_[Slc{fɸc (.xǺ{nt{[CGdXqA+!B@,0{@s6V76i<ۼx%Eџ}iƖOxP=%_ PH{z |R*حr5^,*dc瑃Z6b>"]cWn5)㷚A?$0fEg^/ 홷ե p=#j@=s RԮ5M/:eO-*yc]eCib4/?\o/g׭~$ڒ H,?urNxoȡלxwYkxH@7>ȡל seGií'TֈrV9sgO~W9vZSH QˆR?{S4="Yl xfٖiAŒ[\/b 3 £L#9O;l>P!.}O/[eyX`p 7A^tl zGҤ{ ) &FXryq:YR60ٽ'\rd@=xӦ?ZW7U{W?md㻃tl 97p9ڈhRMhx>lhJG+K?X8<jdE2dj!sϧֺ-IvK*V4*e(i7Iש (4$ 1np3qgtg'J/-'jذw8JS of8&1h8IFoݮq (k7+ Wq;fC\^\vcgw=۞+f;X^HPN;[[P,zun |Ǯ}]:ܚt_H `?zyiT,ZQQ/݇ tjb^1,v{Wl](=yJ(v;/$!#}#|_Fſ3x#3C/ k讋&Zt)5^uOȃaa8Ty?ޯ:A*ZC<2ski\ MPcih!xշ29+/xm=l$[dh:yȅq`>qq]O+Ǿ.i65~&IULB|#ں( i!O$JTz7Z;I$b⏊]w tV%mCmtIFuROc*4M-O1W޿bVg|+o_FQ@Q-ah6O*wt ;{nt"[iU$1 1pJ^ ,LRD`:o^:lıC9ȉp@ʌ09 ΐS[}/Qʍ--b~/̐,hǐè#BcXv3i#bI&L9i)6iS@]hQ%; 8yjذiz[[FsUp9qpΒl{byd,j\أV7!l9Ⱨ FmE~[*nUMBۋLo|+w;%PMt2q9Rl yዌѪgk/Pbѭ%A:;HF85ЮsN\K M7Fݮ*5 1=6shŦ[XݵZ\ { r}y5FM*mRеsw>cj:I4Piv̺̪,RMdU$r9;Yȥ-4b+/gNmʲ G?*){Fߺs2w ƭN\'9JV%rO&$+/3o?ݶڵ}&GV4jO^ziW׾~ԟ5үWKx1)nsn #+#*It!w;K۹>He60JUse̚Mݥů%>WbsZF]wEvڒI |Q]y^@b;P!6߱æG*)NC8rYt-)CyXmwK%&cP6B9波vMvB.0z"-Zmk:a0M1cnqL.F;+'M4Msi{pʢ"0@O9^߆(mXsIݣu;.،xtxkO_-dnFj% ?Au:֐$9I(#`t''0x%tCo7;S4!rQFFr;ZhJZuۯmkgwt 5i/kv6Lrꪫ}b|TZM:tͧ"YdkiD+RI־Ϧ\j^&2\v]ܞ뢃N6{m|@Sҭrlk?ӭjC GxN()_xk7^(7:ג#q.1W9Fzⷴ־"ӴM-eU7RSy#NWj\Ҏ0;%կ/m%&0_׉cWw prIQ$%k<+wwt' o/,6ytIf9$Ur%2oMQEn~˟O-G߸Qe'Jgc_(%|_OzE\W~,ϥmus.୷G~{Rb9 uOW~e[)TR&e'#\p;Eex'5|94j:cŨpFLƠlPOstfXpSJ[4ݲiK7پРːqЎfݷDX`]9QْDN@᭜rM\NiK@g+@W'oBRAr%kIarNઆdkdzcwY|KD ^[i&|bOǔ@9<ltiE#\mx'CB;O=ܒKu<ɝsI'e9KvI @BUޥu}C\M EȸUPE$d^G t>oۻi-ȮcM('rzV焼=N妻mOv˖w${J7xP|s>m!}@E0E<d;֟|1}u{m[+[v#Q圕3X^Fq ǤiWi+"¡`QVDž*[zΗhro$RS;P52T}>^Ğ++mojbih$ ׇ=N8ρff{+X0[ ڿ s|,<-^eඖ䶊0  j-cR־"j0itX<sv!K`zV'5xėG9I-mEy !NwL/l Msr˭:Wׯӣ8lB eQ]ն{^è4RG{p\3d]XÎ۠7c5ŌI~%[+r Uƚֽ}&66WX=̚.Z2A`s;tO÷Sk776meB2J5OñxrQ'mdi[%F; ZzX~& r=bɭ-K:;\b0P8H=x\xQl|=d0m] c "r=-#WZ.Z̩mO-Y2$9`67sU b72ǜHד I!%O9=Sz;~"Z3<joxY/iW[KU;i 2y-XpN[/\k>\j֐[KhPqFzoѵ=6MV *'N([d`Œ AUv_] =*!@n mN8K蚎u$1#BTg*{-[US㷕(Ad?*K}޻/o.UuPB 6} _F'u; :~."T!H6 < y=kNa.{<7ZѹI;pUWԴ{ٮ"hd1tSȼjm\7:=Ρ2,F8 d=@sV7k$herIohW}i?4}ޟ PDQ1RA}j{{K.TibEmM_¿N6oma2{z |R*؋H5kY>U<19Lѳ{$Kjj^s"p3ӯZvͭieC6kڻpyǮ~yP{ZZM/:eO-*yc]eCiw\隐{/?ژˋfϺv#.🊾ZV*b%Sԯ5kqEsHnF$uL[P]cPY|Zxnm ;p އk6}·V0hڊaea CFz7>&auyuad>SԂJ~ kKgh{{pIAŒ꼏Z|ܭh+] [MaAR~`~VA4˻_J Sq,T{bwm'="kPmgҶ:9ݗpV#4B]oS_.z.dUIGHVW~#_7ÑxL1w棛0f5fFH=x9x6n^iI>vG,d$I5wGg8_5dFKcCM/q6e2t}GY3_inbB*sң>)Kq}ZM8Mo(-t#mV o-쥱X}$@ 0 m*څ 1mrtrxi}./O&o$)y6aF _&׬I:lqPO?)֗&$+iKx xMv߷q+3׾i6x[_Ot$dZǙ|2G3O=7{zFmvn.!MWEwA "q܏B*LԵ N(Yr?/#4GaI]^cY3Ns$?0qrg;f~[yL|ZIZ/?O}~Qּ[Ӯ?(ʲF99$<9{fU7qi֊Y㾒2Cy#s3?yz>%.$c@ r8&Ѣ -FAT$ӥHn߅= g.m@|rU[gK䦅\*"ے2/\w#ЊF]Ο7UdW1&} 9=+s>wiGr6geK;ZB =Lv%~9xP|s>o&@E0E<nwk|{eI?Z? z/ŋ[f (`OMcFZo:]UIKئv $kd>M?xON14ޒN0^)=N8πff{+X0[ ڿ =M$]zYS[{g(o/BZ:XԵltX<bB "r=O k%.5i:CdXǐ$pzo@nnK47,rY䞤:oml|K;,ˮ$G#z Ybh^>ԯtnt ,m%{o;j *sުx "oͯZZ6a]M ctH=xxiqs^i"䁖!F2jƗ೧jzŪ65{F(qyv֤:+"K5y.4^C *sާ玟^dҭ4{-#s dږ8u mڢ~uJzUtr-(\ 03K:obuKuHn+r[̔˫q4;t&ǎN& &Tc g 8X󏜷Q WI\=V֗۩i"e,뻞9|5ú_ ͎9 u[|zLG&+{wX"Tw ry4qk YLޭer28z5zPM?Y_`/"$ 8zÿH;u6swnP4(ɰ*2N G?}F(n$߱mF; ZzX~& r=bͭ-K:;a|qzN{=hEŒ\ݕ2/\w#Њ<%uޗʖڄݵ#"L cw8ϵ]!|/s!yčy;$$I'JoUa-jLq[EurbB< ǾNlի qm7fݚDA OU]ho8Vx9zw%r'廴;CE>VLiw(߼ gdqoV,u8OZM&mJ 6kV"񵳁F?ꆒ9*h.\̍eAcqּ^Nq3ZEZxwpA$`u5= t)s?CÕ SF51:dEBH'5>ڙVbJ[*W'';Сkc8U2{toJ\k]&Ԯ{-gk+V !F2$/ǏZմV{kɶY#Y'.J47 opLmH)l]tG_5($JK K)C3\E`.^};ѥ۶K{PnC)^v`g<k3ZƬu;WemWr4gN2ӿI'彎ѢYZ, GP\Zh!..Nkw$ Nr뻧\ /?*+KewO{y=" Rעk=zqi -65 RIndG! )of>ܢB=؟OjG9!}Dѽ:5?^e MZT: .6x2J[]K:<Gos'X߱>֬sI%֧tOhVsi].kKxFYnRu4{R,%ңzmzTW”cQKs c:?Q'Pg ފ#XN`k ?¾>?3hi0Jg1$Mlzc*cMKF,.`@w1gȍÏjQ >/u0E%H ~A>c]KK* GU7dUnsP4VKk{$}\Oј`Z(Z .ƺv=*\ͦx}k^2ɩ2R5dD@}E;YQKc c:?Q'Pg ފC>?3u?5 ^,i2Ŷ:6Msz(?iؼ;;Y6Te/xg@𯸨ia w ?o;}E>տ}a w BhXY 05q1LɮhРHl-XgS¾s/NK.Phemx!r};fIxmڦkqop]L ~GڽƊ[IZ-9llӤr3ǿZ5jZ隖%f{٘+Z*gu>4N*kȋD(.+*)o;z¾⢁a w ?o;}ExOawesyk=toLɜfln`gȏ{W|6oTI00_WzE\WIJ?5p,ͧ::9୎ i08uOW~]Ѵ;'R&e'#\dzY> |4E2xƣ=Zn(ɘ #Ght7d|&wrO$Y|>[o qwq4zq$B2[Ji[Ys2,>POstgXpSJ[4ݲiؗ} Ao ͫo}ymge-rƃ"' eQMKm9U.'_ĈȠIo+sM!v)wt^ 4=l5y$V)P̍yBA=1n/qkm$ڏQ< 9Фm5oAo4cn;%xz7|.-&y?-#>O#>{zFmÖn.!MWDnٸ@ET>sk6Vk%-Qxx<\mx'<>rI5R|&wy$g#n ]xѯKo q鏛I+_۾ j:׋aՕ:uGVHSM3Þ7m?_wh;..Ѡ$79?3o׺>j^KZ4G#*/mRXN#`]]sQ/.xoǓꚞo˧[kP=Ɵ;\EU AgK䦅\*"ے2/\w#ЊF]Ο?UdW&}TMnxGm4LYr䖐Os4ݺ _h+<ϩ˿Fj'GEFsgU/7f`y~ڮ#_3O@Gq^"u֔$ia0CH2jwOEROև_C^Y<'9moVs ul( 8l%N"jV ,E^g̀gq߭;Jc+^7=V F7$նf3r)cH,lh\jc9?/6A:_?hlc❾v{}V'/"*đ1 }4 .0\|Þj:rJHA fK6WGB|:%N{̚Uqnu,R@p1nTZ/ndׯnҭӣ(lB eQ]վ}^èrF06]$ZƟp1Z_ZOn9cxֹ |=miڅ(I! u[|<qҺ)40|[۽ml'IᏉk&Ѯ,K-ܮY[ 1'##xYׯ0 \X& ~}?8F۩wDUVM^Iہ%ؼ9o ē{.Ҷ-n=,s? uif֖Ÿ;a|qzN{=hE0@EzZZG][WVP[dIrln:/nd7N$k I!%O9=Sz;~"Z3<jox]/yW[KU;i 2yM:>?>S֐[K hPqFzkѵ=6]ZJm # ewj]w_5ټKwvnY%WU vo@[jvǡ^[_DԵ-y!>Hڣ=\_Sj亝W)l>> _w[vn*!@Ʀڏ#9>XiFn!T!H6 #y=kNa.{q-֧tnfN=ݓZ=wl4BdPY7 d>y}M뗚G4{r(%d(H%9uƣYX}yy%9^Ÿ8^%r>np\.rw&5墶G__?q3i[,$a2{z Q* H5kY>U<1 hY=u_UQ-@m없n8׭Io;{fZ!WpyǮ~y$yqwk]KRPZDVTѱ(=Hm'L3$@lᩴvS<-2l x}qW*{-_US㶔;`dzVrV,4I,, 677DǨ7x[j~7^>k+o=i~PY@k;F5@O5zt[iB[FYC6sr5Kƺ é) [*FnpyߊSMgUl(bBp\ֺWitɢaCR^ T#9\JƟvTӳZ-A-.tTo2(P=2+񅞩k-{}y֪E!Wv[9r?Z{"-CYm`ky,"7( 6TIHnbkZ%ԭ7vL%)U+Fڼtuvw9¨z)kBnh蚏ufA,6»+,E0ӢYmc $c מWMC2@@IC,d}s֏Egsi4{k}:(\*1ean~_6k"\#89,r:u5-OVחmGN08bA*~^GJ&c_pNGJdrd`zu>بV)'; kzѿZn*+98+lْmGPHU!獽z?r5ȎV0cC8##T<=cMvi66IfF\AT7Gz5CfL[+[[>YDQ G`~F/uyNҖf$a1[Q74d̞BQHػweGL|G&_d$ہ"m$瞃ުN\(M3DHU@Qqo|渏Rd) yo"IE1# 2ޤW?caYILK+c][*H"H3^ D1WQmg kb|E^fbvϠ*+KewO/ k$5^uOȃaa8Ty?ޯ:A*ZC<2*Kx%b6F*Ci+nGEvzo]!y㶱˰Ҩִiy2ڿzI6Ҿ`J@-Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@WjWhG.#^Ǻʼ? O{\ 3iζαbn+cGqL>]oSռ_&zmm┪+ IGH܏B+'?ȼQ\F51|spFLƠl<.?FkyCsvG,g|$I5wGg /D#%Ѕ&՛W3!2i> 7*u1!V~9Qi-SA{ }&]|7 |m[|4U/uk;)llc4gI!9,B Զ m#Z}BtJ\H9^:tG&8(-s5II kr#ghݪwJo<x]&[oMt$dZGi'̑jz Rߊ/nsc#67Q7lA "q܏B*LԵ+ N(Yrz6؎gGv@{kWLI=k2V0G⻎Y_@7֒V}_uë,:#"D%o) M7Þ7m?[wh;..Ѡ$79?3xo׺FjޮKZ4G#*/}RXN8#o~w^]yߏ'5=CN֠{>vI  %[=];X5%4-&UAod^G >s^e[IoEqmܡA%@/|=NK-o{\% 7nW9Ś;yw-|tP!`y?6y5[Bsjwk5EbF~=^"u֔ia0CH2jwOER[C/!OEiyae |rK: 8%N*jV tqyF8K|1}u{m[+[s F?,GVk> .{}V'/"*đ1 }4 dE(^X 1V4/;iZ\OK1Y9[)h>ﴈnl`me{8*èBTM>IhZFPմ,y[ co>ۭ:Y5ۨy-<[Pr`fCuoEꗐ}p\3dec{:fnԛ9Ns ͪ1Ԍ,y[}X;K[ԴG2Lu1ᯇm-;Pu $!K1mzLE&+{y/chg$9miyB}:կ ?FԴۉuk}&'M([dpŒ ݩt_5ټKuvnY%WU F7~4ݛڝqy׃>*5-z kky-HbF6A5zUŖKEzJr(O@q\CK}޻7n =ĪꠄƦڏ#9>XiFn T!H #y=kNa.{q-֧tnfN=ݓZ=wj4BdP̛2⎂y}M뗚G4{r(%d(H%9uƣYX}yyE%9^?Ao, >wܣc}9ϭ\o \]v yh?}i c+y&m> }>%_$C< RFOS֨o⍾V&k1H7 yF9g=0j#]cWfgKiPx;%2[~j_ 6՞[ D1jn84/0~D.5.MksQ62"@<]eAi8^=KEzJs  2}9+_PhXXlno$Q!a~o d&VYGg0P\cqn$Pb6'K+,\.>V3p@ [ӕSSіu_k#i86I'ˤY]KXMt<)\`9뻎2i|Wj廱6(+9'ⲿ4-+ȡ"Y1rd`p@N1O̺T]l-´ƪSq'ŚfI{m#c* ;7m5_ϪhЂ FsO=z<sq _Am+RvHqzԸ%OmA/p+ bA^Iߗca´>G q{{k1m%ɭg0T ^k:鑘E 1lc5icY5zމm\]3[qdsi:ӗiFkY-+mPEpAjOֳ┕Նbִqww=`X$  2@=k{e7Vy7D2#`N\^c૙/p,.w!zfC[B pc8=yU\iF2\b-wKb(nD9 !@nV#e.H@8ǵiZ<_˒ gvyv7WR~/% pYى${b,M)xeҴq+բYHWs#ʵr_0CqBy]1,ugY'd*Ǧ='5i:ke.n,/A$KI j䞞V~7ћ{[/y<^m"U>loZ@<-u[#[[>{Wx+kMBWی'$g=M_&aƺ⭣̹Kytw1?bj1h&cP6dd׎ÞM 5 7*u1!V~9>JK4ݲiДDf@]Ao ͫoa v[DN@5-[HVP \H<[}\;o_ Oÿid,X]>n❾U <ה$sM ] I6bc(r /-h-v߷q+3׾i>tmI-Г͑i|2G3}ǥ=ߊ/nqc#66L(}7Hw#Њ.5-fdJ.A?/w;?#<ݼ]E(,g|I?1Zspg;f~[yL|ZIZ/?O_j:׋aՖHt&T"SAݞIsͧxní%e4G7cf߃tsR.5׽]IėPhGT^$4#O_&,qcpF0..9(`^c<9MOGӭOC*( VzN5 o=M FɸUU"۸"Ѽ6su۫i-Ȯ"M(,TMnxGm426geK;ZB =Lv%~9xP2}N]4_9C"A͞MVо&jf`y~خ#_3O@GqW|{ƽ%Lju">d~X`GL)3R 'AmgǶTT~+^k0_CG-.aRέ0vAT_k5?ceZ{ɹY1qyF8K|1}u{m[+[s F?,GVMag:Lj4SwY\й+"Pr~_mu +}oXM= LLCHטS}>^Ğ+Zt&?R =ix~ďek v2.ݫ%}s|tMkE5ඖ(/C}k_5}6|h,T1sv! Hq܏B+ngIiK$Xǐǒsz`i|7Cbf77%k3䳹=I YwY]I2`suV{+&[(y.4dF|:%N{Ox7o9KSjq]oh1oMăִ|9/KI֯5 }"O4Q 5cJ[ijӾ5{D(|Þj:rNHA fK6gF|:%N{kĚ]pm$ qϵC?Me^JMXŰe @ː4hVA/poo2Pz=3CAdžHڣ=\_WZy.uum)[a=S\CK}޻7n =ĪꠄƦڏc1>XiFn T!H #y=kе3} A8nC~Dķ7zѹI;p bjzMH-- u*c#?Ztǟ_5 VfHlWz.6F |;>-WvZ4Gqzw[/,QQNFEMo7 )Qe$M3Eje9nf][{ͼjk;X.&YbU@8/E 7ݩZOypm?/͇v晤D}7 V:|rf9BWo4[/yKw-;t@,sUm'г:o-ƀ@2X ?uᏈsB{ }~ eK)܋(PH95i3O (Wu\r+< k iñI3|{2S] ak=ŕ mit v`89*WqV5iXOxYA:DA=ֱu)[[lcG5Ȧ MG(8:dz֍hLPG ;)a"rXH@aW$#lm_Bާ;MJŕ C>VPtSxnZYe~z nב[>tϲy6j1Oc.N>杴y{@P' rVٹ+s~5tِi+i^%3+ Ǩbp3y3?ǎkB㹽5y{ihA.< znp3^w;]]M<-#?Bڜd£o_Z GQj1EPEPS;TKYHd +4MP"M]|)5gS4@DI 0ݝ翥rb)_:(Զj O쒖ʏ5K2d{v.?X#"͜ qX/xfʗ@E3D|iwm8<=E_|/\[M6tN $ۡ\\tV-3B/KRivSpvt'#^{mZkj3v o@1lmս2[+q 60orC.wgkn_P̲ۧR@9RrSR88ۜou%pc[U4g2HMt>&u3}MspJ6G Gv)}/C5o_#G? ֬ k讋&Zt({y=" Rעk=zqi (yrQ3SoN{nbѶq=+!{ C s\c+UՕYX Zkۦ[v#v¼,xN\_/^(]1iWR dj+C|_O%efr1h`QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE^㡫^㡠W O{*2?5o{Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@s7?\)]=s7?\)ItR⒘GRB2Oғ_Ld$*?;l4(jZK(LB20Tdte'\4H$f*S+Kmlu:Qs{m qDzA KէПV <+G9uHmZz\=g ]CVI.+Fqwu$Ӻj]ݎ&Vy5]ZE=ɎYRL0_7 b9֙}k[dDN[zU M <1 E !S̭.S?j_j-CHd1H铻"nO%ݤs]sS 3M$U=Jz\jn۬Ɍ8ӭp'Zx.4I5)-cgDp-.$*(6{TMY Yܘo||Āy&PvKKyFYbX Z,L⦕{u{Cfs$y f[]ƽan6J`vC9q<6.%(Wv j',1S *۞k' 45{Wc̠vWE]28^K̟Y M.CAu8ñ70Z\)n%Y@X{z!knMF(fsr:z?u=LӬ=ݧu l 0vߑq݋"!PX\d }s!ho E\RLiwf])xM$.&A~ tj.zܷvAm,ȗ'2q=yZX~ i=Ŗge 2v*:wu Rvnye/-h;C1#0a>quyzؾ#8Q ~U-۫N m4dcpnd3qZԬR({eOU<- Yg(%Iwzm2k]CPtƞ6cQ,+gU:~ys$mbrѤUH89mlzkOqr4Jm`jZI1lO0v=<u,1ZտLrB_ 7CA(zTyR(Vv jq7h3]i( dP\ GjlKYfъ(& :54& :5~Q@v =|#3SS?(? 5EhA~o:٫e/'[Z56!71U>Me?rTV_D\Et_?2;@Oοil?'_'_6EKHg{(#w/ keݦb+1?4ψw[h'mOlsX~Yz`;집?Zt*[iVp5ݽGq2Fvp= |+_iW=Ւ#]PH62pNf>6hb <}j=SĶ6WP e}3ڴs_\ݵuٲ#I3H1l[-kGCQKڊ@83tԽKI$v3 3pGj6$J)l}2kյȺw.p^S*qz(OQoo.. mR1Np4ט?#Ne[tA"H`kKVm>Ζww(9ϱ ~wWP#X]ǘs1 PĚ4q_ \1 :dbfIKE[dRy& ,?60Fv#Zvgj/i) $c#9\]?SI"mB;Y؅.Xg_ j5+[(s0Fp_5}gJ4ͥZOCiÄ@sdxK [!StKO[9qX)o"m7K FjU89^ |ִ<3gçZ 5̬p2Ďj] uM~IIXfuq-Gnr|a{_4F1nhIE-Rq@QEQEAswol%\A 5q݊ϪZ!AVk+-"gnaҢk^-\~dC>yⱢ#^?K;HbE 6rG~u MIh{,iQ*k~^gIyy[Nk@k]ۻ O 3V/|SYiEsuqrm8!XG|c\}SP..!Hжsp xsWIE}Qu~z@66]]H#%.z+Սf,RZ, ̄}sJZʲ I*r #>rzg[Ki໽eԥNQ>* 'RfxM, {K+=J}NݞW#6⑸ۀs ߐ\-eɨq$ $#9U[amX5"4" 3>Fp8=z 𯆵_ iZm]bu%8pk>bOjw[-B!"rmʞ <{1ѵ/c dka pKuo\&)AXDgU@%Kg;A;Z|ZHf&G`G#㢖Zo麝;E%̳.@ `1Oҩj:em J] 5viYq [#n's?,rO4OdK(]eU]braҴ`+']}>+ 3'$?\ߍa֮t9p,2BuG~+Jcmm};Oi,#nb#n8JCL9C*^5++S=lRA* ۃm'}=qma .QE(((((((((((((((((((((+[eb ZL (Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@e?r앩Yz?뜿%r??2;]Oi?LtPg{D E{y=" Rᕿ{᫨;i#Kg "Vl9X_Ikrᐻ)nKVٽ eRj xx@GONgt Ad>i^ߙPfpE?/͟~$}|xcq\o~<+o_F<uAh?oܱ)8jnVm gc,?Efi0fD9+Piu[?;?CQ G \ ccy"\;4?Ep@zUk|4 I-vןa)|v8:+-ǵ, ao 3_?> G./}"{<TRFV"R7;\^;'>Ίzco^?į#MP3|ob:"hM_oK{(Q6mC/{(˫t組 WKn.X>v9LyRSMi1iw?^QIźLj/m6/^3.#9ڪwtkf9kv`{^=fN4KׂMG˕=Vo "=Sc^Ǻʾ oDT4OTГwꤶ-EbQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE2kju\?&QL((((((((((( 9JԬGB_9 k讋&Zt({y=" Rעk=zqi <-gz:\OoβU+\oP6X~ui׺ }g;y"Ac'$B+Zz>UC̹eA-lX1BScqa\ tMH ȢLb:ev:td k@x?0=pZwSyp0^s\Jn )B6Qt֚bhYsJNݡ4O `ȭ|XJH^C>}mwk˝v>7$T^ú\FIsq\Vc%,h:{pI+;Cm=mm#Mh6#98rըd›|+ Zz|7k%O Ϙw>J$0.gcmPx ܂;/#̐s9:#N۞u>a 820񎞕2z=/^kLPwu DdvҸ#Ek$ q>o^yϰ5)ȯMr|}%VKbIQjR:W|S 喼WkG,%tRgsI"d(#8c: *Sq~{Tt3-n@^xzkmTM0nn;v:ck McAV->rLzW¶C71*3;WoV6SW-_/);HC\c4>x­$%Uk񎢹moLh^$f9#]#kVӺ>4]R&C8%fڼ26w<ʷ ?>6o54ay}O,JImC W*̳lae'8sU-$uiPEU=*#s$ ޵[xb*;z;&rMIIxEbt[xRROO6~X=>U0zs?Cv%Sf``2zRt[&7Crqkپ{}z2^ [K*P@=I`yll-E6}˩$*B¸vO@tnt3%r"1wdV8&/[$>e[JsҴjTj;&H׭uÓ^>=`=_mFw-(ېxoy0Ta\Zot^{b6?*穯]F}8;t .9GUesvaor?n BFl;_>EzF{[KK щ9$N,V{ixͨi6z Z+]~$rL)G=+8Rm]VT&eX%Q't'Z=B[)qE x5B9ôwKs=+*lKA(Uz7ٝG 'Y-3%u?dTMH DZR8_$1oQvL`vⶐں"hĥr2kһ8dyQ6YwE,mϟ1ѽ }Ǵ/5X+/%A.p+}/=cq:.艗(؀(((((((((((((((((((((]oA?O[U2i06`QEQEQEQEQEQEQEQEQEQEQE^Ve?rTV_D\Et_?2;@Oοil?'_'_6EKHgzƛOKW7訂~''ҘQ^g\YE]]kWRDyF'MU?=%QZĶcOJzxW޿rm\tf+,g\uLks< m>ק+K*,T| OÅ 12*qǿ_9E巳1'9mŁ.3^᜾mp;lFk+μ+g4`,c#jA-5i]GrRrj{ 2jU/-z Z%G|4Ίz=997,%Xmy%K)89,nFi ,2x sν_Iѭu8k; |Iu(2縯"W;=,4%/i]' xP@g=qFZZZ5ʓL$L'$8'bjB H. ILѐ}zԒUBգkJr͘6PyUq>_ji$]gcVktm7͹C\iX8ٌF 2Tq xLo 2<0˻~qcfNk#$;/cL$ɐ<{`韲omk̼G 5V+ >NO׵zos/>V8ǕYæ,d}3Xt*RIs=YympJ|9f_TGUm{j09a"$r+$\#!SoВ?bjm.OXOZ elu^)~2ʄær?1cpr 0qU 4qGv<OWl;Oh-̷3$8˸;RerI<[kEgjizȢ5?y[5 Cƨ[j(3w>.TȌ'ԊLTCcUoǞ?~͚Mƕ{4(ܫ)<^ǩkA{t4w ζ!_jÜ㗮ǎG#!E7*/=uRWr_4 GE08DW8>ƽU%4k$Q)u@1־mu-|,|x): b`x,T1WEZR0{R3k?q߇ᥨ$?kkoc_k?q߇m4hc_k?q߇m4hc_k?q߇m4hc_k?q߇m4hc_k?q߇m4hc_k?q߇m4hc_k?q߇m4hc_k?q߇m4hc_k?q߇m4hc_k?q߇m4hc_k?q߇m4hc_k?q߇m4hc_k?q߇m4hc_k?q߇m4hc_k?q߇m4hc_k?q߇m4hc_g{h-eX!kޝ⵷1bI'Rh K_ްz?a?kE~g(ްz?a?kE~g(ްz?a?kE~g(ްz?a?kE~g(_] 0F68&<-XL[ Ѯ6 lp 3VO {|LN~lQYm? ?Em? ?E1V_uOGuO@V_uOGuO@V_uOGuO@V_uOGuO@V_uOGuO@V_uOGuO@V_uOGuO@V_uOGuO@V_uOGuO@V_uOGuO@V_uOGuO@^QC|`T'wOQG:$G!Oi?LtWESAZ_-sv~y?ޯ:A*Z_ ǼW~ N?-!{Z;H(%!cTwx"kSPOZRei~PL,O@=ťEh$˼ҙ0mId8nMfdb 3zs^s im[V- n\/8$%ž%*m$(Sq ~h7el dXcܚ?k_z4~>0r-c+28/簾2m[Og:WF*á-Z[VFfX;~`G=sI;XW6}ް^2wu ǮkE7ڍA|690>JnkPhZL%RKyNH+KLV4(Y>S"l:l'(>=k9I^롤#$dS&ޏPh| ۶#F,nc#rjԮ9#tۈ|O jn n WM˨PÃf57MEֈd~?*]HZIj<>TaØc dt^Gk߷xr7\+ӯ|Kk Q(ۊp}׎_Ky<3(cr3]pSlTw/iuczTCu m%X0^*ޱY&L n-1D'{tcS'FCjܸ@ Б5IkVk5Ţ+-^<1G>¸ݡ+3dE kW_ܽ힞Z% ,YN2$zΟvڎ Jc]026gЎkѧ~%n`k18޼U7Bk;^ VWT *@dfSQ3nG-W,#ӡXVَIlgǮ*kťnEl}^?z:K D&\09 לxw'G'0@ Mʚ9n E"DNOOֽcL[⏥կ">mZUPw6RWc+dщ|Qծ+-NI=Ojaœ\+0E@B{6~GT5XW7q@DgJTa'vYY3I|?ٛq$~5VAeHŗ$2ٯ('kuyxk[kTqDžBkcqpFѫl3 5t{F7Ymʍ#|Hӭ{2ucZU$kmG*wk1txO3Z4>KVIL3F c^EJz^lk˘繎ݤ6*p D[[fUc*)GK+5-O 5R oz w:WZrw2xzK+K0~!#>m\u8 O5C`G} E.Njv[j 8& bckvNfYՐI 70F}}+hݛ,K9"++au@" LP}մ)h# OJkk6h+o9ǖG3w՞eW2%Ċb8كxz=p*WĚ.9dSӱ_UZǬ?J"G72JXܱ}_.j6ifBz!xA4ZI[(Ey@q@˯\O/i[]b;L"X$y%IIpefxO)x;LWQu 3M)Wb@*p?!NW;\2Cˏ]aŭΎ5 /Yr[AksHr *ZtzfwtC ̮K c>uַ)pYGe>=dh&yL[lI'w@V`*H  _le\kVDui9Nje40]M5ʃbcmw_le u3VѠ >kapܥ[SӬ-{{Lqeub~)= {.?u!e.?]g6WYs@I> xUաmc6OVR~Vm ;E-.t9f`z\Ud]daGq^]OPΡw4sr\L!"Y7/ާl)>,yʏfȯ7OjGI-nƒ)qɑVu#)Zq"[%1w1zq,Qo ̚ɞ9">s}ʃilW?lV-c 6R8<qx6ֺ=<ה 18tOTk[??PD:NCyX~e8&jk^6QY[䗱f6pH<j#6Խ;,gn9u6NMnxtO2ǽ Mq;\$$zneQ@{yH νֱ=R!wc; x1evb\,.>m8QK]z;VUSXK9 vP O+=)n#QK @(JN}WgQ5}Ot˭֍6 f (Oc|GӦ9e 2#?L>6p^K5R8c8)dcL쥲FOrFjGw3&fr־xo_? }&Gjy} EPEPEPEPEPEPEPEPEP\_Lyv_3\mP ?wi\?[Yo.4!@Ōxm;YtT8_hǬ?_1/@ӡ#-򨃸cyi͆`rY<*y ҳƋ-oZ+鶺~/biU,̫<MuyC096ǦTm=o6o*8>snqA(8Uwy SIn SH)_ qF3Oö7Gٿwb85i{z=M)E@'y]۸>Ρ>|eeWN23;o}Ta[ ,bHg$T55U.0>PYgڽzI䚞»Fn8W>Fúo zʧ˺͓;Rpu$A*Ar*ٿ<VZi&aw=YRx˾PaO5NjgYЋl?/ ӧv_vfö7Bv}V{XVZI 1'MX-:r$siodkfrT` 뿰Qm=o6o*y]Fڍ{m>+[,3.YLV|r@'$~hTw7Ncs*$K&4Aq+ö7OGd2;dh7CKE2,"0Oa[ ;h&y?5Z;Q n$*[F#O-|ejv>!Rĝx©N3^m=o6o*;o}TN_]еyW7QmKHZ+eF q]'gHRdW$F}@?U;Hl]I$y&I$OXռWwQ\ =2?,W!A P3t~IŽmqw$sy@ # qל riW3i7/i?<3|.Uc=+ӿQm=o6o*Ylyǃ57D:nkX&,+K 5> Z<2& 6y9I3)+F{zlUvf⩦Yu9 pkJ.ڹO'jzC0|H/ cM1ΐ4UCgj| oN`G?مœתyu Ar*ٿs5 ;=wMӛIUk%$3]ptZ<5;{6]Ů VWqڤkö7GٿV/ojz-"j  ֻ;o}Ta[ IX;sJ٧6K 0]~_z1Bʠ´?}_u+5BCm %QE ( ( ( ( ( ( ( ( ( ( ( ( ( ( (8 k讋&Zt)5^uOȃaa8Ty?ޯ:A*ZCW4R'.X$>,q0H",{`Z'lBRq#[ݹOOCV2$)Ngv+Gф0Ҳ/VlZ/[F4O/'`0%Ft*g[?{zEj^۽|NPh>& +?:"$( G OY¿kʒZJC;?xGH]B[ektŽψu(b|1\Â3q{ڼj qR'a*W|ڝ^Uf}Gci)z_iKXdl{2[4rǴePHϭfibNYIgH29rA@=95YSSujDS =iWa2(F@H 烊`z5.^[#Dwx$]V0G}w e cW\h孥h8=jՍr$P⡶ҹeB׳~x>+K=6X'k`\(PN:ּVcqrNGAjߴbXgXV֛oa_A~ɿ|Qկ5Ko]QEQEQEQEQEQEQEQEQExS d1ݸ=?Jkp! ¶mⳫ𲣹5I~:Ԫqkhci- +DŽw |vHKPlo+AXG :;274}OO&Q[Wtf6.GA=gF|Ƶ%Vq,4hWbd^E23uk Z˖HBX};?%k%Mh+ rF>]Zmg|vl(ZTq~];$y&~ |Z-?!gwy?=v^Yy)\]@suTPœG??^EpH22_FMs&.(~~xЇ~=tsu4PšG k_?^Ey)\](710su4P›G{dЀasuP <C jM#h S~4 w邏vqOZ訦EPEPEPEPEPEPEPEPEP^i~&ЬnPq.o2ȣn'] >_# nsp#p1}Cjsk `*ڝ$a}siʢÑ5Q[QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEZax-d\rkx\=- HȳulAqvT{+a Q z`F8i[,``"ρ]K{vע^q6t)ԓl;݉Vnihh:4e4)hIDR1zWW"i6jL?QP 4P0G8Wb>ƷeRk^1* xútF-4C}LVfH B[LRE +s]oL|Mus=Orv nc ǨUW-oMZѬg:paװMFg1eJk9k 3(8rǘxOO\MqH%xdO%XCp>;:< |*HUdf[f_>)LWiOո&|EoXكڛ!Wv8(A@8E꼟~#ECҗ#˱ß_eİ$Ǻ]*1?tip?>=(e_\I?$_ Ee Cҗ>ujHJ8_LX.hzQ!OuGX.s2gChCҏJ^0SOt1ldNoM?>=(\//flW_|u=jVVRNz8CҚOq9&9|B͇౻26ٹیcʹ,|KQilJNACқ3~/|NQh3aΛ/ t:cU]ѡba)+צ}zQ!G.^?Qاu?\,CMIZl]؇b_0<(?Wcx$#a/+~=(0LQw;@L)4)+v l$,~נzQ!IcR]Qڗſ6prG˧K:W'|C(OpZ@u}zQ!Iq''ğrV$דuB/^eOEFbq>zQ!BkxWO+[;8UH'8~ugĿoiY] odc8~3WA!G؇]>$(ҭ68Ć-gt:uzQ!Q̻V='\ogu w.?J>=)>].?c*i;a&?:/oˣ۱,$9>?J>=(eiG&j#*I1I O 48wb}zR}̻VKgHKGOhwb}zRoλ%\Z?ܶ0?N~=wb}zS}Ýv8AGYeyLt|S4FXIu!G؇/f~)'2g62gC7M?~=(\/?%:M6Ruᯈ;MJ#U(bItb}zSPi'$"xNٴ{+va ['mq\-E֡OS/^!G؇7%3֑-QН:^k|Zڕ>_.CҏJ\>+|IϥZ(㦟)٩Zh0J>=(}Û3oOKTkRǟvlp7_ CҏJ9_pIs`Lotſہ?/^!G؇syt>-N$[n(٨>&F?M6\~{~=(o#·ſ-c:^IO?z؇br>]=o_M:_*$ kOsW!G؇.G9c?%`ĢT_idLW!OK@;S}̻Q>'Մx\efKhjKy=" Rפ{DW~ N?-xSFI#1WH,̶-. rGF}.`Gstc^_5{s[I+qtZN95 mmf)\evݸ1g9qђ\;] 2(ٳ'?/.ܖ'$C|8 tEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQESJJ 309 271 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?U4,l @ UsX֟?Aw]rag} ]k`6l?B~G_l?B!XktXH|ACVwEmkO{'$Zވ"5MݴAWu6Ǩkq2]1m$ElGOj"qRv#4u:xG_k[F{^Y ÿޛzɫG{OVCn۟zXƵsp+.©srR.xhOֿnhG~#}G~sEs?Z>ۚNrhOֿnh.~%AKi:MA4grKu>AUkz7zީusnKC<$G<W5X.~+Xa5+ۘN觹Y#O``;"UI`!=H{U:Y/ B/.u\,R,EX&%W|C?VZiNjX%O3mAyr&uw 5tDMf\,\K |cdȥP-u_nh|k+n5>&]4qOtv;m* šϫj~ FHгww¨RN=3&O'>-h2mY!W~Y/` QaO=17gþIդ>!&uO,S[ޅ$Y9<`~ֲ4ɝ6_i bKhMrA_5[-eMV\o{ı)8۸ R}g g]^T)V]JN[H;H\(*1'o\gTǣ&U4=,EF D>nFPe0AGo;_xFQۉ1q81wm7&3iE?+.PwA/<4] vj.cYBODNQw0'y35i&J2xR[Z}5j{ Lgύ>% +k{j+"ǘ"P$gw*~5;CּBԞa]J[͐!@K1s+t^gZj6_xMK ; O o ]Bq#k/DOMϽs~u8mr;Ք ` Epd/c-EXjSWsedQ5?/^I r?5M@?6@wCɧio]{$ߟo>/?/@Mo:z D΍pAq] |`ױŎJuk sk.Qo@|85C.uxP}g9ߓcX&~x7\[."V(v{kh?hsK5%[&dd78V׆|_xSwukN-Zc3jQ»|~b8mm=H,y3\]e_4ƥGBw \"K0t/'$9.9YD v]7dk}ҩkzŧkRd'CDE,ğ` |nD<nɦj7{˝)5 ԺZ'c#y1$N~׿W,uSZR]jSIGp%d AA8&KNowIy~֟fwS _ٚaBȬ6VڬH Yᯍ񖫣YZ:WRèEζw "#ێ=ql򿴊(OiQ&5NS6c/um#O"IfF@pYCƛj7~.ǸQ_|9:gk{ųICAF pDIvT*cWi\gA5-jX&ӄ" 2 qqRQ Z)tz BٟK״k[_E֏[e-RX9Fޏ<ȮFvlqZRw (Š((%Ek#oM뒍꧱z\]ӡNT3[͚WjH;$ 0r&M+@岳Huӱ/#.rvǶj{iWZծ>g6io$$#r R֬fY.o4Ǟ+kK?1D3 <`|●|ucs~ lo)By-(Ֆ+^ŨFѬ"VM ,Hzݏۯ 7W5*AtV8SCnwn5 ;kO imt+mН:tK}偷iMX-N84 hPZıEP=;GǺnoEXM'zUŃI &3YJC뎕|},6 /_k{/.EoeU*|Il3sѮCa Ũ*Hy|p2[$ԺG4N=7OiinD(sŽdKewn$av y5/,<9˥å^AwostR7"GE'2!RKi/MxRUdhwcǨ~A7MOVk>.{e{Zd!v*VGW)ϫ:f>i֚5 ,L= u -ΏgH9  N1Fkq"s߇ƙ Ν)K55i4نw\F9Fpk'gl WVj^,.؛i3]r#?"HF:!? I>A&l"6I,HnrIK͞=+IIKXaf%TE@F1?61Iox?z|A{.U16JttGe UWbqڷ`x?>!3U>Zέ:#Eo 3H s< W#g=߿BoW|;-SV,Dn&̹"nk 0f}}Sԯ4F(.G!r2p{זڳ&T:/{ߴM2'`X;Ϗ>WOo=cVmwnU@_*Ųݧ8:{xn4tT)nXQca/8;qSOCӭ {MO&{ȡDK_ ݛ$4oz4! n-d~I2GQ޼ZHtFȴxKHL%ɴc-1b2d]mjP_L'~ <3=6;uv@2qx~"-~ӴnYL:~0BI ej[ n׿@ZǶ_,i_7/h?dn{O]7zH,qHz$'2Rg?Z毧XV5i$33L@~PNx π|E x.IYj:tL1Ȓc{ Fִvږmi)Ayg2 u$0ҷo ~G̺ [Z>-NJ6[XOܲ笰ss)d`cx_a{h>,Yro U[V,%@K UsZWį k^XF&;u@ծo5 L\oi,+NXp;7_ğtľ|j]kyNZ(i-rQr3ZMZJ֊ߑ||?u Bq ՞VW@'5x'UuO0f,lmYe"13 eI<4}_heO3t? m >aom9gPbXuU FA"O3 V:ޟ.pByQ$h>ffbk-K捫OerɫX\_ב;F҂ &$=q?~O {B5K,iWvvHAhVtp^Up(^ֲ5 ZG^kUe?W*]qu>Y.wm{< IThV>m)K|IN,j4i4OrM n㉷$.G&Og<GǴgS-HB ݷ9.9~?i%,.3nfN.XnLKNfT6ԿFs;oxJWo^קXI[sF F>7ȣ=~+ nxYѯO[-=g6ɵcb[$AR{)>xr@Ҿ]?&7]<]%φ$}"{5mKLM6=R\9ThV$ӼM1/[Io9A#ϔA&+wf;ZJ' $קvKo$QEIAEPEPEPEPXچjh[ Y//C46)F;Xx,23\xĚ֠ysХ DlcUBCgdKxbKq\iīi[yR1‡p7F@N:@Oi!V/Oa~ySQxga{y羶%)VB<;1^w_J~{qxK^I%ڴhJ0L: o?ɿ̤~i~Gnuwiyu/5V=fْ ]rxkN7 ;h֞oODne-5/?j6\hZ % . G 94ox;FK?5E? >=I4ZZ2a՜Y]T6@8W6_i>>{[mƄ#2N+ -惷ᯈׇQkրBF!sJE~?.~Ӿ!G_^{+Fـ򥗕7QPn1*׋t~*6 ^M1iqu '̸A2I6 }woxs6o_Amm.;F%}5ZÚ?oxBw\b0%M녖dv<0}qw'Kgy`ĞM݂|Ǐ>ƵO?~/M44X2gd۾gn7`↻vzjvzO;_<1ydL> t+q}KX8md!Pgm״.5ZKo#-f2VRYr |a_"ҴNOoͻKkFXUm8L/,s\x:Znr|5,0\Li?flcFv_;NJ?/~W}Ay#ub>tK襸e@];Uh)񶭡]T[!\4?a'KMDF/aqm![Vw!A(NqLx@вvm֯f{%HM!_+C@ٟgZj:eF%U)PGRC ['஡j -wV%"l}Ō |WmM6;QR0(((((e 0@#-GWMhq,gʷ3$ 22y i0zufZkv-.-tc# GzӠ( x4EPEPE!;A'?[ Zڣ./;i"x!#S$`L@)QEW~x|9",#vm?׌j%}c)9e `y…m$#}fYahd9D8lU?4 S=b-6kٿ8ZDQ,j{)kt X_jQ5݂{gU8|uo|!_ěKoO֮{$jNJqS+|y[W$^&\]:Ja+gn SŝS־='7:fc.,]j_kI7%QcAĜסꟳ-i4ŧ%RÔYb l9U$𞗫W/3ѴwGϞ6[C'NҢ5 6<ִYR{ĜdY1N0k/YЮ[/U6"Vmp 0\zg9 ui:ZO4*XˏOwVz|AsXdgRGKѫ~~⿍o{Iv|E>vC"8PP9d5}WsE^+7xٔ( Ie =o><%fzu]:3(lIJz/q4Ҽ=iGyGm4U,s {kOЛ;%moS z_<; O]xݴ+{mMBaՕvH5k^% ;1\Я4 ~It;xbadg~;2~힃 wT}t%2s;_ך~ih?ayC:Fݙ7*s2Y?'-nZ$'TSB /w_XF(#'?lA97 Z01ǣʚp\@WҝModycs&<,.&:n@?Z4 /[4Q݋zcOր><.'~1֬/5-. Zt[xT21q.vrvM5Ѕ$Q\ϋastt+֡ ɧIm\ x4$6?"($FҢ襀')QEQEQEQEVdZơY&Y#X]8AIzcƝ`x.ƪN j0>=*Z\fS,pܣ.HL8g_O. c s>+{¤ds}b?nkzw4OVT'a%{JK-$ϕ`W}VmH\1Fؤ uIZ@H]F9Z~>oxžۻk Aw'ŢK\l{y#S`#@|\yoż<x千+w+W/ynJ6/~3A`M/R'k4gK˷&?,v׊l=ODc?;e}oĚl&a*\(xjXw5ڜo|NT{OMMk4>&^ 7 AnWе xCү'4u䶓~YbeuZJ\ӯgm%ë:|i#UM-^b}}'Ehw:ѹ^l(r5u|fҼ}-NPo4xN\ M)ive@_k|>C߆ZHno#Hgյ; 2GNAw;A,Mvk%g4|Gx]|a7Y?{3ǁ^HHNSpAn /}°x]W.eB-SrOq `˅c:(翝Ky[ x3 +\4k gZռ9-Mwp᝱pZNz)x_1]ιۍH#cp +z t;)]f ~e2  xТmnW⏎ M牠׵cAgq588*vYHw7YQ]NU R( ( ( |%kWBI>ОbR@ ?lQ@+k[i"q"+pOPFkٗyHĬ<=xDewLpGzX[j7WGuiqE4da$bJhY&|(O^^_^"KY[ByV'"7u-Cź.iR][9Y H B]qr~hB[/-~W6dCȾ 3\z/Z}{T"|FOn |xVBUPtwx-y:ށ[RB(dKDGm?ֹ W_~? MDo|h}6)mb'qDbw'g|/ot[3EENӌmmeg8 YT=Si+4/y|Km3᧋wj)ú~m͜n#)Gww9VwŏڿdךE u(dQ汕>}] }!mwz6à[Ghڏgow^~dsv5W_&UvyIN&gB 1aH `JmOW}' |1y#UֵH2L#RYElM<׏_NKx{%ZĺsY$i *@Aɯ|g?Dm\A2$Ep FO#ֹv>C> ҆"COcqϡZ_O)'toY,MgVx>aVH)CwS~&_x^<Ý>*lgүiu5+(V%X&F;UCq5[A)%K͋KkD#|W6Ij^^qYLl`]T4]Q_6>)_:oCKwv)kq, щ=TrS-3D<.|C% ^[YMh~_XUuzEi ![vAsE*H; !sُ:_ol|Aeu+?\}T͎m[#!UG6J:?z-?k>Yn|;_[K/kxdǭ^\Gݏy8$5v'úVaek}ስ TAy7*B.H9Qsk^|)qc{.76=N+KWk!&קGEVeQ@]^iY[vqB~{AUm(Ϳ9?m(Ϳ9?m(Ϳ9?m(Ϳ9?m(Ϳ9?m(Ϳ9?m(Ϳ9?m(Ϳ9?m(Ϳ9?m(Ϳ9?m՛y >O@9|M3fyM6iv>Ph@ ٷw7GͿ9wϤ?0د/~x2.д/AxRn{yiZ\yݲ˴c '|Ds3ASYbeI'J.eDVi/O0n4k^([N{ĿgU%kyb b9Sl]$i&1ڽm;K*EMS\ >cg[>a*Ym9W9cK7_.ӛ gO%Eƕ=g(m` LepɸlE5֧Zu,du"&0Ju8+b|5?w7?EQH(((((((((;dTE"daA+ o?_xw6?_xw6(oo4=ȱwl`g֭Efotoxx-20.08/images/gallery-view.jpg000066400000000000000000003644111362435004500174430ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0232Ơ0100Fotoxx:resize|trim/rotate|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 952 1214 0 C     C   (" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?8hkUjڦ}U=J7}* .}oj{?T}( j{?Gڦ}U=J7}(ڦ}QUOwҍJT}oSwҀ.}oj{?T}( j{?Gڦ}U=J7}(ڦ}QUOwҍJT}oSwҀ.}oj{?T}( j{?Gڦ}U=J7}(ڦ}QUOwҍJ礟j{?_R~~-~3kCgس_h]Ǻ~g_[ߍxǗwRwgwog${~7oo~_ޱGj{?Gڦ}WS'O tjnXmy-m-GSu_+iEO^>4 dkoj_|gT}o}mw~v #ֵ[f&VwG'fkjk^KxwO7QM ^4lek寓oPojzI}W_U)Hvfܻ;H~.|PFjڿd/̻cڻ;_Q> T>7ĵKͮiM|6닉kFe_Һ-Kv6z ۝R6潓˚6Vf]ۗu;kow_Oڦ}QUǍf,t7Iv·R\}W'ު>1gߊA]j~Eͭ֋ |+_GWvZ_q?-ċ4͵Uw33TڄZy5w6w Z9#oݯIk )%oIچ KOkZeڿzfW/>1ľq}6u.>вmUi~j>7hr_֨ n.[[}tﻹo}|ccA|;|bk;8_3wͶE]ͷY<+q2-Դ]'CԦ]JTZjC>=[JffimJVѤ=FIrm2_-}gÿ] ]_ âGro 5iúOo|7RI >'4{.=k7zk%^2̲nU?/ڦ}QUŏ֟i .73H߮.O1ǵ-nxUn( ֓Tu KņHa#dUe.[wHgj{?Gڦ}W^Ծ xWkx2P^xeu+\[ȫ>f$3y7kKu֋oNmg#$ojy-f~ozmCw}24VѴy_ZKִs<2G};Ik\Lյ-Y[L,qeXWszc╾Լx~{ rj]p--ǻl?UmO|T}o}mNjM}x=R7I5jЬw̪W/p_|5J ]RmS]P8.\-m~o3j3?M=Sg-SτmfUfgjim[9u/6ݾ_-jxK+[V]>=a_ &%YomVUmi0>OT>7foK鮤{J=.]uK;fh6c_nV_ d^;{\ɮ/c#N-G-RZ${>WT>7꽻Ұ]7㦽m伸e̙T/Wwck[Aҷ'j^ Y𶊱³L*ѫP9]]\G lHc-Uk-SAk=JO_mj;Il?klPvwaf⬟~ǻ]cǗ~y+] H5m.գFfmyR71Lnk3C+ym"VXjipOow4 7l[Ҹ7K~:Ks?6jj,ve+^$ZII.7>|+nZ:}R99[# 7^$<?C//5[rH77}atwSgTO/>jzGKo&;[}6wu}īu/cZ\VuWk2n_1'yo'V-b/c;㷏̙VeZ{>Ko-Y|[xţi?w6W[?VZ,smu$kplUfeV7۾%}-yڦ}QUƥGྜྷjk|7iMeܼ_9oaY>ѷwfv~lMgh1^Zɫj:^[]ʻ5:-R2W|~7ꏵM=4?75&ڼk\έvߊ߳|][7Om#LFU|[vW(_`|kT}o}Y| t ^|[^-*帆]7N@˟j{?Gڦ}U=J7}(ڦ}QUOwҍJT}oSwҀ.}oj{?T}( j{?Gڦ}U=J7}(ڦ}QUOwҍJT}oSwҀ.}oj{?T}+KCm{ƆVcXy3+7s~7ꏵM=kPom[jM̱6j\T>7e9g~'olkF{X&7o~ɷ|vh'SgxTvrUf\^-/~wn#7?{ ye[(”Vϸ쎆iNuZ{.)Vܿ ek oUFnom*}oY8<]L:]3FOKu I">7实nUZ־x_et yom{-]JiC1*rr3}~|cSZºL=N]I#22ZZ}gNJ4Gt'SE۫/j+۫G}Ao_Ꚗk=+17٪?2=n›/ / m?Q1G o*)tˏ_2o[I 'F~eVK[ky'ͻY 7t[qAi6__x=adI 6jӾAc[}JOٴV«k2ۯޑcf_;1|)F{n>}uH[Z* /Kcw}I m\.xoAmQçw,hᅙW/}3>O'KA߆u Uf]&h_oZ/xWg8̱ɷw677|ŶwV><{ˋ64{Uo26wrַ)_7@{_B_7G)WCM=oۏx{Oռ3IMᕺګvڿV֏ީ ՟|5X[uY~_Yc5~7VVktE#*̫.*I$^w"r52uU_B?t|WIڿ'Wvmw7C[]խyikxn ᣎo~ \Hla<gڤnޏ5}*.7t?:5ȹVH /)Et= twk_Y5@p_|˵{6]i}s-24I34jWU !~<'wM_MY]F$ov\CGVmrZ9u,/'hKi6yw/vX\Gqo4\Fۣ6~›  mB_7G0Rx^XVZԛV]]I/nZ|PQ?5ƺZ8mJmʭ[w+_B_7I o*)tl("ֿV][v]7n ω>,9.Q\jj3*ʿ7W | kȾ%VU_:񇄾KMԯ2Co-I42jh_K񆥦i,yk-ծw#IlwU{Ak^*ԥX$fro˶kY$jm>gGmw 5taKyt_HٖES,@Z5k庳ɵn$-m_V Fl۪^2u߾em|3+|5~›  m/)ЫR9կoHI"7s|\o>&k[זo?ۤ!VYU|} o))tž  m~/÷ZusI%nX㑛Vo\ZXV/6?{qu᭞c+呭m&Zoc;R?g fSommݠ;WGq_O%Ԍ,վoovZX֣+Xǯu#}/t?PxI mI爮 xN{T#ԯg%đդfX?_g?ơT|ۼ $s6M<BO?So]׋u#5 iKhm)\uKi ymv*Г4  >MW/&i7׳[iMѭ8nگmՇM6"Ve[<۷2W*Г4  >MP>(k^(7y$K>sZ f$77U7KYuIu+uEeo4ۗW*Г4  >M@qZXma5AufڂI7ݺ*"]Zmo̺n$pޑw|*A?|9&T'ß)h si?174?gfnt׍>Jבw^mԍ_|2U $s6M<BO?So^kË2:[!?5W5 {{{뛛{} ṟm͵u~  >MG*Г4OxTך+A˅iM@x$j-$GpגniW'-N׈#3IpH37i>oޯT'ß)hA?|9&?h_i<#e՟}֡iվ}V|[&ZjۖRy*mOT'ß)hA?|9&? uj#h[V/56v 7]YI m?Px}oޏT'ß)hA?|9&?Ѿz?PxI mFA?|9&T'ß)h_}I m?Px}oޏT'ß)hA?|9&?Ѿz?PxI mFA?|9&T'ß)h_}I m?Px}oޏT'ß)hA?|9&?Ѿz?PxI m]FC^v*̻I m?PxjУǘB~ ^I{q$#nfoOT'ß)hVFnRgᯄ>*b|.jX_gK.ߛmv_߃0k1bi.w/ÿnU,on]&͔bVJ&O/Q5ΕBm֬|UxmSz [j +|bVvש~Қ6>8Oyù~hʵ& vIkPqO򲪲K^c*N1n3՗/}uf?r7mjC516ٍYTo>xZxu]7^̖g$+oqo|Fʿƻπq?EpLL*Wvϰ\A}<5G>,5ֵFNZ躄{Y~啶j|COZ,5皿4ѭS|Yƿ4]WGoo>=Ь4;UU'ڇ-~&s&yk\Iforޤә$o"mm ~j~/34G^ ~`[Uϸ[ 6yީ5j$6n\x7ʫج;wcY#hoG&ߚɷ`o|z_+g^^dּAu4W1l3B̿ĭ5mI7oͻn` ~j ~> eu|ZrkV7-[K r7}~s%񅎡aJRRK?yO+m}- 73>S ?eDžw^WTU|Ͷ}?yyŻSh{ol|K65~ -q#mw&fVmJF&]Imf̿y]:F,`4ռk[dK}ͷk|]ݫ 4dZlDc[|@2=N*{~jn&hnʌycc]S[Vf֗(#UOY#ɻj?ߚ_um.sKR+k5SMk$XmG.? 揰\A _A^ͺÿj>'%Ʃ%ևs:tƷەck֧<x>o<5ycȷdK}ɧ$-?gW} ~h)ۦo&kq{m|?sg$7+'yUwm_^goKgH3uI]>9/wƪc|m 77FA|GxzIC~|4Ϳ봟1?AiY6V 6e+訾>EQ}?}?-ڣ;Gڣ;@Q}?}?-ڣ;Gڣ;@Q}?}?-ڣ;Gڣ;@Q}?}?-ڣ;Gڣ;@Q}?}?-ڣ;Gڣ;@Q}?}?-ڣ;Gڣ;@Q}?}?-ڣ;Gڣ;@Q}?}?-ڣ;Gڣ;@Q}?}?-ڣ;Gڣ;@Q}?}?-ڣ;Gڣ;@Q}?}?-ڣ;Gڣ;@Q}?}?-ڣ;Gڣ;@Q}?}?-ڣ;Gڣ;@Q}?}?-ڣ;E~L7v/ i:N.Mq$}UUwwj|Mu$W^d\H3.Y)u W_M2j&?S|;Z˻w]ۿe[4(C>O_@2n3mݿQ'_ o4c-REaV]+6VU|How={oFK6E}YU[Z 45O+^ﶏ/6dOc:Ŷå7rm]]-UFmت Wsy?k:ğ<%,u?٭h՗˓oͷk7̵o>$w>$?k:ְ̺}mmZ_񾹩hu][MKiIͷ_myooqo/KZ[MF_ĵsha|O}?_z}^`gNJ-tIҮȷo7m_Zߗwޯnd6V54֯p&k.O3k.Y?`_7S46!Vt2F y4{snVYJ;^Euϸҹ}cׄ=?:j OVi>_ WWy+_Z[Y弍$Q}U~i6F{W宫OmP]hZo3Y[upG$*vhUYW L}}TLʲ}?W]j՟.7IdogZ- |I\eǜp~f]w33-s?"RmR;KIiyu4qùvUUo"ϫ>M7^jmΡw[/++W7߰Fʷږ].e[u6ݤzOW˺MVi>ݫj:MkMѵ]JEmu(-ֲ2'Z7w쵬xgKm?Cկ4B~{o5>mwoQWigxH柬]iUd<1Ъ|~7D/?/Ś{Z~e$Y[9U7l%Fmϝ_-|]SV^ odaG=2Fo_Tm~ϮZKxIw~Z-Fݷrۙf<'9աtEMjGG'sCyvV]We|K>zkiL:=MqyPcGq$42/35nɮ$յoiz|{VVo8Mnv$.9vNSn||[#Rt7\մNkO-&֪ƪ2/jV ^ ҵ ?70L If۾ۛv֤'Zoiwv6,qfo sVWFCjʷ i6/w̻ޯ31k_ Ŝ"V[hY|3ڻw|Woo?a[/.j~$m>mYE5喨kn˻r¾c2?>WͷެKnچAY[I|ٕ_(o4MCG.ov|yϋ).dwZXMEiFw]_4WZfy_7m|VMZ/夾K;K|Q/4ȼ?u4M7d~fit7R/tI<-m{~$_׾^۫jWsmq}M$͗v֓[nV_]Oٻ%›KRɺMc%+Cehoe~Z_?xOouqCj|L~cHcv/ͺ9#o}_oX^ -[HP'YiaY$mۚh?1[檷ǃag %&.-ۛvI-mcvf{wn}ڣ_{w|~Zz{Kii]|Mo.Fԣv/w{hmS^ѴB;k;8Zy[5]^kizw6wۖHw+-|kLֵyqi fmOylgvᇼ@ȵLö6sX鱴q$7c\[$/DATj7kkgqخE珼9m"Znkxc5O9cy{]uk> ŷՋ[O,cG=6d<:e-*I%wҴv5kj_|;x-|]{Zxe]RY?r[w[r?y᛭?VV7=cY46̰t+nfi6ߺʻZcEU ~֋~EHys4Zݬ^?\/M>Fkf,afXdܱnehm?&tgqח-4#yl$7m7[Jǣx7?kA>٦$+FhhYUm}/_3Oh2Q7Zm7n%fۗl,[n}WͷmWz?ei~4ċ hoETkdGb_ψO>jGO5UǘV_A'>.OUNj|Igy#GnmWs*k|Agiz}+qos6]ĵf돉6 loXdpɻo6nަӞ M=o}R >ͦgMHʱ̫ZtVO7eft5h',fiF^カ/ׅh%-mneX~ƿ/nw*mgwUi>,xV;YˡɺO|vFܿ/U}|_.^5/5bKgVk4#f=kZxrkA/iz,w.I}&C#iW3ˍWlrx$}:3j\]ɶ8_aAVmo*p/>a7mM᤟KhHYr_%^Vği7׋h_.Km k[_|;K[h\-]ɷΙk|og^-o|ymC\\[NI&ZMʶ|fVTZ;U,3G4*ʿٚ?~o="^Ğ OXZFmU/u35kGuZo5֡&8|Wwmq]||-gP[Y49>$Yhk5[H'e .i6Y;#2h֗S[HG/)')kz$OiQChmf[źemec~ }KZ^iz}#Cc| _Fw7k8b,5]垟wn67ƾLݒ9_]/_ w2/Uż1 rn_%on?j?|\k(BfψkE񞋥K[twM&5afdo+3F+mzm[A٨v:{6|oc/H]a[kF[|fn?k/,m֟')(~sK/|~ 54B]M7,t۝jHu[P|3FޓYrկ^|u/|'uyk E?Oho.ۗw5o_7Jņ,v_7G'v!_CzVj^l!iXM7#G ǷljnoN3S~ }f6XRZ2[tjc2_O 6nocY'9> R7 YKR PD^4SJ5uq䲷Ѭ?.3m_x%X[/ ĖԚݢG5ž,-'/\iQmowV6n}kms,2ǍRTV>(-ÖG:i,G&mK+F.kn>|d_ڶ;\ˎK{$h$i!v$~}m_Ni?nOȶ~GheAfkܵ?Ǟ6ī]i+<[lO3l̳mj{}-%&~#R2rCy$rM#F۷.6_~I|xݿv?fCtxK[wԌۛrfD?a(no.|x2eLٯOO>3O[iVeZoeʵM^O8k|I׿%ib7󿆾n楨fSOP..4epƱvUuh\Z_>),մxV{h$ZO9|WkGchm]|M׿%ci;^ߘ^<+A<5Vl^Ig5IFHyBxX6}?PocԦ8:n_ﶮUsnoz4|EMUo~&O^27,O|)|)o|;mCXtl "+pd>syjr4Wxψ,&|Dl|K+]6\~[yOI clcj?[^&o7s|}į179!M\-8t5YC ʪ|4>#-u?PV-+.v/1-x~cj,&?MW9jc}.$/UVn*i>&u\j9>(k q5.tr|3¶&Vi..uJݡsZ~uvck ~)x}'e4ך Okyq$vmQ˹d|ϗr)NMZ_nq4^7/:o~(~N[|]oם'ږ?i"5|c\կu "R_}Ɔ8ew˵[jߕ|^/gT%{Mg'.xF5 [|3&kMunI ~s+y{fUܻQ_|Ly?xᾟi"\Ry,cd[xf#mʻ^s"_IgUԬnuvmOP8#w26]۾/ЍCğwοy15|Pd^[[ʛ[s/DrSfǞԴ=kJ#mk-BF4OyLۤoy.kZCPomTU[aw?R_8Uӛǘ;,_y_j;uB_}-}}%揦;nkx䑿e]ԥq|'gj>yk_:/nj5 zwzܲM5"-+4{ZH:z7Civ껙WkϾ|@ܳӢ,u+iHkw}ڮ~!l{mBM׮uYeſy~UeUUܿĵپ}[o埂z7č7{t-Omzi۵o]Ż}kÞ>Gߎ-}lfVeiЫ,_}2Y IQ>o{#OĨYqTl(c]ǖ|fwm~OQz.qAk^Ic=5dX~_$ȻWG4U%D5gm)}y/ϊ|cs]SEeٮ/!$mY>]߻UOK/^ {gԦTe}o%i_ݬ{vvM]j9)M/WCO|M{ſ>(- LM֟Ͳ^.3M5k$ʱ8۹Yʵ_~%K? Fڶ_G~MéBs_7VڭE_mRmkP׵K[h 6Uj }Xg_?hC\-ZXXi4y$:;uݷtk۾}g^MqMs 7֑~k_?&K10yYC |@͡~%j̺N3Cwtr++.+}a M-P'UoZԮYZE_1a)H?i/tŋke]zi&}bkdI-cUM/^oa  zڶg I2nVSC#y]2ԙ5$,_~E{o^?%Yy&-Ǎ/q״tac֡mSPSi+G3*m*]kKσ0|/>h-tSX=c̎5o3Fnݷ_}BpV_v5'w֡V-/&|vo浇L$p˺'s7ͺkv&7K4ayrOVh~>/{/P 'ە/l2*O-/ޯ)Ɵ&Դ8|MoI ڲ5MnCҾo3rW+5 AqZV?2/K𖹬ԛ^RhUO;Ya3Mm ߲%׋o'ʳ764ydڪʻ菳7/>o?Z7UΏBG>H_o[Xm8c5#]G#y͵UZ?oVZ$oo'mGqVdeܑ7ޯ֯'kAE&?n>L._zG~ׇ5-iA KaUql~oZvԝnsXWPUeڼ,.xr0X~E5nH%gO[*פM9Ƥyks{O5F&߻Ze. Ŝ*eX4df߽Rm!SjՈl[s5e*4d:ZtvMU,&n6KhK_EV5o)~qgoqp߼i|Uۻn5+X-ZF{jm_k!&o7wnkhpJ$oVhm.uu5ykj4eVw*U'~XUUve&1?jOdS}EZEٷH?5p>5-[Mִ"w,Y|.徳9%մfWɶfVXߚ'o? 9[[Z 9[o1[w~j#t,~8Yh}ilvV&ׂ7wZ6fY{7v?nZtг+4Y~Z;/nZٛ ]CWGoZKf_hڱ|;s>!m/Uq5iEfmwf#JZcj?.mWe3 >( ,c[t{#*ԑ[ՠ;T3̑jV[כYwUO4e;9Y4jOҏO-7= ܭF[oٮCX=.#~h⮓o#F{m%UtY~Z{gffoZ"yRBi~2uemrY+|Ƽ/o3KvzohrGku<%~Pk|}֨減ozO.K=UjG%ƟVǻʌya: P˱~Qk՟̃wS~.Xj&⪭oyo~.#;+FV׀[/ NZHcm=oɺOi,mje7*扗}mf0+nLIn+ٿ7ďω{Kд{vHޯpiư)+Ś~/\5𝕿?%/7^Lx_ۋݣr;Wɷ6ܻkjҔЫFyjHl-I%ǛݶN٬SōoʭW4ՏB7nS^<-wU?Uͥmk→Y)?\SF3/w̒E H̫kƪ|\>&mYw7/B Hfo_iC _⑤X/2=BnY{}5rN]dVXmk#xn/RKqEh_U4g_wn7VK/]TR\dь~1Z摥鷓InVo_.,//!X[%k i2Z$#5m>mVfv)}է){I>Vej7 qʻs?yWoޮvYmm=ʿŶ}8nYZ5_8e"n?nUWr_|-WgQO6OXͫyݮg%_37ʿ_y*,x8ʷEU֦qRhu)A+ĎX} _hL#Uk={MEڗi Hʿiocۺo?j}[iWyaGz Tn?+o vijm[f]y%AnmbKڿ¿ze~|Q>?h{mjjK"r/o~~O& [W//./fH6ߖ.kl1VgucKHZE-Tc(ĉr^5֨jg!]j[|__Ko&VYsV__XM2sK$gxj{{ ),2Ӝ5z;4vkgiW#ϼ[{vp׌y,%+Քe#扞w5ex4l[A5u=B[^~õV?n5GTX.$d8mIi٭ݵ_z^Q~|Ghi,/UUԍa*#Rm7POf_/˛k2hxg㞡]G;vWOk֎*~(PzYjmVM35mzO;;>KH[v*麾u3 ݺLsǸGir7kJVڻf_^oݪ|ZQU"o7r$}ej%>wu|lK{[vm۫2du捷/-e|1mk7 /~4Þi$[H.gmlx?Դ=k]Z-j6 fߗ^U;ipj[*6msm?K3K>]|\(ʏtSNMSvzƃofE3y-ww2UoF]:KXIlҲ-[[^m`𬒪._PQmڵ,Q±Gho9UC%~\kYƹ" 2f]cO~$Y?wnfelڿ3Vz\y̻f~|H'|Q/#xNj7ڻVS'<)g6da_1eUWZ>c& Xci7t]_yvg."mćIԭ59Y~gxR;uVk[?v| Zm|Uyif_Wqڕlmo45=J=?koЎ*sʕ |2mMwGGmjRZ; {uin㵠go4ռVo3-[vRoLwG>zǜQ{#5Uկd|+jg~zm2 j2\)}HjI+V+_ãYMeVbisկ B:^.hշ47$k5Fw-\ʟ:#V2'e6tYlÚ=\ȭ47MKdDo9MfUfRjڌvl2IȲ|˻k/o; iw<_3G~[|W(${|w㷂fMzK-u>$u|QukY[hf,ciU#c$VF2#__7 yiX۷H{JC"~c-'W[fYdwrj浣[A4l1\-6-֮MGq}|Qk"嶠3n]۾Z GՒ-Ho:I5Ew̿ka=9ĿfU?? !oݡU%ܑGw|Z¿?>wXW˺aYߙޯnT<3o/|GOׇ#I|eM_k?-zp;iܳ.ۏ}2vUq+t0)۬mu',Rn}VƟ²L*41=_;~K,{W̕fVIj sA$/VKIWy~HSKY-f%_7j޵c"UZ.oieù~V~OV9_m˚OQR#9;>ק]JU$]۷+mWǟ[=KV9|GucKptjÿ=GCKUoml*{wޯ?3,mQm4̪*ǿڬΛou>ɱ&˫kwiqD[>os*St='K4۟ XXD%ZCQG/-^zJ􌏉*{[ˋo3>^;{+8Λoe^7{v+E3y_?heJR|CT0Sb%)Ÿ&ՖY/S^v]K"uWz< o.sv͹k~-x#O̺}ymjg3 sKyn J2Մmg]iaȉˉ[ov{]9%V۵}M_IdӚ&iӤHnZo*54C}gVEoo6q>o%χ7K$hWUZj3?nփj_bG$`Kȱ/_#go._k{,$rnfߟI[G⇎±/<{{Z; Z&gj{77շ2~}ry?vx5QYYW_k9F%^4ws۴,ǻmtu˦h/)|C ,wn}YH?-RV/ZK왝#xMw~e&Mw?C<>s6x_wV7X\4/fԏ$W\ioHknD9>s_5Y~U_s&ey,!Gm*Nv ٚHffڿteJIG'UĹ>uk~+mY*x?iq۵,3mjM7ajqr{ݺ^.iU_ᬣ(OtƧ/#&Kţ_wj/u.(f *m{;s| Gm"'VKމRQGzj뚵,GwmfcykoonmZԤY4x6|QuWjF[ʍh14ZRo\x{==dcӿmU׬xHյv]yUUk/y \Fۖ=YrMy{A'mnvޯ3wmdiUu |a*?>i5}yt۶$.k͗y|~txaܫs-tV{ۙ[rǺѮ}&5r*ұI,dUmͷ?i/x#)>o.K[w/ʾ\ʿ6__ֿagetSv4nKw6v_b4Oou7qnSWv Ԕ7.o|hvB}q7[jb״"M5ˑѺ?3j$&ݬR_*6˷\. T7*RNriViǗ;,եne*aJti{k& |3v~6mu ˺=|3n?Ǟ1K+ȬYUiѕdܾ}_o4&8mռƋ7\o95Վ2WxW+i~U\GX eϱ |+WVJJQ~ &m2M c_ml_:ż6qb 'm~i?ٯ)h}ž*F^y?~Z[,ϊ$MGۦi[)Kr<Υ>ypʲnh۫ԼuZd϶Ko/r Sĺ}<2[BYge5X>gb\4ns#_iZš;4s|[7߻Yi~(Gگ-vȪm.[ܿWYuggxK[;Uo/sGc_w|Mi_j .vsk[xb5['*] YTq+-ǖsx--c *ȼ{o 7VMp̱7Y`=iƺ~9 uZV5ȫGӌLEID<7MSkZa~*Iߗk'I|? I7Iiw7*+_]ig7W?gU1??#Itr,:IK.jr4&x!.m,dV#<%g4k/+Gfe]۾_W~\~¾ּ/Exvf֭vrQH7'k)J.c KR?Ə 2n+#{1VY/̴WI9-e%v:?/9k}ۼB+||'eZd2Y.nh)vZ$nV"k+7ޮW^/hv-y.sDV2  |.CN4jVW'5Ѿk/zm6=k}["?%;O"řw7oj~ƿٛ^#Wt۾jseߥU'ddNR=ۙ*ŭ´+nI)Ho|cd۶Fo_ iƬ%Q:RloIgKM)Udx|$vP^g)oּxIO%_P?>b|b DYe{g71쟵W_Z^v~R}߷X|D]H/o'ݛO$k${cmZreu<*\/]W{7QY,*nf_}_ٻVR/Ȭm}M'4+ui1Uum6Q.χҶ{$CnoenkԎ*Q,NIR4--ȫ/ʿޭ ?[p{nUM7YsZ[ƼJ1bbv֣oh4Mj~jw\o|NkC$KU_)~[_h">;[k}Z&͸o7U%ī,m+W 5IU}ZFK\؊T1qk P*y+-~~~g2߀gSW^ϽY[wݯ/ؖ_7]+me 5c9s>/ٵmb[U]M4,Qգ{U?VM۾o^cxHzɷjW o ]hI{u)s"m~jJGMs|cƥb~$*fxnuK4Wb,_7ޯ^~Z[KoʵB}Ixψ-+m+2+/h2JͺC#ڲmZ%B24[_I].o6j h󙚰 :hV*OቓuoZ< 44~bɷۿc>Y5,mV8UWZև>ݚ;yfj;o2],*=de.c ? X&Yn#W㿼T+~寠&fgE^EĚmgk F^f3 #zy}Qac}4{]!f+2F}jZԴٶ}ۺW>*J-Of oj+R2'7]GnZǷU۩wMeqq YɹrjXockjh|ʻw7]Wg(m.)/$4jʫ]P)$Eݭi$o_[kO[yUi#eڿ5zGڼ6ڞnY6ߕ(V>J[[ǐy2&{pEj|۪+oK43.Y>{޿:5/o^֥dr{'gffsVEӴUe/-3na͵VRe*| L ~Z~්4|6ky7~m\uJ_ *,%=5|WZ_~~~Ҵ_Ρ"2D˪i_Q:_ k&\YG_Pfji~G\22GpWW ~]r84cmuGu-~*ė h4j7~a뚶}AYڅ_oCCYGbT`ڑ~[SPE۹e?z<+}-} g{ uŏ"Ydv.4i [8>+W_CT='0m#ڥo/mw?ƞ|Lj]$Q^3Umv=YmjO}"4>ZohZhT~X[_gf_._Gqgp/Ѭ~g̶^}-_|>_ Դ;5ú6ݷn5x>;Y?}X홛w7U:2yҫG>kڛM[]ˣ·_O6z'K ypWK+׬Ժ<ѥY7?ޢyE"V^\7omm&oY/?<+ZVVqF o&܍mUo:#3")rԑQ]_~ xƾ"t(H?]L~ h{[毢?b؃Z|e-cR&[z2F"_`Sbdk|CqڶsFVKXe_e[KC O?VmjThT˳EڶpmkN˳8?՚*yP?l7Oov)bi+M{Km_3gG#O,kFgIeMgGMZtPW"/-֭7|7u.߻-^m?CHm>9s-+{嫴P&ꗒ]^:eԟ'9$o/u۷I]*kNXKL}Zo@O~9bG:gZ4Q3gM_GM֝r3?c4Et_ ZU@{%tz(# o~wck^ƛ^m:d泍Zұc?j @|37}w}=ۿmi.n[mVS*dXϜhm"ZE>>6h?i~VG,@{O+Moi-6u(@g329m.ִ͗h+-d/z?& ~wEӿ?&:լMj@_h?&EOX,Mj@7AdCbYYYlVV_vjEfzj̬\²FjEs9ѢvUg쵷EO*#v=;wz M8٢HWMzor@w1 i[tRB1?[8w!^q5E>T#x#ï?M5|ҿ15EUxf3oyrxr忇tc[P&׋uh:e=qȻ̫[tQ@Y7?ޢ`~f_|Mg Ȗ6Kkf]y[^y]OגuC%۶l[w^#Iw3m]U xq1_Y^8ԫOr`RWJdkff_v~#|K>.xPt3>ѾiwH sywR>r̻vԜ>SVG$? AC<p5uQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEyddz&['Uo䭬<~knȔu/4Zt ltM^dcK>LM8GF<+P}}%;3/6 p#+VW_rV5ecIE~~_C`:H^]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@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Y7?ޢ`~bx]*O וF*TuLWywJa/h^9Z&܍jku*sO}8jlh[mK[=?k> KKYԬ6.̻Xa&okƞu}{LVnyw˻?7>_宓6hKźiMꚓ2\G[~6z/;]JO R}Am"VodhEQY ~%msZtUɷmB;:Om{RkjBݷnۻNƓƞ\_^XUƞבGrU GմiZk7 ;Wsn[jZݢ'?Cy0u ]6;ehF̪Z|EPEh?|'ZKŖ;h&Vݙw*ȿù~o.TMsMcMKPeXIXwf[uK{-BO&(&YMdUQ@yM׬;kM֯u=ks2z8ٷ7|M7څX^i,7 $2UUF@4W5 /eծ*~ˤȰShCFw</ nK1eWO@Q@g5VֶO;*uk'¾:ߏ-f׈dKcoF@QTgO׭~զ[jcGL.m.T6Rn5 H5 őm%Vy?ѯmŶ\A}[jv23*LF̭e~VVZEPEdZA/4[}sMX_2O6${/ E״y5m/X=.?3usB_ߗVgx?%#'?di7mnۻwUk^׆/ψ4#MeXo#7|˶Fm@tTVP[qo4sȫ$rەZ(ٝU~ffZ+ utI5k[?Vmnf˵r |vCJ-WM/_7h[Xl.Kj͹5DXۨ4kv슭nConVK9V]FY~jѠ(("xVM5+_h:~̷pjv{~۶ǷyM$֟mk7Qum[E`^x ׉4eնfѯ7}'Rk^:߆u+7WjͶ8dmvۛmETյ?mơ_[i}e8[|AYՠ[&}ZǷ̓ww/ZZg=TӦo#Yk.o[ V׏<3a\i7"Ҡ-k)oY~fݹWV<7Zۼ?i变M=r.բA='šlY|lzƻo[jGNmERԵ7Fk5u [[[_̱7ݎ=yk|Er^$?IuAk5*2mLk[]+hbo~ T_h1G ڔ*r}ݳ|߻osKu>Po ,n̆E9+#^y,u7HO&u k?6/$Tmr;uM[J͵dhnۻ@4Vv?Yԭ<._.MZѠ(,lQDdz?/+e _OehU<['GepMW> Ѽt X~UWn^>eJl,Z1>]}CCOOh/5mʭn-q>YEݾ빬ڬbȪ.ʿv&7]~>in/LhoY1y';jA>֌vkw?XxZ[d#]Z;ھ_'~Xw7Ky|UmHhZ5eԎ̙vhա_/v\~K_aA7 j3Zo64mkX۴m?y_}me/O4]rOCF֯A;kI''˻vߙV *:oyۯA[Ge |_'zm? >+E8_]vch8v6VwS_ ݔ|/9BhK?\xbm Gj7a[?%!~gͻr{V 7ǯx^եP-EfX|ˍܭ*[mSW_v#Ioɡj[G*Ƿf_-g_rm>#~ZW Ff%Դ-ckK|[uڭ'˹h/X*(lmuZ\_gi陕Z9$Y|~_Z? Rl叅 MgJlfice_nzo>2j]ѧ]KC nImJlmH ?>hoi>,v۫<]hߴٵ+_ō~ ao,+63*Z=g⧓{}RFb~Q32;aY#]7~4٧ǚ][{A-RE.xddUfHcV_-_Vu})uk^xUʹy$]h^(˸]Yu+摡XVEV9o3v{|W#e׍?.oyoe}.O$ZEVG,jl4|1]k~1Yt=J{[OvuûeV߇;~?=j{yUռ/qx gpyֿնʿ^CkVMsAקj6gR[:]y%[_'ˇǷszqu_ðkj^$=ZFkVY]kuUeok_\|7_$jjr7O/Rpq~|B^[z}4޵y$֭$͹h$jѪ߽-zipWk[v_웩Hkvovh|caW3]xK4m/Rԧi>ߦxe]2ީϿ׉h~(ׯYi#6[6wvݹ~GY4' >h^_Eoy62lqf$|_^3j?I]|K]A|?fѷ}|wiE?ЦnG_tg^o̻ܻTf8|R փn5w??}8 _CsQg Ţ ͧ*Ѳ|yr2i5Z:m.m;Y#HaU2#+nm]/[x0Xuŵ[Yi-|ݲ]wumg_'$.h7ڶu$֖ѭѭ4s|۶/ʴ夹{+o:σWb+K^[Rfѫ7z;أV}&BU4i[jv6ڬ,jnVWurzo>!h |y}bLk]Johh_3ΏkLun>o^m;Gψ,֩4Cgcvחq[{9>V5ʿ+HwoEZ17%H-RYih{n[TSo$mnߛtr,jk/ciE> /[MZ y&Io.Hw|n<}#c5{s%qxoM[ cwy۫}v{,u +5YSO$Y[=Z5t~Jû˛wͷ~j?׾4|~>? V|Jϖֺ32ɺ$X6fo2y.|F$xֳ$Kuk'h|V_;tEwޮ/R~57tǶzo\ؤfiew3y/5t}?mfߛZsKs|1fZwMʻdעR#oƗ>.&6Kj[Y&n U/'~zǟ<o \<_-duk]wU^K#^yr oV?aRPfŚ?&?1cۤf.7mw|򾛪|`wտm|mjwL^\C n$6afeߗv5 O sD/i'__ZE֐}cOI"eWj~_k^_j M0qhyvWZ75υ6>gm3Qޥoy6q7VHM$mՃ|zu "ֺZɦMeVemvUWQWX_+_.!NTUŝ޷5 uoYjV_Heuÿivj:^kt/ee[V_;]ͺFksO 4NGn"=6ie,6wݮGM׾,k$I_m ke&UO2wI4V~j#u_ xGY{[Ph>KI`~\r_7;?y.⯺꤬LXU {NmgC43[5ռ:.~uKo7o?}jiKAzW;}f/vIZfo%猛_rko|znmRtveXաhZ5w/zfX/-uh|-<36/ݻܵ|FodOMZk{FՖhmZ6[Y$hdhնo~6OJ׿f7-[_ikW^$PEi42lgܻ75| KxTᨮM\^xKԦedխ=JE[Xf[ksI߻oͶf xC4=WĚiy6wwQ5՟wj2Yٳ7׏4h(kk5Vik8|]^gxℿ |ErOm%zF_VfUܰVխ>!j,woik^$];Sk^iL֬'$LۗfXWWK1|m_~%jK躜:'Rk%txVF5ܱ'v/Vo_Wŗkn6XaeV~"4f^|@?*:kL+eu?vZ- z[QRQ(lQT¯X6SHFF[k2z5*Y+9㸏˕UZKxF~Z[7ZǛ rcռ9vXտ_hi?;X$sO,+VWk7Wx"u6{n_ku(A~~vKyOjɷI^ٟ:ՃDښ{W'=Ca&dmh/'8C?';GOvkf<2h7]@X1 ͢vXvJR*2!㴿qI?ۏo E*N-r/mTEwؑWii0o2Q+mTs\_On[ȭ13u~Io _Fo-CNkwVt٪ =M,2 4[l̿m^Mf{7u4HVj+L|a36隭WdI>o&neUex<֡I _R57Qk:R+F%D_*>ŷ\N^SZu*JMOxPuKx߻۷V:R~Yog^+h|n>k[mtywM<#H^՚Qiv}g"ZFڷ7t4?M:!_V?5JHo ݵEVj,~4*hY?Xj9F\{}'˄f_G~jKmu2|dLu0Iծ6 ?v*r7,,4[W7T2F?VUV]z;#/YPSnC7g>N1=?J/,݊5j|aUfoy*kn7mz3Eq*]r8{USH. |֬MCğge[m+y+^ &m*5V8縞Vf)_|}ƃ y-'5V<7Exd/2ETppriWsFUWXkpj|'/tUx5E525i蟲|RxHPkx|Uo[sZ$M'eC.IuscVR#'֬ -]ۛ??Y)UcZg55S+mI![Y~7cfǚ4hc"ͻXƿ7vJ_>ޛ^.w}ߖպZypm,7];QOo,lN2C=;7RjĽcTe_?}o]ֺմW |URF_ij.o-R|RyaZ%r]iKmUo ~9n/hAcr/ZYyS|ԢɫZPb{t?5ȣ^8[Z4Jڱ|dg$:?zqAHVUs*3T0j\Ĭ۾VRG7ף|㫍,VW.M?R[knZuE²tvM5 -.eM#5YN2"XbzG?ix}Vk>_qM$Ťۻ˂5VZ[_&[oxPn-EZ=tԏ;~.K :+K2o׿| :5i#PYf=" |C|<,dUcݶj[惯}yn`U;]?k|7̿/ޯP:mVIVw,mfF/4ba }+ Լ9,q^quod_28پ_'i0\jZ|52HbW_5K=_wơg"ku۩n_7Vڻm2vF5+5<|Hrw ~o:Qfjr]5Ҭ{X՗wK[º;|Xͬ|ׯV+[^>nT{B<_֡;Lw2ۙWBzpѤin}>O 5|;Uoei6h]j;fEYZ5WN?O:Y\gjgzhR-zVoگc+ğ-?7q71BU%j^k Okܫ.e][8 :Zt5;{JuiF'W*r?S>Z=¯&~ީK"t fxvշ[7U|U}fyL5rY|L^L*L͹kczƣUVgUW>(T. Kk2W|1ݤOn_+7}9|!R?xu<7g}/mIo4q27Ս/kc_ GcTQ?';Z4PwOv8礟hnWmu-fIFOv7uo_Yå2$~dY?;|C_>K⻥~fVk(x|[C"H FZn@6UEڿgU?hx'"5]Lj[kd lVYW|xOZ|vmi5 UY?eO8礟qI?_?';Z4PwOv8礟h@q$mk_tZ*ɿMO߆m|Qhmh4U/$_uxF,5[_ !nVw#2ۿ\F]jy<#Xw7׽x'=4.m +$r+H˷~x F#o MEk(-7jZ*O/|毙g=;GyQuK>;ZyVOݫmǫپ/i۟?AM6vmzI𿈮,u{9v=v.W1^Χ!Gc_nUUouvW:ngIܿE.nxϴ/[ۻ]5Z3@zjUv ?)UZ^NtWx¶my>ZlH-zğ=r/H@˵~;[VФerysI{ܦo$1-77~jl5-k[<3Sc𽍭MIc%j4p42*/V]۪Ս?M*R)-⏳{'~4?{l?lj4{iٛWugu=\[*wUm%!uk˖5G]Ur_D'tU[o껆P?FU{mbCKtjy=7,t83Jew ?7׌ /5C |*wںnJۛᯍ~+kڇo_kZAG4k[o_um\c<JSO? ,\ko$p6|u|;,bi#Eyt2^ T9;}CsCnW:mim}xyqvw y3?cVQNa9w>߇Jc?ccĒ'\qgw;uz|V ry4o]WU៊&f-⹱ܾu쑴ljX|'p)'YCMfI%yƯ%GL6GM'trPݹ;|I>i.I"}E a\Ǎ^5/z' xUV6Uohڢiw~ִ 򮳡}&M_ tH#-˷l84ܽʣi)~4۹=kvli^1hw33([seXm[OYխ:,  O4==(sFG_xkMɥMjGK\eU\"i2JNk#e-f%ԩь~\Y\yw[d\6xgnyy-GypG|7_կ}&n]ynX_.=ѫ89&Kk^e<--Ywf%历^ǖ\kD:hsG}Uz|uoχ彼L>o)6k-36|߰o.V+ԾЭc_em9c|,ڧfY&Z|˶?Rhw^lt^LE*̻~Z^xO|xA,Q[̭6pxoti?iv5f_53cR|ѝai Z9+'P|;$Y.-"o+5Msz5$hY?/\MJ;_Zz+jMFۿrվ{ܲ8SJKGCW/jgo­I+-}f \hgm_ٮwTnt9$I_SBXz kF^lxcR |ۻrx_㏋9}$y66mr G>/omsc14ܽͨRr<&Qm6~_uQcMW ',} ZOTaM>5jV3Ne5TaO]=/(ҌOw0Foq.}ċhvU W4ꌿ쬛V .ReU_5F? =s5#wV1/tc&N;ɺ~^ni}j-X+7[ЪO$Rk?3"ku|wZ4"/ۙw|.I4}Y~:aouӥUk o~ _VHFUvEkVoj&kvZ۹If ߁&yct$ofV#aSZ\?|:]m{M[Jͥ&]Z&O&i8bozFPoW.,ԭLƭ^,~"<%ˣ\=z4cX37׌Z;]6IZHnV#4E m%ZH~skh-Z~p)Rjg (Oؒ~lbo9m&Y<3. {&̭_oˍOQefݶvK< xqjOg&̋׮4˥HǹWre|!(DqtC_Zج ɻKfjQ/$.]u_1˝\/ C7[JʼnShG'SwzBSv9kyj˺ӯI#V_VZÆv摙j;ugei6[o̿WJ\9~2Cs̑Iyg j gok/ 7s?HcUUVwuz'OCŌwG"CCAncI3mok\ʥ85c2,733/^ro.|[B;8w ]wV&[ksrm_Uy-'Ò6m} \AvV5o}u(G ^NV![ ͵[U"f#|{ ~_ 28 ~Dja4E4r2 /ޫxOi CM6V[w=h =ɣ|!쉻lpZ7kkYOɵK]w7Ś}zkq[w[χ頏OidʬPў2;^ai;h̞OE_:_Ŀ+IխnLԵI|=Ŷ7g𯌼QuZ6ǻ1ۿZ|Q5x"TI"?ro- 8n-IUh卷+/v_'9 ?־S-4ǾMۡovmycÿRF ;I<:6I%ok_ Av[KtZKO9|U^4-V_m'aEfIuOMmV#Ց=#~jέ]avZ3~㫆Is~#@t/^ gZclVUm$fU#PԮ5-^u+$mvc߳W? =ī-~'_?ZMy+27۾οoͷ寠+^~?~Ҭmվ|7KY6k|w9jHƟjGa tm|-:۶nk欹][L'y~,^"Ui#]Cg~UK_L}][XI>殯8R,gj3iOg}!YXUͻA|v'?\U[H˶Eם?5Jdfm۾*ֶzŭY+3F#4j*tj{,DEGLp#ZiU~R:GFͷ,˷ 15ժJ~_jO ͑i~6zsF*ݫ|סnX2%t|P[Y 6]U7-Coq,R/>UotZ"Hu/闟3yr,.ݯ%V=Fl,jVƛGEeխOz].f k[זѫ_&ƾH70r<-"-FcOZ|qM/j\Ktjw勞|MIԦӼֻܴoe/kGKJ^]Pռg6lq}ݯ7ÖΚ/be_U?L(X/!GmygnA.mm.hKveV5p~. cdGkFh?rX]9K*P;>,}BUoaT,vv-5*~t/ikx?swmev3ViG\/ݖM_z΍/vR:ciss?RcX6e#*D(WI*k}AWXfO{\_1MȬ?cjXZC-uˏɍvz4=eHj|-~.|H;VH|:6^gvx]4l%w_g|?ϊ4Һn#o-cfoko4 5Ɛn!_.EfVV?|SFKgm3|uܬfMSFn-t˩v /*>3*RDw6]UgWk+ȕwmo,{kzoP?C.vq&kn~ojjc~]յlc ;SMcu5ŭk]Js$tfMI`42}Z xIVՙuw5yO^ypѭB4𷗵ju*yyU(pֶq#jjѷ%6ܿ*5dwno}ukS-pk6TWo#Vnb[~Wi>_-;ߺ[eJ0&u2R9ۯ>VwKs"yH˷n]8xIWlP#/[ΒR[{'^Lm r*ۿ5 /muK_Fy+I_vVWRjwxK#MB)jiUhzfzв3*'|-g8CyizۡWo|㯌ƩEG4F[Z4T$ybp~ꌹKFoǯ_MnZ 1w|0K+H~j|j? f~"#in[u_j탍vujSG_AoyoZoq2CuZݫAlWjZu=>=3,Fe -f~~%kV_3Ȼff6b=\= 4bp~4\\M7;vG^xʹ=Pxi۷5|z跌ı-WjzkV6+GJ&,kZ*ROְ8ku+sOY|K wʿy'~>\]6,U9%orUo꽓ºKk~ e7T%2yEirms.?6feVUWټM=BUx6ݻk_i^e7٭[ki"ߖ7}CH4v3nmۚ 3,”izTs7[:?xe+Yʶ+"ڭUG{Hc)S4F\˱Kw$_=13mY+|^i[/\_| ozo=d*r6"|?Ov̅E'Z k[ ( ^5żmkbͷ-5 ~7o-kgu2$VnV_3vzljl9%+,gaoլd=%m/x|aWVŧȱC~UY?Z7q5o/jZ1j^7#aF%5M7X^Kk 8?z8>y{#/ OYkx9_.-|]R!ƻ~kc4ZCFV-ŷBaj7j4_7duZ0Xkfrb^m6n}'ZUu me_\o2_Ʒ۾ix6|7ڵ͎{՚EZTỨˎ3r[Ⱦc׍SS RPn}~Jy/g_:o*O3w_8-o|H[kuG{]25./0prkR+E| mnoo-|9.uyP_3\4r}?.pZ8'xx9d u_5V)#}PvKz}6ݭ7ȫ}߽]nݝj\k+m_..nUzXhӗG_xmomeX]7ݷWxW^}b; ͟5Mۛly~_mO_-rU۵fOUjݦì֡g Vgy!mvjs\UxS{$uE~:MqyK- 6m{;{E5o_=D/d=r4n/:_e{j -ի+iys,k2[|~^ot:MJաo-|xn~|9_ZCxF_&[5o~ϟ'<}C-ŗ~ּɬdе,ʿUs|kŞ>GXWtHI#*335w_M'uJXKf?>;ݯͶ+n_vs$zΏn׍+mY~m o? zK}kfm.,sۛz3[|_ʿ-mmV&rjPp/۷tMhۻmwh}6՚ wPCQUUњOu]'寒Ͻ_u@Y7?ޢ`~^mٯkZX5E]f2"v_-GRRE&f۶Mח럳/M2 CvOeMUYWrm:\h5xy/?^4~kXmUc۾]u~jfYacxY߳=mZRArۤVoǫP񖃬}QX|=(x*?~IY#eZ/5#O_Vhe:%HucR\Exþ-YחO#o})|YQڄ{i+xX_y|\hm_Gqqu< |.Z{ũE}Fm=l˻Jb\F28)fyg{vvvmxhlW~676Ȫ۵vݯ_#uYo$եϚk]>夸drݫ̳YQʄ)^V<$x7ˣͫxOY.$orۼݷ_ǫoVm}CðZ[Uz+Ğ!4ՍnW[&K<2\C].ݺJ3&m `ѧSSGTXN׵*rI<;|e[ծ_5)u?&|О.n%m+2U+,ZU9i'MFK$6杕5v~$xJlO ?,q5m/3|>'n_ij |n|2E67Kk04'=7\t~%n/<?}"gGžV #7fj:庴m*72PCI,7~fˏkVEjukdTlj'Ck[X[kgG?s6VV5met?>V;dfǝpwLW]sco3}vUVݹx؜K*}ʖ 5)Gz7al:jw[LMUL+].Ekw27c۵ٯ9ٯ+[-ڬw~Z0*+?F߻N1__ZQҴv0L 据Iuk"j׵xڅ,v*V'Amʶ~j}x״ms-bHnV}Eu;CVn%7 4r3:W3AGq5uć_\?cy Z6jn֛'IJ\^jnHo;5%F}'TԴ6+źhmVLfo%5fvcWFIZZpEJ$}/$)ح}"]LɶF3mVMᙤ]RJe!5x(WP]v;j,WK)5/Z,//e^'. ]2ʺ|-2UV.K#f毦>kneZb?:aiV}Ok׍bkmrU*Uo]xwPO䍼|o? |TfMnIYUwYkռY8`?ǷaKQt>?45+[{~y'ʿUO៍htFH⼇淓{ֹ<j|ͨ7uWnڦjVS>b&՞Z}4lmk7-xoKk!-X537;(Uw|B3mZ:.c|,Fu%RVf6f_W)h>;{VK-_-'R`9l`Eou۹v{sVO[xIukXY][o-^7iLhJRxU÷W [Y-Ia4.<)s5q44~]L˺*/±黙 ROUmNgׅ>LѤpFG;K:z?fݷHѷ^'W> Cχ?~xfMϟP=IkoinյEo-?1|n*]°ղ}<)- [ffo/\+Q{>bkx:[Ukk7;_8R<hmlwyƲ,lYUZxJ~6icn#eY|/^_hr7Mkuymf[[-8֦y*K)(2xKX[t^mO ;_ݵ37"e%׍գ]mukKe7ulvEWFV0A04eB|B/tyo6ߙyV|-ľ{-YZ6VjM[s~ >(;t]|_jZ58oޮUSs㳌2Ʉ|gh>VI?chU{Eovj봝Pfk+ueqh*K|'f=JL[Y/VuVnE7\8~^ׄӧψ|qy5r>-՛wYǫ)u%)~,eۺ Kú\vi̓ v~,6Ir?摒vQr:>2Qc> Gg[fUV+mܿݵ_g+'P4vc]ݳ4ʻWozޡx'A.#l/!k[Or7o*(Fqه^fh,pJi}9J2,~CƯ: 9t;xVX5RX_9ܻx߉>^T.d_yV_z>"kW"y+nɓWۛk&m:o/ok+> \4j.uI[ocշws5~x_Kľ&5/'og5~x/ƺ~/ m6͖ݕw h5ڠt'=> <9:-m(4IL߼o2?.5|_h ºwU4..UG2m6mgíR'u >;mZ'5/5W[{'XV&Fm@8n<9sV(i/Zo+/[V(hM]GxMq6joE'aY&͵wVOckM2/$ֲxCվ]]Ws|~_ƱzS<ƑUY|v&['Q7?ޢ.]6Qj6XPVUeeUYYWtk^qHҪeV-(Tt2)sRŭ;fi?ew+3;33|IE.u'SyNZ:_=u:x[5HfX_2Ve%A5tf]J;O/n>-Ӽ3ۤO5fiUkxoiS?hpoikKF7UßE Eʻ6ߚUiv(UjO_OժD9yׇV+Uݬ{/2&4;^KE4[V;Pkܿ2xrjim%ז|ɷ}h_O'j4z.𮊫ΟU^1GBTeqVu捙[Z Gk?j/>"Ȼv{v:ϗ Z_ɷZ|/^K}I!#XUn̪{YFoyd~n+i7ۭ[m y55(nFo>#ծm[Kq}w4Wjs|[/,jV?-y5rԯ:|)^2E}<ѳ}mv_٬vZ<*k5>i~V߮g^<%3)(T 1<vAmZoj'n=]fftf/D>=<%ni $vJͧk pjO?1}M_mcj>H/1}?&iFcPecTo'iuXcU;ƹbKN[ʿU~/>wA??&_]Y[A[Z?&1tS9xfԠ5~xE>tU|?MCu7 t[e /G,K gsKY-ffeݺW?gn7Z=r2]EᧃK^xWEL:%v:m}{kh8snm5y\z~NIRG5shص)> x—_9Im͏k읇uj>ݼ?ZnFʆhy_y [6]+|ۿ[TλvǵYٮ.YCn,&Ff_B2W&-g#kO?2|{WI&kiq,ݮWő^5^,kMC7^>\ң(/iK2:/xv]~+eWj#<Oq&vWOHvcsWMVmVoBVE5aV5kiyd| 弊ח(,(^Z'dXx`f#^>eKz9tjX.wm_W]xvKg j-jvөf:u)&Z<4J=#WVo -_VZ6U;qk#n7w˫WUSsHi|z ]I$H/ ?^E_i̱mZWʻ(#n-u#wm_|WAJOi+Zvwkũi4kn<}ߗ~y!ƚ=m_j ExKw-ZԤ[[uU+}~Ǣ|/Tݹa6onnǑg24}/cPi&)#m۷mVVOC{WtG/V]K[x>e_%[UczD)=_f_BШ5jF2G%Rd؞Ǥڭ֊5/Nnm'E\jk^B,˷s__)s|FԻ66eg鶫|.uUV-w3m/5y_#j?ULxnMeeUVo/3G,ig{4r[_ํ|Ͻ/^ShZȳ5 o6z-uCAfLmO5ymͻr3~}fQ{UOBԵOLOqo|-qBnV} |-z~$(k> zլEjqvɣ,:epCݕi'>(_ꡑ֗t7|*9$ݏ5=SG$z_KT?wgf7]gC|Go? Vv'w~^׉s[˧vl/~Xﭭ^GxG 6i/ͻh}kK-4_^%;{CMl$kveWVƭo~џ4J i- i61jt$gCq/-6Xվhd%s8KX1bL/{נx. ;uiv٢zb K]J3qrmd[W nѷ-o*)NS+H){>-վ;_^A.ZӦo wY-Ue5Z(n6,[7=)m-Ɩ?emBɵ~m/_1R}/ҕ>YHWPѡԼko;$R+4|M][qk3n_-~j_Vokn08dMy" ,-6c֗']RyW'ǚ':^k-Y2}\"qpH}=&?uO_jl$iR5bգ__]ΌhK>&X h'?ZYk|akx6HkW藊?1AdZDۚ6i!k ?K|mͦR7-?v^gn,.#>Wܻvʵi_WZk|B6O9]\eoN,_'̫kJ^OHn?s_լѶ7Y޾Y_'kx[4[{6o5w˰({HE:G瞛K;]6mۥ6i&m_jƽ%/BۖMAe+K>y~O7#zE7&˻j?\c[rY[n6o_tW~]M`9:T'&=Cž4o49b[y6ߗs7jk-M_9O!ao5گ+_jL1?4|>[y-tņi$=7.V7ڎ7na?Em7~j 4(^)WwYM<wZC a&o҈zOڣugT=qy5w<-""UUz{8=vZ!l]˻'O9Z^HxWym1@>/` c[vFe?"ugp&vûXZxI 獴OO\|ۿg?XK='{d_ڠ94KAiVhfYVe]_%^x{ekԬ2}y n,?inkn}4oJ+M<-9}׀u}KV?[ƳLy/=y3['/%5km,uZF?zO#f~'igLWP:nǶ?oYjm.hxw_nh?Ki0s'7lrB |6~xUe'U~jvqqu6/oW)h/o*,W{Xկ$M_ŤJ˻e>"G+JU\r>;ިahfY>k)I?vn_OiIOXHP?oiKYn L6O9@OtUm 2W|_kzj+3,,]L߻O_/g &N)K a-sr#3Z:_6 6MY.$i%fF͹Ǻ-J[.dVlۿ[U*rWq˚>M ໋a$쪫zK4k]א/ھ[}/|Ϩ|9>#~̿ a4Mqt?RЯXԭV 3+/+|/\doPִ=B? ,e:'\-cUdq߻Wꏽ>Һlo0K2Iz4[\ m8&Vh|,᎘47ơofy2Xn~-j|c 㷍<]OX][3BydIq#37u~_AxAaRִ>릹5nojWYUYYY[VZk?mbQ{ר}f8U6vhW?YoKCүJ 29Zf[w{wޥmT5{"8dm3,{_#ǯ:ږ\xK./2GjֱƱ$rF_6|Zڳ්>(XFAmui6 ][3Hf˶E#Zi7Z[im~e 0U*+++|_$L#SV47W]{G[][My?XhZž]r'R\|G5/.|I2ݵicV6}d͵w7ʵ7v:g>$qlɸilk3|Wz _MtQ4jxG,syg?&Wz:kh37mZ᤽!oF,ח̟>窗ڽ헐ZV|~Uv3q|a4P76:䑬iG3I7BkGV5oU⏊~"߁j"oIxLvMu'˺O-k6ꎜƝyO#.ڂjmb+\krx淚9Y"m*T5/~Gkk^xV?.oO>"o=G]g[u pF4ƫvۻuh)^jVzj˨-U]h_ym|\Դ[Mxt}KK&tǓ.o?-|ៀ>|zh6۠Y7ks3nFUW.Q{)t=/$ke|ƃ_1WjM?T+/!ݷ̂Ew_>ߴD>(~.}B9K|ƇXw7,Juߊ~%xIͪV./54֫%n]Yc5ڻZ|u N~_Uu&t6ٿv3nZ ~}yk:KT4u&c53|Mgi|5*q6m|ߴoAklu(oK5fY6V6-߼|ͺq 5ʼn5ԥU~s4uj,6o_5ONo"]|r[=Wxhd5eX9hk76&m[{}bAngo}YūHX[2;~24~Vڦɉgyeo.O"Eo-fJ |D'594?ER_!imQ_tkxۛwݯ<xVi?# ufG~oVA&g6rA˺8EY$uiS\CRnf)6k0'4ht߳^h~"~g Ƿs,ۗU>2j{M}m#[6n.̑ej1_>]ԟږ~]ph_ڵlKo笋~-{w߆Pԭt̸;1fZMM_}%샧=lWK-n!J/UD$+8]˵~UVU>nuYi^G$ֶ]FM{|ƍw|ʻv߻k^Lw^hz]btk-QmY$̵,2}_7\%,"Цwy|1I$mn YM434~\ʻf]zlHku ,Rٯ]l/^ ?4OƩyyyۋhkH̰ª_őwuq &,t[VzW?j?va[WvhfU/t#T\kmkϨZE]G$M2G1?ʻs~,';-[^A:t2j-hݻfF#K$_j1C&45v?k軼T[T47?l[[̱7ݍw}mc״ۍjGPVn&Y6fUʭok,>=ssCX\I&Fw 4˺Fo-o/_Zua JWNg&w).I.9,*]_$m#j|=//IXWMč r\6#nVFYv-۶j}KZ?$/%xn+RFTRG$/˒8}՚nw][ݖ KS_|!H|-^kz%p{~]6٤f_U/m_KM*HbYUw'wwhzY u |Qk.wn)v9Z5KK<9$Z5Ρ G|V]~ |}M hzk Ǔu,ۖm7}h.]=Rk+2kjZ曤C2e[)Lhi:/mcP/:u ۬2*±ۤ{W|d:-pk6K=p+~oV?A_xKkJo[{Ve_H˹c[Z̨Uk;4UZۣVs3[[誳,*?-q#DZemujz.g&iKYffYhvn&-o: ,gVԊZ]ۛݷjou4}/AmrVV5)m67˷OfOltSS?|u o..ZE[rGi kփ}]jL6]$V۫B֭dr7}KJꬭZ:-iZ}eɚ?Hoe%|M[UsZk?\\iwIdhaE9VO;j|jwZoIi7N"MmrEZ5oݷ}U~MoX#[[vX|ƻfj^0xPnj5 Gƥf}4UI#mVomc>6&fjmBKOL%q-đ43C~_5@g^^[quqzIj xE9k$UhV[դV7^?au'M|a&%څsy32#M'̫7z?z EfA{In<#_,ʰ=nkro^Aḻn_ )|<>/r*̬ۛijpX["ڭV=k}9״xAw=yu5syym֚I#m͹o^$ikZ^\FW [̎Mv}7Ta'Mج65tʶ˶fo:^y ggu7'XOU|gu)|H}/Ci6,D՛ k"I5 ++*j*z3yCiq#[i:OXFkiYfFUGڤ msO-o.!Vy#]̫Wrok6>-ROci.XW33|>6XxÐ,S;}ZU4K]w ʬwv\ߋg(z-j:x<4m L$hUf]Hy mj!o+[K9gcFcUfʫS^t6]GPk녳[?Ḻǻ6o_2>&\7/5uZ<]il;noMXw9ykG_k$[u[-c]4kv}4$kj]Ykiwڞuowi"ѷeeYk_\jWڇŽפ3mmmEh?E[o_>œEmw+[[ٖѴk{UdZ>n:ֱcm_+Ikpsp8ne_kV>6^ǯ\k6:|L:k7Gۤe7|۷|ա'9QZZzZI-9ffWlV@f_jzZחPYȱoꮾ#_R]R͵O/|{>!%t'6%77^e09$ZOm/;WڗE Ҭn4|4+s}LUj={2ic(w33mUZ/_4,dg"[ `fo>.Ϻ/5+Zm=B++In*Z=Q ۚocеϜ[3 ;Pto Yj,{7S^\魠mi5˺HtGXvmu{|Xt]jmBk9缷ivկi?jߗzLzjmϢ/|rliZ6zf_1?zO?c_/|3~lO&K۫Vi&Wt\qj3WkZştay ov*5.}E|a/_Iv.%'Z(i"ռIZlZfm7.8m/wڕ_D$}-&cn3iݬO<3Z ]\-=Ḻ3*meyzo,<i}wZn9$[-UZ6X^O/CIlh5.qWg-hdVf_⑩__&f5Km3Oe8fV+n]k 3w^k6kocI5욄jW̑/CwACC>V֦f˰;VS̲/о)|heo\mNi\-L*FI"|wWτj -.fv}_3fnT'WET(lQVT0Ŭ-o}M4My<=8B2c._ 7>&oXo啤_[0]%m"3nyߛo;^ \x#Śu"K%VXrȿ%\Xi5%By2Gnj|WVյ!sH˵c$r*oZ/ťGmg5|+bn`#IUFhw~U={o^?]x7{ukR|1-:ҶַmWg9{*O⯈5LڅƓ~ìȲfj mV]]vůNYx_\dO]Q6;?a$ߛmz{UI QwUN1g.nc]KOƫ 6k=? >&YXmCˆ9wѶzW9cU{kΉ/b #V6ƫ,m~3jGUr?˽o*8e{)s{7Uh:we4ZVkl*ks36˷uZk/O-#cIkc3_.*yi?4{7]|UԶmT/-Isio*B4*3oH> o_dݚ=?lq|%v̭|dgM$dAEFG*#$Vo-h~n_gjo*↨UaU+L=/U|@džcq&XGsq"{Y?ynmd~V //^-kXIm\Yݭyq65gmgmUnmV D+vo*/7WJ򕪹# 炴 ,ּ/MJfOk(UkFMxL f|:^hմoZ柤zQvN 1弍 |3yno寢#|C~kokG$>cyyڮf·lWo3o*do.Y4_|q|oYxWZtzL/Ⱦguz ;%O~~K˨-mZUO%dUYYٮ6jU#|lU{OoO)៌>/|3נK;xm5 Yi$f,ͷk*[^ǟu5kחqhwZT6Y|a%čk+m]5zZԙ6Ui~(jMU?ӓ1+r{&D$^w̭QM0t$x /|%׈uX[äɧX6Vei$]Yj.5o׈&ZHGX!ӚeVj˷}U'ĽR&Yj-SU#Zɨ/Z%4۫Y,1<5y7,;l;jSy|U~Լo xOY/X,f];Rݶ-TTCiHՌe(ǣ^};[o[:͜J+m]W03g>/M} mfm>O:IF[oǷnڋ宏_\WJ'T4-4hZP*߼o1Z66ZSZ㨾"xIӣt̵Fr;u5z/wmfI?;e%GkI/5K^-M`c l۶>fmթ #[#ZkXVU4]&fk;[m镼#tvZOZ>fEѧud_-iYK?L*C"Uct|>mL|VGl伳mםHk$r2öVefo毴$_ǥ\RHX[:w  |ɷ-ݮ^OxY7E,Ym%-VEk;5LGa.c]KȾzBPR-lmOo-c77Zqj:߉4k;8VMLj[/˹mV+HѾ)xψtɖ+V]>d~[VUi͵Lj/Ke U^ĺ빚[F|r 7}&|U6a5O^Ko;Z?1X~iUVU~^?j TZG}5l䯗7V䛾fiaEpЯt,5 qأUJ\d^hb&U;=_P񿉢՛mיYƠEy$o>?1~Vfoykƥ\~tyk~ZwV>7V>'Y>VIwK&X^t{OU~Xc>ffvۺJƞ!m6GcXwGMpNU"]*/k/[Zioq$+mUJw56~bڌ.Yt__iO[گ_3T[_ ٬1HƬ'#yĴk_/X|r!BBHLPYIf76jߙm c7K01~cC5?f}?V+oI5-RK{?&6j,m]5yޗ^ux&OZͨ]ثkm7pm,{~Ve_uu ?|_xYTּ7xڶ%ѷ Ffmfµ5s z~ y>bCG嵛oC9h?_ZžefeZE dkWv+W;??'6V!6qb1ƺ G|ō[m66]ߵ: _C2J߶o"o|8?Dk*8GE/f7V]*KCűIoG3.v˷km/^W|l|?x~\\k6V{ #Xt_<̒8ڻW] /kw?#-?/Zr,m(E|j߈4Q/5}BHٚF2Ʋ7ʵ|oZGßzoGe"cMY}u۷_C\Um~s~֞<۹t㕤rLZ61q_akL_EiM3G af$l77nݷ^~/xsþ VR֯-۩X\Mg+$>dkIپV/sv}5U-V߽^G㯍.,|7ZNscդk9$i&fo1V9?j>Z3[my< Z ω5O7ךZƥw;I3OCo&hX.nݹoW_@Ro<- +;]CHZ%n[[Ʒ7mY7mڿ2exQ jT'>c(jKkjktV8o#lvQ5օfvk 3|5WSSúMAcɛp#y5_?=SZ[Xo*Ś_CRikR4bo28VKxYY[/毥kxUBVJT"R5>QE`hyddz&['Uz;-t:J;}Fݖk[,Ѳܭj?IKVxZKV4J>o 7dpq4𼲩# Vm͹Z,ǻrzSJT,:ԧ.h765Vpʑڮѥf߆mNdUʑCSZg˪~R'm_1ٯ'=(:9vjCq?VwVT]V/Ɔ52?k.YV_r֣c{tS5 CM;LZ}Uyr׫6ﺻ>$5Y[aVFkٛʿwBovx.+öwir]-vF#TXs2m۷mnv:M:9Lw#TnjUֻP"Iz#]?оWu)ҧq"W\F3VEڻk>?\,^,^^ v׍HS揺XVBf6m7TV/uF6f"r4fxx#@qi-c_X.Mʫ0ō[Ql4ʿw"H}VM!|QYg[ՙ|s+K|].)<3uo OIe(w}ݿ}n2*49OJkgz}m Lۗsy% q_5: ,-6mX]ۈU1IR%WxZ^U~jɸmWo̻G3|N9%OgnڻUwSyvU.5Y(VO+IʵݿzWeYtۆ_zsJ<]-/okuT7P ۿr˹Yv_<V۷˓mi*Fwz>P=ruK$g;FL[oU}ۓ$7kj}UZI#۹[ڻ~f?^laO6hՕkZVE!o,6iFfX=F6Yhwe/1]j Efes|m_-dhw, yxK)fm,{4mۛw5^uqo)m5eWG{8&Q_dl$cL3*3Ŀkx֎5͹[ZWU_Z2)͵U~_W-ⅣHUWjRK5fnUfZSKb6KcOլeL~]MzV4݊WjU20e٣o3k6Z/_m~7VY?w+neVЗ9FHP뚔WRMQj~zFj^|BB:RGàěVʵ#xrDem䳷ṋ|EׇegW1}VG9"|-9t׵,ZoOoҷ%g9YZg|@rpv? ɭXod]ީ<%䑬N-wgI5Cc'*WY3V5NoAʮ'oΗZj <uai$EHͶX5o\UqxB0%=BG?~oo, hy xڭ.#K8 ?`.te^v|.UNgLIfoom'R7-TIѡLjm_ =.oKW̵VޑSE2i#.|-fŖ|76cx~CfmחW B)UPZ,,wfk[~Ὗ|lm|?漣|9j|9%ƤiIڬk89H"QQokcN1O )KG$_ڒI,5{FYyj_X'3*~jk328?}G*fYk-MS*w|z֫u2E]oY|7_5U?*|LGFG&p6?;tm~iޮul#em?VEa2ܮ_\kʬS8#;e4_˶]O{xlkU-{x҉#'Wnui2w{黾jęHW9K nZf}UK11rgi)˓knjNSx? ,e·]}u#Edo__VnTЯNo sU*?G3Q黾*=JJzžڽj=ukk1^f2_ ۆC袊&['Q7?ޢ?:]k}$qikf^]fA-4صXcijuůMi:ZWS|\6߻k˥NugQhkOד]<ϗΰ53 qN=o{5#w7|7Ič3mV|{^'}gj' X_tKwɧbUkto#Vk杼_ס~IWooƯ%t_i7^G B9s.w8*s1˗Y]f,o="ynE<̭算ÚI; *o ^8+5;]r[P[?%cOګׇW'^2=Y)GQ>kFtm+7ݪz^][cۗUo?jV|I}cy2Ƕ[W,{ǫmcŰٷ-w*i3+no.ɭO=hc*g_ u [\+餍wnڷֺ)./?y5Fc,YW|/4K[{]{S,m#4l>3iT-m}2R')̥\VKyFkЖ_:&V~p=φ喠3/E_ok'-6-nt߰C'' n]̫ۛoUߏ1>j \twnmMxu(Ulk[fۻXImcA!]BKv_kG~_^ ׅ2xݣyաe4\o~]a׼MMc*6wu 3,k k֏6ߗV*.i.>VyR}ckc5՞_MHʫ/oj>2H[L\=۾fgK KMVjexm˻n/My('[i4lHy3yUf(SR^Ž>mG;WśŰyrg-+y*ѯ.㯋5{;[7GҚդ 6fܫ6km_Z׵V>çi6$IJ7*y~+5jB/5å:n񼷍o5l7ni!fU]ua[]ۗs6Uʻ_4iY6H)ˍw_^L~(m?óQCyA3mgs/Wy?gR5)HQrJ<ÿ6_vqN [qˎ5ۻz+nYrۿxFDŽ5{y$eM 7C?NƯ;umR9՗lpnk>e>5>VլeS09ĚxO+y$~kH͹VF/X8X4X&[ˊ/k뚖 ՙtLimozk=GTtlfn2[I_{סWR38Rz?>i'tkިf|Lmo +/mki~i V][WIk?;{d`ܭ#2זᖡxOĚiɶFϑV>p_auscoo"GkEUʫwQRP7^2y9;hlc[[?7t3/ʻ˺'Tmw/?z |7&t]B д;!Urݫ߷M3־e,RZycՕ۩{ZZXoCAS]Y/Ims4Vd֦cŐj|w liռ_w^3ƺkxY,Hfs4?+.]-p~uMsT[9mgj(r8˚1>猾i7[6E}ںiz{MuccKS޵/XѼoA%L"K=yޯ|FqeVHV{t9X-6oZ!BkV]>YO3|̿25+/ ͻw}IԔX1O~oe~*~"ZM_+}vdeoޯ7扬Oc4ڦm1gzΟմ" d{녎Mʭnmyᩞ&3*/zD^+ZF?.nh~+Aw um$Umʭ{Ue_[Z{]gAա5X-Ѷ-+nmWկ'Vd\+HM̷%_xZ]q?vռ۷*WS7y?35kԙwb3G_6UXڌv[|O^< [mK&[.5ӬuO6|oαZ7<ݻr/KK wZ~io47mU#jZ>/ݼƑݷm|ź<V㼏O)~mѯݬ? SR4nBry{w֮S4Skx푭曶6]`䑿ekk5MV[V´jv{vঽuCJ*oOeݹ~jG{D BX?7V/Sv^)}j<[mfooy[s6҇7䵳[;v֊ʬ۷}۫}AԬ-> Yt4jy/3zk餁VHYoUkTcKS=MռIkynnܻU_5|sx⇆WKլR&V?1Wo_5'xyuk/ujxyuk-Rz|'?BnOg 7ךr,3@Ŭ۶|6|;ok,w^G*_v*3neV_y DR|X~ӗjk7 ll*fdǃd曦̶p\26ѵ+n<őwo5^kv7h4ko>iU]٫ʫF8^O͵j=.>U55yK?DSd]5z'??AFU>jЮ߽\,6hs+5X$LWt4yOtiʧ)YnN@˻xNw}q+M{cUnizt-ժğBJKY;}jujp4~)DMV^]jZ3M'/%G^f֭<ɗSH-YdiݕV%s4uZ&jYI%3F|3w^ì-Gc'7,UV5e._2ֆ+}[CietdWOdviN>G?WknVe4{KYﳬۦ35u<'CMbC|,lѮ__W#7̿īW?T4ԗQmFy5ýcڻm9bݪ:;c_ޮ|1؞h)}կlk~[ϾT_dFck|hfw̿wmkCEkȿzhٕW7vWyO3:^ʏ??bOOnW6\/5H#7+X?h7}hz4wW:Cw1 7̿nf?h/\GB^o]/^wq|ţ#-|ޙݮ8(S|Lui{|)4]6K7ח7Mw]owͷ_x_\|AcxGV fjԼy\G%oڮKK7FroV>}Ӛ8Cݑ42k>?n\7׋3(Rյ̛K,l}~m=?xF-ymʭݩF74&j|5i {SpKko5i6a6^gkYK ՝F2~̿{5_/^8fηUYYkkwo^+6M,&e镤d_WoϵZMkq_enH2YT7f-]n/Kk&fm7 &YxVoamrH͹yUN<&R< :q4-u{5oinݷix9|mHeٚbC=CM*IkI?vw|}WHūi`uʱ6w7zr Y+챷N^ q+2ƫsHsFʻ|{UYMGfZiJ^wO5/P wn0٫OMռ%:-}g%hnwnuMOy%ڇLk? kfv3|+GUV(F<9Ig}=gv_M_F+ĭ9Njjo@ʫ3n_nvv~u1K_2ozlۧbmʭǼ-3K9sPUco}Zڭkۿ75O뗋jE}cXrw7ևg%fo. UUw+2Zj=KCQ7ޑͷkЍ(D.c[O,mRqm/^E&ݵv_/zZxJF6Wmn[yդ׾7ҳ45f//ެ9F7%8R0k#/ti5nWoݑUuE]ͷRэYJ*U=D%^{=B?G;۹E{%mVzfDoFCMnZ*P7#8W(f|VUveUWa\k?kZV4~[]O1nY叉?rN9sKމ׌/i^^Y֮dYonMY;Mt6άeUew|{u {yo!hvʻf٫G'v5Px*U/dzB M'[5W ,Zƭ}m+m^IUx ~Wg),EBI^5ijHI3Hͻ35wLV˹ڮUiu~iF&?oxKco%Vo/_&쿳'yjו]_xwY? ϖ,_up1jOs|D/k?^XnYdI4*,H ihY=y-J3WMIb*%{}6UWyfj;;ՊEjUZjz7'OW#$]{Oo~Gmg=b:wLDyyO{>hK^5^y^&_5USDHؑ,uts|jyfګj$$yo:č'MV]d2vw+mi>-&bP]˹WɍU[ٷ^w[xēx+Y;oeUo7cM_5^5٤HW]_{~_kq*W&GЧF*GxWZL3i+դoݲgkCIQh[vrZۿڍHۦY '3|g̿_k[K-mԶ[^GVUG#y.2 z+;'Kݾhcoyk2kp$r+T ,-wms/dh\ۦ_ G|:fjZnk7}ۦfy1t>/@/N޵%BWi1רC?kZkծ _:ph(8d3uo H˵ޢRo.ُOV xoui~"*~{薯yc= 42z!#𽕾u}xWͱߚ+=zs*-Ιq דkG岮ݵZ۵y|˻_k+jRGg|ٳMfWBk/Uŭˍ.G 7pI*I:}eڋ^#M=Z:Hˍז^Ϻ[jUlj~72ujGKVԿt+W7s}|׍v<5y)Cbs7+]9u-5c{ahco6կ\tdlYfX_^8fucB=IÝNJ_%yweIym'{jԚm.6Wo5̫snik>+]c"kw4{WQֺ|r\4+4s«}ݿciԟ47ȵMgeY[ǶI"I_eWOXi+\FLdɿ5}/"U&UWIS|)aeovjF?Ÿ^ZF UUԯVVWesWԟ2j3nWBewWDO]7jƯ&iֶz>洭y|t_Ѫw]ۿݯihյ̻cW~$Eggo2/:OM75躅~mEi?|]e/i༓?a?ͻl?/]Wl#m?AfRm͹*KcC fۻwv}b qΛc$qϗs6-Yqug wח]Gk#\]ڱ/n~f_j:ou7xm>mDMQd5XWu/5c^hk"+Cny1juՏj-lF6mgm-kGXɎOnfIWnn8aY%Ee{Z5 iL^&VoY{w-xMa ^,U-گf6Ykږ~\ѫXϹ[t,j|ya ՜nZlgVUMʶӫJ$g5OatocGկIHV'm}t^}JI/vw i7}Mw5hZjK*~핣˹UJex W dO$͹ogUVv57ὛYvͻoI_ͣ6ckqAk-MѪʿwsm?eZ_k2|j(SDw_oˋKqq#I$|37ަ}?Aҿ?+>i SSȿЃ2T?)s/?E?*XOnۓbW@)) :bBۣk|}T߳w=R{ nY[o. ~ɿe]=)vUG w'_b=?<':WDI?B?*ʟI =/"Te"Y4))'|#=3"UW?6skL֭g%}օkGߖh摤i? O _}߇WDOd i_UtQ"=?<':W'N_/ [0{*y72?tkL ʞ[~?yui&:lRIF̪2R~7{6hVeo}WA\.cqkچgyq7,nft*|۷37UuON%9XwF3me۷w>̵GK<7j]hAMn4o͵o.nO@ }&bMMs2ǻ]ff~ u[[6X!΍IeܪVXot+RM%w6oG+7۾ok.[y} Z̰I5U].fܪT҇.RMGli~񖃫[}>F[{e˹[AqѣxKԯ/4[geY#ڬŹ?wWZj7G/ow|~V]FmƛI-&5崛]̿ws/"1n_dTKUTT*Uhs"nӦԤz4O.SmYw.֯,L__ik3xYssh@ɿMOkۨifjFKGФxYZ6o;oo{,ڕyZŌ9eܫ-ZVd>#_m^_J5LmHrÜ1pK_Yq>ӧ[~י;S%%UMFiZgt>wśo uìBY틷̋[5Ocn޵x͞o&5]o۫J8?ZonZhy|ͻտtY>HzO/ڿ\Rx=VnfC>g,_|Xb\#GI fe{_ׅv南o_Ux|6UŜch)f\5} ϖͶ=[t[ԵeWh*֑m/3U50R.QWMZ7fXo2 {ý.=SXE,wZnwm~sWىP1Nj[}#K5ڶmy=P̮Zo{o^Ѭms}o6U#,k_8ú[Xf_q _ N[}Ǽl7֦pȫV5zy~oXkeZ?bahwKkK4m*گ#jMWφ>_5ǾtҲfn_5_./dHvƭH}7YԣObUo1UV=WN<ųLkce|1PV]Rilc_jq»sVŋ:Om.#Vo-+g~+`I]W_w|?acyxZeeڳ.kOs7V>jRT>in&վY>m4FxNOk$#jkVV9oս47Kռ]t3MZ֡~Umc_U]۶۫18#ao߳2WRVk_+گf_s3/_7_U~*໭yիZdQ!?ĩmj/4qVkym6Yooj?ϭIkG۬sGúo&Iv]j[;V!_q_2AkE}kjyj˷fkuqP4OZQ$ԯ,5_5Ƭl+/hOxíh{? M6eFK6\z4p5>Lmw'6KS%(ii?FN𾗫J;~]߼z3u p\+4w\1kּmk 䳲-273e=&)֚Iue)F\] &wԵk_!m-r7Wi~Koih7JdmWe^oAO}>=Rff]\zxPjƻmWs+4}*Kzn}xӯ>7,o|ձ4Ⱥ}"12NǷ]>/ ڵm=JݡhZ6i#o1,ն>}q=;]]]_jVK&뉙U]W_wz}itJˤس}HՕUV]ۛͻ(aIu4k[y?+7T-K 95([fVho3l2Z_ړAn:&|5q摕gTIqc7߽}e:$_6fʻJX3*HKogؖ|ͪ뤪.S|gE`QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEEKVoH t{xͶT sMze*q<J1"DyWc9t/t6x[ko|߼o]fx?Aeq._MW_-UmF/v~n+K=«I7Uv*^g<úEoy 6zۮ!?(ٙ[-sV[6LcxI?Mi'x}Z5ofۿ}J3XQƟ$aЌ>Pնpܿס}|TRnzd:^$]_⯺>_iexS(r:N_4{mvXz/._jZks ,mj",??>O沕c.zgȾkW_["FUf&VyIfkGMߕ%ccg,9R+.{c}=d]>qW fee~֤[ T/vrǔZZKo}6ʷEv kTz[_Vwroګ5ح/X5er':-|)56J5ǚ[Ȋ?ns7Ƴf3N,??5L=mj6^9VԗkǪ{a)|Ӗ%OJ4yJ.Ys1Z74]e= fUv"Hye17_n52.xkU)[_24\|uEյ/hז2[¿jXYU6g<$K#}h՚c>Vk//o#Z>CMkfB̭r,۾okU_y~$^CaA}g5\q|ʻU~e~cm~V}?|mG%FRyO{_iq}ZM6Fd5_׈Xv o4ƷfP[.<ۦi=v!ύZ?+IG ~4iizs֫$pn}Z_XVGj4}?|m+Uv<(T.tQ|5}jZxKu/ʶ|_^,l.?y k5RȻn&~eڬ*kkOo+ZsYhXw4.ᶒEX6w|ڿz+h:oggXw_VۻYwW6?m~V+Y6/NJ5Z/i4k>c.n~"xfr7Le&V[_?+GMߕW|Cc㶓ֱk3I;Ued*k| ŝ *F[kFXߕkQQU]Os˘}QH(((((((((((((((䏌K;\e|k^o/ZᕛsyqU_^Ix|)]>W.+WsW+}x/Z%Q+5ۡdok7 w1i*xsJ;OxO64Vs34,ݻw]E[68Zysi7}=گ𽿆gPX]RK9>hV|nm/ykӴVi>!-m|6 3mUYU> N#(; dr^mhVUۺoY~Uo?*qz2^??nYڿ/mf_ %֑Kfھ_i7Tt/K?̫gG|5ytyNFyX{vOɤidfeo[non-fl,/?VO;o[N|Ku,:xjS\M8cUUV_7}֬Yiz ˷mo#MOUE^U_qZ9k[]&~i7}i2u.k)E,?e$Mn*kMị[UYgX|foio5m;O총rHY[s|̪ۿvwo1qIHahVo &իCMAA4qב5ª5o3k6O>#Ԭ5/4覎94YUd7z4[{&-Y̙I}̿{vuUVpƥHݙ['_+uu[U0fmkdhŭm,m|ͷsyٙMBCٙY̿uZ3W9Ԕ'пEWy(lQVs7ZڷB>kE4kKUZ1^c.4u6ᛤkȣic n*lh%^.ݏ2S^6-d6XՕKT|EWJ|V4='./ݧx?Gok?F%|=_N&?N?g!\? zy$? AC=P(((((((((((((((((((((((((((((((((((((()C4_ SԬ!項?1|źM2Z]\465|-rwB[2Mo"ӭԛZj Mӯ5Kxa ګWn\8 Xlu<9,6Z|[2/^g{xwKZt$ϸ<3n+SW[ttwbL'[nݵvy{;_&Ƿ۹\4ʔuԏ/)V>&&ԢfEm'p|^[\hL5),Wh{]mw2[*^6=-V?vsz^Tq{dUi?mm.YME/iN:7= ֋3EY Z-|^?ߚ_;Yr\]GCk{Uۚ=ĿUOE_3tl|*ᬿ4!-죶զn<ŇYc]0=ecye.nSԼ+[[C _]^5|\ahvJy 6uṵonmwkv=K[ ^MɶFYd+*v]K\hk;]7g&|]wUeєa,|eNG=;mjI$:\\|,'vI4VCH(䷍WyٮKA 1|Y~fw,* xoIVef[8Wr5B/gsƧiEY7?ޢ`~^I,2ۗrK$m/[u|39}8rxY8bU{us4JfQZfիv*4t~~_C`:($ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (<_k+jXHJ_vXmxokJ[E/>i}?!E9"7{˺\ouvYŻˆ5wuhsHGbz(&['QEXfotoxx-20.08/images/global retinex.jpg000066400000000000000000000576361362435004500177430ustar00rootroot00000000000000JFIFHHExifMM*bj(1ri%HHgnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx, :http://ns.adobe.com/xap/1.0/ 283 346 0 C     C   7" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? | xkZU[ƗQ«-ܡ@id`2O<נcsE;>1o5?O+'>}ގ`Wydޏ5B5?z9Ʒ_QWz>G0XS p96ex{SF->E5 i54Y-{rbżGH#4\,zi@fxw`9?hJL.^+vW61@m@'mWWDWM Ho?O:`l8aJw}_\Fg;k|Jx]'^ӭPWuxQsoR_( ]?oX\$zT(S9n_qr;=;g֭3]\5irHh>}hYE?w?45i Sg֏Z_sG#QMh>}hYE?w?45i Sg֏Z_sG#QMh>}hYE?w?45i Sgֲv-CPt'3\M2FQ A;gRmCWDmq<1XE" FA^_sG#QM28/f5oğ&VJH#S$i K+99Gj/O&I '/.Mbko$wd (_ SGo|?&ZE&,wSEԘ*;h~B95?E?w?45i2}j#QME?w?4rH9TYM2'̎ 5i_sG$&ȾTeGF-55I &v"{V#QME?w?4rH\87H]j>YtdB.}jxP܍@|>A#;CQ763qֻF??~4hsD> x"v,Qӎ+t%mCPCjXWe{#QME?w?5X][;`*Z/d"Ӓ JTv>ᇡ)^g՛4QEtEPEPEPEPEPYz4mx{:iqܤLdFH_;uӯ/lzw4O.ص۶.0ήMNH(ݥmq=~K=^\X{8Ȳ.~VzW^G!B/CW~46-"eK*y#"ഃgi<W$RzC]\}"m58GT 鯗pVC?Sխ͍&H285䟲#ྕkk77e糏ˆV7t\,'zSV$Hci$eDQf8zN.,|c{kmy9Z(={86]{~#7/]uvӵ KC6ON*|P?MWTׅ|?b/ kn9ͦI~ɆD!Kq8;>1-7RѼ1ek>.w6D 7EcX"C̃<_LWϧ_u0]iqaqxWMoI}&[nG7~f! ďZ?HҬ<5vhXiOf`q.Gr]vkoasmW>ө ;4wfh6F$W+ڶk?KVB6 𮰷 [Sw//MRez?/*>ÇR n$[WrgY'i ;w4V>(t+;˫xujtKd$6^SK9M߅MOViQRDҞ{T!y$Py73`##m;/o3RoGW~.|oK@𾑭C>ul[q#|Bk־~%TU{k$Rw+)1o:먬6HOd6zdK1YlXyqr9Gt_iwŶ%&saAޔQE Wt3KM慠"uۘ-f4%*ѶgZuޫi.`X@X>hqۓ^3oZL^?OWþ'mRh.%eܝɽEށ2hZ!=&f+s&?.9z2{ƳiZ%+'Jf{6˶-|u 3֟ 3֟:"-+CҴؙ;K]bͅ Oּs6|Lu/ 3֟¹H 1X(ϒSfkGt˭/VPCjG"x5QKKks\Z[HobK}Iv~i`%HAegw?ZNѬ%yk;捧ZxGXѣm/t[gL58!pmveEF*_g?SOM_YqOxcBf"X,60ƿuz xڭkw\[k1]P.#HqXbK?i~(?i~)r HD!v088#kD;ox(cH]V-Gs[ ۂA AZz;Au;<뛫gr~a`w #}{kl}$Kf~I#q? h|U&?WεͭhΟoro,;%v\oJT넵Թ|)o|;^G~= 1Mbv1Un> 3Ώ744jf(dE  3֟ 3֟˜faxkIl:pZHQ*p9@cLu_@;o(cXM] *ԜӒOGۭG"%O8 t}3:j֚qquq$*BvՍ+艵;GFymDaIPVԼMcEҴ>LҦ|l ,<e0^%mh3_i#\5.(aԬ2BU܂#"5g?;?;sGD*sjEcJ呾޵{}4Z߅ ?4Z߅ s\T+;HR$%[ QAEjQEu9<7D|?&CLki4cG1cpot-K瀯Þ$"]Ckf}Vx-t&;!%s" @7J(o{_>=׼!6/E[D!:x|Je^P}cgŸ]xCYѯ|u$XM~E(Y*Ⱦ^~@+ ڶx=3w:o470#oڍ<r8ڬxSLJMouη:oוPx=-%OoRo_sh'OЁ _9G&(rqRͭHRi/ak+;DoM_\Pҥ_M2JS $s3GRѼMẖúi\/u,<'uۋE7pF\^̨ũ.5/:׃|CṾiwwZ%t 6N$B$¾'OЁ _9G&(rܛov5CMi$Χ.Ї?2<3-kIӼ)wڍB7FLXmv 6WC[x ZQe̿ۘOGmEq?x Z?4G0 ooaxҴ4G0㕋;o5_=ܺr2@,eBA,u::A{ WI%ҮOa+[#ִM޷h+"Vc{9ebW8R\ɧԨV<5ι㯊n< A \Zk mgh#`O[tx)d⾙'OЁ _9G&(r-1QV[Q\O&(rM|7 lPxu\Ouk۱;IWU#9ߟF(vz_\OrDqXڰYYzKe/7ӻ|?eG`n-xy[F#)0R 85xZ)|xY\#c'Zi )U/~:Eρ|G}iwj:Z:^[f̟>2cbtg ?T͛Nnխte,Qm-D;ϙ;O[\9x^ |C,+xoGHXպ'&FAxR _xo:_O_uk-٘1ONo-??Ԗo3w?o^yF};\], (dTenO,Ni[ōΟz%Dd< oOyGE-K2+:XRzϳW3vQ׏uO\xQtV~.fuTfeU,_@x!n /N 8&1'uq^'qQ?/k>,Ӯ%fvR vFܦ‚WY]s|%Va|Y5X&mrǓi9cQ!1ȹ#+q"_.u־$x6A; OR@T+gt+X|G#J]Zh15@us@1+I+⎹ Ǻ揪Gml嶷 ; ~#7{^_Od*OZkFmoE?R*DӺ^,IDQFx2_.GE܂bY|_݌TgaϚ>\iM[ús}.ބmX8<,xm'X+aXxk~>Z'<euY3N\S$udI ` پ|U'G5ψ:&3H}'Ef(heK摷TAvw7ۿO>B>|ً62 jn]MgV}C1XHvvqO WDsiֺD6%޿9[;Ooow|Cai$zA|SIssi1KBD@Q6'߳?[]GE8Z?Ki[\`γS4m/JWC(Ye (AO |Ix4OEZ=A;;+mtӿ&[}כ5 zEy#_/Tm+Y⫔2ka}q>/xsRR~w(RK۔oNrneͱ".Ǿ> jZ&i }btm6 {|9 s9AC>񟇼c^7N'Y4M-2ŭˬ6d+ ( cFs]yn߅QEIg_?oi[EGEhEQ\kxx ILGE & ŋyn ci6&vn\/߈+ O״>8I].x=}?JYm˦8CZE+9д˹٢Rďpx WKJ|M͛`x Y>r),5g kQ.m[.`FmN۝ʿ78__^}YE|puoi$WwB&;^ UVT $q^Ş5wGϤgD$Ӡ䟹Tg*6J-ER(|;WY|a.k 4Ցx!doّ 08ⷿ7-Wm??ma :[>Kj~&z!KT@F"Ml`4i[\roť/jiƨP_UL_BULumn Vmf>6Lm%00>I#6]"K@+/jŏo{s|@EEN] ]XGgEG@6jA|ME UO5^Y#W>>/[hu-[s4EQ8 2I'5e%O=]_BQ/jiƫ[;)kWW1q.UV䪀jkKH-+f9 kYmt_hE%`͹бAnOjP_Uom/5Y&mF S#?Q)ep FHX'!2\L o0HkW_m~q2p6jA|ME UO5]%qi_WQKcDiD @`8ÞU?B! * tid ]n2w;HsnN(kEzwu*:pO*A$3j1_oE2hn~yy>{]W?*@; 㴴08mOgy5֑z|6 kW gEf $ YXzO^{[]=C^G79 ( @g(;(u7o;Vt]5QrSm/42%jI*x= ~A;?/7\Xex|>^AoOkbpرqߦG pSq1?>( ZN_AuY[NhEeFA9sI#^G|Ejj#)9WyVg1R< Usma$ dv> ~A;?/7\Xex|> ~A;|9wiqm4wIb`زpy~X?ȧGDDTɠ/ígE7c}WUIm'E%ZイEH2OI$zjs~%YKdXX-bbM6-˭ 4EdaT8cп/7\Xex|>Aޝq?ӿ.?OyETde,+ |3]i:xScuIuI+r9R1ϫkww>RxK8o.c`:dVO mًF*JaRvUK :o|7^+4 2ȶwm6f<9p 80r#o+{PCSӠO;oUе@E-InoM:L ,Qdǥ/1mhZKOdP."7cf '@ *x=kO ^[udC6術',Fnoo,a{o}-?%u/7Ն_WVmv+yp"E(Zd% Ұ>:V=zT4y4lkIEVn9%꺇K Ki:qM!mq8X)d3{ws^ ٤Kh>3Cvq'-MOs JH8)[ik_k3Y_5xS:.&hw9J3f(2n5u'm"MkFOV^\Cmi*i8aRG O@k;oD]a4'}/@ѕf?"A?@>[[-IMZFTVKIӓH @"iZuyVZI Yԫ2TؠoK~ U5)y{"աVyefty>o1%pFm;1'ut{.u-3VBOii:~6osjd%77R| EyBMֳZjhk)wC  GdԷewm`nWic\)^:/Ֆ֖g\n1|*SV '~3?~]_/6h/dhu8ep+?t_Z.{i5f'&]&9c6o䳹TnRHQsX4x/W[h>!!/j:5sG+02,XI#*FO =:¿-6{'6p o(M=ͼ]F 6@$ %;ۻ-zWi޷swiQiPR?;4Q"H^-åY>$-|Ga:-z.Q@V!Z_u\)PH*HzZQEQEQEQE}NTQӬm7NK%bJI <>7Һz_U3~5~i^(v7V6("+$BAe 0suFA4V~Ξ2񘻼5^Gtuvk'E1&-;ض%.HoiR}6-+7c pҕ|7`F I`YP:@9YZ|'u\m#PAC4{6fB-_9]r0bP96&xKĚOkỹYh.?Jeo; A^E_mi%o/<+PjxT26\[hΖVD̲@ӗD 9߳6=Nڮ{Wpi2 CQyĪƌrX_CQG u'OP񝝵嵷 j͙n^ռg'wWV2Wԭ)粅FQ31PqбֽwQ$DCyMgZ.=Ɲ>!)36e q p3u~4^=Ӥ4{Gm$n!}keܭӒ~[]͊_ևΚ_ŭjWws~[˭k]#C6,-! <$#& SJ״+SL,IR&dl\9 3׭{%֖O =KEQEQEVvz$pIfs#A1Avּ\gOG"1IC{|If[K-.P=RE==H 5+|=~?xך-x+.ŤPٶ%L(Tl9oήg*LJڽ72;TXX͒lBI/z_!=U>ߍ<7cxK:{v#Aj_/\|Sg?! qOO_`Gψ}ZKGt\|Sg?!4Q_?7$V?5?&s?u }nMժ|} qOOG5?&s?}E[>#Aj_/\|Sg?! qOO_`Gψ}ZKGt\|Sg?!4Q_?7$V?5?&s?u }nMժ|} qOOG5?&s?}E[>#Aj_/\|Sg?! qOO_`Gψ}ZKGt\|Sg?!4Q_?7$V?5?&s?u }nMժ|} qOOG5?&s?}E[>#Aj_/ Voww,{ľ]>wK0QFXP`O_@yWzՆN֞bI)JA69Nx4g}WRJRrl/'d"}qOlP Qn^a>A{QSx[÷WZbm^5G [¤p 9'QPYQE|⿇W"kx 1 xHo cm7f___;i%'tKX×P4$9a6J$IgqA>wE͸IKqQ}->+l~ t[`o*o!.PapV_f#+Gѧn>"xOY~,xtMdfb"Fk~o54bZo \_fX->E}pZJ%29l7OM{ k^#{ Yy(Dk$`lNFyw3+j-6miچev˾I %%2`y݃>odž-NGn5M--SFFx8d2Q[^oI ~-Z_+%oO42Sfm?)A=궕+=_źbFH$yTll84HxX4-WpiR{կ5̍nRvۅk >Zxs}j>!>hO$9;h$c?p+ݕ?+rϵ%lj2PwbB^FZ2HlؠkB8<ȻUW^_GҼ{K a=柫5Gj( fܻ,܊nTQTdY#6YH b 2W9@:k#:%1snX 0HzȮo6X؃- k,j"zϖ{}q?RQWAktZ]wWAktZ]wWAktZ]wWAktZ]wWAktZ]wWAktZ]wWAktZ]wWAktZ]wWAktZ]wWAktZ]wWAktZ]wWAku Z6n.5-,.%HozzUESKP*9T紌oKyTEgb[[ zdUhQYؖ&ķ7@V%ɿ?-MP5?ڎgvblwVM-6&8l'ɮLmtm6O-l!Kx ac*? zdUؖ& ;W_:D2^ XrYsh9zdUؖ&|4zZMj0!A!bo=o?2o*KyTEgb[[ zdUhQYؖ&ķ7@V%ɿ?-MPo=o?2o*KyTEgb[[ zdUhQYؖ&ķ7@V%ɿ?-MPo=o?2o*KyTEgb[[ zdUhQYؖ&ķ7@V%ɿ?-MPo=o?2o*KyTEEmlyh29Fs&((((((((((s]P/m㻶3!w&2A+lu+>[C? `s^לfO +d3\iւ<9A{OLAw>yF0[9˻G ʊl2Zx:g'Ww<ֿ%מ:2iZci׾#C-2f9(xKrN m$6Vx\%߈ui/uXMk18M(|Cjקϩ5=/š.q}vdymTEI8SR; 3ZԚ=7/,i"{~&AwUg-_iiw ,|UuLnT1Jl[u#ph ^[DӾcu%1\Kr,l21%$@{]H`<[sgo0K3L k:-~,Fԍ)01x/#4<-'[*l|ydqc;Rg^~ ĚΫj^.ִwg%|A}1qN^d'tO>֭W嶴tD0DGlÁ[j:%֯oʍ[w,+fVm 9}CÛKAUk ;֘Oxv`c桨`@daU_x)ab8~|O;O +6}Uʋu0oe5C⟁Ƨ&mʿ]흴y&9GvPx27}1G#l3-: >-k"#C%^5eE~u@7wZ7? 2 մ5;LNJ;sv2spAQ~_+#OF 311 482 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?.A ~ڸ췜]9?>6b/}뎀W*eZkx*,EENn[p*2 >y&0aIL V9~?-paܾU"$m率S"kt?;˘8oR*1kvCje~9 ӚGlz>фxRkOU`o"x5<~jy){N|.=z{9@?頍-⅙fnDSe|;Ψ(5֯s.?w]L gco'i =8<~F}4]Ϋ pqֽVkd`7CH).ݬ%Kr%"-":`FʼgivJ4+${E(yZ0-G;LTIqlg!6Q)99رm,Dm$ko-Sv56Z=$bA%@ApYv ]0NXgG!vm>kM*yqBn#CPY\ܫ 21UKq,wOG+>y|a'SCO$i,9?MfqowvWFሦ74HA'\O?{j[qM4 `n5")N<ᾆ""r~+{-KU]x4|bO`W͢Ꚗ̷čE0~:l^THU4BdJD3ܠ.6m[ڽ^wm BJ^Ya X1֥/#" -|^ϛS3I,h[t?w,hoj/ fW$zי][OZ&&C_A[]Ȥ>fz47hŲH ̌cZ1m+V-> $&Bn MmfPA +)ssn_%U'-h&~%oSwU-o'y9u [s*SV/`x$ٝ0ߕLg 80&G](bkKZJ_M,V:ddV!@N .4.K~c0}[ⷮ<5x+YE GWګhZEOi ,nǩǠ\KGVv>XIϘ5"q𪴣/W *cmI[>/iV cxwO<=s m6Az~+BVuڽh o~ DOoA D60%h*qZ~N2 zԖVPt{9ZsϘ#+xz>i [l8C]ŎPET_u}mp3Hb ׹_+ rNa]MnJ<R$" b#y=z`UX-1Eo&s<.N=ڽXa&e9ng)n"VXzG.YN|.;.bGY&2OJLj.n-=1%lP~vOLפZkIЬKtG$I5sJrq^6oYn,Ypr&UX\r}?ٯ:te}^g.uiD̸c>i6$WKΣǖޘk2 Jmn," EnX^c{o; J-[A<5ȫw[Yh)W"_.XQ呚zzFZi:bPjIF#Of/ ~)[HzGan,kwJG'{n!,v g,W6_3׍zjі \ֵ͔϶$EÏ_zo b+޴Ov\N'c, 6U(ڀ9鵾^֢A$ BeK|Hz*X8mQɱ[{ !2`b\=+:(+=6!ɽe:-wк I WQΗ "ppxvj^+P( @^gUW2-<2|cTsytA$t>rÎYȷr+ݕD xj|̹7E}nO7ډA,dd)8lz/۝",w|ʎ>sBm]fpIٺʃU7[~NYۉ!_c`YDܞxBy%0r+9Vet0CcvX\,';AM_z׵~1Wy :6D3q-qkɿaGG7;2,/_2x>t$&ǥ}.|Z𨹅&#|-ᴺS |Y&E>hڅ]'Fa v-ݮi|ÏgT:Py,u;[G[YY~"FFu+mS`.ЗGىxPmq@>mUoaq܏!د7QKFW5 , x#L ҵeWTRGZW * ku8ķ@G=]^hwz{ $HM@~=/jb kH GRޣsX평l!i~AN=fRk7 =fWuV!K u8G'=єz?_$b颇dn#1U6/*qrI8Jk2P6#sZ̗(𵲅 $t?rVKI#\j~Qc#lxu]V6˜aW'70G5gb$~ uO䉅EwBsڶl4ĺLSE,/}m*JEgD,xv{cҺ [Zd@P5(rL5>Zӿ7ǟmW[ku8c^=}k r˷MzˬV~w?HմVLyLR\2yܧucJv@Kwk", rvN 7x>)ФPZ'Ҵv{I pkLm2q}mF]{heeWnkh8yNV_oVtvI^6tOjt(3EB:WEzG|yq{_h_}PGϚ+خ9t},Zu׵;MB1w jrjO[HfIAf xnڬN]^+Zcsj/KH2П4{ 4O hIoY)=_IO߇sϵq CacܤzkQG7/oOx 8m٠7Aoǽqw6;8~"S}3'ۮLYiv6cX[f̴O K=e⹵޻I67;9Q")c^JYŸ&$T6 ^}G\ي2w"=w3x{vB%mO?ݬx"C7QXWr*q^=L4}F ;5%݃nhV.EŬP37IƸsUIX?]y] E{| g`̹$}M4 /~-]ӆ\3=tn6Ozs^3+xUO_5?5ުHPmyDcb;KK&X7\5FmXp=k֮&VL*iI)դ.ң%X'+f ;{E2˽a=i92Hxj\7sZgmy)nYFk\=sff gctuEylֳn'KcJ7Fl訏w->e2Vo8aF G\.HNԆOv;HKfu;z串LR&>p&?bQ6 "Dgu9%`0IsG'XWSI,@|a5J2TϯhWa\[+z/ Bwzn{K[U݌ s= m?+cL1΃`REv^6?(URեp"{y6AFx%(b6}?c~%sdz{M4ޝ q+ z4oQ'O.xWP$sA __+ֱZGoK k[|Eyy?ĿSZ)buld}+͈†kwA9${WAT"aNBҠҼ9wq!teij;˔'9 @MO,Έ5t➣M2;-BPv$L507ҺwkudnNHw1\N1[P3tM6Z Νw#y ̌ci:95yIKI#8gYy輎kOMs-܂y[uL2!)WÐ'9cbdF sץwgbdp$ܢ1I*š<:Z7{])R7]ŬNSIRi7rENϐ;#FllZfgcG>t_ ^mu{.Q5`E?v9#3cU]_zv14|TI:^+Ͼ8xj [Mf˶ŵUp}8(rCQ]?Q4h R6\2ȯiV_P==:uʤ;$$ȹ G\^5^jT:Ƌh.\ģgu*zdŮ'ʊP:ዊq24e < @;*k6ŏ2#+÷~ U rA+Դ-oM2yQ7=8ާ-N+nkv,nJFFV|)Jy (NgI^.m2Fc+y@F*t-5*R)E6_7-hږk ^P׮KBe8+` {ROyL?}]dvw:\P}{- *[NTubG']ȺvYynlF;|0?>! w>^yr.,v1~dܻq\34Ƨ~.lsZœ *ɞIo]~ VﯹLF+f^'q6v[x\ҴyuoͲVἨ|!( -!ɨKmk٠dSʌpay,y'9Kg[K."H[ 9(3]vu4W1fq9-WҺ=~3#"KHE Oޮ7t Mgڒq>Qֶê$zSqi^ G{m Ɩ}zi7+~;ɼ7%ڗtIlp'x_/!Y~ծjI4Sêo<,~n^KZVc_itr2ݶ?Ndf~?txvu+:xXӀ=+Hx;N"Y)%98{kLWT]V(FCc?|e{ԴM6šjbV1 0WZ ѧȤH7K,Z)M-]ejwqB$LcX(vWVs)a@q\Τ6f+RK|B4ıÿOlxsƓ[oQ\%ܳr$/QURhI-W6"1Jddze"۟s^'H)(Kn?(̒B]$?\vmbq=+{8bPJEnL0eSF5'8U"6Ovh&37F?\799ʞN6ϔzՋ3֌՜.lj"lExdVRH݂{WSޱ%T2ۣmY'ПZI`$3u-4q>y_kxaoZ}V[80IoբtLyC5#' ݟtXߙM)IH1鎵:JﱤV8?7֬"-sY6FֺTam[)mDhʫҳj,[8M1Ԓ7V+e@k.Ic1n"yqc:-ia{ia2^&ϴ7c[sOgbQjPDn'2!x-s__kiܸ`.<͹+`.5"Z[!b y#`]Iף|ۘՙZ~u.5Ǘf,<}3RKpH"؜+Mqk莳{K<*qHzct셌rh찍3^j8ьfhxwHVQ̅YGt|.,4-,e챘BT9Q\}kmT[.HntMJ<_bӦHmg sm22r3W*ӧJZWZ-jt嵶K`Ո?xW|@jV?ٷP8< @185ivyіѢM4i5}c8; xLcQvg /¹W h7V;'gI-J"ϟ:Y(V֭XihJ䲡U܋n\|dnR̗CٶpUϠ&[ %yq`;[a7p]4K3=\)iOq\G}xaZ!!$Q͹,9$ѴՕ6fʸ3[6Kt{}b4/ hEݹgg,= FVOn=:י w^xM#Escd.cMeŀĺ^\(E0>rq޺?[hz\2h#0fI)5}KVip jP'O>bʧQr3 wr7ݎGo_ŭ6lX)߳EͬlƇ޿+tB6AGBrޤסZ!䙭se"FP(>cT7 v.sZ\MqLrWTo{‰xx\K.p_jҭ."e,f]|J@Rk-mGĪR"'O >ijcVLX/k^&أB~,duڥyDby#bDN} m% ifX4ׄ#I)d5[N4y,uTy {*yǽ{F%!k Hg}k ^ƵZr7ѐ>5Kk%K_29Dv_|{  [W6 l,NkJkp`!)jt>3χtkMkL4YI.b;g5:ݮhwV=<*_m3)k+=i4[:\hIwu)m^߅xsus+dEQX5a.BGKtZ杨hjvڎc4RJzm]I[^o.z;W4ۡV{eYơbt6Z='_[Y$r,W1BN9xV:5pySR>Xo.ȟ$?}5Ow Wɟ>PA~uNEs8,S_Xf-M߭wx/S!FOY,@~nŲ+ҙ<++,^5?>xuO[@Ed2/0OGa+u#i9CJFͅ73GeRܕ+]u Iujv&' -.O7f9J}3@}޵I5JUݵEW!e+nycltzZ^yV䑑|PK0#G؞)6T/VtckWY/M|B?VՠOZqL{ERzPʪ{UN]WtƳtVH/Q,̓$_NٵEBчi:ҒŮ[|U[ yA4L|Mk *\B_B*!|ʛ$'ViiNYT[B@}S?lHjO+ \I4h2NVm2ֽ!|~ztg?+#۲+'rs^0oNb>o 2UlmIKGW[W 럯_1}T_JVWߴ|6X* D@Ь!tC4ϵ;XQNZRO#] )5s-,XfIcQjݾ.IerFP<]<]ZVIkI"Eo9nB^q,jڅucm%}y]vo<e.dX7_B2xܾ-W>u9DNdVG@AU9"'4/vE<v,) C\&pQ% .kF|^w(Ku$ЦJF2c=GZr|J:s\[[tܐ\!ֲ!ܘT»&'Ғ/ 8c'M!Is4H#p~Vm!VFFж{Uk9\YV-늛EVf2:^[*:9+Z+fSO6ٲ葑gKޟ~eOejǧ~~V?u;3~uš RH<)xrk:찋f 9kEu !c>ya.=z?xf+>jAH:O]5;o]]Eޭb|zoAbaҵ[S4Lq^2'a*l5 ̳/0]L^*%uQ%q2.^oZwXO {k+1`W|+dEMљ9Za)SVZ3gq~;7*F1zU_z mHd2[]YO{{IFZP>n>9o.Ү_J$&UEW'ʸGGќx;>bD,R\+JյJI] BE3Yڋj!?xpĞO@{4e)3|)$,Cue5pvJ#I"7mײE 9W֊N}>c#X~'8պW8ZL o캭?60;ȯoO5w.ҩ6V3QjQngy7Xꛓf֮b3=jQUY[R[v$ n)=*G;~DDt]k1#Ͻv-tGsb"Fm6?62B1UR yĄ2m[_OBn ,2 7Vt5ĦDO ⠞UTV-S xzZi.z>vc=4ygZӻm:ݭn%L?CUEd MV\fa:3)e8Bgkutst_l'7Ҩ]3G&S:Z;{}X\oTsJcEGPF\F)'L.2E1s~)${׽~̝dzd?6J{m5^|Q4&^oR|FHqLlqT2yl+Оk{k,QěNQz9#v|jmx4U Eԃ$s=EyG!&UXr?/pxnG?JyZY1!"?j'Xϴ2^aVm?@LUdrç *boPsU|z]ż~.ix}wo29?*- .vr/xE\-ogʎ@Z<J@sDdޗQNƹ7ZqJB[\0 ~TkrhtRxR}J / cU[*hn&V6eB7ڹ-JUKPF~BލXZt˹15!3iV;3iWa³po<,ݶ, ׼1 -Qաl14AZnʚ`oOڬ╂}]|; ]#ϵ](ϥbͽՃtFJnax4oˑY"u놅e#7gD&wBNR̓gIiVxtf2,&oC:E6QB"]$w#9֟j:*)4N9 I% ٴl%lHcB,svE|]<T]rVa:?qm+9. QBWcFeȔJ[Nq4|/< RU/^v7hcU_*' 8&צڻgd1Lڹ ;[:mI%Tֵ5J(909޼fmʍ]s^^,7,`/`Ƞ6{Z5e@CEs䷑qϐr!Wf%Sc8-\͢i:֒gt7 #$'e|>#9dY25j+}&CYK) l: F|lדB%{⾱>}O6KoDn*# nxg%ՐCZ66ߏk-fӏlYi}Op#2efE< c'<6+t _Ot׌yܲ~^+m#ߙ~./ao5Oe_VNxLqNzׇo.!Mx?IxPd,-wZF$fqL4F?W7{$17 ܷҳ>ˢkM}4m$Ȇ٦C9E=2pN"Gl,[5JH96yGPO6&B1sS_|2"jZLa(v@^MZr28 󂽍\hŬH3/1j(]L3820tzV M&}**R0qޞ ڏrEWY.P> (VFs+HWB_5X|2ޙoO$œFK]ԌT^F`wCnќJCsNKV?02|IFqA}+:zsZ-V/,> FG5ϡP;>3\Uy/?{?/2Ke 99ǵmRE*k{qZ%Q̾Tm,}f\x}x+"|8%PZԩCdҼ ;@_zwuordA:gCVu pi4bD+} zWxFet;s 7Vž?f+9џo𢮷F&x_z̷-d+n~U/ro?BgOCA dd ~0.5R$QoژS]g$ǘY^%>=*G ~b67T7ky* <2~Enhyf>Oz[8琓̆1R1iLR#-=k6M^()(ba/67Q#$ 3-JPd.G>^VOZ_[[Y@f^WxB IA|4J~q1#2)~7Kl;ȧ:xSÞ|h0T1]`H%Ξ-Q|.4Ӊ#SZy`a~W3>$%pqE{΄hϫH\^)qV .V?]wq'A[ }Qg퍥Q{PT 2,jpAwZ.cZ1} j+=y[3ǥAylcǔzzϽS3AG*ͧ4Ҫ[,Aeqye~oZ}?{5\+{VqO.vFGہ֪5?+ Y@=2*ٲܹ+mќSeS_b}`۸offٴ{#(eS_rpMG%vt\n$OJTW*Mў1L}7{묋Lvzr:Z~_ʧUv;`)=8׹]qJ_x暢Rlb~_r 4vWa#_EFk0_z= }#mA', B@4x?}5dȻێ#`C$LR)zfINQ99nCDTb֭XXew]AJQ,_yΚw0t$y>}܃RZx:s4v đc+oPv/T7vl;fϳs_jz8~“麖vUj3B}+KA[B tSVSFjã'ǹ.SnF}+bIM-!L5̿f}bmI?>(f%] נ%CK~ I}*9'ٕ[ma_vv֨x/ E*K}9[JW.E;7c5v_ H asZگfOsn,995^_ IsjWOŚ@ V4tѩqګfʙ^IdU~I{&8J 6Y$׾}vJݙʙZ]KEe}7^}wU]\i\ƍ Llu:^-d7ɑW1eg>iͨʟhm~^]7^G ZhXS*-\]6)r偎>_ o٫ơ%c{EbRfp#3iw ٕ%5CFx?X?Fv]+340õ#% ''پ_sjGaX$uW?a3sks ˞+Kxd#g5FO<JO_پc"oo r^~`s"isnh%6jϐ>?ZԢ,+-4oʜZ>}Z=ׇeF1cwpxfUCtV mov䴖N}o3Q')chkDYPOX7:4#%8F2E<{H5fl~Xݍϩ7~IgQ~N2 (ewW]R(fJm<PݶoҶǹmø~?HM(PH:W"`* MCjHko]P# ;) ɴ{L v:֜X@0FbJG˖:%.Mݕ<{oCo4 +8ņI&w9G"ȃ(N v(M",~W>o H z鐇 0u8+PO:/W?}CQWeKWeQQ^緙{#fotoxx-20.08/images/goto.png000066400000000000000000000361741362435004500160120ustar00rootroot00000000000000PNG  IHDR>a IDATxˏeYvGeVvUMmlگƴ 6#dCb?!L<1Ĉ%FB< @B 2v[n6]Gefd}k}΍̌87luskZqNĽڛo<o<-ٷ+flliu7lukQ[pg#-t=YHl~'ٍ6Ik?qg6:3w>gwDZX_=zgk??9 KVOGTN~b{o%oKP3Bd%=p֣G?M!|~!VNHikfZշXב@^,#l;m%L|vԸ.Vo$;q?.~zNveWeX4kg/{pЎ>>yNvz)i6nlnӣ÷G~ڹq4Zfs-%˲O|k_ NFwq>;?w}e޹v{Υ<*AK-ʼnm=7A;?<~|~Mmn;oOmvuXq7|S 劒{xo.g{_n˓+:0XiMNfG߽3{Of:<^4e;! 5O5'b; ryvt-A[{q!ζ6ֻm%) ŧھ&f[#yx\o7z{';?9_\}>T콿u;;d˜ ]N?h==hN㶿OѲ/ $@*nu=yi/+J{&v] xXo|޻׎)3N1v=}jHIi%q^Lm;|߾'{er iK)wkpZ;j+[eMsU'm>hwiz nt>|p~{ۭ!rHEtZ-DVaҳ-(  _u}9 S@lQUW'wULZP;O5q!IĮZ/YϝvCe&@莮}B[o.-9޽5o/>޵I ?@ pq% ,uyG@cɞ}A2N|]uH\u;eqȠzmCOh!\ ^VIS'Nڗ&`k[$E]άVO6I X+8LrOO9H!^sci}g#0H׶V38OA 缲uF2d' %X=V" S㋅ɖhJG:PL[*+{aVO9A'^,q uEp0Rl4~j̱N$ P0ZsB0iC 覹*ZB,UzevcFN!P͞_5_x.R8zV+xY71tv;/ er\LH^#s@.9&j19M+x; *:+BmGWV' !A żc LOYl[M KЍ@ /U.cx= pBxi>l@jBg Я3ξX'{Upb~}-p@ɶZ[hX/j? ڸb C,Qwl:urҬHֿR VajxUȖ/UwLh2qՊ29l>+J)cS9r0-8W^z9dg/ X%\"Y? 9l< LO\jz+W`+XLOŦ1Tm&bY9D8/0 3R$= _1n^%TN"m_x- #Р;) ՔШ*YLOg*3Pfv+ (me"Á:FH[K3妮H#!:%\`= ** LC 'A}eRJhߠՀǧަ'@)&/Qʸ5FNJ\ԅ;ռѕmm<3Hfm K?+_ Ag9PKۄp]@.O |.]#<1CYz1lX½~* 6/^-2"L ʜkcS@ ȂA$x?9DY)Zsጕ' pp Ҥ 6 $4iկ@~-zB==$-SշA^p3 #V~?tUq񲦄 <*aX]Ie" "5) {'''T@gT}nT[炆ZIv=28)vpiާ/e&#bPW!>CXS=cNۏ3@v:A.}29, 9 ӭj7&m Xevy}PO:g2"OؑIUYܽex `@6N_C^4>eˬTaی`ezDP{"{=0a-W8*/'F!_e=,kq3k^.YZdY9fꘝgyŸv!Q*1e3IO63ot0>qj qǞ rv 0n8LY4mH0$C a,?)u),dAz C`e<' ~VEvVTb(PY̊=En }_ `TV B2R >(:`SA?:?x tPM-k0 1!8}M$ |'Y4N: @ }SוXx U0wf.,`F 3֋DA$Az mJBftեEEv GH ):n>+sr^KIx=p _r-x..ȏ|b `HNKg1KycLa|EIXޚib{BU0+&Щv` G@%LH+m H0~9UpY9ӽB MOT,Խ&#nsLxM\rҽggͤ M`<W) @CafXNp(J~+`XH ʙ*{n6z֫_V΀A38X6B7,ucw?/Z*rIt}:WtcJ{utWd\9"=cvWma\'YSaZ 'U3Xe/rHE3xz/ ee1S@5>3B"D jkc&'+rS)gʀ:i` _qj78餳\$O]Ͻ|POG8BE’NQG mQaQ: ier  K}սIcdX晟v`'\ԥM h&xs5Jm@_ &%|HlBht-ԯ{52=p,Z ux5ܓ?_j7β =c0u d(JBHb0,l'\e_HzZ0N U૶> *5 gۉPvlO51`|5$XxSFx\P9+wqf=Tuȏ0Iԩpcqj`Ak=>Z/VOND^]ns~\FVD!T dtJm]!uk !fǬg2  ٟK0xvZ|/WON 4HNFw_ C`d0K0'åFq$W[Dsr d#~#hJHF"bK3{FS'X.|ȯQ ]ݧ'"T3TFZQãjfXQYlKit"Ë8~&g eǩe|q>HTR`^1p|棑ٜ(SE~~RdƳv~]Ŵ}_UxU{~@) k}^y o(mxv2' l^4(}Ja @~yp/S U_^}N u%0ɼVΩӝrUX,#\nq+`%6F8 ".U ГL0ƴl!8=[U@`Y 벑F>疞ɰVtQ?QL\cS0v 3-?ν׽y&+bqO=_XZT#8ɡذ/>O]8J 00F(xԸ?d8ɐW7HieL jBDANNAx~q!]I:9-͉揂 , 3!rk  5BC0#.k$@(}(Kw L?PT=1ٷ,4_l$t]BMk*}IG`""H j#}ArWFx(?V&X$Ph`ubU(r(`vHzoh@7upu /;tSzŒl%KtGPG_5*[.PIG`hc^XOW:Z'@9(-E%Trm ^Ǚe o3Uu4ߞX-g8Q@"~>S `lHUd~jl~#jvTB9kշ v >1{qQk$,pԸ=IjW:J6&W v y 2= G o4)Wm%/N<ԍ=*eu=*|@Nw]IHYYqƈAg9I4v'A+Ub$t PB>3}y4>[ph+VHx_.AMP} 2"IDAT3J=c_=#:f$^cVe]9si _э6f4\b'5#؏ c8t 3Ih WcV9 DOc}To'qVeg%gz6Q Ѝsd ~d> Fn^4ρ ںFzD[hDpNx̘Wh̐J qtI5y(01Txi2(d,O>Ů"> avf*b5n/Źm"8 Me`CDl=  Dzm>H&8P~ڸBX'@DNmğՍ0&C`= W-8 X*p p轘H6.cZkb~՘BR:>yq:ZŃKA>3n]iM ڱf{[O~[ӗKzF.41LJjxiC_>%ʋCRabJ `V>,2qT ~(#)u18c:2J7'"0ZOgWyՕv}$TGezHPD錯0\`(L'3Pʸ+/|r4N R(E_m3&0zX%ן_}|Ծ۷_T;ؾ?zQ۽<܄K+ISR0Sb3@<ELUMhLOfXv8!!h9z',8W! 3Z)QeVXrce4?{IVp/?X'[nGwCO$Zۻέ'~=}ڳ+ˏDqyE͏H>z`tHl/!+N"= BJT@8F 6Swkߺ;mDԡP'~!;m'[Wvok5-ho}7FRI2Kyu0ߘ_)PWPp"WSHL!g fjF;Y@L<*ӫ@xrA R~Y{6zEWU:ۿgڑ}EM ]S5 CzN\sNݷۃeo7\JHurxl]،o vχNW Q%ZLgzX:Aln;< @C\F(|`f8H@7gTuTq+]m|w!ΕZjUak.n߹^W 5g/q{?:Bvmtd%$aȊOP쒷5@P>UX:}~~B aX+8A]44M,f90&-Lpi􍖡>G@hB@ϟs_Uo{eQ϶R==0J>21=JҢt~ r##zH7[6ۜ3q~N/y!8%Tb F g0)tǁq_px4tWՌW)ɜjbY#pxVfO j=T+%ƮlXp%֬|3d0hp<%j$akFt/@j>5>h`MCgX` %XhL^1cKYtgr8xPQ7Ðꇑ$1N*^¹ l9;pE$I`"3ug28pɾ:=㾒_$^>\j 8|Y(|nƹ@b)er QW# 5 7ӂyr].oAr~Eff*,q_}^]j7)}ڜOT|!ޙE `Cs_1x `L837aK 1 Wߎ;zP9N@?x2\1-xx$i` <6<,91n\[:~X -F~3wU ԭkT ױX# h WDq~ (;X F±!苇` ڣ1xj׸u0Ɉ$z+QH?thaԲFH$J\0Sk9|cH _C\]x~p60M[۹#G c2Bs pEpz p+WI=^P&4y"YkD q?%hs :_pq;dq 8Uem)@ 0*ȥ 5I/OC+#H/}_;VBڃa\a|kyHy K~A<@$F0$ p$ݘGM30^PxLeݬ-(rVӜXHT2:'T݄Yi` .W'V8QQl>qǃo#9иohje~3_y 82CFʾWcHc?`B@tgjdi _53ߠ©1FRMa}>3s :O?V% `W"ք|ڔ1ⱵrpbPuN(9pKr;N `NF=<5}Xtz,1s@O5Fm~PuH`˟N~<}7'&'F9`RzB蒙V:FkCN@UcFm04?]wȘƸP?7G` sGFw_j~m ʛ Ržz |cp*f 05uC?[OKR0d~ه${@%/au<7h~g6xc5l8;Ш d-\_I$}\ pNM^PET(~(+Z㌡-Pbp+@!<ӊWoxLT霂 4M>NnС 1FũG`<yO[q`,C%FԾ_er` e D #\Wh]g_f P&ξ;5$e]& ^xhT:!Xi,2P[Ke'TBs; xƁ&@TE)kpqc#=4uTK?L0a' 5 xA0(?4 wHc\?cVN%BX]|!?3\t40#12>9Vӫ1l!@1)Y0(RA/AlɊnOu"3e >zYV 2'Y60kj7zڠf q2k1:9C]1˵̹C$]3AIs;a1TƊQC75ݭ6hSDŽ0{7 z V/Ə_l25FrFD1!c ` 1s!5ӭ9/T+b^4;tI k8 0YkZ8EK>V{uE` O7>_nBDӇN{ `+y U! #:l7 5pàfK02[f5E[t.Z ,.w_ XD?|sLbKjk`v{%XSCnf9#B+G q*`4$: XCVaMx ~a4O6ɧp}w"0n3؀zWw\#cs}*ern} a*wzQ VXg9N$ZXzSF rǏ1T<` ?i/+xX:eůt72j? >g䛹>^tЮ[J([k$2XVG!aue&{=<Ӻ AM}2Ն#N?WHsV:@+XS`R0j8R4 2D2<8oM|$ Cr ;0"V0Z Pn\,Wh@t+>.+@F_Rr'kO5·;Baj6&J/Rx/jQp(̣GuOR=Y59"(`*ECxO%e̖OO4wc[-r>f Ni`PgJ$qߒ蘽z@xv㖣<DRKYQEJIF ϫF+Ff?ٽMɓVm!o{{ݹymm 39E'nwRtwxh%Lu Ð4;,'.7jdC|? Dm(O>_fm3 Hk pF(zHS)ɒ[[W%Iu}[O FQOҁ0*n7BZ8%5~^DC G1^p͸ƏYOP2_U_ C}Qq;8^/}Fˀ ڴc22ʉD㧇퉾Xio=~Oщ$ӾĪfa4#K(c7p F kH`N_^!_UUr /~iGOگ_*SX/4õ2`r D){>kۻvd8V\S"ů:yGC}KVރca9T=>^ڠOhC~yL9[_j%TN՘kFZѨa설P`ξo hܓ|4>78NO\ZO~`c}ꩾ7DW%8Y‹1X{E` t8;fsܴ1֬_ylhthM :mҍ㴋eT9A;mhXfC kk_vAP^>=6w~}}K>U;ʰPjo=]:]uX@L+m`I ꒛T\GC쪏z6"Cw7|Ӟk?}No~-F xDWytWշ*-CPώзBαFjzv,0Q`T.y f6#[+|ϳL;_wn{5sP޴xxxx`-_es65:;zTXtRaw profile type APP1xeP g`la*RV7QBO27>6OϫnM 8H9FP 0$ޗoEsmN=4$1]8deo& tD6 Ԙy.tȯZDEXPu UZp2kb<8v/Z\$iTXtXML:com.adobe.xmp 256 256 R4IENDB`fotoxx-20.08/images/gradients.jpg000066400000000000000000000425341362435004500170130ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 334 300 0 C     C   -" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}XjGt+Yk]u>{ KǓ p$Ԋj7v9DQ BS5T6Vŷt۽S h]~ xWKs!.tZIZ@,ވ2Oں x[OxsQմ A76({dxȯ(zrm@~_yW(c@Na> <wo~]k-P׬RYJ@9𿇼߅7z)Kj2ً+"hW_2BG~(j]A;xdzr&7 s#I o x,|KoUO_H NaAPܝfY'_)M7EڞiZma]X\Zǿ3Ɔ6 vv Y_/?G<'7Z׆Sj|C{2ܕe%x#ka<*\Z;dд;rQl K"x9_?aOl>|zmkZ[Y{N,@1¹_ҩޥY՝x@MOS7:EB]n5fj:gpѧU}ֽd-xdD(1~^zsYytZmvrD2~%a6*p%!=}F)ZW6{wF !e=A5`&mG5{.tgK|d.rktm^ 7b=GftW]Gkklrge!ݽNsZ~#A7UmesvhcD-#]­F=֯o=4wtK*pAհ?kbm}R^|渟adm.'1kZZH@I.!]r><}K0)oD]>}xoiŞkD6(&ENebx rs}hI ?j/? ?j/,i>}?5xm??5xm?asZ>}kwKbwKb0Ol[Zׂon;asuk%JZ$A+pH#8&y0Ŀi(y0Ŀi( CfӵYao9meVTS@i>7x_]a`I !3FR`zWٟ?5xm??5xm? 7'mWA隟­[2xTV7o-%%$IJ6A7¨|TjYSwtu U[EȊ@(]@a}aCW6QaCW6Pv $_>)>,ZTWYTV?:X18+$GKLiy|64,*  b 7ı8Ϡmmuo τwxV["!nĢ*}]%SRPmloع g#'?aü_ ü_ [тvCoEi֖2KlG7>7%$Ñ$  @}յgAAGV'>I:ͷ0S Td B#'mmW>uK˹C,9;MA_ü_ ü_ l6??ra2X?`t̐_oü_ ü_ q}BHוE'֟OTEgOݸ ?j/? ?j/,ZZCXkA >a nj/,8>lcr\c#}aCW6QaCW6V4*g^[Z8P|I'`}YEyU_\4'Þ:e]x7SUGy'wE/ODkH0O jnO+li߄DI=,H']W~%xGƅZ(iڌ7`pAǜ@ )h(((((&/ڴ7ÿ ?6Co%*7>𭏄gqŮk:6w0BCϖM2"2>SҦ_>߬7 yv~&€=jVhϿH$ @Epq[xƾ#.>.6;=+O>IKN'r^Id\38i Xh&wk>%C<~.P2@nGCWYzFA56c(XU$aPz(><=c>r#dgu$(LO|XC;NӢa>X_ ҽf4f&_GSO YC ōZٓٹԴ)w+RREaK]T5TP|w^-ޙ? jŮos=a;x8cx ~((((CVr:x;k#ɯ&u 8'󮾊5|9Vm|-顎ݣrȁ:nRűc|Lׇb4aBIC3zோ:KF4ψVnYa<qo^QMR[~ תL#HY mqm9%(+[A~ZL;v!ƒop@IףVn#|ΕeX6m}nDѕ u+  uyl?mBDn}r%šΒ̤mo VtHoh_cx }IaHYwNkX{V1O'p}i&"xӓu=Y`^3n%/0(֨g! &4ćK}#j+7ÿ ?6*b~O kJo97ZҠ(((((((((((((:`@7|?'|&?(]hpIL*ý<ŞH#M^ w+w`81Z(Ϗ>3>_OG%7Z6{6Ӵ;{HC^VmN '䂁7MǵkzTׇtkkNI#m"%[JoRU6bF/_i*sڏ;[RơMp(۲1;?M(fi4MZL8/#Vcb¹ޥCV~m?_5jWAE!#z Z7ժߛO1]7ժߛO1G#z ZQ@#z Z7ժߛO1]7ժߛO1G#z ZW+冑y$[^jvy{XV(ヴ(CKW?Կjͧ0߆CjX}yM-+2xUHx7Onk/tiNM r6y8޹s@y?jzg+SK˱ΒfIPNN~VG!xkF=^5TOI~WO *mXIT?Zg{qs'mNo{f-ֱqw c1 ]vy*m 7-;k* Cg .!m{bgVpv]O :_]< wxM:OuJT-Zh)#k6e!a=s^E]]Zy(((((+>(x{ZW |Aވ(Kﴛ &Bǽr |GTK;k? ~jv jVnWMȍ 1 bMFV8*1."2V*FAg^MxwImRi1[˔Qy9J)E[[šoi]gGwŵK"mV,6rFbq^E_ [QE|kTMn[χvpjk ,HHb̨x4>eQ_8obԼg]CD@xRI\5hċ*R5~/|J𦱫M}mK qéq ̆ qg n7VOoGҴWɟ5{QW:ׇ5yMBɠ4C@|29mcR|AQ/- Go6Hڣc1(DuCnWO 5Xkif6]X0 +hYHoa3]m+R͸Ek r%DNq\+lKG6_O%uoA_?6_O%uoA/' _h"/?  ?藟տ_>G}E|{ y][hD.4ga>s#*+lKG6_O%uoA>aU=[V$ʁ021' bHI$_$eD^Vu3#_.xlJ?h JMqYեyZީLҞ*Irzz.a[<+ 7syw${G.? ]Ii-ճW6w)h/0#H>?/΅/ 6kğO@,o2RNL/ssc쨮SK~j _u)Q\/'?Ox ]&Ö2$1坊Q~K_BQ0OVȧr^M5Գ4ȁܔ]p CxL4G{6;^I.S,ȑm|M7ޙ++ MGYdo.-$18.wq9v5PcZ;#>o 1mJ?z(ۓI+ ) ((((+-Πl: NhB[P&=c˸𾦗. >ϻ#Tumy}OMiϸi;Q@Q@Q@Q@s5O-[y.洸r4.#`a#5@Vk:tmB̪d^PNrIu{êCom+'f8Y4,0AUxF=" .$sj:֡pq$w6qaEkWj=]/VX]xg41bbeVTFw dEyΉ|9yq k>B h* ioM5jٽȆ8]ѣA"2ća A;=-O_4iZ⫟6_Z\Csݿ.$ kྤ}+[!{ۘETҭ-P``r8<z^Eⶩ+2?; wT5kn5[[KՒ*$!Xo,5? ~ q6zU-ԐiLy*ir@i:WQM~?=>x}g𧆡}]bIe"x9 ׿Z̞0߇|ȴ[Olsm;WQE iXJƮspלF? _׶~!Ю ͪ߫kѴo >eV $0}R/mf\Ѷ=?vǬ d-|L~R>&Y[j,ܚEcw3]DX,qAUAse8c>߇?NdNK'8yWd~LASܱ`w |'$`˭hÐF6GF''b[ZI`TԊYsgD|zD|cAwwez[9}.Q{ +?<|J*|g_\-Ge J~UQ{V (Š((((y um ʩʉUkHUP֑@/O#V_@TVo#ZG?FY:iQYi t ??eEf5*(kHUP֑@/O#V_@TVo#ZG?FY:iQYi t ??eEf5*(kHUP֑@/O#V_@TVo#ZG?³ADQŰwnP㿶?G߀?hҍk,@$h4e [t ^&(|8Z[pAh;oo4m7ƀ;+NrWpB;MtAK1Q(¢( %Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@rZf4{_J]Xx/孍mbZkεiV3V=?ަMI<=Jo/]-uDӯ=.{{kF]F{WhK=_󯒴jxIi]iHIdWgG^O5cen^Muxzf+;|_ҼuYIYziOiڝ0p uW埉-ou4 mGŸjZbKe󧶏xFipH5x6I6i=D !e."yR3cB@tRW?M}/΁18?_fa65u+Y[++4 -H 7m X^xUj: S}KXɀ)Ka'W`22_}>~>=Q-Ɲ-Ջ<ɞ0ᇧW<H^m{W;2 ;W#A#+(}= 0) (((((((((+ϵTv.b.cIb(*v d؊(Sʸ}7^q])&*UsW@k tį]ĪJ.Vu۠ݥ24P0-/4M„.}7s\h'H"uL~ץQ@o fsa3 3]#W?EzMtxc#J)&U*4t)sW@g.q#XGO$@xgPZ(fotoxx-20.08/images/gradients2.jpg000066400000000000000000001113001362435004500170610ustar00rootroot00000000000000JFIFHHExifMM*JR(iZHH02300100Fotoxx:trim_rotate|http://ns.adobe.com/xap/1.0/ 600 1000 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?MVVF8S#Wn8+,'v2bEyw2g43jZ]=Ҹ3P:բ8yW BX9mRQNNqӟMHA}+E3Mg950 ZE/uՖTfg&}9UzQF&srֺk,ڤon$1]'kϯEZIXu4~IY\v֫ BTvT,48/i$nO己rGjl &+3s]#[M0GOAxUsEڮyva"z׷xa_?v*y<״x[P 'Ҳ=6r׭K"Uьj& CVc%aTEjyz{vdܞ) .r.ӚhآNmh+آ,Zg:M̭As[u 8rCMGZ< MZHeN1|/tΫrqEmhyWH=BxEKc8w/J_Ej64CSQR0j 2Ú\ 2?tWS$w\M:5XTM]O%8#S5lױۢ{8eA"fPC:VSkHi7sN^jb11YAYI  }*(ԛs/ZϙkZzlvSf3vmsSZKWE# f{XؒÊR<'"k8JY*s0M$Z#i&¡F`݃^LdYӬp=)uvT*A$*CoqQj yr(XacI/)BUsyо0Ym_h^&n,2z3Ln ֬r#ҴOVjl`EsTB=SDbsŞ%5ҽ2['#5gMi>,88j#ͼnjsNE18%I?Gzl9d;S](?ZQrU7oIȢV<>.d=>7vo3FASƾ757K99T*EK XL:Ud52H$7'R5t3g֮OҪ0t64B~lf \lu;IדM\wg-G5'\Bk2W1Կ,wD˜zg9r NM }[-fnn-4*.1ֵ|tK +V43|bU__/"=A+TebTNu_5Әʨ*~5b#cy2n,渓1PxN4`Hd8>]O8w"YaA0+=>]:.U#њ"*I4݌yaRĪn95 B#=xߍ,~pNW n;<ŭOsQnbe*[#p_ƠsmH85>"4׆w{ׯx`%s5H[KюyXy%3W94nK6xFUuî}*br367 z3G3W!T'F)`$FXI#rqOQVTiK:화E64x0>٩eOSJ7gc+ 绉}ÓּVcݢExߎ<=̓|WFiCҥF7G!~*`٪jUy8=AOMhʾ vBFVeR3M)Й{գi1-zE^+:I dZF͜q,3E ]0Ә3rĜW5c6`ލ0qۊoʸFxp喠|26޾rZ^ICWֺLj-ZBJcR|ui59= ש_FR8=ӦEjÓAd c݂oipW4f Wi0xu+nKSn#]P1]vFz([1;{ƽ\ZQ5v0Ҟ1R[N?*|#l+ȫ{ #$.U)} ꧷ui0;ٮ6mVc? 0QD*Mw.ON@!~n"&pn9.2>C4rl0T⦉WIw$(^RI%Rq+w6ejqlݸֱhT'䚹3gU\q[CBEFx=B풢6Q"SEː^[N+ʲEu ].[)@kC,D^}?i^+PvP]veAMUU>}xn62;Wi6 _`VZmk Nrec?*D EnwPrcp~q1E++.`m2\]" xA">L|;/@+*4.'i"!OQڼ.Ŝ)Bѐ썎8&ĖhW-MAP!O_u Yhy-+zn 4pp;s\=$u ;W%ef=D@" WHA".1.v%t H'ϓO#-n 9\.7o^!] Ю6<"&t=8h|mG^0\W 㥌$ǑELP *Œ,VGX<,;QIXx{TpLӥt&5vyҠO/""r5z3Qk,3"ܐ:ף@K)dYX㧥R eiU޷ˌxF `m lr92vpھFR}k>fnfT K%9QeO}2 ,p1^tUuk|nw%6<SrKx"vVGֳiſ/tfRw? ~o+ʣt+`Lq('{֟YŪjs:%[H#lqy A޴F<хkbؤNGOҲZO<q- g卯Д2WKgoq=Ƌa29F t+85{mlQ "*X9.#xy xC#ӤXޅ,OR=0k {7T6IcNVI]mƖn]ܹ<;#H3 ye*mCQ1l" 69- 2./X6k9Qh7=)M0?L-K]r9J6q٠r۰dzV)n4y%dgA>X֖o^e1ޫV5ba;H.8 Moie \c>,)9^tԢv_֊Q&ў DέؚM[x"Fk:{Ԭ7t r0Gx)OA\lJoG#q~,Гȧ9(ΎJFh`uש&º-/·bzc\ ÚdPHKz-DyAoe W2A-:{*ݜztjO03\u"g{ܙ Zռ9-@cczף\ydL,1׋Rk6ڌ:sv?SD3iY1kQH1l~:ϼƼl}*_ 7.$ӈ^xCȂ?2FIߝ\RswG>v9tz¾tWOaQi3DK8$@u/l)jԺUZ5Xӷ׽e['8>RowcƯ^zIL>J." p |'+: [8e.1ֽ3K{(4 S~xF֡pĒ:q/r|Zl<7B -ެ?sZ61{I=A7vi_6 Eubx-Pw',I>Dg^K l +acyq NdEr\g鞱e{[~8y nVw-Դ JW` :\G;vOlx[݉'; K!fHtd,h`^O:GH{zUi~өPzu:w ڃǻG0*>}&oS·hѼTFpzgTRhnϧV Kd,̥uy0gyu詢9'I8 A*;^;V8*)I IbH4I+4@ǯ:Ml(2[̲jG :4|@qѺNLTOq7-"x?o#h~p N=ϥKݍo6ƙ`~L$ =>TXL)1߷N+@&v$YXsn<IUu&ios05VET qmJn nr~vMdF #<{TMenqH>Л^Xs,|ve8Q[ܲ,V>NJ$,TӊIo 'c:WK2Q Ma?ڋmZXHH0S-odqeUX`*Gso+1j}sWZq!bI26[=e\y|}FQ)99Osh:P1ڭ.m%}+dG*|FCnҗs+qn9<-WÈ'a#ȸp;W;hDx#&Xz\)IWkʣ%^q9fV&Am׊EЎ[CYiZ3y8k6T).Tߺɲ9H-Xc Xz g:N+ve]EIm[{|IB2Ir]zf.I*dfg䞹98v渹hܫ.{In)kvryIt tgviٲ9g9O?h:/ qJpc&' ҝ)%;Ν_4([z47L"z ܉mϗ9j7e< ?Jt9^&ʜ+#'ͱsWS-4=;@8=sDE$QD^lx>"}CC{K,;``9=ң4eȼqb>s.H-8uJFN], pN8unkw,`8'+K&]-V三RU@d|'8?J.4P)۴cj=hJ G 95bM&qnձ&%!Y :'9U ,6+ρ#Pqs߯{6D 4q9Ϡ^NU43'҉|;5&W|0vg}3ꩵ-U"S6JN0 ,x>皨{!mr+(Ȥ;XkLٌqD5]mƑvItd%[dI$VbNGr~"f54k~je =NwFuOǶ;^wgX-% :d<sGG08<>Uc;aXns 1އE$>Tai[IPd\.vD 09@lHݲI4|ܕ=.Uk >m~Ϋx;Ej=s,X}:tǵ$(%TWsp,#J5;fq$7`n X?jXYZIH]e9O⽞eƵR keI 9|׼k;[R%[Շ 0l zp=sJQnbWw=zW3a^Ovڤ,I \9ޖo=hfޠ00z͔g='ծ/$cpʍ0FIgz*A"y&;ib&VVHcA/x U偂T@6㑐8waͨIeŲgf0kj=:[ԶΫmPAϿJϗMF d $`ֵ'EIXZ#\6N^1ޜ3 iؽAY2LfT?}:WlMldD)m98=K{svAnS8v=RK|hnǹ~Gӏz- E˿b}xL6'v8Z$%Sώ3'{q+ 8wa#2`3co^G\/|ukfИdnslOQfxy˝E0w8^223ߞŜZ+N1Ikfv CǑs]1} Qy,g址ف7ӥl#P){.W &x$wE״K붺G&"HCxtCyu(#2$˼FKO8{Fr4oo{{Ëy"\O96AAH$6 OYY*`=Oi:2[#9k e}׌]XPYQ֩5q"[Ys?^_A<#jSX&2CkHH9ず=/m 9 XњW)H,[](*."H[Twdglv+~-cy6=9:֩ZI3Kĸ❠hj\2,YܲJ 眃Pp$'^pxszޥPơzebm%mB'=+Ryl-Vdu$\2]On F\uoi,爙զ,iE'pF{D#@d>Վ3$߃ֺK1M>*ܓx-bUΕJ7֕K]CS[u퍙@#qjik}-;vk†A+r2Aniw_C~0|ha-b8 S 9=‹©yhuV߾ P:TvϩU})⻞I%',v~Z\Zm PDlcg';GN:VNi%u0{}O#qFrH#5zh-"1.瞜*Oңd,y$c9큞ޔ;eH`p'qY6dC1bEknB4 N֩"d+w~d#=?+Ӵkx~{t) uaS{1pr}}3<<ڼJdN*JHP1۞U2io$P uϥ?UC\i۵E60pq3uWifETx©x!IȷeNfq؎OQw4b3A;z/^O_n Aq֦-4DfwoXĘZVl?ps$5iK0zgJuT AqO85Uգ6PJO<1C5/r*jޤ0ǧ/k+];` וK;ߠY|Am9 HܞSsG^M[F#j9<j]n%ժOpnTdqFU7 Q s u $s1yIgb0KKZeb֩-/`g>ʳO5Ty!crUVe;rFsԜұ,j˔K<N~Չ^Lϱ#,Ó 71S88<}S^k +@7VvrHēm?6:dsJW+{۹TiJTn8c>W8!Յkty$RCz@SӭrQ_f[$л; ?Zria,;L7 ^E N 1Q˃=kFp䈫Rg;h"-{ee+ܓ!k*K+[H I*z':\1յKo 6;[mIV;%I&DUa!u>SU;DWf>3|=%LА9bS@@;A.`V ` 3ePy z^*]Vm>ռeOv0ʤ ʠRϯ ׅ5_ ,y&'Һ%FsMz9|A/ ƶ?KI0VpɌ O w~ZEz]+YU*Yp@OOA⥷Ľ?˧&e,QT{wb7n۴_ H4k=9 ĭmm /B2w)ًY3GNմ=Qitb1-ȫz>8=OLڀ;&I]9;û!u.`-5$X`ȸNxpvt-Λ hHd2 29.'ҹq4Sh$ԣgA3?ȩQEtޔ7rۮ`WN8IƭY>;+a5/3GB8?+"Kf;Hao o,xǿ ".ڪ0GU89k7;vrޭ*h>q$8$=yߴ\#5K 4 @#v3=kJ)ʊNY-äL-dԾ"R7r-k z`t$&scYM4e@1Cpz p8xRZp1=v˒hGrjI-NI$y~0x+k+&[>u=0 >qSo1nC۸$|s]vvVq ҿڝr2I2qcf"!$8swɮod:~ɧXmF \1ےv=zqn6q H%~] H@88=ǭG+]h~+xTvr8W7/?'=1@M 9?xK%LiMJKC<z{W˘]-NkbpI=^=vKp'\^y 'zM󩯖|GF<9aM)J-YnyѢpƘ OBpI'9u {i9+r :FsrGâb*x89 ,qڗx'O`#yKu+W0>ZZYE$-CDm@rK7^)ԣ?:'Zy1n@%$ ~>$+H(eQ c?¹)ьqJ^{F\fX[Az1k+Ϯiww<9`XsڽLe('dkCc.fAp2.G} 鳻jw FB@`O s3mZݙm-(# z;՝Z.it0 c$[ ss^J&F;fb֒WbE9\c\wRIXšqkk8'=wֶA?ش"ƹ ^& 2T\2Xp:8SRS2i N~|@c({c8d8՘? rnuyAMry* aqg`oPy2H8 8`U-c|}?^z lJ{̙KiE?z3'|)w ^H-lSIC\ ÜG=0H=FZY3]gkQwmm1$mvˠl\jwN#i+:Ad/yo_Rnu[e\c&Oo+Ŗ>M4-(u cm7+޲aunby3T{wp92`2@&v>RZ(po7p鷾I =T˹;G<{VtM,F;[ycĶ59d_2DvMw$eiDI)`Cr6.YT`HMlB=2፜bf@a@^<yZ;PWWB} [ӵ7u?Omqu#%Ȼ\.XF ubxI/3 `G8&..-071v?Н-8R-h-=܇8b9J}}J K2i0f8,7N][K"Fbx3μYhw~$xHɌwSd/@ВGnXZ0Z æ"9xN^&Hw=a^jw"mA:|ݰp3ӓdz5e̷_1LqSk (S<[ItƽJHBUvo ֝veEǔ$c&6o%. I8兦c`?P2 O Ky gzu\i&pO~#xkA{FwE+לw.~/;yoש>?=t=п!/MWDhSfANTodhaqENqr7c~\T``r KA$D݌d`q;zԓgwx9ҸT!Գ5( q§CמIv{8/3upTm.(vQB}:ul{Zh؊al.4h7ǀyOaU^/Dk9$ijdbQ׊юInƪ ]J?($]6 %hTd`gcZMfX^pq8ozbѦLzvMJORwY-#8'8\^wzEcBi# Dc )G@G[T;9lfԳ|㵅68]͌9#?^zլ Kݣ*pϠc}amPsQsVSS6VnYݭ+%eY[  54љRsH\D6;IO9 >bsU^Yɢ>R``{Ci 4 {xxgDܳ E'hϷ_\Ľ4]>MnNhDp$^6e=:ՙ`5eDVvGpOSB`H&.\`CpaZM2Crz!|w#<U"='vV2?57]Tt(䃑qz٬H{w=3[:eDJ©@?S|FAb"MsO_g= 2-$s8¹ǶJӬb}+";XHYn]:.{"[YK@^Tea)3Z\V7Gy矯ަRqG_oU㜬:*z/-v+ 2lm#}3YrK:KH$QE8POè'`9Rs֐ {sJNkIT2D`{z2 1sQhQ<>G:}(SV=ҼŞ ookkkGE&V`?kxI4U2v:{9F+\gM9Ulќ'S37ncŷG4yhlv tnEQ)̾c(#/ju?W6.yn?qnrpv=ה2Z'QѴ+ML֭o] ߼=j'KhYo ]'_:uk?5 Ot¯2=H9~ ![2Np:5[I=M9SP{SYԫG 8ryg1I};â{ ~B ,\9r\W[q9HvF9s:u=q[OVw4C9rrAC,O}pkrh@!ѷ< =x>Ч֥QF)`qJHs]]3[A\<(%\o`~v[b[q8=q= Zj7zMF\;ĸ8B؃~ubK1ǒɜ{uwҮur/DL7Cq?21{_h~XK5ϧyFz qӌβxkz.ܱ~Qx?7MԬ?jT&9R$jр7tM O6٠!&| "b2xF{ΜKC, ǹ>-hc%Fq?h}O.q}` ^6$G${s-ܱfD91qjY1u N9+J8IX|8;Qd~I6a ˜ qGSy/Va(Lg?4~Ѡ;=8S춶(/^]rű ɞ 2Gzd[ۑl<ˏ&59 9sYG=3[66B~R#+HTV-BWh$2N7tQ&6FyXO̫뎿jQo$'c s϶? 6FY qtuUaOVrtFƑ`avLww(Kxv^qSxK7o$"&p [EݩLޞSc4) sxF %7W't7K2' U7AW(uYEwpuT]W"1 _2)ޠQeO?*I!䵕e`UH<֜WQIݎNx&r)5FM~$wڬ,e1{GqzMG_UP LIz抓vCHoZK$4Ҕ,{v8= ܟ0IqlXxReFyѱbLcW=N?ҢEFFB>E=VM[{u'+.q~x^;ŸoމaxbwÎGbz{r'xFy`3lלףZAn0NHSO޺Pq%N–ˆ rXk{v 2}$WjvE氄8#<,{דA".۹{X\FXtb@|{JT$*I$;k6lX6.Rni ϰl?{[ S!Pnaxo=|_TG]Ir؛bĊnEBF0pVȷ˳r 'hnu2.WsoVi2qs^T%rOQ:|RtxX|}m2!yq=xNAmr?$xg[\ݽރAs$'`>=A=@K,?zZ#xjJ7Ql(,##OLEl\ev,A^F;W7𗊭KoL"hwLnd gx*;מZ~XU8t6n{WUxZNuU,.u[oجUtnXgӤ61DYW?1nݎ8u֍LNOt#D7]1rrx*:c}RfrHG]r#^{vƚH`a%zq^!|-ncol Tdą䟘Dfxn,-tEGJB9^&ڌ0@[jlu9g9易xjKUo/AGrlTx{jWy31Ir&+i[c1#20<`U9oKfxqO_x<i,R *$r%Ē9Oat,IK3\G_NrN4q~o&)ʭ8'?]Ţ˨x F*{ {7ӷGjp0;>c D9??(o\;zV$yed'nuʖ™?5u=G,wzպ졠,'W0'0N8ɂ "%WpܾI57FH2+٥j6Jm$v V|9֟cSaf.$ez#EoRc:j;B]߻A89ϡ3]տ]y]q-̊0,%#oMǟ'޽)'s7 M+QY#mt7 y*9;pj}VkXݳ$yj0:dsޯX#(P@cA\g|C$wkdUd A)jjHh7BQnomq# ;랾\t}6)彀;7$eX.1zS;s\東eBH :Nc2ZV/߷oA1Էa*6*x@;ƵQn6,I꽾^SKk$Qc3MV-B)- ρǭOst%9uML[yaN zz/SZ,z C{4S;ժ^bS<98'p}sZ4eCa[rHְE|A{\ 1}p kK43؎ =jTkݴ04fx  I$-צ:ԖzdDQOnʶ;rہH qyw&e+AM6G J|eۊX0;K p,X5-opۢftsS,#Qs?q G%E>-Þ=sIWq4PyBeln݁mC*O$(HR8;&r͜IOh[` u =i<^L3@ uԖ%hnҹs ֥%w+pO&4l+1rwt݂??Ҝ{t3N6%`WD7KSvU[nf%?1Z1?$o37ﭦ6G\+ ׾+V NGJ@I)2JdZk׎,`Y!VI$xD&IͿ> x7'whЈ sA~.]td+kS(Ttսb-gJV-O1cqWi+٢l7d!3iѴ+dm8wqԖO[`N,\ 2UFބ75!v@wlI^yɕd%{rr{}p9 36.~cNXEfdcgN-X0f3OiTר*GUuq#7/Z,ahǛ2%'#AԎx 鐫:JqɂJybw8+= z]Ӵm=ⳅԔ﹒BL_;W<ͭtfj^E(W鞧WG|4XHHG%r_^~ mŭA 9#޽ ӭp#B)tcFƕ`dQq"swmcQaf|26yڬm^w'  e寈!VG>Gmsz6 +Z:2}(tE d[,$`1N>}sO1^,--ƿv[%pdo.h]r?AQm % ?%]s\tK&X+ QzС~zӈh,19bZNi֧7KGizwW6JHy;I cnռ7oaw$b_: 2I<N[c:O,:=?,Z4E)aN;uDt D"99PIa'sTh2[,¤aK[gO}rwM9{mK{ TŶ$pb>P:\@THt$:\srnqpߌ~5'yƻ4VK->9$ghEr3sNM"1 I#>DHB.AS?D K=)mHEoLUwmNPcR ozZEn y66.!U]Ӝuֹ{DqpY=z@]QQûG{ygkWW,YdtEsҽz|.{T1(R(f<Fe&t؃Zt6?6Hk+BeY <x[xB\+K3G~4meKR(#攐1+NJꑂS<$u ľMS,yAϾH{g<)iKUt\ f8x=k-5}/\hy|xD#۩1ڷ3\)Qʸ>Z)Sq#vH+Ր|[=SavP]Ij"+=#LZ†.Bs"Hݐ=OOJ?(wmoH>Fp0=$t6 ٴsb2zy+voZH>PHW'+ U#ެmE_$ˎ=8v6#WsqzdsYe|Xq!Y:.v`N[\'K QIUUOsg.ze[IrP$vˬJcB2YR=:֍ $8P`8sWHYf*ރS/iV5w-UINI'j1޴V-Q2jqw#Q4qW';.[v3"sDEs_62sz. JL)fvw6M#HÑSAzp܆k|rQ[:EtQeMsBH-<+,[jdj3vIq +}ǥck^mm'!G|}Z͹g[e4þG٧j;)EW-$ēu;Nvω-5%QmXdf ڼ4mNL_I GGbB;g8R_3R4Օg703֞o Ol3׊tQP\!肤Ԟ esG pd_220FcYgZҹ\dhqgmA#Ⲥ4֩H8G\5֖+%)}0N?*˶eLUaҢXPrU1SnUfT$:洍88O5|.~#jF0=zՍ$zyVT $zUVʻFFOA\h %%ہ7sơGT,sI=zi&NYFd:W43mկR?T}Ďx;N0=QfdY#**FSVm 9tnʹ8]MeM:56w-ye ;={f9OnF瞜֍iRw_O_R(#>b1T_HH^K-ӎxA+LN3N$2ǓUSVՂ**;oj$rH=*i(@1X,F}?Lf=rqTIϽ7c֕G@z⣍H9TyU3?:~lتR!mi _jyEl rH'Q:'dRБ= 鑞=vt~N涊(]N d'rvž'~&{-̭kd#p=+ ;;XqHaqo 6A) xbVէG&2w:ù9oԆksgpmˇTw~&A[wPFdc=Oƴ%-1Sto-σ3q,91\ w6C>zb|W!1Ît H7t;d=z؞I/wr$ևU:ͩ]. ݈iZU iXnZ!#:SM8KP{g:IAكІ(UcP0* 7VaP&ەg6.PȟyPSiwнj:.XWp:ܧ* 3*6uK}z)zɉ ɐx3֤ڊ zޗU*|SAn#ҥy;KOOw`Z&tc/;}*j=QGk 0bx;>";dnGz2cʘ0y2{s|%Q*P=[QZ,;c".T`´G rt4!T}z{Ԝ&U IS\- MI ;T߸1-IrGJceFNj'p ǩUp0`FGFi]1zWO]õ O1w U6sAXr6TG }*[fr~am,1,nE') l vto V*hy¹1{c_-&9H7g͌<{zM@sXi6&O%,Ȫ26sg_iͬ f:Hrg*{ף)KcNf%m|O3ܔVg;Lڽ5iq}0T,HK2Iٞ 1yN|<]m{8($;#O'OۭBߴ45[/AVwx56YAuA&q;sV~k?Q~_Ěf5ɤg7 coN08=1Zhɿ#5^6/H.%[>4JՈU$ҹ6È@<*5X 4kU 69gVWƬk%ڊ {<!W;dcy]O]k4x\]K4)p$_>yun%x49Wmo $spkLfUb-|{<2SAel1qEIcWV 8#ҖR7d ǰ>wf>d:BNծ!Y.e\79`Аǥ3CԞn֞c ^"0}*|;ia;O,"MU׸oZbrNȨ,\s1x=uMꆴeivZLFI1%Ԓ-Ўsyt˻F!Gx!P=p"fc1HIR|$W#Bp @jnz{U\m_Sy渒8lnj^š0!2|1=}ik#V!9ߜۑUL-F[^GEsOgp<}+ļQu"L$WRu1q-Y\ҶtM'Zty9z°uk;;MJ[ULarcTehރeڅtUQ>br,qv$ 汓EG |Gvl⮺* 3H=)٢lAœs׭.[ZaHn,! aCyE?W i lczi9SH: h9r{G$l@Թwp4p8_<>^qO$!ϭUVf-u=] ڼrBYdpJ'[/Pִ蠲ՔoII. {޿( 'Csl/%TŸqXNȸrZ*GS6XIl'#Q$ 2}@Ij [^n "dힸ]p+Bè98ףj$h AOzp8ua״^C8ut,a98p=]󯿽?_G_oe4"Xcɯ=7?SSwb4eڹ5A/ ,}qJo VcwB.x:rz]Sne^Far=mANHDž/+]vRH/e{]^̰V;zr_%i>Ց7ZuU~kɩͶiQX]i+ F^u,y&[|4q0=5iVޑet&RKCKFUʞqLdG J?&}vC+%'n^٠0UftDVbw1yȧy>HOR5mJ6I$S^m>xyFEf*}k{U֍4ط9vOiBL:k199^5xAuNl_"n9=;Pv|ۥF?7ԑD|z*ҤPȈ2\db]7=.?UpZGeSzeIx3ANԐǜQSIkŋ&HfW#&_sf\oŽ#SPH*{zVT8ifѫ`],ʣ,6=W"ZVvXxہThܫgtpa֭kuoz;!?, %r!:~?rVϋie*@ڶVDB2Z2't?bb6TKO h|7gn 0$&xJoȷ{+rw{!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-20.08/images/grid-lines.jpg000066400000000000000000000417671362435004500170770ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 233 294 0 C     C    " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DWK+o>)>ӝgKUrʉ#yP8F⽶Kbe}s=-uj v[]O2W}IG("PX78@@b#(8]t7>\-4>T'n^A0CAp h@ǁ<-4ƭǥǩOMC(`F$Ac.H#;{Ի%rחlj}'?£ԏwcwݏ\W[ @[  ̓,RFw`p5|8moEΧrt}gsX2F([vTN1Mk~62W~o|f4kK4;k+J;ÕA=2x{Vcj(aOcޏ{ӰDQ WjlCsqyuk;I/qAS] oc.3}Ÿ@ }uա<#K~w76*u{hͼ,q Yn PG) &QBy9ڑgs5=jzT+=ucn弰A*NqQxFt/k`a[JYXrT0#%Gn_GI5OR{[k1p; Fݺ49x9 \6j2X ҋDƞo-y oY[ )(bLjGp^y_T@Koh5 @&ַ;Np7 O W7vc2|? q*{uHl_7+,h}kM#uC& i>}?; +_GM#f?>>}hIwï>}hIwï>}hIwïwݼ.5euC&q|8*8AY#ȫZuJWy7vV}YEVEPEVV\JI O`KESivy wouleawrU QgLK#wt iTfUGԀE6$l 4vh Ɇ5x/4=f϶b杀}| $ 6#Of汼M XtnZyɲFCG^J0ǂ3 Edzu(KxV mE ~`qZQE(((oiz Z-JgDi1  hEQEdX&񖫧ǬivV ydV$ȍ1ޘSnũ#"?Bx{TܠBٗQGE}=ܲ#D;6d>nŧEEPL`U"IsJUx{7zHtYz>k۝F;'SmyM$͂& %7(8u]a|6O x{Jhn/DVK̹?7ilT?x6ԭطFc`2!;ƺu i^ա[=^M4~Κ>O Uӯ4ѭ{;->9ݦB&ݓHRvqơ&m Reօ^) 0y*h,ab|l{zާ6_F&6riI,;)%1qRO EŬ$zD. cmA;S$HB2}^^;KUHMlsȲ(ьdďxlyZ}/*xQvnpʣ p~%Ugׯv).ns0X$X[h̉\zi>~zB\_%_OzS߇ *JYZ֧g_hWݽWdc'VbhyO+Q xj⎼l/-t/sos#>m_Ʊ@_0,Wt-EϊX>fttzk<4uhA2F=+O>o־ >]6fOMd}1 ~>]\~EH,ljI|$ei?c +>V_z^Niw`_h:gonH7ZkGYaK3`}9+ٮynNiKwy?2?axß|]?z{"abmЉbYBL#bY j;ޡlf2eđ6]H`OPk[___E|tMO _oU++G? ~ӼM˪]RH*'Y@)&]~aj3:ζ !F83^#G>$\.l -RԚݯ Dn8+{k. IXnBO)ᶒ7c5v 'o[~,7 ngFY_ngv's5'_/E.SP5;aRtה[$Q!c&Tkg'wR "6ћ0H@#90w^`eX&J{/ bml=v$'_O+cFr3c88mo0{^gͱ|X4jYŠ*9F?6ERU7mqj@IEyh7CybKv֯uS[JBӍ[l̖>ȭ5[˽{y#fb)@ff򜅏'=_o0zq~jQJυ$B&c ۋB\J"2p5nB};c4m$ okurKvh`]sޓvWbO+K<yyxOVЯO^Aĩ qbFSFÁOuKo C\YC$섗( oLr71s+t3Z)=λ;W*͞?ݫNC~)j-Ν|I4-Ν|I5'4_=5nmb|5{sokO-$5W!x|'t(6(oi;|qmqJО#Km{~tOm{~tO֊,3|Mox_LwwkllMq9=v[5?+}T(@Q\|t<<|mK&t#kY[\2끃jE(&V"ğG"ğ_0OX|Ṵ|MRŰ_&FMFZU_&tCrw1C15yީW"ğG"ğ]X7]CKbM[g^0#k*[Gm?销*KIT3"#cTX#>I^a;f+-ˬB!df艋>,?^R|'if:r!Ki(YM8ӷ+ϩQGo <^?;tL%ǭɧ]Itx_&8ٸ7㊭mՕeTH`ŷ/CWVa[OY4 iIEn0X̷QEoWS+| >꺔۽IB)˾Ӵ ''oQUwb_ק wo& ZM!X-㹉X8IT0 :{ԔeK-$|Eh~f5+'◎R%.M)4cyf6E@vbmQ@_)d`ݶzh BEZߡEh?g#U-On{[(-ed3隽HFwڝ `G'5^KAx\,5 y-Dl"d-!!#c>HNmh#IIt# ӊώMR 嶔PK?(,@&#YÙ$G.>tt)V&XY q S|#u'}]v"]_mw$;xc,/1G?(9v__J?> #W`.]BoKHz;/7ºeuM/U5 kZ]޷orI_5|JuKxV>Zw-P췗R\v1$\wOcZx=OuD2,1^BfT?~dIEԼWk-L|9RS C*Iu۴#_K;guLb=m'G%"xlgž; m$b0ɯM'fڇ ~tV1w_ifvUdbRi'qʯz_m_Y> Aqk oux̙gڵᶴH%[K7AJ䑜rqIG;Zso[%&Uei {A=N{Y^2 Mn#g+Kt.=k~*m'{CI%/,\Ux{|3.U_ E׮7sci gVKnh|gu\[I#<_ݭoSu9c7,-5H5.c7-.AVM h^5tOmmͬ/.e~.HB^6 > C xOP[KOt6k4w.# &Qo`[x;ǚ^XOëO XcԬ )yK1ocOmt7kDdKcGy{.˦0G0oSkb\l]ӼME'ݬH_UaI4;-,%]#V L1֤#_m隟;x^b'Dm!c ǮF+վ\7?6YGPpSӜڼ? |qxK|G=카ވc&,rێٯr薉ׯoĖW_GGw k8,/lK,+mR*Mʬ۲+>=?`En#0@TA6wgא񕕦4PReQ4O_4SDȿY-q%G*0*J)I7h߇ ׋"dƽZ0Iۭf^xCZRw3@Ey>@Z^m.|ga3gvY r7LyQא:W|[ sW|Yk.ra7wORzca zSuk+rO6U5cYdKƖ^'TQ "d\`YZ/Gֹ)6㟉jhFLzQ3APUf9^ z;.ɇ-RCySȥTh^/xSZ ֚A?%LYٹڽzqD&J:=ԎTAWWgCm}S\i]{yɆŻQk +i>UuOVvδ * :|'_;@δ * :|'_;@δ * :|'_;@δ * :|'_;@δ * :|'_;@δ * :|'_;@δ * :|'_;@δ *|C&dE/!VjARAaQ@Q@Q@yJ߁lIiWڗ]Ĉnm' IZKeSd @Gs-Uպ5ikG^ k QiUhX2:6YT,oS[gm>jQu@k9@m>[ϼ_ڷ_jP[ϼ_*?ڷ_o(| u@k9G?[| >m>jQu@k9@m>[ϼ_ڷ_jP[ϼ_*?ڷ_o*H"4X9ŒU[^p?n {}(Jպ5V׿r4[^p?n {}(Jgy-0{@CLѐ~]QEQEQEQEQEQEQEQEQEQEQEQEQE}ݝZM7M&7w-";: c~c5\/B֏ao(|K@-i/t_ȵ_>%T[sI.O3UdBH;qد@ڭ+/ Ĺ/d*IvKXf X)MsOoE>%nYKFiZݕuwam]"Ze愑!lVݴ pƴ|!MgbNlzuޥl} b21 p-vwZNڄs.m0Vn ):^? k?ܤ0(((((((((((((BO'ǚf?)nƺGkR/tF=:!m,zu^\Nmeo$iW]9jRh΂]\6<[q.<_nvuc?T~vM_sέɡ/N]Xu n][^@c@z.'¿^"u\CJ6~% ࠌ!9z!#M31੿ Fz? 1+vt%k&[Nd}7gp+JŠ(((((((((((((((((fotoxx-20.08/images/help.png000066400000000000000000000131471362435004500157650ustar00rootroot00000000000000PNG  IHDR11sAIDAThYyWZUUW=9<=>Ɖq8l1 ZR@h(H( !‚D?VZP ʹ6 v߷g|{<{3}wUW^O{$lóTz}z|W[iwcaqn-!g۾\6kqJsMÚ{5g4_]"<_d,b݉c'Ő_l_iLJf*{uꞪ qj[lW/k=FLx&tMנ{t䲔Hذ62Q 6O%NJ O?70 : ;O{}Q_\8b $IĮ'XuF\J5_S]m۶x,f,\4 syaG(5?p?N7! %7q,B2D2md9R-,m~&o4]$ u1xƊOL!΢ɞ6mt4@ZiNmF];Ve9321{坭[G @*lNg" 'E"Vm-̆*7`T1Q;YՐ0+`mD 0M,NE-c5ؽꪩ;'X#ϩ6[98 꾰lRTKn9$>CΑ{|O^“ :^r@ ;<;9Ӑetlc oN g5wyѬi>5Ӆ=RTR$cQ/RyH$b^b:iz 偤J+5Hy@(_V,8(蹝 rU+pcT(s^ A$z^ފ}Ep|hMZ&aQ\ q̟ׄH$#̐&X:O9}_E!OzqT~V!<̩aJ4qch#Kt]@ZUM\3@=G}]]ަxr< 7B֑&ɖBj1qr_)9HZ˶r:\A1-oDUe;sB\;UWUg:d}!LRM]Zގ+= H9y﷐T"-z"$DZy_@1 l޴ nj/y#'C{Gpr+'({/A"WhhּV2XJˁ~sO'ܵ𵼬 VcQڑTY1 O187.5Aw< L=d=yD?}zDO 4ÞDf1frͫ/Yamq }ysK18:pN=ݏPF+ЧT7 uἇ^3o 4{hT-t1 R읻 U0~b_*=ga7?ϕ,|z($m~+5N:lд.:c'YQ9,0dS襪[5k%lCgNeOh.DH%顡B@$&=i|A" H8cBl]EgHpH>]-'PZa.b<CA* Dn*:G9" eC9u]m>PM}rtjN"!{!Bl+'<C;I g\M W/EQ7s9LA*rb+uPcLnDeʚI&gb,,χS3 D]{<8DˆR2/%!@&6TN/댇:aA 5ϜQ;#'(C}v(&\ z.\ \ H[>q25;24p҄D.1*2fYY&4L[$o)DNsB&]}[2F>J7]O֗$ щX3h;tJ}OäJP&*kiw*$J:;E?q1Xȭ\ QچpiPkj Y#:*iWI8RVZUTZڪNDݿZqGe Jk܃!y ZG0Lځ֨ م no|Lq//dիc; acVT6`>|߳ SM7{w)5h{Fni=氨URZJҘK6ooeext>cY}܇e;s;#a=Ahk_t17Ӂ NkU>Xϓ*{E$%"dsTdr}}~GWҎDw?ook诪5n[V4)淭9DzNhv[A`ngL[hB%bLU;Yb>8q ;:zUkQ |猏!Io6Nguj[ΕU~^ RQIKXN&%;oS "/:*w\QSS_5U$o=X86#w]D2oM } Pu߽j?rK@$*Hߕfɗ kcM /OwH2߱|V>JtQIǕGwSUEE9"j|r/:&*+w1O5W's7$̶\HIʍ@]BmvyJ 7/P]=~fA֚nSBk&6TSҖ7"ߖ8V!bJID8K.Y\$*`h)~BV%d &]3q;RqL9teT]œ)VRƶnٲ+=rFR'N"55%win6mX_F} ߖjWH4-uT -a#>4ӕ&)S60n]֐ꆬ%.RMBW [G"RDAEnلL0=v%'-L97ؕGDܳyfT8r'kjRD*_"Nn3pa*""4Kc3,嫆 tvcjN6Uh4s#?X#X ~&!^3˓c.D/s @m=Gw @)rz&wSNIb m ۥU~/R=,:+3TT ܗ3`X]77&U~B9_{cs F6JG3i"!|-Jh-BO1KVcIjZ >(٣xͻ8iBeXIfMM*V^(if%HH02310100Fotoxx:paint_transp|NE/iTXtXML:com.adobe.xmp 49 49 0 ]¢`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-20.08/images/image-diffs.jpg000066400000000000000000002066561362435004500172150ustar00rootroot00000000000000JFIFHHHExifMM*V^(if%HH0231n0100Fotoxx:trim_rotate|trim_rotate| Fotoxx:trim_rotate|trim_rotate|sharpen|resize| Fotoxx:paste area|trim/rotate|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 600 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?cw>r@ #` ́d9'$T?cޏ%M?Gz,?}Oc}.^6?ᥬi]DAq9~J7?& o??lξ|_(|_}xn,++Ro{0j1,kSog\sQTSL_?Gn4۟YJ S_ j:N]m Њo5)=q>èM[$= Px ӕ/?%[̽] ou[,6i裓^7\_I٠i⻯+[u2./EtKf U8ٯ}O<>-]ITIԤ -[HaY}ƓxqM_MҚxİ1.2={>+/,PQ%[.&VL^@ kkM7w։߆#Ht0\\OyeS} [o]>fc|t}t_ e\m*]?JZHDRNd݁Fsں|?z^sɯt>9%k j` 0)=#Z\gϟlΏ?^٥g7 Ҵ [u+`1I!\GnvD> O|.4Z )5O6``z*}_vF+dSʾ?lνO|+++$^#,anhXn@Hi\g_ ]躆[z6&k[o-P0FG-;~*Gӵ?<:|Fb~G}S*ƹk^ey qA&i) Yo9̢Fg54mB=F8R M\lRUKKpHE=޵?xHkxPe6QGk𯉕HwFH/`z]$zҡ:V<;גi,qǒO@v|#v&9 r LtX k_72Ik|BnܙX2Î2MeVK ]V xX\^gbAm5 V+ؔrv8N1d=bb<&(DFF@9AjD nA}#/![Ʊ4@ʿE$RW6+&"?eVŝ={pj4SkݴjW 0׭Oj p"0r$g*G\Uu=.-Z9P[xo``WRrlL\4OQQ'dUg+iiD6cwQwvX*zWe-ŵEndwb|#J+>cMm$ Ӑ r:"-\wm;1Q]C:ı#ob ] w# rۂi{jD$ O$=!Eys{S&#l8׆mہ< WKiķmelX;: ܴn3~ԣ,R1L$E n q8̞<\VxF^M#>F'U>ap=9V,Ν4a4&6'] ֍ke|4hY119pʜ渏ܾ7y2o,wO'#SW1y-@1Jl}߮0oĉ=<%pNOח6!:Pxҫ1f vJZW Q6jRjwP= "%f ~ $sYm, ;@Q xRx^1Q@>w=J+ʐW:DJ4gWGY/7UHX{մԮ61J| e!$>nKNCVF6O(rQ3:oh&dG S"1O` m&'*H=#ۊְVjWvv%$'pcHvN8ݕݵf402y @ֱ`eąC~NqPɥ\I VSdgVlIdB!Glw+0dYXGQl$$Wrgߐ8S:MZ˱3tU!f`W/tۻ&O 5bc iSq6Gхťg@RIt@0ʈAns{|5,Bd - fV0!;H. \ڧ/5"&9 %s_ӭblK{8%, ʬ7Z*M)g7Qk u^vOMiְx6e^[]>F@YUUdq9vPxcqA ;c O=ɪFڔw}=.VxL멼? A-atjY HQ&t-0pIfcUtHM2u 5did†# cUy-k> _QWfvwƻ|;b%YBXfiRR~a =hHf!hG3?¿?_.~KzՄn8Rt75x q~3+ C5 ?Ѕ>V+K_#+-ҲHz}z6E^YpIկf!hG3?¿?_; i~/bbg&y1{q!QyX}^x)coa{c:LFrk/%,Z?@._gţ9us%Ćpcw?6;f} Zug0$챜A~]DG(KXGskUEUC9QOn rY_\ O7|g9'KM PAAS3+ C5 vasKJ"8 5&FdU%i AW3?¿?__ZR ZΚ I{I<.`~3+ C5 ?Ѕ>P9me8cc#5vźF'0vu5 -(gWBkX.~M[= /Pk[+ch_PЅ B ¸bZusfx e5W?&$iY/#rI?)dwf;M.H"V唨?jBI,`X#i #6bǚa}5a=x8cN>G?ft ߼u&] )(y!sgg=BϹcc=yn_00F4Ws8̉cf^P RQ=[c7C܏9:暊:qM!^ts;Ԝue@QL´uO%6Fh>p"d(qjI:Z_r]^1 j8vc]ݒԊ3=HG;WcUaӶL3hqX+l*~>RڝyZN=kd`($׫7ޙne,/|]I'Q9օ'ZdfUm _]t Վۄf!3"#uGhϽv1x*Hl"K9cdmr.6`6l8+y5qru}['ʬJ p"y%5EU q*'o4xIX\O1OL|׻xW7z͵뎬?+΅M#%.sͣoK"F?^E)g Ϗ|6{]2E/Lޢku| IN@ejYmm-ʔ;N[Uv/w=Od:6.çaHI#돬_ _ƚ̞Ѯ6anɌH ʒH[7N8+׺=ųR}Qe+8TVem͝>q_hxWG /ZI}X1G1W 0QzW:u)򦏏u m/4R.nڦ,1 U0sӚ~̞<9Wė:lc Il9Bx·|' ހ&ԙȯkZK֯o%4:)*r_pq־հdVI&c}s$R1ز]3kH;M9Ym,:-p*5P 5jZ61Ao%xj,ɓ'%Xy|bX|=$H1HYw#YӓU\xZ]gioRpN#^ kJ\d:Xi6|Gc~Ρl5+{mIep!\PѴGxvѤo1sm[꥖;%zm=_ ?e_PQn-/4ېX:C̈T1(0O|sӾ_v-ι gGFiUHǖQ@U1,7f9NNJߗҊz:i\ji # sd+8w\8݋Q+%m) Õc gkֱg[>"hZxd;J%AF㒸#N΋Aį餂+ }-Wr1 mZneu~Hf4o2O/W^ho17gSKlY*I2t+׉<=]ӍOp ʌ舘ף6~ o>Km~MSڈYb`^eN@Ri`c U֙\Ygw-΍ya^!ߺPy ci#+PKkoc! *9"}FkQ~|gi.ٳ2xdbI~q!j ~(Hg.U.a-Z'S"B_cψuhmgֿ h^q=|1?.W@َƒ\srIF}T!ԍX?v:FL# TG]2;B9͏QY+^ wwvZ~k3.&!vuH#*$.ve׾6,xkgz=KWuoqsagw$ װBY@v G@v *xqc0e7P\T u,:6u{XkfbR@Gbf/z-zOϧ]5 c2*g-zT*-xo xK3AgaheP<*#mz彶p$vܳms'٬_9/Zt9P/&xg 0sj4O ៏Gwx>^ѼC"ڊG6[ yȀ&FycS>&n}CVUՒ7R$ w q4? l\j8FiVVSbN;1վ$i:WyjIK|RPI WikG{K>kMw&G 9-uiAoz=՝q$:=x櫯Z_/'|-gek]]ƳCr (8S؞*9x,<=dũi7Mj,M] elcM ¾'oOPuM/X6STN6y~^y29stsu#z"#o)TE=ĵOXkZhC>%1ؓ϶q+4߁z&i u,"5 J `Jc+ +]+k{9O|h"rgrhSO-]\A HOի:m= {$.[JH+Bx&XFݷ#?. 3X>9Ԧ:1I ksn;׎+ӵ 6KwbR<>7YF%w%%oݦ#$59cdoț/^bfWC7V9V $Ƹ.(mlD01<ЌuxV5VT|?ιiBerp6IۜHt^KKh-T T/rG_bdm={UYgy2y>)$KL]O[7*v\XW' Y!ZI`J3`n[ojC%nhZ 7'ǹ ,0P3]ޫK/tۿ ,3ieܸX##R1 (ToU Sߴao o7c't3GUr@8s} D5ĺ\FY }wO2[u˿{s0 s i";e>?溿[vRChE.Xwk/@زHVԧk)?0mg5αΜwsvuH~O[oya$,^% pM$Ck 0AN՝%qo92,H]䃃XIz ;,6.ު=Z)Ovp55]?Ytm9;{YQN1}{U"aմ'Fm@Ҷu.J"HIlyOuLf?$kR59K5ǵHG؍'͹9ןOSWL.ew(LeQl 3cN(..ZZ+42#7`g Wb(v `.:c4]%K#ͼHKwg n5RR0xǙt\/lIZ$]We뎀䌚 wo-#hww nbkkI˽IˌQ;g²[iΑoѱ]ry>Tk F *߆mxBy]tOOk[2M#h ŘY.ʧԄy{S uROјzg_Y">/$8nL"aDl(-psvZm{Ǟ6Bź6mm/yI# >1d i.lBj߲AvnX懎MAJ8#%"uj6z%m3:qy D4olT1GF<=[C⼖Y-I.# JvV$6HAOmM')zk߱ȥ ^SiwÛB}vaE{®qrwl8-|٨.OJռ m1DvxY@'pwFnx-Z`i>d,Md p+Uխ5Դ>w(l `r #(Ax̱tiaj%o{v^7UNX)Of|Vƚma{≭u;FgL<, gpRNkҾ|[|ED5}dOh+^Z:]قtU$$ǖ3{Oh7V iw$9Jq3Ze~ YdPr*;q^ ʺXR[8P"rO=k'C W'<))ou=y1.>P8R>Vk {"MQabKS#n tڗm,&Ӭuu&c~бVC 8|A.ekKh$t&YPn{fynnU(-5;cTR]hEgU-^mB[bY#IWm~xz+`Vo#W ZGn= SW9;>!3AlqlI=k<}3Z {,*Q >—[Wڗ_~zֵosz-ݢ(1 k])Gi$-+@=I*9 W|<ǟgPivvi #6B`t tz<=awWlodV%袅q>Oc^ ׇ4>Ov]FGTCߊt>0m*Qд4v׏oFn7#$py Pz+ߨo;@'QFM6).EeA x5GH<%I,Z,qBsxw]:Ε=rU.W axzߜym[Òi1ցye7wWk+JQw˴a=vK]HӾׯkwDr?֖ q&N⯀1(=?axzߜaxzߜ|UѭM2?^w:%+x$$LH kkI֏15>W7P6ADm}(zn`zߜ`{h|e?{2A}mo(Z:,Xqw0O*YA3}֌}~Jڃqo,lD`)sYƣjy._^*Z3 Y&D0V >?^#ύ.My.^c#Q1G^y ~+gx;hӈ\8}k-sukiY<!9sڱkFqli^"OJiEAQ=FZa,6wE'W;=*X,ɉ.mF]\ ?6ҫZ]j~khw=*t*u")?Trpp6)7nǑJOCm9-z6+qA1gbǢhuNjtV{ed$u:uiPvơǎ=S:oeo-|h sOjH.EU?vq:hK+F&&+)⹠7r%5+{KEt}@?; ;VېƛHRkvw2U5+x-ku3x}wo v\'@':(62@ݹJP#3+mrDK{BMMP6X̊<`@ Gfqnff!D0 M(nHajCMܵI1 C1m۟z-5lj7 #<,27!ғS?LkQdY#9VXgza^' V,d! $xRmU$LjNh2Lj aKxrb9s4mXzt͆EVi֨f;Q'|*qhii׊]_A0@0evG5_IOAy\Mv#_(@اڱ¹FzɄe%rշ+GSV:DQM܎9UB9'+Nޕ 4ԯY )ſXg!!H"؀3Tc=3FEc~puxr ҽv G c)P$ףhF杭iQf3۩eO8"I ^4N\wH'BiZk${C௘zkѵ wK  kI#thǛlBr&tV;;k+M+þ%񆓯iS FZ)$YTyAX7*94!jVZxCEquKꑱmH|`)J*I\⾷֗qxN+N ܯ,71[7EэN69_|K-kCҵ+]#C[-hUgg`vKX o{˩_W6wwjf7{x#)x fF,_s/.>A/ Iu% -3iFVUPIfo`bK(ZÞcV(atz. zK'WՍ֢hB&0=v_>,#H|Q(!#RbbB#a~`H;ᯇ'n"៊#M$Ѥ]=Ec?xv⾈ҵ6/X@济7 R']pdb @_D_c_Ҿ*hw?\R[J4saVC|D'+-YK~߉^%Qi^KpxUw fl)@$kM? Z]㙗Zݥ}&(/“ZcÍ#Devw:m#8сG . 2Ms>>DUN"HULh &X\uR!$$'. 6WmmMelꯞWĚ,BY;9I+,"d Fk[ SN?jz]q=P7E"RBF%X&@OҗO =i f l@፡$gd xN|Y閠ku+ ^]I9IZ6)ھc'$g51v}~{ijtT!{P5m{$?I.ބr:=S^ѭ5n l\_/|aX#/VHc- JҪFpWF< '|wk5 /S4[0AA8e ZrT]h!3 MJu?h)]04rTu9V񗊴V⍿j1}0ku;}OxP7d1Ԡ[m>7GmxS+<'_s} ]Dr=j~i?үgҵ]--.&xw.x#"-mO[h]ų=A5㟉zè쎵u$s^b'r,|ok:VnI*l흋9%f,}E߾$mwy1 ek_7t/ i>&դ}T,-me%N0U%0Av0F+Э⺳6 UJu-tOs5fr:Wus}md\Elp^K)dDKEY<=6|?]zalZ0A+vI?hZY}cu5hm)ˬ͏zqǩZM;A2dr8L$%P1@4m`DLuS4vW{KIj\GZ>bx/mǗq Nr9wv` czMFwDm O4 O~OFRkOq*w#]| x-uQ`ӷi7,-Vvq“= E CjX."I 4gEyfK]j:g&mVVeYcGs][cekhn*$ת Z s*Y@둞*Qw}y?~=h:G+SV"-紴 Tw~ #\յ4NQ2cuX+~!izWt-繻Tʺw6r8l2l(eʧ`.+#tc:fiPٴw O7V81P_=~gq=c elGmƺ}ln" 0.2ޞ0&X|OZ:[[_֧0|5RӬt'"& [?֙g3KӼ-i޹γ?/퍸2h1ҺD>>_% AwuBI'&ƻ"x#b sȡpZXo^𽎇kY_C}l.oeG9^5W%i \Gm;[\G,/ 0JHh;fRk<}jvi؆M9 ^XsGAZ~:v>6K_gX W$85ZWψZյSr<ἄy Ӡ#Q 'SXҼFukt^/ xNsHn#p+9 `t杢Z%:l=K+ @8_@a%d+g2vc|QI/ޯjjhؔA\۷1:)yZV1ccX~5(gHg|DsiɪЉ@#cG=?: Q`y-/zc5ȥ̶;lghvHhY9?(Y3!Q|]5QןƳ5mNIo,H-qI!#N?R|I<aa!.C>1bL_C#9P"ݎkmZXphZ4r^D*"KlSgʳ*H,x^| [˻$PЬ*O"&e-reS{%LdWwxy^s/Z,>!\5A(\{k2Ohlm?4/㫝ƌ\yR<'*G\o4=C$43?\局y:KFtwtδYX frvم-gX7u֘s!U.7`p2;fJMd9SU;B 6$Oֵ#x ͑* wǨMލE\<ѶFӟ?#Qm fxhX9Jp= *qRTv>)<>I.}Inٴ I!zW[l$#Sx;/^D*iQJQ+;:(FQu2Fci/XXم2Wp鑸V^rF1xkz[K^j_Rx͍Xٱ B$mkI񶻭V:}t$ƌ$HK?*vƲ4Ao hϨȊZ~b3,2b@dtEzvx҂]o:t^\*#]@AfQ/&I:PUΜ[1|*Q$qx6[\[M16ّAؤ*c |u'-7]^涰whe ))WO-ৎjbO{ekII8R}і&l~5OOM^ #~d1:DLѻGݟU(~]OE'Ot'|sjs = !7jJ|o>+. a֣9x0 \0,Hc/Q.ˉg.42: J+k5/'M'k? xEedž1Rb~'R+Ex8WF۲>7?F\qj_hv|%ՕvUJwX [YeLJoNyX]Ekh2H*:E4Þ%DF7 Av"8 ƿeC?Ð\PE+Mp.lQ,(ryxb~_L6ym'۵kok[rRЌ?{j>|ojw!?sߴݲ(GQ d0\|@~]o=ؘmo.eLqF]ѫ[W)Ē]G%}oL[ Ho.M Q> U˨w1@Ilb c*G'[,a $OT#Xu7$tM+]ZEa-6u;!mܜYsASEyb& cd" #A0~ <2i {fpT'Z|F׮֭q n2BU fPM)Zѵ681rcݺ=4:އ}V $)Yw?qK:O]\70jߘ,+H 1¨y8ں|:~ugcYPm4Ynp:,'#xEr^ͦŭ7L[c`e`FCcױCF]ZNPJݎ~魴I-uJ8"v!HU)Ez~/ukC~%o%yӬ#e;2@5x*RҮuT6On<BÉ3B6KK b{\` ˺MrIsԡ cNRkT<8A[SmV{;`;5b H$d85?ZQ~mm976PryxguM_TClD~XAg=+پxk>u+NܫAg0O#4|H׼C{ R Yv/H䤂AgFu1Or2 ('V rO׭zſ/B5;xPnD%9[=z 6Z^I+[,l7*wo>0_ژ:iڔ^i7'J*e%Dž{h[M ш 9 t:wt[O[{݆} JFG=+t?KP[}7 {vߗNC×zũߧ㵎QV\a9}?Z4{;"eT(G$e8YⵆX(8ۏlU]nG7ZφfoXh}GPҭ-m-*$p/?S^)xj]Z% `d1c8kEoO^~N~Ӿ&m ^R.ë]7W궷WF\q2HUkhio!nxOG ?4QhVj-z%MmL1=S𴶺헎QçŴ:WִP33D׮~,x[WI_6:8؆`:k>mд]H?~)5E'kW< Ml>+[%f`cV?ݮF_i/ ]kwλm*?|CqZ<(SYu?EZ]BS%ƭx~CU5"wEЮc"ݹ{P1%cȼ]gχ/ֵ˖NFtG2sЯy7x&{ox/QM69"n OUTSn %],G˟뗾χ\|Qlj$qUǽt:w#W7/WpXZV WTR/ _?| xE/-Y@>4r") a@⼷ƶ67İ\iZ7ZW_i:R2`t^t+XbԀIK}r[ɦZ5ϜSt5r([@?'no3w͖k77tt5 6j c'N>'=7͚Zoyl̸aH4-tONV,"pIIy%s)@8 ѡ.fJ9br/7cMshv|9ykMI-ϙ,.^1S/ _H]ie2*`=KGVolm,1t]Gj_̟ryr@Ǩ#QsF-N-'#Oäu5rFpd6Vͯ+,F=/m}>T{;ɪ;Wۍ=\8Gda oimN*a,GؽF'[{P98r }*s 1Ŝ'Z9K; 4@D^[Y֤gV&K(Z\J9Kש~V!ִi 7Bg/-Pd9w9k<5-b2  eg:jv(7ʍq]^:ʢ3k_>=+D]Qm*k )cU1k鿆>قl֭%*@I10_4~šįB[B@\! rr:=k[Ҵg%F9#0+6*xnhi{ͬG罐#nqJ8״tP[bisX8=O]gM~>M*<ˀ˅Tg۟L|Iռ]Y{+i[8 Xg~5 uҴ7d{hJ#(<μ]4<5V Z'Xf1y h^.6^1|_>sY$D`89x9o+ !1"ݻw|˟-ciS}5x7U w,** *AzHÍ%1jWE-p*[cEF|Eq+xDczX"ぜyWN'%o"YømH'w玕R}ɏ_ز#J@P H#5ԽIt{mIT_.4 /pW$&IېUwOx''I[;6x5][C 42c}$j:ey0RA#r%.7'~S|ID~-6W,WAC؅{^ --䰴|1 *#sMq\y^xST~5|sבY $,Ēse_¼UFvқr+xK]M(s)l@w?GdNOMbYh /5m9.ˊs Eyv;e|yy^WeΤlѕ#x,rpFs϶iq93I<#l@  HAˌky[M2fo JS%P#Yk^S'KxoW{xk dl 5_,cGiK=r ?6x@mG,ƼGakoٓ:?e4dMΣ7L}DI7;+|sԅmV־xC"/ 73,vֲ#"ەnc5>𖟤ˤͩ[t饑@<3 cN{kyv20Pp/wI.iׁ/=s7M`u)RAsWz6mF7ukQԕ?G;I :J ̄Hp$Tq\G }Lmȿ4Kh^eI.FOeR }~̉,mW%P{$MB# A F0qߖw5ٮi:O YVJ|ecž)-YxN=Fɍ6 Jdhl1+#Ox:NUw䶿c1jӍ/Enx{I5{uWӼPn-/x*eYc|Oc* M֛6;$Qoj!+v` {LzOpx/c|YkyuE-n#ac Kjs^iK\;IhDr\l^tuxouu}&t]ȻU>rf{BŨI3ZFGׇmyDKQeb03k˶6p]MnoGsP{/<˩TR1V}WAda#fTX#<ᾴyGt뵵m;4(fr$7)%JA#Ko;3}_"FCoi;WngZEk,eL S8)ArIծQr唥/鞁~_ZcO2xI$2= ?|>/|KK_]߇0^T۷t$z Rib3pk.~Z/to Z\ӈ72LJ#s_ElƟ=oe*áz掛g%|F;d(!+BSM,&lQ8=_5] z|;GZ3]\\(`hf,Ie޺Bm!'u ..Yv=:X~4eowI ֵ_kbK 1HxfDe8 E.x?ϊ Ѽ*mK BOH]j$AbP*X^ũjWc`US ^MO>4XiZ|Ǫ*=ĈEpː0󀻆N?lbk%@ԞgBO'̮q_ukB6jpO#јSx̒Yۺ:vU3&\\##l79 wQѥm|A",1_I<>)lpGMaቯap^~e'wLZZzL 7o] cI?x^xkW,ͻy/x9J[_K=;ē#\H/ )eW=FsskIjm|>Qrg7^C0;!c\h"ҋZnO`qtTۏ2(x98#Q\W%4 Ec&{2%S2N302~oscjzw6rix~Ck狗CI!8\8GpN0@/fԬuh`U@bH`?+0G"G)E]) g^6K{kH [[0$;q,r2:u֌bVӫV̷}H;GXj"[ׇOm6_!NQO'fݓ3;t8Qͼ\KDcӟk7Ú|- [M,dgm9|M``IUdH,ß̬4]S<J+˴ya%nSVUfi-x /C|R3ZW401"U:𬿄~'/V5mV*$#$wx_k!'lp]FsƳn)Anrԟ;uk#^BGcS<9=^X/79x~x&w<5O 7I1ߜ8 xd3.p^@9T$l~v6Z_ |JfIɎ`aF85[i]P@Ǧ}˟)GPyk+_iOiusMuv&w"e[sI\ޛRVU:x_ c=)hȚtm֚An?u&H=qYʢTx$`g_2}v+o }A.<덋"%KpҾ5_)A}wj xaM[B΄*)1ȩk:=Ԭd{{]Ky|z܎G ?hepJRbXAyav2c0S%(Jݞ:旪Yj:| @P\\9!Ty4qjE!ԜGL>{o,I)Xb]ݞOYx2m+MY[#iu=r6ֳW(v:Z5 ^ú펯c"{e:\Dr9޻gm5cozd>ō-kSD=/|Bn[;'xv88b.jV&/-@kp S|JIXAZ{"::aWJovO4ū-kNz߄[Ib$Rpk5k; +U JAqy Lrx!ȯp{/ y%+^f=2I#'=y,r7ړ!`6=nTp[1>s[S҇zuz=յk[H6£dg$qi-B/ Ǒn(;8#"k=ςiW, af|BB69qϧiC_?)=8X`t,W"՜9S𧇬|I&j#ۭ4q3.$g׍wqg wveaRFqG^W_ARٖfȇ9@2Ğ2GR1X7 ų81;w`eFOo[{w(&އn/s4+ISD5ʭ)قlt+S]NMBk$f ˓>ljz@tjOde{'&؋<| |ώu{QIRv3sJ_x.[Yݿ~xo\ԥX`Am0ݿNG~GuxnQkNxYdIC7͹_pvxi2\jcС~7AX<>SuҴ5IwH*eċzIL)1^xgOZZǣ6>qd6;(Qn#zuS*~M&oԙζ_qi$񅶯 ܚݡuN~$s/m;\6}zW_%#Z4}oamL۷./$x_u #Rnlu gپXYgʽXdUC*2̲6"qmc ^2m2_i]5K:Ds"!Q) sU>G׼3bM,muVgqn|Vt麙DPIsn4qe8*9Z|WuKώie/{Qeu}lnbd-0 Wҭ{)EJ6Z>벳w> bkV/inFo S{Zg|3 V^45ldfH#vsJ*{ż  XH8tۢx5t4)of[XAօ4W3n "Ð@QmO_fw4mdan4Z(IPʧ?Ÿ  ex*[M5;(>g[P"He|yeH|nP*a)a~ZZr6{ksS[Y{7MJI#cN#,#_o z :mlGBCLr_úxl^:W>乆"D*f^'>#xeu t{pIHwrMp,7A\/rPv~~`9.?mBCg{KdIgP(5SVKMSL𽭍^sou-ppGA-mcS@ui߮ml_a/G_0gxS:փj6x+AY>7FٗH4ԁH R|ШַS[>C > >C O5S;,,qyarqzx`'?T0-EaM j:Dwd1 Vz*@ܢj{j̞%4Z.%zoc3 r8 (Q'֦ǽ{Þ SӮ$;iicI(C#\Xڪi.{^%ݷsv̖WKB5Yvbqbb_ ]~.xPJ~&$:z[Y73?7kGbkW2XWWmcǁ>2F}]^Mݪkahy^ ' "¥c;ض1fvdXy[ svvam 1\v[;մ ƚ#tMU=AEA{>ܾ`t@#z~5 ϖg]WG|QӴj%4Ctxa/x:[R5 3\d2] lj#d` ^==1^g;aq $z^e^hֲn^񍎫vUª 8=+ Fi.t@ nrz526Lo|! 6q*|.['o-egU˩ԏ#YԍW%ftSpSm+eS U$jF4[0Ms>7-$S2V lnyC!̓!FT%5 2N^b.HxMrNߺIA]}unm2?0dv'ǡk; Gj"r]@nG,9?s,ҡGdaۖ t+XQ4aγUh|MtזG F?o>=ΖBႩi{V;W;P9PK @0̨p BRw*RK/qoII8aoV㎀̥牞6F<\g+x\|?=ż3=Ɣ.g+ֵ\kί2)0Kv88k:dJ6D_V;_Ve+]ۂ4RHQ݆0N3\]ݖ'@8/A$ઞ: Ŭɣ]`,w[#)6^wxʼnac6ʏI#s#HSqS{>E<ŞcYڪ$"h-czH1UWxRHmJWlCH'1Wcljy3ױ:OK$xٍ5.7XU(~[M$6F{]~ xI-% 2, }@fy6LGR3~zjB_̓hrQ4%G ҾtHg_jzHD$ߥ|Zrd\nW g>7 rwjڥ.+.|loB#k- Vv;X)Ъ08V4o,mn4ۨle ̛A%b߉>,ZBWR{;(YMyq*|r}|VG'Xx) 9o- q؎򲢰i޾XSڽNm^k8Lc MNzI>;xGD_AlڄapdHE~2?⻭3FC- J#YF\#l/ G^ Ibh4 ʤ)h+t?ii&n,/ ՍtaӦJ>*:̚onfC-ΗpAXT.hi=^t]7]=uYm^v3$sҽ)<uxz[6) q̲pc=98 zu='%{^u7E ޅO'6k% ;wn# J[{KKr yO_j?ԭ5) h;K#S#rVV^RpAWn'Sdq+k=M0e`̠1sRMQ ~Gr^4J+Gp}fkey.zjn5]FImlDgYIf,ĻiqO5,frvH- .p$k&v,ooe<ہ܎ā =Ǿ4l,ItAVn#8z wXׅ>&rHnd~uQR՜:>i6^D6Ĺ#8?i^\5\FM^ܞq2j;չ/3rNNOR+ޘ$ v #14B%cVa 1~+.h6igq=œu Dc8ޮxO % sgL!kLN~^NV}m.PICmp $_j+;m.mJR]up# B6Sw("dP6%K.@߲k7׍loۛIo,b/!O/~#2{sv_iO{d89Umߥ3cl<.oak-!l>i<9 r8j|4/!fEIqË;MŬuq$ >e GQ s#3Ο<ڐ4K;{p Ve9VW'+ݿhOGM:]JVZҴX9*dSR7 {6|ݵ3Un>;;]͓DImm--{In"m+DhTu<1#8?(?&o|!mfյ&m-:r5MukZsF> I6?F{/,3ۙco'pg yڻX1]# Ծ!Mr!羓4B-L|Df97.O W>oxu?-'f}N̚l\I+(Lsžjhj,י5kh=>iHKDI"(B^rZ zsK_'W!(մ{;Galh߆x[@נ9xH.RhH@H*H&R?f?vM͖pXe;Ep1\`r6.O^t)yQA ЕG.WO"vy~%ĶGFRiF_Pb8m}|&յZķ{yck]mN%9j=aq fU$:dUXEo8?) G?.g|)oxuȓ^Ӽ4Ŏ#TGrΈeڠA#<~o:狭"u+8aPђۘ$`r++Us<ƭ|I$'wt=X,=%)ie{=6o4P7%h" <;׎ x>;oFt I6DW!vTyn|u1y_%|F]CIϚY 1YzWԼ9sωr[Q#Z}xUN`QJ?)yPU.I+ߧ~/8F|;ϩ_ 4h>YZkz7ݤlj!b6>p|w-{GV97\IHF q׌xZZ6fUY%5!F8?Jom#X8;] Vs}9*`i_6"*L^|@.h67PY\I2GF ~xoդ!֡C#ϧlmh/}:]yvFF|/ro/U4.T [eI!?Fu(2Q M >'%<96k|}%Pa|1ع+?8t O7CJ?hi_g8t OwN4G?6)\wN4/[M& pi4OM[Mao-Tz TZ.*Ο?Ə? (x[OWR]&ZFK1;ߎ+>x3].4בXAl5lmh/||+WR6]GPH\ݻ.+~#:VKRFVv*x ?:[Vg9gFC/cv/̍$&/u<bc* ,.t?iw|\h9 =EUk 4?Wu73$Y%B1ݘ8sLҿ%WĦsÿӿ)?ƏN<;A;kҿ%WĢv_xww'7[#-fbHΞޠȥ{+WG%˟19 :U[q|2[˷] 5uEq Е|$m9*-E)ݞ p=)S5^& dI2)VK\!-ĺ Sڸ*6c+>eDŽe!iuY $}q]lpÒgEwce{<7WB3$VmFG G|wVc֡{ tuI/4-4z,xWH#g5ToN=[=ZkYTf[fo %5]%&g|6eOFHx,D($zl_4] :3Al`$Icֹe7im;j{ᾧ[OGu,mݮB9w\u=tdfa9)ޠsqW1p6inӀ&~<N8nax1~3T>?&}4I3`G\RN>m7&+IEϛ(Cql\w,W1wHpI!;ݘ\:d }{($h㐺cG˻8KM=Pc YÞxs$Ԧ#nfz~}WP!n߭~|Z|I趚 +mC+cjھ:׎|-XO6k$s'~`c G|פ:|Iz5%b{U!w#q_G])im3ZS 3Gs޵5];Xet->kXĒH1d|Io5RAHE嶠E.C!G#ʌVuSw6aq<%Ťl9.#H<C/t q(/2lY͹hsr8"lҢ+h>`r68{86 .3ޚfUl˵b3|O@c5j-m?79<b+/,ؖ8 9ԒཊF-!e{C\A-)a p!}>eCG!K[>Ex3CT@\jJelD>lgkܞR(j֨E{WDY&2(:r8k?foF]0\u31rD<nv{WGm-}ZKx9,(?([$p;z_Xےd+MvZҋw;aW~'o9|EXn- yH\``8#k~CEkXoH+~+j^HX+[;7%N|=4KŭnY?; /xH~=UHCC牴e_ٗ| `rvJĵ0k(#F؊˴,4QlhgcȤTPK^U|xR]&U򕃅yX1w zwnkJVMCw?Śf5$|Nke86zF5Hd3~3׊z.U՝! uq +g<<<֚Q鈞nY6J0 ҼTPJ_n_5o|ZNc\}Yȕcw[_j1TƇeiOi!aM:4Vd퐱eSLyV_c˦Ymquw wLI 9zڍNQQzcQiXEn4Ԇ w9<euV,sXlS/QIfKYv@7&;xWWWܭ.Ճ{OI9`: s)ՋoڶπbfJ,r=O.иy6\#e'"4sHX^FʻGۿn}-Ymk8P#+3cZEiwͫ.[XnH'm2;ˉ9'u+i\$7JlulϥPXۛtos,RJv1p'٭]^7|"-E..!hwԦ sYsIǨMV"|]y"ɭtMMW<#[ax5ׅ;kAgql*%\ )=~x>KU^F0sNG|t]F0 ZeRiY!RIBr͟.pOB(t.s)8/~? -b+),u ]HeefhPr"U c⭺~nZIc|&+ľ|7+OWMqJ6Hg5&sj%zetg<6- ͫ\CF$]0kco$׭n_R絗OEYV!fqq)RF$nA{{KOwvi) k i>r $?)9^ncVso+&ftKgoV:qE"xIM WIpљ;)1oeYkQ\)-2M[In# 2;X+{׷wJ QKyD_.DG;aVpG/b $UA pyMnH-V^:#Kǂcn-HU@`~np|?gj̇YC ܗA ɀe :#RxFRcMk~0UZvE8o/Hl"՟P(!/ ʟVUXcoC Gm0l .퟿ Ps~)o Ⱦ aoȐ!FS qPv5P?:.,Il,xa9q0IiFURsQRI^=/b+U^N|z[~x"_ izUZmc31W@Vq'U ?CP|CuqI)7uȿx``5yOKD/BA7ГpAj.;k)qSiVzBvkƪ\tZ}RTkF~z5Zqae}[s#F[/qeA&tIqL| %;9dC( zk2RwWt9ixp gde Foy= ½|Y"֥KP^2Y.c){ۉ>M% ڧt4KUפ+K[#+XA|3Fb֗T2,z8)n'j7Gφ5:~!:d2ܗU'OeSڎ[떏c`n/ =oZ}~(Լzh,c(zo U> h2DԠ#Hbc1[ꉇf?.cq?S ?|5?^,t˝z;mP;Xɿ?hmrp%aMv_IsEYXu :0xon'j7GwԀ65^ceyuZCmzܱ`=3N)<;j4@VEv ?]}nqEx_i#IcsJ~Y3J S73Ư7\0,8a浖oֳdQXzAVd@$gw`U?Z/.t4gUɊ\855;؀JzQ6CƯFPoM m嵽#Bd rۈヌ 6/w$LqWihZpҤ[oFO01}1ǿ׷[Zs8c%#t58luD=-[$@*{MV([+{== K,Ky#U$BD!9x=~f{FfFTVlx\`{A^mIEX{úi7B&Dwx~X->` Ѐ>ƼZŦ^n33Tq|/fkk[rH#sUFV51Oy*gJ22;t&|=iMz\DЕRjԪps_>xz~#4e+1{W|07 ɉbIT+ 2| :ч?*:7=4Wz:Ўǎi2xf{cqbsxCф]J"{JJ) Vp{2&^҉7)+Crq\V:#KFt|KpٖI'xw2I8kG| 'M.[ L5RV36_ G(wmsŠTYH0Ɯ&JZiJ]&s͜ aץzNi iGn/Q#Ǯ1^q?.l{S4#Hi4#Y# r5Ȧ_F˲|RӤzTHG*U$s+#˨4E Mg ` \$.٢G#2`h\7c6[̌%KXi$#<RԾ+ Lmڦx3 3-?Eī[[F3I<}ek.W&ygngqBdb@#J-h>mnl+p,zW\Jj&{ole\̤uu>޼㖡jz+{]JqeOxIRXeɢL-q]m_\)<SMC&Q@8'B ? J:Dڔ WƓwnA @ӳH2r;p uHM&U$E `ӜEwu=[Ƨ2eԁXق2m=J{/8ϣϜ]9=N>]7V3s; >hO(!8ۧ_zZ<(5nܵۖ ӓS m/;D?Ϥl5{IJVGw)SljEH%1bt1G}*wQzこ+oYm$)"i%[ꮪTdvk]VZ[ۛ -,qFꥃ`׈g!NgԦEracg~H-94J\!RrOM[ojn7Z Dn1Z^)ZtƓ3a$B~\k>xx. &ٮ5)|a9# ps^k?9{cebm>eBcf#j"2v:·4;JcnnauҲ +~xLEw%o,zkK*#)%yV&]Mccƙ2{Ky[ l>^pjOeo<qh,nI9QKKVFpQǘSnh_UXwME]kӔ_Z-3ɻ,],\M|FqY:el- .hae1q8v}[:kx_ :sӞtZh[|ER2IPa*VSгT&<)+;IgO{;(R{ uͪ+S[51Cl`zݼw H#h {Rյ Kq,xYBgXMqʥ^ޤFm/ieCchuf oE[;ٴ۱k3ˤ`B `}귂|YeE͠*Dț[MF s ) Ň%1ZOj|$ ס5^g2FS;(dzr`Y 99=0y֭C\Iewmb+D[o1"յIWІH0;]Xm<9#s%o,FgcW-ί]40&)fBO S:cMk+S( ozFǿs#T}+,z*[>Rxau,Rtn`m"_ع\}GZ+j!z&h,-CX{im`LB!6I#d7Fڈ4na:{$W?*umcxGX'u:&6?1"#)N򝧨V5&t|M&_bUL<@wNIYM;Ժ3r &mkZZAubUTt%\g?78G|CmiWTtcE #g5ʅ8xk 59O~5?eU۶qwҵ5k*M?^{fS0bfڥH;*YNq︊PqM>/S6K{ 9bOGVЃ@5CZ?b^"4R PCcx2:Wz7/ k^)M[WY~L'ifFA_$MZ/P &/&oRFRZ+u$ep8ږa7ӫ7UUJ3\xXnjM%ܚY;Xcgh5]'TxepmcFI]MS2ln#f¶OTVl.HImጽfdLS&#N8sF;m>&EIDn啈RA%8/*-+YJ}9]^P/?'~:k Aמ 6xu-).LY]T6_+}B~v׆4:=cX;=WM1cqŒ+C >GJ>A[Yl<;=Бl fr]_y\W%Ѽ?siex..49@d#khhrWT%tuB9;ŏXk>s@[FY$LVY"*p( qTe'Mu+6re֣h)Րo:9D9GKT2R rվd1?GC t8AsjR-m|(~o. ;kOIS]Iӄo-Nß_46moӊi)8ʁ6Y09os5_>/[NE , OɌ7M֓-K$ a k:+"B\/&/OԼ=@Fk$+q02A5B.~TOASBm7=jVc,5ڐ1;] (ьx7~a!gHV1>rD&Ü0(~u;?-TF$1J:R>R]DZZEim["88x |!5b[pPtUN=xX&k?kǷ^!=kaLNMs,8˞=+^hҜ7}Aeٞ0M+(Zj&yojm![4fFF˜v%|Yual#MH.-rmo8O GP+WQҿ.O"[GGC]j+ r˪bI\g?iZ:W6wd86WKuH|1 );P g򡻈h7L񶁧GyVi:{L"k0`ǎUOɠ]q<1tTAgv@0ziGR3\E. xEԾ\zz=ߛ:b[AGNiG5'_M diڶI3 ܁26qF5ٞ0M+(i_'NVǓ!|dx\2Kymx,b褸 b#n\leNXCchcGEL$3H1 }6 f$"`GqW[H&u< wqۃR7rV!IP/L~pqM&5ѭ>k 3d85$KkDH~٦[X|RILS ,’jRTdH'9\.O Cֵ)+c3u++>淾x'FrXSǢ&EX<$=NYZ1bL2ƒu:XXH |LI]ȬFۅ@G'C^ɡIb_Es,[dg@=koDnmCX@31˞}j굿:COpmB%y7÷dqZE:\O4:6 O=qW_/A7&%$4Upl bOm2Ma]j l$2Џ+Ͼ)Z06:T)y2ɑӶ=vB0RaNR[$+EXg^Hg=uP, ;p%@׎k/-COGcC!7yqOƽ Cs\GZ]Es@FO=<#tl$LKG<(3d.>O3ۥ\uGI$u{N.R/"/5za/יxVX-%m+N:Ԗ)gcym䕏NrHQΤb⍢VF+ȷm!i[,p+)m "03OW3N%yK(tTm#_Z<[ێ NGa? Qpu,uv"v8_ss=IVp@vZQ@r?ΡV ˔frL_e}SeҨ<Iï2=W|YizVsn.WanI229Y1͛SVg:'t_ S$76SB+,# ޸^)m3S"g{[` 3)9A\~-a-K놖[h\ᗝ/U>]R+{}~ B}ǯkB1s8EQ ?&('If  mČt44i2M顶3~FI@A989^M|cws6Px}OI$ywH"OZ<%+^;B;-W(uv*B AҪTJ*Iۻ Kڔڗ$$ Pwu+|N}fr:зh2O,m&#ڊ͖010 '#cq*Wxo7.Wkϼs6B۝8my'5̣N\ДB9'lzƳPaQe&f]Lj_-dohTx˯J$ JY$8*7)ocseC"LT4[ 9jъ-uyz&lB"0=K0TnW]uXSV\k$aܓ>wblѶg8=1[ZEzztyAAAr:$l˺"k.Xr0<ߌqwbiBFMZ6ND1ާMпmAu{}V|kM8+bw*v2}+KkfF$101h^/-$P䐴WJJCUa q[4ņk1=XM3@iz1#@90ȚXh:-Őn'[,UT`A1QhcMē?-b]NjWk;jV[\[Z=ݙE1 n6C6cU4?>b!RtrC0sV/C7tWrikAE%/%+DZmk[单wI\$ m_1I[|Y)ce=HɊ)jj20 YÝXռEoKmt#%dGh@ڳ9#{ MuD+je[xPP% P*@xAN*톭i;.y cFf! áQT״?F#Qu6c!Hd 8x}FoS40˦L'&2Fܪ8`d2אVv5Ɩoc+;k'E+U ܩ$bֽ.MӮFap;";"~Gե}2gr-~ž45m- 2FW]/A2aOo,Mov YݭյIxU9M@<|G6I rmeՌU3eB|# YSWM/̇C.=_Q#Dt^2y@FV7DEej߈|1mIPדS԰i!͒0>NX.^u? [}N(70,;`nue`Hc`ٽOpe8cRUF]qy5?U RSsZOvQ6dyFP{. 𽻍R%X1G4QĿ3JF#hVbjĴ̷1nkX+cV@Jg.rt*Wtu"Ec.ci\YF$%01*IKXUGLHĬoBvqBUg±jqG x•g.i@q"7ͩ7GComL9 &;=Sο@8[è(K@;y3AuWE s$C? (Nࢊ(((((((((((((.~JjZ{)#QE0?& c' p[K* h98 9Oco5у p3Z:i,A.:Ev yq$rbKueN'9?RC9.'}ѫJp:TZrAMV-h;S.< !qJ$<YjsMg;^4+D7Ϯ^ Hmw5tZMv$(9:W:|q\>i$yobH* `uEch ѥC<),CN3u]vO`_7l3d1 !7q& fv2N>o%xMT&qYI`!dJ(gW˙ ܳnl0#lyKTFᴍz6r=rEΊ+X#632So*F%Ou,o*@|WL62I5ӏSyxfy@cК(~;mWL*(uJZ]M%ES(-٧jw77e/D̹ C֣؂y@([&zgҡgL#. oapT)mv5ñ*`4Q\jHbأҪ AqZ6[)E] @#ETTQT͖R T9JWxSVHnfe'(]Yj)$1r{ -e 20(zMUS.'9'1% p9㠯X_j3xrbD?dzE*O]Z[fUw en ɺLֱy9QHz}yvЇ>\<47wab?T,Rvd #QEl1iXuw4SK")xn= 674 1107 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?kO &`ExWj EM~_@[/aiPs|W.=KH26"'ǩ<5t?j$x Oƥ2גႸV-4=ֱ5EO0?h]7񜓀>]WPp97mndXB%bI晦ٺx45֑[0vVtv  ͬwkjp}ZU~BLcܓ޸yO3]Kqru \]$0;RC',d*j6sg'R69RIfG0WzWm& u=2T81Y7e53!G/.S"|˒wm'pZ7sfͼy`429r8O?hZ7Sx;ޏ;ސrDހ4|{z}hɠ\ޤ3~gƏ<>R}T>}hRڟƏ7ޛ@r?joQ<Sx8<cGMK6{+\CzoVwq\ަ7<'|hoVG@rhoQ;ޗΠ9MQgy֝hoΨK_Cxvqy4RԾ{xy9M=i>ުq4\wR}T|zq春`̳DsB+:櫪kVιu'gbWa<2a:NPcG—k(gs0ҹmIZ$B̂DYT2 9-"=atu&-;F:r&b3 _YH-li;5=J <( h~5}Hbi!r10=Z|>p!wD]ٷj Bkő2ʐ;\9ȟj=~L5 un?u]ܿ#~f㹡_\J@08ԗ8·n4CDSӞ F[žG^'=Cg ~:*81¶<];;蕷̚R:v_VMU-/tOg[D:9GݞܿL&3 ⻽D w||#43_wr@81jG8x3Rt%׬td"1'V!FIq";83GO_ݟOkfM[X 3ս+X.-)SBgNL? Խb1IG"vpj^Tg_wD>vpj~T/Bg/B5?e}WQG";<}OT/9s?G 2裑;<}OT/9s?G 2裑;<}OT/9s?KNL*(A>'S_j~U4Qȅ>#SKU4Qȃ}FLQK/|9?+Q9s+QDt (AO/x/RbD Q^]r g^G!zwQȃ!z>UG"vp_j>TzG}W{E9zG}R=(AKT_wQȃ!z2 Կ]r gfL? Կ]r gfL? Կ]r gfL? Կ]r gfG}QfLh;8/Cu?e}QnLh;8?Cu/e}QfL(;8?C5g}QfL(ȃ!33PwQȃ!33PwQȃ!33PwQȃ!39sO|?r-''r+Ƒq MKKu,EG\į C"d{Ycb.OAsP5-U]7k'&xt)g⥝ԯg `t~Z "`FUm-'¸/'H}v nz U~\|=ڏW5JSJѫ}N` ۭg8MK/ez|QY"d5f1IlzױAy^Dy^[1*g tE=үZrz?RX֯wgoj2. dm䃓AެO@{jlէx&b1&ߺBb_C`0?j'PnU~X_In0Q01~b (c@cF?EQ(QF>OPE~b}?1@c1Q(QF>OPE~b}?1@c1Q(QF><+g|NB.ErcsF9ǭsؚ߲!]G,~] ۏָڇ>$Zkqi7N|-pᆕyˋūa qdĊK)9J*LF\e^, +i!հ;.}+ma=Fq9x#=|qCm.x|O~֭"1y,GX}~|*$߈d.v'ߓTPt#ZtުWCo|Lѕʝ 1',?CSZLP>asI#1ʇ-6lb.ш߻p؜u9ksٟ\/1G,?NjqXxVIK^"X]xDkVsڑy |H[,T%/1^fX.zg,7ߘ^WE}?eܾyX.zo,;vtqE{4 +żmUoy7 tb)8+4>.]* ml`#{8D1AM#fg,5}B\ ;Q CM+9m=ƟOk4)3Fz8}kǵ_ *bZZ[oȊIVb#Nw?t}?h C_4Vz }&%q0J$uNK ku&dWOpsޕs?h~أXw=KvQ ;M+qF(\/Yo/1G,3ybbSr,1^[;޷}Y]Qծtke+ۜLgc?Z:o/1H~)i/+o;׵C׬Vٴȡ)|J\$5|Wi+k7Z}HX?;Gjv}k OMwߘ<1_7|9 _:`c]F22y+qX%7Ox.-) szϪi?+95qs{^"Gkm5.b썛s.x _wZ\s|-BX\W! ϯ?izg| J×zv l^^El0o}I844.AI6q`gz].>?~bZzg|Zs)5[]"Up{89|/(EӭgմX"@gxEϭ?i?+]~x^ h@,FY'W$h-bz;IܺpȪ>}Eϲi?{@s-zC,חE c#~%|i|x }ci)^'`S\Q OL+5 4lhad_1a >X8j($I$H2V@b)%q\OL?t,1^YKEsԿhiܽ. jP1Eӿ{_Yo/1^`X.zw,7nWהGxsW"mtѩ}.I(vs,>rnrӿ{/n*^Yj\MgO*o|d𦫨Zǣ]pi{q(̈́I(jϤfiܽ8|Kqj:ޟ$:mݎ&6&c}G$/aE[WVuf"Fˁ[.Z,+S)^b6]=Fhܹ铟Ҿs?~ $Zs<Z:-V{ܟ?g^"'"[A2vAşF :u׊~s}FTcoZqmF}~U.IN*zKo 5 #!2,Gb/Ag J}ZhcKo$ W'Z5oz֝iMmo<2]!`bW,FO#OK}lt Ia]\O{3BeA#EMχLZ=zS_G>ܨ>خ'Kkڻ6ƑkŨ[!hvw*J yZѴ]WWa6۝>D/QˉY6<@=Ӿx{:Xj^y<Qa~k;Kk?f}-2,θbW9ۂ8X?j^$JZimk eřwd@Ꭶ?YOK3RujHB#gv,2;bz du>Xg[ӦlLdԲx ^!ֿԷ^[vLSRV|:u+RCuֆ[N\ē}W {NƐJu mˋbcP~A鶄?-Z-Kwh[tf՝xڜhK,Fd/ON'>&y]b 6MRy8Ef,W y;׌xk@H%6-c& Fc`gWw{c߈V+տ}5DK;\Zp9V}bx7U?H2݊?.X{$׉MIӯmm1% 3)A#-BIZ+ s4ß moHTMsڀ{V/m3.D/kr T657pKEihQO :嵅}#m6H.Vc1C= 2|7@ӼQ(Csjx ^-bڛd w7N}k F6y᫈kk,$O a WvI=HnC;tO'-"}bh`Prķ՟|9t??mj3^z c߽pĚtz{}J=Q!.#q h pq5V?/\*sN}5"FֳaTgpCBZw^i X6w .unp>O7 YQVaʻ~u kڵݼ^-nBGQݑ-gֵsy=F}N'l+(tҞ̮];XuKӢ7'؊n>b? mm#4[&; *1Y<9\$LԍSyF'Amt\%σB|)w[4%nmcIa.yap9q>tE-Rm3G%j {h<r{G YmZMЗ @\29o ƱM^L}^KSq`Kxʫx%>My-_i-WVi'#ڵA}~3w{dMC[:MC1!bbV]f_[Ieɧ]^Kwk$W9e3m=ՉY!mi::%s*\M(ZA,ac~xHФюsiVvܫs+3O ç(`{s@Qxnx=5Zb rrvዔD֮{um`36y Dy B_ZVK [[͖5ĺa#>bO1Bэtgj>SEm[O[R9>ox|UVr|< .w+mwBx3>W7VdW =iOSVS^iڻȍ9:Sz-Q|;4zuelW9χ|'/W䶍sԡe.NU/x_WuGX.c=ך-] zqol=7zޟ}]b>sqv_.Y\c7f7_o5Mr1k|` qǮ8Z{ig #: mO'kk$=[XD;!V0gRk!tiz^[YXAa*F\o+Fc>GNGG.l[HVG<1Ttn+|34 [)t?Tk7n-<{>oxoJc9$^LwW/W2ŧAپEio iߐ۵A[<'f2]*]J.0vC0o1G#]85*d2/ϟ=x{&ƙr:E- -AZ< /O]Ee:$*-|(\'w=s'zE֛<:󦹞|w=+<kNj[5{[Cd 玜V x_:biQ ʞ:zW Znj ̷>,uc63g\zv;M?YM4k=[O&l,TR BvsƍAnuXlmo( lRrMmu<\iiC/n<. +jz(Դ-2s[v-bYA¨Q{) A^ )Jq1|W-P0 oJz-n Q/5oMZ]M6+ho1)! /`n'oZ|wDvȢ=Vflמa|amnaХ1kz!͜rcZ ~ůYay,aĬ~fOsּV/>8ɥ[m,/s4|PH.m'YᦧxZ)줎LHU-BqTLS$$2n7+#Ͻ:< ,/a(dVT' VB/JZ)å-myYxSPm-G"RN=zvR@O \\35;XuWB9\næ*Xx[w8A>-γjq.n H N=3T/K5M-e]H9 7ҸKᇉ/gҞOOa @vwdVzmm6M7H_}ԫ0v>p@%Kw4qu?o|Qf %Kq/a+oEco 񏔑d~fi AyKsF54jˑR&ʜaH# ndn兓v2sG͓v&zO|1jvvwެ; ` 9_Oʛa*w74 24`JLJ^̮sOۼ߳Anok̓9n[rX|6}f )Ry򦠄3?ohλ~752ħʜ֙r >wϺ?74g]ϴf2Xgϴt?ok73Š: ?f_\ϼ7?/kZ6lnG|o/kҤH^ߥ|kMxdUIR{fF? ⓤQwce(R.?#VPY+YPI(\Z[~TDohIqSA& jSF_Y}ȓyh4]!w&Fj~Y8_MO|_O =E>y'IIc~F<۫#>ǰdQEܯgy7h6[&)lG.#/oɯJ8bx#?4}'kO }S_Y?O'h 5 TRx[uy5Ǽ7#] {JǠҬjdlmzܥ^c~s4ݓ~}]?,9VnFAR;՛Mt?^ӭi{b1nU+M(jAhp;WzcHv67Y\R\+m>HjB47P)=(ކ rzcE}Q7OG?k:Oj>\1<Õ末 GORA#19?tTF8\Ҥp(X٥?G٥cT;Eױ?OW 돱 iI'?N[9猟ɮ@gӂ9GԼ#~?4}'kԣRm9_§cߑKcpyH'kՖ1ncnB/1mL,Q'icx|_.~bPI,ZK2/N̎tZNqޘj9Ujw߽y̗fH?KUe>N,v[g-Ʌ#9#͸|NIi}H!f9>C?.fiC))9ҋFX9p=jPӁspWi uF158vBJcjw7sSwk7#HJ0Ng"\  5$wHڦE X9e#b>>@Prj!\z3枖Nx#q@6w_߇_*3vv\)ho(0$>/ZGZ ~Mov5in#G+zxys6M5%{~GV5z<s^s]gКpJ3^6&kR0Z1dS|ܞ5UME:1"7ۅ]c}KBLT7"+w늊CP[~Uk^9hwLo#nq֋ܤs6,?gIfی=OjkMJA nG_|qg+j0<Ož SP[_קQN|l֥$LRT;Ue_ 5] /mB؁L_ZǒGr4F\?Ld~ЧrJ[V%B:%umgdGDu(Gdk^zԯ5 =qCpe7ze-Uh\o*KڼXMm0yafXO6/ޫJ31jHfS.j,3F ׶}*u]ǠC>1ݵJ~eSʏZҺUh.c$I]-UA??ąj6hs=+~>h*cs#%ZHF25adx #~U3#v s+"R6^9nG4.Jg Gp~1]+kV~$V'~2hte# HF{N1L1QS2*3 )`NOzL&9.q?kv9%wgwznlzdF߭Dˌ c4(XwSU"Iŀ'`˱~j;i6M;DMoqq5(\.y;Մ@2N^>cl  ku>Cv1[u#%[붍s3G kbP #Wx.-ͅKv⽇Ú:ռ!tLy;W5V ntBTMK ' g\>Nh<_gumzdSVH{8\Tgr~@թ0=JNBn`K R'Ғ7 + @jŹA![N F*|? QsC-Y7wH{Tf^8p7#EΥ}(3$(%юdh.챠,N0\?km.ת?Cy?گ<[]Ѽ0c\DD"܎|jL௖̥zb!Gc ;AVBzd'rq\lcg:z}i`Sgk)6k.IgګIZcj659s^3,By LU]Vזu@e<$S+޶EXM:AA֮R##սȌi)֎QJq=scP3| ʕCpð-tY\(,9?Jm:D)7ݢeN23\򮑴i3͛g(@º-:ͬfSFI5!S\%7=QR[m5{uv,FFje9P}Ɨ=34˙c t'ߚҖ#D/n9kM9e3јҥ)sOMJGoxaR-b!t*sbĞM%j6UOҢh3L0 4U sFPF)Zk}1M֪q?ZB`k/_kҼylFk7`g-EF1> \ѴkI'>+1gOq54ym0fO0o~hh}1O uTH9u7Xkv6b^|uҰ4{$ʫ*1@I={+Wb3ܕXGIATFMΑz 8[8]u%6W<[4Btmb}5?,n2XZO9W>$jSϼum]Z"Eb#G s7?6bR̓0 |Lwc`䴙y:Ud w3o +tX7o-1rN݊] ByYi'z<ŝԱ!?5N꿅z_ÿ C}B+Wܦ>ҡMjj x?5gWZBj^u?tzQ^ BFiBy{rsU3Ω2@FJ UA={tyq*"#r+_V )MICqA~-v}ʖ0C#Ё޴PgZo&?zg 5~=|=u+Iij3FǦkjo05Ou6|Cl猏; ꗶ^iNԥAj\elq3zo f$,gm"U_qWc|LI$:F:D0|8ξdk0 YlX}A1d^G_~6>۬rݸV? qʣ1I#Ӿ;~0li $--8_.귳};NkѼ=ۣ+ӓ?HYnk*"x'𬵓]EhgcۇK~R3L_[4;tF$<0+;h֟ /.K {W|wkJ#jcUnVQw:~PD c9<gk\gIaW%ҽ:ZUnn_RSEMῊZuy,KT[=ih)ʫo|=jp9)cھU9_r0rcոDfD!yh8s͎Z@tk"|]#|Ld~ pH@g`:kk2ߤ\8;wW8}IYqx✚Œ=7262@󎢹cԼ?gM5$LgKqkZukZҶb(Ҍ]ϸm H<Ĺ}0MqƻGkT}em+JӶ˩+nO% 89 +=;T|)5Է/=Y\n|x^jVZJ\vbTR{]ժI|efcR\YrWv>ZjWbVd1::o- )U!V!H#Zԟ- ?q,{:<Ρ#/ f[޻"d4e$@}uĽFF1#=ȻTW#yq4H -'.q+R\Y|2&QwN?Jqќđ0qU|r?{y4uVkyc7';FR:p: ?l7>uo-aKE^W|11*ԐB+jZkOx=wNKO.{g Q9ڗ~$w%`#78>(ɼ9j#ID2V+i^[}v{=kuw6vy-)E@85hVD:,vJ^+y$9OJܨDxbgQ$q;\`uyig4BvKfHv89:^dZ-F Pw}Tc7}OtvotO[wq4ݹu0v<_Gosݾjufەyv#}]_:it-A'"ޱKἎr)EnNc*[\x{$lҽWey%ʠ1EhĤzT}*Q"gҼ^>'Q]ۢ,Ƞ63z!_gC`KF ߞ;VN*zc:B8>Qe7I-.R2~Vh5DgH=H%w,^VRO$]:I{ ;R7>SF,6tiv_RGms(9}**gHG;OB~TΓ2eG+G;O8@vb}'. ( 4Z}6GDqr5hszfYe O7R)R~к.Rq21^H} Y.1J9^Uj+iq\Θ^湗fI|qIīi#ӣC]$ tO*Z˪01#dqu^ޙ96r'=9zu[{/m;Tf.Jk{{VZyR)\ \*=F#rw7V&O5#+_`5[7VѺ Xۀ dG1ujw>ۦ%dcW;1ʺ3;穥{&S k 8XɪUFfwisȬkvn'V[,{T7SIW:_XG}{m8Z4+YRNBxC{kڼzM|{8ܬbSG/^3ѵ;[lVIvǻt}1ςdIf&EB!8oƤucbCդDle3GQN} ȱ\*JVzZrC9=? 5jv7^mya<~TFT#`DJ1USE NUti˴W[kpXPz5KI5^sNלu܇IԱ%NDI^kKQx3,M Gɗ۽Ւc @ [ q_N'hn󯡿g/xúS1 Yi#Ӂ-Ω5:*TUIa')JM5ouw<#YQY<_3]6v0 bK:Qg|D𭎑{-3pϞ,^5r8qYu[]}ryzYN5G#55ӆJF7W*J# x gV.9Rk5RafHԐxnqXRV# A79]؈"ֹx~4[sotmncW}xTjR Zǃ,|9iiAVޠz֪7DcY>r+4=tM-FK^6ؐz_pͪj!\_3\ W ^1(U@kCD'1@m|S>"x~}?PvMnqQ_|5<%Emu9a]|p6godRUis&iNMJӥ 11ъ )y(>S\s*g.ǐxx5:WŔh,O g%Ynl_Zn=M%"cb1 sHӕ6npF\vϽKypBVaо ̪3$'<)X,?g\KKIᐰa +5֧A\Eng0^NGg:nen#lHe0 9ZvQqHY;t֚=yq%&Is_0OsN|3A(Qmz^j|3\jv 2 0O7HҬo-/e3BC8$+;~Vg4Gg۸?t28c]-V]u.?mEc)+&:{5ʹ67GkY ܓYxr]K#{i05@OZ/oy+]K^{P>L&Q:; r*jݑͭP4%2ZKy!1޶hrY#o8F/㱯KAxI0W1.\cz#i /xGȭ$MlyrPƬobK&$xvֹ]i1^h"k-PcJZծ/%y,PZDj!$s9u:K۷O7i|(Tzԋ,¼۷q<սaH$CR]~E&M\DQ:rL06穡>ј &Ȍ0bbAX1+i#ϗ|s'Dnj6HkHQ[= }Yi~lIpZȞBp̠ g-Y-V7Tg@r=g=qGKsޡ{=3[hI!WP8ANŪjZY6 LW*qV(olrH}P,/e$$z=tCM-ϭt}Z)n{a<>Q6:gڳÂmN\1#9GBNm~4|3^=/|D%XCϯvTJ[:zVzdMspm3m\Nk_yWKX R(rnj|pzkeb# 7=r+4˫t,MӜ9ִv(}٫N [.5; ^aBFUsk;Oxc@Fva$zq?3u/p4"&9cK9[O G=|5j4|LյXZ ={Wx[$q!+QNC;/3Xm}Ee2I'0 w g9=-fNXDjI Ԟ|x ۉiA ^1@y/hgO/,͉I9XJSHVյmZCˠ\1k^KiR+D%p~:+Gt˧uԕHZ4 &I/׫|+~#_Oͭ7]W!I;Ap;Rd{Χwhe(ؼ c'ںQvq4sC֯|O߶D1۴NJw^i\[~H֚\PnelLdz)xRhvM3ȯ3]~Mf#FOQWOqҽZ񦝡$2*XʮC7jN'dLK]"X@}2h~S]LVzl첑c}6ڔ n!ӧVIj\M*wM[jm%_gw sOmHb=zW̿OJ랣=/eN*] kq,㕬/*s]@Gu҆^Oz)%:RoO(zW=Ҭt~?P dm^{TY,Fs\L1[xv\ XxiҢv%ڞ uGC'Zz R zu1)1RÐFzן<:q2c pckmFw:}}2HN$֦O\_h~|_k M}mn眀qv_> Oujov@rVQk>(Ž&K-j׮LwIzvh5Hudp-NVwZz t|ZD6zd޾vD#~|=5 ungҹG/[æh~!J  Ͽs晍ek<:0X2n$[&Kf@$G'1^V\C{r%?}ՈҶE͋Hm.Uc*zCs<ښ,VyuegŴUUu=Wҵ 7wFoE}7.4H_Lp Xf&. uOҚ̠-a$9D-ؿXn" =H&4}lǸ~x nemj#0㞿|xMWO# gv RVeS۹Y~rJmtPSj|ӵ]VX<[(:{1xSb{8d+w=էEcH@ο.9։+s>t= : om$,ۈ~?Լ7]^hq4.y%FO_3AɢtWtd3yǵ}Kin^ ܰߴ YcP H21S'IIEXsZG-n֐]ȼ޾!+&㡯V4͹>Ү<uir(Px4k wGm3` !`dgtЧ9;-z ? caK@@MQpwՍm`P:qڛl [k߅8*'xz^EyiVSJ/Pb׮kXY^'mLjn3L|{ť b̯w50y0%>%-9j'ڒ[}ayKKq>~]RTi(&i-['bxC,4*#5h_90ھda$%9xÃR3]N⛘DH(p:*}ZOcb' >UQqɮYFZGmݳ4?uA#S @,$xWNAbI^5u7_$r[?JESDbՏM}^P{acWxPueeCo&q}zV4r>CS,S]ga۾Kw29'9n(%3i.,F5.q\wvnc p8D[Wn24ūwtkFO.__Go.vl3&=X8VjctN[Aoq3t6܌:~u_ I59{{bN9Osx?1i5Ϝ+$ycxjK^`׷d,s: ۙC.Xo/|m F!Tg'ga.\Jʿtg~uɪhç[=&uDU?<Ж=OSQGҼ[mnqMX'|u{D乎K35]@+9uO-šTC1*8ֿ= s2=>_~ 5/U`ԄӪ\[,, I'ga'<RuHfyK\:񪷾XgZp}C[1(7w={RoVkdtz嶵yE|"^MdEk%#Mcu|IԵi3vmE훉#pp ~xkxxZz>SpK8O銹?ךnڅw2JppYF͚;v[j踵o!NC[,R=+mg_WżF3{Ә-\Q2-Ɔ2x nAӥy'&s&5/$obn8Cev7rjL*_R@tVI#@8xʜ+Z":Ѯv@qZ'B9yf5YUE[;Msھkh׫xS\ԬbyJDT_nӱ;_&^'׋lA5uuY#UuqA~0 xFwtGIJw#vbǸ+M[ݩ. uK9epHj.[>75H>gڧV*gT{WZL;PQ sp+s>z?URRoCU 1<anQxG$-MFcz7H"%ubn%y3s^X/{yA ~s^ yo8M?V|Lh{G|Ge-}OP@p-#EI$ 򫏌ök86yX>5i\ZEݽ<Bt k<%xo@֭;Gft˜q5'2!M24ʻh <'yk Քh¨)uI?T7+u u?Y7Ć)?iU~G$R5\氂&03In*Hh kqXo4P5F+Ðz2WɳN~]MUGYjtvΪ\.BYH$L.33Ɗ1 W|t7:J@׎K[_"*3޻<ڻ#Yq;T^#r\1`*ApnTw99ѵ.QYp<5Mp|m< վpLVab5m[#=;m'8yҺ#A }%Yz( 5i^ ԯmi[~vߗSK0qjt,VdrJ*q|%2l>Yک*O3Otwt40$'犱 YXcj@Z׏H YWr؂$=!V|֊rʆ\JX'Z"dVIM g_4- XFrrXUuQ=8<꧇lNQ[:fqupÿ8^NAõuo`}̾lMiEP@U0*G^,2%Qnj}zRk"kBmW GcZm}*@8Ԏd]ћHFl׎|O0jֺ`=z9lg>$hwL\sv4eqdG4 $|+_^Gځ%򗍻*I_¼~#kh.IxuUmq"m>V^$WͨmjPӤZ[xz\Z`I<DwvUZŶ^Xl" Lᯋ.8H-Z*O]9!i?FjLRiܣvtg C.}^iׄWhE=yOg5vZt E-Qleϩ_`~˧>xDIun?3c+IMm&a68</δ|w3E=p1 3VorZ:$H+c$ImR_J䓝B\M-ON+{%c&GHlY#5:އi6Iyh0e|:ْZҡxч.p=AͨiR<0G<֩Ř*mnE Ԅu HYaMQ^ۨK(e&+Oxrpo[rcmO^ LqiJ<gdE/fj Ypiڍ Y;?Ezgb2sxQi|o$DQEC PU7$W5 a%6d;dnwgZ(rSijt.…i89cTto ZƉ%+y.I8ip#-MIc.I &J;4fBb ׸ɭYn0,@ڋ3ҩ\;[,'= *w"!*ڬ~bNsL _l;YsXu5x]7H~Vلgb00p;cҟ-b{R,1oyF=ʳDO_:K#f>c$~4˓!P[kIk{#uݕs۽;V>TsZuv=۵wa~lq|^RpMyfi[5&de(#=r]a^mQZ3)^mm/O<ds(ʼGFdSUZW>崚6q1 G+b[c{IQf,(n c[t"L5+I``esʴtúy@@6ѕ?Jx]^'ԭneIn")}cJ}VgjZ]\R-,y]F;3!A)^coϦ>#k4 U=Oζ-%iz֓4p{RǗjzA2;\Q&9jx׉fmuۉte%3׏lnvz$ /(;A^|5&%ͅR:Ϩ2sNZ8g[=E_Q.l|@TQr8;?ʟyIc+li_#O3 !q\]̲̱7GXO_))$[H|O;IW_sEnL2Da`g]RGճvP] 5WSRt~a%.g[ݤz0&+ŕrs_W-3G Xe0=ic=mm۩Ϸ4 8w,Qe]ܞ+υ>#~Ywr(%{[BuΏ($\s1^㯉w>"h:'PZ{o1L|.܃GZ]O]?\z}ܾҼ{ 8¼#kg69l)C nFGҼt%\f ;(oⵐxR+Mfu;XOojEx F]e8.*&CJIԙ6TTojV⹟HWW͈~3#K]`дkBA6AV漫Nbu*qg?Ҽ({{#OoxQy,}a\L.Mt}s5ֱH`J>2+Fǚ۔S֊'w4ošxl/ )@yEnt2CklbYKc'r_hTMc-1Fn1;Ǭ2jbeʕSǭǕ=;v< e%Ђ!hV0=Jǖ+?xB^Ny <8sMmY]ZΗVD9VOC]j4hj:7{G 7µ>%ӴUfS|w7GtPYpA[j.m #lR{Jm3g_+`Zw=8UV?=!Lu܀Iv<>quʻE_O%q@քɅ±϶W󵷼ՕŜr$@ŧQ领1Go^za|hp?ºㄊUG¯gi7vȱ'=V\ L/R0д9:}<¿J. alXTJ%Ҝf)Mn<< qmw>WSzV<8;-7r} GJ&l-}ԯV50.sEWYi!OFg;A$N6q_q .(QiQF?˜6&7X۶:f%?Ғ4N>;V6$ccگhIu!>g5,WDNʹXخxxSשJGY @m?(δXخȵ&|⧞iX'Z:gf|%V ڎ6ä<6*9$Nex|OA<"?TYleOH5@V2t٘svV+in!%~ʊsGbng'U! T` Pgeck \6Xĸ$Я?_OЦZ/k N*ss*˰Lk_7Oy"vK"1W].>ZcȶPR<15vv<y5M4T^U[SYr 6_jWg W9nC UF&#D[DuR]'?%g7y%H % R] 2N0 R!Pdbi xUt)T1deGs,,_NkNk')9ܪs횲fZlg"1B$[{ձT9#ZRDs]+u˭B Ú[+'úwuko|-`[g-~`A 3ɗ$DT;OJX-LY/me$Vv'c; v,2K rd ? hym3|*[2l1!idw{* "EG2)~IZG4`n~aǾ+ndJ<^o{wc20JkFv ?5eemdE,W**+| uJik=wvR@X%XK?;L;sǚշUA(Sˡi] qOb½iu~#ඖ6prP?:O}5oSu82ǹOҵ>rX_Uka;$'B9Pi>3E$rƂ>X+>"[:x<ֈXDD08#8+&ӱv/vZΛ}@?qV22m:gһ x KRg_3ilO? #%a?S+ dP/& *.OcV̮2GW➲E׉5兞r8 . iG/Wjͽ; 341D]1 ߃Hs]ZGWW4BϘnzi++s*j罵Bfww`Y+ OFT !A7X}4_@tnapH*M[5ʔ!`]&h%?${Nc^[-u.l^B)a{m^i( yb?҉M Z2It~iH߹'Ǘ5Dl-$;Si%n6zEhnDYQXp=3T-n5uh4E8O@}\=2d_mO|%H,GJI6dq_(x֡˽3Q] YOYdiFrݟ^/F6DSUPӿZºCA4C4MKrxw=.YԬXsr@'vxN&;'dd8 Zh>=i~%kwH܋+1> l}܏fZ5]C$6e!!\pNy .fok`κεK%fCb<9'y8O7lFYQ1|ץϾ?+'PxYX B?yf+|~sILᮭ,HǬ.Anx<W;PuvvgFAK栀*jO@Zo'x'o+^RҹgE=F} yW-d2}UŽv'z`\u%弐LXdR2#Wyc^kT =}OW.e, Ǔ9r^o_˪i4T'ɧ$.-v's̩_5 ±r˱Oe¿]"0!$fIO˩y|xT96gd9z)iM yoJKy|?lQEܥXpAx9ƢV=4bR^Z|xYl&6sC8r~eͯxkqC,q0;xIq\_%H Atcӥz6YS/u^_]1jw[S*>f|:uS΢{&C$/^)桒BI Ze48AQP8 <*IP8 <*G@|(󏠪e.ڀ,yQAU<AG}V(=Ϝ}q[.@<(󏠨ڗoJ>8 oJB?}q7qyT|۴F0 TO7dEi%~q}B>!_cQ`~MyM4R&:Xg+r *ʢmB>NߖC-*?Sh9dS~+ ;vּ֓wcZT1$y瓀>,7̯Me]Cy>ZݑGE\Ru9ϹO(_g1h"o p^U!sLɐ*UA?:RWAr@ѱa*7d\]vUqe'~Nc1S,q6M$/O(fMuK̜$S*Q3!Hث*8 M,&559*{+2JyQ ~4셻taqelj˸5ʗrI kȤFTMpW~]n njIfC9^5żkXm699V"|< Ō\H$Fx8n;t%RЪ<M`RW> ]Xc0>b+RiV?.a.\rzuw,[[_ 5@kv6л:`2A@[Vʵ9kkk5In-lygҰE#"Vߌqi$ۼ`1ιohvOϒ?K o`Z8,Iq,M }fb̢!0=p~^L_.=k28h 1\9+b\#ABt<՜hIVll\t`?TT"l~xjLWKl8%nZZc.B~hl֘X>+;? *xr$MΉ }].,bgb3 xsQ=I$YFx'W/iq}e<[YȒx.hfjaok~Bju}*侣w-)%r y5ޭ-w:i,h+3s÷jimWNY lLrRCVѨ(ߌ-: ʐFW|%7BV|w8k [P$),Ky*x "Xs"x5LO/YƫZJ'FT3Fz~uO/~ڄ:iӴ*Q_N'SD91?_} V:},N;ǀOs]TeR6ZU_,4ۑ^C [}'Pү(l(3ʎzVt 2,fr#,OEJ\G,r^ۤE ۅo**iI5+`àyk gU- )9V`&r0H_MA'e<, s+'gY~xkil"_s\M+ r>Ӭ46k 6Ĝsb~cOW϶ ,5"6]Ҷ\^k윯 r=dt-U)zp+ϼtncԴX=rWlnhc+u-#P+=G*d;Y[0FGR*V淅"[ s(Wt;1OWOҹgEaC,ȧ.GDޮWtQGs X1_xQ>˥2c`5"_:q%Υh#zPKna=?"h9"i$~g'W 5>%@ .l*Zi涵∙ XW|JOP-zj>+2;B߲ءv[Guoula$*FUG+n-A{kZUYvɃW+LֹERmB\lyu}M: o75b5I#ju9 ̼֩j3qZ $(V dZ?)Eð|Kfm>ӗෂThV?:>=hPC|Kvn@?Lo$?m uk}zP(<h0dqԟN)`8Рƺ{OðrSNoH >FnO?]Wڇj;`|X#~Zq7V߯KGڇ/g»9q_JU+;ς-''kWj{8v ;bB r"bǩ'GGڇS?O:%>Q A49u}zz:kkOh$~Yw_۰| Z]j_QTùM6|5c+x^9Ug✟Èdž-N}z{}z/q olFEښެ:Ǒ$;Cs1`rpϥ7KԥGFOB?5?b/ ,eNNi^\h-mCF2!2r lc񥻞4ܣ;A29+b_ ,e3& ŸۺC\iI:k9̌_cLQo!th}' d<ӿ6ē^vzc@š"LrGo@׵]WNK{R?/n;?>vWoklFy-sS=ZzRrL_E5x>qEW\4>/[xpM # }-<5R-:G+m' [*umI1sTMX]Gqo}~)c9 _ሜw9IKcgxM[ ?(ns1Pp5ct"B3gN+?{* G\6iZ<~w oVTcf*WDF[!SKcQc^C!9"*l7.Hx_vؿB֬ɌLxj;؇vBL³ OsZcc.Rt.'?իۻ뢫'=>Z>BlA#Y9-Vwriw0LI xܮZ+g-ɋF{?4žpC9Ez4טK'?Joi*OQP>u=LjJEWJgp!MA_ͻi] ;+Ҧ9NlE9VN0J?*+S?С z2?Mk$I,QFPiH~ufe5l;j:_m?{׉C8ĭ/bNO^[oc2kLc_,*&uz5OG\$)Y^jVڌ&e 2H!M|h7X +os$$d\tঁhmbO(]$V8S}_;,]Z% .XlD7 "왹uI`Է7`=Pn5qv˰yua[ ) }"2إ{fM@~zSOUY<'2ٿ‹4?k,KŇeO)?EO([I?ɣI?ɬD]B'5Oo $&K9G˺7Q]‹6?ԇS߬_?oSſ. Eڋ?Ս]ԿWſ._ ]ޥ?]g 7QtIXO(Sſ. Ec߭/c'”xWŃeO(_HzެoE|YB+?o 57Hz?g ڟ7Q?^‹5FV?",{Sf ?g ڟ7Qtic+?o_н3@lhGxV8?]E|WB]iZS_/j G"+wSV .SՋ]œ0pl"is~t0"v6)?@ $S%pzm:Zy"5=n{|} +) y>IS&yŮ7myzEy=Vc)x63+l[9lNӵ+=>(ooK-i!jĀ_FRZ=dS O^cijh=7W.~0"d&#vi)]خK+|jcݳIlko?Լmo&Ⱦw݃9+')CLjXeL76!BuQO j ?Zϳ񎙨jwuS_7xtkq׊\vrE[0u!n$0-ճi> VYKh" UH%GG0ƦH?7O~i9n>k [ǰ'4lE|5mN0>@K0Hcn TMXYGWO%e.g|xRSr'ZuiyWj 4zT:7_jVSy TzŚGuI_Zt[EE6 A߼.{< ?hx5ΏSIhx5΃Ihx5h?}tos_>sG0r?/\o`:_hkCtohk4h9~=MosK}OG0r'ǩ@|uΏ:9?Kֹ^֎dG@ֹϷGG0r'Z>85 _z9_֗p_upSyϷ:pCq4Pι4s!?(8߭so>_f`:/ߟZcg5fgU`O} w?hK _UE*U$Ck6kn *]m5nmqҳa< l-0j}Qc_&@E::S))ih-"Ө@Q@|X'⺉w-S?OА 'Gg}Ԏזp$ '׻|/[JgmPO.PIi,ev#G93鯂V<1uIoBXY+ $>=H'ҢI=ƊƅİIje2E<@431v#ԕ~'cLS^$:^=Z;'msNMa,_y-w? {, 9m9?Z_xelgHZ7`W+Cu5>G)t%#KCǩHo+8'8g1}iQ!kNm.9_bFsҽ=> 5tӧZ imRbiH/}꼜?/|FѯfXK\13#;;A:>H!Tag#VFFԧ,4*|0DG\\(T\<ƨF<|zn8T\?^KGA#mW9 }[{G+Fi4<?彿•~ʸW^uya V[?G+}XL> Si<1mQ U QK٠ᾫ x< 4kn&$7;P]'W|Dό<\8SʖWUt?v7wzolgowHآ2&Z]e/1:H"dyi|qm|OO[@e9#aw'" `vHq!F>O=|V!G7Kz nu1H#]+m[{[G+֋˥k}ps?$lALcs^<)ٴgS])(LcG9 #ƶ-wIe`ыcz?YdJ|F,XF:oی;oM* E*d9٫G.ӵ;56M6[]!ǿyP9?]M&lokzdq\.p 8R*|&Eͪxirf2_p9<MEGhL1/ٳc5q9 Tu_½2^GZWZ?^E?fy{ɚ 1 .~%׵m;R<.H;~RE*H*p}E{V3XGpqnˎyWNt6څ߄^+uu341J4(ih(/JZnqN(}NiQEk [|py5_&'1y:ZCrp}v FG&infM2By>9# Z8\9{A<23! uAھ|s@Sơ|^46${'Y5 M1,fH$~?Z6b -D V \Ĺ5m˻K[Ӯwd_pKT_ÚZEzBK}Jޘ\]u7M+0PŜUʋy^x3 {? r&Օ}|lF~;Meok77j:,H.̭%Ԙcќ?4I&{KHo(#)4GQ5aW?,x-tKr$$i{X jgZ[]oLNl5 QYܰU_fppsqVc8]q;%ݣ)w1?)n沼]ND@LX '1% GW&ٗ[-"B,ߌSZ?)~|EiM|ok'Wm]L^1iHh[1Zo [chGB.Pa4 @. څӮ5q}-nNq<׮=YSKy75 WjC-0\YFv j-\xmG'%!/w>*ExV&E;0PAU\ EW:Yu3h0:Z`hg\3F23^w~_25մdIp|gm>k&i[\]%c3咤 y^ė^-5xhߎz[QEQEچTkHi|O&fnĺ뵴K[k`T)/OcGz(E1Q@|dz6|d6{%vZ|=P⻽4@tQ~zf3V:PZU8PF @:RA4KM=9/AҀ \F:uR1ץ.(J1@=E1si *]!Yg8d#V^s۟.XI!{ {Tm@Y[$U31,d^WOYKSIkwl12`R+~'hnhQ:Y n3F= 7i:rxNܒ(K6XGROUQ_F{⫋ =%--KM/X#ھo}Vɦr7"*vU1'*p\/|',VcTwYUf_2 &ߔǾs_Vm?~(^ꭨn2G$;pĖ89)_ ][OӬmvqn`>[GL}y^iˢ{Wo2d ɓsot[MRTͬwio2$JD@+ }ϿJՏ}^7^XɻN)2.T;x~xFymLvha)udWl 'ҵσ<-aEzRbH``d^i㏅׊<b п$i4c@O>O mOdi'-ry"}MK;xm4Ktqј9SSj c[k:cJ|p1ǵqzÏ'/f$,áxFGPzu0z2 I?OMlITy~m]2T\Gv>QyXEu[(.OX`m܍# ]?^cMcm6L~cxv,;J }MnA{{S_\wP4QE ( ( ( (  ְx$:K[Ul^k.p<ɯ^cI _=|f)oSR/Oik5H2nJ9|-o5H-mb֥V.LUg \Hmˉy&\/P퐽q_p  /ߏj!0N^9/ "Fr/zgxwKtb"eCճIZ֮.21XjAnH|\c<ZQE ( ( ( ( ( ( ( o ].ƑG˘THŠbcf3^^gBO3DFFmV&4s*@Ͻ~E)N$E+>Pi@(v;)r|1~,{[FMmIfuVr{b|561Zis^XHs; >iv:6mf0 m'ןƛ,KODK RLqfUq1=*ŋp9|[Ua;lyP 3 -?N..u[ky >3|k\O_Km2yݓ. ?(5lJ$3m4q.A>x;~k&b?ji7'pUA'xDu{)K9omԅӬ&1t`JֽWUѼIF\)k kCnpU6> !] u+e v_(/nr1=<6?lHь@g<ǚkiCk>|Uķn:uOݝ>\bO|=#G^'$^uN(0Z/5}&-fkq1"90:3T|T-^K6-ȑT#8`>j=_Ai֚^6 ɹ ZOxw-I~VTT m5HfTaO^sڒJc (~p~8O |z崻tFǂejZ>&vY︉N>gr9By)hڟ$O7ѬcEw~UOj\bKd&i#x 3Z)c#HYֆAEpLi8t\  nmSԤ]rcV<+zכAik&u[+8V3}9O$gcēKy7.gAp'B~)ZwTvᛝ)ZRS$71C:i*ZGAUo|m]Y귆eŕxj%woÍ3X/u[)k+ea&?|e[jmjSEU\ xs֓a-N፹F9⥶<1-V[V6Y30zU+G +Om edV 8¨nK?i!G97<496hd5MSJm-,4-dr}9V>srφՖQcyf[x'#5FञXҥNT+E xCSti58iٷ,hG G8qDtWcbAxcIkp n #+.ݮwAip5D-u͉ PM7Am/R%X*'pGG$MhԶnbIb_-B2 zf->k~(k׮ 4Qx'G_!&KP63s95ѕ I{$fIcK+CXo3+$ێ;&]ûm P!h̙#J?%n{Ԯ/ ˬ ZiIq?ac#Wmi/n4WQkJKLYpʣ#1&6 ڨ}sexxᮄpyea0l-Ѵb][[@m{;@Tr8=\ΦVrQځ\7Retz|B  B1_c[kw> @\6ۿLY2IrgcFi "k0kM\xEѕʉܧc>$5uio7 *}zu|:[EX`Hc )V2N~E|:~\xG4] Z,B3c\QK`"__N6v.ꒈH "rN?{kWn|9gwag}n܌чizm ]K$cVQ;Qİ< ]>.B>(Xe=?hdUxo kW[iS-6]Ҙبbˍt3%KikWAy%{P[ Ӛ__捦jpcO&I$9bP 9'8z#ᦷSPqvj6Gn`Fzprz:Ŧgũ߬ʡS 5-={7gl!m>Xo>fHwe 9>M/ݔsh-D"!˻v7CzWqC ]',}F3WgG=iزc\NPQEQEQEQEQE|g)=/QtK+8{tWd..~^Hk~"k tۯ4;jqOV}+`b_}w%։i6oy%[tem{w楇7Oe&"Y'C^+У ]A+! njZؖI77Sezu[9xHֻjzXwdOٲi9)1l֌)QEQEQEQEQEQEQEWw5=*LM^uhll-mɀ~}HE#-i~4B$06.ʼbH;1RɼOcOmҭͧƑsy|  s^Msė|sjzB-09<{SnQ<iski~4aoF&KCViw:ŴF=Z[}1Qh2g4Ŝ^1Mα>zL)4S)U\s~+Cխ÷riU^bT#q\6HKN CL:\:4;8lI0Y@)ZV k\u!$g+$|9e= jzCݍ>8epgy'ƯP6hZf"][30w sR}E)iڗ5ji,)m&F1v^+}>@R7vB"&AyvcLbQ/xqͩ&s[+L@mV d_B|ߋ>ڭ]ۆEMH<38 Iknm'Eю7-ʩfx`qҽWN8Ihb4UQ-tERQE-UӮ",l|GӭyMW\n/[kD3/miW;a }ӌֵo :ᝦ֬c$aU?W.Vk2 fB2T {ERQE+ W8H$,̤38 '? -g +ꖚ<jG)-}ݜ~;b/FiWpk*ْ칖6Bpt>T+;1]<5jV>YŲ1!q 8ShԴ|3uiEu RAt'kK|jϿ9i_ tCFuV-[sd܎ZGWn'4 <`ӱ,u(8R.+=XCMwO.$˒6BY $ ׹qJ 㟧Zޥ zMig]CnO{c6E {Qֹ߇ IxZŭtk=Ie>.ufn{WI'$qNdc$G ;,4ycξZXh7Gȏ 9sr?hk2zufV7)!?{_Ql ۨ(BGB~LIXo?q >QIR]v?$^Zjp=Nĺ7<9xj7: .V.ޕp l3@y[b$ᙴSK&mF;cg(ƪ6v2{י\6Efwa(-u6p>HW͜=1_\G.j`~zO<hVޡ}ek&[ x-b;N?)Р]MV񔨾.ઇXg}z#dciTUxt9ɷKZ/bWm񢾠Lއ?EIozٷfQyߨ# ?LGR<䜯YQ"M-F<^tgDw/cJ{Lqum7 ӭnxc^$xK^NG\mia \ yi>xnVMbm5弗,nOj>71bɼ%syͨãa ;2\sڷ+-iΓqêϩE#nLZLVQ0jD2Fn§x'aaw6&2kd۰/nF3M;y#өQEQEQEQEQEZ:Wd9mmkPI%Tn3_@?<㎵ǟ%ˈBH^\# 3ҥ?ZKzu  ɎDwMYFw(Up 8ji[,n| ۖ;x犣{BD`:,YJ⥁֧|@VT-,e_:ótQYNֺ~, x[m5e1v勠;|'OZ=DgHE%U7ϝہ|z xS?mWKymooBB-\gIUk}[@AÃYY%.YZDXk =cZōխtӦO.+ `7^|"ޫam}v\ɽEZHu8BȨC9\>&u?K]!%[g@m`KMӿg[Ki=Yq{;t)$z{NмB.=xI2zڀxߎ[yK0 ~yk گhZG[IǓq܊OqQHaJ{R:=gx_|EejW+5ū#e?Ҧ-ھjcxpE)O}*C_]CO\w6ȩ%u$5bž3uMQHgkGb'SSɡj#hHaESl||&Cl#6·qkֻ{ sןz|7 1%wh .6ץoi쌤1 m¯J;q-€6jʝun8:fv-zS̟ZXz| @)v~fEfz wq+{&nؤh"6M&Ӡ_$NkfXaz-A ]]p|+ŪxSw:$m2m嚊я-*5 >??Ɗ࿶'𢯕7??^?ME8?h]υ;3 ˻o߈~NiVW11F?ޯi:'z$R͙a4`g\4gZGNjwwY|"{ZˢA3m$ }|fI#etae9v5 //u8}YJ.Yv}hGlfg.$O69MWe+*v8b&j׭_nKF\#=+l{\~4+cWxOzuthV\˻d|^{-~kMQeQ%/\GtL\ῒWčKѮ@O*\'$i}<8#}n8u;?|Y_ Eptku?cg7q9O=K3:<a@@8+hm h=xVMIAZ>+MѴK.Z;9@黃Eix"> n'{k)"F,kSiX `.k ʟ.|Ծ!x]jc/jJ_ERǤEu$R%WzUtoomTKӘ5‰; vS<g7U޿5{o&WE+Ǵ#%2zZ #Kn<sG]A|'}j|w62{&VI$ĕ'ڶa)|yCVR$!OZOZZ((0(((((@y( *&\ ew;νGqiW6K+K]șA/v2/%xUY5A7f p4QAc/ j?qgo/~)72qjnVA#Da1k/ k/FܝVϩJeq}mfq ͯb+R5']#0yiD.߻>T״Vz.#  ^Hk] ML\#Lw|Ò: |ogeiⲄGW<:|(jLViJS{S,5~86Vۺ}Q{MFr2w8 u kz}ԾӾ`% fKaNqGN+]5 t4mcI:R獹/ u 尌\oldl }r}w[([ (`jSgZlw@OAۏ~ZVNCoHPmLk̭4 iQE,#f ;桏6揦{YW^8GONojH1_ǏpO\fp |uc謶~4X܀i\}i ~.-mHɴ'xנ˭:u^F2]d >odid, `VҲ9WO7d.F7{X5HRk9)d9ThBɕNHNsU'%Lr5z-H(|Q~}Pud]2P$~GD|k ,(;sj<>yQNYRW|N]ڶ#]}SźJu[ !ȫ H~]}$u.b*AfJtޖUwq?UZtE7]9@&+{rj)_V;^g=EB+oX3UgX=]a,6\\r1͏'9+:픐[mWa/y5RO\TVt]V C 78*K_k~K[x8N#ʓ{Wjڊa`cW]X[kRVT:m]v>kO^S/L{$Y0p+n2&H%͜}~\">J571a*ƥ+X_Fд\u<.r㨯/L82dRuE}uxzodI^k\UU|SKi>RV3 cܕrZ{r@<wzmI{[GiꬰJJt>wuCD Vi+h"Lcmpɨ/Η:68D&i#9R1$y^-𥆟 Նۈ "379\ ?J-#&kgeLƓyW Ta&kX0|a#[ѴYi>'/uVO-$$O&(Pq95W/>.x~J;&<Dc g]#'AySO[MIԎo؞B +v%'Ŷ]iG;Wɍ| pqJ9/ jO[m.aso F)0xl=:-aY<)7YmqLfU.[PRC$?CV&o'7m}z~/lAizv_K{Hr9෾*^5y3о,m jWEZeR㑌ՋZϊ6xf]B[ɈpU`ȑIa)>3^t%aŒɉ#<~8}m@ <  ݞ65A~/io/Z}U W{ƿ{$])/-Z+x_jo3)sm[sHZ,ws @@#sI_kgw--,^m>dBUv(I[h|}WmkpE[`,~x+'n};\Zj^kUdQ%>xv`JA]A/Eݢ[ 1\H&&֎E ==jzXgzOg..[{UD\"JٖA۞~z~h4J`ovţGdKh6aЕj[880bF:JVIExVj^O51.g:z95kNMz׭mf{KHO˿#Ezm׆kԗC{I ;q'߭`|2vMf7t\Ml]rGBR}IEQE ol Q+wҿ/?+Q}UR mq~nֶ3*aVdžvo3S.2kWZKTq1Qeq=J6;ſE΢f[(zG!muP,oh*d=wsӚKENHNwt $?z\J4qJXГ)?"evsu;2!}OZ7>,]MCC[HՆIʷp} |"<>SO~BׯIl5+N:dzW?~s%Nc/47F[)2 ~WC/"$ QX݂3^|%yմ;[}HNgBU`0+ >zOQFǏ5jn_R}tt:ؾ獟fv~\sW3txIlm"G_q\bFAѹ?|:mmEU 3F#Tm<;gqGbyHQz>X۪>+Z\ktl`wASVlҵ%hgCSٸCć/R]B*^pAnqd&U㓚|fLJA4VmEuoz7E]:O£r.c~uܚQy-xZՖ)-Z#4%~), J߃NU,σeu)IҼ-.a/?oߺ#LԴ֊y*zx>!j6iA]]:nOXL冬tg?c۶Bl+4FChtŘ` 0xTOz3ߕ?/QG< ' f}6[c vZ=iӧBJ;rO~7xU-Mvw+' j|W?7.|6Lvk2ԣ/hq~gٷfy?n=>8#RWCAp+~ºd[Fwf>|o~v#IopKL^F ់?E5=[IEi$Ck&sgʥJv ar+ k4{.gЈx֭F+M-4(jQf_-rgOW-HSm+ 5r^Ij%S/"u8ԯutk߂ z.T"GPڳ a _Q/E&_=1{VKimkmԉv;DU熏4Uejx8Mc/wl?-TlA#AXkf_6ƺ;[ XO}< qy> 3jK0_6",*럔ӆnF3²`~UjWU`a~+oxræQĠ33A_,>5' 7 \,&8yn{5+ڌ-r-%GyZH@6<k5WPy/Y˧D}+^џzty.$3 ۑ#85^o'ý;1F sk, |#Ї^NNӃWjg60G%F9L~b@$czdo WZfbYԐ7.Y,ڴ==eQw6 ^:CLi!Ӓb.< >ými>k/-u9Q ̒=2_^\nET?_} 1G(&O!'֗__?s3—W?cWE8nD~G v ?ux#;?~~_֏ڿȃT?_܏/]^?G.g}}ZvM۵C~^ Hhy/ޡxwn.xgW$(ba_-=]b];Gs,Ьc#)5j6nҏ7q!Z}̚ hSI9?qC@{y[N|V2vt#+UVU\;_n]^ݏH<=ᦸq%څqц3ިEwRO [Prvȋ9$8#<xgK:]Ci5”;!1,s{wx[F5 MY98iZ\XX寑:u{O" hYLr8>xst=n B-c[!Eʁ !b>Sڸ&}oF].m| RaI8#Ү =>OIVIdlFQ9Tjb40{#KNmwjk#X{h-%_VUƈxD Mט(h湤X4{8f9vې?A,5}rmd[ n!`@W>kYП 5kG_H> _]~?_Чkv#V# W?cQ 1G+p&?j"+T?_܏]^?G.g}7hݫ?JG v 'G>۵C~_C]^?_iņ?nD~G v ?ux#;?~~bOG_APG.g}8x/N ouǗkN}k%=+;u :r( ~u?OY̭-L0@񏇼=jxIH6%+ 6:O e+;3F7G(owrj֑7Tkelej:7z]_U#áw{¯{:548%d]\#;6#vxf~¾4ho6$n "8{YGzMϗ$%b`w +5֔9Q/i/_? Yf+?|kgoKUGD?P1obH ii57K$;Np^4cʵ4\;唜ޞ^W>GV ?ux#8?_&I|IjZ}urZ#``15jNuۛHöJLvVQTxsZV|.g}O?cWxoj0kڿȎNr>O V 4n sQxlj[,88 >AZϊ[뻇O{ldn%9Xt8KmGJIIJG2mќ oBM30im I Xg ֺ^mZ)ϕhpChT>|bQ ?1YG+@1:yo_t/G#] ?G.g}/Δ~/ȿt?3¿<n~[56TCKK:b+_ דB }2dOJrxZ;d &UUA[8]&^uk Oiom+6rB߹W#?ZE$3 a)=4#H.Xى+@<$tRnN0YMU1x}[eQǿH?`Mywda`|{V?BgvoUh{94< 5/>Kؕ# qI)]]Mqq{VSt8'|:MF_*Cյ&,HpTׯ5-T/Ui +O\ӮͶ9Ex7dxK JjUBU׉.^{ vlysd.sejVŠz+ssp3"nS0PaZQwfoh#?c:{넶w<AZss"J]*_zL/Z)ɸqM+agdy؊f}/!Դ1Jp7~1_|}ZnFL\[_ZFcJ4OR&MQXmke2?XKq]#93oj<D,u[BPǕ40ۃǯ8S|.ELE {#u E O?e= O{MG2?WGpȇ?SR)x})ب8oARSQ ?bῑ7~Dژ/*xA"zjED^o,/m ׎O^+8SߊiZ'.5 ^Hx,22yzQ<:O2=8|$^Do@n$cH߇ xSizeܖE^EPhN~23^-~5Ib-PT 9\MuxOUMR10ZA*epqZr!ic֫3XEM ny y$rc c߃ MmȲ7vytd'CqKZ^E,pbP<Ҫ)[k:]-n5}ٗ<;5_Sæ҂'G?_s?;\Ӧ-)x})ب8oARSQ ?bῑ7~E7~Dژ/e/Ͻ5^{ j*(ژ/e?Ͻ5rx'ᖜR^*t}̹'+_ S^llei,o细n -{۝'IiP[q˹Q5?Rl'uU*})USWQGpȾ⿵1_yʾ{ j_ew_Loy߳7#[qW?mIX$:s6[xFnZQowE!68a?J|]VZg:ƛcS'KW{ jQ.TG >W7K<kyMG2ׁvQϵ7K<>?k? ˧tl4"?174rO^X7j=-vaph8XF&~zo]Wĺxf&fV7gcƨyID9*o6e!FP?f47 .h漼V/P龧AwM#rkμM LdLu6R4pwnEp9M[IhB=WμI6.d0I; Z g\W Wk6K=k՟vja -U[#Z0s^x},K7ZKc?Vg~j6ڕא*Њohl2% |}xA?-KtoeJ#KQ+>QJ[#|MoMn|Q(h?~(gYChr:Pwp~>EiPAX^/ߛIC,Ce bXv .Чg%,ui^vب0sGR𦌨iV`tǐ]έooT^g*n;O+'pm!_xMwkʗQ:D2 [8^3O*ygqy-DUIcc44V{s_IHn8̥TpNW=;V:x],"]X]Eh cG cI|KᾛbkhaW+9ʷ=$fqz7E|'7ۯnAo/o7mved$VhSg`ZuAEP0(((('_/|xռam/1!^|;d9";- [Nn4&Ǩ2ŀD4yՏ-jN]w6wwD1*O2 2Kk/M0IT#1_Lz-罾uXlXm< B>h_54ݡ A+7x8Ix%-*Tq~+|EX*Nj- x쉛ICpCLz׮_K#ójWmb #h8''2|@i> _@ڟdh&bGJ QӖDgx"}7|qIOgqoMyF^0Nzײ\7i4FtE5cG ,79t hblcQs1KE+QE(((((+>:xŚ/jGMWms+SB|R,jhRj0=ԩu"'QS|t2Vtkw^iFJ+WYt/12E g Knej_FOy-EhdAEܠ–_ڃzui6v1޼eD_>٫]h?٫T᭮ep05#p >'XMe, #Gy쓑X>>i uKKPH ̷)BnvN T޶`cBKּ~/'Om$݉yy"Hg5ް>w| X٧+㲨IzEBm<DڵEiuu>Sr|ʼ!p;ףQx6yb.{gSJCv((/ mapSk7Ķqskr[We/q{}uoh/ԭҭMIj_ajiWZ;YKe%Mɏ9yn'RRR[ (`-~w~ܭ!__Պ\'A?ߖ%]5uc<:rq]͓ҹ pC:<;AףL/c"HnP0ﶀ`շKIz)hZOΫ0c#/1߭2Qxi!Wm>EtFs5~px HP3ǭEn|čp78?XKԋrP~,T ;KU)P\M< d+%)M{Ý2 J{T-hց)8=TOc=i2yҺB31Uz>{NK+rmy$X+V#u_cӚÓJ˶v*{sI"r<]dngq6skVѦ64NR1޼?;{d6ʈ#9?ʾyX]=xcA ڬ Ig+%ƥ]ӱA2 pX^GmY rR{rki][EI =+gt ꒅ=3Vײz1\Gm r+(=s d95W#>vmj`:>ƾ֭n4,M#U Wk.Mޥi #ǜ\uWэZ~:Iُlj|m{_L3@+WW?u\Nb{zۣa9l~GO4 ZïZ8 ,{:bhF[O#?_O"]'|>n5"PElv/ {տ*_a4X Bחihfc\y8ќ>ב_fv +1Z:oΡs#y\9{)4Ͳ dk>chdP&*7|ߕz/J=+^Wxo qhҲ4w窌5]?g:?7>'Z.nkYO LbtXI|#zzV|k(+T *Uv<Ԝ{o74{MB EZۘͫ|)?u0̶}u-7]V7v=s]/kN$xlQQKn=$c ߉WPѭ; RsrMܧ8\֯>㯇WTwiM%6NHOc21NO:aKL(((((n>5={P^64y Gkm5g88'_A,LRߎIۨRn"gfcٰxQB\JxbIrI6%aqor>(Q}Z~mE $V۟HrU0JԵKĶo`s,gROqC؝>-χcwS+ycc]Ÿ'nWŰ_d%xcyJ[yp[XPrS?Yx_-`Վ=o52ELF 9X=*㯈GȾb䳲`KK9=([m拣k`ԯgj00G.g}FY}}9fBAaӚv tQEQ@Q@Q@Q@Q@y__7[:u3\5CroRyyֽ 7MDn-nQHyV+^V1h=ORz׋YY< !%Tme֧-Ɵuiwgon"5\d;,FxJ?1iY\^Ηp0KP︣BI+] 6īkRNO"$ d -olMeu{uu)necPaf|B H#Ҡem dk5]kYMY-ɧ<#JʂL 1K=G_.Zl̫}4xћfOBt ^!M^E\tPʦ8V _#‚W&?Yin74349{q^P_7%Ο-֝G7˼}Dpp${WGrźOtin<'pT֮DwfwO.%RKfWR[ W|<^𖟡q%VqYz|4{:A*4??ËIo?vK)Qx |1I~0iдSRӵXmF2̌[h 3x楹{ǡIs2ޙ%'eޱ޲5ŗR]hZK(]'vF֢IVݮn7Oa  %a>pBf|Mk?[6Q˲ݣwC]ï>?LVe+sir@x[K{ۭ>{ie@F%x ɮo^(h2:MsS@c*Fȿ1 r*V{A2={w 4v2T9м/A%گd10&XW[jѢ[ia v#y$񎘫koHм}+c:D=rʱsMl&zpc:>> ( ( ( ( (=+CΌ:Lz˙nTA?z2 çJ \ljnrn*3Ԙt>,r-@Y˭%2&z?hjEޤ7P[smٕIll< U )ٵ]f.e:~1a^϶lχ5fNděp ߶1FK{]+}OÚ]ڐfCsZ,)gwڵ"[mm]cO2{3/־Kj$^kU B gz4~dzxO!l͡g=sM'{ZΏ +u{ȓ_o ִ37Zi3|@֠(Gc2J~id#NI7|=oN,uw60?*q<*R,✣د>WKq4\Φr>U -ahEo-͛ 37]׉[gW>LZ7צ'4dBYF9ա=x"F;S)%dx*Gj}ΟxpЗ^Ze:@ vOShccXc m0Qw𥟋Ò\LhKWsX z je=5Wz֣ꗗfaz!6Ǵc1C\7&+C;VQo\IjmUa^9$t^i? '[o[\MeNŚ3PnDJnk<*GDqpJ1 q?Z]:Cz~ou{mFFY8 +1hv+wp7^ rMCg>hߴGQwEueدOUtWg1?öZ֤I/4ϊnBBV 74ݾkRXiVhbHs'r1+Ѵ hƵciꉾ+yf<:Vt-ORҬ5 RMF;ܺ p@ѳl7V= - $r=*J?w:OH'3,2n;[',nAExV]R^=J`Z2)n1Q{~Q^kZ'Դf[f[sg#,ow @(5:Ķ%# T EPEPEPEPEPEP^MDfҚPy#ML&(?9 ~"Z~p\Ow ,̻"o@7~jX< /}q7tӧN֮wZL8H9]߄K)GISRKF{dwp:ס?Ư G1Op.mmCN /KŞ'/miKi7+ɹLb2::v8~vR^sꁴ`_P7Ih%5*f6:S%Vq5i|d^|NÑ.VoWꗚƋFkmSܓF1+1vx =P]ׅ5L6E|ė}f&6(k],m*$8+6w=+1 jͶ>jޭM4ivO_^"@H)hk:e9V c*~QrBmT+OVh%ڨz7wT ZKݻ'j  _A[FcSbE/EM7>vø*uZƅ-j}Nkŋ?}G]nSb_ _kɜjߣY؏pTkQ|twkʎz|/iH`s} afiZ^o-6~޼^:xJ湃EŘQN|=k_i_%wryVUbWpjIuN&/N}W 3Iy-K&fnȊH'J]P ';r/Hgi6F?=Ge믇wM. dOKp\fFMgx#7i1s/N5>5"lIٞ' 1`bl/?9 1:V:&fX[f&Rd~thxoE!<;^ѠF+r#oUC ^C^gbcշgjھ}o$x_-V;s=܊"LO;HZZ&=N}Ka{ /724b<۷p<؛G?xrF&X0`}^PoX.P:O^`w5_zŮ5O&m 3l?dwZLlhܵNsW]K֯Ya NI`zGÿ^|[uӥ$.;X|2f 7n]:WkRQ-7z  vu u=Wςlcw麽ޣc;Gm; ]Y`[ɮ[`Dq cּ7e;ϣ2 47ēEt|! W~=ݏ^^hZj0M(Ѭ.AF"5ov CIlmk4+pFD&AJyR&cE. J>$fb5?2]n/͓, |21⽿z巄O&ZIkM;MY^ONٮe5尷<5v}3UfYrASG|y~^P_ *Zi|lw_B{ŏxLִRK oJc-S֒Yz^9}:煼M3i>01y8_Ѝ+9EYiV^iſ3Xw@}ԭ&>\7sivd7wE7"k:N} Þ |f@4CXIrMv? ='ysu.O] ;,ŤZ%۽N/5A5j_|5.P%VJSWĭOL<a-GsyKI&VMrk'UO g]i]ZOERJWqIޛ~%%nu Z)_yД@Xˏ:iI_JUdcHFLT11 G/."Yeܸ#@/|:\XDpe~劍1{\?)MEu f maTm~ls\Uk9O rO0 ;a HZ=Y %K\B Vt%H P~E~>)7w+K-sGQ؜MtWUbbwc=]ѿjzN[{X-]<,pX88 xiP{8Qt.J{ O\} a5xF ^#ELǼ[\V6]׾j)rKݵC4{Cg % , xw_ 4_[Ue,Wj)\pqToZV4wzF+XL8‚rqJZ^$a7Hk;^O}EM*$`q/=|C,E/7bIbFr>#X?g;iUK--{ɋ5f,ᗁ5i_Zՠ !)'O.PE!㖑g)=h܈( < <3BƫjbY}2˸~ɑ~n(((((W3曩j7vƍC1s&~#xcšc}N-:sgsC]Dhu1^gIþ{Ƒgj"]g8zT^ 'hZ.V+ S8lzm[6G.n1q|'h25}bh6bn #SԐ)I+Q~ x?4[T1bЦb2/l5 Ú&K+ȋa$eG$ISIzi`ȖuoEUnJFAX/CdK鉪n 'Oݻm\]Gk4 Q0UP:OjmXw$=)qdxIRUNl!>8ZpȳFXd9z[ }Q@R ܏ GIXmq'p{xJ.uM.-"R&9-Оyu:5š"D=7?47K?k+IFBRJ(ئQE1| g~6|f~>C`Z<;R~՛qWW8Uӭ<h-7\+"JNAּ],?Zh=՚X)H[Ev$O^IqqTԼSkxH3mbodipY"XT g n&M3_J+(scqך ;/Jɛƙƣ)XuꚆ;HҼo KsjrO~/LSß-ėͶf+ZHĂ=ų*[89_̽5/gA/z Ϻ#W}ͬM_4Ъ>^ؙi0Tgn4}Z7 [os%Ѫ>lm 2E|o#b_ cA /ҶZoS|3a{qqH?E0:#p0d>Xx5[Gu[-vjη-vͧEh5HmVϽ| _ cOvr xlJ1cW7ƮἾ4GKlt Go??[!:Ho??[!:Hsy|iƟ-?R1F+O4G7Ɵi-?R1F+O4G7Ɵi-?Q8_8j6xNeFp% VFA / _ cĸ,K.kP;he{ FmiBjwd?z$:2L| :W cOtp=OUQ\:M,RK{V^G!>i8oYi,ҵzp{Y]ƒ]/僞KnLښhӐNƟ i-Jd{UC?vơkkW$8ٲU1ب|Ju7rMnѨ7E1RB1 OGG7Ɵ i,> xGKW:vt#ukwtd(~\xiA-/>(~\y|icA /F(~[yiA /F(~[yiἾ4GKlt1__#:Cy|i#UeiXWzqKe`!=3W_?%G7ƿ i,>/o\O@[qk|mT8w'z h5x/Co|Ms(4ue۩[Pww_':?~5GK? ?n&=bUQzQ bᯊW1XjO&YGHn,|ϸ+?o_ҿ[?%:-{|Lܺi9(1qz@CՊNO[׊ݖsM6a`J6 _y|j?5OKdtޢ? Qpju,f񬱰W1 0==_Nx.Ie Z " 7Ư ,o?_Y?RF+OpἾ4GJlt1__#:?Ἶ4GJlt _Ia۫E{2daFx0+Wsh^ԵI[&X Ji9c?ޟ#:?>4GJlt-~bW7Ɵ_-o??[$N(˴3GG#vz `~W4{;Do\mrfEOOkm u1ڀ5-'#W_;Vj@I#W8t曎jE(GJ\P:Ө(֟J( pB()@S%x.|Zi?֊($?icyP88EcycyP88EcycyP88EcycyP88Ecy4CkE}}Q@mae(,_}/ (pQX(Gbࢊ>e(~4_hE<֏E<֊(k~,_Q@b HqQE751E}/ rZşb(Qm*ݵY(kOp;w^_QEwb(QnZh5aVAPSPڤ^Q@ :Ө (zEQE9zREQE:((fotoxx-20.08/images/image-management.png000066400000000000000000001506001362435004500202250ustar00rootroot00000000000000PNG  IHDR؁ IDATx@GwG"EK41Qco-&j{b`C 4qEPQT&AvfͰvf%P"D"@h Z{R"@ D@BBD"@  : D"@ D"@@F!u"@ D2 "@ D0 D"@ D"@h D"@#@  D"@(>@ D""@X}r&Md0?I3L4嫦)E'x Y-S8ٚCY>%H-̌Gǐ1t{ PNF'c-DKkXÞEF6N'>D)jVX}A)> Gb"Pd;qaο! ~A~#xN`Θ/0c>D ?H8fEPLi!0v`W1$FR௬-F`'WO§GakX9SOw*٨"PwHai "^ۉoWQİCaƻIOqy\{<6ihhF% qf2:̂X4}п[ʎOAw0 ;H)ڶfGB/̙+xfкi }Iho2Yuh'NǪ}aC܃ۈOHV6.󂥖"rc羓P}<$#œd\:˷Ñ/Cۦhl"VG`{Č<(jɣ%Z@E2Hڎǹ]ŲO3<: -0g)iK7"!Su+neokW4 MmBu~8Kk14ח~?XV`fO*Gӿ~_9ј-SgU\7g=Z[>`vo*M9"Pb yNbF 1anP qxo7`TD"Y Ywjl8;'әsD<9 砃 "!1z u _/0'&6\dػزn v})VX0(e"/ckX; OePϬ(qߖ9a1|4R!q̀x<㷟a*0~7^D.ds;6L?7׋Pjxɰ[Y=W'$KTAs=PuH@e^>|[ q5D&T{mb4Q0.\EZNE _`T A_s%+Yz*2@'`ˢߑt>cql틇 ɤ'~a0Ԕ yغmqӗ%`qaw<s[hDdwZLr6N6oXS=^ Eٛi^,uW3%хh<~3N>`#l]cE꩟p3q#o,t Ǐ=oK+%ȃW_,]3. 9kwl`2JA q*$>sG֢m̘!~܎nם'"64R6:]9jl<[nT3Bf܅#`ƌBލLzɮ:TRu(@3S GmOFmF-S:lZ73#ԡ+xPuASτU%*s{w9wiysjs0̟P s>o y999AS&"3 {`p CQM]_&|q B ;拔 WsX9Եes83qq:S'?hK*VFC0o9Kxnn:+<2,, s1ьI'ǐm={vRѴeK ݿ"84n^v/ї~Z&P*U_x $C^,TUT Wg6ZQ* <}t Gl7nMhӦ ;XV\8y9 Rh8Ȗ)4>T92o!!+]>xco H.f]#d/d`ޡ/y[qe$0iݴ\v-[wh= #>ֽ'ZhƦ}Gf~!D|$5y7P㉐}[3µr# guK /՘}4?w?Ķ# ,|=ʃ7Bv z&&R\lcS+xtDnM>n[fphoB Ħ/G;8Une0 nѷbȞpkdǼ9m{_7gI ’l3S3X:M)8\+ȠӤ8wuƍAma荟ԒwS<>AVb%dlaٻCk*$X.@F2|>4lyb#ܯwoN Yy|?\O̙w N?ݜ79Q,f.2 (7I.B3lU1khк_ioq؋j-7=׃# S|<]ϼHDXv8i)7J]M(MĸuC!v^9f%H޾=9+naÚkg]6S*nq9C|ܐzr1mƈfgX8yl+ڋc)@ba б>N*(wcg!޶+F 3BgN).,ijX|*r xL+hswD_N_1e6o W-zo*?[ü}01!Bn^Ɓ 9 +SQRffB#FzJ*d 7[Po)]4Wbgd#@y5u·-?p<$#7DAaTZ<\7bպڿƗ3)CS]/R,O2×`ֹ ~p ̉£ZYKxWc?ڠD7dGoLt<2 kyk9w 9%|>~8tZYpy> ᆭUQkL6 6r#e{aI`f=#= YYT;3{57 cꍡcGdN!c6U_&):#!1ǨořaԔա|i YS_ﰻ m,,׾cc!(ar&[g( xo}vF8\>f_R "GfxXj8"T@v.Bv,l#LXÓgG*,XMdp$dJxZU[7@j,{ +"-D緁dDQQHIA;6VYMk;zc{e6Ɯ<,"uhVh Y8" jYt_.\"3'qsaE̜-ЇniܼyYb;ToGC^c&AQ1q?Wt SvgF[L2@T/2 on A%5 BENQgqZ%3%)Dy_ {I?.vȏ Gpj zx`M@63>HQ1O#qMCeXmx%DNR"2|YRD 9-3 4-{QhonlaS~1>k =J׭y-T q]:F`7='9 8{"XA[K}ՠvK l^2!QG1ñw?i3 cs,p 63BQCLAlXom!H}-A4 ځs ѱC+آ3xW=as fƪ 3pYD+ዦ.&wԥ_@Ӎ_cEJ6ێ!hBhŁaߙH2{_Xh A4n\ G&FﴵTZC`*a5e+ +ϝ+}~OnSItY l̫P[+nH^NiiaFwCBAA̚ >[Yj,lKe͵`O_X>æRy p=î?a6y86q.MF$Ka l]&T+]pH Taa!gb6'?鰰=e3{Ȃ/2g!r, !lL Vkiw4sQћB DÓl!)SU.b(}[LqleVVƒR}Gd J <؞9u4*(AEM Ʃ]/|+"㒑 䠢3sShݰjG#3ǵ"e[j ),7f }]`Ab1;;Ӱte `fc=0H̑L#,9rch1e6csZ"ö|rp:˛uG'+ *12fh2"!MekSBb$Dl"1{O@zf.t̠음 B$;vkawfVÆ #5rm)o(&X>_ƺBڽLy|Zk tbi*8'nE#q14ѷ;VYBf6-V9dm4m~/+#]ˁWW5Df&%w׍8S4bQcdRI@F Ku׳E2{ead~J/y倽X4f?~GE s?oRrJ:f짢˟e$[iW@EfƟ%ZhѡYgXhD"@@55Ùj!D"@@&@k tpD|32*ʾreUD4,d6&mR8v옔jGj"P( T D dggcժUpttZO>Ğ={PXXX&I@ @Fah"1O?c|>!!!H"P&(SDj"fee̬jcǎeJDTyW]Isv%&4hP"D~^R!X, =H "P8˗/߿Esaε T QX[G e P3R<n܌3MR*O@VV׮]wϡD@QX9NtUHKKDABBtuu?)Æ gR"Dr('Ϗt'0 p/U+Wx6L vvv%+EWr4>@g?^ IDAT f=fŽ~:\]]kbC4RX;vw5H~"PkѫW/A&E@@N]A@F!u' q6)"~8'22^ݬY34i M*Oʳ+_~I|'0p%[n\P 422BN(u")6r8@JDTm|=nݺJDGGK~8JDTLog>~.\jLEKAMZPPUVѺ`GY2 V{)mqYXZo cnn-Z@(~` 4 d6vZJnT2%"@*&Mr/PsIŌMI kry%z D?d~\C]r\} +r)%@#RڰE-asNZWX_qN xիW? *H)2 aZNgϞݻw$'1ܺ?C*:vH }I' EP D pk}/r)%%%%XXXH5lB КŠ7DZ%'RUUU9r.ΣLMMI-҅T >Tbl1H nI ªmرcU[(FRҐ]4ݻ{CUB3\ϟ T%8၌r EhzB \|͛7v Ev0i߾}m bIfSjd~M,A_m4{xr!%" -󏴨SǼy( ɚz 2HI ;;;15Z/UF2 Rk,DH5n gϞחBsKW(Hb39.< +g GF[K:qbΝ D6QXԩ"8~8޽^b"Pxxx{4UX"@ DH>$U D"(Pr"@ RDB)jLR"@ J%G D" E($U D"(Pr"@ RDJIc q>$qD)RCB|d]mD_dHdT[TpP-TdkTGѩE^q#=UCYfTkG5Q(%}QT l/%MZNwmZM'`ru!/OkL@>Yϻž'\{1O/GOG`R:J t;;.>g= D a@p\91ŏIJ"DܽtL5WSs!29S Չ&4fӅݶqXj'òQ_@v+o(T{T D!@#ӖuVBILOo}Oߗ4\_}RD4d6jE x(Ftv)lŴq=ԭQ;GADuWbF`nmcW"<ѷbȞpkdK+4n癅k0,LM`j<8ڷ qOaj/<.Y!X>79l8r+l1b',C5m/Ȓ/aiM ~CUD@m F,`neC HܤGP6u\0h \ O.#E>nY[fphoB\vdD~EG8ۘ C7[%!3:V};.f70k8(0i :$D~NuNʂ/8r FeeeqsE)s :0u8qb gnT`V`/.ni!qW\ AZ8mވq /0kC\xmN>pe&aZE0`4rGfD0vmc'ÿAYYYFh%h1 R̀X|nZ:/ڵwNJUL 髃X;ƈfgX8ylCG^4s$IdcJ`lсn,vňcaRbodኟ?s}2&`9 < 8L^DoqV9p1 m_.)}(Aؾ9` ,x8m ({c.r᫓KB8E^ Nh΃)!28ێ@b6ДA, /_lTq Jf@QkL6 6r#e{aI`fD#ze%D QX:q&>BBFm"19,ܳy<S3kf$E`MiTOy]l|.nߋB{>#hSt _Q[YxrQI_br_d S]t I &*d2hҦ#|w_;`MT2J+(>"@*2 jeDPZܼnIAUErl4mIVYj $&+X4nHqfrՑƜ>,Wygu39ӑ'ԂN9JPEfr|YyI%" \ѷ9+p{iaZZT ߇aޟ0p<3c {B&Em?}egŹx xCٸ+7;I%TqR&F r} ׄ [Zė^.>SMbz DJAW6FVP,H:\q yl#MXbƣjIYŘeajciBĢw_z؄+r+܅e<%2JO-vet6f2C',T ]z'wp8q? 1try0YM!/-Kb2 #;;ASY9!?p$N"s>LJ"PQXS6,20V*,?)? VdNB4b墜t,84T(?C`p$]`U CC#*G D ?GjzWVj<3'p$1\1ms+J?)ȗӄmcOO~C1*z6 $#C)eBV ~s+2p14ѷ;irƣY`k^_v|Zk tbWhoqN"nݼ| G1JI yUoT~i# kkQaԟjְô)y:| yEF`pi%>*S:O>[!=Թ$M-0{Nhա1n[OJ7dJY\%")޵)l e><+oSsgm6<-i`ֳ0nq!j! Υ30 t$!+#Ö¾ yرS p_9}ij| ƅkxB5I_bdMgpX+!m{Ef?,]aN#Z(V ="-[W P,M0m 8 39P750$Wa>ذ {oá wP.moA[R\W,_5+ȘZEԿpi#{N=#&ܠY,5|}`3_ӣwڎ#E\z 6:MČeu,yN;m_{NFi4m:|ѯ$!rp{u(>]Gᇁ]߯BHM}dO{s&,,)\zs@> Q^f(-,yz""łV@IM V6Tdqޒ"!R 22Z3umW`;bRfۘ H|!V칉=g`0sStL`FPHqw%rlI.E*O j|TP xHfQ *d ,Aɖpu FE! kxO f5)pχ bKfYt$?c/,⃠QB*\zd65^x_ǵFuKRч5J5 t^v{ 0iIj+hu8m۶c?Z{=~ZQ1ޏ,Ae_عWt5Osq<*rܝ8kcVHZi?f1m1|k*PsN^'A2l߿Ŋ-xR`B72W! mxlm,qwCq,WTCߦ3FO PM{cd+WQ$^tPo?EQ lHܝ4f(ϧ۝*!MFF.ҽBZBӓRL@]NaBQ>MbA! Vqk%BK] O!7 ,x r2swg??3F +ncRR@fɗG t!sgq;TЅQg SFX@XU]'yYg#mY_)@+q)}.+'ryJJT2 ":8> NJʐ,뮬v 6j"PѤFq7a:O\a/b$;9c>COVb^%Z E)iLTpEN@_ 0[rY*" FRov_;iihE0.Y9(DKx6-W֠+7aXz!!6 = s:D99Y gbiI³]#(# !Mb?>k.H4ceޅ8v!C~ٍSuHq9u39.`o_CB(B Y0MZ*|mj#.6Kc?.+DNv 7qm?N\;]"2E~~lmP63ȖpgTT4}\3L-'X7wͻ`-;Ĺxrh%[)*"ȇmg̞r--vfɒ߭ ){,. Gr:&h&/\J0 X-nF=4(þ/ZRъع$tZ qyg*u!aN ħ,`B;S 99H{wSCanB͇HA ~hn[,q̻4"YU۸ Z[ۇ60mzGsK|Y}pн$ AXt_dn?磌 ]7bb3@)MZPvRRQ3B`,%)4ŰqӠ 8qmes;7kRd3L^''lmeӐ֭ 0EXh?LEK{җ2v*\MJ{|e=?y.kCB Q(-\3p;*`hWkRD!d#Q̑CC8zB-3#4vcd қKrq?|rF#C~fT$~h0n XOj:#UK + @Zݦ2ꂮ0UD;*G@S}On^?vČB!N֤![:D=5h6tUBn~ 4 ٶb&fg!26~o2C.,\~ʍGO1gl a$Ed"iMC]W4j<3Z^̽ƱXA_Mp/dk%[{?o=ZR_3pLr'yQd+%} HV#܆V)r/|o"J}gŔ-!kizWBqpOt5x%g# 91!. qz0<1E~/aYzKO_Ùp5l3+`ER|o'p s)+E-! 紆!kEA ~Z<]Ys`5h!N'pZV $-GWXvΞ8 ohhEA@8:M0aj,LiPW !j  0t ge ..rV6 NùC(0r'bQ7T4ĐnXgr#ܹ&=YU20)Ap?K# Ь%E^ECеkFT3^^<2*T<ڣPF-}h\e%"-m\[p̷tXhbԤp</gKA} OB~n*B¥4&V7G^?38H6e ܗKx{Kb]t,1ru4"PHak+&,-sᗚ)o7ps$6&2`)@탘&<:AGn`#XU~Θm!~` ?E]5!T,9ұh:y Yh|\;+/E(!b-P+tP@iV/ ZSboa9ljӰ9sB3fαJWMYdJ9{p!8;~m"n\f\rjO{7*f:"P?QX?ک~H G''hgG.Xx,;FσP Xa헬% 'I`Wb.sQ3DcQ\*˗!%w/crJlm\/ЅYMr.߂}/i}-|Xl كYc@QśBݢ%fΚ7b4@A|"9 I֓|Ūc+1jii/ůw ƾLvB+s@Y2+Qk "@@&@Fanz'O .^ް7څ0IKnwgGv޼Tob [;kjTtke[CŲ7ğ.c8T;hl=,> }2<R Ql LQP02Ky-;Ö (A-G{ :O[ Umc:C#~Ra/Щ3GvAd'Yi?$ F6p1RyJ"@d 0J2F20C5ܱ~.vZ&v'cGT4a\8d/eƢIPR^&TuLPI}:6ܕ= /~0M# =D@ 5|.\S;W8+7q>5 P*cCTqHeBEcԴaW []+-"@ Clk|#0 |c49ܹ=3%"@ AB"ưPvb'EZT \7Ν0 ҢHt. D!@c(j* #KIӬ &l hj+ D"@3!D"@, PGN D"@Bab D" H(ѣ"@ DPX@ "@ D2 eyD"@( 䒦@JK11qx泴4iQ7)*؅\OL|"BBc OLkr$cG?GX!@Ba~h@ޘL^IZJM " o>@O^F$(x|B\zswd51/>PNBYL6UBb,t6etاQ,>"1ILOaA.r t4`vEZ J;KD݈IEP+1Յ 4 *xY*ւ$wl{ }qax COI( C( SDt<^ Q2/udkS(+5U$SJH(T O DN: D"@ "@ D)k"@ D 1]D"@ $5@ D"@3t "@ D0>ˀ|'$<;i|Ģ %X+5H? RJڵkի "RLjV6޽{yaƌ&!!!>}:wSD mċ${ʕ+obcca?> L˗/8p. R^۷oΝ;q%$$$HoC e?~իW߅ dO?T'M3鑚5kbܹ066˱pBDGGio[D c̘1~>[XZoH1ȗ6Xl 8R5 2҅O~9r$"##S5Hwh׮(^{FS눀 x͛ 5dT~o̙3eHTȊ YQ}H_| ssshkkzw^Tƒ%7{&***h۶આ['CǔD@Vq5kF(rRKBdI=H(/19Ο4 yy{M˫;v(!exax8AvYB䬨о 0/  y808j(ςiӦʕ+y;r9#@q?IgTQ 0'22+cccЂQ%jx(#GΝ;y/r9 @qK,Tܡ>%"$慒  !S%.!-Ν '''\zU " (o'E~ω*yI(TO:Winٲ+V}\P+,3)Ss|9rG|||yi''F374sד&M~E"aN$^کP,۷ǎ;uVܸq#ǸyS(y/"-?~}L_$h׮,W޽{@:H9k&&&\X䳁իWX[[vP!=o0^n4ݻwF S0Ox͛#((HxW0S)D@P\bͧǍ.4r>, #@B yNHHo6aÆ5kݻ?Sx NnIS1zzz[$s}x/nIk\p|-[VX3cH0|IʕSaxtttUL%) ZS0Crw) }R2\xk߾} CCüJ9%`gg'{PϜ9K,lY!@q v W.]]v_U< ¹CK.PB6J{$篃qqqؿ:q{3o\ }}N%PR%O?$й Q"Lqo߾>KKQw>I( s%@\)V>S?y.dN\u;u ۯ_>Y˖-`jR@ϔ|F Y%q~yuVhȖ ٢Q;wf =<<UbR(^i_Ph"a6'RdOgϦTZUP1 /t(lU> 5?ԛ>௿14?T|!9Wϧp#۷o :k\LSK|}}qm.SH(*f8nܸqUF%H ܧ疅|7or<i @q|%r,BYBhsVDžP Girٳg.  BIܹs'Wc9G Vm۶\pCnx$%%Ie{QKKؓ%,BYBn#!⸐Ag;v|qp8|0Ek@:L>W g.=?=z(d'N 22'.UR%n޻w/֯_1cƠE߼l\/'-[D^䞉̗%xܘޖڵkpuuرcaaaHdd,CW 'Hqedq\cPP-/_.rsxÐ I \(Zj}TSS+ᒉr_.\C\׬7njvUgݺu+PBD"蛢72M@1Jȹ$Gű| `\lYU _Aac۷Wv/O? o IDAT3)25B!%W7G,Y"#8p ԩ#DA*&PXPՓqQ.z03B~pܹo:u ܸBO?5jԀqΑ\5̅Bn$a Lirl Kdj5 EEZ R0/"o׮bcc5YUk׮FX*k֬Y<-|ϟ϶bV۷ ye GPLdbh* L85SǏtRA][ \Y=9p\]]M4SW%7(ZⳄ#YCnݻwop+e.(R=i|%r2 PXD<8ҁfquI^~Y \|9Cr͸*9rnl[ v )9OⳊQ3Ox%rpZI(Q6ӟ?.o(^~d>?㳀9%kk\t;p"?ѹr4)o/>ܪ[sǏ~T PH?OZ677qW0eڷjw3흣#kEvXdZv[΍sJDB9X>ܹ͛7Ƕy(Xf EK5k[]RtqIϝys|f;F9l Kdl5 fqR5mT ZDM(H|9sW3r^#ppURh 9&9_ <ɀqFJrKd,ej $Z(/*FႻ$úQF*G4w-m۶ܲYW^=Ͼ Lv3GnASh̄dwXldA%=.旘 o$}g#pr| ]v <fȚV{8;; qWXAxjY"pPgH(!|t2NEzmڴIp]×dNQQQPlbse;#krXqdTrذa೅Y"祇$!2H3`|-W q5aɓ'ݏ5(j>S,鰚[s+ӧѣi~:YFJBaᱥTF+Vf~M&%Ks#Lwd͙|(Leac[o#VBvLKGa%wXZX­r+,{;GUj|?Nn\`gc;g#R_غɨG{{8pAV}Dħb 5]MgTn..([5w}YC5c{?aΰ2(-ϸW?5ʗB (Q/>^b=&Nޘt%/Z}S=,>'ŗ95"@ȍLZ'Q뀰gaFiM OOƘ[aڨ+fO}`f ѽ s+RN&Fji km=l|,s;pLE?k=_ƍC3 _p<%M6V>X= lpf w޽F_Eh&%>"*W $0f h 'xW*c'/+S}  T |GbOp@gr yHǑcCjbf;$=żitJ,Fv"@2)&F'\?H Ï1>\zmV GMG_Z1Kƿ>qCj|bwՠ6,EIxe*a DwӁe8{ *eS"+t05lt J 5M7@;?gl\ŁX8yLYt ې'ط =bє!L-caÁ[ʄB%PVwA}PH H.8&Н~ 7]b^!EJ |?ûX(t86w }Ì`䄚Q~sba%%%E$ؽ6G$]um=P~URoƜv"@%>ַ@jB4,РAE|~/޲Nc6պ ];?۱ F gfOކ;+HLSy8{X L}”,:|"cNm&UnT3E ;uy]j% f>G BK,r'%MCS B´PVx&SKmߙ+. ܙ\ou0ÇW~x ^=5?䙇Hs)3g a!%"@ D@^Y]gh%f%{{i fZˁb88B%g,/Np| !tsASG_ bi"v8cc_E ؔDHʩ`A#-lzoΜfͼ1y ܺKހCUx >YeKtuL-Lߦ >m/D"@\I01:,V ,kjqb٬jBd$?iRᡳ D@<9vp$"cj5795J}"@5^ cļswh\f:qyil>NoEHݰ?t`(hx=;>eP5;VÓx}8%sX&-k1i1w;foB6o؃E:c60. JtBР ti\o#`<{dXDWUJڎ) O5x=ۡb-YtlR<C&E x3h;g֠϶pԭ2ڂI'{+T/pTUܹpFh9n-:)?&eݼɻ37}sA9[1 ۀeI:ʆviֶ>RmTMPj쌿J" ;*nEH@b.+*$D&ߺ_6cרyK/p+> - W#/@h *wAÃqh=a& }d| omfB7/n?qpV+A &93tĞCl B3]͑)&lZ %{VArClX;.3wjgomB9 N  [k1+LnD?ňsVj ]9 j}`1]faS "X4 RW-f}eꆮ@L=Z6IO51i`_4XS,[ӵ!mi(}Wk>>9'z45sȦnj͓68xcڦٖt(ݠ'OvIg|T Lܲs2̢jC:A &D "&&̻"&.:7P~>>5Jaec =ucl}SKGKD"DfLݫK/!-2Lu!2--REq"(k~B4k6f EBZ݂wށ}@xhI׿!N=:lqxr6E9&ff͛by.uHL`dL(Tѡ*D;v ǏwB 'D@k3ZO4jjjPak㹇`6͠eW0{/؄$&-O`X9>CU5csXeHI -M19 K1wy&-{GR^&$-y a'FG#]2tVepJh ,xzr: ,>`>m}IP`>,߿‡oUg7,BeJ9AW)>Ge[PFEO~87oѲeK4k 6,~=.c-j޴ B177(FevַA)9֎Vyߪlsx ־DbX0Q(ZD5hk\FM:)IXDf`$6cx8>Le{%kCy^͊2 pհpS~:͛›h'A9& =0f%{ *j1]{$FnxkIlfPϚ5kPbETT k׆ª30MAC-7G'(e8/ط*,yQ4en2SA,5m!"*_h0": Y5AP}T s!իW Mq."PxBBBpQ8q7oª3Pf@6iƘI{>2'lv/la%7zZYLIx|$^3OseV.p6 pͳx.5'H'M ΎBZ~(s[$Y11*${I؏wlHWm? UjwI5EiD Y$(PV'g߾}+|YUQY+8q eM9Ta[X/MVnBviMD=W#$ t,8sHvBq'}Q,_T89) .>!^4+>z:FU8͡C9;ew&:Ef%@#TknPR~O>#zzH bNAh 41hsZ7ůSib%:{sAwaal1b">cֱ}ax 9 mQO$-cTIdg xlk@U#9H 8ɼP2}%,O%uYUGe@a͜8Pd``Mͯ~'ɡ8j,4^_xhRo Y/876..6Xgо / л\-8/ ؄zbqn*G\ nӧOuYW1W'wBYۯ!P5aujXyquܚ`&~deY9ES3AIK}$gQ>kWX[%B" VՆ,=oT٫ IDATd][*kF_[/(R=4>DUfR0{EI 8T`dn {s:Ne\(LƕKq.vaPc5>|FLt, 5Һb!vE$@Y]Ɩ62JʫE?|ͻHRR-ܜ,?I)0J+M*RaN)1=<~-r0I~Wo? :Ivpf?dVW̜>& :&6(hAx-¢bC z ^%Ɲg^Ok._b~X90d?Lcђ?_wb `$>>b+WY[ O},aCF.Fȵ0cX4[mKz^9= P (878*+)3Uq2 A?>VPI őugh<#4/܂Ua v1prr**fnҠA5 ^^E 3#j8f+6CJfaV H_u,PGBmE篓sLTta_i۩><%G8oZHp& )dC&{pR֒-{C Db*U5 ż1`7`Ÿ* jSRl4ˡ1ғ84 :8>އcP0bRTڇ}ge)l⅔G釵c~ u\G1 /A@^>};Wچcl{l Wq2l>rՁjR mѨ`c鰎88Ƹ`Y3aK0Wu#IS*?٠?0]- lqWvvv8{ rknw+akkJ_ͱc.!oOKj7w!?sO%/Nn[x.aZ*1ժb|N޵nD/l EvĝWAUUnnnj9ÕGG@$b0.-|%M,? ZQ VгdZ(LZl;LҦX1LUz`ќְQ٭`X8C]a){"/E G}ļuM±PnT5`,*lɫ0ux.e˘3l%f8~d/vBl蓛8[qڌ^W$q3*Z=0|&a0>37Y kā1_mʚB%9+c9Q>NTvA'4:87n܆re#Kb֚[uZdðP1.Х`yW1!ɓطoj{g1Er^-S8F8g 7¬SC*^X_xq~4'8BJ<.*GU4(mMgb<>}_*t(BRoܸ޽{gޝm N:ԩJ*+++|ʘ;d˱x|F%AIU^c(m{!#@,} } p 5 ѡcjcx5F7X*ip8x-ۇލRLݜ0S\4D_SJѬ Zԫ.wZcL:wfKQ=+uv't2̙i\ qC׃ ڢym'B#ZL: 6+1]YSpr["FZuvb{=*4.F}= +0ctS}ӱqCWuSj4~L_XS|yatX}v:u /^UHTSc1=XwJG"QJdTfbseGӜ&Vp!:2H"-7t^ĉO$w(ZRŗ/_5d|._RLAUdobϬ4ӥy)|oדd|fk5a` Ssy;acBBBEh{JЙFx1H t FLZIwC(\:(. Աa~ "_PFqa K8#mbQYYjPwmz"DbB /~xO/tעo`{3{ oc,=a,9_Tr| l--%~~x}r1iҎKa  Y0)PE!9QJ0Ħ---ǹytryE6E>j[qU<{k 6SƊ*!b;;հ23sU]C1}oQ&&KHևAFMCK ,cxXBRVU/SCKL@Ll VuG/o>4GҗǸ~' 5Gj}J5RQc&%ĩcB C [ qX("3Ƌ͘cc̭sN)5D=x:,XQ]OD +Ԥ6C J&h'o3s\`): ; - tc/u,Y(h,kL(Wbv-Jzʔ}$Xܼ8=9UCNai ]U2zT:(H5$EᲶ$1n_ʑv3RhqaP F(# 5&r3A7x6bsAKهhRzZL 1s-r&! w7l9"֪E%0eW^CYKfjvej{DOæ08<e>fP?5}R|3_;/SH**%SQ[D}VNr1 F0ДKEb$$U,SP^b~9ou= cؔ&- `7<;cGswrO9+a[ s2e|&&\58N&հ4 ހ3ޏw36sS}hZ{[Xiۜ^ˏQxF\(4qGNXw#Nj6\'+4V1UPXW`ۧ0R۔Oe&h*?9k m;8`)`DC:MZHvJ*͞ ?(I+eT0 [^s(AaX1&l{” Bb`p*fZY;cW8t9'f,kJu+S)XY=&?h]2 uSV4PQC5A:)j[q,߅6g.8}kWd0P1-<%kxj&7LSo 僪F3~&E1D #9CWDJ9aK0VzĄBWI%`W)#{ݍMѣe ;WB3saHʣ\nWNU9;U1y3n$ιq0w]Roܟ'w%)P۵ڏoC'YAХ rXpO]cǡO2l"/7q/LdĖ>?c}'l`˞<2ї1|Hžaa 8y5vQf7̚>>apo݊ 7q[ :mJ'w@z#׶L PuCt] Aa~L)lJV9+_ 4N._,9FvﭥqC}F_PR5T-ǢHRRG#QO^MXZwP )ЭSp;%X0 J5r>d,4UJS`&0\'&ф;QI昿HV^Q̴3 4zf,}1GTW=) a?g*{8vx:jսSb9Ej ~7 SvFB@Ia|جr<·G\ * xʖgܸ} VTU F* 'Vcԭ0mV[?q:=6{A{G3{nƔ1(wi̠4:6;=˱h4 i2jbxu',^ac·R!< *qv7Y80ۂ9 K*K7Ğ+P嫑 aFS rW4FyЫ^ڞLs݇7oGmX7ݭ sRp~VM͒ e(T~0fˢ8큉v~6eѮW3ΑZފPY0K󝓩}@bxm]l{8 2G[ (X1ۘ=jK$qlfo z0{R?Sif~t41Kƿ>qC$ZbDb(أǔɨkǖ%%F2[(zL؄dl؜JB*C׎-`VJ9%n, `4מ@x!^`2+ظ6[6T+ i2.q:ѵT1-`63< j]AWJIWM)8Aʿṻp+dI]S{S|f;Ll9ǀN̡$@?YW/YAhy_u0ex oLX:qdW8d,Aɸ4z ?ǩ®- MVa y]c-E 2FJ1?cϩ̅6VGiѡxv&щؼ$6`ۺ37b ޤp߿uVk&ٳ-sߚoѣCjh _]w{  3aE>NTČœдU}*ĴCdLrHI)} }ZN,w}WҶFV^=bq`qZW|lxXh*sfH- p!JC:$boC~ź[h޺ J'#5L.[ ? gCmЪMX>5hq _͌yX/h]3 fCMQny|~xK߂.7ӭ16Bz5a;cgo̝7y u'j[e,R|*oj yfGXd,52S1?0?M=Y_߉ D67ԉ%\2HnPHU ֎.6E{8yr%{8@pZMm#@c\viJ+$9)’+Fsp0£bajg6HIHLBwc%q2b"C{7OSYY?68&ʇC2L<2iCVtl9T#-wqosޛm_H#\>÷uR֟$~BL7,ι q#&^Amuö]'L24?B z*H ߆bԺ휔tOnUd<ǯkXܤ1qz0HڐR a8~/yB`Ȅ8$ZX0D\8iS_54…ꕨF[Y,D9z j`آuPp 3 L7XU*` o%Y0rt޿{8s疄w @'wl+Kku\8 )(B]C j,m9lRshqJ,jkɱP(.oӟ`z943+Ϩn*1xv:#kOߩuhÙ#aQi,y:vXǺh[SoFPpL2˜[7ա U-،an;ޠm #e 5{A\d]l\%Ji!k/Ɩ+0k~4R\.nsOCC> Yad(X=oQAs9 Vcaq6Äh: }̄5&wv{nZ߶aMh쬇Ow00Q5ŸcR)fCJ0RW4jd1Y'Lp}mgqN7ЦFUb6uR/ث:EjjZKzf Iopcbiq3 6KlŠx ]z0Vcƒጟ:/1E*B6Iyytd_E+,C>ݰpeY9uPv2Z2o>c?.#KHD" b ʯ*-VZEjEm-jS{o"%{璈L"]^~>9|?o?PioV۠Kpf쩘;1[A'\3b0IBcぜL Bl\u^~qx{$~(1^MLTVfcX2D@'AjT{P VVan'ǝ{ퟻtdRяqEXd;bs D_l3-Ðձy~k_Vױ{XY1=d8G֦bgi[vV9qV:*V1uܝЩ OƩ0'<u0緓xXiUAhmao!&xofyX'\VGAs1pȚ[TbnŠ`,j2,/!h9q4M= IDAT_YQH_0EO2 =TOB.^;J4=! gnO^` +³Yw fqt,y66N~EMk( sN͙`煏ZHMGحh\I0Zր~Wx<;|]rhABi^\%"x|Uk}~bRru$lߍa^J&={ⶳA NBe079Ĝ&/#5%E푃Oˋw?iտcޤ7]z=l4F@|| Ѭ?ǮkEӒ)ִ6 Ϙ.WdQ"LS;0ځG""1_[Jr$żu\ =HkVL3tPZiͽڻzJjhf%ڶ69i3/ɬѬbOt$!.{NL)j7n6md>ZE'_02Z_7/ŨşR jٽQO؂Tjn sgň#Rp},t<GH1eCLF,8$DSo֎i7p*pyfY O" 0U; 62 UЦ;ĭM.;~bo}ש  dRx0؂G hߩH-ސ#z1=HK&&{bNj)TMLbf ;C1&vh9',àn(Vhժ5Z7o g˝)WR׎{~ӾHIch&I#-1nhȒ׊Y>6G/2PfN#19|=BDEJxF*Z]ֵ!~=4ň߿kHMb!02O<$ *j|F PxR(n,|(Vb+lpp2@RNq>V9{܋HXf7 PUrvzKPtxt}u3Fn ӖGYhV HuM5 7OuTLG-' sz/(>EK{T/`D|B P (uDCY-s6VX'FFh 5u@/1J&&<,[bgdB4t`/no,5Mgb-<:>B9LjPX=3F~݌ѩb_\x bjLlVN\#M1jU"zbD&)~cT[Dk)}:_ [`ܾ| msFnF0+6GTu;.AnA+HɎ_ %e1)@7I=sG;oX }iyÚ*~hL3 Eq|8??K%Wbo.MQƸTSpaD|z܍"nga<~ۿD̙X2Ur!Ը F^NPv^JD۴ !%~7 + u5cW )d~%캋ahZX=G؍:-=v|::)Qu)3EKQ0e~Y.-cK2cAy?5/}}bޫp7Kh } ԪU*C4:6to0-?J].2 ͠0IYQǬ[Km1|J/$1qpRڸ⧍4n [F{y QE`PXD(Fb HaXNeKa45mjqKg<[ѿoVn]N:yOk , b}t=;Z9HIѨ#5ՠeowX~nMs{aDnAS|BP3A%:f+%:9 G𓻸v$MY mQxԴTF Ec(6/w ;# Nt, ˡ2#`t!_Dy 361Z3"< L3]![PwQf9X:/o7Ǡqճth){Uw1HdIOcpz291$pI*Y_97 kA_kY'>kc8r,VZUm.m8s>BbRkb}FQݿ_ f'p$CS]}ol_JQ O+2إPL+„$f.ҕ;,+G;4.AZr *˜ɜ*C]3G@"[YegX界˙+YPVH: IiOJh9)W0] Z,!Ɉ!!oU+!-&7<'ܬ2' #*KaX FZY?H3g,R=e m=Tsq]@O(ӠP,yi*@"FW:/* @('5(@(ӠP&#:. U#U)nk~Q.]EKa^꒼(@r-P} $hG6a(@ pm$''W&PAaѲģ(@ @Fѩ/+P"P(@ PMWWgy)@СC(@ d 0(Gp8s pF gS'P9RM6[)@ N]$(Z x((FT f(@/_Fttt7@`PX+E@EXj+jYn P 0(,;)@UP)U"lX} ߘ'P(@`K)KD Bٳg|@`PX1띥@ؾ};߿_aςS(LAaa2O bjժdX( Po#>os)@󃽽(P K[S(@%c%$f(}~+Ę (@`PbP (n~ߡ(PxW>N&FOOɫQ@n=PQLThjjB"h)Y, PL P@pA)QU Pe'y% P («W/M o~; P*WN:A__Rd2TK} U>Y P(P,'Q&p)[_ Pe&̨y! P] HKK{Y)@ kz9 P$r94issHiPPI)Tje(@ɻ)@ P K-(P*/ÌU, (@`Px<P B7 P(\Aa6|Pv[NNN*P@ Oa2e P(4l)TbF)@DEE!""8 PJAanOرc8qD+8KL P !K 88fffʕi;`wKRe#Z1esQ^ 0(TҊc)@ P@I qIj2- P\ ܻw+V@\\\3C P< 0(,L Wϳ?ay( e0S(Ν;(@ Ph ģ(@%PuԁY(nا@)(Z cccahhX 3I P)iU^Y* ThPNES?,<(P>.ϡʵ;pܹrGf@y`Kayxk dy 1 PH} +Pe(@ P0>.L)@ P %IDAT@`PX*E(@ P 0(,L)@ P@`PX*E(@ P 0(,L)@ P@`PX*E(@ P 0(,L)PABptbrwh2~[<C^&嘳f/#Ûa,b")p<*[c; /cm-Ü{$@޸89T E W4Qzd)(ۮyRTC7hTS [f;|B P TrXމ@\{N.].*d%HD3(@ d 0('Xf ;ga/ѾW!Ivv쀾C&`GHhym3sk{.}/Kн=s{MDNѭ`,siQ8ܧ+:SBrՈ<5ˆC.ո}̋]|fo}|5s3rĺq7iX|ܫ u슏MSӁ(c!OQX6>#r9|٣~ji$f|3=;wBa暃N͸P]ޣ|²'ڻ z~G\.}bץ}|B P +(5ж ZwVV-f1n\N0guWDH N`B0Qp%dG#{0l2A{&~'_a:kFs<A+jX1k<$d^,< k/$ǟSm=]<'FXm k c֞}4o޹ ~: qw.Ctb /Jy& 6޼]Nq4qk\tl;說FvǮYާqY͐r [N3r>xB}p7& (@ OaqxT@ر.ץ1^p[ E kdr\0tDx ܈a="^"n&GCzs~F p(ŚSJ ^6?4J_u_=- Ga߱Limb&a8k]

    3_PWIxT^@9RmhjȐTKFFFx#JE -%+!=-[ ̝]a L-,`eM`(ց 4kEReiHOC.Zw/eO!2] :ȓ3Xx B(=jh٢ v,܎a#,Wvl+n&/ Vl' xbA)qxt? )fO@[k x _C=6m>Y7<~~Q]l@1|e#B l6ek;(WJ|M|eM"&;faS? E3}Hb8L}ucuca*3r-T^ L*ʞ7z!5Ƴ[r1.96Xf.k;ீs84.Bbڝ;k!F۴WvF>ptF|7y,N߀=0خ9ZH`߸) 0tQ=sQv0{bEA> P`KaxTG@ͦ5_7b v$jbQOzsF}!_O4wz[^s'KO { FX5},FO]0cD1Αhx&⫶8f 5=Bb06Vp\SGZghޭ Ĭ;o&h;7 cwĩp{5U>Oj,hSx @׀i|B Pm]69O P(l)TZc)@ P@ 0(,aP&G PQA2L PJXAa 29 P( ֘g P(P KQ(@e`PtEXtSoftwaregnome-screenshot>eXIfMM*bj(1ri%HHgnome-screenshot0231Ơ0100Fotoxx:resize|NE4I1iTXtXML:com.adobe.xmp 528 860 0 +`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-20.08/images/index-files1.jpg000066400000000000000000001616121362435004500173220ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 530 500 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x}.޹ MEEj>|Wjk/(Q_VGڽW_GGڏ">Gڽj>|>}z>EQ_QȬ{z,ڏ"EEd}ޏ{`5}}/+#^}ދȣGYj^X EEj>|Wjk/(Q_VGڽ=?ᏆWnlΛgFS`W`ŊD`n`O&|OQ_QvNp88냎k4g~ɣv~frxb2ƅT+|;Ohrf7H9\e@L8qJ[_Q_QȯI ӵCU5,4//l2&p;v~7 OMO {{mM!B(P2Ksidv9OEEj>|}SDž5x+NӛJҢh|G6Dglr >,ysዻ&jRx;;'fx~h$ @e g%{E1|ַ[~64xڏ"EEzE=}u /V: l5ycWX@D0#sV|>}> 3>meI:t %/3E_10 X=5&j; -uYT}(I6`oD%xZvc+m1v&VU`@a˭i_OQ_QȯPGoxYwb`Ѵ]Jn D:lR׌D>ԕmoEEj>|Wjaj>|>}z>EQ_QȬ{z,ڏ"EEd}ޏ{`5}}/+#^}ދȣGYj^X EE:fK{kv|1;sc}޽ƣ\ޤ/=󲸈TU#~&=KFTmGMY%Jn$Hz;kVwWRW;(uۉ|07ٙ˿ѧd>Z[d0T|ԡR2WgA:-ԲYc尒HÌ.n>%dȿ7)$91y89mBՍ&d/4 цVtB]n5xܬwIkkpTVR2ֽĿ5M#PKkLkK[op:1zs\gۈ.EVP@ʓ뎟|6xѧ(YJw>2`+R*vMYkϜ/~hO$s o#2ƧҊ#try+#0\hIПw'xw'x̛i>}O;YvG;YvE\I'ֿBܟ = z?ܟ = z,Oi>oA]#oA]#f?=}hIПw'xw'x0GO~ù>{şkù>{şkYoZ>}k',_G,_G.~{}_?Og=Og=as'֏Z r|7?.r|7?. i>}O;YvG;YvE\I;5Ƈf~d?ga2zw,_G,_G`էXI֦ WJhc /!dqӊ>7FA ?hù>{şkù>{şkYό~?xQE3p#'d9x[隞׶ZDMKn6b|n$q1_xù>{şkù>{şkBăE͵Q!w31{m4%!l22 |{޾r|7?.r|7?.ٽ^ק/v>-9Ah/ds[=۩gҠux.> ^.m xⶍq;3!$_Kù>{şkù>{şk8&PO{şkù>{şkr|.š_?,ZFgEE[RQ&}O<kZέgWXe{v #Xʠ}oA]#oA]#g^HZ|)s\i"k ;+VgpZ"P͸呝ܻ,%9$Aܟ = z?ܟ = z\GO~ù>{şkù>{şkOi>oA]#oA]#f?=}hIПw'xw'x0GO~ù>{şkù>{şkYoZ>}k',_G,_G.~{}_?Og=Og=as'֯$^O+C!A򯾿ܟ = z?ܟ = z,w: #0$`?cjsiw1lT2)B2Ow'xw'x0hEaZ 9Ԃ #.xg鮚c ^a#遏~ù>{şkù>{şknƤl2\lsfN V>=?ZA⛿_9'ܟ = z?ܟ = z0^ Qե ?ӱs0J7''+7+oGK3}K;Ly1!$? j6}!^[~GsmI9M4,lw8nym Qϵ7T4ZC-3h^cQ1&1t!P ń1ۥJ%KA9#ހ6}!<+;^9м)Wo J.[6(.[;_](WO_j@XoͿkoo"%o'f]ż-(>.TVeG֥^Xa-+mw"a'pO4Ϳkoo"6}!VլRlo[Dko-?ž,м64_YZR7I!XQe4oG>Em[CX4-K&hĪZ0:e׬{uk*?dٺ (H:_6}!<*k+:wdpi}"$` WT.fe1;Xm Qϵ7Eawk%[GG*.:䃁Bo%[4^Q2@zsHͿkoo"6}!zj65w0h\Ύ mNG5?h-3_5?h׷z V.pdI>6(oz_OF5#pQj\vdP1aК&c YI{n۴$?EiӳwWBym Qϵ7Y~#񭏆KjZlmR0$}i&$h[̫: 6}!<+>|N&f/(ewN{gcgm4e(8lͿkoo"6}!4~$ӬM-BM_gI EfbBJ)5CdNʏ s'Em[Czt/^69fR3gکi*ԯxDAm&1:9 p^Az6}!<)\E:'_,MCk^mRa3,V^I`Hϵ7G>E0kH+9T"UYR3u޳eg3A%?jd[o1D NOJm Qϵ7Y~ iZmo.Em[C lnv-f'&*=Y>0x/=n۹ou4Ն̣k:/ONM5tnym Qϵ7Nl9n><Ԇ]ue.2py26}!<++Lcĺi.;pTHb'FN+RkO{<2Vsym Qϵ7H58H"g>r)?o_lio5|_<(ogOkIuڟ3 ]*b 5V^3yn.`Wv4o>Em[C߈?|ISZ4Id(Xu"xOI/2U$c)OͿkoo"6}!ESoͿkoo"Q@*H^ 'LXEEX (|&:EQ3yQwX_\ag=Ĉr=UM.5iQ\X a^aqMncWk~!o ^i?-BX,7;` .>#jxNy{Vߛ|_ϕ ǵ#"6O4yfo/_&15I~#a~.[]I7T1 `bgG.u>뚟t>WLHpZ]FDBsO4ٮ[xWt@YWX @gE_~'4QC.uOll/'iEuCHU/x mOķ'Ė~(KmɢiuI-!,Anֳq(9+kxc>dTܮ6KGVZ4;AuF?i{8žָQMUVNK6žU Y?[i7X';Xy++ +xSW>.Lеh'Nqcr%;!aGh^A2L#w%ci~ 7zvzkwZUܮ-|A.6;]3Tg+?D{ki\I#"$†!A[2+v|\jZ<#6ᱫIi:FDm.JP0~oVh2ލj$KE4HK'-++Ngor9t,:gʹKN޿<);G<zZw(߯|xGI煮u ƞ M <]=r^4-g0gcPӯ%^6TVtbHULlXI(-_ϓuYÞմ&J,R9[0 P QGK]~rV.bN0. }=EO ksd mo<f_-Ӓo)vץ_[kzmuckSD4->؂A $x;9#{U`Z4ß.ᬚ`n)s}>r.ҙ9Wߘox?LҼs/j.Nu Ш cmݿ1[D0T~|ק*] e<] ,R"1njzR l|I-wX| kg5Ėw+HIU?g*v`d)=ScZ4||=ާ <~,{6G|68R(G.6dWެM& ay?O^ksc2n^hk-/u V]oOq22)p_jyʊh_hi[$umx֞(ּ/.|3|vn7*`ٵ 9W~'h>;)4.[I.#4 H 8?3c5|7Ñ]?[ǿm>5hN?Fҽ^oqrs_Z^|Zi-m7%NIl9R@:ܑE)٧߅Ȧ|R=1oYjSRB83A'qp̓^ __j紒E4˨i,HdY H5-b?Y4vtG@ x=ĒO_K;jmֵ>^jWp|G׭<-,yg8#A_FhZ7l·8U>d$nֱm Ump*J ( ( (2T3`Ӝ/\ZQxÉa%3$;@_Fwg{'|5[I^u? ){Z_[EZT>"{5OeiDm[RY>VLң:UxďhzeoZھV٥g#hLI֣վZĺ6/zX=g:"A6\vAZaw!O<n JKvFȒ*i_ OKFNqm+| c]~ [cᛴOD H$= JT?ɞ3k4AӼ65^$7&|?ki-C WNE8[ShZ宋sY^]:qi+A:-`nЃhkGlI4VYڛjZն3#'3Hvڸ9WQ _xL/\iVtd>eRFQ7M1?(ɩ>knio]&N. V#d3r ӧakI/<^j`KeHU/2Oޡ ᵾ" RF2EP[9ϵ]ah8toMaeO ޫKau:FW$˪(`db|)uyHю+lk #23>g/ tm?%~<_{zk}G^ZT:"iL@!TH?6}~|C_xr6m3QgYt>R:aEqZ KH4:Olt-FMC@̍2IR9x -9}g@Ԯ5;j[KRuuit+Ld!i"fvI 5|C?5-FtI+j>22[bzHm&_ˀa۷*t`; GѼ/qY,k:]ٙv|E+5ß11ҧѵ$Z>@j4rJۼrTz O*%xSjXѵT'V˷q$YT8x]-o uFBm/.4 ͪh~ڑE+$eIfQGN soxy5.lיV$m\wkVMզ5BHnOHu։hziPj5X\BYB㡡_NoCeϓha@-@´ q9qcuxZR3hZ5w2 LF.Kg+<<xoNFon8$u-|Aȶdq݉H^Zzsy_ŭ[vdu] Y|Cd8e*A^X_V'n4 *RI_Yg@7Fa0S}x6Xݽ4KFP2Bc8xUƃcyimhg2&l<~6񵆼CZQo^Vm8JQ|9+KxWL쇵R[2#"y¬I@G[ψZqc rĈU̍?.rN*XxU<1k[k2jOf(!$l$h_/~qh~8[iWg4mDU KH[J|i~.oAi}G58c7G<+2 /]ݏGmSVqhJg yLbu?ɨ]&̒ťCn[[7Rϥ%kk履袊Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@%*4mB0kEg]K:iJ6lg8exi~NG!*$VkV;/Cok}>.h~ڍn 9\A=+a}=SZ55ijʋ@-H$P@z[C|U?W'_H_ٮ/#ìzR+g8׼W̟>x 3o.=Ú捡Ymn])}2BJ!j0}BF1@1 SiW9+;-;zޡ⹯5 i|>[%ϴJҗʏ6 0qp`~Z$71Ig{P^/VF".)ffVp&RF2H`3i_`;P~?ߴ⫇k/^6omPu;۸e{PĐFd@y`78*ߌ~!KxPu-#~[?4KI-m"/ ~Z[ ko;w SiQjU!΁/c;E Y4[y<2`17Wiߌxw--]?CwdxĪ8¶ҿLM4nڇ?Uv@-O\Mks{oZi֚K78CG!eq/ZymW:HI2p@<Iڕmlnyڇ?Uv@-O\|cYS(ԣPn򥜱Z@ Kh{gq5/{eWK:K=r`>4ɳ ⫗[u/&_ڇ?Uv@-O\Ė[҅uK/^F)cs1U*'űlZ+jw.tq/1,{"TN_>=?ߴj?tE6;@n5Msq}mh!G)VWi${uZĝjo6IebRpTwecڧZvn+ŏ;P~?ߴ>~О.𯈼_=3^P5΁u%Le8!hd ,=x[q\ qbG $(?xAtm+ZEP"uYK)BWvvկj SiQjUQaqjTyڇ?Uz=X8j?>_o 4;QH|?ywueK%h\P)д* # H,xĒ_)|7֟j{v.躆>d/ۉQo,:yGʌue85Bo˧C{Y>.!"  DI?319ˮ~1ux<%|MO mo~tbXݕa;E,I$qߴ;E % .MC#kk;&ȫ)eUZmo֝7W7Mj?lh,[I`LCHʤ+?Λu}Wfկmu{'k[hK4{J\ |`mxyurP-!l._ؙq&T( vnŰׅ 7GzfT(c db >=xFgKQE>{ak\H{xȃ E$TMJz]y~jߍݟ=˜> kxMA.t=,97EcQ6 bY&뉦VkW{z׋bG /cg9pn+=COԾ -ktvT3FbVGQ¼h~9Cw=׎ &g٦%$Xõ\ۋ/$~.ߒj_ oj>,EZ]٭ޟi<n- iG"1`p.ڧx3xW7}ޡ SC/ѷ*\&1^egNx[kPMm5XbM6+I-fX"xKJ|dUɾ;|B]ƗNiv<B-`(3sg>OTg*'W-/ogocv/Oj.Η5M`]Ko $YF Ƥ `+{/u[{ĺ) s#Tݙz 4CW^J[nDEB46InH\פ}\[^Zh^m2v>siAn]t/[-:n\</ ]OMҵGy52k.m1+FA^#~eCm[xP >| 5/O>!_/t k[ /UTG|a/CPӭ43hi;lbPIVG."$1xBGm5yhޜۿz1׋.|OO.Rl+EEenV{&Dm8?eK3^*^WխnF&}x&P|kn?ZKhZ5䳵g-vln&i6J*sxSw,^Oqk:^}Zbj1є=3 gߍ~2׀I-f}w#L 9cڡdrgSjs?j^|N!]Yqkuyytr)Xy#2䍘,+AEX -<% _ĖڝH%Ku*Pq'$GkĞ&{X^SE6i ԛ|ca|+/\^iVIHţ6q. q/r[zk#i>wO/Ein k64YpMƽyd|0*J ( ( ( ( ( ( ( ( ( ( (1}>hv'i9!Y3†`ETuNjOvy]=/T`A yg>$|:QFk_ *tkQ* ՙ"<ßpvkVB+;JYʪ@xW˟|A~ԯ43$ӵON&/ux 6=@_/ÿкΟmCH5ko\syۯcdZ{ v7%7a AŒwZ6xzξ־8$eH" 9i`U_~^7L3]xNU%%:i>֖voe$@qݒE]Q3Mzq _P4),eUl|EU2:AL~[xcֺmƁ[_魢IO,l Iw+>||+ZuϣAic ' T"h=xFyKkVSݽ ?.f) ^cif9-vO}={_ϡt/zl~!.mi5X%hܡчV6gh o5FLzV6T*ƥTT /W ok@lyH;+#I8d՟x+QW'WBu{'IȑRv:J-?B[^ ]U =ksD˨P!1q^g'<{c_Yi1~}Ni#Fҕ,ۓ9>0c㟶Yl?4KdOqN(MJ:݆Q5KKE[I a%Bؤm7޷}v^ u.kv^.ۛ}֝M @Hb.6#(ԀA$vPG#x-]݉BPCBdAlu&sWG|'Q5X.RӦa6imqFB۞&*wdbٛŐAemmuWu(&$ep2J'.p}l]?# x;Sψ-9uxzYh.]&њd+!ݕpAȬO.}aWtAou ܲݢ)UUG_1G3v>$Լcwj:Sˤ3i=ͤbb K nʂq>j~:T4Ojos-9S%$.$l ,)'i~C{OV>Ӽ< ih]iS=2IfGX v T4|)'jSyy n&BV t:+ ίv5GK,͎hМ=%Pd5 4Y> Q5&:pwoygu.Ygħ맟D3[?|R6~H)\}|,N}2\:Nσ*,hvsZ Ulȴ-3ZǤiZK 9aF쩄X@5 ~ ӬU$ss_4SOzuO xumKJԬ5 4fI[Jf#Eps/C;}?Suy%9$WˆTL+_>j.񕭤phF=GXC!$Af KV SѴPcMfD0s #Fz+% j:g= 5zw4*rmZ1"w > EKv:.LiĐ<6La,N8~_JoC~ ֦дɵX,k4\xwMKVPX̤T$^=+W&o}7r7:iAyxK>Z6:]-@V:薞>Ix_VtOyu=Ky hHS9Q}⾁־}iyo<<ݧnl5'ওUQFno#K3 WO __G/W+EÖp5c nd&Y c" N+&])}CL;[(@#n86dr$b߳%tx9`X[[9t9ch]u{94RRKx!.Ov﫚Q~?o{ytv?xs@/S7FWĎҀ,Ѩm*¿ ~!~?] CR,v-,[)++A|oWޯ lSVj7[]8Gv!N2>RHٓZg_[k&[#Oi;'Z/_4R-şRѪ+KD[3կ4SW~@w0MԒ!)J +$vqa ]9V[;Tμkymu/H"׬jVlVVq&@1!jޟ{;6>Ahw.#K4hBO*ZJdK@qK?wmJOjlĩ~\t9\wm/iV??g4Oi0Fh-m>tk5Xg$Q$#`5S¿g= 4_=BT#}gkp3bn<{~z<=jV7ךe ~--dnff t.%A,61޽a𵿈.-blju?#7_KxNU+d Y:/u⫻ɠ"yʒA>Q<+^{Uxfo)l֖!eچ-(IT;Xom6Uj.MnKd;?xXtŇeajʷ[Ci@$qT~-~/|Q档hV[hvѴ@?ys"ق όRq^Bז}?[ER(((((((((((((((z?[W5tWK趮jI}QRZ~a/C%@y}=c5}CnȞ%XAc?t } Iiߊ-YέpMpK9bĐwp1Z~'ԼxǧG+F 2ZTRfPcMCZm~i!:mG27Bw>[19j~a-Z:@+3ߏ+g~&|D]>:A'luF{۫TF3Hq+FXAĿ _^ ,umVGaD@AS0kmீl{hZu[_YZJL^Tl7]GJIio$=/:ԩz%]$S C*0Y p{1皭o7i~"^K?VլZݩmoHѵCG[(ԖN,^%Y@\85vO΢Դ[]>&4\r\"Y`F6{֧,<+? bU6'-Hn)4 3P'c|= :|sTQ ޑ*9's$[}˯Q/5Nn>%>M\XE(!6;69|$E|As.ócV9AIMz&jOo Fim[l9Ef0՛ipxJm5jқn$̓H*j%vWK>:ӮIj25@i Ε}wErV%*KCf5g_ EsPOEt8/_}jTLE xk>>_o 4.gHx~=Om+[M D6۹OpEPQׅǓ1|=O()<7t_ G:Vhú^{i0#P3m_nF8$fvzW6}VCү5)ki +A'o> no&[(ŦoDH kk6ݑ0JgbvOƋ/I·0ӵ ZK_FDe ޅK|ȭ#u޿egoE_yZᗏ4a=r?}EWXyq]oS1M߆<¾| x\<3[yb u|ڒLn~ym_ꉡh$l˧?">ݲ0 s|# x1 5gmuk'Ȕf9eތ hY_)MkƝ|м]N:mq De#D̝o u/] W ]B%յi|Nxͣa>}oPSjZQZ 'che!ŊvTV<?9>YdiM*u;nݭ"-Ug̻BtekowHYiP MFԴ"M_Ct4 z=.[35oqP|;m5oG%,^n-u>Yؤ||.#2/+>>|9GUxKb-7FMM7.e.vXT<g|=:R}-<=3%ڣe `Uu,"%.{#/~64 cǚޟi'{{hV3Nrl#;ؠkveKn|X%Pw?Z;7H6eV-p[JcvQ$tJ?6a{^4sX3yiڧrN28 y9j7~kߡΗ*_x|7Jtmrll>u'O܉!j"kA? x׉EO|/qpnżIopzVP  #٣ OKc;m{[;I'gWk-2+q/i ZxRe EsPOEt8/_}jTLE xk>>_o 47߃dDӵO%6DJQ۴ r1[ <%fkk0-$|se6lg83i5~ j3ox4h 1ÙOz~ |7;x=ܾͪp˒= /`Un-f-=d um#ɴq^/ Ӽ)je+Z:Ox1r l8=+zW]/-56]!O^W[V58krVY]ԑHP%maKz4x~<5u[߈| b5n#8?bx& ]i@M+VkdP[3^{CO xtij ݼaϖDc] k~2UҾ/ͪ¶N6rm2c1Dc(c ~>]&V ?췬[l9\,ٰ.:=h)2yj(?Mv\UM6 ҶdhYVAZ[? 6t b^sD3duXԯ1~"ׅ+A⸴= `+{o GbI\HHr6vWvvx۽:'|5GCko[hp]o! p*q\k~ɷSsK=Mk~g6mh!s3c;dJMv>_uحuIu3^ Ƴ*`A#P|@=ŝ>O A`mVm$F.f)YVBv憕Wѿ%k{!'Cui0izA6yd{;今hԍtEb.vݧlj>)i)-lkBżIiŬp l65=sҺŔ)kדiR躒hd,Ѥ Z2$lEC@Aiψzv'.t9|D ƭj-qFvQ3j,@V5ov/; gWԟ0Э<8-1.᧷kw"dNk}Y~#x]{hׯo_ .=+k7RIkً֊%o(fm>1U ]~ZomgGk5#-AK9uOإ$OzϷZhV^i utg%'E+M3HD? r<_C|[x$Ï,Dwxiz@м5o=7K~a6Evj`@N&%|cb8JK}~#v{|K[$lmCN% X1Q>[P|4YXge$1D׌|[4?BҼ]( wRE/mL-?h&&o$l9 5 ˏYi9ƽmy R=[rj7V© Zzh_!kfG߳_m<3YkÚ=i+wm,bFљحT0++W7쵫^MeNY㷙c'2ڼ!'qɯ4[Og9-'Dt&SaԔC%-2`8 $-moO#?խy{ +sj c!˼sB?&]QZ>W?/u--Ɖ1oKS~*y9OԼ=>5Fh\_.ZUAJaPjԴ:‹ۧOu[ :K)=bn0m|*euh)_5]=]e1ؐ]N4omWZFyxn%ke3b/wBJ87 g?ׇ|SĻ@R/خ{bP ʼn5MR>m~;ծ~_ERQEQEQEQEQEQEQEQEQEQEQEQEQEQE趮jI讗%m\?I(>_o gև߻Kmᦀ~ծkv21=ɷuMI\U ox+KׯI;5feӮ,qv%p{gLz7g[./SƮuᛜ+5b#fm9 {EżWpI ȥ^92=A J[|;zniѬo'w" hWkWWVZ=ōx-\)*=2p3^֯Yw6,ԵOk7Eu*|~Xc%.~Rtuk X k8M.iխFd˖@`_RZ]NJ32$VR(FG#ݲŷaCkkcZβu=bXXe6c AHl _1-7sYѠmJI`wIkoA8GUw7{K|z𦡭Ю[Ew>Nw72 IP]g>|\4_J? xŸg)b46d& XpF=H5EozL=T/umSSnn˺>ΨF Qr75Fzǃu4Ю_CFw}sO_ͣJgN?寢կdzB).,VZ`1JYA݂h$T>3;ϋ|I|&|3oxgTVhS:$3堍J6A5eɬvV6i&3zgx}vE{/_gsS_xW֮m{XRMy6$9Hp(ThsRI|πpV[UӵF[IxIpYN9#z zϊ}'KCYP֭a:nsy,E,Hǀ&7LWtYhN67zӢ-+t92cR46$kʮf?^x xe/ :}%ߕl `7^:{o mc\~c`Lv|ɘjy-/?סw=Q𾏦m}OM8neQ.JD{n`Z_-^{^->V _Ú!cDw !X,g//մFӦ]Pf/‘F̎+s_PO|F,4n.t^XGpM+Ik-8&kZΦ_+>.> ߫y#x###1!Oɹ9Uo|KZURNC_peN3*frI,[jOtYw0֟> ?M=>Ct+ЬoG"\ ,!Ao5Tm/?["-ֺ%j}/Ÿ]մ!kOϟ ̊p9#>xF,-gYMg0#dG^Ģ}?g~+qj~K6JCGsJ/8Ni Z~4!ݞ6uMj(Š((((((((((((( Oȗ>EsPOEt8/_}jTLE xk>>_o 4՟ Ӽi_ MTF)_)Noe9Wyy9~zX R[XDc'l$&xcQ]wOyΏ'ѕ;3V*3Sxs xz%bF21uWflp UGDVuW?27 úžcM f] ߲F*Q&֬f{;{縁KH#9J|7'!MHC-pn"/rlě?d_oukO i^fW.u)C`a yg7F>bprfXM7zcs8jrsܤq FT_Nq4%\Ks5՞ykq I,S+HFBӮ?f6۷cKy, 񠁢@@yp8sԒTnߏ o~Қ/]oN4}J]OL[[fز !mfV* Ԋgcº7/xw3w3s2130ths\?Lx{OڕpEqqw3v(EH ~_>^?ćUּs>Yx:ys B2sU vb|GH"cP]I"K(c>bI5~95.w&s˥P9p"[N_l<gUI?gPfЌ̎Y'pntv%fsn4'|߯,l-lzMEEM8BoBIf2XapIj߅t vJ9lD6 -/窅oCᆩR|amZBYk!ԚZA7,+FY0 eA\b_-q &.ou f]~ $h_ph%l~;%u mT * !r9WǏ|.ޭxCnh-ofw4o%mfVfsǠ~>ncqyzf0f+oA^=ωsORke.jv.ֲ vT2F|zo?+KwkH GDм1XA py%3#hј?f+4+YkkW:~6%뵝#,Jλv8sRN$1PLK:0셲3Z^/ ]ZVY("ӟS?Np0Coh>\dM~qQH((((((((((((((%m\?]/?Kqڹ'QEHh|=ϭw_M^ Xa g)ڥ2O.8E5ᑠh[ˈIm4lRTl6[^U-]C{yg=̫  $`|k7@[_x:(0ԭfv%'Bk+}J{KRt1*F#" m. ;8#QCDP0B[z_&xJ OKK4Ȑ6.fxAbCnu_3ھk8&kKkˈ KIcLau#&Qy~>1iY|l_J]:Y^ﵛFX4vmn-#XH%񝏊n~ {B7~Q\Kou z}U bq_mjM麍Wq43LTaVb(DPUF}_$_q޿5O h\,E}I-ƚ`e 4aly(1^/O;XOĺt.4<7Vu/y]/bf.%T3_OQU_ly|xo]5Rúugkkr.$ʊ`2($ɃZ]l~ xrxR^kGDMFTUhbQZOXѬ|AKaZC}e.# WBP`ڥ[V?|5=V^/ԆK|C$?fbkI%}NviN &YTmcã%)iag_P@ oP> +x^+oxvPtxvHkU!C001; #4!Ş/)冩QKHDV6:(ՌBʣ䢓v/zvo?=5:_GŖ~ҴkkKY&#gxA 211ai4j(Du[x-n2WXN7 sߪJVw䂊(PQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE趮jI讗%m\?I(>_o gև߻Kmᦀh ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (1EsPOE&袊 {u 5Za/*(((((((((((((((((((((((((z?[W5tWK趮jI}QRr$XY/Ay綰i^GT,r~µT+ Ts+/{H=$FM/ ϖ|%M|߆h?4M/ ϖ|%M|߆hdE?(/ ϖ|%^-7JBz/i|iQ<}/Ƞ o/ ϖ|%^-7J<_?Gi|iQ<}/Ƞ o/ ϖ|%^-7J<_?Gi|iQ<}/Ƞ o/ ϖ|%^-7J<_?Gi|iQ<}/Ƞ o/ ϖ|%^-7J<_?Gi|iQ<}/Ƞ o/ ϖ|%^-7J<_?Gi|iQ<}/Ƞ o/ ϖ|%^-7J<_?Gi|iQ<}/Ƞ o/ ϖ|%^-7J<_?Gi|iQ<}/Ƞ o/ ϖ|%^-7J<_?Gi|iQ<}/Ƞ o/ ϖ|%^-7J<_?Gi|iQ<}/Ƞ o/ ϖ|%^-7J<_?Gi|iQ<}/Ƞ o/ ϖ|%^-7J<_?Gi|iQ<}/Ƞ SiM cNkb+%`N<_?J"*P(QEe\,ߋh&zG56VQG,!-rU2e2N®O+@φuqNsgoAM=5O&ĺf5;DG( 3u;ZNAjjԻeaؓ5|cN-zF.4O:{3E>Q۾1׎~=g]C|g=ޑT%(YyY{[mYZ2GevE$«wmv\Fj].vb=;ωlnotU{0r;V6*f=ASIw+$&OݐNp;DŽD5}Mn-G^tаIû$28Ȯm;'5 BfՖXauhO0; 9-]i9?f'+O>&Z.mcyGiwm+]ƈ!\14/~-z 2'[sxom|ik*ڌqinvZ]RÝ'Lᬺtہkd|-yn'ʸ_7x#:ߏ,c1/jnw{h$(I!Cf<|杕_~2n/z|aRاw6&dvl1[%/5[H/Je[葲8J~SUCiuk5٥O~=+t{okz"xij>YB Ti-W~?ݟH#M&}>kJ6Q^dV GnTrybό4Rlz~} xOR>f ]6gw͠JnQLnQ:ȡʌ<kP:d\51q ًs | A'zk_?_y~1_N{7VGQCs`zx㗇g\iW:f݄Z_ rڸx|?FDo,l rC]|#BFpxr Nt2%׮χx<܆;d_cp%tWj??``6Oj^GS*Ocu[M4cS+;C}~?x.DcFִ[P04  n2]Pc5 u-srd|-䏕^C[ƷK<;Ijntx5k}Z{K+E]0n#^V*m$6N"> | g;|>_>xZQh4y2xCvѲ/m'?4Cj^ccr'}I>v 8T_/WXks#i|[G <c⹝3\e]}0V䐀1lcs5i2]:̶^,Fm1ۉ0\ 9e]7P4m?-K$x8OKK|^ğ {|=ZjZ`ζfsFZ~w5;+Toa 6:~{iwur%10۳WitQEHŠ((((((((((((((( _w@ix=*G^kewv{O33Qm;WI]r1,h`@i{]K"*_cy5?SV{mּ1;vPѼ-&H1 KgcG2,cy[ c;=7W} <B{APvZx}.$V-Ř2ߜWh~xn΃cׁץ7%4)D|?V a''r uGq}oC4g.3@J7|B>?Ы7n:\98ϧZiUE\xF8V-`*D|?\׍x5CsiN6>klwBdX/ _KЬQd"PFlk^oK=yDhžg ?Ox3??Ϗz'(} =#4žg ,OQ= {/YGh=#4X >?УϏz'+C?^ {/YGh|?GOV)нg?^`3B>?ЭS z?G)нggD|?ZBƏS z?E} <B?Ox3?BƋ=yDhžg ?Ox3??Ϗz'(} =#4žg ,OQ= {/YGh=#4X >?УϏz'+C?^ {/YGh|?GOV)нg?^`3B>?ЭS z?G)нggɑ1 yw+S?se4azwyf`ER߅Z_jWVzΡq<#L31MiW)=b~ n+w_jwW$R`7 m}O(~'& vĶ~">.mZk4{h$l#EVc y/xl~XHm7R8*iYM,l~%[BCB(B$P:p)u?MnU:w>6G\>5u-W֚K~,;EIi"V3FsߠoVŧ4Me캗CBUeEހB~П?%;%[/$q/h,+jvv .;KuT2y@.duNX2XZNj|˫jw:֡j=Ε,P"ao:#,fX` u?UaM {/F3fsvQ^'#Z%ҢҤ|=umyZ!>tn̠ް|3l/sZ0CD5 Ğ:Ѵ-BK6.$[iha2ʿ{;F@h'.) zqF̈T6p2=W6E_~ǀ> 7n~YxwzM佲,$m[ЉaG@j:t^#<y-mugLmv^8bCph'.Z: _[ '7'>ƥ_ u- OZ۔ 'pI ol>x#Z+Evtilu2רiGVH }x}kM<KiuC*-HY`PAG΍φךn/5VPaq3šw~WO M药Ǥ7 ,V!Y4M=k@7ihu3of`W-$D[ILʯ _4th'. hn滏שu0Uu{p ۲qҒRN/gV)>Vϙ&_Ŀ|.|~}:'mSan$#$W~d29]KNׄ[uk,CBcmDAh#xh'.Z: D"o+~#|}gu<  zɱnejcW)Ct[|[ %XLcO xU qGRܜ?h'.Z: O+K>=Maqm z}m]9Xf 56it{ ^*?\V* &;kdR9D.1kZ: B~t_i]SGڗk x_M$2himKd'$x`>'x|=lVVS`ym5 ZXu${i` fM𥤶gCzR`jEy$>q=F1j#VM:@LpFҶ:A'UD}bDWv=## u>Gj#Vt/'?n}|Z>u>GC_7Lm^%hTRRy7=3gW_?jk;3TY=pFEMH j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#V]ϑW( j#Vy|Ilb_oQ@Q@~*?o׍QwoH> ּtԖ [Ga2gZH؊LN_oVorOK^ZKm=k>(6bw_y_QCjѫ̳K0'q5o,ƥ;V4++KGkhi-L]_ˬ*&:d|Ș<؇oPF*[x6Wq[$ $ 8S4@Ӊ h^1y|O7M5R]"h$k7$vEE{Nl;?\O9Fe]9`kTMŰZ#&X!}Io=8%|ykz-^{6qwxњ(Q@*tе=g\ѿe[tHc˷(;~v5ВqMuwyԶZ[f.g8º{q]c^G_H:mCD`}I'wW|IVzF/nt}Upb것 ($qgMG<'-gDѼ%o7wsf"IWl#$dz8ͯ~የQ_xn6"BDpŸ[7Z爵Iob\Џ&@m˕'~d=I)=[~[~N(-wo &h_i @\*E$ϔѳ NV8z?Cͺ]_ >o /\pAsW+6MKAּsmn`Mw%͡Gd* n5Z_K ֥SK]kNu,%` 3l~v W:&xM%#Ϙomĥa ߓ8?ACyOѯ$URB(p U`9'6Ck:]oiB-今HKD\h*/ MW%{;Z&e:זvz"C ƶ_<%''5:Ԗ ,hv6$봿6W]J?ej7:@-udlA~Č7HxOp4g!o:9p1p\>*A㯀:m,ex-vIr};WWMd-%> z/{ do MgPxlHῄZox-CQӮK˹VK.^F`el=3?վ-kT- ?^)lH .є<յh[_~8[?ڦmW 5ڽV(gvRާf?]*]7VP{}F%`h}o Y[?5#Kx4yM=Q @p ㏆\hƃ,kyYʁdXJ #CS>=꺦˿ ehfvZH 'c~!?<{̖Jme1KvfNh4]yWjzvu -M+Uٙm1ćzԇ$8[%-SDmmn-bK6KgخdH  r/ڞ/J.5Cdzxf-\qz(mV'+|C/t߼Ϟ3qS+Ws<}>*] xyŭn??=:T!яxz4t;=2k->y/$tʱxXYd rN/f=* \h^(ڄVrؑ #9hb MFkf>:-c,Eƪ|LYQCle*Քo!emkϊ3 .#l[~٘"m39| np3^^ma3JӼSյV&T2ݤflaP*9$I軗 (Q@Q@Q@Q@Q@Q@Q@Q@Q@ۥݼJ7G"aD|=^ښ ]ɈgyG*?7?3 O?_ѩߛo5]7phs s?hͷĞ%ԥ1ۀ"0#]7phs Y"AE >g?7?*phs \g?7?*phs \g?7?*phs \g?7?*phs \g?7?*phs \g?7?*phs \g?7?*phs \g?7?*phs \g?7?*phs \g?7?*phs \g?7?*phs \g?7?*phs \gB",QEW>/:ettx%g%yZS'Z+5(4#Y"d]ZpH\d~t> 08ag~-sj\k=Z9(wN8LXB#"(,g\?ooU7iU1*4n pM.i<;}gҵ^NZo<7C|e=W?[j:l,mD=02жT(z ) :&Il";IYw46K|,<(/<]o:Ŭfno\ֺ/;h-V/ů?^_xk/u1Aa'SKiZP6_Kc>Qָ[A~%x)}Aa* F]9RVjf5|l~+jZ֩:פu K~@#P᷉u?lk3kOM]<1fh֦:S#.'x<Ж?6F޺y~N<$7r:YA@V-3('WCkwu'P,&e)ko*~ |4GuZn-]$yȆ@ѳ cϨ==9-4/adT.4BFi3q H'jք_|=o[~#t? -ijm9Oɸ)F> ־'xOBl hAI1q 珻?~ko~"-3ݼy<-=x<\j>pw<nM5(`E*ș+6r/m;f_ j𶭬_|Ckզ1$pst3öh./]%#_*|S\ L`ִb{AO/?,)MΓ&X2#a׊9&^]hٖ)q_MeɥX-8$0{TGWT_^ Kvtn XN}wW'/xWTaΥbUIy![w"~8i^*յ6 .Se"ܙh] ] 85wo$@7B*[u#9݌`sޒ__}z4${hŵ4nMvk*Տ6m5n񵄚+8du1ݕ;3oZis^_+PUc'*y>j΋<3Ρ=V&"g')9嬜pXcn˚_"nol=[h_)'ڧ?ep˻nl椼-;ߊ4[]AͨBұJ[k#|]k^-DG6{Oy-b[ l\/x[]+y[ˤ_Y.Pr2DXp@s2_7uo(x7LkVUč 7P:2+XȪ'h j>-.`{ h3X?½^ZxQŗ olI)'ՑXFTg;x8 kD=DojzV]V,+ Ypxu'Ɓo =2C4̋4C"[x,OvCŠ(0((((((((((((sZ/gVҬ//}o"g41 Hm~(>67ag#8$TE 0 *=[ߥ ??/P*=[ߥ ??/P*=[ߥ ??/P*=[ߥ ??/P*=[ߥ ??/P*=[ߥ ??/P*=[ߥ ??/P*=[ߥ ??/P*=[ߥ ??/P*=[ߥ ??/P*=[ߥ ??/P?t%=7^ķ#[\\ǰ+oG=[ߥ M+[XPG DGUGAS?{Kcϕ_*=[ߥ ??/RO|m~(O|m~(O|m~(O|m~(~c~4*ޢ~︨?I +'c~4*ޢX'I=xC'OۻvqK@h#{$GP^*Oc~4*ޢ~︨?I +'t\Xۅ#@h((((((((((((((((((+;NSd%]me 5Y#?_f]9eGwY, <1urpF<5{k~to+R[m.DFXGi۝*l7mo,"Pu:>񖒺X]ܧ1cھl<sw?jx=;YӴ!8UI%[vѲE<`L//g{mI6miϧm)x]vV6C.Fˀ|_r)~$ѴS_OkݭD53B1+;âwׯ^O ^ wֺ+5oAWelwutudѼ!a-jqBeؽ[Dr0N:Vg!mm.W`D6}^\z~3^% |T*8 =+gKld촽*_5t܅8Qr!|8"M}r5[FJό ӗz;kMf7rx|+u;54 29] HœC[kTWBX?!_1K૫;aӖWӴ[^'Ckm!#46ݭj"SjM(((((((((((((((((((^iwsTV8_ o!tXgio& GTXRX&ҥdu=Ay Qu$Q y2Ez]&[+Km6VJ:p?& fk8_k_-io&IFٿ?& fk8c=t+]7m b~dzoJh8ah=t G$#ZlMz=K@k4M/5ף@q 4ֿ[7]R_^kqjqFcsvN7z騠(fotoxx-20.08/images/index-files2.jpg000066400000000000000000001024251362435004500173200ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 330 500 0 C     C   )" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽWc?}'?±Gڽl>|?z>EaOl>|{ӣ[#2+A՘)wd#[?}¦?‘?_ [?mׄl/bmu䌵{,Li? ? #Zj~,yӄ2G*B<]ݸ0^9JNItobZ4xf *'H@$d#^{Fׄu<ڼ./52+OHaʙBەq 0Fmh:?u;dG6 }j}ɿTC?}'?½OڵZ ܘfӠR:)\+<+x;w+O^}R%̟fKly> /K+17em߶?G+>*Zr)Դ"Cma5 2&G,@WG| 𦳮x.ڭZn F ;}I%&$( 8'qzk%I5s8卹1jSxGPȯU> :}xB^oello _mX 0*K w$]x+Mu(xk[5˧Ծ5 J)^AY^?l>| >}] +Mki[x. l ׬lix+#e Ul kV-].Y/0 :y51~m(]78gG+Cx+|o}hx4Y4f3>Z` HG?xjx^5ĺ0G"#|T~:,:9}&C:`OUWW/mk>BsuVgu'9aOl>|?4V𗋯/o FEs˔/HFysjVj5cc?}'?±GڽF(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?›xAAݽNx{oQGo5! 7&7WtUGԚiþ7IKLNYvnR@'БP˩s I1K}ݍn#1^54=f|QF ,j֟kCNQ #! ]zmVo>f-| Xm*8}cHLp*b٨(Ӄ+)JXюi/oLuni DP FA!"A1msVSYH>ƽ_-Q6vF 4ۦesH~6Io6jqsA1^<r?FwbiRφԭJnVZi?Xq{q,SK o#2ƧҊ&9<_Mv| .N4}O;YvG;YvWfMoZ>}k',_F,_F.~{}_?Og5Og5as'֏Z r|8?.r|8?. i>}O;YvG;YvE\I'ֿBܟ?? j?ܟ?? j,Oi>qA]#QqA]#Qf?=}hIПw'Ïw'Ï0GO~ù>şkù>şkYoZךkL^%V$mue8r|8?.r|8?.It'g>Ҿ%Cj-cQ˧_,OOZO%{KiQ0F$LW9UoMlqnV;W}qA]#QqA]#Sn/mtHY-5y,Jc.@T}؊>69|G<{5A)0|52jg,_F,_FpNMƷ.IKiEqa{i"9PaۓXO՝㯎Q&&&]:X<7%vCApI'#}qA]#QqA]#Qg{auw]i.t;X J%H';g4|y}";ZivGs/*Ckw'Ïw'ÏV>0McTc!~a7K #<&5(UA ^U6_Msfsn=w'Ïw'ÏiOO7xw:OdW\[J(2cqҍw|9[} x>A(X$l [,_F,_F˥ͭψ4ϋ~*Ѣ#bt/Z1RXh!.I$9E,tV]6t̺,܊~`Okw'Ïw'Ïu7W|_[55-R-̡T€+/'ֿBܟ?? j?ܟ?? jJ6/}oZ>}k',_F,_Ff?=}hIПw'Ïw'Ï0GO~ù>şkù>şkYoZ>}k',_F,_F.~{}_?Og5Og5as'֮i !xIp>Br|8?.r|8?. 83jav?cY_˦\E=*şkù>şkYπVӠ0O)A<sK;\'t{5,~жs5ձe(,9pMsrֺdׄMh$~y#FȜv$+vZe#dmJJ"N; ia}\A>+|6Լsh:6NY[SA HGR ?e=WJmRTK$4y9UEvwkh>?EkW# |;٥Rڙב\mVrɴ3'j}i^׵\ZlMm™G7$cfե[}{Nk 1=nSofֲ-"DYVUbO+i283Y|.6xH񕌗K 6H.LeKHO%6Kg-K5j?6xn]PF+h%Eq4fo9~ddgTcz|!qm 4,Zf&Q2Hs;(caUF3^CՍ^QEHŠ((((((<_;C`Y/.dU,V4R̀9':ֽsi+Zo Mb[7 ŽH*pF{Nv\@XCj(sjvZ1+r k/h0ž擤xcz:b>r42yMڷ2x )#,es'ߍ>6]~%Z%v񦙧ol(WrF@Vvlj|Kυ4,W“階_ dUJ%1mC110ޑ k}fsUO.u +ۖɉG̙,Bz.>%k%j66:k5Lq?Dvd192ߍĥ{./>8[֚|A;CO}WLێ[dh?*i %dq\;_x~ܺ><~ xB!c0gjJ߷3YAM;/a/KiS:DV:A㙡213IHEC*'} 'AK[\,pGB0w)BH nF5Mfl[[VK^[d*G2;HeFvi =G_xo:o}BA<Ii b."䉋J.j{_of[RAyȷ#9bbp>o<uou/ FP,uje)8 yKO|κ~6_ƺ_/BVŮex;̶沺Ԡʒd#)uo/KiN`u&2De@U;NL[ΛvtUK6|80^twy$1-(Bzٝ5T}%) 6QQpdccS+5n,y=6-f.φ|;&ӿMRDhlR,[9jyue>s< $-I  V1!##׋[~/*$Sx~C}IGXe>qj~A9$ڴ[-SPI-`0E#;,4ݺ[_~((((((C])u}C9}bMOrxHֵg-mT`oDn[~A]˟[|^OcXv2?Z6譤XB',9XKѪ$bdU*)=ϪtzN蚲jVylnYcUьS(t;HJ3V_c@-zWw?bQ OsWľtSAwӞ."`nZDXL0 sZPLWXVa Љ N6D~TxZa% @˞hN>j(*@KhgNUOk#ksZY3[$*.vKaT ½)tXEIV-׭g#uAsfKeioBw#߭zX.m3Okmy[1+?Oakm/Gn'-lx)Cxk= }G`\|/~69"Vgj0k䯃>6?3m o9 iHF!ԁ:~" ׵W3i~ O:jC #+ rOKQR;V|a/ LW| z/PLTԞP/v*쨻q"2J++3JW/ꚽ<ХɐʥѭĊ,'a$z][i-U3Vgj0hm ƾ8m!qKgWt?0[շm>lwrwD0$0\!l as>z~u5xo&r->(ǒdsjq.ex4ғo\m ƏVgj0k~\⿃|Rӗڣ^ s.fUIu$(( ~`+DSm ƏVgj0k9OVgj0hm ƺ(+m35_K4¶?U]]XS? %?[i_񮮊,) LWa/WEm ƏVgj0k¶?UG+m35_K5E?[i_[G5 -A!ϐʦy?3k+ˬncZQEHYyZ|?}74Fcfk;^]Ŕ/#|pC}? ?~αx?#jfdtm6^U.)yohq6qI}^ ד>i:ֹciWE\`I<eN `B<9w–7N]KYҮx5xR%]| ;k$IWmK\_R(EJ$$I!hf\V0ѼgQ|1Ԧx>(\Kl\4'eʈє*s%;-^?qΥ.K}2IηxsH״MS6?#˪D_(}gE(,Tp?/5EGK>څ$$|14|)o zo߇5CR:}tGyLE$T׵|M{8 kvZޡYxӮp]eaY>}?qzw?QE ((((((((+hoggo<^^CE z~aݤ3ztg?3_ }m׭үff{iPv)ZҞeu RW[JGʐQmŶ!Tl:|5H-ΗҴKIp PA.vdW7<#_hk> v@Od*̬] WEAާqk ΥxIͨnć$ȹ[k_}RQ޾\_-< öO5i]8yYBfd VWoo [^]Z5n'H"F7Ƞk=c0xEu_Ksijpx^%3ji5@rpR^H.ߊj+ľ 5]ׅM/5{+ \vlfϕ+ P+v[j|JR'>*[ʖxRJii+S{_.BnTW5_.9ZW/>|%Z[i=93} HEⶩQEQEQEQEQEQEQEQEQE_7x± zyu5CkIET5~A]1լ>[_i:pi[-A1FHO]>Yy5߈_ $O xzgP𖟠]RK)"34r Ch 6N_oߍkGx58kGH׎&-'ٗlJ##̀Ftz |"SJ2Zkw$fikOktTQEIaEPEPEPEPEPEPEP^C[ [/ˤn++h;Fy^Ht{4 0C5#ޏ^_JIfAKG[CQ4/ڇD7=/^1qJ-'6V$o 1L|n'Gck+a1]G}uEV,TI8|@Ÿ͹0 ̞{l#2ALokJr}|GP].xtBͰ dgti<h7PlJQ[h1|WWþyϊ_m~׼ |`m^MH/s#malkjMa2Ka #C`988^=A%Oś-bG׃f .$`^[6̮1yA>xľԼXZ[Znu+)Ak~b/̠8ZK-gOY7>!5A\^Fr{şee/5[x5HC8, AC~Q޹SPĿxQ|GZ_#)p!g[xŰx*nZ[|ՌbeGqǮ*TS[/k'[ok⇃|iMaZi7ƌ2ʌHS؞ 2⯂s^jp0l1 0'M׀,< ]6^#/ 87RK\!g2%.e~x?^hAipXZ&%ev$Ж_V/?>&;W5MFŚ1MBRH 3"3pU- 2-?X~v˜_=x47SN¾#.xZg,fh%0%T51 2 Ѿ$IJSߖaM=uI_=Ǚ PJm[ =S?ïZk+>Ů\AV+$xsoن֡xO_ d-B+j+$ &ؙ0u98W_sQ7tտGv_y6 x_Su:F~l/[3?4)[CI9M2MJi1n/mpA_'uyw92ֶ7SQEQEQEWY_ 65_7x± &QR_t_^|b{O]xbXŽ壎! RHM' //7{;N|.u^mk wqn)ՠM"{F ioߍu.tZC x<)>k֐j/mOsc\0qvbLnae_W=W>ot .4[[?>S wH+ !w¾/4}b饸uC^YIU̅ȅ76T<)k,fmSndeĶ+/+*Ҥ->:j}?Kv.{Ex7~ v]Ο[Kh\y$MA۲8Y|!t+K]%xz[`a $8yW-+_??h7G"[ ƪіe`k3$8_%Wi75$յJ!-\\:m)L#nGoOroPGZ?6ejZD,NGnҴǘ\eРW+LJ5 tnxcg򡄘 ŵ7#o8}>g>]U]^ӎAi0?Z;.vneO,N` rM{/O;iAa\kYIgt\ 2gnF@ Ծxߍtf-=/ulGuo0ID.p -ȯqƵu[ 3W%]2-!v͕Nc$@K ƥQ@Š((((((hciWwˆ{v~g;wUM냷 =<akm/Gn|0C5#ޏ^_J~7o~I]_O偶$qݑ$(B#*0qH;ku=2Z;ccqg1&dg8oȿk_3|3!VmKM1wq 1/@ nA6WNJqsSG4RGH۹n xZIS+$L:a6%og})|N]~G㿇U |=ZCp\i`B" lP\{e|S5/ּiH#i ɠ5nw$o/)@|Lenެȋ}|%ETQEu?xr@ޕihaB8b jYI((((((((FV?!Q.ƿHmi05(&?:Oӟ9xM5w?šg5->mGBm.mI\o>eEu:KTOxs3iiqu ]9|P.e]>SS Z_s׿l:杦g%جd)!yF!glHh;G<%xOzRXm, Yo޼ȹkLXs)s`C-wVHN[kns{3j-7Ƿ:OQY4 jWpy-Z\ v̋3Quk 5ˮ]Ǿ(N|!⛽5;#kiiϝ AKA_w7ßYs{b&}դWQ_^P]y~ꯦi-Q , Y m'Gg|*Ux~ZC5[XHm#IxD/G)8*U own?+7['5k}7o>eoy}-#wM'c.O?υN'Fiռ&-⇄sHr0|W߈m-DuMfú-As&svl/߳ bMzS=B&F>0Yvg[/Z ]6yaq=]㵃hW2\(%O|KGCwj>,jEqe^G] "X$TQďj/sE񭶛y^\`=J!7AT1(|>yxko [cݷn{a:IFUfO/-a9P:FE|}ޏ @D i9$R7pSG}7HI "gAT@8]jK}EܹETQEQEQEQEQE?iү#R7~݆bn᳜g?3_ }m׭үff{iPv= (((((((((((((.ƿHmkkˬncZL J( μǏ^4h]%Dϩ R!Dv,; ?:O_^y]4_xzAլ-=KLA!=ҩho^p޸!5-9|'oV_rޯDxV=/.ucWيBx!A)&ShŬf-Z\*imw \l2xophf񵶢 r˥HfyVA&sیpkw&`e'J{߿wy5̜vK}?KSc;m'MӴ;R7$g ufrylT,k;O2NK/ip,q70`@0=Ÿ࢚^U.rE%E/&8ûI)-v=¼? @9NGJ_Ogط[ ĵyOxc@MO]1hYfy-bQpCn8o'".xUfq_YBnfYKgKT;aOe ^T?@#ˇL"tf \piV͝ġUd \r2A3|eoymYKKD#:&^M-e;H0SLB+maHf/๏\Ѭ#" Q;]^ӵUׂ(Y܉ t#閖)wmg8Z]o/ KZ/((((((]?VEloax^^C}?V>yg? JfAKG[_$?3_ }m׭ҡ(zQAAEPEPEPEPEPEPEPEPEPEPEPEP^]e#w+רחY_ 6QEHY|{ƾ M'~ ^cK=>ILbWk܅@ pv9"? ꯆ~ ͏Tgm=~!jvoz'_]?5\tjڮBPKaro&9&|PKc\~cY躮6ot e!e}HSןs?lk -{ZCqk Ƒ$O6Vh ^-|W4_7?Ͱi%#dvy2#(vm!JhͲɿCNҶr^|A#>2ixR;K]R(o2BJGph̅.W zjGï LKm[)XkS^4ooȃk 0͐IGo~/_lt^A0,f T_jw9Mޡ;UէO ~OY[<-ۼ9y6ĬdJ(!9RNiGK_Kr}.zeQL((((((((KkEjhm.`0'y6x{x CkKl,/'Lc/ҁ3َT,{MKG[^y]F'OC{iR`B)((((((((((((+ˬncZ/kaX֓R(k//󯚾(XNۣxfKV.ˣB_nSnI8V? eaai2b  x5"F-Yo3'۸|I?קyJSJk].;KjjNͦF{r739EK*{/xBŭf hBȬnyRʒ^E{~xo^"N"9aӵ+)R9HɆDܬblW+/o[jB[kiM+3gp vqU+$noǝxgPb>d۴sg~àjRд4-YaкHcϘR3g*ֿm~~~#}>&>hvu귾*ɭU!`&*rVOU&KUmwW>h.eHyK)-"d_m7aa֭-/Rխ&xD[ 2@)^;WZn.VKMF)"̓ly ۳qoTe*~IieݞC~_O?'^毧3MbqE$o pHi2I>g'| |Z[j֯}5q[BJXHC$- d}J?f&iM.5Kk}^;u8a(7ZxA6HUI]JӜc>xZ5?dtՌO1Wh:PO?!5[wxoox3sj/J8yb 61* $.eKK]7Hgi 6]gE@nttoᯇ|SgM3Ugq-յ;$IQR?.v 9#85{ | TΉXETs`Y@ٍ9#pqN:?{kձQco:`T{=xž{j%A{h-^{dhuVNHr7{];_[,d-42iꮋ>~ 4]W ǥjIhK1 2$Z[??|o^sCM.Ş0Xxi-^G5F[F0 | կu+];3S]^TOUdd ex5<(|R@in3s8k0'^{m?tV4k[9 [`GΏ27:|XZʋ ;h-}~]=gcI m@%%kٓƺo~$Ƚ*onV5M60@UA8+V?K𦚷R]squg>gG79cxOJѬT"8b$VPOȳ>Dh%_R_>Oih9h;ywrrFƯ<~'h_ pkYy|弿,I&x^5 -jwN78U ֱW$x:_o۷~mwuD+W _֖*|UUӮ:CY:MZ$ʳBXi@][(i5O;@֝B-$U$Sy2ye2d?hZ-𥖗m: ,|Ms=ޱ%oeByܪY#?Iv)}I +xPRqZm~V[e\UE+YjךddB,)<$R_ڃgq} C\6s[%v-c1#C8!f;FNI'|?Ùo$φltY/`[kBtk :W_|={|+h6d Fvq@8Z6o.+RS ZTb KG[^{JZ_z?z*Lh*(Š((((((((((((FV?!Q.ƿHmi05(&?:;T6< ,`Q<-qYK10~A]~QеO<ӵϲAuL|7 4@ mv[qw>u>_>/þ!|9/xAnQ`Ic'w6cn20P] ůvd,(| 吪(mכCw}YW#P:m6]rPN MKLt__t ryX2UPϏrk6ou]cSG'Եf%mkhr U%XJ8J[/w>Tž%пcGc ^:w5jA ;1RXzՍs)jjkvTO jh&$#2Yp~=Fcu-{n.8ir#UDUQgį|]%ωU x[qnjڍ @ҕ%xdρ[ EiRz_+ P&G2x='߈_c-RHVvСrJA1ql.Ҽ -}wHk h+ſ5Jhj$ֱͲ(>le-#o1X|e}\Oj)5tK۪:Ɲ DBWxFi?˶_@(QEQEQEQEWP7~$1BJ*߉E/Qy[!X` ׭g;JZ_z?z*3BV?B{iR`B)((((((((((((+ˬncZ/kaX֓R(k//>%@/o]]*+{CrI4hdVFp8v_t_\ht jzƢnҢOo"ci){h$\d.OhZOMlAGFmVz;]“FѱF`NGq^'O¿/Emoy˭?P73Z7({DUJA\a*򷣱2:^+?'ԲBj!qq3OiW̐W l5]oZ_M5|}C7׾t;wC)F9F ׼Y$R-ʍK>H3JO/wo"{O>m)ͧ'//D@s&|NgyίUڎ 7u]{Pm)%4QǵB:>5(*$x⺁'D2{VַOm[|[+>W3d nWzW-..bQOpf8!q0 yL1K7t>H7u؏5RzK~"O?ix?~'<3}2xq̩bG/]|HZx]MVN%;HD .ƌKq)~S)&om~'Ag(~D>;6xsURr%1YK-IRɡ8{m/ok RL2"i4Xd_+HBM'}R8mmscvUWhexCD !ޚ w.Fk 8t?P 7Pn$2pkNj5C# /% iE3sL&7.WEUQI?ЇiB?43}[Jo xGx;XXc×B"-& PŽA]C·*u*K Ld K[uivūhޝsڨk:z;z,1>mjAydnP[ɨTTտ \ONkWeþ,?MAΦ7ͩ>iPHX<Jo;rAZx&Da< io+UTP95'~1h1\)G}.k=fF9 vHn|BqA]}ٙgԣ+Hj'V]~~݌c Y'܇ <+ |-Xh󣰀D@NORI'5V~ /PK6Yp\YΓ*>IRpvz0=ox:v7X/i3cW̾CҨ<gža86 Zxe x~/|p~i =ũsmomw]xeROaeӳoM]mVKxBM}t'ִ101rSVwcjC ( ( ( ( ( Ė}X^N_{ xF2)$:d}LuO+дUg?1^׭ҤESQEQEQEQEQEQEQEQEQEQEQEQEWY_ 65_7x± &QRdkPJЃ BC3pk_-$O"ۿbi3{;oj>մ]FY% cyEk#-2s;˳-/75WXuT6;eHWrUp 3?ğaS}[:Kov׺Rij)ӠxiY͒YGH j=RKh8U5HČdc1_38~?9??|I?VE%QAj}3o-^[Y*_ID&Rs+}֟nKk6DZJ1yv1ߐM| O(q'X~rMiM6R SzQeQ[/!}lGtcğaQ O( SlT9OLc?e:qEƛ˽B+6S&NKڳsᇶ5W3?ğaPgڿƻHmm,H:׽?Tu[$PKk&k]R&v#̶u~$O"gՇ'@]L])m[Xg &b=Ls(u֯-`/")9mdvs_38~?9??|I?VEv}C|]aK6D8`mE W{U,0ҘVUUEfc<_|l1iQ@~>6_j?W/ 5ZTPoύ?VeƨUEfc<_|l1iQ@~>6_j?W/ 5ZTPoύ?VeƨUEfc<_|l1iQ@~>6_j?W/ 5ZTPoύ?VeƨUEfc<_|l1iQ@~>6_j?W/ 5ZTPoύ?VeƨUEfc<_|l1iQ@~>6_j?W/ 5ZTPoύ?VeƨUEfc<_|l1iVgftp+a6_j?W/ 5U~ c2Mdt4kQeƪmh\5ջNH*$q@_|l1~>6_j?S/$OgKL@_|l1~>6_j?S/$OgKL@_|l1Mk.skmX\~5uOgKLS7/}wazsn[ѣ,QPQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@s>ڞvχ_طw 3j!۠h)|?M]]$Jd%P=+'ŸMk߁<ωG+!%s%ĪS!O2XdW^"񝇉t^xv]< k(nHuv'Qqwʶ3-li լ5kaƅD.ˏɟoOtk ŢY7XGddZ6m_/'kv$ jv3,wrdBdki Gzܧ8]&wķukdy$[yC܁I'-G&?Ŧُ[.<6f; Ui.92.)]mv{мyggmCC4"wI[69ـMPouZ$>.m,迴=ׇ<*tX/IŤw;BLM|?J0~@=MfPhi:|AX[hdHe CGIm5-4BvP kPjvV\&r*7>虜jNOy{cxYֵ mVSμT)"$KH gz?׵I=`_5N!k訢(((((((((((((((((((((l:=W bZ8V=3mhjg;o1]µ?3[vtP;2-OR*lh@DQs/|%'q}yF L 0u`ps8(jg;o1G+SC>3[O m+8V}oy#K$m=)v|Z((fotoxx-20.08/images/inside-out.jpg000066400000000000000000000630041362435004500171060ustar00rootroot00000000000000JFIFHHExifMM*V^(if%HH02310100Fotoxx:trim/rotate|NE0Photoshop 3.08BIMfotoxx,  1000 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?S((((((((((((XkVg$Z^Ha2lIU\W~Wxo<|A.%^(fDD7RU_FT*naj ~et(~)o$/%UCxU@rG>1OmF)~&HTTP:VG~VG:o=w~xS*¦geў1z՟5|/cRMıԪ! 2 i5c[UCxT2|ǿ?UK19ۥoOG__:3-Fƣx;Rw} _|boEBo1J_|b.Ro7d 79|ǣ^_''n |{u_)?uus,g?ۖˣ1 zΧ5<b&I&d0O02ROyLM%`O0q~|4NJE'?t~xsW/1I۪c?{ʣTK>1^'HI>Q-?Ăo'ۉ&|gooJkǦV_xR_kǟ7U^"1;f8ۣӣ[Ԓ_O_iw}bBn>&~7󒏇+v7|q9Ne v5U] ڷU^"bwVqGGncR_P.G|_lMį]^8adҸs$is*r |oJjsFFsWF][{C55^'U-k_M)b%X[wu>&DgǏ+\&r:TC- CR>~'6z=Z:M&p?c_<~ھ" ?uu)=z[iWCmCX?|٦.<-`V1Fߚk28Rt{L=GfoSl418)`~YZk[#hmOx|Cv ʖ?o>-x<{j\e?>(]s^zLTz_&3}Ξ*n/dn}Z% [N\r=?1eZ];~.hos g=7kWE[X>ǟgj'ws֧&+G/D_޸_¹|wtF5]1Ukg@$G+09ls׎eg~ǿso7:f?lklYLY˹8`{Q\BsQx+z+H(aEPEPEPEPEPEPEPEPEPEPEP_^&o+n|EAm75"BeDCu!P6_m_>.^֡ĚʈB ` (s+ WfxW#yJ_? r|_IAM1)K_9>+1? j|_GgFRW7Ob3C~uS^SWzx,bj}_"/-$T %?T ]I/}1)کMOy׿gjoiKk1OUU?)˱Szy:[㟶??7ħ?QgM̘?>JSI? Qb3z~&UUS<,+?iSy" |pU?>,*wUK?|ψ_Q#?ZVhzc'c>D >?SU x?>T FRkgmƿfo5#?U)C1iSȵcPg6Ȳ Sï^Cnxf* hq6109bz5k_l(3h#mx?G y UeZ(M~>nv(`,>pnZ8>Fn;դ3SxH״o':sQus#9:_ktz][M.q<E|{gc$gBbWɔ3BpGCv :}M}q*ѯ8 yO'hab!F5} (fXsZN:'Z].lJ?Nx..ҵkVxO2N;׃|ut?g C({alRg=: UkpVż$C¸ύtl\ƣѿVT:5t>W_*KkvUx+SGCjs?E=GީĚ׆]urmm?T5Ik_/M/> Pҥڪժ}c]?d>?';cr?> x~nk'&O~_ouki~WOerbkd?xZ/xKN08oSIϋn?y~_|\u)yo~UQ}v5}O2/_qkGo~ꪲ_iO[Wާ^d_ ~x|S/zIo5_'o~kkŤO5_JVO',+etO1TOQ~?>އymsZo*<[Vη}}UU}O^ac'ڧk?ŨX;Օ 6j? oGʥΡ)Q,Wfz+ZVT$H##?e .7|a2|M6o|󮊸TH蕕wq1Mj*[+.AP\b KX?P6Ҿfң.\}GV 32+ܵ_s(yNq~u+0O#EccEsy8W+8~Btẋmvµ*8T!lENh]̽^{}SU;\[銰64vqdc_WWZ{+<vGU ={cE\ŧJ }"vzOZxj?/5M@iUֈ7#Vk5S9-]He߀!JyǸWÏ|CneܾS]\d #=g_`M;+ z+MRFgEpCFIiww?_ hVGʵ̒R8ٯ7ZnKJPkX+.j},>2x5%F2Z>[^_m^־4-wjE4DAi/&ONj|?e} Pm4e0dp2pt(K./,o=}Ld cUӼ'_G:m+X  9K5Rͯ_ل)ҤZ:Fw6E7lCƞFKi*?{9܌FONAu]??Z~ΉEcsg :z~ ͪ=Ưium5C/d;uyiF6'ck=¤0$c0R\D*U۔'}"WMV ԩ^Po)&z;hXMV[WeԖw1cY䃂LC#sEgaJGל^'1}V3P/H1h-r1pkskkKDS< mޫs]YT"$٫qTT9+5ȫo}Gqŗ'V+~\N{W7 X]'msGb=Q+ X"5 oHO&'g,TʾAvDŽ>ZO"OV!_ }Ormi)/>aZ//rqU)Ki]ͥTp7k i[I8Ч`4W3ӿl wRTxZ]O#<O?ih|uOWY&o{GoǏ+lju.%pl?66~Gګa\Ujo{ʏ{ʋ͟VσtV?p-$/a[]-v܂qEql?u=ǃF;; M^y%8Hd`TVm 독~&/i7*{ 2e(fٕXfcNWڬټ9|׮k\Tc[xl15-.m6H0e p90xzށWm񵽓+=lPCq BZ{3Ʀ7m1%A$ZZԐLNGC j~:<Ro [W #f$rSZɰ\>i |E[KIXd8$0q6soxMЯԬT-*G9zOM7E&JӴ]O):^Q@pv^YP|/tk["*RXz?#gKCE֟m6j1\M4[VC yf_+`FfiK=Z:fTK*Ih}{鏛~R[3&k!g@/xI$Үf8>imX1dU\|Qmmc.xEK棤9F E\WegYK4b*R̀r3v5V9[j_jW2"ZD^k\t }j:[&KH湳(&! cnݫмFX-Kfb[fh# =:G;˫d E(.rIv AUuӴK3>G''sǎ>i~wsS [pN[=.FvhnH`7H  ^|IgwZ.9)9<ŁXaI$X=Sմ\W>U#zwr̄p0k_&\o}xq> ^ũi Lj Qc*\@8}^) /(zBڨ^Ou{s^%KlkM/Fj I^}cm_eRgg?0GܟѽWTٿO}̜?ڡcd~'J5kd/T )HqԿ/ϽI#Qܟ>x3oܿ/UU'KbCd^'̟/TOƯ/xRx'o?k̟/Ͻ/~xj&|I/rxZT?ڡC/kSzy?'⓫W^/T|;%)q}Rxg3^jiMMWkܞު N]qkSzy;'y!_%>|8'q'N5s7KIo>'MuQ}~~?Mu\oo{Uڡyz[ }5GDc|5'⑻:Ư_ejm3ĆFܿIg>l꺯{ޮcYf?Epc>d ojtU x[7N5_CKg g&C/r ͝WUܾZҼc2O2'şWՊHkH8#i#ßd^IR> 5Gq_Y;ꚡTIL'4u\ax@'pzo>R1E̯wµ:^қOmIt^p?@+55n/?i ;Bxn(*~$7)Kuq袳ʳaʥd%?-þ|G;maͶܜF6ǂ3VÞ$4q5$?h;oA=;(|h7iiDvʽ;u+ ZhTQn(n#'iᗒxR{2RUIvpnvoCj%J0SM*5ѣԦ4|3'ٵ L| YR0O#qmiZdW\G[]E͑H^' ?],ES n4P͞J6ߐw ֩_[_1~jO,Ym(H$2l9x8Fmm:i]ړc$vIr4num qX K[Ou@3iqY6z]&OkEl7V$ `TĥF,N:im\]Yֶ32 xE8 [C$V5Pn*>ǿSW%nnZֵDҴ4m=RNz qn&5pKd)8VgxHmV5y Urwp22p*}btiәoPx:"1ge :>_Sz~[a 08 I>Q~˥y-]ީj[&!E7*oe-EkrwvOq<9[߉5;!]BF)kX*9$c#h>0An&$uwY 9B????o švgmFI)]QbKNjzΟrʰF3,aocw.~{ú>$s(?N'c?[o{NtzIŚ(D>w?[?.shk--Gk:ΟXHBA2g jsO> 77$OwGĞ >i_ο@ĺ;8dR.~'/4ktԞ]^$?㟛sҗ(\t|Ic'''.nF}Wz; ]K,\^t|I/MytI5 >s_2M4Qww8UP2I=X׆fnCk؁8obcz{'Oĕ91G;O/7?bomu{/hi0)>̪Loت ~-c5 jש4%OJͱҟ(\s':_7b5 .n!߆%smoxJѧQ{ /8C-R9߆.mizM{}{?8Q;ú>$zJ5 asp?}xHiD;PW,U} `xj5K1&YKq@0`O[ \${ A:k]KtfӖ=jH\G?tŹr0W=iߏ1 _zN2&+`3m8ir9اභ+fv:Է&h @ tSݱ9Ǘ_+Lj V@ZB` ׾k:_G{|P\85̄3KO(i\aaT'yc7Oo7=ޗ¿C ^O?e?|3?ڶ/^_z7/gsOڇM/=޴SQWt`|'d?C P̟> H/RxC >> _j/?ϽUZ?ڣGlS]yx'/?c̟' ]S?/2xԾoj'IK?\ۺMCx՚Tv_c 9~`ؿ#d> icyޗ_Jo_^d>'\֟7ڈxӾ|F7n}~;/Yv`#Td?SX'o?c=OK~Mڏ=~?gt\pޮOG 0WGzy6 5c?' x GS/2K1OCS|4-u^hm?foGxO]jjOf`?;T,|{djlEbLp~Ɓs6ix}_X}e#^$Nn,~լg*a潼cJHZ>T|Fֳ_`I|?c*M.$s]g<{hvSd$={ǂu׼ e-[xyScdqboU?Xyp1$ӹ˟gP1ӬVWW.cr\2n?xWIφҼ?esns۫}kp26.J[޵IfS= ʶڍ2ϫ(~0̧9n&1i?sM^_3r%'ħʪdni'[[QXt)bb)ߕoѻYI6xɩ BMsݲ1 $XGh8#(|i8'C?SZOVC]<+`e~2:*q}/{NfɩUYܥsae_ ^Oج 1KFxѤN9 'MVm}sn4(dK7~l )݌F WSqc=uik{wLnۆ~lVtdч'&,c! X!+ӲItBNe%&SI[mi~s⏋l30[F4Sqzwv=:$nw1 wzׁ&xL&Ke*蔎Azb7uKFڋ߶>puw:-)bps5gڝY"$)փ58C{_6|O^i,xM5җΔFN\ Uൻ]<؃<n?xN-}9&o6 25{՟_q e}_u_lۃm3ră*JD#R+Q^WH/L󸆴kug }_h:xMԝn.,-FexumцGZ Mɭx?W_^=hrI7mĪȹer: TjZ\$Jfn\3T?,{]ۏhZ;t>q+3G'ܺe_iII`8yJdFvѵ⅗j% 8,wI1U5*u$_jjYckC(/5 WHx&x.<`A*x#CwV|n~:2xSw^jFBm4꘲'s,$-Ϩ~:$e[)N1]@qI 5߃"$ּM[%׈zm#|oKmP[ ףjvj}+37';[e(s+fmۼ%~oA\ O~׼'ig /-Afm${vV˯9ǚjg>j|w*OayN?5k.5}7L`o(-oe;+F[mzONjf~Oiw6raI, IM!2]2G*NFOYV~jxږ_W֛ :'Z>oqom{xer]^S4R' 1iZu'YԞE{h"DFpv<⾋:dqt_?dҼ!ߊu%ҵM>--) Db zd׍E?+#Qվ(k"е(wGSe,wqDۂ;e\ڶ\@@e9OWsi-sG$OX/'`iAAdIr2 |GO⟆<+X߉<.¯]M>Y~ ?v2B#@qMW?ld_ΎI)+|??x^[KVN.F m2?.H <3TPH,}=4A#n4zmΗ% ̊%XK{tkղ~[_ӚO[/t^?Zw-,^ 6_gg.f)tաBt*#+ rTpN1<\:exCf9lΩ,lѲ8|2Ak/'۞:WϞx~;IiSxᾕ:wə0!e(2 >oVZ.{.[TRM.K-`$[ X'U9LEZv~2{[Vg~4-cx;g.[Vm<"PdmHOsV|n\ެw^!B205%oɃ_:]KY%׈o]5̄czrӫ xaaT'yxgyo[oz<-=jxٿF59j?=ޏ z^oj׻:Tk_ MpyOZֱ̋ >[Ͻ/?g`PKڥRxGt&}{V{7s?"?}{RVSke?1ڿS]yxy??үT}'n.?z߳5^̟տ}'m]~>Z]Zk[/Yvc|u?ߧXW-ޗiM}zj-_ޗiM-_թO~r|U?ߧïY7_n֏ί3u{[>zeq9i^}}թ_yz,u˿5Eyuta?/m[T,uxol7W$3?c%嵟ou9ѭb -4vn͑lg1_A⏊|;c R;>-V ẄE6í}L3:P#[\biTj q5Wkf^gú|ZmvZloMwbĜ*F OXq椰PgY|EX/h?fIJӒ~f''-BK֢.PI<8T1PūmOmWX?#@ ?3V|-^/[oUh5Xjğ|`toN,M ƄDW _QZ7*qR9W;*5FRSj7.NTnW{ONj+6vӯ[2hD%%%UOh!ž6!.y@d}2Zm4o .׈r!Lr瓜d`VwxB׈|XAbVa\c|6=NY$#̾&ۏgӝZMB6ԤԯEj[j ^=;$еA.o-,mFKU@x5ɡ)P^@1T@1WwG&wca`4L.A8G+ o[}7uޥr/=dUּ1.X-moϣ_rq6>t=k_|}VR▇]gR6Wnyf#D Wr W`z+X؜`A~G҃[cq,8=A\+?Vt Ok> Sjwn,WywY$+&A7Rt5Ӭ嶊)t,M#"6tvݛ9ݷz!~kPOB}}#k>1w&z֩bͯ@Xwc@2_p3\GhZmO'oS]cJGwo<Ho(|`*H`v7@qt# PGzQ,7),p;\oeyi'.2ǝIRq8_~ ׊¶yquv\7n `NxBa5W$s#h_ߑ5M{៍O/ZՎit6VdymR_.h; bm8 玕xozƿi >zgb?ƏI?~Gא?]<%AtiY6Un1]j׈?h=x k<=s%ZExaZEA`6>WL+诖MRF^)Wk)z}=*Z\Ʋcw<J;qU R'A$R,/!˜#=E7%??Zz nB6D8K(yCK5G'WGQlG#dX +HKbH<,l!Ͼ{piw-xSƓq^f{L?"hd8d`FჀqHNz_ӓWyDž<cGI줽nuDeF# #2F + 纒/,ܣi4F*F0=/RO_b?ƄQ9mxǠʼӯRZ}֢d^)2J>3>P<{Uxqjzo'0VE14Y99O?MoZ˿_λ]\~u=|~|k޷ZnAp0k?ut8'X|ogy=m{{?n|]|\M-z5/fCSٿF<33^GMgz5/f{Oo?ѫU~|+2W/ ̋n >w]cF{3l{nRxCfQ5/7]J*e?+ۗj}OxlkZ75>Rx#g޼>d&zu'6U~le|u>"6jڷ=Kn?fַoSO4^ V?_=OxUl[/Ynd?ۗS//2/n>{-8u}}O?Ed8cz_E~'MWVbO7Uj{oz,̗'-}2 9Yr|MU\?3_I|.l>x",݈hn>Xh3qpGy~zpz -5+iqYsP}8uay\WOk$FbɅغFj.ݴvj,O^K%EPWG<7&K_^$9xq@}8uj 7㵤-זں(XqHċyO_" ;?-Ƙ##vXSNG9| M8')a2?]M**ߚ3U 2EK>qsOpi~9; `s^|t;\Ecc"KwbxLJhMW WI!4"5XMyߩݜ^I[-ppM)Qk_o4|=i yu%I R[x׉n|;Lb`(EP?vZ;Tx-&[mZ XܛM>P'\\HomWּꙒqQUGH }z|K.|BxԒ~-[wkW٣}/FKĸԦcqpd0@}3޹_ u=d|)0W8*xrOOkд5ݕ'sP 7;tr-FiZ!HVIIy&*(W{h-N\N^<>%={Y[⍎e`V x y'Ԟz 2Y_ϖ [DnӰ}]SR=:'$?t{zz4ƊU@ 8z ~&FI|O/9Ӎ(.XEY%{Ejw"O؇ڇGoOE|v?I5\Vf e:}f_74g]ϴۘ93rq$| `}j Y|QכM~+xY"4FʥdW28㯧,%G 1+A{,> i[,y}aSEXMoVO<񧊤ѭbZK=`0J!-:ΞXt5/:ίMD;MIgwbS-f#)w94R#q/)KMPNێs6~]wM-gmFO15EO1XoxVīyvSK=vvMa4"J$(\#IgzaSKUB==3G)M,V>zw 6[A[ /E551hO+p@_vg)aI Q(dhbrq]>CMpeQ"_GrAHλSrE_o6u-8'R4)uaٻ'pvW=:^Xxija{sTվ "G-%ȁF 9* &7sGGK/*HkNEkEU$cT$IbR$ڪH"yjzsSO[iٍe}k&1Hc;Ыue9 ׼^oZ<^H˼wcSjcX)U}zPXxGďsWr[5ѳ;l#UŒ+o㛈- K4 7D7 k ݶn?I£C/ZQ 3/:?‹MS~kk0xB؜j>=OgxJRƣM>y7+r3` >\ׯ¤~u G@X]ρOҭ|/\,nĖ޵JE0P.v RW5 {OĆuK@ʉF6&$}ZG_vV,[Yu:v=A_>:IH 㹑~xr u62־dcY]ek]1p\; X5uJ[_TpNI*s;^nي[w?Wb>+?w/~F_~(&D'°.H /cjj2ɢ_G^JnYK8_OJo¾lLuDVxq F^~Wj>kW߱Ƶo+e/㍮ewp.pA#umzIKk;8<䊪V }X,uDwRx?qyNƗ֮IGC?5Z֮D3:7rE/cfWyKP4ΠǞEULm/e?a`1"W:b֟r~Ɨq7/}Mj8m5)}2KgQyx8֬iK2[:>:-Ge?`_Q7iTߗbl~Z>~Whr}}Mjx 8֬3ws%yu)h ̖νU\u'B=>Ǘ.ԋ_ݿxm1\yIq܇sZ5)Ϣ_A[l5 h/MUacq"/+?$t4|6>߻ҵ~~͔m<k6v.4;1-*~لz}_ -T_P7eRz|>dfy༳ˑ[{ģo5lC5WWo,o#Wx_ęJHxHVV]7vW[_y ΡKar=B?: ߋ%QԦt?Bֽ'AMOx6wtN?5a2 [:Uڧ5v1hU]_ںBI;Ob+7K=;Rc[H߉0hwQ/O;Ѣ&vNH'[}{/rE6~3}k.3pR,Ȋg.nn5iE_r?׳w|>x>oLV}PEr/ǝkr[*CAXW>S*m];5ՂJ_~)g좕uY_U';uJ6֫eUW˧0f#ElN 7t_i_7 |W7ޚM靿E?AZ#7O[ב,}$,OMW8D!R8WT` r?*C|L|'~,?+ۻ=s|oE=~ǟ 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, 1000 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?O_(n渕#ˤXNDV8sGڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wz-'L? fSGeIVd4ҸKGoxcv'Tl>|o3RP-<N"ȡ*ilti'|_7"|5e9e -.c|+lONjWMg'?DW'{[~63aOl>|g>s[ŷ./t}|Y"Kbb꜍k^?notT74;Ր?pU@ g]Vl>|7Ĺ  ~6k>}k\Fmneiv9! }/|y>[?ĺ5]CUy2%j k۷EaOl>|{z, dRY.@g.m>|PB=TcZ JvGuI} H>k؛Ig#gw"?/8NGǨfxk RZMw\<}ڄ%!=}FM'Hv(BzZk6t}3UN[-g2FNxAs{Kkwi%=AGКXWoowoS:+_ijwA\3iw1esx W9PcDb:tlsַŢ_\ա mJW>p@BvMyTpT}W{Y>Z\RçefG)Xk]i@In!Rr> T^?.?d睬gFwr6 !ao"YYP2{Pm(m;l)֣>hN 3]5}|gᶹ\CZx󞢾;qq 9Lޒ|Sq#.~ ,e`RGP\|cj+i6@}&;NB žb)'|72V>QiiyirI/N횳o qڹm[GTIc7&]34ضd坶#}.j}5>S0W5յ6fǠbn,o.N(eT =귈'MK&IbFS&FJ?*|}iZK*}4M{et}"qR+Iho-RpHdLs@P%qJy%'20+{ mmлs3Ir2`t:KQ82ẒTTFΟ&6[6z ьJD̑I\wǂmn+ g%:X19!TJYE4W2K"CAFZ'Kk7sDj֫Ru8l^~;3a.Ne' Ƨ.V^]a7$,֍nG}yp[/uzv}';3cf|1|A.4v1l1sTܧyVR] >=?cue puξD>ּ[u3ieʹCPW?| YBp7kw{}yQijǵQ`*vK .ab~^) ׵Ndy&۱QA nx-7Ƽ.ҷ[[=CybI*WSA*9:"9t(RX.IXg 'z#5^%ᏁF^ZĐHwf?)`@5៉]:-֗lä3Hb8R_$1k--4J^uW6K3j?<{SSA5Ln#w-nO\d|tەՙZ|Ǭng/BFakaaX$m0 xTcռg<6vHM$>~B3є1FR`}kF> ⺚;"}8@'@ \b|5>U/4[P6y|+Se@=vख,S&;\};?ZI8DD^1Tb vFGsK+/E/o1YYs'Q+/o1G; ^(b0(I{C?/o1E\I}q|=Q{C?.~~}>}E~øm(q|=Qf??>}ElxKǁ5}kA<ۑȬQ_uøm(q|=P[ F|o?9hj7 -sz]!(f\1Gf_(:|E#IXy>_møm(q|=RZw_q}_x=-ss<"ݭaS T WĿ Úz{ưܙ,D7d{۟/1x?_bGikkx}sM$]:FV -ZOqQ?< :p^N3%m($%XH*owQ/1xZ/}χK?Z˪YWSy6vN~R]/_j/o1G; ^(b"U]3 yrlϋ7ֲCahl65Ĭ (dsl, z; ^(bCv)G xE5g-XZ2T%9Gz-ykfbvlnudS־3ZK|,\tb-я4@g"cʚٚ>q箣Y3] {+6h&sPg^ڠvL%ioU}Y!{)eS(\d FktUx35E"A՝%aM`#APY˪isۉVŔn%@?X|G6x;g<eM߈5CMwQ 'g#sץ)AfM`j$tI+m2Xぢz䟔7O$6̲ +-Uv[@\CmKRK8|cO㍹WuٟX񭭥Ɠ1-Ǔs#lf!xAO5v֫:1]N:oc2Wj|=?Ne5]sKS`bz]w/z±]Ym=ȉws0@#  ۩xYc_U]8iזSzurlYyAUy|#h>i{vF˘#i $\U5&2FE UVQq8p@8=wii+.b#f~e#}:JqD>8ifa(aeX{ר_Gӥy/ ȈUGN>Y7Gz__8UC:̦Vlp YbYOH%AGLzg?tiGZf4O!?inR4I2J[ [7 #ahF=ƺχZvBA`W 3 5&BI)+K>7Atɧ/[l?x`q_&x^TjnAƾ2k+:L6E-`)vgvI;i <{d87nYyf3-H벝OsCEV}K@F#׀PX~ id714OLVmd/n2wtoj4-:F]bd:ҋ]7i_ u? ٮ Mp9< 75}Vmqnʛ.FjR?5ˍzl(v!QX{֖-i Kq, (`y >-Kr-_Z}kg>> L -#8q [sY[=~{+ke$K|)o w_WHxt𖣬5voI_;lV$Em|:@5 kn]]~gшbەB'=3\߈~8i~CvXyQT\mc Q" >$>,עhLVes6+ |~#4+LlQ,- 9E?x3Ow-x'+J̬H]+{0Vڹ3Ew >0jO2mΏ=ս6q b2eyň> ^bZI!2[U \pi%h>(׶w$7T!ny,HS <7ZfmqiRh+$$Kg-$z|[w _喛{WEҭm X#)RmoPqAbIo*l:mnJzhlR++XP;U6OI,{gJkYѼAj%ܲ*Nbsç]}|# t^:ȗ6.$U]nϖzg> Z'ïkS}V7 ۆF>-Ȼ84}jrִڿ""v[[VL#p;FC_fOZm:4fPpXKhf)-ye:<28<[O+<ۋm^1F ATr[c sI{HoY^'cjֺJ ,C$dsºj mw/ ȆIcnb8vy V5>1aI{ž&HE1 [;83ޭ+Y1^NC;A99%U-:}̖P%D$ڥA8sҼ;ǁ|4VĭɲO$_`e;Ctq+ʴ kvq 4WEeiyVy@ y?4;_Mt=\I]fBmZ9g).B0X[>ImZ~6Rn-2;dW~2Aɮim.-s%2Jʖ lxᧉtOxgX<5q\h``"DE0i+]J?mo5qAxC&eKB(1²2#MWnoQ=!\vZ<=؉ lPվxZ^eo6[OkoY캄w3Hxbφ(> _M[iakRo06Յ "N0/_wo]꺦xD'iǥqZGퟋ*WoR}U vD㝥w t4/fOͧ#w([CmwJ%3y5oTacsmǺA~˯(MUn[;PYr4[WTt}֋~c{$K0G3D!el 8}X?2y3KO/V?ƾ\wxS+G;Ÿ1\Mz=~{LW_?Ht Y?!%gkww?b.û)C tG`ϵ>KO/V?ƾ\wxS+G;Ÿ1\Mz=~{LW_?Ht Y?!%gkww?b.û)C tG`ϵ>KO/V?ƾ\wxS+G;Ÿ1\Mz=~{LW_SX],- (VH1w pǝ;|4p{y }kn? 4ُmku[Yn屵X&BLJL95ɨ;샓sVg#ﮥ,cpG <270! #-B|aka{DY]-یOU_? ea} >OW ~G[?V+o7H0T/b??\o*/m[c9 X #4V"?ҲW?L|Tf3 )ȃ5־]y淎Ǜ4PN jf]Ȫ$ 2nFk&ּIP'|^VZw WשqW*QLu6HQy`cNgd-[Zgr&0}x\Fխ' BNz+gx [xJ<R `} Y$yr߼ڸ $qȮ'ZcPDҾ sl8FtΤ Ե Q;y|4TW&pޜh|AHd7QCdu89WԅRfJ9]I3ltM_tѲK`~l `tkV7R.p;!]|AcL#9rM-,0 a)^V?|灓a[>Y3:fU__޵f8ZzOtF _ E4؆ՓS6y>Qqkr%[X`'' N.j. ťxDďp1W=L4vl"Ir`m-n4ɢul ڹ~uƸkBn*}oRc6#9T)<+o9=P\4^ש1"/KO9 ^xKTq[ԗ,N Q敏ȥ׌-{տ+{]B5BxE-ogm_ޭ!["Q@uZgjQi?+ΖpA c@*v <;kouܯXtM봠;_ݯ%Ο=Xh18d?+74[chn5 ݌'ڢ]  fķ#]HL˻AW57Zڵ+F%,~$UC9W&1{?JOhgW܎$,]j5~X#u̬}=>NNO&H A1ZXĚ|~}i hZS!vrx?6,Ɲf+A%ơb("3$E# GZz-Cz$t--dpH G]¹/ٟ:|Vkb+U.n]XpĆP3sX>;4fƚr˳Pʭ#8Q$)ћbx_[]/%КsùEgAJN'mo!o?uwlqGxn'_zhn>}:׉\xY58t};zls4e孥YWL6Csq5?.ei:mM2k9`|W_NyEGXӵēE&ĪƍX7#9Z~'~߳῏&K_,P1r+*`3]d/ 6h9n>ݩ8G'G PXJMA /SеmRVhlP*똕:fP?4 Xjhd6.mn }ت 0wymߧ޴_3X9>T9C}<"Y<ѬdWR[6$_A g=ge닯:r8)4Y-oaBp2yz{;\jS6wF `m88D^Hlgs=IksnU3os].sJ'o~ E 0-% ︃ӎie|?JfY.hC 3<mE tj[\82‚BXў38hx'_i>]+X̽^*iɴ,VoEC9)ӮՅ:* 5?TuC[~Hݔd43 rZ ֹ~_Z\$*ȱ3a<&U!"d>4[u;Wmw^ $+DY7gNB ՗JY gKɉI= Vd9kEo{o;[Od`0ڻ;+ç@F;3< }}&`$A T*잤8QTA4QE|O|NV;u߈$%nBSk"=ҍ:pk6W׿tgVѬ|6p-h+C.GtoxㆳXI'UWvz o2ެclxÁɻko;~6'Z>Ygw:ķ^'/@1Hfw*B#9$#o;J #Ulqx<;QExkoom|Atm.e[{>ܧގ!ы]֧A^^g^# o/_7 G]٘/OEx5 o.k -]٘/~GQ^# o/_7 GGf7|aW[K&h|M2ha?~{u?/Z??%@_tfc̾OEx5 o.k -]٘/~GQ^# o/_7 GGf7|aW[K&h|M2ha?~{.[jSZ^AլRHeP xcB𞜟midc{ć"g.@T{W! o/GSqk 1y-[Crح`Mil`̓N\:qtoKFjӷfgOK]O[q:u` 4X2A$5֖iiXe}wv I rkͼ1⑦khE{ FQ&'hPMroEa_k>G%w~ݢ/kצR  OVUjoB')ϊCZ[F5PE<T{ҽ>Sj6q42mݴ} GP+7Q5\=ϖEG*ֈOWoC?w|,d˱>,8[F&ݸG'\.MʫWA;qr:4h M4ZH75'~>gO>$OꗗwhNw]HBnim ;DW |;}? n[H[h`V#*rECi#h& !qkkDܷjِWP~ +&W>?uEr> _ r;[mB,? rR͝9ErA!z:9W ~յ+a]i'? -m_c1K'd{0);Tm}q0V"hO-Cp 8d[[tA\dyߌ>-'ȶ!lp`>:OO¾ĸKI]'.ƭV^m}4_U[w$G$ݟ[n|-<83wm`Oc8'ImXF+C̖_ ~z!drԜ+yH{z&ՒN/UQrzʾ,um,RΧ 8`ݽ,F|3)iTFϽzí\]k!<6͌u^8;Qߋ=Jt]"AuV_Oa[7yկ5{o^KdybOmwgoZKtq@t-գ𾇧xz!cK8.vq1˫ݽ\jw +i۟Y$;>~%ѬN4bA;{@Xkb t⪳9'5H<(K Xj f!3 U2S$ `q\ơ2~ PxFGq,OӚ4g]/~({ KM*7ԯ[+w{}r:tuV9oO{pMEoI9HkO5 V 0wrz$r>I V)]7~Gٓ,Mz=ωb*VM7- ʽ {.$p{>GAK 'MN7}.|Ϣ47a 6 FnL*ۊ_ǩHV:}G)iegX0ZQ+3EXZo+QZOwow۞.5k*#lQ|k:ZqmhtHVذ2QIͽRK'y%ysF,}I=j:ZnŮjWDdCi J(&m_{o"[Ny3I\{ ـZRrHEZ}y=G)=7_׿|+ŪS.Am웗^,A8[5> k_ľ#HG?dSQҶ+yiz4tF xpwGʐ8ex&٩X̀"K_2(FF۰>޼zL6Aq{Y*aR;A\߈u]JW72R+u|pw5֫N#IJ =Hyn++YNu,LQn==+r鞼U7Db};O{&D$I))} !OCgDrdqI1m{%wAky\*"ŏcӭnh-PWSЂ8#ޅMtA4<n<?ZoZtZսŨ\[UHwr{ t|5&FO|#jݰ,THhdkyU:2=GQRCyHFE%%?g <{y;%mdF'%dOs^E4-s~*Onf?JHچoYus' czxu4!U'l?x' ?7]7|g~}?út2jmG } 늹ks{&g&Fa,8"dq?x' ?7G3O%^ vפQG3G4!U'l?x' ?7^EϸYo Wt4!U'lzEs>dy3O%^ v WuQ?x' ?7G3O%^ vפQG3G4!U'l?x' ?7^EϸYo WukD@f䴏Mɧ=K{%lfF+1]5dRdRac燥'P_'jZir 88QPsM{i~^I{u$&r0dEDA]D֤m∞ ?A7BQ4 (o пM:?ztu9 qEOSm|uѬ5[Ag~L,) *F:*ֿ8xܞeG_pU~$E"`dzAI=:mԉVTxUQ'Iu0CS½S{Ze֚fFi bO]FWrΚz֑jeŜGxR\\M }Q>FfTԥLW$ݎO+Y-1a-oopm,0}{1_0nbV!MVbsqS޽ q/ƶ:Š *| n؜*\/nևƹZ> ҅1]$Bvnxׇn=Wkp5l\,kQ_7xij^/م0(MPDT@ǧx+WzYt~'cԏS\{KH9|B-l|]ej a7d.sx<}*?kj E+7ʨ6Ìiŝ/Hxm4ueܗG$x'48(DEZҌnM;ޥu;}*$zWܭm5O\@̵f?| 7&6=w]I"J`Rrs I:ڂ WV#"8ң)7.X++0MEۢ$zώ-#N4 ۷Ii̲t?r^#FW1*٦=\/xp֮G*!C 9&kB;HI׻k[Ÿ#wJ¦2cFxmޡ$+#_Cmk)Uzu~FzeRyf =x}]7L#tRL 18RY> ]|M4ܛ{~:֛T7}o)|ͫKy\$8{C;GG6Ejsx1D? @sCFu=Fcwwfv Ev w$#V.ZlJԴT{ndm5y'W;?gg9qp#޼'l{/ x^Y(yySHp#ӂ׳hZd:sx#;F8=bGE=>u&.>`2=kUoʴzc2Pxঃ|:r7YL%|TH o!m>HZlGk[j6yl&o|KqU\bXxƠvQ hоKgS }j^.?u(|3L.Yv ;Fs~t_K֧\I\6S+ӿg3FŤZ?oI,H#b}`jS;h-p81+r=D F /ep gc}=4ϱ􏍚f76:&[ &<1N,8'մEqwc"2bR+uVn$ucE ړ3 |lb8{"^-wz}#Gnn KIG+9ju܆lHtcf$(czWxr o|6,6vD̎LC9Ul$ N*|_𭷄t=?W7Z]/-Θ $&7Rc]K$88S_sE;-O~$JC_Y#g/8i?ȖKgPo(e3iV64q"k2G(8vцv(i5j*ڿӭm,?12UNjK:Tcd? k7|B,׺OiH4}"ojd->Ȉo\Ζ6;\{}FkJQ[}tBϰQz?&v)]xKkki&@)v%?[H[Djf-zrt)WN<AFj;FX[n V#< vUNmOYmlVM<8`2:sߡo :Jz/D6r @;=F%EssÊe6F}/4.q"{J>!>"Σy֖d[[o.<"~5wLuԾX$IQ]CTP6W7*s#uiNoɽw&2Q7cMR,-j{:$d C2̤~&o >g5\?`פV[y WZeBg .\c(3Ի -~R+ Q}}oleM"e'/j)N #Kf2*2m?t' rQvbmkM;ĺ7KC^.Z-;@$2,'aa׼?h>nb"KoVXP*Us>*}46=>ß+m_+V栗xm,o- ʣ'Mnot !yOE_15;@־ hpi2=Xu]nbx`{JOUe*I9b:fER\.x ;sԀQEuE|W[>Gx+^"IkjfHvBv7?2HjʼÏޱ+SկZ1TEV}uiZOOi_ f;T-Iཌ 2;IIƓx ^:~k* ik+&y 6QkEu5tV[8È qD?5>2.:Cxw-m+:(C;.1W}Ϳ؄KMC ksJ3i*G}X@K <_44*aTnMb#fKNӵGMi|5kL\~ӭMH`glP 'i}3@d76r398oR5pmη$ 6Q 5KGr<ٖ%H[ \끛x/@<"Ӽ1Yk;%6겳K}[''yx}|C 7$_%X~8tRcy':F9Pz"YwHFxҮt],nYa`B3`mok:χuYZ4ncS Ǽsq t.zio:m 쩐1v!iL2>؞(X (G2ھkL"yquǴ+)ެ+<M¨8a̦!* !k-B{-J -]pѸ}z׹k6iӷ>bxBr>y?Ֆ.OiH; -ZgZb1<+98Tt ZeE.[IHn]KU^<q*hC^]Km,0|on5)'NooD}L] KSG_ʩW⯇<6Fs#;o?hFj,VǟiM+jw/,e9Ҿ.v[_8ԗ͞/z>F+->O\>/|ZX[`MCGx9.O H oǹ:޺n/$E`HPp1}0tVwyT-Ϣ"|Wӣ񵎯ݢW2"U8p*SKմIm;8ﯷja+r}sLS*m,lROTN 5F&:R5'%Pp޺-&;H}.H6mL=d\}ҌTvG,:*U@\ת|1 y 1w xBRr?mE((Io?$-x|_7I?SW???κD2HEma d,; ~ZjO<>sO Oq@:?msiiP^G(G20 xC_U:w[B̖Y1Ԝv]?yHs@ڪ zޱφt V4Miɏj)>l p{RR-+ݜ[g@f @sGN>sK\-UaȺ)s ZYk>G紂#dD[ɴHs࿅~3I7mZGw$1Q(.$(ĠsNN JѨ|pԴwvmp*Zqa^#q#$9!~O➷~&xWk{dW@ 0 sʒHE,3;zUz?EMA\vwrydjLQ,lW n-x 6 B%q[Ix0|mTb3d֧'v@ڇt5+ka2I|.~V/ %EXs./nWj% a/͡[Qp;Hf\Z1䞛s2.!M;M]崣.vXW ^k ioK!Bsێ.5}>JMC{w0WNB@݀2[qOzRqrN.(,~5eFI\b%9.Xg$ʹx6mM;Өt.00v H+|2k8'wHidcX"(,#-Lu~K,MB[|tS Ls]L7"%C@5-nm~,Jen-^GHV vb WxtߵQ6+C]c$W#`3_YN!706VF0F.~^m7kWxzygIwoce2Ԋ֛Y忸[ ڥmKs8cߋbMI6GƓۺ.2ra&^Hmٯ}AҵA x`h/@C2U }ao<.NXI-hĐFɝa{w3◊n|#^iSǚcilR qmUg1O+/bmeLZ˔Hd%O ӛOk5{/~~=/ ^E障q=q"kk.dh#`ʔn MBs ì{ʾ?[Qo-~]r/Dbfi)0꠰ ȮžkqH$,{ 3?@|@? ڛEԵ-GG՞Q6)R@HFf*snngѿ\myNspq·/@5oll5WdxotHVV A_7gB*r3+"U.I*X~FzgWw>'fkGOB@4|#g,_K"#(>㛏iViBA.mNZ_cˑb4eTE_*?xj֗uEQF.R+,tCp&TZ+i[ݯ_lf2Xizeođ8/ +x 9iPkm>޾{C^4SBV|=5:wq,HR5Yf$d.sڣh>$Sյ-)"^HХ\-9Br7d_gw!7}AR=:4[&gqiՠ ".L.00"RwW (aEPEPZpJʭ_ D01 njSvg?OY/;b.^U;T4 \k7ѠwW)|XҟxMV[熑>~(i'C^&eVwnnV~gRq[]3|Q#蚖6jSqWU9.}k|]%ơ\iޅ3#~Uj-?*kEs}X"[EE,pFq_eE4?X# 2+&]sJWN4KHŷmb8n<)VF~Gաh^) ߆h-d"^Wo]vzJ%%qV~['MCXOU??/j NR (=ΗY{Vi7[y_Mqw^'M[e6~6IۿRI-X㵎gv#{lL tRb*[Mm{:ߛ;m.!F!O+.uW(b?tO֮\Z(v,Ĝۚɤ;O6܀;c^ù<{WgBg.n\~KVI_|U7OEZCo4z\/A'1~xsźCiMͬ@hӎvAۊo4t x1JoF|[9SQ׹W:QRє#;KYfh|E* *} Ұ|3!yn!\p>]"i265fM9Hȷk7|^3@r8";\yKT/C gy ̌V;5sZq!{G_sTPSxsIX5-[tb~P0}O'UƨO`#'քjOOA\ѧԩx}MYKM ̂*;MԿ,E%}Yon-q0O#<כW zu;=OlxStf\gbv=v5y^4LbN>R:+(GA7  J*e XNԀ—(F:|Ewko /!gGAW1jbIjy;6Zr֟M*G\'W$wbr0\Ï _k:5݄np:Tr2 +پ|Sc]JXeƆ-=C!YF2 ճ^+g$a:Vݖ.d |Vk2mr҆ ǖ--Ojz3ku\)g{܃}k G0@-'N{MzꗖnMfNX$ߒ3sV׼nJwi3Ua΍J*+)ͶMMg:<7ghSiݤR.%ϘXeL|\ sҹߑkKlkjīN oY|_>M"mOWԢ['q9[# XsҝZnN[x9n5 }VIܯg, @MŸ ^Ek*JOQVJU]e-ux?YլO[q~蠜ᚧ躆)AgV߈|wk6YyY/˕Ӟ:`U5Ԯcc90&3C`34^I](c淥륋E3'=T X-vjExVu[GVV+Owq3vEg 3{ mk3=j4oFFѰFUԂzwe(SĩDr<7cWv& c#b@%I o{t`[[ *+7zt]J?GҢ-Om=o:.4 o{t`[[ *+7zt]Jkz΁l*ã+Tr)?@) X/f|NͽYZYgbXzw?-Om=o:. o{tX|N- ŭAL6!( )!Ԗ''%kkE]xCS@܇Vᱏֿ$t]OP {>[+wCqxrXr]jv1ɧfYG_y\z/Ý?IXmJoGy39 *d+bAnHԒHͻwaѴmKEoQ9ɯJ)rej}Ɨhuh#.) 8#(V0L~8+?'×Z/ï_<u.Rmzggx㿉m_ζQe /9X4# ^0M_TR+7c¼;C޼nb*6I кeD w˥sO qN`#Rr Ucʞ8&^f -okFDo1AVF89?ֽ_ jH5rl-N?0R`&tŒGrz}JF2~?mm:\^J@u5xÞ +,mc} sORkYX#[vt3?x񓩤4GG k=Yf+kK+U(hܬ(y;u8=gx6\2)dL|WҼ3y,d8/X}M<>S*!Gݎe2IFȕxujPO>F|HZG9gf9cU,= Y%Mrl)g:WWcWk.%'8=k58o2C·!qޯ[h`zYDKpyXDk燉bڕbNN|ծ)RJ\N{lky5ſ7"ǻ&Vrd_NQ@Oȍ/\覯}k"uj^ֻLSE%Ͷ)s;E\kRK;wY&*]X{A^5jΒ*\>gnO$~+b8nxH~ Y=+ aYZEN;GxC]~X_˦yUܰ"L I=+'|+[i>|G[MG޻!p`1W;S,0^ZL*GRO@❦y-o e%؜+¨$\*no_2xju-5XO(B=1;}7| ^RQa$f I+4CQ4aoGk#=vsq>8xXx!Tf 8,pf,gS$kuu#wtbKqu28-9/uD"( bڤȎ`>]w=[~i*4B{g?-܀}FhGͻu#ˈ(ȉ :Vȟ695>;59k ui^E{s0(9 $c^}Iv_/6KT-kU sPpA\[7IUS"C (¯nML)ڽF'tz]F;dl&/b˻onk|YjK:UuOuȧJGUK[O-E$ltE"2J&,XܵwX?T(FX5f-^dOAI$IEt:w_.042?}O{JVܤmm-pA<#K31Mux[HKqk=Ul$\K'"?{ q$ջipȦ9oXw:.>pIighUO j_>=Y6Kkԡ fDV:ncgwbOs_k2$UpO@R = ;ec4&Fj_t.V.W皟U+-"ݔO 5GnntGtAS_k4t[~Ɉ)ۿyךB=qֵMē lIe1])(\t[4S\irkDIQEQQEQEQEQEQEQEQEQEQEWzNx[n#kU-rX|灓a]piy 8iI640|;,ҿiЧ03F7J?  +_g Z֛CϰI!+\U$02*{IsGPӼi>.=F:j[k$Z- (RI.[!;^c p41+w|#š=kZVMB#LsGXVYʿ6>`ǯ{ĺ;tM9Ӓ壵EX5Iq I%K]?[KoJ-ԒݲIbe^?u7kZPcIf'ܓLzWz2j~_ּ-BO>64I!<W' ѳoCu'<1W,tb2L#Eo4/W~*x;Uյ=%eӬ/SF X&,;'bDD ΃z倮g¾"5sPKTޗr4}#Y!X$es_i4_nד?!cB\Ec p֕ww:/ [KHbտ{kԆ)n8])lmk__]>qm tV_ "k(n^$.*ɡj@ݛG:DŽ/|' i*\66-QPAS{"?Ҿtw^,r_H#p\@27!k| N~U}-"҈`>in3KyWHsS dv)$RYHZId$j3Gk!1s黺*p4prDI'=:!BF2Z1 IʊNbڨq2K1,5"6dߎ)dوɀc'N%*\#Xw7OSP^Bב䞙!\oK}qZ0ͧ]#:`w~S" W[?J?e??tKٲ@r6\C5 HU-0o.7Zk8aԴMΝu$#)Wq{EHϨEUk!>)"6t~}% fTRO#༚y1 J&`Z<=_\ͫr\2;sֺo|D\'G@`*o xQXrP04T,k9Nm9Y?]z5ʴ[q02B-t%#,iքA jQǩ.n{؟DO],t.5?1n \Gzn6hY7m wi%!ӐaD:l6p#npp=1\Lv:Yiodʺy| C(3^՛=;D{)s`ħ,KP:n՛\5XMurjnm./\lFR 㟘-涷yKKOMcj _2Tә>麐sDXKfgf15(B-Yʦuf_}b+nF KVvTQ 0N~n2qIYwH͝Gw##" Gz8=kK8±)J 6Ugn=GK{գ6*+s~A GUZ~|fO GO!DZxDo6E5/ ֿȍ/覯IQE(yi(4c4}hb9c|]?xGDmf[Y/!8\ׂ3^Łc5# RG8~jw km25&\H < AfaRAA߅u%$s+[Y茗od#\ӥ7{ޯurtK%e>݆PpT,z#Ҹ?2wz0 [ iahv%"ִ|c.GCO xkNmRK?̸T\3y-Cvzmw/H'+Jq~ҢolO(_ ͬ_nVנ[ᆁ%ƿ}]>&EVa4 >D<"+KeӮnlfH0 [8PɐV5 U]Tmo|uoJPc𦃡[yֶi-߈~{mղ72ͅT~kz+V95| ޤW&\SHv)߃ ,=ݹMþ:b v!c5̪+S՛8$ׅܿ$jH8%ݷrFm9f>*,DFهPݱ-P?zsKwMUsäj}H?k_U颊r~g(r~g*@ʢ_ ?_ ʢ_ ?_ ʢ_ ?_ ʢ_ ?_ ʢ_ ?_ ʢ_ ?_ ʢ_ ?_ ʢ_ ?_ ʢ_ ?_ ʯ<kڴZ6z2 ȞR!#k?_ ?_ KoJC%΍k!2@F}TOl$M*Pcԏc^QQ-- BiQ R"1q:>R]c7R5\~/\~/-@&["2;"R0Htq:?#mxnA2"˟˟d6kţu/(S)G|*Bee)PA H{O#?ߋ?G#?ߋ?OP,=yZw:l,m"Ӱ>- "E'kTU<7wOjk*>,#F}/.V^Twh=4JЍ'=UJF7 _ۿEVKJٗ8EFZrbT Zvj%ؐч~ҽJ *5"VG;Syx‘9%ҷm\M*ӎjjY.kwѵ),E2/Zxj.58m{pN;)'o E enǾE?Z^/>/k x‘-p`tMaj|~Y O=[#⼯YC;=QŸO}dLLoG$$nM42>)qCjշd[A'hh K~o&W=#UVa|UܥuyuΣ6>:qb<]'"͟ }VaA}~uOg\> h׷]c)+|pdݡڼ?Q{wW|W"i>Z(#8n9!{5:8kEì~,vm"s&|w: t?? dڶU ccP#*m[mNgb XLuUJ(<sJ(AJYt#J  m,Rwx̊zL˺RO͓>_%.0pr(ɜV-laT3`'PDf$QbMcgGaqa/dBܭW}n-:Wf4%2fk: #?>՜ॹqY%xX[9~Ѵqm9 JVs%4Iy!f,Ttܸ/z.kZϡR0UGz!2¾ 720 1413 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Hj)cy:4fɼ~tyњo7GoΡ ~tyњo7GoΡ ~tyњo7GoΡ ~tyњo7GoΡ ~tyњo7GoΡ ~tyњo7GoΡ.h~zƌuhlm<+7@œ198 kBf9&Q*2|Un<i04p3+0 ᇖsV;ChvF[OHLb8~`Ҙ"xtybxvkS: .۲=5-t29DVK2O@%@l83˼hk\*j_]6s9Jƺ%iQ*1lqw(KŵOke߹f/4f?Ėh:UKu" n7;}>4kzվe4Z(`6'׊ l5;9u {@T {v]O¢ھ~9 1_|x=s@s^p[GΎ lzx4ټ+tq9xvhwͭ$=w2Z/7$1)`#Uoht[of[Ӑi|?W_6t6gPn 9ď-ѽUgk4]rDlr=Ϙs2W?-Ǐ#붶q<-wqt)2Kr lR+sԼ;= v!-d:֗X|6cUadW麷}4:J5[[iBpľTO{M6V+a"`;/oدM{y'y7GVFWb̤'5?M3NtS@y"IwW.$urzg "W[K2LU€ $pƓP ́efF +W}iWPkݝԲ[&1!䴬Y(k7 _Pm,R!#UBG 78.EBxr>_oνJOvy Ձ'8pN1/ZD777ژɖITk3oVa9^#(y??cxuxN×t3,s,@GbzAmbkȦZA<h_X7(mgP< 'ơ͚D-#UQ$ }g&zŔ6֓GhBp኷~ԗJR&+аV5\yrUng LF_'Q,t-n Eq7hf6dȪMgw5asֽCFkw_"d(8x~\cҡ/edwBг 7O 2(` 'fG{ {m,k$q%zb|B4ۻKksO%yW99:MGq[7qh'zOknW6b{߱LՀؤ@Kln[k`ymdGY]Ciy}cyvdyD5>kډ㷑L7m_`ր~cxty׸^14KKkx8YMȊ(Å?J.s\[x).߮c[h&[C3!hyoΏ1ߝC3@y947oΏ1ߝC3@y947oΏ1ߝC3@y947oΏ1ߝC3@y947oΏ1ߝC3@y947oΏ1ߝI4jɆ3/N^;f/\[[\d32=HUY:x,[Y%w7Y A6u|拣^l>852KCx28=C FHϨ<o_) b'ɮV{S؞ jתgEzWt5=BHøࣨjw{4xU'rqW_i{=XyvB~^ :co*ѣx0X`k=R897|.=;LkTrQb0 8?gGO|Bul?MWWyoso}Veg4E?8?O@˓S}Կ62k׬xGB_Icq+>b;FL=zX&66kycfړL#&(|@$ދ8w,]G?i |I9$(WEeo4(mU1c t=5M$8 mCgdExC## -Gާ:GAS^E '݂f?HG c ,vyAw08gX?7/G3o[ʐj$Yhڹ@'KXu 7M-.ܱm>S%BB@_w=_,|Gp6Ÿc\:'eܜ\c^vxO]Ƭ+m%%>L[ NfN: Mֵ "f-Źr~gb\gIr֪~gX=7/^_e@,lWw5C&ks."ˀdqxJ8gX?7/G3_EF*c<ͫ8銥ox,]jRGLg Ms*3-dtLW9gH=7/Ame7ٳkuWzZtr] ۭ ڸS!=s=xDll僣<YFpp8g+VPi# _g og5m?h-eY (meY (-eY (meY (-eY (-eY (meY (-eY (-eY (meY (-eY (-eY (-eY (meY (meY (meY (meY (meY (meY (meY (meY (meY (meY (meY (meY (meY (meY (meY (meY (-eS}'X> O9s30PZϧA[Q4ln$g=kun-@P-ݜApl/u tۤ”2FH\܊孆u%iMrtxY'yH1׽{Tb;E }M||md#~h'WF֖SzDIR(*l;|{ <{+gn= {sPpś>t~8$wg vD6̷e:rG~_??T!X6.f.'26u}9U1U\=k"ǒ$okMy!G'9ЇtzOsq_=^) 9-_c ua\HcSICE4ķF2;q¹6\.]W#vNE:췶[^A X{ka`ix'Vfy|0 nNxj{Οequ0 8+$\=N4H^-#NY`T@\IRC+A?Ҽ ;S1-U4RD/˶"]|qcP=8/ {R'O3;s}sEĚ}ƷC Fx ,=[8/ K6OK WDbF`jY\1$O Ɉ~bwo޻֢/6O!]DZhF^YDiީ/%PG#7cc8zѠ\o/ s#Dsk,:o/ ~b˜5QEM_GOsqs4Q`y?Q_\X.t_=y@SG5Ef1/=?sRz4}=?I/Ǘk;O'ԞjOOyƸ(Rz4}=?I/Ǘk;O'Ԟ'_O*Y2F-+m~#ztF1.fDQǢ*'>NӦ=zZz4jOO}ƾZfw*وUf0x՛/k,\JFB6P#wu+JGjOO}ƗI/זX m}As@/qXOۈ۫tBZI& Qm"jOOyƏ'<^a}qdK{3yw{? ^`%8S\՗Ĉ5m*-.Q |b3 l8GPOa@ǗhRz5&ᐠ[خ[ 1x$lm.dRr[wZ'H{gړ_I/׌BR&diJŐ|o|gKOâiZ^qi,Vw"c(I0=~ԞjOOyƼisZŤWBa${fp0i4Y/\ ΪlPe'`q@ǗhRz5z47U͹KET z >i Bf3@d)BUwsԞjOOyƼ\-|BX˾. _AUM6;4ԞqtPiǗhRz5@ړ_I/EvjOOyƏ'<\]}=?>ԞqtPiǗhRz5@ړ_I/EvjOOyƏ'<\]=?Qt<\]`{iy`u.zK4W!EO=vo y60jω/5I5i$E$n ۻ*W$r=15 T7\RÑ&u )"5M myN4e9ZTj,gVAIr}氵]?:\?t-񞚱±7C)n¾.tH$v[9:Kݞ9ZXڅv<pAN85>kx!FIaI9潮Z[\,X}Ozȗυ$WJx~=ΩV|1XÎ֡2Ny 5znKIv}0yxoClVA#n`g6r,qv%8s{XnH< oa tk2=a7+Z0KzT|;6m=axdɵrqVV77 Z!Mp>A\x'I-Kv[^C#o%J2 8$_ ^Ykd{}!y&ڮ!F tsYīkCʄd8AϵiMO  0! U]3&|F cbuG8@ ^ ZiXĒT}VJR𧆧.Ymb|jiHۆ A?K9lReI!rFv 8=}<`~gYG*F:JpySmsKcȎ7 dx5tIky IPq*OxM 'iB +mD8Z!}"Ůek<:΅sH xܣQ m+ǚ6%Ƴ@y6l_-1b0kvWӮ$<$r{W1}NyAQ**ݶ*ݟl楣q4_͗j ZmMu#Q GQ@+g H0 -{ 8v`G#c]>;^s<.bPN}kqo4^K`T*p(Tq8Ud-/ Aܙno>hUJQ ;G m;ĺU2Iɍc# hɆ皫q]ݵ%̋p|!K>cGi{G-J.o쁡eLU~h]2G-64wjvZh%$g=fxkE<ױjg$_W2RR0AYpxS6h(4lTp7SٻGbY֒]":8cִeY>Bs^{6K(ln~^h>v\ʌΙ! ;X|iϰyw+_AKI60J,IRq]U֎heyr:紟Zw}q5oM2m|Ǟ֎=x]=-9,ꉅpsGj ;Itvؖ (OQ#4?ŅU&Fď!d`qM6kml+qhl<0i]&5mn8M,j_29\ʄH\Ui/ڥdfhXdg`<և|m]4ԽcTa$aÁGQqS/&ԯ"V*v,c&V4w!"&v}x_7:URK ,!q+9ul5u)u nF_|qFEڭ~Stjxy02jpaB!G^>€9߷pwY9ͱčo쥘kKQռ1- ZSG19\}dבQZڙ/FѢF8ǐ?Ì#5˲u)D@ ЦZ~ݭ&ws5cdd'Iuߘ{:IϑqfQW9/N*W7 ʰ&Q.R۸iiz7t7Qw!*|<c,VN|Irn+v G9>-ѥ|jocs h@b1#|GklhVVYx";[.K]N(tSi8Y %rI?ʍ %U 0>7.!KyXvJġ$P,S *> <ϧ4~jҰkogwn,֢6;3 bZsrk^6om*YsG`+ez{P/='D. ZKt'Ƭ'Ҵ{ y>'p[hf N+ oie֩u5G!clr ,q'瞕[YqsJmRQScPAFj~n5M>h1CHۙ#9ڀ64izͽiax8=k#jI,h[t;|brz֏Pڦ y`IIY0?Jh "Es *ԋ$SHFNqIg}B#L풘.dyۂ䑜{Eq%Zi?~ս?s"$oמ}! %~wtTV XgPTJ=FFqREPEPEPEPEPEPEPEPEPEPEPEPSEj/@( w4qm{]^gvwX̄ɮk@I8qHʁܕ9ҼM,[)hG u"BUImݎBOnЗܻA֝ 5HKK''#Oֹ%Ǎfb&rO q$g,嘍-Z)J Mݜwg,ohd-af&٪0]g=ju@r\qPM.WݮrKӷj|-+09'N?+/S|\icv qG 0Wpǚ42 *([q,FI2p+k`4.nnefr>D_0Sڭ+huY*/dV,D8CF8\Ȩo6x-VPmla98v{su<^O#Ncor>=YK4֡]Fb%v8sN[5=R[V[ȼeLFft5E C$+bo iͧivp5ͪi]Rd9Y|;`Ӯ{5° 8򳳷1QӵY. [hὲCq~=:V6m>:e6~dC$ d5%)%uX1*xH4WkvߚP;P3Y..K{}D78*Az< hѠH3گc |ܞ;U{oeKdE8I$L0}&y&1Ik#l%/'ٌg !Qݱ7*ӭ"`bR݌_GMqn*Πcyڧ孽t\a An u/~82+FJf8L }NS h¹HJ<0c=H=p}j=:Zr%rdٗ\T:w,,%k{s$Z83+0 S#i%ƚ{1k7fĆ&`~~M#XҬ_G09j/$1}EtצEoclJ!Swob{n-@@PygZm>8/HT?,"hВ2aWlmVkao<4b>'9 ψzEq qKt6EWY9-8=WX5ie4\a0)v88'χ֐ئ'_;sW5 iךvfsl RKyv)Md m9,=34&!:[9ZO#P,K5 DP RSKe}>&ѣ0C>*徱n^ё>cN^y !y).=sTn hVTid,YبsN5cO\4ZhR|ߢB=|; 3爆#3V\yt:&[T63bGWhjYYy%fr6fbYRIQEQEQE_R8Ӯ#_ݣ}DBK9ϧ}Y'ǡ8o>_:(L:?@<7}i C0VK$ά?ҟzxk|lM(Ff?<$֟׺dX+JXeBdzc5^j~M*(,.t/6HbvBʑ=u葏 ͢ZHkXOT'kJlfkS'V (<~{n-tb4D!$y5qP}~709PGޘWޅY=@Z}(cێ:UmGPv؞Ke$+lwG8fi5ķp3@-F0V9/E}FνIo1 5OQxV_!l8l5RR\5unf$}(OXm4Ӣ kDU IsU4) [}o~.3sCԚ4!>wEc( o(|kUK$d(  u8GPA?.obu+j&̫)#C1P#?JC񦍬Z<3"S(Ev.Azx FVsǘ;={?i@^#\G,-u wxQb3b.^yfi!8ixIҮ5k-m0-RA#MEͤMyiQer1 YW:k]YNLQ0"}xK ۧ S|0$w89 `w5?p1l|3ɠ 7M}_gpC쪣nI9CV ҡa佈XB/8bN}ᮃ-T8Й+1ugh:j=Ie2[$CxHVRIw@dv;No3D2˜1ӂs۾* IMy/KZ#&POy<^0\Ǚm!%‚`s@ex.8kP $\>=jKynm K.03mRz&;(#褪I l韭`[?`[esǘCybs@g5",)_#F1R[_aNiG´*Bܭ#~>!]qmu,$`r2sԺo;/5K*e F2z fRˈ\*@2BK5rʞ>էo{ & ڼ>z#iO]J̃ZL-iMyI }O=kNZmR[O$"6P]Ǐn4XnL qn61ny3Wi&wkryk4[s8TρOSFN6Œo]𖛭wv*5E]4j¸ iX5)p1c995\ K[E*WiLk>3c7g'nhsT98bt^^ hfHy8!}=o\N3TҬ5B*Y,h 3]ԧ6~2N֔qeh}3Q[bNsQҸ?hj1C-b[a 'ԀGW>e۩濳{|>:wk|ɜ`ɦ>/VIJYcXʔFG-PLixf/4u>Q:dΏjеM5ƾ`6H[!PsyW'sTXew2UecqzH֗>"2#ҵ(((((((((((((((jnɦݺv;M|o:THC.hz+' Zc r{<:M"=u?~USg8RRñWXKw1iB己-#`7$n!UUw=ztzīrJu*%l%Ӯ-UF/J>gqڀ; +/H񭞕Q=11>\֪|My8hqt Cc=;^o'(cGqr+l A`;0>7}k[Þ.mKT[ WJ ~^(O]Z1Dynv򼌬2.sVMI#@qԻ\ٞ{@Ep'he^x X8NJ0ZF"0}B~;>w `zҸEEg?ln5qT((((((((((((*hCSEhQE|;UrH;F=2)wCOB^Bc5֩EEmt5AieHI'o47Ckx7Sn/ot̒?ڥc8 ¤Tedq)~ڕ„xӥr4+i[Jkc$8?{t+uq({j~?Rk:ynדT{siZGUT6dglHk+'#xR7]*$%52ZFbϝօ 首C$JVE߿npܩڼtsIWspLwRYpnA3E}tHll&doХGH.I'< g6!hR A T.ͤʒZX̍8Ec ]yVf!rBz~UDtln͎RVG*9TAEAUb (;GNg;\M^34߿|Ӝb'zz.tۧX- xmxN!Е";K80r9>U-i>Eɷ"\(;Չ|%ܖ$쬟h1c6nۏï4<1m=}B"'+1ͱgx B~+K5XeI<ːqkxOEe$NW?(_ğҩ7\X{NSksU-;@3`K{˧[6=kd:;>hKj:MҮ=H22Wq݅.L1J%uYvi߉x{O9Z{8Zg18 S6~sȱ1pvDҒ#vU/ |Du0Z6m);3q}&XMNڔlΪ3FN TpxKIuKI#{Yء%Ab2~^&mHl@JM2_ѼagIw[]M+qQg#9Eussix*F2zzºUm1,- s Y1x}遃|I4k4֏Ŕv0<~at,#YuMZXw^~t[["~ ?7+8\ܯ 9k h`v;T @&I$-!.X埛vy^*I̒,8eL3 /h3̳DeG8]F:>4k2LRݎ3[zfcI0h0ێA$k; ɨApʵԥ]q ӵWƱ3Y闑Nyfs둚fI5-*3gn_h]vR2۲sW!Ε-۵Ĉc&W'$*!F@;P_&XX]\,LIC2.[S}j燼Ug$nrA3M8;lxRG--RMB+fK,~Y"(VeLV 3V-65 BO y=s mGIu%3&wlkSWT:}4V^K~LT.q!p=QEm+m#o,#fݝ=j +D1](#+w(&F$b,'6;]%,um5Y-B3g=QM˥SN#d1+2z?V𮑪 Wp'Qo+#$c:EڮUYى0ր9߈v^, tIR<8Fr1֭h>9ҵ{#r<zb]rڭh\ޤGs?YE ύ6HK;G ;L9|hvi}{ OIo99'0:ܻIl9W7 Am"j܋_bo #9WKiZ-&3;phۓZŃz rYG طI<&Udd1:ik[Y] {oT;@M𯏴s"7 2ȟ0`JFx?/Pj;GK+K(~UTmggOΓbfh|&q#r;rHZĵwIm1Kq{'nK4*x8<+oCsᵐj&DH'=i#A;Y7QSq!b-I`sZw.{m,3[GLrlcZnknl.|R;"@ N3WE ]RN$=`9 QX?44dY$!m"f$:kj-VA3Į H_VbJ~xd[ Zh7"^3kT)zb:[IT0AZmA1 DAT T /P}uQ@. 'IH gk=AkK3tU& ^ XR؆b\ҹkW F/2WiIkxźBRDׁx Ye_8ypgi6A#X_n.t`_7-d1BEcx;YxnT{ƭ.@Q@Q@Q@Q@QEQEQEQEQEQEQEQEQEQEw#M 5GS5Aڝ𶌮.-:-xdV㶸azrzrGҼqW qe_-<0SMخSzJ|/skPZ,Lıvg9> j1xvk#a'K*=zik? .b{ 2.b˹W3wP>ojvwpx !p8ފLVꍤ4Q_aft 9뚧g6Y-̺M#DK-$#+(iq^V&]6ym%*8 8޶<5nƣlJ1 ̈́ [l.f̋m#;,29 uw`Xƍ`ediܫ::G[M:YؙKF,din2{㎕Jž.NlKg[> z vPHFqSPEPEPEPEPEPEPEPEPEPEPEPEPSEj/@(~E{u]CC#[h`aԭ*>X_WTVְ4 Ux>gi OM^=~u5彉m_S؍ r !$~yկ-Nb7!crz_!0xNGVWpzJῄ/&Эe̒HŲy'/R<lL#$`cϖiq Y\;Ns:R4 藰\I[G{~t_UmSJIbʄquOsz({i@Lv'9?%m-!b>uW4 -⸸$ Nyj|~ɤ^1``ByEy&URK豶StzTxSCYcI3NIJ@s*V1rf mU k[Bq4tn #*d#Fzt_DKI:*He eq}(O ıah`2ۊ}yZ`rڇıgHAۛQ0ʆ 98x\oxfVٮ "V98_A7x ʈ"5.($:tFRUzP>$b,GKʍ_$t5ot aZxl#<H9cV$,t.d2(W? A 'ӣpD;3c'sGjSjVv܋tM?HOJtt&M͕vvGr1C <3+WyDc.d#{d]9TxGB>:28oTF^(5ԁafYdj ?Xzeu{!b+Cp_qhuklh0f! n#$RUXiwv+&NI#8OO% n.Ae/1ߊZvjlp'lr3F Z3Z[MbQ#99-y>皽ci:ֶ)ffsa@^8kXYit2- K#%WV|=K`בAes4Ck 9;Vސ5Ic0yA9;YZtvV.@U^]Hhrw-Ѥ;JռXmltl,4"LvfF;qR'<:nj]̌l'wϭ^Լ=vW1Kko*>T&8ZIP7qʗcTSsɧx={K6hΑckrYIq[hzlR$vqn-Xz{T#:(4]xg)S@tWFLm-pp0{VUuVѡH J 29@BJs]VfOE-26]2)nOZo hS5>kTmtEMa5 c + bvb;Z[M,`LE퓒jQEQEQEV?y\_A5_-F+[YYQ|n DY#dq`U5~Ɓj?M] goZR$:UNp zcp_0A ۜq@_N4DF* =3k|5{gЭ$'PƤO[OɷRg2vdߊq̥YY]\Hd "3ݐ4kC4kt=[, >Ƴzuc4LyCznMQ,Ώ.:ֱ_L*a*)91QYGM- _,8"Cx[0xC@xtV[p388$d? p,b:2J~`V`c8-KqsT.arMV>!XXɪ[ku=2LѮݯx #xoHgmN+Q83`>m 7*f&-eT v㑜V,>{Ew 19,H8қtX'hlB $~@y{楹ޑsEMdAH2@RG9Z=#]m$mt㷒伡Z:B[SxZ[Wج1˿w^v5gk\y.W d $g8h]Ĺژ<ّ!< {7W͕ޯr%Ur@*'hSjݮ\Z$l!s1"&iE6q;|q?\QZFrlb'%<4X5/ZGbZAZ{ yqg*[o4ItTXMvr,sOCWL-Ev^YivW7*OpJ$х>9no4{`b@Zi NkBծMwsiql<;/~Z$+U-5|B0@H- NƋlt~r=s9FO4/%cU0ETSA[z n5{M2h9XJPdu 8;*$XLcS4@x$FGOj}煴[.aei3(rjsCRf Ϛ6:l`OaW|sKص]ܭ'Yz7^GG6nYFy 4MCmePhȶTX|.ў}8 iMfG!XgsSր)XxOz\gӞ'Yr`]ɷr<>kr5(ƕjM<.W `LSVyޡc`F휀@1[tƣcsɶ4vo<]F>c)u-H Yc%gEy0u$A1S'ލ5ԗ/["hZD]QH_^x}U{y.]ސus5?;qi5̰yBmV5 ]cWc=!Km:HCjŜ(|n$p)x;l#yv'v=b/,m*c5J_ƦpC0QTU  u4_v(>x^ivVYN s{+<)y'|9 ]oK)p\ }"]FQfHh"9bh'y5\dcJbzU(N.L30k3yhkzk!WN qqh1k?M%+kw+maX#^>.*U:iE 3J1^+gE q~6F+߯r8cs"C W66G@1#R4_GQ_%'%]GwI@s,bE;N˃]s[W?D"xVA5iţe`1]z o_|#,uK$ۻ9wp 錜Pԩ+tV[GhYœ7㑊ͼLjdm>ٓ/ePH\=rP{^e/ԗT ,a4Oɝ͞q8:ԚG5)=>VCdFt {ZGi|M}KI'{剈J)x/G?w5ʟxsNu 7{rـc?*W?׼Uy of1DQ7vo#Yzw۽,} r-+mQ)V5y XWisNQ#ҷ((((ZJ)i(((((((((((E?&y/Ý/Pw<ǿEJ SsH_k^[. .Sנᦛm ;I 3dOQ}/K\G滴=M^Ť1#c#s^×#Sv`ZmʦT)Z+6x_Qn Po1w*r=GOjaoO&ܛUmj5 `1Mg vWēvh ,RضG)^~ijmCJM9b8)6buJz}x_i;Ibp5|ȕYXQ<`ƽ&/M>Ж#FLTrWvQ^oe?yhdcHRHfUb'QלU[==tŵK:9ݍh^#=ywtWnLOaO ]Tc)!rH֫>06 ֦ygݤi3H\U9;)ks[i" 18XsoOZZvLm4֩#v;0GWr/즟N]%)2$8<½LQEQEQEQEQEQEQKE%Q@Q@Q@Q@Mݨjh:(>T=?A>^/ a~A9dv+}"/Fgi1q^AqsFFu=~@'$/3ћQk, !q,5]>kiǩZ,ym#1#q\ GYמ(7KirkqS1hW)/?7K-N$@I ?bk?{/%v7cրPuش=P] 'ҡՃDgG}-N gB=LǞdjs6לݸѴۭ14ylP4`L [-NhZ f(h'%G4u޳;Vê2}N(px\)8V g&7{{/ݬqG Ir̾Tj.lfkOMCY+ LHf#iA5t-(6jdb % t+ll->d) 9q SSd_cGO4/$b~b=;S'&$Mm 7Bҙ2'?t?Eq)xkDJb,-(1>t.,nİϵJrH{g=1\:VGmn!A. U !+HmuFV6Ɓv9*1~?jY4PNUY؅qѓ=qM[7Y$Y#bU($m6s+ºK4P¸L5nFtlmlhoIK<ˈ-DIaTj.itVQg&vT * @9WE6wqu.h7cS-" 1SZiVzsXZ  A1@MFOIYH&feTΤ$ Ӓ5k :|Vw5uY]  K1<Z1Oٽh l$h+zj֣z6kso2v1%^is_&̊˴4d` 屜%Vu}.M/U]FfJ仅BƆn6FFyCpP6>xgDvCJ0X QMmvB[Y+97 3j(WHuX-979B>2+F-Y[1lg;=jЭȃH\m0g8>@ux~-..# *Ŷy vkVT*EQEQEQEU{\_A5Ovm!mAs|* ur=UtepX`Vx/(AM NR9k:Ջ>GskFœ_ۜ"I |^ =|%=ձ0X{y{GۮwZQ *47i>μ2o"U]?F}*2V 8ɾ#4i.9`H$d,:!wy=ϵuxwF 5!u_Nefg}l}fM*f)tb0#ǧ4xc6WVc9m;~lg5NMmKڃ]DȎIXo#wsumL%,-YцJkcki,[[m03P>.MZQ Y̙<~/oE㉢WyY~8hɊ09 CWJw7MZ1!wHcKmI(H b(a׽qM PVkSh.,^Z&v1F󵖞h$3L<&RI\1&о,,~$D>Jv1֦4M9^EhTaI㴿jV4ymm-9IаڻpTcEm^t;];&.g[|\zOZd)iڂ4V$:sZ3 B!I?L.&s[5cml`t62%ڕ 3p'=Gҕ~#=CDY{xB&!ko hl C ݻӞi'8MY8O:@Щߓ?NΣCm$^JC3*ܛqpr+OjEYo>o:7lCҼ ]e :i k8 (Txh׎+>? x~;ImlV kБNƗLi_cn@Lm ni7kV/][K兑Τ9PA#'5[Ҭ%  Z^av1 SՕ`eh\_ ) ^oM (gƳv} qۭj(cKD`*-\{KxqEHP{k]Ɵm7F+n qW<[)=YaxRs+b/ h=GX+[D+5vHӯmol'9I# ќzP5oHjƛo-ǺKk;Œ=Vj>+4]6;G{x%1/,l)P =xkMK:" sIJ <*T}چ]Q@"<1iWr\r L FtK*$cct渪98;/Cf2Qٻ.ђxG/k%tȤdQGSVЌ׍x[Cf~%㌀+pF ?¾ 5VoPڷ[AI7ўRh?)a 5 ^]8 HӾ|M dd H8#Ҟ!E'+;[{I<|ng*j_6cOM͠o0CNV7%)hO[i U8DSS~1Ը;De:^<Ճ 砓_|%e\c;2s1&Qj$nb_²ĶN]S *|M;[؛UNɷA P\=i4S-A K =wTQK@ E-%R@ EPEPEPEPEPEPEPEPEPEPFGV32Q޼P(WX$i'*_5pA\P/q@KCn8Mo)5o%[x\[v}w`4bk_H]bCElf\`QRGiɨi>$VډslYԌ`8<Ѐ +Bq}pdC2l9I)W?I lM m&,V@#%YY=2k-{R^Z=ł$ERX |o05vZ:Yoo4xqM0o!Wo ӟj(.W!n/9^f>cVS sLu}vZD=h(3ҼMaQlKc+nԽ|K&-4",]E9k]48$݊Mq,/~m*{r}Ez%ZQDkӭ K8c !a8'ڻ윂C´ӯ$mS-H2nN:4s76=D;`Lk<%p;>G{`2H $HWko߻WJB(bW~}rEIv 9=r;4 <"jcM`NZr *I89wsmy}GhǤF+db%cn~һdҴ[ A+w1ޟes;oKN(D%Ւ&6o,8Xd9$}8Swi 5[L L1ᄑzyVE }vFGUK%- ;fۻ} xu<~}B]1?(pb}!Z.<: ]1hȔ61}[am-nM(1ӎAeknTAUإ# s v/3|S&6^'cO<¨x{kQ =29`(Uv] W 1SMsIuGPA ~ |a!u.crDutc𞧭˧؂Z%62l^⺑ch9$y? ;[x{h"H©ϨQ:r'S˻`+DFêOI7ęeO[V qU#p䎘5eiW O/;+=)ͦر%(#9y_@9[ bYԫޤq;W5[HN{ c-22BÒۉcBܡ"JF c{TiX$H6$a74Wګi: *3|ޟxM,T[)S3XviZ|qZ!>TZ[[#o O'.ȁK}qր<彥^msb8w@ 0>o>BԮodkW#<,cbqɮ^Y${hIcOqOm[QG(d.yHH'T1&B3]xߥt |Fsbֈu$i>v Fb2c0:ΗFxO%v:0 X0 e6IiM[K"/7 Nx񂣎sZ3ZM$dk,vd dVsh<]XO\T_Z@?P:`<+Fk5+ U)mДQWݑ1rsU$j2FǦqm͋M,MP{WE+Kt@`U@ c(0%'Q?e/X]W d2b9Gw/kri kH* uQEQEQE[*f77Q[~P]9smXޝ68DQQ1P=Ri- hmN38?umK17,,Lma+m!]Hʑ2:qPVvBEh( 'Z@pLN\IqPF ^ℑZ}nl.eo ‚2_'g#yEK 8DY#Q`j [oLD+z8>|}I6O3t-V͞@A&X*%5++(dyRVs{A(n2FL^k;o1R984|D26d`3:e+ȥԐAۂ2s߾+fkg3UV(E²Ž8 V ѲZm(=Til֢٭6E˥p|MI$Zټ;CЂߛx~$I4W2[[&)4p٧b'~5kب(9y̰><4_MiVaxdV0:H~&: 4Eq'r ,O8>t,Fz3H4 VH--W`t) =Imm9'?# ,CP!xMōhc,0 p}j׌55ĶMy`e2Jrs4KkHvnՋ.ȣc B rA n-7GLkAm4_gfS`l89Ki%kS,s l48-S4 s˵,h T>&.u%DX03dgG0Y*aҒ XXb3Q h(((*hCSEhQE|]?5htHzʶq\BܸEj9 oOhثΠAq5?|9^0*tAu3KHoNu_ݽ+q/T? PQEQEQEQEQEQEQEQEQEQEQEQEQE{V[GW;A"2AwP8H"&8 |:޿"<N :sӄe>ĺ,b@iq O,eT:0?tAt$v6vEy79݌ Qg`.on{wm3H?v19$QҳU:]O.!D T2H!A+WMZY \s\|"]1ce5`o( el>YC䪓+ts5gchp'}NVz`H "7M;[9֪.]K gns3ǖc݀;)+u[_!^alxnqS0S'Kq[[pvm:Qp=2W H1;ȉ 䏗i޴g]4i<ؖ H.w s,Լ~n4$p0$<{֗ փ9P_!vN"1|?P(Щ+m#񕷉˽F[H% NYprҽ$6xEȑ

    X8!zlkڝ妅طwd}ʪysIv%፟ ~|WsU|=%3ZeFEB!ONⅨ=((((()h(((((54_vEP̡J-:Z/³kqBgP>tLV\_f9VeAհ2@쮧sЊΤ9)X:_\-t_&EFwǂ?v kXH,p UR2e'^%5EUdl4B㹂&J2}*]4ՙQNW9v|-Ov6 K{{[-9c1ˁ$[MmE+ .q}I&5OZ|C>j,Ѿ룶CL$gg?N:R7=FKǨ'h~S9R5_m Pq۶97R-m<` rk Gwö1sswqW60F@+(i\vVwW"C#Er"U(l;qoJGϦE)m8o%y|wM6mB[O_šno5(X--U2_|*yʩ= W@KmfMA<$R*n%PsnٙDd%@s@ý^\^^Mқ 6YP#skoi[{P} B HaL ({\~)M[ kNfUrDa}vڇbE)4EK*pvTNMz]0G[L[xtDLJ2Węmg 1ڽ.?oϠjv6W,[]GăpB ='dxQZ$[աD#i o8$Oψ#˦sBm6,Nq=Zjv:(ߒmIQ@滚3@ EPEPEPEPEPEPEP*6'h.y[iPk JaXm (pi>y1a~8hQ.]mUIǷJUQY-a0IG*9\ fN+YPIct=H;[h@ơ@`ʕ;?WAo$IHguoWo(`Z|PK件Ӯ.. N]1"?_5TS~$xCv[.'p|J V~"/u;O3[I!9hv2c筕v5+' Zog3X,mR3>7FXw{ ~DҲp1׶Q ㉼Mq~inBήxAowry{ [hcl]$ix2ֽf<: k%vIʕHkSǓkfeXy{P+?nWk_Nc*[ux YJG$rOneRݻ`z!:5,n 9jQEQEQK@ E-%R@ E-QEQEQEQEQEQEQEQEQE$dTw T$e##p=r= 7[O $c09ڱU4Tgys"[Bft!՝֟7U# Wŗ:ˤhCđ"zrNrsT)ͦ[4S+rO\Ź;XᬯsTPTB){3cq69vh'[3fO\,v4VCm_4." YMgQGk\X.%>\h(?Iy85krhwp|1\=Lcbf{b=G>9o?le!`YO(,Nkouy5$۴t#1@+t3ǐW@FJ7l>^=Di#_Vgs6rOjJ1\7tjt i% H!\\䲂ǽU5վk^G p'$@xր= kOJ\̨L|9*XdzZ׈myeM 00pIL{ P^miAhdWBHnBM3$Kp\*'?CX>5>$KsJ|-v\REr I>2;M5LӾ+QEQEQEQEQEQEQEQEQEQKE%Q@Mݨjh:( "^բa7;)7ߒ{JU26_:鄣 aH 39?S; yYV %˂V6F$ "g?|?gkrl$DlNKJ6f@>MO<u&4o?"eh'$Ǹq}_X#I)v UG|׮\|.nZ[W6:?:lleG'u ,a/y9o|sCEՖ$rxm'5btio#ȆC&_Jeioe36\ s^SQIri5s+)&4K2JpPL}C!o^;rK)1mNϾTUqX|=]m&&Ty[Qy-:o iyr)#ZJ3@AmZY ۩D|lme`dsc]z:\>$nхX|{ t~#mRX 1H6͜=[$WFbc[L{X!݁kѳFh+Ϡ5ڭsLHPapI ?a/wfֲ^kmd(S2OZ4k^ uI併`%ƒNHӞ@H2׬5;]6AY<h8` dsjig+iuIQk-pcYwI }zO^ eKimW8!PC  u/멨jZŅId1C4`(ܪrGVh,>(RhUx7 ՠ?b~ ;xP#Xƒ0X"u^vYWQ@ig-ͬ&YHk %!##ڵ#kϢEq-ݼ ְtw9SRKrO yE+<[^h@>Z"` 9t^({} AK<]!k&&"@׭ _^j^ԖQt4 8EQEQEQEɈXdbpO)=2eI?$N0z5G$dJvq KYNx~xJkCJN1wg6mgm^yZ\m@f >'VѾO]G4V^ $z{Josc銻eMWo}ˌ ~u|MZnّ|=WPE^!!$H\T^/[ı?mǛ,r+ rL%#qlx-U緸p}vc6avPGo{,RRW15qM##vzu8#! ٳ m5.d C >\N՗u)hڲm;sSֽnxVmZJV(Es$3޳7 \Jȱm91sc^3@{{;_xmVlY`)b 'zc=j?xY}*eo K dcBUBWRPKFWMC}pUU;oLs[)ukXu-DٖiX$0e*rrr=khyx]66 [G KHK! )ω3k kE5\_$3H;cts]v>,5h.n[Yb3K w#xq]}׉HFgHgm2_RY^RHKpvN^}C}8j lgpɯZ9VS7MB<Ɍ1 k*_bk_N|1ylr3 ^E̬Mi^]z[I5~;.H\\M_xZIrGsDz S7(8 ףR$n-k;vv_d m短t>d;섲jiL<}q4Q`uE}zO-uz UU$'> +C;s]}2iڽ—k,rNF:?64iS8-yE ivC* Gx|E=-G` $TƱ=->cW=x$zԸ/.τԱxt#vy5xShY!)H׬Yrk2m&rIYzZA4.&ݪ ToH# >shZm:}z"]+C;#1Wc{%%q7!n%- As'+t-oJ[2M G6rـ(թq^[OœM}$7-U Y ?.8nuZ]cʺ>]Ċp@̀\ȠJ6-T]隤PC#cEmuim, ߇g{ԢFP"CI+q3=ɥRC! d޼\Û-卣lVw&5 :^ukc $PL8 0@sz%y”շrj|m!R`+pzWEPERPE-R@ ERPERPE-QEQEQEQE#X& fȀC3oW5H18Wϖ2%E8^nR-taC8*|{U# E|R^HxpSn1[PK aTd9u+\^+h-|R(dluKC]k}Voyݑ6:M}w`-A 1nH>ޛj;=?ΣuΑ]<>Tq4Ӓyxw3Z̴=$m!qMr-y`)C1Eyώ|ھڞcd.TlqrJyrg@SjI3HSh %b>b9YwnWiew_3v0;S#ό\dبdGpcY;@RWj4okꐘ^IԂ8<[j0ivpMOcŽsI#C澲l'bcl^S,zW,'m9C PƎv-aܚfi0`2OA+5kMr @ҧI%e$cZ°t:GEVDX#rPn9kvR":M1L+fx[!qz*j3@wK LS%`6F\ݟoRK%4,PvnOz(5-n/ Dvzs%BP"#=+& G|R "Ffe)޽R|A}wiaS62G"G<*J/\ߎ+Z^_ݮ(oYTąP{uMjͬipYfh%-\2ݹ]ռa.,\# ԛ:h"R # c;I֊z]4Wk-`!O,f=[8oē3U%! d<{PyV$sK@Hߊ;8gM- xT}}*ks$k$6b#éRHU9? jTt]B =ԵC_DHsg{y;I'+ճEy[jZݼ\6 UslJ2Lt8Vt;uINխݼyj.~qֽ*<OẗTݥL 0n#6;jס"n`&'rk-^PNqwFKbɴԐ+i;PWU/a2jv.r=+y׷_[;# C!#Ic딴iţ:3} FG8<ޢ(((f"($$y])Hم5k-\t6 F.6w "F4^ WݍEOdtXʍ&TlBx#8cP_6`2,m@mׯX|XKNy<`#ML&0N3u-m[vףtᬤf d '+pzuşږTf{o] ezyaRZ{_; d;Bmwc(|qCw%lKCF<(ou+x^{+C1($vc3j?j"]*ˉ>oujU1F@1½<mLJomjV-S(AZ|=֤vZ=#l$ NGS49hlBQJ2JTω6նs<KYbv`JB޻JGuܓKtq2K"Lo`X W_sx|ݬ Nk*]\El>Ѩ=΍$+[JR[Aꮚxӏ"?Oe$סUsc,w{y OxODeUu`33ź˄Q#qJQSzTԴ=>%h.ݕA?OÝ 5i1k11tx:tuGP'2k)<ָ;;oxL7h ]#}jj6H&/yl%̪<{+"?|*7r`(QYp`PY܉' Jd23W|exp\Qp=)XH]eYNAӫ+š}Ε+{c$V9V%Q@Q@RQKI@PQE-%RQKI@RQKE%RQEQEQEQEɥXaWRp+m0xjr6&Wyid'FUbk[$#(c/kTpڕ5=}c#HY-xnn3#km`Qv^X*1Mç\ڰWE9A+q5sH/H`t]FpJR3)4 oDLioLs~}O]y/sy!k&YF-Dhp u^wGՙ7v ngp&e{V?5w6z`g&=kBo<9yb8yc< k 19ϴ-ݶ^ZGgfI rH+h° #T4֫qSLz g9p6Tiea5w\8 g /^Noj$WeIR'k'$Ez5-ynW6L}\FYzxǦ)=|iڽ9Mq(h†gP *F@zuսSue#%%f7u%6ҩkA38cTasztQ;kQs279\[}n+)q n\_>lEa bIE ]+KʜHۺހ=BK"dh7 +=QAygdZ.FD6F1L fF5BRCۘʝ^e(]l88ր=23%O8hBM6(- h;WQRȬ zMcv56s=#GM5a)7\uvaioI$+*v.Ez,ZDY?(,OLtW7ɒJo[[ul}Pm=asQ7Xoӟ#D~|GY`7WVq2QDڂ2bv}+!(T1jNHURW\ci˚M]]x+Qq2G,CO,8?u9~GTy(Ƥ-#GS* `:vgdH/cH-,0FW5ꔹnuD)xa{9~wNԿR5HdkG_.m|Ica|$1[+YS[˦,s^ȳ=&Bd1ׅGjkmn Wq,q2Sd0|1޽_\x{-;Hv*>̎wu VKN #y9ƺJ(+.Cs35@uak7O5]ȷ =M2x'~/^q%{L1340Fhμ<_B(uUxM¬ $r3c펻n>\o|ýb_f;\=r0< ^WR଎Y;KeF8oQEQEQEQEWԿw\_A5`BjH@ cY(G+Ru-g*<&N-᱙E"#lwcV޳¦^~|+y t _ZzYffIfhPng͜Fq5!kTA$vW}vH.Kw^Ey]ƺej<7Pϙ`:Vi0i]GP}osq]%x啧t5noK!asq.EYtD9;ccv1ǽvPٿ +[ޑ6Cg%vAD Y}JSMRI>cjyu9aԿ>"yj"H'\ F6!CIdn32Cd/XdczMX1Pjz/̭4FYʮۻ͒Z.n7M Iud|! 2q g8z>i(ɴ;ćNԾm \eL挫qҼp8,|(|{Վ( TkvT%Co-bGYA9 Ҷ|um)_IK[-P?7FFWA<-7Xu6aZ8-a2}$db9*oK59Kk4D[Kڠ HFss<'Oi$L-Gܒs35shK]ǘ G^Wf+i^]Gs䧚}#'9E4_v(֡ 15CỤm)ja¤a`9%#n1IkR, .Q xC7tO=vtm 9Z V'@X1ov>+qmc+m6Ĭ9*]S:.}oiqf5[`z z]J]<(6P0y+1pMw tkAA]K?Ef:vѸ]PQEQEQE-%PQKE%RQKI@Q@RQKI@PQKI@RQKI@Mݨjh:((G/9EN ;jCeVI^Ɣ{7{;h5B:aڧԐ\r#H nVp'.R7J䍇|IZ&MŵW*\qqpW ;9070kԳEycI40cXW;9@[[i$e# O#Vο%ͷ]ŢXV/a\2yG?ZM6Kh,>wq5d!@,# ~'÷#mYZR[IG8ƌ M-oD#:!A)ZQ~<Οl(G3øP66֊K=ƺmoC4 UZ 3zqK.k;YM0cd;q3@!Ԇ2DcpMs-p6XHi7DitѨBIc3zi(̬O[jz+Ogw#&֗qݦxDQ0f#x)0~4PBQu8.8-be0g.JhUҒxD .V?.v=ET}چfotoxx-20.08/images/magnify-image.jpg000066400000000000000000001651541362435004500175510ustar00rootroot00000000000000JFIFExifMM*V^(if%0231#0100Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 805 1025 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((X" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?R7iNO̼VY芓g>mRA9lֹjri s3\wC%HtW7X;Wr O j$*D6+D)Ey#l dF8w $lN7F?«F[$.rNj6~7@$s oV9/%y%F:TY!ar$VH1Qy; HZO=~jcI'l~)$akpE޺?6.Q1? $܂/X6Jc}&$hOqS&ڰ.z渍:(]$Oh\rP] 8ʂUXpX@yIh5n9#zY@[*ܴ2 (OL +763"m35i\bԟΡ4:e1N˸ «]5#C, Tyr)6 @ךb;8#nUc`GsvB J&ܤ <ӚK =ue Zs}xAq1sC#Ϡ' {)%' OR{Ip֘妓 \Vv#Z|{O^H􉵁9֡gE B-mx>U-{JIFB:~bQSd廏S"ʒ9TUv@VS#hgnY˜qԏҤI" n*wb[LXp3U}X<=*HzԨx=Fi 61F7c>[x§@!B!Oޮm BҕU@Sި6@{UKme%䈊őN98jĶv4jA$Ce~gG"Kj;'\:_QEmǿj%,99q~Ґ#@r9'ғ#w ӹV~Z|9mZ?!|X4-K'C6㐧cWB~A=?Z*C,mIo~QrSƪxȮ#p{;MKm?f%*GqKs$qeeT(*rߙ]7o˪-Զm4N4Q[ d qeiVeC0}yǥ4&t'k'Uhl(^\nplޢV6VxIhR!|}FA鑜U[jMC:vљQcIpFGixkb]F 繞IYq98+^x^Kxhu}6춠d30 1+94k,uӮ>T1+鑟Zi6tmXjrçM,Y\7-(Ilv5s9*꤉V zdr~fizvuyZV%AKHz<9y>4 P2l ڙxIe]jo~f3ec2`#oAdvMye4+K7v옡)X'hL}.9&Qu&GF31[x"Xtoh˺.ۥnH^ILW7g}iMj1Kq׷})eͨݱ^jji aFv`co~ἷ'I<+u 6dK$g0rNMA{i}(LQӈE{$$bׂ?Zs-6\ۤ7 u*ːn;Rdh~5{cMΌ#G'4LL#hk-嶄$`1®hy^v1vJļkE U@^ןT{yUrnNQaH1UwRSѸdD+ yVng| ̧p7p9?3.6*e; G<ԉϷJՈjwR3#o?:;H XdsKGq=3V5wp>Eg q׭I2C{@%p0Rc 8@xyUozbN\VcF0Z -c rj=8W,zR7 _KШ׌$G^>fdn1R>1)Fq֥2*29sOǎ>3SqqIvMFAsS1FUe<~R4$ 43𦛶 0$v$8H,qߥU'Qwql%9+K0<xp;4hI8z]ɵ L( }9#8^FqW|3k l=A ݚFX:t˶lN8nG!Vbi& zuB(MƜۇXEjFvJ@232Ю`I}-tr$㺂2OZm6NDXm Ue?1}jlHKr*␬[Tg֕G0{h$h$( ?҇6I<}F=?>`A=xW$Tx*0N==i.ZF*BQק'"n _z}2, 0EI lb29#qG~ܐVŖdm ϭUK|#?bF) ǷNb0gOl}*$oN(uh58Rl3]F\<7'q'=8ӥ8yJ1j$R֫*f^}T^OۤGHA^s+)67JBDdcV}4,8aZ["׭2oripcd܀-v'> ѐp2GI%.Wiy(+מp}jٝH]|IO9%mtpe;}2y@gUX ꠝ>l i.{PnZ0Gb2pGև!6lA+@ʻF}G FNݘ VE>azouA$c8+T~-"6esp̼ zַa2l6IT0=9[Gsq ?_??GO'_DQZϝGO'G(CI+(\!r4O"(CI(/i?ܟE}E ;4O"Q"}WPEA '!rEϝGO'G(CI+(\!r4O"ϝGO'G(CI+(\!r4O">w/i?ܟE#Hd}KITAnMWԿu\A.|jB`VJlrFzU9ON@օXw,^[s{Ԓ;±]z|IURON+cZTzO[;!>؀rFzd‹CLÎx散V,Tq sߺ8n?0qz}^Kyҭ C =)Y# iVbr8 d8lwUq'`\g'sv3*ųe񜃃XyC`1B߭1™e9\vҤ>f"B?_zkSOAG2@ǥ. 'm,nz*G%XS*SF3<Ѽ3Xz)!,Q1uF .#s`N~sL w8U#T[:}2F]ڄq>HՑ:u4a#\H~k쫤+&Ypvc\e "ԙXˏ!sԠfd·R@-(R ߊ*mدId9!AUC@^R簨vLkS<#۴|JlcXQ~\V٤vnz?_Ρ6@Fgq*orQdT2(;#qO)GmGQP\!NT-8> X|'`pq*PzA2evqӊl5+,VmJ$!r23 ¤^ݳ[Ac+a7 |{k;<۽;ŒN3G'Ec{xwAdI dm)Rx/<1pGiYZբʰiGvqٽ :IJL]ekqGAXb-Od/RXcgBQl3;Nz^>4fD0hcyI;ִ d5j.VbKT@9 Ydi {O9H5:mKD{[W؎G8$gdsW/MЭt˨yT>R"r^Ch* ՊK(ѰR &9<Twv溅3CJaUcuy?=iLLZF[sPcx rk&_b2׸]]wö]2GR(dL$`zg,4&{Yq$^GPO8+P.fvqqwoۿt:TfrZB(*#[Zo%] y=߿9RIFyՉy%;*N>lq7/$i HhZъG&'OùfToԜ}9iڝ*U=0I tͣAѰ;@do.`? \~޵4* rG5,hJ2\T[8\ V$=03@eȨLc AR@r9튂K9"nJAWR8 (ƍ#H$\(wѝt?5f`oLVDDQFHXzs>ZsӁ4IJE &} )#HXRo^I^6]5 XƸɎ0%#RiczT]ϛ}zq9WOik&!k__^HECG/@.&l ֫F3Z+"8;f Gkv*;v@Ҝ슌u3t)q'Og'6ulul\cmNz&ۓ-t@2>VtXy4sxi ~a';3Zz~7iڤ,rg?jRpnIHMMnFzqwy` S3z]$VJzOcKյ Nu>]׬fTRX0ˀ}zU:Mn`zIsA!ԖK’=qʺ @X(VM5HJNTR0@@=(-ӭUM=*S nbH9*3!w"l ֬l.׿  ir>9ݑ}kI?T' lpW4l`A'$IMcdĄ>XbUg?ONx F"hV%Z!A}z#o8,3!GMsV.4L2y8ӵrXܢ:q]ϐ{dgYFc=*"N]x⸫9BT6s9kGB2U{XFNLd'Z*7ζv JTj!AeA֧K`Le'Vgڡx=XJT,fK ޢC؊ti y~5,khFNU-J ˞))C.f {E?jX#?үϥ]i֢DVNҺԑDK^IEX0*sEzR P@ dC*\Dffan5|?P oA'F;Q[qh"Y-uvU_Wweqek ĬE&!eʘAFy=wĺ6Z|:Ą_Z$w[v԰\樯|'1%ɕMʪ)߹'׎x^n=]F "}Wr KLqT{Woh~he)$idG831,͎'mr˦ ѣa>1qHSi'*j0Pw^V%dmц$`/='mT%p!kl9>յe_ еf? xIK['to|/ޛsh]nGWbrmʑ_xnLVPn.,"erW6i~٬juymg4 bGLԚ A 1%хIӝ͸zZ hw2Gr,׉`C%Dq;cjiEAeym}es ;yu$r:vSQES.8C ) +HHٿCC-!X5//QDma\8䎕612V)"}1{[VMKZ- Ȝdֶ{>[mn8']JjhW-Dr7"sn\\}3\ ֥ ,w€pqW&Т&(@{KfnT #ַ]ە$fVlY vzuz.9f8ÁkĺʮO^5jQ^MQi!2|c8{U9RI c3]=΁eJm3~5/_SۭtgUZDM9ڌFs֞cލ\Nz~8̬4!UH8=jdX d~Rq#qקxb1\ K)ldPFFxVn#etIu~1QHB+ՏmBeڣ3zZԻ Ɗ@߾kW%е^tjmx/;Iq_v\WPL9ϧaM-` $ڹQ4&l` :d`q|K `'`8g5} 6p@2y;moMW-KM>}j289"(xV9`C=kK(f28ڬ.$T)qll-{s@2ԕV"j ҫH 3֭^$9<o^̺s$P.vsZ{C+lO-epj]y5͛ D~֘\;Ԝ8Eh 4OCK' z{t'd}=Eyޚ8scs4ӬA}7PFa̧QW5XF,l(#FNXdw≘#5xX/`5ӯd_J\Qzq\F'wh2Mi"W2+v`x%hۇ\u_O$r8hE𷘇6:eE~s|wo*F}롸'|v4Ř *+ Pk?jӠ(8P`z+klj!ֽqF璸P\m.>5dQM$?=in5=>`0G vW34EvddqEȣl(]7"ZrN!5[9װKF%ߎO;DN3{|_JB.oѷިݪFڸHPFۏ=3T%-EAԦ8ɈSyP'iWD$HwAe^iʹkn͌`wV"Uc6;-ڷb0Q #~Y$CY1I9Ka:iX#n:s]I5#;ҨG|ASBul%8X$jkXxif$ex2D *dfPw9ƽ>k Iﭯ&K` 0^ nO*Fj߇tK[#No^Il2qM-ΏZ:ږ*FC |k oE[YK" .99? CvMgX̷ l$SpNH;ZeBj6]D%T0YO #=.Ay~~7'8+?ŷ?˫-Fx ,F8kٜFX9WKYKC-¥#hȊzldP5 ։]uwMs$&fxT$hz _AmgEե #MRp3jt&mH6d@Pw3u9xzv-K rŌvjO.Vn4M2UMp$94_P8]Zյ]:}}c{qn.? qN{;2Q@%dPr@vV6kem ,1FQ7`JD t6r@fH2,t'JU,+)n'&#9l/$p*q%UMcA7 ?V궥:q|*:|c]xfֽ.NӢ{'lq\MG<E ;o}?VHbWuW9sHycgG,,t & 휓+R[I~!gs^}:y:q^Y|G I)^q= È^ψ\uE`1kؐe޸žׇb+WNuM=8j,E#)Z/ ưڃqb 6a@G^+w8ҷEKyloFs}.N1ֵϖC%L209#:$r6eFO_mA :UhK\KN=8:gm8(V ZE =6l33<r+j Of={ tK@`k8#nHa?5̊V^B&iwVrиʺX=J~bCww:x{ZG͇ElҼ9k6qF0Ptڊݮz²ode{gz`hAg~5DrIPbr@\vfo*WGr_(֧f,s1\;]ē4aRIш g+m6#,[k6N =WG;::`~M) 2*ǿhh|`Vsq<=qk+>+k+?Csq]4䝸#W ŏtlSQ )JA5QxVPԐs5|O)Nzd;Jd$g3h^hSCU$jxY$.3-w9㟜Q#J8%؆ٷvQiw' ֊/,8z+kزLZK)tص4?x11z_dfu"5Xy+۾h"Q\+/ZZdI+#8`Ax=;sG]: ;RHkv4(LH6nKptMo[[Qu-4ZQyP\*$ X.ԟW6ǩ𥽁CnNrW S'!^+_[m"wu)&3rHKHbnbE6@08*1Fl0E Pn8idFp{Rw/j;_,o *ƹi%ťvW[p%+G1`)>`P6^]K#vIhKI-:zV͝xF;!Ԣ+yIa=T\5I|x nz&i(SlQs1 .Ȑj>O1,9>Տ"Ŕ]h\(a]rą=VĘiH H#:gbZfMB024` n8%}W3Z-/5kt( OӤt]2ml$I=֠}*;[pH^YVcՉ뚺QB=i6lB#ҤQ#\1تNA &r`oG#klֳ҅ns#D883RJ ]HZm_˦+<9*zI|@\g$# VzN}41nh"1#Irn>vi"Pg ykF7~jT,6WVKxdxـ)j ~tSv`zvʵM2Y?ٞ8FH?tחVvd4_1a!$>o-вI%q?yܚ=ռ*BR9\Olv"ɒkim;L'VetaBʱ55O- t ۞蚌Y$/J6+ -P<7WdHӦs#Ҹ] SmFohfYvPWvpw-mso(G!d#|u淧5k.k rOOOtF\d,LB{շ1'8evpk&mx FS0vpfl3c;3j63E-M6%{5dK7IbSӯ",VVT :'sF@BXo)Dr'标r sϩ)H%|˃1ҹSw5$##3bs@z.+CBFys5aI6Nq%9TA*Ih+ZǼVbKpce|Iwp6WUk 33,y-D@o|tbn nU?\(SJTǫ~841<~ˣk-6p3ΧEP]"ЇRT.2>K 0@L^9]?Frt q'<_)sswrS I-j磃jɭ̕ngcto%E?H$M#D{*F&4\ ((()>0Ny)0p=9Y$6WY|I 3n%`q8zz+ʯ [v8\z:z[[<2Iγe`H׌W-ImSB1q[QZSWs<9G`@jڽBQc2> lUo ܏B| i?]n $0\rdl)Y\_]$V ڷP$̻N,dzqW[-pbU ߇53v5m5[ /5S5׭iYjoyBdB3'{kK; TIm#zu[JKHU7Z<(>ԏq8Y # ֹ_5Ww3y3 W`ז/It bݺ瞕~IdYt&+yP;syCkk+_:1j,kWtr亟4cH&3BBrx9R\=pLl Ş=NGjJ qNJWڅj\\2^3[ ҄v] pahҝj/cH6$^z&[TqH[n.(jU"w^iVdj,Ľ"8 rZyx5LqY|AZjp]0oCcs9j`{k8WTdRP>_ ttA+]eIyhjrClcqzW2#okf5(|naߞ?6=Q[u.ӌzԸrV<iݭd|W1mo+aq"'MGJiss;zLW ӑںa'c[ >[J5oPIf s54sb$G#1aۥky$)`z5߿Je0l8$ȄgxN)\):,H@z-u$`sk|{oҸ$@|HRΙ ~%CTʜu=5w(m/P\(Oqئ /'8I4A7?k@)KzVT%x|>{f5p}k}Yu=5To*7]Dw$1dz kΕ7cU#b4Dn99ːN0F?J(i_Y~b תiZGWLdM ]9'z![QιÁ^GZŨ閏uAijzYXdg }ij|W_[%ҖCR zQâ_IX\y#fs^u6ZTMim";Ie@H<\gUx/mn.nm[bh rkv֔00\qcc}” d~SZMrK}JX]I&iyqKrfcbN@ 9Eyw=>cy-Է[MI"m Ӽ5sqy:kmwhWiW.K.yU9/fIHdKk0$rt=j=S#uyӄ&HNcyǷ5x]Gũ\Za=2)U`=f{tyC228#?i-%K 9 Ϡ\:$]E`R)pyzT񥖣\[k4 L,98TG;IX "E )AA$\&YKHcxd֗Ikȯܵi"N>R?<d '39nl撴P5qT氖J's#,O)f! r}c+jpw>Jx}O5u'ʮvw Xmq›P0eݖY'#2i 6AV, a=q\P.oYY#\LS2e%y㌊ӥ6{Qa]_`YiXUN5rf䑓dn#'޼Qq}(+r?w&Kxט>cr{wR^ v񲟟c rj= ʋ#k4vCѝ\d O_ƽ&MgNkkYg.e\ gQ*vw:J2wu6;@s$LHO^s\-c}g"I}"$}Ӂt{"sӊ4@sٮӮUc9=0 rr\Y?ykiߑ"cbݗ*NA]{؎u ^r{X]?Ͽҝ%r6Iu#\{QQ߳Y:ۀ@}讈Һ9_SOۋ$vֻPH AZڍw62. H.{vvGw$#x@CڕIMjsΚ+[kiw Pnz.U #N*@gI0BuW~'o ΂A ha Fm#+1oS5P?I?iO@[iS֯q;Mc$߼$dm 򨴻_WK-,[i z8<~Uk1I~(L~߶ .ur4o_/YiOGcPur5? uWu*pdb8 TiOGcPT?iOGcPT?iOGcPT?iOGcPT?iOGcPSWMpԿiOU)m*.r}(O]BAskҼ=[#pT'<汼 ,Qv2A9翽zcmYfeqܚkVwҢnK C3-fE$15<%N22+% 4PDdq_NFf:J7:XW =Hp=}k Q{ Elw<ԛi\CHbEfa zw6X@@\jwLNGlTQI9|q$yio%VC~{Lʲ*;4gbQZ;hkk&EVk1QہW</J|y]ݪKD4%IJguR8n=i6R,Yj:\pLx*+X07 w SC3Zv@jԵo86B$xgQ\spF\َx,vw#mxЪrF?Nܸs2lBNA㱦K D8q[ጎq N:s4+n#ZT2Xd㯭t&1Qzr{E#uZ9U<>`$q=z淦A}G"3k}-]e>ahJ^ ˹yUHcTxqWa\oC rAƫPkR _p}TGKlC սX_{i\8 wVFYl],@hȹ>Wa/Rm.)YL;뫛i#D =Imu͞ҹ{FRgj.f\5;llww(<sekr3ZC2l*qǽ]GSQ˫hvG#>Y\ڤycK33 !BB8+=^ )e=zr?Q[SalլfqDMTH9_B40#3[Or3xր*Fq;ݼY=U5ԮieNJ>Hv!K&B;}:B&:[ ˵w={[i[2I`RRg2d@#F:sj=?j'mIxdlLc_^rt8bg2/G:I\!pFIZG+h뙯4H AldM{WE`Ń!,!X|awǭ;\w6̳/ =ѕA3(((((((*;bi'RtV[Z&8U@< $hϕG/\K5Yk MZPin њh7AaG8fsW`qAW?2TLt n###ohnAYXnll[1:.1x US"?CzOk/Sl 5J/oj|?jwB93[`%|GW.., $:ڹI~#u;Xda'rF;G*^Yy|g?oXjOsf[)d,z:jŸ/d@Z@= im<={ BK Y)޸h"Ѵk5 eV { cvrkjI$$,]^(jQ.[[* zqҪV9 W.\rJH|[lTcPJ']L-aǸw,EXgkkp]90K5n-dG;N10Q ުR3Q-'M[d","dXЀN{WuyHB{Z㩣:bag/=kӬ÷AUm"Gl 0d$ @FHfJkÞYH*!&݂98{R[9ޑ8y$敮U2ue0Csӆ[b2 E#䓀8<{]F-ݨk$4d*!GrΥ.]Za,Ztk+H$ǿ >fsz{WǧG? ʅmX{QZQ@Q@Q@Q@Q@Q@s!S\5!Koc;7F}{wt}iGy+YM9^"z@Xoky..RdUH؉fۜ q97[5cgȺ"0T 5y^!+<ώ ?0@ԧl#|UTȓ 1 QjN_GiKn"P=̊Pv6 H8ڹrs^!+<ώ ?9 ~Mm"(o`o>ǖhi= d簰h׀u2\~Cyҿ>O3Z[imL;3p xf ` 'pGq^z;]AV)~L[$G#;xV"H,kCbKc2sֹy̚q""`HqU'de)s!rde8!{&;F,Ðēz줹G dWkzA |>E`=?Zb|3HNFܞs[D2YLOr -i` =g)%woe\g65iqtW;2N9>FU kT՘1o?@(F =1BVu7;$VDIWLkqxqs)vL}+UJi${ w**o;xvi0= '|E0^lv{Taƺ@scA^%>9(1:feeؐsڴ/lt]X4W<*-|bl&#G?>]ς HYr 7~5mT-Bhʞgھ-R(67sJPTlpxbWkrO,zo%c''>Mnfs%%GmgGYr# )+7O2jZH`qk0w0as !v=2k:lWe ^}zW9!5*32:⯭r"nTsTm{ϥ=4i`RcVm&m3b#vca)/Bzǂpr;֌rkt${I?pxQWfұx|xg$Q噥qQ& @v#ZIq3^*69!N{ A3[QVFrwczv<(:gR \H{*MJI7HfBF*{w{/XCpGLs}+xZRw _+e O2޼KGHgS`GpTp9Q 85QQ6mF*8=q\ qt~$=:Z-'# RjLTIkbM^?3O2>ʗiH삃{59ᕳ skn4@Z(rdj:& \gsiW%S+b6rxd#zM+QkWj6#|BG_| [k{U%U܇ ՝&Gx3?ko^%$U4`]R0IӚb ʌJnBU]L[Y[MA"j/r(^84*_ZOkwsXd`61WYkS1x2bˌD* j*mJ?@*{fI# V&CQCpJO$g>՜h 4L@v}:m4 9}N1TqBuq}j~ TgaH4E6ſܺj ⼢P 8QAh$eH^~r͜8Qijg&M 'ץ2 9;0( y=1lsLzУs3JPYDN܀'+K)FBX_|N}jVEb#k~Y5#Cp+ybqnƩćVi-x(b#tLc(yۿOĄF#y"irx g>ܴIrȭKkdH* ]9G[&8rαQ¯qկm:wT0ojLJ|5sqqmNwS?8#{LQ}}V| <TQrЭfG Saj-om≠3*imԓ\ض2Wgc+v)#qeԎvݏ/ rv̄f8DZǝ8 =}IMVNM u4[Cn b:3wԋV5Pi\!vh (l*g9k#7y,2>I<&Y5oDye 0޹CE׮<1LrC@>\VZ5.8͖';FIa'xڠ+^eV\Z7>\2ǵZռ[ZX$mfq.mzq;M.18֕7I\GONڹ]*e+`tr]SX ; 9z  ?ڨg.Gy* v6"ЇF,rOTU(7* dg'Ki儆alu=wgO$>V9vt{& n bz=^:FBFﱀ9CktƘ^Lߚ]X1Hhd^{A`Ӓ=1;Ta)kp5pnp5A%qy*1ކu,A\s%sO~u4lTXp8ki+۴qb;pU(S6XԂ'ܧ*knDb4q@ss9I9Val9N췗4I>QlqzUM͓ӹ5h\3N܏t=Iݖ<?mP3K` u#ꮘ"lёpO sNGM5虆 ``uNV%m`l)rq\l د#iӵ"L5HC&x9+;q튵 ㎔v8 ;wSppzuC-v*M^Rt)8"Y:.7hU' {cVitq VďYb]6 s/ N+]IkMիj:ėzwRӡv_ homqu1۔PU}*ϙؚqEy9 Wƫ4lTyk$/3p:hOJ'$8qXՌlpT/o+"sל}kKz{W;7aB.ҵԙ-ORr['ws+~\OWC*H݊·xp̪\F=A⻣gKSܼQ2?\;°_`Fc~5&n=+ycE@{l@37x=ykHSQ!\HdATqJ0H9u\`w CJ~8v"ٵ 29QzmkS H9ǿS\VPKvI,݇ƧJYv=랫sA-}\}ryiĨB/<ޜuKD0k >t~ %I\ҥciQflNYƑH/qГڴmxRC wU/&6s-cE+z-<@738 (sƸINm)4g\z{cZZΐ,HTpr2GkĶW 9/sةՍJ8!׫)"ܮIzqֹg6(ܝ1lE '&Whې5iȷH#q9od߇Z٥B.V'1)iGDt~+(dyߧz}^ilH Rqz\{=b,䟩Eel݄?T9:Mck9f,p$+CUmԪ~,ɚJk""JE{Ӷ xcwG= ;o+&O ,Pԭ,$ġ~bpKoc5Q`*icy=46kvVRI$ϭC#4l3sڒb3ҩBȥnCr"x[Pߨ\-#:0Nx aszq]x84\CraӦes6[h#xnsp^)ejP@q׿_zɯt6rq'9cs8v5rbu޴:fF\TYǭ>h܅Fsej7 Hq}ҴPFvzj>I`2c-SV-]ʰo a%aLuQN-0@SrcD ٞz7zM,ن+GT{` p{UՍĺtvXZ6]uxpEW=Jfhr>Pkk IOWok{Dẗꯜ9};װK}#XLv>?p%=49f ڹm˼`H Zm؝'n#<{_z`s) q;SkC#sֱMx?1tb+yY9S^ayJ@|rAg#ڜ#v9HϑݘjX nx {֯4uRLvPs^ǩKN#c `v5J:9.@!NK;`9[k`Fz4'>GBXAkq5-׽f ,z͏4lQuZs?(aǽ+1SN[p9'[1yS6!;:x'UvNM`xE$22qfz6&H"rZ:5tMgQ~h#\j`"s9 Uq%ޔYۼ[ٻլ@cR%b\&ՇMy*t>7tl:/fكQ۽B8=ǭlja*cyUp^9K rG!zסY\31JpfΊ 1I*qnua4]iJFGN*yW45`K q0zwb WUK8PwW V팶O p\&x=sړZ)6&{aeR30O]6Gcb{WoH.vʺ}.KdH. H';p;&kdg⎤V)y CY\Dͼ1=c№NxBҺc>cB̤IcZqYqw9TԀvq`:dҖ ;bw8ڳ0 =L/H_ I'LVК"HҬJ[n@9̻ qBSyY8U\4=.F9npqYzzŐ)Frp:qvΜgH뚂b^D/fl|)bO@c^Ԉ9'(|!'=)9Ol{VnBԃrV^`" lיZ^2(`wzVnL0=tnsvk-Ok~#QL?^juq d`)$+Zůj7&Y]{`cZ_rEsj*LS0`$$p;;VЂIx9MY. C"uv@"SJfm` (\tCsUأc&xn?ϽyѕˡtC%,e pxllER=s1Zf0@+"i KK"r`Ե$QFn.B\ghz3Vdo{ccL䞃꾓7T=^M$ \({UJjnggj7ҟ-:UR7 b a'zWgo7#nqpf@=4I pcJE6ʊrH)1$:~UE/hwE-"EaH[޸׌Ӛ$ֈs+f¬i=('I%YV·خpZ(_$E<}k5&RZ9TREżAҽ#{UAwp*=8)5XK(#5|? rP|ӎ W(źdkJgi.p2H}p+"EIP8u+ƿpmQ[Qr{nű 'ڮܬ$f^ydJ/"8ҷ"/N*v=^/%.tx|ÀszsM< *cw>+Ҵ_LM4fg23j`\ȠmrOq+yny_ '):Y,&ѷY"dnI7]tڕ1 VPY\r0SWٔ< ]0 \t*W#.k\< ϝO1Zi[LI jkv"C溽oWVc?{oU:h`Io$$x 1;vVݢg0ķpn]t8qME{SΤ{hyTfmi#rۂxߎY'fɦ8M{U{F7Inrvv.G1pEz6|mU?4s_G/-x=*Q<m RF:}kuл#R)'㰮no } H#9kk3 )iZջ*Dku[מM-O4`Fss޸<5cy 9T }@$+ɵqڽPS^1 M {/Ao<,82`xQhr^%N}R0I`&PBJqZ![\Ư!cn'cQ6mu#U:Iy5ۨ|X!S+A>jCyjkFUrNy+D!E 5Շ/SہRrF9kѼ XMr3%9#?L.Yr8#UhΈ@iJ<╘"a]ŋqX`CNkҴK"7X73@q[Sv.W'dg\Q]΋.IKN AAtTZVD-m|la};L~#o7"xz`-u3$J{nfykq+='`,P x%O+I08 ~)omJ xJj%PҳA 9\v % m$JbCɸnG+GO+m +}5 vfi"{B ;o_J1,(vN7dAݜSC]g`>c] +4kOO>nU:DiѤ,d(}я^sxڞ"Km4Xh^ t6-%^ T(tV2Gj?ڼԅ :rG^ziݳoƑ$VvvH5xN@ilN>i4!\@pz<҂:T4M+rؽ./ԸǷJvDzQU)ҙ5MU*FyXcke=ˡأ hAH$Al{su]I\Ԉ,?Z֟>$cU\1ԳMecbNjUnՐ݄h^Es\szκ ?PխR{i@Bx e$X,n-ݳd©iܖ:7C TRC~k(\7F8d2A&8ֱ|A$H& khN8xdaBњqHC5bo˨e$8P8JRH 2Q#ƻ71Y:.7L$c?Yy3)}W^zmL-"i.5 3_0LJHsH9W\N] yټ6; 05=^b|JOۂ'\97=zeoYq$-Iu#RB 9޻#JW0 T0-Au;tҲUʜ``WBfBS!swAwEIXr{z$۠# ' zVμ`jx|h I݇rیw9wTP #Y$kub3 <Wa\F:RJ@>Ya<ߥnˮHv }8JMF(80ct;8&+drznLXuck9[e&lrxǧj&RfwjVWEKC#ߚǴkj[/(/ BG}wbBʠ.F;zR}pU[3ԟNiilt{M3wv$ӵ6e]'Orqu\^Y YEIۧb*-'PD4nCg,fq1|&S vsޣǖU'L1LG<*d s9ʣ[yi;f#0A^,A8Rx?_]9kLWWQ[4(VYP'5f8=q.QH$pK]nI2Bt*/x|n|==+MٓJʐ!xUE~l<O"og'ҷKRxV趍a%aM۱[۹ӯW!aw%XIMjtRHchoQ$m`HΛ.5c/qh̶ВTU9UPJrGif 6ַzlV$1"t %-F9sk}v%ǂu XH ։,;{szT"i檆VPx}MCm6o'@^=\[[ÿ qyӧtﵘ[Hc8>JHF\824^o'oWːnWϯ2-Ky'6rVx6;GcJV`2u. aC=qkė oqb<(scڈR7T3++Z౹!dRiMZnl.#&ʜհP+ͦoi3v\$l q{ޏYj%f\7O/"R%YA%^cb-/[HY>dp޾&mJ'= 67дH wtMo 3WHdE(?Ü==W.fΈX-Iu d8xē[fA0H}=WU7eܲ%=翽pZ5> {ff];9=?3]4a jR01?_\B:q-H$af0\M=ں=,-4tp=rzʲ f=~x^&Pq'ϖ7*Ѫ#%䎕K}kx"2UGyϿ# ;{ڲٕB:IR"r=NCZj alnq<0#tOM1.e\  c(ԁ_2{8}^Kc[DrC6X{!7l' w43Ib+QJG_m2W_|r+CM:˜^U5y ]&%rrs+rQkX·3G4*Ze!sGM4\NOoMupqYZRD+c5oe'jiQE8##=?bkL[)lc溢e"/ yz-įqi3Һ;xfF)@XDZkuD5իaeBŹG} H$3SȜ9~jD@tƎ_'+[jV 1̽=B+cW~Fa-Ⱦ88?Y5Y RAz\o{Q^B-I$^9ǮRwш̞DMD9.IwNKp**%ZtH9Fu5w҆VD44E${. `_jKC 4lN2sֶe LⲮP8e Sam.;(R1\cڝ: ʑil勬y N0 tr Af`]6Ǯz⡦.d˱b%1q=W5<3&SנC0hIw<92hMn[Zo<%\Lrۏ_|lUaY1Gdz9\ıDJV)VXՒ5,Cs] VhhsR|$fx lKC3 \UlmBaqq ,ݍ}qLӥ['oG^WK=(Q\WFXLG MfieT[yWQEp[/=8۫Ej#v9r~'׵]2Q"ʋ#*e&aFH4a߂k[眑,=iDr\Dm)m!=N3sVa6SH'+JDϸ;+8yH8 ۏ!2Gp+Wcmȹxy$lǟj^\i^y[ קz}R*޹NNOp@bTq\B{F9P*3fI de9*5$-ǔ|abHcu=>[>9Tdčgkk-Q ($0qӚh~.5_k]€0QЏ_ZFVЉ#Km=s2'Y:uV}=ȇ#G{0뫥i81로"pk+kX8"M\N*:{&@ipԃ9'#楱67 syByF㑞x)=d슊?^)p0Fgf/-(r1XMuvoZo !< "ź|Oy:c+,4Z93לD Ԋz43.cץTP*1 kث%,o hʞhȃ۶wd훁U"YC]1%sʮ1 24i=t(cgp2ϭtZB[-XQG+Ѯ࣎qNj735n_۷N1)=No>1YF@O&(P<6N9vMJP- \5`"FmƲ%gp]KnED7DPGI&6j߼P,ҧ"[B15m}nફĐO|ՙ}0kjQF~*!w0[K}-In|vm>.c7&pzzbiW":F=:@M$Kbh[$6amوgF8@+>[ [u,6 r zVy=y c>3^|NC׎E$iUpq qcm .~I>+ )-f@Zӵx-1(O~G&ɫKPZ3ϸI$! =ԣ^`#>N3Ԃ =@c4 PIxx3V.ňI[y/rx$sRiڵD*VbkW~ ֦`ivhE쵭. CLY]rA{J3T#ygG#\晨xOS[^}}D;?CUd^ <:If9 _(ë"*è5{;y*;T֥&lg0'WL=ԕN~ufOp~e O֓llȣɞȠ1162;ґf{y-`^6bI¬qL RM>h'fΗVg*T`}W"XE"dSdk񭪴j&{j5&bvǷ>~]R 3 9Q _2 M ʤm' 3#tyr:FOj|o\Twڳ7Q)3\G#?5]\Gor\vA 9WAB98ǔq(q887c1\`W,⩈cN+Ks2FHwg)8,TN>Ic4oH[qK#HsVdi y"|fѵk"/CdU#N3ZϩXM)l q TTm>ZT9<~U66!Jb}x^6Ppx}땂 ePrMiDWh'8㊨ǹZE1F2Nhm Fx c}ˑ^N&*歔R*nMlAxUj1+b|S"}9=d-H';4+[pZ;#TçjIC.JEy[k>V5bKJ4`庒. 8RZʸ,03>՝dai i3>IB-(_j {h{yBZ3!f*`Mkgy* '#ҸKAծSjE+OPsְ^k&m ڻGsv6WyWS@g C1ZGm/4aI'1ج㳽nY/ ΄%pz䎀5$LԮz^m k^kOO#v:c9kug\Z]hAf\H:A޻hP 8rY&Rl.$QA=+OU}\קZ@j%͘{ >$ .I#b=l׍[U9Q=71;H0z}0k K(Qgش۰ OܶFH_ "$P1\i\N$FI&]bǯ%֑XSCi9 ~KB.qwd2CJr'ujxE9<p={S2HF^KxoB{r ʂu:nΥimzyzA zJ1iI6tq*EI*[3EOoP֡ȓgqیsy06ϓ; SvC3&noi) *:n-e?Jt:&nfB}:Va;ʱltCOIh9+# s0xZ5T7^@ݽ>v O9̋crC.6gj9'U4$Wg1ܳ]JiRyFGxvz|%:Yk8+.^ǯ0v{]J;;TKs%Ӻ7F^+ЫbosNҰw~+ŎxHR% #bww^"S6O0ڵq[+p1 ՋXY6 nvJLb F $jnnTg#0E+G!Sny+dGlIRo"7:>1+sZݺ) 3Y&Bqi7Rءc\{IHey!>[LJVn#Q5z8~bAHgޅs*Gk#_\VÍ>F&HJcj&9a9]?JڕR Er3>mjd#+lrYse2Fvgc=E,L( v֢ku{gi!rC3OZݳ N' rjTMWc&BdSH7~5%=>aUo4rz)^I,Ii4q0N}WBz;6Jmd*s랤&aG@G>L>w1 r@z}+Ǽ{k)r\|zψyfD=0?4 e̻x|(6iqrjHX)7+l v###..85y[Ι}=jk)vB4{@īI*XQT3SQѺYn$z@qMľP!5i]jyŷ-#Uhzm$(GWUta]\Ϸl=IT8OS7g UvLgSWi9'ڡ~7Oƽu [9 VrXsJTq x$⥲bHrqO~j`gAuZݿgml}(x zU*$jEC:CYo$5̌qFz/%2LЖn݌iB64.ړȦ"R[FNy*9#`Jz5fK&W5hhЪ(f@JM4 m~8gI w+&vMz&rZW22zQ4ln4Ŏ֚0s֬ꭶB#%"*3H|GJPȓ"2h/Lސ;O|S y9.Z61kW?;ChƿB%NOOʄ#|+p) C߿" <.g*oH6=ﴵĔqN;PiWBqcd\Cӊcnr1ZI$*2mlM1?OrA!sj:|LHUt(H" `qޚ7rO5Q6OH0fhg*\fJE93R>tpdtRO$J>eɏ$*!U=*p?Jt c4cFi 84'灟΢Eœc'#J=)uv04*̹~ic#ܚ$nAZ? rWN9L3ZpZE8<xTdtIVPzqToI!ՎOMtzXL9I8v;WOX~dlDyP2zdi]sI"3qⸯmUrQu1^Ҹqԟ~iqO0jh` t+!p=k/rKos|2>cȬ.I[W|!-Mgww+4-!LO|<nGS>u Y6(qn HF #9V7,}8^]ǭh(n㙡C` ]fYCJ]t%X;"C 'x԰לW/ާe:%{Ou #MeI`׊ҵ'C[{N L.{׃N;W[Oxc_m_Q.@r*{&cמ=>`Jǭ18 l8>Jӛ¾ xtXñwwlo\=Fv׊EBF~V+yFi%fpG5;ihBy; ts 4dA\gu588vRr:6.eB+ qsWM|5;DcZ{VfoײkWnDv&D5xE^.y^񪗚?jU@xȮ*=UV=7j!qu4ҬG8OiʎTR$#3RRa$5Å 'w 8nL#x&0&LvvM?x4vF =`fSckh<¯jw_S!\;_l{b><1L]ZX]?zWyzsyT~<+`Vmd(zzcꚞok.`7yN޹z(=Oq*mNӖ==Y6U6_};ù~t1ȫ:7 Qfaj]Fռ=>maH(-~ʴ}L-j>@96h G=EM:uy ӞMbǯJէ#xZEsW0ڐ;QN8{՘$S5eK]rsSVmrm';sߵTUwd,B" ꧬGGF݌;Fi㧯N0 |9OB<-I8}尊H" dkw[.U9'kSDcQЫ ^u7ۇ+ӧJ_-K#1r03!mRFqad;3e`8֭Y:涼MZ}Nsa4GO4Sܒy6# tyW,O͌g09OٖrOȺ"R$bmڡ$|?>՞;aY]%3(a?ҾJJGEwf֯)nQW-p7+sEs@A# ]=%rH"%L`2L g'ǥC$F@xU\J%Ɣ`dj6A;8iޘV^sR9KF,F:Qv=*$pII hb3Sz*gsܼI>VOgsS".v1j2oZE%{ՍrxT\N(, s99vͶ|pT|{' 8j膭i:mYpIT$SX3d1\UգhF :`~/W w&o׵uڴd;_]H9\)_cfeG5iq]ib-Ϧ?tukRDByn?Q{qk 5$+J? x;a*)$󞣧ָMwFI8T#<(%Nsߊ?{b_m?LZl:ős x=4WZ|_{}"le7OkbH{O5-3RI5{tzXv$ rW$ףKG9d*sU.O<:֌r>j&@j>u=Wf\5r?tZmUK(##?#xp[@޹j᤭'p'=1VbrxSR-Y:^Ucq^JK;!4wּ#HKN܎޽IQki QUq}?U]@fb̒52H`r+Ŝ-ա8qu2wڼRapFnkŴzX |fcG$8b;wh Y :+%Qq-0?jB[ 璸?ktՎ9akA5^UheS3z\dzpK+(u 8^_GlY*bGlg⽶#U#uQgf;kO)-9֧-w:ԕE ]֣ne(:U8c;Pn>hoB8(†l68cH}Če}3W-+N-\Y}h;ke@]qJKo,{>UlgT^rZ#9qseflT mY'8$Li^̆5 ĩmnYU/^+PDA@y^գ̻]$g2j̉U I!sUY#FKV)3"KpTS"(H3+HXvUoioIW;I: ~k G۷Y{ښIXzCr!+7o !Jw*}b.4۔{V_\i+1#gκRF IwFg nSKq5ϛS̀349@y9VDYUL3?ʮIܮm슠G^ Z17:3JPgntWGVvIigr~tx?wq`I"E瞟_ރ+zCmv @3YqF?sS~\bĩюsV,IB9e9zX׵>qJ ׏_©OFzz $}V 4+򐤎t.$`dsң(J< 1I7), wk@OnzWTur#I㯧z5 IAPk7-Ǝ$qinHKc$r9  =o3b.$8MXˤD#w^;U7$(${[Z"ndŠK\y=zQTՄHOy7ָƊ+xly rJroV[MNB(SB%ch>z(bv?EH2ſtZ(bGi%fBhPH!5ڟ_*(:1X,~4Q]X=RkZ?z+)V+:~Ҋ+b֊+/ǜb+?~HQErϳ4ҢE&I?ޏB!Ҋ+w>CVM'zkJ(r+]uOB(CCK>E0E gC~QJ ZZo1E2Z\tR}h;F kiUv+dAS_d >QRR4T_񢊕J#(>A(d7V>E#kOTu+X}\ 5V+VaobSEhzP~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-20.08/images/manage-albums.jpg000066400000000000000000000772741362435004500175550ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 344 410 0 C     C   H" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?GL]";{o" N SV?5K紟ѣSi?QQ@~?4}'j*(_O=j{I}Si?GڧF%TO紟Ѩ ~?4}'j*ط_]FoKRPl=H9Ǜ5"O\O=j{I}7◍b~oeݮu .BI)Yr/vK6Ė*)lq#O=j{I}/왡@B=};czMVkjRg:R0OUWj{I}>?5^ t&-C~?5tm2ÿRXZOez>.~z^l6cnn# *8ӊvk_ J}GڧFO={f _mr.-v{qϵw:Om]~A{2Y֭xȘ$>\g)uv>RTO紟ѯm?c/!W:?#k}^MYeDv4^w[{zƱio =e݌x\Bڋ<M?1/STO紟Ѩj{I}>?5/ڧFO=EEK紟ѣSi?QW§%Z_(Ϳ_j߶n>]_ϵO=j{I}2p*Os#mY%cCu882ƾIY E(|98Rg^#{wgq>{c$ D}+iq *֌}T~؎ C 9քy =K6[,kVVӦ>b$EM7?4WW?ό eό e5;?GG;?G@\gz_ z_ .|E}3@/Q@/Q>fv|g^(v|g^( 3Q_Lû>3/Ktû>3/Ktϙ> |_,y4Q {B6#.'m}Ez;?GG;?GEk~_~?CZMNk=`c"H wU=_mÞӿ 0Oi5&xf`.-efeݍ: @/Q@/Q/yYWIlax#V𡸏ײu-oFo_>տ·zM߄/o]lOxq/*FEƒ@/Q@/Q~ֶ +[l|[`. ҸׯiAPEmyVHswg2wg2(Wٶ~:vd^IZږrڣ[y[87Ӛ<Piχ/x6s&^$HdMcû>3/Ktû>3/Kt/_D:eØ=ge}sTYTH ( ͟xz]Aa>1ͷ*@'p?ޫ_ό eό eM51foOHŽ3/Ktû>3/KtݛwUᏙݟ:?ݟ:asju +}`Э©Pgu+/_Dû>3/Ktû>3/Kt/O}M_YAm|]tu;u5_Lû>3/Ktû>3/Kt5z._wh}/3?{8Xv|g^(v|g^(j_Lû>3/Ktû>3/Kt~vMۋ+^Ƒ]Iʙ8A<1W7>3quvn6rkz_ z_ iWUSvN73v:IrkgZ|7?4WR:3l?'ó/j76:<[Ls 61'kᜦ?ك#N4$gӭԽL=QA.w#3K|}k_|D|NzӴmn{ m>%d0l.XrOv-[⶿M8]\jjQHw T>#m󌋹qq_SL=Q_*EZ\=)kh\j8K8$9 z[{[7ś}5kzTŦP,,Ab,*OJ;ҿ7`C8T}h>$ =z]KL=C^.ĽzT&> R{]I% 5j:m+w?eLnn$Դxه' "x7kߥe<+pZ$evf%P}zLY48']!l?>/ hڅ;zwøMҡ#Ery|S5j.)~z&xK Ҿ%N񓽵˧,^hq.DPr`QukEx>)n OWA|K bHR'ċ7^\Hnn5rȲO(9ȤW {m񟎿nGA׼S{K YM_]O~I(z.[ֱg~Kg>Iu}eڎbH+_Ǽͮi-zm,6m*E̩=qWO A~*l;(V,͵ܬpp~ڄ>9]+ĺ߀MI7w$k8Į$YI  WOe}_IW?_:_ /|+aCkMrۤm˒8߂fjm/n'Uvʹ*L q>o4}=j~4tim䈈8mŸZo?7}4+Vu&)-2rsꖮ_s+~|R)5M[OuFI3;F[@<^U~zͿƭ#·ZiZ&-7w;j6Gl˼n#z1ڍ+%<9~,MO^ O.6D<" AA߂Kk}/`x_[a[dɸo/ ` gz'_o:vh) jZmͦ}> )P1)TⅭ7GTW|_5߇?VaiVG'TOr$ pc g=뚃VO<.mɃ4}AEyFèR 7X/\"VeJgk(((($_m yLB>>zp/e ]19<@-c7Z>}PMSH%ԒNxG_DHtcȴMSw_/'|+q0U[A۰i9Kҗ2?Zw~ 8ecGm,dW9?ŝNtgBDksi+n1"txsתih|%]P^[Cl2(7Rj%ݏ< msv){o{kpX]ʰ#j? |[/j^'XDYRH<(*6㸜{%_?㏂>"u_x61jϤǨ$E-6:aNAZG?~3pӿexOuмGK 5Yt$F nfe!ҷ/xIFx'xƽs0hLn\zWL,r:}a+ޫFX#QSuכO}*x׎"<*kb#3Nfp׸QGX |3  ˛u#&iZB6n*,>mWǧTGI>6ich׊h9_kyٝw@p5XEYme >k_i'.4]- Lw12<2a$b%)kzž=W7izT>Kk{Wglrd_F ֖_bz??7|?u/-Kã͢Ep9K4x8qcOj/'Gٶ 2ňd}wY;#n/6>!t{ѓR{7TH$*¿١_>>&OIg~ne4Hv`nFkh͏sϾ|&SwJ}T˪kW[1s+Hcݑ>Aˏ Kt!;W?vE %q)y|q_KB?OzU,n4?zd Or5/.?@j+.Oi"uK ơHm٢a$gryz[!_|'& ko]Gx+ұC3^?ɪSpN/lnQ0;sT .%~U}UOZcƟ$麴z(1ȫl*0@|uiڄ"ҼwvzsګƲA'ϭ{ hke|1YkIoZZ+\C=1*`^ |WQcx}S2X b5vnFQJ+1p0B^ uOki|6a$&2opAJG[Vzj>{ZC*[_Fn;&'vrOO,'{|uy/I|("]._;+ ǁʶALWY^*Լk+ĞO: mncO(*x9?1G¯zMw_R]>khs۬[񧈵K=vG<F*np~lx^_-_(~z>1> 4?I;hEC nl u@?Ѯk{jTcOn|pQ̏rsi߲.Qsg'SdžaD,tU#~G{exSBMhRɑ:ph[|_tlKO j>W^ˡk-minH6̶z2Z/ΟY[_+&uhP+qڼr@דÚ%7^|OxXխu'̄ CASO%]Gi^I B :[7h{班WcxnmNN{e ~~Cs jZZE*U0v?c z&T~#qk9ɆR cJ<[7_0F=OMƚSʁv~=i+ZMCRѴm[P/2 Kg1V1JOy.|k5m GOՓRyn"?pWzO rr+[o%х3*7?*_;r<SJX[xjd[)%tUNȠvb{?_8^CpK bj51,@ʎ5 ,}hiHG$k&i˨^+SMgo >.[ۋȮhQ` 3.fO_G]KL$MٜNpd|ܑ.@nYh[jzƫ}SÐ]kf6pei< ۷SYw?4Wopx{úCs#{)wG$kWeM֩]^'|E%78w.N#rRxg~c;G{}dVKyp!asEJtHuA -PQ@Q@Q@Q@Q@Q@Q@y"IÚL-G_4`zs^ oak@?x{_d5 W>6DVLvkuF ICU@ |EO}F=JxFBJI#"A$(Z\Sп Ǩ#,j'*';?u*MKJ]oRg,V$dV]=qֶJuk'FSMaRyklgCP?Wv;B<=BƯ2? ǫOڗ^*~񽿄4 C+ɡ~ZVh;O@;>|D'uދ Ev(I%efB#8 E??xW VQ4HpTMpdWIw<k h׼y[V"mahFIdPYl(% ?E|۩ڮOŞCX,+Ѡw2nA+Em=_VWHw֖r.TM)uV4~'PzxH]b{}i JSz*n edד~ֶ_~e~]rH̅ԡ̱р$g+=φ#xs!Pֶd[NzC,x'~ k^$c:+ZF#b'ch_םgE~ zΧ-B(%|ƻrgM`a{WQE_JV (Š(((((((NGNH5 ^-/l2?>gt{.<]%{6 x*JϘ|ncxzV:u8 dxVk$dxP_RGޢUi ZCk j7k$E[}k.~xA?|G?Y֧am\%IyHJ;,><е wNj j^oa}Ra1۱1O r9?ڣ7)G]:\Z\r]n-6-pOP->>>eoa;CTү(/m#4;8;K Gx+^m4> c$sN`3)Mogl&Oѳ~ў>=z~ v,/4}{+)$ӪB5?|xyi=ҽz\鮱LсH' 97ጞ13ڇ[wZt*8nI$W b>PcҴgۯ x3P/}y"ڡ? h_gxr)n-t~M9+3o3<) > /;E4LzsRt?ľ-gm#9Lq$pFrHNk,𝾵1X๼Ue[.BdcWC'm'$CbgqYYo VSO;׊5:Jݥ`8PK2n<Eu7/<;ZcWo5X^54ՠQ e0= {V_ ⋗;'=C#it]-WݴqׁZxG'g'GASMmBjY΍F:VPjz-ɯ?ߩ >kfsZZ 3:\}İ64  hLaXĚȼYmϙ僳;3+<5eߋ_5[z 7k<s}=я ,NF,䜒*⮇uvsE̟stEKv9}+Dk6ZJo.qC ʪH` ph;]#a-[QNӡ퇛X 1~|5N/>0> V=©D#pO꺞j&eIT.Fx|xGG~đQn4GuY$wYgޭfUl^^?n0z\Ϥ}@roh|$o#)g?_xj(Q@Q@Q@Q@pጰ)խm׈Xc^ BNNI[^ߛO1G+k5i+2(pr2xT)O̖AH wΚ(e`zr 7jyw{3J)v I)4rղ z)QEQE5S.NN2hQEQMgUe-րEPE5QK1 'SVxO,Hg (81H5%QEQM2"<րE5dP uQEQEQEQE{tռ!xw[MDMFЭ$Q2:%pU~1k:/MbuRjVe:kq9Wv^j]W/o"džcTo5wkc{ώ2F8x{񆧪|>~ƠZM{͏ʒIn!#<Ҽs ΍^g$fP#LU$NtZvl[/x[|]^ <cumN=TEsnBnc;~z~$xJB6RNY-ƧkB4Mw)FU%ר|Y7#AI_xSr$CN3垙Z#ދW_t; _x7YI i۔ o*aIK_Tw4wEkMkam-P<,"grv}g]x^K$nEnbdU/Van5|OwwGĿ Clj3qs%V85IaY ~%x>^xTjuV0ܤyf&3+nݑ2Ii~%-M+Oig__u_Amb}_m\4lIflzV>>kzLJQ~ki>N7u?hܠ"}(WC~6ymXn|;7-$R e]]Xdv9NJ?fKTҴM[ZYjN!Ixw3 @%ntu#O{/uf/=mkhg Ip +5/ ~*x?~F?&ѩ1._o9W|@eO{Y6[OOG;J;6̽3^I~^+/Qq_ C즆=Ȯ'\ n:YM%?~kOH|UϏ/j ,#c|@ J/" .qjI=kI^)6xծ|5/HDd1=)O/ xxCĖx:α%jP?D܊Km<`Vߥ'WXMi]'vϪ rf) ɍndjĝ&/y q~(P !^~1vr{蟳WK{Uֵ/xt՜[,pf4 n>^y㞵k_uKPsbi6}p𖓎S硬? ]_cWge?;{=}+# _j >\kӭKhͧnEC g8M{eYu|6+h^…\@#8$dgW >|To9xUˠ:m4W1<РsWkz*╊~:扥_<o|/wqëM *N"ղ8>(84)6o}Yd/#nÌ;}1X>5Ox栺ծ."&H䳸2y zԿg߉5k~4|#:o [ylnHXqr龿Ů~z`*KhQo^ӛUҐx|/͸*GrW!?z++|,jvhw74$T3#hd tg>#0,f hW:](tM*Y2>08&i4 ͠ZѼUkԣ6Gxϵ>L6'[[ݧ%{k3jմOVTv>4ե5PH.XeK(x |pe|A&AeAo[wyK&ʞ{b-Geomo^G.*qҽ#MsYw|rVͦhpiIlxi71_Jktex?MBi5;tsrq%.27uOoM>k^{ '}]}FY=òD_vv8~ jlVuK:KYUl?&Ÿ6~-ܕf8b?' f_btMF=RV]Y"S2Fr*/_-umgm_Ovk#=^=BAYb*#qޱ,Kto -POzε ǚ#Bzt_Ljk]ѼMwoiw=񫼑[Ew`6Wq+!KawoG^.4{Kdh]Le\2/>(xAԵ< aZ_GiIDU5\sZ6n߁*{?G?s 5=F[kŸmelݓkW>/mwONԄiO,Oڗ'M%̭%$d^W EVդ6E0Z30EMoVSƺAiVն{xB@o8^~_ =j|Wωu{J RYOm}91?/smX~п_YxĢw6PG#Tt.fGqEq5~ ]˨RVfIw# <I}x? ܾ |Ezub%ͭ*NBғ"~og .v<4$ nEy>㏇^;׼/k :>MKuK{S)Ǽ;+g %]<uO No,|=qos4Ȭ`e(9`GU3Gᯇ|+QU;c,\"dZ|}yƗ,W.'cAfnT ⶡ◂'Z4 ˣi,&[=\4DʼnU bEWo_HuKNԴKzmXшWG.$kymmh{~~*$:V ])&Ț]'sc?Y۫zeկt*k k}-4'Ue{(51y.h͘lh$)[ F[>Lk[t-،>qO [?_9] ⏉;^4M_ZtHgEU` +K?ߎǃa'Yb ìBM x|E|K|E%0鲴p&Xi6O  OM ]eR!I`+|S?k^ GoYB鮿CPbV3n >bd?t;DWV^W_ OulG#' s⵷ k_4XZ_QB| oh>߆<]5׮naM_1orEp֪cv}%q/>,;+MK!t;k_5C6V$t>2oٶ8Y'Trx#ψkm#˶ɑ0zQ׆kX| L?ծ,^Z1qI(`XdGp~#x=70._hy\ZϘɹ~dMsё5lny K/Qjvk7Dhd\9@Wv2FFqI7enfMo+V>h~:Oxdgn|CᏅk,mi|$o~xVuN~k]hWIn#*<#(3]y>Wgv!49ExT_i}}ZFxoĶ:JJ\s"Rf>T<+-_R3/ِxW?V 4P_h1O21ipҍwlG}ZI$][v`*K ( ( ( ( +<jxg66:ɽy[v0&9OyO|iSmR۽1h5kmvS F9(i>'[hZ#͞%Q֮kjzڃZ݋Mݱ$gP{WQt_~SZ߈k-f rkտ6aP\E\1ݝ}iE|Y$-{6-sRwis4nxG5"Ӿ%N!|O6^\n g @<5K]v>GٛTOPY;Wq}+/UkR|7^j:wjWʍa(Xg9k2X~Yr<:|.i#hA$R 2\~oVbWm?kL\|Sp$^(oVܧcoB4a{-e?q^c*ym'KۙtDKaEq8O~Mѵ|7>۬~uRFN-dt]FY; zo1agnoA]x7q(y ; wϺ_G GS.8tk!R0-MK˂R$|N羾FO*XxIIWh {_#xᮗ?4C\ދWZ.ѲȎd;B=+G~>9x7-i(W oqqQuxN_j߉zTQn.l k`ϵvssȦxu ox=2 O@WY|#烎2?~~+Njr,>=)`:kk~ {QӠ@F@ZϊߊbMkW>mw;2kϾ'|4}k%ZC\:7i O"7?)?&}y\x=)5$t+a9F;H1p5\h2xqo7m:;.n%Py5x=[˧2KK >"^d2$2 ]Pyg>>i ch>_M Ybfe99+?iRxV߄%Q& %H;F0\ڦ?j_Gel5euI)7oO+> tQ!{ta:qFqhW&O?׵F֓Kyh2G0zW4,^od`icw v(v4n۷mـAVRiwdToWM 3|A!Ƿ{]r(|*yAVF {7Ÿڗ\>^Ѵյ)W+tЖFڵ 1(B޸ ֽR~ $qV GE_8gqO8EaM}ĺMA7 bqY>96촣,6_gB<@],x7NkoYC-ƣ*s4;>5iZ."hV:9 >Q>x> s$){)3pyutP'Z>, 4y(o*9?<3x#y[Y[PY5VUW;l;EY-L G]cҸ _ <l'46xsHy껎=3^E095ۍ_Tycȣ #I_ÿKsyq i;,dU$<λ)lxu?XVO4kRHwq $V~ xR[ iIg64\d˴?5QG|>]MxS:f4bø*?o 3+Y7:ץLJ?/tmgIrɼ-˙2'Vx?EOMxƞڀ_ޛpykf@QEQEQEQEQEh&߄/Z]\믉.-̉rϓ#zzMߴZ4oiZ΃: g5aF!l3nҾş<%'OxE\hV!2|qҫgݶ7?r#0i@ͱ۲1F+O&/஥-&m^T[tpF}޽o{ --ńSY<ֈZ-y\qZ-__?i| ~] Kvkpi2ܡC5bi|k7桬3COխ.y|r8'Zׇt&]32Xi@pܱ̠ӥl5^mtX5>$[zvmnP);&}Ofg.jߏeP|V-^Ime6 X^^-|9H꺧 wPFxYioMd0J1 ]}6zdn` "^sI'İx=+i2c` 70rqYxo=~OiVDmKj,YdR=W]:o- -m;b +M`0j ?u?_ў k>'ho f)d4ͷ:` Mf}~,>fOԡ+qшN<_yPԵmB{Y;'k6 pȃP3zz-/ |wC&isOɥGM8xg(7odee'J_z;[gxO{̞&4*Јʇ1H$-mz%ӴİJ0 J尌28b";G"ռeAmzNbzTz:ɺ9a6=p;o7:|"E|K!ʊEc܅D_ևԟsjNOCxƱJՄ[ݐ3Erw|Q655 u8a|.RG$௭|kOoKuQ(!sDQkh[EGp9J|k x#EH^)&aǙڪ:ҷ6/ oφ6xz|AWN  .I ;a]/| s{V]DA$ٛvI~*_Mi=UYOg|-"``*ɍzZgNZKEm-0ef;CH #*"*ZIx|??F7|'W6Z 8 d Elwg*/5/O67q_;=< K3Qwr`G57x*xklmK%I*ƃ Ŏ[nNV> [E}U5Rj sfdϖna[k?/]=/ďG]@(`8ȯ~!xA}DKhxR8nH?uc{\''a |'6qx#c԰pcHPev %woOu<%ZCWu]FC VG9G~Ot A+~ǓOBgKm95xSúG۪|Fku n T{O:nkz$7^E{O?t:̢_7a#@Ey ~_m3?|9y _hw: !ԡ{X6##g_W#=INI"ax8;;֤Ata qah{xvk[QӵovHִr[E:m 1X =&#߇5Q謓NQY=дd$H;@ ]k}E.|CXOu隝PtT՘|(i- FiRBMnyl@bxuO ?!g c|7zkGqh,$pJvFG.-"o3u+$ KE{}nY7ɒֺ[^m"~ծDf HyD19\{Wxlt}F8~\<bEGŶׯ&>ּ|VuY|9]CKD0jJsy*Ai'dFpJ_Lſ-O×hbյ8m^@:0$}+,hı\Beas_W|QABx]i<1{ȸcKy60R, ? k >5mNrTVXyz&ggῊ 05~t麜 y1= |[75/xM3U]rp|W exN gw͡koiGLk&%p*sQ? Xh5K&O֛XUVSgpd`#rX>Hko Լ[ee6szM M:FФױ^^֓]](di""K39#:u1xMZ;.OY]2f-K_y ֫kesH^[#Tc Ot wHe⿎:7x^է뵹t92drU Fz1]ց_,o'|?/:?Tǒ7ɱ<+xϋ?ikOj0] 1I Bג8Xw9, mIw{cQt":]\+3o H-<9.Cr񟜣ҫxkׁi?3~5H.%  ڎN3޾nFa&)ZxOVa"J zσ|cjuܶ:[5g2M6 mW \l~QEQEQEQE~1m|}^MƗǟ?[Pt Cy& Z((((((((((((ɾ"u[>!MoEqi1Y<{v`\{FiiVZm+ocg [ HBU(((( eݓ$)s dHʒV?ÿX4Fm6{?J[h$eiJtQ@Q@\>ݗ29w)>?EQb(b*GJJPOA+/"OA+/"Q@~?>?EQb(b*GJJPOA+/"OA+/"Q@~?>?EQb(b*GJJPOA+/"OA+/"Q@~?>?EQb(b*GJJPOA+/"OA+/"Q@~?>?EQb(b*a}b᳚H $Wׄ< <,|#k0&$^Ү5Վ'%h~?>?>9Obhk{k 9E4NCFЊݫ4̫s3SSJJ4>cXNG6w4۶ e1mQEeUٯ༖(1;Tz(n((((((((((((((((( o ;]趯5oF/>o[³[#9sacY:}d!HC۔ur'|jHDoo3 g>tk9nS/xeZ֡J7_,%Dr wqwG.|)/7:zMci 1E7x/G ž!Uر-a@1ON5[}?no1̰OI!UP.7*is[*|7|?>Ҭ{W!oǽ'gIH?0.y+QKhW+}ߊqxοR? 291 606 0 C     C   !" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x}.޹ MEEj>|Wjk/(Q_VGڽW_GGڏ">Gڽj>|>}z>EQ_QȬ{z,ڏ"EEd}ޏ{`5}}/+#^}ދȣGYj^X EEj>|Wjk/(Q_VGڽW_GGڏ">Gڽj>|>}z>EQ_QȬ{z,ڏ"EEbvV8!N+?%g曩ig~K8cHVHب3;Oz.+el|ȣG^]Kx^+HNxd N@#ළl_S_f}}/+|S7^ K 4/l1BgIuH⼗Dl4$.u{1uk(ݍY8i`qj>|>}^?Rx?C 7OUlS,^r `zV&OÞ|Wjk/(Q_VGڽW_GGڏ">Gڽj>|>}z>EQ_QȬ{z,ڏ"EEd}ޏ{`5}}/+#^}ދȣGYj^X EEj>|Wjk/)q}p {.=O/ڽ|A^Xy>d _`(N.ɖ+9,o&CueEM9ڨ5!E_AqGZuV zWmk:[ W$Tol-2==Z>gTkЊY$+5)MJy)XN{ =@\olvY%'X R4)J*^,f"xha߈.,>{%2$G_\iek}mմ$8+ܮ&6#M93j 7x;c?ǧ5>~n6I2'x8#W~YRmuuo߇!F+յz ߘG֊Kᬌrp9'ֿBܿ ; z?ܿ ; z2n~z____=__=as'֏Z r7.r7. i>}W;|WvG;|WvE\I'ֿBܿ ; z?ܿ ; z,Oi>oA]#oA]#f?=~}hIЯw/w/0GO~ù~wmù~wmY_Z>}k++oG+oG.~z____=__=as'֏Z r7.r7. i>}W;|WvG;|WvE\I'ֿBܿ ; z?ܿ ; z,Oi>oA]#oA]#f?=M#߈}Q/]V{@"ݲfPU8vܿ ; z?ܿ ; z,VuKFgl!B|*޳$YvmL$o0=~}oA]#oA]#gik]#ώ6ϧx_PnH!_TI7u Q=`dSN=w/w/]- ?5x–wzFv7rZv'qQ*6sq\yfrbwֿB?ܿ ; z?ܿ ; z\sH>2x2x-qZZIr-ĐFfx;>1Uk=ڤ)6*; Wֿ__=__=U[GxRxӝ CAr\c,dFy뚝֣]=ԍ4r99$;|WvG;|WvRb~z____=__=;1\I'ֿBܿ ; z?ܿ ; z,Oi>oA]#oA]#f?=~}hIЯw/w/0GO~ù~wmù~wmY_Z>}k++oG+oG.~z____=__=as'֏Z r7.r7. i>}W;|WvG;|WvE\I'ֿBܿ ; z?ܿ ; z,Oi)0[X&pTܿ ; z?ܿ ; z,uKvU!n',ڳuit] $r.ak +oG+oG.|(HkM>{%*>^1ͳ4ȮO,N~ oA]#oA]# iJ %8;5~gƑ_am;7OqZsj]8i/P:___=__=y\ Y{y{Ɩ*Y-{n~xފ q|6.Nn}WE+j!$,t 6/%h!T">B(] A-⯭Rz\+ n}0K:m:t$n+Jҭյ ҆)wjrH$W>;Zɴ_k&Ahm.H HVGojuF5&Q^wWӾ_h^%Ű󤍟3 me 2;J+ĵ?z֍+; _EUIne2Vd1V8vjm?׋/o-G_ xW}:Ops1tmIsY~_ݯ~_gƱbi&-*ERFr:[ _m_V9+i!!?nB/tS!?nB/tQ`9B/ _s _??!uE??!t1}|C맢1}|C bOE bA,1AA7]=XcA7G!?nz(!?nB/tQ`9B/ _s _I4'u 2njyR6~U9Q]s>8?~- {3dhD΀ぐ@އ]Mj'Ҥ ?e?{e?{xލ/A=c5]IQHu GKOeIowe?xsIMQAvAiVul{O7GO7^)eİ[jSm[X^ֱH\/!r{ -`dV&;#/졽um9>ss1RH0A!LSwVϡ 5_niTy78mAּEٯ:oMIZUU$hhr<'"F\Ӭc6cB= > U'xLVIb^Hg]8& ^|/v M} %Yye]fGPF7@-W>))]_:o5}6 ?UjOoF"Pwt ֝~3Լ+g4`SM"1eVl nrSn# lfy.1^(Dum4XXj1)zdVIRe `F$8c}s V05袊C ~ˍN+ne[4e@ WqEy3zd&'# XvHau*(F2֐q,ɚ;VX)}Wq=z5dx_j$utmD%Д{pe`ܼ`q]KywEE[q_+滪(3“5 aM2uR;#H9EMAQ誣~B煛? |gᴸo[pɼDeW#8ݜdgUN2sA>g=.&O*z=K >?s2T\G>?s2Tg=.&O*Wg=.&O*Qp=Q?.^q???'Ez+??'G|d⨸^{|?j>?s2U-v,rĒI$zZ_,|e]w!CNQ áZRΦsMi$B%ʰa($s\CWǺU[i:f6pUd ͒85TU&Ӻ$&M]͕z[տJZx17! 9 {>?s2Tz=K >?s2T^q???'Ez+??'G|d⨸Ey|dϏz\LU8Ϗz\LUK z=K >?s2T\G>?s2Tg=.&O*Wg=.&O*Qp=Q?.^q???'Ez+??'G|d⨸\ώ>_DK\|dWOeBev|ɝ8#8$$~4EteoMKJ%d؂tWZKqp$ ~n8Zei_[FMkQJ0HM}%DG:X:im';5cA5K AzP ;'C-'F#qæGj/A4OM}%݊~xzXP ͸Eϩ&?ζ֜shG=%i ϣDUhh:m!%Ԓ۸$M/!}i^2o/O>=@$8(#ᧆJ[R_m௙gvvCtB@?*??"_*ῆPb7C*ϷIy=1G?.HQ_İM-J#SUrrF;&>T ϣDU;8?W:ߴwm 'xM3JD'?YǪOA4O/Dё(9$E±z(0\^<Ʒj76Y-VxR6Ĭj><snxZG%VOXCG_`/!mS\XækV6+-Vm"Ucݖ |•W}D_k{͉tMGhڿ2-m7DDb1Y獡`~_vGm=m^$W܊F >V>]͊m( G;>xdon[zֿ?5/|)Sžv [5DI>9k[I|QM>(Fm5uok:}}/ B>ӳ%ImmߚWտݒwI&}u ߆MDž#im4 8`22GLiC o K/kZl֞o#;ʹW:}sڧ+~uGZ^j&V[{yaTy{"ZyToϹMJNWöŵЂ9g?#`*xo©zmȶ£q{??|ABeց;-=iz}-KZg<;?V{m-O g6w#vJ 7M䝛Km?Oxx MzGs>3ZE恬>S&#Pi T;dc5«'87uim}y .TV3w$ʒy ⡻]e~7-+wgnF;kREfd'y\ kφoqiA%% t#!#;W? |I+z~i׿7b/ƺkO 7MH]*}[GDQ۲Iѫ~jd^ݤh cjV3 ug28YAJ_$^)fo'xQ]Rj_ ka,ieIoyk4}amrнMb&FvqEO|og|fྲྀϫjImg-ɻ9AkZ\ @1Nұ46o I]E4Eqv%&gw2$UMck_hq^+Sg`50G7^r7e6^yv,cgnPx!:+F+ϭͣGd!O2M=D܊=cPo|fk}(oSCSа\=s/~ƷltK&ԭ, dxV+V1h,/6TDb9,I5wDm? xvV/?>=jD[:[Y#04.*=,oJ >_Ckǐ0$FpBoKxNo[x euU+*ƛwhҚR-q&|7v:=бMM!2ڷIQs_??7'>!|?a,7cmxH\\xn(QD'8%_$CT}O_?|Y[ӵuWJ.=[Wlr r*Ij$}7/2~-RL-ɠ'F_$\\z֟?As}: hnc+;?tbHڦݶq}%46uB '5αsAj̒iG'$wCAl T# ?L][bd b+~4/Kxx}3Y,d,3\ud/ғnxQQ~\r6?XLJ{^x2:5vIo V@Z-1~\K ͤXg7Il}=Oz4xj?=n5B[WFߧݳl% dfO5ɵvMQݽ_#kϹxcW#|ו?bku;T>x7tCl-+~Qu Inr ]gMgHnѴ9[dsrW#Jm/?e__tX{>=+{\-D3V4'q{h~?5$J-{Kux1yv匧mRqOݨwZߩڋ|=, xjNIVq PE;W_#zoN_/<&n:g|⿁~ tp|Euac]ѺuY+&RPYT3WU Gqkgߟ/Uxx3AECx7ʤMH°"xB}2y=vI-paH=si(&7A-hk]?^=glf+ GFX`x3NA @V8Mw]m:?MQp*xx3jUQhy; g1Ō*6e'WUׂ5OAxJG׮'ծ%h.#= / >q7w0=o013O Yk63 HJ*tʐqz?߁Z>n'aio+}ZkDC8Qڤ׭j| k:o.ukxfQ6I4G*@'D(+$nDZ Q_$RxS\x_H$L{HX m7 զ}MzoG Vc Q_$[4P7!~zoGEc Q_$[4P7!~zoGEc Q_$[4P7!~zoGEc Q_$[4P'55Iy[8 dSKjXQHᇋǟ.pu`fc8[?:>04.2|4=ǿ|%~C|DK}\Z,*?8NBo>zo:V 'Fal Ǔg+χ_W|d5i^4&.b)dP9 @`:9?5wÿxvo_AiivgF;pmvF Ul[JuWf1jJ/&U}"tLuo6#W2@Y: w|~q};EռiXY)iOt)_x'\o u<g]W%/~AkDr-:ǿeǺq=/+N}6O4sc& zZ.-wk_/ kvxJuGo\=뤗L>"AQMjh; p\/\q?+y+/\ noOba|pWd:W'j:]Ι5/i^dR-U3'Ue|/77k_yR?63ߴw'JSNMRﯴߵygm\$9.R|G1ij" [e@,=3ֲi;.kW}Q@jub0SlJ96w>#Zkhn]Xay+:yX JkcxAE-'*YK=n@ʳu x |Ğ-{mVxuHP)&o4mmsg]UʥセMVdwq]I}otxFM2>cv+KOᥗơϦXu+BK ;=1ռ9VE|!vRYݧٌ>t/Dm;#`q\{~͖we؈7pYr6n늹[k~w]L~H{kgס>,?ZU-h~;־&x_د[H5viC&@SvkQ=uM4Qww8UP2I=q: Bm/zmV}颛>TYzs¿ztWIBBD}I({c~ YjBEXFF\2 WmgK)_*+Ե3Ӽ?]J:iowx;[4RG0#pe.8Q ڥi:}|>: ޢrtHa2ee~)_Mmwu_m? hkVڭ4yt'uU/^𮁩i!gkkZ,p|w6بiJI>a^ Ey4o;g!bfKv6eݯ_Bn} =]jY r}7xZh~/ҵ;%<'jfAK _ 6V$А2̄ߍ.t~o֑>q^M!-;X3Y]%o7DqW:k,g p #A{__5<h]ǧ4mp)SGz׃sxKÖ: :I^xɪ꺭s_[֖C* &MKwm-uMVR4y4*xGF`pPM=u] '^ O]WĺUźy *H#wJ׿hxkv[OImhPa[$q2?5[x5 QKa7a2e~s^+|7φ~"ƿ۝ R&Vu";X#դےKnݗ碿 GkLs]E|{OvV𾅩heַsZͦ_@"(̶䙒s%C^:+z$V5vU,1m1ةO0ޒkx_Ӽ+^:veOsw;m(dW%zy_\XKKI"V,_;㷅u|񆁤ۭ֥[Ψ`@wxlh^;-AVg7˼FFH'R_ckMos_/ty5n4/Y?wv<o8M/Х.;|#q d:b^<^]X.4.XMމSH|z־FӾ!kVN{-nuy. REu }+h4I{XaW8`G^Qg^ _ouA-IekvpTR*6įSuZ~_z7_;*dGs 4k,N6:==iPJ( ( ( ( ( ( ( ( (9O j'Zͭ/ j'Zͩ{QE+>(м:ω-Mis$ |G-?j)< conwc8Q>nW˹p2'kq#7_jvmr!id8l"yk+۝.HmT}ۈE2fO2ZOXx]n4kȺ!Z/W↗?|;/.}{B'I ~4g+`֦Kܚz8s|^=g>xOw: vw#kW;HTs9cb| j׌[:k!xzv Tk ĖڍŸC"rKbe7Ǖ?3߇4 OoxWbI|@'k᎛u]Hn$mKɈ&/)J3Qy~U'.~4;]埴?i> x.-BO/n#WwF껥:ݐb0q{gRvw+Hl5LpTE8#xRM^"1U2 qNQj\kԘ5sWYk \i~'6ˤ]X$f1iwD ȅOڴi^r[q0+kingCHYSxލIM ^NUe6]jܫٸڱ>0# |#al.u=Xs40#Ѳ\ҜNcVw߉jO zlaK pWi0koƭK<%y_,#ۻGGQ+l'T5zn'u=bMrRn\DjFHAŚ=χ ɫ[_52X@ٖX<ҬxDoe}lM_~,h$ci9d8C<$C#.j[d{뛍gti$vz}V'hh-ȯ%|T!t_~"6/xc.wi]^^mٸ}6yvF1M$k0#(Ұ_YY\dk=~ 3a Io" M'i]eI#QT%X%}ǹWuZ׊R=:Iŭ4T cG1 Ԛwx>{M h.u${H).ЃެT?k [Ζqe 4Hn^tQx2Ěm{ K;ɥ*lvNUBӯ>=W-ņ+eSʄ[f]U.̮ӜbK𿂮]FM7,IMfm/"0d8nl`1|'mxcCs_ƂڍZJLKܺ4fvb ֎~u2c}=z!|=Of3xrðP^.u$>b*j'+hO~#C&}h`\ LWg&Gk#Jռ4*9ݖ;4Jgï׏XK'ӾѮf|$Aی(TKj7;mzd|ou ɹ[/ ] HF_qs襈H|c/n4tG:ŰxMIcf6-Nd@<)?-vxzoCqD-t /,)# B-Oyn~m~VƇ?hOQ^K-լ=d&IVXԎ^2z~P|8- cOZU(G<@f#c'הٿڷƅK/>ӥYnVtUB*Ƥ-O5j6#XV%止4*~p߾dA`R@4J{+o^v~}_I|GhV^v$R]ı"S/ 9by!x*-6YKкFwҦWpr\~ڿ.-]jxu1 [[.Mh漣/ > d>? _"kve?%&]ue8"0z?v _:(Եk Pe3:iǚgQ?c; ׌GxHKDwg뜚M~_mwlݾ/ wҼ?]+ ~+iN/-Gif1If_kr[t?N^ؖ&#7N rNjo7=EkX⿊Nsr;Dl'}AFo⟇z1Ȍ0C8 {_u5i_|K|Uӵ7\Zj&J3& W,r1= s^<:4ӏ:[y'aV Ŀ@rÊoito Cj=ƭme馴yO;0 `:xwC׭cmZ)fvP{ b'qKGe;rp+ˢXK=SCnu(h #r>WyWO #c.CU+ ~|׆?:$}IrEw2G'gi]/}=?4rvAxdZQM}k ɷ(A#8Xooƨu4n.ƌJTlHr->]jY/|-Wh&6Rdb!O|;>x~>5} n\jpiM<:~3kZ_KkS:z{<;Hh/ޱxoڛ[\E iF.YefP@6GR 0=W~ 6.}.NS+sK%_b]CF֡]>I4Z䱻"ԉ?07 @ȭڟ*Sk m9VHlk_p\/ݔM|D⻵=l<7é}lG$hmW.6uclhYwvZOV88vZf[׵ $@C98#}E%tMuuGƍ/_uމĖ:V襞3!}!J OsV5m2jW7[6󜑜qP};?K~ioo?C=b+eYCwwva"m2lw@`LJ{/]mఃV_"I-(C.XFIZCy?ZV>𞩤hzu_7ª 9?Z?OJyīխZӴ6f̋yҞU`ӺJO_KWvr=77X|_=н¶p^3[BUe'>ѸB=kşn,k(t-h~+_ո]yIj(B0#R- *[>ob^XC#`>0;:.3}JoTZX_:֓iٛl0G u>Xha=Otx]u4{GDPO. M3NU[ g1>.aѷΠ5MSX#U] |%7ng~7Hk|`w(=C@ִ h,YghXi#y UJ ^^`~޶F=iv[AݑͶgo]?$:s@|=]/Rk`48q(&,H.@9`Gp*[7^.HzVvA;u9*Bsk}jQ h*饣Zk@5NH].2 :Ɲkk=lVӚ ݌^tWS+9^95zi~w#]}zo=w-Bڝ}KI۪Ogwy1 85NiMņWz핮F֒SEPH;Ad⹛>%k[&Cpŋ@Jw#ENk:k׮>i:7ڍ%ij,7_7T߮RP\2ʝvșT:_|'|84oqֱۛpz^W+]ke}x_>,x zob5 䵳.T#ݹ$@ _?Îk?hk-BOT. ViaB.9N+xxo\xFmA?v~zc;^+7>Ϋyi[ݼ,aebvA<$=*a?;N-*[ɯ+?gC\xw"-'×7k6|m+ syNnh^#JTOU\D$?.xqէ%xfQ>/7\F Hҳ4>tQ>cyeh+hbs)> vWg&ݯuտ :|'>k֍wxoO}n%́|ѦasqǺg]&Tфiu-7 XrwFX|Iu/ |7ZZ"eHdg_2cիMa̞LOX_Am6o,6an*# #kɎZj_{>c-7ᶉCwx71X n.s'1U~\@0[jj7&h$"$*'=t9/'2~ ; TB VFh$uI[;\k[ɿnAi"޳[Mƙkud U@v2[q2+ƽE+s.Fau98O8gI#kiR\xY:?Q#Fپaa($ !E}NυQk"M,ƃ &*sh_%_xߋ^l|?GXtw${- q zs[vAeg^v'EdY Em+3xVMu;MDFFc<=q+ `q6W 9.6V BZB #(h[ͯۿIQ@}]Y ݡ-0R2@$J xRWTQ'cH"Ĭ{һ_ ƫ 6Zţ͕H'ʠhWV'>ZW-߃~^(/t2"nP02:W%շ|?zSA_Ӵ}B]:{H$+$ U`7o-,&IB@qv u ޑ}M'lX!IBH;i TMm ㍅}ƺۯ8 4yv8V0ng5WǭOtX^QOKx\$Dg'˵2k/_LIui4#JҰWRYڽɼ'Uj8[[V'(Ukmx㽇-4}ɬaZ[C=@ 8 ⛮~^]:W4*k&diTW$t ?1|a:櫢L=;ŐUټRmь A]GᱴV[/h%eC\H>zP=Z/b_~ǟi־!{]NC{4nZ-C,SG}6ފJgkuZǞo=;Hq% X䑰8n|]7a,,l,3 s.A<kT{o[쨢C (9O j'Zͭ/ j'Zͩ{QE*o+S컂$q{T5On?66tIYl2O1ЌE[7>9xPB[xQ|AkˣY Q 1 ֚55ֵ>ì]PB%aC|uq\x_^Q\~,յ|@6)Eu G;-R%R9< 3&덯jL[WwR/uV<|"|cd^!6&yme&I%:q)Ҽ>ËM#B$XdmyP2I5`NO[[~Wx'ں/TG5=Q,X 6h@DXzm~"^ktowm^h2V !h.x\y˿gX1]-_U}z[f%Ķyc#;p>~k xƚlvZ>&l3 2|mnߋ9UrmSZOt+zZӬqgB BT1q57}#i>v/YKyr(>;ԇZfZPZT^o+9w[8Y>>Ožseo=+F#!l_A-ƟxᾷOxNauG[lAw,ANv8$0*=ux `%QZvysD 8 ?g?7O 귓_Rtn]³1J.TUfۉ)KZzZAf D$nnyj_o|g~M7}QJ̌m _"^lI4?kT^Q/|>m΅Ǵ-4Jcoa B)oN)o`9l<6FWFOύiJyNߍ5\~ :'íCIгi:6ܚS;fa$3T]Z?9iZ]F$EYW) <+?&mYYi:7TD_*S `rm< ע4cz+5:nwO-Ċb|8q㊽9>_} 4ovqᘣ.:{DYR[G4B@<h-|O\xbN%P..#ڦRPh߳֏} U_4k6?z.a7WŒrzK1[?_553sItBLuȼ@cVn[ ̓ ƃ^+D85+{1,afױW7< ɪ/FEf`}+Ҿh7ԗUW^DŶ )L/8}eOLJ,;{PoM,Sa[|Q&wm>kHMRysDu;Gfx ?š5hz,{ɴ{9~m4_] ռQ yP`s[hsV_s3ե}" u[/mAa4:(,ilI 8>a\F X{+y?ß k_M{KiP{emDGNUQFG9Լu? oPwgswq k6bnO<]$֭uum.mH42S3;y|戾jÛxk4p[åK6M+OPv94t{/k[W+|]aishRm;=1/SVkDL&c g"kέ/]:i7(w&Gc+pc[4RW"EIٴHĖ,XCY֡}i":~ q&Xcx1 w)?/JH[]kxTG&cioI`ԾGڋj/s[F$\ǿ>#:MK]2V0٭0wZc;@ w;E`Z+欽]h5{-aIi#]<\fxg&/[FkF2 (_6T:nT%u6sE}Vkkm$.@@v5>j?6H5Hm5 +]PmYKN-t;FՎssFm]jW\ZH :)ٮ(>6n vw$"W]gWx4(oմGQaBMyLk]m{pxj4쾱" mh7wK?vQEIAEPEP)SD _ыYSD _ыY/p (Z] c·UZAn?*(((((((((((((((((((((((((((((((>*ȁ1k6*ȁ1k6ER_ VV3XvtQEPQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@O5F-f֗O5F-fԽ(@kw5 1UjkbΊ(((((((((((((((((((((((((((((((("Ŭ"ŬڗQH|.fa[*di_h=>O=ԗ OǦA=RΓ~$?O*z5?/'IQğUѨ9?O*Γ~$⨸EyITox~t'Ej+_?:O{߉?.Q^sğU/'IQp=Γ~$?O*Wox~t'GIT\F{߉??_?:Oz5?/'IQğUѨ9?O*Γ~$⨸EyITox~t'Ej+_?:O{߉?.Q^sğU/'IQp=Γ~$?O*Wox~t'GIT\F{߉??_?:Oz5?/'IQğUѨ9?O*Γ~$⨸EyITox~t'Ej+_?:O{߉?.Q^sğU/'IQp=Γ~$?O*Wox~t'GIT\F{߉??_?:Oz5?/'IQğUѨ9?O*Γ~$⨸EyITox~t'Ej+_?:O{߉?.Q^sğU/'IQp=Γ~$?O*Wox~t'GIT\ j'Zͬ}vOIӮ1 r}+bQHIbbʣޗ?Pc>5=ƏOEA8/qz_S@}?Khޗ?Pc>5=ƏOEA8/qz_S@}?Khޗ?Pc>5=ƏOEA8/qz_S@}?Khޗ?Pc>5=ƏOEA8/qz_S@}?Khޗ?Pc>5=ƏOEA8/qz_S@}?Khޗ?Pc>5=ƏOEA8/qz_S@}?Khޗ?Pc>5=ƏfpZC4O3+ cqz_Gƫ+3OU4Z[lnٙ¤"44}?Kjج?/Q+ cqz_GƳm+{&!faBPdsV>a>€,}?Khޗ?Xυ_^8mu "@<א(ƏRb&48$] 3W)kG4u-!!dws3F6>4}?Kjج?/Q+ cqz_O忙b OI0LF#rmS@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEy'ĭrHN{鴻io;^ݟῇwluORU%S|l%H|F=r+d쬣{bGLKx),#4U8o}m']^OZ7u@Z01ӊ}C:dΡȷ@~8QHX$ `_ZN]f.^kj.I~5mAHL013#9Yҵi>"5=KPյ 6-o$j-~_@CDnӏ4FFK#tpQ|=7c8iĚ:{EŐks='sTu _)[?O~(_hS]kfl50>IE*čۜfBmo>'@J`m,C%s#ĊՈ?u?Op4Бg4JI섕((((((((((((((((((((((((((((((((((((fotoxx-20.08/images/map-click.jpg000066400000000000000000002057551362435004500167010ustar00rootroot00000000000000JFIFNExifMM*V^(if%0230TȠ01002015:06:10 15:42:31Fotoxx:trim_rotate|write_line|write_line| Fotoxx:trim_rotate|write_line|write_line|NE http://ns.adobe.com/xap/1.0/ 8 500 1000 2 2 0 C     C    " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?P((((((((((( Q53躆-./ͥCM6-0H0 G"RI`GH&'Y9OБR/Nz,NC_3|4y솕tI 7g}p?Uj~,ueJ.?z7(|C~B{5+\갃p%[gzWG4f8ZLY?x*ʤξU&f֞G} ǰ|Y?A^ajDK9oQq=y߉+\5[-6)x-)D@,HcGGpoǐqJ?"=u&MfR&pA1ƪ7M.K/*Nֹ v2O洷i6cʎ.<&Ћѿ!G\}+ O@eյ}Nc97>;v+0Du$OR_T>5Ɖ}(k>]g|~K||O-ks!Fqځ{7jFx:m<ch:"ۧ pAU,#[4mn _[yjI(H#(e`g'89"Ћ8gn7u`i:B0\hAb?-<0mTgMSK~,"9$UrBx 6< B#9Q3+>+^hKkNkkHw G)vqn!PsPxZywjoo]Cfۧb(p|`w8rLhEߐB,o^{Ri2f`QyA5 ¤$8p+Wofd2 6ZC #3 d ÒǪhEߐB/FrڿRj{9uz7(Ћѿ!\F_uz7(Ћѿ!\F_uz7(Ћѿ!\F_uz7(Ћѿ!\1uz7(Ћѿ!\TjG8r^ ?"oW!ڿQ_~BjGTs)hEߐB/FrڿQuz7(Ћѿ!\TjG8r^ ?"oW!ڿQ_~BjGTs)hEߐB/FrڿQuz7(Ћѿ!\TjG8r^ ?"oW!ڿQ_~BjGTs)hEߐB/FrڿQuz7(Ћѿ!\TjG8r^ ?"oW!ڿQ^uSn#d ~ls9⮏wiwxG*+8֢+B(((((((((((((((((((( $f_<^]H|@'Wj?sm]ƙe$s6-=E'pP)'4:Wӟ2Pv>~!XѶ]zT^kie36 8ݞg|i#I|?n4|58]{ 7I gu(-E$bW St^Y߂oXZiNӯ%F~s By,P{VQ;ۏOZn1|4nԽ70LH8}9K}s5\3ѴhItGJy(sGGK4iD쭳+8h8f߿q*@#_qrxU`A' *8ǹIqqr?:k?ZAxk:Jd ۖ 7Η@!v̼oM2-_/qi+0x:` rr?3 ƫ/"^T\:C]G,{LJ,J,V_)wV>vXź=F̚=nipYw`!-G8"Ȏ˸ynH<m/%I,0ʙ'HP궺Vgަ[Y.q$ǖCў+xφ+5dy` * I;C u%GדGIx4M.-# (lrۯW0YKCkK!/EovDuXC?gwH@rr9ztkPqF|3$$Bxl\K*cü]g I=9"|D~+#5)/)f,@ǵ9D *.x\g|_ߢٓG#iOwik5C $TzzT' '??Gm {8k[U8K ˓pwEEfO#6t^K '6gS!9cSjͽ& sN*(vlfDZ,QD,v䚓\Ư<њ>c?_3Ye3?@3ѐ34s 53Fk,8,~?GrAfjf_sqׯ5 ~Gh 3RúUEqwmo-l%pFPO'?O,<њ>K%O,EH ;PI'x2-3YHeXn$~XI5[мGMSKԢ=3G$4,Od|kV_i|wy0fko/ů5¢^k@K+pW+y>Z0uX4R6nܱ!Pč@ 1ЌcAv\~#vM>I$I ܯNV!UrFH ]/'ZͿ՝mזٿʲa~\,+a>: ?˲#5WIjUTqŝ8ipȑ7y8P"8ir3s kwĩOԘD\0[xvn,hG˻vcnt[|-fE Ƿ]F\^[#|Et= [&7 Dq|Ĵiyw~r =']GƗ޻U+uX! l7b1m\0]gKkո+! XHm# 8e iw%kLi Z*0ەF]JR" ||?e,&h]mq>7Ck.,m"$+Ȋv'{ᮣKo]pm,yM 1o.m-%Y E2 }mX=͕#U(7O]M@,5GVԱh243% dJj$br:@# cO]Myݎ-~ ;8GQ/F1gkWYh^8 Jc9*~~NƠ|'9y(jI@#9pU١Y$u<=2g\uP gb֯#o֖v:tQjy+X3.bC\+ᶡKs]\;uH nhb%L֤,㪠cŲK_$s$@q!H6b%%d*6]Γ{/BN HuONKk $y>k(ʫ7=W2^/4KU&u,$O+gUƟ5\HɄۻf$*ɟL3u`wB%ť#/N͈$:pǁZhDᆃRg'~cy]p(i&˴TqW퇠BhWp۳2C<2quA|W@t ۮ-wÞFW>#oSVU_S4ԥxg ^Q }ڱ~+0|N->IS[̐)(!0,rFs'*>+7_X 5`Ԧ)uҟ!1D1cylnʓNGExNKԔdl57vwys49`zֳ8ܒW2xk&݄W]ZYsn"NDD4iV|ۦbd"]@h#YV&?9|Hx1xsKm[\O1 S+6 85u}7I./ 4m #?7o(Y- 5kUtcV~!ukY4(KM-J-XAld23*T>ږ?ںSr^96l#ìҳ(trcZS^,^yEpТHŔK$[ J`k7^.iKx^dA#ܥMO4s7 KȼEǣ{ yu,^:вDB%|(u_nUY@\,-26T5FMƅ|'oztYPI:ķq1ȽP 6Ϥz[7P6r}T%>l0gB-累Zņpi 4q|cA,lQ@`%M |F4+m9eK)ƶ5N8%6D 9haBulU3d+ nΞ@9~Qiuxbo,/E7߻|F`Ht ΛY;椈Oqx~_9yx-#\qm##FU>i";w20RW^"㰆Oo.f>K xƿ߈KšvmXne&1C[yg!ZE<zNxNCZ{&ӪKo4P=YoF J\9MXTK3MM׵hgLKfe1'$}͹Gʪ3SS-Keub:Ƥ<3IqrQ>؝NXWrGsiuxWLky:\rA)\f(`~aŢm{(ltm:FP I6*ʱ߂Gp:d KNuK]ȵ-rQ 8Oo1E#}RȁƶU:{`5)縁LƉPKG1?u_\/MU3>^ >Rs??“cRHz _I)/,wːy{i(@$q暇r_I!isX\ð6-*5}C|:}QÙ:?GGbkH53Oqb`6伄6S EkeXVw8e@25_G}C|̏OMg~ HՒM.K|.Zv!͖B>f*Ā_zֻ hqwG4r O H@Ic澂ϵhgϴ? 9[ />!-~d y:g2΁00`BeKgGMc~3{εhֿ?ŽVxQ$gK{Hlewy̤%A#ڨOw&S/H]>+w=FwH O$y|m +O_G}C|s#+#n,(ϵhK2>nOc>)yEձTr*6ڐ@B>{F >R"\~r+/Ogϴ?Ù;]<B_N \opbExW6!0${m;n]^xaї|N1_Ggϴ? ?9уG֮uyIBn_UFy 2BZ}6߂T&Xx#[䳒7x'h8,"s-%cYXFaKrv@8O@pז&`w႕ +O5YwSrGҪ^+"I+8l; ĕ Ò@\݉󗅟cPjvQ4bUu I#¹FJZSFl&>/ .r\n_#k")kZ妝9M- A[N45nݥIw̻h Dm7㷄5[Xo` Mi{,)vb6k`Z|Qwp7tŚuڻX09# LTeFPJRVOo?C?G}V۝~>oʫw}?"uC 9 1֏h뿵F_ )ti$meYNC} ʎXGz?kl\@(wЩi{D]βR-])xW_? =Եm5{HS_A*F'`;wk"O|'wƙ|K֙&Zbwb4(0e<Uu%NԥfzdSxO3~2||o"]קּXUK:o1޴@cD~#i^+`٫K&1 x9wO/im&+Y<\Cl 5 }"ٱomeY.agq~:| UWvKd{y\ F,弦oOYuϨ~ 497YrO,F5h AC3HnFOٶ GO-ۦG=5p ~7-66W=#ZtgK;" p#|~(xݯ*oc=.sI/ѨO> d-*!``ׁ>?:&agZ;QDwy4(bi] @R P )?M?5x|Kqnɬū6vr:]6IHvR.ܡ+$|T OY6%9BGᅧ~IGYж%_FQ4%DM P6B7T=^նH|Ѿ/\M#g~(?73o׿i?@s#\ &,BXiͧ mZiJ." 19@H ޻c$<;W_s6; "EتT"䪁>){sT⧍?p׿g?@s#/|-}]G{jڋHaBP. SC3n0k~񿁯t4[<\a,,v͂>)|_Χ7__Vc]߷Aky%9n=hw=tWWk=Τ@>n2̟jܤC5>?SA7Q/p{IM"h9&HY3)?ݮ*?4'FxN*t~hn<<<@־*'մ,kVpZ(E3.LL.b#s- Tz wj%ljaa0Vƫ=x<æc4L l1APNH?TiUmFf Kb%['j5-O*xGM /̻8yߍSƻUU~ltxo<"4/ U>q۲8$};z.)ђn͗I^2M]yIZ{y?h~"tZ}km3ϷE@4ΞqJ?౿gAa{^h2yigX^g' 'Ͻ^3Fsρ;s ^|g>#K/uM&(*c~33A?A?F<?e|"s3xky ޽>4| ͅ%YAv}Y'<>c&ɤ?Xߌ>}:`|$x*6}3ǁ1 o,on&?y|!&+|gƁ< Ͻ?Xύȳ3pӛ@6E?x\Z Rgݟߌ<3\'&;8Q cŅǧGlR0J}?౿_ҏ|o| _Zv}Bǁ?Xߍg{u.ϻ3"ǁ1`K?Xߌ8ρ? G=j5ݜPۻ[Ï2URUs':o~4бO]!|h}%lyZn0sAYph#E%Bρ?4v}Bρ? K?Xߍg/NL.ϻO;@gg@|h?,x{u9f}Bρ?hg{u1ALjL%?౿1"ǁ?R%Bρ?.3ȠiSë>=sy4\ɀp76zzo?.P+`LWlBT.]~^(lzLn#DV57ܷqx׷PX`tzb!`Y2rUIbr0;kّe0b=}v N3uz~&ې S)M0]n$qڠF$1,N}JSG n#!F+8PȣapRqY:ǼL+c S&r<ڣ{ԑV298s /cbC9AL0!A8:#if; <JK"IaQ^9JͦzdBI 銹{|~*CH99?ZeiX@ 9Oֲe/i"!YPIŰI8)rjHyc,rNXn/nbfNly"Lð:V=֭iOj G[Xh&Cl[XwRT1PN]T|:џR'0֟q Ó8ed' C;1*P)A8ɽ;mI.m5hVW!+,KSm.< $;ml#\`mC 㣷#-yCmG%V `㺍+ȿhxڶ, f ='R=LM᠔\ש-MӼ`sM?ս9港?ićJJkyl3Wd F:ry iG'' SZxzދC6qDz`qB؏Pp1KSE5]dc~{acEe vP1ԑ{~ÑbL}tvU,:؇Nb^,?>;WPν}^/6Vㄎe8U&Ne r*v)}[m[&b%\2.{aףG0es_}N_/Pa7,W>ռ۞_ b/>;}OOϳ]Za # 0Xq=5ukK;V[&v.\nl {_!K|E&M?QFPc$30o>E:˗e}3 έog%'׉!:LnocMuf pO@p?Zvor`mdB<~+K s [5"Z4Jdx|&6Rp 0;;hvԺu垩Z;$6p rpO])A\t(7higC)]%Tmw$q_rCk\fx ůj -55(&.Ფ<^5s]N N"ff}^H08MhG,MH?s^NJ>U$DѸ2OO;T{qWVۜF?I/ |BPQ @L2?rxXGHggF/?{4D>Ah:qz׮+㖑g|[|6:jinbrdbˆI*OꌴQǞn`=N+_? (hE>G H n>هhW[[liק\\oa*/.U+/ yh?r)s;_9~.6 8  )^Sk6v>Za60r@^c xk4w۴RDJ63>nk(RG}c d{@M'^ Ѭcۙ4B ؊^f$rxZfUPG5pgYJc\?t~:=+Ӷ[sÐ%W]0G07dg9uG B3NoS'*l8mY%lP`ovy& hw77x Nc̳<ăX?X+ab?ֺ߆Kl绊H.ϖds+Vj?4 W@tt;2H  #kdg3\x+WkCws,!򁓜ceϑAa\[BQΙVJ aO{kпf?kE\ioL1]n\m}s~~ I~m4m2OcYl%V)|t1xdi" \n~V8'>!5 %-ʹKhL)!;snWW M7"&,ضN">[ҹ)Ԅ(;> j)JKG~YIm?7Ud]9aZ IoCڪ{8GtBx¸9⻯#ʒ@b3H4l`S'9sFGBT `SwZ .3E&SԔQP P3ABM1h.F(ϱ 4!)(qh} 5izb}#Tֆeq击.8sd9_g#A|?nsHFprO<;W_{9 =Squa8.y"Wо Z[E%M~W $h2ǐӉJcb;ԍ==\{o>[Gg˵KFG!?Sq9vP9#+<(<7tigm)]rK[(<;H#K_~9W̦ĬU"Nb5֨dbvc4һӌ彼93e;եh*}iNm밬1u[Tly/oⷖ{{o5@@bn##U Ũ’xZHV8̌r˟`ܶ EW#) R++9MM.ǩKJ8I$|U/'hI> "*{\ٱ nzqFV<.ݫ!qx/bGxp:޵odJplo<+'\{W~bZl\ۼkq.Z2$ uR8?;^Gsr]i9wuGo)gW RI=kVt%ʼn M' [˹/D2o&%G`2G$^.|G&k )H- ;pҀI!pH}Z5+_x],lֆfy'$}yE|Ccm4^iy1.2:6 $HWMLU ۿo#`N4'n7 q\7;քiHۛte\2{w_CJxRl5{]6OI Aϖ}Al[_[^'+ 48&13G⯈AAqx" ̗2\@oH [Zۉaʹ[q.-HX99bkJʠ'4yog[.dJJ/P>pۜsߏ\_"'|U}z R rqZÚd.Y 67UۻIN>S$\Ou2#%'ڳE|ıBw&%q0ABD;}O_$.:y[ yD1\:AK>5xnF/cVV}$c_Fy1bu~:$zk*8ZVpUխm%,1^ǩ|=дItܗ֒#D)M峏qX{ Q%¨o!9$WNϫ6l6P41d=Sx 1›SMd..hڳy WHf\-#})<~;xSO%>\W ` *z`g|'e6wK6kg "Kgiِ< @ǵ/t?725<1goj.4gv@z3z鸿slE9:YƯ2X$oEv}51qk;wM4j|C4kh<ZwuKhz7CǞ^,~12izn3Bv)}:_)U ԛdZ$"pugBہlr۩_iU\f^ 5茡OBzR ^kOڼM^47E ap,'ON+]AJmؼ@zry-3FF[->< z5歩j:6Ĵ6N*,Dܠݖq^+)k{Y5X5+v(" vIN? Y~G#:2 ?˚'M*^h8''I77Fp6֐~![Cuv:iy4̊DN.F'MK|D>-E糳=˕'>R(%,\ynص@T_(r q:zbVRU4Ts,LQݪ)F_xž/ԯgKլ."2o`7*(aGgwxOX-I#F##50x62HkcEòUxƟgGxv[^ Hp `)l)ldU^MeuKD>T` ɯUIewEJV/]InO _>+폥LMH%|%J| u߀kd-^OKO{:Ō-:(%6*rFCR*Wk/ȧ,Q'R/ 5p?Oc|%q(~#M4٢E4`ݔdzds^nN?ߕoTƒsOmҺ>R?)/LaEз|mc$X\'k xhմKk{y5H"iU2,>R0+=O h|P^YcBO_n.|ARF`u c8\FpsӾ)}rV}bҟ >t7{lCI<.ofHmbI1Ec=K_ !jqU4ZoϸokrC.>|!72no_H̬yǽ;g& -Vkx!T?BO?1UKRQ]Ùͥs/-υࣺ},]5 .Zi 5y?f4丆{x ?'i'ᯓ9i'JοQ*&je?N>kFeޗmDZ=C] lTr•[qfZ2RGIzR銠FnqҔ61@3T+nHU*a1Y|[}+<˧wZ=PIv蠟ƅC[~#}F ' 2;W! {WI[u}B}i$ucI"rMiґZt 1(P%FKv((t" {QE?{ u {a$rX1_5X{-'H6.m ?ͺ;N0=C /O7nto]6V6Nxׄ7_A}oⷊ I$|H %[AL2KlAYiV2+6e2;5YYC;:irs…yTtcY-k8lm8ļNU#W{g/zi.n-lGTe@$59Ӿ:/R^A1R=9Qדֹ4fo~3 yԠ t-ڦ_w}NaV=== ?>':-@Llmfr)޹nxfy#+nL2C@Ne^xp~(󐋩@@c*)O>/⪕H24ϙ/*`G r⧂3 N;s@?!?_ # y M!^7E:QڹZ bI>H<{s]3_r] ʫ{3tjD|Hӽy=z֋%Ai! ϱA-Xi='[Re\<- qRAvvji9jubfRKK--ݞ]Ksup#\Hp=;לkP%:=ZagIf&M*$]*pGai|oj2!ajv8'?+ºW4+ _[jq&MO[s;0Z:N_$cjВ ~%RC-` Vp>׼(v6\c%ѓ\v Q/*X)X2x^5at23Pb)< |Sžhhm 28C[E`*Orp>^:iŸ & Q*X8.=?KjSHi\Cǃ<5ˋ 8'p</iRZO_;I (h8dMtXn[=HO5)¬_  i`!}?jc/#-?"o~XG:5s}_<<5i]!W pHP2}F\jf߄sygI8xżaNcq55Mp <jCA!~k(a% a#_B|:R"6/M FюGj][/ux{Pڴ$𮲑Qh ogawݯï œ鞡X #4?v+IkVix;Tf_BD~~&(|VN˱|$Fp;;eXmVdrʄM1d+]Kok0M̐Gw2A^ | "tUY >08?/ӆt-7?;HH'ێ*4T]S]+E𾮑Mv[r< ,y@F@-4m-gD:d"%i۔R?tOn)<1&3?*#wH3V!Z%!ų?[Ѣ_~!%eI!>[c|MvЊêR%o?Nuh8C}-]vYj:sh:~+$+  ;PJI6ct6m՛jKJ=]Sᖦ^@]4 -B%[<92lvL+ѾH8# kGW5/ZƓ-az¦U%qKͷoCqKZ*4k%ouuVwZZé]%# \񚬣(Δ dG'$~zUH "}|ǰB;/xK'Y#EZ=O,v3Wg;/ׅ5Yk=Vٞ$ AҼux= ி&_firnʼn :W#;W?th|=MgM {DX˻%IA^tF0A֛p?*qṥ `n0N "A "w/,[DӖXy(?.ۊN۝02ie2WQp)8ma1~3v8R[[$hَIIy4!Ic{$݄ cM^mMdm k|IdX/kpۅz+Լ=.ݯ,G}QR9IaixylM{v=Z5MjB2{f⸏xVZJ+;HQM\8@= 3^}V Ⱦ/#ou}hܫXaJFRPFFh@4E8 q(4d&;4u""E &3M?}ooYWmk!/" .5X$Id%(iT}\| u[YU+1[g~>&ꚕ#:,>Vݐ0լRUVwMoi_[N֚L\VVSLepoĸ',XrC`7>{v|9,e#\վ'M8Hm9\/DHwˈF}ScNuJU2IC5e@P7/T=~UN0 'J:pF9La#yUc!n#8GnPF Ꮊ^UM1TJvPBD'yytrSMٴBKKIJc Vt z<<"7]G⥱qM>Q#*IӚfG w~Ïhu-Kp^HrsQl9/ ֳh{刼q,$Azkb,c;TƭYgiS/kDzژ办65IY_s131xlEZfk, qcɯWhtQoZ"es29=Jz}|Em_?<Tf<'jշ{txk Vn䈺1z{4GE!<`tңQGǝWO;_ًp?kQ uxoHʶc19#ڽ=NOJZL'>SPzrW>94oXKHWdUIes'Y.cx#bWw|<{T9J>iRmR<|ڛjʬmCWahdžzq?r114Hs+Fx#Z}^/oÝ=>WO%,e H$05Jqy 4*W7-߃0uWI,YJ7c,Г=T5!O1d=;okqq$ML?n@~Ѻ{tIT<8!+Bzw5:t[\ə"ZtO(.}OzW?W8Jm$n5  ʋ>T`FVXG eI63lWʮڿOw^2sm}EүKmN< ARG}m&uKսL}@lKNy}+7<m\]^Y*b-'iRFl`qxNjBxGmtWytJѩ")%3gڼƵl>u0Z"ӫQ5BIkEͤlIտGL֟~$i"[-g~c])9+VfC4̳` ^44q9U>xL1)vo7#FPs\WnPXl3X 9}|@= ; 4_ͷr?/=HH6R~^%(rXaOCϝ#8'Җ]N6ڦ302N8'LW-"|(gcׇۚY #,dTA$ 9BRI}( }yq6+FS\Nodc 0q\`ЊI#~5!=7RּK_8!C8kHhψqt17"_ j&+Lfuu͎Yz懏|_{jZWTU'@o^;=/oc1d0@2Ā$msF!WWV]ˁyFK,N?J#7wf_Kw0͒)A'GVJQp3&`XG>EqqZ488b;M8 +tW,/fW{$WN[) 07g jFm;>è@+6rDݚ[@'KCa&=Gq5Ջ죈`^65N MπբvVS=6O-̆yYõvߓ^HJprƸ2߂GҲb*Χ$m*TiS#xJMM}9Y8BCPЖ9)*A;@]KYr*2FοA~ξŽPp1=j-IQxX:{ikc"Šv W|0]^|9D}CO7d^.Ƞʒp=+MZ7"yaeOsDͼ%:i:א̳+"8_@Oz&#xkH4=.ό-]g߆Ɲi?zXb?*ei$D).$і%R iO>Am Xg$^v}GZzA}u\\XMIY1k'ڳ:tDy )M4m+16P9ϧjAx57u8 9  A2sҁiwqJ -4M&j??Z4ЙTL[7gq@}@ {);tՑK <w>WOЏ˭y<.^ٯ[B3+fv"inzW91*2|3b6#D$צڥӗ…!z`uXkջHǹK6r=Эhjuԁ 6ʁlcXsq#ZB8ZEtexdZ`pQCWq,4}rWĄ0hCi,QTF8>yYHZKhw!vZzZ X]`GNVxჰ@;gҤ=XU]N7͡FE18vOia;?;Ӓ>_N*_&+$ Rڃ1<-dW7rn,F#!{ww5[Fcq :mO}cp <]w)Gmou p\ѰGTI2GRa"6=9ֺ I.$b`N:LS@֕31xLWUu-UHVfbmUUXW [jRiz^ݪ a2W!D x/9)QٙHRpc/܊lkf%NLrŎs-$NrNkT[Auf_閶iM2IfW.r9qB2^\i݄K2!n#1瞣 kd#!DQ6=1VR+uCH}Vt- Dy[WSe`҂a@S{5mFLkHG'y,to>[[[$ӝm$~wݻ]V5m<<Ux$dN:t浥X N ˫}YX[1WYFGoRFK]e]sw"pb}p.@Eu*̡DȮ{Nӵ- W>,5cy.w<d.] %rPAòXXZ_t.-DV"&U;:TR9{.*d7l[_2pndHueeMYW 4YP37 "I- 0APJx9[^k9k Yd"% HO, iu(-m*DmXnb1 w.pHnt+mB8;S(-)U/w.8RF5[4.n$qnYu&=I%SCǤ4ٯپJDVѨBa؛%z<; f>cvϽrJ!ux~ĞbR5K##7lBZh}LF 4ƶ%ݞ} y~]Õ9DzI%oGϩcFGpMHa|4ǐk:;5X@ЈtVED1!bJ1ȫ>+u?^jPGYۅ%DN(9< *$Kaaeɨ}(UPlf Pafyt}nƷ2XxöLV&eDބO2to\Z5+ShbuK/\[Ou5Z6UFwYt6t|<Ӿx^EaH7;7V=9vn9NzoWSڶ]6s Y 9#GҔhB(q7J+n1}{PKl?"YS%:q-|Oc^5ׂsi-GhG=A1Į9֢PԷ| Ԍ$wn [\TnۚG+/ĨSO'V{6>kiO-uHsVj6Ja$dLR7zZ<6Ƙn}ebɘﴳ*(9RV V"4sL+;{5Pwh檿u+Yj }e4ZΡrHܔ@ {~|2FkҤ7 k+Iq6n:zqURLgIK/Qa,:qֺ84X5($S9w&`ʑS$C۵cQi*G3xb'sߢX^FHrp; Psb9$֦f|c5,S$T8{=ZTGD$[|ÃҺ^ǹYPyY" .VA Aԕ⤎58YE}?!\G0h ck@)6FPb?bҕ T,;㊜niyr><1I W\4=}O7<#kKu;f-f%,JcX5u)ѧ`Dqź`~k|%ɯUk3̊voA5JCc|{j~-٥ $WnW#'n5hRĽܛnU|wlPYx]t-1Υ[fP͸$UNޯ>#~~6ֵbi3{4@=(1޲% O߀{TSNUJ3B?*=<7nC~9ì7%kI<`Oϸ`=+IkfN1QJ\],<Տk 4?쎐ޗŒ\?| (*:{Z[Z7=W5ëfgdg @sG't:N(vfuۊ6WV*$5w3ֆ1q+MSCR4gqN#r֥6xn|dX #}k76za;hws[ɟ*UEV'ލV zW_o|_Ś=GTЮ쭝HD0#״Eu Gմ;ۘHm"+Dݙ&rgt5>AŚx{P[éehi嶛 1p=7T`0DYpp9!}kO-OZۗ67^o"kem~^*k,iZD@o^p2:V,S&=FM Ó3HcK[Tft2yOT®Os(-;N qζn59_CXU{L>1|2OENHN\Rӌqeegcha`hWm%*Kx+[  z{Z]m K)ث+d)Oqk{Q-qS@'N@@ϧmte Pb6Bg y㎖ڍȌȅToVOq}a)~Qj!H$e\݉"]/RO5ϗ“ֺ4cr꬧oz[5WkɄr~ Q葯aFG ={TYCty۫4d NxN>4SͪGe^/*P:A">=zi\rnt:o/m BC?ǎIϽRLrж <+Kg4久r#ns5-~-7k2:BuTt(vȄ8bKD:sǥi"&TsZC=+#gp?ww9֭js(~6 Gt8Զ``S#P OGs qϧRPp]A<;ٵN"xJ">S:SOE]1^)]\(vr? >!]}L&Yy{sx:?߬_P۸PAD5xê[ֶ׷~lo% qy=o.wg=mQ PxKO>ɔ]OQ/oz/ 6Y%Jk'̓t IלKol&KRʦocޮ5>ZN EQ 98_l#N8!:?YQ s@Ypk+S/o,u|Mkspvڲnc($M|)Ho`%# 唻aҽ qxMٝ$ˈ%>QB[f*AA?Kÿx~}#X>UCU$9ڻXž6|Aqca˩PNcm7.'qxhb%98/ 4XI4<(EQJ]ːj,kG0 <*͍M! u榳.oC4(A(]tK;K4M LzmG'l>|D:'s=8qii^72\>dA[ @>¤kjШU 򦧦k^E=zߐ8Xi]wc;@$xPqtS`-7kX7oeU$mbs*'mxKxQ9_4Wc)os{n(,AP+;r: sLRS+pI@95RV%IȿzgR%pl*&ԡxh\dz|=[ǷctV#C6S!c;?d\ ]NhF$$l!?88ScϪY=y>lBc9 =xNh4xH<2O鑆#q3Vdg4('txNjW/k۳O>zօ9!`X԰峖"tKÑ>Νa-W~v ]ć SoJŦ]_ix7;1 g\$1* =rq]xw.YJ >>@IBi <:px_>h6΋dy=yBK$5cv7Aۚ[~͚7,VmҖ>Ad:~|e:pthI{3W[@cpѮ;8rvWE)}wZ :NmpDFB  okc`7H H *#q߱m4p.-;_g'$y!QVA8$!\k@FO܃l&{g'/IK 7OӛMKO) `D^0 'x-oFǎ$,̬8ב q`%ObpGhzuIul3T+tx"T:^,Wwk;䑜 kEsE}⽆oښ8a1U=@FFx^;7ƭj6U& 8l(T/sGu3ciqˤ\$/sEwcWV M=LK!ok]!mSF <~Y/^^^hw2Z_ bRG \9omIwl`A5M/j2:"1&%$%]n;scZv2RU6-S/wQ1qy?ҾmW}.#[*(U.= f;☯LYi-ۦ6UNnñ=3 ~x:Euh/$0A4lA6`{7=X.E9F7+3|q-^ EZ(k(KvCr5OM.%6%ߞr(Ė*pN8p2k?@RB)N/VfXHRfk U y q^ǁ}O࠺7J? 3WC;7駏s_AAu[_ JYE'O}xE UNйSRprkjv:Tc qT# ܌)r׿h⯄-/~*idn?QgL]xÁJ NE]R+L|﹁.e+־JtxkX'QN1:yjĸy9uKDy?v׾+jr4QuqDP~ Oc◍!8:<1 1ݽ0I+;M;_ܭ+3np0=ti/þ>ȦG&PGOUӫNFzѭ_yl<1jN֗e |Bz{W-q P}+o}n!od]0뜜0חfIe[6vF`-^V6LNtGumysh|}"BwvĜgS[sAhyq_μz8{h"KF@WZ7,s.lh۾1K"irq ~.ggC~LTy{R$⾯WO]C5f Ol9㊷],KP@\c<+dݓ:eIJM|GW0G*f@?iu2%)rI521OB&FAj\Qlj]#t'cHtA/S8ZXM&і4HQY$%DD{z;UCRzKei͌JYTlK.e8t#$6ڤr2ono.P]?eB~̪R=]]t(Fޟ0C"#8 # <Ұnޔ42P7qXLkHR$*s'sWF`E!E7Jn[ /@=ӯ+* ^i;9 z4 ( dSbxΕ$ r.W8`#ިIs $U\ .=`q` z3S+7ݑ[i* 27hi Y'HM'gHv9#w9TKVHX:sױCW3V |؞sXޫ,b-ma YBsdcP3=+a-YV;KdCI'= $Vn„gs~_~~8ѭte^/&W|iR r~U=뿞$E#ۊyp33ZgN3ufv8«e$rlTF>a׷bnG)6VkU 8;dןƚ`AxW5[VKavqm#u/cWc멠ĐF_*F<zVa.F\$+,FӌgK-xwv`FOEm%Ԥ>7|{]rw¹.$;N[9ݟNk'lh`fC`WaJLkZ+H^yFdߟ\F)~`?#wVs=8ݬd2[mn`WZƥjjYl~|_l~%5y hIm&#=O ׈IZJDGE}:fSwm]rK;[[[\=s&C&~d?ypP ~s笳5(W3>'Ԓ:k9拙JYts:>$n%vPV>#iAԃ\pzݭ$ED\L7 E2+|'ҼOtOI[- ֌,1@*C33|ҿ2w ^k,_ϦkzFe԰wA FrNC/ϜcȭBu*GH6U8j Hf FWN%>Vz[iڵ,TD0D'6Đ׻)B~%ĨT&ny`|^|O?|)njiu{kv$.Aého+-DRjZ hm c5ƪT!UӒR:_=D76TZ[ِRq23gAķz+ި< 3\c5~z]^i~'4;DuNbU{qkPO|o:Sb$kէ8҅NZW>wx[[M)$<a*ʩ€p_O?: ͔eGao"]*9Omݫ[r2|S^ϳ.PV}T<;.n46;#" cj# H}kIoӕ)r=~~l4߄z 6LwS1u\~M3* 75k_x)5/DRZy 6U*\e8<i&/+O=RKX I! Ss_9| 𭿋?xBQ5@w) VT`}s)I[*Ism'_ o>? AfTEV ln 9=Z:_ 7vri>EfCg~k7-Y_A:VX/%9Rc+6v{ItIag_ݷ["h'*-K_RjKўo%|Gq>m了+VY&`A đst8߇>}5_K4~aa#{; P;ⷉ͝*;@A#q}^ao[nj4&kt%FDv̱Wv2TWe&6V>dm,ǥ\X,cr/_wBF N MekCohOQs#[E$pN`࿀'C>9fԱmd.cb[ryJF u z7[þiy*P2F7沍O{q|R nxPq;N.w|@DRE|72<'Ƥo1}&Eu}:Fr _lgDX3)-K)r8@ե)*J744&9-bHzOϡTFyD,{ ~Yk_4=3JFxːO*A;O/P[d,KtQ_^^sidwg)Ҝ]ɯk3RIcE,W8\23f3Lխ5 r PA Pk}y:ͳ;ˆS OL[&e @Pdqְ˰+M5t>:7?#٧Ǽg0}x634cnNq.k#9ݓotyI}> UF)2=PhFbd81TtzO@|9;l.;kx䓜ϥVTŎ:sjQHm+]ISrjV &ubrȏ# 8a c[| j.FÎm$&{ǪHȆ]J{׭ecǿIRH#evݡ',=͓Yˬq=3Q?1rN1#:ք++cXp+rvkQψVfM'@66s^/[FM6tѠ.8_r|ޘť:2pYAjTPTׅYڌ~z?qz/ oK,a-G^ -1u|)Dq|G]zR"C]iP<+|?B%m_OJ<{ǃ5 a{!Id 's~4i_4H.u8,5hWA$6u^; n|:5s'Y G#Dy];[DaMJl‰DӴ}>渌I}kX}VqmH|?Z(fϱ5wj=283Gyt9bg]C\1KgБf[xnU:Cˇ|l>fJOּ:^-0) q8n&$ I&ѮG0+ڼw70"|m`Gz9:nsJ*{vY2͎:-lYhz](=kKJ0˞Ê/3N#ױ%?3:q~57/#%6FX=J Fzգ.ok\*ΥN+,lb=)QRj,LFp J4;Fޱ+F3J$fjQg qosmBLvp ]|D\M"c>YӽQ[k2Yb2隻k̾Dkc>YWgfz[Z\ߴWhw@xֲk @#eg cRp9'WHIW1UsFYr(7m_kmV'nxa>,]TFz(YA$xR38ds@l M(Afb:xsO^(QlV(=Z\gⓍB3~sቒv=w 9ox?915\kk>m>ՙYɁظAְJJ;=ϩiImRL_KP1O,I :ʮ6Z>w%%PPkI--Z Klݞ)? "B{n {b\p3Upc"R!{ow]3'z4dBU]K[%*|9rs@aW949Di.siT7P=Oi{oaFTR}1I-9g!}{t MXE=Q-);Ucp{5-SB$u]$~U^ç۴Ӿ_v>Ԛtox9V5C+#jDem߿|gW|?-Nrܱ"IOLc'Ą~&M뼏zgۭiè@BНt#؎Ƽ F><⬵wNW&.6C'`toUp.&2/N< p|͍$$GYT2խku=Gj?rk7jۼ燎n: ;T`NJkHn)L:WV⥫GK1xH3mj0*UWЧٗF6Á[+cdy|4)&Eر7ub=9~Uw|'Iqj ԉ?_)=woia%̞;>"*>2l> +ԃ__CQJgR; 4MZ[gIbH9`GNWv?r,Eda;.9\ _5U0xM^)]Nz6{giu5x|:c8b,־%ԗgRfX>KaݕHwlke*a[qT߈]CTdB{#;7_17G#<Q^ZiR$N9$uܹr׃~'p}|R -vt}ij NxO־.0x~+ q]%`XwwQ9+P/]ҿgu;t!'b?Yk>k+Xbmg;T{Wz|%LP.ZM-p2Ar>1NNoFtc*~9vW:}FƓToۤT}嵫ψz*Y"p7B~ &I4^ NasK7Y u>}#:+M~h2/KY8RÃyJρ>#+'W$KJ]k.Z?i=KZ$~{}SFfbu+A'85kOUOB\ܾTzWyM͌{xB7)m83D/.4u?|]V6d H[R<ӽ{?|c6ZtzU(2+/? L} J|L9RHVh;[ctG+y7]?(~,4v?E/兇G5_P?IxCY7yHQWߎfU\(QYU%RN7iӗ҂M37ו wt |rٝbBG5izo]t٦ 4)t:I_JQ];mj^=Jڊ:K|]obk)gUA3dcӲ}[P*2p2zYx{oRXikRgRB +M[Sј|}is m% rQPO-Xx]<G$]I ,tgj9l5jܬ{L}?]ƛZE $ #o> xsW]BNi.}ؠ0k'=Yu 3=+ TiFY, !g< (b=,qjh<-WojSۮfSlG ;v:}GbW k},tOyo˞ ϨZ,HhtZX0=z\cS'#?*' xϽu 2`<_{xo rf0uڬC6lwK,sŏ՟=*Rr`"3*WKo$L' Uuϗ稯M#֤*the r+fM=tB7 CYz"кK6q͜>N83W]ߕşQJ/V3᷸.Q `syof[+I|:Wm*̇8==m.hbpT }N<~kmj-Rs"OYIuiTPkpsu!zv^AE4Xi r ÷|esMcz#-7 uaQFNW)rb1=uveW6 Sz!wPWnki3 ]UΚ;f":Z~ WLE #![ fڵ]#Kb+ַ>s?i`o9k=/V44cMkbֹi<)3X'(l)EKsaCIk3 xhNJI䀧 _|TfuH9s!L*!orZ58`uۨm.ehE -1Or;Ff;EY-ia{Io EGu!ܹܖW,6$nP{_xĚ9ԬO5rdXhC:A`GBEr0Z%l/JMҕӠX8!@2Qԟd} ƖZEOB ÿ_^JVN[#stjh-Z &vvm*Sbm-V1<|dt+mq TW7)0xw7Llgj1=j2Y_]Dn+5lRRϗˍ1$Z,.q3{sRNr 8`⹉ Tfk|*)x^@>/y#<ֲÚk.#VV_ :`Mhq<.U02OLdBњ3Z4F)X?RGT岊R7O*/)bĜI-C8 F>^ޱN2Q%bI[ p zFmktACp6rUkp)o)==9)%(R2C*673Pf#7o?:[DK`S?;I>qY=L0:(- A9 B?I.pl#tO1}VՀVlu?/oݧ_ҫx665 f- g >eoHj̰1y.֗G?*/#j gƾnx?'GgW  /_Ǚ휖+<so"3:Wo[Y?C>h&4_Gr`{)%;|}PFe huKcݐd6i|Au="ê2 \G5 :W]J5x& `dyOmq?xn!+6\H ʞ81W>s0ulq ~{yJf'q >i>{U;|aVszssݪWRcdelr@w\ .=N2 1=@WwDkqxF1X=I8i9-Jmhϛi]:IBo'rryhrkߍZhvV5}y6 I/a5׮%=*7P8 İl]yj:WRy+m#rE.Vj@Q隓oX$B\~ 6-R2kE.5WIEke>QIw'xbEstKcZ8pww4}#SVM'm݌brQҭ]\41R>`N3W>H(xw/~ߨG0-LUIz]׀5^[۸(n߳K{;v^(QRY:NEoCNY/<%GulpFG׹#(*)@4SKA-SEITt*t?B??zO#Cǫ9onn&r$F'NVzwo> 4ˇi-g"W6bG*=2TsW&F,meް;F iXT;nz SVP m$=Ze/c'R[e&[n}I#rH#2yJq# jDj_nQ 3*}>\1D XYtr~Y QYHTƑTU3=OFuܙm%#?_>Njpx0\{ַJrHq\H. dlx=Ƴn`w} F?2^ک-Q!Rf|y8<O}J Jc YL[mT 84u!,7@zoLI+== .:vpg\"\$2hJ==jO" |R8% =AkaBڵEjyW0Psnf5d:Z8OW7wYZR$- R,$r¸͍[?aZK/mf- Qcq _dQd z1IJt ;9yS;F׸b6~ԈOt ffU}x'qWq$`ѹV<[$i :c!xo0@sA-H줊oA;F푨{ԓ^jhm$y;dr[:siVWn˟o`d(2rrPܥosv&~Q t{9Iu$Ƞ'uc{ ;9k#C ;2R?0ku uSXRvfǡ[oTOI|3$~)E7`o'i!$!#d`x +71[hӤ+}:]YgkY;Ab_ "YgR⼃ú<MwCT5ki,oK_9%-n9!|khk9ӵmMaM"ݶD*K ( cúM>%ëjL:XKpH$VIi~*]:k뿈:tOHT60D?TerF<;y)+S!0ߊtXY֗&\J$Q\K ,+IZuF6oi+ g 8(sr{lW1Z#m5!=̆'7- M;1NKƤoSyzXz<|K\ycb%R73ڀnx[0x^[?xY5NOK J;CI<2T!# k˩:#OGo =W3,k'Mr^"|6‘/x=LJF|@tvP)s4?nh,P00\;/ZLlic,ƥ$g +PDlYuVm Ah$*`𸻶WU.SԶˌ|UXgc1 ?}e/<6ZEwuGJ\#\p`u޴:ׇ755ԭKKMLH-"K09 ``6 lо*y໥l4di$-]Vec>A@:Exu&`~-֡ϰGp>5|3( bA;@/,WE]Qim6M$*yh鏗!/-{n@G~ķi0B-.Ӣդ0;!0AIW`/ٛmtkcP"i㸐 0 FH i/#"kGmBH"-,eF)Jpb'JMfXƗĚ ƕl^bc]F9DFU0 3 +5mmAc$6ixd\]8iX+}( A+ɾ!{㙮ƺo1|.1: &Sz T 4(A >c[i:.5s9ݰ)bgרv#Lq")]"Ǟp !ItSGIңg{^YH#E?!xX%pxW3ߌZ>|l%<ѳ>:zpqx~>Qs<񇉼)i?WCk!.uEvnF2Tvh9fi֚MHo2F$3|9Hm6o/4[)լ^7yJ#r(?-/>Ѭ]WSIìB`>iB .-I}&]+u>0d|AoҷPmhݞ/ˢxC:u-H>cM{_f +uu!XϿ WϿ4<+*kdFYr}k؋Ǒm4AyfCrbܽq=S5yV+(tP>?xY!qM:)u(y(>Y>M7JM_8bg9p=ƩhϗmC0;E*v7Y%|eAɯIhĥ=5*"?[՗Mu1mfH.,: _9gش:tx׼_q3P"Yh;d/X(VO$ZP-DJ4-$*gޫ\Ku"X,8̧ˁһ- |1.ՊGipiko2jHA^V7{lX(Ċ iČO^Ny]9. |2j0N]>:ln=zzZRxWC| QTs֬n"*qQSs[gk7'}[FRկg6B;}9HB .]|8ir[}RWx8sXqGBsZ޲XoНk>X=N^h [\OrGLEO&:pj_ڷ\v;C#.QR9kY7c-NMR-ԥOyc4YuQ'ig21+;H6c\Ceo8Ʒqor$6OƼvϽviV4Miv2~ZjiV8*2灞չ难p`dO| ?/ӧTolC9(=I94ggwI6)s“ܤB%mۣkm0fxZ@-0d#ڵ|5ujiCAsRGAE}B2uKn?)f,+$ pΨ µ*>kUYxF71PpH\S0ֽ8rF@+yOo}6˓Ws$3{v;W9o'ց)M cjO#CǭB??z8Jo67rcSr\MԎFd\؏ȮD{krs"I ׁCQad:}<;Gd}j(獿}HG/ڮI#KÖl^}Ƶ&Bd-N@'8@P18օ]>8GJ\GM7'Ь3jY%*ZuS.f7CKswN3V--)Qy1_?l|cImcPQUh@_1$z6p]0y-r$6gr)hp;OunzaI 8$tXNy~h.-MR<ۇ dm=s!Ɓ3w˜yޟRàYXHϩ&ݜX6s鐋'i;a\ 㧿j{Ys vpmR:8 5R7j0ƞ`Kb64/1x j63W{+i5si 0Xz^r{ v9eS{g K`|ș`U< E84{O =mBm;3Ѳ:hOO>Ս$K0lܑ'q5Vi/"=_"009NzgI_7EsơNIh6fu>$&6n]&TgW,W~2Ojĺ^uMI Qmx۱ յ RS}=}+kxSphZKo,JŤĶLm]zg+[S*+wh%w $c`A]YH(! 8:noYkm% {/[xF⭢.qݣf0{O A[u=Gn*&dFsU(9*xR\'QA~$lad{"KblKm3'./3ef#**ǖUT?,ԷOH\{W^4]Gs4,TFqӚ\ܭS',4[)5-g I  of᷆4Fm|o^)*<'8ݬB ,|dx_γ8e%|2.:rG&􏋺M')xZ^Le .lm#5w+G~Q[/NRm#T+p#۴qm mĀWNfnju)Էxvn]EV.Domr;bM\[ WRiICkXqgڼiu-> lܪ[ 3\8IsG{~ifV:Sxfj9d^2I>}w7̷(9nO6ۈ@p!f':e6 M]0Jn1zEj+ ~a\۽v*J&<7>xjVWRS.[^$VB"edٷfW//5G;wkeɹX ];ɹ#¼Oo՞LiFs_ҽY4Ut wa*Pnǝ}Zҵ{|M4&;R%C)_,|S. >]E+,'"k,r$3][t[cl ;cC{8&E/f/&63y6{+0ylP4}fCѢvmPOިuz"Z,uRXiqmK[L,:0qI] ii'2°K\us]oACč,Lgj37'v X|7/ϋ 2\[oⷻ,# ~{2~l^閗,|jۣ>ȸR:3 e4̜^4RO[si>Eu fP.]A+#˓~kO׏b!V?1sI=:p=Q|%Cgt+d&vO2K`da'x{Ko_bַͬh!F:qu#澦uf-oǚЇt}(x9 [)#}'M߶G#$ھp)5ҳcԎ&Yx 3ꚮ=֓kvwZ(/'񽦧gqsy\[2I"y(lmC/[tݞwK08yOaYUnegAf/xR 5i N?jVLyLTg'uaŽծ]7Oww z~eps|fCD R9(/$z{q]WJI\0x+F2v8oګׇ iƩݔ$]_nD1x̴xv##?AX!Ү|+NVY|Uv~Oyfu1tj5(1 i>$F+m:5%dja ,_+Gͧj'pA ,y|=GUV)p\U[]&PbF_G&$|W$ΦMҭ#[ְ$7~;A70IhYJaaw v0RN>G$"u8ȫ<])3m9njZ{qlhv.6V9vԓXԶ+H˒ Ʊ{ڬNH`o.+m%FAZ5ڌwNz^B=fgOvgj7-$ZBU62x⎟.ZJVp 6f;S!forJ}zVe@ݳ)sZbn 8OfջyS,O3I#C+K θ?QOoQQ]ǴC^3vD:yY-paiIeoapx9+1Z!i0P\,V4KXfRN;`Nԥ97 zdB4yd nZkZfQag|6DRmFe9b{}J>:k^/Ҭ_(+On/3V%k}kڹZcu{.}64^3O#dޤ9\x=@~k+0*&D6ڄ\L2v!5uq~xSE}'}u(4`Jk,&O*)|S'.pOˀE|.jVr2;Gc6Nqڿ?WK=yo~s&n[\3EhA:ub[9S4nJX!J--ϥ_lI.lM峜y|/0O/[9y:|-Y 唈b'ddݭ_/d^xwgk^kkpґCJ儀@^MǕF-yn -SڼUZ0Gm)8Mn}{xBc3z=叉^ִ޽ZL3F 㧵}I_ۿyxW5yr(6Hޓ4Ο~_4|SZ? 'yIUW3{+ON1J,vD$FI!c`=>\(XLxo|/k]!S"i-o15_GMg?Aܓoq(MC7Bo즸b[4kO3;{?i/1|nqJ}5%#¿тbiO"Jr4R(5#[C7W~p~zs^?gV+[*_n$W$^#?gRM_OG-+Wڜu_<7i:캒mW *bDnG`mxMkK9JH\Ñ YQȵC\v}W\v0Ż <3FBwE| #'B?>fJ7D ,̬ dnj5~co\??eaK [ϤuψQ7Ph&Mi`8XEPvFGN u[{;a{ˑ @ Ϡ+~B*?e,z_+Zӊ9M#ٿn D?.ۆq<3h#J G no _|"]yy)"e)xVx7>Z31BXHak[M}㯊#$}g?c_H9jom?I}]+_?Ndxzt$5kXy%';3Z-涵EPZ7%@3*W㱯76?gm֧:C[rHn >Mv 0k}i_/|r~iM,$p4k~Q(O灑[W|8ȯyӿm_4KEa'Ν::d(Gtq(ѿ!SuOoӾ|&|]۫=oMkj ?6%Uzץ}ៃ>͔^;KCPkym/y?rNG_.|4_AȴX=,F#g4[xG3q/́aӸTk!WyڟwawK5lJh{MMx݄[KtH?gw XQ>ng+to^OF/fOkgWw6y+K]ծ<™=Xs=)\薌4*'Vē#aU&MғJQ~‚S78k/PCB'a3g5^!C\ڏ?O| RR+>hxK{ۣdvbK?!\'o6'8~g^~x~.|#`-4t9hnRs7_f#+s_&㉚GM<|Q~t(+.E]kQR~¾ۛGb"'h]M̦'^ô@Ќ)#V84 o=| mMF)RKZ yo񌑑6[]<A85e-?dx#Z7xZׅﮮt=Es Z>^VRhkQ8| ?]e}AWFW5d}c]gyM;J[WGp2duֲ4F@Eƌ9߷Ƕku?FR/jڢ908|/2$L%pǧ|4|+_7͞ ߈??OCڔy@z L<SQ"fotoxx-20.08/images/markup.jpg000066400000000000000000000776031362435004500163370ustar00rootroot00000000000000JFIFHHExifMM*V^(if%NHH0230Ƞ01002016:01:17 00:00:00Fotoxx:trim_rotate| Fotoxx:trim_rotate| Fotoxx:color_depth| Fotoxx:draw_text|draw_line|draw_box|draw_oval|trim/rotate| Fotoxx:resize|NEHPhotoshop 3.08BIM+ fotoxx, art, x Max von Sydow http://ns.adobe.com/xap/1.0/ 2372 3832 5 8Actor made famous in Bergman's film \nThe Seventh SealC    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;N" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? c_i81XFMx̋AKh ZӨ..T hlv#۴ r5{Fצ6lF M]tU֭?!=+W]/ Xy# GR] 2C &ӻ;SN;qQI5iEaAs?쥸b=RDT9&=̪ 1qYr+A\,H 鷵M5D(hƝ82엘{b|C'NP5HXŏleN Do%w}?ԥЯX@ĸxkC9{5%z\fQٮDq}U/,/MC;? \[E q]7Q&G)n)JKFoBS8!Vk@Y`ԓ G[7JKc(6j%)kou SΐrIź4v3nvvI;4vAr~*Կuيiu?򪎷]4.$|۟UbNOZHc,F@(we+)8K}H ubًwܧp>[-pOZF5]ѼZhJ(drZFO-H gBqڪ2ܩS'ɔ+qHM^SQ}hbTdTߙZ1Q`)rQ8vG֒i\zE:T [΃j/gb=4:+cLbx=uAE+_+%\nLyEeS$n e%t2娛%[f#neH4@V稨"['Y3fibJ%˄U M~_d_y~&AF?cGv1 Vu\}V7L\Nŀ1ٲ3odSIUU#ktd>62-zmJ1;_[Z OCvzc W>zeTY{]7<)<J¤9c(//18"]~ծ'9` 𩮳e-s]g \J4Ρ7ݛ0IB{%wG?~m&Ȝ+8 -{BHVW|,34K}U-ٜOqqQk[ȑYe0qMs6pJRNϠqINs߁QHeK>q$-\`sOjWb'wq,l",aB !W>ao/fTS࣭.TSdIdTfH+(Sa? C\LL08Wd_wi"b2b+O1c9ۧҵ-+3`~ NW`~7<͜˴NIs'n Zf9ʌV[iB,$cp1}RR.̱ UQzӂ6hna>^\;㿩&R$c z{ſ6sWeЯb*HFˎҫ$+rHsf̩, ݏzbDrOebÑ,WA4E&lIxLp1QIQmnmԅKڤ;9G6_m#4`A'FM~՛MQ9)?z? y-s]<-'?TGTmM_r a$Ej뚯nsM7؏ֹyU [;}QK(A-&| )$_*fAV tTֹ^Ok;BN9\h7[v;{7K}Y4xu#0S?nLkGъ?WF,C? ViP|_ܩ5=]sy's(#n[3]M&o&9 [sNg'տi+1v9hT:zV>+cm'8f\"SXzw.C \NQVdӫJ2xFT}+{mMȀę X jZyNRIb?SY7=E59KkM%;5ܱ=9<֦\xG~#[a" hBy67]{q[ POaCS@0pXvO[i| sԏZhaa7,\֪KsE1.rnVF\GG(i0aEvԈpR{ ۡ!v=?)@lsOUqZvH' @ u u^Bu3J}ʯ MhD98rFNyT&W"CM*,;BTR^ldW#Y 0C9NYDxRrZcx^R;`ZṄn-$3JN">_r8N;7j>V)M6V+ s]hiYbyQjԱC;yr͌Nea=E% siw AVR \~QPOe kI?yͦHxtYsiwʏ )uW+֊vg-@bj-Տ)]ۛ(H|O_cQ*(Xq:ZoI8;ԥ@)Y> q8-cW/y&vPL3ǩ ib,Nr; m:jFRI=e\j,ޣc02zR1@zz(5zb6 =jQWHc݌J\xJ PX RTB9cSd ZLh!WRxvliіlsry\3R3dH wJ``@2=qN)M1ΫKE G<}*d E7 $I}Rȇ&;#_/!10=VMYX{id28RW50C{9!o_,&Ь} ̰Hҷf9T}iC.QlF@-tIoaZcs0pGI#oެ H#jX %0zqTV #jÞ?ȎdT }ǭuGL76MK9܇ׯ+ߥR:Rjvn頗zMh#J-8M2-^\ǒ+WE}EgjIk#,`Wo +:)RI=,)!MҮ$ң[`VD JހՙR[wh Ps׊!}m2#m%G@&x8oU544Wrp KgxS~Ӱk]Vmzq4٢h<1^!ѽǽ_2z14`F;wK ,~\sBovF`Q}*[KN}|QP(Q{rŬN=*my&}1U` Ɱhoq \ H=#8ԆGdTʬ[=x:ջuPS&zDJU.GspΞ4QJq@5{*@^S M#/'۲1SSCa-Q9I8A׏]Gie&tdu3>eeڑx\~UcAE 2#LrAߑ nd\yR8;ID#8LȭkYSRӱ*zMMEK =D1AbPMbr9VṂ%`3ܥ Ѡ{ܷ1}G!A6OlR6¯ ŸR夌CIR%E*A'/ǿz2lY~tlgoBzVD : 0V,\ >dJ!19㨭"۲%ǩhc 88z r2c'K|=#r iװkcW 0T<[MK}X7ȇ;søMM<ݡ:\u̒ZexTHA#=U-lvWwb 7aaE֗"Wp'^bO5U$]H y#CF6(Lcu- ZB_:U\(J$ø^ȥInml61E8Ypb92=u,y!ryR?A}NułW'X"K{c$'lZͨZ=\Ȼ%;Xhjț ?5JhoO[jbr^6ڝRH 8 6$30䎝>}k7~Z\Io/O@;]hh̷_.}z`=VZ+!Q8 m(V,1vn'z Ҿ4Hs iڸYChY2x8ib]'R.?qt):0URòC4sI/ 8Lt=5GziV?ʹd KF|R4Z6LkLG?7V4nl1[3 v^Fu]>Ok1I(ߖ}p2j쬏0jZVbc/ޭCJe TqEb"xr2j# T`ݨ 1M;^)pH9 >[G`SA(ZBy{Yw7tX22xEupGɫ\-gB!fy#ă8Ӡ2\ðw!\q٩ibbV9 txk m=XpȬTvy**eДe w5Q=}:K&d;10D-Ͽ=OD3"c"Q)H"S0\XYYwb%xTfي13J''h< ?"#qSCԨ Y!K?&y"2_班ts(\ >$/2ӾL졡cu&ĸԫӇu3^o*l: Eƽ}3!6cS]5r!hBPnM`p{ޞ<7f&⫦nF 0{ӕ>/7m-TMGW5m\=h*= UppA#@ѫ~t*r*VM r?I- 8M;1Zwȷ61%fOvipFysZ C.=G?1<ڴ=O%_FmgAYPU FyT.>6ؼI1NA[a)^Ãvr)[T[Gi>\q Meo=1kxڑHq+YR^CxrT(0"q%XyzqZZseB pyuп3JLj8ϵ#ޟo4fAW}lo{HES,)$jbNWycښ3Pyu~0ؼڜĪDlٙ{>Z±XY.^8 *{f nV#d>=tf6Z2Huv,82;}VxR(a 5˩u[(o$o GH:^M[-P!_#c𨓴J/kF*d;]SJ騦;BH)IuYXxTB%2ghe*N%b_jrZ P=#lY Ku#U2@J!&$Kc}j Ƹ,z -+=TskTdz> `90\ h3tq:PO? t(Dn)%/zV;٬$Ua?)4u=7VuGcj/ӭyyˀx^:q֛HF+CɪO"$#+T9'k*wwK筊DLcI#vF_sq]\I vm~0$79V-Ų,N>f9Rh@á'u1ю86~Ƴ4!FXv '5x-)#t5El))#}ncܧ-wpNҬF#ڨ{M=iP '+Prȥ 'fr?KPIqRFY`Np{})LhNJHjr^'.?4kq]I`k/}KOZ<<AL[\C%r23 kW5S[kw옓pZ#"'8d(S2#z|aMWTc\$GCzyt )۶ sULe_i}C{EQ$(:(g7ZXt_ŒWPX`sצ*#3`IjɺgSqVsޖ7rD`0:;г[ "GI#8r}I1U?6Hhygq?< dvIhR+8ujuc T׽M*2h%HT㩅EfxQSS]̚OF5 Ilri ςp=iBy+">nRE7q:nRɷ9*8#Ӡq/D#pq,z4s{$) i(P9?*ko L8;>ƚ|Gs< 18wٲ.morʑng(6-z5)[i Y$Uל3(cfx hLB+-ogg; ]$g=1\f:e91ʊU#4v0 $ǫ5cED`TțT>ԏ-#p8-B6Whwc8RH:|CglJ<^02}ZTg#ڹޕ]OZO9\ Lq?~Έc可ҬOcw$ B0V#]i+kzS <8u39(8 `'h'ӚҳUhՂs'΢J6^fvBVNOPqiS+иvy!WM+K ,1S~J`b) Ack8`ǖE:9ӌr*eI %h4<`6~ZK5e5~1u$#]%ۢ0>'?TticFqc c{``}y?Υ5k ["!`N.%?j.nWy[ 9 Xz=}1*J ?ڴk= Q"Q¹j#x+#N_>)qKC!⥤ VLU&lTH _0:`h- z`t j A: IrH@ 3ڷ2BFH#*BԻ^F> +!7mq'8( 0n9叭f율>7 ',JFI2-"%rrA=6#qVe Ă_53?hN93"1)qb}.F+j%_yLnb&W؁R#UVX khm|Ӽzxf!iR? ohێr?ܥO9ΚT8UVGtU<~T&ՙL4Rߺ|]LFjE+VvF3(GJ<_/,?.+W+5zͦy RʗܸW+u*"&mFj_Epe\RQZrH"\ϵW|ߌWL&N?6)u{n}ͤ5wVH$}zY]bmUՂ%” tOҴ1XW^m4겁is[̘!sN]&cvGCD]ktevVM iAMaczTTZtXLmEGbs>v]ʎ?I~xrI_ AJ簷6isp_d`IRlU2 ^\/KHݷ&a-˕e0ߗ8-*ncQiWw $ϠY?Qk $^$Cۀc965cr9SK^KNCc9Cl<ɜ":⪭+Ҍ5 my`~Mri[<$e.&zCpNt=GfQ]ϭiٸVoEUVÄW>4d>V/`Rԍ, 4r Uˀ!09'Tˏ.=%P ¶@*  S]f#C:ɩHHG'&oH= zSUVVm5v왏ְ⢾MpxM&ܨ#5kio"b7y4P ɻ]id/U2*wӠB8=d05?h < ~qO#oG~gʟ))kgU0זqߣ@Üʲcс0 jigw :bJ-Iqn8VK9ށu dH1Bp5-$l}gih$M>bw1r{TC#X\TJzxcm ݑKp*p;-ƫM]L+P][iփttldvǖw 2IU#Iuqi&uj/XKaG6jI{kw bF A3NRY@T靃=zҒҖ;ےs5R"\&HVmMf 8ZM)jVV5 IJ%όydRDS2p=N*u''PF Eoo9{UT rXN)2n5xog׽ %z]yRR`ɜo`QT\U2H>|,n LO""S`n<4:Y#)7P\cۜ=j8]:lyw7㏯ZBZ2%Քm 4UPOAYzl5,"9GzsA%M&ڕi|vg ֩+̶cOc^\Zzpë>BMʰw8b+еK /hͷ +=i89EZE#ږXK>5"7 }+&^fGrKXz,GZ1&tvp KZE?֭('bϵfv˂kF' LFh*.rKG`䃆[u9XNрW8"M2n>H~wV%c2 Nzi?*i'ȉAgG!NHOT1VaEMhڢldt vNpEASۺmLȥH x$V]]!sޅfeWhU #ifr7XG>?tYD1meҹݫmcnvm~9zyg7֣: Fvbe63ש!Ln,ǖcԺv1.9 tm݀m($vU$BD K9P?Oyv%G_aU=nIbը3rz$Iq[ۋTcQ1pX5bmZlj{_NJԎ (G PQQRhv㨨S֙9 g qR&bk 9&TvG=h9=j̶vӂ n{p08w\"d@*$ߩEH58 I?0pzm=TFZOs"9S_[1u-N)a@qJ&73ֶck6*ߩjkp8#V w :"?~w"P2S1֦VnӫWAOXm'je5XMQc[IiԞSO @1=/o+˷")1ܐ 7x\4r\AWSST`ڍܑkVɺޠ*xm6 Cqĉys2aCҼdܼ 1T-i<Q40IjXAl4RCo:ZIgqڐssYwZ*!(nySV{G:t(JzT4Ź'Y<27;YG`~O] +.a6<c9t: :u`aJI3tJN3VbbyvƷ,*Fc1қs|,UpFI瀣ɨb0oR+˛q $Bn3GL<V͞4"Ό8Y=0EA"j~ixnUld#P:KZ#'V Ƕqz}MYw  Ýux#zES{ȅ#΍Ńj0Z_nR˴;[=?fiᕆFr+ W7Hμgq)Fƴ(U;A\tcGV>5l? 8n3MKS,څeNGaGu"1F5*[+jG 'N.%MWR}HNd=rIzcqsDK]?z]^UO.3=*D"t)Yhx*P{7Y|ʼZ8iI㚞eSUM@R%Ib:c_#PܴȄ$(,Ngs @p}}V)ް9{US_gԌI3Nh{+bc1q ?ܲ7{qp%)$'iB+Hٶ-'!IL̜=Ibi 2l玵%Mb>j݇Ѹ5*>Yy p>̭[FOLj*~# [0=pzsNA>SZE؂qWb1ND:4p0b r e71Iea?̚|Cat;*W 391oь3FT0Rs~~U}$QaId#9Tf"ɓs?9MySrZ2X,ME-6׆oèڝ[$3Lvȫ;F5kJRX^N@?h{t)Ɉ~>#H_?ws݃ˤZKh[*'n N:5/GF 6eo{>|+Kשm1x2&"*I qV¥_y84ƞ$]H+>vf+u?U*x"Z^NBa~YAG*ſm[{'RcZxKH$?b-G5PUt{ȥ<-G!;T&Q4vt WwkK:whSu/oêֺG .OAX7i-8Y r:#pkQ2Y2JGgi|9*Jy D͸ q]ڮ2n1?1Z(͔#e0$w ֨zFdޘ p}vrO8S},?BPv( cSOd˩? wEFEW[+UX¤xď;ZTr󲱹2? LA"|״\ sަFL`;mt#HuR=8Җ#2\E(5V=tWH[`&1rA[49=#FՅѼqnB2ScڮY\%ո@!%HJ=ީ{.]Rnn]2)EFLs ?u'q1UdyE93;tfg$dd81~>\eT@ʣΓ`I:Q$BMza3I8*,^ߕ\-7zsadxmӊQ]D^]r⚯TƩje?ucZJ✠) SL%}0{*PʹW֓t6l.=B5Meu˓6HnyV!mKr"4Gz/5|ۿ  WO<*"K7P7LPHW9ǥP>}u\ɕ;g$ zu3;;GfCzcW-cq1 Ax ͂90p8]3+ξP}F'B"c$c=2endި4)8$47(v"b3h$>-< `uQ)%+X f_yhy q=+Դ[.LB1_ '$U9'+mD j!z̈́1MQcN^1N,smt[8e)"4HÒz@{B[߳זZs#?i<y7WŬ" >nUY_=N׷+8dE5'פA6 nQ${Ӂy$jϰ51}sؘ[v 8?[p)rptp1uHm'[TIgnVPWcrw#@Nop#,dی*/ӎIzIRWR;yKq&.FLVl&0vUn>w,MmgĠ)=^vc0)p#݉瓜>SN[Q0$55Wwn]WYK!>k2q pACTm'ʍ!H 6G8X5/E[cIwB, #܍ݿǙ\+3+:Nn!4'5^Fu= ىT`jM^&P,_ ҍ)-uxǖUad^!vԿlN]Is$IyyK!ٛt>F^E݃ڙ`8 .IS-p\yPNtYky,Jǐ8^+d6z[sCp'9ҺlƹO0 ˌn$NNG^+>TfVl>ףjPFhMǘp9cںjkvRo&}`d`u*Wm4jQWݖ$ MElպ FrNw_ǃ:Bapqb~wy#Y5OUQEڇ09>Nq}0Jewdr85cR7Eh-D8ŒcRto+Β6&kz&Zkm.Ae9Gܜ3Iu=ZlZmHxX^P=MQn-mgU'6vwW4v+E`FH>-,cy qtyavүQrtpwBHklDG9t㎕G Z+V+9BgUϗo0TgvQǸ uOMU=S}2Ց+.5M7Rxqڟshd-t˨y~Pn?1qMκtPbsa0]gėҔY!0%9Wx@`}{R|%raT;wdd8WHW(hZw"y%jPou Tm$N}N3Zwĩ^Hf#/$:g3&kCG0)P}y*E˴9;L3\+m[ݤ1]Krůۨf)h[f ՇLA a b*:ʳnEX$lS|Tk`fW6 RRSd@PaE 2< g= ԕB>k :1+P1M }cP8H]$ `S\^e%FVO:er˶E*3U9;ZCs0F)3gێ?0孇T" 1Tc8ukPS]ej۟J5>OnV(3<3qlE%m_N:D,a#/7XZ;2wcjxrmKp$gEBһ9zc9v 0g)Ru$#L@ v[& A[gNf8NFOJU,\)SUjz2y6cx8c~UX\ V]. CVtFO?زF@LIr!c _c$>PM_C5kvwK,H;V^*{F> m 19>B/*t{+hkkg!2XpI=;WC])thKv䥣M~ `+G-.L!aHK41'̊r.I@yU$tӀVM@)n\+dsDZvxO"]t'h2߳Xɏ0 X 烎8Y1\yKKH?_]'QV,2BwW-flRAⵃɞ8EY-3)y><Ġ9XdɬK|Ardvt`Fpbèxj,:}{mҴkԅ+hΒ}M[Q[v@O 1Кк[FqV8!rǭݝiĶlKt`FAKⶻ\>u¨{.M;:^K-k MlEe -XIrW&)u`XӒw«KvaIDC`И#DDpxkL m3מlHt\,9SׯN%n][ʑ*68 \p?.v,i"#zte<y7+|z6K(4wGȓR: dTucTR(IR 4%H&M0*`r)@ڊoU=Z=? KF cg8sUW4{`6lpxBEd0)ym.j1rrX6n9:;_K17M@B8K`FV82O={H(*,`n `;4;C$۠+};~pr^!s< q>W ]WZ~E~ iAk6d6.A#ϭ[φ g8֭?,F)"-$vEhֈ%XnIиS. _隕+#%~Rp::A]@{p~KUǿ5R7H#2885Eʟ+!t}7YR_=v`A i)rYY_5ȏ,NI OADl . =SಕQnϟ0d-֐e}"JyZ>$,$`;~T>mg.1FڽWq#CEr ,۾q[{;-#XcW}.>MK~o-yxP<ݧd;\g遀?ZPCb4 1éfl\1ҔQ1NIj@18X D.ݏ^ȁX7rIzp,k(-s` (xl1oIB6$^~e}Dy4ep>Q]q1>8#EEUUΦӉ0K(:3Xt(h\;i\OZ(u7:{xl+O("<yk-oh-*Āy8?E[$¤xRtBZ({gkđM/Mc4QW--]5Fgc9=4QMd'j(: BOCX tp##(;fotoxx-20.08/images/mashup.jpg000066400000000000000000001445461362435004500163360ustar00rootroot00000000000000JFIFExifMM*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-20.08/images/mashup1.jpg000066400000000000000000000437221362435004500164110ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 200 254 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ W{ׂ~~7Z],jWF$1G667d\(}n+x(aO{xC\ko I]i^Q-̗N;~>i^/7:5K!.jurob v_~_mn~@@ఌ`8aO{ZgxG.}*YAjאa@I|t߻<UC𝷎Eľ}Hs228py88voBm$'?DW⦧# :mS';;4b#ⷄW-_ñ]FO"vHE޽ 'Kv2}G+^}ޝl}'?›&!h2X{֟.ӣr#n(?B۾(Wǡ=3 uz'qZ΁(:ק?FϿm!#{_/?
    S){O޸ek^,|㿅G hC  t>Ί"i-vA#W]#8\hIПww'Ï̛i>}O;YvG;YvE\I'ֿBܟ?? j?ܟ?? j,Oi>qA]#QqA]#Qf?=}hIПw'Ïw'Ï0[C>4r@&i֕r[>دܟ?? j?ܟ?? j,ú/u4tuGVXC$܇dܼ8h/j𑨽ѲV"5.$1y",*9WOg5Og5asG⟊ԒZ{_ê^f-TyRH^:rsK]Hj+VKBMp >`_jù>şkù>şkYA{_v|X<=9POT_YHBG"l^kxP&wꗒWrn.9y8p1}qA]#QqA]#R?=}hIПw'Ïw'Ï\IX'xd牷#A ~qA]#QqA]#Qf>nDXkL$? u9턒J68{_Og5Og5as?.v[js/en֦k I Vvpa@U$Xc<;YvG;YvE\_=ѡ%̋prvgv$'s I,Vf`dzq,_F,_F-XYWˇϽ>Tۜ sfK>OwWteb[M9PVe!b5$nVfՈvSu{ iϛs+2c@XȖQ4@Pg>_(Kv< \͡jNih ԰eaLbX:w%I`HQ䱰.k5do7Nˑl*ڥ_ޛoOc&=BESE'R$'&.MJ8N =} τnFMj:~$&#@kfx.h|3"Kk7_S4}ɖʸkuJrX Y?y/,~=aunͨVHBs'$V:턒[PCwW?]NlhC{qd-8һii%kyu1,+S^*F'MK[}/4HȺa\!V"Il(5 uzφiӢQsDJBA"̧(5KIwBSvM3GִE_-pfQg|In|kvHmOԦ.5(oKgp3¾:,ZpcFK6/̍!kK Ȋ4euR4:Ljl<4| q1_妊Rab#fD  ۳UƵZ\,;.IX΁iI_Kʾr5 TdWt o[}ƻjՅƕg[[&$3slѷZͻ!X=tw |Qo>u 6 핂 (=U5x蚁-@MkW3_[4n@PO_)6EJ4a&T)*ph_!MF;i#Pspy5n]GjnVF.K bmݕ9t㞏Zj> ]=$,:qPKNm.JJG;M:O.u>./3 L gJZӖX#7K 2/>]֛5?LkOӣ\GuH$goYjc6Q +R':7&e5Vф7@$S .r +9+]OhhGs#%:򢤤KERQYھxa90]I'T{Ξ%Y'duf-;v%\e]x_K=ɫA5f R2(9=*ޛ[[I WrB=A<؊? jaziVۄ12pLc@騦2.ѷ Ɔ4Г'| Acӣc349ĒI5LiQ+:Mjy79LT)xEҵds IJHcfQs9pkLD* 'ZcM-"n4qU?1)T<&/awxS6~O<[$jn?PMWď~m^I#KS$@7a3(U$1]mqeϡejNmXwQG+(6 Da000NkGY20e=9 qQE!Q@Q@>xGƚzށi߈V*2?2f![#$꣍bQ*((:~AQE (9fh5е;{%d$ϖ˟VxwO ~5k~:U óؽ¹%|@U ׎c7v^KI=UC+)=-~^gjn@F!V!aXޕ/.Xm9h/^VxM[p@9y xJx~ pjD @rb#ho!v_]QV}/%F.OWwgcFuO-kYʂ$ Efͳ!nf#Ouo>_uO$qİ%!@-W-m2+(}\x#z䚶=\7^"\EYU㚝ӏ?W[]?Zjz^"\4mRMs$ I$$lA;m½?mCV&\-h|#YL91U27kݺRӛV& i< ?-ojixCv4 ͸ e)"Ӎ_[q'屳[\ccwQ Joh˫ɤF.˹ vGDʬq8w]+]wx:};QX328npı9Mr 8<׌[y6]{"s F2 FQ)%nm?eO[i<6WM{P{Wu[?ȀƬyC.ljHq#j5Ξmƚ$l a3*#rk *ͺhT(_ڦpOj/!7Fv,J!1'gt x j{^SUY[72Zl0T1 /k~olr ڦ7ȭq/73\}-;@̱EbUYnqUOSZt_Z{RLvOI[d(߻xϠ'?]mu=]#Xa#4e.VV|dS4_:vzLjAjf.چq ܽābTX#R% 9__y=Ҿ'?~|L&ծSL!Y+Fk[|[hn7X|t[Gl|GZ]iԉۇ @s9ݮ3|Ѽy+bQ&Mj77F&Q ;Nҷ8{^6/<+;:֚6`HL hMt8]/$i hB]7Xk˻am'UM pG3G֎OUdCaKWsi$O/#& @$uaas4^,+Yi:,k J+nxgd;٘(Y[,g[]r^0~>(|?ĞӬ<'ZZjo5PGmUD +9gAZgEAy`/d#k '8>;/Z}oJѯtcx^E/NלT?uw.{6urjsv2O#w [1zjo5&.#hpk+A0@*accoin`5$`ry5bk5{jQEHŠ(((( kWmlSr',8Uc&ڼ`m2{Y4?X>[_vkubiZWc~^NkH㟧0)iŶ~@빼ߔCg T)KtM^whUBʥA2X)=<]˟| 5nzڮ? ӳouCMԼ -mk$(fu(жZ ȑ0UQZz~.“WJNMA;5s3ΝwA5֡im uE=6y+Mo֯ylMZNZ]wdZ8!YvV$p#=Z;٤z{j::c;cW^k.]W[Y*p[?oHüGe4xR|QO+" *ȄNdҚW/եݯnD{;JoNFnoeDIIuOj"<(;9%0pv1F<)|_ysቮk jDDfBihY 9ko x'5c2éڥc};I фb"98ݥi~wroG+M%TfP.)=)vJr,ٚDNxrHzʾ.S& ZQ}_׭Hlqo&Dyp[9]đ[#ӵx C=no^&k-{4ppFX`u=}DJ{߉?ĭ.H<6w  ZMx{/i KK # pqc|=D:<@fԣ{2]r~pNy&'t?~'⎓mkZWo5]tuX٭0M>Al\\tņ k3+mO*2y5O|s4WZ7'^$:pʭgw&eI"l=|8K^ԼE=C4&oϊtRqA{+Dw&v rE-x ^='R17(&Xq)a e=??>_ K95HeDiHAesH _Qc5Lj:zP6\Gx㘌3.Ve'=_JSi NאQE%Q@Q@%ʣC#s\>oMecӦѦUE23a|o7$XەТø+,5[ewk cdWҳu iwf7:Mɼ"d/=|A s:Ε5ŝr>ߴ3ķЭ}7J5[_NvLѿSi n.Y}n{%C*E(˜dY^~=E[1Z.K}$O$cR־>0mMgN]J{ؙ'Lo[.Zi7]8wd(q K{Weu}WF;. oKHeּ J3x/^^MdLYc iq"8M蒡䄮COx!|m3_i9YpiWXZf{svÀ adJ— +w[~8[,oD|=stw-#`RA<ּ;zGl mt=N`@ 4"2JW[.rTQEAaEPEPEW-tVXLfKVht ucgwep,K'2[Wpau#/yx.X).Y542F#8JῃzW<[oK}S[":s&vWR1W9kH' .A[/Oo]߳߅mSmAėz!ImH.Tw |SָX%Y;B@(9w?%?+e 4^VGY~~Ңi e[#&YUn^9'$4+Cd*H+[%妓H!|p#a`H%Ns׭v?%?+e 4to5uIfb2궚Y~ki#9|Ġ`\6~$ku85CYOTm=%ibD@S"}{]#_G$Gl!?ƒ[! 7]]=^:i&ʀc9Z>K.!x^7jlNzW .A[/O]#_GN_>y~l( k2Xٻ2U;qڹnu: 뚔˨"I,@(p+H' .A[/Ow.~AxȺj qvsVt&jW>7PTƏ]_$Gl!?ƏIt B.[|jj&-R-\+˹xArK+Q/W| Yj6-K[5%Z| 1HEw +H' .A[/Ow{Ң%?+e 4KVi Ң%?+e 4KVhJE`He# EQEQEQEQEQEQEeH"ydqh 3u&\+?pFpF?PX\vd.OЈ OKW5o>4 Yy8n1rX`=꿄3pGF[KM^=JȮQ3x iluYϦk5G%UqNM?o"Y> ?YϦk5Qi9|AsNԾ;^G/uY6GCD֭j>#үt\) '{ڸlséBLwQVf=ɭ;,m^yps\W~(xYVOi)47~l\Rxj'"I½͏KW?MQ uj&Uxxg^x7p=X~T B$yUAbNp88KP|@ON:}ʛjB2е~GY uj&T]g> ?S>#cI[i6V ci<Кz.潦K`٤\o93PYϦk5SZx! yngȊ 9mr] u=36.!1<;9o V76-t_&+O@ER((((((w|D=tտT54`aޛ@Ccx#{mi[{l',. xoѭ$}]:_lZ΂Y᝘+pW$RAՒifP0)>O/*SNַM]YQ? jxDW7W-5[O٠6!Bh'E@15e]x^$$WGo 54w)*wwI#˅ŠϴxK }ğ*_UO[]3Sk=+zuxz=7&+YpCofE*QxIk[ė-ֈvdjK+>-2b]BrЩVzxK \O*3&JV:ݭդWqGN8Fr:'zGot۝/pc xTE{rloyT2V62$*A&S$*_~VzxK !<{/KO|5k_m7mqV5xWb1Y~oi6Z M} J+mkyf}$Q^ЩVzxK _V<3_ MxP_ͭ SNL"8‘nW` n ^L>x3Kӵ fk[er+G?T_=Vt}sRmRCNUp#$j ks) (((((((((((((((fotoxx-20.08/images/mashup2.jpg000066400000000000000000000502531362435004500164070ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 247 262 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wz/i!eE ͅ)4Sp(gQ݄ݕK?}'?¾g6jszvx{[CoYɧB$ $_ =[o{x? mXoaV -y'l}=>V˯ O77@R R8*рAq^x76Ýw7>!:֓m-Բ|[ۂݐU~,4:͔j^;}V]R C$3BѨ$Mj%̭+x(aOwr|8|co𬪗JAlxi'G/4O^xNXk\r?4&UnjE[[]ɞ >}Xj^(aOcޏ{`5eD I'ڬmi;m19zE;'fcY[?; p^_+:} r@|yvBI #) SYGjvG|@=WqW쬟ǣO]:71pUJGF;CW5$Vi*.XA&kO<鷍e˧݁"=5^/9X7ag@$Y~lz?*'Zpo{ h >F2At%략_7;YfcuMK2JKs,\hIпw7_ x-w7_ x-k3~z}_oW[=oW[=as'֏Z s|5׊2s|5׊2 i>}_;UG;UE\I'ֿB 5 z? 5 z,OlxWς5ahW'/lˆRA>;UG;UE\GCMCNҵytإtQQ#Y\p@^kRO,'Z [Ym_BFEC Ҿs|5׊2s|5׊2i_nk{i$Ӯm-bIǓ#|L yV%OjrZ\jIo&ȰćϜP0l=w7_ x-w7_ x-;KA~~o_\z6jOn#4Nd 9Q8o㗈~'ZO_ት)l4˨0#;WPoeV۹w=kw7_ x-w7_ x-OzGO~ùk_oùk_oOi>kAe#kAe#f?>-5), єSʷ_ ы k_VqZ^*G^*G.~ys,JMø8! >y+y;+˷(Kc'#=w7_ x-w7_ x-0u}$m"qF"!ʺo I,Ѝ"^x tҾs|5׊2s|5׊2fXϕ5$\\G>|))6Fm t vB$ -rÙ[ :H%ӯ"W]|aҴo Xkj}iWצ-ʈNO4޻Mȩ/`m ./ǮVeibehr`rdv̨(5Eo{ :H%ӯ"W̐~~7|6o5u}qsॢ %2r#'>3rE`l/b I .mSKf6.@w<=EY4&.c ^A >kr bί+mRtN3P\]UBɴ+%ˀK*J=|uxKG4nTou; oY @[Isc|~ ¾F5 {lvN__a z֫6ЬRGbf `[5>/De#,O o+y#h x [jq]/D{O u߆1Z\ZKqwykY H?vc ,aɮK_}oYn}KKte1pgTRwms:+SYo fQG"WD6N6@X>#%^ j˧ڃkL3ʰO&GPk>lgqjN)w$+_3W'ůGv<i u}n^#b<G&91_'%Wڕۤ$Kr>XJ\q&ooCN_Ϩ wGDEdᷓ9U_ ; s$ }/q';~һ@/?пekqw—W_*w\܋ bRn Z7?(W {;9<)o.a\[AmkڕGȱ<,u.p8b+?пB_?{Ƈ}y-m]qioE m <(d^[8xBk m,W7Q a5 3Z򋍋½#= |*@/m+$W eol 1?: 7Z+KiyKgx_FM3^ꫭ-5+nI#1uJ3+(w{9>Ѿ#iv:WZ-ݵŝ̖( If#r MNR[[ɵ-ky@\* =+(+i\iaxm.-$3;%=.[v pyΗxA/'.[kyd9倃I s]MopxlD ؇.lNE:ȯ®3'v8k[oѮ!aKT.U'K1lZo]ƴ>~޽8bwUhTQ26B^??x5! {#RXwݓ, )ޠQv+#<1ũy=Ƨq5XR<7WEC (Q@$ԟFY[*?:+.d| Ow@=t:M/PmiݠjҒ;{9Iuv8+?i-" { `zwf'T13خH9yAGh6 |/ռC$^X %(;Ua+M+;]oZnڮj۶EmiWj3Wĺޛx`nڵ*4W~|<}U_Ki"fTG0} 7oBR_u )鏢So='_%GSmqg?j>o='_GWPC)A4^-O66g%FHm̱E:'l Uut?(k=λ7m ,F :pvgjq]/D{O2O?7:n܌2)u{!!FI5.F}FE#'&}1"GxއI@<⿉>0.xnY7VQ3B8?,8Euo>##-ax Es:ζP#Ej[D$Š((((\-qmf$R%ʪI%bC$$nAVSF:]y\| O?|;<1gM*f'$&9'ÿ3^{csfHT`F:5Kayqm$ḵJRpBvːL|>_((1_ B',mm[{o"Cjo2b$I'l;izKyqG5̳`dcVL|>_((1_ v+$s n|.,3~Y.%ewi<}o i #e,-g~uH{]2y;3fPd-1]_)"kE>Ћ[]:Uye֝5Er<2q#c`PB~Q˜}B/QoQ cEH ^\kڼC("AI\0?x΃,Forc#Km5Ŝ3)VĐ:6 $Z.xnl:KRHaԅP_5rxq/ooiwq{l- 6YHڥR#$dy͟ lt71ZA DqBYr (Lr yHhmU*J:Wohik{ Kbogsw^lr:aنӌ` vQwYl`x=i~ PYVxH_$r!2Fx5KrZ"->鯡 [JÆrpIRvm*piY=3ʊae^8">muw@"ǜ Dy; UE;<Cx=WUiיbr"߰NGY$ɧ^ӬOnn$r2e 2<{E/!tK='N[n( 3;~$֕Sp(@QEQEem+ qmg4ђ2*?QTgVw oX|z-e( \|)֓g'/M{ {A |ew-.bO2Ht]:&@U.I$ %O|;{ Ԇ8xv CҲz `u| sj|O{Lf@~ѝvz }6\ڍ:thd{m`Y5؏BФrKSߨj^.eCz bJAϩ4ro\"%{wxWObnKm>So$Nd FŔy$c_3HCחVfҾ nu?B[p$-#|pʜA|Fx;(|Gf x-㾒 ٶESG_Le̓/!Goj~C4j>?|Og5{:u&қP Ai ۿ4,Ѱ3pp>4'}Z _e{8mnn$ugi 31SN9Worrs7te}65@ܬ7'4G7_#+~7x 4MPXoLX.F%mY XQO Cߊ~0ͪjP#q,.d)ʥ< /ppja^^ڭ̋ &yU7E__^Ś[/ W)6#䒫 U#/RxP_ϫ\[W}&ī=˽'մV[N>]Ŭ$oRAVZׅ?_x(OUKIYY V&T ~Q-sĿ!_!t*æ/CZ;5H.?zfdO 01<ծoo9W-ou!QU85P55k7^^bG5&u;V)+k'X'ɉIJW?~&kҮ,ax_hp.u8l03⏎|eh(J׼M{k_.lhP[{yn4ףe_>#_-Rλy$L c&DvVHFH dq] Yi\ExgW5/$s"IBET V\ :oa?gx|ːp+?t+bm~W(4ᶀ`:f82Dma2 #?b;՛;/i字=r,LK ʯGHls/]?|-غ[ߕ -'vK4阣4v)"ʇ  0*jmGlm]>K5Qo;,xda޵غ[ߕ ?t+K{MJQ4 &(a^rw2kI'iυ_O oH Cj崩qnb,lK)~uk_<1Am'z#.-Mđ&s(*J᯾<>w폨%\8PG $u3NSIZZIfI,#"Գno/,KwvSozƒ2tBJ{u65с0+;FKXzW:Nд#Xnv2y.zሮOtOh>!l2B/Yb:Ȭ𥯆gf/e3=F)WTQvR{4𖎶oų[4St+mwUf8t}JmH^ڼ_W[S[N[ "zmՂsn2 4ѽjjZƙ];D[[t:(zȗFf:}ό>5s bUIWKKI`$fkYC{G{%BW>tH0T%I=tZߺ?tZߺ/?\]79~k З>ɢ-RRt]XwcHgï֋ZjR3n+ޕ_ im}V>$WM5Xuuɽ0J}xExQ44. ֖z,VIE杜LI,#^,\W5~9ZO>buiZf<,x_OAo%W*w>Q ]|8 xPx Cmzl#T.׆%$NNr_JoڊG|>McĚneofXHGbnaWv>"Ļ9&_BE垩akY ,И |y~?՝KŚ]uʐ,2V@OrNkh~ x&|@Adꚍ(^XG6#!PX<:Agf[Sk۫'7Q-EhqET!ۆiMZN+Jw}]YG~mB-7PN6wo so0P7`N8#־BnZT[ukVu&]^iEmȿ~>&}2=kŗ5Y"t+?HFѐKnW#|SN5{MzOsԬic#1HFݦE@rCVQE!%ÚKY%Ԕ ּ.>1U(p>ff^ v TM@F v5RO f[6̏ڴPWc{M>#7n5 [7` n-{: $s{NMW^k䳍.IeUr{k(K/3ZX}llmh~Tou|AgjǃiUC֋+ f$zIɯj5Ku٥ vhI[k X"Ry$"~opg^EqmPq%,* CU\\G i捣O+jji60ėON7*2qעQMhe/5 kFƺLZJw{fOxv;Nt -n8Ŝa+mˌqmH uv}tM4h;;cC#*2!Q$p·6X[Emm v(H(SQE0 (2b+7S#bj?%Tt9Z]2cX$Sxnf=tڞa@  mM|sXiDgtHdumc`kw_֡'!i6 R'Ԑ?RvWWiPw2mnN5oڛ~!|I<]eg;g QxNΏp8:]Z}}mM,3iwms%pFvu<zүMtPj:՟Xe'h'B%Hvsx$Yjj>*lR5^{31|UcA!~Mwiy/ws+は:t/|H׵$6^'v] H̎cvʀpFmR~#L',M躖=Eb[Wr{#)8PpKué[{TKO X< kn$:}4W,*mPR_7k_1=3^QO]:]gfЏZү<]Ah~<? OY|/I{,Qw!@ k1PůK!<>˖1rBl{L6׎_隳|1]nRZ:}\l{I 605.ߥ;u 9Ukau?),9J"}zIY(6!d'( *F]} KD| {/Zgo'1"ݜݳ5%E|/ 2I]y?uˋ닻 eiӸey rzr+_G}F&I[E/]y2V۴Mkw+g;^|Kťir]e4.(W{h(CL,- E|Þko,5gc͚ڽcYZhWlc˼E7ivE5.8M뭿ở2AzԕL)|/R=(]$0Y ǵyׇ76C& o?`{E$k0eA;I +ӖitvI}yY^ *-Kk{Xg.+gvS9RFz<M~+^M$kO\ |}ʌd.9\c msA]l5F}OOa{zv}c;y0}=M:f4$ՠJ}>6Ce r8Qy'd<+{'/rf/q$rp8^>i.?cO][&\y/f6|uGvM_/E%Q@Q@Q@5=F< .-wt)ן7Rˤx$^WCpS (˿:^!~ԑ|AXs$=v7?6%H];Ċ A4 :w 7:X+#o ?/&C :߻kh.0 ? fF<$do$pWEQEQE_i,o]j6JmFm 491 290 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wz7!agX麎[^ ^nmVxFzg*nݕG?ڢ[F$ DI"RxGm9c:>ԯ_ٲ_ʕ\%X* <H׮xZmWL&ҼN=jαg>GCi/x'Cl>|}?¢rSٽVӿ$Y^x)U>:pIH%WɧYKC : Ackb)]꧸Sv+(aO{>|3xs_Ӽ?ubg{*7o9R0Edx{uēb!Zj eCFi1H +]?}⺵&aOl>|QоOMtO\WOZd۬~L n<IjO *J躦Kh7]NX$u2%8 .y#+ɞU >}^ᯇY}?<]a[Z>ilY¬E$7vq㓂8?Z4;?l>|?z>El}'?›&!h2Xd޴|* flnU1;(cWšIiFGSʫC,2>zt;hFX[9>lۿ/<Xȧd+co:vq"H"]s ٨A,&?uf\ɫ:Z D8E:Z[m{=wEni&نbv0V)$p6)lGl*s829?-,F'/%>SF^Ίߊۆe|Q_Uvrp9'ֿB??? z??? z2n~{_?gZ=?gZ=as'֏Zq8?*q8?* i>}G;YVG;YVE\I'ֿB??? z??? z,OlxSׁuu7TCAȬ,_G,_G&Z:_xS{Xiekhav]qH#*=K㿏5}oM.Hzl3ζ6#beTHB|*O}qAU#qAU#ψϋ_%o U]4 UP`Eg.@ kᛏCzEkO|n.P˴aCm'?yøşkøşkӸ\SiuE֚jjWۭ-.`،rWy/׏4]OU,E]13r󢪤[Ȯ.T35;YVG;YVEy/՟2|(zƱxUTK 6RC*<,ɓ;8m^:j y˪ʎn}wÏwÏ.ϊO8ޗyi^!{ +fh⳶^o̮b.0+IЏwÏwÏPGO~øşkøşkO*^KO \6~>_?gZ=?gZ=asr"ZfoUl? u9턒J6p{(owÏwÏ0 uM.SF/*:=OZSk$+;8 m`PI$8'5qAU#qAU#f>n|3Z4"c"NNFybMO> Vf`dzq,_G,_G-XYWˇϽ>oፇYyY]]io.0j} 2.@N6d`ẇ_g7ݗ|-'PnuKY/0Qcvmdð,6v*+L5 q&|Cy-VRD$p<%l|}ŗ;_jw1y|K3aA$( hx@ִK^Wja%.tY7EBʬe;KwiYO4Bj$ #Fi3RX.r@R*-GOe#GE̡ 7A“"ͨ~YM+ Z{Oq>_^ S(*eȪ/d_G8=h7X}m\F%/9w39Panau_[";jΝ -l;m2V)r}z15dʾg<h_to]M/{F[8(Cw=q_!k 5GOoK\nBӼ9{ն)1lĶgA Ov]SV =%$#ԧA{hD %rM$-=tkHBL(F'Ҿ iz JK!G`Jq cZÿŗ|Qxi}Ewsku5/o,6x.aG?5/ R_Ihïj8v--{㶁ZY"ԞrxĶ6X;bID)D㓌WE诫GKxn.$dCn9RW(c@;k<[Qu{@ڭ흎(OYFJʭ2@ePOI&ϬVݯ[d_"tm-?aYԸlgn3W/5AA`mKBk OVqiod -ʗhݚ#媑 P߃:7[^K%k$kwp-UenK;3"{-ϩWH&49 Jy"[yL Ͽp9P9'^^.dHuI^K:A*xȯ>-5+ ]<4 bnvUX RWc^ RY)sB>KcDmҎ/ {Z7~'Ӑj77R[C{o-~YȦSaPY\nt St xc_Vӯ67۹Ou&.?!.A,zm|Ch{]n_ݝkfwfb˼ G2p̓~ |NSF{;.IVh$WOj>&@w\H+rq?fxxLfW[-th~QK[^m 9Vr RRF~N~sq~ c#IL-M:y-7""6QriZs(?O>ʼnԞqhnE691"l"nxFڵ74{җMm$NfUF&&A ]FK2%ٌc#=E\[-+A_[2됛Y\N8i^4Q 3P1>GQ^gyI|:4mE@Xjj\[S$VG=H٣n68tOh#O>ynʈt~d[de &WYѿEs5KQXYsJ \!ڋV#s` u"='j:uuH`,Um o+;c0*@!+q7=2ԭ+1LԐA{\'?x :>qrEѯCOeic%wQOH"*hn)N5 35˔==In$`Grvrg~~\GW'oMд M2c1#@A"X~,/oJĺF1m=H4(C4~b@RpQƓ澱Qbw f7G;LO#o}PfvTW(,,'4sIv:Ie]nUS J`;A ;Nn-/im.8u W24A-`ˈdsx]~3';$TY"J:0`x 5O=hZro2r䝨;FyOo@дb/ K664X}B;y~Hal2'a!4~з'/5H=zk>_/OYxl w@Z37|ujڹn-mt E}\@VHYBDm5w¾/ "{?Ii?\%ճ 7hsfȥ;+|uAx{^=*_jE^% )RŜvzmoRÖׯ6d+g,ц! v9W| |N޷CjCR,e ܰmc_G8o-'\UoSEVh13Ev\;{-: @ k{ksei=IeįlJ !PJ uOz-K<.l `q؀iمv(2+x-UQ` _)]7ᶎ=$V^/Qq N$Ct3w{nO_Kxw]Mܫ9?,҂zqSZ+//b\SwϘwx-(|I&5YOnZc U#=+~|?Ú\-m/q<ݨj?{?Y|-d.ć:%͂V14q\ 9R>NçJ_ǖzml^NPf@S;|TUrekz*z/q+i~}>nK ."+ddʔj˝,q.P5.; F ";%Io5Hŭ@'_cRc5yzmX7>-oCWmGx/HӮ"& "|{r Gi}ZZ:|ViWVrFM02I#kr~dF<.p'xJmK]vs~U+i6g{mJ^Zy=W5; WsǝυyڿYwaU}oo̕#~j5}v"kc-,vۃ=fkֳY%KJR 5ish @->ӕ}! ԣ K{ay$WDeW];  n#T"B.~}ߠryCJ-nLڭ%Н^;# yI2q^Bံ彾j:Nz^鶾@؂f iCd9j;W ?(tٷxNON ;;1ەPxz k|MI6v[ [ JhKjd կ6V;*( zҿ_O_ M}qj}b'B(,øcb8|C/ѼA|! C&n,pB_aGO/h-\|ľ1?sik|+Gh^`u$aY˧@vM(B 3KM;&0jO ('K[yff=5^ůkE_:R/G$k7`D<;w{Х/7R<^ᦟm26kAAc'D~pqз^{_/3隬ZY^A%&Uꊢ OmR:EVTU#\慮lnnFXwpHʜ߄wo]{ ^[VM-E(P=\лό5]4tj Z~y[lnCz?k4j:5.͉h.c*&y ?/?q^|e,,[RAk`]Mqp~Ph4VРnĚsZ#sjLeb`>uh_ ~OkQ^ދ⹦5 ;Ƒow8-mt(`0/QE?4gb#;/5oxG|7ZbHe >)*Iꥁ5?[:ܟq~j}S ( ( ( ( ( ( ( ( ( ( (8-K#xLWA{Ipś[?wZO?k?Ѩ9'5OO'j(u=*KgU. &ŭR4$ᥐJ9jNj|9y%enoRkSqF(%P0@sO?k?'5OOP'U3m"{ AI%GCPvdq@0((+ KMO>!Y MŨ!Ug##yX#UI77Hׇ=3Aomo ^ؼGaq-8r]A![sIqM\&~g5FJ`Ǜwy*y!Fb s܊Uݦ}}oE&OhmT[8 $rw{_kZ.]Xx^ PZkr}cX[HUw_C$}[ޏoX2 T3ZR#6gjI?|i3Q]K|].t[tX[o9sÖ 4^4D8fP~oCs/먢/Bcľ<մ5{xh4_|wh!uPv ?އo O ֶt˙- [4'|xgÿiK6|[ψv@᳁m!V0ElLrL#dԸi<^~;k_i_5/:Dk]n)섌3L#La psyl.֗<7mVm5ĩ!cr.p*nVL?*ycp.@|oiiDž&1F3ΗjOL <-_6Vpi7ta%YX؊;Gs G"` `+~ ?f7Ilͧ]GdmF`NOѴӭ}*qx5mBMkYuMFFinb{(|ݻ3}7e4s[yiPhxr[iw3馵Glc,P3Z510@HaEPEPEPEPEPEPEPEPXPxV!_T:jqD/.w[KϠEkD'KK7Qz;ENG$cbx7@]CSY6c &7 qg|+^lmmeE 2EY6lM޻Fs]EW5 ]&+;t2qq 8 f<sFPG?4/$i7g|3X&Ho#~0$dcW/K(/,b@eawg>MZK[]>dٔe19S57~] CiXW:,co+qA⻘4)5KU>N+r>S] AWGS/[^4ׁs _AZu֫#('|;mkhOĮX3.2B gCW젒8Vuk)t?Y.wwoqc<̗,I! FvvVb mJG~ |19uC^d6xWH_ dIq)Ѥ\c19c)|tM[H`"G>ēzGC{MOE$V/ Q@Q@Q@Q@Q@Q@Q@Q@Q@ wXљUQO@+I>%f5lEId֥ f$A`xfOڭŕWvw13[΁aA\Uǁ7w .o[qCgZƑU.`b_,?%uo|+Moz [AmڋYῺ7"ڀI-\o ]fƲxPZΩDXͦ@ZFq"rE^HnwSѼ}x SIle,2:6>aՠWn8.؋I'URh[B~`~p1_uW\ |Ym⋏ٷAˤ^DvcFAHM~QK-&ۢ?4ѿߙ??4ѿߙ?`s~1GeU={S|qMf4Qi(岅-, :?\`QE ( ( ( ( ( ( ( ( ( ( ͋ĚDRj) L~UzyPHvI;ONù >uxk> m^Ovw./[:7P)a)ښOD}myOS6T5|˝gFu5-kƺ4vZ6=0nФn,`@&<_t sxN7X֍RZ}%e!PJh 'oֺս#)U#!\$p{K ZU+{U.E<0"Kg[OmWA}95_E%tk69Kl2z YVA<'-axMҬ[ ]/RήX1X+)<ߝCB@+]|pa57Xtwɸ ; Ql2llÌ k~ <9I7bAc5B OUD}xJ NhN$$dN?ѯ)O~k}3Li)L]F~"# )@gKRд?gxb.U$RWLF *͒^ݵ};i_[^Aq="UfWIj6W<55MGW_uNҭfB]H+d!J/x%C3]u=J8"$E#2 TKvpEܱq3\ξ Đ%f'2(8ܹ=Et);|=~}"&O Oci:-vЁ0X26XZ۲=_L<3ڭ֝ywl&~nFݾma-W*K*7Sq#$tȯ?j6.Gmqx: Cr.R",Ѩ2$1/ӴA`נm%KwT#$7#u7p ⽷oHiV~𾱪>퍔0˽$G _=O4>o\ԵKx6&oX֓LRa(*pG{"I˽8^%XˠpI!YTV G+So [\5U}{T8#H1D*FI=k?x)U^3s)R-+š4XdH⟊u]Ej1ZoK4i+XY?m]>M[f;OhڌV%b˲0Hdf0r9}找g_H#TƏ:n%5 k Dm UTl1De"n=u?9mjηKk}4o"]:l]ɒvG.efG\o`ׯm:\J-`M#;^F8u%ޛqKRmnBA&q qTjZ`Ӯ\Xo%I:-s9@r? !yjtT-ZcDHLtؾxgIֆeYj{PGɹh:(]Һ:Am4dyfص YsoEՍvXCy@ZJƒF$n+`zit'_~՟Tt}QpTY$r`tnw/7.=awMq%ܖ"iF!flkp#ܚ +x/E:T}6Uk&1[Z7 xvNK->m:- Ew@)e Grwb2<\ 6Wt57YOOIVʙ Y@n}p+CVz|?m 0/RׂIo%ռ;jr\Ewq%K4hcF'ʄ>OL`I9R;hºΨGahz#_lRмm4ȬnRa&HX.{Z?we۽CuNKhБ4Qм?evok466wRU^^ //Ҭm,kKWPGgI0`;@#UWË <>&[$gDu]=Αpߖ3"_ OF+27C^e_|5qc-܋ VkXnaNuYY8;V-nv9'/t}rtnrɕ|pO#ym_O <7*; ȍ,l yPN85/^#5. TslWh;N*>ьU])'2ZmZCo<{ xoz$N NAv]Q"9i$*qUҾ0U|>oZOàIuPM.kLQL˸X2}=SGHiFPZxq2mX#dbi|0_hImy Z̷nk%<&K6Kį?/~x䞼OGkx?)tVQ4> h[6YgJapx=|qsxñZ"eNea$18zk意3^ͨ}.&uUUUpc@UHRq\/Ě觹'D3 P*+Y@EEP3Zxn@}")4ɵ Gl+M$nV2;>Twy6}}Ljh?'jtЯU><װYKuM\Jά8 ^p,x7o|ui~BךiluDEǐۜqg#OWniVΓI]I$ų#|qV~V@H-o"@ထeqK~GW翏QkzMGImRфSY,nG wv DQ[x $MplZN>.lyLIIB n뻎ž55Ak^_X-#ocD xN|D&:s.'VƱFB\b<9A3𝷉^Ygjѐ1,b;C7rk?l?.id_&/v_Sρs ?H? 0uo*:4yW+?^%ij]m=2ݒq+rQE((((((ږkiV]DO$ H җXׄ> j-^,jl;.,nxSm[E>RhO j^iZwSlm;uio,`n!=áuljo'o,.4v;xGsNAUguWG ,|C7t{vhɨ,̏{.O"PHHח;4]w릲4i* (ʒ6wk_ޟW|O>~2ĺlZ|+4-o3ly8玧>'j4gG+TǦfhf j]@h˷$e]Ʌ4} E|ۡUgCΝ 5[k][Klo"oee**k^|jgچm%54.u/=#%EdP kn5-o_e@<m'BaW|hw8wju.o4cnn`Sr1` o2TW>x>*@uZM&[]`7 š(8EhJX|cxwSU[BڄCsh!'y$1ÁSﭽ?ӖMB_@( @'7-tK==Zc#,I$u^7Hꟳkyin698 p3U.Q^}+ao /5_97n~k&xK t? ݬ6oWYL)PT3²_j6j174Hcx4^៊uG×^gI|WdX& 1i o )+;3`PcrEMNNr:9I^<þERQEQEQEQEQEQEW) [xUzi",O72, S-c'yWEym4{׆mm}jQoo$f7|˄ǒ@B#_k|=o%q`lh42>2 ?] oc;`и&luV8:5ɩjri75̻%FvX 5Hg?[cNsHo&h9 %$DžR޸bZGk-}A}-$F\Eo'W7I>a"m|Z(?f}VdWˬ^jw*ĩ:3 LAq]emF 2;]NfNwEtyG x;v76Z~isCMi-^g %ui 1ٯcZZ5bY٦X#K1J ʹ)k#⋝jTҚ}bF1KVd86Fg9F;:& !^RLym]$ [  WQIh~A^]{Hn4Mvկt2< q,|w>lg|L095x>x_T𶍨}ȓc yLd~ tW.jUmuk#0YcaRT2PA5[poKxInBI)#p&QYKWF=) LU4; !Tl*[q)jkvewy&sK!r9@SEFVm=3yo.Ny9EBpM^!'IJzW}?BMwOmmB.)QXm:{vԬU=g g{`}kn ( ( ( ( ( ((Nÿ\Y?/-״ۻkMd][IrB2&pU'^#v^.jsxb+;Y+{Bgm/ x/÷{xf81)ki-LU9[6juזSCߍC>"4 ^->Zknk-Qj%op~&zÿh-su4q;ar >cǞ٭|O @ߍ^o*A xD+;`"QBDe]+>z Tƣ43xĮ,%.bX,C9g1l ?xiu3Bf6Ұ:Aˊi%ѯJ4[~!v♅!4jt2?mdR˝NN01j4k^ It ~:,nh>wu8-ۨ?Oq-FE)e#Tc 3y~i-WU5xg_+tS[G|s[Ϣ_opioxMINyO[^E5K$Fy,HYpJHIP?h;xuO{kYZH# r;¤lysAm-C>x+]sJɺG"6-鍏 WzZRΧ$zdVv+<2/## 2y|*WOi|ii-.\iz>Fgđ .<|q=O~!I!md>W[+ty߆Y\%g?ޒRMZjVHxiCC,|,._$4\4 yH 3V (=f'Zγ2KмY{s!iWu}$'mON?; s_=?LCPmZo+umjKЧ*9~?Cbߕ&oyqgB{+[wUEsSqI]+>T:ٴ`NDrFdO?z4u}PmKYӗL|bgL p'$^v~@ WW6֚4D J/Of+n'!1s]T|u6hv>Ҥ]:U.BC ">e n=G9դ4+xOg_. EPJRA|Y}kvc:Oni ƒ"8$D;i_OCG/um%pYLr#G$lz288xwN>i+"YʱLKܒkѨ{5ERQEQEQEQEQEV>u ( Ayrn#5X(=2^gʘS` 5&^62 Q[z$RK <+O:;]BM:572G$BC HI3+_<¨n/,Fsz6 um+_r ?h WĚzNm kţ dFC)XDkwoOJs'r> z o{a) 7 +bs>ⷈ_K_eQe[CB%׃;WN6m =OZe߇n6 '#1x3\vnx^]ω7P;S yWW[HC*b]ɗh*jZt֗ ZFi 9GB+L] ھ64VV)ܘ ˻ A"DާӨ5xgenfIph)/_MwDݏRYY?U?*I;OW֙GsګIu\̰'BnԠAӾ(ΓG9{kyݝ+p\nmԉ"IXpyöbRM] IkT«ZUq?MxoT^7J|d@7w uzd+ȼ#i&}4m+\@4x-#6,﷟=GGwڅĩYc!YϽtփ':iP֯|Cy\\^][[,Rm0 lI9wNj;Mizj^S.mҢ6%cn%q$*X8? _3+~;ǚxnFz\NK+-7NH)+!8e\7K }]jiU:/`0jZ.ekZn{{~6Ɖh<7~#%Ѵὼhifg`VtK8-'kMDԴi;F@>XBFQ0QHaEPEP\ne ŷƉai%<L8tؒHrI]'׼=.k^Įjvl9reV*x@x?uf8ǓUUa;Yh4e0{v -#$RG|WYkomSG-u]2w{c: e]I 2 l;xPxbokzݮt̿i{op$Q3wh⩟(.#]Y_donMo`^2S?f{Hn_XgQO9++{ XPu'CV$q]Uۻ5 jM5Kk֗+'Z8KpkӨw"> SZ{QlE onZuq+ M$n[q+=o>KQckK:+61#xѪ|,ѿ?ZOV[2$!G4X#h J*6]<ľ,?~$j~MEsXؼޯ4S˄&*6?|i[8^jvw@iMd m\= &V#eVZO4EIbhl!}{ oKÚTirK-QYFh+,Ydpc!=MKl ;mz7:7$[xQfԣ=>[G}d{@lԕ7Еx?_]\x[Zt:fֺRV>r=׍*|_>/WN>%C=7v|PwH'994EK &?uQH((((((((^AʢY~UHȫU/Rj/ |=o⇏l#xJz[=P~D3fKQnJK $_?ovX˩Y{bgyH!r3$zօ|aDEIw7wT6Ea"۬I Q9}eY 2^Ijq0P9f rz@*l%;OY_.UQjTVpjS%bOrO|{VYFluLX H/ XϬI/SUCINpJ}pqڴW\aȵ4" />(,jڅ"Tu5k &'ނu!vG*|C~?((мmkZ}o|+%8(S=_ _xOf^b~wEgG;Ơof裫jα%[Y.廒j_eqڜ_/l'n?Hv*1撏rKjNڶˈmdx[ִW\aȵ x_?+ Yuˬj&~K2ao`>iG_e̯u!vG*|C~?*,YZ]Zjs\jbAF0C&F̲_$: ( ( ( ( ( ( ( (9$oīxv]]-^=߷;W?AY~Ͽ m4iC[5?#H ۽]:Ņê{m#7 *~Ոn"b$cP}ǥ=ד^MC@MumRIJ"!O.PGd* r:T"U? DίǫjZkqƷW aTtiʑ$vB'|lzZtOGn8tp-m  5YZ9Ql..跎yLEڵj@((((((()-!WVὤ?t}\|%joώ,갏 *! NW?wAMC>Ҽ2U.qzbnV SȍvnQ䴒'5$DZ['BleY$nW֮24k(.o'V͸$Q,s$s+nqGU7KWi?/xMkJΑLx)KT" -! ۏ,xש|7ƿkè=X,(I0@.s]gxo^n:GxͬM+*mV€F9n? <7}u.KMEtw3#[E-28r ?1hKIɝUY #P1~?Uj{:׈`5/JK"ePd`qRh|/ ?![Yčc➯cjJw0䷑#S+ |<}',n+oG[RiFfHZHuP:^ g9xuQ ?~7/v[|?l/.[>4g?V ꠷_^ij[QfKpLy+Ŀ5#SmHid&%K)#8 Uxš>}j#ѬnSF5ԤnV`FAk9hE6x_xo/^ ҭoQ_mW(ldks,Idč2<Ư_ u_ҵ~󮲲~&0-7W<[>4$Vm2\wwHSρr ?H?jFQڴ ڭNT]Ŭ$o[ppH8 ڀ ( ( ( ( ( ( ( ( +?jo:OW[HKDdE2b2,t xN? x,Zl"nҚXt0@G$_3eC6*ȡ?/͞E|>%Zx~z𥏈.a5ڼs{Y1Bq |h~gۋIu[nI* 9Z)WU=>_utKXD>s$ҬU5\EC/0?? _hu|1a~"k5m7T&ե%l墊;xJSRނ{_}AAQe]eXge%PXZ]-t\+-3P>=j1ہ{unmXx;8\Zmgw1:j-N5^b9\~"MA9v*1撏s2  /*h?~[Fo_pI$G;{)޶[aWu˿ %5uS2  /**,Yd?z̫9"`NFO9Ҭ/|aaEKjө((((((((wZ[6h(.ef7RHHQ]X8Q@%1F(&Ӵm:J.f4. lS,ťiVzkiİo HF@tSAY')<Xiڿ㻾b`Ly 򥬛=Z.-Ai"EK{rcVa:;ue}O[cmcofԕWU+,[p͹mNKG%7%?ƸW&.W&.QZShF пC/% пC/%Ѩ/mַ\4m= uȞߑVblږA<ʧk]K-'ڞX^lm.ɭ$BY3$r>9]GZouoKL['ͷ#iU;KaOUxZ֦ D{3m5Tq]&ca{gȶJH|/\&лg:}^;~Z&M-bn>x|MsMw!+MJx#SjI)QkNevfK_NgHZoIEŽj#3B22 yY!?#~! ǂެlm<o9xt <,̓Czd~&]oڃ/?XA/7|wךѾx{@¯fuh]}#\<}ڡm?̽o-'F/NGk22-aU%x&䋘BW7[)y>g.rKX>xGȑ"7SziGm䷛Ե+Sѵ-\Yڌkjb/,I la#$^E_4cVq\PIL6s\Ej\Ē8w#kվk?0btKM˿4DToGP3*t}T|~ɵzY+URD⦰t];9Oṵm.#M{Y9ETZg|5ïd.o˭F[sqؤ؍*q<U g߇ 0%ie?HemQH`r%iQEQEQEQEQEQEQE^KeSd @Gs-Uպ5*(7V׿r[^pҢ3n {}(պ5*(7V׿rשּׁ9 ?4?ڷ_[^#nI}NH[SYR2eg;W*'ץm),*HQKEQEQEQEQEQEQEQE@6ڦv +џMBW?д'j2. y ,P<둑-64ߏ:V/궚v_o,*ed V!QU7``32H#ş #uNt{=4XZhޝs8ii䍈`s) |}a{Nm"PM2JNx61}H' 8={y4[\jq `Ybp`T1]C>$gKGVH3Žx7ϋ<rڞ]jWPM*Oy-4jWC3A]¯Mz:Cdq!EN7<8U/B5wZzOMlki^$ZVnBr(v^IxKop eq{Oi(4+^nEK#pzzs۶_toX:ޝe|4jN6:gh%L,6 ͱX `\95;]RP.ޫkeV- gTw XgA^-iVLGNZؼ׆5 Qp R#a -_~W5ZïFד0I3 /_M?#͓x"O\j5{i"kC(#YF[{/l;uuK6M#W5(!욼  lQH rF|x{Yxxj:hwi|xbUYda'w'kOxZd|x_X*FX+ɻhBy[ߧv:/[]SB>.xĺ sV[&AQ`yU޼1:WA&!_Zj?ּQSTе .Bn .Z-1G5=Q]̛:GxoƲǯ_EiqMشsj"A"G-. *dY ֮5)s5Gu/n fkĚd4Kim'Ac@c/yYɠ#լ 143 276 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>މ|\i~\?G?^C[VGg-7p6=p+gF_ƞ; R/?}ok-2$6F6ٙʍU8I5I3IJBҦ Ys#HoH;tW;v~9-\}.+#GhaZb!䞙'/?:au?|QjizCs-r)Y^wv8{y^K6HXo3"~Z6Q #LoGoloDEloDEzτ<;I ^m nFVu_֮oN ?xcYXBe`$HRERa߾=OL-o5mW~)ޯ.un-ZQ@Rx jVh/}:cz'(cz'+so3_YZxfV~ uD4ErQPPU<Z |;wzՊT$ R )tY<&(T6yUJ]eOQOWj g2ˬk~|94VYR|֏k`@3h <+x? _xKW!nF[x^31F5loDEma?^~o`7MCT Q) W3{u/iw:0iihR$/@=W%ĝ[]F$HBqIOW~?{k~=H{ůJifq$@m%:,s|K`[_%}9bg*5zWmRɧK,O0[xtǐvwcsWB7Ww?aZ;j6I#|Ki3&3Le 2+oImV+?~#?(Ԏ\ ^XLDŵx8___/hw|263]6gπQ<;Ò^5 In[~?fÞ!K!L.1/lq}eA/l?fww/ xa4Yυ~.Ӟ\{n_[$2wT6O`{Mf[GrE=1Fj•@['ޝ8 %_ eA/l?f5 sOxZεCTmZ'oOW]RMvKnY](5F0B>m5l VǾkww/ xa4ûKĿe`>>)[խUkPw]6sZڏM;"tEx.#k1Tpp{W_/hw|263Uf-~' =ku[o E/^-º^DhWh99byr,hi"%αE2$7rc־w|263G;KBtkoůmhl-S}}MiN:aeyؕQMnfF %j^%_/iيƝ=^m:u+g)rPJ؇w|U/>.[M6ȶgvVC}0"Y, NTdE}eA/l?fww/ xa4$e]ϑDz_{ZkzFҮ"Ca,+#i kO;&o$d ,߂b1df23_pûKĿ^%a}|5bj]>k ż$Q6ho}=3ķ63"x\|b9s_pûKĿ^%.],?ş}4Dӵ{M7vmabTRђCuA$4Gŏ>𥎽4~Kytx r :G]z %_ eA/l?fOxQSwoj)WsE ȃ||b~?BƯt%U*B>@w|263G;K,4^јy%XdTljXe`X9.GLJw|263G;K,當mhd9imn231e6zL!!Mww/ xa4ûKĿ.~zƃ~ہ]'k]i.{3 _kûKĿ^%y h]gYNTp9bY={~w_wq=Ħ[{? +C^%FE-Nnrs{P5|M~I_xCԣqj02([ w$C5;TI<.c+ueϪ]Ϩa;Xdet9 BzQԭ49.縐F$񯋴Z>hm5g`#-"3ʪ;v rđ]WOxww41e VWe3o0TXJit;tW>[-Yfk{łVSo*E8dlwVψ2_Ac]?.y-/c{%|%9rf# ]2Ӡ𥮱w}imyIHo Jק`\[iJFpyټ7oN{]hm: x.PY=Hf_h\2n$"W vҹ/K@߳lUqͤ[h9b.ʐ*VM81Z"֪榍qie}Pf##u"b^ ~ [>%NL5k"-¿j.5/Igcz?f(k'ݯꦧX薆Q fc@ps2Iz_2sl/_%گTf4V¼䌨< |A>#ω5_[z3붚}ȖMbږ`,qݍRW/K09TQEQEQEQE[Hu=[QӭgI/b@!sD( 3ZP P~Bk oEv,LK{s]Zx@K(eO E G ⻺)j<Gw/ ]iFG ǾC| w$x@:LB .}0ł'bN&( bTE TW|c xM#WlMQeL;]prAx;K NKH3N09pǐFsQxOF7vƁhz>bhV gi[+$۹tU] 7 rŢ~ⲕa0A[U }vdE #@rN]/I״TbɆQ^>̷9MgOiiWZe\%/@Y 溏k_Zz(+Ɵ<1O>|CgZꏂ7}꾟Eń>즰%#h]"xc"g#nkZ_tֿ?@=w9;u=RR^q_:Iuu%fId+3c$UlSGvx?D]x p$ߌֺ;/{yVhXNA l>7h"i )@*/_ V+Oh6b F`e  wS@QTf'=LIJ?*OQk=rk=kZ_t\Z6ײ,E1GULqEY>3xt/ڥ'&+ZMt.fѕHWC5O?|KBLo[LZ+uD~$PH#oO[g>u_"QHdA^ 8L?ZIi$UΡ7FO2ו|B'o#&hwmz[DǶu% A*o*kuipv!{Gv< 6nIfWʹNL]c_=O[ٴ5"&,\ՙ>+:-WMOt,gl pK x?.5|_XxWMZo`o}3Bb.#yC*##H$eJsS>z6Qzo ey[8(K6/O54~2|;|@PK@XYZ($y| ve`s*kЫ|exd80ԴtGJ# "u·gv#KKiQwyoxj4ʡoL0!) =[ PimunPK-aeٕdSTa^7|M.x[5-Mcko?bH-S00qcKa\1sl|Eo\Mw4neČ^<y^=l0o-=ZOk~Tӭ55'O-88mqִ-:;[[ƺBVKK.}mVlz_9?3x7Eѿ̰x?Vh!tKFHYZ@w0K%kx'Rkj77M(34*ISj($pIyEݯnסZkzea:]Y]ē<vH= xgেKn-t[i+ Y.`"^(՝£DXQ"(Uz Q$_ KSZ<}O׏-<rדO [y)4-1 m7Q`F+=f5}oA~%4+{xfSeO̊A 1O\&ԟE?i^=:E7먙¬#F$Dˆm^&?C Wgk[[{8mxYv2QZ#}V[]r~٨ <ĝ2ry?nҙkb+ Je"[R"RK#TP2I' OIjڎIO3P6KTC(㩤c}.,_JF*#B @$q^ }k,ҵ]J/ j>k|M⏷W2[ I!bǕAEOcմCšu𺾈<ivB;M,y$YZ#d57OtD=m-SY^5cݜn(u7ӼT?x2hdu VMI GFɹ6 GDֻxN{5^3yt]Y.go'8tEp% TeWђOT}ki~+]P4˕ ݤH!GZ_ c®}Ҽo<, 2MPmM!3L; ~]r}"0 &ql-E>RڇP"M?-M^Wwm卑̱JQF]{|?=)xWXgFP8eaXdipOz[;Z𵂊(3uC^Z)5O5rRT7+p~$qbDT^?@]yzZ߆ *ůo./Q_]]yzZ߆ *ůo./Q_]]yzZ߆ ^t5 &H,m,ʊO?k~7L _,?Y?7 W7Gůo.k{ş_?G?Ft~zZ߆ pxycfYBZI@=:օR ?k~*ůo./Q_]Iiְ֚Z0F$FOM{8,- a cE?k~7@k/L\u bm6OX51Y ,nYTcӽO?k~7@Y?7 oxtoKꯗůo./Q_];k{ş_?G?Ft~zZ߆ pk.{jWpF NtEY[ #Я&@MEPEPEPEPEPEPEPEPEPEPEPEPX0k:_ͧa:[qraBĸ? C[j 3.enQB9ZR|wcɹ?n`6nU<5>?k>"_d^yw O4i|r|>7^[Zi3HH;Ene7Ğ# V#Y%[4,P.H9ڴ[[_փghb b,,T`1FB@ފ{QR3r((((((((((((;Ȓ}K6$wSBmaRFA+wGШ̶ܕ랧P熼> kwj-SSih G"AkKy:KO 182 226 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>މ|\i~\?G?Xi>X 7"7"G}loDEloDEcޟo|\$|Dޙo im}goFL7Ju_V3~މ|>މ|_.gkڽu{M:i0$Ьz،L_B=?ðϬ;/M%e(ӃS+o5Kߛ<ȣȮƑxK.Z_dkSEа;[+iH<#awZkT~.PrU=N7%qȱ'}IOQOW 5RB6~"ix,nxA4+˅\T/SMsRo hMioC!y6*.Ϸ9{Z o_loDEloDE{ ⏈꺥ͷ<3Fz6r $xP/Ou)~}嗆Eeql鰡ފ(/ d猞cFCr\9jV7B׶yGq4Av7pI7)'>,~in!"h^[՝ ?)dtwu/t}U?Üe+<;ՓOəsG~û>O~&z|~w}_?߆_7hv263G+ h>};M?ݿ 'o w;M?ݿ 'o M uዓ`cFf{~n㎔WhZ޳k[d6I'?V+\o,sE)[YO'qOrs鳇y9Mr`c$&Aݿ 'o eA?l?f0{}.<ݰIF/e-#޴kgF}eA?l?fwo/ a5ŊBoc˳\^UQO'<_wi`&+t9qV3ܝkS~&';Mڎzj%dLV.6%'=ăzhw!>+->HVRUeu\n䎹Y~j/84>>Dt6iZ^7LF$g-gd:G['Bn;G ^4ݕ\ Sw_φ<2mZݮ`zLʖUH{r<|-7[[=݊AiYBGuFd`q^*m3:V`vgd sk:IYIW˾.wwjڮk·#s5\(N"xn9UR}%~.Im/JwV -fy $w֏?pi_ xUҭS[Se=.2W^F`V셷kE ?7_25gus#¯Jt&/D+5:0w?0+WM_ci^<̗7[^A/B˄;~BR)$=o/_>υtHaKϝ2Dtg'I/?kbh̳yHif2xȯ(ׂ5g xvV<9G$pjVJ"F$nY(“\_>Gn^ m<7 rb($ݮPI*e\/57NQ7 uGhe)9*zV#}3wڦ鷚t:j$61Ychv<'$z%nmA|5Ow-`V#$3o; ϬcڪJ_Rc.mY?@.$6ֶAgpAavMqoEtGk$6pf8'KO;ĝ%NLׅ֟1<[ֺg(Ckn8u|;Q}l~!WsXky49wcQ(*r8f_߯SyQn4aiS;q\/ž5 ln4MZ;V&7O#W௅/(Үt4}oYKs%(]HcW|+eyw/]y 4dM\ulx2ޕo/$4[[Rfo9o¥;DXQY 7S,av3Vc7>xVҵHum;7֏Cco)gn6MjMX0Ȼ/x>o$4Ew!8Dq5dS_{tv~.е [k]kNͷO ,8<i̚z/]Erq_5 oMi:5/c0łEa}8ƛf%ޏzMWFԮ'HEbg77IouϭZJ6fڊp87J1%ήP;M`~vtWm/[YHȗ$QĈoV@ߋ/%.t]FBI]P kkqqe2e1eHٳ(M[뭄k_Z}nGq),LXz855x[{~߅f߈QQ_-`{: 8ݼF.gՆŠ*=A)q I2:R;~?Z/H[Tj"5謏mS ?O.(^?Z/H[Tj"5謏mS 䊊w:uW흜*d-]b@2BL̀2N M^4֭}{šF[^^٤ #8W[E  ZõΣmՎ{yg2!IQeB2̭9?Z/Hеv_GBE 5謏mS ?O.(^Z/H}Inqui#$P( ( ( Ż}*FG[Uy>*FE@.?^>B]9o-GV o j̱yd%*xo.a-5mE4+xfW"c夛"Tb&23=ޗiroķwwm}}Xc+u&HqTMͅƛmk>7Ȳ,}/0~иX|g7][G%ֺ5ٺm_KIQO<`G:E?2;1|yxj&,t`/^YIgwּSE%υIOuqq .0K",m̅wݗ?_ 5ր<t~x}:Nu5K>*;~\<;&kW i%OxM"bpeku彖gk%3BeˑRM0V۴31X!ooLjdΙ9h:myuqo&H#`?v*p2r ૕ԙ,:K}$ 4:nuvK u~eǎ5k -wo=6(,\H0IKqwY / fM3Xj& iiŷiss#,Ҽ. JmkVm~uۗ.=_4oWWDNmm%(mE*p@޼_Z`쏂dmKc}ujn,U۝Vkp_ K2inƞt#>^o,}qƷ?d|C u$:O|^/sҼws&qmqxLH^M!ibv 8_sZ_[ĺw{{iכe{Ȼs#.`wn o$%Cio{N wi3 ^o*f@OE.F|c+RN$^I-۴OTݸi??-q4\ U4Mݸi?@^l[vhطI*ſn?O&4M^l[vhطI*zȨpp7[ȣ+@Q@Q@Q@Q@Q@Q@Q@Q@Q@U#㌺+1'h@Ojj>%]׽h׽k̵> 366 285 0 C     C   I" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?ސ2BD@k~ [(R$&7gGx?TWz.{aaaKi"X`jW9m޵|dKKi "X@x 5 'C7ɤ׾2]K) E p%,T yx9<1kCĺo,sou ۘS XaU.r3rz$R~}G+4Z?74VO{ BdRHebۚB[!J㏅w/o|Wkghmo7o +@d`![qc[_#[S AzH" |;/|wfYjYYi[K["I´,[$gg~у?ſN7P$GDc$rsC+_uI_+W(aOcޏ{Ұ͏DQ Wjl}'?–[N&eO-Rرƒ Em3,X|W{X𾃧YZZp@n f99 ;!3{Kkwi%=AIyOv8j[y,IN[p%OX>{ ugs-{=ź!BFUw,yAr4q,xa'ܤO"Q I8 rI]\o_kƙ}avf ٹ)_.$9GI8o+n|_%@f9d>WfqUㅣ*]#˰rqTvr{\"qBdp=J\+]N50*`(H׹_K4M0ʭq y<:ubrӎ|Qi3բQgFpw>?+}_;UG;UE\I'ֿB 7 z? 7 z,Oi>mAe#mAe#f>G|6KƵ~ҿbD=W_x/O} vShZ+!Gs|6ߊ2s|6ߊ2.|3RV]>,L ̐bB01oJ/|; ^%͠?ʘ0׎Mm|f?F/|E5_cBc fMߎ+w7o -w7o -M c᫿+U[V:m1o| a9V@9Ӽ]cŞ>6oc$¦}k/~*oG~*oG.~z}_oW[=oW[=as9]#zE2}8 AP+ow7o -w7o -0&{q'Q cr\VS642A 4ef g s|6ߊ2s|6ߊ2 f pٚ],ckE)@=_AQƁ*K)ۀ~W;UG;UQ:Q kJTZNҋk>0t#$yj}xP{|ۉ{+#~*oG~*oG.]6F3svj]K쬒өEywwh:OudzKilH`<+IP3f6L >%OZQ-YuP#6˱2z}M~'M\W.n"3)Ec fa5񕿈綺KX4Vm"[cUnj!s8um?⏈cqxC*VMZ۝w'JL${-[zҼ=̓8DErV*zkoG+yϫkRϩ7tP.A'gԁޱ߈z_s e-9 ο M}RMK5H[r}h(89%X }||:vw=XkjVM: BuV?3`ԁ PNǰK[-,Dn~^H3p3V cXL$̬y>w$.<-f<)sj}ig˼H-82yc 3Vŗ/ im)-?P5kM2[}j2$B6Q^Q\7娛|ۿC+ecqslB2אߴޕ|wpiڋG-侽EޒF)\ K *w:?|iMkg{dzt}eYd x|Þ3߄~Zxo:M|o67WS@ TO.X2g/-[iy~.o/Ua.m{ ss,ʱ:RrO|L,Уɸ",g͉dN߳g |$um>JlWHVH]n-Օר?FO9Ojkqæǩf#8ݓM,;%eU!&}Am[_YxJVsKbEC[ZjsD$»N356m]cG,¶v K$+ 7rjR{YhMHx"JÂK.1 E,,͘iCu~j3]M9=l ϚX(Wn[i\P_קq'oXk?4O cQnOSMc,тF2guh:ͽ :|   ]{Şޔ /sA~,c0#$>J:6Vwҥ֩ryxl|{Uegw$wuvk6ߥ{ [4$aɬ|FӼ/h;S8toe,FKw\+!8Ŀ +7o/|Sk}gucY i"&ki"||mkfSm=jo'K2Y 41X*-v1yK_fҴOb|߼J98O=Ozςּ5iR=l̚d`41(6cľ'>;~|] _0lHw,ln W$WJjc6mԼ؝O+.Ou#"+Yyrc 2ps|dzE?&@5nXjzhn^ !09B 'ռ3kmO??K`Y 2K*G qn[+T_aw/_DmgڍV#P}P&E+j^wӢ)VeH[[wsax{Ě/'ZLt[_gh1/9_{MO>mf:<>dQ$^jG,Kl 䒾~%7gougx#ƛ,tYGcՓShDJ#HY\l u_v`'}g|U_~αb-kh*km\=*!uȭ3NAxEoGsk5LJ+g4_j};D-yǙ6iko[h}ms 5<.2FTKz75 ]hqۻylKq9̱}YC8ՊNEj?>ii=ݖ`ID$M ;cOH,|Ajz]徣]ij[$he]I rՋ-y"RI#TP2I'$ԭ!;i?_^'Ach$ѠүuxF5ILi6p8k>+5+\0n=N[My_sF&~7[xUοl`f,w(=-9ioco`KHVY $3m0N㞦k~ͥu/n< w5&ijPPp8+I#^Ώ-4 JtftӭU"$xOzJލkiW:))_0Ǻ~ Z9^W&*ǡ|!MY脮|/z%6ٖ0Y}d2qo*ۜvkW 6W 6|C @W 6|C @W 6|C @W 6|C @W 6|C @W 6|C @W 6|C @W 6|C @W 6|C @W 6|C @W 6|C @y&#ǎ?#E]?._k7W]^[W,.c)l~:To ].onaO,A'_YV_f|Ϲ*"?7z lĩYUQīri ?Q ¿3hZvqX[]fύd. Y#8aЏZ5+ $dXxYkm}حw?@j}[ZVjjvzo/w c8Fqķ񵧏<&c,;:Q㐡;[C)ȭ!C|S6P_,_ ϣ/*XF_Uf>'xSM -d"xέyÿ4[-jI#&Ój[J,DBR6W$ m!hZ+ Ÿ30/?gѿa]'#/]':e [`dU09+o xĺȵ=J#Mݼlk!q,KpQo`) 7 "OoETxKjZW_۳@ֆBJ1T zWYc[gG@"; 6Q،cvOoEQ Ÿ30-x? [~_ KRY3D1X]P^YM$x^9eaQ`</?gѿaG,_ ϣ/*sOG_ʕ?XF_U|)C>-?Zu*, 7 "OoEWyiQ`</?gѿaSxڥ[Yke`ݾ6Mz_ʹ4rLfQX^2Mz 2ovд,#DPK31\ŧiִ} /dTvrTm9B`zE" g>-H{[sML8r22ҫ 7 "⫣dڇukX: 2|5X זƹM ldmVLF+A} 7 "OoEVoOj⇋4ctuSx5K8%wQl|0>{w_ʪI|)C>ҵ7]uMB48fe }ʓ-?yǎ8׏ZVK<+|'}gVn[ox°KZ4iceda}U-df*U}+--ŽB):@g#]2OG;15sWٟxůV,֢_ʳۅ\qD,pe&$>&. j-m+%ʈ٣r8CqkWv3T W7 :c=^[j ^JږŪݥݴzo؋IWbF,?9Pxr?}qyv ]KEE2I=mItiZŽܶi^۳ ?WtAIs&|헋gg%]~u14 s$rԫXw>J#kO6"A> LF1Mf`  CIMcWE4?UqTA(Gy~ =e}XUG- '}5\_Er4?UqQ CIMcWEZOk*(Ϧ"-T- '}5\Y{;]H:^ $U2YƆW ]O@5-5$C+<JCr'`"ѼOgk^[]<ǩE-FtIBW,)GF'񮭦oĞ?n_[xg]ԙ%2I?%2%Kngil[xVHk$eM]g!+)o!51y_Im*rښuܨ`## qm߉ҥX]{\MS^sOu5KQ43)(B} o[ST:_Zz\BR̬dLU## {WC CIMcWEMWޚKM]cZկWMҴ\Fn—<"ݜSNZT_~ۦ4ܴ01FQp=B:xNWz_s[,vZo"I÷Ek|n}6^4GK@A}$y\_YvKSmY$2:dqj}~SѮѬm-Q6F)[ѥP{o!hQ^3ύ.15-bPrLs N0sQ\O?w^l4tV]&F6hg@̛1yeq=Kӯa̒kqf@U0>_wKH!%hﺌݶ9>~i_  4&u-mI Yv'^xo_G4YYk xOLMZ]Y&ZďnFH2 ֵbAo\:-clIwc籭`$=MSnM(<>hI.#E"4{s|KmzVW<9b"óǢM[[\#YI"I>t_ΥܚJZmC? [xJheo~)n&2Eq}Ĭ%%6Am N>?ӵTҼ{muq4ڡlg:@&1%DʍjH}QG x No'Z~ʿ tx> l|^zOE{lbxp7Δ܂01_KxKUkԦRf縊!( V;N,NRY`c@I!WIT4_K[%ݿߏ|WQoڮm$~mݴvUPRI^9| gk+].Y͞c|@35Gw]]egס|_`<{%:L7=u/6AeE##mbGC÷p'f[hP1>7dĴ <KEo+|w< >*x7Q{-#O|dӉ 5$Ur)5tQM?$~ ߡ|/[DsZ7:^jfq[5lI*0cnKZT,M Rh9~j^X#7񘕐 $) !@m>fёΏZm!̬͟/D<pIK)|wޛym ֹ eVf*;A`5M.J_Y^GC5̖J3.I `'~t'v eYYLC5I4dA**]6G ,qT`$ ]ſ>-ƻ8e$Y[qeWS<9X8ʐ$-QRQXx'ND _%72WH[Ed1U'W>ӵtkz|lAԤX>Пh+Gc3 "?63wwZ\ӼLe6;Zۘ[ Î?o-_^֣^X[g_1U{_K8.bϗ4k"ȯ.-߄$_%;8&KUxP ';ҭ@ͧ|U~_x9uGY]7Ltm*. 3@TYT95of/ &[{i&[MCKYᑢSk)# 5~FÞ3Ku5MkGpڜZ=E=;T=vY :hxWtBݭ"#$L#2aʰoN_km-/@5B14t)_.?\femj~ =ACau<רWs?*:L QEHQ@Q@ZX_> ZĚV }{t #X3`),$SU>x5-_K^4mӼEo+4e l`pR0Se|MΧLѬQAK{ZUCݶ߃7hxA4? +B.}>KV9XXԶ1oi!Mgt? л _MnLf ~wI| v=0ƱC~T]tq 6šMIEJZY!_";y 0U=[Xldﭴ8qw*e2H\z ka $1Is2ƲH X=+7ĺsc Ism$*OL?iV/vqgGSE|p~ǿ?jX։mYiij!y$27JH9$c珇iW|KZEam:}24f1HEx9?T[geRP|v>aG: ? So O&_.@ &>%~^·+HZzMM@$nq9=1ۺ՜:R &Ia!mHF{pxoHе%l ݤF$Sr6]Iӡw]CT%K5n>֙C FA@u?O J?7$MqD]_Z֧<_YDh1gr¨$\w<Öz5mGYѮ0^Z|1V#SՁ ${I)Q¥?R{/&Rt[>wfuxrb(K_?I^kVPq%!ss%êqo$OzL Z(Q@Q@xG]*M!8EL1U"7$E; ${I)YQp4?7$xvk tfDH#Ӣ +RA}w㧉?/Z=Ti O% rwtk9K.s[[z4ryvszRZJu<.׋G^.Km1m.I4_.mq!Q-:%k,/ H=4ڋ>1'5/#\xr[ 9kE$@>ŷ>#=J;cW0m"Q)ku *Ŏx߇u[NúeΣru-4=&ݙ1;OlT2|3ZVe6ZWZ-`*2 ?ҧmxM7WX4nWAIER((((('A%kW˧i|B9%*ZIJUFbBMvWx[㾧ڟҵguhTuK*gGDO|rHZN&{o=Z*Q?x2?p?}ՔWǪ>)Bk:aF!y.bBIgʚ{];GZ&;5%j2;gd$g HUǶ -`#hwV> _mCM;2>Q~,7t; xRӬ.t<eef^Tげk?.wn}7`_s\v&2pQ7tRJWn)5=g oC[j+}?_ о>~)x/~ j'.\i%y>\H2Qx612s.q1l,)J+%Oy|@-#yGfT9u\A ܒĖ =A5矴ޑxO V_xZ7FJu’)Xn=U>LP=vI#/  1^}~3dZ].t׿Տ#Q M{X5wTQa/+ 5cՕ=ƫe=0$e؍ϖXO?x2:Y%nh^yʂ+?-%tncGRjm3ؔ5Kڝ:M:W'{Pҿdo|mu:7>W* Q\¿ x_-it7A*jpq*C#$ȶ>т7y`sqٿRR_w{O+ 5c°^V?]Xg M{X5cxe WWr_Ko}wl.&TWuDRB*vzyoj?ZL Z(Q@Q@r_Mw^%y~}w(Y*e ]m9%V7 WZġ-w[b6B* `5WsKJl`. ZhK)3ܦ+K:1#95P+UϺf^ZjjfmwJM4+mAۊ߳s[/<;AwNi Wy+$NpװOwq(G7<9aww~1yOvwߦ #{ᗉ gqf.Ȏf.H1p/? U~OzXfuݴ]O˾Wab(N]_7H5׹'nGn_}񯮓Q ƾO7^/AAOgq:UH`ěI?w<ۢE'Z9p''_IJ%ql@ h$^Q:Ǎ|IٮhIs kqs`/lwy*Hl AW?z&xwH-dvq$q$4+ލ ,+q [6hG-o٢|_Ud ,(3şѿY/$QCfUhڦ\wi54s tq2py goKOLk{$3\YܘX"1 Y޴<#iuMIO>9nSLRz#Yl~ |'g!SolbC \Rw}ѓ3{EѼM {ME&,H2avW goKրk_ /Q [6hY?xz7 %? goKj-o٢|_Ur>PnfKi;#& R##!,ލ"?ywye7ۚ #ySg!,ލ";>koiz]\[x~1K{suumvWްnjI~mHlu_>qY:62#\Ai.lHG!YX1\W0&!EWƟxg5A_6>:Lмw/fj_ qmLO&Tg^2Kiu=GKмE\]r ll:!Hڻ$WZ5wk-\O%7ng(&I'滯 o~qizU4KJ.2oFu`TA5o'𕿇t1K5Kw;O.PDpخڲvoO<jޫዟ gRx7PRI*AF@Pt|9[}I4dx8#px"MUާ{ekZ֥E]LCE T]$ı$W֍~n⟽_3hc  6šM^^O/SPɩ06袊 ( ( d$YJ9;)C P7(&¢/ ~TE B_UZxk-'_1i?Sz?ȾGhϵ#:'oՏIAI5"*N1]=텶i%ݼWV z<_1i?G'IEEY+#1P\VDn> .cU^'SL1c ijow럈qhͩZjwjOҬi&1lL68mƝ2N]\4,/3ݏUz\ǭ$}G|QE}5_b ʲ#^Uy_`Y=oAOCw _rLOk 'CAKh߃'toߗ~mOiŘm/2}=Jѯ]Isqkgiko/×+Yֳ-BDhX _r 뿇^-[@xFԿw+E$bk͋9=}Fu%=]汵+m'<#8`cpXg`M_ PY㦅vF٫ڍ,Plf 2<v8 |+};ƺ ^O UU  ^1um Z4uAj6zlQDD7DFF]=Ϯ4:zt=FGgS:@ic i+$dn8k;_Nu'4ہr.5oL+aowHW[`2~~ޕQ@Q@Q@Q@Q@Q@~ S6ycVVoUEgyYc<[|1h@~>Vj?V+? 5Z4Pwϕ?տVgƨoUY]YY1\, gB芛s 9e#Vj}?ҿW/$Q_cJ\@[|1~>Vj}?ҿW/$Q_cJ\@[|1~>Vj귺]5ʹ0yh!@Y Tjɞk+h%ţcrI2 1_oTyYc_cJ\G|CA+rE[oTyYc񪪶 3il3d?("xNZ,;y$=LA}2-yYc<[|1U6!<jQe|{gR}?ҿW/$P?V+? 5Gϕ?e4}?ҿW/$P?V+? 5Gϕ?e4-BYM&{y."psYoTyYc}ťv25ճdsIY '=)e4oտQgƪ|CA+rEe4oտUGuo-RqY9=?%qW4MHr\2<:ގʜ{P( ( ( ( NiMN+Zƛ'SVv$ Xhe ,1K)7,KHp29sQSj-wOZtJ%8[$#(${SጲkΫW _[*A*yJ>PHkF_ X~iZ$fKFtK$2 w|_'̗I^4#OӵGu-i">X8߸ʸ56DoxSNo_oUȵ[k2UNTwcBH/al;Bկ>+m޿75Y-MxLK f&!'~f׾9_'k:hhƨq[d*<ϼpq?[}cHt=3f]Eyoaq c5Nڥœk^1{"EX OϽ ͞u6gglxG>)~8>olwZs(2Uxz]KAcZ+@(|[PNM1#XFt!bQe'GL]n}ĔhS:OW1IaUG\rXKBGX߶vmR rMm-gx-_e CARͣZxHm^ɴ_&H H\ςJ+;=SݿmV]F([yDU1 eU$M>=ƯnwYK3%u4 $^æ@꿯C#Vl<3^ڵk-x cC E!?6ls2Jr2;%y|+P/j{(iE"Ew"-2sZ|wldW$M%rگ7hWCYA=4}[^Cz=cf-;#b!FBB˂~knM-7_i&23\XO}OZh-2+@Ad]lddZ?OU//ۄ.|1-um4}QE( eX$A) V]>-4{]kBo3?7+9S 17%9JoRڝVq>],[˅&Ğ@8.i:wzW|24|696øͰkJJv%+!Yq\P~ $֣N}L$¶oWgEq}o¶oWgEq}o f^j7(O6^Pd 306 273 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj>4>/|)xÞ~{;A+n;F $]/\O(r+fo5X#[]H$H.끊OV]a Zh+ٍP*m9voi_ߍazHGDWKᏄE Y.}^G_Xu+ )"[{h.\< <iZMfŘDX*vwW;ے=T~濤(aOl>|cO{oEoeMRC!=97ÿxR,5o'4ky|w,@D Pweko\[_8/DQ MqGI5<94_qpifK IEBX9+|=ezּ[ex@PC:(cGq4R[(7H@"RXZYr0=@-cNlYǪjVG¡H,=j7p x0Ђ2ֽEf-xVlGh1y^yF)VđrvBZKNwOP8qoŎ>2[nMݫ]& GG&F^vIXġMW1 tld.fEq=vyw'ܷ;sYլ{-d $OAIBEu k:ڜ %"XTVޭݍt/杬YV[ۘ&eȠ>kNNr3Fx`Z859ʥT\V}gΞ>}Z$7"P˃QҊIQ 0y`@?W);h~(ٴrp9'ֿA_aO[??aO[Oi> yC"QyC"Qf?>~}hIow|Sbw|Sb0GO~øøYϟZ;3] Qǥڋ("m*8@ 2c־q<?m(q<?m(j/gt|e|x߇64 /xNu 6ݳ@6};sx/u鴸nI,KR8D`F\_møøYυ PEαx#I XXzxuH-P:pGlڋ5Q_?0-?0-.PHӱU:euA~øø0|@HmH߉Scj:Ưu #^UԟZEE.|oD[jq[HTVx7n)Z֐>gBylq~Bq<?m(q<?m( xgW^u5ݎ>lcr\c#lnuY 6M*{X@IҾq<?m(q<?m+ʫakVn> tU^pvz'mٟ=s|^OxkL|]f/b+m{4Cs*cH ){0"#sjL,Ȫ]Wj̻Eߊ 5|MVMC$1{cBQz 7i}ߚ3֬,>*4+mVm3YF Nke “ۘ ԏ7pM" ̱ W ֵ!.$ڄOEg2 q^ Ծ jշԆh#Y"u62#|GM_‘{VtyYEo8@|~x@߄5 \sR--ӥ RGcnyP܅\mG]ͧ,/C=IDn\ DY.p2 .5k> o>6ZeifSHѥ ddsVt凅{PkXE s׃_6k_ "xxt'$ɼ;eu%IdFΣ*3ڱK+Mv7߅ 4(jMKY5̢BHb$ҋhAwvCcKxb{JGQ\?,%At}Z{|XCoݻt/< |%>Zi4wwxwzKw٦ -3rq]/j?Nt#V3tr\3e7%hݢV<0, _MwgϤl+%O@ =4 +o,:̳=ȊL-#Jf`,0)tzg,ᦻ%P@}['k=MohVfOhownl@V$N+c)x_X5oxŎzTe,r-AH䀛z/υ?>%ꚏu+fu ,A}eHRq46߄߿cn9$qUev=4uw&kz+Qkj.na 3UF< 8$׉~Ӟ R{ Khç詬I( iZo̶d#hKO38ɷEQEbh2<+9ۻ3+e[k4z m=d-_܌y5MBXd";B?gu]+4]x+M#K4}r5$ܚ6r(;_Kk=~[[i8]  ZUB?i n^MZXA$Ryl:W- h\}6CGȤ|'h{~?pkt֩o$KiaڳijDxѺppxaEWvݭYKTpr94ޛcS^мn[kPmڣ2UH VP5hvƙ1ӯKy2yʶUuU5 J-Τ23]EÀY8ܪzm}Mwak,kumD p:0B=i2< a %cB]p;\?q(ׯ4@ۭ%Σ8/O;DY`dW\ "C#tC#'+m yS4U/xGNi{`oa]J76p\Ƭ AGzH(_9xOlix{w̓Cmp"f|#mA%]w=YM;?ϑ u{'$Z>}w>3esndX5Kb%@cS@ A:%'[E_Aės^?ʖ~8t|>_maVܰ(''=i cSec`=)-]$x۫>xk~9!,P-0 xmxObuv״sK- I,DRU*IL~ؒ$  -"@0dz?iZ_['OZ5sRͺYlfTRYHأq9&u76Zx:? j?axCI)`ơ]m+xnz?m崖-ѣ.E!<+?{wڥUց`;# 1&2I,T1?zugk]?-SxU%KiSMO4 .Ag9/s Jc*H$$t?gx]GZݤ ^yg5٣`i-_~[[[h|s= IImIo~fhlk^$>W.៉2sybXK3p/ZtQ"=O*?2D9M x/nj6?5IYiUčQ,p޹@N1xcǿkx֭ EA.k,*Tݺqۥ{̞9ѦbLxQ*>ᛝ¾&ni$% )"y@  [-F Q4ܛz]??ʼ~mZ$(?¹oͧxCCCXz,jKER˿iK'mGTA[) m2J'QX~5/d/x_)y]b@,]K}ҾeX4m$"3G}(ןޭ 꿯ݩgI7^мASAB Zk4tdN RMb3qFv*-W^%{sNx nr?r.Hr;N}h6Qp}G;;V?(˳??Wb_կm\^^^jOo$ cAPG''&"tqEڬeGZ^e -ACv mg@p?u6o)cxKÚ;_4>}WMiB[alr2Bkh_>⿏>߅|xE|Ex9R+kkfp $gQ" QNfL&29T3xA6y,ğĭ++Yv[z߭'s_iPx'wk cty457pW>\cXԑU[ tb N=Sd KȊI"+SOҸU`>ocgi~&}ٚBnĒ?T2(E+~8$k1m/Pռ;ee\kP[-ŬKpGH+@"+\+/7dY,m ^%SL~gm`<8'?)ɸP~7fk&;Hb JtDTHe= 䜀0{ kiWz60XIefFnd̘eP͸?18:GQ^G~z&⳥Ϣͤǩã{AgԎUeAnHǞ/_ x~;XKK=ĢBeuU ]0 )3^O/S P]Z]i›,7`~VJ? xLnsMjqiYh-UsZIS$(gu] \t;-gP{?QFH7ܤpn/?7?oφ"=?72;Z+99Yk_ %O{oEfsIg`ʒ 0Es ? ~)~ <DlHn-n!.>eHFV^CpAe퟉|i>-#N)GK)UGՑОP B| kM񿉴]I4/C,uH+W%U'vaZ ghvgRl~-WOxNE7)>3 [}@oYD Ƽ^7ĺ'-Km_ y> Ι7K2-ؚyߓ{?+x-I,l]H #y//)嶵#Hm}b72 KNqǧ",h`*=+>3xoT׼SN 'ł >/1zo1?7֒krmEn4ZxD'tOgD\g'bC)㚟Foi?jzuMwgA41#6ur30URrIrkZ~i%j[]-%Gi.ʼn6U6FA/]YZ(&Dq;q}P6⦓6k[ֻ^_ |A[~קMꚏdaC`kC% (lX 7 ^_4<o9=嗋FMq۱-XBlf/ĺw[xJ:MB=.}FkQ4+#S1^VV{"?,ZI_Yi ]0,ȧYj6Qo_|R4u]-5˭> BAQ{%0䨯SijE;Xk+= YB~mw`7|)kWPh`'UP}7 Ĵr22<}m#m4=yV*m*Men|!o-U5|Fzx'>xbm>G`S=Ann0)]Xs#}?}ӯnz/>Em{. u-B+ $|\7Y4j-дDf00/G5 | qy.4 K E 'r8A篚iEχ 5 GKMwhD7A eG9V>_#һ_cwdOOsIuk/A7}mgyD"kX.RA#mURrzEw:6xLԴ]OOA$7vs,J+ pkcgk l-hE2H|I>=J>P.ou+i\[: eg}4 gqH$M[DdOyQEAQEQX2}|=sjkpV'ai /'j VsAkymܶ KoG"QE N$GxPr%{#8a}[MoNՑC,Ai,?kiwz&NY5Q%NI'OZ}v¿(,=yxv[_a7Ms4lX_g~)G4pAʳ ? X E*= ur2oQB $ X(ccT^x[Yu KX4끨^WvqVwYx*83}:+ξ|B!wćD<-jRZͬ:2MlFeB T CBt?Moboi/&]kI]DۧVrccp\8tB?Sx' 4X +3^ )ž3xHΑD:dBCrI)+xៃgYj^ 𦇮686 9[?kgKx6rݯ,'m0 FV(ت w'飵E'd|;Ul[>Sx|l]^UTP0R]I~T},rgxd4RnD5.px/o i^埅k[4x"WH\ E6} (aEP~3-];2mx*O+<[NME(Iir-aZ@a̿LhoBj> :ySw8mhmw2ՖLbna r!{^(^[?jZ/J<'{QŲ[dBU )0VZ; Řd9Lei 6}l"Ud7sWA )]|5y?W ?K>w w~>q|*Fh.!fie5 4][O<ZUwک0_h,)!B}#^*Ճ_Vqk-g\o[@:f9j-EV< iN\x "Km㲉-:ff OB3p.K$ BMwsoQF*/Kк/_?G- c5,?<'_, ek n;iQY\I<^lT v\v^kV)QEXWMj틣Z꿳O/-7Kd[̓}V'qqWYosqm-h/m.|UbF X8޶h]ЗI%EW,|\qm~ V[餴t2*`Rpާ]cDmemy7<L;h]ЗI z䚶 V_>[wY/~.T.Zx5o2u($@?-b-vAM.[o틯9]K%]%)ď#;Jg<מ}Mt𝯊tۭDL/ b@3*tv}Mq]H _Fpgz3"|=cGDR]rBJYZQ( AldkA׾xDu1&ۥq&v@5֯kм+nc,G\n Foy Ipx6W76 {E}k4&M]ķ'f(ǿ/|'dk /| [ m~'MdQU8> I_U崻>1O<24w2Y`:Ix8ٞ V':y=u]W7.Ue* uQWoT5,aER(((((?OuC6uv74\L"oi0!q(7GWKտR%-wXT>Y%wg&/?Sx3l0ȲHF "»q9A/~KQu-V}7eQ,Ide ېq@$_ <3eaYinzX2k< QWaEtR&m2tI/4%ل$m071=+:]cg\8Hж71}ֺ畵K$&Idym6e`3ӵ;IXOt=;–I%􅥞eL7GY]#*U5k5_3>)xsƾ$m5Im,5[HO.91] f~,WNW>N-#=myb}^V6kf A0+ªKniMiCu%5jy>Y#F!An_qku]3 .tk{Ȯ,#nmss$S$BJ4ݿz s.ׅ-27|I 1{s-L|^-_3;w36 s=x_~!dՋYv4yw6 s=(/ISqoYvmS~|%ž.F4]h K¦2:y K;E.f^P9f$Z-hIO^i9ĚiIe>pVYҪMF.Od$ݐ3s%~^!/^ub}o[OSdG4$UQٝ 8SOו.՝C8KUis̿o9YF|[@?oDMQP a}5~.©D>b;Y$cl_p_KvWGgſ fJ?o9Y6,3Ƥׯ-I$Ҭu(4vyL*dy˟obZ'Z kzsM5&yH."K%fގUv ϐ$Gl!?ƏIt B]77_< ]C3ƾ7f = i}ͼ^;,Ҙ:L@`cJxw6|A5=SL b쮭7٭n.03^ .A[/O]#_J[kS}ß~!_+ᄵ16\B.O͝8IZ! .A[/On@`9!hzKoxqmƒ[6;}#=~Ji'D.nn&yfye|άi?~нcʋZ:3? o9G3? o9Eb' ;HJL?}G: 9e3y5ZY[XXC{;6}f$`drI52xjY^O$eX%,NIڲ>z2Ӷ\Wm'Vk&A%Ѵ WI28՜&5䝪}_gv#ګdh_gv#.3!O K5doóx[:$څޭl}zw EeZ7,cuuX(nuW) @y*o2j$+$̱&bB p:R՘iZW-MğM3Zu+Ep6@0πxo;W~rX+ vOh<7A}?rgULkg{[gQxefv}@E*U .T>G;?㴀ˋRF|_jT6j5tO>fTp8WkLYMz9*m&9.ݘI&gv#w|Ti\趮gv%Xyu*˭A{.3xbYa%UсR= (((((()L<)fv8njW:Xs4KXqٓEe>GOG{ƫ'oH>'RŴHllIu4,qB;#8|/WFi2B.yPHպ4r9!AVʞz~an]i>:Io+#l\WnmГo5\L'h5KCOm+%5uC.B"UO z( xJ g]KH= zEWG}_T6*(,YZώ;5O֥1j!@ןfGUiSOsk}yXVP$e9C;K?[OG{ƫJMG>cî(60wQ+d0%8*2A>-iCU񅾩-cỸ-'o8di`Pw6J;[A\.owjK?ZWY a8QXPۖ$*IG΁+]u;6smgy@rFUSG>67jV{w/$|U  #5|_`IԮ mto]#*O֘s}\I>|!vT k38(OG{ƨ|u;bK\xs_n-jt`:HGYrJ#_WR&{4g[{ˌag4 u#TYb*:f $`pw(5Ŀ ף>&>F_8eݴ1kݞ]FNFQ$r)C=uTQE ( ( ( ( ( ( ρN?q4-B[k\ۼ nR?x-8tGai_oϷX$&s0Jy>fuke}#~/>!HE m.`i g'IѦ/KmAGV_;Mi!5}͞kZ/Kfoncզijv)w)rMv??v:'eWij,@l%qN_+_UGgjoqi #Gb~:SN˵/WwgCg:'2+Ǭ_%^kVRB %FwH t^xa}6WVkYBpQ>/*Ҹ|usCIkQ]*Xk-&{yBp:vћEv^ zvH4Ʋ4ψaI^1^Э}Vvxk 'mn _WicƼ=Oi6^OCjk9ű;Vb4fTV۸H|y.Σa!iY\IdCd0b,Ywy +~/*Э}Vvvw~hV'!^9=FWLo.-4Wʈ̣SԌә!=Ҵ:28'WeeVVt~5.!,hl41OTyGx&<iV:E-?Wh}XsP 1`:sT 227 280 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽsSc?}'?±Gڽl>|?z>EaOl>|{ς|fMCV Epe8]Ȥl-u#k<jzVh^4[Co YB$ I rǙo4ݿ9'?zO@+д_^SL>ͫȷahƄngGNkK/1RgkY4=VScA *$f;Ł: וo{~.ek3 sp8N@$d#^_Z|K`Ӽ) MAonmHd1@Ñ+'x_H><>ڂh ѣIK|A@R"] >}^~xgV/n4粱kwDX/."U`YʐYAf^|9gu^j>-P$m U 2ND-AzuoG}'?DWڦ|?4 G3a6o7 >7}S|HA\bsj<]>!VkW^-ng\]5+g;Ϊ ]ܓMVaEY?i >}^'O<ROD8ѠաUoh/Rr+ ~ x5m5ӣ{uİ`0[ yI5*]H@`|OOY ^swZc>s[* a_* <yؐX{15x_$ gd+d1"`A5znn,t{P3 a껈,=jef<\.sur2} QѮP5mS lmY%@TM囹8,B:t>d[=ztRX$Ю=pP}>%3'ihnݟ׭C{D;CYe,qXy97Q*ݴ>$Vi*.XA&׷[S/MޛxZ}. Y}A^g7u~tDVl=OG_Mna*#`yC<ԃ^K%=kLniYfcuMK2JKs,\hIпw7_ x-w7_ x-nV|OZ>}k/^*G^*GOZ>}k/^*G^*GOZUx]\) Ѓ__oW[=oW[=asϏWۍ ZDp9wA r_?oxCڿ5 jcxP _u:ƽ-f4ByäX(B(`oW[=oW[=-_pNV> 𯍵˪hW&/l˂B}jo|B5 :J綹c"gKx9UUs|5׊2s|5׊2 Y|k_F}4ZKck)[WЬP|1UO0m'I ΟrXFIJ[cPHvs|5׊2s|5׊2.|c?q}.ƞ&п'9ql!G\W_ҭ5um[3iCT3͓ך^*G^*G^a_qſY]YŬ{-9iVmЍ7^y5Okiiki-v->? 5 z? 5 zn-oo};mmK]㋏ ?% ϡk[Oek#8J ğ%_SV6N;w 2e=TCtbZ՜m־s|5׊2s|5׊2 r<4pG8GJ՝ۼ|Joz^*G^*G.~|]j2_IHQa"A ~xgM΁eſmՀ>;UG;U\,d=h2}|n4τQ㻯ƖRXo1[3=k{kAe#j5 z>EYq،³4eKy.Yk;U9+ϵM^#Ðx[giW/qIu1f{;B *طnox~YIpj i V}RIbKJc٧|.i?Κ _QO__%!\ٲ`0JJ,4_6ƨYCdk\\G 8xRſC_Sv7𾯨y"Ld0cф\׈>ρA5O{{;Yo]X8IkI2"`O#qE7%p¯_VG*uaȵJ"Ǧyh4+85nm[iŘ|aG RK w~>~񷈮5l>i"("1{/D IF6X+nzKGWwqc㗼5 [^i5kXN$ssmpO ۍCįxxw-/k{ +I"}vnS$F$lTU /mh^4Z;FETfIT 3RA= ԜuD|V ͗"]G.H6$nTs]oēig3>7{(9- ʅ6e<M/_؅+[W*zńr_[>yh)S39|k:M|Ariw:U٤7IO5M{xg/z卤5\|K˒ߘm1Ox%MƇ/NͱңBbύ(J^'V<}#[:-Ƴq*\?fYBCH:ù^@PD#jkwqqo3OnBrhds|moxToI7jzu2ë]+H#EN\\uk^=ү/|oy%9 H ovP(sqB7m.}kE|e㏈~&񿇼i(4D v,Q!C>]ǯxJt7׶VI]H#:]0!]_ (s] ݵj+u\'(x@]Ư4x;5#TYe*'˹)Z koџdQ^'ž: )hNsohok'?3_I Η&i5?_ny_[ֲyȬ/ӎmb:mƣfC1ng,J9]>\ܼ]2e #8;I%z2~~6;Z֛oaxWN$yYg~ypIx|U=tBK{%Ӣ3[) l:lA`8~ C&"g]ƪJEOxoiӬu.A^QNV1~q|RjiL&kugepT#\/M&썲yb(ҨƄ<4x᷅!&gBnQhcvg>7 8]S_i<W!6v{GZ/¯ t[)FTcUD+|!SVLxm'.2nNkvZiR4mno9kX?s.^DYNPF*?D_e+@EN1 5 Y!]S¾-Xl'tedp8hmnHI_\Oʽ/^,Ae[A$!'Wß[ ko|ڊ*[~L>sV~6&kIcMHx~ˎ5H`h_4DXe݆$oK]:fsj[GAI]>_C޳=cKmя׆x{ƩiWO\.z:HR+=Fkv?1yqӆaڼ;7y k<>ڣxmB5ܷFEIF@+7{ _OR_A/Ѣttyn"bPp2͗e ';cۍĶڅoWSO1rUb+6Wu{o.đX~C쐲Ja Fᕷ5f26|k?Y~ k-sO /o ֵ+;O xªI$*P(sE>}t4f=ޣe] QDd7DyXb[㞻ׇ"fP%+ ::8!=9D5{Zz=+NMK]Q42A y]qv%++񏈣Tam.[($"Fbfsf.mOiVN֯eXn$cY&to)~"F>PC[p7&yje0G!te\%F(]ok[ ߧ[OD4\&mB9(K9lKFh\V'>bW+|1B DkwZb$[׷i$;@ Ѫ j:.A^ER((((O*$N kZa74 ;V^iI}n6˨Z%W-jMObs[L:^G鲺 hGtnqJuiw:w&j:OJѺ:\hw6ݥݼdXMv\ʶW4[ɭVfV2k_տ;㟄&M]m iy4J=Kv|7k?vڱ>i=Z]:I_` *2rāSi·Zdֿ3RDŽ]k n :h&WvXƒ0x.QfԼKmo=9aIU^)HX2 3I_aum$Q=k p`A Ӯ_ƞ*w^ ,u]Q,_"9[=mivq]Ayo 1<,7oBf@wc=JS{=:/I"\yS;*6xg57~:ΗxGUDcFeR`U ; yEqkFcm|2a1uG'Ok]f)T̨S͝6 2X.z xK߄.xCǚn#<; o\{k|Cz#FC"61#V2F٧aEpRkΧ{οS-$ދ<#39d+I\s9%;jZivJ $۶5%!]ݩ-vEr_/ k^0𭎯׈lՄq{rs\R2FrQEPEP5L-Z6Xd^ i⻯^þ$4n%cU0Ed)V8o .6Hnm!Iجr3[m'k%e }{@Hx.E'n r 9*Ҿ Mmj,6ܢhv6(8z:' fA?j-?WRI[aȺNy|Vx;Q\¯'OF qj l~of7Syh牾쑰e?B*Zm^[%%s>|OoK't+]]l!G:8/] _Ǟ2m> eC /QϺo mٰ"Ʌ䍆ʨKp4)@X~=is?[|ӡ-Ο-4j>*M=dA29χߎBCFی6̲ov@X} ]J;y~8!7-M;S&w{L$WL@\c`'? x7tcK:ޣ{es,<7p)dVܸ݆S<4Sn? 1Qϓ4O_W¶DVh~Z ڍ[\[ʱ* <>\^y{Śm-uu}j Xyvj!G< (}*i |/c|Ril^qJam.p `2&!ƭ]W'H%.MVKLҬWrO"^;iT$~'8Fng2+xb6:115QCm4UQEQE'M _̞!JKdh$ NN Mn(X;$qǡh4wkZ&s_h7ZƵ+v^xL5PT$ZG7b=oŲJT;vldt$Ү+Fa?ٯx`8ansۚCJ̫3y/>;'K\[h tcC`0Nk AW̚[k-ׁm Q? >? N YvKOZӴ+n~ήqշ9s li  .>jnΩy-ZIfk#R&Rp" ;qוo0ѿWWQ L爠>%嵽iq5[gF` \t#MѤ_v:kG,Ƥ-~ӨRkIĆF d +ogM]w֑Ch92\'){-.k߁sqXLש4lsO4Q!|(&_O*./?կxz g)R8<֕|;1xHx2=-<],) .Eꠋc_>& YdomlhL|qd1!K@ Դ~NOdO:V]V"l4HX8,uW >{i.$< uԵ=eFb_2 aBr r+qkc|NM{)QC`u; ,,xfE"3l6^ޟ}_E|J׾2y}k{UƟ5䰛k$̌#P(k_xZ6,UY$ˋreCR7e=z_Q~(={ZJϫ]}U y-*ϥG滪yO e6<`|{g<3kGI' -B,4m%\ǥ_"ki[Ňh3'm˾3)TPt-U?~¾k;[][G<۪<F B䌱Mkt[#xKݾ%߁oEnEˌ i|u'k/Ln|#.KQwyuXۑ1ga&۷sZ+䯅h|p\ķGj2NY 8opJx"ϥGCƇćR]Z\xیV~w~x^ ;‚Ŀbm~Y%Ø`n~X xyլ|څQHeq"%RO^yj5ss|K qo-i2yEzG%P,"3hf ~ gxgyUjz_sT sNE=Ai| ًkƩ;HH[G$* qRC<8fX>ցቴ*]?PArkm,d cU3fj_SD4mmݮn6ԝF@*3aYA7W?Gmcd\@L`o–>HX˥ȓVr]^YX sV- ^I,n }vƢ9u~8xWi?iixjPiSiVn'!IFrx>"^AwBR7>&<[/[~낣 ;^]' iVIC me";Ty0o4g t8<}xOJןI{_6r/,ɑǕ0F@vsY:7Ÿ x}45j>okF4KG< ZVv%'u~sG}7eyo 3]4 Dxx?zڦcK Ǒo*=|v& qP<GCq$R/";ŏ)0IJ`m.1wWO%'fx3Lg$ "IfI˻,RH㌱H;T9O;/m_ j1 Μs4r$@X+r3o_>'xJf/iN|>,B4_%&ۙg9_SxL~qgm7gYRk]7JW~PrIǵj / `_kZfh-7#ɬsI{{"y&L$ʪV`@@|hM-ݞ; i~"^2%:r ew2ا^V z:m!Gl|MuVTA+; ȠϽ_fntK\nV"Bd ]rqg#KOm+}"Pq4x~ȏ&rA<hფigs{y,:鑤%IRT$܁8l2 o}6rbI7Ы|WӪxRRlEjdY#8>XI9]ž=𿉼?=GzPé%Ο'9?6-Ϣ᎟/^ RKV ޾_ )y㊩_] h$2O3$$̈P 87ngm/R9yVX| o;[kշ1Grkc2&N ¾觥v~#ڴ7[ԧ]0DvDce0kl~x7MApA.r %2Zafwh*Mnd s3> dVBYQHPGJK~;M=mluմYkkZ)EvnĂ#1ӒO5HH(C+X*VAfENkF A;?O-+vZ A;?O-+vZ A;?O-+vZ A;?O-+vZ A;?O-+vZ A;?O-+vZ A;?O-+vZ A;?O-+vZ A;?O-+vZfn[ۅ2N3 YJ' A;?OX϶5G%?PH Bi_ uj(T|Ua,/`B!L :' "ҿ'g 4ENjoA\>,RzP ' }_] A;?O-+vU.mWjKUE _i_?"ҿ'g 5WQwƨ}_] ݂fNOZaY3y1 KY`,RE\#iPEPEPEPEPEPEPEPEPEP^M})|4vYӬ#8y%DE9g$u&f:{z]qiwIkSM@$IEWã; |_TL9xGXK7!PbF #;/Yw7Һ;>,Ctd-۫gL1:ͶmN+Ih-#X~ƺikhxXAZ4GM W1ldNWמc{MOM7WpjZФHCq9m1cIjtz|O[|"A#s~*uo|]Z7a,GlN, 6!FO"/RM|;mR]}`>\[\F[#:* vz ju-B/ w4eKI7dxf`GdV Ú,-.F" s}drÐ;4/>0R&Fҭn.Y$˳k(O,f' Ƚq? ~ >=^KfWNNJWӪ)o_޺t?o:<ŌZ˺+A*hz6k]FWwemp]Bv7b=m/Y\=Z5;-}#1 8o5ܫ_o;Wh Se?OA+kZGuK+LymdlM HX";eoßUC{)!YNG]ep>YRqOO$R2l;]9$[qMQRPQEQEQEQEQEQEQEQEWZZBM×wZ"6eUCaR28#(~? })jiǫѨ9_R?*RA۫(f$ܪ)p1'^Es2Ӯl,7W6Wh[p\Byz~? P)jiǨ_R?*WQ@sХU~#KPO=^Eq~{y􉴸-8iةPی19$tҊ(fotoxx-20.08/images/menu-bar.jpg000066400000000000000000000712101362435004500165320ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:retouch|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 821 164 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((5" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ??~ j)4wIJ(;y?zft{io5 3DPbhr|O^?x?ׯmƃqa%ł CW<*$xoFXE'ٶ\;M|O^?x?[5tD.mzP>)OlTqd8=ጌʗ[鴮sG<z٢ o?x?a9׭(?fsG<z٢1O^h o?x?a9׭(?fsEl@=xu}R}K :@m!|qq{Y|Uv9du.L!wG.$Q,cc"Boivz-+L_dj/rX&~a|I[x]6Hex8_MZ=YvO*PKGOJOxv6l2_ yP7fvuZIpeٸe|P9 }O; ť߃`Ӧ׷9*ː}Zua%%d@ U5_zl^Iu]{R ۳÷ZΖeTI9wvsi5#G F@U7i)%[62Wr@Ȯ2EE{5ڭVGPtˆΙrdT"P^01ֽi{t_cAֺ{UUb\:cdWuDu®⇷5I7-С@cEP@Cc$䌃҇k?:*kԊ;̐,%T4-AQEQEQEQEQEzx0~wKC7 r6$bg^MRJn-˜3$cwjRo5(<9Fr9&" ^ӣ)K]<&- q29N+eŧ^vn5 &I%w8JZ.XUǚ9'^xP][xq];By*27knq^Djikm |93]CՑ<' gP9g57P$ѡ qT45J&|T+#s 1\ՋN&xo,˻ 3Xmx :dK$[b.%RAE^GW3hJIWvA4ߚ34[4袊B((((((( :=QAwk,|BJ1՝i$.$tp)jW(4K'+ ,y$M]86VgQڗ}29|74gSK)0S$94|=YhF7R8K)Qs]Qf]=Ƣ>Bb۲I$## 5ƙ[ Mxx`zx?Ö89gNNS"WIZnOzexd29,W'>:>ՍϑEsFI8`+,|]SPt.moݠ!uc856"6cg5+`\ Nxr0濄|3ayj^ Ŷb"ia9<}:oc=Ņeƽg|^VL`'힦m_25]F dBmdV"mc.tFڽ*3Co(t-CJh]Pņ #{}/P/yewl,lĪQxljwtUn-S:̲,lqUfʷddpKK/$qv%m[ilb󢙮"Eue<:VV:Lֱ_[l{h :%FJk?J2r`g;I|+'uΈ|pΦB6A9Ĉ.~'k=5U]+dgi88y>WLLRd?V}J)QEQEQERԍN7q3;3Ӛ5ZxJ? Mi6zO.mN^ZwIr ?o䃅s)撕:X/ޞ)dy:TU_/v>s$tSԖC.cG0`ɖ#\=i`>X$XUGӼ{{574X 1c`߽GRkOoE+032Å'M_DZKkZd=F'*+C ( ( 6vlo47-#N ) $9QGKQEQEQEQEtQ4HU&_D-TZ&w(?\`OҸa!-[Q6CƬײcU[5']7?c cVEOVW2HP3>DvESQEQEQE)$9C\* ί Z2d,2 ~U^EiY25N6{6NXG !k߫wc9TM$]-_ ,f9u$gezWMww5ԍ-d۫19$];_5}Vv0XH{8QXaSZ\gsŜFw$9FCtHij3ORM6((((((þ5̖d3Y]mn8S^ X)8~72N v^<VW2z%c潦[=AР1Ԋ:h o=%m3^$IfuME*Wsv┷CХ, .W52{褯z'sɵnnBZ ɞZ$j #+>#iXKDpfOq]x8r^1<d͢ٻ')Eu^]ψ4kQu=+OcetnuҳԱ4f1D5';Zsdס¹MnϘeK{NWnu6uOuED.W[:Чoms@{_iv0D3[۵(UodEhWz<7t۩Yاs.0wt5'`7BqR֎ֿWo뮟uڧkP/y\ɨjZ<"2;]v0?5M+_V[A @rp^Sx4AyISwW)e_P)akvXl0T0=9k{ZXKmZοXIwih"c(p0*wMዋ=jO9rѬgq֮#EKw\"Var izJ Vo r$`N=1IpZ;[X-KYo9⛟4:"]Xdi$p6#B/b^$4FGMVVm;4ܙH9?/9& >#ЬɦjF;\I r;CېOLT|Qo_El A)0iRSz++"^ SI+Š( (u 0ihim_M=T/'|;#E+#8Ѥ{(}*J(rQ@Q@Q@Q@Q@Q@Q@Q@H"i$8U^\ -Gt~,3i?~ψRGgV)+͞v$u6w8U+ߊg:[ew4DrD~%VВ7R`V 45h*̎. RxHQb^[NF=.6&r;^B|n精 oQj8 ] ^~+|i< {]TDk!ʹB98#xkSJٷ,Dh{)$zzoK=džjEwKmVI>x'$G+c5-ײ3H~ǯϽt"]WDWZn;o.X{;Hc26"=z*m߽zù O^7/%;v,g"Psj/@.608|0c#7ڮ>_>_ w÷)bBլ)wrYWpltu5MNU+KK;VK:}Š: X9,'^+ZN.,br >|sf.G{s<41E.J?sTRt}Š( (iQ\5m$qBmSqԂM gO`{4nI7 5Ru#fZi:o74?RVtoi.V]30ZdmQGw-}V2i^˛_EVQEQEQEQEQEQEQE{??Rŧ=km 6ޱR唟JFSKݥ׻3Զ g<6nqP`\hO<$9>}/5Wj~35H:$zWO_g@HחD\'-8"b{H[=?שK?v[~5[s>jWmj.}2ӵ ke\Z&gl-Ť'$Vik::>{wocHF#Ym'ivfk.˒1WKcGQ^YK@}o|95H߼nHy{M ʺ>ul M 6"ua%ZxOZFkò5ss`y#">! Эk}/5@ 9`koEmfD kVȁ;N(~ hV஼9GԠKjWWV[sm<0)hd2(=F}+ݼ/e,8tn#.'nRq\W|:ֲHu #S`8Gz);+]oEK_-L7QܴL<|SjZ³jzUNB[jIUZNs_8KO)7F[0* GzĹ֥O S~'fkv7 ق`Q `sZ6if-aD 81r:X.[Kuk;6)/5Jq3z8|A=77 ɮVWէh킴BDJ۱3PDѫ.q kZ; NO٧x}~R=GjwQtyZF9tSeQuV#CxTNK/uo3FY} R2*9VXݖE;5xZmϨk{ueQ@Q@Q@Q@Q@Q@9uHՙ*}6¶_ÿ ֹۭ8z(=ɮ6xrԪNoA?^Ge !?|e&HvC>Eşm7~ Ѭ?-y[)ldXczCFwN/ncv(W9\m <er֎R\^ySiUV[\'XRW!'h+}j$&+KVkWҵ+mЏb+`jaeiU袊6 ( ( ( ( ( ( (5|-\x9@1s+xw.[i]wn 1@궗iU<~2NkXRĚ}3- ?SZ操l4[#ܪwmY#wLIomHU+T*x57W me}Mh]UVy$?Wj|-%E^[1,mr60:5m_~uhbҬt)O6Ie^>EO/+zqh,04$zt<#:ꋭxwV,7=TVZ4e^R삒"H98:-5u}m Ƹ@c'DtkT omj'̚c UVmqۥ7TΖ|1;I#}xK 9p{WCxwCoXߓvtd?r>| v> =״^R im^!' WP]SEUQEQEQEQEx?w^K%E#ֹ  {cN/u Jj>kږ{j:dImְ.p~z rr8/Zv9ohs?(u9P{(3-K#L'/k5oS-P*?J,Z-9 )oA^vdqkP:1%Jy`u~lTU=zQ^aQEQEQEQEQEQEQEQEMikq{:g$Gb$x :wjvg$(f I=+at纊]zXKWde UTSKoh;iZEs{dM1'{LC[:PGim _EUr'R4RWlľK{kmdC:CGX?.h{AqB/HcR) "y b&QV:Vm4Ls x5"Bksoy7X7Q {LQ@Q@Q@Q@Q@Q@Q@z} ۔uQ3OȖ+DPd,)ˇvfE^a俺:4ͪʰmv\|v-9˃~кVc ͍E% 'g z9'VMtw#ma:zPi(pWR lT6SFU#k{S;_Oe"$#hFo({ˬ_\^HK%0,/B?z1AOVHݞG{]{]Ӓk{, w9`d>|3[}gD7Ipg,G+su+kl7{N$#';\Úƈmg6Ulʺa:qҜXy3}o_ZhVCޓne{0e:rÁ2NIJ=pux{Ty,Hoi#{ה_7w Iv! yˉMcC<#,ɖ59Ug4ni}λ}K5 -G?.}ZV!FŦ.CpNIxj*3ҹ-3Ʒ6P[Ϥ7W)+F=*MCwK4iI>[ L1R_7Zցyu %D7d' cMyuf&Ҭ}GyM1vghbXd/Iu ((((K;%B8aPQ@LX95?ixhhܨǛ)RC $5Ч=&H|>1O;̀r굕JФ7c*y-O}7 K~*'L;YaݷkneE HDOB=xrG0-slў}<hԋ6fKX@"ZN2_񬋻i5 <ie 觋V '%G0WRr[GEuZgnk:N/,4VYWHsni^.w-\imQ#y~a]mȧJUV qd.[Yԥle, HzfZ(Pñ>K>t =ʾj33֋]S7ɥOުoی2DzDV|OVf<rhz;ZS|I=sK VoĈ#23QE q-^[ͷJfSCC\d@:zH"G.ck2cTV\/4mKWofM=. g$Az b(d2!}GC\䉼JpT]6Iw8VReSVJNi-l3U^õ*n׌[4YsAjpHkdEs׌C?][-GU;KY%S,g* ڽ{ם^v?q_w+]4#w\a=99 )AkCDѽFECq"ROR;q^?E_5?z^A0b~j;"l'i!w`׿z׼1k4h6h@]rR7;x'gk [b4/%:id2ee N*淯][?^O.ZL_$ F#rdw=T,-gh]h cwAFVV^_OW_=/XZԒ ?z,_WBwu!v s\Ƶ5-PH.d^vuY>]HǮy֊%ݞɮkV*i(ҡ] @\j, @w-5ϋ:%EխI2OR;q^AE6_I4 |$H ijʎ r15QGVå((icuz$x,"Bdt5Z,͸=uCY#`J QWRnŜ\3QEQEQEQEQEQEQEQE$< KI"$ KOG6CkJyr3ߥyz߃u!VyYds=֒v:<^Yxnqdcp<sr^00i/YKI$dr:}ݥ=&)6v \WU--@h OCח!IBі룅"2]WR%ظ* +ϡcd=+* VSڙ^嵂߅ W-o̗Vn#d tӭcxubmM|iK.90F?&8++K湸" }Ӿxm%qgdlz+`:i~-^u>cs,M<gu(8{q'jڊGg9U9fOCJ__fխSqWG=`,>? בྷc-W|μocU7*xL5zn~W3]iEoLmV kcڷ٥Nss^>kbK_0>u|!xvփ$1O K(}3sPwxRmnKk8L)bI83\7m,zxRo⺍UU zv=뛢:6%ڌOO=.tOV5/u]B5Kۉz,TQ޺#Z*ȆՅQT ((((((('k|} IBja29;sYէ"vnTek7_/nSQ]7Xh9OL x.eƚmK%G,`t 0}O6{sgeO&g{vrVGu|CgTAO,F AӠrWx?y:oMnV#,F/'Gz(gUFSҴ+99^WLQ=*ä4btFN3 u 'ӣԡVV0azh? ;fExỵwO;^\Bߙ|I?E7?fkey~f)9ۑ:Ziʗv( :]ߋ5Gm{qZoِ-$nܧqk. vI-6K:T*٘otnJ{v)c@Yɞ8W7ׇ#i^|W`ok-O]IcsM-6UwIҩhkd+xnǘԩ\}܌^ 8?K$Ii)QEQEQEQE(4P^7đ>Պ7!Ɍw9= yE#"I2H2#IV*w:fž%$6ҷtWksJeV!f2ʱdz+د Q)u"c猵Yi$6ӸH)C0SI[N'<[E U'\܅e!٬h",p076ܰl5_ie=8x ̢:vKNR-b1Y lm@#5~○}FGͧ.(zv.ś9&5K=zZH#1@NrdxDw| ws-y2y6F'<7$dI뵁I^⿃:6{mw³[iȓGo !!:c4ۗ7Ie,c} tQEQEQEQEQEQEQEQE6i(GP?A^/i*OuCm %(Q-xZNUȯ0`4oj:lmmBi`Tsz|mo qj(ѯXLB7ͪO1UCD69+W:뾛Yku;.(HxR*xbP}^8㧵}Q'ŷ ]T֧Gx 70Tţ|}h7\{{+O3?'$#ZST/cY|2p>^ zwDmqg[㵓Tc#E±N w t j ?WhiV5E!HznO|C񕞭}O,ʈdž'5wn>bN3s$kn~Z:3r?[<)5/ R# wz#2qJ:_Kߢ9AZΡl-1.@V۷G^3yxMB@n+zw2p xW`0pG@iApa~iG-r""0Yr:2xO X隮u^0-$y ,0[',Oc~{|[T6֤bV<׌|M[]O]0DlS{ϭgQEQEQEKk3[\:^'s\>jql d(((((TO"Ql4''3q<{1F[zEqt㧦O^):97u&WZew5MGF*GPj⏁$-q5,ѳfی ~SFJ!*S $n(X#y4'PD;]PЕj$oi:vxA ѰWQu/ڥLek+xљn gxú`H&7q q=h"({Kx{yaШq:TQEQEQEQEWWSu[5/,vłF~uN +b' 8JczN.4*#+%~#0}f J[Iϝ I]k /%4:Ĭ\a'R#~5kYވ @}ꯓc5ˉUB3m~ٟI6}?@q Ÿީu@.?m2C9ϱ\&oq}cFmu, ;G[i4MWD}JXr6tq*~qlt_MKIMAޣ@(1?$u-zXKF3w '7<͝;p> tvkU_ZiIKCi&i*;w=5oo-.O8.4oMv]YCJXCA1VJ%cy6U쩫 G0AV?LoWK5]7R]BE_ jƿԼ;φ/!9pS7O'ʵuVkwuI#c[:FU*Qb9Mu(ηX ˱9`V+]c~_Fq;| KFpsv K$B;KRl[RyC!ݎ; /w+!7RM{צSUㅶ_CS<X]T݃1}1OZU,̮I9W [z=^-u)6B /v O]J/,_v`,0J?kARVuawB'F+t^ʅ|9f'{5]AJELWum#q!pZ֮kj3Zx흓jZl~yt2@2^F_ ~+jfT;[Y<8%qpy5o ǭxKՅձyTlutBGvH,젻ҵ (&#[}xN['mJݱrklӖ4 Wo՞Y擥#\jv_c2K@lNY"1+:n,n&rH\ JھǪjy&G!֏'}]۶۹*.m"ƑM@$c^mkjv ?qT;)lev ^ZO]?iy5_YO 1:sHn|ۄeM|.'zl ЖD  KOk澻 i QuE:ńY<\T]h{gUBQ..<26>P0)(+C@Ԥ=B IPĿk>ȼ?9eZ5$z`у/SrE:MwLUŐ ֊pX$SccʑH^majqʮn3V])`3I\vzETiF[jt>3ѴmPK{XpTY<渺(ZIsH0PVQEQ@Q@Q@Q@Q@Q@Q@nnxu=fif+` qHg) ܰw`9hkJ˖-g4- L&uM2 ",}GXTŏ4sn]pO3ΟTѼ&r1+[\`VVBާuwa[xyDj 0O~د?EQݲ :Sg#=&="{x3yǕci[j)$jަE au{KYkͿxb&x EkM[MumaAyMkeQ]m-\%oQmݹ~`6}3RI V>Lz3[!gѹ$8xʶ8+kiw$ jp+6sg|0nZo4u-KXB/!X?wSRjMWݢYެ ^Փ ⣪^ݴ pđ^ߞEsWqxK uIJ5: <~@yQ@Q@Q@\i2k)e8{S,.><0dMǹ?q5x_^OD@ M5ߗ)Z:֍ԇYw2/o3p?U{"@Fdcwx=B@m0@=kceĒ31@q}kf~m?}j3UM#N&3~?Z/.Yn T.q~ýY纳7bIggW CS\XKҋ{Q{YY|2oTV UC=V hڮp.YgpSInN6j_Zk/\(r9]1/H53ĒjWv3Y Gn a^wEJV\";_~'Ѽ=}ɧMop }ޤ}}o[iԼ;k{mڬh-kY%Nr:SM=Yר~fD]>HB.A$䃎7ּw JZ12PsAݎw`cߏ[KQ@Q@Q@UJ58&e R$BV &}8ϱj51$YfXu>cwIiku7E4[lK y"Tuǵz/g>u֢t{Е?K 9օM^7J̞NY;!Ldvd^௉W^<ѵY!}UcO;8ˆG^8 p;Ӯ#B"u)riX@s18<@KxrxAӡdiVeOr3 (Y|=.g֗c3l ǜ}pGP!լIG'#8+9~%xhȹ+ztIeOi|I&nu|蠏nv>KO_mEz͆xRy}>ec=(c_ W?.q^ݫ$v@,Jy 8q9'܎Zů"[As.ZH&ܾK ؂`93Of1 F3Wџt/rCfC Xr=*ùmKht"3v8&JOZde>O) u?Iz6~o<΍GF*=kIQ@Q@Q@Q@Q@Q@Q@Q@Cb=?JL^m>((]p[m%^.].hD!hpsձ\8aV {]ס9֕}l͏,uȅ3-BF1py$^k4ך-Y_ܹdasZ]CDWc $y7^ wq 2,PD(vY_ڞON v K ֵKCmG0?gn#ďO_x_f<֧;dyR0{ ߆j=l-qjSPt#38S}Gz/I9ᙴ?Yx }--'N[y-dMeUA'Z?ۍgMMr8`N[JZ].oU] HGt`rT}F$qҼ/]Kg9g&goj1isGs 9 +JW$v{^>񥮅q4VR{`WZ>ypKaPPpG-G7f}Jwp )U3&O(`QEnx\MkLYG{enmozâ((((((((((((((((fotoxx-20.08/images/menu-summary.jpg000066400000000000000000006250431362435004500174740ustar00rootroot00000000000000JFIFHH&ExifMM*bj(1ri%HHgnome-screenshot0231.Ơ0100Fotoxx:sharpen| Fotoxx:resize| Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1197 1163 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?ES.d..!@F {@Y|g:@zZ) dx]-^Ei6!l( )`=.#8p3-q@e=4RdQPIz]?QRGu'(sPhi)E:MÞh=hhȣpr㨣#ր}Y=G \!9={VTxrz'jH!99 Ў5vMfkq+K)SNkI}E(9@ހAppSKE.FqZ)7Z2=hhܾ)7Z7ZZ) KmJ9.-F$df.QIzѸzIzHRnSUKJ/.# =9߃@(Qنd\}'UpKzJg:+喿bo4wDw)k\0=4RdR(=h3@ E&df@ E&dgҍրM84 )7Z7/s--NT{dHQ Uor9#֍րhu= C@ EQEQEVw.vSOZ5`z 4 [?C־F#a*H5? Dʈ|jě+.\$*&da d88`==9J[T|ׅDž>EԴؒvzQxSWND" >uFi..4^'rHLg i pІ}rU|z-W:)'? ]Fxk[Ux؍ {:_>{/k3,I .Uz8zg=oZjſ[Z쵶.'55ʷjZ:ƣosp~v`I$׫Bͮ_1}6n&(5HGrڜ('$⳯cZ46Z-S #=^ {Ӊm[u3Roks^]N$3q}?*&vMk\[ ysl+#.5igfcyck_x&E AhG~zY1ew/+lBO_׵tzm3s\HH@[N+Z[r]Qgxa|Dӵ?h6vQn]}r[qbOsS״ Xk^'lnaK2; )g} x[Pi >bgdlC?Z Т𥗇O$1EX,x5ؽ u +X}B]FTVĸ,>#=덲MY}a|k961}8{5i[F6QUQm!c6hړ ӊ}E}崽[i^*5YY6ZwSbʿ6qjfs.R8{vv_-"@ukSO`vEhcg&Rk9Dkl<'V:=G@?2^X]>4#@4dڇ}O[uXJBrdCY}HYa8vGiZkrrǾxlmKvfc`sk1w84-jtVleP^xUHz猴{ c^##E\z,҆ۻ(Gx;q_)WRgIom Ѿx X1H}kt]:.m!H9R#|vWc ,<$:ʽTlu{-ar E2VdP셝bU(P`;[⋍O/=7ZlgI.35HOA튖(e<9KR&i&7 o*#O6S"5hY)ޝ:ҬdƹK6^w(IzZ Z}Z[H%`;Ik&SQӭ!"Klu4uxņiot]6<_e+HI|Oď;+א<- C; aקQֽ[ޓƳSό>ߦzTCk9VIgn%Y㎽|0cKk) w/;4;9qKዽZ 4. "X):g-\[JdUܮ=WHWqiD; RbfV}\:`wGqb?.㯅.dQ36IIP ׹C@Կmm>+žА(|sStWlKݱfuQbд߱n}6nc2_h-D0[1̠rEE/ڝ6sH ՘olN{W%<7]^þ&/8P7&g WZs Ή$2.GPC؊|'ipvaj.d(|r(?.xQlt{zn Nvuy}T^d HU=7ᗇu^Vx㺞[ PCǭ{5<9 MpT ]M M]%tjl#X6֎&=\&%d}H^ȒqK izҌ чs 04? 6ȿcOAk*gmH FTZυ]jeVҬofAyG@aZ4/~#NH4x. ^yv8vޠmo^*LI5_5×e,Ҝ޽[Q𮇩[:Ma0 K Zc=)!VXOcyD0kwm7WRB/n#Sy `;uk/>5MRxt})q*ȭpXGJ_il}@y;kH6heGq9<hAv+_xNk8^[0Y錌1&Zi4Vwˣ5!z۰nlRTر*U}SDӒ8Efۭ_N )x*귰&ku-~'UU$ZbxVrDv/|^dNKks#[TXlcW >t=7E>λ7zh[9υ!ӎ=w5F-0U VŹ-ܶs YN|rAhX42c+gRӭu;7-⸶O9MO[hwm}b[mFUxIQFdlq8 A6^M,0oy>[HxWjz%akwj +*ZšcFL`0ڀ 6՜^ٍ$m{Y S/_L{n1*^5iX % JF_A*/6O%)vǩaO]к{P5-^MSR2hiHRDĸcK#IЉAr a\x@G-ttk(PrA t=?X-4PG`8Qauj7z3Y>P3cڹ/Q ',nuB4|xwàvvmS%Dv:ӭ|)ZjO߹, ,zؤ3Ō4VV9R~T#?x{Y.Oi/sv/t3L$Mw{ai5Ƶkj_w n\.O9wڇ,uVK-1%[x!՜(,OlquVV2\K$^dY䐗kWjX޹+Lh> #FflxξS$ 6E{,5kC$2ۀ]x54Z pEajٿl1v<=vk+ @B$}+k׾^&je4_'cGty!ЧK̓X)R6&jphu-+ʖ&)Rs|]#MkDQ)**}kUwjxڈp*|wZ;)VK(#NげN=h3ǺοcMC+Fϳj_xƖz.tPٮrn~eN~NkuoZK=M10 pǥf_xM5:YD:vV߻OP's^:񞧧" UbmvY#@\@3&u81G=&) ?*赏6Z%ze$%[LfEc1\`4u8t2hPuĂ[PWQ_-[]a-Ƨ^5"98Xj!n_ [u z+XL؅C޽7~Մֲ;M3O4 I]$ȼu]&5žD48wLWZ9<V0}t^H/$HouX{;8RZ|6RQQ./uy6vJ@9_ɥ_i}ms@S`b8/kzu{g+ir^e,v0*ǜ0Ai,լ gu%A6bI,NC]V֫j_futC~çɪsZsJ|G@9ڧ#җAg"6ͼ@Kwok%M"]*K9ww- :z .忴5X뙼ŻRJeC=֡s-MVTWV_FNkWL(2*mK{fVb,k9n|0ʠ'5xZ wM+n)Y0#p$q]/luMWK0%Ψ0Hf,w:EڤlO \("1$sE1}kX.^ I0C('M˛{YեkU4Fnsds9[EMqk:Ɲ[N&sq*΍pndwD8; /..iD $͈. $mj?Z%մ΍wye=iӱ rAeb#~i5mR*[)ɳgOU߆j֐@ ky/c==A\K:[Y,t&;py2mo_n766i.&F$8>|:=[m1LdP1|HZZ?D;?+p؈3i;H満H觀V? sXl4h.ct+gҵ}FfnO#@*ŝ^mlWQY "-I޹Oh+ 2pZOLP]=7՗4QNn#(ЄB),N2 tu+@] i6r8{w[6estZKhׁDi,w=U.ţC .->#+z4}욞ͯgEA 븱}]$> Ft=å\,73.p1<7~ffKˋD0  Z7`ҾEo:z6)~Қ425(=8s]7h,TD09=P,?^K$pwB5J4TSNM~a/&oay\ ? WV8TmrSϷ}:VopWt GT-"y 5^8}kE톽{:R9,<܅nqQizMl,:aaqD%<xh)>ǠxmƵi(ÿϵO?X 3ϩD-Ml+4ZY[ۤW~fm ϽhxOݚj KFU R˜լ>~W-;3{kdw2%),N:ck8ʎ{QxS}'wmĹ\Su+umOP⍣B[_|+-孢k-qu%zcK^=m?bmħן]x[QȵK㯭miwm:k!'E0CSϔ~Ww7.f jF+ĝYf(\ wb I't{'^u_>Ҽ ?Xz-~4%EP͐s?-B`3MЎxƾZZb!9ۜgx&Pީty9lXPH vf|KOGA֓UkAi r"䐎qL6/cG RG68;~uX6j Rrqz4ӯ4kCG{GH@bti2"nuJ]RI2C AFShqz{=IԡF2[#<:,KG}a&`g\6޿|qeV3iӊwqsX ad\!G#څ|5c "[R%Wg-zƖ>XQʯflc>P5km#_ \]2 $9ʖlbV^8קZ(& ~^jqZ[̐ u߆[EmXkVOGV=+jwxPb4yg]x yJ]]sTij{\k[z/vno2.5븂eM|[zLlKFP?</jֱc9B@ |R?oվ!sQjLY'ET`ZOk/^z ESxNxJ [E7R-d,X1i~#:.oX W*om^"k_z;Nƥ3`ǪTkA_Mns$2=@`ZyZmtj6;Wql}:UdEnt-R񿁭AK47 {cJ{=;H׆S7Jmu 3P/^x<{HlwZ?]m-O+mۊסxSEoJx l&&@H*Gub|H_¸X<8=й审@S`>9zocxM V1XC\Ŀ ]i}ۋ \j('gz=C^hv6R[UVrpa[2no|Y ᷒H-neg_@'4f{BYL;^Mr_C $O`p:,4;4Y=ķ(` j>8ޱu-[O7_|?[Z]H7,R7$gU>mlC\xҟ jj4  ^?iz&5?:IYBdh;Z,4KծⴵBC{םx/xL^Z0[H&u V2OjVQM.ڨ**N F;} ֚Gs0l,لRM"6ʃ#5=[@<+us^OQ a)wįG ͨ4B@r@ 湴yuΗw{Xp0TӮOZXIq4@Di%BOs~=5;ga4q d sƋl"nnIv1=Ld5Y]A}mͤ-$C¼OTѵ;]ߑu[D?c &\8=v^ Ñx{I1rAx6~}s*Mη/kWYۖ$,{ oxetj`s g5Rot{F0L$Vpk)u}"SNˈ‰r2]| C=$kl6kF7 "RVTwAk'vУ\ujQk:~5]^u*bj8+Ӿ%iZUݓўA[ ;]KRҬ%Ԯcnydl(:YkVGo1F?"g?P]^{3x֗ݽuSݗA`E+(ltRtsSa|IQb #3qH𔓘#-P2l9:v Yj^$Gܰet 1x[O|5ִFK˹F2G٪VTN{$fU?1QҮ޼{]Εx1r}m"pǵzw#h6:x +/t4<7yK6jK*Ǘ^=3X v0ڕr΢ @3+Эu3 mf%$yEl1=凇/ᮇj\éIonhφͭ٦!;AU薖7:1Ag0F#<%wWJo[9Ҥ[˦i{<q.ovbKmR[K{W0yEa'K-<{c:KU̸{ מ)VU|Ek%w8ơC跷?A$1?(}gJφ]^M7O'V*`W#Y^GkM>]F{ #m/EukhMᛛ)kEQ1V'&Eln._k\1rBy`wjמ"^ZciC%ڱ ϡc _iֺͬր40z:޼SFD(~UU0F;͢^oOwo%ޛmP <>64M;۽ g8䁞r'ĝ;z5 䵂0,-O5x~& $( c  yh3*E%7J2X~U6`YE{`jF;R ̄?Lφ+[ˋfh͝ݧ#y}oT1cp)䄶"r3Ey}{%dʚ$HX60 4-UWZٮ] dm=9j#֎j4qJxQ;2<6I)=,tG(((((oM|u!b7 L}-ǣh1yc ]߁-<9q6:LI,ޕ `HȬt>֋Qn$j`IQY> Ȋ G(S,H班tRS(xZ{Ӫ5h73/q43NlqӞO:sZk*@-p%vp~nm6CVP>ӽ[Q*џFx" ABOڮ:Nl OT`W_.vϱv0> }:VlIPmejʼnrrr~i? Fu E-P+Rt86~ԴnV ]IL(Ƿ"[A~Wj&c#WPi-\ȂUld\uoE |Męk}eW's.wOznڳnޢi@}˜[ۜpqT >f𬋝2=0U.z q99ޏ|䞂J]#48^ZFb2XHMRZҭ~Q\rK1n}xuЮ<5g{}}t4mR1tq&< jxM_=G4".Ԫ< 璵m`TUB!s\z/?X_X%9(vXHR28x t-К#,MغBI+}L\/Kf?)LfKW&$>R4  `FAȯK:nj7uE~j(6c^FԼ[q먼Im.472Do PyWuS {di }x?Kciko0=xG.iMsRۜ\D{y#ڷ="Ҡ#(ndU$;Ubq^=iwZKL{Y-V/޻ ی(+'ɩ6~]cNnՆ\BͿQJw>U $9!]vڥrb*?ykAyFX :-Dm4KkDRmXK1[LOZi}_iEvʑlxo|}ďluu޾8WZYO tkK_Ԏ'R HX F=i =#8o⻴p)T$:Ex¿hZ:k+;{(m%K'h$' qA>.2G h ԳѼ[٬,+ukv;Oխ[-ncKTY#G4_mKj6Z@\X[dҧnp2_8;xUï_/ȱw`=A"8 U(j|1'ndJ !U/GWm}%&sq7$S2oJ{fҵk Y"so},Z'ϗ#6q}}E!e_?x=xH@_lXv1>|1%Jn{ N{N4ktmf$@@n\g 5dݎr {@F)E&4DVMrnN|ݟ\?^EeQ @JwH6_ƣKd{X%t6`OAW YA+Fe71%rTCN^I#4yNdeP =z(ݤ7wP,u*?o*(DۑB )U(xjUO祥Ϝ0>W +X$h\*qEF,asZB҉LQqLP3a|VvѰ9bPAzvŅbX"P(9qӭZ(˥X)[+gF <}+/LeK3c^=+1t vc$ -B(9zӂ(# i~U=AvCՌ@ӛMo3u P\U( [mf{@Vp-ѲNV0OSӽ_6ꑠ P>i%c-ŗڊ_XݴfVqZ}VK\+tωpG-CC,PV9"9ᯉ0t[uqD iu9Tj:匶l)  9r2ֵMnARN6sv1OprswۏLSŬMZ͞6ck+݄G!pI8_TUW=UsеWWPKݐ$IX(1[p#N⴫cIe'/丆DX# 2<Wk4ӥً kcw$R8ȗ6OQBk!Kzw8/5gjV q7$B$اP=koľ-oKh,8ZBT3d&+μ#&Y[ZޥHʶߺ֗`\#Tgk^O/ b+ * .QKo*Xd,X"Kv]nΆN)GJ}+%ƔχO՟˲K Q _~'CJId7AN2Hk16f/Zy.J$`s>uW:GٴF}Wkq;[2cSsڝ^/|@uƁm M&l7?,]NP97͟''_Cn4 &vǷ\P z+ʬ%kţ5&mͷv2sZ_|?\˥jUѴ[X `Eyuu-[}aY?P89`ǚڞoM܉p G4tG>*֋mCQԧku_; d8^nmeN9@84WxQmR ?:~ʗ+UO:\:%ޭx%feb;h-\DCt*hZUmN7N$AoK!$*Wx_%΀~sjEqeT%0zݕ+zZχ0<=H5"Cn]w<㎹$V\\1 g=W=F]G. ڭՇZBh>ұW@\|JčZU*Be[AEa`Zzm~!tN;M>{m4ܴ w0;z,: V;r >iilq}[='T^g%Σ%X(cy\p6F}{R{Ǣo#)۳^\]Yok0Z=E `tu+>i!{ }l.zk>,Ԙ-Cg?Ki[Zf)x Ih`=j?7x " =WH^S\dj*PӬ㼆 -$A`>S-v=2Fu5H:]˽vxEzi!4 ).],]@PA'9j}w]|yt.MMbH #c Ҁ=~ Zx_A:K td!_x^;NQDgłN3ӽzE5;EдS"+7#Wԭ+-0NpJmJ@z{t s^F#kpn -䑜n~-ow/MI[߈Ռt*bV s0=*0@n'HR Rv< s_fhyB0o\ǂ|P#Ӯd6Ws͹p}#:⫝2IK[{w|/XC@Z&߼cUzAᇹ:_}|Ͽq>eKeMH|9|zSǚ#Le (ofb8$mUx]֖=A|>h.a@Oc4QEQEQEQEQEQEQEQEQEQEri﷑P ~K&s343R9#¨': 5qyZKEǔQYdRe}k.]6XLW$lR??^lFoEƃvVyJu׮'^SpS[-ufo'rc{ gz@:W*T5a ^OxVM[]yuc$Qh,8m^\uK]~M![ؤXZ"F8 `uBYS~S&s7 jN9!J?s[ċm?W emh-\Qy2}+^Q7w2ʋ,L-*ĭйPBsj>J-}};/>(ޓiyhpI -`v>^y}a=heC/ wjOqy[k%r' ~>#SKrqьdu\Wma ~ҵkw/.Ǘjc\oT027 YYH&TݞP=ҼW+@ 'mEM,G($m [qjy#ZK?نؘˌeAܜU߯5 km:xaKaj;3WO Ok쫛x>!T/(CEz[jgBhmsL1܃|Ackp^ n2@RnκTE)X #;=kK7|]]ȤG'a1מVeπ{}B K^J%1@sk(ot&Gg9S\;z\칗Ȋgu6s=oCN-4A8d}xP^Y8W{Y]nkj_e]3KjlcQ|9W777{7 >gd# 9ҝ> 糶$uhRdM #a?.Nxše֕-ŵ$on S~5x@5,n%7/IxYU\d9QGçU+s>Zo!g/mci9L sx(:nPzñ\kQ>VoQ@ -*y74OաY[H[wVp0]V^Q[eRp6R\)_J^}OQӮ(UE?,5)5UEwBS>f񎽱X|S2]"o6~ղ{WZ [)RNe>kK~ë zϗ.;ֳ^/o,HgKw1jҼ-Δ׈IY@6lUm_h`N&A M3gj k~!O[7\@а#ـ5kZ4].P%[ydʾ7MҘ'[1nHm۳zfeU<+ V_ Z.mt7evL< #c'EtVt9ɍ۹: o$P<9:S}AGM>aM.=nP38&/I{xoB8o/NP/=*3Eb.;=3T?|5}YZLx8$;'hr1Ұ=t)x#UQ渖bn%YQ:> .ˍZ{mZdG*J$mϔ"u_& 漉ċJm$CGRk=4]@Y^\' J)s{`sC/6j6[v<FE[: ayͫNѤRZ-¹պ,l̷Х $3T{eoY}yc [9P21B5-*B;\?:?oiVK0]h[55Uv2;vJ o%]!FǸ=+ᛍ_Hev/'M&6L$~Λ%=LdxߥcÍHB45WW|棢vm,VK à@rI"u\ȱʌʙp*֟Z@'3Y~%։A,2;xRA~nuuo_>bӣ^ ]F2# m{|C}5bǖU6Q;foT'Rj-ai|+3Pׇۤ'Wkk$'Am/`'|ZumH\$B9m4Wmum9fF*%}8|oE}øɼͽs!0@r< kiZKy{v^,3Lb'¹mt'bMVi=x 'c^LA@Ԯ.gwZ@|EP_Me[k9D+$HY q^@=E!iZxjM3Ev!6Yx"h<1&[y4yRS/.xxyEyvQn:^bJG͂K*m̾f}sGK~[PfjS$5X2ĩZ03@ֵ蚬wkZpt%2dޠu<9=-o^|/^n3^KIhUc˵7aZ@L,̻cb~\江ZNiM} ea$}+ڰ)0=09xm7]{yp76os;8sRCQ,nVt,B\!LKS7?$uBYp&>fzn5χĺ:_J֜qnF8aj7ެvX{JOK]|95eAm^ypf{g;W(̴osm a&nIyg!|lkb}Y@]i LPz=( QEQEQEQEQEQEQEQEQEQES_u!t^Mb9@ϖ޿CTlJE"7G*9W $`W |3Ӯ%fN^4;c붺:8pWQw 94U@ڞj}N",zc +O7j:(֟E :&kym|-ȓo ㍽=XIb@sN{!4AʢVbs ׫Aֆ- P7pXWwKB]˲ Fuj|),!?^Hh[ܗxcSmsG-{ 1o(دv"TX}g@MQ u9N@k)#E׬n-却;^šHc?Z(V*socf.6[+[p=y xDZi׺9eld+ 8eV%>:Ɵa=OQ,n^ޮxv$@]Vak߄n/ttxZCp%V3@yg%w P]:[xg2U]~ԵP t YY#O5P_o QzC*g-<(.< 5+d]';ΕgiuYΘidP"ژ ɯ@=)pu^ `+0q7<ψ@--YuxxlneYzgZ΍kVW\\ ?FҬtk%Ҭa0 p;x>$3#H;X#K>F*NI翧"AkYı48"Yz)[`z?un/.|=A|A ZL xI{qyVoan"8#WcB5=b4m>"]6?:)ʧp+;X}GWT`-ed\F0*pt]tyGZ;9d#lrHZd^֡-ė7巌_?z2E:xIqvI)|;gp5۶O 5ҳ4KѼ>ۥ0DNN:5iQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQA8QM;I)\Q"[8[ ;QMV 2) (/ze[YBSQrECos1 E *ppE48pEeZMU 9;\?gi i}ϵ E0Sլ/lsfʳt,2(Z IAwn_iĚuJeݳ1=oQQFHn)QLSԵ{=4 ɄhAA@視<Mϵ Z.BJ)zQPh(SKKbil 8QM37 RzRM.jւCE0Bw1E0H iP2MJ*Zj|7|ur80Үo&E40w6O &9:c R)ǡ(Sw 7 uY-TE UtrV7COMYL`ccp4覇RZ)b4)`0jjOvrco0Wa(QEQEQEQEQEQEQEQEQEQESdL[`?xBxcTaTXJĶKjg~TӱkTFu>m7Ry0>4v8s+*bYoAFhůSJ.w5k:("LHq݀'$@9׋hmĖsCqةᇵr;-L!aN2$s_Ok/:)O/soxoOa:^[I>F_c]2Yd.#z"i3g}VrJ3"+IYkO$NFմiN; ν)SZm5{+Aʎ8v'2TfCqisk.r3Si2>o}x ,T-FBzg=ovNg/hZ֦B.cn!IL{w[1s\iemp͔ǘcV4 tJ+eW-Wʌm3 kҼ9cIB n:\Um0ϵrb'NQ\[nk-k6mI ܅\V9?C^q.+ӵYUk&b^fh-{Hap:cڳ|m>.mÐ0 4WC5H5 X]),VX^&C1uBf×6֚Φϡr7EM85:w<9aՕErewsMj]{Kdk8d7Up(Sɭ^eO[.4L6aqAsY~|[fw4oun{7#^֞Om#l6ˏD qVrkEt36st:#iPsJ%DـK u5:v[6='ɨ\E%t"[Ѭ5:K V.m$hpH9-<4IEM'%OK߇eڍO u xX.^xQ [)Y H'jW:i?Kn?ՂHeyl+(\/*0{~'~VS&Hc:s"Ǡ-j+ovW4ѪK%763,팊wBugulHm;-X.aI#E0@>[jVmxKMŚu7f=OdV/ u5;h#inSel3umMO 0FK=B{"{ew|#4ÃƷ1z}z-ᷲW\dˁw躶;I%ޙKrq"˰#/V7ykgw/n㩉z/W%5텅x~an]jFQ䃔1ϵ%ny>v}v/5[y/-9"\sdGŭܖ($,H1N:Ӽ%ˣ_dTB"G'8L<=h9ʤ'kZ LI!c;r6qWx_QWf\Vi+犃W/}BKGIYˁ>gAQ0C 0? 5Zi"QmH@rʭٷ9O>M,)m)`&27G9z| O,,g-yZ_-lo-fފ~Ct:Ố&䎤tC|=6 OHm*r}t=O,RKN02z:^)"5oimnL-cxgb}KX~Kh=ꭌ?zdIy3?N*tdҡJV t nyޣy}O[G֖Hc"=p {}icO}v~%m#ё<-ϸ˞Z@_2뿝ªj>mMm"G*`/[7yլ<-hCIk5$rM'Fmqgiz>fL0F휯_zKxnOҭ,7~Ю$Ѯ4w-_r4y?C+{C֥{rڀWo(:g޻ڝiޛ0Kx&vXgx{fH<;kCgj>@3\dʬjUa-n6}mջ-4j3OS[RWPxf4|C%֦Gv71Wr¯½M=継imh"b9T#G a6}x'8 -dyHU,73G%˹Y=OA[Ʃom PbNAqf +Hu(fL)I"i~.Xugy$pn.NoӼ/Hm4J, A,źd^hvk%kHed`8+7Q7-[JI22tixgM}K⤚+i6MBf%o}^G oIs*bq`^e%P"IA})n;[IX.G(HGa<#^EݕĺxK{-E-o.,ɳ#5|O{F %R碞ko+ 3g9ǮyCÚNYj7QM{dso+cP#ήu?>u. 3OE7ܰ 3X.|:혻7OdV3qUXd2?ZOx;A WƝ 9"E&++I1[yJz:PL4S_[Sht~6G$Z)`E,unK;m8] .Ivp@ztЧPm:MBQ=#>c}ׁ<7wckgq$ *Q:$6Cmfo G+gWgPibn#yY U:G r&đs^ۥivzMvzt+yڋd汤/THk`!^뎙n$r^Ե\SOB!2F{fOG<2n/J OH/4d `R^ivwVP$Z9a1) 40GhMڎCuq^Hg ԑ_:\Hi#i/"^#9+Ԯ|lbMcnGʳ|Mᛁj6-ԝe h9oҦ=J5kj:n}!60|=I$BWԬ;G,P;gJ)i6|X,b&xODn`մf[LUmo㭨TޅK2܉';a+o`TڏOL rM1tպI ܹS4NBxf]* a c{Sj^ iimyۼ61om{MAW >d38R^ʲV^}&}ZV4AkFxPYvF/W6,۾u#aTz}?CEke%'SX;5՘jT*o[&b43{ZE@bq^Y&ۓ OE>?Vg6n|!9 ׽9xn ?:Jƌ ;ԴtXƾ$կ3Oúg(ܾVF}_'gM[ܘ_zP7`?Z(o.$ZFc=֝.Gc9#pZs.7$ڥso źZYSѨ H5BG9c]+V-4MZDC#'2흵.gWRhijR~F0sڬY|4[fMSN2쀏2BXҾXzuԭy%b AYdOιN{K OͻvAL^d&+ B>O[&7jk?nX\WV/4e5i F2)!m~-B;D.2n9|{uX<9f5 @֞vҥϦGֺ-[zڤ\Cqysr@0H+sm?U{+MW2$ad9``>]h^+Kf{}[f"b0r:՝3*G)mu`EZJm7^9}J&.UUKOϲ_YKkڞ*Es #nr.Mi֣h] SM'V|5kUS]e\&WnBi݌+vđZ\>8_XVoocn#@e7`gis^;þ6ӠD+ilυʁ8&CBl s#8`7NvtgW{w i5qb)2ui4I{OÞ5M"iG6- ̎FȪ2xD!-r9Zg^nGTT*)Yv@U<ԖtHu[)ӵhIi\n7 Wϫ?-Z[Y^\F7n$\=|[Cioo" eJ|-OiKgsjE$U@/1ڲ?]dtծZܪ2}i_>]&;KmRKi](uRqYZ?ĽFOi}Kh^R߀T#_jmsHwziO0ѳ>Q\X]h-JG[ APOmD𥝱,گX9%B ޶1>x[Vq` ׇgS4]^U]ǰI1^UP}~|7Oq-wXܠjdZ8kk:zxMNO{G.Ta> #Q*acIۃv="x%E7VCGJDJ"}%~K^q?4Ż6c4ҹpZE)HJ@ԼCgjh3C`$֢D˵Os[W7дVNS)dm R Bw{fEUԬ..`i$Ȍ @#r u=oRψ. xX-<\rpwSShαklyBH^sc8byeuHNzޙ#>}ovJ zW_*,hsM|vgtX s֓C5;_xN+m~h ;Ť 0ry\Ru4ئhđH)?s5sx/SC pZFWm,"mŵ<ο>KIF桩Yi1[#FcsV1^ox kڶp(60ZLpquVŗG7Z&s99J}ZVSe OwlБ5?~3<^0 oqUMi6/RS#ms7~uLjuHZMuy"A(;pzѸz?Q4  d0pGڹIB&ӗS[`bZKWa6zJY@ G54CYi./L !P豟-Mv.s[siçMy x]7r={.E/X5$Oikk, !Y @=N:,<Z5VظV U,p۲($ zLjMh^X+$7wVKlɅ8#5/OǟhZDpY-fy71P74vZƝiuk"[`=W6s=$=A,$W6~;Ӛ{{^e+UMkZܞVJvR[Dfr\ŏ1)-Az̚zVu ʆT Ҭd?Z|cộuO.0op:/$קh6n4542Z(W'ю 7:r9wG"eNzu\#Ҽ×߆>jz]c # F27#jCj:w;5Б[m;$dy3 4w&au1aiv.md ̿{q] ?Yn]-Yy`DgdK+ĵOx+RMdn-ոI2 S5xl1>欰Glf񡁱 EpTҜ=_a5m$-r\,:a߈סx-u?#B6s_j=hz W>+uGE{x- he!9\Ux6Z8Vm;wc_fߌ8]Ago% #*Rkta*ȏuJ9p(^wCĝS_h_HѴ /E"k2J,{ҵdOy%OCo/Z 7mmm>^Zƾ7M&t1qWl|_x ĺ~.z]]zcsڽ0TcyO¯wZe'yC",{Gl{ֽY־ ו{#8)kSZ˨L(x-F@NvK ^^;=WN <@j#Ʋؗi '޺|;ƵoTԓfk0~y5voKE;,3JXq9<$k#M/q73c-_⺶i!Եsksx$^[lpd 8LJ+_cyO3Juf$]lfVTt0 ^-]gJ%PU #+cڿ.|jV[aCJ@FO&y lu>ѬmoTK FE|N? T^Ks|~TApN2uS'l[]^!QX:ֳ<#}'_,- =]Y9OsEF~OCӴS`%<ʲyּoSQIqE|@Q2H6+>!&]96PL*!hM`25wCMoo6_jVyFA5]Esд iWwmFꔾ(Ɨ0t! FzM] oHGJ"cr{Au-"K+&dQ3"ĥx)AbIy;hv:,%Y@ʝr96'WS1%N Arǚ{s[^k ]][\ Ր-P0>X>񎯤kNN=ޙ%ھ-2iYAI\񜏥=hVI-Xp$dÃoQ3/=kzEZ۵7F1 ds?A~Boog[h؟>MU{@p7m~O(p#{8:mݙd*4$_CH#`^|NmѼ=o`7: S䎃ִ|="-bCO ,g挍9O[Cs^Ӽ9$=< 7RMq ~x+G-W:S%@o;OQ ~0֭<9uhB~֊ n0}zV!ԭ_k,QEcȳs&A1I;:/Ag/-#ñ$OUq?Nxn"|f+yaxTu UtjOwLJƝڋāU2sr;LQ ;761)`i/cڞΉ<,֒]_/,@KxWk>=k&loQ#,_z[k@L6G|$䑊v2։mafM^!zv;/\]xj/TcqG%B%Wc|@T[cm%/r*Xuܔ;KFKb\)ϯW=W6o5ˬ'yeAR9OoJ<+ tGXlm5V]Zg14V7vj]9[J*Iźκ_n'nqI+[|<`ӾƐћwG>31$gĞAysvc9\PGokkw:a}i6+0F==&L5@cY^SH[,;}E0:S.om=Hs"999Ywo, i2֋0:ug ׇnk Uqf!UGԐ+~$˾|3mbC'ךpDs|2Ժ_9L|q;sj~ާq{m?r'\2{FH [mAnd^łc֏CuEJ/i`6sBWPq%)![20Pp}OY#ZEa(v|[\hWڬmK@}!&$06i²%ؙpJȠ5-`GC a(AbǠOd63MS)'y} W5YtK  h{E`wܠHʒgzӵ:v^A$hEڤ)0QTB%tyHiEԐUHAR5!s®ztךK>wl-) Ax#{}sF ;PEk#40Vɥp:iri?kC*]8m s 趚&+=%C9V 9WY|_[J4땶oզC!s>% ^eזa`4v/WKؤfdeY?CUyjFեm[uvz+2Hx<;u%ѴHsnVlna汫hEDmu]I 6rq#Ҵt>+S'$kzXQN f8>ڹ?cִdk%+mrgU56j$=y# x,dn q=i>=#Oѭ,+te*ӒĂT``vⱼA-]~vPݕ Z\p:FkSJu>r:V>8t ?7V4oh5ݍń2%31 9 騠~zUfe+s]]^oz&$hځ dHv)8WcEsm#M)>˥22*Fqߩ#zI#i٣Bp2k<5;[;7+MMʌPse"=BHC\12eH{W(|?akkoko:Em{Cc'=9N6ug%;z>Y[9_º(_Z'd7[L#;eW}Eg|R4H9npNJIg޽ր34TFyzµiQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEPh+ZAym-k,2)WF՗ xt❃ȱb(1XV5^N5+-ܻjޢ fQExskZG\37*F3hg7}{k1l-|ݞ~M,5h"lMYfD)FXr Qg4GH4m}sks;>%^ 'gSpuQܬP'OjlP>̞+̚Z^IK"b`pH8jk65ֽjjqGӄ) ?x^6V2-.A.#a#~(-ω^:Iknl P*j0)/,([ki AvAJP\9eK[M5֚,ȹR0'YšONƋÜ.B潓hhh}kú~/s)Ǩ] k2C$7qZ+%q$צcwQ`e׼CVl+58c^-ҫ? Kmb;z=>kM&<9zQF)\_ hzt:h[,_1A4y`կx[RA֣a xWgHP|>Wq-Ht*Fc%C/<sjGՖ4{ye67G90>[E.BUok>\cB699: ZqJI8bu]%=z P((D>xc6:g'lv}>SB4.'ܶ[3kWbR<ZVzpk^?x"QXΙ)yBN!|ӊ 6ӣZHz|Isz$r!e8#ڶ|gS>+ˬXbmUT ?,ۆ+B89rxVmi],FB ʃ\ޥk^tmKw,RŧlXo;#M#qFgmVV]yOcD`9޷dɥJџBuc v+ֻl MO Mo\O`BFG>CP 7:fHg$ Ќ}=6Ѵ{ҲKSłY@tQͻG6ᾦSHm|?\Z5yr )nWm׭E01'=@{_$wv~ [j^7AlB&gP}8HW`QJ1~j5vMM-pgxÉKzM껌n[t`Sz6 W-u)Ţ19ٿk2kmIuMVc+!oN\^ bǜZx[-N\ln 0g898|wG5Gz4"yY A'w,0>GAfvm{iEx@,~X 'ⴇJ@REPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPAE6C$P߭漇wEC'~;de\1xgygi}owyO m:J co,~SwTUyH-dV"' o ] )?N(bյj>8 $Q;RN ry\ Ғ&5QɀJ@b0WF/ \*lUQCHt9'{5\n@랼 GoEsk y@(]C>tƐt <΋c+@(߾X|G6;i#i#_ }MYo4rpJ0aR\Cl4qxs+ԼX-%X_in%ݤbd]F?Czێ&Y^K^h $!Xm8'muʓo*wH#EP*K/ *OZgM`[Ayo;2q~G\RA{kq#GR8*  TUXm/'@[Ky&0q J:,? EU/!mAHdm VX-t!H P i\k[ _I 9ETH+eo-o$\pH7~Tf-syR\BFpQ/oBcwN7*IX)Vody hZ@26JWWhkf|E“?ZKJ$Tޘ({ju 8J[kHna@QXwyc2an rzfh:Ώi6.bYPТ(((((((~r_ Hz_q^u+}^Ὂh7qZ]d&ISxF*!}lZ͋۬Te5Q_#Mi!w+&q`1ֵңBVp`ص$ˁIDV(=>/wr=L bѮvF[0^9co#ZVhK{#ųiז725F"L0~{Vĺ_4ox:}[L]$X4fy5`tJGcb8ߊkm,X Q㰷d!r[cCbog䁌Bc] .V6&aQY1.#q{;oEӵ';A,~R42|?P ~%PS= <ך@`pq&:I/Ql%G ghZVf^NhJmr=W-\Ea6 feV@ߥ{~-iE$Q 1!Jr;Ti[yI]q^FW|j-V == d4?g |9:nUѵ?[]2n Rd^횾FNhN㓺ͯDŽ~Oy+pj3ƯE( `Z4şS`$6n`)GLc(z_^,'{9lqVR?.GvREmfxUKxՃ_<7>8?44_Oec'v0.%F4ۿ$$l0ztSt$9QTNh扏 8ROZb*}N_-Fd-vU|=mqB&y|/ʿputgl`ԭl&3B~ђAl!swޑ}jnmfmavV<dxr_xN,'ho## 4o]'5͵~mb!#3R |0gcXy[I#,w6#˪h1\jpX7_E̒!U[I\uUA9*7v+ުΖLm#UyJZKmrh3YZ>mcᯇv5iuek Ǘ]t(Qjh8"q~kC}fo [kKvFC3,?=+{C͖]t+[D>jlmk[A3Mo"mgtz8F]^Ipw8Z\?t&i`0dePGn;WF;c"jp23֞8_eX+'6),u˛UZHV6^¾@aNn=VdW[ycVels1Zfhun-pK\hBGwA8ǥ}jtH u9ٜnN%b _ }Q֒],E hR %[ų}/V$Md @Wp?f^C_ x_BH_~Sz++燮4boHQ~L9n?ʙ=7<7s_Zqm-mk$BBO}%m.HpxKg~Cq{^xH~:sk&y 8F7 xAđ\Ehssd2X<ꚍo$*ęc&Uk#b-R(Y}!bU̙@y]* uKk]ݴd+JN;Yg&ljGjh#J s|KaugO%z yJojO'g;QxjewM7ʸ{W:ԪēcWBT '௷C(Y8-g}#RI"c6iw i Xkv\xjMjz1(3"52O#il̲|H7oi#Nӯyl(02SpO]' h4-IMcIDw6Vum\R:ƇWx*l=d 9jG Y95ހ@ڰaƔ#lu{ɰ#=zmщ.^} PJ[)Dwc9|,b&i5G@ct8I5ZE gz[RP|R l8?EwjT^7+|B6I܍`YX'ɏ+߇ kAC,;ŋ;c$߁L/ޭelb;ev1p(!e}oq"zd6I m3;n9+ ?b79kk)aqU<9XxwKM?JɶR%X䜚:{ƖuwYXƨJ=ɵ;W akuaqh2C ˕1_H7A'8$&x'@Q@Q@Q@Q@Q@Q@5S#l t9/Dc_/S^oiiz"g_\LZߌ?Uj!Uμ\JvmGj4@Tw;o?*Z4\ ֣#G;+Cm]+ힳscWӣY- yV'܎uu-yW-JO@cO8 C^YzWWwֱqdKwl6=72ͮm>Q=sgkDMoQ >$dӯd8"*F2sҴ|IkQ"-c!vJIdtDžo/dmdC,K : v-qϥ$]&i$6ڎiQ5E}\*_K5Mi#6piz.ec[ebRp 1cԓYݮnf;y8BNp| aswjT 9= =z&5+xi_pQg+Ү~f-VM.ΛƗ 9x6wsi*|1\/Û&xy{u`'O{iO-cvLe_MEEnIMg tYn.V\_z RWOkGY|g4"ǿ o%*2_ :DƤ/c)g]ya>~m*<5qR\x ×&y=Cs Q:]4BZR46G1*/bM}o-ʇ[E'8'O_xWGa Jv\$T>no/n,bkD >bƟQ-|Qwk]'S=YCrۉ8⵵?k>{N^cF`߁ׅo s ѷ8;Fp g=sY&}޽xsIKLQ/LmցwxNjr\ye|gssgx/ ]x/p0Fi|r@n+ݼWiR0EcGM=1䰈Y<@۷uѵؗC? 4:ެ,4fVL7(d*m棥ך}/..dPђ +`uK@n!Fw:)'Q[;K *ͅ s懸--QcZ-ssXY̤i^-{_Kyu,p@G,`%8F_ݴ{s  6`s3b (&3qbAc"g89'#4^ iCށq}KV?٤*#)Ҫ Շiɒ ,J$:*ˎ:רi3%-#ǻ'k0 G|9wՆm D(pGN=v H/M E’_-qR>4}2k(dRŊI='~Xmo-o| nA4C_ ,'ҵKn/c$) ˑosoo5K=J9/m Xa v kt=""Xs) n5t^jPAn."hvv֟v%tS']qml-!ҡ[k p\'ֶF[[oȅ~D8?mxTMeO=qks,RF3A7ti~7+ԑi#Gt^݁K,p{`ӡ+Y ~Y1~Б|lkڿDq>Cm\}C`^2l1c<]~U[ hRBKxI';}xWtDOgMkm 70n`oFzcw7 Qv/'9g i|5jOC-ېZL#Mni|7pFbx8eWƉ7oE_&y߹cch'բ/-tV)"ȋ1ֽ{L1XDl1!Ke9>X+lxNM9Me GKwϱr0VjzυO;hRn)< |tCš2i aǒCɸ6ϨY:ګMk_<giH1;q>.?Kͯy&5_{zWs lJmbT :;^u1XiPAxYdFop:U xv Z[,,\K4,;do`GjZ<_j|[}AcmФ$dt jx_n9g(PH<gؿëMS+K;KP]]Bv0OֺO鶗EP Xr6: 0*{yk~"M׈nt bpc$c⳯]JQ֮:o%Ӣ@*v=7 : C44* lFbKA$fb@P9=h]x^OhV; -$bOWz;U*rT`3 )<3bd ?tpZolAG(EE\(({::=_KV˧6)O0#?(/oQ 2\O &X|=kѭɐM t#CywwW%pl9ql5ԡԯ"`FO)^8j1@bQ[l#lj<%ɭ&hg֪Y6Z6LݫжB{PxT&(X:5+!_jVNiop-@ [9# һ xvZ]CIuu-LAgCx~<:{k^q''UH}+ĺz{kɥw?f M Ysi^~'U 0ֽ&ᯄ쮣m9X`IISx]^> 0; 7u9'zַۛE.gior#(_y}WּOu>!Tmw-RK>s1z;/m#Kuy'1/^+ݧZhZ>,v|9^3Mh$S]zv5Q}:3GO_-bM+6cyMsՆAP4)]i:֠}ݚYE2@]ᆽo.iyjo{gp#ufO62*pqT| ; Uhigicح Kaw@']hKbB@ʹأ>xVW{+ `1xֽ+y $E(!X|>ńwmۢ]aIf @m{[Zij\*I,E˳sTVxSM[Oh̥dGb*5<=oE)#bXܻqOT Kt >MjsqH \h Se <3ܪeR߼:ٹus^ ⛝"="# 0U[˾~KOH\_&NNF}i5xsYSҠxFKtgmG};^WׯtQyq4u5/ծ.+կl@yA8C2N-F+8(>saƑZ461!6w7^4R՛JsjZEFJZ(((((()dKA@qa[DK_+kKiW3ۛx-p㛿EnU-,qzIP 3!M:Ъra(ӇJ1KZQYΟm3Es{k*򪑟&F>)ѕdvG2۱~ҶC)ϴdJXC $&E01wn4-ܟj7}֘~t<QHi QMqQMɣw:j6u8!p2NGĺRh @4V>=+Yp]#l7#b@6R% p]NLk-.8P%CH 碏s@j\dPM-F(Swz^MX= Ǒ1X0o'l[9< '~ UZӮb9B6@`pGP)ɠQM'ZZ)lQJ:n7s0E0743)\\/. VIao嬀0 Z:fe+P,NcsdA_4riIZZ)7u1րE7wF9:nn;ѻEuU즻8mR#*Ri\ TTQ[<C*GS8"SR /Ei{[A{pq.;_{DGN cX{˫KK!g- ך=<]9{Y#Q`y`SZ91|e#89q\&&Kr&RE`ˋStځT]c͎ʏ #RN+tX/r-9kv 'ł{ fR[.U DZib,dpfsy5:ԔυRW=\t抎XRD ԕ^!hk?iWuڧ?B[ٵ̭Cf?0SɮZ[}ބJcmuZ+XZg=+=n 4A&&1CI u q=n4-Z"kwO,نquk~Pnxsc”(ͫ?Pwk<_&_k7Vx-[C L2|ssf-CæUmm+Ʌpe? kk" lAUsZ"OGcYMpGLٻhuԟx5:&LP:I&B+Ѵ'֮L&Z}ɔsk>6|:K#7`]N,,f.H3\LJP;[ciФqV##=qs7~*Gѭ4s7:P?8m¦ZxgN'>Q% 3}}ֲnxn{[8~0ѾcNOZ(4kXjڈT9@qT5Ƶd[\if]; I`]O hYH s˶ 9ڬadّċySsGugɥ}:\Q]*l >%Wqkvf}Gf !@#>;,qQEoh~"!E$Vv#4=Մ u˿x.Qɺ@\Hc\4kPx4׎աR=^O攚vh#8,ry>M|#-d-k,~v8n톶Zk: ,QHge5AxQqTgvv/<@kw1G-D?!:Mb My F'?'%qYOiZ_Y6/=O𪓸$y{\UbUh۸IC{i6k֊+]RcU\7+.xfkl[t Z5{ssWv'{b&Kt 2lܒzׇt˛EKag(тHѼi2Mi;ȁO'P#ΦM^MC:cR_j39qVGҥbv-m*͂~jk~іky|Ḻ :Qcun+ZRWZtOSRxYGtojbR8|VƏk:Oa}il4i5|#hh-eRc:TxoLTP Pڵ1n7URgh0z7zޏܛpuha@qR&w~)BجKMݎkӾxfo2Q-aAQcIK=.me ~C? Sx馽eW'5^WmaڕM9/) ^aMOif7iA?0v߇~Ԥ{YbF";ƎޥAcw_xYrmm V!YlvZu5KX^M*YeFd@Kr{;S/<_kQԊ8[?"Uw ~_k-/ZGʉF",PӃT5?x{P't6#x$h`褯$}hπ5 ٭mgG#9N܎@y|Ck~_uM^h@2Wʑs(+NKᲴ F]XSzl~z+k4*`PxB+jH&T62<7#+M:ú!$jGnЯcg< 5KZI[Dy=+RilE&й93Hg xSmYme"3FB̥F?kƉ;Ŭ`ݛЮC!IoZ>|7!rRqO[6k+E^K R66m0NGj\6zO4y 4m"*@qgMZ+tȰN*k;W𖏪#r!ix:宊5u]40Ga 08 dWևIB-9`WlsОxAsm*\A3\Đ<-Ŝ1QxRxigZAK@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@#}KH MpדGw&W@*贆R o^ |Lt}oUnSmRÎj5usioV{kb{e$2BjܬQCi&I_mQ޼;\P)Iw.d#iBBm2B)X޿pQ@Tm ֽ8tWk>5X!tH/ltp>$[mDczowskim,vʱ-VY56zMi"[6+v^Et^4mZm ;OaךArh}MMLźUxlxL^ u\zw=@/> kZ\ zW"{vmV`W{x+m/Rm(\gO|4W:濩>l,ab8 =WËmC4˹Τ%pv")i+4ZlDvbҝiRhK]hPi v-o9@f&1Z.6P.čHFqkuO>ieyvŁDZ 09 H,Hv?kb8 hI_Xm4Agc$*j xT~ɣ1i?ѹ2k~!EUX"*zY hy]x)tK )6oc# wuɌs#@5mNzna"xo1X) N}Z[[[̶Z,H*OLSB;-<<hڼ~cڸ/HıiD@\mu}0<3uO+(iU}\FPԴqeMWbHk.N;9-Hiv}A](1^_kڧ+JX(6;gܒf ܂:aiVv%9[7>mԾOk}Zx{}c?|u&fˡ6Sq-F͍{Wt.mfHp։=8 |A6k!'0y]3Xz_P !V6d<ErZo\XjX!s&62FjU@&fvZ\1Yx?Z 𝥔3]K[7efġ,U{ uK߇zZĚWi@Q apOE^xaaH΀m;LCpSWgơ^!MFVQ?$5KXewuz~-RH'eW6|yU %ҧu, ;fy@s@xօcoKj. JVާ}'YF{we[Fb+Qgq;[<]O47I.RKJdy<:<)a-mo5,LX9U҆kawa$9Rb3u"u{}`&4J;3bohigM. #n<$b34URqQA_ nb%LJ~ ?ȱΖЬ7Z5MS[IRp=yAykwƞ_iso:[N>\GA{lYYiWڵĖt3)$:7=;JM.46Q7,йi-OcZQx?]:Fi7QfMqoh4c?614PmE^Kme}gy`#pq":s6nOiet;yh2ZԲռQq;Rb{an$^9<*|>n4-7FWlRk ~V|s7zFVQXΒËĎ{}: tj6.$j6'f1$,rq~!jOD-.4L7,z }iwE|<7?/7? icjQG@Vi!SA ր6}STkzl6W6)0+K1뚗+m>(M{`D"E]ͷ¬!|I=kTMDu1}jmo݈!Fǯ5M'g9 b".vz6k &khzK Pssfi_RKiI:}E~v _LE=Z$|(`yljohVk].KEgByh` hNҌJ(((`QE Z(Ҋ((1EP( ( 0( QER`zR@Q@EQE` ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( B2)h ]W:F&KOoY#VbX߄ I `O9c箉a~(Of9G) <+YCg$1F*PEPEPEPE ( ( (QE! Z( ( ( ( ( ((h((()A ( )23Z((((("(()3@ E `zREPEPEPEPEsEQEQEQEQEQEQI@ EPEPE! u(( &hh((((((() &3*D$€n^ZhiN`e>W/?Zl<= jI[p-񤈗:(k˓!E؁F xG&["iSW'sH Y,SufN7Oi744n⌣~_]m%y`"l^fe'L|ǂ-c·W;\)U b({jzy=S+bG t4^J_2Kծ"_)w 2u ۭrUsԿ|OlHK-(baz\REϱx^hufB.6J$ :R[Zl//C*cA8X'ٮ?C3)mޱalמ,3[3xR }赭2kfP;ZEJWxi|p6k϶ H% v7:♤hƟYtF -8=['>{ԬIJP bO2D2 ȿ#<zm nm2V㏛TWxm~y.!lH<9WZ[xFqZ4{@Ltݕj;NVSc7HPxkza(ơfdf4/Fy+Xm^xf-{kԡeH#ۚaIuvg-!rL'5-ީcfP^]8_6UMLk>(]wvvG$Pۤ4\ %튾xLѭyk=*FgKZ̷֩nN*'9Oowos m4sD6 .j ]:hJe9fپ!qq^v ?޽V2+e TC});Iu.)u4v-P۽1椷լ.n[hYȯJ]3ͼYW<'p"$uHVaТ|eI4<[Ɨx.eHY;8l~k9w9d^5u᷻$uU,~=kt-.75k쵘rӗgxsں[+M {xd\\Omٳ,3#4:#ˠfW*=k]񅎝uow=vos.跒70I7G@]iq֚[YEʡR:Զ9> Юh۷ɏ-sB{kz[݋X7'KnsY74m[SԬ-Ye4d;ׁjz]Vz:~Gu$7pG`fRYUX`MԵk+ծ. EUM;|_dkwMMy-qgz]:dZ4eW߀( M /nbxfICz!峎nxOo4Ìë:s@+#`M߂!O7RHUӡ]I4[Z}Q]@ۘ!q\ҭe1\6p:Kwd K}2fU=ɯפI=11Gnf x3~Kn?igќƷ~VךIpz;x)S{;%Y(9q*"Yb#c\&kMwŎH0<}CZ:0F6jh!Q$,t t/%엺ƝcֶT22in5}>渽)cwT8r <׆v}"7{rE u ).!-' ySmG3u=Nl y+|xY}IkHKc!=O֦ԭ4vv:Dn(5Qo~1=@}-5L70\ q5d_ 5kmQdz4)[k1D=W}4էFʜ}"Dt.w֯t +*s񇋬|;]`A2d{ׇKK6Zr̫3Hi[0ъysDN~=e{gwi)k ix·I"]f5Ed12}*ZĽ7-l[ڵvjc'4-%lc-& ysmgx4mkAՠTW"IڼzvkK+ XK Fkep0$cvT-,v:L":c-PXu{cujnmS̱Ȭ84}#M 9vxm<_[tYyQY5#)Nx_+O~G"KM29y3+ ČR٭u:vy$S+0sRjyEo@^ hF+m,/ z;@v଒n}eTKk6 mu60C;4Yٿp&3.}7gŤiVH7nap#޼f헺?o.K[4Hrs ϸVPxj!V*sҀ=McNmBsg4a"}}kl2i y}-S|As-62 s]CH7&7cxvcvq^L<< 9ۃtO0c[s^ËXb+ ?1--g_Ĺ+;ip{N-Ee弆{H.eV$ {txG,w $2}k}Feq\;_,n=)cFG9eCixX.ea!%m jIrE':.g#"fB8-#7ڎ oѯL˲MJ0@l"tv fn"ӼYqh=.Q z{q_+فߋ}+@;q%V5f/hXvu+4NHzJ6]:ye]8#R3d_->=&P}!U_Gyt=kic^6 =ƾuV :Ԗѵr6mCy+Az(EPEPEPEPEPX>=H< "EkzjV6 4m}JII]X^?%@W]!ch{+?g.w^\$^֣ xٜ-GaM\Zº$I4t/,5^%ۍOG^j 0{ {v5J? hou`'6es6fA?μCuWh#p 1}*޹x[]iK[B֐2Ư!v7^XD4 tGt ^Cx\-Aq](,w;g^%[8x߅歬ᆇ&K3/3;ZRN\^Jk@xKA%v.e ndYF,d^{hCK>nE`d &zJ}{Ňt Q2^V}K.m﯒r>_ҕIcZ{b o Nnu{ձ26$k;G[DŝĦ2M#5mL2P>B89(#K^{xUD{߆sXW1_Z MЮjϖ#?ޏ =,ۭ1wxVj^!$HniowWs,, z9=U忇[xQ/u)6OHlWqI^!m Ȗ\Fd~mq#*_#޼Em.{x ϴ#H cGK닭[Pe׊/4р[[x dPq? /2ڶRg׃!OL[nt(_,4חt .ma VY  `{Fh:=GP$ܒI5m997=nQEi5ܶgg.H\u<Ov:XjwPQ_9V%}y# 66S]]? *dv#8ISU ZE w4vkzƷ.d]"Jd.{^u 7v :7LNGr:U? m0æ |)Glj/o,ۼߴ )msԂ#җh^E^աEֱ{Vr̬]X I+~j?kM[{h@{Zq ׏#j:/gkPn@xʞ1U+`/Չc"A|4(s#ۚ5`Urz-`;R5 <:_k:W/,nm,\g.~luC&}Km' wm4F\_P҆k;/7)?#i={EB=U4Vhl<_i+[f38Y ߆3K"+.*wW0R\xOZ|wQ0򳓝zqB}ku>Z.&+{z ݷۨu=VJ[c'.%Xc] ]hK}C4F̨p6\> n|Ks{wjI=ʦ</lm^$Ѹ<皕B@-Oź,nf|i$pĩ!] sֺ9n%g-O[VH-b#=>1%67ٙ#S# rsqI'4XIgK]и̮{kSÆ|uw=ݵ֧w0b7?1M~Y\^GiX`k7_IrsڝGZR^y]iw yk}-1F.skaqwRs12B~2vzKk=RO^?n5JoYV[*5xR_ޡis%su E%c+5SޕPlV𾳯G]D]Z[BfcX+yV|-~ VQEhnlc|)opٞqd?ZI⫛Wny46VBrCcYMOj]I$1L5}q@G&9>uq.Rm.inPǦI+PuazM;=r ( rYKt=M+`94`}:?RܤJ4ɾLc'8\M;Xmŵ/"R& <`s^c$/?Em}\M QmzI.5|u Z̷+\#inV:dp89Q(t ( ( ( ( ( FKA2.mW ),l:hl]O}olHbtdOǭ}uku PFkfReR<C9ROT|~~&Wnn'r0\K8G[VTN_a~B?}) xc' 6[K3"ㆦ~}eM2N%T""UҊZ.m%`v>ΩqZI~\HfeQL j|A̗K&cԵSռ^ OF ș*̾t{ yZOM꾔?FӴHq-̈0e`0 (^ZZE5Ů&G)`*<9AcZC08VLp5EexI[>Y/ܗ\gi ֍t:VgkpH-3%zz%i@dV 'z]ׂ<3uNE{>m޿^t@r-Esm մ1$Hd0"?^ȭ;(O.u "KErt-*(T9]_tY'-Qnj`ِZ('y?Ӊ>m܏4Wյo-% d: ( m:=L1·! +*HE ϔN''k+ZX%QZlX@P ?[M)Ī*]E4lnl[[LFsU]SoQѬ.U<$ {T%HB,`pұtxsMՎaA~C9 ?(Lg4NMCOѬ_%Hu=*~4;~S&۾j(2A.,a{>3HO&=Ll;8l8/iQEW#GWeROc_ XAgYU?.sKEg;ٳYh8 Ԍ>{i-C0 i`κ(:2!--VM\V *q[ZaieuhA$U(t]"; ֡7,lm4;(dz(kN'D6^J7 CZxwHDM"]>UE+P:ZP=gYYkkEo8xYFiױw2I(3*V W7z|Oq٥v\9}SEi=iZ?!~W ; h7VWZURFɐg;I<vXZq(ðqs]Ms>s_.(4Y#%z xkMc| a2&Ҋiv:ŌzWVctrAxnGҡѬNo*}뢢0-]]*HOL+zRkWO/#X \`+ROxsCIY4iS}8Ҽ&k7F7x@9d-m:ܠ/F/r_On-t?Z( =29u }@bn1~U-bӖ8l/$D>Lcoj_KO`a$H^1Q ].b.O4~wghv?n=\ꋣY@ϳNs%<5{JE3'Oֺ:(|ZZ-]I#kfX!KxUF~U=x7Ú5^ekw&wK`1_Xz<j%'X@Cݻk^ZVRZ_@Is7^𖁯 6*dnQ@@Җu[$10LyJF_AY0 }dm'e]_x#7ԯ4[)on32|)u_ i7:ۋq7o &=+("=lX9#qU<5\;<|pdawUGmGgqy]>c [ ,Im$O-a4Pw&Fl)d-Ԍp?+",nAt1][9QEdC ӭiđZBA,}YȰ:lfcLWj e$<I/Ú5tޢZh wA?k60^[UwxK@N-*,MJ }EnQ@ xЫeaB+K laH=+1Ú<6G–/! >K뚬mi$Vn]rXMR`z {xd0ȥ]BQTдȠ;(V+jq?iQ@AҀ`#Pm@DzTtoGiLѭ-c}=AY>d֢O4FɐZ?֚}Սfw[|D,Jޢ9OxfD?*X|:g8>O|5L&t[;yB4a9G*(>XfA+FW#pGSҴ4 tf`O(cx\&((((((('.jWT,p4(`z@>1Mڍim nOlI"<c]Z;[m5&7dwǥ$˥tR!:{)<dQX++R%(6D2I59H4YMP" S/;7@88Fk5wvCml,ʻ*uF*⵲W)@\zPё\%K4 JPGmnzF 6xc9 ÿ!t uT/iq$?fѯuBrkamytlŢ($?#dzGp7?x6js]Ğem-pXgsrt{ KH5R85_zvn$+AR=r#/_Oi[3/nT @9$Rdz<95MZNu.{i->ր,꫹O sZ+,+[^]I[22H::LZ}54{.R:+Js\4hXi\ 'o9eAl= 5|q6.߭d%\G} ŵRu09@Igor֒[ۮϩZ$2;y[Mk&KcpFoph2=E-pټ=jq[ܱV+pW 4j7TNHIqr8sҌZL ֛꺬}F*{U2\#/R]%>ʺA3n8@LQ^s|Pcm#QJɼ>\Z^#Ҥ/쥞ݮm@YR2@~t.F3%xSχKkXe+vkeQuK~ ˧ĨLq'OZ<\,lҴBw-:[ ٫VFHdѺ2{Gs,҃R5hv[EH>+?hzޜ{RᓒV[-@DV&AwOsi6eKv4Њ\Z> $F=SQMg2~x╛3:Ě(> xh2)2=kn&Co-:,v "Ue'ny `V -SYӭVcdqǿ\k#hXr#‘TN d=Z}\Ijr2~aIėz⟵-ͼ6BZ(V1Ïs]nãizcM+$S@:Zzm^Ns ܁ypO5B&]^ț3t7 ̨>zןimonRHԬ 9l c fo+Jo 6Ω[I?w5}~Z|jHAoIj?ll@56_"P6G\wBIˢi=쐤Rq'T?5oKg5[Be*~^h瞔dg||[H U߂r0E;?c[F}`AsԌq@)s^u>iglu}W7@򃌂^Oծ[V o:\~ZԤ=EqxkZY[MxU1'5?`Q-H>ɦJޜyqdR5 z8]]\[x'njZ`s*r>2EvxkEԾ%0_,2Ȯt(((((((CҀ#8ay%`,͵Xx?R*K-̰!1{VYoS,1N"1!a?JӰ+{EE*PzkVgR"v%}V 5EP:}++d\隍6<ײ͉y8=?r\i_-Νygy>j*B+xy6仞)i▤ oj^#t} M1+[1CH55Soik׺uoN-=sҶUR3 Iht;V2&j,pFvO+4|ܤZ[6rϽz|3sk6 %!➌}^mZa51_Gxkz|RxN+FcdGt|;r,,4.tfyL۲̨XW{mjKլdG>%oJQ[ U51zJ7^cxJR5]$Ke[r% U/Hki}, :uMoeϪZGr~pE-<=j'O짽%?CZ,Im5hfQ4Vq:ɦ%6w]O-/\3+{{xfy|9;W]u<9woYɨ3Et9sYJ9-: ŭ/?0⼾z>m*ZBC=<$dҽG^Fα[%l\T<;Z\֐vv~$1ָ˽6ZVK-$13Q_ /dΡjgQOOpFxe5B#> $?B?:Ek[kH8=B]"|i7V}" lZ{" @P \մOk MѳXn iGʞzIxEvimq"X|kg1<:;w~+M^tv3*YyW|5m/;t=6;*K o=רI|g_Pk3VPX`u8s᫛--$0"`\c1<-y^ $;pw6o [+乗$v]ҼaX\7U:cbj]w%F8,qRjSOjoJKe仍3sZ5j ZHugl]Շx@uu-V+ ̓qU'bZ~_k:{r=+=3޹^,Z"8`n]VP2Wj~1Gcr*#zx=쨔ЀyGw=^<9Z]\ǝJg8zF. v[-ţ[S+b=. ثrCg>jRh(\t'N-2Pխ&_*6~oJ>'xr@lo#xі;)LO Mqt -2,@zu]4+"_VIۀ?mhG{N۸Ⳮk˝]t5{G]-@09^Awinc}#E>|=ZtPZkMHe#FP0=+otjٟڶ?` Gj~jg֗7* 1g4\a6pym4Y])VfyQ#S`R@OM͓jNog1'uItoY.KR ®Q{N|=~-)A Voc<-;?F O\H-\0 6Cf5՚t=KXZ_%U[jG=-C:dv1@R)\'IZ͛t2LU~.uM^k+aGȌzA@7]vDdѵ;kW `ee`2jOBūiv!C`'i sȺHq4q?5{f+.+Y~䱶A~Ugc&,5˧1ٷ:&-wSq)@929]'|kƇj:[ijv9=[(bRǖ-5?&T/mcJ#g=:נk0MwT2NCh0~Ccb:vtR60<?+0ymF H.ycl\.]д8hZL˻΄Hrp}h ^-/hZ xa+R:%X+o0{]MOx#TԯA=m.,HDždv5[TaOi,ki̯r\g̅T28#^Ex֩jI{{8 QOO N~>,#th nQ@ug-kPvy.._v[(Y aPcޱ喡M5by.6HWNm#4.4ty4sq^VAKzmG" 0U7+t(h(((((((=(2EkzT:w0:MCAd}՜EbUUG^O#"+8= R\8vOz"ζΤ2U!׼Ky (v۫Y;0 m9gk( fm%F>q"T ]S  zu:RPWHM&x-`nǮ+)@`<\z6D8ǥsVr͢鏥Z(]&Y|NIBzrM{XPS@\橻4 mLՠ4Si#2%?gP$k{O:Qr&u}u"+ H#^8z]76M;޼OImT0Xk/vh`-%acz/`4E73Cw"X7P$V7+Sr m58M ֙"iE'T Ty=h3~('mGO֛PUG8"Ws `"CƧX&4VPHL@Iz+hF6zBȾ!隕 liE;#w+cCq-< vv]_DX78^ϰQsIi`gϾ=O^K Y" c)tw_VCSX9{iF62 `(-6xkz&m 鰦҄zm_bӖig-Cvϵt{.ڟKC_?(!2ڹa>UXZrSOI$A.2;{f@@:Rô+w{k[QjW/-XR_OueQ 3M{A1R=AL/E]uhd'DO3]5XHgTڕA{myeBb~ҧa8cXED^? v@#[k:w^'eb:h…fsUx{ǐj2 m^7cwu* e>?OL5& t`xjNhl++aT8Y{%ae(Qs͸ pҷ?[ּ)kZhS3J)cµ{[rxfVY-oc4Xq,öhRP)@2&UW Kǽ.Ͻ 5am|8bc Kώjg\ ye ZLm'|9_^/i7j#]AXcsBUkBq6{fc[(IjԬRI."TU'zvm߈ץR,vWMkm+[@$Q)XcRsoWs> []YPlaVgX|x(MnOdE1#]ZK.H "(A4sޕh/b bF;JEexoPТך&7vp^8 9Er3U},&y/[|_;MR+Kmo&ێvcOOwi$i3/ˮWy&Ԍ;v !ԖƲ G^kxSӼ;yck>l ! $^#=Ahy~[Ɣ 6D ֯|"tmbimkD ~%JlѰg<_F+ ,xPOdlim;4l^VRϦ:u\r s2¯ zvzM9ɩa7LULG"$9olX:R]JCys$pč1ꬤ0縯6 6 M C~iα-F$҂8rT`Dtڽ t JZlH(C ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (E%((*YUeTQX2s(n>.kYPΨ$1 Nǧ8E`)Jy*)+QPEpȍ *MbU.b̹#!wTs/}Qpo4-Y )y[zf/Aޘ EPEPE! MO0cl3w TR)ȥ;@Ph`w73ɠ hqhwGQI % di-/[[g@J4n0He( (y-^ %&b ( ( (=*7eB1n=((ykf0$p7G tQEQEPj彡\OFF؁.@hVZfK;n#W1èUҀQIv O~4r(((_^Aco%Ac/$_Egqr Z5pX!4zLN'ִmB6I-P0NG1( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (Ojhյ;XijZ۴ w -BO+{xF {پ3xE%+ s +$?X)m.4gdioP+?^,Vtqel Z੏yj\ʋRk#^%uY"NuF8oPk'LnO^*^$N*vrcH EX?-ot֖ݭO%,R;Ğ|Eiƞmfx 7uh ImOm63\&z'z~5oE.n/ Agq]6MSRβb*+. ̤u4wU7:~b+A<ٜc+єo+_3 A\\X>hO|WSZ}koh/nF1'vr3N*y~O-IN-IDeS85&QԥY}Ŵ/*Apx'\y|lvgmww3@\qȱA)YyTXE;N2X]?k,v$waKgDr!y{T*Qjצ)\3a3Ɩrxē-U3Ζn ?i׾,&74[.l⵹0l̠󍦺z%p,w@XϴI0+CT^ϪK{ 6,?O/;FO"V15w3/[mNHeX@p0@$pJ4MWW ? 0yM *o9aAYEڛ)/9zTI/ak뫨1-1PSX7w!EZ.s ֧$5@^j,eyhņmfbDfŞӼQ -d7Ҙ;\r3eí -l]wLӖYScRkKS$MOP3y!pv*}Aq5hmKy8ǽ{Dѣ;YJqW6ч#,t|!2)rOZVƾ*m5mM۰NF5sZZjlu^\^V#L9koiym=Υ[ݡ㾻i<EC|6_]̑ ˖ɆM=*ӤYX cA tGK-W;R,iFV Ulw8^G8N Mnŭib'yx5mĚwXlxten}r!7]^j[<' ;f2(OzOt4+&SE}I,nuGB[MJQB$YnIP~cC [>җ6H~egh˫j!$ֺZ|X`Q#8Q9+дφ.o{$pA:ePz Sw.5 sCpVEn;8;np&o|7O]4\7o/<5K:2^ʬg0Qgon1m'h]V# )Nq[_^^E4H*Qrߘ~U@c5^Ѵt,O2gv^Oz6 {cˌnF>_W׼7m>;/j3 3(kƧ;mHKbo#) TH9bzV:wq'Bc y 3֯[:_.滶{ۻci~/Xxws5q,^i 0@r>ZoxEQYKCQ3J[7ķ67myowlvPT{T6mcR9e]B)9E=/> (v6ľ.|9-ޣxQodC#L؁{ׯj|ZuesEsD0p{V*AfI tЕU @Y<70ʷ36זӂ<:wQյ]w>& RSbL~` g?z$~ҿn4ɵ+'uI$lU*A犯cC7ek5g#zv43]O?4#Z<BrzկijA7- ,a~>|=%wQG-NbS&Cψ ͝<_!);4%9=cŞ*uTԭ4Ss/LG^s~)anf|搻aW`?(O5;ѠkpofK'YdP6c` {Ux#L㾙m/3#9uuKKmwዟDlut=&@Y0bEjzu;ĵh tG l K|6Ҥcum_Nt@OkRW;皵w-2E8tyRkDd4x|Vj{t2UI_zLe7"VA\v.Ş2>BaW+O/͍Ug[4kG-NṆhm)H V$Fi}{nW]޳J3V<3+jd6ޭ:fpJg]~]^;X^r5)<&xQ׼LiceTy91>cTs_:[&k8cVw>֭.;_,m.Z$(qZ>(6Cm2]RONk|.$nfgcF:iluuHH-Qb5_|O-$YH$1Ȏ:€<Ě{merhmk縮QK* l@ɭ;_jjeԦ}Jky$|Ǹ/:n.mKXZ}=hCOChxqhn O*&Gu/St.pˏ2z]׉x um} K92=TjwIlRi-7ko!8$:SZ!5sԴl^= J`VVnPs|Ki/[]{'j I'pABd1f$zwxOUo-;K{ ` sRn/wέ$JKi"rA#?xd9q^s=>,/v㌐J=+ť6YH3u8"Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@A}m|ihwPWam:?Ԟ73iebiv/OfOOդ|7wm 7q@dFhj 3:é~}bk Z\d$SI5]PGY}y缎bԾ7zq\SnBR? jZΟ=vRZDjV!'=?/|IqAkt+v{0^Ti r-&'0{ULRHF# Iu^ D:5柨z֜ 1Zmt`9zmU.x'Z]^GPP z~( _ 5꺎l$xc".6P9֭x3h>%Ѭk], X.[*߆|7z"^ɤItbș]wLqPxڧ&;cL2j6~{+ʀ2ľ%o-b,QEpe_ Nx|_K4ZB_E.p?himGLҵ8ɱ[ t9c5V֫ވntIt՝Oi[V9* !q]m|7-ͦU܁+4uM65"Ӵ?jǎ-J; K&o>cg8 MG,Q-bX(@_(((((((((((((((((((((((((((((((((((((((((#4PR((y(JZ((,EPEPH? uQEQEQEQEݧ=iP Z((ǽ>(((((((((((((((((((((((((((((((((((((((((HzP5JdK>g?K>58RAf? 57c2][$2&^NKF]&oL}ʧԭcO>Ҽv7AZ0 '>v h< YA c8KNV:9P}iiDU^`S@+> fu-NU֭ P9WA&iF2oo#AcU.n=b:TڱtǩCc*\ۥgP")$g'%xp:%#k!O;|Y < /Sc7ny7{/-NNdPHrG#6Z&kaw ߙpȉ*BZ0Xo/LiPv;kXUbZ؍=$ C@#خ[AT?-M$t4l;ISPӨZW7V[m9-03a u%yv'f=2`cnMOhx9H}/y?n%m֋1\/ ihQ[g?J|7 :oD6Aktm* U|iq, y~9bLǻzע-M]-fLhphM{.YF8gm6˺y#Gk@%b兜LMBiJŷ12 kJ-y~9[aۮ;!I)p.zVdԠh#G1Hg'8[ /VHe]mKOS~'PѢ𝵶1P˵T*wn&tW7űZ=5yDTI g'9]?ڢ“,xow}吝x<9kw?=m;hѐe% t C=>ylK10 9 sH,{J_I .`g?fKx򤸅%8B4_jUVky+S-x[`eGf+y{@vxSJlGÄge{sץlAldyP?++:=Ĭtxa׏ JH>O֓bEv@O.pҀ=[xqOy FHDq Oּ/@u W%`lT]/OGXgO.iE#̤;ThGWNsÖ wK,@Lp8k-u\u4VQڴ S 7U66> nou+n8RܝN" ?R5}[bC,|:0sӭyi^׾#h)koJir jF_sYk:8^T*\4{m< /k77J2[Aws6q*O+S/. 2sgl{ѽ쏢gs?ԵoN46$m}˜kVZ]iҙ Y^"̧ȯ$_`*2=pi -3"\ RWznnT޵ힺ {DmtiaQ"RBY͟CVuS1G6#d3^w^ڦ${Z^ۼs ٬F15&;=@z}7E}9o~8 OaZ}Fnrd{zi鷷67`\FRԡ3ByEծRE+kY続Ygʜy[J4>(-Aqpс=y6%*.M2⍖} B~iC.qޱukOj:Υiėy2%ǻ1X)@@LAuy(hk/G-e}\H#y 08?SivOvڤx% v緭&`Q~fP\|ۚ%EYIO_9kvlkzޙ]{)H#hYXүx,4|^|aR"<('4=]_snm""4\s㊽k$m$w6H_xi\~54xdR z V=֛gc)tIѩ,ܐ[Ǖ%>Xl>>9t2G"yR_\[9$gؽ[8/tZkZ ic2sWj/EM6%Gž$aBg7q VFES {sNט[Lm?Ofa9Ny5ڍ6׺V2km,_Dn5d:}n:;Vѵi"^Ik#}w)O)t k1{op'i7.g5IZM)b}*խd2Xa"4=+òzNq Ԡ˙w203کBI}o_62 ^MXV'HfVy4v ?Zc^۬+)pO5?:]: v!>F{f|N}C\E7FHp۹G?E{+‘4+}.0~fk(4M/B +Ģ/L+)|NOI }FBg5gz񝝽m>rʩ'eNS n°&AȥMFX(dk?3xqZ[i ׯZ|YڛFJu ER(-gk(}L}6!W?X9sY/bui6#O>xQSwr\C#Y\C/ڣ`m_1qMB;@c>R #9֒gipg5_Jm].Ps%K,lF )Wx1МJHMa)v>o y"x=wX7z=Dl~GkJi#JxnaE@[ҵ|B$ird[i`o ~89{wDG<-pKoyaHWF ʾ]A|r'DJ/)PzotM "[i_*eSd۟>M:BtA>,ˍܨS̈́H6o!y- ^&go7 qy 1*J0rIckD4cc<$26Rj&K.lk@ÌҼúF_~˧r=V PM.+W ,=蓴~%VepjiQdA.#MȒ.q;kӧxe4_Eg t"$nŬ1\ksՌ6E .zsjjnd*B-7229 (9[oTin!@+=^- |4s&htbf8,ƹ˛|7vj[w40>)t|MVmo$%} k$3uh Y^-bP4e~E@%Fk}Z}?SLӵEFsrdc ?)iYT{\Jui⌞ u>ү߉Z[_ɑ6zs2KUџ}X}g7mhrA(xcp7}=j°a;޾{v=Pj}4-[91n}^mFJ9_ni"FWD n.#]H>LtK?)MRWg[Wq,sq\K+#K{Ft%D R68NqO\NZ*J5[Z\pO|P} N'x}3R g7<׃o3:t:mi_[đ핢 vՉlxG2rY![H$,h8Z:ާf  Id@<We.U=q@, Z🊭GzG8ߕbǦAK{otļb`!*1%Yr?C_8i/7"K1cA 4{7i(/r0{z@aCt\XlxMB' Y`zqWhZwwG+=JI֪x#S6$@ozybhFwG Zdl)#4_^@W3W ,4OZIs5#riP u]h7^#Ю.:ܰM[˛ ;a5Ej'Ug{ĮC#pFF=`sd)"F#e?hh"|n2:Wk796Z%ޣ.kk"/"iVp`sשxWVtK /Zf:YxӵD "28Ip??#_fK54y wsK&FY)gK/7[HG5o+]ޤX!XD}kj*%>^EukbFɹwFZzX[o&6$PQߡB <(TrA 0BA5Z5 &Af_x5kh<˵Cpz}*k z2iS͝sQbBkB~ےyPy=k\&ץ:k#=1]/m/ھ%VHq[k 8?SKcaӧMͬrvoNi,,q.e&[Ei4\?|͠iW*.|]G8}~*UOWњ Xq2+[#31~{d Q3(aQ@+"_edO6w5xVn4=#DuuXgF /@=Vaol6uzO<;g ¥ U+̯? #öw\]_b7b2 ʆkZ֐f٠3*9H =M5*:.8;v?BnX7mxrGoYƗ4bgG^V3|PPIGRd*˞qFLH@ ({h]<1tPM6)Qˬr k#j QנJi0A ~M/n-.nIu53'xY g.M_^|$BmE`7}qsڔ~$49n5{)!eUTG"ݚd!PM m }(xy5ԺާWsEEƨ#&sy\WmnV tmpXR):9-$1'$2MKxQYR(Pkü)UmXS;˔70AU @=%AOSķw 2qA"G0uo4Kk ž̹p1mFT^\i5E. ?6ӁQ#h,J)2ɻsJ[:\rEP DA b$y+`׍ƿ|c֮4Դ[`.1qip,<=&,2>udړ"8?8;x:o\l-L~[[ '9%Ipk=+NR h1!GNkq p:בKioI5m |]-,Zy#nqgֲόCx{6\]ƫi$fcYd.r{ QӮ'b,0p\z/1~'5-dodI.xhc Pj]* tKyҁ>k|eo}*m=,5MzHe=r7* ZYߛ `s3ޥBc`<.ъ&Y s^Iψ5⫦Mf͂$n2ITa <:}+556XX1$m xWxŚͪ&irm2srkKĚ)P{fAF'${t47azE(L1rZysYq!<|זMeq-[JlP0doׄSSu_MF čʑ!U?!=G/4MCR0d89>-ޟe 1>ٮgՏ+=dP  r: ͯlH$ReG;ZI,z嵼6ƱDT )h/5 mk^ =DQ 'gjkNO6_mB@?Հd I{hKŗ>Ze{^1c=j˦kX,D1ES n5R'ۛ UEwI$,aqNE!  zRJ$$?Ւ0H++W6^EL q;W:ucY>)RnZ,^a : cuwuw6)i(5FoFi[pA^E#h7+ڼwųYxKxmgtD#dnz2]i2{58N9f"K*,9 u֔A 6A^I?iKGjΔ9K!6`]φu<mu_qۑ̠,`:0 [xUYV·,ϼ46ib {q1ǽxu]lkZHF&1o]^xY5VyP>FVdRnGjzkhZ&`{NpqZ&D-8W*^ifTs"k$L.w: {fSJjAĻ~]pAM"z=wQ1f`^87u UM.I&]=KY|¯()ȮT<<ֆK K(۴ǜd~U]bX &HcE+) 23>)3a>mKlPlOQբ9-WmI1.$)jMk֮RuḀ #?u4P3O4{ DV񈢌tUs5w6.3aJ$Xzv:Trc1i~[1ψ;cNIv,nsTGhFK4F"yT@ Zi#]Rؼkl;=I8Y3s:`gY|-ymgD~]au*hD|?ѿKl h}ՕC=7s U/^Ms.I3_wss+9ϬRKP p dǭW<2KGv ?uP;4HJXt]1̶I6#zjNong1UNzJ( YMksT1R0G\zU՞{|yM$1J(>WQ忻W$%+PvU~ÏͦC>lAF9guP7x;C$NWO$8K/O+1gda#MۉN?*ߢ08_O*cON{_x3BNGkq[`shU|ѡc5[Oy6w%[19i定l"G 9C+.*rNx:=s-'-$Meo\hvu]E֖鸆nG;d^ytM+č꼁׷n@\ 3qͧ;,9 Ľ9j  vQ VcPD|OEFAZ 2\OPgޥ@,3W@^š1iw Pq:/A7Fm9[Ļ|/Sjh tfS%M $B#A i^YWF6);0$lV:%B FIoqU:TWY(PCrۛh}=+v>i$Y | x=k/ښhy/~wvss+4@<kOchʪ5%(>Va3+jXVng`Y1PFzw>N?.؉=+?3Z%Y vuP#|=Ʃ}= 6PԀq|U!u&s7FxǵtTP7 O0˧;G bJ1;Kӭtl%ڑQWU -_KŬH%LްO?ʴfޗ4\YLR@:'|8WMwLTCnO41ǩc&NDyݷ*(_ eխƝױnxJgb|ZZϚXN$/5_jMiZHہ*ø)ѕ[x9PZ񽾝kz$u)yNg@=X>x~{[ջqnDRk Q+Z7z 7Wr[,퍾b>RIźkM{B큒2*%FQ&CZ9R/ũXf mu[crm$( kO:h?i{[/kjjn>&.{nmm爬]r{W/'%Iamt溞[R8y =f:rzmm$|IVfN`fԬ$BV4@|UeP-517F[-% T~~6ki_M*LH[q]#L0_\-rpDZs随^[-N oQW% rUxsq,M+l@ rx\W|}q}=5𳹿m>Y 7B'4u_KEr~ ֓QoEKayG=j3y94.yu*uko Dceaj!K[z֓uV^LS,$.69h` Qg!xþ'$}YHP;Ÿ? |6݋_](=mgwpiv6C+x8|DR =B d76؛ E;ӚHJѭq{5cfdR2v>'<;yo o!;JEqOM#P)a1ZpB:'"HWg7*o%`q~iRkحu({lV&sg?Zȵm%I.`(3'⹯xOSwuy2 XL tozz=Ϋq]j6٩QyTbyLzPfs\ k-Rȩ#G9") g ǽg_\|qfv*Fr{~t黆hyˋCwz"gqbWSĻ96<DԖ׶ۖKb)ڒiMJ+\ ]B-a?{6ёۑ\}+KACu#hژikKek2jѴyرEe{s5ĭ8Lu@85Jxc4NGSEsV'}{wR2zXD(G 8@$-5Pnc-CZn;sWKE7M=hH&c{s,q\7_;C\c5H 9^;Vn<[ua< Ky>rs\K1A`:יi4OmFQծonFؼ ~ 沝5xlḄ ǐAހ=8|H$X-d򧺊DnخUT{Z Ĩ(F+hUkl fqǭhZ|Cn<7>Pm^Do";h=|A.9]i7iXB'5HRpU=SǗj>-mn̋sfm<#w}('Z'4:\}4Neߧ4>#hrWsF*[DRU-N{PgPX޼|Mk.RH^x.mIw[⋏x{T}P` [@ A<+ˌ0)|]k iCPVfȱ~768}hr#Op|mbZYɾ/]yEf䬳t<:ιTŧWsèD4tp8ip,Z?t,Zd,6= szw]j"t彸+{!A;{(9H|o>]HTEL$sҴIiP.#s;@i\ +_/4X ܫeך?vi{.FZ MoIgq&:II6n">qg)F+).-<*H|sZm[j{;\y~.J %vH h"m> izu3Cpme-dAol覆f-OǺ}EqI3 ,8b{ @uF\<_tyo"A~v poa/u=3ĿiӍvb$1on-ֺtz)v^^]ttdt iv%{`Ίt;7\\x_ xQKoR:={^ZZK9I|-1lv2/?ۮmo-u)-)D=3{ #IjI[#(ҢM>&hƜj0Ũ3%[I S@&^+_:rPvZ/bg7{o;OP,zȞ;MA|_.0㎽{iAGW杧2iqZ]̶[#JEt{mBXYg=@GCAםIţE#r_pׁ*#GmPkM\[[ .mEyY4K-T٬@kIn{]gI'#m)w 4E 9QESdCN9 m-GZ(<7- vVVl8ўYO[XuᏨ9{W9oKH-ogKTHDB7SD44$tə}+rx>_M CB^ΡAyK%eaKI,=RAgѨjv:גJ~bcrs϶;fYI#!Tv;,1ۻJ~c٩o5 3hPjEN0<潶.EY5֩i BUYRnКkZ0v\9dIV Aҩxf <,.ݛWoWZZeY_Kt)(, d^ &61jVyHwdlʺ~\T7k%FshGFh avZ#Jk[" Z#s/)z{Wh3cTmU^ce\\'3&b ݏ<㯽tzn kj~(,l^ŭ%I8ůI}hq0lSON1Ԍ'_?k:f"1;"v~&T=Oar&}I$ tg{~Z]ZL)% ?;P>qk7<|C_\ݦm?Oi)ƳǤv:^~Ct#v6kwxlKI#IAlVZπu%׵S喩s_5BY@h _kYhzΡu3NHf1Bzt^tm pI~ew֣i ̃rbB4zn,rI}9>ok1R(#:ڜ1.<7jڜwۡYV)fg8_eZqN3 K4o =°ih<驴: #V5HS(-IC~D &{T7rNz NJtˣioa7Qߵ_0 1Ѯ [$[m#jj*n@0zGƳ ٶ -%ڿ4;/`8ֶ&I#q]A5_S,U=x~Z0MsE< \lO"MK^Ҵƍ5FٜU@i/u& Q&]3zy?:49zfsj~:kG4=ۗhX ^]>}ik޳ ϔjgxF5CMOHr<Ե -2?P,+=i!OPյ^+ H^zK]gU/<;i-YXNj+X]q}+6Tn"͏&D$u4y`֗CspE Yxgħž"ӭ+ymMJi\p0:פ_^kld=G=ig ]0<}Dr^*濩D%%Ɲ# Gf08 x2T}KZj[Y0BBֽrXaId!TZm.(aT݅g9m)ch ^ѥoDrd UҬnu{ HYs=zM.G[SvHGR$9O>cjOB-̒DѤؑz-)$n6B;V:TB]F h 34q&}B][(٤kWqxOLt =?Cq3Cs4̱( nyHmJ;9[jL}inu* Ho&-SJҍNҒIlS346ƙj] q$ۀp}jLJ6:G$:PEw6l6h]Po/(Zs͆ XDӴG0[Γ\?g5H8;K/z4A=c4Sάyu-oJdHkYeVYR[ iRsv.a< ]ڬw\3&#\zNo" Rګ96fM9u7oRI#4ڋv&Q=qa}Zz,JD21^sXW^D#[WV k`XuGaף#NK|dʂ cNku =R>0Ejvb7]ͽ7sG $QMR״A,Y.XExf,jkNLʭ[;Z亵孬ZG8e'v,y56zGK ˻6:G㸪?d޷.$jx8 z:#ܷ݅k;lHKt $ӧ^GfEm \JE-/;{Y!u!X=zmRi,dUR`rpj]WL, ?&0z+}Lq.ڵr6Rd!W<]|wkzZW,:7sW>YX!X$qNy埂/E FCueemgwus֛ZnCn};6v kL i-"%ddT9⥫O^?P$gR?gxg'/|KoX%w鰼w#Pkew?u(Qtv)beR 6pSUu6|B'c,k_ ^SRIPK! 2T9TlXߌdy6J d#col-o xO%$PO- 4nCXYU;ȣuMR{eFC>7@}m _$Iog-ͼH6ǰxGZݚ5ZX_Iq, hnCr\zV4/`\ch Sly< 3@Ӂ#sܑ(vv6rj.doݳZ~;o#˼vθ\梏k=rw4m" wǥ'Hgs?4V_GRMīInAZwwP{(le֪UB޺iFDqQ'c[jQYGo&@G8 f5/MqK&R&6o9F;TU'o,,V`.C`g]-=#Kt4KcHXZ#nIS1is)<\!.ЬmEV/ա`YN܇^3]G9{t:Lvh˼29w +W8-|!¯ֱ/Wb5]"[}HBQL93;*M78}?ž"!͍ΜK-\Y\A =*K> KiZ<:,r$ L"BWxk:ݥ[TRp#$v'vqzlmZ<(,g7 5 VJK/EnZH9Nk2 3X]Z(l.[#VxBY&լ挌zwYbFpy?/0K:6:}X$9x}khx{pxI[=RI!H W8>7Ѵ;M2{PC 8*sՉ'6!V5-Ӓnn;d٠xgZMݼ o q -!$c=}yz,-59k).ZB"=1׫YxIam.<&O.PGrqSlE]Gmi3li2g~@<߄cY7%2f, b2]җ^ Xbk[t zQ{ 堼ԭ` <ON3@뭨G62"6RGڛdז 4GOHA~uMtӂ]9FsKQj]>J:I>5+Cygèsڎzmi~t) )yP.K`lm|- 2G WzeK8PArEu~%mg{{R)Ѷ4X:Ө_F"G}Z[2$hg*kq5žSjjO+́Cqczt.r%Oݔ*OJu;N)o6lMmsKZfF݆zfQtocYr$&;pFH;k#Śv\hn}oƆS5ϔ֯axzj6~s =D"xE{kReQGSAo`CDc%c뎽ƙxQzޟhYa,7k0`l$Uqći/o5_8…`10s,mX\E~R@y5Qךwf7^R`0ppjmS )iV{kIXp}>#:96T31 FZxXXG[>A< 秽P*gzuc=[9ԭYDgJ5hZ]6 Sh\0Ӄ@袊)}NhrG.:{QFxh{uc%ZDz܎U𞠶_duld Kt6OY HZ?4\YI lz`Jp]OXx*j&kg369]^d0<=ix,:acda}+Siگ;]?ʕ?eXmgq:qj۞GLFMe>ҭu | ܅*m8#VGB:vYji$bCװ`zQizVocaMx.e!LMt_mKm [)K>H8Bھ`g8DNi7Fvf3S呗#zVn/th=upDm G u5AK<'\K /?h7i"Z_(.! xxy5=QKddt1xZk}n(3ҀDdʓH\cd W'ިxUeK4 ==M/Xh'Њ3z~ўx+DYԴa(o67>SV:ziPh7dOعXX6.>k0=(=)%k /Xj:֓b͔ڪ;WJ>#mW]n5 ,@g$IC zLvM4rCH.GB}j|JbyЏ.,vqd@bwTg޺?ZƷMP$v ?Vw^#4DzZ_-SmZ(#Ai(Z?)Wz%X͒X"׮nmaxX4#$c^6g7m~ ny ln)[KNp'R8m&=B[xS ,b?mthhoq5ucu84ox'T_ L1- L g>^n|Ch x1bz7t[}wLk"r 56>ۇcDH4F5bn3 $,Gwl$#p^Wm'KHtKJH\=8i75*pWRj,?|9-.Eƹ&UNIjxlidXk i-"ȯk=(=)Jږ? co(whW^6JN$!nI۽n^sZM =(ڹ)=AhxMO4㼳M'+'8Z^K5ho˩I"]}ٍlW`zQz h7ӭVmȬngg`s=%g7*6msȦC UQ@E&P(ھ2WM ;?CK?hVŔ]%D#X*G͖5KR5e]jֺEvnI$4=E@*gtMPhV|g8e۟5qwgK->;=.xInvފN[J t=EJVV*Hx)/,/%Ğj:H9Xsnxmc] LJ!HŜLĠjҍҋh#´-{EZ 6)4*wCBJE6͎E7ESqn׷`zPT)~_K%˷{bθ pH=pMy_$eIOSӥz^U1KmE䚛ҀXօc*=?MI6rNK1=M08[ë]Zܥn,E>Dq\qNS:D׫51nf p:gkۊJ;=~VKEs\ ۭnhY\[OᛘPH Hl*柩IYz饣i5@F=ܶJkI+>nvl9.En.e* yo4LHerTy@XN:KI}r-N}FHxU;Wd0sLT0YisEbff  2"wҮo5F&C i`b'z^`Z( ( (($;J?oKɿty{(?jizeF}`_PKH)hns8@)`23J-A'ix-u:LZLQ@ E#Q(h B=hRdz21PI(=E-3ir=h#84Ҁ *92O"Geݵ?g\N ҄Rn_ΌQ@ E(hQFG<#ր )23iq $ڀ% (!K-NOhemJ\ÊuD񮥯.m $4$`~\s{5ֱM$;ɀHNGP<S>e2I(.A $`VN=2K5 ],X&۷ܹ2+?$\qcab3qrRNz¶1ԂG- Zy9ltϊt?Zǯin!pp;f(tP-EBeN-x.ts0qmٔљA5%vxMNi?g <0iՄ#'ϵ4q- ŴkBK:@͵ ƏQt rŰ󃹾S׭+PxWVK7z>Ln%ٌ$ ;ᶳguL׋rskLp.΀`s)Л*xůZiԶn'$r$3Z!W[; R/wTܡ{]/×u1801cGvzGM&`)x:U/Xis$Z3-ٜes@pֹi^&R[)"CNҪb = sSk3n4.l%E  ZUxMoVsO=+|3׼o4n 슪φ<OxJk @?X:mсe= -p ^89gZjVzGSBs"6#Ft~/("ggLr3Hqw7FI'=۸8[Zu w%TEYQȥ1f9uc{chK f^Y_[^x'vW7E}0I7#C)^|?n1wi-grrsv t`֣5;X`X`╀U״xwDn.ie8J.9dk2h4_}-hQpztW_Ỵ}kX]Cz:qqG{u20fyHq_Xk7@Voq?t ,s 穯IN>Jn'MDTt<GJ mkOZjZ2mp_Ior5IOӭd#yB}iú|Vw%F= {WKz֭g G彽cqI9N??oѵ-+KKp“rOP Z?Z [_IfWrY.#]Rk@t+cϭC3d=.tQHHTDU[>#j%k_5]^]8ynƺ^[躦ZE=X3 μ.{g?k -t'sG#ֵ6Z]WWPPv{V-Qwz$ OxJf; 0 $i& @xT/:Mb,ew J4VǯJOëXoLu: did%Swj|.kUVmu2Z c3zo}% 8k6}ZmZ߼}˖®FjI又,cܓW7b~?-kX v6y\ZɲH884ƞ WIFkm^ >/9 ȋpzߥ3Wo1 ktحBU||8 k~lP+{7PřcZĞ%kK#%q\4[H uωiO>l}T0ʰq\߂#}SLcG瀧rw>ihUVE&1cGx&sg +{[)}xAc=GHg΋뺔Q3 <} $`/ ;߽qڬx Yh{k[݁}ˀN:d@5ӣmI$ʍ|86Ge5IcqV~U?^[Ǩs 崳9'8OnI[U-,Rs jXhrZ_3O w*0@ωo/E$qV_Z:7RMv]]i&ީ$趖3HH'`04; >ǹ];|m@9>ְ>; ^Zmq06Qwo\Y_F% r!T/ 29`}gWo;DF0@'>i>Q|Z[4`M[Y$8Hcimű k~Y]X6lK}9$*T14 io{&Y!$grZ{EFk2Ng8jqjs\9[ n KWi? ]hffiMHOr=:GYf͢h~ys-;ooxV?i Oc I%WN`^,`t-e67&2Xn/$׺fތ+ 3R g ,/MWW5$ryt_c'uYeI",`㞔xgZ5?kWmqAMAݓAWx-.JlK `X$Q~iX滾Rgk'`G\\G6^E,-2:9ūkYkRyO2D`wgrI=pj/8|c&Hd~i\2h,36YRwSNYcmiyswnN`8}S"ޛyS@#>t<8'XgT0/4zLZ+DT(涢i=R-BQgV5}Ҡ>CWټ 0:=uiC7mddY?JOt8a4E)2;HT4_uS:-o'-ӰcԺo *m*k n,+ӡ9'aʒKֵ%t8tKBmPe'<scJ_CL lqwd`V- {X.(uip )0gRg+գH`s۽sZ_ĝV+%Յw6Em%r^sZ^ts=ēvc=tQEɧ5ZJi=E| DSa^jWVֶ,YT_BZCw or'zdvw>ls5G#8ɯM8n.Dο"y#ǽvM:+hmmθ0GS"x:O5+@Hc!;xrxIz&^摙G,Opη<SֵK_@8h0ZJcO6(gOյzP)+:07abgw'8?w5Y^_K"XE1aVݑ3Ke}}a闐YjlwBg;Nw EOῈNo_ŦJ #, ݸ +uNK_ \XTA0ҽjԬ-^4xXc't◘,7xcYCǾg8|r5Oj|.n"99ێG9/~4h-i#>{RuOx3P֛TH LL`^];mož% XgB[1i. I5P|fl͞³k! vHQ|d OZZ$% ܩ OpkMƟsj Xt2=IIqS4ut%Rs"\Js_/iך E_KE[uzS~(x"ųPKe+h;e@1CDΈw+> ӈ>{ jk}=9w>F;:Ձ1 nñť]9#1fo@$Z$)Mn=궹>L"KӘOXn p=WD:@t5ߥp)WƦ֧sA;놹#Ȗ3)N㹠 #&fE#W'Dxf]M CGC{Wxz޹vmox$+- ヂ8j.u+mEO] (NB~;Ҁ*iF/u;٭*nՈaU52xhķα`"Px#x[U%+eg0<@J'rP+iD-!''WΥ h^{xZHSߥT2oF5~'؍鷢 s֠ǁ5 ^e]LL jR=!3%ʻY$8WRЛ⭔:M-n2 )8#eI|;iZΈ:G$9jYÿb<;eumvEJF%KqW|q[S#YҗHKqm54X*Wu%H+#fdd|nFRA>,S٢۴?h2n3c84=sE@ml4 ydPk(䱭ψ1sW,$m6vnW 9#X5*x+#ܪ0zj+VZݞַOOsnws<_}wP3\r]E2۱EN֧۝#Dkw C7b펙 _Ht Nu{W_:XЊ~f$rxIxCbh-`r\G6.9 k#J%k0emǔ+c޴4Dմ>OxlVޙ33vc=4ωҼ?KĦX:3i$fu?k(q<~έ˛9m-63,۲봌.^{~@s|T-wU]: ϱMvJmG߷8ݒ3δ> WUoln^ͩiok##9*@9-/j^'֭b6CͷT>c8 jot[}RmBJкHJIVs0SZu,Vr(z4<+5Zm2}>O/8G3oL "x~K+hl.=Ba۹@W=_=(S:H99~>k?싟\AޜmQw9O>(|_m]7=C#yqwY6- oWl~F7:x=EJ(Oc%߁GEs_ibSŲ8&y#ާB֣qc-HRF sЬ\jHEQ?/SsFI"Up"]F,&ӵ U̒dŸTE[o^~%HM:)湎XԖ 'q=ꎡ{rH峳[Q \0^B83YÍz[:{y4  JE >SqڎMOg[Cm6H҃6h7X}<9یsj(w7V&\3{xB3Pj oS5ڕw $A)~&=:x5;; S2,c89WhחtSYKe3}def_q^r<]B柨K4RXcgӊ- jCMkB_$)c7hNJ(((((((#4PvpE(((((qKE7ozӨ#4иJ}EPEPAQER4-Rh``:n NtEB|eCtWCP.Z`c1=kl..(qEPM3ҝE7h΍ӨsNfE4(F=hMhQi\cIEC 0gɍ#]p)hzMӨm(6cgm( 4 v ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( CVIX7a֎yw06\~cֲ|l9*<|+R {@Xl(##B+'⧈1kpya F5\++5;i_ivڍڄ"0cˏ*9֧-|PciChj͆*J|#Įj4 C;݇8U q+O]3>Xmm.[gXI9R Tptz~7 GyWpq5kv:E]c okbIm9k"k8i(\o$fR_,/.%ePȢ>Fy'aJgЭ*8R}N+pYxNѤ/$Tơy; y/mN^Om:c^K>p#DǿZ}[\|9;*_joa?y僵gqKtguL~T#q5;Uyu[cYn|pA8 !]jvڡY% snŐnܷÍif `"vK[)rdHPToMW),s$x&?y_hg^Nw89n?BѭZńoH8ee|3q w)mwZ R [9|:sּ 5t{ȵKөKlf;pL_:V\Δ|4e3bLzf=dHFS89cUm:[4[;ۯJ"&l >'^-M|@~]Iw~#XPZ眤z*jcir$ut9ǥxZ=FԺ2'jq2qW-xH^")#M$9G[+*!\:A3f}Mxe</5Iuˋ5H>^^3HYiUFKL|IokN<7/>ʑJ.ѓ#_o KmG<6do9dFw͌qɛ,9mC.KY$RUoLO57 =+mJ]?wzns/foiyoއ]½OZg{صi218 ր=M>wU'֐(q^S~"Z֒v&9mϭr { -#MlJ>=Rm:F[X[ '$/Nkee}: &e$dgP ǽZ]V_.+)B$m=xχ_]j+c:C:̸^Wh}qI=ZCocFdz8]ԗ_hJMOf651O ^jzott6"9Vڏ%. s^?xC$SJ*U zUaxwg -d)ǖWs:-F՘FYH%Xd|׫]i~J,W* /F?_@&~ZAf``Q+bmg_iY$4~P+`&MyO_=imx ]'RV5f!ݼT}|2ԗDT )Q5cXHGRb0nۼg;~ڟ7Px.tqrNdYCw=FTcGSk jI)49|=gޕ\8kgM%<7cwg*ʭ]I ڍ5ۋ )oE|=%kfK33vo&qwti5mΊ4\G*Y[M3|ӵ}Zp\$ZKΫv5`]yA *.7H=2q^Gi4Y#1xP{Kbk, M#'a|Fn5ӒM6 ]:9!ZPT*랤Td<ji6K,sy3lzG5E߇K1LIUedJhӴ t! 2lYX˰Ĥ^SوWWRG\iDIPMx.>mxlSvԣv$c9 RAK'T)'wic #?u@j]F}&qKz9:k狍"ܶi;A*?ʼcu/ G fuM3+T.}s^GhCB_]Io`r 12nqU]-2ImeOԯ$%O?2()jٰA"^^ XyL砫2 ؑ_:hj5,~%w.'i^w?ucqtw`eoܕD?( $WSqRp{6\ ڄ` kɴK]>%ꎑ_\XoRGdmFyX^Z_]Lc" S&7<.^bݸm l`}y^w؁j7wVCs#JR?+r+>rAWдo7Vi}<"&FRWW ko,ȣRހ MVj6̋Z P!G~s6)`~F^"׃o>d*۾ ߊͻ_4{OImjq&c: g+#VUeݼmwtxm&Z7wr?-GWdQe,;}>Tj^HT08׶\nxO鰹Ii40]&p9OZ2.LJ4~Nv6Aqn0}1OK?fYd37B+?wztj\g32IU`Xg?A^ Kk[}@IE|ynͅ`=qLeѼfKrKU-IH ݕƷ=Ds2Wj0{Vݼ|1˨߉NNyoD&T7vK1n U]sE9°==+O<]qm.gklcLd/KUGnؼ$TҟKR w1[,up"͜gipk[ "<"} Nf~ 0\IL[WM1ڗA=LtJIqc u^jwT?38% i'ʼn;Eѽm) »\kCmoXM*3Ͼ4^qUݞzsATA'u=_n/MƏ?Pyc֮|H쯵ONaqy{$rJ̤jFw>T o_5]&k1E5KXYq#!Z@P G׊ii@Y.-f`&v |mhm>o,,j]AGjP||8<)s{6tpo.>ɰn~~մ ֶ0~i%!rwgi'q OLgQͱ[]KȶP7+gsq/E]oc#5I|/}t[[˅EgnmYDdP%Xdgv5);{WMIj)R[DKm-dhPmU+ZTuu4M\|HK`oʛ_BZd mfj7"-ѰN]~ڜVNuhq귰K-y#oԂ;-h3^jnEڼci#Rē.utP6gNO@KL^.ⅼuͥZJn$p8F!s^LZ_0̱G2Gt4KYngy{ueIyi\F G5C(((24F9+'4!u# kpO5Z.d/Ny2k%#D,ۺ` 7:LGYEg"$r[e Xi>FS߹ftj%yY9McLir1o:39M{դ'WGYNA燭m뻅ԬZeW$Ae]ndӴ ن6z Ж+Pd63˱n;Q{~stUZ;{g޺uǖV{e̮N6w%r3Xu[jhKI41i) kJuYwiB F·il..,\.g CN9iZzLqi#HҏsF;W7P=mIaӞ+˩nDBBvܿ Uߘ,D|+IZm#:qqX#R['E&٧Hۉcl^h\  vd1!9wDеڰԧ,n1ٿp91M*0YL w]űCLדW/մg=Ni,H`R Ks5GϨ)IM"U7 犉թ5;#Z+iIYaXI PHΒI5zg·ψ3=\ᾢ][^`|1m YM,hb7<Խ-NMh{;i^04ǡ#Xm"hVbLt jzwԉplݟA#'穮ޫKjZKI @˵I ;y491XVpX8_z [ıCqFEw=}DKofvP0z-Yq@dD1:欈l0Ap6n¼B^% T56-Iw'ZC"f409=ضG-ݛ$d#]֝Ctn~Ǜ3^W}gF~P[W T\(B@8=꬚Lqln!LU]ys1ƪIZkxgdiGdm]Cm>-oFMe0bsOj T@b9 }hVĶݷ2'4YܵAK=3\ ZECKYnݐ0\uڏGI:-T/ cZKp=rO涵WΈ:ΖmeIiC _0?JOxPznlx,lヷb:7=_=H%bqtxO6[k;Ckh ,6-v [+;U ʉknG∦tsum&閩.U<+mkZ1;F0A @mq5%!~*_ۖ0e~xύg^j\Zr4?aO1'U֮O ^[7p-d8clCvP=6pܴ ~5[[B>k52K#k_cܾsICgzk5;=mtd F҄&z*^[pwؘI"aɈ'yeƥ#RPbqJa8<Ĕ ;;׈jVh8V+ kwڴ7q=F;=ZU'p<[G;R5 Ryώo5j\Au,6qۧY6` z>kϨ5CD+Zk?grK/0wne]Ŷ?jYHao/#JW;'~ ^ΥyjɱR؍r$[k&&u[V"6u_-ێO|N &KgԡO]̊"z+ŵ+k׈ ^u[CB< ԑמ"Ykmq5؈CX IcMm0{#HpkO{{8'Էֳu ]^pmR ]`qo~{W[:QP+IhNGw=; |iW3\2Ug F#j g8_ڝƙ B! #FBaŸ}PJd2qPX49ް(lq\~+hO3nsľ+9n%]!kV&,Mgln:%ݟ3^C}MVJӴ;kw5奒4611pz+>:Σ"&tD ?+ڐ1d2 }sOHFm)'i3^{EKR[[ ]BK5r,O#5Uq,66% aS^-҈'G+<kyYpTo28>c_FDVw~oѮɳoO^7cKf6h'>o4iel7WH1ϸIQj0B|Tp9]kƃZFLoٚmUg(#DǶIh@6Ԏ8> Qm'iWA5xQOWQ:p+ q*W)uZxoGSKO5쓘8P1s@ 8SY}J[%%) U ]⪢*(@_Jmfo]/඗Oy=@a xXcĞ)ԭku(m[YMHC,Z]:f2;a30b5=q֗B\f~%`|H^ǢjVi[i:c=֭}WnmXYV#{.bhԗuPT8wԢ6Q=տ¼_/$5V=^ KK9chTuަgUës2 mihm3n(0w#K_Owye 9^'xڳ9ԯѣX}+KW_x+YDWVpiG.CS{Y .KK QKelvcž,9cKxK#ו7{izj6FGEpH5oú,u_W[5%b*c Ϸ5KVО;tۋx{;Yϑ Tiu{@dlͲ  *nB3\'ƽLhx_T6ݛ[bw8'ڰaYFxM)GTTDфf'A82XI4.ۙ }HiX[-n)1^]kq$ii=H%nJU"cJ,e|7y%-@^gŶ{<ٜK7Oʴ ( ( ( FVޡsuj!6"r[>iV *4%WoҐbNb9ja!VJRg[|,[<{WD|>޵~4(4(ae[aH别5ކӎANV)X+xwZo/az;HevSw_ k()!5 G~y:;iķ 9# Ř8$+1<1;6s ћ¶ɦ["V =O NÍ1C/Mn'Q@< F9%(&rOrmlaHqj*ČO\Zhh9.Y~G7$}s{VGh5ż]SlYDY@u@zܑ+i_ ;j67Ey%ŋiO/ Z BP &Ī縮ŠeýO^.a[0LǵY6zjm>\y]UŇz|:ޣpT`qۭgi ӵ.wLkKkAn#,OsutPFYkobS\8^ g}i兙x;Tn (fANe',Oor19t4ȭ-X餸S) <' κ(iXD71*J4maB9t;fb}sZP+Ωu{=Xs$%ӈl3]uƙoq> o^IیuU(šdh$NWG蛤$˷^*&-fV7 LzJ(6 _SӾZ}]At6C3D :,omj$\6 `zNe /e;O$.2OO<ЯS i[kC)e ]JONK(̍' Ycɬ=/.>-2AmI }u4P7yH٠si&7>A?UC\u:ב,ƱJ!tYT <<H&Ú fE^4vnx)|mB\vVS;sӂ{ץKBV}kU6uYW!`f9:zWUijZd37(B9z"i%yp/b${{ڬy#l_N)nbs$vdFQ@`i~t-bu{ ci22z{xs:w.~҂v,by٣RNN<}+< oo+ ַ 2HB&;<<خtW ߍv4P5h.顴45c#G|P>wK㾶̐G,$p(zҊ\zΓsp&e}7Qb|2sca= w$Msq,4$m&9 WETOIma,?1>(~Χ5Zf $- gqW;SYkMFT}ڱ"(UP>޺*( \Q'ėV:spQ$vJ՞ !X[[roy$|n9 ]% ]9ͅfC-7˃jٙ$4F֞)iDZmQV]wW2]TuI T]MNo~ mdEY>юwyS,jxV˄ h7EqnayQYbQn -k*8d2Dg?S]-eXkeo'm0AxW^ uKTS OQy|WIEqP씬7 w"ʹS ;&ZCJƚl<\'=먢0) [!GK S/1bŎrO\ME 8#փ "+xMuU,N}4=.Hn#]6?&xSQL6_:i6zlp qvWG|u&?tYbR{wjkܙ$$Bb3ֺJVӾhw;MM;<3!}l']!oJV[SAP>V)iմ=ZNFi%3A5qc y LJ2I֢#; q'cCUhXd@ɍ>⩿ ,rI`R ݷnvy@BלǑ_xJN@< e  xYՆhb:YN&w}Y8(O&;r)k◆b{MyZHR'05sZiw<*(iM5&{,b̾n3Ǎt ^4ϷX XZ7d@-Mz+|1&T![8fD$yZ~xl~^4ʠrSp {-/BC ;"i =`kIwr2DIwÎr P;L1I{ƺd1"5k2hz$Mh 8nXy/*GiY@+Һ?^ KͶbTH# @W|Dt[>KFFD񷿭G|H^i2=բaMFC{iEPbRh%͉ȯ?/ɥӣ-ahev3qIOT&״M+@ o 6 S'XXU-a4{Fcvh.#F(B=> ^^Ь]%mC|8zhbO> Eq77];5iofy$KoWV>"4j:lV7+ȩ%%=)]nQ^_7O(Ӵѯ%'Mf63v֪rxY:}CBiVG =TTp f %7s7Ѽ3q4hX`}PH2G.IafK"Y[r]n:39:IEq]Zy[IWBFi-WWL;(D8H~*xbiiqsn>қI<;vۀsS|9jiח,&`w"{3Ҋztk/ok$c _4f.9n9@fǭvW ?O [Oo=LmI kG@މjZ|t]%x7.G> UHN+~!hZƭm-w)hH`<ՏjXʗc3n@ہП^ -p_<5ewgsw2i7pE!`l`z>aRmd%amOLqEqMHc,@4gbPB]UWv[HC"F^CW=^6WHGHYQX2Z0d V_Y3 lc:~N)7W-_hƢed RTVf沽,U I{{EpoKw$W;FwU8. =f\"4{Ys#pABx *+ZkM̾Hy-$Xgߍ}ֵkmIԯܥfYYTU:ҸyWo Ɍ]\dc-V@x]xF$00vcqon[EqpJܺ5c $F %T↏&..eG G29}.Ep?k гbHS2_zi0D"J0:+#x{R8.䍭({?^Vi˩xx/1GRPUHN>^m^gO98G 5m>:knRXڻ ̠Ldq@zZ] q$Zi69'+/Q|AeQz Vz-m%[Hd0ȫnb8*S*oi%rۺI= B3z쨮"oo7rݩ[cve:d|vnFasvfd8N <ӫE%y,lf߸g׵g]|QͥյֲgI|`uhMǺϧQIe$|TzW-R֢ҡ+wŬ+)`8Es{ߊ>do"ۻG8 cYiwwy%hDaxgaEqmTϒy[[I*ʒT29)pFyEQER78n4|k..6fP'Ҽ&4[s.t8o&n''Mz']>3Q 19ǧ^wwrO.2zҽhPsӋ}|68WQA.JA.x7" szF>jo(8+z2ZrXI@c§,OCp}uz% 7mo!u ۀzװZʼn?kX_~P?}zjȿ /7g:+v.x-5fhuk-I_mL[RGYZD:vҖ(L8$~zqֺh/-gg[yᑐ0m_JH/.$)JHu[yE#ɫhZ֋e&+yDfEuەc#޲A3kᵋQ.%LeF\nYyF`ʙ#^8[RSFpry-I&\Auj6 F0-ߊ.񮵩%{,}˖<3J릿a0$Fp ViS#slc^څyT^N>km/nteAЮT9zSυG}=ƭs kn0ncxT']-3yWa9C"mє&'0ڼXfQ*5 9|V&j\]4[ZS1k]LZ 3^&gK@\nG{i, a#R3G{kCEk奱~÷#M𦬞U ֛|!ؠNst^',Q4QR@op3[KsnhȌaC~{Ǘj o X嵖ɚEĆ43#>oVio\+`q^s}glWϹ=+ɢܯs eW{`0_hEkMcu w1)I=;Utbb%LJx=k.z<z˼6-wmgtH$YՈqЁ+-k~& #Eݽڣb$,;~f\MV&ͦx[@4!xַwK8.̲Jpp8潫]4 z9;;~G՛+*Eg=VRE J/c·IB;P7jj1f?=Jx5|3׬Yď cp)fK8WuS}faIM>K+'J8yl#-1pOsև-5úDgK,_b S; F|Kx:·k$$rϐ/kn/-Q{b&\۳Dh 0 S:UxN[;ES7,]TDn<ݫXsc`FX)gOgZcqnBfy\,>_%*MR Auʼn>!ӯ/}2( }ImN_?x} X>k1VfmekbG SWO.đRRۜc9Z-ث7VL<{RP5y5;[Yj lp6{Bqcx}m2M2Yfy^ .O$]0He;~ $:⺂Ga $i,u_Yt׸/orڪ0 9#h&ߊ.V( HoO؏! q_Ԥ4K78VgY^ VS yv ;}8pN8}OK& wT[$]因zP^.k [P CQӛOeb$׭A4sF$Et= T)}i%[q N k0_1[eF$Quq<kη>w,f [;o? \ 犭y5YIiv̄WÞүjͩ\ {kP.LD=Z+S𤚟_mF !4)kC&~p+|/>!dK*(x:j =q^myks\E(^6?#Tu-M)#b`́=ƎUmO:ԴJҴ+ t=6akc UBpIlk[I rZpޣZtMtmBu .23}JX^G*A&\jzhf+;$#G@q\,31ex@r/M4VY#I PiXl`?Zmǘx{ݍ'_\Rnv3UuLO[CK%+ 3{;]nMk$,;*vaQ֓68;N>ӢҬEn9jmQpNk+B𷋟Yves^\8Qg{D3Yh"V=u㊳o471 $N΄S_Y?M9PU{JÃgʪjm-Qiힹ4&n H$纂)8ZEbj@'Ғ#3jzouYV[[o\H$ pv^y$Zw7wsj*HY#cf=JC;v8'@N;C{i4O4WQs/H86y}焵Nm=#5|"Dބ19OhmSM#KXf.,䒣ns[^Z\0JSYZ-4:E}(vWst;+[X\y7|/׼1uhhlfysu$R "<5CUҬ!ı_+QXyֶ^Q2(4YPp~Wͻ4Mq >}zs.4k=QvyڦbɝD}:շeT,$ҚYާSo{=An.ud\r Hs%|=SSiS[/70ic{ZOs$.Z bbdRIK!ySjzܻ9mWְWD,uYDu1h ǐ1ڻ _KgYnMhl4watm/X{mFlS1?WlV?0[*`>Vc{[+˪kpNO>nO)Xu6`Hگ\=k/m@MG >Kx<,$d9`=sD_.+q"ΎMF8/-J;,bs8XZh h0J9ǎ=Ϋg,崲Rܮ,%F[?tW9ƋርMvIEVf'%\k~YP:l6_.  ;x[:U|&KPA,X;}kmcyN:kF8d[֖-1w zfj.nqBoRjtkZualQ@sҹ`gL8yxgPMᄙP4_~g )>\5x0xTⲺ <#l}řt&k6S[tGb6<һx%0GsJ:V ;7l\`zuRօI{c;Z|-ݻiCOuW2˱|GwuEg$mXFjĺnzu:t Cnm^y5{Tra0,xz|c fne%E[. >PNx>_]6X@8]΅h:%Aw$@$wⳓƖí@}֣4(ۓu8tSwÚa6iV&uBWcF;7/Ci:ޝ橫}'YzMy'K./uD"y '*>'ɤХ_[E>:g'}{r=n`i0~.%w}H+yyQ,yӥsxgW8m9RRy-C9z$r2jXteK\.?X"4`! M!oO Q|1;U#ʮdCSZ56 )zvSPI pOjځ(-PEPHzsKA@!`S2/GWvW3e|)Uۥxޡ둓^ E[3N;d[.<<7l`?,d7òxn|I9 dUtLbo3o]Oï x:ŭBs.N%8WVUB-b% j@ K^6Nߌ[J~"_Z=cT&}6S2 18(G5/GֿfbbB*I= `x>quep I(2sj(~>{S[:_8L7HWo\硬𥮡'T1|vwg'{WДQyťw1py߇4Qȶ ` c\=@ E-=Q+[oE4͑?^fPgI=y+2-἞f+->X r3yUKRl|rVw U#7m$ ?>6 sRA;__O}#ZM>-Ć-u0xc Z[$l]ǒg@ cPQJ-Yӕ';!; uU]Z{u%y$ Id}'E /cпO6MR'gʣgnݸmkC/!MRiKi͍aK/'4_ rZf00S`l޸g &mF힣cx?݅1#.wZݕ>Z}>6m;͜zX`׿QI>iIo|qcڜ^iR$S)NۜWmjNޭwj\[h[D0'+(1|EZ>,I4^hc`N+ B!Ԯe )#%1>Nc 0 } EH?;[ͤpyvlUc8#c7 5c3G[J=[q^E Y>sM3KvPUH#UVҶ g ->ke>\r4nYgWQMh&|+<{y JBƷʠv+`;9xAjuiFPnivh?n7yu SW,El<2u* vN#mZvQmR|zbg ⾙} HMH x2Ll5z-PԢg^ pp=+`>kŒK[ܛ+ 8!$VƇ3NCNݽy H{-Ϝ*t7g-%klYI/# {In!|!><_Mo5)EKݒ8V FBr=PṂKIu \+q७Vfs̬hBBWӚ (ś+xQ_d|q_u]=..Yy,:#󮪊:X:K/|+wWE<;D0ecoN:VQbTZNg٣lN=+hWQ]iך5ȽOFK(?ZJk?xL!]hPBd>bҾXgK k g}K ɘ]k+C, \xy`_Qn$yk ǥeoY|M42[C{m|6V9{=u4*MS@T[ ~e{[LnwˍNsmO-,UGSxa1c_D@Gȴ;mn-罎[?a%N:f㘹F״fxKhqTvGZ)5qchV^8Є}WYM=擏jkf/5kĞImقymhwXдM{YZGrgt&:ZXBD+9hԴ8/%7*1e.@rT]W'𵆵5m]|`ڌ=Ŭ;rc(H۴9o{;v,VurqiWRܲYl2@at( ,,2 [gsk$ zW6?$m;sa)cH^E|+}zNdI/c+xN gڝEQEQEQE}{WO:ր,QX/=nZq1C USc=Gxfp4nEPpE!"")d{pE&h֘ E&h-Rn4-FE-fMŒMi  PC z}F}hh( 1Z)3@`qךZ)7}h3@ E 9Kq@{њZ(&E-fw08Ly-FI.cRgEdg,NTpTz9C۵!ޞM۴[*H|O.Oq񽧈^+ya/4q`J' BN2V:"WxѮ.}k%R/hm̊) +mW~[Y1DdXP6q,P4di׋E&ˎU_[,lۨy_3+}{so;g}s5̏kk?hXZ7q'wnWϨOZ54 Ne錁sB WW[iҴ9YGͷjL7|Oq{eu(]$~挕늊%MNE:WۥbdI$ 񝤎h(ݵoUj("]7NLfg 1M/5AtHgXf@t m<9f+sx$kډ7N;T:|Eȶ׳Ր}KPVý/gE]}mB[x m"50>b;s\$t+m8Bs9hdSK5 q tB6jdf=sÚJ{+&4Ӗ%4ec,0<ǹeK?![A,,#mH[ :Ym┵5;m?MJѦkk,k xt=EῳcHp ;j :kr4yr}㸍8x[ݏ,V(^klHP=+şo~K]>94أ7 rG@S`8R 6&k$w n(E bxK^MOKۘH\lV! #A.Axi!"X pF3b*ֳV:iK;o& #y\-u[U Q @!1L%9;pH8ҩ_6761"X>!0~rzU\i>)mȤGpxlNDŻMlD-{Wuv K<@gY@C ]CAk^M"0@e K~{eUzM2÷̍gZM_~#7#[׵t? fUFccBI8#|.W:ƭI<] })=R*eѲdM{ac&7+4Kj^J H橢g\6h,L=xWsw6SK{{ʘ3T-BHmm (#pߖ.:bh/nlm氛OeHr~ײxA\&HrCn>b jּmMH/F+C׾&Xz[`eIKw&+UŵQ]\$k?U `mjnʽpH߇i#Jw< y&o_%F#~'%r3OZb7.IR[xF!ḎMc及4m@̗,@tM"6/dCh)L$u/%jQh: X({}-m'}cE]0P7]Kf(t;9fWMJW@w;嫮RXC\麅)e`A/{&{;OR`!L`_p -k^<i[Eujb-ʌ9!Ku6)ψ4/Zh.T'= U:}y~DnD !U z<)隍X[Mo(eYk'7zv^y#'k4McP}" ZEM#P2@juiGii/}6.Uu[xL|yzHx[R^⾹7-+ p`c"7|E?\kN9ѧKaC.r_#I<{\Fn{g^6mۃm9MB?mEVR,pH~L`zf628loCnT !UQo7.5A&>E*Xr{%Γk$Ե[kTA*FR WVZpki-mA*We"֧aoH&bɰg1[_$ꗆK8&Q~Ύ0B 8Jԋ%wp90~l{WK(WY{)'|bjo -)m P+޻m'xı\4m\Lˎ?-_j:ey-#H]X2}(#>MsMⱲM)0֭ \kw mE-Nwֺ/zΡ:ljP#²xV+\Fm4Q8G={-(kƩ ݮ`0&r)g۷n8txJ6zŪ}ڻG XϷtԵ%=R,懠+HvmngCj,QV 3[S@巎|^ Oea ϸ2ON5r_=bG6?ۭPLDݕ5<2Se;e- XK\ Bfj . ܷ^&QLfD 1n4de(㊱ NKk|nBF=h|C6 +N&.NVa"I'KlV 4>ݔ;dYV{I 6;2js4Hın+ }ED ŅMuw+GF qHӻ6YwApOG7mƫ4{<$ 7ךÝ3ÚF`gMC +7"Uڑt Lxn{i\e,7GoeT\{n?/XN\0,Ix:zWuxHc66 X*۸x"o #Wz۸8>M ΓՌgn1|r!kmCċֶӽܦ/>Wm =s_FZuƝ%.2yaxz?rCR,IpFmI[jq?uj="ZM6m6 ]yt#8'5U_%̯kXO];A9]|ZXuv%vPՋ|!ӵ6w=K8ryHOX"MͪZ^BeL)`Aj<9>W>?[]$yb0#b|4+ V \ԯ<[Z[ȉqbO&  ( ( ( ( ( ( [ޛun弱2#;IZ~T̞5nᘐ_\x$FP8`Ive}ky",(RևP40+GRN+u+x.4K9yYC!:{+ƃ²>ov&KbO0)A#9"}}m=zṾ!HmBodNԯZ#<s}RXҭu mrDl`}|YF꺮zogѠN s]VqmjBZڒT6I`Cz^ռFGgv,32/ ?A}Xۍ.L9Gq%;44OXZܩEL$l&cUh0iwvvnh&#kr0.FzxGW 5+KW+x ha|W:}>i4 p'zO9#Nz%gG;=BK;7 "}ۜqg5Me5F%T5PpPOqw^K)c@͎Q'S"K=Ji尖8$Jʢy5뺄y Jr0^KڇM}=:-f t'kwይ'R4='NĖw2L7gpkzvigo53eOOFngqG\Vmw|Rnax4%ۤHw;/ X7-e\zRx`tX󦶎2[ǜ+?́cM$,V:~KB[@!dc"|k]B׺Λcj_RQ% &@SQ ZMh`Ht'G\~ 5z__Ou%B@+;jˊ81'壨t/R_`jIg-(όN TR]]ڏCjc<`N<4hl'&IdenU%ղ>ah )7d57O NVQ8Kˈ `!L lPŬlK^rz"zC-Rf}DŽ'ox-*r;(+#*jҾq~ [oai:O/;av=5[RI|aX?56WsH=kqihLw5^K,li^0x=o/=4[nZHL~׺GɌD'<SS=̱Aa xK2g sH}jjmnfPrQ{j\ZzVH{p< +ڽ?H~4 R~T9؇yjr^ x=uMCA^H ~,GMhY|Gҥ5[/4k{pF. iv[bѹUg#֨GVCI`aXZ&9;ks]=i-n}H9?|JهH,hwEOL`/Z<[ M ;9h76 .t!%k-4;hIG$ap6)=ޛsXi#}ެC D\*%,#Ii Id#Q߁V7._nLyd9b@1IRVR}*.6d8e;14U7v2is̄P3$c&M[ K*[{V+yR)e9+4 4=2dKh[}񳷗wAzt_]Z_6=Քwis$ m 8 m5 DKlna[uuK 8<CS Qy"I#uW|Wk"].DYD_ey٢1r fIӑmtd>\ Z:,#r麭j-' @'U&irau]>Ox I'<y>4"/,3"h>\|KO@&zem4$Q.㑎x%3|!%3Gl!4S$SMֻwKib#}^+F9s5|?~_MIF'j{ Cz^\BaYbJ1aew{jgNյ;-FX@npA~)4[[}g% gxVdT+{4THU`2 d4-?VB;qj TX2SH⶗s6p fX\.K19k?Td%dMҦn(O<#CFcjګ] W}7K`rg ~‹]VK{\ hr W9qZoAkng{sc5]Wܖk`PA^k3TߖgZk{la,q2iA,jW s1g5k[^O,ǘ9asֳ_Au M2; nex*̮=E[֬t}K-ot..}GW9q@ЎZk`$ qw5&>K[KNH䭌>gtvoSjJ*Kqy/)U@au? xEKit{$e`@bNSWIXQ# 8;TzĉNP6dn|z[i~ud.>i%ܰDWG85gI4 XzcjZjI&BݷA%$u#" N^e-F=li Brkk/#~ Eʩ($\yB7e_ ZO{ou8mKt;F=\aKtMpYZzώk^M ݖRo5ビP+I:Y>Ost~%tnXZ vXz+[ 4JNG͝[ 1ڿ+c<Фдya,4YH!%G%r)!kfꚜ"I$r8Vgc4^ ɹEqϥ`Z>+濫Iҵ+zs;E$%@ AZ|+Qo-uHHRn_gR[+'V%bK uǥTѼs{/5?PncӬYJy`f\P+>o;7KH]IoeeP3F=MlEmz-oYƙڬM!YaB1Q@:/ĭ;SNguso+99)oJ1MR\56JWo&j:~qCh7Ϲz|Օ/׺ޱjtWZ$/iw#FC$l|HҎ/E\y2Snz0k]\Ygմk)c/8;ԫq@ޭ+X4ۭ+N4Vs;6 wHFAP]x/:~%]1.YH⑊F 0MoZu"K-6d;xnO{܄9#fh>WtIy$30"]'@cKMwI3z:}GQtV]B gDYpxޣ>!çϥKj^Hp i so|YYKcjp[g4S0A>JHoYcSGrϵ܌l8 }\ԛWXo3@gPB.I_ *Y'pJ1qC\mKJӵ7Եiq  q{z־xW֓OO PLΤYQllڗ*YAujWΖ@QAgM*COm S_b+Xi>]] ز*t0 s@k-Egf=yD;Y]E;ma9@EEmȏ $ݷ{ԴWw;\1nbd$08Z!@gax!7dv:a7\)k%#oIk.ҞFx1ާH]^_; %0EǖY涖{Mgq?0^Zēɹp[~yyԖ]G.w&dZvE|O5'l\/5+:xWw˵±9iUrE=7 om,]4.V5z:IMGMHfP91Ⱦ̧~C_<3|3 D~אxzѼA&XRhouoͧͮm5\r@#ּ+H k~$Htem@Z-GVյ+\ Q"1d@W+(\$@Ѹs޼%x}rE< )fGoeQ>¤[H' $bT U(#8 s]LK f\)ƼNoSXW;{Bdҹpz55RkU-'.:tQϭgxK{Wӵ lgn57qhjħs,Z2=kmK^'}wwZd=1^}pe7徵 WPٶҴYҒw'0pM1H7#kq/-5;o.i-Hnnu>Ǩ[ū_CLDOMn\u gڼhV^ksEqkȱ4wҀ`K5?l]WTMh}x%p{9 /$~$n>pbi21ai5]O5cYKE(`VL0HiHQFGxnI2h3\om;v{WjDhv$Hgiaac" fŮ}z mq5 otFv^&Չ. s~ Լ!ꚇnNa6TSpsmZ{)ӭ6q[J0$ xoL#{Ğ),"'ʆ+ub@22kGRѿe?-α%`%[$O$կ^PɝJɷiO玴h۔yp֭ɧj)o=n#W`@hx +`\.~RԮu :t]À: 8qHvƐ~uq,dcuIsUd ϥ.g.kZާo]7 'Qn=#Q+<ܶx^ȿamA*yQ۹B+cUk)|EƬ`Ig<Ttvs,QHּ]:5NkVˎW*!Bppwmԭo$-gId.elg*DQ7mYܘXaxFO"+EF;Y$=B"~j幹u{!pꚍ͆B-C(ն_xJv1`a.HHvs©iz X\$ pGbBO<5OQF};xwɿTYRxwEzwc[f$~lPРdQ\M35,{^9]&λ?%)o-՚4VvSqw --RKF3:N#ÚD޳9o.nG^K#h!O'*mUtIu|.A滸6C0S]ͭiIoupv4PIȮFpx5gu-bzJ5=-_ZϱhbRO(J8j@܏QA`:^!Gw?uu;r=4Rn#0zbCzֻO!bXiK4E=[#RzϾ"ơO4Ybկ/Mg{5vuY7^<6FzOu/O;V[,-0,NOJ,]wJ}3Id_+/98K_R`,n, b|. ´pHXt\Xjj.=2t]Ca,Wis_8i1@Ϩ]Ggi5̡pv2pxxWm4Y_76媎ג-{5I>MzgD@?]Po>a'#ײ,䫺vߕ.{#ך } ==jO&ٵ"6ӼIN+ִ|/qgHkI Ϭ5 8Cz)Fe@)<(: :¯zfėzw4};p 1&}:T%2@/')9W~gkV܋Cq%bXv얓sn@$R(te9HȠ (((((()8=(5+ r}WD&{~i6B3($R9 ~|O\iiچ2bBsn_jPXyooG-g]Ե3OWAo*PZieHxU׭k2yDzM2o! ov 9>bqPօgi1,/4I :dMYV=BhԖ"\ˏskm/ 2P~r6??󬋹(RK@¹{ ȚRfG/>SP]EyQxYlt-.DZWe 3]-"mnk5I_Xlsp1Ҭ\PK۩c, "ayk]~6 Ҽ2FqʝW_JaMKU9$}jzZ;+Y^yEI|Յ oS~ty"&// v@3Ҹo^aѤmb)Z aNFGJ!񇉚4Zmbm3~QHٞ=3G+{ֻ=Z[xd4xXm(#VGm[m>)m*5?¤ +/uMxQ{;+;[i-`,W<+kطeJXt7\W>ux]h:5D>W(!_B~vti)c4G7u8}.=f]:k' |%.뵱Qͤi[A.g$7-Mdo0#G:ߋAjPhmc1i$R,OqKC_}&VNd77ck-6<z`v6Zck-aoo0đEn=4jm6icy03g/n-O":k[^\rmntpѻ#1ڪ48"Q֒O*(<Qp=jH.`hJr eaF9VoqCci1mgҼMKx]vmX^|A-lGj[/D ԵXHXPhumϴ[? y*OO^..弸nI3F]0w1 OI&6?wt? iwN.-bXp3X NŦ\n ;$e۽s:_ cM0nM=9WctK;TNYL),N7{u[[]Gm-)hAwcuy9ȵq'捅źɷ$ [t.A~.ԴY4bx7<-`qڦvX=$;U{fX@0j?kZ\1λ%X#Q =C`r>".m6Im|Z%-K;Ui`UӚ-V8<~4 藞Чntشk+KeET 5ECFj 9~yj^%-ܖ`!Vhbwx"WnmnXsN~rQ7ir-Ss,Ѯo^J庐Il#rb3ۥr<յ<]#BPYqF#kZW&,X9sf|W iھc t(`P77#ՏDwD[܍M2(@A$'i`އo~om]2;۾Ж=(u{ :R+˝RHHb` 6_-^$Ʀ@֗sY4)6!=GU Pz,oGd'q{d2gyjݥkio^(LGSj-^qA媌gMrԼQ2Ѿ ZFEb>x<y }'Qky9-'HxZn K+a.$?gכ<[78t!|sҠYuO6jmgʍ"}=[Uд_gc|W7V.>(^ !H X1{VO|Ea\iZ/#Ysus:=5Ayqqv lϵ0=)5Kƽ'qkX̹݌m:[[[xw/(0cqԟzLj7_ <(QKyV?xwLƙkF1dNFx(jiLx#V`zެGo o "B#a2`sCLȭ=p- HYPδ5sĚ4z}οfڭ4 [v{M@Z'4Yu%%5mt֨eR:i.|9]_}HekI]g?y|;=ׄtH>-t,o)W!XFy<{S{4:=} R=6.y99#4 u.mZ;ލG='n%KX u[Zƒ "dC.O~3:mͅ=,LM?6xK`=F; 2WY.cӬjX c_ҩKO Ki/Dc 6s\r[h~iYhN:W;sKw$6vwV<` UpGZ`wiR Y&x¥"17j="+,e+4nbH_j:g؎DadbAێ+NJ_I/ hR,#,Qv rh4hi4:̥e6Ȟb6cOq%am dB29 9u}c>w֒CHGnJ>*>(k)/SY<ܤ<yy!ᖍh&m EX,ўM,42[F#8%\Z9mEغ66֫i7Eǥ5E#~bZk]CcscҮ d׍uۋk{5"<$]v2?4Jv*8cqԼSWZiZېd+ȵ\gF~}0)åS[YZBFc%Z:W9} յM~Roڨ؈ y^Vu &O/$=zv;W"<+cZKuvkS 22<7=l- xnm T=iV %ċcj$Ps(1#)j:nct"4\W22kWS|_wO[W}kB[mPsӎ+Oͯ /{)s>N2zU𧇤:K\ny~9Kc'kӼSo],moN_y3#ֱa+TzyiYgSo_xUAsAi"˶rFpk:5͝Y_-b85]uMr⸷hRS OW zu\\Z=E;[ϫ{K=>9Q<ҽc暚ݞ"9kh>XF9;Tqեqikrv0pÐFGQ^g/4--:O:@]Ylr 5TiU1D`]heלn-el*|Ϳw9鷯U4"X[h>v~|bpO¸[@wcuq^"/Wӎi6Zc8Jӭ \I #n=N@jei[4]@~sO+"'[Vv $"9OpEhj,/5 Fk-Bxmmx1d}p.[hZMCc3(HUWQR{t{uׇK{L+%q#~td.oX (C Cigc%=UN>Q1^QoYMNtRnۧ@p NcQ_5X\iֳo39 Ko_j^tw넶A!->4 +CҠ_Y"7f.Akx]SRIZyH-@ g%y!qPi0^\]Asp 2@1ޡj-B&R#Pz5Es3A}*N-l-nم+hioV(Hb@0MEQEQEQEQEQEQEQEʚZ x? k:3PKs"I x}T秵I{b]K]ˬ ۶s;ԡ@Z:Q#^Om[Mkkٿ8xte=6l|I~˧]wvu!a1<2_#=i6̫YT89=z@01K@¸gjPjup ]t.5@,`u{-ͪDo# F88+nLy 5N?wa>?? ɜD8=km4kZIMNN%}6W^M~X$}[X]^J8{χI ~/촋 2XEח29nkmeCowZO`wv71w;vh|--I{[,\)pzf¾ӥM5 cPe< oQLONbYPDn5 h<]Vh h͌GϭZ8֛ܖԵcQ6[9]J>isN|kXg/$g郏z'%"X{R:bF-z eXAw~ Ai5W`ʊ=uQq[~;׈eGڡI˻Yo 0H<ڴlhpZGP3I>%˱vw4V]3UծfYu;TG.TScs[ :yw{, [wX/sᦎ+rjڬ-sZ2&jAk6Vo`h˽cnIsֻLRѭDIXV,P~nsF{jZͤ"c1٘>f:)­Ѝԍ\f}v+ZEVKvS˰UWGj/:jڌB .n̓`*? |#ogFb1oj(/](,I!y~յ xO[=DIK9I!qёB+kL/匚=y뷰WG]m$qY5+,-a0,7&&t?G~QM-M2GMӵ]zozQٟr8ideͨH [0|rqh?(;m{o3HX]WSK+wokvE?(|7/$nu;Y-еBgFɷq¶M2O++HƮ'Fk8i6zeƝqX5 lk6Œ0۶͓ωt׬MCQ(Ķ/Ey7~"JWwײ] FwWB 6nenf{ۋ(0 ^خ܌4⬾Zj7Pkۓ,2>n0? k|?o / [ ސҲ|ioci伇xVyn҉c o$p -HQmH'%\ 0 t2FֵGV2ac1ڽJ{Lg,ltWl4yl.Z"2Wjz6Ҭ?W׬-m[[қ6kƑ R3=֑0rAݖ5ZK$>jKCs4 Aɫ_n--zz= E_ZLV˦mn)sI$y5QE \4hlYnK-4BsJz욌]A)vۆ8溚(| [exP!Tԃ g_ =: . keqvona]𾯥hL4یcں=:Zo#L"-'%U2i@3J{:ڞol.젷Q*Ϳ<9%{H˗뎙ꨣ:>Z_,vw,eHNKzbxKn'i~#*~ۯ4; Z[MVd,^dq}q^g4c hX5w.e3Hxn:}k_WTVo@Kݵh`81r},3\}Kn]iI9 FK料{[QݭEΡxm!< ۵vbm%Q7[_1/ aV[-J'خ`pv? K; c'GsГdև|'j'Q[Cm˷с]e!Fh`xǂ<k6zV0IkVrh L]]ZM^Y6\] AUq`zq@p|'`Ն KB\t1koKJm&j;l 9'<׹B{N7_eԱ\2YʌuGVvGQh dR}Ft4W1oiq(,)єQ ˤ\ꋫG+bgeep2utV7CxҧiBc 7ȹ{{l 8hl`ЂaE`"H5Y :\S6Pjj akiϗXՇv?.84 aEym7Vnװ\YM \;++&Hry'~յ55XlBF@ΦE\=iI˨QX `zSEr|9jkZjIvQk(vXIeG,gcH݀onhRn-Xj%ؓ# T7wcj X_Y]իlnQbZ:월2E0Ga#H[V4MBBד $>5(UvggN{oxvGTRQcGF\1#$(8ᩴQ5XeQq%H+K#ӼCn7xQl7GmQ\}_ [_Ki6 D7XϽjE}&X5IcC/2A҄H5,?죻 HտOlsYvW^^Asg,SÕed `B:\J+wSSudU0 u[{2zڀ5(87Kdۻym #u pH<ַ|u;]I^+ du4W+ڭŎEbKƠg$8LѾ xo[K#y՞Q$`v\t_|--賋X Pw98 0S^vzӮ5X_eUsUa\R֛IR_uTf2 c8zCErߍV]7Q݈9ʣ?<45%mV!rX& Ǡ݌g([Qׇ[2RQz FςzT`~4j;~l R8nG]BX@Mj?ޓ}%r 7r1Y_]=eH,6s(;־( Ih]Zj7FݥHKѲ|4O^ⰒSKڕ1@=w3jE$1oq;q>آ>աC>#@MYWv7בj,S͸.pFqJ#2/ lOhz^COwt\!Gv'&ҾǦ]}?#SFc4Eq:O-!Юu sUh㼖=n8كU/~+]N9"mW AXd)dgޅE't&:ҏ:d_;*rGjw{:`#{h %RTw+\u׏-"M]{Rƨp3W5oM Cv7B˗=@PN=Y:-w]ո.7v6ZuckIٝʃ0P\=f_kֱ֚i} 0'qZ -A'0KFQwր:**e;FOz3CHխuIyQF~vp=_͘?g,?թ{4+}b]R%ӧʎ]w{b]C2׷U[[P 6k^ 5n!ΡtnU<Q\;ceK"Ѹ07cf _\^?Ԛ]5Xť#5cjt{! BzgN7-ٖ`~thv㏋ ״x5;ayͲD6]CsYZ+COЯ濊T* T z5\xVڱf[*Cm ilc9Fs:(((((((SKM(`/H״GYn#-k N0Vo4oi5KepL7G`;׌vHy\}ƺiKũG$4zk־k+AN=9 3WZqSbN:=Oqu}tLc)x)w8T}gM;Z߲_XoNH[\:VWUz ӌƪ x+ F"RQ]cjW0躆dtZEA۞iz7 jYX-N `*y{A]@ɬjZ%4RMfFxg <1w" -8<^n^Stm.M9#'6궪t]SH mi+ŬɽX dg-#˨>Ϸ3 ?zl3)ǖXner:ȋGVxn$ѯ.-.KV=C$e?M.4{&ͯgo#ikic\nuAz5Mbچ}Q &%Ϻ 0; :RxJk{c[P#+-縯vc=z҂1z#ޡ{In"fVL@8ke1]qӊeP} 3>恟;:e:߬t;Z]j>g'vbw5mrin͒Y)cFdC]+Qj)դy-bx \a5D-:S"i:Nu^H֌3hS[#jK7ex]$~m/0ŧwGK6yIl5K{9KMe7*63I{Keh7Es}cd8n@$xuԷv zdխ7lf9Hsx;{U<#ɩi Kn Q ?*+xZZl1vcԀI4oExLk{&)-*"Jr~XoJ׬Qs7rkiI"k9,ÏJ ͭ\j_ᙀHt e) =ph]*Oڃظ:BvqI_\: U|Cso)/8߽\Cq6<p{敤DΫ8]cš֯la3G#cgj浏 ksk׶:HMcd`W A7卦eO! !NOj4In`{hbI)f=>|7\SM2H[LiJmAO`xsJ=7.ZO% D&Frzc_2<qDfºh(Zǀ+ >٧F@'oh:,4;R;l")ԟ":wWԼNiST/gNў;-jY@Bȹ8^Ey5{?xftLO-4~i#.FXws[ +LXvPpx|g{k^[j=Ԓ+>X_YKG[RI,AG v8ފ@yjk_^!ZŶ*OP OrG^3Kt(}HDm`xDo-[#OCG X $,sc_W5{fzQxw_,ͮc ŊǟrOh-<<7:|Q;KicEM0yɩ4=[Ğu NTIKkmAk&-c'^zSJ0WbVD=G#y/|%UxOL䧸ڇb<O VcLZkF9)t'WG'XZ:ܓCwGi"p?(ZO-|)Tsuo&d7swZgu}mfOk8\F8QJùi{\O jjzuh@J'RѴMoM_OOZXm4e/0q^X^#XMro/q#!ڄ~vX 8;T."ԧFPFe4\M, ӭmH +B0<[5{ƳY٬fx]7$um/vH]B7.QBzױ0 0@#ޅ_YlumbY /23^eERhyЧ"2{ `L"L.˒lRm8Z մ\QόX]Cd>[#^Ex~uF=2 c$,7=Զd+ %k@|q(iok\q)Z}su{iQ]\ ۑqں8HQcƠp$~uњEP3P=h ( ( ( ( ( ( FyƓ{a \W;`l6o?MgHkW7LW[07kMΜ;UjVYfæ;/#<hbQF873{ZMljkk,eL==A\<=-jSutNc V/9-TGF>*UotAj3Xc~Ҏ0)kty򓓻 }Xŷ5 5cY@c 5Uf@ԁֆ>oB_Kg;Q#Xa``Ƿs] 6Z5ĭm^OɤK-)9{{@ oŷCtFqш~4C k4'ˌ;' i~-.gm`#;f,|N/6*zw <\k/Hpݴ?W|PWιٿ̽1+0&́/)Z0U2Ҷhn<2}dzV?ior^B|8_Cyw_JDU9@&9HgYI2vqikvNo gaEsQ;13p\dH w`p3J!_0z9hWsφi]W!Ec<\01 ڻO`^lb>\V5ra 3?|:5ۍ.Gcr.${sCXz&<^ ^Q͚Iw,[{9 qިhWRN_em24a푣.I$_JA6,Qcn,CZ[i ,|RJj۟'4d}3eLoUsVҭ5M)36tIz .$u]fV7I q,;#'һŒT{5MܫbX6/ asu"IvRA'ӽ{VfMH!.Ryf9$I_gF 4% ﯭKW j_&D-D7lSv,uX-H:H|"skY-^dDcp(x"=nJVUڪgInRܮp nе߾Mb[AwǙ¨nH vE?d&`d5-\iG4}k[why,a*_ҫ~?#<,B>^尫^!Q\7֚Jgn6],|.ɬinkNLeʿLWg7:xdaj8>Xl$[7SskXc9ݠ[iR/'?^Xn6w\rFHq#(#@r@8ĭr.S@Ʒm* |B9IiZ ϩ=j{YPipw̯+0dךeeeRtm-3/B@8%&~>,[ռ7 bGl՟=eYd7Ր pD 9 Wl QT`})F<`J|髾'<24ps-xXvxsǞ<~HӤ;/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-20.08/images/menuG.png000066400000000000000000000146021362435004500161050ustar00rootroot00000000000000PNG  IHDR@@iq pHYs  IDATx[{pu}vWc%Kl=mY~o`.!!@`MIڔNa?h)N:}!)$ 1ccؖe ɲez?V~wwʆI}ǫ~;s=sϽ.KYQZv)Wqi+ۥ6)~)+T3o4.eЋ[^ ?q0LpO62%䧋$f).o ;4O$, .v_ڲkI6\y< P*yIk VƎX6M5###IF;?f\z&[2C#a,ő0]N}baUY fDž h,TȎ EU?qu@sq lYFXtvv7$ *̇ wxCFx;w#Hxk߮(CRHvw#(H *@FUx ǎyt **v%%%U zTߍ,~ 88. "a+_9VXh7ßԍ:n %KruY[L1P[UoՌKA >wL8w)=i9 s浉L*,X}^.Ͼ+jK?8 Эd,2A"[6Waظ AO4Rre|;GB(SSSEv 8I89N/ibnUQzjz`RBF4;⺶v8qF$Fzh6ܾj44c~ƬmF"'4%0D8K<'N]dT03g0s'(wD9V(z$vFΘNiNeT$ō^AeugC08rz M+2q%cQ/#/f+' +WDLTq FOEw߅,;=? lZӨFߝ'IhHt SS@|FqԖDCd0`bf_jaOoeea8HMrDS gljDvPӽz555e񺈡eex`'N=ۉݰY8pKWS5+he)dO<3gǩTKaDQ}fbkDN)R6flb6/^O<o(rE:Ը^DC钭 F0XIǓSZV_?wVטuB Rjh9}EƑֱ^x4~>i`j8²~LP3p-kD^FȧXq#l3L!!G?ݤJW[U=2AGe|lTl>@%$CR`\M+b !UW&aM%{Jmߏ. }vٖwC*~]Q/ /m[ة`^u%v.#Qu>݃1 NN)e5 ]:e.E᐀a/ڇPEoGis7?Jre_(@~2u$߇?\/I4­x̦:=3ۅjgF9lO|#cGpq([*"8"y:@r8H׸8]YML3IS~i'(_ Dd{D)'ibJ:t DO2H;vŪm7cd*(WJn{}c; H_yEЛ cDJ&! ~bN d㓘vۙqx9!04'H0+qԨ$-Q%8̽MikTݷ܄2 c)W_GBr{n  [MPD1Z:cUeY?PxsM.YZNRi֘"|bgk+v[i4PH19^.S~ugVos洰VBjː`B5ܯ̉.D*̅~ߤ^i7]R\V95%X쭝 $er:$}n  c%"ѥN' -V_n_k=  \\RXԁ~!x.aYuΧ])L;^^1C&.;.MfHD{x't IX_M\GKq'ǵ?>c oczٓy۸{nHŸy:,#̨ Q3}1b#pyy#2㴡ԅoWឫæF[j,j[f,뇋FԒSŢruZş< -L\4`)v|LޥW>|76:qα B1aN9ҹ*puՃ"WYH ?`؎6KQs4h2sqzzz`?::d?s 'Ė5跾+VHxz>y̗3Q տ[n݆ѠKm+mX˫&awS6|q]=>8xc#)/w.wa:\#}x?֬]/mݪRb<mş9>׸9ƅp[*gE)ί)ľ}Q?fTљt6D@{]NI3tIME - APeXIfMM*bj(2ri%&HH2014:11:03 14:45:090230^Ƞ0100Fotoxx:resize|paste area| Fotoxx:paste area| Fotoxx:paste area| Fotoxx:resize| Fotoxx:resize|NE`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }Zǀ/iTXtXML:com.adobe.xmp 80 80 0 .IENDB`fotoxx-20.08/images/menuM.png000066400000000000000000000200771362435004500161160ustar00rootroot00000000000000PNG  IHDR@@iq=IDATx[y\y~ާvW7:+S&TJ(*r L*q 86؜FF !Ѕ$$ٹyGwgvv'S;믿'RIMMJbVEuJ%4 JPEd)+< tƾdPϯ$E5# EDL6kS*q߭R* y}rh*#l&/J,2t9۰1'VyvOKWKޠ\nU6\"!Lrd`$df#s}KM$RV8QRE J*eYFא/^1h˺f\-<~?Wt Y`xSNĕɌA]٬ڟ?o6ĹR>g^wмfXccTI&S6jKhT''շZö+I%Yۖt&$"τrA=Y) 'x=SGd{ַ S1 C}CCXh1{H[^$*DZchOKQK3dy`B+HIī+<?ވ[)/O! ,<|!+p wxߔzn$P?U4:^B5^l9a4j\h5?mbbg3|? FGJ&e@@6d r<' vm]ۿ7543I%V<9%qz[]ڢX%@ RG+ 钪\MJꭞ5 _˟y>L |T8omm0GdtWj5?^6 ;5(3ޖ=M0wxd>R&CGG|!\$+mM߸)-$0`N[=eN<ҟbYGB#Hx4 ڿxgW/r.:MiM8-yRϣoӭ1Y, CMb+W@LZasf? xtj;׭[ܚJx_?ѷ"۶E@ 2d'[]v|N$/2y[otSIVB'ir%tI5d ȿcya@Ę-j9K֮ mmit9dWYrf_,DA-w#e%=h=4q=y4Cڗ%YS7Kn?cGɬ>揚1JoѲ+I nJ &465͛լno5<6u4[`t(OA#?Py9`2j~i$f@G*14b^wӸ}e$Qn/46:jv(f_rt)+O|X%$"׭;).pȭLOij3}|Ltiի:*2XM3ER7*H J^:!ꁔHȧ雹nxMj6+Ԃ$'&0r}p|E_װJvhI% $6&̧Ǧ.7=daydϓ]}#k2ӣ.9aeL.= I`N,BЅUo(NH]ȅԼt@GA! TZrHBwAH~׹^HR34m[Ud6oiDӐR&*d\3K-QWԈp$\ ,Vj ՞a0|-3ĮrEA`T%sc1h| Uȟi>Cjq~Ć7)1lW`ֻ çro(^D}[5 ia* FY97!*-?Id_6U>Yhg*l)*gmwubp@^_l}6QbTj6nFXNZNMʫ?6m{AR-YPƠڇ= W=`)2o2 yvjrJ)m*I# " dcJ /~vE"Pr=^lKmgӐ' ϟJnͿk?Ó\!y^ +ADt-46Iз0|1OJ0[4qqs(jeޙ`z^ɭQ6Ɨ2kͯ~T~$~Reg~׆PդĽ{ybLD!KYfD,0o#d⃄U_4&(_I#4K5i87k6fd-G;I3>pS81bs1\>cBtԫj2{\`%ʁW,Âz`1B,4-tq , <!U豇a9c{Eҩr+m=/1L$(?(/kWw=Rž^aWqnϋ(|TJ?CWɐ /-"|=y8k/X;Եtpldn |jW0$ţDC^ς!# !JLmig/ CͿ7Kf|l@a?Rhp!nڥQܘjQdˀ 񤽪Ӻ;t5}kGNţe[82oF~|v&}Z>۽d{{ZQJͮΘOw/d7a_84zD Jy\G-RWeMqT`[. yՀ0"k.Su`L!GSfj ~5+,_ܧ/;FڱwiX 7eԶCKƯ <!9 tgP^cƚ͵'1|~ҭU=0nuAQ:YjLOgIfȎ(Bd[6V5yc6BW"%.rݮ@a-L U/Y _'Ҧ"p@6N)Z=ZmrP,.)+ұ|+T'&,> |W调qb_(]dY.4ft4jru}>?y&ilJK6a)Ug=<@rgZ0tMnph|/|+aŧh(hR PcJuÌ헟˯$iz6iy3>%dƃa 97x'i+MLE^ h"`NHm!-oV.ű3yt cïNxnx* 0xQ+*qӋ/Nd@!AφL^~6Pn&3=d_MNm2uҪe8 H|<\%X6>,U7{i;Z+2t|>8G㪪U Sq`7;-xBxҥ֑bчǤVlxֆ !0@]Y49\88Г.gKw,7m6 AeeaB=`vfjW?n Px;mP.O4-} ߰%7 NJ;ѡ<^\4  _>|b4w!{bqf vA, {-'@RhBv ycݰUM=H7[PơQ8)O:5,/6#毥یt%39/Z g`LAQh2\JT;#-qyYf꘯#ߵ,`}Jo1!üKf︄e.NW3}*:uKV6TY 5>r0˳r)5vnwUj߰TXmYHT~,d2>ncJ+z::Ҟ>&W-o=O-'yVU3xk~w{SK\([$J@5 9lK>fLN6VeQ<}#|!VW=ayqP#.`b8F%[{s`20q[܏6ayH?]8=9Hf *L;aY`fɁ9n[X Nl _A xى@?[nP3`ZTzp.35l%s118Kާ\<p>O^{ `r FEV,!}< ,u?#F\b cLy-+Q[HڡO0:`; t@ 8^Ly=E4(AZ)y ^s1@N gU-:f_.&85 C=kaЃ~]tYzY`W86Fc,NG}ݟAS\@W@b>%HO$7{kZ2_2B `{W>0az}STc KN$r*jAP̺?&@ğvZ=u:4 K!;t $Դʪ*lRr3?UIZUp_ԪYFU8x{r1;_}OLga,t_'>/`3l4ՆS@MFT8^ 4wv5 #̎A])R)+dc7~CP=pI `LeGѷ/ y]3TJxn )jжQn3&< 0/ytg=5?R ~AӒB]k z GHYg=EyLgaJ\0;KљRμw*8.ǥq@cxR}C߭5 i@`@2lFi*s}#_Ί ~G[T7 k<wGߏ,vDJHO{7<.',n܀!"Eڏ2#2>pL$ƆSH^skknLovo)wn4\-<>yo@EʱQ\p/fc& f`h}]8b+ƛali/=y \^Me=ZpcgYrGіT\VU#ʕKS Ԕ'#ǣr%iq{yqWzt|~\CJXPONi%r&6`K>X7>=8{z@~978_/xL*e* f29<0 q#>>T DrԨR%| )%fL7[_j՝ceXIfMM*V^(if%HH0230A0100Fotoxx:resize| Fotoxx:trim_rotate| Fotoxx:resize| Fotoxx:resize|NEv`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }Zǀ1iTXtXML:com.adobe.xmp 512 512 0 IENDB`fotoxx-20.08/images/meta-view.jpg000066400000000000000000001076201362435004500167270ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0232Ơ0100Fotoxx:resize| Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 654 974 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((H" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?8@5/`OY4K7o7Qfcy:<~uhK7o7Qfcy:<~uhK7o7Qfk'`H%, ]BNɿ{iqS\~vOh,d Lg '<)uy5>!cc;,  5oνW<=745iX0n*ަӊp/d+b-zg c$=Yinu4O^`xoΏ1ߝ{=[?‘YribӒtkbyc6ҬO$9~oN_[ \|Ia )kAlE[\Bp&(vǥ%ť&npHk!O֪V/KkXęTHwd>q[QjoѧL"~;6Iaҋ%RҸNpվf?;iP-lDv#j7_sI*)_i-8^4:p_3ǎ8A;ЂI~uȖk!pzFzazB<+oΏ1ߝ{4·֕ze-Ta v+'+sWMGXW2<|&(0ϥ%އϾcy:P,IkYfæK}$ $Ip;Yspbizvʹx`| $HȜc M/޽mou0ƘYʗFBDYGt+Q=2x3),DBy;@?7^ῳO3}_b}u9D-Ho|,FBVL;O7˼~u$q Ҭ.,=Ezőlde]+YF y]zq<;E{>y71cR0g*r֋cxu+B:HIAc {HmUUlI;\[y*Sx'ZQ{c'È4엗MNUʴ*y G]c<;,IjZN4CtXؕd*}6ڰIpC`^.uxu}0ل3u?!d!qNҝ\:wvIJXmo`lf޽fr]6=9$G0?{ "zuե͠s@Dq/n(imbOC9[aGjrjk%ɾl9O5I_Fdz|rV%DD2:uOS%팖Q٬sγEm2r{J"+ g'?{]k]h- ,P&$ {zu4?n"t-ȷ=|K_N%{mv+QZ.Zʲ,?Ҋ7:jRZSTwIןJ??~ӿ-ᑣajqӭ"Rk327a)UIsI#y<GmcQNy @km/-56v,ak6gWFH!&,*QDhһ<;xNGp} 4nI?t'-e;w|ňϿw3n?W1IqcZ{sql-m&9.¬ ?o_TĖ=Սݼmlpo*Lѯ\`pGiR6.oIϝL.| ?oG*!}I}>vTIf4G',ps*}WW5[%c=$Dq1CndH.| 4[35,RʺN}|`|cH-2bIG^'-7;}%LI ?o_TfYkhDѺP6Hzk_]ip@..@U6T>PGh7D?ևSD?>"eAk}3YİMm-bA %ֺn$e2J mH:v .ᗍmI$T9WI>'iN$:tf-"f<Ϳ{_Rx4[嶑YFBf [Y7O`ku`fe]tȯ_ivGX$ON㨩ɥ$SҔI;I ?JA*!.|o@)z(҂EPz"eOF4/7E}E1z0hv|=|-' Sɞy,/fSTxT =i-Y\1sU+a͜LBI38k2װZ5arX3Tdu$"PT皵^Y) 8*~a~-pܽm<(хuViǔ iWחZs*ccڸ:Ơ ɧOi% Ⱦø*AלWc?7hrr~ }1s9_\Kx 6LYp5iOwM:5_(96~@q[o[%c2* 7ߧP0Ět1&wk)<=+9.u)4:Y/!IJ5iOp~@\ӭbQ(g7 3򒪸>Q[jn\Q?G^ZM)V`tȒpv#W< ᑠY;Or?4$ w$ߧQ?1o+88#G٭cª]v]6 77 Gs{plְoR695.o% -#<($^k~F .o2|q@9E I"kjRg嵰!P7niO4!*fݵvJѭY.HyИ[~AWI?7R/جkQY3&`:v 4eu_l03E^H]N 7ߧP-.杦uif6TrwSڼԩ?7hֶi,r\yxKe$f)!r!;^X+~F wIk%/B\Z%pFqַCD{KgfUprpqiOMDIo游s$I n 9֬Kc"< c%r1Z;o(~@'cТnӈϚu^'MoYwtwP,ۈv2+~F C qj˺,,Jp {ԧÖM$OO½靀u{o(~@hciYYCluȇ"7"YW;3Y#Ð+{qyz>1ar> =k~F tf[Y]gئ=]7FK {;‘[3ӒĮ\tLc5rT9[/xyRK/g'"WQLy[9_w[ ƀ388#ڻg[k=ZݭbKuSsR]23勺 ocq2`q k_w߾ tb)ǵzf Ǧi(XZ');"N_ ߴ_ 0ߴ[~/(p:Y_ x=T#A(_Uwc][{xу$j$L;Y%Z)tb)q^$uj o?OxQ;k{do~+t.-:sfG>TZi PHU$5!كȚ .c}D:L_#^ct /c888=*<6z]^%RLʛ*vqQ^wy{¶ږۖK |hHy+CDk>.,y&H9I`#iǦOiER((((((((((((((((((((((((4Pz??Z)h7r7Y,2gc&y10y|o}qn\R&3ۜ@4]?:&V;lgJXeW;_5?]wFK̓>A4{#|!02IwaGnWnKXEp׀K%Χicgqq6p/& 4 Zh $YOb)@˟ҷ<{SY]ZSJ$<9I؟yߡQ\SC"ֲ9v6@SFױs*h+.ۏ 0xGҸkנtxC h57Y?)-Z'xֵ#Ye*zOnodO5@V8={uva HT'U#Y\&<ʳ\{vV:]?wzδ1UKqx-Rx֯y c.-H#e:!9+_xFK45e&W=zf]et$Zkr}mrfdM>a!-__u/&9#CA9x3xW[UgXCc;muxծEv-͞,>Hry[~ bMSRl͓$]084؛w>ZoKm1SJY#NU/#y{[{WcbOe>HK7%_t_lԾZAHH?CXWsKX}s\Ex5t_-TKT6eY´8Wiñť˪X%j6)R t};Ex-+RmjvUHB]|Xke}vPɰfe%G"1 #+p)Wzo"\uwv=?RԫrP|''iɍfSO][8ఒݚwr msҳ<sNRI*v3+;%I% tP>LRG9*\zVY>!,~~ :umAYiȍ 561EWVM0l^^q Ga}Q Ga}Q̀z+dnӵ[u]dOUϥut(((((((((((((((((((((((((((((((= 9gE֊`|գjZ&[\$L8uL5ҟq/جW\ffV91^^'+{Jc e(ݝpLMUcKA?7>{\NI#I +;%I6/W˰IJTU,$h^PV>{\A5MO@&aZ!Uz =F#o ;DW m8q޻g?uY$W-kx_Iĺ9ROs+) 93Poo$kl۠\1+_O3?t_ ʺ,Lӣxk*#_L?P?5 [݇Ft_W)DxmŸ8=Fu}:R;װhFHe $kuDEMF1 +'z)=M-j+*bI++Zi1nc`X(!W$ҵ$E6G=x;L֭`;]$3C0n8=H`59ؙG1\آ) (((((((((((((((((((((((((= 9gE֊`|ȎR ]ޞ>O~p"FI#!"m9RMvթ8r-.Γ$9Ԭ)"`#/zK}Qfӟ5j}"?k[IOnH屒m&?koOUT5է2?i%㳶[0a$=+74f sIfh?4G3EſvKD?ZsU?E`kZiܳT|Sj^0c֯5U- *lI`9$9wDԎOk!E诤l"=KG;w0i9$Q~$뺃X0uHKfXv88g~6I$0Ddvu'Ӭc##GV%[ uhS)5k-e%ҮNh6Lïn+{]-t&ٷQiz=69ZXRF\Diz|H+X0p0zu\-c&zԚg.dcbHߺ>OR1޺~%Լ5.t:\!H0A `g}_cu9)2,zA hfC,χ7piO&׵-;Nx'm^$kwXpqV4/.ux,! lFﰴ^QZyoqmR'sGZAX*,ӧ4nxeMjI!Ӡ֎6B+7 M7X"-אm'藪%Q)V.mscA7\^q*Xkc?kϴv=-#UU63.nu34fnfQ3@w5x?E-`k\VΛy\_|*K-a8TBZmE(((((((((((((((((((((((((((Ah~P|h͚jz3HWh-o,VY#*ea8kʾzV5.cpH,xM{|E 9nJa㈇$5fs$ݤf;Fu `JFzkh,!ڧ8 x2b֭^?kO1Ti*0P[!*)${7=v#&65h7iv୺1^ܶ-K1SzGE-y@RRJ( ( ( ( (QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEPz(= r֊÷1G~-$S=3ob$n=׀2{0+{E$H#vD#eH'oUIE\C(*BX:Qsωlݘ˃!q+6|ϓo#=+5]·y+7ƚ5 V369@kzINPӁY5G Oe n%07>3Qy' 睫 &Hm,^I ګC|q;61O>6Z2>Xq9\Gǐ?ZMI*i<̠5-V+;B#'θRT{c^u />#[OyW^km//f{=V"w$k’vmkR<_s CRӵ=9,5 $bG*F"?5v&<e,ZRA,$H{0q4Zh0]kAi>ݵ{p(!<cZ.Ui$M2gg*9>#xn`xn bDd%#t}+_6$upS)vI\t;zR޽I0WhEaЁIK?~ %iD۲##V|wkW\[orȷ0{@b\.gѭY {<%<T< Xka4? UN'N)o0Y( q{Kv4c>]J[}4M"8.n:ksiocٍ8p+ Rs j3q.%F(@-~,\\xtg:zwD7y^߻wtmNW[r)X\gSo Oj1ůԇ: P !F;Ԛ;5u4{/GvwD31l.ZSK=2HRTi"|l2\Z\+$ F\ dgԶ^Ou;Uet#TK)F6p:Ѣ˫]ZK٥QꫜN]SO}Eկe9(WQԬtVmJ"vcR}2H^xFWӭ&PE-+8'NH>EA}}i5@?T_ѡ][OKIئk !V@TSf8ay#Fv8P=IY^Ξoƫ)vqw0Kl.#7+J LU(]>K]=cmpcV-Ƞ *kTYa:Ў TMsIud,m"Ѣizi7Уmgd q](((((Ah~P|h˵ DyѹF Ǡ}XZ9!$|uߊs%!AەGP?LIdP\~L(ʽʀ{dԚ^,yIWf8'iNk~X[Y[ħ*_Hhv<#]Яt\4{]]]Wb`h?ݯJ,l/%͕t0qZ>4/A=`qY Q^WиM'Ҿe|]_YE\qEu%Ȓ+,DKk;T0[k'C6mK 1@'sG4եk%nG"m?hHojek;9ĒHGN?ūح͵ܙUq$6HOM6S6I(!W gi=KoxѼofNG6\3D,n#T{f_i^Ӵ=^Xu7곖3K0qI x]Y$1\X1 xRGlc hP3^Td}w1Z9YjƳ,a~u+4=؂+J2SQfˍkN+{=E *+HַtB:KsqwqI PF7lmWiXꚃon`#"]#VCxSӯ/uM" ^nTHl/$kO<z{"h K[pH_rpkDZ7sB.&~,hqOz⿈ZޟM\؛?Ki#-%МNj;Z]J-dү.om8RC99@f Z>Y[K"'F$'3{Iخ`Ofѻyt_7yKm7l\~%ZxW,L\ZZ|W1_'Uz'9χ^45{K#d5][YY[AnRȃ"ޙ}IRX:m$P^a^#JMɂյPDn!sȀ4)xsZ:vEީq:ܟ=Qm. $ zD?ƥWHw=F$)O d5^|ZATkǧ\40H}Hℵ ohz֩g>&-f!a2aڝ#׎+d,)"ld.?EQպ~$xRҥ{}2$lx sɦMD c-yKpsԌiߨ5 "&>>烎px;Q?rٯ~a:FL*n*%e٘! Eo4H#h9\p9םk~!|u\1Ҵ ʢd?+~ s1?h:MPyslkb ҉<?.{,c$幻5cbb2q?M7%պIb/fAu]o|^$,441IUY2]7pG(#ƃ[Rmqmml l+\LiТ'l0 [t{},jkl2{pJ }5}M5+X/㑔Z&4cS 6G×4+F^526u7K.&md8 , N<\LJ< ,?gԴ{{m]:$bKP9ǥ} I@}M]b-FMklc6w|u߭z Q@Q@Q@Q@Q@,|hLl.dy8qjKh݁8GB8Pc;=kè7Vj߈=4HyV:cҹ_Qm^EznX.XmU[j/fD;!5_MlÇS^)ɡ>,Q/j @뎛Iשt-ϖLiq+y[~2 V.mmK\VsȌd;WRtl Yp=+6mI˓#>`Iɨ*|ZSڞhRM חާiQcv ̊S(U[P3çmnei<#sTY}}? ywHUe뎸ZFbJ= y\EUlUhuQ{4g%̅[Ry+OvbkU7>?/ rCqR|,E.?w $b^JxJ ')MI6ܨF,@P2s\Vx#[ՔvPMLoY|2wa\hh^):8*Z(>DCD?5'vܖe+=WIcmiɄ̾^Hf$D` -%lR\n[_C>=#kWi\kqk]%V%O07?|c]o9{K>4sQ393N{Ng_"ٙو'pNrx^o}T.kg#/mI-'kMt,`(X& QZ͎if|RLWRy0 8u mO?:lv˨1aqlm7s5ZkP=_}8ⶖC>{ayǂ|qfv2툶S$ +{qZY29n+εzޑƚ4RiHl,8;$rWsjIgS3х'vqC OkME.mBqB8ɬ^ohFkrs+~x{+=FDC,c8lجez_UѣK]&;?5&dmʠI~o~yv^Λ{s<6vkstei_fRs'ÊjKei?5QS&=@<: }c&{W{[K9g61m |-kqݥ{'IW c'ڋ=S|9h.żM 1Q`z|*ڤ::xk*N2P~l`u8uχz4ZE[Ρg30Xq^ ?P`F8>(}ndc톏aaw}uil_8L0$vUD.M2RYIwۉ,[TPG<7xsNk քḜVVºlwqiB(hQq)r>(z^ԭomQb瀟w#8b;)^MNP]&^\ a3$u#EEsO_ =@TF&Ю$mȽz)/L  5ddc8:k{ cu?Ĥ`4~Ti1mIb6g ۳hb^Itx f !$vr=L_gey*ݻ ]E^xW8Ң*]U6rю|8I@XB,r{Ҵ7IYUIc'!cA+2xnfmV-7eI" 0I]z{WMErzh+ UAc=q޵u=lJp*O`jƲ\ZdNIu$m}3L>tJ>KeO$P*0OGPG GVD3+qniZNpeGלV9d^4GK6/WÓu>ͰJnUY_Ɨvx 4>Lovo755(譞SxĂWw8a?n3Xm6OƾVbgSݑ#zjGVVp<Iʠdt#I<r$V:-> ۅ(ːzԶۡP&V؞HQg''[ htNH>5nRiaA5@dh"<9"mfX֓i6QڲZTJS6\*PCC|G,")6ObTd95ACsXt)<{ɯ`豉0A >PxǞ$ּB\\f;7W`X>bW985꺏ٛGկoņ/?"b@`f> 5 &hUn t<({Stϋզb MGrn/Ls9C]čz_Goqi.>,Nc?*,VBI <=,^[( ˌ`ߊв}>m-nnBKlp}3GV|WatFk8* {V׉unm&@i Tb szl+kgy-X5TH"* ƾk#u8AC5I1ˬii3:1#R.cu9o-Nyc;I<}Xa\Kin͍uҐΞu{ Ŷl!+0UT#C~ݘF%:\O jFqcMC60xAQ'.k 4ayw+uhQ-SQIƤαKYTPvÐh(((((((((((((((((4Pz??Z)cj&%8Ĺ^h`[:V$Y'Ն+N!O}9 {6dǏiᇄQ<ʫCG˷663K#ɵ1/c%|HęJqve6LmFܒ?c>;Gy `6$S\ĚR[y$J\p+_D_}7:b3x;)tOa+,278v-XkڿYxPFA?ί д]A/tWAQQJAcm]J{יC/ 苮i s1]2aڽ>*)&S2܉o.'c \(RUs5]E={DL:YavgVs'VoB!ԶXHl{giQ^ 0[fhk.MŵIm-# 6gգy+kjEnL ű.@=O'r_y-l9ks%ĒۃoF3ע@Oi-6d̻ۗe KxRm}F[ǗNx5QGy]*CW 9!GJK86 ?(VDxJI72@#ڻ*(`8H!qw!;d\*ᲄc=H]Mĺ6شd l Z(.>Z5ͼ:,Re 3(p{]O1 B;MZmSg#ٷnF,S63a"UȠ2+#H8fOSGٓ`)W>ʟ4ɠTBEi%v:w|$ ra;+🏴 `{%E ! Y;ܞ!gy3jQ\Ǎu]B 尌y:V3!% Qjآ((((((((((((((((((((((4Pz??Z)܍g{VFMQops8e yN;+~_յ[[M]ZI$|]SZ>k_[6OsboQtm^+ 2[;d&!ջW[wJ.mKrwZ=ϤvBb#=+ N8[TK>e.{tkmV?~}k1M94c0L38>:'8­c D ^OօF1by#^oĎvwm 4 h!+6HG~}h{4h/ǭ=V@0N 8ZV N2p:W %eޫruKf ~aǷ>vwm=o8'oPbYRU u6K%ђ׫xtm1E%Pls^O/zRR{6[+Vϙ ,r#_57QxZ#s5t붹X_e(Hv1X_ݎ R۝>t889$8^ovRII7&{| qi6t ,?vE2h^j5=NhU.#vLqp3VDsbkz;41rN~oTG,)+ Fz^5ZX߬OrFɐYF\?αAq5zIl"gk 8E.̮fɣmY/Lֻ,7 r*» [Ե85 `R0" #>x:_tHM<<6a);]>-PX?;۟ے|,mmOzt\|@3𱑘YԿI'; tEx O\Ӵru}Fd՝IceR99bs9׊SIHbk3,m'|v>${;{Zm-̛.|Ӈ̠} 7S6lͨhZ;HɌYmW\MSh2J \dC/fzcڴ9YKᛙ.."Qǒ c.[E|⋭VsZɪ&bGFЁo-5GQ5Ϋss40nTSj h{=oޒ]xKK{M0d1eP2ɒs⇥jW[o| {[eheoRcMęnQK}{p+[EѶ,`OOZ~/Ѵ{]?u7f r2v:%W {ZӾZV8!2 ur7LdkʭY *W67W];ƥr@瞔u+´}705[ۻhbT&ky8z7|kKO%o"4ţ7t nEs{Ms!;\"3gczZC#?FӟijܬeHґH~a88_ƙM֬mBV9eG$P% uK|+_Ԯ!e2|.J_hztn.E%ɉc]X>oCJCxnR;r0M#1s z- X(((((Ah~P|h˴bI AD̑'v >wm5Đ\ʇ :vz_^=b ?lGz;ȟ5(O/\ug;eC?yOc]O(O/YSӡRwϊir@!?מ0OK]Ex30œ׍ͨ10;yoq~":J*)s} ;v W]2%>U20ަNI[h<#֑}1ө#:noJh =68J8<\A5^uTk{v۷4N׳׹?4ԁSZj\-΢ۙW){]A'CT7ړ4Mѱ"[pB{nwSxN z^K1 $|{ fXdO-n8_d<ѝr[^ܞ+B"qF_vӞ'g;DH/d7s9נܤV &+lh%W1-B|ǵq"+ :Uy㹊&!p`{{w,~-hcNmPG4-UXXӟjKƂl5 R #̩#dd5XRR+%Ł&DȎi|#=B.|Lս73H"0`s[ үuɥY<ȊcqkozmAqms24ۚG@fwcڽ6GKm>+N? (zR-x $ಽѲu~ kG {H ʰD.> x7A^7/16v}:@1g emŤH%p‡-̋[AY@ӽŌg\.{}sTnxPi^3HlϾF q[Z[j:$dc1Jl y4ئ5)$ӑ TŴ3K z/$eѹ axֲ&=+ϧ-mde6 [ֵt =.eKd[fe)?8P sn:VVekGֵh^PBAe94u:w4M[UBwv6Tmc:mFH{etr P#/ᆝdUՃna{RWYψaP3OǦ!I4ۈngT#bsT_V4zbRn͗eÉw,dt^ օLTxRin4-i? T_\j:SM9 {#^;74`nA9 EJS,E$(pT0 c(|M+Kx22+SC񆓭q(Icc1sX_ tkk/u+识3H& @hg"j:D26X 8^еFkcyJ[", K3K/5CM+o$A0dMs*/|>۽fT&:]SO9T_f]CUUozT򄬛fy/0~B?,c{ݧGIݗoNzëI4 uY6#Bٻ;<+z:}䮶IblV, 9ojo:L\1+}KG:`wPF@98[> Kxme@4nlj+_ XS^4ic"(p/su͟e[8e;X"qsBnEQEQEQEQEQEQEQEQEQEPz(= r֊8?ӵ;J @079jdLNG%lHnti,ҩ{\r9!Z|-z3 ML:jQʰ|otԐIn7cҬ! t+ҫ4ՓӇ*ϩTְxE{D:+HL0+?}uo ^)6Qި3 36H~?֖'ǔtϘdxљPvgӏN;Jo X'alJq>x&UЭ[/Ÿ[,rS{]4-t$J;\nc憻ؗ?!~uV¢翭}4  0ϝ_z|/( w#/`IHxS%z/n꬗jq\N8k?U^;Ak1][W͠[ߏz >U2/(:ݰv 42:ps YiʱDBAr}󊸾ظ.-a{ć)+ן,--^+$!q[wZO½r]cΖ9n.-"[w9fx$rXHO ^}6[k}fh_7Iu އ{kg71C<ӖH :xU-A!8F%R5iOX 9Huk6kEvem='W/b=ZIgo4/.J9gAǫJ5ʼnf#Y-p >Yk]7G&]çFWLOVm[-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-20.08/images/mirror1.jpg000066400000000000000000000227771362435004500164350ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 109 217 0 C     C   a" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? { xT@@tYsZj> <i~\ڏBEd}ޏ`5}}/+#>}ދ运gV,Xz_TgI=,te!"7Zl+ༀP'2U˥}m_|F9©->}ak_M#B𖃪Zj'rX 2_Oσů͡h:H8O$ @Ok[j> >}cu O|I{;̖Y Ű8<+Ꮗ+uUu=OLMme8a&%%&u9=G c]|[<GQ运,;ZQӅnfZH7U1\Jo|6QW~.|LA>18zRyy运G!^m{:'_i 7c~ڼVO 2!&'[9o޳/[lWkt飊XJJa$c?6BJhm_ɞA运NF+m(}&ß|jxGYշ5(oi^S88 " .x&u[9a{ \]2/Zgo?SV6G^ZgYTzvsfFdT%JL#M7g(;Nfj+Yw. 8tzUYr:qj.o#pB#);Y W+ApAAuwZ_N;"KJ<8¯z\Do,߬b哎w'E'dTSWQ6xHn(Dy@f =T/k BnP~-G #^/ӬEH1 Gq(H1+zp9gQh!x;]b6rV\-;.3 w 8Tn5y+i&Z97 8\Ɗ<_Sv~{de4~þ>]{[=u_7Wɹ;o׿9^tr;o׿9^tr߈|{xuMR[MUKlaC?09#''znwr,Jv̵#I rrNM?(]{[=u_7C{v񖫦x{WZN^c;a%~fRˌF{涵o0Ykqmf;hȵ<;cJ NM}[=u_7G;o׿9b~#uOMgP[[r.L`,2!A'5[U-HԢ`h1fA#;o׿9^thz/xĞ!''׮~TeqTׯuw_H(v$8x5 [=u_7G;o׿9JS| ?/2D9͜{X0KmҾ'fKId~[C=c+ gJ^} [NI a##V|h/뗷"/ eNya e$E W#5Z\k_޿/_zNK-Q=ԬKBwJO5iqM/#<؟(8'w5_-{#|UK_>+!oۙh}` FV7/uO B ]!mMk݌Fv |(I__;+τ1[N?5:hwڌhVs%&m {k? ,xW!K )V=Xw'^^bHti_wڕ&Yb|Ipjc&[[ [ј46_~xt+-JuWI-[ז1,&2(p9Io|-M%E:hu9$EvD%PCpw`BWzy~kݿ(wN N d2ܬĎαAc&;#ekFM]I?O: O?_^dih:> -u p+R?B&KnGsMS{Y##;Hb|+)S"YW~u(tt7H7erD)|KtjńZ:[[&4m4Vm258gTJ@$ 0MŏOwkGitjW65ңJ.fvhˆ wx'u_LD=ST^v'y |BxVϣrnVKbt4x0G##֭|}si!/5#r|B-5ZBX7٭HO +[<˫zlm+(_(\$Fާ+}L>ՋYkKRL ``Ԋگ> x::F M,R,J#9$NIꚷ$QEH²uG%U͍ͻ#0Gb+BIcPb$7A`x1ƫ_֓CKy>C b5V,=H@(zqҪ}|Gگ?oL0ظnuDcbU>y>CW? ERU?^ϐ(ľMUMNWԴ;yUXUmf#-C< :nbGy>CW? 4m"BҭE+mnsVWj SW? >y>C ^RF1֕_6:dgSW? >y>C e61փU1Ҫ}|Gگ?l"0cJuRU?H)h81@袊(((((& Gmu!A'U4tV/H@?j0&um;z6=푺X1;2~O^{5~R5~Rx|*6\EYtVo.ExY-"m!9|7oohZAbob햡NԲŸhG羙Kl継K}P*LJ"@o#G7lq$cu?WG44u)[PnoFO(8<E=sNM֟#H YhP[_Zh zOeYU68Kg羙Kl羙K>>ekƍ+{C4pfHgV@,c+oڟOyt{ AosqekΓ6挕rb׳Yg/QYg/Wj_ XWGӢKY|v 뜑&ȨQEQEQEQEQEQEdn=l9૿᫽?'چ.m9[gɆBX;$UQ`X!Pu  =;HѴbi%A۝x湦ecxZ?,1Pz 3ߌ|1h+ux"=ddy4j ar*\+zNj幟},ii Z$oSy$X 1000 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?f8\6xW"5֦"mKMeqshIcP47=9Wz=a>5>BgKQg ,y#һ__Xm,ػ2YJȥ4Үi vEƑwi<`9uk.9=Zš?\'3NiVBj9Oxyl:Q^eb>` khа*=r3D.ۑQ>C{ VOdOߥs$A=4=SH\Դ{˼C.3=Bikn"SQ( 8 Rx榶KrK/E5,`Jli<\0ϥ)Oe{$_G nࡶӯ/1`#ng,0Xf82} *;j$rjMYa5]j Dx9#isҦӮu;s)葩'I/qЀS5(J<7RHhs!u 'LLCh%aզ;s:@o.o,=*dWWE}0(8Ȧ)c#ݾqnv&CIMɠP:UQ I@g0<=g 4pZz =*aG3R[s Զ nfURO=*<+.a*B}G^~ן ~Y/-? /~Q-]*yqf|/Z?ࡿy?/&fզncڦ, _>I =@@*`gS%=ۜsjeh~9W.7wl1KyIl`fBޙuOX6z:mǴ_m κopWw}~Ӕbz&]>spBHxZW> Η(ݱŐaA8ߍoXmu -o-<\(fcI=SR2M= ?d^[eUL;ygǚ;]hG$NB:&'zmNJ$Y#J)?)vm4KM^ӃTd 0M=(C0E>H@ҠȦ4 y MqNd;9=*B>Zw4m9i R)?( ȤoُZn;s_#cS#L1sߩx{Sw6qp81ڕQH'=:jm{g6+acsy+O%i.a\dUeFq^$lo",g!Apx+ 9nOIєUSV6dbW1ܶ9#nJYgx+8xrv5߷n,$) @s㏧~[RͶX['g^ ([pΛZ20s&I'b㏋ӥE'M#xu;ӭ主a@nG9|Il"A~U`骏V8 PU p2M t(|I7%J1Һ-GwU9blr"i:#si8J/LX$#ڹB BKBQ{Ip^ZO(M_FAI D_Y{)GyĩpױW?27>TMH>^=0B8]J2ztrƢ<פkzFp\Kۣa[ҩ; gIrqʫ֓ܨ+Ř2s9̀J?^9n$ƾ9ѵI]RX\8uȵ>̗ybO5c)>uPQ7SF#jO`JIVGcvs׽tOkt"#.23FJp'T!^/uZZL ; { 0,['$szѰXV2y /[޺c0ٟcTL5"Tŷ.8׹GqOw9ke;e ͻ0+Û|E.ơsAx$g.bzgq}LwEmeLgv3 |}r q^_G:9 p~!NjгN,mSva1OQ^r4m柁FACvPS=I"+f1J:өASǽ5Oɠ4ͼӔ֗4cƑK3$` s~ oAomL Oe18jK5֚ơ'=09; 'k?ׂ#ngC|珼BzWxFjK[Ih s58\ݝ U-evxMrW q@d-i# +NHsԐT]ÿi7ܥ̈́ZD#ٱָazp1P>nƷB'fs$A 0x"Uh6!&vUߞ 垣=[S $9"FᆿcwY4@:Ky}ހWe zj6 @dgf t\qy?U@ 촥-iRmnyW98=QnLV@\I3PN z. ~VjE($18ȋzҿdc*缝jOYbH)C5~9x"I38QV8a( J/sOi;*TlQmq])^z^[1?#QQa)SpAxsޒC<\/M3JaʻUԊAݟT~^$m}(~߾%F}$׼U!M.MKvcž_μCmZѵ[W} ǭsbi*OF~|cLZsYޘ&.yQ}rH/.Wi1P  OP3ZOtév] !"F'sx|#y?/]2ɨ M)[8%YwrXyl nNz)>'Go#66J܁W7V\ǞyۑkIN``y-}wM+mU]IXŭ!O@9Lޗ;xzm%2[2Z F|㍭ A4s 2ϰR6E}#}i>``7&28da ~p}]ld۳wG&'?[XSH9j.E}Bɧw!+AAȠ-:q|POQL JzPlT4r)(zf(?J> > }xKֵ1s #?b%m 95xwz"~UFc<|3_3ZˡxsO.f*Caj)h !fA$r z,\Y$NkDI9 u EOm5Kizͫ>X[Y!A~Ve4m-3_#xIK3d(7ފLS^"nX2 eKNPZ6ͫ&$~b=QuJij8 >*im4TYHG5 x^׆/滰l$Cmgx9G1f͔FQ&I`'!{=@ǯnRdaVPmj,Zod ;>ioVmR.#GG_ZͳjwW66n]E' ^G9^;Ⱦ;uߎ_xKF70? &NI+׊EY9Wߍ8t74O3SFS M'C+u!𵭡ɿ|dӒi&d+.V%ĦN"zC<3?o&(jf|O*?G?gI̯C\!P>EdwJ+f`膑I7f;pܐ?nc1+-858߽@z{3Ҏ)2wT~Px"5͂A29wZ,3C* ~𦑩OZH][JE"u ?_fKXw謯np]Rſ s^7'R5!=<5hJWv!mϟiu sZX\h7SꮱXB@$|*|׵~!Km *+,4'4۷ 4;*L$V;&N:M䑗1\wG(斌~!՟Ԥ<ՅB8 ;ogyiע9Ap:'_j!q-b8-cکi:5-<.G>7`ppG-Y3z8kIUk߇%Ԯb@~=z?ڋ:CMu?x$h#+B `ry#PѴWVV3(ߗ:?>&]PC,;dW@+/9Q"Ǹյo3FM1Vʏ'zT8ʞT|=TtٰԄH71stFWx4qIR9Ƞ!GJyP18TQCAF3F j&jMTq)< Rw#Sǿ yLZkK.e– Y^D7E-ΤHbi ӟ Z`w2$"c,Ο.1@}k~?x⇃tF'>*[xUd[օ8B<۰5^vڅ!ޖo&קl;gO<]mI6ܢbKR ,nԾ%^ 95b'Z5%Jk F5iFX~9״{(v_oUt> fHu+1_!|(K,HpF95S\_f( )rS=zWC֭;\M:t 7c^-MZxph[g9y%dWa^gcT6ܰ$|q@ySs_N-3}xb*@i1q^8z= 0]EX MѾ^1Vx>H\{q=jսi5Hk_DeQ\fxlυ/KKmgI;f|/>4Q#q/&>x 8Xi!4j޶2ṋq]'Pp gs\7W{C?/5Q!a2 q*r*e!QHA0} ^%²i6O}so*9~*L+99x"WWnkwmomm#K◍\2 8+JUis^*Z|u/+OKu"\e-܅d]ގg=Z^=&{oR=KwXDWx㿐x?hڭ5ܲ+I$PMĴNrI=r}u- z[9)05',3#gF)JZ%{o+ۋ|dh8)fiy嘖'~Z%u!`RcJԔ╏5@ );qK)W'Pd1Cր-7<{Sz(f DE~54oxJU}brDnq_ůůmkVm lLx_^wm-]γ/DWBOẄ́w6JH$h̬2p޾_WE}>Qb(ƛzF~⿉z-J3¸5c< ^6l[H⻘mb. sd|+Fk)R]?25wPDd*D }|-U7ttҷ}NT}:ǪÚݠ{|eDKsc–2v~aI>!l`z݂ͯ:9P]vQ _6[,{8zX_#Rrk;-MZiR/FqSdT p)dz ;)k} hA?F߅}Y !_$c"d:Όua_59< Yu[uPz>!zu!GpeN{ψ<7xn S)C2 w漺 .fn,SJ8)H@_)Շ Ҩ5e,D|u^V5˖tw*uxo[mvcaEKx~о.#Og Ԟi!\q^{~8ѵ;SIʷe<9z\')R;8ٗՕJ K^kACݲ/ROYyZ~_N#-iNZ;E#{RsCԍRMn0SiOSHv$9\w$85LiH `WޚOJ&&(6Nj46HaI³)TcF* ֿ쯵iiIq~ <ʪ惶5.ZtkE7nЀT70jE+?6;kTo`%\cלXO&:ȇ 9VQV*!/}.-܉Xn)|Si+̧˸7ѱ'X׾ɨw1( ?ҽ >(xY|qΓ˫dno)'&wY` ߘ N T~svȩ.嵙6T;YH*{JzW+Ut|ܭ);AI/?Z4iSE㊱j*49JXŽϔܮEh&aG'e@ Ҫ WhҒ)_L72V5q+JxwYbx3^w:;>'@oUSqd'3֯,D}hC'~n?-7w!ۢxG8qn~~#?m#⮤ױ_y@<`-z.9t-bֶ-b35JTӈkSr_H튣ssz=ws >⼇Pk`'%~XG{FWWv.9] 5S2|SȤ{IңgiTbzޣm~rO|I΋$ K?A__i,2H:؃jС?Kjm]Ug\:iX) }MhzƵ"W˜dvs:ZB>*YUǖj}Ҳ7c6~ &e!$]Ec˒k&?vq*'MZLiVkX+ {`25|U-|{ios,`%%#cȖzujQX^GGz;9":9#c1M;S3'OqW4V9dX j rsK<:P/J lJUcL&MDSLROEľ$4^*2F4ٷAMlK] wZRԌl C^g'z=VE{DC&]aA';C嫱>,xMu 7G ͼ+ed\^+V ᕯŭ&B#lhcmg]Ì>OxMӄz)HoIXPGi5 9\:"pqz 5}9LNcd8y{$Q֚=J{X{lf s6|3ʹXox!n\2t0@cGu]# ong܃k&KoOgBB-\$~2&{o'ǯNcCMtL_~~֠>$zm޺oB7mi# sglej1YۨL0Y0lm`q ;ψ.#|:6CrE h)b,[rrp}+EZLuRWG⬣?ҧ1V|MZ\W[I*<_M̮x[A6MWқ9)mb_B}m%!8옋b{&%p`?J\u527_sU#EBTk[RD3@Tdv0DrkvHiJ-{/9ju& UnqY~"˃"O OapHjψgV/׼U|fGl*ą>%T9Icզv+EbLLC`7[yoّC'\[} g Mf훂dlu`>'sŻTllDLXqL9<( U@+UX)MGC'/84 rkzHBr= gMAD[["}ZaK~8v:~|dž46sFof[wk"gw9=|GOJJ%u$iyEnBlg#+dOZAGx㉼_bBlm-q9mHa$Jg~...(P= һO K %I#ȯ_ڇ1i y#״!~91r3э}WqUaH$PUOށ@}-iI>x{]nG^$\^݆I8LM#ӛO§SJzǔȮWR.fw-H0 9 Z '}O4ɓb#d#5@S4 fڐݪK6F(4 'j^(ʃN#=(0iQ:-`aRTf\zirfV8S~yrkc.$W%i7Y/; XVvfԗ}]\^,h%,gB1%)84c7|GּgX[RMH@8$u'bU^G^ k.[[[R2N~5sqsFy7 ]?jq|C /sOh^?1;u6G} ysҾc~Ӿ&;-Ar=FJ/5d8hjX+H;WENPW3dPՇMuƗH@ ,8M$#=Et\$ %n7VC қHy;*[3\>L򂇧{KM˛?'tIrȋ ?*yDh|.7MI @CGyosȬewj[ҟKǔ uPm9alr9ðF;v}}}5Rqkp.(zHx~ Mw"YloV|O/ƾCi8bKI 2@ 32! 0'c_&H%-/_5}&O L!cT8%,J8]i:mݹY#<5OKx,n4 ƸDR"G:&]ګK2&v"UuU]ܤm,֦7v7\|u}WCǺρu{o-KbZP AxFB/,K-)UpXz׭,M% -WJ>Ե4o4E< @⽧naѭVDm0sW?38|)w-y !;Lv{E*n!A19#<gJճT߈P!Al- ^Bɋ랽k؇}#lX]M? sۥi_eH$glL=ʷ|اz,;(+T-R7iuKsحMnM;#YxvƉZ|~O jZ:$zGt%20d_8ÒF sEtT$sBg_ύ\ڤq"\/*I H9Ҽz? [:Ewn1I3i96 B S@V3cQ[s˹F+`tu_ iv#ܹ#8T_񢊇9]jRm\F^T 9z(ؽi+3O'lII_4TQMg$u5-+vh=:M#`..Qר*Cxf(e1$QEp<o򦮋 $33QEk.rՏqFaLJ<]q veǙ,0]#vy#QE* )_h[?di"ҵX30hmcۺq|C⏉Zu:5Λr7 $B&(ziw0HZ0|,7,}5jF\hm%Wuy<3 E:p]NNMGȖ򧚲c$@q{ } 0 d~Q\ltRkjY}߼Oyq+.Ъ{=MTЊZr1O[] ; ʁ2p=;f(g (ڊHʪey#8/Tfә"q2e!Q4wQCC46Efotoxx-20.08/images/moncolor-small.png000066400000000000000000000054671362435004500200010ustar00rootroot00000000000000PNG  IHDRVFhtEXtSoftwaregnome-screenshot>1iTXtXML:com.adobe.xmp 514 815 0 `zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }Zǀ$IDATx=nGv@}ёt@ RJBJ@T7H"Ȇoz :.//4S.|p{{Pd\Vٺ]__;MYכ3=u@]Pu@]@]Pu@]@]Pu@]@]Puu@]PPuu@]PPuu@]Pe-p?wDNxD]vKLe*STcI7c. . . . .. .. .. 5ӳK>{U]:2<l=YnW~YVֲk"㒏Ⱥ{reɸbsEIϯϮ7ANyd]~NSa]bvXR^f u@]PPuu@]PPuu@]PPuu@]Pu@]@]Pu@]@]Pu@]@]c&29 j^jYézS]LUS5-shqT o7T95c//uI TF筩Le*S;u@]Pu@]@]Pu@]@]Pu@]@]Puu@]PPuu@]PPuu@]Peqx4=n#?xg.3!/g>|H?%ێyLe*SjT7f1Ru@]@]Pu@]@]Pu@]@]Pu@]PPuu@]PPuu@]PPuu`Eƚy?gweS9dWTќ5pLUkon)ZZRST2՘n^7c. . . . .. .. .. ,)4zROJ7|ɀh3p:ol=N~v|~M_R"{`{;Ӣ?j9]|U9y̩z_i,qkꯥ&«<,s gSe.3ڎyLe*SjT߾wSu@]@]Pu@]@]Pu@]@]Pu@]PPuu@]PPuu@]PPu]yb'w>[n^}wi֟_W6+O 턽?džvCtyխh{0,ҟu1/1Le*S&+ߌЗ. .. .. .. . . . \7MSDL4LDCIENDB`fotoxx-20.08/images/moncolor.png000066400000000000000000000051231362435004500166600ustar00rootroot00000000000000PNG  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-20.08/images/mongamma-small.jpg000066400000000000000000000515031362435004500177310ustar00rootroot00000000000000JFIF"ExifMM* z(12i%2003:02:22 22:15:5002320100Fotoxx:resize|NE6Photoshop 3.08BIMsfotoxx, Chttp://ns.adobe.com/xap/1.0/ 690 292 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?۽MF/w5ۡ+n6vDu'#kZ65i6*€Us2jϪx'N e͔1ڒ)>+մKXWw$m#(?{ڀ36SEhfsbc2h=9UAȭ Vܾ]hɌAXg4ڥ$YD2y,8.V&lPGmJxrQgN? IsנK}>l[eҫKJlw֗{f?FOP \q/۶CcJvggPh.FԬ<644qXXpU3'Jwߟlaƃ}G<#? xű\H!^ @n}?pƃ}_@n}?pƃ}_@n}?pƃ}_ ZkxG~4Pv_McKvǮ^?g~+/ٷJ@9Z<[[MfREĀ7*m{C|(t*Q״~tK+ٻ,Mt YIGyv ((P~ͿRyu% ל-}W@9sQ?#E5B4P?ӿ?Um|=Ky?z=/gW0g諸̽VD^PQ<>#NoJ%?/۶&M1S|~PêoJ>g8#B}>1n((W:iқNnOٷJ@9ZTe5j?'h^_2FWXUbrIٺxA8ZvizME,vT;,ZZAzU^TW%ww{gZ]?ooeUϿ^M@wmEl{?^wB/(?{e+wZyCG@Ky yK`-?-۶. NoJS~o<0äAHdT6q*󯋶CuH~a~3_iNn{tP~ͿRyu% ל-}W@9sQ?#E5B4P?ӿ?U꣠NhW3ijm\Kq,pĻr0P>akXZ\Ɍq̤׃] +ۻ=? Yh|-iOayd[>?zoSWqb#i{1o]X ^yCBOn#NoJӚufOovgPGێ?Z45gt&jn;_I|^@qS=VֳHeUc`k;P ƀR ZCIJi(J>IkڎQ~ͿRyu% ל-}W@9sQ?#E5B4P?ӿ?UHڌ>.looY,|%9u|Y[xkCQ ݎrx=A@1\mVgK Zё"m /\k-mϜO`?o[k2]/E׭[0' ^?w?y=JLSWyCO@|vYs[my<!_'ݷVMƯ FoJkT^o>gZj{n;_whn @/uB} {< ty)ǗC+Q֝M^FM7Jm:?Rq>ER>Z=Sm?_?f)k{C|(׿9̟=&OTƝmvQQZfCq,EQ.3?APh?ӿ?UƗKӬMag8-uBe}+ŽBj^j[6)KQEzOPʂ=KqA'Be08$:W^6`0He/!\6?zyٷJ@9Z +ǭUeIu)&?u$J5j?'h^_2F]@zw{GTt*Pby9IR~MR1"Qʠ?uzyI1X[wqwq@|QşG̠/Cַ.|QğG̠/Cր4,̀ăִ?,?[>LH<|kM#O@nnk۾ź?(OJܹnW4l~[CۖZ2/ܱH#GY:?oh>|g?LG@ClT0r}ƫ~ͿRy V|'*ȱ;!lH;q|6H/^vfYOcNT!u@ƽ!GdЍkO@6a-ךѼ*쉖=X`[)I{嬲oHF}erO5BMVfknȲ˗覦^sMj~Hn-RY^D}]q OΣ\iowkt 3H'>ئjZ}+K[1.)=x5>C>jq[[\_+{w2*2X;:*wq@n|Qø(?0yn4|"1hJMH||kQ#OY:n?CZ|swO'V毵g=?+z&O_k >zgPOxPyVMq|%guY?ox>|g:W/ Wt%Fz5GR8j:X*C?ƫ՛VTez׮B3B$K!] gss?4!UWF2#:=v o#b21zk^_2F5j?'hU״GA^"P}C=uO ufXioj~Z[ߖM|gtK*7ܶJy66yh?^h1_0_/$v1el|ðhꖣTIj*OP? mxn?5Vjz% ל-{cIuYm) 9aӺOjٷJ@9ZI"@1KŇH|99=;?^_2F5j?'hU״ՉmYi^d.8aw%݀@Miuw6Юv-z+nxۻ8, j "OUa=)sPħ]hEsCֺ+/1OJ|y\֑_w=1hVMH|kY#OYz~y_ֲG'*]jg۾o}s??Һ;,?ﺹ[l*} ~n[l/=j[n[mE'6xno?_/C-vC>?RQ}>CS* oW1U-sƪU˯_STe5bדTo~wq@^bۭ#3cw=1kīSzhcl$^Wnkn?NH{ka#W@.?3usg=>]-ז&~O\ޡmIsj[n[k9kvDpq$qaei?۞[qI/|y ,ɩ\(/ʱ?0U'&/ WtOPCXXUzpUڧiƩU?QTetn@p7r䴅wp*~w orFG;{Wў,m7QOs+n)<;<㖯~Y+C^OT*ƨPΒ!4K{״-ֳo-1Ģ<21S $1NOԟ׋;b,)M1־{c+=fDKg0? {C|(׿9̟=WA^"Wx]N2M{qbiV{d 'GLY{<#k.ONcGa7Ҁ:K-T6+c}'Wۥ$4ÀI3޵'R:9['ӊe험%^omjHŸF7sCֺ{/1OJx\摺O#ZӴ?G[)꼟t?G[ 꼟yul7W1dmu^Zy_us@?& J51GOs+r)<74GQ7Jy;42}@8LlMo??鿎7|;["?:,v%IVqUX*()jqbxgCZ8 ׿?ϭOٷJ@9Z~TᵹY;?f)kݼ5b)p,T6.>Ad@!GdЍkO@ſVO/!gآ̆h~@u5>aqg=O5,$u<zO< A^"PVX足r3Iei]ep>wqVVjwq@~b񶹋#uʟFwsWt*_ޛQO+ZDWXnGI<{ki_^W uSԵm /OgWSyul7W-$_/` =3QOs+v)<74GzVRyomy?^h1_*i{]|@jo nQJakGakOV"kOPVez"E#Ek|3m=,F/29dd.G6bpA9$~]+g%gUk)lgw} +[KHfbQ0vgqp5B4QsQ?#Ez =;E^:Az+S76-{nSzֺ?1OJx\O#;Zt-GI<{kf4WXnGI<{ke_^O uT5] /OgWWyul7W+j%Q /OgPQ7J}6HG_G+Kwn{n'6w˪^\/ʰ?85o?h1_)PGҮbJ@?ZW.S :|LVf#kSWPV]j#@6H/^)5g%a*'À:g eu(]ͱf̙}~4kOF!GdЍj~ $Yo9mk$ВLC*FVh3 8q=kT%Ŵt}Lk!S.Hݎ0Z-uHT+~c{hM^ v:d>\OA Nl]6S Ը:Ǯot1OHPrXrI$lmw{g{g)gokw]!g=~ߏ]]?,mrN?hwL(/o{mFZ[L(/o[mFZ[ϖ&>W\G$_=>]]ז&>W\ J1W'opn\!Q@#Q'cZ'om><ƙy=FyNIvCMk}*R\>{UzIei>@5/Edֶ?ѿY4_"![)dWxRR#4qL]eKP3͎_B_w*؅p,FSRl'p8{C|(׿9̟=WA^"W((m3O]I"YG4cۍp8>!&%Xt 9Svn*X1ܾ8;yG֯mw{g}iOK׿9̟{C|(w\դ,Z\Xn$ǔTg$]֧<7 ʈ7,'T!=@zw{G@74N\Htl@A)puVv7JZmJq.eWE?_nl37om%CzֺM[}_;5Z,6(7o[m"l_^W ucivěմ+*^\S+KWvJO/J.|?0}Wd?$Ju <osl8827?1XNW'~om@QKܨʰ?8`}iD0bK _֎+:>֕VP~J=Vm]kot(?±+sW3:Шܢhy} Ǽ#Jխ;XbB=\>!—C kαy:H|<PQP3sQ?#E5B4P?ӿ?U꣠NhW+,iW¨֝fVsZw~([=?G@5[y`~*[)UbJ.2><^xg7D?2/^6H/^{8$D?+,kr9lP;sQ?#E5B4P?ӿ?UimD:|qXۖ!m͑$~PlnokkkYi<1Y[,>nޠzӶn+L2hzm|9۰p@*wi]iLx=['}iOK|oCJyo?{ZoS7CPҷ^Fv?t]__6/+t?ODؿ꼯ysOlwW#j%Q tu;_ur:ƩU7OP'~omX:M_yVܓ-oHʘ =(οh% WG~-ΝGCձ¾W 6>1Yz|\P]@?EeP?GX:/Mo(k#+ k<¹wBf->wXW#W Xm[ W[ʙm,1y}:x% ל-}W@9sQ?#E5B4PwomV5>)~~a,Уkam:Ki䴍%DnC"h9ɧh?ӿ?Uh4Ol#$2I+N5@5?QYUǩɠ /Mt8D鮇ljxW?]?O¹OٷJ@9ZTe5j?'h^_2F]@zw{GTt*Gß@:M_yV܁m#F~]"z/Nu+~)73_귷(&r!O??|_]~џKo?h֛Ck_ lb*j_~+cSS@MtX{B鮏ljxW;]?¹OٷJ@9ZTe5j?'h^_2F[fpZ_YXZiə2mF@I-ψk?pWn9s81ޱeާ6[kimqdfTh̜Hl` kwXѮm*k;/XU} ŪIKig2Fm7nPHds89W5 L6:Ը:c_{BSFC"aĜjϾާ%o\~n9Zowm[c?o}hҬv*ģշl_~W ubv;QDؿyqOlwW%Q >ß]?W\~lG$^_8lb/N|u+nK`io#XPeOJ/]Һz?$u.6zhH)`kJko?(ޙk5~h+uO?QX@ksz\Pn\tyxW5@6H/_Uʟo^r]x!GdЍkO@NhW =;E^Ugg5FY%2 eq{<."ş@:E_WA_g_oEϛnm4ٞS7~'y$gz:w_V; J-C[u m-W=V-m~(K@tM|?!鮟<+? (?f)kSm?_׿9̟{C|(t*r8t]#NGbxe=<+oRխ49Q\\݀?Yx۟lin#66wvk{pʞ Ʒ1l}?/+ԴױGw݌~_ZS3bKK{w?{@e%>WMf4-)un"l_~_ 4Rb+;[J+}wb/oS*H|q-Ҁ6t1'>oz?8"z/N|u[>]q@gG,bBh% W4sJa?C[ Zy:Շà ?DkstSq|W/]OP~ͿRyu% ל-}W@9sQ?#E5B4PO՞sScRM\09'+*m^x=lyKThUI$WeCg#WCNhWWU[f{6 %J謭/>wvǗ2D"֭Q@1-ogɫY͔IX²1>X;3c['JެnE/Y\۵/#dUx@mn4Dj> Į|N2ޔ=-`R#_b ]n{:ܫ @DjUrsmBp3ު? dcg>w y}h"Xe\me3^ʢI>tS1 }yY3UWx."ot^o4[io>;r0br09<(~+@o?袦ixX~R?1KEs>$~%Ҥu[RvVh d_z?DCRת@/kWS,wX<20sJٻS8Rk"QYɼ)UU %^4_3h?8B9kjz>iɑggbGP5_>uWv2p.O87I%8oIY\ =v:ݫO,$X!n|˅G$z4h$kJ23OLgz4oErpB`d^]숖ܕPSq$ Gktvog xu=tIb9{e-KkEP{bά噂%Ͻmx~ F絸[gYN۷u@^a kzIO~P˃]PkOF!GdЍAĶKk]s2_C\<BWҪ4`bHm %8T.K`#c$4xo &ӮZ'I "!bP}3jlkh[h|=&n7':OxY PEQ<, "% 55PC+ֲA,9;yLSTs ?M{[G-F'ޅ?fx2sx.NawǾ02z s\':>7K6[;# I2DG.a̳'766Nu>1"dc]Tu* 8q\':OxY PT{h/%͙[m̰:_00HǸȩ`ֵJFVW&a䆆v7'y9煟Uo*/l} 'Hh\˃\5kww$GKT;??'://WmlDf t/sW_jKⴺsZȖpͳh6ds ??9煟Uz=o*Nu?g|7@\j%X6r,Ⱦfm9[8STs ?z+?9煟Uo*0j?'h-GXP J@$Z(fotoxx-20.08/images/mongamma.png000066400000000000000000000641021362435004500166260ustar00rootroot00000000000000PNG  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-20.08/images/montage.jpg000066400000000000000000004327341362435004500164720ustar00rootroot00000000000000JFIFHHExifMM*V^(if%HH0231+0100Fotoxx:trim_rotate| Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?CK>\޴K$3l6GnWKg`ڽM-xC?>/uj^}ަvGd|^mu[o5^z>EY {[Td|^mx?ڽWad{6GnQ {[UjŨUS}"m$ 5QK.Mڿ>/ujl:-z7/흟,:ŶؖW[_)sc%2@#КG-?4W7VDmo3Nu 09L4;o|IM-īYAPIr>CV)MLgKՔ8 ?:ZW:l:-#_ mƫ,59?Md!W>ϚVsӧk=4}7vZkj.c_\bZ-!w>bPJݬ=\l:-#_ mƫNJi<5-7\5=†Q 0nE>/'f5+GMnl*kqHĆx+Du?u[o5G6GnWzïj &0]YC+. 3X?jRcj {[U^dʷ?ڮ8dlK4 3P3^_xWq/i%G2F&X^O/|ory[[2iO Gb2ƟA]_D:ZHcmyN3A[|O,,omEq5 S $ѯqhz ^7OG*lJMB: #qU+ïj f{! Zۣ1'>Ԑ+nyONZZ\O!ª3I<;Lyii/L.cK@6F-",~Rݺ\.#|o{Ɖ\.q#sqY7f`jڟmHH׉¬qr_J2n{4l-F cWHq_ˢI$W ~"ھ6]}YT1K+C89yYh5㷰?ƾ-s i5DUA>Oӓ]Oÿyt)ʓ($.d&zqVZ]+/ak G Ht{cN~meFAb- ! %s|!PyU,sneS և|3?3jij^X¤QXɑ=7IYu)ib +~g 9__Gׁ\L|5SU6^$Bf0How4,{2{WtܺU SOSqOZT\ m#7a@rx_5[ h|Wm(JLV#cs~-}K?h `3f:ٔ]QFVG|gE(D%vnǨHZ|BF8s_5mQR`ݸ9qO==*I>3xxKL[큆 d K3]˜/<.6v̀3jx>,icӀ{kZ]ׂG5_~u=gx\T ӊ㿋:kh:{ŝCNx殮a:*O Gp~ "Os>n#]ͽ㜞+V4Ee@3ox}xΥFUF2q*ōÊh\qv㩬ifdgTd~xWĶ.f-y my:Ep5ij}EJ&AF #~#Z\X"V nn8ڼㆋ߈W#a):QR~yҿ~n| Ym?PQMMfv.f$PT%$rq°|E4)*2Fbʹ<;%F߫kSX%{msFO‘I 5iؔ̇ŒqZ_i |O8H渹FtDexMsiZI%Fe@VCn`_ 5m3ޯg6&POǚDqy?Z+9VxZ4^_+ x<^Z۶7Z͜m$$ &ؤ\Gǃ?]?շdVK[F Je+h4߁^;%G^"7)H#!NpD8~0B00H( gg?,<,mF>U p85JP橯SZ[# 3ׇuX^*1Q/ɽתq}+SXD:V~Jv(W+;2*4r$!I c+BsJtq6q_oֻ= > #on.PDWϰ*ʸ[_mg+\X@+0) R|q1}'||Q{8X1`&ay66[=x ~&Zմg\vym s 5Rµm3R~VݤkדfMYʠ >7-<{ǫY[ZɷDb9 #~ wF/Y|;g7 2]FX1ӌ ~=?ٯJ'...ΎivX븜>0{.zU-; u[/7VJ(rrw b9e5Fڹ:uWYӾ&22I@d/-|:msl"1!m@m#լBJcFR~UIhĻnhR߇l?}Rr޿&4l_8R[N:Wq6^\7-w Os;eG V-&LkxEzf%Զ76>calo$gǃE>6K *` dQЎq"N]3M9r{d00f1iv[hdK@b%тu8@'9ɳ|f7ֵ[ۭ qWNgO RiFؔZt_TGk $ ܩ5]iBv+uʧsj-a% vk.9}OM #|B<crjVqFebO'Ó_V-Z-gw!J!'%R-˟(?&1P[_ZkGֿBk?3?[CcK&EiK P2qx;2su-j+._Qù<}C/?𢡊7G;2st˿j>}_O ?asߵZ>}k/w'eqrx_Wn0ڏ\ѵ:uDn+}8mqc885;2stù<}C/?𢡊7M^.NYO|oM\!{Oל.+~pyoiSO[4ּGe2f -%XK[lgz;2stù<}C/?𢡊7I+Z?Kn'[ ]oHuNP4mIV2P01|O<3xWQ$Ռ.;|*Q=/;2stù<}C/?𢡊7E ͞/⥗r-ټ?jO! H.szO4EY%BPi|Թ3chڽ|9}\?ܞ>ß&w}GO\j^{{qpZ;ֺ&BC8 LjsxF<)Mâ72Dѳ=G cg5O ?M ׆< ~O\!kr  A@ld1=W=A :gVA׬JO^a;ɸ2Ag|9}\?ܞ>ß& [ލrNiƗG+5~\wہ!d  n=I N]?G]$wкӤcnK0>L1ܞ>ß|9}\,HW~+4^#׮5 {9ty,v|P 9cҲ~}k/w'eqrx_WnϖUՃz0 ]+Y' w'eqrx_Wns?xo Q)KXXzu|GXZM{sdcliތ8tܞ>ß|9}\,W"+ԂQ!cPb؊ ۱ ~Ht7  F} ?O6' U'JѝulM.>3țƞ-Ώl- ,@rppsϠWr]~V,;6ғ[Oש߱>s犵 gB-.!a6DVLǃ+H7.-mslrƤceew>+>t((((7 7&gu_Y4?&Ꮓ-֚}7ZƧdڄHY6 $R<¾=CfI7lF<(2HHї#'enaq\wM&I.nft cd2m+n_|BO^ԯt $Kmn+)8=Ϩxxh5cA+-xg]IX3mp2 7<q[)][~gRS0H,#bC[ϟwd_5?cSs P^: 6e%YaEHcܮ{Z0G<֭;MpWxٜL*>ei#?0+eDb\Ap:q0Ѣ$GϋֿF` '_ӇO#^]fg_->ۻvSey8dN]&Vdq@H$_ ~"ѿn/݆qF# "CyďjQZrDt>j&E (!!)Լ^>8Bߑ:nLL|͎Ltko hׁ[RudGT#twֽL4};T7-qjDІl=[p8ix__yM#U\>F:rp&ռ%j7# +#fskkE,<`Gҿ:mdv&zOߊ7wN +5چ>uoF%XRcajF9媱:lSEm?Q;*]KRcm }9)|BNf\iV|1Qg xVl5WSӣa1.X| s^~l1RZnQ>/OMB {TF Q@F6  1^nRk{ "iɲL.GSZxͶouei(bv_ŷS ]HCw*UXaѱO==YR= ǖv-> d7:2lr9RNW+S}u(P\VM({Tux*p7cu&OVWRS&^qoOCW& {/{؞jcdּC\kZ`6j݃2AdM|1Ps W~E߃;:7i5[䱁2 3+Ӫֺ __A2ijqraM$m< /6ڹs$vLn_3$1A^KJ\GBS^^*s=zR*w?Vdmٷ:{/o>Y%~t?yĻ\=J+H,zيGnp8\n?)7_N59 VJ0xګWX[=}8?k4oۣØ!Y8V_9EH6ܨ>Y}9n;__hggtfbI5䕒F*T(~U*/hOBz#ͷb~JXJ,ݽ?#ADk7G@S|Eҭu_˫ui  w1ܿ*Kk;4JkS˖ mb9^+ZNᱡCF K#<_%̖?{pdBu 22#&<;wxYK4mHAPA`p¾4)5yG~;Ӿҿ?hSO]8{b;ҝZ-mt#? ~_e͕%ϤiH03ՈN+V|s[ixcTؒ+xီ V`w;:kwCQnuGcp (氡 %MIֽ=?k{S>7xhi^A{tI]Fis׌w?P i~&Ú'54t>J`0merHLwixX&hoK!*~)'nNk)b5 sk` e\Ȅk'80,M8k;~ҕ=yfNU_W'vWTT;}7QJфzŢevl噃+dpk׌q&4vVy%Y:'vz?#i-?.u]6w$e$gsRIoOZ1.4=4;hfS=pX0Rv'xʧa`kk[JΑT dw5ibKkBKӄZTJ#qR$g8=3_O(ٗEI2|G!y d8˓2ӛP.DŽ~|eWJKm&&Gp|<:X)woy303[⽯ hǿsV^V=s82O-i+mET, sa]@Oo7>JHn/Oj[hTlgY"ᯉS><埒WG##mH 8~/xfns4(# \n@7Dͤ|@b:cھ8cp]*GG5%wKqYìŪmobi$hj6fֺ(܋+\xl|죖I@$9}CW."HBo-u=Fӎi2{WSTu;Y#3T HȠ7lcekJnCs\ܾ R(H:֍onfN+0hc7( @s_3o  [j~֔gb'B#k n_5U QrÍxB#kg,ёVb'~kC?\x.WĐ_ƶmu)-hN-ԁv̠4/$ֵAiׯۍVҦXlcmLeG2G ]/1^͞ ??mquyjZ5oXI[H,ǴoA,Yߴ~smi>_NMV4{xYvwE3,!/9w'KFO@G?6:~|I|kZM坆y67SPX)Xw݆"?$>]ci:xNd0gΪASZi] s?mqty'~kCѾ9Mayj:}mtKcmcT8#NX;mJh8?|AcŬVKD"\c0H' k>I|!HyR&;(ՋV2B)\f:]Ϋ_ jM:"I2xk00+3"zU խ4}+JUK,%ug!pZPX j&= ~ҚW}Sd:sG}on } ȭÚfNhn׼5Oţ\kO[hc,qM$2$DcOEy]3ب?Νsf-9u(;{RӣʑDbu(RFdgwfxsZ).4Y_.@Lr+[,lk twTWvuuxoh$ZӬ.,til{4`gH9 Xkcab->8ڥh|H #FbwI| Ey\m<+I){ u`_#ĨOng--/[X&y }ݝ\#IF"YJ]+oh+6/-ńr"F DB"͂F9 ~wzOmcԴk˨M17A% vH~`{5h4?xF[+MFVb 6Wd'a;*HSF}sRՏu 3KӬ%7B5aGC"vEuk]yuMhjQ6eo4Է;\oPlJ#k+qD D;=+m8VoޭK=4A47`nH "yPpLƳg5ޗ_jv2hb0\R3) qU#9o\*5MWڸӴGpđ`i.ىrwTi5$8;MgL?dY$MVvmr bC62Wk母*м9$Ι:Y;Iƽ?oK &~ 7pً͖ b8|5/X,_|';k6MZO*)ZIXJTIxkHlN[aԨ??ʱ~|RhYQ>k]Oz=o +F^e]aӭ5?V.hȦAnnn"vH| wv\,nf2ܴrmYrp~e3#m6w ?g-ԭ`sշ۝']VTah8:8BJL1%c!$09}s+>%=hm,ąfaH]<^4#(ߡWC%{= %%ĿFr+K#nf>t=IʿGj_x3ྵi)Ŵ̑Ð=UK06G_ANP' 5[&DR'u,vߡ+W~&4hu=K2(pN9Eϡ7d1_*$9Rx5hW> UK{2Մ+t]0@i8=Ŗ%XZ_%Ah\UPe8F)'gPpS>;OZ̖W4ws.Ic 5mb=N("F2cdKᶾI\ҼKn&o%]@@l29+5x7K} p/B>狡+$:2.!O=@kL`Si;@$JtGSUuoC#~$.`XpcrK=ZϊZHgZN.sǧTnk]%fDiO^YׯdY.l%-!{E@.Էڈ4UpN`w=o}-Kp;}5R=-.@3d< t}7;|\7LJ(/o'GdcwW$v@S*z{I~$zIm-Jہ\q^ 5ǜgʴS$D 󞟍}AKytit*^_*:`+d1 ek xi.zQnk~5koR]k(|2p~٪_=]pŮliٞKk`1U(G0<2:%j2QFpcQAiLJ| *k=q5iڦs4Vڥ9DTE8@%[ |7 .+uJ;1q45KNo"EχuM6m:.R Ag02Hk =axk 00coverkxf%x\quuY WW TR^҂( tώ+xñƲ[_[kq@+9q\>5~cGV!" +Mi~+wb&%՞F]zm.ǠgKk^Ԧkg&x #@pc'v|{^7RյjQr=[Լa+B}֟*:ǔ @Cg9v̿=d>X#ۖ`Im@5;hgNe7i'_''ZѼ+_xk[fOfFYAs*1WRI8Wۢ俯-Og/L<4W!Obl#!dlϯA#ZhoM cv@+?ex66hi^Lܲ3nbӾ8~%x/ On⾑m0IN$ukK*UZ$n5"֨Ĩl'q>i~ժ;Sb8\]%B]K>-5%`'ʁP8|'9⯆z-KU--h>]ܒ91$p5 54 dՖp ߎH'3|.# Njpq zmctR.s>I{i{w,m"G?!;[d8 n ȯ/'$7콣$шZۘU)!U8}#EM6Gӵqw+Ey7iBrC$~·ZK[?lX0!Ps]\gG*2OmG_[ Ө$|k;t!mZ,+k-CQ[H/M/t-(n!ʣ1w$ gum$0im0L:zGxN`FDmH@;Tך,**[E=SqG?MC/@OuU6啙H,z#YǬCڷ#ƺŽ6Y[G+ t ^cjIKYܝ5uфQF4=i-VܥLNךPtcIҖ}oOA pܖO-?6YU'=p#{xXkx_Ϊ޹%/&/u&8RsjՎWzVl)]&diXjI^zd''_uM.7%< ?!V\{oKfe#'G O"ğ\6uƂXoӮ" hQ8ʐdʵ$k? '^;o2ܴnm٤8Ul-j?ownZ7wQzHs#0 gtnA,A@o-G(Nµ!gn5-u]P]_Zyf#2񺍮O> s?藗:TVq>A HF29 oϥ}Ej?45Ih`x/$b;ԥ2,)yYy`OZᗅ'۝.RT2WJ!'iY^6SHavPx')¯Gm~8񍟊.4 ;zQw^d ep "N:`~ j|^5e _dmM1<M so1?by:wʚk15tg Htj#ڕ|]urCu? /5?fxFuԵ/\64[4rDΖ"Fa$X`?CKSmߑ]ooOGO?7dڷa.Avi䠐$!>n[P;o iѦٙ.+(DJx%תݶq!)].g1Xk>R]v{-_ɧSZj {ᘮh~^ fZ٭f3.a0[e9 FO䳩ڧo_Ώ1?goa݆cegJt{Β"@[*gj5<5s,ziLJMxxqU*S +ty, 1CczDב0jcٿqTU9?L?,nC-ܠ?o k-'>М:PȲG5~y òF]^K)* e=@W`_q}v8F X kKgb5d2ҺC[M5 _,>\x,?g'+sfd8*_*voB/^y~ؾkzoZ {m%$d9#U⬷YPoArwe̥OgAW~9hvAcZEgn%r#.<2OZYGGX`\oʿB ]> ZuVVbaxfof5~ BV?xTj:qndڪ€e<~,Ua98ƿ' ?4K/F-康&Bb8ݏ¹[~+xKT5-*6{FǤ m&F+s.&sϥUo'rODº)'6Rɖic~g^x؏ E?.do ECГI9M-TCf}oAx%_?ƅn*H:0`~'_ξO׿&¶.C"fx ; !{uOEH۶a |CNnM+t?B>ȟ U'Y{ӚUvV;^z}MiZz'}~{>L0[pL"D죞9 ~|@6j5iZh_b*XrNx÷B?o/~$Bc]Kqo2_2HFBUsA5GZ[E=J?,Lvmim:3L͟߆->$|]ԭ }.O/sxDT3e`gk =''y65K8wJ1}k'Dֳj8a7`~<׹Jȣk_->"||[#N}>Z7ȭROy-ߞ9FڬW аiRTi/~fXNֵ /Y.ܱ8]{7X\ ,-k$g Ԣ[>7xoi$ʖgs7> Y\-mRȓ\-֖~86jqCٶ&L p\Tp8m;1t&kFqGG FSE7[ ]E2O%ԁB ªl/Ssk~ O ? xip" A8gg5{M=j^u/yn|g -|22z_Qr 'cݣkϑс?ƶl`:tDu#-8WFw@3-jUGpw4{]c}OJV?F_?U, xb&ҌAv3 !`¶š`<'$=O5ѧU7F7bR4,jF27 {&u|ZԴ{;-E;,,O;NO3\ ĚGRXԓ\ߥ{wߍAvvwykh;Hys)N֣\螥63j_YN%I{o7W`0Iʫ|]~m:>g݀ pH# #ھ!>-U;|uwyq)U R*sQ^EH<=@0+(`-H ˶XGzt_|[n֮ዼc|?/]"[xRYa>=~e*ㅍ7|3 "#89&[ش-25%c^2}!X/+'G=L'4V!ׂ~֯ P_xJ&/WQȨ@+#>^ᧄtLm#ixtQZi?6Pk>+K]ǎ5Fb>Mt?KfKyٚ_~FnI}.O;[Z4 ٽV!HP* }~S^~F|jJo/ }x]_zƽy?yrT6`{IƦTUٵ*쑧Je6^!ƧgmKvk#qn$zW1_ZRT͝v\p}>>Olm9$d=ɧ_|m1u3xzti-Na+u;{ R$/,mxJg;|:f],6H(CzϿlQJ폚6Q/qj5V2g'-^;ODATY=}8w 9|I5<{q[j:v5Q=ܼ@t'8<j?=*67Zmi\g'鑞k ui4JO\?Oρd2.?O?>OG/n<s%]\<6+y;A-5OۻiTZ_X^}`G*̀h&7 qntSMWGh K }Vn#Edz8׾¿k|?x-qlhUNC*R:{rr$pZ|p>7𭗃|MRcdHgU$q$pZ^6Sĭe/x>F4MOF˝FIRT:)n5+Y%0z+[qGY3iw$ YMQna8GuV{_#ZA^x#KM7[ѻpQ-̻)#g/L|]B{{,Id1 ^_Co?py}_6R\m+(閚E֎ZRִXlfkV!P*K :b#\mK p-۹'n~$`VVojmٽ\ɜr)G]mmJhzkV7/ja{#QE$[y2rr2 c ]M gi0rX@9QxYu_ n׼kk꺤>􋴊Y6;"d>qg<>"miww6{F餷 $dcZ#Lo/;3Fp ghko|8[gdNU|VZy=a:[J7ySY@dB"T;d_Jd.vm7dk`['&hm>ux1N3zL,֑:5 6_tZ|Kyn>ً;[P#[TE@~ЪTVڝRKqs(8g4$䜦ݖV۸r9;{ e76z&kc#h3 {Wxv-_SP][;x[P4&$9vLj+`ncmqWkp\YK7C}FkŮ憐P7F=uD%h}Llcצ2w;Fp~5EZ7:qW?_ )_`vy4k' 3fT] K謤q1&,zWןfş-~LPTXB!5h? tM*NӴ[xɍ- ǹ2IcOjiP-q/,~(h>֠m+MVVDieGnH,|jͻzއ=O׾ldDO弑2H-GErqvVwќ+~_|-ַ&erNǷoz~Y⯉j@[yP FvӶNoK;xsPtM~R*~`Î}tz j? hxJӓ\Rh˾+˓;s_V=RMϜ./Rr,u]fB}ցr0sAiUOmH Xx!/ֺNX&R ;03K_;jVυWB݉m?oxb=kXHZ.Up@=Ec)?W#v_x~' 6cODA-AׄG`2mugB@YW9x b)P$4*/ V+xaZM$&sYojVhMVDɆ _L!(0Q sUCEX;Hn 8oj(uFo+wZy: ?ξJӢ)u;ZO~'xu C`sWm?f>/^--Y_ xSp"/ ?.N{?f/mٷXҠYZ ț(0K1Gj^qM)@tB*<zR4or i2ڋx"< '^t.y4xTCRIM4ܖkw!JWAO4KO[ 'Nivfi9$<{ F~ݥəDm3h@$q8xտu;#M3̓0qp#:c9%U^*Ty vR$ַ'Чi1>pEơGq̍{5^5C%Wr$^g25OSDK_!#|qs;c7>X?=,S] \5gݴmqU>Vva騻TcJ$y'$zZ,y ;`pAo/׎=nNNiH>SW>~)k֚NxRrdfm%tE9GDqԲ:|Xޙ}:SHV\ռuRxm|B_N~2Tgt ZBH"h'Y"P8<Ilo5=]RǨ^DZ"_fp@9'|70GKVMx!}&@n,O.kUV b@.:9fu'El,mh'o;ΕR=H,WM|s FT/}gοo/CM7>ibh*Hb.@8"Ttɪh4;YzEYXP=8Ŀ <5yplhyfT>rCvݪ[$c9ȯ+Ѿ>o(1ZE;$~6#T*W9{p4ӝ-wg<7}=HTVk_:͸$$d{_Oo1y˳'iGW{i_ğYUO9ieV+}.=MY-ű1>Xpw lxJnkh ^Dүލ>ou<[ɮΞʌ.7z j+"/7ikzt 6wHU&qUf3'5&}\ukBيOJ0)%O֐tW/|=4lK-;" !FN1#:~OLק_}3ڭk:\#xJ)iCcW4=uӎֱwsqy$KpFN22+R[c-j"d ][-PH=7 ڬ wz&%Z}qv#?Sn"*~5s go&p7)#>եON{xQ- JY$qS*#?;vv'xVĚn26[Z[J!UXhPclbL`%vnl|R׭_߈|7j6/:֧(:F1,IW}/ȪxWƓd,Jo!;A;\֬4X<_?GI|VzncOq_6Ur:FyucNJ|b; QIVHp|xKkׯyKnm{<9Fɦ{?M /%t1^$,Sx T@t%W,P=b[ Oo3LJ_U ħF>FY'^uWzZq03X+fFE^MdgKZ/ѿ F?;Q|^mVփ^>*4K)!@;*(*2OV u}#Io =dz ^Ź(EXӵ 0Ɏ*]RzOzn4mf M-?, ġ,Jr7\n1_C%hG) +97:j)q5Eq"JsNPNdW[ 5'R*qΜrJԦeth^X%^:P057WxM̬ Lr ,7w뜀yg|sIwe[\]JfsmyX-3:N+Ķ"J~"F eKz~̍6?|]]KEʌ-2GpvwT0H99k7Ѯ!o2mhȌ(ijc*޵_cV{]f9;MIRU`HfG׍)-4l`dğ?͓jVݴcG\xb_jM:1ZAq hsķנ'6 wrk;k%KiR<}!+1!Tˀ-F%x.lm{(|m'E@r~Vqbݚ.gch~,d`"~\gluVޠ@:W~JX-is$-l$6 v'#Z_(&/ۧ΢S ;z5 ]=:_xN+o8]d\vf=I5K+[Fź?.@ȯ+ V\ͪ@n5f@`5g W":>位.&HLxb3+<^&Xx S&v$ hNE7un`Wr@#gMwi66%;{]]ヌwe~ [J}KͅcUub5mkNDݲa!J䑅p}=9~!𗀯@veXVT3K`7ߡ|c=:+59d-\TDrA85Qtҫ$[^ޛ4쟗}ߙ\jSATDH޽}B xb#7?W6K\ؑ22,)ʂY1 <פxcTS̒r d p8T8ޓaΚOկko>8[ #% A_R/2YEH\g>O^Og"CF\ "+ǹJGZR2FC`1q~mc=\Oj$TI],A M{OzF. Lj ߺXy81qOO ^ Ji㸬\vkiCeuf6g K27-&5$瞚[ :gG"ad`|xT>xtgO}2-F;{U[es%g*,r@%x&x_QX幹Annm gӎ=wOk:m>e%ZΞQf9%hR ++ aJt_]i)b5>Gq5]?B&+h܄3M,SKqrF~sZ>(foH,7mEt-cyMw*`5ǒ9LiO'u'Նq ~`n#fWEfЕ,3t>xfN jXX;dcUnXg:URX+n~'MjXN40W=KڇeuyX$Q(嘓ПAWxK⿂bj 5<sCW|D|5?2)n|b\eHݞ~ ZsH2qgz9~< ZyW}Ӡ-m:֔n!F2w=_t$M[{u<$1<u o+0:-܋wq" 䪄78:|<9lEYG?¾k36NX\pHTPpvvx`iRҴ[列Vwgw |^eK (XnN~Y/%Op`Ai* JI!_>3jl 2[yLWTG!'GU I'/PY-dTڧӧoFVGLGQ|B5-羸D![UI]ITdU\W4Q}/&$>M9?/խf2,]])2!Y pTn Wuhfj?S374}/Z08`{,y]u48⟍5?FOh^&k{ntYmKѕ#x~%_IaiqMa0W77Z~ӵt/ReaR>XU?/A]7>giOPc#WqhEm4mAFȪԂ)s_QQJe[5;֋?ơqy Ɣm\CʅEx }GJ,9S r(?J5OVGo9e3Alx4_Ubk'-w<.={VzO.]v D&d+; qF(8hWqOMȔqOMȕ6ַL,zx%x%M( 'VdJkkF\b I?tuj@|>~íZGp?]Lv2d<qXi>׵ o IqCMIq šZۋxe#HI'gu G*#­VOx·IoD-k9?tL{L`Qg|ѓ$Oֵ(5ΙA_I=޺Fw-P^Z^"\HcGn#`W"23TF~Ӿ0񦕩[CPi$|K5 >1w$V  ]xi͢=Οhe+p#y2 ڽ>-<(-<*l*ߗmO߳O뿅_;}i0A:;)&ܲdpvGq^^{ Nj[?y7"Q Nj[?y7"V45-a|o3wW+Ghi)d:HyB:r:/ҍwfk)ԉm\+qHδ?)KRHԈ5 t7`?2u-P{ ]#kے׊_‹ t]HX]pny,c8#{KcXύ_ycz9hcOQ8#iΙ}(,HO~|%gWl w筅_QCIHƀ* 6aӳF?1?iXC|Kamյaw5qOBψ~i{mRɌ3 *`\ҾO_=aakuߤȷL~>l$+d/O-v{[[ed P1ZP<۲>b4$,Ӆ`7(瞀Λop˶C$mhHd\O$Uo-ؓUu=F_i|SYt7FTYx0 `a?.Rk_񭴃O5,ͤ[Ɂ VFEyհU`Cg\Ib$ T+bHLHRҀ7 x z;9cxͱx8 ]bw`ׯ|C^IC<򭭺$Q@Gӹ5Go_PcC;r"`4n;Wf䡻ӻjԭS_c&h캝$o1X>5]"'{^k,pjF9X;,wt8)AK\_/)vm +nhMFᯅ<=sY60yΌFr WE,5kmj<o4uޙw6&vK'5W{|M1#;IOluCGBRIichx4ӗ/⷏?jxGޯck ][A3|w=zx< è~vGU A>|IǕ~_gvÝv5v. 2~g#SQV* ~Lg<ة][i5>5P^NX ܧjB,A FWx=bI/|_K~#*ّg0W9>:*p+V߿_xhZ(XjkYY?doS^!%FT$Bf?_ kO h:LuC}Y'{IJ1ϑOʹ`GwVRQ 'רc&}k_1K#^/-U86xD n;Vݛ{D i?xK?c&T{{(v=>w,[EA+In϶2!@3-a|}.Ӿ.W͘#66$ ߾Ny=_TMa{wKxB0 X\¿xC¾,ZޤbI $${S|z|XTrI?i.ei*7JUAcm|7qz.g &mH[Ȥ21u~>;ǂt!p(# .wLIY>(uǍfw"'J" 7}Fn0< !XPbMGVS3d׵5Eh҆5Gb &3+ x%J$mݍM~-w[Z@;{gE,|_?<'|7> б`) %q~>~> ס\0`kp$~k[VWvV!gT4r) @ "JyUp̽(I*pG^?#?]?"?ֲ?#?W(??ES̏zCّ[P??ES̏zCّ[P??ES̏zCّ[P??\Sk<1} RGJ.Z$>fG=n!Ə?ψ_ ~"4><99`M.;AB1uW42?s4fG=n!Ƣҿm|#JuƐfG=n!Ə?_W5B:͆p|K%%cd(޹#L O?J|Z\)D&˭v:ֶm!1]^IW()fG=n!Ə?1x_SмC MI4ˤT>WE:?L #?Gdj/+?4['i/dkg05QY|.ŏC:;lʺKY?yk)H9V*=zcW2]Z;G4쨢 ( ( ( (9n-^XA$lA1^ yu{Q_8Zߴ?GT hy!Npzr>Z9}_xrIK̏}N3~FS;sX|9]!c渟x|-ho,1NAI<}Ӟk<A¾WP[;Ehi(BJSƉqm3ĭU{$JY*DŽq9[lFƚz]$oO,?lݭӿ 'z7[Uc,2n'FNj$%[\|u6#DX$&ȍRB 0+ߴDz17VnHeQ(=yՄϝzN)I.*oɨ?W=> *?j?R3k|_I~C-/g,yxTKKNHaf-ªA!:1TtM4"KN*?}rn/5NIŽs޿*V}2و=Mp%((ޞ]aCRo}:n}]j 唾D8~^iW>Qϧ$M #&nGW|;Z>+km$[h$q_~նJp+>іv>?f>CZ&ٛm®N|QÿX}NJ'{y0|@=f -:)pE؜wt"X:Ɠ5{zg2%ُF8QCnYA`JS{ҾNCn g3X.9Lq'ҾQN>×kŠY}Udr '=k]A$'=-oUH2>s_SMӆ"F9?kSMoGqeݴ\6"oBscFwYb+q^?{qD~{di_U1yUyUs~W, .kTO{Oy&Ok_b5kO` 18u5??dR q Mf :na־cn+iz]@+NkM6iwGQ[k&pF@k6^Jޞgh|~JZռ2[:v ' WkkUEoiҦ6[$0d.y9ν_]?v:ǖSޘW⿅%wq"0lc+^/A*I-Q'1ͬFKer P2JdLk 2[[$RVSAӋh#M$;m[܉P1c!5_}?7uC=aP/+ya> ѩ>Dow6x?fo?1_2HȥNOPk{ ;&mFCG|Jҵ۝j;/:eŲI33HL W[↛,WV@dQ^Az~u|K6X †XX5=G s^+x/GtTF`<O߂ih&[WfNs:~٢xu-ͻ*6: uEeHҟ\5 QmHc#:6.6m-^4*Ws oOVЕ|N)RסKZ_kGt; z֟b kO/lKEG:g ?-~_EBzӕ8_#S)[|j@̄*/j@U k<)Lӣ,[?oƺq;Fşk~0ѭ.^kDž'q֯kC__`WgtSJ)iNP;/O'GU|QH((((((3 )boL#iՇ: qzW׏~(}GBPnytfXv3Lrʰ8@Ŀ CUW]V-bj>=eZAim-hm'8 @c?]B~3<+ V|}w,|1=E˗yTE0}i'/j~*ԟ:Lg$+ˢۍ8|*[=߅kx$ lRAN~j __ l5;J[v9@C>=ɨl|mMk-OWm#n-eV94IٯR}t6ezzM~)k7z-u5?e4&ܒ(!IW6/ .aa,7M$R)u,pָ ;G4mJ:tFX[Zj(8s$;^^K?ėw )ź}}N ۈ峚IaE.rG lt*6U|>Ky|C ? m< ,^K'qBb5Iy%+5y|Yyjv=,D}6VMq.70rk/ď=;H4ho /! iJ}spbHv77zώ>_Z^Z PO5K<U')~W_e\ZS7Q]3]%|euOu&͋a#`*WUJ\٫ 0i *쨢((((n=~ÿ4'H#[Y 1p *ONTIQaX_-8*nG6kL6l0ݘgJbAbbq'J)=kFVs|'iNx%dug=2?v ղEI˸ʤcMݓ\j\"QFAkg&QIadE*ik^>>X>]r~nT<=A [<ݷ=O#hҵ_h6.YkQ;G%񞭮][ZZl8yyA}pqQCk-j ga@$XrOӭ{0L!U:=[ZjZZݠF=~q]䰐*yZ0v|mc{nm-w{Ic 䍕>oÞ5Ld-yKֿ4u2-+Ε8mZC6'oq|5i\tϷדTS䏋?T._NM)(sO|DkimZ[ 9|+ŴW9NԺXq;{W84}rI..fIT=GȚJ 1vw| iBP-2 {Vw6HQ.en$ږP/x+[/-_—5 `V 0>e<$s?Za  \%VB#6VČrzjgtϜ$'4Y|Pn鴩3C4'Teg#ԃ/]WKj1m ʂ/-mT? l?=N \YݛX+5f,h:1%|2:v_vWa[X rͷxDq;y' Mc' [߳k0:"|3[j#גݣa%rKIT9+@<Ɔ&bg{cS1ngڄQ0DRji4An%1ҹ dDkT㺱_{/ZP|,o,4vVҞMaEHǁ'ھKxFطW$52rx~m&'66 [GK$i!}j:zl&ukUԖkS"*3*Ga}iy9LGW%y]\A wmkh8ϗNֽl."4pޔl|Vo4v譣FfQ$DXܟgӏWj?IGl~#3e N9=Y7~ ޗ^%^b\uWg;_j2\BѬue%WIsYEtu??QOt=*d- [y#?k?eߊ_/^_n!j4Zlu{u6yԗ,[?B,|ws~heƿfpf Z|Moa~us6뇃xVa>NA4t/oķvj}Gs2Րp}OZ]gW|Capj*&9,A;zgl jMI8(bsl2XtXoc9XK9U$ǰy|dYC=\9  ޮNP( 9vHϭz4unaġFzt*NNmz3T- VtW{P\KH'$@9<ץ76ZZnoHgB 8-A8#=?uzΓ+1ɩҬ=ci kK=)MxÛVQ_QgVS,MxϷW.OjڬY2#BVV1'551>ʻ{iM E)u|+kso˷q5񝅏ӵi#UX`gB[a сi階ot.U9R}:w<3(By e{Á?*2#VN8{![̄GgNQ#Wb˥y'$ԭ~Ӿ_i\]gqO~5|㏁Ii^چګ˔I@?}IS̿y8rB `}sW.oLܓxҩ7w֮Q߬Z(#w~_j?WSEn?G#w~_jhr}n?]MX[X}7w֣Xu4Q`9oFc7w֮,-߬ZFcEQ߬Z(#w~_j?WSEn?G#w~_jhr}n?]MX[X}7w֣Xu4Q`9oFckLѮC܎oE(`QEQEQEQE|(P r͕ÌHn#i|<¼VYB'\N&5((>?VaYc i5q/ fbXee#+DPbL}p a̻[ ˆiʢmv?0tȣL\D_9#qWGZGorPά$088kDQPa_ʽcZ{v7%ʲ+ֵO|EZA,wP:9H߇q_WPʓC6m"K]LJA F0:z Pt}=xՌc#sZm^+8խʻCDҍ6 -L6zk"!ntv,`9ܡ+3vڏGun Í `3>Pk ۋ&PTu.Kثz+ :x+k#:kmrEcb]h;S:OOwҺ/f&upef #Uq_=ZVMN÷/q8tv愦;˻ʆp[q":tXԲj0ǗNȋ`*RpF՝י[Q|iZKRmُvŤ;˱wκ//[-{.u5fH9"J!p^Z4^"ҴKMw7Eċ-Ժ5EaMI בE [olES2$4eDJ-KcO MтdPPݳ?hk'nO²Ik!t`>yf9 <?gde*xpq͆6<s5LR:u>TᇠeyI|oyY&kd4^2GbH_Ix/$zu% q״n|kQIT7fF(~Dy3fӡV CWKHF2 *v ֠P'Đ SRsq:AI6I23_LCxU}iY}"*d~zǀn#ִHL=8|N#GIۦp8.iݞlxR^𽍼-2xCIAgmڡA9=z~;7/|(E Jn<_.x?tI:>Ai=$fm :?axAkxJ4i-|*F@dwwpiYF~g-JHլs2l͸$OyV-{o`r;=r$ 3!BA+\qZ/beOP0K{G"凫w5TH_<]ťϢvUh^Wr50[]'\-$95?aGYҤq +Hɭmk9 D[BݏJ5~ޱ>g 2C'l ?ggSu>Y0 qɟ-3I{pIm[: biF09QUZ5'A]R7R47R5i 7 U=sY֚lث8#{pO#Ğa^;Ek7 ߩ?C!R5|cgpB\)$9'UOj)n粎vd^qq@ +eX%TkY._ 3_'H~=x ?k[ ,V"gdz{ވ42yvC #1'@^IkI8 TYvC2Ԫj3gfԟM\Ѿ1?: yrH'$:\zV{[]^VhWIsȯP||;mQbNwƳ`XUCXf8FH%w{xs/|RzE Aeln1&Fk^b5hzLJ{=FwҬZHϱ5! iw>nFHB7s\y 0gv9^=ðww/%Q}D&1;^9嵋~mwOYmgAD20 }ŭ?z]KC}%c!:@erUVO3^~fM֗p&yB !w, b ⨴ _eoj֓48N85VkS_A,m KEh\*Jfr# xvwEYVX+`+r3@[ZYz~F֍ xOׅd׼+xPqZX}㰁Byʣ It5f 2_i6-|H#l f#iOVwzW> i<~6KB3IC(wI8Uk6?ޫoi;. gk3 ===Mxٵvf߮?z7 tuulFldz; ]?0aO~o쟧x⿂uu f亴kN>;xᅦxj]&TIf܁/.ley9O m7{\į>}j0KCH9Css+>}hQO#Q G'0QGր$?H9j>&>$h>-of?h,y!wِFnm%s#Q G'f^#<1xsORZ;|:n9P-o޹9jZou{Y֬4u9[]Q7w]R czQծ虳 JA#r>yo*V~#"oT uD KZ|]m&pVBl!oXëxaԊ`|ȮP1)8 D%>=bzNڥmj>#a9Y-ׄZF+7i4a1#;yg_SѠvy6S@{}ǦS9(Eͽ*r8ӂմ;xh_G|]r"8;UEXՍ7]R`jJ]j M#O`#TPpVQ0T9$geimŤQ|ZȡK+Rq^8"iKg~3,uSӷ2~ϯj~|5xK\%7t3J.+ f6>|?>=^_R>(`QEQEQEQEQEQEQEQE|u7UΨ+{-w#88'g+Ei"X񍆽`?vy7?޿S?Wv?l/,-uiV|o2tQԲO lY03҉sAܬjwGA'O*Oޤ//{?hq_ @ź>ѬI5`BN (ɯ~ R[c\Z74ApHr1Zfwpͧ $k#`Qe:BW{uUTSQ{5˱͊ҽWƾ~l|Oșwܨ+I?)Jp6j|Z~ym?5[c|WxUQFnu-`RۄFNZUHJ\xwsk(Yb/H "ʀ1}0g!=jVRQMb[jjtAmH/{.Co1=cyB^bHϕ\nOKS 'BO'* 4'YG|inHPcN"Xȿ&>_yhG},S:+^;MĹ,g kAhMm4qca ҬxƋKOΑ{n.6-ф#8^SGa?Uk4`|4c_]ϴ>OPm)|l m&XeVI4ٝԌ O:v[F0, 2rN:8#P EGLh,jjBq@|}9?dY";LΈ,l&$`Fc9?>LjAs‚=a>uXTyr`g=3OLjj(y[²<?~l7WxD][[i|Mŷqg~iO /폫~!ͤ=W?+TRt`2a3[QG QU{W6B)710srA涴 K+[ B,VK+\2y+~)3qn4$*OsY_%&mj?LK].*pFx,z+ĸ =>iskySZs=—"`$ں5߽s[Ys9eur:M-M[ .tҋɼEv*(p84빬|Jm.ķ7SyX ܀9Լue} Y_Ė֦12vH_/.!Ԭrn˕)0%S5ŕl!= 1FXק7⯉/öR7圴)# Ҽ?5[m=M.-"uѭt1Igu2yg&O ҬR[Y,mo{_#Iu>3(E4[B9zW/M>Vj A \tf3~&>#Ѯ WH`hbON1]|-)l.YhchtL~a8%l<~9|ouc]Uhcگx`R5l@"kM!-58ʫG8J1S3{t?6Hz&\ĭ9 9|F~+^?ZoN6^yi0׮R֯q"RI?³[cHRKYjcwLA+zIG<ѡ)c7Rdy΃~4 _sߍ.]"9t2m&6981O?߄ q / TTD#ܓV?hڏQ;KVͭ5&/7{NFU»Vֶ)cVڟٛMjMY~ʾ a6wΌ3Y&Ӂ%wceBcϵc_m%r ҄OLמGOV.xGņVw6w p[É ` ^_:{^-ѴK}$cz~U=H_$>VMZX.Sas"QWNJ@Mi.Y-&cǀô&"?\;\F77?IOI }_fô&"?G;Bo))#4YόkC)|)k:UYV3,c<HIOI7i4gξ }^XPY!Bo M;A𥾃qq*jOl#潏7?IOIt+);;]?6C vXkbQHm&a ` d1)h_\M=xo.߷g;^/\IOI7vw-oYn\n4촕E5hf9~jy9]xGx;TE΢n%n',dcw5_;Bo))#4ô&"?KK]?}ou6|ѯ|bNLJ/jkg,Hݪ'pd&q+|S6ƖVUO̍=AIOI7vw"2Y$f*TsIf⹷ *bO>5G;Bo))#4ô&"?E\=kJ(!^bp68B}VQ}A x,BtP?\Z'7?IOIufTdԢdtҷy%L9$O_@/Y)X!'z|ϭ}kЛJM 褧$׏0Zښwwtq&?0}V\lmu~cl~]AR7 xfKԛJk% 0͎1e^>T(`QEQEQEQEQEQEQEQE|{+v}#o6kW­b}=-u_]Km.Xad~Iu,fŝw Smo*f5i<,.o4=Fk˵9*T9PúT0x.FVRЯE.%taH`͟ᮿ+5 tyoeq4s3|O$jqs;Kk45F~11qWTpRr]m-:k{|TQQnWzg}%Ϋ!vM7PHc{b@X]7^ m&apVF)!f.T1 5ZhKs-ӕM!rKyXP"++%.mT'տ JF5ѭUa􌞅1wiz,"ō qw*[# _LxoVI^lʸ"D;$`g|~;-/,K}|{ZsԢMn|C:?hIdǨ6^T&"jO$5YZZۣc>\5hƷ.w+_ &*~> ySХ-ccG_eF>VVn)+ cZ9«\5jaڂ? >mм0l}[oMԢg\ 0<}GSzQ(uI>u:e#>o-}dC{+Ѯ;#>;:7!C$9֔fZ<6309Ahzgx&MJHA.9*'|A}h5魊A2q ;ҴcJúʱ`7JoN(φRiBt9'#'ӽfR!8F:M4kO}[Ե|ggr1}Cj&Y"Ok7uh+gО?½PUvC8j{Aq$f %fX] ?<\hI3D_8  7r Vx=냲[Ӄ.rl8n>:z;E$sXҀ4,(PNX}:Y"koj*/'ҩP(:t*)$g@Z}wqg2$8eoׂ hU"ﭤOx>Z@to'O?tFkxtp~ Uc]d~eA-4ύyt[3r!z9% }M;JN|=y"xEo>IhDF|=0xZze'}|-q=ӓ_ށwE&~^tD 1w >ʡh¬$ܩejuqsߎWF-+Mabhl'z> ΋\iŬQDn`Tw9a8;vw~Mq7c#ɸ@E@F=+xmc>5i`csךj {tXMs=50zuşoJ,aX7 ÔrIa/ux㶒)b\3nnEѿm|AՕ<28S$;5?t{7PduYtp@@$q{XBՓtQNEӋ7W]{ǂNn+ yDm럕 ZJ=5 אO/݀b"lSu6Sp"Kv #$N]~:Mukmk+]imd`~r2yS]ƚ[3W$I(8]l;G@OG@O_͗4o(~#G4o(~#LG>/Uk8,H<}5_2~.ALuMkS+J񥻾p,Ĝ@W 7_|q_[`/5hJKG1K{u)jƇs[uTq#qJ"_iUܺ-YeV5dKml6mg*NFI4EwџyK}\m"\>iI 'ɒ-t'.5[ ,R``Hۑywe|qKq_^8܂ EK_e\<* tr̺,Vkr"hXRz}fY^F qAjа_I1Jchm U{Xíʀ@F +~%I+o <39تҽLr+ _/5R[=nb_`*oarC ~ZÈ<96KӋnވK:t|smWSmĪKE|1`w~U_߳'Q}ٚÅd8Tɑq 0uS_?jw{{ĔhP<:u;k-IyqeoHZ :;ƾmi:eroIL/ ‰?'z_ ~&B=G]}G;wk4#[cUq<:|% Ě$vml۞7U?|_CzGq4F_u{) `T.V[k?xoU7|1GQh(K2 JAAvg.(`yUύn,o-mxK;%еԤisq,/& ɺ0I@w#⦏=rV_ԞQbCgu.:##cNf__M[e/qm3j7IypʡKcgMV}:O/;Www`-NљHVQT%pąokx* 6};=GG{?%ݕ%5a9Sӎzmcu{#Ú= vz%Ntn*@ޝ_5J_ǢO@"\Ҧ/b#M؂IZ&1ypHr4:m;k׼'ҒSݭI#1yK hd—dOwra^uou-NζB%٨9fu ;y"%e4+C nu(|Ji?7?t쿭}J;!\%]]iP[\CG#]qF[[ݭ΅} Bv+ ؃ W> |L|9>}RydC}HSyns_yۯxV7YZm/;LohJFrUŭQG~t_xbv/03os4.pŹ|og{oYh뚬h% %$1p#;׫SΫgf5ŬB9D.P qǡ2'j> $6qj&06nNvd ~Y[}--[7[K~3#jӾjs7@KtI񯂼kxv%y.{kKmeѣBLe]s|W:|3<72i7:6/GssWk.e/ՎZWWMgQKIoaZk&or#9]|Lj"m:gk 涭=NeKm`L|es2 A+]?־Ho4;S[g3<#<TvO%I6bíjI V7j5vd; 20YQ]K_z;}n' ,4B_.n!V"q*;O\V97^!MGhd7Zeլ7FKyeRuV 0ßѯ4vZ4#5%c؊?1_CǞ΢NKKnmtf];Y fv$tKgh&OS/A Ut[*Q@Q@Q@Q@Q@Q@Q@Q@Q@Rcᗙ#|]dn`FMs=,22:g5!>QmE>em,r!JeI_ۿψ~9|%t iꚅR9F\2 /I< }u4^XƖ6X'pqzÚB2W<~N|'t0V{JfSzPUKOCkfo#|^[\ԟ}->h˘X~#/u;yOv |+};2 >dkf`V N759Ӧ#wIƣd;?|F=6 R!;K杁M[8)5>}gEЯ'-n' +J0:zJgv3kБK,Gmg>5uoxz8ei[i" ,H'FDUmW]ZٟE: EF5#dmtk_SOC_so X~D"<aVojKs6lڋ̠H瓒;viOGⷍ|{S]: ;Uc=˵K\n+x;Kui<"l@ܱUΝzM>Vo~'͹h|-c%H&{YI cv`z_ c'h{D~sPWㆇ|^kX4@k׀.4v mmxevV7X2Fe$UBG5 6ܜeH[F| _<^MkcwWn#s H^G]ݰӾ0:мQo3nǣ)]+:Wci|yk]\\mαI`g&lw=eND2 \5:5x+lC{׾bk6a7~,G.e ˍ|ǒ:\4g d̸ܱ9Nlaۥ;d$if p5hڝug$HU,N63~~P~ Y_ďC R.F:pZ)]luWTleuD]7+`R>@~'hv&"E  r/$?\1xwm-m9(('6dN9ӥp?lt}Q b tH6alq_*%kxqqqmmu, |;p_xvl**+ힵ_C]iQ[Iky/pdHg 39 >> ֍g[["eX!G~b;drcߤ:~!e0ƊbqvҀ*O >2 Ozָ#8~ipC %I{~]dZN-ίmD>_ 6=<=mW7Pvg ؊1r`|8_J]Y> K{=C^i F28޵?`H R@WTV*sf}tx !cĖMU3*F:wy},rO벼g)vӌddq}=E\U\KSRQW>f~Z]’HVYeI$|۶x/隆4b]V@~dp}pwfC^ɯ|RzEn'I/?QqpѻH Jd|R?g+\ǙtE ,Lz>2zU iCZm,C>>!VBw<#W_|5FVo:1!ie~?o -mt*KWW:Ziox^٣yu y(q 897e[=BNѤ0%˝DZ(%UFz־8a} "Y#t0L#sDby9rC|n?}j4 -õK?.~k)VrWn]z=iF3c?ݴ&$ VO(3\{oſIK-svޱʞGGWd<5mz.,P6c%8ֿCbg:ui7o_?#ǩM)5 Rx?fo?1_2ȥI 0A56#yNsD𯉴 fmD,܅~|$i]zPߵցI-Y>wbrKLGMs?ď F]-)2:_N2 7)wI sj!(߿xTeiFӼSz4ZZķv"&&7l`ßV-KD՚}:qwumXcvi3gH FNFG#D,*$' ' ׅZQk:FC! q9^ld$۱츹+%sӊ4ĞK;+"V}3n{+~xč{$&K[fCʓJ2pJxѵkMZYL7ַCyJ{7yܦZiIZ]FZiY˴xHJrxχm狵hY-n9vtQq?+kO-M͎1ܤmsؑϸ>)xhjC:g8Ef§r{>eFr.O + \]xG?6VfM6C! §E+fILw3T;n;sxyTv=9ML|SMr}@A+O?5P)yڇ[A(U)K?Gϥ"K 'sAcW>ΧrKxD-r"᳐1yL o;PK?Ev>}CZ]|{ߊY/ߋpC| qiڇ[A(Vdannm6?})ߓ_aS(,YViլN,SDD#*GAWXbzHH\+ΚUecs}1W0>Lgo?ݮ 63F` Nl`}jW5hM ڊp<(ocW!?խ,n$D}+XI'n\giĺw5 |(%#ʗro=/F~(R;uz2pxXvKş兡==Z&)=Iҹ/ML+Čw1Y,e)Oz^j}3%=2>wmt1-;VKu'U!dg1b}y5f?+=:JR#gï}"Zi6˫BB@_tuJ[ Qv@a>pu̖ztыI _sG_;|+Ժ7}=yy]);Ubo}ƞ Ჹգzu̔,vEy$)i5O m17[l*m=y8=Ji㛻 _ >YD-*9$`ҽ F7PU8ω>3=ū9Jq$%s&UOk<%\q}MB24vwM6Ҭk9{m5o.QlPpFP yysR];=Uto'jQEQEQEQEyy&Kkqj[8I p6}_ѿy&KkTٍ;jzf#4-kA,\!crzľt鋦5vr,arX$~Ξ1oKiۇ09n`d#5|x6x-hI#O@65ԔpTQNk+5y ^\_XvP&cA՛G'WG5Z':)Y( b:ٿU_zwa ?icc}X} 'x^C7C# m_/ahVQ~涟&W;(Ṓw'uFq^A{|r!E`Ax5k7d/u.ycS¨嘌+o~>++M_n\ =O5O=bm}5pM4+;~Afʎ!6-^޽:o N|:v7)wnMmV=2{wLA+7V,tZ[)i#fM6#yzj}>~ZɞNH-x^03̂G,rq^!:~;q^kqG<2P9s<`g}l~'xXM'G5]r>~;w߂6ўˍZ'`Hc$Ae9hpErss#=PhjKoZ'+&msA r8-V~-xC2Yæe"о <0')j[N)%=/Kk_sHy'N{;+A$Jaz'$;35)&iJtڕGUotۇӧoX9"xIM>L{(=)[Z'1(rCHu5߳ F)5O_Lv:MM/^Ge![k{f_!˓ #~'yu<1tfL[V;]D(γBU 0}@$M*~/Zլ¬Fe9铃ׁ_7/5 VB 5kb\ۄ8} Z k}E{2\4؅ٹ d v5WJJDrSo[Cqw3mB6  )=:5~ex'SᶇިZA=Ӻ Sy8_+SMܤs)]+o?t%S((@A{s6U/χV4MND[k=^Hb2}'j.|1-[UoQQ3E&ǍN$`SЂyCx7,MXSZK _Nn%[Vo,}3?[躮.D3}nI*v3mbqH]k~$Slמ#֭,ΩqZmq©~~rI-!'=p1T7ۮnOkVI%S$e*|4N_֤z@?xzNhwwڥ̐L0)\So&e1"6],C9'錌1~K#4} jMn lD]Fysd ko&@%HF],c<LexA_~Kؖ}An[Dќ|]xzS5듳S׊X%8X1ɕק"7g.Mʍ5 p{CSYXuF:\YICCm*Q末v˿?mkOC[fk+s anH淙78A&cF"&:mɂ-l$_Mz4k%Ӭ |E+:.ƕ KSmGH&)*@'鵿iV=7dw|ᄷ$anC#Q1=Λhv>24 k2NJ+X31lXx߀}]NU(X9 x8c i!0>R6Эu}|MZN^Xi[gYaCcw0DAixMxoRҼ9yfΎȱ,v g!8u{M+HӛU7ouZL?}G+OG=>kyn bK  MJ_ZokBB5uHl55oFMm< \.Ln~WA (b?-p|:5j5.Qlج6Ȏ+u|F\@l5 Gd̍g?(ʏ4!U$1N^R<ӣmim=3Kp|4bGn1AO{//LZm[i,֧,22rO^ \~&|?Qf/xu)H f.]JAp0񱸊$ahҫGn`k1hƂ4s%x-Rnm*;b!6^ sNm˒+G~+ӵx̗plUUguܽۃҸ9緆HmPQ̑@Y2pz5m-#B|\uW,Jl>A=~*&h9-2#5 o[|rO{ſn+'>xQf< i^3_ZzڞYamUa2sK#׷9T2m qWWʙhL? x}iO[[kZg%$vWtVRJ[S ɒ#]>ⳟ ฮľ PIkp^ 5FP6ʩEu X+dh\(ϭfkL߆llcP⾽M||?ľw/ oSk 4mnl z9_@'!3 \h |!q? rftg EUA=HN&KDuzboiIgφEZ֣med%A/|Ly7c'>?_ß { h&ٵH?s{tk1?_]{}=H&kGf9,ēӽ!L3]i8jP1ʐG`gqSaMZ?4*$m !x k>xu.)9fȁ\Fqz~&K˩#բ\}ݛo2AG@%zxp !+Joĭ^u ii/U! yPqޫMjC6qc=䳓4A )/#=@㩯#/i > _`{ - K{Vv}h>>/E%G&(E>b};fοe:7:<]{*ϑ3?#c÷Pk;b~z/gOE2[Ijw$J9`d (;=U4Y- #S$9j@Q@Q@Q@Q@E^ɯ|RzEmH?׾)ؽy6>8]Tyַ:M$ 1|Wׅ6j6-·< ,"0Flޤ.q_RU QZ4JTȯ!ޡYՍU;Z-[/꣊.žhO}RIX%L"3!d*rAȯ?hOB1X2;+i 2"޻@ws+@`%ʳ+ڊ\{\;8|bËM2UgeYcv}9;y_QDai`9En*N32+Š%A>OU`8J+QW`8'PUT\ {RVUkM>]s %9ۓXz׽iQ`>{[vxP]JN*I\ք#iB RӴ W}$WОZu*ǟu>lH⼸-k 8T8rI?.Os^WOJxV .uSDypeKn#b&L8AYzOG_ʋzVw=xi^WOEF[wS)oźD#'<kĞ$Zroaev,n-?Zu*,³_OJ?Y/%{_ʏ-?Y/%zOG_ʋz i<;ۯK.|1h(OzO-?Zu*,m%閬2*0@QEQEQEQEQEQEQEQEQEy9WS=?Ú^jqu$eƑJPx>> >#xK#[ %جcѸ*_i\0|_-jX4zf-mi#`/$RpJ1Ӝ~-Ԯ-c,FO`.7#"`_pOS-9'tυ'u=J>8n59o'}F]=Fiv3yl.[w'2/4m?QLwC_P93#χ?xA9E~qÓ>?QLwC_P93#}3~^qX[\qj$A;dQEQEQEQEQEsmnerd9$_dk?57y{o/?ƹEx}* B{l{w #&)$gXGb y{o/?ƹ(/$_9!5VnM+í`mL5R7-G^J4& $yYY.#i#=?KGԯ5: =KW\LI*lcbƯnl0Q3QZbE"1 Qg=1RC=[YF'R jE=CH8M7l;w<=Q{k r-me)+ kg޻j /{t֗:'qrE @]<čV@ ׇЦдɴ8R=2K8~,^`h混kHdKd ,r)HQIݻyZ}Ig?h6^'{0\۬nð4hZcei~ 0"ռTt.5bf\412 7&H {ߓ~\U]ۇG/'vw{f~Yׇ|Yu='7f}Z[ՖX-TcZ1o5:-/>tk ?oI;&X:.ȃ˷͹E{ݯ;Ht}&akwd#iV~籆I[S`UF瞔)7ēx|O|1[Zv,qHa|1GF2cc9όt{M{^.֌PU`2,riV\ AI9.|R;+ۅZО٬sۛQD;OZ5EUjLx&%‡%UB$׷w_֟|y+{ѡ@C4 ]wPżKhon`I$wZiծ{{2NEN%-f"BŶI`!}c֐c,bg8m9KÚݽ徣. WVQȳ) U8QC],`|8ŸjPkrw\"f! wU 6CJTz8564kc?g髙I\PQE0 ( ( ( ( ( ( ( (>&?M itIKv~pON8?xZ^%t{ ʡ1Z߶wƟi7"Y\`#["? ƙ5喫\G洒e~ 8#ztcGZם n<-M;Gu (&V$J5HpC13Ꮔ,4biyx!u+[{-rԅ+S ^{r"~Wvjۭicg-xn?\4sۉmehPɰmHlyniL iU_Kt&]A[ ^*i}Jo>_>8??jTs1~-Y[tb9YRHFjl2xUVNpGq^RJGϲ|Sߋt;+i5 *j 2=5+CH.Gb~o|_ZmB9{hdudPJ95=ICkD4ETc2~3?|b nAK(aO@gD')zxCn|ǧpOZ? ?x]^{hdxG|#hEB81$OZs ʖ]N.E*2*):O>Iak?<3uz2D"f ?£>3s+i5e_˿׷?QUh-.׿(ʟ?#?/gD'_h?~3e*Wc~2<)wY.'Ug#C}kO"O_?0'%{/){'EU$@'gA[UAmb|XO1TFd0 w$g+2#h ,22]X/AvbspXrLRFvi%[ 97 Uԕ֖mS|6<{s;z.nGqqdv0`m8~Z541%*l%ʔ1;@>G.&a,kܣ \c&[?@lkw$PlukDүI $M@>m|3d%K̀wgR+֕뭾I&rƓi~,z#2ِI^&A4Z灬B EqrA"|# 3t<ÿ'xGLj|3kzZknhmC2#c_Viu2oqm}$L1#2C.*S狺}^G+ݎuR췖3,AYf|`0'>+k^,m1ӵ6«Y 6SdO, ßߟZ&`uNy݀.9#8 1~NkTO<3!iVF KQT>gْ"%6D\ I^]9yg\v1C); x+:Ƥ7ڥM#c9E~I]K8xVEyѰ7ʰ J2W8ϸ7]a`R_oO5-FKmΜ 9eAT3*5)CkN\],bqf‘#q++r<>| ??MfI@Rh$X㉝@ԟ?aI"G.2֖ \g$uU< |"vAE%9pAu,|;td7/ϕWU|Y|=1'QԪ} `5+y<9k&ey66E,CJS#P:5υ3 \<[yT3JUYr$|g3?|b ?G,_Y?d[)wx^ix&ݢ5-xm5OK諤D " I7=q_m}:ɠg!RBW>x4?j:*YCMly5 OUknφ5j#~ E]GoYM%žvld!(#IfgJp8l(4_ YqnI֯hK5_n 8Ĺ~Nk)Q^_?>=.$?4Km>8M_|k),@Sv$v^!n|GYkiwciGw$@Rdٻl3ӵK_ϐi\[]l5'jqtd5V.gR!KoUSV~Mj3^xv A DQK3Z1@5+Eo}=]~xYKq㶏r&'th Fim$\@ӘlRUD ٮ>g7iw֡x宫y l_ʒFT;e|dg=+ )|^i<5~'<[q隄S]"΃O1ڤJXÐ ^E_ap) (I\W3+@QE0 ( ( ( ( ( ( ( (>N>^w[,mj=̥"4 a 䎀O|Vmok :O1%-:#'>2xBZtwi'ЭhywE*I* Rsb|7h7Yߥi%8a,Fl^x|23uMWV0Knƿ_mj/NFI1 "=mOZ>"unܯ&bdpͳ\ϊ~ 7\%ޛ<7vY-!Sq~LEyLJ)xw\|? I>2HL@SnS=MZܺ >IxhhAH~7/Īo$F` gǿxCX-b}-"Ixp\rČ_3=Iq7-Gkc"i̐%/jZ^ke+4V098ž"N4/VXyTdd+VףYr^%b6K'񇄮X,;'1ȋb/9x  xɥI`R8vTPj/LK$c2W~iPIԞrSp29ی׊A?+~6/Z٣HLc־&F:5RXIKz>0n fk 4 9@[sӊ&x[M{$]+.^if ۂ~^F88E~-|E+jfk3:ĆH9Fd#w-3/4 ix_Zne$p-եĪ:LX:(>\AKOGʬ4Zv]'xٓKu!XPă ˷$I5kǺxVyu.lI2 IQ,-ÏbuѴ+dmY+K23-0v 3T˫9̲0AIߌ9bqq3^޿kᲪyl-ROа>Q_?퇊|Mkm]u2?0l Bqe}s-Bĝjm[NHd935#7Rs_=ƃ%Y|f-'i^(]3%mI-KUcmx&RvAy=+?iCD,%8;$`pA(9'&EG VE-$_E;dJC*MT+HP;dWO׷6n Goy{g!TT 8+pߦtkpqg;iڦ^jwVF#-!5t'r0kP|R)΍jt㾊и$3n*͌| 6w:oipZ$ ncjo>PE}9_?"V❢𥙴bXhf ,=)3 4^Vqg!xҬ?'֮K\ . NNvOXjmJI/3“y_`폆#8{C㟈~ễIhqS<Ӵ [:X8n]<̯$>,3ĭ<һDE)k&4W]C-\^ZIRwx٭R3?k1|CuxST]*}G%!qaow6.~U/aa+F0$%1k?%k=ݼF9f"r>nF1]YUG:}t_eqD?EuZkej_jhûw3!60gbS҈]wF"ߞҹOx@֯#ل!FeV k[[|)u@^,;SHd 'ڞNƼWWabE^6;\啔nݪ9w^F5K-#{9Wz{Q5GO6WQ- d'9ko۾2m}fO1D0z{F~5X@fO[3ga#@KD-{֩hx@So>?Mչ ~ζ:%ψƋY8IUϹ2S'5GS5/W 3Dq"O|')A0>ּAm>7Oۭ$IdFO88:\OOr>[s݈z]usez_Q<ʅm8A\ׄg_peVbwĥG$|' :rUyF*5#lwҾ s/wg Zɇ,YbQCۧW/ՁH[S7,G8d`y:ihrW#>` ~O/_8&gioQdxXLdp=AcfAK/"u OU(fAK/")w|U)C6 ]1fAK/"@?gGlb*C6 ]1fAK/"@?gGlb*C6 ]1fAK/"@?gGlb+U[K_]qa%y M3c;PP `:OݭidQ֒j8Tq֘lb(͟_Erc}Z?N=7:-ׄ{F;i#)y0WKy$Kd22fAK/")w|T!&H쭼cb}Y 28'V*}^M)u+#LCķe)IcgGlb+M3a__ ismK b9٦uKg #7 c=5ֳ k'k1%T,ȶkmr>FcE=?Q[/^ #[@~{4OK}2f)"f<^qlg+ups1bI$y]SH{=-˿bk)EI+d+sc w yI+d eh ) + WoH͏ʾ7vyu/|Њ?|^ɷ{끥@(5bE&ò@-&nY&0e|d0A_,| ⏁7Šes.l,#ʧk0^H㇌<ןW.|忲hI؆ 7z\LD51h(>|Ek({qk0'yov2IO>8?ym,sNWfUVгxᯏC=Ӭk6l pPNzuG.xGd;[KjLFI6Wn,#zWljʤ┚wƥ(k}{//3WqhkZ3·\ەO)ܺ9iE-mNmhwB&f\'^"tiaq1D\+p[s@54_Nkl4}bt..Yh˂lBpعa*zͯx:r}'jַ?uI4!Q<"dpWvvCZ_-| K[eXXH\}1pcP{tcj:D/`ɼbϹ #(*V0:kh[rKɠ(E,2FLaNCk "կ*J#HZ?o$fls4%k&+>%=>5x/<@ʶzqkl,m%̍ߒG@xΰu1a&SK~YwJڎN |Niu?SK-&&܀+,*-doBb|Q}oi ;D]U =ǦzuK_zćMVhЯ@N:H϶̬F^3Pm]g^+xDW:p8 ĤdsӦ+>.|.߉ m嶍4uLN O Erä\]L GnY>#lf xO_]/M{PZ!Bqg_(aRwZzmt%ʉh> 42:4xA茨t[?tum//ڪHݥĀi߾"{ķ'P<^ZFl{XFUkŨ&Iݓ'P+X9ʣR_({H%Ӿɬڤ)NB g w* ;;K ]CFْ&,hY~yh.F{W<~?7:Egv5EoUdPq+kWIJx:nx4{y,Wh[)za050JmO_~uiaJv>Yk]V?LHd,c`Z6CT2ڻ ej5Z)BL{i& l56KϐȮVy e9O_,xv& O.n^iXEqpx}^"In=ˀ^FT0 J&ԭƣ}V֐[NO:ƀ>mwh_ swe2vbLY.6| 0tK~(lIis,洳Xѐ0* 203[@O{[Qae]߀#,NFq|M" ~)w?o%8]b%ɖ(XepF~t; @TH$ A`+JO1,}=? _jVq)(:6D*KD-{TO׼/(_o[46:爤&i2\XJiS6)rNaM`G >úmﬠխ>wqg9COa 94T%sڔ_Mlg J4[JؕC#ӟZxU5I~yr1 $#7vn|!$Z߂o.%.͵(G`)*VUMCT׈,uHKҎU|͞pNHZݷToF.}-:K%DS΃1zMGaC4}N|cn/uo?b% V 62ͳwoa\Ӝgl'sZԴR`5}n(3Zz.ψZQk :a" >]8<^Aey}ѭeG[a"}q?`|Vk&Xi&a%ߚıaKtZe 9TWGG<8}܊6?` ~'Ik?5{[QQ)QEQEQEQEQEzσ;5$ϙU20A^3LyJؐ X^MJψ4KOyM%&vH-㷓"H|Xþ#uXֵ-jJt/%dShD6"EPVHѵ-{ac o@0qҖ[ h~} f0"! #V<b(p*"Cω zF|9x:ϭ4269.Wszj-qbuk RJDԷxb`vp8`Ui<%]E@Ě=K`^ᱺR7 ^+rAҴmD}"ÅEzS//&o+g's?WMB((`QEQEQEQEQEQEQEQE|bxA~:sIyR%^e_G~|]ʼnэ_"x&5c·K%”<׊ UDx8x7JK4]_ ]YE+Q}I"f^赯FZ9GAkK}?ᕝGi "g4sq̞%-N$%s%d9lmp #5#OxzCukbOm˯~(.u&Ggcz_GڛTo!$Vc|7+\{;`0))AǨ"yZ=*y{(?Xƺ{S_TG?# W7>*[m5 񼤗N9Lv=Uh{ GмK:σ Mi|6g tG)3rk+m+m:9 QsB%l=YBƖ=K]1n|CvU^&cyy88xbZvC@:y+,T|#k iI:?=sǫZ $GܬdGPAZ_tn[k n$mILq>f<-dڟ5AnDZD.P8p9^x~*کn#Ycyc̓nHQׇi(p>ûYk]VUZ_~yx l%Ԓ? B S5KKy4]46cHVA=z䎃('%ֿ[i6ssɱLv{ҼwZ! isGw.%#wf5lgsHMxOׁSX5̺H%^{dHʹc^͟Ҵm6! 1.%Σ;HW5͆>c2Kc}OO;IYa& #UAE]Z'l??P?Ou}p_ύ4MY3[;Hy7sk7@Xa#xs^ROI5un>Ѹnڙ UqpH/KN<8[Z=FLI`cf䜟¿3;|\ŭQYdb [x}ON3hO3ƿYinark'/u\QZMޡ<Xm%VC]m%{B9ΨIiG@OQEV)æjC* "(2[z SHi~һ(/ *>/y-:7E@EvQ`0=6hFFr8UnE0 ( ( ( ( ( ( ( ( (<k_MF -rgy391G~;|9gǤxu9m-5]F+y΢@&7dW<k*@?']kVTdFƝWUnϟ?f66s (%;<"aM2;w]707dW8&]|^ _[t7wATo]BT.ӑ{GSxwPڝ7*,)1ݢmH +~ ^0X>,6f=kt_[⿃##[ ЍT\~+|NjGK;4772}ϻD>Ey#o祿1^?kw ?۶]*~׿`snǠ걍)-;#<8c(יkݞOO8߉;}yn1H 'p {ie [ ׁMK[-'G+˩VI%F *fbAt?#]]L[ɄS);#74.f88 GOw'*IDMٔ&SYmJO=epbOïV͗/eMTH =GO0+M?h N2v}ƒ׼<^;i-g4Nv[m(1 FwG8ZPhB8ciay\-_l8|GҾ#C˥Gtc8CA9# 9{7KrwWM#0Y@8nAӎČ|WO՟Άg{,Gπq^ VǏ?ׅUEM{TJxGUҞ~_,QYSJxGUFׄ}Iܶ|ۚY p¦Y:Z_Nֲ {u%_hf6#mfglhӄT뷮+>-Ҽ׏ב|{>/17CX Q쀍݈|hҾ k>mi62^kPGoUJc<I0<|u7&G X439f>li]+Cѯ=/\a:.ޫL ?yXt^6l5< 0>`r9Z?Lm>dhp9\论2D ]pz$*G Ҥ7\jQELR Gbqׇ~^y\I8g@O G>Kuk4֎d]I&<խgIϘ2m;y'OgscM~Eo]$)R~:Bg|u[ujF5I]Fid?fCˍV1u@rqp_"$"i{[ &%X׿!cyW奔?^+_E_y<%S·xn[qvmОm_o;ի:v>"J 3FURwHW\w_j}3"_ /7G<+U||4+?Rݝ4h$HAPHGrFnuM^_Z[I=>G!;ʑ dUȼWB}MQ"_ /7_#xú߂F|C躂ArP`T`}A5 XY_YxUD.cܢG-:IJM]0NKE_o*yW奔^nj|Yq{ jSxwϦ*(%!l `? |qÖ%K=֚(& ,*I`H4>x}E_o*qx>%˦xDӾt{O:(HՌݐyf wѼ'}fܛ;m,8P<sɸzQ,CyW奔?^+_E |2-%u Z)Zˍ< w w7^2|-vrx}E_o*`7!=ͻA|!G62FHn*]g?iWWOlnnF>oe|{[MȼWB}MQ"_ /7_+C/ RxoaMQ|𻷔\ځ(vYZ/ЯSTȼWB}MW_iQG3Gٿ/ЯSTȼWB}MW_i|H" &$"iw #yW奔?^+_Eދmh{ v2E?F1\7`Y%bemc8piw<+U/ЯSUt{I,JU7Śys_JvؼWf\GX::NX]FEpE_o*yW奔k煭aw}q4:=4[̙0 pxWoyx-9r-u&\R)BvH@6qh{)7hOp3AWdɻ9[Wï>:oVzog5ʙH!w`2 @}_|EAx dȾ$={T̮|ZIyJ![N/ odPlŹqjb׺,]A*0y8 M*q];=Ϙ>κTv#UA#;@T0q!`/0?J)k>JbVmB7yЎī{;mU?gTx#0X<<롉WFZ>hN]wE&W ')F>b+?Qy >,/9d0?jNǥDx6U#n3[¾n 6Gqte/.<h Ź^6t= KmPb vY~|Og 6.3*G|tt[ ]R SE0+*0Ln-j/cO2Uד^nu%9>z\#ܜ`TWNlİZ _Y'Mi`.FB.pGI8WxMJimТIQO¹cV:QG*j%AGqItmw-izt;Bpz)?OR*=wto-IB>OZ߅6|%7|.-Յo1W2^1d|:n`e[ T7DB+Y_qRm4ӱ$qm(=#pֹw/k?%[;HmfkY7K QJ }M;iI%9A1t>Dok%[=em)GrFy t5cWdE~?/uiP};QD\ʰ D0%fݍK.)+}S[gb&7aߌ7I}^g k˶Wm1d"S@<t @U dvRݺzEF!J=g{tT?I󕯁~;w$Â_-p .͆8V}=  iqnkix;Npy(T%-o|i&L1qzWQcL|j^;BapHan>lwm,/ireǞN!I5>[|mhwPj.b-BЁ >#NZk_CiRM__#|%̱ŷIbF Xb_Vz>8$13'<w;N>v>9xkO^}-N.  9`G9v>>h_YYdI.|<4l/BwY$3R37g#W%񞽦ȃ͑=ۀnr^x_-n~emci,"$=WU'RIKOY 1!p3zfS⟇eb񘃎X<3"(I2coCב_>%x_._q,188%K1!IֽN&]O|c9=k|A_ LV|e.G'hrCdxĩ|M'75ֻ2Ār8MtiCXD8POjxuOX˻Np'qZQܿ .~I&ǽ_Uƞ*ak4ba#i;_TƟgㆲ1b*я^O) tOS uR%8Nw5鿴u@E'<ך͛A5Gy%4ߟָ߃5O:H2Fc 79ϥg eYǚ@.| >uzխݾKgJc n93^tYQ6Lȡ'k#'5 [x" AsfW2\1^,GշϿ [|1oG۵wks*'6nCriӚ}F+YEC~gzc'ԓ}jW]rS`mbK{Q1&[FGC_~O1  IT'>)k}1-ψD Gsʬ$̳Whk0 /C39*fֽ^[=scN򢳻Hdq"Bִ4_M f[=OKWM"hf2\>W)떺gggoX칸Ӟ'XInpJƋ![Y􈎵eӮo- )#o%GW< ]({jnj}w9iM^Σ>W[iæ]EPDNBu~ &V5HY%s~('iHRB.!RB.! \|%i%Ӧ/u :9CKwnF$|ײOگO}RK[WoBhʜHp~3KK]βo|=s?O%ݷu+Nj@hR( c&?o#S>|3s-`K$|Ha ]CC ]CCJߏ6y2*ZoZL[Kpt4ՅC U:X8 mh'e ?fہ!4=;RB.!RB.!Y_'wV.cL5'_|_aMQ K烵B; >iQ; 3vڱ#_q0C5 ]CC ]CCe߂_E^گho3xsD:m-xoצ|<=ayyA?IqAa[eǜ\2pWi7KK2wvioGE/|!q$W-6_ \̶|.Y}Fp3ĺ?Ə C ڑ#<%_Z a. )|_Pt)|_PtޮOG~$c#w Ky&V_ٸ2dlsֳ6x;Lf CUxbMϽ3-#%CHyᔾ/Ћ5c-ȣ3⯌|OiiWy_eC$#q%>ENr0St'GUI%Ss9Ld7ce/".e/".W~?O,;?;Km{̡~Yy1)T՜qں_ |J8ҵ GEn59` 8>YXX9(O2uG2uMio!=[}<= sZwuE b1\`8ai_1a6 }mIc` `e/".e/"._INOGYyúaq4 V/Y&DV_tG ``1^ ]CC ]CC?zNOv *+i>_E??]_E??]+7O#]mDG#IRB.!RB.!\:F[\] b+",y1iNyĒrORkK)zwg(^P%.=fmwB#6RY#ھ# _[l@Iڪd?Uwkǣ<'g?d 'z+?z,WOC}NmVİud*IWSo.L9R3h1q~/A&Adn3 UxG_7!꩸nϧ}]u08XENP$jh[5'?i\1%Ljm#2 ɯ/nRMuhXC3# ڽN2qڿYkB*+d|5IʤާFZ*Iv?d?u1sE,Ǧ.4j#ĜPk[>*x6~5l=x>o?Eі-Òo(!TP3XVS^z5yX|tZzηX֡ 1rstw@I_Ƌ&;Yᶷk{CۺVl?-u=[NғO|!Ϧj4_٬!̀)$i*ڧյzMcR' ;V4=\vi"bKFrx5u5SMԧI^H5C6Mo$p#Y1b0`pNr+mBOF/,Q%I1H;s231MsƏ?3>wx[P{{xV]il`0Y9V'5O\kvMl㹒4nhi?;aG&ichIu#4ud&Y-%xIB0PSz_(xTu+Rr-ғ0lzʽA]G^ ;Uke1|}?O}<'Tۖ@mRI.y=]ezKwsqRS|(mo?ioB3s4Gi;'+Q?.?ɿإbɦ5M*EvMn=k[TK_A5s.}N#&~3hڅϖhXֻ- ]WQdWΚ|U # 2"fm'%q Rt עNɞ|iΛwGC!YCnӒ䌈!c;Vw5ƻ EHQ@OWQ\++,w C66JFÂUO--o-绔OCHzc_Uo.VͥM%}(ig5y\ZC&Bqv7ӯ%3UԍAO aP2v~s(f{mgW)G35Rc}8>_oT%$9 u@yuxWuIm\ŜI,=kb ,u)es"՟7 Cyr=,G>7<[i2}ƽp!pyjk}1둯:? XY1~>WqxJ9a"jqMrn\.2+š-mqfco%k ^K1vH)ƧYlIW[ϧiEw{oC1YLu }+|9sotnE0EnEЗ1 r5ex^Se_ ]xao%['w#?Nz)T4#έey,(x/ 1j&lA wQXy$֍|Iosvw[(U}1bk]La '[U 䟞Bx}?ZF35)@.c$yN-8GBw7h]i?4nuMJKi&m׬r#gh&Qcךj7 +MmިNv.qNII9]0, ]Ip(R¬k,_~tZޡ\gu )3&EFo( r3_Mqֿ?8)!y>&{ 0Wlٯ}Χ,ma8݃q$W!WIs<Reg/01{~Uy5(%tyxYihϥ~'0x @aR7`Y@1A?.IIOJM;6Ě%$J<5音ux/,"CnI>-Y T1_lc0zHOb=e |m]Ks݈L#?7R/=Z2g./j,.0Πs|uCxvZefqrsqc/xZ5ndYDCw=*imw kq ŭf~urP_'+4}/8wm$1P <עG'MlpmDt9z:Ej6Z5{GĪzAg8} ?GƐҙPicqFIaSV]:\9b|n lҩ 8sugHd> +ud#Z%h&Kx,dg'p;~||%IBdTGzYd{4?:jKm;wkm74&(~p~=YmhbeĒ`m?wA^hjS}$yN8+>OC񎑥 5 6b,1J>f 7副a҆"RC'ZQ`׼)^A8#N#yXbXY\9$EoAxZcWK{0Ezg!~f|Hj~2Zv00?÷ 9(WK=M`kJkCQCo[n0b}:ˎOźq|kqea7/6{,Wb ,B?ʭot-:v F\cm;.̱*^A3 $>Ÿi^{4ꑬa!Tx3ipj#bb2.dpz P0Uwڳ;\5iQ߲.ǻx7Qngo$+y *kD4_ (Omnb̎Ӆ'&FxK^)ABA cLxOZ5htNQpZ>lj<9<kΣaЎW(JOK]o^J՜,oEo"O0qb1-msyk 8a0a;JkzEFg$s>$,٧[ip6cpHJ̏MYOzfdC#b?++) Ȭ~'RjH^+;^#~~Rhz$P]łAi5x p)#Nͨ.r#&sSxKXYؑNEԀ Ze4m:inkXq<(.zm$߅ 4ټK[ޥw"ĖpFvB$rqIo} ZkKv+4ܣG9s@tVZFdq.хbΏ?9J`b;mLVo#N|7CUk 0ҮqszI[s SDb6#pÂl?Q`< lUo +c}(Y φ`ҿ6?*N|7CUcxG| >X,0i_G'>J?<#cxG| ,s4  lUz<#?9 WQ φ`ҿ6?*OpGo +c0i_^8?pEN|7CTs4 S?Q8?yg'>J?9 WW(?Q`< lUo +c}(Y φ`ҿ6?*N|7CUcxG| >X,0i_G'>J?<#cxG| ,s4  lUz<#?9 WQ φ`ҿ6?*OpGxzSmq]yQSTUP5K0?5z 4XW-'/}RJq5D>De7 R;yC__c?5K>l0Mľ U?۟Mk ԘӭaRUY?_巎׌ lJ8ڼ\Rh*qsg.m"y]W6lX.1ڸ]%8*`EV mؤ#'=+Bo'O %$Vi_NyiN߄[k#q vjN8?*ey(jGr* 0X54VǯsֿZng_kpXGUn1sޙ'_ԏ#}wtTG>qoZAC9wl>$xJ±[æxɮ.aBtzqBï Wrzbg焼iik:΅{TR]H!pH<ڝ5 n{Ҕctu;iI$_F"1,/501xozBT{v{c`PA__ٖ|AOݯQg>߱Njc]3V޻Y$.P tֳ1tVqfpG}xjDdN]>?m #XYEf|\ )9}˦Evc\|4ė"$^Dw\(dE|~?SMutkW9K<8|Hm`~umme7&qӓ?j]:NE F)Mm8A]q(<0_^"\ěQ Ll@'ԗKMu? ]~TWVH_FR0G>C':? n3OQ8˗gL15`w]#߳MP^XtH vŚ<Y`?W\<LtK잧j'Me* ,0}=MB.";3W7'*m:T$oCL_b 5=Vqipr*%` 'jZbsc&eAW#='',QS>N\7?=gھ֮''#yr3!jN=+Χ6FGDl!ݑ? 3O46`Sc;G?1qG ھuÖPJ.M$珛t/Nn iju 6nP~B* oeJSϓcJY#@SokHL3/ >wmy(r'k{%߉XAғnIΘy=ׯOZ6r~"ŷ1}1͏ʊt*sE!՝Soj z6 \v8ja5ͯFr:_b׫l;c|muB\񦏬^fĚz!eL kI)[ ]S/|9J,/—C_zܥ%ϗ;>L6q+o|/˳⿇-;:zl& ݘȒ#*[j}͍+sgsC4.2#OM{|mo/hVuk-LMYm$4;yo'~fjO&.}Ʃ/ե_}t#07 !$}c6ziZDoF0cnr"-Ⱦ x4x5$Kje60Ă2ϡ"7o,}KGuxڴ3EiC21@gQYKz73N><־xߎ/ꗚ׆4;{eMvXD\nЌg lQ[ahڭ-={K{KfؕWTU6䨥SuO UM1%xmmK5|,NY}EV<C~o/֏t S𦃬C̟ͬ*LZ0e Ntx;*+8 :'f:?8 :'f:.Q^{ NjY?{7! NjY?{7!p= N<_BΉٿN<_BΉٿTWqtOtqtOt\BгoCгoCzxxШ=,=,=Ey'/gDG'/gDE*+8 :'f:?8 :'f:.Q^{ NjY?{7! NjY?{7!p= N<_BΉٿN<_BΉٿTWqtOtqtOt\BгoCгoC;j zMB-vf| Q +@xAx3I^ 3+-|@3ak:8TjrKgtNŚջ[Lv bV_^$Y8ᾹwglF< L!)ϛ r< i9+/tw47p E<lI@b,o&W/2H' 6o<%2=~4mtI"I$Ѳ!IF`t|´I>m/p]Z$|P)x[Nd Ɖo< |G<浡}jrk7*sd* 2$R:YԜn%zkv?WtxWѐqߞG#z8L\KAǟF9'gbFqU't'p8SYͦ0%H'>y4>IT0f7W'Z1=|P|Oi&5"$ љ'kݜld`"jy9Ւ=M҄_^<[}SXJ㶟Y+a2!b d`}6obD6eDmcpa2 ϦG,Bj0n3,:Ŭtl8`A$ {v5;ˑ/YGkTQw};8$&TQ]gzzǓ_qV.4jƛduԩvsxR@$V^uKƻK-ͽ1%aIB+ m4=RC1޾9Oݜ[vxgsukmqNHݎC[NX)ppvc?Dؽ4u 1Rѽ ~|8I^nmGUd!H*x q~7Dow]ʦ˒?^i_R8voЋ8?J~ҴVȈ0_dziH"la'&{#);G떟C=_i,K [ |-ιqX f+!i,#TR73zլ-*@fAP^jKy E+8QD ;VWP9q0*;SRB| 4 ddhh!SӐS#|#f=wϊW=2:ׁ&LגRF؝Hf9F@$J9t5kti..u(%o:]p^W{ X>mi^-ʤ bT"{ tia/!eQIFG'ӧ9%*qݎ-4Ʒ[ifXB)Fjta_L.qѨrǎ9~2t–֗PJ "_.FU$S\gwk < [䔬m 238ܙJwzN[٤_K#◃TmٮTt'N1V {'kZt$sL׍lF`ȓ*1}ImvSt?zlg𷉚јO*] dg|_E~>)BZG~ddO/5ޕ{{AsjΗjuz)}el.oz¿'ڝ-qd$6S8JJx&CeC7h#sŹuR7LLq*" 'dҮ-i$iY-!|VNt͹^I]k3q\&IٔEi7G, c2(!) *힇mcq5Ƨ$.( bfOC(,cs&eY`ґ/(t  &CsV<}^xWEդ{_u YY>irq`0[kNW?na}mmnvqCVgRX\rzz8Rz+zޕS]v4vi8ftf<|h,IMUh7؏"O_?0'%n[%- KK 3bڰ?E~ Lc %q> h$ڍ9"8:ݍ(_>!xS±Z{-Ȍgk5£nQ=e[FkmquX6Pڻ1PH]J}FUfΑG7,1H5x~KӭVEIeYܪ K!.T'' jo{>%><5h y86go$u݁WŻtyM.Xvk$Ҽ3OZ{Z,T2͸M"HRwyj-E܍;Flg8ϵzXbZTϠei4`T"+eTm;sEAlGsY# oH2c$Wh/ϔcrq}. 26hm_,] q^^.={=I Cng_~}.$\LB6¾Fn4>c1݅ <{_YY]Ux4]Qu;{kb[2eFӵIRFB>G\%K*6$cy$94K)q_9뷿4Go4$FE$%F:s޿V;W{&e]'eGs9$* *s#Vtvpg%s0*IʒP\סzohzF8C*#d߁*x=t=.MBZDk&7@|l:O i.RkcpmaˠmϷwnF>+ϮQXhZ? E_ No|=@*,-I<`v瑎_Qs꺖TM4AY̛q2U(gÖz}Rj <n6JSuxk;j:lMtzQyvW('FaRjSe་JӼwXW[1;L>e rqۭznj]bWwZ[YF2Y) nRpy8wֵ߆zNJKdq請Ϙ VAJC-uo]E񾫣KjEzD:6Q7S_3U*pu+9Bҕ:ok#sMCPM^_QKkth @Fz++>~8յ h4)b71Gi%0W_#x[ }?Gοj,lc\vm=Gό5u-{嵌2$eYpsO%Nю*]&z}\=t75F 7 K&gabʾ}$6Zzi66#`s5GŭUOt fMw\X-܌zt^ᯆ>:N}ɥMb [nHyhܣ++GúV`=ɎԊ?ƞ] E:n^)61O%9;Y٘1Yp]gV=vՂSNVzj:h-.5 l2ƯpWZwa۩VM}ϒH>d'w t6? ޴74$ Dv'n_KX<]) gl:ZZյ JVa}kƮd-/fMp#8^W&-tw)ѠgO9nLڪlUcȡnLy|qFMod6_C(B*n;4'C$qSG5hXd\Ǥ]?W"hJVW>TnfS?f jT嵚 J11(D;Tn!+"5|Fg|ɗ)kqqSi3`Q٢ʹwc`#d y~:|Hi!|-ms ?-˫4B'͸ȸsdo|xmK!_.j|ŊH`A푸xc"zФIm'NxӓWm~k'F-g𽇈n4{g!F1,+n$u,;xix{Cn4o jPi-̫s< BGWgCËEk;hխo ʅ*Tʕ 1\O>xWNa`K:MvXsm,[s|nFmܹkYy!kˮ?ic^QK _xxk3ZڜYs筹 e. l}žnRM]UG \0@98/ AVUk$[W]fӈ%n(nk~ߌKquc0<&e$9lp0x9{+Z}_w W5^N56!{ym'**7T1Z7.iCh֧hqZh..mh d7)ף9VEt5촍ZNY`P|{XJ#zs\ْ=sBm7SukTѢ&Ե [[MFpUWˑcYB{-$b_7+~;Z+xH"ӓKӼn.He21) u+mdۣ~kQ,ym[%a.6m`E*{_?ěx"FO6Ĉ݌3pu=sx[vx*2Xk BӤ?5ᘾ}ñ:!VI9]Uaoq<^yzԜ1V}ghzƿ;^Ӣ5kZ=pA51Oׯm+ ?VjmVUA Urr3fA5H^}2(x]TzQE?uT}de',5K@eʙN9r#EGG:bQVGAxIԬN>~eϸ;+r@7n>ȭOF3\k@h.chti螺6pW,ҋiN i>gތ90~ǟ(&ὮG:6:˜Bw8՗oYuy-q^mi׋oژ DqO-`|AbŐٞpK31ZwK3[P/3ohh7::] lל|E?i?iҡhi}wǝ),?9v3ջ&y֕fQ%U7|*F9=f$c2U_-Sx/fuKï~<J㻶1G2MH*I`AʺoW?Kx7M֝dM芊G9 sQ|_j_~2g@Ga1_zxSPƌ[e&UmedgS3o3^k 'Xpywnɒu:'΍Y^7M0it(rqӞWsxnz|WG~}O#HP$Io<0s-Xi#/#?S%gĽ[Q?((DÀ!>'jʞ$[*Os=I=eIBjһCMxf@L}_q7|+io;E%58T Qctw_GҰMQؙ?¶l??mR/趗q!l)*zq>²atZЖ>2"|7yj߫ۼ:rF2yq;~uyxI?O_~φ಻Vszw#JY윓sZSZM=ۛФ"c8YҢ?*$k#}{^M RKyKHa{;I/Y7F(r5Opx5r^ѿdxB $. O<2c@1m0ktº2ywN\ C`cG=hn汨~f/}ƒ{ :S-[OakPcHPqaig4h:` d@wZ2)rF~uѵpgok!PK+SӨjuFi/~y8?~Q9PG㟉c f;<2< Xw #6;Z$||(Մ ҀI3axGzN4/?4HK+Xɴ8=d_Mo崺b/vʟֳ1Ii@'ncʥvq%zeI:N->u3O$o>SKShdaXc7 4 ?߉N߈+m(a'_Ab3OfdVsQZh7[g\1Qݟҵ"Xv0E}Ixgnl~_Y@A//WJy}.Ķ+1߇g;Iͻ&Ɖ$8'/z ~3DZnegu`#GO#^JnN*\[?)5J_ckmx" =~t>kk2}=k2;dQ < O#&A'/viC>'_V h*oGa|sWVS53JNȧDI'vp^FcYʂA׭jJ'= SiYK]+n KtɬN5B{rJ=7Ʒ:!)%"8:{=_:=UI,# a(Pes]Jv=n(,DZ4y J:Ί:qrϹsYhf/y֡so2EKs&]qbcͮ:40!Poy |c6zPџC&lCc]>_ mżb|oHΒ_歾-6)ʡĜ2j_Z^| 76sc? ;0{|fK?GkcR{ɞDʏixK_G_vt#!)Zi5gRBFl &#arK5>l'-_c+MoW_I u!dq׭Txdʺ*|5_e<}OkxCG-oc+,dմ]RFcPӧ]ZJfl\m'P';Hԭ5(mY]n;TQ05(S[kW2фjNI>^⤹}#QQ[g6 qGr>;CVi ,D'ݥAim~ ϡ>?[Fk=J8Lɀ/&>YJ8)>Q֝8; VUƤ;? /&$N߅Y_(O?I rȑfbk3O ǥu> gTe І+] 4x]I|GbKzVj~.2U Kmȗ8=yU=\K.&O8;A# j%V0ѽKgO4S.'k)6±`:j_ DdR-ېzW~Esx`YAϾe D2V?GbMK<@ s~?-n&.'2B$=ky Q#tUr5_ P֡=؊E 82wݏRx gxWŚXAOʡ_jnh >"~*G7-ՂQ dR[~/ßS^<] ]n[XcybIeMfb_hpf OѾYtuVI"1#ɰldgcڶ5?SLJ->BN9xPЂQ_uXŷ̐1gv9R1{ך|M_kZW5o Vđ2Ken>C+wgW[{)U/~N.zׇ|*XxK?/֡kk'ǯQG^>ڏl.h_}qiqwI%Vie3n38L6,wd)O>oo^9nDÆ5!lnKIN>h5xmJ:А%Su"u5- S[G F>Kʭ:iE6#z#z4?Y.`zk?­ +Ai*IYGRC_e &8WRާtFnae$תF+ta >WkQ-ҤԓcihSǽgx{3⹧Iz,%~vKވeGTIvUW 3.Tu@Oh6k'p0OrP*JT|(mboqȐaMzEú^\}J-b"F0ʰ8 @ŚڽΡvчQqҿ|?g{wA!%ƘIȵG*s̾"~>*|GR1B׮NIw{F%CiK)[udUPF~"ƽz"]TSzV_FOYmWzTF/;jy> 'PLr,cWk7ohUY:=t po8WGmwO`3_uR)t;͊08$MdCB6BJߛߌaL/G/YTQ~5rZ&ĴJI?yo&q ) 0c$089xCVz3kî7Wgt-[V6%uTԣnd[pqܐ6W>UaUa9xDdoգ*k;9ckC'V!*a_`iXBE]Dd&2 bsҁ\@]@chY%H8$~UxG?i*u;[-jz]ŚBI J$QGSЊtfi|_@Yj7Vt[LHw)29Uauψl4x-i7?dt$gE(!Gpjo|A<]|K6MwYZC&osBC 0=gHq%ʳ)ڻZ<;!]43><4urLkB ]y댂3^e?*W fx=J ~6x%)`#d7Sdc 4[-v1 :K8Nsҽy0NQo\ ^ ֩%xbYgU>\;' flr8|7/:yXݔ8X6&KHsGJQB(-&UC'񾧧m3Hu9Dq#݂zus_zz\kG>!xQEBbXaLX,ā' vzd2pj;~7xUԵ>sZphv ,3Vͯ,[kkH5 # x='v4&o.Y jz>Gg1$s⸛_i7ߖzzM+|Quŭމ1ȝFUr cQxs7+K+Ӗ2 n9}7YqZaãϨAjZъ[٪mu<mVOfBݬֵ=L(j%dRaҺhSYQYkmO`/ě;ƫ 'GKQW m@ nbŰy?LC%|sok٤Kpwc3 ~mwCүbQ_̟O.+(UcH^i%,If&"f*R=F2N_3['wo%u}=}UjΠ® $~n<+2Iwtdģgw[!]J1u\4P-\+Q=ͼW%: TBU&9fmDL 2iq> wL @ LNpWwI,\Xx|;O5612H &6{^J\ۙJK;KP'B4%;S$8[+ܕ-W:xU4X # ɶM† 0<WS}7Lܺ6ak=Cu*wBѡU)r3 x;Cuxuy4vʯDm'=꾣x:r^_?5%[KH5HK|I>llc<ׇOV.].o,ʥ 1J3tVku-6# . \d `_z_N$`}B|u8a OW JV80u26" HBXtp7TҼC4ڎnҋ}0@ gasu'ee^7;[˱XxM$KƸR5d\A SAO ypmcmiJ-w caڷ>+[.}LװI (=r2'>go9b|3Y9S+e.LgWͮj2ŦT2>+-`ek*NX^G^f⮏!x,A ͤj~IZ,m;$+z1z ^9khhvd#8r^M@95Gl7biK"3KAr4PChW+p< Q*v7@&r禺Y\W?=V%-F-8 ʱ tO?$A?}XuK$,Odea^46Xjj}m:$4Q_t+?cNMFO[il+ԀO]LuZTf )rL髪W0H:O_YI jwzl)c#]*qN0'~Rxη. _}XCJA~ƫ+Q*ndyl 5bLHD~in>xmG<{Wh_ PiWzۈf. HY|}͂Y <3cxNqscA壐<#'x#9 qxS+aGh `~@ \a@==:Fwuc,X,J*K_yi]k ]#7o,u_˫,vo=\0[W%'< c/xOG /6P72cz`sӮk\τO^^N$6V8Ak%O0 aF2WR_Xn+*>^;w?^ *Sm([[ ;%eqN&TDӮ?"kgsko($S"0~^4_͜ <)ϩ$*# tgR~$A+MݵQJ>:wQݧ}Tw)]펩m{bTF##9=Os/̯xsNvLhɯgյ Ԇɔ8$\?~~14@`_ ?c澂暩?yCx_3zcƏ-l"41 60@`^1ڂG6}gxb0"4yr>~cXo/D#^,U>O| dז6BM֒D*vdgf9 qzfZez*jI7_]~њ?JӞ-ux4x qO|#y-GHm( := 5h(䌔Y8h5: HĆI$rĂw@QXU=JJ}5uu)R(_]{}1\CvuńkL| ;JuQ_Jt9fӁ&B]H`ux牮"<Zl0]Fwi7+}K7W-"]ݼhI}~;푊g9*+hpRNn#ڗQԮ4I^b.o$_)]؂F0EI;\{JϔD#RJ/98=)|aVΫm0+Fe 1Gv 8ۜskmCl ,c8hw$]]"UW\[BW+*)ڜ}?|\GIjTh(-9N+r F@8S ]|o@8t˘U##kcQ1RwiYsT dpTQEIQEQEQEQEQEQEQEQEQEQEQEQEQEkr^. xό.{ x@ԥkmk_[Q#ct݈΋.G> ̖:Vm¯V6sBoC692_xGvi> ]+Km6gbQ|^d_dK>4fЍrmsev>uYR?-YH\zd{IlyG<u/*}*MSg2FʪCltԕ92F+^8/ Fе{m#Bo賤|FT(?,xPƏ,* -҂qBoC6922+4Ib\{|=sB&ӵ;ᇋ|An 6=dXbMo_x[p{`W|O[}|{BsZ<1] N+$G@@ r:? h czlJj n! VSIgR>NzF6ywv~elqB;:58.zqVk ~Zz~֯-Ebݳ@v/yJaF +h*0P)p'keoY+w9ubu(ݰqjni4h#zXMrbU,R ;G~ xX|ꚴzr'kѦ-΂Ju B\>&k ͋۬4FȯRXJMay61u ( 89KjMl$Q䐹U]a6&ĩ`+V۴Z8[^%,nNko$ROM%ᯎ`,mɄpJE܆4 /%Nsc?^9ޮ&xU#c\_|%uhM&@Ѕ}]LVEer~(}FV Ycg8Sү|^umúuz6Jlx[وP&w my#-A=kĚE\Cf|‡`2B/>¾+|Os$Ƨhgյ;Z򮤘]9ȫ_Ϥ^htnkR"MOק3QWEDGGuMR֮ol 6Zᾊ *eL!+k|EYiz*wyG⸎XU^P),2׀iAkrd#Yʻnާnh "{k &ULkȆ%fDF`?&Mm%nw_V4TyoxaB[]E,IqFy}5sܘ͔ۑ=|}]@Ҙ3Ќ׼xS7αlzb9,3Џ֡ASQڿoGWS9˻: ȉ#ʤ8'8}A4^⿍{M EoB]rEԉǎ 0 i8MsbV JJ|m.|u]R$Qa!Yp2~Q=]wsA wo9yiyG8'v_ĺޣ7aFOws)u;!Xey, c S_5:һIz\wn _ֵKnƷ_d'gZxHm6[<RF˜s)Ar- N 8D ,@{OmU$gYܺMj1i,"Kdr.C֑Xm,p+boYah*IGdiRI :5ω~. -"׭~- lcJ5UR@9%OQiia?实 1mC]8V.WoyJR\ZZڃUynmTG,2985WzI%ƙ! t 6Կ6Z{InDּ4RYTכh&-]*XF3[sv3+zЭOu_\5(ӍXGWvZm6>4E ndBc&1{֚Ȱku#=^W|XդIt${dx¾x!>Ve݋0JNCzk~{*jusU)^]Zg i_l|mhQXCi/yc$v? 뭵5(P7%FU9郸3{\j`ҒͰVh6s܏`} W:n~gƲ>U3OQEnq١I|>OQEfy'(4??E}ȣ$EPh"C<Q@١I|>OQEfy'(4??E}ȣ$EPh"C<Q@١I|>OQEfy'(4??E}ȣ$EPh"C<Q@١I|>OQEfy'(4??E}ȣ$EPh"C<Q@١I|>OQEfy'(4??E}ȣ$EPhr#jJ(j?Z֌t8iYð2GgftbIޣs^+__=kƨIa 1;I|IcؒZ- #Ó۩8t/'4 6D|ܼNK1OTUy*cq oOE">EmZ,f-ԓڊ+`Jj 9Pvw kxVa, F2 bpr3ҁms,V@Tڊ+X**Z#_ߘ3hΕk?xIBmgh/Hc;T6ۃ~ As c  #(}E[WԪSmk|G5;m-۲,2p 'Ԛ7| -5˄ˊih 드E] 5/cGHp4ԃXh?Դ-[Mi{Mm/%o›dj!v7DǣdxAd}dו럳u맸QYk"L,LH:hPդKFR/دI~jLg4$ぞ’oأ$kK#?,QEsRȈT WkO8seb=Gj'-JIDžU*E}:~xL]>mc'oL2?(RVKC/k5%C9ԭ/"[ʲcnCFF8</ hkXd.]_.5MT ۜ(EGc6ܝ8~|EγvR5Yio#a%UQ]g|}L r@}zET}sGFt^ 3iz?7 Sr6+_«yCŧj1(m$d!s(*Х^NUb1I~N﹧o|8K=Ccf !8#~D9 uJYUQ ;%oBZ#4M{ȖwѼD)zY_KՓ'?f_QO]7&5']o/xOԢԒ -\jL ʮQ 袪1QVHRfotoxx-20.08/images/mosaic.jpg000066400000000000000000001571241362435004500163100ustar00rootroot00000000000000JFIFExifMM*V^(if%0231#0100Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?+Sº.}zH2|#FXa88wWCf\lt3&_Gֹ#wQ <W_rG֏Z]?:>&8QGրCωuu(|M9^q]oi>mdiz]֭u t7yhppX3Mkv.o]?wTƟK%ƥtMzƢ e279*@;> ?[4YZ\ڵԚDqݽ̌*EPK=H❘]?:>& ~>'@ţ[jq\[:̡FwYH~lt5߇ß6ysoi/SRk9#UbV,9MQ[y <W_r]?:hm4fXV4- }FRL҇0޹z'W;^o 5ާvibd!+qpjBnя|M9G.o]ྗOKj~s&%VgL,ayV60KI16C[MM+nCωuu(|M9Kυ) ·wƷ&,Mc@1$tI==k[c'di[ӼAOMam*,#G aITq[dU5ggZy7G7_ۦS%Y Ȗ1uGZjWl{c~4=޳#rث9-YXiq9h1){|b /\mV ϳI\݂ ^7zw>V3mZֿg@axuj횥yU2(e@ֵo_XjGh?vN^fOX.?Z[ԃ➩o*3lI$9X} 畾!^ջR'` C/aRndTcêz\]\iȱb}.ȶ59l{UvPQ|t6c6w oyЬiu1`;~FxSMH77195-͖ŖMN)Œaoҩ_j2~TpBnȣ'OK⌭g+qB:ep'@8k{/YMv8瓁\:lhW2`2}ԚU7r5x&Tm(SКylk;Vf|aߍkQ6 o39 \np*̩-pěr]Y`Œ{WtB+)K"Ưݫ獠iJ7mV;+Yf/.w犴|CcBww&wk@!돮:W=?ớ)n` ۼ(Uyq~ҝ('QO)_ĺULBO@Oosz`#kbi6 ~ vΦI. 4o"NEUDƛ6;,[#*9$pzjh;s^wC U/k\NzFc\ @xz;`fIּٕZ?z^2[ۼ+FOAOUml,LC2~\/< xGK=QG^s#[= W#ܓJ&WaNAm\Uy,^Q =99mspg{ 2HI@ąBwݳw|tQ MΣ)a^=Yt[mcn<J_Fs>oPAwoku3i!F@ws ¾-i?[8jq%v[:[G5Z̐h{[[z v;[/#ryg!ۿřxeXr=m].Z6._ !q~5jso<dyITa'O{ufZ2'c۷5>VhmE|ÁN|̪ctav~|"A\V$ڻQQilkZx]ʎ}rsC~zmSXe}j8Yg^O9SA{NkwJ"kx UJ_m MLPnƥI,51.E$?[If\CLbq,\8c篵A}ol͈8f'izS@!K=VʠӸ2Zm-/=s#* G9W.gD/5>k;<%Z8HnFkF܁uu:Kg<% ˥aQ[R8lJW7nv9uޠyP;MTޥugq,@cGjk!9B 1vk ?78홛hڔۏ<Ԛd[(T5Z>S*3:~чVmݗ>_uw->|1BQksͩ_Ec}/'T+ȹ#:T-UHR}Ȑ^L Bd54^^yE\5X 2 ұVGKj).5{&yg*\8R}ȩPG-qLb~^r+3f]u1Ē[ >j'ܛA-df28>Bz>XSE '\*&VwI'Y#IdVAY:qҾX?.~(fATo "._"X́Qj:ki/Ro Lß=U:jg!O׾s><,DVڵQ`݃޼5hkZ]Wl+n卹Lqw>.4wjezrv14{'KuYFn:q\ף^!7SQ:X~=NO>-];;~u;}*շ)OW~1^-vjRpIǨ,xVIfH<< _ SV𶇫-fObRzgyۖ6uKw3imLͱ-N!G< r=fLm]">"VܾS.2G\BԼ ]ZޠYL*1-oۏ<+ Ø^fga98'Z4ɽN7h{~+k~,_RWku1ȮQ余W/v?eqz%2j;kLI]GRĆٜ^}L{QŒbm/S)Qxg^-Z '#agS9>4(l(.s=;O(V'֏ZSQd(ôS,%~\'֏ZSQd(ôS,%~0_Onx+ 2FF )vEO(i4'f|YB|3+PѮiC[Fl (/ŀ =OZ4[iDF-園%_a`AoξhY?J?' G&Ϙt73jC Oj7jGEmgA8ZKCm2jQ Nrq_HôS,%~vEg׆t]WfRP䅆pWi$5oMe,#um6]*qmaG'#;E?QhY?J?;6$Kշ/,]^ŬtI<:#}6-|W~;iâ`f׆؀rva@A_KôS,%~vEz?'m:akihp_& wo$vrnNzոhzVеOJFէmӴ`KbhU'?;E?QhY?J?oQik1AU>]QѼ9 ~)u3ϹӼ/'٥Qr.%dH%ǘaq_bôS,%~vE)*]FfXiH6sVeW$ǻ CQd(ôS,%~|x%Fje*m.<UǙ*ϪKw;mq<' GQd(9_4Ol>'i1IXDD=W@>(vwz~0j P$qLm!O5H$(rNN~`?Wt7j^"j@|}kn5[鏟]w =98>rxkx&MHN~vT'@k࢟{)xkI|8 ii~3MznŽG* _A őX3D&XyH7R7WыkK#X!&_F; usw_S)ԣKKU~Hc4:lH¼O1g,G?ڹKh V{V@'8HmҬG޴$񷇦,NA*HhFbFAO:tI)[ƭC@ѭ^4j7-#m95<31}^ +\Yyi "1マ׾{]:\+rL_(S OVu'aKKۆ_7ˑn9q~ls*+_:ڦ-!(Ւ@Y«`S~5<;<\F!_,/v.$&p1=Ie=t^FAkǟҴ}k6t;hQEr1B#)5H<$*;ϭz~7ў' #:Ɓ^,"5RYP10H .zwIbM .nL~fG$_@~ƌeԁMB+ZY xX.#" Wkt'9TZUr*+Ӓ]Mm>ZI08|m2a4f@'P@`{cEo<6uGZ{mYf9P@~i3\MLӘ (%xO%:nn7}S#N6 ֮Ze,AKh 4/cEyʷ+?)c8Qǽfyi% $rM?hnNRv PL4hhԂر2kugYEs%rϊ.t-.k_숢6M0}khwJӴCm`1{X.}{q$djQ ±r:SZqtmŽ]ų3F0xgZtݭ֜uKݺyg>aN?zcgo9_)i:὎IWR<#$s[oh7rm<^l+r斊RBkğDŽ<'Kj6>,[Ź>=Jx3C$p203']oBG?Ty2ԁ?FΗNGS𚭥ާq[Hﴛo2O-̍v'݌0+;|nt'W-R; 7"P|΀c_٭mkGKkM&("R^JHhߌ%Ўk^v>io4:l1")$"\l,"esb?V>$n|k]O+fpǂ#}9677:!xohG^ ϲz |8aVxg{B.n9CԣM>S9 rpye΁kEK%Zlх0ԔۑFS_7xUWk:iòݕFHd ؄WGZt.?iV16d\ƭc aʜͯR|t}(`Z9SL ~F)>(Oh 6I|*vk5̓KiKm.I8'ֽ{𗌼s}¶7wH5.I4 2E)_0X`/ȵO< xKI 14k"~bsRzWq ú-֝;3io,r#t)]XU"4ƫo<0o ž"Zޣ<B˓}3*AU>HT4ŜW*x@,>/ǂYյh)3\N' ֩Y"nӷ#Bޙ? Ai74SmF WVRHB./پG]./EFqbڊ'!f|݃cO[I}F;`I#wD ` |+K|W,<_xţPCqy~mMrgA,[ЃhvҬ_T¼7r?_DEK|d}϶6|K85h+%4饗Bцg l#w|a>*G*_\CًgF$xyBX6 ?᷇'{{ 6^J:-ɰ[jh\ ]u7G Jm#)*yw_>M*v  {ĵV*g⍖4<`Jw_ib;o-eJ`W+YöW^2!e}WIŬ5/WĶ'Xb]ep#/̸ၪݼUkZ>m}=ͮ5Ž .%6HUw=m>x>E Xdn#Ͻ]/|D{_~񦹤i7]f]*jPhQ:<2Fqy }~u&b%A(U$7(>U47k#BޙhZƛYu[xU7{Jz #DMR Ap}5<]GzAaOֽC\Cx糟WKqTtP\]d-Nz ƭaඹwkI5ƞEWq\7+1VnV{05gc5g13DcASӷ^8H|S|/dZVeo-ÉݼdY$ү-odv>$( A}e->QVo-=j}l-#0,~7z!.[JoS.֍7y|]7d-ue!h&[1Z=tm&vC,$ؽyp{vjIa$]QhebAkn8@xRFEmn֩1ա(F`9>k8T_PpCn\dO˝Y22vV^5-i:"d`B^=`-%ƿ!eY%0ʄ$c=kľ&|!}&}6){ + _.Ty j7~"Rgu y vYn BW0p”i9OahPkuf<1#)9\nTҮ H Y ,}3Vj 7vPbe3ʒ1Oj%Iּ?iڶ{ ?6(,%Bv8QkǨ 3 0+3Rx{|5mR#4Z598k[-{I"VE17* ד^omGݺ%6|˃]:wkKt2 T+! sSV4{[HK{x, @8'tI+wӧJItT֫Un1)SAIkd%ihjмu|csO`'&O4͖PMr\[ı\ $׃Wmoۀֺ_2ˈ#".rOPxXyUO1k+T(|.Hg&gX]t֮5s0<¾T9Y^eBK>Rhk]įBvޜ"/ḥ[ZI+G8{~E53f ָ[?1yɳa\$切/sR595-7S UuSaI#vO9)CmMDQ1tN'ǒL 33tWo|2ihI7!}Z~sN7W^y0ƚ`| NQ$>ӅǓ-bB n$'W;_F+&O/p3yEl؈e\ԣsNKO<+AŨŪ\k̤o8W'^=ȍJZ<+@\^Y͵+'rwDB#AT` I9SK3-Hi ^ RQ1XBR-rI w'eգXLvL=&0V5SvܹMpNg|7 Z)n# ,l鎧[<[g_7ȶ~U#$. =3_8xj}~LӴ{MBUH*ʀ _YbWK:w1ޱȘ9xtӧES Uk{YHsgco :P@8f'k^/年gB>]w> k>Bw0= y yes-V9÷y''>WS qJG?x?x{-ZR`ڽo7RZmsXxbZk-Vɸo=E%<" Q^,I (9<ڕZ1o тRB̊>5 OWǭhz7n}L6yneF}Frܱ6G*0еRwik\ݩ]KxiH t>6Ӯ5k6WZ}ǐ7;FFO$>&?׿$/o jB+bUT,]q5KV]7_B2;XYG,)U :XM?N$ו7{7W^{7 4_Tg٣`K(XHk.`hlC*pFFA} YНIn&K= k`ϝacxF7F2H$VL񕿅_Lӆ=VU77ِ΃n084\ t[QH# ;VO h &~t_Ώ9Y:/4I|?Mr_i~']mVY"R}JY- "'jdoaA _+h/C j#So pczߧnyq5m֨Eh+0ȀXwY'kj*)/,c4θ;_?QQNK :XMek^)-ΓkUH.'b"AZQp:A}5zTݟ,gҽC- ƫVӬZG589n+jkƣIjq]]FsNOu;ښAhܝZ` \@}_lm}"7dݝ+=sڽޣn. -n#Kx`N81֭nG-_3$M߲X^G3HbW` œ`/|TSxR2Iy&NQwb]991r:ti$6#+yd=~GV1MnUQWPM~G9;GO=fN[ǵKyn'JK#'=/hv:\֣n/AkiTǯJZ?*<܇3:KI5i#yYPD8܀y"_)'˧tz_W%S..)8cdDqdqkk,6\RZUalN-qKN&q;JIX jUGeA8ž(<:WƕZ,|gNB?\}Yss͞ @ 2A7}۲M̧~ƱuU{F[g0b;Y;TN{Wm>hqfVWY'9b3b{Ҿ wѫ֫xظQ1?hašrs':HjP3Ġ,rPGii =oq5|%^#Xxf#q8&]s~tE}r?fORV%oC] uf"d 9'? =B]YT\\\]|ƶ;-F4bi/k}[>y+CW連 Ң#+p>ti'h[BX)W.qNzV炼Y>]/Y5}IR7v#7?lJƗ70' ޔ^י=^jSȂ<*qUV*А֦ԧDʡ?+3B fBKbpEO<~fuC:[T"ʥۦGp}a:1y~^O]ia4,~H#Ƹx\p1]<[4վISyt}Kd3DE7_^-ƲGo1ֵ+wWtp?Z]k㇉luIJnU`䂾ֱ_,Nep=X՚iUM!m?8{F~oo3=e%f=@*Ekn6Ԓ푢y{=k<' je]bGYnw.=2? |/@:-y8qu*]OѰ8U[ }=v yVKz(kxŃWNFIp1( v[zsFFa.Oȑ蘿ʑ7pFE{֎5 ;9tTTH-#9=kWEOM!q5 j iKxz]nb>[׀sЎ+fGPa8~Ԅ1ғY/-f_1KȼΛR`sJr9k~G=B!!VgdrS. eש$vJ_iui!$pA\㓏zı_5Ūj0?uC.5f1fxذ$ewq_L˳7/ hrǯ+c-8UO$M3x XhSxw~NmR@Λh -ï Hڹ̯˃wDͣݱY̖ZO ]gfCfR\ ʂ0@#҂ l$osu=V/JG]Kң&yJKXYr3W`g8֖_נnl}/?zWֿ͟ yJ`o ^E,oB:!|"n%Zh_͇[Þ&ck{*9g$͗V?ݹdQG~%rxK-SNuΟ{⫦x 6v 9@>%ٮA #%TFT(Mx(}[[ܛiok>z_#x&n~$?.ִ9u.=ޘ!ؿ$[P$7mg?6ɥ;JmpYLVr,"dds]-~Tk_Էzevȥ]υyWydk**ZVo .אwo#xpd|#+@͵m粳Z@_ZoL NHq\ٔ䨵4М*e\02+9ArR5Hc,Xqj+@V3}Y$ G8RrsZΥi}c[ǼQF(xGJ%ִ4l@Fcx8%|f'DM2fh;J~izn6}rgC\g{WN<߼yn5YE+hRY|l-OdmQAgֺ__IcH3JFk<9g\xwIkG sfr/]旨i3qin"}hnXG x8'vOzujGOtoK3i"}KT?KZO]Ιwi w x ؽz^ KQOn]Ǒ\㞇~wFUֻem" c&ݖ4q(T|q]Jr揘5$Ǎ(K9d5x.$X:%QI8隵~'Mu0x N91MӮ ue!.\yc~EKltPFM^Y6;\qw]kMZMWVh(˖U[!-΅js\Oh7(nX𝾯ĶU0mb0 W'=[N5DmVMErD[Ip&&۵G2=m|OjE:Y&U2abN89׊?|AiMG =>fN;UGQq`695W.])SmMYik[X0iQ~dChc/㠯oSK?=~dh:ԓ@p3#y 88ŃE!g']\=8ug8si.~Ӥ LPZ$Kh!d;5̀<8!u=^%L$`ƣ<;VpwMuOC$MXv+_ktXu4EGw"1q] Kj,]\] v F+mc'cڼ#? ľ$t iDZR./NkCumnJ -P$Ǧ_^*̌4 f)-?b^>2HV2DԤ#>o\Vw}kC\6BN)~:5͇«oCxkUX$q$mfOT;sPRj6s$:}֩o+>X CV5)_R紹er$#`7}:N4ҫ:r䄴;m.v {K(,SZ:9c=Ӽ.;MBE$aG$WVxsB;]2ߞϯumh4ր[F֯6{b^ܩ\7 ߣ|7 r\2N6F,2eKUMO[s 8 \gڴTyqŭu]'Nܓ^].wW?SGZ}3h&Dm-)8\(*#+k^u%Ǜ{sy>|WQmU4)meMѨVU+G#=1Rxk}?GSI ܣq U1'0pM';{]}:ӧ*@1 QxUǃf5[՜G$l6G^SM1ԊtvѲyiNBsTo|Kj~47c"Ie-ty_:~9&Ktm)>v|t$:mѺ.緳 weR+קjs!N3,Sd;FK,t0z!# EQEQEQEQEQE >6X-uRlsol,~HĒ&<ھ?3_6rIocP^2e#AN1Rg5xs{MΕ w2ڬBdAܡr+|Wꖚ,V& >IA;;b,r{{X,HZ/JHxOw%X @xYҴP׶p;~dA@'99M^FxjJoڦjiXKGĐ:Uu AIo#짿0I &>[&ArIRXiʃN863,v8 y/侵[>)H%?.n{OwWvf$:ew%Hde$dbE+Ӄ{h;(Nn h4wAY#%◣5kׇn9z+[y+sVq=OjGwƷsq\0, |_66ׄ]; 2HqEqQ$览4qVϡuGB(-$ ~n'ך<5?!3}&fg*@c 㫇Z=Giv_No362'AkRt1OvGGhdӬbhekô/͂2q/-4Hu]Eq?!a y-Z7cB9.ͣY(Y ׷_y!Լe$ͣo2y2~Gǜq^>ir|MEcA}ye Yr7A$|xKhunfHhB0x[M/,]n|? |:kk5+Qbwj .۾Ww,~{]=$׼M<tM ;㎹CveeNpUZ! SŒI@{퇄t]ZYXEo!"@#[{Sթ5ԙ*.ڜ3K`|uFϭM%ˉ" tF8XnKmvkPWHHc;fx"'|CvZ4a#ppBogu=OuWѵN׾}Z6I@FWK_c5͛E,i q_}!rIkw^iRiL#X3Y#H3W HO7RyOg\'m n>;||mGi-bOK!UOҽ᷂[xPn4i;tbM݆~^:Fֵ&(-Bu w5<'ݽܞZ-ȘL220{bTJt?Ш`g5s;;;5K{bl`d cGl)|Z-rT3sLW7xGz&UXϗoIIl8\x:)Oxv÷*;ZVXpW5*p4r5=;@gz=!¨rI^p}jiE 0?45{mn[l*JC=idԏ!I+(-UZNmEITVGG^1{kA7[=mxBvj gWDgA׎a׮o#AgU_e[{7ƹ^=][G0a- zקGtڛ|xIVU}?xk۸$# nHSv{u^dn貝`_|-:-!ʲ nHڼoi<#j\_CQll O9,W7VܽBX+#} eMrϣ{Gn '&&4ofT]󢎓}B8Iٗ'-JMrmSD)eTQWkpc Ys$rג+VB`2g9%N`yxU qy-XbYwR;S<񑏖3;sP_˟ G{imu SU ?Jw> xMMC_kDU El#|c0oT[XYhY]zLIY"7 J\]-?+||O{_uMsR%ͬZKSp0rno0HRYq\-j:fDmo4Y'9+Ak˻X ,Ưm ܱ^^J8^/4(݂Ox[࿌t{έoVVznѵGsۥFG>\շvqB6]kZ%xͽ,HNΡgcmn"# @ձx-.owRPB]_ڊ/N 8@/ XO?G Z,?_'ފ?Կ)a?j_|~z( R R#-K/AK뷢8@/ XO?G Z,?_'ފ5ᫍ{n/c2H`1*p](?3_:|eO1XjzSDwڕK#޾?3_9|``u,O%hԙ$ՙ1ٷ,8PELv\G t8~0-ΣKim@pW Nqn} }Ys&kmku)PaW wdsyƯ-kȗq^E.׀;:1tbhߙvVovH$yl FJ$*4nv,ZKbH v=PymCڿ"]OIżгyr++lc`kxnW!`~$6v_,שRMlqrVnmmw2yJH g y_?hͷdw8''WW6sG-<~g)""OoT.c2Z~HS!K psװxBm3H夙R[gt4 +B'n sZUaU^ua︳?hn AeMUxѽ:Z^GЭ Le $wB; [xDq!SنG4' tYfa RbE)1 `2ت6XWw^O=RU- i;Cx885gMi*? RzgR]?U6Y_>QKƧg[ϫQVK;A1T:nzqC-27"hIA}_Zx~ko`^bט2v: Wxf=}͑$d\8⽪UJZwkLu(nIʀw#}+9i-m&" x\p.nRQ-5 N\1\Ye8Ct^ku)-JiUc[Uk o jFb2WVo|t_PtNM#` qz?|EE /썈xz/_V/mWIf$c}_B{kq^4[;xV# Q\A ?^Z~ ^Ƕ־r"gy=7ozޯuiuf1D̉jߕs;$y*z:^ܳ`Fi2r@#X//?7[k6cidKh! 01XMZ(폟+N{`u 5*)7Iཇ6߅^-oy$6KHZHDj GӖdAD Y!aMqm'>Tѵ N5 sG+q{&]nx.XĺUޑxT"۝x',t7M+ Wk^/7*LzW>,mohBqs+AlJ)b7-7 Drs+W6spR#Yx]JCwq ۟R0GP0J/4$fF3䅔=z}.c-N_>(ӤaUvUtGP3BHA\w漪n˟7eS''G_U?_/ړ[Xq&'R0NjlFYdMzz5tˍeWAufFp]7ƿ^ ύYɨ)nu 9Һ k"sٍv]ˀO~kҡRՕZŅj[]kLih5u =EzΉ6#‰ ?cا௥|ߥf#rF"g:W^og?κcxT^4)$}"L<6PeŸ0GJ4Ek(XĆ>~57q8^|Cm>]֣%t˧ּ}XE^.12e)AK}C%b<؜T1A ZX+3R0z޼VM8mc[fKhA" wO{VZY2I$"R?/$g¢3J^ӣ<<"ԼChl;r\U_tx? !8(@]cᙥmj=uYo{*1$nӶ9u$2dYv2V–Vq¤X;=#خPAꭢi5Am!2==)+1j+uĦ>=\ FlMB[au﷓aZ)T5b*qy?ڵPD6y:+ָC^Hhs7JNWj.c'Nzw$QGϰ߱C<SZ'*}A}>MOEA(h?5=ءP?PbyC<SS@}>MOEA(h?ǍOX0+ʌQF :&Ez_xPtWNQw~ g_>L/{gx'žau Rfm/ ,k$mzwV~--͵iߌ޲Ot{xO%U%EYH OȵZzףȴOPfi ӿ /&3zwEo"?>;[r*rbpkfoQZ՝VK"ibfU%ڑt\#$V6[llb5's 3@2fskMf2`H v1}kA}5F[o8Ph*"A#$Vл98+dNF j'Ttt^OhQy`@j2E=J&Q)elմD&y=']҅q л{y,YX[Q,jb0dvM.xuK[{/ .s"69;D!G~^q_;2j %mU%F,;O^wXemܙsF6l#O;W9 ueWHա!H9a9+T|+8_gOGNRfkˠxDIVMKa2Bx>E5B坣b.H={ξB75Mɒ8Ė>Wns<q3XWjDž*W7ī;t.8̠`NHjˈ5WURֺ߄[dI"im8NJL_yMu FA8sz/Zŷ+H1>^HW,zaFC~U z~i0jq/N|s};WMogx)+Ԯda}Lh˖qm;Gk[V[X2#ׯxR_ 8Fh7cdr++ Q:\bJ ҷӍc M4}NFM=!e$6on՛< h'6h8#\H 7PWOU[|pf9sI#Mϟ?m#2:^[v[y.wu{}54#k1Ca Ln~΋ 6 ם"x6O vۙcߑ]ƭ.l2R0@8ۚ/DEzՖ;sNs1QRR_+|jV}3S▣xcñwo# ';dskiwoڎ ͱiYYH9u^ ۯGTV5;|CgYt &;a9cȧ=ɯ]s&mf!l󜓎K7-Cd]C Fv:pKPƷ2dsѾtf ^1Ik,9tkۛcˈKpZZq@{9{CkK+Wc8 nz-sKjڤWva9vC6sˁTT/ď1δϟfQl Igc~ }!dN?^ ռ3x. ɵ?\s" v} /_`?5ԏogNp~D6k8TH.ٛUKޝ2|{i^leԟhXt(:9?{iҾ G[M505؅[=ǥy_čkZGѾ! vs88x'5DžK}#L`tI£C *Z$]om汵aeV[-WCOIVѠ1ޱo+}1u]J{NY&F%.@0xR+MdLE>Au[9{c~=*|nlz48*[0|'?):*AXВݜ z W|Gte{ Fo@s۩_IZ0*\ -0_S—tmawvVe H']a.hK`n:]ϖmW0>4N\jzeHm)dd )v"ῴY#prOk?golJPŮn[,He>)?!Tj!I _XxEPEPEPEPEPX~98n_dK jЩyeGRJ(>*EoWַa\!q=+o^uˋce[Hn+[ Bf64w`2 FuKm~&G1 `#>w¿4﷗]Yxžψ5kcIn9u(*EXĭ>7n*1RsgχĞ7:G`VVԵ{yծcD3̍4o{Qux t$>"Faˡo/g)wk:3Z7t1%RrkO/ѐW9ˤZ%Y-B[4 >}U4GP1_NEJ#h1Q4;y4GR(]P\y4'}uדq'-Ctf[ IuD|D$6/bZ|KqtU=; 3ZS04J@7n5Al[qlmЯO=)[FxNU&FoosF_h7_WZKKh.?wڮ dn1`ӥx|?6:Op;3hz z?zVp[o-%b,G8BTl{ >6~Dw`3hw?3^5J@2DRF]b8ZC}EUt8V×hʹB$V?2 ͰйKV5dvዢp _:Tf|sZ%(mdYHxč.A#'9I{cxkǐZkwڽWzd*#I&ew`ckV3i֩(䈞8ՁwWi tgi{a4dlU&r]T`+ž"OV. 5y0:x[SdUOM{if[vOG%.nHs^qskc[ٯyvլ"[b ǻ+O9\iV҃Qmkvʑ FWC[|vyyOkƽ_w-Xj˰xV0J~Ww}+A;y˙KY]8˸f zx5ytӤqNNc< M)j:3Tԣ9b׼=mfQư"JE`>`$}+&ut$t?x@a#MT0G4±*'3Yvz1,dnWf.6=#&'pj5n%s"+sێlMIR SQd._} }Nݖe8I6_'fJgl6 -\H kk5H/4Œd:X*NGN{due˿| vd\CH1´ʮ.Rmn\^C׉gLy`ou42]\" gW?Uf> nKIa?3P|Ӵ="ЍD<э>@S\n u+[{5f2xQAv>鐋{;%yWY'ƶ* %&-&6 PFdFbH7^WѯmC!"CCdLKe> f$YZ A:OB˦I<3-r7WM2M'Hk=Z8gin!{p,npz΍ggKEñB=ͳI,!';88!;; c0cڍyg;㧎o|3Di,ZdØ`@;TܨʤUȺ-Fi}w":ah7_gu)- ZZv3ú[Yjw o#eg1s wCsVWвC:Eڃ9_r7o/ԺJ8 jS+S'a#\ky5̩#82VU,%|$R6 #Ygxח?kIXwq^TiNnVnί*M!^M'=ţ48 u[=EQt{ $"O%&bRQXZtӠa%+q,YУHk٦Q0sjE诿9?#G"dttQ`9E?{'k/7O?#]Xsy~FEs=4/7OGE^o쟑y~F:("d=5E?{'h^o쟑,9?#G"dttQ`9E?{'k/7O?#]Xsy~FEUm8d*ι^J>̳,{f׵zy-Ğ,elC 9c߭ \M\[PX]*"!c$ LG#Y9o]Tf0 NzSV{epaj+:cumXAS;M4qNgcf OO}]?gUYx+:. G޼\r>}|&8L>vJˑO_>I4>[VG1-dBk=S)btˆʒ(־φ#@skk"!8(0~kh۝Y-`7LmgVoAӊEI颷Kw=8N YɞGsێH[p\}lgMRb@;rݱ^ti=CܣZVyOPR RC"\!B3`Ml;|aekȬ4XR5st~t9޽7Lնsl-2nHPv#:^v9FuZݡ,db JUo×ϫLe5~g+}Ӽ=x_0G@Xr:>߆~:%ďwO-EMipv$|}൸FѤ$j Amn-/KJi fzczT^=U~g,*SZu{o, kq, iJ8A]&Sfi)-Kgpx>渽8ϖ: 'kKM_O١)w"#.F@ZJ:+XQKE:uS:uݡRU'$ $tv|Y+]k~N]r{wh6bV~Y<`t՟W׿fjʫu<mϸ5YV 413ݵEck9P{gQ K#'!ÿ -ea A(l#:eƫ@^v_3_1x)VG\3Ǩ{z -N6$l㑓5L$@n^#j$RGں.N9㜞k6,xR=XmY5"KDX,Xq; Ooj/_Ncoasj>E+. o|gQu|Gawn2b,Po h ]YjVkiw;> !H\8ŴJn;Dvmf/zwkټq 6cĉ#u5? 3SI.g6ozby9B["0@ʞ+ (&quYc-ɵ%vq0ྜྷeQN K+6.Eʩ؋U/&9EG  pRkEnYimVUIǵs?¹V6jvW7 L%}s 1;i$a[kgCFP(l?3+եJ/ (^g2RdGGŹYGڤ)@35V-a7ėSڿR%Ss׷b+wUs]dc:zw=/~ӼS}@Xk$6,}x]~i7]ClXwG{=x x2źUū_],rK j~ˉmF3GũRR9Nom4]vEDD&Y p 8#}w_ |;h|x_Inb)œ?eӨB%p{7.|~wO+F,eD SbUSkU4' Fg~?|Rd]XuxQYxCZvFj`@<`]Ooh֑j>[-D2+8=c\/KkfF'FU=k+NTe)=Og i:ui@W;@T q9?tC8?*|O>;WFDb@q'jG,`)bzp>z8C!U,qץR2;+&9 ʅ 1P4r)CcqQʱ*{(guq8-Y3\e l'8>[8n+}m5b]>nm6Pbo7^g<͠l:cKWPJ)l[0<~ՆB@YGC);/Ee}aEWQEQEQEQEQEQEQEQEQEQEQEQEWA{=+}1פWxPx1Dh8ZMsl/WEM08;z*;>9ݰ#81bHáe2q8-|jidF(8Cxm4o%RAHh|9k^ǚBIas$EL,ɵrXZ.ؼ6a =I/g\nckI[cQD زdA-玧ҥfŕ,o,OrR.AQz}|7mw]DMg'@l`=k-xFo FҠǔXMN@Rp^nF597}Po:INZFBМ =b;'R3]-@:J+[B۴vtIIW<{lk|!in㸈6 `=˫Bו3I+]_q_Pq/^q^wJpc_IHn 2ZՄ.Sp:]>=>ky@+0pT}W&;8Tܯ|nWdxDi^6%[jm5]3^-5 %dd=QO|E<744uaC#jĈS+| <}), Ncu<ҽ5<F,/!g%ن49yGNxG;} /unBN<%dF u٠KgrLk>u?lCHM.TD>\#`ĂUp$ܞ+JyՒjĺ.GCo #^xk(N7 <"`Ux|kg]yu%f){gy yQ5.*n6M.>Rs[Gmt9Y%,iK! # kxZu*)nk\xJ='<]i 4HdUYdfG 0hwGQIdvn/ iͷ?Z[->fe۷15;YZ `ѤHU:|0,:;XZ>OC_aਮ(N/fhﷷ_|c9xv[jZڤڊm]6$~~JUcI%WV1C1 ־<9VڬMD^w,c1'y.Q1իis`V\# 'd?L#um2w188x.4]68?pors{'y$22]0?nYKV2|.L.*qog犆cur"Ir9osҿG?׾hЍֺ PA l}~sOYĐ%!6b:֖/5T+<=I>YKv!m^ b?xkO{ 4y%9b\έMF\:d\W?_k:e-oY %،MDc*ɾ]⒊]OY< VҮ xxI wn?OʽK.]p2(ld+ t~yg<"r#8W|+N{.^xuw r?Z1XXBs^*RRYLAogM|..#7||t·ڵOH^$>lL0 +g++ cŰFd`EnkZj6/X!C$rn=p;^Ubc)r{X2Ođ4`Io.4N.-Ryg?zWU7⻻;*Ġo|W;ivEuxVr"7 ]Ş q_?#1Nqܚ C8.fr%m$^n.Ͼ1߆zm"^@I`OzݭtֹYශ pE7qo 3H\ۊPc`JӒ Je55֞-濿QZF;x9'5fGQP2 [zU¬O$/i0[ O2;Zlkn[S Kxdu byLU(;xBY-nK+]PX~l99WWC֐wk-m |G3m$fYcL_1bbG?d$L=NY+ojnۚ|"ʿ@U@ߞy8F$;GOp&I@ 3\Q6rm@85Y920+Mm剮\6phRVjㄹd]JC,sኛM=+4X09|T9Q6&7m~~T vfx^8wj>]:NEIQEQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ycMّ#9w<קKjS\snM˞e࿀=YmniP;hve$qZ-wVڋqG ɞ jۓ|'EM+UbK3+rg+}w9y% '* 9Q k>=lm Kh4Y)V6 .r k~ x21YYރ`g_~3xCȢvQ$[34RLp?Z'iǗxɷ֭O,/_[ )C2*a鎿N|+av{Py4sDQR& l>?I'7?f y2c O8?'mw<xoeycJ) %w{tOnhMٻ/C}O"Ig=qxψ rǧ6O7q rW:{A ߏ S /,ïj^gnn48eHlr215TڌK4 r$K=g#0D|Fy$%c#5.tSpO/e jƞ&[9 pqH?g߆'tK+KEurD4:߳ZZfd1HޠW_|.0 Թ QI .&Td_gR9/ug3yt'Ʀ8Y |V|Nqz^@?1\CZ*QΧ|zW-KN y A\LT_`fD~jJZ(ЌߺGa8 cum =HjZ't}D!dLNF]2 [}FOdܗݒE Tg{v˼Ԭۛ9[Jm|oA]׆?b}göx.7Ӧa3ZN$޽{Nվe*(pH}^.8tcbdGLu5Ϛ69|O zڹN.mJ1K2{<3 ͭ9D۔sӃ[F)y]ۓa w:g)56b ,>>&SHH8׌#޿EF#StϕRR߳BVtkq !gN,F3ux?f Wz&ɐ,Dě%Fy:O7~p{|Wu:-La 7ۜ=?P0rGP]ZtPU} m(?\WjIE{u,60W^~Ѿ t+/]-h{9jc@QZ~%w{ x:C)s y^sZN[[C5-zU7ʖAo4oF2+_¾ Ag>%ȶ!:>=}O$M5Ycij$mXXIj-o⍽p$1.ɸzg5P|B%])ly:G' w)7$ԭt D&W bN؈NȾ#"M#yZ cyp5/,hd܂yyv`%WR^>9Z9 N~nWKY#y5qT17ntzWG[VKxaTHaK|**_Q~]FFEiy9lꘇWmG[7pT7CD)Z0;s޻1 Je|Z]ؽmXgEkb?l ~~n'O֏K_uR#ʻqsM`r]6nj|ncU:*r--*}NQѯ"nzw/~- i+T-滔 ozUwmzQ#;;x?td"$[x"0t8n5:?$ѴN_.$2Jsrp;yw^ .lY],WS0, H% wLqMRKpǿT}/u= C ޡ-hl몖 s]m}3UgNǓi2ŭ^jcH[Z$F/ÒBm|( G5Tyɸ:_\β$.orOmzҝ y9/MAb1ppvx !O$$qkoַ&4cA#CA0pqtש[&ImiR(^=@@Ǯ|k)Za6d.>:}^qC0_F-^?"9a *_}H]slyRGD3m? ?Vi:kh)nf7t*N-c)ms4kOIqciփ4%5cS:jt1)K*cqL8Rcq(嫐M*YL9P̙;sFvsFl>`qB0B.p)J&4C p.%SO#cemHF75& dcTԗ_Cz݉AG'%J'Cld{@ ʠY&07X81 ;Ai))[ 1o0  ~)l| sJ*%:ʩ91L^W{֡E8ڂ<8d:ݍqO1(0o>5ӝN{9S?jI 9p:%rjAI :l`  МU#Dg =4f[w5h(̚;̫1Cp;[js9%rf{8#yiiT3S #4b8& [S/v.@ nJ|ܪӱ*/*DJL[:]juY LyPۗOH4ױ9m'=7k Op52JC q]uP8Ihy}` r:8"QϏ/1F9G<1ܥ_ث2g?kueQi 5ⰨsCT5TB緖G;Z[,)6fv cB\oi>Sm"4?eTEj4gnp#@dMbZ(jx+MۊXo@Zt6,#+˾J-NQO{3dn_"۲HT m#0Cl4- ?-:ň 8zaRE6`R B ryEHR4 [$y5÷e=m1Z{WaLѻT)U\k5 absi܎SX<jjDU .;%K4a,(";o&XapS$ ϧt *=&Mh:t#`oWݹipho'# pbMVrd+ sŻί[W5?#9Ñ:@b}ЇHWYu GWY !"Lq7 &ىv[?c]\!^rkQoDŽ!J9A:vz!o9qeCzCS$x !]=$0T¡h֭kARa5G{L%y Npn+Ӗn]@GA@0Z)b1dp5SŞ!WdYYqwSy.ZE P",]L @+ss阚'牗1\VDܼZ{28!2[%2N@MQe?D*bVb$hN"{onbej̪6BaӶXl?ꏴVU>!RG+yZyinmK55OOψ<DY95H-e>\im9rAS\qsAc)U Y `iL1'0Mw'}n53|-&{v=: ߌ*dmO-u<rn63*ji"LN=Ӭ*sٺj[ce<.m^Ruj'H230M dh ^FG)y\layuhqQCUz&iqRfNufI7@0DwSU7v\v=b-SP:%'4NSx:`JJgRG /tӪCtnz>GF-}܂i6LDҙnHtY@ffheAF^.)LˈGkmMPܩq8NlL| %9\X>,Њ"c%onpr ܰ5`d2HP5Z^I[sGH<bhiR ikDGağ jUj [iYCrZ7˪&sZ>dN$mi}37_"!ZJԄ'NaΎt-፺>.q xaA}w/~*H@"~@y 9T|l@ $f_U͢5~ńZsZd\YBdTFmVn`Êj9ڦXD 17'on 7% N4D1gt}?MqwDQQdУGj,Al6ȺPZGAD=* gc,K5w"\ sG.IIs&E#N] 9Wkg'S Ђ"1@,{qg+afA+E T y$PuP)lEY~wĻ57WT$'CuRzi02Asr 3+ar&~8lϐ ,#0)۵yye(k:ra@y4ӏ]8Xx΢}-&_f<~#,9hŬxO Q#r% 02nG}T*'9Dr3D>8 ɢ:i; *0%?S*Bd?c_: |h4-9(&V.rJ% %}BRo@mI x;TrVΔ$kQl-m U,&LV].'Sa5D> ՎzΌ>=BF>f^_>3 2'T U uܡ]hCWUնr+/i}~^WV*/q{UeʕI@,8Wg?p;xnuEWؠҡzPmhkD!_ɪL 2A(*6JLDm3T{=mB MsI;A@*>f3T8]Rԡ֮`TGÏ40 iL+C^1mXrqD t'2;$03ٗ f:0hXF'd HwB=o@qi^Eewчx+ݲ!)9$WjV[PHS҆<&uh5S};<<&.$xLI=(@k'^_.L,Ωc=yBF{Mm}@͖cVJg.Ks `jdD:9茊o?dO~BǦ TuDJЪ+ʘZ~dID?~ωV'^ L ;icY-<vQo-핑(Z0piarJ޲d)DnV[mӲS|ǨC'F4Y:Tn"1eC,)(5GQh1 P_bNzM~rCɂrgIx L|68/bv G !E~3!L 0&tFEjԚ:" YXzXx.,(ͫVJtVhwBtG kR^dj+'0@,mz 2u %r--ra@1$L]\1ߊ>9i#r:Hi`)ukNf^YeOrC7WT Dy@o[>{Ҥ1w4T;=BH?X!`w 6iɏeuM ~ ~tS730'ÄKt5[JN0v$6ep"?i&0-eA4;8P1 )IM6;fCT./D!3}AEDjMa,pET,E@n% ~~[Glǯ?Vh{}xU+=>iu'mgaLh$sCozU[WiZBT f揜Lmwv[).`zw;i$_֕ESo$Qє&Ȑv 粘2I )A s/?1hHۛ#;PisM/@6fۤ=IŇu^kU+p9eڤA]V8&An;]^&ȕϞ_`涷)F f3왕s/jG!97޻wOoäwM߾Auxp_-=#5Z[";$fߺu7X * + <fKcN̋aY3Y{^Lpphdѧ5 t䓻'V"j-̡Ͱ[˦WR O}@^>1|u/PXXI3ݤyfFp!H4gv0I &y޲M~k褡CivZӇu 1_+fĥTj\#in糽unUg_̲U #97{{`? 60 _ѣ=\0wa{?7.c&ȆvHzj4A\SJSϥdN W~[cC KW>So[[({nT w:[ c ʍ+Ykpk.CP3)Fwq4^4}p$T{v/`Le?k`i^ Y/Oz lE_R-S.D}U P.j3jɐ^.7oLugv6LʊS|ՀB"?7OZyU`;f+?m :a<69|#]"~c"7sTrP)2?PS32x~DO#Β)5så~iϱ>.[oͷ0͆|6X3ZWӧ1raγ{n5)° ЙkϺ;reN\}آ:|/j51X0#%۹m###s }gHrgxqkP4FWwsk. 6ZH׈*Z`sm'LnSs @.YKtEXtSoftwaregnome-screenshot>zTXtRaw profile type APP1xmQAn } ϡ T+Um?^BuDx}}}?E 5K+ȶb,M+F>H6=vEHaޒUZo)g<Kg-*M¸+ ) ,us9$ND@HDPT'n2}$C[,&ʚj2EvoD[0EYn7 Ks} 갎vxLtA ΰD]B7=`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }Zǀ1iTXtXML:com.adobe.xmp 591 728 0 DIENDB`fotoxx-20.08/images/netmap-locs2.jpg000066400000000000000000000367461362435004500173470ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 243 222 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{׹~>K+M7ƾ u8tuDVK[Ѻ+5J7Wi[s>}A# "ſ-oǷZpxo>3On-Aƻ%;sPo^/M%o="{ _LB$JG;"a[n+_z~}'?DWGSJ]ºm͢_hW:w,8I6 3 p/x_hzu|u5R%m,+?%ڥG1C\~WW>laOl>|IuCy{;z5ޱ3nNZm3m%[.Xtަx7Z48.oj~ _̷=SH8rH=:${Lk[y4y(aOu_Wηgcuɯ!$u]q")bQм?%hSBKpHR^L(ےO^U˯Bm'l>|?^ C]xUx[@K-<|957lגDj]ͬY;d.WL3+3SmGN_xébH)c=] >Ȗ3TСXdF6y>İI1|W65;L׬t䶆Y$YLUے@T᭼6.el>|?ꗿ <#}CQx]wTmBTżJB{8g4n絹6vet|Z vmoq:4ǝfc$βx^H%<-:mbp>As<05̟r" pOsK|oAiCy]5o·enoHT2"aެI[54)7Ι}4_ۄ+ć H=GY>]DB/$I<ZW:/lqB dp=JX -x[.N?f9 P^W/Ld[ a,^(xt9i}9^ߕ]>*cVڍvv%thZ^A  2MYFu3^ (Ԏ\^XJx'>}k9>%P]ZS:ߋl+i>'u?ot<ﺸ]Bp8lv=|oG6|FF=|oGW>+?ʚ)outmmn#AY"1/l4?㇍~"q,"ܭ(CDc1A ykat\~= }#8v}='?t#o|KmJ]C:%$e}ўVTu[D|DoIHbEch4 Dp L5Ql ozG;gߋ=;1]?|mxS cu0Ա89+KO"im#[nye9%Jc/18+ߋt\~=.],;s<X2xjVOm1B9Ȫn ;㷏4{U֭ Vhdv-  *f?fo"#A&ӌa_ö|m}#?>##[q}#f??tmj,,JSk}CO}@_mJr2AwZc͓ (G׼X('T߆foe#י0ɪvW{ 0)JY;>\]Osq'q3dE~Ἱ?:wjފcT<9U$7vgzY#:@mn|A;xsP-<9y:> R[ݴ)s C+HX6 ׭Ҭ'|||nW?-,<>"r{j1 :FrqKZMd@lG8[*&iY bQKgI+ rbQ@4{-,H gJK] Hlg-ZERűl2}v7:(lo.$HsF=*|NѬuO.6  xT{FxOxUʁ3B Z@IR'=.ʊ3KG[^3,|@~Ak7n$ YA۴H&KG[Y_7í?$:IOto|u Lfs31m ETto=b(G>-խn't-fv}&ؤ&A;lPɰ;m<gO"e-2oxo}v%6cm <ٍU6~_q8E^·GF ݛ'/w)?aER(((((((( Y QWDoׄ,ׅ[;?ǥQGc7U𾍣Z7-^h'G4!Q@\p8?ti=huZK ,f[/[]3i串1uPs(MK?%Aٯ_kWx{nim.<%jhX?gGŏ>}vg^ ?ek:H[asl 9>?U k?t-'\iSarʪ7''ɩ,GOmi]:;pC1$rI&uj U|ASZ7l47N]/G0Kq-[t2F@KG f]'7ğ+ƚ\Ißخy,4@nX"O.^`2!NW^4 sQu)RQ?رƗmf|^1vMۅ+;/a|<#S7Gٵ7@Уơ0,/?)Z|~Zz7?ܗmּA]^n5NJdVN2Dؠc r{W=It-2!)o KT* ZJ״voo{p*J ( ( ( ( ( (0<"7?k­GU??5V#t*?sTQ|**X/Gn|_G{] C<k6ԥO[O/ĺ%L]ˤeZ=̱:gZ?z*tڟ 15KtI] q{4-YWߋL|D(g׮.1KH!5oYMUa{{ci%kk ؼ+>Ƕ \<׿͗w^/iSW|98m4j_\C ֓3L7by\αhzlKpf i%6;I$g$жӭl渚%mIaZV2u8iZX)QEQEQEQEQEQEQE`xDoׄ,͚4fɅ\88_IDoׄ,1ןh%Sd6A+7TziWmx Ǩ2}KG[Lokcpڝ؆^P7e`G(l'~n(NUQu~imT~ ie+(FK^HDAAxD@Q|NJt]{_ i<jWS"Or[Ynôm>HRJ}>DI5[ZDvm˴#misHĒmS`rMm]D{v?<|yeE~ oCӯ{G\!QI 6wLm-#[ };S;ſ:&Nݣ* kj*[~QH(((((((#x'f!c_vDoׄ,_1ןiɑ,?(T1KG[_7$ox=db4H[yLJ b<4U',ǫx'^е{9J,$ K(DzN t*( uiY(yz،o[2\8|{m\ =S_Hssff-"Ti.1sjfV-L/+Ķn o((uOip5|CtrE3j^2{@GǽxiDچ.Ԉ0&;K-a´sj.Mx+W[⹸$Ī;eLO)W/a2i\Av6 [C\W\ׇAxC4/Ycca=hѨV:w5;"2VE_@tn,ȗyfKhpxƺugNcsꤘIvE.nc|2ٙ$>q*|ƗTӮ`AnlžJj*]{k_=¥綵=OP-~!ƟÚ=.k UYef]rW z.4m<}b6'*+_ 4? #}B-B8em%6 We.~i//<յ+9ᵨ5;m7 .:>!j𯏵Kx.UՌKx4ۄ{O.cHN<S_[\鶗=H4,T(HʒĊ@4{M:9P?%[v]+{]@owJ T2GF$/2_>]i>/4'צ[ˆF6|s0N{%?غ~|c\Pu]ύt/>}gVzm7QСb:yWF)`kuK >cFҡd-8E&y@ psLv1dXyv*~̘P0c(>oi7 rvR-q8{t"YF0h<=*s[+Wc 嶛m (|Oym\gvLHÚ4?(o?uCh?~cmeBҷޑ8cU}v^5ӯ!{C@dk절FRYtW o[߇瞷 3TgEq?<_?my 4\g-=of[~z/vtW o[߇瞷 3@AhgMS q?<_I>-h zH?4<? KEK[i{ӭ)D;hR;Vf`Ʌ?PqUOVk<"_{Efo+~y^EiQ@'(ߵW?ZTPo ?OUv'tKJvA%!eb*)?u?u_k<"_{[wSWU/HkF ꊽϳ8'g~y^E'*;߷E9یY]Բv_Te}n ?'(ߵW?Uu?u_?u?u_k<"_{[wSWU/HwSWU/H ?'(ߵW?Kjy;[X\!< ɍ.pH1Fr2=EAs[ۋk-*U{v ;[<9PIDx (o_{o+ۺ YEۺ YEYߵW?G ЫE$P5O<[Tϲ?q@vw+4k28>t/(R<0)HA( QEQEQEQErK]Y\GWSkξ~RJoWVa_4]}MMB8xnFaOih/ӼR S5$&sg$v:DE5ijFN-MG0ejzwi?A4֓3=qHcVM-2 āJ1'MfJ%qaqY4QLXe`F#䁷$W_)^xMMWM_4jy qvFZG v[^xPX!6H]HVr8+ݮ} mH u)tsF :,,}n+WNa'`#H\ #ֶ$hX(f66OmusѾ5~O <7Ki _Mrʑ3+P#5vF\s_BU_. 8M'h;"pr8/| w>&}GP>he ,W@"P Hߊ^P=ŪZm*6  x~ xĞ𶏛[xó.Wrc$aLd6m\\`z~x/W|eu<64cY@Yy<^7rr{RwozσIɉ(x\w4TmQEQEQEQE]gVWw GMg$LHbǽC?>o1^Ey/D*0,[?/fl~H+( _G"_ػ'ٷq[D$ }˟8zؾ1]b}tOW@}?>o1G/Dz x[Fm.վȒ㳑g,K2Tuki a47ʗLTj6F_5@}?>o1G/Dz ؾ1]D#6"cɯ@(hz{zU*ddRl{d*fotoxx-20.08/images/overlayBC.png000066400000000000000000000017321362435004500167200ustar00rootroot00000000000000PNG  IHDR@2cJCIDAThA 0xʪl \z {"ۭ  w&y/Ɍޟik>^j$'0BX5 @8hq `@ k#q\!,a @F 4xns8{0o״Z9Txضok`O'+ Ei6ef7 z.1zTXtRaw profile type APP1xR[ )zc a TU&l4 1'sK9~Zу%O~BN@p>[k>ȫ#rdQ%u-Ѣ>n!XfF&`fLnnF]Cj2S{"f0'7Ã>NYQժ>4a)ҤJ--22˒4B6X#BD55ڏf-mvnW=AR iTXtXML:com.adobe.xmp 200 256 /IENDB`fotoxx-20.08/images/overlayBL.png000066400000000000000000000016141362435004500167300ustar00rootroot00000000000000PNG  IHDR@2cJCIDAThK 0T<< \^H\)dC_&N[U)g_!(-B@<VX!X s ``(-B@<VX!X s ``(a0S.u<“M@/e:.b,?:kY3 BYa "HnJC(޿cd$rZ1ɥȒiW]ņ. u=}XPD3+rWǦ*埈~!qS],%KR L5 0Q>u:BiTXtXML:com.adobe.xmp 50 64 ?&}IENDB`fotoxx-20.08/images/overlayBR.png000066400000000000000000000016031362435004500167340ustar00rootroot00000000000000PNG  IHDR@2cJCIDATh 0Uځ&"r{]:vhmP @F 40BX5 @8hq `@ k#q\!,a @qC -[0ǚXӞj'4;X|Apb2XԽlzTXtRaw profile type APP1xePI y94KjPI#y>?:kY3 BYa "HnJC(޿cd$rZ1ɥȒiW]ņ. u=}XPD3+rWǦ*埈~!qS],%KR L5 0Q>u:BiTXtXML:com.adobe.xmp 50 64 ?&}IENDB`fotoxx-20.08/images/overlayCC.png000066400000000000000000000017361362435004500167250ustar00rootroot00000000000000PNG  IHDR@2cJCIDAThA 0Dkmų[oEӮELṌ1yϸ>3_3/v6pA `$+d0q  8lfLv[ nSxG xMs1{3mVq3Pl-c43#;ᨗpuv$2 2I d8-ep[0$1ᨷ`@gI. `zTXtRaw profile type APP1xR[ )zc ǡVVUQ*Dv$c<_ޯtwSw|(I /q \gq6:ꍼh#ǫDURg\*q`i:323crS+a/P3a+æD<&7}"M8U}hRIZZ-vd7K [(b~RVh?yݛu2m,|Dxx~iTXtXML:com.adobe.xmp 200 256 /IENDB`fotoxx-20.08/images/overlayCL.png000066400000000000000000000017311362435004500167310ustar00rootroot00000000000000PNG  IHDR@2cJCIDATh10($<-E' ]?Ͻⷵ b& Nǀ,Y&@;6B@ym>86x;-w Ŕ?Bl 1pԮ lU7`i5sW / Im0 фm0 фm0 фm0 фm~* ]rzzTXtRaw profile type APP1xR[ )zc ǡVVUQ*Dv$c<_ޯtwSw|(I /q \gq6:ꍼh#ǫDURg\*q`i:323crS+a/P3a+æD<&7}"M8U}hRIZZ-vd7K [(b~RVh?yݛu2m,|Dxx~iTXtXML:com.adobe.xmp 200 256 /IENDB`fotoxx-20.08/images/overlayCR.png000066400000000000000000000017321362435004500167400ustar00rootroot00000000000000PNG  IHDR@2cJCIDAThK @c)Cr81.9m'oh(g/Ͻ, 0`t 00`1`#4 70KcV9>|5z?!h~hk] [?sWr,;,4\9ֺ4\9ֺ4\9ֺ4\9ֺ F6  zTXtRaw profile type APP1xRQn0=x=U.KE2`z>*\xBA$4WECqlow6:ꅼ*#Ǜ ,ZXuIfdf䦄Gn9f4r` LiDٯV K&Uh9hz8i8x6Qx=z9%ڢgCލۙBm`D_~і`"iTXtXML:com.adobe.xmp 200 256 /IENDB`fotoxx-20.08/images/overlayTC.png000066400000000000000000000017311362435004500167410ustar00rootroot00000000000000PNG  IHDR@2cJCIDATh @X&6 t`jA̹x9 mt\oDz.xFI"h)cGй<םϖ/} t4bYTI#hV+{H=ֹ Ѩ!u5]r*ȁKOO5rVgMX4@vA D0F̲$"'!@YdK\{`iTXtXML:com.adobe.xmp 200 256 /IENDB`fotoxx-20.08/images/overlayTL.png000066400000000000000000000016421362435004500167530ustar00rootroot00000000000000PNG  IHDR@2cJCIDATh1 0_ApY 8&Ǿy}V'] f2!x>f@WNʀÝ IpӚX8)wZ3 'eNk` 8i,;2p5pRfNʀÝ Ip9"=]zTXtRaw profile type APP1xRmn STm̄J3O6xyNUr~gS+%/~AN@p>[c>ȻZ#r|ȯdQ%u#Ѧ F[3#03&6 v! 5C.7T_"f0'7Ã>NYQժ>4a)ҤJ-z`I̲$"'yDo}xNlYdK Q]iTXtXML:com.adobe.xmp 200 256 /IENDB`fotoxx-20.08/images/overlayTR.png000066400000000000000000000016101362435004500167540ustar00rootroot00000000000000PNG  IHDR@2cJCIDATh1 @<>Y"z Lvg2?k}7W2AZѿlٶt>nlx=Cs 3`PggH03a ٙ0r i9uvf4:;3@F΀A #g 3`PggHoة .vzTXtRaw profile type APP1xePI y94KjPI#y>?:kY3 BYa "HnJC(޿cd$rZ1ɥȒiW]ņ. u=}XPD3+rWǦ*埈~!qS],%KR L5 0Q>u:BiTXtXML:com.adobe.xmp 50 64 ?&}IENDB`fotoxx-20.08/images/paint-edits.jpg000066400000000000000000000340031362435004500172440ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 229 255 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? { x<>}XjCSc?}'?±Gڽl>|?z>EaOl>|{z,(aOcޏ{`6>}G-M}ƳχpJoɗ|ȏ+~b1o '¯K^]=#Ŷ>%ީg*]ڬco R̓~C–A(}8 >0{xXT3G5hrЄ3yw/ x zE-o 1\] $7#AI\OA >}]g/FiWV1}SHHr* 4;F7.mculLrG3S>tC̒!q=oW`FA +< k zHǚjE8T^q;:G?]Z]jWzͭ`H57K凓j@Q9\K1#aOl>|o|3KL|#s$p]k˨Oga" r23jڵ:'aO! B;zbu.{Ye ʹ1P`6,5+sqe]]>8T)qp WQwYNG+! P m8 Cy+ؙ5kx̍ ppӲ֒^jS:}ڄËx,pimrVgr|Rk#f{٦HI3I?̑>sԷb'ү% mrZG'.fEq=vyu'܂ާp=gVQ_?m(pOo1E\I}pOo1G;2x[,O'W;2x[?>Qf??}Ei>@>Q 'b0-WtYiƦUF~b{W㗌`JItY&6#.zm(pOo1E_gR^N+M$SU< bС+KM\9J%k[8.f62 "ɢA ق$$H=k_w?d?ø|C'.{j_'{Y[Ke'tdy *^k2+ThwQ~}k\1*::V ƿ@>Q 'b\z7I팒`.\j_h+59T^ܟZg2>a y"[9im7)c [:[FX,- JV(<ǥ} 'bw?d?Uʰ{y[E x:UmEu_Տϯ8GGQ_篈L[3n}cEUQEQEQEp/^o[^5DKvp ,qWioq42(t*FAuWVUv᫿Zzm]J*ʫaGRs*ᾩ3H&sa:eՎ_]"/.T21Ci$oKWH?ma'oyx/,n HE6$LXTY>9t[WW]o,-N;ۈŤ,oR7شm(YR7 `5oRskZ9ꚭ]#e*';w/[wJ^lPI ^Qx|YkGP-TR/ʏTvg=ѼkXZ&n4k.dEQ40zmy3tkOզ[)tH.YN8aU,cj$cqF]i\+\@Y9^q:x~¶:tZ _\,h<3lrrǒI+ԵMOÿaei}CA׼qc_j4X9V3"FwT&WO^/Gu-+J:Q5&teYnYsԜU89t{+~a'nѭ{espw3ex^E #ծڵ=/5O tUOK@S%ht ?_&-T.5qi{F*oP 2v((((((+5+iwڅmre4\i$q2ț>o +~*90xL {\Z[ChHMaFF|/?{ihλ_riǗt x7W>J? GҵV[JW(1< xן^|(6mB_ͫYrCrvl*)홆?+A9!|Qcu=eΫe>= \  s pʛi&Oq{L F[{}Qojs:ٻ*$Y1b //!;I蒽Ȗݿ{?>,oޥiwښv<ˆ#Sz+yn[zW1\K]Eg5裺h.ɱCR~&( F+,<!w"ӾDwŤ^fyI/9c I筼/ڣGm=%ڭZދ~ʷIT7#=ƏCQLY֚}E #Kas18}[__:b>Я4[k/K/m !j~"}6/6>4 koe  VM hȆuZuv{}:9LK9nF1 Ȼ*O[i҄~xJ.ޣwsmM`1KVyRCqmվa+?(J}JiOzo?CCKKs9Za#4D<Ȼy$7^ԭ<5]%:aʽiRvui.ZfwUې _) wĞ|1*!D2 I'pF@#<4|K;i9/ڗN{[;K{r;4Q!K*ǂԾki}O.?_=FďxVpIig=l[Jx|;R{d8q^c <'gxxk\m' fKv^[ϗc8_sn`xEJȱs]!ѩX5,(@QEQEQEQEQEQEQEQEQEQEOS),Spl G*ڞ!??$nͪ$s_At 42GG! ?,me*C/?.?TX +?BNp$, FPqЯuߋ?c5屹meYM&(C ~f#T|ETCXCi]Gh0$s$q ] y_{[x+ ^#DSGpYyc$nr)PxGò[-$># v?y\1R]C\n x]m5hu0-ۆEQC,2O5ICjbfӴ՟OG9%>h~j`[k2x'|O.h\Iuj04X$WjdF@hj-w]/MΧ}jwHpdY , xj^$_EfvDRO8u>5>&^^xF87AX䷾QneXO#3Evy%N~v׀-Xh\ﵺ{%7%aFm͓;bմW?~'xoF՟SXOew6|+,%!m+(h|Q.bּ)xEJ<9uoXDF"h~GeᐑfkOuzUz4 VXcI5uV#j>au4rC@lP"EݳI]ջvvio[IAoxzTjP%j@+>#-۶[O'K6n.a䉎I2I,NI5x?W9eIm!H!I$|z')-#g~Z_ Ao>0_ .uluUT-/QB땜T ^[|C׍J ep5qϿ ŷ^#̶кZ9oK|((9Lg85/tAX8F|i? %[ǏzWM>~wj7$6Uy/n|)xnIn.Okop- Ԟq<5Og ^tx[}NPtךVp 4n !)`潃/6IͬZ\[IHʰКgo\&M՜)OaGf q+ñ @'$T?@zNj|?2x{o Und;e_&';~x~^d_O<<.NѴOF/&Kg7h-T,eԜWGٖ閾 (ڞi%y\ ٦PW`=3GtۍBk>OnmKu1sR,S"c['[^_OU7vpN4-,j7 $gd{Wto^\iQUݤ9b૎PR80Fr"l* NK'P-m/ln0 e8 إt_š7O<+FW`{[3dD"D=b~|/WakVkۛDǾYw!P8;i}6 -[wh_zw?4y߆qފodwF*r3>4TEhV{4_^[K *Eq$J$h綫k.Ft +kN6sG2T˫\ܢ綫k.Ft +?{j6`i6Kq%ֲLVvpI9=1nQE̠z~+*z([Gح_>o<":;hb}#c@8" ( ( }(UT #V(>o<"b }xE| jF(TP:Scƞ&ռ%Yt;uHˈxDMQІ\#9Ϣ"u=X[C]K%HɆo,d aϥy>$K5KAMֹ'[R M2m} 2XՋߋ4jjW+xdX Pj,^w KyB3*ǂ,kмK>2\wBM.-;O2I'8O^ mr_6E+EY5 an̤M <+SO$.&Ҽ?Hnͺ:JG%xɋǚYPǖGWl&ᯉn<#zԺ%:Q22:4P&fQ5ikk6^I]aiK [j]7<%?G'WI4ɥh/B <{U5|],6VR[Y7C$q:G_'hwZyocq3Fdin~{C?v'q;H'̙Y|xL$h\t e?ٗatEgf\ZqAk7@4Vwe?ٗatEgf\ZqAk7@4Vwe?ٗatEgf\ZqAk7@4Vwe?ٗatEgf\ZqAk7@4Vwe?ٗatEgf\ZqAk7@4S!Qi]EPEPEPEPEPEPEPEPEPEPEP\-σMs_.-g-!Y.c2?۲sOu[,y=3J*ʨvp-}PG]ѭ4-QQ&3&w28dVR0r?*=[gtSTKCZMj],+o0́'nyï)IڥvM3Mg$-%%2cp8`RZoj) (((((((((((+ǣ|[=D.eZ{+ѹh/+?C^X^ =΅L-$q1$htxfil5% {oQI4,zE^G(D'9,ӵz >sI? Oӻ[jv0Wk}x(θX%0DUcVcѴ Y>Y ;5X=GrO O~t넞DӭgCHї@ 5袊fotoxx-20.08/images/paint-image.jpg000066400000000000000000001671241362435004500172310ustar00rootroot00000000000000JFIFHHExifMM*V^(if%HH023080100Fotoxx:trim/rotate|sharpen| Fotoxx:trim/rotate|sharpen|NE0Photoshop 3.08BIMfotoxx, :http://ns.adobe.com/xap/1.0/ 300 891 0 C     C   +?" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?S'l Tϙ|XcEkTOTPQ/ڧFO=EE%TO紟Ѩ ~?4}'j*(_O=j{I}Si?GڧFoE<;xMVҮc-ąrQ3go;}?B>MTO紟ѯld7γO Ϭúfk17㏽57G]a|/OjzXơ%(>n)s{}y*gj{I}>?5> J jZ6k iWo;KK[8zUmlz/7'hTOףm5=GcGm_Sӵ$kf"HhM{ٟ)*hz?Gwqof{@e"?1Ic{bWkMɏgo+%O=j{I}ğ^ڔGmkTí-V{ H4EHL߼Hd tcY &so+ _ؓ5xwR7MbVIuhSi?GڧF'hTOTPj{I}>?5t𭇊!W:]ihRFՍKK {#TO紟Ѯ jǨf< l'0ar;m=s\uK紟ѧy;77n8ׄm|Wwry }¾o֟kc_'?^9?SSһG3X8r5o.Sw89HƿO?^;Ԯn/kFHD5>tѿ:Y~AƪؕUpI=Q@F(F(F(Fwwƚmw]uRIJr7Nq5^ >𧉼'Ӵn{I-n+x::1uEm/7w5݅ޑ n5&H3 cU+_^5ޱ-Ʃmd@dq}Ԯk+_ jVi0[_2<䪢!2 +꿵5Ωb$OgxR~uqm)$JDKܓqKVszށ6-ޛG4k$ŏ1"iw(!A9 u4&) Sz`3$N JdORѓGE6s5ލ -bMʠTh`z_ V )w-Du)k7PeA1 } 6e톛}ojYS}tr!x<Jx#Ԥ>vi ixȷ 8bYF=ǭzuG$Z>mskKy =RKv8$H60 pWna:ml-sYOUz1^wx?xGTMB0"22<ַ|I/|WkFaQż{ 䁜V5%{jSI=Γz7LI=Q@҉e$I5vX}0}->cj1F :xnnf[M++٭AcRk||0UI' '<ܿIV ? _L%yeL]J-՝{!G.mum'w~V~۴QEzͅQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@%;?t4û3'L_J3_Lû3'L_G;?t4\详?/:g .W ?g@3qz+ww3hw|gN8Ƌ=z/>(~1 ȿbOBc5 ?g@3q4RVg|*s⦓K[ԛMy۾@zW_ͥ l| wj^%kd*P$;t#cҨû3'L_G;?t4'd7i6]ߵTrC mƳxjXLo}?k݋oz>_Ū@G2Ljn |HLzû3'L_G;?t4;4wMto-k_ 61ll'+ Ԯ|6{bX%ley ?g@3q_-#Ѽ7{G“woxG> ke\ǭ(; lX l[_Ŏh:ַadAx@ImQJH;gYw|gN8Əww3h}O_?c:g ?/<;BxoNK[t%KNO$lvK۩%eG؁I€bw|gN8Əww3h3|]wH-.Ż"XW{;?t4û3'L_ExZVOqmI%D¹c:g ?/xo%6UE봜{+bf:f}[+ Ev%KĺWvhM#8ܹ?ᢾGm)1|(CQG3t?s+_ +_ ?f/  (bQBE|+OtE|+OtOG:?ᘾЃ(_ S6]_ S6]SbQB_o :|3iwz I؀?[__ S6]q_".a$Z:uymwdm#*10p{P^࿀oH-Y5 m@31etf߇~|G5 *>[u A~f>a 8^i?J㗂 R\\"FK9pck1%(~W#nOA ~Y[b(ܝ Cqnħ.eq~T}p˥{hUԜ(-IZ[OǯˠxA4I1L y3&'^K&ԕ㇃Zk;pmĿTOav.1'qS᭮[i|x_X&K^pY NFqڣzg>^j o jSEOcݝKt' ǜ5o=Qngqǧl ɟ⿁C`V(kMe/Qc*O,t/S4!>@ WݬJZT,B1b>OJn}OT EdqvVXO#!U co߳<'wtׅt {٬Nyn ,$psp'? $8ǖ,n'I&v줹戼JDq+j ao\<@Ry bL_toj.*  Swg hxclpqay3\rȈdSoT<i8M'~'%<=U;& 7:E+'s$" cTh_ o{;[qmklmB+RwIhB+m]!oXnm\[LHdu#!#ᘾЃ(IC)/`*_ S6])1|(CQG3t?s+_ +_ ?f/  (bQBE|+OtE|+OtOG:?ᘾЃ(_ S6]_ S6]SbQBK 25  h]G)|gB(Ҽ/_ \ui'Z}7ݬH\US@tQEPQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@E%X][-F;I}S^Niʼn@]ym77 qj1;aWW$

    $ϯ <ےN xG-ZHܹDC9$wH/ܰ Hb rbJmG<´H-g@8YM;jl]e.rOZ|/$}~6|mRd:W9ͽvg=+4);}e\uBÞae*OO֤$s3VֱrƒU'8 1Қ*WtI?az*zǃHGG8y8DKb" >b ׭xŢ\FHr2?޺7[e r=ɬ*ECz6w,)G$b7&5|O=qϵxS&u/,A5*{7ˌ[4uK˅9.vDq!f$UAۊ.`gwx8^Tb;;;F+kxY%p|pwĦ95&=7.HQa9o #$˻>HhPLL~b:zS6 [sm0 KcqZhx{]ηthU 2_}.GS|1A|o.Y2=2sǽ}jB-mg tɾKj3]~| 9e_ {?KCOw|tv"Y1)&Brx+)$ 9qr&q{)ԯ-Tw m.B0=x,&UtyJ8pO5|H]W9ϟ۱nT*qkY76RDOYXZfjNuZ~^W+'ɣ r9EB'?Ej C늫>݀A>oP+j]ZZI10rH皬AIϽjJ+ IjNRrqni}K&%a/=A· 9*i7kvMC+<ONpBOҩ29!wsTK6G_CW单9 B*k;&88J kF{מOT|s)Yu-~[+L Jsks6PmP_[^P99u?i9wrFzԱ1C"m=37ҡn/4mЎNxCwpÉI=+4ϓ}ēs[K n有FyԍX1cݼ溝$yB[OqJx s&ٲh0ϭgR=طgbUiָ?j4 TM]G,A$qsW,ImųgѦ׺R7|x8I6ulzP7Sz;F0dʐ3[.)Vem9J?Xn:dߝsh#i=UWcp*H-XUFcQ>Rq'&Xs'UCΥ3SPc(j8mpXUp]mdm>J!n ܪ$o A~v#ӎ ZLE+dɓpSF1©K'yץq8rGy)I-X҆A*[[̋k9RYlc#=C"ſ ֞!Үuu'6"ۆEǧk*ͼ8*s `a:xi6zVbnܼp<#W".݇*ޗϤ _2ƍ/-1 LqY64&Uct\dí]Ե!{ei [ZX?B)[s͌Tz p p2}H$Lp8\K$Am,Qsc~?Ҫ*EvcGdUMB{ xXG\ְ3m,?t2x՛'X^J\D6(UGaVSL]Nj<)m&F;ziRݝYKڝoxsx{XȈ@ܑZp;c28u"M?T0qֹB&z#Õwv`>ƌoZкFTUҩ[ |Yc=3C;bQ&XsD*1*VRNE䟛j{+1bslis"^J1&'"umy 6vnbʖ]JD$Ud=%XTڥWd9#;+G.?r=Ghz\_hu -c$jڛ{2+eЃ ʮR ո-ʍII⺛zdm:lS洭c1'5 [0IS*XIxF [#bg\5z<@Wօ$e ]c<0 z}ku2"Fynds(ݻjW4=#[Vj്N_klh>XIJ,gO>s%O[䉏8pq}1ӗs4awhwp.O\{I~>4[o4W N3A'tһ gw~ eu.>= x|()H(E!Hp'=p*Fo 2`"ګ=kA,i|2MDI'95<&$o_Z5DZ!gsc&}}0u>ιGp9ʏ`&n6Fd5ٕ%xYN^M<0OQZ=Y eɰ92ݥ#r&]j1egqVثhRbˍwR0:t"ԒF8V6FqȪYbixb xWvG/#D~2@/-SnZ + B8ϭWXr4rΉ0~]IUկp9#qUCM$0搦 g[[s,h;{IbՍi 1VqeB=zU}.-A8 gfg.+ 6ΈJȾX]l<[OSkLeY`3Uarrd M vw'#JŋGh"}T0woqSlE6ΠGrNwEEi sȭAeyeO7CK4dy"2ANlh+h=ϧnghLeH6~`T+gձڲV;;wXmˮ{~5m8(Fw8&XndC |ܜ;Dܮs]/WsHE'1vH"C q]_t略<`*u<ǽ8=.uEڴK7٦X f,3vgI഍"@Ko䑌ff玿ʺi[5?JnǖNyE255o.YX0A'>Xοg1PۃJI6@> 2g$޵m \ cymٙdQjEb;b!沙䓌TTut2Br)TmaX#{g~[Kk?:I#dV冊V7VfSm1g"cا.qtֺe$oRH KM/ O٣h 9)4& im[$-#dIu-qvʣ@7.&e9>[SWm5Qkr]; Mf| Ⱄu6tePC9,J H9E*G<B3dqqJNT>\`ƕƩ_ίO$ d'jP*z.);蔻|YXyg}9AR-$񭥤~ak%lB?F㮋q:ᒜuf5bYS )2~H eZCH#=3~> @]:|NNqںC9pҠxس(Eh%HIҒkW[c3#lo.i{,s8y\ZәHCFlzwI+ml6ivDдdrkY흗`>ϺC'>tOk\KaL?unqSgM&&0zz*gTr$+^79R:3]6ݖ9[z"yj8V܆i T0iA$\@[m6@wnMUb %U]@=K.b 廜A"3>Vs՛60%FXYѕPO[m4/9nszjtI˒20=Eafы+s!aH 4ˋ=BSnK@Lu+Ys#I{/pnUyA@I+/r%֬60 *l*8Iabry TF9}+}4hҙ6 GZЊd 6TY̭:1֥i+)fӗxޕ3H9($b 9&` zԵdM\c8f{ '"cff9q2#:F~t l@ *’ 8FJHM=NeIm۞9cu2@~dҞ/ݳ sU 0<`52O`H7[֯TVv1gvOֵHeA=t'nfC&ҊIn1Ի#j~a5nFqUdBǜI3Ԍ +FWmWxp@8?Zʺk&s1<5y \ayҡAqEd =s9i=Ս݋w0H,MV_F+=r?*;98jr=)=2zA#?XBq5z;c-ǾvvOXxҴWxN:Z<dȓ]Ŷ >gܧ$Wodʍ`}?U_0U= GqDp9'4քrkRk"dVU=uUu+NXg}EZf ,2 $&˷Tx<$u,8\8hܧ>5+JSOQGwFxK}7$*A QMeISZ~[I!H_=rնI_RM#2KNXچpsTjKdoj!h6/_S|6 _%ջ^KX8omWgSI f&Q| G@YzziZ S)s=.~ZnS!ԩ"x95TQ85exb(mBhiwU0f'X$+n[}Ћ+h+?K*\R gI q^*1|0Îrv<&7>QR:.t+a`8#0+N䴊8w(V,sکs;1u+r zC,9UesSIpɏzʂME"-:B( 1ը,=1K C/Kp4۱*~dv]N{ #rHVV<)+qQ&wDɨ!_(c(OnZF k%|{ F81W_KVVa\qJ!{\O] <82]|C1ߟY ᠼF?XPs*"d*qtg # ^:w20q>(g$sjd^:sG56>+|Úupg P~#XV^MZ9) ұ;=3[&[+D>%56z90v81WxM ^avy5 8Ч&GG4+)W-sg̅3،䚍#;2qpqj V53Ae0Qy;%H>40n)sN3eQ 683$ު; :6@V 1 1޺9 t9ʻA'wS}+M3JC}M\/>pv]"crޠ5tI30y^g6zL-ٛE6xYf?*ÈD|[lhܦ9A&[S𧁮[{o<|3Tv6#6=>\|5|'02_/|}1 vaOq^T]VCe^b]= tMMlbxطORflr="]ۛҦL&?Dg** ڪ<47!dQp_\]tw2Gb+ef M$$`Yn歉129`%dmgy6$㱬 j1$ }~cϕ#ijKFb}N6Y,x8h k}Agxq /[mT]K=*JѤp)&Mչ`?7HVs*KxrBO(>gyM21i8h(WwEbyVޯ@m>["sa%5ڹe!iIw OkB9RaCS}YxPU<{y?)`ԹFoo5ť3-n:cؠKJ6GSvxs֯ꗷZ(v` ~aMq#09u5V1b@*ut7bsN 7yȭ˃V,sڂ' þx6d搟=ihuq{]RD c%A9lwKyxz"y#^84 9#BTƹ!Ks돥YA 6ABJ3dv$r =}{ѯ{FsAVnYX󨵒HX.;zҽкI?9#<Sx"Y$8OBqCZ I}bɫ_x8t?e̤G>1dFd6RvXlǧJ |0dlƽG웠_Eou^IxCm _R|6! ca*H$㩮iW^RY 1mcã?xGzdFg89ƒ̀A&L9 uDYe(8Kqu0Dx9$?wF2p.sֲv49d !S$CF{0^|@`,`c^Wt?^6q[z ??%ŌK/$JZjm:[>2OlPfyyW>t HPqz'> jʅWVġ`0gՕ|L[+8;WoLõOc`i-WyɮTh ӝQ?{ ٻajaүnzWʟ|M/HgȂ]D/uRv r};W;TtD֢⅔'~Ć.8lCsVE"qr@[IbТaKfY X3XT*gi2nytJĤ]f߈7>8!ETJIdr=XYLd=knTH:89: +]29Ϧ*y ʐ}V1e7vҡ*0cU6i2éW`O̙Z[@3ZII,K eǦ}*3o{c5d 顷*Y/KQF޽h`UN2J1^Wi[3(ӼSI#F3g޾ml''v5yrgsB-y Чhϣ.M[Kb/'㵧& H5W[#xPAڰ甝RWigly"[9N U.y1Pԯc* ޻.XSv(_1@:={vh~&eP;79)x4?NsOSPA*N{J 76&+(XKQA>T)rdv0Ā jʒ3?{bjzPy܎sSyt/;qJS`ݝϥCqxd$u;{ю7-p7y!t;aMB6 s^6|Ox\KMɈ2Eă{G5?_zlZmpRͻ>GLevc+-8BbE`?pIjԿ_Ѵ{.B'O`Mdz |Oܒу\M tZ뤚We8>ԐǷqǸ8RQOqKr8|*zs] ŷ,GBQP6< snBΜߊax\F c dVyhĞA72Kmn}w:Y:W+adE}wn#n?miBsy:sU/ڏVHgrinM.NV# 1ſ_`I/B4g%+CXyn$$+/$ًOVMU&iyŬcDG"Zu(ȀtAjӓhtbLGV"y;>Ip#cIEJPEfmy֕\x>B'+w(lO_sRc24ILHvDO#vإ 1>fCeEV0 犒;2bQ|\w:բ; _ȉ@^-;d[N+*um[ʍ 3W,6gsC~ &_VewGA׆]Xb$ ;PQ2x <ץǪk7 K?;e$,C3ty?/e4ֺܑ4Bi4#ܒJ'렲1 $U9Q}ȍ  m+P"B5 eO%R?+<H<(q =Q+29E l w>yZw-nTGdTD3`)r*YJ@gAp8,_N& zW 2,gy, O^1 Myl 'W&>\z8rLꮧ8]?EX[qk06 dzVz] .A!q4ШNͪ@SgڢkYrK繑K6 |cEyr(V 3ڲ^4'T`Uf.CyVDz&-T8݂rrE}?Vy#M1K_=&!9c};,ceU L~WNi myOqLM|q[liG`2UB. Ky'SȌp9kLJyGߎ&ݴ]RWaN\KZHs$gies@)1^2 $UXwV*>rx^~#-%@mn73{콦xVb aA ,I[\xBBWX: IF6`07zW1XҎxғI O)PWAsv+Ců_A"M̌n|_Ɵ\';d$BEsJ2oC9^ZHnеۤX\+yFuU[~Xeŷ*Y˩#t]Ňb-wό͖i%sVaWy:VW:ՒSO-d+MzC`?*6|d|Bd{;qg I> &#T;!^xhrPDc9U~)_B! B7W=SWBb?J?h*kHѱ8*Dee 3VVbofJ+?]Ʉt&ܦY?AR q>A9`ڦb`:v*-$*~5Dڙ'No4F*!bif< 8dju$~4GTWisKqw ŠzOAXK T^:bԝ╔BGLUD.|xP˨ܵĀ/ ?°;< ; Üdb@u5ޕrEӮ&#>PیgΫ49rOqײIbىW mACD}ŲG&XN95xŚQrȌo;-jl*2[8F-XaW3}:իcV<[šZuG)ḩq GR ų3fOCK8犯4JpMK$砍kt|1ɩK6̵$cJ7[_>c3._B~X6_َ}~eEK8?ZCtmڼz=kd;aܨ '}mG8_+Ϝ[WmLwc ]򛛧HAWhZ=œI+kN7W%ͳWUic ]A#=)[f܅]ji@ciژ`[$p&LJ(SyNz,˳v〣S3+oGihi`ڮwu,g6 ekZKo;-3ի ݜÙ$l_.I'ڧbϸE@=Dx|֎]i{2 Ahd>=sȻ=N3BN:gfٲ9p뚆`|NgbpHW^*2r;c|>&2*玝 QLC$ڐ|@7J{\qiyDGih1f}NISk+A5d,q62&Iǵh~Q.ֶM䒨Np΂O,˻^az |,~a-ӏjoMQFb zPP9ҬeQ_QOMĒN8+SwDFa4٥I<)\z)XsENIt$~|$gaEpл*kniOW,Jȡ$ejjiu;Քަ8~ 2=*qw"Hhqd?o>nן*_tu}=>i>AT_Ѳ!޸WJnG٣;L)ꤴ>վcPgc޺o'[Cquça~Нbr`Hk'[#$沞%Gd奏_߃ZV[7吀N@ gt\'5O NVyQ;;cIa5JqCoj1 p;{UkL?(J߁%?t5?7z8%;'9jؼ֓\ R~X $Z^vܩpGABrqT_^mD8$zҪ ,pHi㳽\ʯ+IhFCygHrvg֤F` !%RJs==ǐ7c8Ѫ4'$.1ת-nyTP. c/~ϨX"|d@IB-*rg pzJVfh#d}5X$ kOٓß|qi:,~fz*sO46[ TjZƞgԭQ-dc~CHϔ2@o"w hAsO}fR] 92~\ ,bB@0q ՂpOZ3ޢ:hlnhT;N2*2UT?ΞŁi/<kCԒYCeFI8xnepC5oAqEa,̐roOH2{OAnt>U) 9D8a[~>>Ñj/w[#V>='=:K:n+7,Xp:QC㈵:kXs+J攦 (KEux?eq:r=`#XNk=^ݐURyEs/\mtv؇[OPe&AcZ"4nXlM~e,u_ 6͍x4/_[k*^'PC޸q51[Bv:%@e{}+R+WX#uc# x% w 50oyQff9*w*֊BlJzz<ݙ՘u+E4&UiZ `ub9 /4/=("UIa`=>WRukS~|7<=m+ 7MRʟ Gk? vt=By5;2V 3Wu> ᥃rq۟EY|I㑋2!850C*H8ߝ+-[61NW#NM|SCwOm,GQ袻pU+a+Y r1w@1]aiơ@z(UY.}\3C n(5-MV ދiCqo5T5-$R1cފ)U\C;jl@k.vb8zQJ|o֊*^Z[ĠrTsҾEΖv9*(J&#Kx 5_{~(NA'LrQ\59O- :.Wq_l)e *EH|$Npt#Ed[=HV<`m NsEHWpdK`~wEyتʢ=̺*9> ܷv5ɑD39c< k^thucqc30zg(2Q4>*a)N7g_ ~6[g-ǡtZx!e%*()՜qaNyϊ/PVDۻWo\}od+zUeNQ9U5M}w(cSHfw.lJ(UZ&%֘cq{TcMCoQZGVGqY8Ix7 dUW"NCHvMQk(ooʉ׶pqXpY,3"fk(m?(#Gtrk yU92n*4Йc/"9\4QY2Duv$dc6K${ECRXgr={Q[f3,9cp('6 ̕wɞNyv'pg#;QE+&ҭ5@O`{ק|#iS9#V3WZ=\E H#Zf%A89D*cfotoxx-20.08/images/rename.jpg000066400000000000000000000437661362435004500163120ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0230Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 186 428 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?*PfobIg^xKy#f\V^IkK&D#zW>75:sCl}gh t=W>O 1JVmyYjڄ>d d}j)udK"8[ƸSYk> xPQ#Nc.23$|OozۍfK-򛵋cep0ybbO<&)b誫K~cy~&Xy;魵xdx^D43D=umƳS"k0FؒpHsV,ke"6a;d⧌[Ö^GSx.USTKd``!{t\C˞mvK*5x\Vҿݏ^zkoRkm63--(khOpHS7^sci_| cj>5m7NcZl^6p3N{[2o 7ݭ垷[GTńJr'L탑Ru14}+_&_~(? -7zj6Z儲}kYBdRAi/.i^!tۋ-k $"MF_9ܬZ0徇׿i_| >ҿݏƫGFԭt]GC: :?ZJ$YRHă`m~^3Y/ oŗ!M 2$XMwpGV%mk>Jv?Q|`U>fϥJW&X ;n8p/QM'16zuG>[`@O*r9[cͷ\(Jv?W: Juɤqҿݏz>G0XJv?Q_z9Wcbޏ6ҿݏ}+~}ގ`n Qv8*J}Yk]s֎`%/n+KRcE1r7)\{^]|Ե.lIEoJcnGحȮz*X֊v;#'<4[8xR:y2M!1o,,EC'j~o<#Eb"AR4H櫈y]l筢dMo<#Etp75wGijXI.X2fo<#E_}Ý-~K^;izݸEE1#*TOxZO5ej2u׏݅YO[lOحȣV?Qsʮ~x+Ү;|?pV劕"#޿(濉?g~-j}Z+o$d60\מMzحȣV?Q_OGGL一~^DDP|C[R׼7q&mbuߍgzحȣV?S}Ýv8+B 6hEw5އ(wqdڏx'AŢrME$D|np@<һo[GحȤt6.Ey7mf)j#;Gr#$1ڬ38'|?a?S>қL:d?QJm1^+({6glO[GحȣٰF_=gj}xG|>o<#E͇:2G=SV?Q+(l9їz>Zb"[GaΌ{z}xG|=teޏ{֧حȣV?Qs/}޵>o<#Eb"fÝl+({6glO[GحȣٰF_=gj}xG|>o<#E͇:2G=SV?Q+(l9їz=kSV?Q+(os`?_Rlh|F7쥹u BZXizd>m؀ If!@$W[^WJ֬G4#]a{ZC[S$! B[?5 lm5c=Ä(ՙُ $߶o]Ckj~7t!0X\jPyl;PNxnƚtgKXhZ:ךAv8U M$yB_= !F!B#7V,z֗D腒` /YIzy ;a 4B\|R4e.&׈@mͺuŀ+~EQTzu [F=%??l?!ƽj@y/'/ ;a 5TPBo^Ey/'/ ;a 5TPBo^Ey/'/ ;a 5TPBo^Ey/'/ ;a 5TPBo^Ey/'/ ;a 5TPBo^Ey/'/ ;a 5TPB[SwOmvcGW8kXO,M4gPQE5sXW_/,EW.nd ]:$+&#blW4Q?M'du_Qg|E[wҏqe}.v]ɹV]yֵ o x9]nM3^֯/lY,&$kQu$Ida ' 1[o<;V5Kxƞ".t/Q_Z=pB++qּwW};nU:kZ>\jZu JiayT8Y׿iN^Q\w>kjėog &s^ܬv2]b("4HRW;(=&H՞V\j޶p'V24{ě|¾w|Q[𭬼AZ-Nᝯ^#t16w-ʇ6ٯfo x[g5a\[X մ3hD0C`Ih~ߍ6?ҾQ |W .i8Ӯ4c}oqs$m[.:2~$ӚRnar#!?ӽjK_߉DMTv>@&?~hK[?V𵗄<o6m(,b9S˛nc; >\Ba-~èG ,CR7My [sg5$%7Q /綍~%nik2C<+źGoZQu+\53ubWx`t#acy'gZ.N;ògYy-w?yx ,Yʱҫ*;79^ /綍~%K_߉[_A$ݭXk_uOk*4uhU 熯EiyD߇wKa!;E*/bBO쫝OJvPv{ Sel1 /綍~%K_߉+.|]SO/ji>I-՜um-$ucbJUzy՞iдVI3$6"w.@~ORXɫĞ&']h%ۻ\$>(CO)]}`R\$%7Q /綍~%Q\$%7Q /綍~%Q\ۤ9A/?_9@C/HywW ?(?!GPyEpG(?_9@C/HywW ?(?!GPyEpG(?_9@y? GERC/ևV|IMdV+GHbu9gV$cӢ((((((D 񖗧j:-g[K+yjf, ĿP1ۭ7bݤw,oeH $sT~(E¿rdX>y$^7pF~q\w>|BhfLttM.(tflľByqe"տ&Nze΍ v*Jyo!.F Hbk^]kZf~t? ]-6O"+V60!DC( ך|Z3A5 b _I1\Qh<eDEfO7v⇆ekURQJ$mCNDdWfgV Y؜[Gio]? z~|IAxoF:?k&dѼ9,En4_h6+6Pbk Y&d89K8#%X2Ducv>g+wz^wZZ^K n#[BC+!g] ѭm_OgwI6VU|`?@z//֦1nE7qp`|[T3oji:ׇtXH]mʩ YT@;x-? !XKMOJK MX[]vX- Ʋ"dn9IQipiY}N;Җ=7-Log-xjh'PE~ by!`A )PFOcRk>xgV4SLƝvlo_~-dXв w+e>(\|#_5O&;5]krWqu/4[X[k^5|D#roVbu߰eo"`j?.=/| AV] Eb:[Y 8XUs6y};s[z|KSDX]_m4nu?"2ed` :| >ľ)+=w[f?huNARzcu믇 K-lICr=˼16f]_?! s:f^R6lqȈIQTӊSHu==2/hVx;|0Si_~x~x:Ojwh_f്XMnD,EE, fڦxJ}wy}TTOn9}č܃#?~%|FҼ7k:o[mA4ԞK-k Y6D^%'h>m3Wo}|8ZhΌ!$2SBIvU[Sݟx?ZMAwx-x#ް ' J/A]rx2IiO-i+wI͌98+Z :!!9lq^]N]]Yx3i' "3o<.ON#]SYDHwysH[j+GiڒZ\{n1 Sr.z2A)5{zF|ys׾rYxT_7lu3eksJmΫC Pf>ĽZ~:jiuMVKY8g'w&rѮ6a_Gj|Zڏ{5}GqePWrgC|gO Aؽ6AnǩD+':mb<<ĽvKO Ut4۳wq 2,A $M"&Y2j_c+mkS$nO.sh68cayA]Kui5gI5o krHGWYbW"RBrk<xsR,tbXMh0A Sڮ_bRiy/ i21YAtl=N;xmĿCR0Y&_iS\[,P\Mm(C,+Lc{w1$?~JLúlz|PKj#xLf)Y@d1EyZ >|946j5HG,&u >&tKag{[G' ĉm,BYG䁥2 ۑ^BT8`9Gs{=Q|Dhׯ'vpȲgz%i,Ԓ"4\'1LSӎWVvO?4k0u拪xsZƣZZ S,iDxR+bY80xc Yxl[yUnx۷? tq<3m$Vv=8fspJV>c|txkW/5m"5U,9N4q|H#IcM͵ѧ, %mrDg#Rj?>[_ << nolojE:}[He>J%Dc!•bZr/OrZ¶٧AɒCs󷩮irQEQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@\m>ޙQg[_:xGuOʀ,QU~Tg[_:xGuOʀ,QU~Tg[_:xGuOʀ,QU~Tg[_:xGuOʀ,QU~Tg[_:xGuOʀ,QU~Tg[_:xGuOʀ,QU~Tg[_:xGuOʀ,QU~Tg[_:xGuOʀ,QU~Tg[_:xQKmɌ3@hi߶э/Т~F4chB6(d6SЯ Os04~Ɨom} +?_4cK?ƀ4(i߶՛{kxϙ 1{((((((((((((((((((7ka2jsY[\AxUh$tVy׍O/ M/w6'3c.;E "1Ts,Ӓ֯S3/>5MPh -22sn!l(q[:_?sXe#KY]C ŴPҴ.L6hN&XB߼ޱ$:gc ]jZxO^Vԯ]"ktHZ%Hؒ>'d_/Mk^0!'u|icqe-g[My O"F&e^pF{k_?A RϤY(%p;Ѻy[I@ۜ/|0&S>}-l"V RU *$XHky\Ѣ=u#q׃Iԡu0\NbCsnd@A+*pMfZu = Z񯅼Kmm xN÷63KnJ bI+m tcꚄ~dZn̳]bdFEk|uDG|z]'ⷌ5 oU|'ayI㿺=%ɖ$X,?.</b.|Sj9;ۀ$4j6C? O+?ǟ^%z$0\]-qnxHUID1 }UQ" 䲸'nnX'Wt 7^0w->;v\ */:k+_V^-wLƥiWP<x徎%|1sO \_Nx ͆>&/F[q(?$cs]_.xV(0rvՍx߅^"|7C# Υ>os 㷎Υ  z⛅U-Cm|20OK!騢EQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@TtQﴽ>EWfRh+/>|;FG9VNGG>yj>nvyPw#7ff9,t 't='SxF4Oa AFW=(-EpDEpăjGUU+h=ދzkdv)&(D|=B?? / (D|=B?? / (D|=B?ӎ("4$R8*pҊ(QEfotoxx-20.08/images/resize.jpg000066400000000000000000000443341362435004500163340ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 302 367 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?R!oo`>ާLQADizhLQ@>7F$LGf}3Wizj:(Qq1,ѣ3jѯ?b QFG\e|, b]Kg̩e@>xk[ xGM `OC_V ɀ##խINgf}qmZ\6 &YRP Rxl8|9Σ3ƚ&;nP1iV:ǾX%4#dL}^)ѾljFM}_OC*UAEDvqIkac=_4iW}ᇌ7/7tU'f}>7lP&$+O*Ā܅QHu ^xm`7WlbBS#&F׌<㡠F_f}>7u|/Ҭ[h\iz:kKee}p$tK$8۾(LGf}(6Tt-L3_YX\<ѤjE䐂bH7wk Ciwx4@?}tk=*5NNdҖ[>b ;>\guֲIu$Qjxwv_!'Ū. FMZ=F?)`bcUQrH]3j~ծgZWן &p§G \E+ |S#"T7+~Q_BoWG*ЛQp?(?T7+??M (W*ЛQ &p\k-ZL;;ˋD:+ %X/r/Ka,z֡ Z:H v3{ W*ЛQ &p\G<^9$K5X@g9q]7^$RCج(Ķײe]0 ;O|???S#"(Ox=N}EMPֵ >{[iXfdi$oT7+??M )\lbaeO",gޙV-w[;mCy:帎UEYd:_BoWG*ЛS> |EI{}'Oп NL}NaN Mp֞*ִ+]^=7.O]T7+??M )^e &p§G \E;E~§G \EBoWE |S#"T7+~Q_BoWG*ЛQp?+O_'[AΛzJ/?S3P@JO|???S#"!«iMS1U-Jtm |S#"UQREPEP^5x#If?W7V$VE@ ^\O~A/C_>׻OL>tA[ T~ghӪxľkn nmamG&h$ڸb ڗ5]z /Rm+L[ qLV#3+ rK =%KRD!vm*UIfּK૭^5[ ]@ms_9Ln t_?Š(PQEQEQEQEQER+k4]şxN]\um`kE\5IU>O4:Ai|뛆¦ Ԟ".-&MOX76\ ?e9/→ͦ[izoB}s*vdVRUW?zψ6nb]I $GH?||!H1TTY4KO~nsÁJ; <#ĺuφiom[W O1"B60y4-~64S٧s28䪓ҳ{R? K:Cr7̾5>xOM:^^YMI}4%Fc} I׿-O?GAm;;_?Z7;&^S%M[Y \ZId+0l*0P*$*խּqioyE쮋*%%ݰHmy|+믟}[Nl~AkR]Z"8n-8;x6Ƕ+~7|&t_ 7z&"h] .:ግ>R.VG$]gV s-lMޗ'ЯK $pPN'ơX${{XahU%y[K Wқ/A.eqh>"J(oDy-ݐURT@`GRhX׿-O?GAm;2s¿~!i }fK1~]?l{Hfq";.xSs/ڇ-+W" 4+YjOkե̳-9RG`>G[~)?Ԗ+_Am;? i-ך̒OO7±]h~L~2\I ko1̐0p @ gvVOO.];Ytm":u1 >E/g35GN_I~! Z@e[A@c2r;K+;'O@^/ &} K?j6 Fz q˾!xCžNJ>$ ͧyIgxK,(S€4>>AK,?Z~d>C–>)b5KPSOt !b+3d b5"m[Wׇ&MkTRaPw:YUA;v{ol})xjzޟc[֍r-/%JWY(H_kwX񖳣k^ ]OYhךėehtڲɟ+̟2sMy j^ |u./2ZiTIfHE ^/r{,?Z~o{[\[)Up ICO־L_-_OXhh#ĶV~$]6!g'DK/rd?G|4խ7N$LϾhKK~(w޿QEQE<)fZnoGwлvNα#9UH8\'.=𷌅teCtb2.\fV6ៅAtUFpwBDS,=^2olH(w=1[CŚ+k&6.q<g}|EÆF11Ohdd.rB8$>w uKZ=s [}=4cef#$%yq[⽊YGp9nyp-AWwMpILkg}nA = j_X.QؕQ!;ezzKk[momC$ @Ҹ?4}'ľ4_)ۋ&)!(؜WutHu$m&[i neRcKyaweOTS{|A!'V!3Ki8 m.ܟJu [dddžyټOxVɭ7{B2"cӫ]Wռ0i0^\FEl$2̪K.I _wlՉGTfn{6! )5pxC÷PG4:uȡrH SZ TvO FFsh_GRܴ'ڶ8#iD 9$9?tU}?K[5V{^k F ,TNO?_'Х]Γkɵ ZST]c &Ф?~Flt/VY]f7 LbM+`U{/.|/Keyy#2Mɪ+Q@#NNaKVL]Kn>  >'',1j-###wv0 ė jcURTL6ڗ6ڗ^kti|P|YtW|K}WPmeM6}N_L20Y}%_ ycv,YBrn}~{AK{AKom(_E/AsÖpj:vku w6ʡD*bXǦX[ZDXk9$z4Y!w/i F=#r֤SN7¯FAi]\v^[]L ymnCmcxWw;wDl<[ݽT_]\=Z2X#F@۹ 6ڗ6ڗio+~ /M_o8٢~*M4xMGLmw[[x j:^5Z":|r<$zgˑW\]g\"6dkeDY$}Iu-\Iw?g3f}ȠBs/=n𞉦Uׯb4mRL@XZ_"079V,PNQWYG}iDE <ᚿw\ Wv%̮=;F-rAi#AC+# hHAK~N}["ԯdlcRyJp;[$ORTӡK 0םgj7@)d6jֵSJL,f?o[/?D=voXdѹF*F8=QZ:%ZCo M&,p6v5~϶W{7:֧"q DVV  Ѓ]of|izLꥶ):Ƈ5qڞY\__[FNuEb3 kǟ<;X\mB4hMk\\@dHі59,F=*ߊ]axcXӬ{9A Td& xbc8 ^keΟs4l`2̒6'Iי5c / ΑOQ`p2$VR4w>U<^?N]tQsnT J$@Y~eyſt-#°PqE2xN~m,4}2CO.YP/oU$ݵfjMj1ei/RqVmW7znkilITڱ%VOY Ki*" t|Kok0x [oC3\ǵcUb22b:m w{?|4<e;ZMݼw1]#B2I=E7M \m馾DB\|#hOaL4YZ<(9bUٴ񈭮5Oo=ƶjȖV`M: d-)%{O7m} aR~v~,֠YCF>imƒ`q> h(~_Yil] 'Y!p9 7a^T(((((=k&:}BMir!x `dxZOtӵO-7^IL82vTp2 ZuMTIwp|sD:Xw4sm8o-`PVfm/Ym,#ҧרąMu8zV~YrS4+..I!:} 2Ė7+oWŸ Qp) wm1L7 N5K^|;CVJKkkub8aW,IyϕnǮ: iVz$ŪOm%v͝eV|Q@J2 ⹌I ,gP n]Y*37\*$pc@@)k|_ 2{E {Z^WX`ns@z ]k:m5è_Hdw3NQ#w4F z׼IxfYuK+( |ɤmIwúE4rg>` &8RpI5 5=FrGfdI-&_Cgݭf|WFG; wWd ,I~P3ҵ. I 珥Ewi34\n*(%#+?JY^E<7#a`AZڶ(yYC+ k{)LܨA]ӵkmUCIch}2!$̇rTVas%ˮB2}$ԢY]C) draEPEPY.cZUy#Py籎I\囦h\{>tU &2vZ_8xkZDP|,(P%bB8!\b:UՒ@m.<.U PA#ָ?~~ԼxsV mVHF&ƍsHxp 9cɬi -Zf&xF=̱B)Xg1}*Lt-b=S(N(rк\ dhhzNCIAiT𶖚u;,8ܭtϔw]:~~#cvkkm>1ڣ̏3ۆ搗zдVhKWwp?՟#?$:mݖK%[3˩'bZ׼,݆&~ Fh:%fj3Wu 6U0]³Ŝaޛ`|w#^܁򚼃e]+Xf==bKKGfD`qש VD ji"aooIDۍkyr9x!$ls-wG-jj~#I[|±n9;p2dn7<R=K@fDER0@eP@##jo毎Va@5 ܎Vt$x'ONjДG-K?rE},6aAmgЌW‹ o[h,iZvmj2nH8" =.簟u3EiEԄ#rG9cZA}k5)=c)21Ih/%8ρڬZ e[Dz'kpH= w g1ZPġR4@*jr擗r`b(Š(Uuk}.)me[[-NpqW^]F8+ 5m U쩙-nn-q Ľ(ZZݕ}3E|/־&|/qM-|Ss&.Tt6ϬÞk+Zm /8IE麭,)c1FwsE?:GTW|nᏂNihAu" $AFfA\ßv/l|P'𞐖k2#LCv<WX>^w6jko%I4:=xDzj/ =b]km}-r Տǂ]㽞]z6 T>gOG$o1_?>D=jP#'u{;M^5Sd70ByeTzWy2y%DNۻgwEy/-[~(u]ZOHO-m#DY.ğnK|awGe}q(vER+F^j7_NVh2H!6Z]ؔ<\`?^n<[OwxWt/p20A',Kw7moKgQ^y?Y}WWM;I7Ph-+$ qHg_3< w«߇sf-/6eE܌\yG_|(Ľ:Sjx_~E$HFԬN`\g-:<<]5xm-(HS][hu~]iec |g+DNX I6TAqb~oMsOM\Cc$iwv+!Ŀ*'**q^??ߣNekVe^&]WZ]w}E:[p,-1 Rκ=嶳{]u9/B0]ZF#x"P*rF$WjFA>__py"Lg']ZW anK@»Vm h5KPϭ4jF9G4!{k6xɒ55%Nr۽Y"-EBPU- ]mRu~ڃ}nѦh_ڃ}nѣPϭ4]jFA>_*[hu~]jFA>_)(8##8=EQEQEQEQEQEQEQEQEQE-ehh,QFbH {hW;qIeRli]7@-r 4Kڗ$Q_{A׼)~Ο t]M~$-ii-™$}xFyl Zw_GIZIuaE_thܙ rN+K{ܾf\7kv/?KI}\؝7{nN>5冧Yj7.O^ē5ڴXܬ^Wٌc AUO~*w~"t95+صo^xwx1H{QUI%f>O?Ԧk>mq-6*6K7FN;d}kZ\~"_,0A<϶*'GL0A'Nsx[eOE[(w_7&L߼i@7SAOvOkv/?GN&jx_ؾ-K;pxX]Bi\lq5<5cy<;#7:%ޙosu4WOuR'$veG6>u se{ޡeeq V2D $hՄFGq'|pY<-'l׳x$ [PEuH b^'w_tg[&VOè7 _0mdbBE ^??vkϪtAEPEPEPEPEPEPEPEPEPY&/"\H̪JTPƴ*zƘv:7 `Jtm| nBm}zgvP`2y>ZT%x4xn&iT{]ybd[4 XiZht8}3]/43t3-󟛎yVYG@N9=믢8}TYO쥷 607 379 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?R%mcQ }ϽOF>7F}oѣ3WGEIF=_5'f}>7tPizhLQ@}oѥ7?FGSoao W<;|`| ė>4<qe7.,"H5u:QͽU؜U5WoɺE~voѣ3W~co|h2ZŝΑ-;-hEvsb3p >kmε|"o|[g [t)`WkKISw^ֶ˿X3W9&gcI$W~ ~Ч.Vԡᕭm&1X65%м+sº _k 5犒Cw~Z~U2g9-;>A|ѫX-ՅX-U2SF|!ZN\^&ٵo&j1Mm/B=k⺔y_*&IF=_5B$LGf}}Bh$g<N>grG gf}>7֦k MLH_jvׄczgt5@>7FU{ 2YMilΉzB4w;⎓iM'J/.45s}B "8873Wj|BKN5}3AľS,t9783q㏗\xC9I]pMO7SI&U}>%K{kt˕au? FxF ^3}ڣNU.8_/ >N9.wOG~Q__')¯x;#p?U/ߔz?WO~S67:e7vw]B(áV*֗S6}h _˜h__')¯x;#Ӹ70׵+KoQGxqc)ƾ"W^yWl̙?,=~¯x;# E}J~+? izơ?ԴxOƒZ֗cqeg_Yf9IYAk'~D?U/ߔz.J+kYI]Σ y__')¯x;#p?U/ߔz?WO~STW*=_')\*+w~D?U/ߔz. E}G*= S#g;Id3=GҿkWO~S_>wOG+Q%h/3 `{\4%;%VwyڊAY5 E}Vt+B?`If@vPq)d#jWVNR{fyZˊ֬o5/їTdQEQEQEQEQEQEQEQEQEQEQEQEQEQE_EkVO?S[٩וjɶR+_qZQEQEQEQEQEQEQEQEQEQEQEQEQEQEVO?SY>OE(?Zm#f^V2⵫&FKekPEPEPEPEPEPEPEPEPEdź',M湫i6b ds\|_U|1A+4VcrcRG)PQ^^Sıx~F7&G%mvHv6dN~,PKZF+??ZwW  w}C>+]&]~lx/I"&qw /u+ǹV&;+| 6ݐOvՒTR4Q\ Q24ZeգTo9T_>-oYr >&`?/3=N(->!x {I/nOH H,!$QV@|!"^P赯--9x6)v+sgp"|]Cj֣,8SZ~/ /VI _87%Ss ރ@q^ ! yѤDcӇj:α;@9~:, rNxuxg.V!w:α;H +;~u^?ӿXfF?:?iF?:?iF?:?iF?:?idCE=kU:r"W9Y?VxCE=k@yZˊ֬o5/ї@Q@RI"ą(p- G*Yb- գކ>-}AcNfXK@A7gxBC .J8VE@Wĝ_MtvO [E*e`8CzS>&3uZLuq8PNXx'ʟڷZ:ebv"aV~xW1ZE& +j׃Ͻ39cRU rC <1v&Bm4v;-b If#9'lQ@Š((((((((|!"^P赭jz/yAր8j>:=#I4m^ThM/(hbw7sɇM-9HWd1g[٩וjg>"Z/If}?-({vQ~u@OLj-e_f)FZUv2=YO-wTP3+Eᡍ#F6ڤY(EQEQEQEQEQEQEQEQEQEQEQEVO?SY>OE(?Zm#f^V2⵫&FKekPEPEPEPEPEPEPEPEPEPEPEPEPEPEPY>OE(?ZֵdCE=k@K=qgn[I(%^l: Eo";௿aXX|'g?mRWE~ƶٿc w3qϻoc?|b~NW?_J,{Qcgw\NĿsf?{(OQ Eo" -hmHY7]j[_.?,?%?ݽ ?(OW]ωA9]i|vg%?]Z.0C Eo"J,{o#u߈U;o?OT.~QcE~Lcc>4S;9lg:STY?J,{Qc5lg2UoC-Z?[,g%?ݽ ?(OW5į3}`oN'[&~TYoJ,{Qc-{f|CZ7c:~rasJ,{Qcc63jtZv+֛Ƌ0%?ݽ ?(OWV$՛/ƴMFfo.}% Eo"J,{c7+j[WmL,QcEy=H~kFķ`Xv'(?\L>7Ddi }PQ`QcEe{K|n֌_3‹QcZ'DRG"Y¬0Tv5v uѬO¨xMWFf%BI'$I=C.%X˻ Es7 >iˡJ"(yǮ$DiWPl K7ŃXi/KeԌf=X3GKkmX$S/YbM"٥Hw,dn5e0ږun-, /"9=x}7⮇K7]-ͮ.\Ah}(G+C%'&N)-]/;7.m&.ԭ&EBE(G<žx*?kυk& f07 Y_J}z~I^z|LYi@,Rն{ <m>4~ͷ6mboެ0VQzj$د5sO䑕6i7,{n|Ųһ|8Fvp]*h|(_LKW^>u~<s{H|FJG>\h4c v<+|P~>0Uޭa'ԼJN8Bl!d$l\歫_W>Kkq߳?ϟ>F3 |0?fȾiiTm'oqJK ;NFO x" [H6(­֐gᠻqlujz4OX,@a®:7xWǛxr ۉ7{KK]bes<Ql]RaoƷNɿՑ\nᏆ7:ƇD)a&F0ϩ%hB`͟8>&^:Ǐ4$j?j໹DG (\~'¸I2o.|:Gug~$|VŇ$v+aA]on}$qI_/z׎4q]h6qi,T }I( "Wv?<'^cGk{,"I!a.Upr]xox#z%kziuFH?y6v5/gN^+_k-6 IsRXÖn❬+zNe)E=*}.M3 sįF k>"JMWo7'Kt d6 a+ GkǹG+[{={~0-6g.o=¤xDLpr24E-mq OصΙEc3[{Te P c ~x;z棤SD5 hnbtcWePX b4c@׾y;adf*lK挻-9Kٯ>xO !iz~;xV;t}YCʒ:ӗ{1jV[ំU-hW+k6+y%A#<޺JFQi^i;wdT-GPʹ.Ii/ISZXg/j K:8^~w|a@&V|j.[= >s}j텶e5ݼWV!H&@z=tGºt^>$3\E mդIA|L'5Vqˢ5ҼtTdܢD/ qJv[|q 9e C4vT`Xk=QT0qWѼ's ũr_#dƔy_9]s+^ r R/.ihC#Okxgĺwoaq@xxc>_΍Aٌh^Bmu;,g~wgqv[Ϧr ,y8I|%>XZ.!pzB(>¸iM"Ji<1UbП@6nd͏?0IS'T񕾏wW1d`-M?6[EooAo5 `(%yhVz=m\Oi7FWBFB,iMcSAѢj]ͧ\%In\\["_5rj]}hO%-e>KVn#Uy[q,x/>El|Y<7mJۤZHCqHyS xKV>\O].aA<(kZm$_oQ-gz/s\X6iek!U|-{w-qOJ׶j` SNvH.&^1Oo4Qb=Aa[:S}k#,RI2 LzweK[Y][p*I*ƒk$^[ T~iz~Zk4ZG4ldv/rI ÎiykQۧf|'_uCKR_ ˲k0gO$LN,2>R[̵Nk SjՍtCyh 9?zD ck 5oYh8Z T0ư/  )PVy_f'G>ٴmmN-$cW($Ԟ&>U%VX/xt_\[2"Fg: {sZ,:τ#K.Py.Hbہ =B_o;u#w5Vۆ8¦tXA&>pq\ߤ?hQ{(4=Cc~ȺַXh؏sm2Km/͸4~noY.jV ERw}ڽ#OZ+5 nlnR:+116OOk wᇉl|[*Rc*y忻1qZ/is|[m7V׌,`%ċ{{yBm좖2hʴcjŇN>CǕpW,[+h|.~):ƨ&oy\A<=q_CQGDm[w>&n\SJ>>>_m,>Y VVQ[JZ&׮xaъȷ-#*;;Fx$Q2yҭ|d|5׋XqpX5̛c1qRH p;V[]zWd'] ӓtR’vf.b7dW4Qgݳ|EgSV~ q`![R EK>\[▊/C3_?$gnh_~+߀X{^6|]Y$_CM}6)y{̀=E}X~n[_lRT.J@~Ȋ0=)_ևɟ֭COϷoXJm}.4I$wk m[{&km8W 7 nhfrKc0p8TR[[/vWxëO2Jt j6v:ψL]yYYYⴅ.-f?!5¯j2wZՊڥ{uPr@eG(`܀裦k;)6BV6#/ sRwyiwwP5;FI;}3K@(Uφ I}0F%2o Dcoz>6x2^SVִŸu8,/-F҇ m1VB;qX_ >]is뱮xĊ]$d$yJ$"9J* ǎnjK>-u_I59|u4_i~3MpQ  RfOh3㾩O3i6l dЁq(5IAj P{+cf :Ɍ _Jg=wEڎTM+21Ő%$mN 'i_looi-ֽ{-v>bY}GAssiң牦w6w53-Ԗ$mB^kOxQ|G>mumzr^t6S WyKWsm;jAdMԲ6Mł;Yʆy;Zh[4ΗM(@Kk!w#iX?gxĺFuOc}.[]k},jU\<95c׵K3^ӯ¹&y|5veۏAmw 5[ _K֬;_Z\Ky8# i(Abf$rq(|U2 sL&Mk{` Fɒ6GLW}|==_+E^dUeP=+ٷCEgk"u(jZr\DDA'%IK}1u׸G4o5-#EvV^eA:$A5`\yޥYA߉ӴoRVڶB#QOYN+-(Ne#|%x(x/Fͼ3.n4vSЧ#ȋ7|_~!x>+'LDIgj`O,*7aIwuOi~o|ZmZ/)2d!Tm95:\ڒGۨ.Y"6bEf3VvVn;hvms]ƽq(5+)[k1ȉ"YRFVxotgyc-JJ/g3+Z"!?4}R[>>kQxvQ{ z IvDvr:u~kV|a&cԥ<ٕl;jdzwl|H[m} _-̗Vw<DcH:wݞy~> [mu o:DZL6F<_>:Mho]/ ~*$I q.HO[Xhkz7Wk]FrAiL>mp+5e c`u}|Eme3A\}9$2C(Z4ؚm5ns:g?krY:wu=}CkŶ3i,<(+Tޣm{.> ڽ= >D?#JK`9|c5~ΚU{okZ]jymhB#͎AӑV5OC.3WX[S9fD11AWV3=8uo-6Zͫڦ6$]+BA̛2X(kkxc#"_P5;?Lզ+{Kq&#ƝZbPS z(EQ@͵I=]+oK`K R8FeVPpkf@Z6x{rhzOj-=VcܥySڗ+ڧ`؋˔ϗ-D1< _~ZGZ{/Ɏm5bP˕Fě85K:φ~2Ӭ7Iz6x|U3A|Rg[ᆻ~kF`u͙Y") Q2K KsЄ/Mĺt/G͚]E+،l'|xOk6- K09y#ͪ`#9qڼw*]jvPx@HVϦGȁ/!Y8=*oo,t:H/uMBHn9D,Ja-vv/k\Vƭci@)7 #=>v sۚ~4oZ޹6cɨ"*0R )<)[Z"%o4,ңd0C$l,;זxoXo\дW׺柦ǧ> %өw_Auo~"%ԼaXGucJH%pI#=Ec6i;vqZƳv6_hE(r 8yW_!Kۍwv>-dk42KaF@76tſ$^뺖%ꐟ=ͣFג|fVÅ۷~Z|E5-U&̫kmbOPi?xm MfBΒiڄR\#)m#W@мGiX}u=ɣ#w>ld5y W񽞗i//ku!Ri2]1( 2_Uch~>4zT R^nswʰj7Veд*{^3|1Do ䷼Ҽb &2LYH4"8@']=?Y|υo yZ} kKybT+!qYA cs"kmou~<[׼2^Ӵ{IuBTAx۷nǶ+>';[uSOV}頵REl28l q^E?k >5h맭kzie-p'jI9<R|sUr &gYo!)<* Ilkz _{~Zs?DxtivyI<&X/峡8?w# +;&Q}M#Tѵ]rB:e|}Qcp/#5x]|)t:RG h fEoY h[$B|6}G8ҖK^o=3uak}k:.]oNH4ɑ͹`qGUk,'D5 ! t,ONn<58!J`lSL0G4<t,&6O|E#O$ } Zvjȷ9e/*Ȭ,X F68kߡ4yoXxKK%tmRL`e%@Cl=q\2](ռQyxnHތ7WB(b`@< ?fouփrvڊ35GZPٳ`:^k}pZ~2Ե`tilL6qb9FAVW piݶdžE}? )oc闓V;{ݚv<yĿ|J4<3_dWm>&6Ц@Q$Fi7vp+OK,!/UߌH޲T$|#f[$}?򸛲~'I뺥FB JOb5cH,|AZewoum )PVGq^7FZ֧K&&8 |E3#-Llr;r1s*;OZm-Z ޙ:Ub|}D ^6}R<{kĿQIC{45=IYw#+˾? &w[b!gpJ6DYx0 xSҴ]?A{}2NyfF;PI$&g7G8_чϓ{'^#B^@A$l#ӛW/lS^}#i$;WO9䂃V޿<_W>ȬOZVwckj6W7kʍ;*`:My}u;6{Kr0@FV y\h]@݂A=5x]-wPj0k7pV1ͧDj+oN/O_A"Uѵ ]WL]^Y̲*䌫 85~#ƶ~wss\_j.k>xF<:ԶLpIXђRw.ҞOi~}ϥh>Sl4ɭ滎#fY,zm}cqݕK<8x區* :l7+-'Z1<%缸-n%hPdrpF9g7Ï xwKoEéViE65-6<K8RomCN%U3RBpgxI,Y;Hu{؞kkDƘșgk\O5Sm.;A퀔8Xۊ|E#jKmmsWYnhs[V0U~szUIrQ5ϣ'xqB5Xx {{ϸ(vm'|ߖ1+EG=F'SĶJ1[Ik**,|FFF_bVOh(((((##|g{Sq^̈(`EQCN۬YcPFݑ9riaV cfc0T"=+>*U:a,fn秵]/.on˶ڊ '$03HΟ{ps8i7Nĕp0 TV_< 鷚|>報Fw$yB69ힹctOִi%ʹ_ kNԵHB1`*tpv Fe*'pX,34 izFviѽNf1$)šH޷ΫwjSܒIfeB3zj) x#T˿ \hqeo $ڞ>w'SH͟sqqYikeY)eBK1+Km9z=dxW'xK4amiEzR{ɮsN-Օg2y,@滪(#ϧܶfŬɥ–J4QƎ΋ 3SjZ}9 N`]~Ў\8R%@$ f)@.noخ'vlYc`Jg>OnN*whx$|vtP}u+aoi%aY{y/J$8>-o&k6Y2 2ʸf9 #xvP'b-ئn~Lx2@z sCչaKt]>8YC11[eO2ze?(,3ܳfw:hh4La30ETԣ2%G̻]l# պC ( ( ( ( (>د>=j3^xBWYWڏնܗ0Tm'ֽJ|k_i6:aqeA:$ b8Jc ^qzOC[w6& \c!]uPּ4|O2ؤ}誕XPw9 H $Pȷ_r{].ζ5BiuAk<0K]I] uVbe*ֽx: L\X`\N ܟWFxc~&~uK]ֿdMh1D&TG ^/ou-2ME(AEǗ+Fx D}߭dkV'|Myx_xusa,XE!q;#-/k٠Giu-NBWQWi5o]Ѭ kKS)(>T;W*0OZF|ˠj xi){[Ooވ6&!7 *B:fa'v,֒X~h*c(aN[r1^xᧄ-F}S>t"m6w.~`ه;c9Zw yo x4)4mrA>X+㠦 [_AhZu崲oqm0úrj"E *ܥWk3̶9F ƾv 03܄5uC,=Zkɫvm~G_ҟ>{('_F]3p#G?ԡg_ҟ>{('_FG?}J? _|O)CBiO|=|mh}ԡg-D)/aZ'7xO& >K\-λ4Bh=swHR+"Un)[K:iPTSJM2׉ a]"t+6LH (܌Hq!OG?%md){sݓ}b+9OBG!OG?WA=+??(_!gz mZyuªb=x;gֶh (3I LJuHm!_h_ ZUKM;]jګݽ._|_6ud{{IoZUz ``zVFAtG/WWw,-)";UÆF5Vvc8$ \m-2·%yƵkm۪ܼ7(_4sN3M%Fѷϒ/57Yi&Es\[B2cfŤC9T mDž|!~5eRI o|wc3J⳷#8վ֟ 1۠HX +mn>(QRMk4qHaѕ$; ¾vŰ~6]^=}EuT__Z+۞vm~M9?|Zw?៾-kI}Eui;G"sByr3/g_Z_?G3ů-w /hO??g_$~g_Z_?_FG#! =Ϝ[u*YF?`-F<8PTx[MԮ 1i!,bT5_SӚZv_GM*(kFuٶk;gU U rm#"uXiek A T`'+9_F5 DcYO.m5MLsӯ]$ i.[_B#GBHsgm??66gm?ꨠ mHӧvCpsז?PE~8xqMԳ%͎Aw3G ガ;Q#*xxZum|ow5]9luTEVI<#_1$6|O7S]VZZE'ᴒ;}._^Yy~Q[KBɷ@4_iZxwm3P΋JԱ^'pzsxé= ikE-qt w34֖D̟/h4l<7u?i&H4paP;vK%Zmtߊ=YVhtR9݊co{wֶw0MwG(e*izolx?ieHio|{J ⭽rDմk=%Ǿw[XXnUCBޗW|Do\Mu 5k)"7cЌ18Kz&-Ρi"IH۸ZǩϥC$vD90q$u/>[$)hM$'÷rcK29TyKH$RFNH/CO[=6K 4i* }芼Sھo xCM+Y=F8 fPΛl`֞ I}q #%qu$QM^ y/M)'n5?g66o?6;_=|O*${*IdWpY4FDd#v&|Vq˓k+*Ih歇^^o>rE?xe"VrE?xe"VrE?xe"Vtִ^O;k]XvAGyD-q]U t7?7몢1O><5!n'lE4{W^a-v^)頉ABQ5Q>?Q=S]S=on?>iuZnzhCf;=NWq:ľך~#jwW\!EBɓ }8Ίq|OόB_|a2RmC↵!~~!V‏<>Fp CMW/ }Q힥N4HtuYn&iщVڛ ӎ>ޙkZfugږ%w,>h|&֕_o}jqI-b6ҽRGFAX(m* }Z=}Euj; >| !i|>}Z=}Eڸ/?}?!VG0kէ#Q; >| !i|>}Z=}Eڸ/?}?!VSY~ľ Я--:WmBE[jc2! d _DQS,$*ҳabT _]6*1-́ŸѭoOJ$D[&P u8lt[9tfPH`p+= S=eP?[_,9OVgAoYn(\<+i$#;ùQF3=;P.."yXKpd{+GoGkOjS"3s RH$\ʓG⏱5i[wYEͼ14${U_n\?##o-__.nń5֑psFb\Ae(2_v>xz[֮N(HuETṛz&v2nl俎-aGTfݍ3s|]o8>4 ]2j·yfI }"O'Ѯgǟ|?7s{p-m{%6HQ؝ǧc_ii.q`--|3jxKP.c "H'J?e s_@~>|0Kn'G:Nc~ӫ>o\Ugo;~6/ƛ-BBֵ]ȼP{'[F0$K8R$WUoh~! -`lH% k" |![xoWF gR&-%ф( ^xJCїuy{}FYX3Ȫw2. RյCwVϾOY垵 lꅷL2 l WOE4ѽKhKe`.TW'v|k,>63ܨ eC#HF[_|3Ŧ+ԭg[=;MkoYy<$aWteĈA%&Pּ!xaohW~c0SEYq?Yc\xzDwr^Z1e֖6LS}usAG5ln/.źk,jA|ʚ'R:߃4aW>1I.}ǩu't_<7sZ Chi3eO<,we-_Ait3ȢQH(((((Jb??hKyƏkO\>x$CR"6Ge]!޺kO??hVi閚voKD0*jcZ#}i##1QA U7ԿzW_uPKkpݮ&m[tV-Չ$~+|/*{1K$Wɂ.#@@p3t R#Y[$Ga9*]6i3D3MPJ=KnoH|3+EͱХbrx}J=Kn>פZ5RJt=(]6Emر=$R^>z\ԿzW_uPCEsk-mKgr%@Wnt8VY1B31d J+^>z}J=Knh{zW_uQ+|/(aIEC%PĤs3E 8`PEQEQEQEQE+]`,kO|Cb>&M-mG8eInDGfm+pkɨh:X2Mo$kR^?xzԾ;MRko&FF+z2R``վizp4{mOW֢xE%FV31ەtnt/#uXizriGq5^%i6/.0rBZYt4B H&&oQaigw8Dܛk)ɌFPn׊3?ZtޭϱMhLp(aH\ t +?tSOә/@<~9eA3X,ᲷO9t WuK/4϶ d+`8FhW2zĚ)9x$ pxĭ vx=^!>,2~Kc"VWx$dc|Pnp&l<'ɴ]CM!m:{[=T>\<[@цHe œbEi×+scpG[ZxP<=Wq1]ZuAu"*!adEVGLjm/mNaY"Ϙ(Oc]/m;ky?Iʿ<׆cǿ|?g/[kɪhuKM2 6V]m3{:M? -a'1bdl^;&Z.c>$k-{x`|m#!%yld J:=|]~v:UfL[l%BP"ePxXx1Nv0#9"BFzU զϊT&d woІEP>}m| %޺@Io'0eܣ]/m~ﭿ~%׺7}sM׻CMhmc.kӗTW3}. &[b⸏jCWq4e!%2b/ Ĩ8 Om3E~ԛⲈEqKy[t)k7)|Zy¹|uxz[OR>ӭv,&fS)mfMg8Z7ŏ6:5$A*jL<剢yD*bp]O(/֛bi?@kOZuj R=~ۻ(WCx-<_o[xP54Ւ5KB4c6RjJOK$6!$QE ( ( ( ( M{ (b 6/6/ A(*Q6/6/bŠ(H&Q@l_AElQR(ؾ( (ؾ( @-Pfotoxx-20.08/images/rotate-180.png000066400000000000000000000135231362435004500166370ustar00rootroot00000000000000PNG  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-20.08/images/rotate-left.png000066400000000000000000000107261362435004500172630ustar00rootroot00000000000000PNG  IHDR00W pHYs   IDAThX xT>7{Vf ,&& KEQP7PjUDAR6R,mB@$ lef2{73"Vů_{wy;i HvDrJzsX!j[M춒iI[eN(OdihM@sIgg{SOST P(,v90j!^LIb]a&@FIB!!kxe{+KXej765xm7!Is\*R8o5ݹ C2}aW8!޼:(hIx0mKP[pǜE,{uv^jIEa5" Na~>!4 1W,ݿ4uaB];W4 &l9tW^ Ŧ-хL¨򟱈}M 0AO7f`VO$CaF~ʼԇ^<ʀ%U>&MrebvVTxFr(]iS%3Rp}Ӂqki h'xz՛Fyg{i BZX6I@T PIrh /FEK*v}ؒ᪗ˌC aaz4 =b._K)$X >]+dk%}:^ ")P.8@Df5BRRx6Fy>SO>! ǎ͛63f\4i&a[B²Ot3ص@j51IhiZ>5,&;'Z dJ)‹@v jθa&Ȳ -Ttp:]PRRj̺FB((5!b0I1)oL"ljפߏc@D);& d9d1,W9DwH a+2-Ƣ^oq`aݻYR,''W0LWUUE5(.ȗ.*]C]fP!"4qxMwYwUQUE|nl1#c1Rpkϛ,q.m6|vTb5fc9Z8[̎tR S#z~Vi;gj#픐l6,H0xF %y?0q/Pڥg3F+ g1ŴFJODXw賿tƖFoVv=_мِ2hե䤿ݛy6LZwdbhR,Pi-^# ߿dHK`"Dž`6êWZ/ϟ\.%rs) ^/M+( H\\.aV|jB;8f8y;O| ~ZzaB<'A,@L~秓gOqoG۽ 7V4mubc$[ABz8fu!Ge^u&uϴ8]'mßjfm؋Yۏ.Yy:ѥwz.ʆ_g:c^Ÿ:m)GL':fC*+$- iJ9-v@$o1P/V9I?2߼Z Eq|1grt, &A1RWgDZ. q M]muf| ۭepvFJrqt٠cR"߾|ac ]@Qz3w=_W񠬫Q} m̀N)AV=;Ĺa!Bv*Q4;XV ]7ȟmB6Yc^x+2ȕ ~~hմvzLB:a:b&K R6؈_"_龥F(yEKyJv-ifjӡ S8H'VI@LDSg:tIK}&l袤Ve5E-uEhshp.*%%6Cʂ%>j\\t'ۮkV^'['PSK7ޣ(v7Z!+ɇ |HKof)nn:=~ZoQ2IvvN~u[ h4$T~_^?|y6K?6LU.\ `B&w!GMXa(lbqHIstϳ$EۀT_O>)kL#f& x̌;nn6 榪OR"&t- h6caU(/1c1Pxٝ S<{֮ط2?EH_cf}.bT@c5FY4Sfl8` 6>5ʒ_Ư^0VOOB'&qY=G|9t868K>3zC23Awp_ғ%,=l[##7*L1i&smbfM>E$}gR$)E`6 ʓƏ/phegK 'g5ȥL׷Pd C>](e1F7:bRc"D8*M`}>?6T}1@ǴG>!5T4M匔EЌ|%Oc=}G?s:}G" =i`Ȼхr͉iLlj܈hx}i i Z 7&tIME )beXIfMM*bj(2ri%HH2013:07:29 00:13:020231"Ƞ0100Fotoxx:add_text| Fotoxx:add_text|NE)d`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }Zǀ/iTXtXML:com.adobe.xmp 48 48 0 m`IENDB`fotoxx-20.08/images/rotate-right.png000066400000000000000000000110261362435004500174400ustar00rootroot00000000000000PNG  IHDR00W pHYs   IDAThYyxUյ_{S $@!LU"E eZJ?M$"Ai-T?JȠg~ B&9g'\0}_?ᬳk.s߆UWr!OYhW w5=.;Rgw6vs2b~YMG1~>o{ WluRvK*`<i M4_WK:$ O8gMv udg?T{êq73_ugԇwM,NRYv2c&p`H" ۯac^˲6xK?6ԓ 'WZ=_EUg%==GFE|-sG+6T":b|Da>=/ ` #9q&a*淜˿Ss/nd6|dYX}q^/O3+3t ~;p}'4_ǣ=ޞ;;TՒ? 6{4}}m_*#7@"cǀbALO%av& 懺[/Z5xǟw1p~A X`;oCy" ;D2_ |\=SqNk hů.~ˆjHS$ t/z\&vhk#w=YBd6Ž4. 84IaZq{Zqu2=<{uXFLZx _aym_qYj(ƈ(uL q-){ &+RD$f 1/~oYpjox* ^?su֚ͺWKdnꋺ⩕ZY^Ш0fN /_UyvڡZ`ep6%R"ٳyFĦ)/ E]E"թ:;5X1z]'`E3wa,*+Qpl뿃FHII{'>4_Ȯ|)I4g۷mb i3HUi%Bm7*; *S 5U!X #hfm\HWv`GgUyM'_lK%ب zs.z;y.2|nVIƀt9:෕xSS0UB1u?<4LlHkvp v#hq]⼊2gĭbnHx(jܢbȑJ8t ٽ{0 zD'OԊ6ɡ`h[s iJ##XkNw˟V[ާX߆afL7gEfe$t>L8H ͛K&ZYxGS8!kr)% OO~(CbƳω+ r:J(-tIM &Ƕٟ9&))NiefJ 1}[_=p҈{E$)rbZBZElJimOua؃ :ʩ>5:E C6(k}?4Q=~l}/Ȕ%;ؠ)?!<&&ec} ^6 bσ4hu#A4!b>++Tum.8WaF>p|TA0+-rBn]YCRN2DvX-J $'[Yذ|qU<˛~[{x~K{^>p Nmлw\1T:X,)ɓ'ʁAmݬO WܯXTVE>;>j%_$@wEz-('Ar]!{,߱}̼v>VZ'xׯ0~ 4Ç'|7LZZ:x32Pa/^aN/rx?BAo8}]_3~iQٝ^ϥpl_P#B_ysk֠h j-mکzjj*'z5EZkȑa[{ݵ׼):@o u %;QL-Hu4uͬl w9G矞Š ֪g= h}ݶևg7)6x5FO_mYُOv,8HfdU2T6Hh1De*35C] ?MCgqѸ71\/Q. `ɍuۼAQfʝ)54o\m z /sCk݊K[^(631T\i,8y 8yi@)TJBZ*ﴝ XvC+ၘ[Q2:nyF4 ۢ`yv3쟷4! q/XVCDYI!Y7JX ,V<> -u?oپC2kG*XŊ܊9;w}1 y|<0HGuƫT <; qXktIME 9͖eXIfMM*bj(2ri%HH2013:07:29 00:10:570231"Ƞ0100Fotoxx:add_text| Fotoxx:add_text|NE2oY`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }Zǀ/iTXtXML:com.adobe.xmp 48 48 0 m`IENDB`fotoxx-20.08/images/save.png000066400000000000000000000117641362435004500157760ustar00rootroot00000000000000PNG  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@ 551 688 C     C   Z" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?)X *K|ɰ0FI*?pg'$q/!K̬jZNK}rk?xniZ6_y+G-|?1ugd*̔)r~X6o$rڊWUYUm~MFf8Voڗu Y#KU7 /cj>F5GʃNn7.ݲ/ލk-5EveK+yjWk2K ̩"ە~_m]75v mn]722,+$&e9>ǦCG+4m"]Syk|t9K8em$+q2ʱPڒ,oyh5~gyMSUc5!u-/Z->m3ZV/ռG+T^,|A4 ⅷUZnݢ2+Ϗ|k~гEPxt'TMH9qk,3mneͻ5[4׮t=6}7VkxlZE]I_<{3_GO5o@o|WK?Knt{lsƚα7\j^38oʿld[o_ݪ_GSekZ*Mqn#o>_Z|u'-u-kOōckϫ7udr>?GSZ/Ǐ[YMY~oݳ~ݭ^j&[U|?6 :}ލm[?Xf/R8rhyh|ŷ^? ׈'Ide8~_/ܫtFo6rx&&鶴~+7j3Wf}eGO3ig?g7n5&Xɿn٨C-oej}M4ku{lpk5W0rhyh| _?^vzK*9,m?w3L~oG16>|Qm5CY}xnv,~tuvxX?d`֮4U[s}棛Q4SK9vs@ۿQ厯ok}̾d }~ 7M5inn{mZo>>Jw|4yk IhZ9O=ai-KG0X|S/%Ih zu?<5i-cw_GO氿%,nh|s=zu?Z?`S_X_KG7|4yk IhZ9O=ai-KG0X|S/%Ih zu?<5i-cw_GO氿%,nh|s=zu?Z?`S_X_KG7|4yk IhZ9O=ai-KG0X|S/%Ih zu?<5i-cw_GO氿%,nh|sL HU*2P9EfMxfq b Byq\=E_Np_aԟ@]Neo[uV]˵ρ:5Wث6-kռAXfdɹWZuc/k:ǫX޴oVN'fkx_A{#&obHwW_-ݲʫWleOԮw(c#u~ja*3\JJҔHW[_6O u{#YYjݕjVM_n3/V]rnTd4y_T/CNk={C -cicFon5I5)|'$s\m'{"4}Sw [Q^olۼ-snm~M|ɢ[d*V6fcQ~oiw o;UKTM}a#>x7\t&ö7n]Ga_Z m&K M%d[wz}ֲnl fde*/A ;Ǩa7m/uVsj}y.n>][믶\@6gӿZzA ;Ǩhxg:&WOy6X_]yuEEo1nZ=ͻ)waܳCG4+d[o=:O]]cVޟ}./G>7Wxöz11:^X4}=:_?_XYi6V6a5ke,M3Ŭywyzh,??9$Ogg,Dwro+h,??=sKxgYɯhvzZ=]ijvi7q\sGh_פ}exS_'Y4`k%|6o4|7ڦdW}exGhx:LMfk{f_6ovpۥ*KUk>O揲Of>]a)|X4{08>O揲Of>]a)|X4{08>O揲Of>]a)|X4{08>O揲oԴ>4G٧j-nڭWm}{Ím&aYo1dYkgXׁ~"HPknޯ+6q5o.cAĞAW 5õաpŢãFr,twl}|2Ԭ#~ ԢOm,^m&k_WAgC+*/(>6ߧڷZ|kh[;ɖ []=fFX3V?vdmXs¿|1XC wM{H՗̏v|if YEZͶHYfjw43^it|+WGfiYG}~]%[&rFۿv[n[)oov|wWO%ݮì4mHHEo{ɿwKUE]V$ķ[W# K|iWXPeUe_VYZ^_K%K>>>5B-9n&WfYw˻j&ѣݴ3.$~r+y̬ʭZjljƋu\E[F!3/_ <E^]_j\6٪N<ח>5fM/n+zYR$ٳǚ&K>z&_kJꚮpGX+vUV?¿ږt ,ڣhwo&󕣙~o/vk[Vyx~?G-۬VŻnFV_1wmeo}xRGԾ%-cԵ( $̱ƪ_Ӈ}_Sm㆗k]jԧ=~[_w-zMy%(kZ5Դ6Kk-=l١zHc*֩;EZo坊Q5տfۻrɶ6eh ǨYhk m$,hef_n_o^$1-dɑd_;km[jVۺg쿳u$QHz-6^ʼn/Lu;XmIh|=]ʲ37~ouq~4_oG/-lᣚM~U%>^8 }qwF2mͻڿwmgHkVf*ʵ}⦺fk.=5  bFv;/7^?oh _YwhѬw &5|3mZ 3k-2L$k~kZ&izo MKm/xO2Efܾb]'Gq|ffXZFݶ8VhhQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQExJo,PVڻ|3_ĽST'#4Fo_kk &|^+B7[v///k/G~I_P$v4kڿ-y+݄bupG^ï.꺇uYO.6Xʿv?:}-Vlw03G$ke#? BĶ1;k7OmU_@}#O] tcseSdvb?խhKI%i#4oVEmIo/ˏUWoʵjGbfWkEe_Es7[_ گokCg}-LXݎC#d/yC6|?UmjH|_}m7KJ^9c7Yo5yW̎?ycVt8mv7K7KӺFUw][d|}EͷnZ[_ևK?htg}-;3Um^5_5Z%m-r2'AKI.]=̞cmWZuK%w{H/˱~M;F/֟>xg.*u?wKػEPtYy_參]}UeeT#3 `xӽi>o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZ՟|*Zo|L r.U_eUZd/I_IrK+yۚ~2' ? 'Û=Z?k̭nQ_W~RRq]4m ][t}[Oivneܵ=.-1cxV].5ᱯseO7q?E|a/5oڼ*dk{~{WnaYfEdUow[_Ʒ'8mY&fUk<ˑp1?o^,6Pq4,;z7?g[;]f'yWIyko_t_7C"܌굪|Z'uxUlV?3/v?iaOgwecev:5֯}I5ݵkZ:#y4{wnOJ<5o]6ͷsWr“gO(R?Yde/O:F>O^Pʎw4{WD;+R?viUO.͆qofƲ2j{*y7̻km!oF,J7VXM[U,Z:{9c_={e]H-U,)΅w^?bu ;I^Y$;Fkm'Bga:XYn>\"$\1ojůa~Uo-]rN1Ҕۿe{jSGCv_wWq'M61X.X@P Imeܻw/UY\)g5Z>O7 CI׵[]:CI~fs]VOkxItGt}'Iյ+k$mm~]ʿݹ_Mi/OMUGjʪV|=u?I{6߻+v:_ν~Z5.Yo)_:EnX5uYxQWD˪Ik^[Ej 4t6/̿/ޯ~xvk{he_r\34̿nmՓ}Oo?m=Z;_)6Y2VݷwͶ oloW>"62Z$4t,lvgA|ZմjMY4ui'kkUht9EuZ=* ?l4SK6o-"_Vf~oi?ٶk!+yڏƋ Z"fws^Gy#O|D[T vkPu46aUUe_A&5&m&[r=Xs|*eRWq#3I"{ux^W\Zkjͣk}}c v_3jɹdjoşx6Lu[9.YY~e[n ?_l-o-yoۛt13|.߻ZZ?/~cm Xuh2ymmf_x~fIQz՛J ueV>oyvjx4_VP֗cjBSB4{ 6;mj+_>>ml7_1mڻPQmVr6q̫ѫ*̪wz8 vF-=7H5Oڭ,*ͷ~ex=?HZk37͹dVvjRV-ڔ;W]$4}SwAm&Ѵ{V{vgZ?_uh]?LO4-g{=߲Ƭq.̫yݫ_i<ڃc}S]AݲI 1l5wO 7W-M<ƒݷBۿk}ݵ̷wn-[ m\^mDL|wޠ|'E>i׬>Y$s47WV 6fߙ~}Ym/G<uxSY|DֲC$2I4k5ѷ"ݯ@ox}"}&_ڮ_j~_SxGOV9Vi$Wv͵w7u4QE|>% 4KOAh5 &n&~-čs}lR՚!4%_n/ʬ՟j^'եbSs*e' rLݬle۶.jS%w.Ycڋ}x/5M^HdXdo=֩oa\Kkmw5^o׮mbhծ$_걌})?JuR>t>XmtZH2w^Oki"o1w+mcjUnI-oY[^SvyQG7۾Z:"wqqݬѬj٫eun۷+uog#/h1eMS=_/lZZwq-mUث4:y"eqj򷻅YsF\Vk>|̲y-WѦԯUHs㵹gk%牯ViY~ڹqp}u2B~jE;.S*/{;=/S`cXӡӼk[x,+q ̗aTMk UaII6WWS=HֆHϷZd-ZeY%o-mxtyōiNGšjW_efWlV,,0.nV_K\5Edk8{c-SYuGVʱ|ʰѩQS\`.Sӿc_Zݖ#Q~A: :}ŻEn;Y{l(˜*2!~:_k =>KM6Oi$hmM"̻c/ko3XѼ5r"k9#fKٿwq>2_nǀnI$j^8|7#MgKX e9smӣ|YkqmqVKt{k8Ze/m+BO1oͺs4]TxZi:ycykjM$֬ol~bۦݵkԾ%Jѵ~$i~㼸Ѽ32̲[5eJXtXfտgJطۤw/k[k}.m{΋N>u/&qT5-QYcKFڳx~裷/c_n[eU_eV<3-^.Gy3im%3[ǘLʻUwWYo3AkKK_GHk&H푾_r>._Xڅ.|%5qbkΚT- .V_rz3~}ollumNO6v]/Eu=h:+[V\C*1ͻoWt?âi:6-K"]R9chcUeE* W|\OoE4x7:is]5\ffivVm۾VZ]ycW4t3O5,t0ͺ6m|Ѳeٓϡmilhڭ7~_O9GjG믕?-|l75_ŏx{K]!A[j#]/ʶr/ݷk[K ijo$Y/?9.6i[՟kx+[I/m$MR՛sGpN/fYy''o7VvٵfB"3I r+}\_'x/jxsCo/mVM&PaTm#v^:77^CwǷ;EUUs|g'u?k&i[8fUWk}Ὲ[gk&ui,j2hx>Rk=f+]\-6$fH]wʵ: ֋ynVk4uZ6]yḴ}bR5zMB.;J?d1ƻv̫&/?>OMU4]Bծ|;k$p {fIUfh_KizcjZmmk/;H͵Ok ?|/ x:Cj[UUMk۵UY/ŋ.Zqyyg\YYuFɹ7ޑw6ݴ3fKImA6{w,&c%J̺}7Rm2wn7mV<_U#.FT%Efue/-e5-?ZVXU~ofZ>U~&Coopge~ [~"GoƲ[_B4Sʭ5{j ޴WI$rG"BL _Ug>矆k~i?-|FiqyDM's_? .w {!-yZLrN\{\]JEo&q47<2ƱfDᾞn7mf_isSid+Zk2̬I岴v^.Z49Of/ktFVdY$X>js6>2 Bto÷kS9SzN_5(VgXCq Kq6$?ùV[OڠW[rv׌$VKgk5k >M[-ݹ[k}zG{O{݉.qjmb_Ck'.$sY4߻VWEkj 3,\*WMVO3GOv?]XLpR},.Y,rş? #hݾ"])<X7-xOmP"|fokLZzy[Bh4?w1:s:su bHqU|e\i:InX~ 5FY{WwVu}k2^^ݲܿW*t2Ы(Rmx7QԮ;y8Uvy4WQ|fe۵kzvO6ݨHYi-7X|&mX$q;47O|mKvicT[k*ڧ[mdUcn]-p}{@|Gj#3J5QӏzKY}+^4㰺ia3IWRQqnZ]\$?hk|k|ukɵ⛨MP5{xj0%;2>t'-z>cy۾]u 87j{E>r&)Jͽ>k|9|SI#úimgkpۤUs3nU[~Z־ kC|#NM=w p&ۦ^]ncm-ZjY|Ͷ;;UIG~m^7x=Rm,+BƲ|̬Fۗk7o+x_MPuT~}yjB6~J&1o\/t5)t]еǖgL";s-{xmo4>췚~so&YU~nܬ?i6EȣC]acoPhjOhs_e4S}8-Unk/۹YwV]xFK{GC֯+Xٯ#;LG#*۶=F_u ui6-jVĪH2/ʻ\lB{}^=cCV'c[h.ݼ-foWfUЀ=Ί K~qqoc$zY#mۙ˓*j4guK?Y,Znlw_je[&5f3m=N/ ӫq&joI:4z̶s,*6wWi_oeYK/8y[٭y-,L{O3cQ3XxOYki&խՙM_Q| M{[.|E4k v?M}Io3_Z .4Juknc*O΋ dˡIkBnYwUmmo1ʬ#J:D FVݯο۪ I<&M's_ſO3F>u˖dg)VkE_x~u Ͷݵ[qƲUfkO V/t${kVJvi/t­.hdi x}ciuq,v7 VYú=+I4-ov˶cv4ծl*4ΰԌnz[?Hrŷoo5oxvVV,µ-7mkuU}9\C9 J=gZ5WjI *3}6Zn/fߛnԎs>$~Katóx~-|}|jAԮlwkQ7+YF_UٓSz{eZզկԴ{{i$cmǷnefG5{[]ƚ֫宊ơ,+izڔ1o]дۣPԴ}oZKɴ>{I5mmwo\Oں{[{[X.wBǵj 鴝CZ^,23-mnVAku}>kqc4r^n!V]ĵ/C%_jjXKtqVܪn ?>jOn0q6ch|w4jXYZ(7ZƱI$ 3Z2'novYH,KƗŖ6jZ-}ԒG|\|P>5}3G5"lڔG#m(f_|.=# MKSt5K[KX[m3fmMGex|+4ږklP|6P5oʿi U|Eg*pXIff7me_/i#m6<,9&<+YWnۨwcGDzFk{xn5FڲM$lm3/=Xw t=z?h3]kumIkcyY~/h歬x}otۥмWue}wV]}5xnɵ?Zw6ѯg4lk?7kkjBe~n׋"umcmVv|KjZݝz_5oe7 +W|A%v ՋkꪳM$,dI667^ GRZo-wy|4ڭ/f<7iw&S\՛VRfh |no|3As*cyF|YlQ&lcG寜vsyVUZu |MG2[w}[.tk~Qo"Hʿh-Ry!}>:x[E76+,Z4k$KmWng*,6߽֯㸒(ddo.Mw|C&K%Wtz?̑}5|հsic^1o'hj$[6]wК6ݠr+GmhoZ>eCmYm*Z.$ge]۾]ޮJxV3i:5|⋩乱f9~5k22VV9cxvռ,7:=Ԋ2=Ŷ5hNCG|κ5-SRvFvǺ ku >kyc5}*Md.g&9#[U6o~mFu-R颖cTbʤQP,,9iNJ<3Cb}Bmt^4 յ ;XgkVkv~i?uzOI BÒI[rFoc]bTZ/b]-x9!YK>ZC㸿uf/Ug][ʿZjt7b##R*%~|Y746oqH+B6=g-j)_nʻu~S|S[ uy><|7 mb5h%b4o ;m5կǿzw&&fkRa&gH]wG"e׏^}}ͳ 4(ԌhlyO~1x6$1xdTV~ꭶfXvm*2jOGIqqGyoGΙYU>m]'g77#v16ڼr^k~˞$𖩤Zi>uirI.qi#FY]߼_i垱qH/o zv^[ieU/rm>4έ+]I3izmԑp|媲ijV=4=KE-Yk/-KKPYԱ +CcU?X~H-n[|2kˑ|syzzC.emA3-]y~g^Uܿ7U~?|kgloYAyyym'$i7moqk'O-t'Mm[[G(խw3,km;UQ,xo,t]6aifڳ\4&~_S?I?\-ڷ\F3FƾL3mV?h feoklfXZchՙY^{c#k6q|D_웯{_:vE'l]Ǘݷv<]2GMKRĖz.5r.7Bֿgqq.eUԫ|Pi|ީ7^X5_[魤yVf/W🌾CX׼Dk#x;M5e6nmx3nghk]VAm9=c6eh]Im_[|vI,[E.oggm[̸f5ܿ*ͷmu*{ tۯy&Ԭgm.,!>^R}+Wh>!5Zt,1%3F6{; ڧ<#+xqA34z[-ŽGFtrnU[o[צh6ZڔZM#,V6o=˻>&d|H\Kz vypjz_O}2-FקB['ѫ*|w˭{gyṼ?,3[:r_ݮw\-@{gZn?yWnx=[zGwCXzS"+uHmX>7j^9l:x1d̬t37+/ Zƹkߋ5-CZ>ɤ+K%aUio~mZ \xYZZ:G$3/3/̫ƻWsw^4Elc5=:5iwZ+.VeZ^]Oula[{xcUڵEcxwZoTm:7JMBtۦ5lEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|^x7Ş77}JFk9-Y41U5Ok_x6 33/۹ܻ/x֯!{{ujl_ ύkaTՑhbҗtza3y b{>Z]njj> 5vCocc=3~w~U^B߶7jxWAg,RyjKma$gO2QRgi?縋㿌y>|]4~Enu(sS|0q=5R]Zi?vmi?Sj!>o.hr~8(]̪/>:7'-|ji^ө::1ڽ#.%ef8տ]T5m4Zf~v/+^?;uHZF*l<pJq#\W|5Iᛉm~-;7[FGQV[57w5pV.H1B'>c;xmj.mYZݡo~jy.cX;j;Ȥ>$g!cmvmՒmWSQ׮m*5aM;^kQWr* I_&V5_V׭|fefX{LEcqU>5L p$6Yt˭[n+ƙ,kuVVtןö. 7_k{Vk_f~ ⥚=O㥭յԍɩ,r4 -[ݻUv_-'þ ^iq∴6ݮ#UX߻V]̿ź"uD|&xOԡE9<}+]UMx^,lԵ+kI!lWs|#mo^'G ]{Am[kV-aV[Hhn7GYֹ/|;O"妺%ˣخۥԷneKĽ6A,Ԗ٣41ɹw2 nVViZ[_ x˯G5G-36v߻@ϝ/OG mKImB;cfo;"OʾK.ݿ6t><]GzGV6c [ûvc6.<7]'Zm-cmwuof±4̪|eMǫGn~äǭjI6i5mћ˓_፨zr?_^F'mw]HǺ5W%|[| c[kpin?^c=Ք1,2GBFͺ^Yku w6FdVxKVF?MCpI"ȲHѴZ5\=[H}z5I> X0>siC7˷vm߼ǫk}.m`+uaX5UX9-Z k\VwT͊[$5mX+IȺFטkK&MIvMg ]/6w=47Vk{>ݙdhYw,bYUޫQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEIʬE-$Xͭs|fyFhwV_/}oς|/]&M.,k2o[Ym_iXQt_kV4*r#6/m44λ[jͺ5u^cMY$/?d/DS}kM!V>ʻn$M㊌%?W=vj>jI.٬45Ҭ剿Z'Jv/uedU%W\Z?-ZՉk5\ilLmFrm[zMJ̻v_WA?^mkcB.Suy$𽎓~b<Ŀw7KqF3w?-Wcf)}Iqv[sZ44~(R䏽;N7oZEfڒ8<7${a6V$r۬27͵Uf!y3]Iy_3ކ*u2O&o/ofڵWvR r|ngB'l߲=GkUp:쎟_ 3kPU}#퍚iOOuZ| ;6}O 6IkV]Z_o|>&tJC$4ֱq++nۛrޯ;ůᛆ&ϗIV۶5em7xu-Cմ߱?kΒevowHpKot } 2{n47۶ Z}k w6ךm>4q"|6e{ ,M7.΍|_}4:cc5/^G&28Uٛ'>\K ZO]YMoq q*|?esHIԼ7oiٙIf[m˵_$wTtˆcon~Y?5 v0_ǩo"Xn9.E0Ч~Y->'վ3bn~dܬ_:Վ{g?ryO?Suj5]G64}>m\j@|+鶰U'ØL5Hqv:j7• y)]s;]uZOv;>2ǟxY>M5YU#){(+>h=Iwn_O8;w.CWUDk#o⎩Y|*?ԭ4Z3up> ~/}KKO.6Գ_ܴ-N({}gXǖ>_?%/?aS&&YoX?a2xmcǫeÈpcLj`_Cŏ˵h9<طmizfe%.cw3x㗂|AEl}7TRO.5͵ffo ~7i$M[ҥQ^];^(P+VdV믄zc'40ǥwûv?Өh_?E𾥢4{ߙw~7kdo4EuMJyMJ$whls|P ?MCufKi!4In|wϾŸ 7W^|/giXn v3m_36kg_A &;juH.&Ѹ[n|߼mw|v|7(xMt>ƼcuO˅PaܶU*Ԗw,k[5 ] [,~VV)8IF6UZ+vwl35,zN-fY*^s;o:Emu/ʭ;ګHuo.MvCFʭnei?ş5I<7%ux>"֮olqw}k64_Zo ir4z$,?eh.߻|Z( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (9ʆ?2h_HkǞ+5ci$oW̫_5($]RrI=ڵ_o ~8oc/4iJGxO\]An$I-cUO-~f_ ^MèiQY̻Hھi]M|FxORk{mwGbߕ]k"?}oLVY^,¬W *u)8rqkW%MM'sZokVs肊(((((((((((((((((((((((((((((((((((^|eh\ZHFVV_YY~eVM5Q~Fzf}qymFVm]u uѶε{5K+I%/W:5yьxYj҄yu׌<;-'÷!nxGn{G 4j͹?TX}PoH$YaVWvZ^(4]7Rm[Ao-Ἵʭuzÿ>MB[u}rh|UV kjՕjTZ1mo# թVc'sZokVC(((((((((((((((((((((((((((((((((((rVvkqjWR|vLx6>﵍JmliV5cZ,Zc[43/݆W7WkÓ ,^]ۇӖ&Zo"Wt7-u|Q}>u4 o'ʻZ6_i|15mgwƸ捗j|޶&L&>?Rjx4}i?լ'sY#kyqk?4ȮhfZ6__Zq=G/riՅχeEcib-c wʾ}k;+|i弍B|3{ g[˺VCEe/Vez[Bٿ ٙWP~ &A+o1Es|FZ 7mԾg]2#̭@ Ͷ"6ˏZy)Y۶v9Efz$U١ds+"*zfˈˏmWV^GX"UWu7hzgY'XVfz(}4aeܱn((4K"fڴ( w )U˹[w̴( ( (4$qIUj}Q@Q@Q@Q@Q@Q@Q@ɧK"Ļw3m(c\F,-"̻-6}R3*+36ZZ* ;{u+[+/SE2kZY%f6ߚ@Q@Q@Q@Q@6~ MkYe 5j=߻{5ⶥ=q}v9eue?Yk*o?c㮟6Z?!]ܪWa_/#2ޖ?Tǚ|㟉 aok&j|ͱyzu{oUYHf#|ۿW|h˥=jUkoZ;Weچ25k( <-8n/qxkrӆ4Ě4[Vֺ]]7-oL1^({I{S,y xF[]V՟ßZUr+WUͣxni4pFm_hWmjq/tW֭mdi۵]g6F:w uU=W_ ~7j^2\j3+\,m]_>~%i7oj,)t=KX([uo~ *;[$\?VŦqkz9\kM~ݵ|3W:a_-~X-6Oi2Y]Mfmku 6 b?>&xZ+Hk^(vk8lhٗl{[v;j>"jᲓ>M|pq$,}hfX~oWvq4lp+FѲƿ+7ezx_E&v s󞓨~(ItsKY;k $2I%/l0m2̫V?5eqy[h~$C%HmwMٵvnXk|7| jYZmcY>en굥gKѴ;;"-{_ߕh^4O .Yq˧Ʊב$jfV_]FēK^*oA>kGkUZ 732Ʋ7͵OkFѴUVeX_%M]K]JMAtE$]]fx>=.u> u ?/e%U]Y6 kȃKEPXշ/e7_SG?Ũihv2I5A ƫ$jd_mZ9?r2n &MmVkyfM7˻s5r{∼'6}kg$Y>Mylffo-o/WW{ ϧOѬr,+*oV| |sä}?,iH[?n][Pr~_5k g2u'uWORi$YF6X_ϛwl\~&QV]jKƜ,pDr/omvú^\0Y5UN>xgQdC>I+4fe5|7>#xJRtGG𽽥Ο4w}M$4$7ƟxsOxQxoP3f%mHY5[ݶm.Vi$Y5_[~jjvi'p,MZyjsoP֣5l%xX-;C:OJ&o2F{tvYku÷˂{uhoPWIo5,bYfKemc]U]<#?iu~Bm*ȶ7noo˻I^/C? >%[m{Rkˈ'feMI lͻljZ|=魧ǧ޴V?Ջ {#{x=[K.カ>I𭭯t_.͡zHQy33CY?WQ&:!Y:WmWO[iH.om9$Vmݯ|/syq/[jW"F[]: uh5V>)5ǚ}ÝbWfr5[]/;vXimU{Y&mMwur__þ,ԵR9mR;UywfY-`eVd5fUoF&i\1qc_?*4/>4 x}u uYAcns[ZܻUڿ75tU/%}bIՖPI$8dho%de[kkݮm>;,mo¬mѴ==kh,YY~*o~|{F׀'X[=-mKԖeVf9>2C_uW?k^45][Kk5t,#Ԏ_tw[%_c^xsIfS$5 ~4t>mcUPV񕽇{ R\-u%Xfm\GFUU~ee׏O#z^->-]&7i4[{5h~hVOvmӼCx]*6VݭPh\۾*z\12Ʊyoj(((⯟PV o:9Ii>߻ǷF⫶_4c?[?ǫءN2yzC5Y<'A~_M]ZVݍוtZ]ۿkV2c÷Wq?Ǚ~!C7G+|n84An!x7LoևŭFI/OeܖIj.nnSZ1~KUZ5IW'ʿI+I*4}j~|7R~Ѩ+-twv]9c/k˯ɓCfPG֞];Cڍj֦3n5yz5w]om[ѫW|D-QeWʿ#xft!H*5}pk-/\כ]6C IR4#.q*mY~_/OöWZnQj~IfroZms6~44y7Z$G/;^oMdPbfc,$_Eo-on$#]*ﺿ5zG-GTmJhV !Ζٕioվʻy˘'|mڇP9_5Zm3m_~ 2x mˎ|m/̵ >:u>}34v?#Vvo|WA7^A.u k$K"H.WgR4~ZQul).!-JV5M[}oP55Sȹ8ܬj]xg.IJts7k]C.k6EVVFfz%Ru>%/t_ UWM#Pyʿ-{}ߚ*ǕF'RR#xHǚ|CoMJ=?P5 8fEVhaE۵['˺5X~GVU-&HdEfUOv'[Z}Pk6WԦ82%wwB]'?-moR=+Rm̎7_/nݿ.7xW^f;.;_O.&u˪,W痺uS>.i|HFGI/Mk2ݤeiY#F^o:޴׍چٵkеǵfJUUj <7I5gmuOHhWU[[vFxRz}Οq&,q䴋4핾mU?k{]?E}5մ40eEHaInUh~VdU>˫nC'y~w7ٮ?'~7A@5Λ}} tۦ9-eѯߖ8 3g,>69gx[]m6FOxyF*mԖo_k}/9VI*ڶ*^S-Ym5+c-VJ**vej>xVZ}>˭\[_FH4 q.շvޠ5k~+jږgh3]Ei$ڻW#wZ-RA]P. bI|wGnf]i[jsG^yy}omcyy#M5fhdo&_fWl|7y7Vv|jJ7|6Mf[Y&_1|-ղYkkÿ j,2h3GIuIy#Go/ Ŀ#/ZbK}Oldn-nՒFUeU޼uÿ-׬坬i-綼xYkmYY~V]7Qo Ť~<3,vд;5me} _xMmkǟc֫g=YfZ5icVo/]>h xx4}ל[7 [L6jZ(((((((((((:zAuVO5EtohOjG#ZZqryxË-_3^\<ʱP{[SEGnȻ4N.[R{m%cX-7?v##u{xxWχ[?Oc$y*F=[~Zl3 T%=ROrKM VܭZ=YǎY6k9H\ ]^\񤊫ɬ^miYW[5yYĹiǘ|Q1-KR|׮C)#Zͼ)27˻wjVB&xS^jdZyK/K[]w3ADyO|[}٪Y{syuf5fW}ps>ƒ{$R۾oV- k-M$k:fVU^]nSlo4ǘ?j ~ĮeX$=?5|PCm/Ěݭ734rm]kqkö|i6iʬy\ũ]xfM>tZ-|jqNyM8Ju˘4SO߇> ԍq"A59+-zPkF'VS :6 .7BoV\6J&_WПV kծm*۪ɵUv_4Ŷ-mg#urm۶\5B'(5%/t|CԾ4]ys/a?6Fwʭ_ɹU~G۩%C%h']Ȼj׃g 6q"q$~[|ۣV{9XRN4gƛ x^MVIeudwڮA/. xo:}׊5HtKKmTas+GmXs3|zl~x&~}}[KԿ]6964w^]ݹv>z׈} mt V Υy3mY[V _gԬ4Xd^T6 eUݺ5Z63Cx5s?-Bm/!X䱵eȻvטJυrI ɯ5di!fi?}IwFЯvײ]~zB1/5IkfVk[;R|vrU|EA񭽮 T.&%Π6k4%InEU]/P|/>#wv7MͩOq;X||wVoƭZZ4;IuuI$rZUܭ"UL\_麦xFK.=ZMMBfc|ǶM.ߺ迴ͺ]^_Ih\I2ǹ~_d^X^ %Oo%wQ טm_w yo-w_ n<u,jRlE,>c5q2u~o {}h{ſ5Hon5|>Jֲ}?2feo29դ]~VZooޗ R]^;>Yo5I?vHm7@R^}o/Th.4ۯ}o+G2俴o|6jReuh`AP[?.ivǵWʿPtW~ԷXW øIfmBٚ5UVG$jۛڻ̑[.IghwlmVoTtQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEsWPծ-uZv^NaU^sڥVzIX=v5ffg4|M+xicܭ]Ư4| Dwvp\W^>6;l~ʿW[7vOZWuȳ0boyoglʻj&4F2=(xd)w-l4}>eVhݻL?ښ&m̵TmZGc_9o>f_V]MzөQxΰɻj{Ʊotl ?yVC7qۮ-^G6X ·VwŸ-0ӏ-35z'k6˨5 9~Wf J*4>G̿z&Y7\D ?-yoxk'tyla~Y7++5/gYsT6Z-;_!Լ%.Vo~en|`%CSVUkH|+njU)'??r}F\x6[?|đ{ėZO:WeYU[Z,`_ mҾ%[K| W?Hu3ivҭ"yr4{~eVU?m/52I}=/ޯ..jr5^ʧPe|Ӿ!i7 *_m}- }+V5YaZdgu[_'h5ٖ65}iˤdz\=._֯W:5F}ӦǛk{C}_o&^ub>"|'W]Z}nfUif>e-W=I=/'RR5JI.ahUt3/|/?c-|UO~]ſn[UVݷk|՛g-i֥:no&mmvYZF[i>@kÿ t{˨Lmag {Wn?z|]7|GZnj|Xki.|9hix害^_m=+9dvEe٫J׼'c_oQ lcI ğ̴?i啟ki&V;x[}.ݳ7;Y]/AmŬl8.w5+[h'k ڳA!֬oFn8efffm"qwU[ ' '=M}zC:M˵dfhmP:择A_6 j2*ǺOmUmG _Kt"qnh汆Ei?VV_ok8|5ڮ6i7W[/m~f_۾oռ+jKg+}$v~_}=2ke&rIqki&#Copmp2mf?wm似K^]rFƒ+[nf9mxΩaÞԭ'zxOY.n.E\\k[ M=Mimuw՚MOHYnwm/@KxF4gos'$s75WG?mkڵb7,k5xqn:gge%m"r|ͻk{wWEH׀|Xt]kDдpM[5zW|[K]KA-5{f_H.ZׯEפ[,rG4jѲo5}I^c/MhזrMxC3y/=_#U񒧉`a8_BMro[}yLVI>fܭvͯxgS7|h_o g\7i+=դo U!OZO>fo2iLjpncc/2=ݯ̿WWlLc&yɶ6_9I\Aj_HgXmA}kU󚾯3ǛK2éwaVMv^_'MbXVu|f|/T[k6Io$ݹ9nﺵ04ǖ^}O֩fU5[Ǐ͹plȾfWo*G+nz\9G~Pӗo~>Aolv{jm|FzFɻ %gy[rj+UcmzKF+ӝoͺ[sW}aH|+mu,22H3^k#Z?-}oGwsxJ^jN4u=#2jM7'EPv|ՌlS j^Tmn?'j6ݫw ֗i[u_A3/6N_+ꬫH7v7YEt9da}Z><[C*KTKk=Bė3yVt4Zǁ!nSX֬i!vͷ=m4vV >e'`K}B mVWGt$yo. \K8txݼ%y.}n};}u"-hd?5qv[v5뛘[_6F_zz_4R|D1IpyԌ¥]{wzM}xUUڿݯ5OYټMuɻvUY~{KX|/|'-ݤlʫw_6IlX[dUtmR|&nN}5ᦇM-ԛ;Y~U5{nvYYWrk|j7mbn_.o=a*tcJՔeS1=[Y[+Y\*7_3&bkWӌ˹ZW'7OoOo }+k6SM崌~_-Uw}ϵ[2/7:Vj鶗V p4mG3Gmܵ_P ĭV-6H!Zj+nU_/Ws22k/߲uh~ ֥V$K{{XXcݺEY[wn5OxGGѼ#ue7\mhZ=j˶oe Cu *;n-u%-渒fMж$Z=mmMr-SE?d~%F/yYWmGq |>eX፦f,*&Z-~xfݯ ~"%v®Տ?{Ɵ|eK_xIM+lVn3}z8[fMZ^x/7 |p],33+7*֗iug]CRޛ5sqorHpifk/̫.M#ǤxĶ=Ɨ%[\iq5гy Umxu{-Kkx,kvFYoi٠  CR<'x/U\ִ)k&ʭ#7U*ŏMYhjko}> [/m|VFvɷnKm'XC$:;iv:C5cj{5qo}:a:ֿs{}>V,VeHWt7nn MK'!xA_CC}%7V=een37o;Ad}>LeH|mmo{u 42̒Hf_>_ǹcV]y~{6g%4y._~_7_MCMVe;y5B(VI$aUfVfZfoR_ck&k.}^YM|6ݭw^&[g_I5ln4B>-mѶhƬ[}ε Gqqmê2,1̳G"~oq?qs>&l/*?.nomvӾZï^-%]Qa?.6_1U/to_^jjWqw}5&5*Vx SE8=6 ]ƭf_͵~z=Q@Q@5O6uq_iKm:YV,l8mmaKkXcX%ڱZ((((((((((((((*lYRwaq5omBht|;fڪYn<[ka7L˵Y~vCl?lo.&wǵԼ#$XndZtc3 ?o2|Veh!W'̪ۖGE;[B36j,`]ۛe^~7<#j֗$s,yS4l[k}⧃cn(ogo WN^msMs.lo1em۾jɳf 'M/_ik}s]jެ\jUo,mvƭmvWV.~"TԐZ۬f_]ƥ;7Io.}6P.XdefZ5nok<:=h7Mxdyuq 3Z,v3|ͻ5sp/mq7qկ(j>R4k!f5ԾɷɍK{xݙ/Z|i/._\xI}y"*Y۶Yv+{W KoDV/KPٺ5k"j|IGjO<+o=4][E&5[#ie;I q#/ޠ oi{gkq&hԾ~b5ҳ.W*~VwW|Uo?oGޒ9I^Ĵh0U7u5M;R̺vȾ^yj<(T%V)ͨDDgٶ*eCXknw/RC{Zf.ڇOH̿yTkGt,3ŷmCEX5WYW RN%\;x;V k%O"mz7>?Y|dzu%QMv? X#ۻj yMᎭ-/\V[-i.g ^_u-5`_96m2U|ZKgīFG1-{QGE]swn~5͊ߙk3`j::N$ k?nOWX| {\-osWa@|̫*[n.V3,2}x_[dԬf_l5k㵎١omkZYc}s˰&fZ̗ ̷I,v_-v(x ~!Ro|-D- Wn28ve{j\4'u CO ?nhU.W*_QêYG$^A,qY_i2Ax& ]Fþo2OH6߽:r/ uO[mò/Oha]ox~keu}q鶳4sHydUCu +/Umk/>M/ZW~J[';ZG"G34j8e袊((((((((((((((((,#o%7']O/]=W_$_uEvMҮw_xzYP?) 5Qw%^fG(~(#noxANv%/m2$wK ?h jK7,/OjtחM%Fƥu>(4kA'Ke<%yk;.<^9JM]֗+xi}>n9➥,Z]{ci4cZƫY+٠gLJ5%#Oc~E4e?$̿.|iu$4n~3>X_*^8GPna[X$ڿ.ZF]XT*:Q7m;.yK+EoC$Xoݭc>}go>,qokukY&L5wVg?¶ۍ?4c}o>˷oǫm=F}zNln/UUhd?]dgoެMGI 蛮l/o 9ffmɶEz;x]k5a]Jhc!kYfU٦\$n5IvͶd#lkX~-?å[Fllm&ɵ n:Xh> 5VI,1%44my*Mu ?O z,:ķX,MEf2-R=[RUie+_퍛n*|*F*񅏇an"ZciH[,F%^ X 35.!mm;~WvK_ڋGxut>ܸڿwcI!X sB۷Mk>H%[;;].ݢYfi/#]70>Zcik|1Y~XV5W?ڱWZ_YV] Kxb22YwkU~о ~jڏ2ƹoQxum$r75|{v?Tl5[A񆳧I5o.ͨIo}n3nhh?W\/\k&W& }9VK_nfo9RPіmF՚Ѷ?םjN=k7w |-˺O3j]m~ռ?R'shgigiџ75G{y}65Xs}cԯGxEo1vm­@|C7o5-3v7֙.v7tە[_'W|7|ALXyO;|ݷvծG7M;M0G8h-c6|Uݻk6ifݵ|F[X$[Xo-4VUUwFIEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE6[l+G+7vC:ET%cfo?֦[Ok[껷+DY[Qt8m൏ˏ|\/ʷ/kKcǝǣXU׈,,_jZlZ}j˧*jzie_wux֜*}ծsCI\֭ei?ը,(((((((((((((((((((((((((((((((((((_qxnՓ~| !,{_RkRZl̿ݯ~<~ӱ+CþY|ApdrT:j\/y|Q^5 &\,-Yk>vOi-⋈B.۷jk߇>:ռ/(AInff\ĭ~|'UzyWQѾ2Wҩ/#ԍy|OM'sZokV@@(((((((((((((((((((((((((((((((((((-~ߥCrk#uπxͬ5m, ""|p?CsxmҖKn>>f-}q֌h81t#Z>[ZC^>ug3Fە[הѯ%{%k+67OVܶ{v׵f41hH2ҫ[I\֭ei?ž 9%[y|^^cNixwJ5GTP &Y$O{hsDZ_Ẃ񥷙wF |S/O/''GErS|Slu.]'\KY'Um[oڃ6O}}K/V;Ʋ-h2hpk.{Y#}-v~+o՚ ݵkT?-ZVq5P(o[Ik8Wvof=@shjr]^HC3T\Zp\w/@s EJjz-%4.=)is&_\]Bٵo-"ȭ&۹H省:QEVt>#n5i4K9u(t+p2ޫCq$2K$-EVѶ[z%jc%[ZǷ̒O6ڴw--6v\\M$/V-;VI!fzF̬V tQEQH*TVP5<2.䉷+/-PjvwvsGzM(yu<{f+2=&kX.Vvͻm6OiqOicko[vբjZ[5kc Ͷ9.fXF}R ܬȫWf 4Vu-H5K9ݺ;hWo_vigֲ}Y#o*izYjVf co~;Itzmooi2ȻZEPEPյ?A[Vo"[mdUک4^^u-:;hgȭZ-T5sO5+Ȭm|jHƿ&e__8K6e]Ҷ͵V(_/ϙ"ˏmCc3^Ckpg7p,ڭ,nxmVXYVuK=oM,.#fxr}ZEc^xvuiچ_Oy}ߗvԚHchfmi|[2\GkZ}eFӴh iKլu6b\,EPEPEP5#Q7׊՞7ռ%fTu tAo-aL *^t{dW_%o!I;nmZe.^Yxf:#'IIZϙ AIuJۤ6fmZo"ۻr}4!keͬkoo$v /˹}wcӌcyV&RS>7$~MjO]I'<Ov}Je5/I'_K>g'cݮ'⥄vZwMm]+B׮4-W*wWR7Vfǃmuo]ѮV%m.eeݷutZ|ݵ=Q!DAd2.ߙ x^CfE;mzVyVu%V#oZW v|<ǡ唹jUOc|&:E.eHmխYt6^W9ª ,sno1vwA¿/ú*dXcF4~ZrU/xG{/O&5~_6ݻWx76nCZϦ43.Z<)gIuqk%-fm 5tVյ <6ߗKDc)K弳5+卾eeKikxe +t^/ǗZΥLV6[7ͻ48o_b!:rfϋ2\x_Fuh,246ۛVdYe>ݻmeFmS}͙w{j-J8n2|9@ϔKX MKXf&IK&O-pö&UնuZ|33ZáOnY||.ShK_@ֺM|[CmimhmZ;?$+3C ܫ翳k\K Z+^;-[fhV=Bg/s_qq 58jUj|sZω<>׾(Vֹ }J(8aO{ԯʻYv/˻mrNJ(^>R5-*Ymu)o/,ٵ?2eo-co.Mۛn~ۢ>X㦂1xz5G ֭֠'333|̱d$5ƓZk-?Ğ$ĸ̙ZlF_W99UeEukk v1vV>Ekd۫xSռc\5I#G}5]5ڿ/ݭ^#~>mSmjվǣHƭ'ؼԷ|V۫(k}[PF ӵO#ivOFYkզqe|jx[۞,IA}ŧ4+%we>UluZš{{mw+.wڞ>T|U'VW*׬̓PHn&pp kc_wmU/I񿋥zNJ4 ٶqy$]Fdhi#on_o5㏇z/K8mu=-uc+.Y>$~b[rsxV6IuB¶7n-ͨ4}[{yVʻY˭u9lG$Z ]SZNx_O5{W̑Vi6SxN[yq./]M{thc]÷t{Y~U=Y:泥:4^ VeYYxs^+L:}Ρ} 6[i$Kok)Y&WkHUw7UU>2tjEwu&i5;}Gj/eܻo>-/麽n+}&t6ܳMj̻UW@#x?N|KО$ֺMk kS~mfѦYf6wm_[wWtPLj&?QX5k{AceG66ouo'wi_]|@wk':ܒ\\u.O%kmR4}sK߳GM$2mfHY[jAMmrJռC?>4OmeKx̓|ouÙnWN hjk;ɦWɸvc+.ߖOݝ徉o<_nUח7mU$3H*V:K{~j׋uuu 7_V3C/g۵VZƗxEo7T۝^i.&Zf4N[Ē!A^%4R_Ե%cT]|6fK] ռ;y4ZVo$VSC/CD_^xK׵ ;Ot?j)cmt"c|m_x05q6Mt?i~".g٤?m_ץU Jt4?I\;&J-'sX:yZY̘u_4[ }'[w lVkrI+ߚ}}ynjq5ZImukYǚCOKcOM+0ZfߋWW -%z6/^nꚅQҍOicJ%$ Tuz~VVԸWu[h51qyx\[VKdܪۗt7\xz2./VdUk5k#.3}o ^]Fd *j_#5 ; ŪOWCy/XCVpѬm. ber[}&)/M2ݵui6̷Fuo\/^k1}ͬYc_|7|EGE|9}wM_4wa?3o٧.Zq*},bu/ōյp5j۹k .Ed,_ƾ&񖽩x~%ծu.8f.n7ʸw %,[~ݶKGֽ?g.S_k@jWR·1ڪKjƋ7(kUŤvƿ|koiŗ\jy>KIǫ Cݟ4[=KK||\UIQG.*Zr5׏pֲK?q*z]xT H|/k~Sŗ[vϛo̭w-t>,h:uϨZFg[i7,[;뜴NiO?޽2]]KaFo=yگ,čKnǶV]Ukgd#jɷ_~jֳՍͮMKx%wWs/<>*>ǚrh nK^+G4S71W7j6kZ`_vtЫ*lt>5^}_]o/^x^M5oI<<2ݵO_ _}Kz]]X5pno}cѮd=QV8YEm/f]d%\Ũz<gu K(4{eiMmY,}&[4qUz}b Ŝ4mo&om_wWu75^Ң[9#\lv__ǍrDz-x_Rԯ47f۷wwf_kKI֏$nc$qinGW8W-[5ú7z+,vвۻo˹U^(OkM_Hq}Zq}Fyq[o̭_Ix~6~(;XdW8۷rZ|,]sGAE,tZտ{v@ھ]~&ֵ=SZXmM>+顴g$[ylۿY~(^ĭsVڬƟ/AТ$[I4kfei<]7¯PrV;{KKHU~m+2Io)t2_[ê5mwYUguxį^x|7| .$ҭlCj[ɻIr.[:69/K .4y5/.<}#ykmfyڞ&]7?:.bMJ;/m+.꯯x(OYޑçgs-/h/7T|Fb=k6zek",7jx^~ xSczZwMnEo[7V׼=I6Wl: msoxw?A[ed^ݻ@[5/WM<$ -go:Imfʱ1ռex/I֬kZ}]'R.)/$a9$fwB6ګUZ28-_.8]Zx_Ö:#L8cf"mP15m':OhzNkwK$UkUFfWW^ ^ &kֹ-?RЧkHaGoˇeUf>m۫7}Iޑc-ս1 ۖEWj⩬;W[k׋jYƷ3/dWs>(֓T~0fd׵Fu .,2=mfGt/5ďk6֛ZGBH쮖ݺ[l3I"[nߗn[Oy]-ue΍f%O?nvoNnm7TúDc}-m5݆M_hk_i$ګk_<;q6#Z.'˻VXf6km}_X+ñxO&/mYl[G;n(((((((((((+?!ujW mgoI$5^fجݯi~ %NxVa87^ ,]nC_S=S容?VC˱+n2̬]IO_⯉at]]:r*l̿{yW);}, Յw.cM\OޓCkY弼k|~} wM"2rG۫Y5œOXeZ/yC{A8o$UjaF2cq2ȶ3w5_Y,ٶI:+G}wj y&_(Y#kCjkl*:ׇ5Gdm۫ؼAσٴ{_.ou]i^b9#y%<o\57[hv*41&x2piFu#|QodբYg!Iw$\|%k+{|ʲ*jK~Z9.o/c_#ګdEG]د#[cs1_wc7pn5j,w ++|^kljdŚ^m.aI!]{Z/x{Moݦ[&Z /wth~ _U6.馓kHׅ1p4?J2/"f_k_"kc{ExvuXhn!|7w=_]7jp)c $wxrM_CizVnhdV۵UUV]7}g+K&}G}Ci{I'9Ze]_ݷݮ)@n2e#宥Agu[idvy{*sGԮ"Ԛ魿լI3*i6s\|G}vH-mn46I<ɼHF~f~y5k^Oѿ!=KP7P*۳*M$kwޠu8{OxFѺi$ɏnYUUmۤV_WQǬi#ukfmZnk+~oZ>I.,#i!l*=__wĭG[sRMKKԴCZY!gyqo 6ݭn&fz+Oqլ+izmP\xEݵ Ne5/1fm#,jyM⏈eOxeufKY-VeTM?k*@B^MOj|IyJ+;!VkI&[k+/^ouhf=M'G+eYYfhZ_w7>R|`O5 sMnwK(s"вǵr]F>M_I,t+y,vS$Fڌ ۙ6+.дWGgG , BPV[̎9[*#9|G/Zq4wKG>Z7]Sύ-HճZM5\KG^-f=k/urI>PcK{"Ef_kxψwC{g[[.~dIrmwW3{|*5tqUղͻxvfԬMC yH?M,\o k4V:/.ڟḚο7쥿SKÿj^& Uc| Dki;7KMXlM⒩9}UiՕgK}M1xV9dkۻ[Uz7ޯH+?:6=Ǔu?&ߺMw:ֱmhk[w}WGo_V 5emKX2*jxMw*Rcd/xWn> x7ׇ|A.%xvkm{w\/K+h>VFeNJ>h2֮;B_2o28c+U|?y&{5$ݵVN*3>A_øzgu +34[eo/6ϫYu Z}JSt |/Aխt=2 6Z 3,{ޮsƟ <mhmRP߶oVXfl2]!&%mխc y>fowoWY^"~\VmKKMU6oѵr0¥eᶰt6kykdYY}7ݿ5p~|@񧉴ۍ7FOnm>5[{与i$U|UGt}Ed|Y}6k{EU+]FZ=m7V26>qujQ}?/ I|V~/<; \qM9vnVvׂ*aa𮝠mwQ7]6w|5{~xST }njT΍WOhU~{wP}ῇ:nAco y-6Wusz/oAMլ|v?}dZm_QFi6Jy}Z¬U6Uji n٭CR[$Ok䬌Ws}ՠ\« KV֗&-?&I'̬ʻmmԷ_ǭ^EZi\iq]/|Uo]kuOwX[J;6;+Lr~_Ue]{oZ(㋋Y{Gkk={E-oT9$akVUD,mܲ7u} ;_zO w1Iy4SitM$3I6ݹQo°.?n>u5-gOKUUWl6@Gx|5RVt=U[dXcIIj;k6][nLi 6OYeEUVzo\j?1YYUmk~!|1iF[{[֛ͣ&VK$-k9s/| vKhUc[8c6enݻo˵j;w,zխƃ;J).UO+Ck<;ImZ4{Ǫ5妻qȪfЬg qV?\&:_'ŚJ[4SL/j^[qy{cu䍼ٕVf]m][rթh>-CKE5cHiUeݻ~.*mt卿$ \HЬ\G#,+G˷-w{FlXoZS^5±vzT>hqhzS\5pE'|MkH[mzo >|}^h|v3j n_ ~\j*?5x_Y˯AxW2\i> (z‘8OVCC`)rW? 5FwW}f-$E =DxebE;zRǯjvE瓨/Ex?rga־YdWp ?Ewb>3d;\>ZOҼfNEvUu-w+00: CW%o}/øz'J( _ڈζZҢp_ kAEWsyNXL{?y2h$(=(?ƹaKS!1,rsj=+( ( M.ddԁP}N⟂";[|B\FܹǽwQ@{ScnA2F̏ 847 540 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{8:S}:e '͓(}:"ִ{-Fl+̖ѽʬjN;sd~ty}:|I]ټ$7cu15uY$OB9_K}:>5seSJIon+%RE[ (((((((((((+>Kcm{g= v b.Iǵy]ӰZW,a5:VkQMqo"$wV'_^=otG2GyaIWQMh5te Ė. U7Mؑ$=q>1)0n']rxtz/cNڌ\混sL%`{WUj((A4-/!;mto;dj6M[Q"rwhAqqk+3`=r4AV8*7M:xa+FU} ovǙq|ݜc?/⸸& ar;?J|+樒I$\XlU*J1:]6bQE +е: -i_W? ǩxTЭ01fH ?\;+֏W>kJu;؏$Z^%$!J;N9k%4W5mVͦRTޣ#=G# W>kJuσZҿkn[ B1wlsQ!+[ Ntc?1d¹w ZW|B֕Zo=M'm;VX̔2NѼGkrYuPAN('σZҿhsZ/5mk{)㷌K8pWS[?#Tذ]1W>kJuσZҿkԵ =.mm¨NMPd:@\e؍pNp# W>kJuσZҿkRhs:[M:̥#?E-tMFUXVX3PW+е: -i_z4T<$V59c3W7Y~Q5>H?.gVYR$syPk¹w ZWEu[BeZ^@y$ַ̊^q_AIgnTM2Υc$7EZm2)buxX\7 Y| c>X?.ԫ_ gc8b{t3w;&'T&A~ɬ/Z@Cbzp/31gPߍ(1P?WugQ(s4@= &x_뚟u+;f* o,gT>?~*[?2I׍L3ƫ nkJuV6J%;o? \q- &dR#Rp 灒SsZ?\;+ַU[[V\!ҨJYqxMZCAp[<@?\;+֏W>kJu~1ݮMkl'RkJu;hV:Wy9f *Ԓ8 ^܅ec=3σZҿhsZ/?5͌Cp AWS]5Va¹w ZW|B֕Z"ͫZ-Nֺ8j|Ojzn.^mH|CHڀ(¹w ZW|B֕MZݵs8a'$/tv cu ʘFἷvt8"VvwW9W>kJuσZҿkOσZҿhsZ(OσZҿhsZ(OσZҿhsZ(OσZҿhsZ(OσZҿhsZ(OσZҿhsZ(OσZҿhsZ(OσZҿhsZ(OσZҿh+XRx76hF=}vՏ"o>%^T/ +y%8Tݢsocb sgښiNC buWHx=`>O/d۳ƌ6dG#}j_+(G. 2S8@c|׵x@V\^mغ<=ayӒL!i%Y';xNOL`]k[H{]67t+Ewz]Z\V1i Mo8?Z(y6iXn+wPĢH_#Bb|8Z}*x.2;,یcv} )\,q?-|%b:@bUe9^_Ok^gHAo6eb815W<BFHj2]k ߙwRh$6&I6<( =:>OΟoygs%$ C/¯W[#(w3@;ƒI2}[{HcUGKnvqu ai Ps&P?ww _Qϋ< > EԍanUs xNk`uql +,o1VFW֚Wv>#\FpOM+Ǎlt(G\W2XHBv1UgNN5:de^3;A^ocЎw wdc<߷_/vHَ֞G/" /Xv*%| e>xY mFA *I PF:zWIgI։Z4Z0pR$3/4TCiQOzuN&n$=t9ZJ՟ˠ5yu"\dg Ż{wb =ͨETrWڃAuY~@N0T8i[}3Ex_mtUaus nʅ o7czIuG lHw:6!A$ơ'o?cެkƓ{ 'I r O#iAjO)@q˂?zͥ/޻NRESj+ѭOi77&MU%.I/BY+wWQUo:nKgVJ0+M|b 7 9DZGPzM,|I}}( w<=55I\6צ˰Tki bh@ $ovow2iȺU{¬Gč8ϯҸpt M"ooO<ð໸m59\zjo.V&A:Ii5k8Lq(?\456{3Og֡x#?w{ᵏ M"(>z8}(JM\S׵ m=6hGVɓrE5-h/-晦/)Lp+ȯZFϏƚtwM"mC'n4 i1ϧy-s})Z\]lz< oٮ|m?:&_|[8"5܂4<ČgLx|_ukYҖKKa(bai=z@Akqվ[.},lg?w9z4? xNkSh(f#'ccqPm P|mi_hڠT:mhzcmw-T'k &9l5Kp^2IʻZwl;ˢ8kM`t!pݹA֪]RNignepI c՚$RAq* ]O7Ȳ]}7tV,. ꬎyuyn3\r\A+ٶ  Q_<6WG>xd$#Q Iԯ4;+DnRkm?P2&CH#!'{(x? g|=;(VF q|pt[]|\N ɜ2z@?jQEhQګ Ͻ\6XsQ}_Ҵn.'縏ɚFKJcxwtM2\i>ipFOQ}_MTmq,F #G$"T2A𷇠xд(R0$ r3և٥ˏ?>/\~I7:>u"sY"` $ FF dA~:o{x|;t4K-*xqkk0sI_MpKm7vzW76?&m:.0 ׊.;^N8ڣiO&K?4LxwD>/ng/\~Iv;[u(c6=Wn1CyVgYvuy O |S}_MT}P4뫜lHYNk;i.m)xK?4}_ig%1F{y?tK潸oX7j!#wf.?$h4q'@uG}KIPIql0{5 FD_Nu(Hd}; Z4q'G٥ˏ?` ZiO&K?4VE&hg HccN-BHYMIiO&K?4_SдVTT/dmGI @XP&dzsi-Ǎ KL`~UcMf.?$h:t3çv6O HN19j=?ÚvZ~\*&#pQ UϳK?4}_M#MՑUQ2UnaY7uB.m6q -Qr8®_M> Y-`ki"FSni~gncg ?= ՏK?4}_ w]6Ӵ:|ĉ#lzd Ӭ4-#N{?Kqu dY4q'G٥ˏ?L46I20u~/\~IiO&3ݍ"緗|1D`cn8EiO&K?4z}Q'=~P1T<5Yct<1IZFЌ8;UK?4}_GjڢZn /HVkahl9*<7S֤4q'G٥ˏ?L'Ӭo4 t3Ԗuͥ$4FpXzw٥ˏ?>/\~I4nMN[-PI9g9Z[Mf.?$hRbc'Y%ާqkc5ۢ;K݀e5YslS J@5-=a'|,5!h 3(@kͼWcg|.Rȿtk)+;)m+g>EJx$EN|ؼCJXZa?oSSCq{kyU[uޡef/. ,&#NZ|1 `Z0w Ӡn? *ulHmѰ}j|~ZJ$͂H r&[v!p˸D\n#vO CYL6O/p6s7elf]2f :0۸=?4ހz ֑$]@@=5+S-qT&}7g\h dJo.+5L*+9ڧt:eY -MN8+'+u}4DFFą=pHR.'+X% gKVg'9ǽyAЯ.p=}F=L3&ޏ9uRH67UMlmp;R[_Czit=~By01XcFu}5nZݵ 1p3.}s^ '+ᕷ\Z`o{Tڦo^5VQFCasӶGUkmBCݼȬ@\I.=ܚpdhʌpik [q>0Nÿϯ=hjgGOGYyk25iYZޕxm l-ebUIN1^Dtm|2Ե]NdvqCuߏ<]^Nͫ-' &k+[\MunOlj\e;5x'-.^9|3fʌ $TI}{%HӸVEZF +XZg4ݤcTb<[ JݴJWo..-|6&rǿP3s^# Mcsy!+fF&FJnn`̺(c7HF~~K9M&]뀠p:z֧? 7gA.< `y{$x_unl5v:9I*i/ ꚝDe}Me[c6wimq<+)V ';C{ XMQP#lPI=1\53Y? hs\e1łAI?0꿮Ƕ\_Z[D]AR}y<ְ|g[xD5pҠ} O'>p\wjYA0Uch-LJ.|E_ĚvhRE tu1 H1;kasisZ !S?gGJ3(xĻl_S+i ״YN4:Ƙʌ"Z- ;Wm,{XDH% (b2222*Fݏ}Q '١eR9X2j6M PONs^ {g L4)氊E8'8k<9e|*8obŵ杺C ]-ڂ{*~+LKufǔe]s^?{MG]m;H-pocz|6<v SvM} ;{WN0\C8&Ulz`H*FA xhzNnY]åZfPy$=N|%u7b&[K6:(AEPEPEPEPEPYeHuQN? Ҭ侒Ngi1ݜbOPҁϗ)ֻ=R@:85\'N v[f!V,. *,R-X[F?QҒ; 88-9V0qjgkTiyv+HX g'M>XKKwHZ5!~SKhQGT]7m0#gjimY_\LK%;#2ƀ->ɠXMNU K}*FC2nA5N].R 67B :)dӬepY; R@J- ke5]N%2m ݷv:f6fvkx:rP}GdG+;+{uK`q:}+v}C >E&a$Y,^Rwh}sEEx>&??|ͣv=3֥ku `2X:"=$y%!rrqrehVLJBm&# #N^0BjE3ʏف- mu!oPASQ@NH4T~DJ}F9YZ*-Ŵp 鞕bm-+o1dFB=qM66>sz՚(/' e=a8#GTPIEQEQEQEQEQEQE6jZU uukۋ9Jf4WQ~Ia3$oʇ ='a^]O|&g6tۈk&h <_V:&}wo]57-q$sV ,I-U;FQ !sx/xs@k[Gʕzd=X_F'"pW6jZ~CciIkыgSXVW'}/᧙yvLb_:YO;y#}-mY홤 t'C']ɤ>AG'$8ĸ)O|g8mV=NSVjEvg5xOmlu9o-tq$jdC@>ڽ+6btkq+IךVWuyQҠh+c#RIksЯ&>LdM.]9jrOs];Zz\[gk c[tv 05$z󭫘!Ṋ9qE =NO`eq#zdv8\y`1^R.tiVڭư#fռp\ESxkU*(UrE]f},9qQz" ?FF@{o'X.J TK'ar=H h7Wzd>v܏98^aZitVp,Vq' XiYRtS Km>FN#fO5вƴt+]ҧi҆p$/}|2.@+-#Ú>K|XJYV-9+tm;DkZR)硥Ѡ4L5FMJ+yV}E褟ޡjuz{mwzfe/;CC'O^OC-{>)4r v*||Kkk{m.Q*6U3}.O+mj 8}wP/緵Hn)q}r2zzRZZ/xeuB1/Rw)r \zסƳGTlo[n]yltϯn?t툵Ai$ y'h1ɩZohS_{]sZee ܶ,f-(yww /4è@S1XHߓs*ρ7s-T/hȈ`H[j>؋="+K`Kyq ƅex+o֭V-$+`N 2gm*CLw{P?®izYkvPjSϥ@חUbl5IRhfqMtzo@uMJNM;[kfr,>oƻ}jv/#Rv8'_JZw[K}WIv@$11zN&1>]6ڽ욅ikq! ,#?jqE0|CڃꩨK#+ 쐄_,c#Llthm"HK^[-ԄoUg4akv]nS8MhMJ}V-XdӞ4{ռ 9JE4O쫝7>/M[n<j^u5M10ߎxӺ-BFO|IPԢ-*cK[lN=+QuoxL.bX/nc˕0FJ4-2S}BH[s('&1}1\ouሴ= :Onx.`iQp>`TCk_p:GCgH٥1 .*>YxKIkv[.6@*WMExoI jLH㌈ª 1$kOA7?GW͏994\,tQE"(((((((((((Y*AJ5s?+~՚(s?+~\߱5f\߱4yW?Y+yW?UU4rTLv(}ˀ^ĖE Ss.\<_?Ƭ@_?Ə*~W4Po*~WʹcjRh$ xrd`ՉG@yW?UUfTC5ݬ@9,sVR$$0dpH(*~Wʹcj[ʹch.~ E7kZYTje?8߷y㏥z v{bs?+~\߱5fC+yW?UVh UGs?+~cwuuq$:\1vұcic;}J9clq@<_?Ə*~WKmNbQ[\u&F῰Vs^ζ(, /*~Wʹcje z!@Z~_,\ \<_?Ƭ%b;Hs?+~\߱5jOh3[w|۱T ׬Pg,ع̏t4[_sʹchEV<_?Ƭ@_?Ə*~W4Po*~Wʹcj[ʹchEV<_?Ƭ@_?Ə*~W4Po*~Wʹcj[ʹchEV<_?Ƭ@_?Ə*~W4Po*~Wʹcj[ʹchE5sw8hQ@|hwg:%l](I g{}T2N6v֤b0ׁvVv1r0+SW'KҬ,.fDȬQoO ac5mwU%sy9ǽ;ksuik:ng~WEehTSD =vmx6,>G?AzޥTzautYW`NHgMmB+inobʎ,oD=@ Om_3|%x`-Egg¶Y8TE܎3j׏QL.uSq'd`=k<;1iV3Fq$ ΥNT#9 T(i%K \dPm4gÏxcCo*_vB?(,NzOCl6Z]LQ68k<,>!uMX^ƛdqny8_RENJ5gWPsj])-Ms}g-Ε5̒5Gpa2H9'g u]ot-6G5>ځbbC#9sֽCPѴJ, >1bVEL0*>᷆}= _>(B1Ncb.&uyd"8_ _OW(zQ4xox O-FUn hx"B=@ 9U}BKotQ%YPFGN*\9Ufu_5kZIҦ՟Yv{oIJ/F1o44#<m/N/w4N'qm bKyR;D,''t0xGöi'W8yM_kᦍL, <߼}E[]^(# m&HvI9SxsEm&FK@͎ :ۭ-b(DZ/9u-Ş=McB]n[>7R d7YH|n$u}R ?6Ϗ5:*s^Esoak0xHdt@{({F^%\O6͍@׌ ݯuqE yKE" Ϋ`ljE=rXh$ejIuT+c#4kD cͅ7)~mbD$+s>%V'񖑠]ns,nFLoWJ5MkW% h6[F&+4s^&g&5HcI꧒FagYKgZsk(ĐʁǸ<6kOTk?\:XN~4!s@*zumOiyn(8Y\Ha&FN9<ׯuy#Tlo%ڍ< GPևX\n\1nL(#vDnyknie=VE6Tޱ SD}zrٴ0ee܅s^s]NZd1퐓lʬv]vg_`tXzn|L[$/8yܧ,pPyYj>Si*eCpe(~7翭{|feѴbR2zisELNIK [|ݳ U_[[KUk/ ɫi2[MVHc*ijdJ~J?_i#wplxlt L̉T–@=3NҭLI+A<%[) ((((((((((((h@Jc-@_ |/G+7ER~%WQחGK^< euGʬmckR,:GY &Y[/a\_"Vo~)cCԯlu;2NB1YvΕk3bHС*zi-u_ |/G+7EtQ`9Vo>kAY+} Q_ |/]-Xk_"Vohs_o4wJ! [F%}f_"Z(+7EoEkAY(}f,5_ |/G+7EtQ`9Vo>kAY+} Q_ |/]-Xk_"Vohs4zG$*A ]/O|Vh g'S5fy?>/O|Vh g'S5fy?>/O|Vh g'S5fy?>/O|Vh@ #2*)P1!I' ,쵯MmB]vKݰT"}Bbb>C{_y6`g98V<, { >\,cX6fqlٻl :xI[A88GG֩5{Q4oc9#?XwM Z7 Ka?di:lQPgY/ZK.ne#8+Ѭƥ,GYw=>ĺ~{wbd1igd @ROPkGoC_.눝Ʃ=oGJ1iY={lyŮXkC$,#omNӾ/.nyEH~Ԛ۲<cioŪ37Eݸ$sy5wqỽFOȱ\Ǹaq_5Չrms~TR:é\@ <2TG _ʽ24XR5 0$WU{=0eܨNJzfn?4{4VwA+?GA+?@4VwA+?GA+?@4VwA+?GA+?@4VwA+?GA+?@5Ft Yö;}Nig8 c:+;wI_wI_#F%gh%ghF%gh%ghF%gh%ghF%gh%ghF%gh%ghF%gh%gh=o>t?&ZYey}Giwo;bQZTQEQEQEQEQEQEQEQEQEQUuIM!#GVgIe{Ogyo{k*Ȁ8A5|u|;46+=>lWwOmZ욆j #mG,eԵIf. cRK1,9>.cը/E#K+.;xt#! ⻊CNvpRiǩ^VuhnHTX?x#GwssEJYªԒxxExVXӮf)è\H'W Bswqfp̤;jl:uΉ I$OX.Ftn94-DݎD,5#yܭͰ7)߽hWh,= -8F/u-R%6bd[b/,u_Yu'mrA{ ,R2#ҝwyu 7R,PBۢi}e唫59޼[I(X4{:JK)#IYP3raQ]I-t{OOZK$ 0 @uKUOz;zK^#z/>Z\[j3KxZ_,Gu{hk6,<@765XK*K#jv M Q\ývĞov଩di*ZdQ-O:UI/>"xFXeK;twb8TQ %:+uw)yaA]\Gefbg".Y\`h zEyN}oSkU j=$ #V(LLFI'<tM?4X GD)%G4[W󾢊(QEQEQEQEQEQEQEQEQES]C)V`l"p?P\vڦF`ISuYFEnTX9 }x~u(?@s}oA}ch[ēr ʃggڶ|k`|7Kh9,ɍDZ<θSu(Zq{󬼚{yF=0dsؚ~%KW.͸DO.2P>θSP?3tCėv0ƍg$g*@ qOx6M;]:α^kZ`KDPN5yθSϨkzNukWF !Y[ ?fEH{+h/D)ϝLs]q> t}rRn..y*GEπo-mSZdFUW(Xk?Gq>  i]zƯ3>4$eF˄ck^yθST,&7K{ʱVE5Z[mWR {VE=O:}Oy xq ^sszίk0ܨ *qsθSyxgW[Du;O+I caAc4x@TW:rtZH[†cs^\ϩ:}O7y-\u=p zj̭$Pq;p73cCƧyi%ƥ>dysθS3|&D#x Eِ{[ d BJ`o>WC\ϩ:}OxfY[FS]L.-n\ dų}bOk7:A$)Fc\ϩ:}OwM߉ FIs,A[ .㊿N=cVՠk{V_GQI 5θS4Uo:}Oye*q> V|-{o5Y-Q4l`.[ t5}?q~_(dSmI#| c{j]ikIofřԪŸ'ǽwW{iiio%n9۵l}?q~_(dQp<\o{?5heӖvFN8Eb[O&ڇY<7~[lyߗ >/'`i3̵oh ,u+F-n,|>FO~cS'k]^8 -& ,3`}dC;G#w•-o E/x5+(P۝6(̀ʫ}?q~_(dQ{+AdK\Nj~յV~jJdy &3y<9#e}F&!"@8 O.?տšvwC4$$T*@)qƭ}Kyqƭ}K\ :7y_׫BH-`DEAfDI$RjoG"!(ƫkh ^A)\{i}h|-wq{xuCcm鞞Oj<]4sk p7c&c;0:I+]zX 暚O})I19ǦqQ@֦Q@Q@S%u7UP6 ]vn{kya{bc$w d>_€'(ו_J]C+F@Vf/Am7vlu5.(\Xfq}}aafYd6v'f5^Veeiq>hGf6N 'sE:+SxkX5]2[yVjsUSRX7o{.NPE=#9\P@OI]PTzO??='v@QQa? t ?:IEG.(a%@OI]PTzO??='v@QQa? t KЭ-为 5,I j=I#P*4xRX4:XnWHQ sOaX.IEG.(a%@OI]PTzO??='v@QQa? t ?:IEG.(a%@OI]PTzO??='v@QQa? t ?:IEG.(a%VԬ->1%meHaa? t ,9=Vjb8ȊcíۡArG.ISEtzL1[ͨoEܝ:D~tZݴgk$j. Y4Pkڵ77,88ГM>:]u ޫ!6vdc=]M7 2:V=S&Yc4{*O0QF`vᤅP~VW^'iSAhX(ݱ[^;VW7b5[K[`6̾\d$k؝w#.Jc#CN Dלa@3a{?乿PeݴKxucrq3ۭN*?"ӵJJ\i d1chѵ,u-NZ;~A#6X>ilj[+Cv H`';gffiw#|KiEHir7 ɩ|%{_~#sgu -,TPU]{W_nJ'KN1t($`]'厑|iLj!Fr٭@`F,9Rg6Oo>bOR[ -j#8ݑ+gq}/e}-k"%Veϣ[ii/ڵ]-O毘]r@'n1z W,K Oyk=tsyR{B]NڞexXS2JP$0; 5na~lڞo#d9PUVd|62\w/٧oyp$ycEhVZiI-_@v`{1S~2ռX7Z ?5`B@0N2{Uռ)}!.uiCm?v9<mF kiw{=Kgy+KOu|1e'Q,ח7$^LeF ~@WƚfzӠѵYLJٚ(b矗k1ixt"}QMWEHS*-$'J?='`έiz6d~Nc[=)ᗇWë D3>qު.ֿO)+ω|Q/~%yi,3ڧWؿ#YZH]Ӊ @zv_ VUmʼX H5::Ғב]q\}'I&9F2v_4ZZ?%Lo?|EHN66WzH1T(1#[(^{+I4o1xu[]-BbU t\-QnSo03ZMT_-֤//a&Pf1}ќJO{[oMBдkrk:Ki&Mm(zO?FioMq4^ OO^sx6ohz}HkhG6Ȭ%2Ow GweacsCRK]`IhfIӼyirifߊAV[)#*<$e ceRA \GYxSRfgZW1%LZ<{Q#c' g< |T>#h`S1)zYOE~x5؊TО#]J6 S5IJxC7vGIO%ɇـᮑ6yZhVw.Qq mTfGWx]J[S3rNrG8|om?!)%? <SlZi˨Ett7H60zk>/_ x+)m/ux'n%m!#o9'ӵz^AyKwud,_k$Cry\S Lz #Y\Řo qt4_>O/gku^^xi6,dm9|CⷅAvŽԖ Xrc c=Mw׈/Owx$Х䓞jǁ4wJY+b 9=M^/ԝ_*)ҵ=;6rj:Ʃ 4MqlA@7u}++sVZEpSo~ԋcoÅI.PzsW4?$JK2V8(B(F`Y \o{o: VK,3ī'$gր;:+>%麕nmibqFEYioC_:ݜs\X&D, C;;*+K*D&[\_$IDqXrN)gצht[ ShېpmHފ߅=KOh6z/n z$,ntO{MV=ilvn Co5Zڔ,bZ[t7`gIĝH[ ?U+UV$t!=ZCuOyz1$-#Й(ZZφn4+SMcfX qaǓHhu-4 c-:o=DžnumXkܯ#2ܶrO鷺Ƒ+MNY=56K8県wU<\͏~17zdfHܜ דoúNq,KDW8z~-ޝ7)ZbݖpOn} xvRϸq>0HxĿdY5 keb?3p0<׌x'z vڙ"I.RB 3V?A^NR/$ImtN~W>U8BR(ⴺÒZ,|FD8+cº4Nb[VT?(,3MӴj:l@jc*ƹ-R-Bw7Zs}45-~ҡ@Ge# 9&W6ӷϡ1u {犧*CG9^#GH<7qg-͵m$ܸ7N{}mXjΕŧ[LǼ9,ĤsEsӨ((((((((((((((((((((IJ!nB.I$4muF9' QzW0ů!I$tSbP+KGNX0`R `s}XnR)"J4 O$b{ Ϳn|Cc 8gWR?ׯRx7GHfې|#}JhEks=pV,o$im2e=Oq>2<~"j&UDcV_!]@ b/& ϿO 7Fgii]-3LZTPlZ"Ҭq=m>rNg9=j?G4?}MN3*oݜ3֢+YZBFOseku4A46^HI~/QլNKgY_:}ՕC=+tlFp(_}},oqUtqb,-̞^یg޴ %vhkAH #E0XZ-o.QI ҵhn_&/[Kmb.:!۲Y?n_&!۲Y?n_&5^ko?c,g[ijMes4mz,Mؖ޼ɿ?,ytv^? /Gݗg ן7@ݗes4bYz&ijMes4mz,Mؖ޼ɿ?,ytmz,Mv^? /G%?2o.K?^d]v^? /Gݗg ן7@ݗes4bYz&ijMes4mz,Mؖ޼ɿ?,ytmz,Mv^? /G%?2o.K?^d]v^? /Gݗg ן7@ݗes4bYz&ijMes4mz,Mؖ޼ɿ?,ytmz,Mv^? /G%?2o.K?^d]v^? /Gݗg ן7@ݗes4bYz&ijMes4mz,Mؖ޼ɿ?,ytmz,Mv^? /G%?2o.K?^d]v^? /Gݗg ן7@tI2,С#b zQ^hVw3߾Vo:ZyR_¯#$wS̑N2ĞjvG%#FrP,,-" =I>6P]J&q=5=d\SLү 9ˈed _av^}^ ' #F}]v\²ȲFNEKEP\F n dZ UkbFl`JHKn*}\g;]k1_^,B%"EAdv&3Ľn@tmuY{[xZ f?+cq֮uH|Qmn:m@yV%E$'iaqַ9_BM.kE.lח.}hV840\|O!n*Njuw[|#lͧEM&p}QޗPqx[i^(tMJV;h%6r8ڧfˍx-Ą.O6B=jFoO5 é-uL@LUΣgi}was3Cz5xVPMB ,fqpW? !9< ='ñXkj4owj[1 t)iOs7-<rI?.G\UN%:Df*8uܥ8 Zm{UmKZ+K-` tzSm*ύ|gi6kq1 ۈ9VeYŴkJxki8"YHt&7=͋˳t'8dWnzc9Kj#I-.u@H)y^YFG(&/oɋkUdEt ;udy1VL_տZPGo7[M5Edy1VL_տZPGo7[M5Edy1VL_տZPGo7[M5Edy1VL_տZPGo7[M5Edy1VL_տZPGo7[M5Edy1VL_տZPGo7[M5EgZm(IO=Xbf7bf7bfj6 udcF5DYi<;-x+|ںYq>,NT ^-]]h%yisëyQ[+mhܳt@Ҿ!_UⰱKbuIvW+yK M u[3[T@H*^sj <7_IeUv~’۽_9~_7WŧԢՃs6ssM+wk<8݇w$y] X@$: 5eoi, v#PK@]?ĻK ֚u֥m֊b=Id5}M~$x ['Х%r 8'⟈^{a}y ~͹ qk'\5oYxRЯ4l {O;Fֵ-VWx|; Ac~qwdPc#p x& og]c hڥجH[tdh ێj=?oo-,?h<2#, @zZcUյo+q(XoaSfӎx-'S}0 m@޻i%/-"ytİ. 0:x\݆ZkB*iL 8y+5+k,wBd&{Sݸg$Mt>_;N_åi4F]όH>fl}?}Q/9 @9>6ww3:6mdur6>Q=Nk9<|5pnf#ĨTX0vûV.l.3wSl%xފww1-O`Ei ǖSql'װ'm<@c^Kq/%Y@l>Eadq߀luMV=SY忍bK+(Tx$^il WQQ[kAP=A87sPIQxc=.ऊOB2?wᗇ;Ỉ5iLspۥJ<+tAP|?Mp\rм︿?}P3xqTg' }Q//*:+OGrм︿(_9?^?_UBxqTg' }Q//*:+OGrм︿(_9?^?_UBxqTg' }Q//*:'o*OI.TES?ϾT/*.QTOb? U?>Sؿ<ObOϾT/*>SؿES?ϾT/*.QTOb? U?>Sؿ<ObOϾT/*>SؿES?ϾT/*.QPX܋T#&vQ@ZYtĶ{[."eWd.2wc^ i+_8& B|ݾnth=¾?#HMdL`\q9?jZDm/ѵ+MBKHw 81kخ|6Z%fL̒m$8bH-ClVi)fM W$|(~"կBw[~/}-m @@%L՟?x/XԼ3X\جM#FQrGw\wV7 úmlm{(_0n r;n}{kRlg=%L9Amj snMUhB!s֖iVN/}*/1|v 5vƱXŏc˷$12;b4>Ӷ65[F!T+ F*4yL_ښy b`p?}lzMXijR˝=0{{h7:uZYYFOcȯ%=X ] bfLdr888֟͠jԗWZrkJc fb2~nO8C[ oL4t{p[np}El _棦kp\ψokAu9+GEpOP2Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@z"y#E"y#E!p< yYb1yYb1yYb1yA6ޞW:B10JѶGOsTt i堞K#q3JGAV/?o|޷O*/?o|޷Ox~]?弈۱d6 xs@ln4i^G.9eN]4bKʪ2̤UK BoÌyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ7oڳ&Sqyƀ#-:$ r|ġ '#m7l[:Q@% Dvm+k`nZ\ Bp2kK#XMkP3Y[Hm)e 8GGGx:t]5M&LPA 摪LKpN-KMg$sra_saC\ WPGi\R^l}ݜNڅtW9ĚujZLhZʌ$?+gzڿ᳨xWs ='/=jĮ.%s&R3]w+M^<4&{[j>։CS,cppp|u>xL-[]W+M^<45|kӼa lxauvUY<[yXhocVG8]*|Mq& g8!KO^@~"վoeH$O24c1^?G+M9-sa VdKy)C0͹y+jo _]#M' 89|֟+M^<4=D6Z幆O_ͥ CfnsPh4OqadkHԑ㞇ϗ+M^<4 #? =֔J2Ó*_ xCUЮTxI#0B7q~yz?HgILzFmE:Z G ,\Z oWDPE[ĂY0ʝ J|[yXhoce{;]6m:*J6@s!3ĜzW]xRϢ^X鳷/d["=rAp3Ӛ/VV?<[yXh "Wտ畏&/VV?Zz?@^<4yz*+M^<4j?G+MZz?@^<4yz+P^~{_𫛪wy(X-RP1+clze@5#^KFrտisG#^KFrտisG#^KFrտisG#^KFrտisG#^KFrտisG#^KFrտisG#^KFrտisG#^KFrտisG#^KFrտisG#^KFrտisG#^KUvmv˟lxG]z .jX.4eY>mIcI!s8ϭmEG]&^&şڰ-M fvŘWj'kS_t-Z]: %u'`wC_*R\M+V$Ε%yL=~lr:ֶ|?&_ךC?tiV9A>6{wxZI{Jk䴊 Axzt_x׎u}"E\{TDiId6T_E~ O;]?NJr6'.n<+ RI IqG#\Wm~kVBN.Uku1dtZ}Jt'~!CBִ eǰr 1` nC:-6~ƈB ox/;~Iqaag`g/J |IJ}> W,^C::p܎oY/M;u5;Np` ǭ.wC(aЌNt 4]\!HY{?@B9q_){}SazRZXnT6PH랇&4P_QuCm"hCϸC`y{VhO^h:GN2X95÷ vk_;ɢ[]wR q)?/$Wg?(}/ZID͆ $d '$%S QO ?R[sGۛ~/?O H@Z1'n\K63DPha2r:zv7QΖh6|늽 :t: M:L7߿o=vڽֳc?BYB9KjQSX|'׵ +]ң}vڜ'ڼx@#jhZ淢>}L?ZZVr--57DHs* ,n4G1{X,o?LǰKM*+Kx/ Ad^ydmb~UFۊ}) (((((((((((((((+3?}AV3?}AV&) Py?N|MhgMoVyʅ7]&^&㋤_K&.5HJ]RhD́ҳ~zyݜQx@ԴˍGOl.l-&udp3&Rj^45wZ`Qw: OzS^|es5>c ̻!3Y;gvw>dDh8Egwk|w[O%𶱮Oh*j0leڤ9C^@|^4?LVu&aoi!FYB=dњvq($2@ 䑌vpzo|5k.TY]"W?6fx[ռE&LѼCYMbY4n.|ď8OZ:S4i=iqgDG(eL U-3ƞ. 7]Ӯeg Ĩ'@+ 7ӵl,\2 f@,GLƍY}6KH5 B9#J%d8'O3NXZ:3+0ٯմ|%-=/平#QQ[nxy5Ok^͊q]R֭&tx^JLkJZ+nW\/.|{I,w"DY KvI\޹~ٝgTgg=#A#xwO'۵>qyS#p>Rǒ%妺 ~abHhIQ` |/Ý.CԦMٝ6{ʇނnNZZS(WKϩGX5e.qǑǽ@JSY}>7J+aING^G:zƛekH숌ѧGÃkZ_u?C{o*Y|<zK{_p}!-DN7Tth:YiZڡ ( $~up-ƻ/SD֡SFmF1Xձ㞜sQ|6]b6}<7,Ƙ۰#b$s-wcGjj6ּ?㍤1ϭtx^JLkJZ+nWGw#LӬ.FOb+F%X 5Ǿia7r$Eo=8?yu?x %ՕpľQ _c=yt[ ͬ_鷨kԆ9dy!H5>gg\[Wi$l\t?Z5~qw SOӞarXI$V?mO ]i:~Wuo%$V22n*H_tH=\G%ȝv#`#tznerm<)yJsރ^+}]_6Zwn.+/6 )mЍƣ> t|<1ZHe-h$X#3V֗*:NsDѤSGqorʀsG[6-ά]a&ȶ2,$?ynDqcm^>B`X$dy:Z^4{+^ǫhoKq$qo`{\+Ũm- M,w-12k#58մ *XloDpm㓜Um+]>ėur.$PXF A=x#S|*[[{6Sr#ړ9$f>6jw,i2BA <?T7hz^mߴoXGk#su{t|<tzbmiy if6o2 'Q|IZ1,*Wڍ|^/쵩`*[{70g9A5 ߈P5-SOVo2Edrʿ̐FIO5IGjRkZz$7u!~?7Zk3, Ltd9q^#]75k&N'hIXl$cRxoFMOonl&ޥda| qzZݿwTSӡ^kup^5n٫?5}d,w &}t_RpƓvi_CҪh|Qo1B7 W|L[h5=F8d\l#p&l@d֧¸nS \i_kh?ESimtTSѼi5E2 $Ua6LU0g&gf *u=}֧e ֺՕEKtV1g.NF:nɴW=N,5+[{ D7*-#:Q.c$j3Bn/-( ݍ漣ᾉE =Wzm-˺A =+VcjRΕW!ӧ*P u=9G4߇YF%gz*jmΡ[Y6ڣ›jZޙ\%͔9S8aӽ`WmLc?bT͸vx~!Ԇ%?5`[wtQ"a]LAO3ooV#5<7ŬEtWDLbMی?Zj]_^QգKAF$S 8]v/_K[\fN&1_Gn6mH/>4MmB ?Z1mF<kRg`gw,7Q XDDhc(kN=F5/BUi$3}=}kPQEQEC<Ed@Ո}MEQEQEQEQE Zۥԗ)Ks  (((mmm1A[lHdNsSQ@Q@bѵVKRHSx# ڢ1u/ 躕褧qCyhi7\-A98<=k"V"AnqcCkCmpƒ jTz:TPEPEPEPEPY!Y!5QHew@{ѹ#ehÅaazv MCMqkBIL3K{Hek|o[_׬unv! 9mK`6퐒AQ]xX|Mb`\r5>bpyZ|D+>V_Iaq(@*l9'9 ;3ִ:HtKX(9S[wo$P4 Xzx"9u+jpGƒz:q={m2:%kI$p2HRAҵ/e. :; -4BK;~b ݿGo[EyVcVkrl"x !'#ީx[/5& ԭa<_cuMm`yc}l.{>ֿ ^2YαCap2F˕Z."x")lEgy~_]`=+oqDEWn~٧ZωdǦڱ)P 3"A<7fD A؄X^:i7Vދ!G9r>e'p:g^o_x> uOK"Xb$/vKI8$t;>!.^mt-bU818|nqGp{%gKcN=y=z}дo E,rx95S׵@%~xKa mLHqzе/P<r\v[4+&Op6#3Iq򚭯|@<-/m5䶒;9vN[ӱTh?E:ɈF7RA!2x~I((((((((((+3?}AV3?}AV&) Py?\G^~F-6KL3DX sk ׼ uZ<9ivI7 r-=t:=ᧇj~nԋܳ^i;cۓY|$ч.|;{'bKk|mz "|B4bQe}Z[{h&;G9r1]W%NןOUNxg,>lv=b[>fuϨjՑ|"$0cg>h[O]H\ޙ-[jc=>x7SkVwy7LȨLQ6r9/ZOu]:WN:u /~? _6G.Y,\-zj'Ï}vT|KqdI,v6c }lVVJbAUVwrDqjYW3Ž\]^-h^g;qRYLl,g#Tm"KgU[::g _ov-;I_Ԯ5Ui T`; ֞8m2;_~vLcPA☮_iVW(̉J/i'؎qҲk*;z.xkxc"WWrNsiY5t;d7NkZkN>˥%WƱe -NxW=&M7Zŧ%-۷%?) =NH[^P{=Bxy!6d"{k<"tK-CNmqmm:'N]mO'mYdRwT|ەx|M{oMCiLs7RHm =:R^Fx\vw7 F1h'IY X`{K,H@.}$μ~ ?|c-4[E[V, *d/ t>_Rd~gfӠ)wnzN9SO]wHz ;&̩sڝ;`:nMc$+d \U)X[u7V&iuKMQ|xBO;@~?Ȼ:>;/Jԟ,y>Ы01'Ԏ;MXݚtCpB|<9RmlI5[˫svm ^( Ķ9 hO45}_LxI!R t}ݛ?h3} et[]JGJh !jk?HV`Vxo/t'M681KfKڮ]mF$F@Nr9$ H|ԭ/5}j֎w!h9<֭qZ-ޓyrw% qw)RAV1Vn=q+\2j@C,l_N;u:W=&k_K^~ݹ)q]ft,l.nE6u Wi/&G5{uC^!K>Оno#ݝu=~).P(eW`P,y۴c#kW/U@cYnZPpF96JⅅRϣ I8L18$ ]sTze-5[#Yn۰XJڎiBߊm K[H/R儎T#zj@W/T:e 80m9+=Oism52 E/ہnc9ݴ=BmӮ&LaiS4W]8V&/ ;}\K{XuO4 qAϯj*+ufG}Qm+iLyB95źo2ZU?GBI hZz( +ωI_?귋"u,>^݅werƭ?ӭ.۫+U%5(@ |줜vWx%ѵ$1@eJR46G4ө$3" `ywVQE ( ( xE_Pխ:xE_Pՠ C(?O'ſ ^xieսIѼʜ ׼ VM_YD* ~1ot+Mn^X]m<Cp'y`}?:;ּs:,SڥכkFCaGI}F_i,p89xDԴ Vؑ4Jc d=4]? <{c\O)hHʧۻzս'Ve2ɬ-}>c]՗r0޽)Ķ:c[k[egWUbyխ'Z~Z^cwvC pN֕U]H55ZsXKKX+.{^>&׵ ie;8`nio_nҿ"P~ڰS⇇nj)-a[X0A.:z_'_2_xA/t4yC_uydMpq ּYhwFXͷvɥ qRASyP?xvm1LYޗnT::sâyu?_^}vE_1- co|u=8Xs,okbBbA[;i:׋uF>mۉHb%e,N V/ 鯦khcfdt۵ s[t]gI{MFբ5{!3?V4Ohڭ(PN:t4׻fsjz6\Opd 8v#?t>ͯZ־ҦΕj^yg6;Wn+ON:W{k?_]e)՜|EѼIi`]YX{2SoGFΤ#I-wz⋋`u]MbBlTo`Zc]5 b3%=M漏*Cj:tNHZgD JǩjVF@l<Ezւz9υZơxTX- .$f|x(01íM+MѯLM HH1ãG'^E+溏|ֱ0ȖIwG~Mtckn\TGhymqWQ}5۷. :}Hۀڅ$Z ׆#h)+X8|qu4SSXJ4/H gmkY#]ifi#0m@ϟ8q^E4 \=G߈O ?j7kY5iVF@yۗzҳو&זg)'kh\K_wtf"%HmSdr>\M-oǞ-6IX%ϝ6^E5/KM0էn$Ni?`,{F-&Y@vm5ui1Y-t٭^͵7D$`]X'nH,C䮎4w$-tc^+;|E[>Vntn繎eE_1Sn{E MrZ׈s۬qY*dJ}kqEi~[Ong ay.m7l@ir.ߥI5`@OQUqXw9uy{pq(b ?7$}J!M YAʃ\WkEG*+Y࿇6O}WȱFTC*QsںmW~9om8HLcun~Wr,QE!Q@Q@fx<"?j֝fx<"?jQE!uj'k4ι&wiO k$nvpy]v MCMl~|wjxj7mq5$#'8^zoy|c]rM.K5 -@c8$8^T*Zɍі+A;+/DBZoɮ7WmWzvI6^چʗp`b- &)k s%뺽t<rHǿJ|U|+h Jxm>q#i9]}C5$E$ѳ%L=҅rag(zzM{-(:A]M ;jyy4#Hլu cjAyfm@K6pc8U|3OkzxMQﴨ9lq1⽚yR$f j]djvzΙm '?smKS\6hNa7a)[K ;}<ĺf;;4^;ddUQ}*-߉+VJ gO?OZ:*mДhe\7?y۸*Ny׿5c:=u_g&g|q !#^ɭiݑmܰ}Ttͤ:!da7Iwj]?Ȥu [}GỎڪXʻM #ֻ?xbZ335=/wi:h߽Ϟo_m_j)t=%le].YI W$IӴ&|U`@N(KK]?x7xCTմ{EL+|<\RQT]?[imRd cҾ执ڙM&=C`r8j;/ x~; JIaI$2 orv CŶ Sj鿺zq^+פ}ĺόu+_&mMK@ vx};ֳ{ERHӥP[FHC>'95KfTkXaFuV y^;J/_V]˞?do#DYE6 nCQpnzdz]Qk:ԆYWx*Hʟ^Р\],m,yf?[ $fM;QvɍJVL#,9^fY$gr- (S7MMIM:K,VS#sW=KiZg_ ]Jzͧf%`.+"?i7}IzjǔFAl,Bt+U*(Uf]u:}ԣl[$n9`<է4{Lկ5 ]:'ɹ@1?UXՇ]ۤBFE+iowסiq(xYW,ˌ@g5k-lZH5 mvE^};Q|榱_G%Z%ŷo< 60Qv&1[ wG>_T|AmџhF rWuinͨ Dgd`s~Vqn,-.l մ7Bf@v ia؛-B0Og>R1EeDx?\VyT,N+>YxqWu/~#oð$'oj/ hzh}#Mg38~ 95K緊HX\ɀ dӗ1G<6r~yzn5ح&n`:cQ㎵ǡhCvmO={ y-Jv>q2: ׭Ťif[ kcyh˫i:}#$ G`z[5]{LotMF $1[#kNu Xj>#537Kp[ovO Oឱ}p%ilY)9>9Bм-AM{9rʌbJ@+ԯdAd=^X^.-`,aЏOGr/ާ^0sgA 9@& z_]uz%qb;nc}L*%sv_O\suRY`H9!#> os|1=<&s&erI%`I;Cºa\kW±OrB\7B,s>eI4gJ"ԭie mi  B:\`NA[K_xZvkS1'nq?Wn֗ 3[ܐ!%U`x w^}f0tHTÚ$6Em)V%@W1ޔz[?|OaӦ_?tβlVV'$w/##Nv񂽻Melj)6Ka2)v6K>gmgoUK *NO4KlrC2F}i= ƞ M^,RK8Y\'iݑҬx3^6R5,o&!5ƒz:eē]<?Ԁ *ͭ@+ES#t}V EcAwx7}gR{ĝ'bH?{he5y3?SYZ_Z3R&.-ml͸^Z:5ؙqnv-cжWQ@Q@fx<"?j֝fx<"?jQE!uj'kr KM{vvWU awWqPy?Y)6ZmB3%ePaecSTPmƜo@[7$/nl+9k[V:KO{IvLC1'rjρ-W[T孤 (GZ }0ijFER)TS߾i`f7uvXvxR]JI%Ԛ"[%Vx{xϱNRǞ{bšAwozvF 4lśv#| {BkOu)+ahrr@pU]6G#_>7mZZ 6D?twq޳hזRjZwWfK_',,FFpI.XwĿx~NK%#kzeu k־-kn>mF<̃$\`{&ݜ.q{WE|5 ǧDR{u8+9;Ø\x|˺)dEVU h~Hh\čY!ݹ8HfX|$ͅƉ-݆/$AHƫK q&ŖD`떁d.don$x!5#iEsa07U;zR[ok׌Si^>BiLyطMx5,<"Bj=@SK!>t ǩ ds0PEPEPEPEPEPEPEPEPEPEPEPEPEPEPY!Y!5QHew@{ُZck?O(:( (((((((((((((((((((((((((((DŽ_ZӬDŽ_Z(2 MCMl~j'kR;6/?P 0hG`bh}(Z*/=i{E}0hG`b%L/Q?_ؠ h}(Z*/=i{E}0hG`b%L/Q?_ؠ h}(Z*/=i{E}0hG`b%L/Q?_ؠ h}(Z*/=i{E}0hG`b%L/Q?_ؠ h}(Z*/=i{E}0hG`b%L/Q?_ؠ h}(Z*/=i{E}0hG`b%L/Q?_ؠ h}(Z*/=i{E}0hG`b%DŽ_Z?_ج~x%IQ`(Ȯǐ*٭ ?_UjQ@f0qT}VkoG٭ ?_UjQ@f0qT}VkoG٭ ?_UjQ@f0qT}VkoG٭ ?_UjQ@f0qT}VkoG٭ ?_UjQ@f0qT}VkoG٭ ?_UjQ@f0qT}VkoG٭ ?_UjS&!䑂O`(;/*[߸.@YF#afN)|G|$٭ ?_Uf0qU/ϝ";Q;o ?EEkoG٭ ?_UK#e[U=wzW*GP~O/*[߸|ws3 }֖mwϝ""5/*Qj?m'm@a~5{uneݢ,m@a~Cq(,0HK Ո{?;Kxs@m@a~5Tb6_6mpO@}QkoRڏI(P_f0qT}Tv>vm@a~_;Q;o ?Ev>v/*[߸OGϝ""5$Fme9$@}SG|$׽ZC~#Y*P8Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@gx@{Ѭ Qy?@,o2߰nϟ~}JikX i\h=k֛M?zD4gxCeZkxfM3%P᳴:f==Sԧ$TkjjhqjVڜ-tW=\%։⛍?:6V5ʤox8 s_WX:rj6QEm&u1uFāBո<+oX۽óv)a'tӚY#\"5 -ӥ&yx1]u?Ď#'խI:^i)..MޠFQzWK6M~^ILlM>8$$1\V?'O? 6o9ǩJ@E<;K>dČ B[ai5 #r2~cUƏ˪Gtӄb]$kǽpxSUw #gwc ǾӾ0Q"wdq$׾5}{h!Ƭ&u*Sm_;oa W֗Yo{nac){V/Mp>]w'M}}z$bTUnƎ((7uko Mi\n2}@`)M%ڊJh4Fc0߰@ ;mzλɭiz # mdxaQ^Yz59;H *m6uiQЗb<x{V?jU=ݫoutA!`D n(_)R)> |K?+ HEqnd%cY7IltljakBP:hm* p3\# 375 476 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((Q" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{8:S}:e '͓(}:ԚO_jvhMq,oc=d~ty}: t~B)XdN:}j麬 NOA(G͓뱸mCͩ`2 Ҽ Pr*m/o=*X^DJ%1MKd~uo־ZE0Xq-$G'npl<9=2L3$$3lKy}:[xQM†h>`7ug=pOmx\3L&G;y}:U g&V=G T2|<",\ʉ3B=Er^loΏ6O^;QwPI`-D|Ϟ9ǿ|;erXo`e+W!H#Q;y_d~uYu^ie[81$ʎxp_lܒ-{ :S͓!xmuŵHfXbېѾk:L{k`^"$€Ž86OG'F_vA -[.%,x/bYƍ]iiݦ1rqGo0yO'͓75se2ʬ$aj **ه<ňzu'-ϣWz=垑i2FFUԎ;<+iq4^ɪZ4lG5SXxCĈ "2WhfUd$Go/|2ݭIUSHWR?BFSuuk{֒e/95KVk$ӮRŢZH\@z)MZX<ϣ> tU[[XϦC-0,KKTϩB۲| xum-t#סR:WQJ[c)6 ]a Y[ܽ}O#ѾC +Z w2&ӑƼΊwⲲ]ǻC_I$& +GI6s6ZCʭxwƺ=焼6u]%<d2 )=q_?QG`ѵ+_tMk]Ӯu-E/!D0'>Oz+iZT1ƩmxTyLlh^%E M ߨ$z귉siv>ݿ+`d Sj|c5K y\g^ ^*IZKיnj-6>M:Kr4=k-gS5?W$5'hsŻqx!x#Ze:2s_]GiUe)V>Ӆ8&Kjx!T1ˢ)SjRU%w>w_߃Zº*W߃ZW~kIe 5[ 6KtˉepzxN״&h{K5T 9w_߃Zº(w_߃Zº(w_߃Zº(w_߃Zº(w_߃Zº(w_߃Zºu I,´e0$vZW߃ZW~kIe P1e<ZBt{MF$H.c"0Q@B֓»w ZO/WUY:&m\P[G:5gʛC63 -i? G+е2tWuwom#mU&XH20 :{/ -i? G+е2uTP+ -i? G+е2t}(<1yA!GUj9_W~kIe ?];'+9_W~kIe ?];'+څͭR\[FsB֓»w ZO/WUErB֓»w ZO/WUErB֓»w ZO/WUErB֓»w ZO/WUUu BN'%aC#m碏s];'(w_{n[f3Ă6 ,kN9_W~kIe ?];'+9_W~kIe ?];'+9_W~kIe ?];'+9_W~kIe ?];'+9_W~kIe ?];'+9_W~kIe ?];'+u M6+xam dt[߃ZW~kIe ꨠ($_ĎUHAEa/oGc X}CWxkLIO;9vyVH*,Q{ƽ;)Id0\*ܮB~^9^ ^%kgԣOlIx%hL{Vj|:WGJ<3\H.dFKOgZ7:_jt{(H#ZS 7V֮Z)Xc,V"P[Սw4k(Dԣ\,3ҫALoo1B<۷vv9N+2Y|Doǫ64]_PHn흢*J%$8 J/i ׯ mw$bbT `9$\7g0\k!CAt=`i^ _uM'QTF4juFŎ: uI^뺦tkڍT9$IϾGa]O\ZIm>>($9\k:5i0^6вsKyq|mwϦ_]^61-ΐE qoNMz4Wm52DWp9KPPo.-Evʣ{s×ZgAM YX.< z{vM$a_&⟈:h:UF,vbی{SY^;힑x43 %pC]< Qu 4֗/: T}jýwFI-Vʊ07qޗB#ƽs5̪$ ;;u-GG*;v:03]5.KX=]ipjy'ZrJ Z? [xRQ{gܡ"0 ;vcGs]|-t"Xeh䌞ef}ϡsXi7\IvjPZ5ї^NkFb J z!It~y,۝9}A^i ffFYH#.y8UQOGkEm^A=xB<5Mo[|`u9n5 Kf $%8$~#[p|]0d $ăh;b-g:K33HxQW.|-\kHz$ q4]oĚ޷7֡I"ʌtRoZΫi7rMnU$ca u1]֯TfKm␢̤ӽZkuGLi'g;X$ߍRhVgx]GQizqs5%ģi$cd&bK^,n@ ~F%i$2:S0OW5Zxv[knY 08ǁI-nkE ( ( ( ( ( ( ( B@< KjEwE-l#LzWixZw'tYn伜@v sH5O$.GHO4)q M<ÂɞlהS/<=i6:EypB^L`vJK7"%d2C+yl7.q pAǥy$w&]㴒-"yQv>84'WvDKu6PIJO[J_Q\5JN6imtle{IHYX0zVv|7qh|:v"vg,tn۞U㽵[8`{T<,)J>W5=^ 9/6\.N=1V,,@&˜si\Q^G˛]o2Z^Fx}ef F?({Fy }16֖\0HK4uB$]/vt3ը&oSH+qC 5םF ÜyIn-=}]Ic3n#J,G^ay&?wIiaow{q-^Ncv_Б5Q*P[XŬJd`1RG<`z+jo+uxW$ $o!c̷h?*䜒;mgvVM>kmq%vi;Kc,H=*\_ýwĞ!Q46N̈́&I0#^y GA>-Ğ*tPA{"y¢O'\z+G_O–p>/#!cӿgjSzyll4hh佴CqNW 2Tn{HneIbO@; xQɡ64p5ɖH̟ˮ1>zMxĚŷZv&Kqp#o@q޶>xTvf9]告wz}E ((((((((((((*ouw^i$SD#B1*|ӥ_T7N/~{Qx/:nG{sggIL`e+<}}k,y|>!tխ{- rJg=_%ρuGL4z ʑ`NA< (pzuxsT;[un#FzqFmd"mWVTBDRܡ+hw}ʿ뷺֣=b2ڭ( 0R/ۙ4IIaĪWyE `jS8lotu5}k2gR\67aj Q\[g~Ԫl (;+Tބ'9oҲ'?5- cVӥM .hLSFf@I`01^E/!}O9ncXu+\^sZXB3nzWJEo`mt4SyտO owG=-7WlO׭jxc~9&9!H PNqƊ[=߂%y~kKH~ww=3\Tf|#yhS6G1gz1޼:8N<7^kvڅHǥM`I,zеbmܗ:#iꋨ0\m#=1I fu,L s5庮s1H`?:g2$ڨ$<%mi2cdyUt?x]7Hխ. J#~U}~M W6Iuu{f ._Lc+y-M MCA/-l4ˤkVݽ0@sK5W]?Y˷#b)+j]i<1~aiFG^ޕͣ]N]!HD[deIC0S$:}QX\$mBH;ZV{[ԨEQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@_/۝n-{{ٌsJd"j42YttەvC5? w5q Ƨgks>2yn=k6-Gk6择}U/6yRqY:=7C5gլnر eۀs\'=OSEv񭍼J̳@IiJ#1nun/!toU}+exmfWV,T N*q6[ާ izv:i+ 7}=?1\O?4kV5)frOCYl|/wMdPX4ȩn7N+>HIc#I-̒GdBC3ӼEY:ίx='%/$>h$sRJVlq)5 [̑H.Ѳz-M>;{ ^ Dћ6#7(jw|DntwKmJ!jEV-"/ĝ;ML6-Gc к_8ϭrd>Gu!A߯lg޴. o Aյk ,ttH-lAioGqxz~tb**[=Ixúv4ZH(.@QӾExw&Kn!jksDWBS/L&/02 I]\/h1|Oia AP,79B~57A5m>УUjs䫧Hг008Yt|NeGw k{m=(PQESkidKKI|2#'@(hM tr>{@@(hM tr*/w!En?]exk'}&yT+,+;e">{@@(hM tr>{@@(hM trYҮq!v;8dhT>u@G.s"]E=+ ##֥vTRTw'*-Qobo}~!X~!l@(lG-P*-Qobo}~!X)j ۣm䅆IO-2{:) 4U?^&CESEn?]hM U?^&CESEn?]hM U?^&CESEn?]hM U?^&CEU7&ux$Hd$*Nv # @C?|穪9G=LumMax:XYGri7᥏j~SF?x׋NhZt{Y,A`IVcpV|Kk4mS[.8HE7 H'qW?R_>3:߉/eqNz}kd471m=GC4vZ_jޟh_%Ŭ ЈdaX?ѥҿU?*:K5oB-}}k}}kڳJė][&(EmN:֮qh(bo-WOii]>Wv$Q~ϲZϴªɥj #؋+ncU`8 㸯(xv:ıT%D6m; d ơeΏ NQr1g)ɶ$z mi_l 8TƟz$QQeC}G5iZ^ /wyK+ZJbVxpN_ i5=B5{YQdm#zmN[?dh?dh?xO5xN${@ XYݼ'Ũw])64,rnG>k>[h2MBM`l*6 5q'CFúHٜX8$6^pr+I+;y_D]kOhRhXMbM؝|sn3*7>m'u-A-#gYH\Ƽ_[k Vl7ؔQ%(euoamllz}vxi!ج|Fp1T]7+^v6!OF/.tl+DvKJ_ gFl"HOiGj#` vj_FCK~h 6NQ\;UqK rI ;k[,Z\e^H[;5 /_fA% 4壟׾hzŵĚ-ե,1#/-wH{($H]dscɪ[5 /_fA% 4\V6hm/ѳP߅W+fA% 4l?$Ƌ+fA% 4l?$Ƌ+fA% 4l?$Ƌ+fA% 4l?$ƋuA5[fA% 5 ݭʹKQG` RņA5 4g֦隔i,3nc 㦮ig,XEUah_ i+SWr@y$c&4 >M5o2v#6/Q6/P) Aӯ,.-@o3G '85($nȐmi&?++€it) n`7*8 `tƐ, ܃|޼V@?G@?EYAV:DL;`hgVMBi VܬS;s`]G@?G@?@S_:Wl:WlKOQ`_ `_ "\jB"X) ?QVJAb8#HtU𨯭hdO1[hnv}P߅6j_EP߅6j_EP߅6j_EP߅6j_EP߅6j_EP߅6j_EP߅6j_Eg!_WDW*,t3m *QVe7\ʃ@U$dӅjC?|bi? t ?4X:yKm??J{`r7 F']nk)XP[$"!3`<.p}S4X:ؚO,?¼Qxƾ># 3XE"$IN:f(ž#KGLRGv΃IeQ2+Bcbi? t Т34X:ؚO,?´( M'?G&@O (?IeQ2+Bcbi? t Т34X:gG#еNW[y 8G KMS]+M?]e})Y~C!ޗ.Skxƒԁ{WHho#yn_jC:- cdy9”VXg0Y$Vr2F1Oƿl_:ٯ ?fq5ZZX=tڸrF oZ<0JOYݷuJi\WH5?Ƹ#CbҮΥ+.}sȪgU/O6/6,Ƅe[9K`}k^e/7`$?*?:[jC W9׎_ jvu) $o=qOwmWӛY쌱ɴ qޚWMgZዽ=cfPY#X0%QE^&IM48gJ%s^ []/>.t%Υ {p#?3 im;GgZ[]K{h4e;:ji^Wcѿ4X:ؚO,?´(3?IeQ2+Bcbi? t Т34X:ؚO,?´( M'?G&@O (KkV+h%\$jVS_+bqw-KmEri&wL"Kqd?0n>_*ȶ޹izͽ\%.NkҿcKhُ}/@VOx_߃NⱩEel>_߃@V_}~ 1ϥ4Eel>_߃@V_}~ 1ϥ4Eel>_߃@V_}~ 1ϥ4Y"^&ُ}/.t"%s %H3x:F*FAhzFiaZ5$NI$O'BOK+He'>O_6rӴt)V9 oq'Q( )rA1<}PGq@ެtbv+G mS@tCƗmks}mawm$fwnV$޻?g(?_r |%-lniIf?699=+m&|K A#H!PA~ǫ7zY#M[4os4 FR}6[k2͐C`nUtMVX6#YPABGpII?_re(nǜS]O Mշer(KXtHg&HU) ޢ?_re(@֯K 3}^jWkW MjIfޝǫ7eh,,ᵳaBGUU}7Jm:;yo%3^z}/F}W/o9@[sJ?&JOldc=ĘvS-,RH\nq yn3QIPO+_(}Cɴy\!bŕrOnl>_߃N|4a5Iuyypew}pˎ8~65MKQa{J I!rzbcKhُ}/cRُ}/f?5(?cKhRُ}/f?5(?cKhRُ}/f?"_+bCMfQ,I-d.@5>j!Q_^Kx I0e.T\ ;q=S c?+ixAk(Wt >Pk~_-Z05ĖRƢ@Wv3zѥſeoh?_vz6O٭ĺ|Qd*GU#8 >Xj6G$R(ff1XdnN¹WzY>ǪjQ³Iyn7vB4jy(/7Ӟh~ǪzY{ϱ[[r.wf#oS]Eje',p,V?ޏq?sWg??Am)]?ivHϥ\F?ڏ!>suKj?_;Un C}/}W7Q)ivHϥ\F?ڏ!>su+Kye)+ 2qQΏiDO7D.Z3q!>sTRPc?=S kQLFOO7}T/;ZPOO7}T/;ZPOO7}T/;ZPOO7}T/;ZPOO7}T/;ZPOO7}T/;ZP6-K{ o03+0?yMލ/8Ě,ZmYw)ֻ43K?:5 }Mu gmܗQ·R2t|q qK2:VoQis5 7#isGSbE:~V<Y?Gٜc$ VDҮfLndpXkAk5ZhLoN(nם+^V>`4K/kEzK5;7'$~tic`f2]X'$z&LX 1,:cNX-Š(QEQEVw״ uA4z;%Գ|sA)Wc?O²~x+ln5ku ]+I;ċ8IK+{HtUBVFX?ڬkX>'^92v?>jƻm^Qo %r$O'jm[̂ ŊIcDhc`jՓoht vP[lB'bio<o"3\,[]Bj??*J(4. sz}W}%mQEQEQEQEQEQEQEcYGW1Uʧg!_WDW) +*9XKNxvZ2}q+? ۦ϶_F7}? ꨢsۦ϶_F7}? ꨢsۦ϶_F7}? ꨢsۦ϶_F7}? ꨢsۦ϶_F7}? ꨢsۦ϶_F7}? ꨢsۦ϶_F7}? ꨢsۦ϶_F7}? ꨢsۦ϶_Q %`V {{wGJ;/_'h\ǤϮ~.”Zi#C?t ~QEaG2 |:T{E }bBROEgmbhWp[iP}>oҀ4p=e9?Uv_O'@=e9?Uv_O'@=e9?Uv_O'@=e9?Uv_O'@=e9?Uv_O'@=e9?Uv_O'@RվD/VGLF#Wf݃'=3QfR]-eP%rH#'-i_TMmOojuQE+MmOojuQE+MmOojuQE+MmOojuQE+MmOojuQE+MmOojuQE+MmOojuQEO.qd&$ĢDumW~~ڿT#蘪q|; ESQEQEQEQEQEQEQEVw״ uA4⫽fIkY[]G49Bˏ#s W[CPm,4cq#3zϵw(HdW~jDIqƽl_ :]'>O*:mۭ0J? h7T[@uE.7T[@uE.7T[@j_&;1-cv*} +7Y=v@ɻbU;Tw=?:mn J?&Jv9Sw1b??*Jڢ)((((((Ƴ_+bNB:|RU(ͥ[?"H]́%%J98 I?]6S\m#g(G+$d@U6?>ӪMIg?@U6?>ӪMIg?@U6?>ӪMIg?@U6?>ӪMIg?@U6?>ӪMIg?@U6?>ӪMIg?@U6?>ӪMIg?@U6?>ӪMIg?@U6?v7+%Fђ$bFF3j%:.kzU}<2ެَRAX.Mu,GeL6.nx?o.$-^<6 pŸ}e^]nPrH 9PrZW2i|ՁUa|6? (-O&H pI>΀)`Zk{_t]Xݩ;EOc_ ?V7jFSy(?-OՍڧQTv?k=:. _ucv<n?睏"+`Zk{_t]Xݪ;EOc_ ?V7jFOy([,c)nA9$KRO'#ZmKbB@?+/oy& s*AAjͧ*-d$ynq %NszӢ)(((((((+;/_'kFE GMfkZ曡ZƯy vg5SE~]m#U)b6Hgki}sJ^7VG<)xCK`gL$$ Ґ}/?SO c~5cNqѤީ_ 8m;KG@QFu-n wQFu-n wQFSmt)o5 h^Gޱ4cRZQYnX$UtaVz?j?hw< Xc7k?cƫO}&ݬυ?v>?j?hw< Xc7k?cƫO}&ݬυ?v>6?j?hw< eK%ply*: O8Vh4٦p%YA$T4oۚ_A}QGxAF?MIۚ_A}T{4oۚ_A}QGxAF?MIۚ_A}T{4oۚ_A}QGxAF?MIۚ_A}T{4oۚ_A}QGxAF?MAީ4$F^?5Uf@՚((((((((((((((((mm!GAUUK81?'?7!\6{xzh~muq +K$.QoZݎD ͐'0?;f?GK.ljmJ$ViFu [^xFg2\ۼcҀΛf?6jg~[6 C.nIm[/ NA9o|z֖ZӤ$8 .㹊+ d Olա\uUWF7VvwW (Š((((((((((((((( {ASW֟˿Q# ,'8wƲ# {HY>XtP@JeNBo-t۹?qÔhwd1X_ot{м;X=\-2gy+}Q7o8yw+N, 1 cvZNxU[]>+m׌tI)wAmIi7_Jĺχ/fh]>syn遑׽zMv @$|(.Վ⑲ϰ`qR>0|N\M.}yw?ji$3nzֶ4'Ğ!λki.,#0s]#U(ǛEc#gPnao9c2c8q]U׵=SU 371 558 0 C     C   )" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?EP{2I5IcMK\F?w~t֍Qg+MJR|@u$‘ w&rgXY(W>)!SI7Jϫ?F[:rCQ#kRTbN뚨|O}ievn2 Xג:էփ{|kfsz-ǫ.6zϳ/or8y|7 Rj+Y&4+$wE(QTbW kkozYE|-H]#*`^s^hs|?]+Ze5Vi U {֩>[C( O~^iv^)CRGOki(!G><)xb-T|(ȋhǵ|Cx2ƫ,Z%ZDCRH `pxͨO|xM{z6$_f+mF9L[E,ʵ>'|T~xUoė>cX(Ž_ 7o?͟t{I"dY۵6T\J*i:hZv\zI J[pRJ;Хm.+]/뭏 xMwK>nۥջheIR285 ߇"|CǃkԼ E%#g~ z_~$j~W|Mo:G4t=e+Dexb?O?/7;~/{Oj]#28|ѺdnWW?X~؟OQ"L)okn՚MU"mRtd[&kx?-gxUNLJͩjow˵Rzzzp?`~؟OR}?ȯo&:GQ{m^Q/JwͶ/&(R B}MdOK]e Z۫e_i4ֈHFxR]s?Q؟OQ"2wx]?Oîji~$Pt$l4͆CbtlO(bu?\ThG0r7|>؟OW3QM"'ShGTs)}?ȣEs?QtlO(bu?\ThG0r7|>؟OW3QM"'ShGTs)}?ȣEs?QtlO(bu?\ThG0r7|>؟OW3QM"'ShGTs)}?ȣEs?QtlO(bu?\ThG0r7|>؟OW3QM"'ShGTs)}?ȣEs?QtlO(bu?\ThG0r7|>؟OW3QM"|UkfAIU{GS,%Q#n8J%Kxn[@a9 .~YB5?xOM*u/¿t͕;n+Ҩ9]u]$Wȱx䀠Yp001+lˍ:0i$ZG[,Rgc2$qQpgk:5Px:Q,M.t>Vݹz}<ßx66 erCi oEm9*p3iQI?J(s.>~}QI?J(٠g]G}$(.>~QGA?캏IQ]G}$+fu캏IWaE;8j?/GٵŠ=vqf_ҏj?/]{4ͨϬf_һ (h9QYJ>ͨϬvQs6>}QYJ(٠gmG}e(6>QGA?ڏQmG}e+fٵڏWaE;8j?/GٵŠ=vqf_ҏj?/]{4ͨϬf_һ (h9QYJ>ͨϬvQs6>}QI?J(٠g]G}$(.>~QGA?캏IT7v:=f|OϊxRx#O'8/c6rE1Ƹb*zKAN9W^bDa"9'x'%PTψ<[ڛOJ@HՉFI=M'ў'7=G\LJogMM!fyZXkv;Jgg~ǟ4_tPhePZ2$AvΟaLJn|s-SGdV׆ m&X(;p?ik%:֌-ő*#XF]N7 ă0)%+oq'~kIm3I4CUH}5HYTnfWJzğ$o5O Oii*$[m+nuQڝx>]SľՓ\Ԭimwf\e>:J(Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@V-6qr2pqߚUdHPv vLga89׾ x{ž#'K[됂8m,́ұC\ѬgIjim~ O<⥇m>qIIIx/ܥf}AKv־FSм5j Z"%uM+n>ZG7z[#?!zh_ˌ}ͲL`` J]r,t/ x4Vkoq67X|KK_ښv/1,a<呏ncR|6Yz$^Pm-!XT1ɿf/#'jJ{ 8uqkɻ<'}V?/WM>I&D ̻)~:Uxg_熠K')OY  o~S[j5ű#+~G=E_Ï_ 5/Xk~k@#T qr;uc(_|MԼC'"-n"&Z26$|By \ GKŶ߉;%;(e[IpfRBc5z_Oit;ko[hd&)"۝ GSU<[?G$Fx`?[ZQofQ}5ߴ}o[ֿh,Y#{m=TecNC/<+>7#d$Kg1s{*9[,:6_XxPŖ sk!RD?2qO />#|9ú+ifXj!8:?i Hkw>~x$Z<%\Z֗e}lKVB]ۛ`=:^eB>Z%*[G4#ۉ09( S#x[X-|+/iu*7ʠ@VbC0Pp=M,lp|Gg8C i{;dž׍PXk)&mh|D >º_/|5-kAT]#6GR{mԆ<{|)kFcj}gT.8 yVķrH[^5Ӭ 69^$+a*v39ݎy$dȾArx7RftHэ#8?\Ptvz1wLנ24R۴>r6B6ϵueҰ?_U딶5+xd#oއ¬/Jc\gYYa2h̛yeVl ]4/VKu$3yZŋI~^9L^y?gWψ;^ Tmo[m<~d;Yvp:z7h]2m@KcN]Z+=̐1ڲ:[Ҫ~˾-<[f=kU[o5mu*J"sq u_x'<'uKm4 'Sq`"Y Иu37_k> m8u8H"[&v;Q֟p^dޥg/Ğf#Xn0Fcܼ>r3ox:cAas5VO*A X*"_?|-uq6>[$Wl8*y8c]?ƿi5ֽDy 0a9 ~5oK^-<h+kwᗊ%#)ɰo?k5O Z]ȵ5'k"7̥YH ב@>ͬA5zf`b+|#V\|;ۿ 4Ztuc>#)?KoK[u+%cSbFHmHlBxO ?j6O4Vn9;/#3E:O K[ kZKC\3 L=?>"ozeu_g 3Yʛc̆4<` Zk}I<E[\G$lO֠a3mh~B8пi+[t%hᖦs4VuuiBɖ߿tE] x##&cF$3(B[)++"qБN5?xGu:%PYd+Ȫ61YL~ u6jiPxb8#:]>P݌ck'j|'Z ﯴ4ku*|6S mφh> lo$P wAͷks]'_-/IVz^5wUrI ǵh?|ILFk5ԡ|1,c v ?_IU5_mH<ݥЅR6[tZ]S? \˩ OztQK#_><6Cte_2ch{irs]N.]8 ]/?**899k> 뺏iˬ`;9$nUqӭW\Gh~V-?ĺKG". ϳ[]]7'w_޿izȴ>\)ur\1wmSWth𷈼Wi}·F+$E\1\>=Eq칫5^Bk.#{YdT wc2z'ޕشU40h+=sw]Nկ5>u庾i`9^^i^0 4GHΕb2^ 'ʁ9K{lQE!Q@V'mGBitok#XEy c'  mόmk[EI\iC<_!UO1yswv>=S?I"ĺ^XX8Z>M7 j?My<9mܭȔn,%n+S6~ g W$ 4FA{HlqNS_> |[j*ֶnb`=Mǟ4eφ4O-UOȕLm z |@ͅ,qKfR ##y[ƓX{i3N}JT[5c- (yUW>k:\ڜju.rCEoOw'xUBimou1!Fl6Oj[fFד˩ZݙLANzW'_Va9>xr`T+ pFFyϣYt3B1yyR=. zҜv _;@k -Vī&G]AqNO5OxL5XUmfTzא7;K ?<3iCP细82? P'^h| >|eMZ}φ,GsIfZ!#Fc+vcSIώxs@.7>o-&{DtAk,K6j⏈$:I&Y$e8.N~\ t7>Ql%}&?\Z{cYZ6yp*oB juue0yjWKZO?o |]#mՎ林R $q˲5w7S]|]c}:vEΤb(J*GQ^O^/,mtmv#ӚSI{ff˂C֧kx᮱h>)Mnj:Z21H1Ji u85=šՎ=+Fܴwg qoү5)uXl&r@p_(}u%|_@j/N-(xn)XXVIKFqO;&?%:qiuXX)U[wS\m]MAHԴiVQê48UE8vt~u~^>#xǥxO =!Y|#k4?[k~">{Amk,*o8w~2ӟΣxZ҃^XMX>v\8 Ŏ)_}??i_ At[sxi'ItȁG#@^5<Bj+lD^I;t+l<aѴCusD-*8= t4sJ^[ZzwnLFU8o3W>hEKupaw4-J d⡽~Jzq )B9c5[O#d_EY_x{!+}Mn](L:Fo9/|%'Ë b]CY73a7 I3@;|uxռ#c*ċ#E]~ČM|p' 𾝥Gk4'R"g71!珵þ0ҵ}ZgpϵNm kZO$[˥ML3OJ:Cq_uTuo E$DYmNKnf^3B;௎>_^5X#IsomJi'#׊҇4-;YZ}3QM>n-9bgqpWcѯZa&H[uz yw~_4&>hϋ4#y@YKQ}.z rρo^5s;Jh*]?kG_ OS,K+pQ!JO |qtOWiLeR 2Ot+~= EۄG#K0' q\ǀ|mm|6W@UT4C#u(yNd+S<$jw7V~$0yDJ?^i>ؽ;?×b[%F6y1O"\~:?$2(ld;0r=+?%t xAfIg<(UQIgr;Wg-P"?-tH/.fKgK?<^K#k z[0xdFBA޻ZkZ?oxjSW:ZѮgYHdשhίX^iWW#% )4(QEQEQEQE)k zU]5;:8xQ“ԵtӬ':~s=}  pH,5;8l[]h{*$7W#J9,<{5|Q~C4XhkW 1 3 Q!-Ff)4{FWIMDiA,ݜ 5^W Iu-q~Ǻ%Eŭƥ̹[QԶ ݏu_7tMW]OX5j(i2z jCD|,V6xdI4(\dch%…> :oK;E|6Z.>%*щlHیW5ƿ dh_T/qqvkŸ'5o}mplI,c10x<i_Kt隧[i0=eTYn;_ß'uu,I۵9$gx՟=7bOOeY>iȠDG^Aﶤ\^hMo+[6UG ڪ">,| E0ʲ/6iRA~*Zj AoUX7y-<ͼ o8*~pĞp%kzW>snQc&/{x6-a f:4bN9v[Wz;l6j;Zpslf^)^>+?m/>Ֆtw?hΉʻ1Z> yRwf)|`mYdyj:^TNqtN ZbW ҽ7/4돊ȯ#]bR8TYۈRTJנz/ TZ!Ck,) s\OWA~1gQГF}jzF-4,.|HqpÏJs +_|F|Z~tF].!d3tk+@ /ϢxtX=Դ*]+f&@cWR89vQ_~:n7O \h':Zs~hp&zHA'6.-Nsnӽ7's+ <N+=M3iJ`HdWqY3Y{ |*,mt%.n|wK)CJui2 ,@k_ j#|gubŗܴ~^?nq^R-ĆX~M^[HE@v{g@\xj^fk}6+M"r(sՅ|uoEVټ~tF?<_֯_?o'A;-gy$4tt>Fފ)Ojl_v+vc|"?m!_ iZ 4:ԮͺRHZ^WZѤ~|9;4iVgXL,$Sz?밯C_<,y|I=$ !1@ҒWd.8e'|%9l4:LtއjZ_xO}M~5k ml :w}{3Onṥqj6\Ŵ9-czuOF<{O -4h-fk "_81.21y?SOu/ڟGa_vd s~%a_~$]:M&d"$thʰD2X`,tk3gAkվ)A;7Doh1q=O#8>Xh!񕗊/ҧ:պ4~]?1Y:__xOBλZ?.-HZ`YPL_x^Қ4\4K{EK! ``clk¨_|o>+_IMSGm¦|y^^滟sok 'U|J]D-se y=SimZ KŠyB̙$0IEs蝋6qFX_,3S񶋧==c[uѦ_hgXDS"+A?o|K/]-|G5%Pul9k]!l1:ƥ'[sq&cE,]%)4/FPASx⧅<j~.\Zpn!e2LΆOuʕNNo<0kvziX?K3wR n0O|RbAAh(@|c|"ӵ[?i:}iF A8B|uxB|3/K[֞kU]NgDY\.!Fv65HxCΑk\!O4H:x+9ՉQT_,kԴQ?n<37i 7=oP^O5k$E4Mm幖[e<**2fѴJwW>m ;xdfXGcexLW(*ys_U}/^n"]Rhp.;v~H!b_|#ừ{~&Vi^dJQ@EWhzXGKo Ho跰z]|NQH8GZl B3_*:;߅~tSE5W~'Mrpzp?<[s ƥ?_@5&-z(KjzfBp5Up|b@ɣpDž5O70+ jQ~FAAJ\k{Mo{l@o*Y#!ARU]@_@84+ ='F(4I"PZecؠ<k4'kVK#m,ëخѷoOSVv%=.}!?^5Jv k"Ip2:d{(a^X7ėXgs{M0=̘Eaj'~$_|E/=g7GQE{&jSp& ccw T\w>]@_LqH#58EQdjmiw~4|[eԵkO Cbֱf'!To_ž~0d_ZL0gO@pe_ xGƇȷ6,,=֏ذ[)T|rn[ǟ |1;JNNkYdip1%XW pyj(|g|+dؒCަ ?O8/es71%p95@G~ 'iQh^cҥ{5 eK:ºWΠt$к{ۭ6f3 :qkQ@y_ x;LӋ䌆Y'3UٯᗉZ&Լcqܽ.\`Hs^EqZ;߇e=RˬJ@ x4Oͼn Wncpvl hV*-; ՋI-+K܌'R Bʃ2qz6>|FG;OR*/@~7`qg:Vk^O kZ5# g(;Sf6m ck{oed#HU_^;dg5nH8Q@{~W躵^iw([g$+( jQ@ڇmWQѯ#Ggk X^3cz?aGZ+ihI$,B3ms'wqӓ5@{Ͽ9DfK:^J jg{t kog7 ;ʫ- $ry<פ@~N+^Hag3zdVPEPEPEP^oO ^xWS!/u,'ۏ0zHڔC]I "vFaGzM\-}t/j^4U{/i'hR 2=}GPWλ>}V[p qIk6w{R|V 5ƏabmaL*0 !Imj+^×^;k{?`[i7D`p'5xஉ]-֌%ŖTF"d8f9^e ߉~[x߇=2;6"e$[%`wWkWm+:5 r ;Ėѫ:9!$_-]IV'75=e~=,n4X ¬h %/K:xj_7J/`nl#1]O>/iCmf.4 [[[^/,nmҳk6}F5{EH *VJ-7h#v*[kd   QUήS׍uVq2bDԌn.u5 M;s[}Z]W_זq;ޡ@v{e> jֺ|NDWmmovѿ䏔ԷerJ+~|S hiqj+,SJ͹`wm2+W-|%≼=[^2-`K?m>_"X+N{WO:GKmkכy6N{'Rmg^֯iJcmC.ݸ$cH.{R}hgumJmKq[ifeDybY:{__S>!E$t9ma7sʧ&X)EP3ph E|[?|ZּOxDG?*<Ѹ(i\0=I׭|8oR?x[X)ӵ9^7v;RQ\t@🏟|QxJB]2ƛFOBg\]?hpIxY3D-̱l4Wźe4IW1^FngTlu}VAүu;C4RM[Ÿ]oK𯉼=o++Q+{d,VΝJ/ v-3R%K h# `O~CГ@z-3xŵxf+GvH5V<0`sW:oo U}u=Edv\9G߂<]ZcN!ң f~cÃڞ3'qDֱi>:T%rhļju/wFO<.$2:$q8sJ 3Vt"nQsXW~&L_ Mx?714#fG9J]RxV⏆kVwze\M?';>Co;;H/u{Ng ƳB,$ tm/IokZely*]F3 ><-._xN""iL.̄ėC/xn_xfTd{y%%SeJcqsP|9i+QZ6wmX>aڻ|ujJtGsV KTtY4]@x'gYD*COf,5Mukddi<(WCUY XrEjj_-#ʰ 9gu=L?|VK+-V1O"ZBX^Cu"<[|eOo|5Z`X@MlgzAP ޳ykzHI8/,Jem㯵s: XO ^C_Jyc1ǽREPaݱ]%Lկm!}OC⿎ X_7GO(i;[fc~?߳~]GҴj:u4q@^Kew,Z{W_Sš Ѵx4^G( lѨT`fNF:|@EVT'U$@y/yW9ŋ]sēZƇm]3Ngq:G('|(V˴r;@ e^%ӯVe]2K$u3y`O5W[p|]7^j}4gEm^ /. 6 b;<1WonNbJV!%@9qF6=? |N5ěVzvc\JK "؞Ii]/'xS}5-vO*gw :?:z+ݪ:uɳ,v0uBA?Y" x>-no,%d8 sEp'  ^'Ӭ|Ou孶pI#Hq/~Ѻׅ<໸|QDfFcPEX>)6m#Egy+,!ɛP1#f $yc7D~4 =MO/p؅U}r;Wx':F5΁av56Yryg`}ME GWV>X7|A kA+[TJHeH[ OI4xJ xMexm@j0N1It&WyJ2m8_z>jO;=~+#K{½;yW!/OumuZȀ]WĻOVni2p#Aosk$3w(#+95b74xX/bu!y{tb]?~|x|P'sk/ inkv[pi.Oz< u!)o 1AX,rW>ru: AamnQ'̲HiW`XijZzIe^Go"< ib8\W5}K^eßCMu|9}wZMn!Q)F Tczއ<aֶ:e] |'zlI++j^_|s|k}h7־BxY<( ܅ *>ow|9B[">}u=~,_#7˒\ H1TzW//H巎][K~?A=qWYxKQzf]WTib"ۼcrzO~ x a$Mxdd̘PF<@x#~&\{M_O% [Ki.d0ӞkKǞix_\Լ?{ֱhRgGaF6򞢽?02>1EL>Y6x@ Oϧi>.{9x"^KH+:+Zg<_<@گf_478"aO9J)_ϕ'|;n XV:o-<]N8Y`l0Uz3#-3Q 6;Xѡr1nn WQ@X^MƫJgu-/ceHề6n˴ rItW_$ҼeIkZ7/lme5 /ѣ(G _(,yo<9c[2鳝>]'M( 7.zs\Ï 2|Kt7SyW:[A$GcYVXkD>} /cȠV>tZ<{YhwVCh:@O{V n8ƞ5gk 6$zԥE5i<~~i~*֭ٚi4{ӯ7FT$A=~WS[t->PDžQ kijxS:?5UDỸ Ux@':Şs{WlSE& 0zέQx2O&iYXD!Iz+3nľ2?{cY> 7ڒۤSkqF/7*^*}r/|?qkI{Ce!N=1_EW|u]WR:_|SXhNmkCC3 **y ?eKxIRX[ (H ;~*7Эo7#͍G55HS#'LBK(m2:P;QHN g8So>(x*v֏e dpyS 9rc'gYEPEPX^<MҮ>ɪ^iǕ3ʍf Y׌4 OiSN Ep Pg:zj/_t/Wޛokb|3[:N'rw99't+ϋ>$? h*;8lu{QP$ ܐwP.cCÃ!QR>6uxbwYx)c;!_'T`7y/R|; si>5H߄n.bl'h#1g1pTq&}/AֵR${f.nLͼT6>Q{T&MrNR\ BԒpQts.3q𯅼!Ǻ-kisCy7TrʎʐWW,̬T^E 0ZS00 -8~^ |Icqtu_]EmlD9R0 aOp~ ׇ%{ஃht1Cw7KF,r~+`ptаFvƋGJ`þ񦅧^Y3BD85/h:o z2xgE5{é:5##9W =jڡrrp1L[H1x(ci/êʑJG^GqeobEiqӜ&̿Khg[P];W=Obk>6Q MVa,Gʊ3ܶ01׊:bϋ%dƗW݉V6mPH=TaǥP$mţx~4R ɢ㱊Hp>=GGtiv,7qYJaZVO[]W>!MWBtt-2OK/RDH *c<_eIy1?iy lwtק ^|wxcßsx츹;QT& o$*tg<5|?|K[[K[ VH;@=~ЬA ;Œt hD`3д|Lx% :[oC43Jr خs\_?]]i-5kb6ysevsWR˶DYцE k"ғZ~1~x/ rkw%IJS"P HX'z3?.Z-%.|k{s Ò A5CA PxPT})^UUGUAMu xkJִGԯ ++B+^I#4y_ joga{yg77ex24i;yaXqĐ2цE"āBĊIoں>ÿu𾙦XxE5o(142dYpb@L9u\S1·wvx3{+RPNbF%?8ymdI&:nPqI%3m"Gܠ╴ޝ_&Rp F!`$_|"񆵦|/J< }yԲJ#>\ą6R6<h8m]D_C??~G]w7RSfP6s^!AL!c(lz_KBz{WH*B[hX(>]㟎W[:%đ{ C/'s] OrkvđJm`lpͼM(ƆAʌƅd2HzQ΅ϝix>2[(6mNH2ō{׌736mgI>nMD:mNI[xǛIRc1ef8߲Utoh߇'ԋXop'O2%3|eϭyů-^2`_ ;|@\gv"!]ƽp&06.A"xXŽ:{K#j1@%Jl8CU⯋%/|=e\tXImU'NO_t#cnJJG@8P+_qG$>ƝQu N3Ees0@Q\~~px]/ x'#тSԟL(o9#澆PFe ЩXJQҀ%Q@Š(k-o C~%k.Zxukh-HdN>9^uOxJRX({'yF]6x◠C/w~:м k3? K>tXK"bp=N@j!V|'&|t{vM 9}k5᷉,[MC¶Cڋ+E9W Os_~˾|8o7=*m:Cj.ĉ 8q֟_:yϋ(Z%垥\i:kzmB"a)c.BH9BZu(og=^x#nQ 9FLWMKVy7f4_W2[$s^|:l5.Э F qloO :D%6x ox֚VѴ^t UFꪅCoῂEVNfxeYvXv!b# ]BKxU^.RiC,RB/ޫ(UwqsĿkû-GY71M}iWsh5셛$'5x ExFmmb16;Fqқ| <WPLb݀#r243<\5_z7sQ?65@]һ=KO-f.u=B5;ŏΖA0!*qqһKot^5Fini7$6Pk=I  Km!I+iok)w/?5o wծd-mXq5@n/<ִ~TՆ5[BS5/}-a xfRᲓJӎopҰZ-q} \1VuZ|W qnJ$1&r8nwbJM/|: xtr@Foj.u02ʨJ9 {]pwi:WOc^2rI2),I9'kaEP0((((%)&9f%cG`!G~9⥠((l$1:j 31u$`;RhdIbC$AEIEPEEowיM[`XuSzTQEQEQEQEQEQEV/E5{k |yqn+j41BJ$N]d0<E'4Oq<Ѿ&O+}B?V>3+H>]rsִ>+~߈W} {X UĬ da9?~|9|E6൑Y<:>,_0F$x̛A +O_Y\[x[tf/4h'Jhˌ5>Db>+L|}q·xnbgqHOT߈Z'-SSC5N7ȶK}9bO[O2xW𿆵p=Bro,XnC"s9Ke"<9m ;HlІk|\񧌴ZK[](bRr~ONK xf_k˙/Vcr2s-μˡ4)7>zo|1>y%xjmeHǫ\~-էWY. 7Z׾\x6z]bMcԭt?& HTAn{_bZ闞&Y`y˩3OXB bG[mFQ6)Rr7y׈?h%_ZFrg6 P: G5<~*/t+Js]2ѩs BF=~Zfg5_T!HmDH4UHNqZA:ݞ[%bI`˷zK6?H5  g17|xmm끨Io@N8|ğ@+DFhOږdguXqEv FzWq|ּ -gPulV S ɰ-(ηvZx"6UqTd$8~О7.;R_xʹi[(a.]>K ?> 7;kԱ 7e8 1gU}Bkڡ&[.9.1E$R-|bƺ7K|m+䶓u, um}WF𬖚W,Dy=ϓڙjF9'nI8m-]7.~vAlX,'0DFak^qRWl&[-V2ukpc }9Orqov+hz<œzgY pM?,\w_ <wLwzfoI'rg0߻ NlY~͚}X[M_ƺa&yQooB$Uec lgk7?|w^k{>;tL4%#`r!q:ga{x/Ǻk y .Ly14jM9oVk] =/Co KrQ,[H$IA̪d_ ǩ~%>#]u)ʍ܍9'| YxT̳栗\\E<p$K4H; 09'4_eaEtKx=_ēhz1ӣ'h,k>#xzEPHҮi衉\b뵆>^~ռAU{Mi,;"Vhrw" ~YwʞlSHU仗k4XORM5<=]ZK<)^9ng]O)4Ȉwf1/i׏<<<5/CF4qye_d"#ugӵ y- _$̰0Xq8⽢QEQEQEQEQE[MRADKդkˋ>84y]"o7sǚ펩M 0xt%++o<{f|$z^/WԵ;oj6cs/&YD6:zWxZaN[G/⻴XDr0Mqv"Ѭ]xM#*\$!Տ?ڧÿ^hjsꚝ NH,Wrs+/GQмc{xw~#6-涷+FJ%xbr8_<xpY4jPl41,NX8|~v:Rw^$Ro A&6fq$vK/#j._omoiD16|\.vwx&ōi::Ϲ7Ɗڧ-kK1񟇒JMCYפ-/5J#Ӛ;\\Zdil,Qpw)(:W3ox'š^k#T t5g4q$1]N[q|oro X-_ ]hXF1Y,;@C#~.]CӬn&_ YYi9yeM_ >ZRuLZx4h8ȝi WtV_Q9:VY3yv|0ON:ϊD>~tۆvT)Ǹoh] }migH-ǃ|]oūc?#$g2)`)ߜVޫ2cJO-VWgr/WVm{\cė Q᥺P-2,&&̇‘9V,D<#u᩼Cxf j\ G7H5ݝŲyE_; WI]:xmzKaӴ8 8%G6`TG58LoԎimlHD7p"@#ؑ^}OZwsisi:k/rIb=Mu |xR0[H5nQ36eT6a 3m)GdQp?ɸ# v^u˽JSo'ygM%gmu @y-,^ xc9 |)]|44?:mf[".1`+ͼuHQ>tP~k {ne!XH]Ej|ukMxoQ[XXֈ+~_UFvyj+eO=e_5=B+ =0H SixK|%xj[27-m/Yb@@U!6ߦ)? iQ?vNBb_ox[lbS_h=4׊\:e'Бޱ<{g>i;YxFӭK+gt2` %#՛]{!Y[J<),G;KiBCSt 2n'OѼ'}=譭mdh?[>"}-s[ZAwSʹ,+ dgO|/'tYմ[IѧK[_hWIÒIw>|9/eԮ6Zr H4ѾxLm^ْv nTD|B( @+翇PxC_~#e n KX6ҋUJo;Jejz;xWu44+ki$}."Jf嘍;繪Si?t UKq$>#9OA7Zte]sL[K3LLЙD¬@e9`ZtOi7znOXIլ\y&x2v7BFpE%_Pn~]i áV]-UޥB1.ygPw⯊ xFOmD6I"bTc^^.4G]|Jԯt ϧdO3 Qxs88LJ|Xg.tM\ ok>f+Q%{pW ϡ~}!#᧊%.xn顶Ɠ[yFY#PAEw֒֐Ccdܚ0xWY>xOv~/E 600 1000 0 C     C   w" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?[|k Sϙ|XcEkTOTPQ/ڧFO=EE%TO紟ѯq'_Fخ iIXjwf9%tk쿭Nq$NzM.+fG!bBSuխ_M;3OO=j{I}g> x0tKikZ̄aNdE"~kW k|xC_YtfEN) ӷտ|紟ѣSi?^?٧x[uo jzW2s-vD`8n~>x~4d%ܺ;&wSfhm%4i]7'hTO߱_Yx?,Z|DJHY..?կ~$<}KK/D!\^MY7kkJi>yTO紟ѯ>~|Dӥ4NuKTO紟ѯ[gKlV x."-;/ GO񖛧sh#v=x}Ϛ>?4}'k| ^ DŽywLVI-$1!g56 #Ŗw lw@\3~߅5OK񰛳~Jσ>?4}'kO{[ڴ> .mzkD#Tp0~l{מ|Rm~,|Qx]b}6H~9+ϛK/]mm;3þ?4}'j**K紟ѣSi?QQ@'hTOygo Z|Gkp=v~q"̏w$I95@Sźމ6{/?f3ˑvܜgnqFoڧFs98IU/LNh-IwGu4{±A+1#Sj%߯CrLy"T.X^Nײ[FJv,Ejxcƚ5m[CծFqm3+)C4|ɴY:DY)'>2b85[,I8r;FG,T:ֽ_+;٭Q 8ծ\K{{ct v諑?$? ]u{Gʅ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@=;?GG;?GJ3Q_Lû>3+Ktû>3+Kt\SO +YWƟ<P Mwu9-Yy#yl:U?ᧇ=[0h0%Pqx!Mg+v|gV(v|gV(vw_KC >%|HO.m5df{|m c`P1ճLVj?~|9n 5?k[KoIllo|q>\63CyR|FJv|gV(v|gV(ꜮZ +_֧| 1\Qk&]y ;Vo8jic=Z*0dUlq:ݟ:?ݟ:)%s76]L-U_] nмM>$s67ZG*(I `g;wm|68' cpҏ|3jOwgi2wgi2v?&aOiZ{^&:֫yWSxMvsȔ)l~R| o$@.uo# xadd@/Q@/Q[Uw>lOh$YP+9VЂz׾"%͕MnX9i6GO8տ ׺û>3+Ktû>3+Kt#j+wgi2wgi2Z_;;Gt}SLF[,JݑG?s:s-oPկY^KWh2;bnI⾋v|gV(v|gV(3W,೻^nOq<v|gV(v|gV(]Y5g;4Mvc&ַ: VW".Lj|NM}3@/Q@/QX _mowml:g5#:EF+oI/c~7 iweLۀtU=*O ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (8*((((p7?J>?|qKİȐXl30{4-]s&ŝ\!2-$%C 1'q?Ok`4o@etAϘj9!֏W7>^g_>ş=оhw>%;Tοj:y8, hKɲD+o>|EPxNsQ_x~>|O~^)X"t-Xnz .?>*| P{V Zp,㴓@;')srR|Wot)/na \8HP2Y5WB.Wѵ[-^X\$%IR@8 WVjl[֧4bE]א<0J-`#$} )IxQ&0E[ꁁBYוݿd*<0zj6-\f/5]4h!=^:jiwcd2݀HHim+\ESa|SeVQ/&8]qNگ6o /Gu?5b; 2bg-oF>%#L-$ UvC<{%AxVwWDLW\Dfm{0 әZ\V}miw 76 mE9)}Ako7*k>njl|3uR,j.>'%_>e}3WV*J1*q%7oVC8_?諸Š(((=p*hʥ((((((((((((((((((((((((('}Gյo5x@+@ADmi?jɆk`$.q笼u_R/1H'S rОhj:e-6p\\#L$W9GOch4vF[.&iX]Mt0),N"GC`yS:&F go:>ĆHچ0p@1\ǁg_ 9ԖCdH$֮d\BBT8zS5 :m'u*ěbI)ZG">ͦy5RHavrѻ eVπxko5Uk_PT[pBJH@߇OCb. |Lάb jNzR:Gq,)Yͱ,cJwk2Y["m"ѫi>t]m&&h|l৆':nKep#o6]O<<m>[c'd̓ K$Kw$<x}]U&wѯԭ<=&v tx;}NKyv>zXnUf$ pp3)k<Y|\%B DI;c0Z>v˽Wڪ߳4 k\4nlu21N:gt o,Ct& 7bb']5+ia[?~Vzī^YՠІgǧW0ݑ Jˏ00$m5~XhZ%Z~aaTi>.^Pּ,p3aG^ߍ4^kmSț:iQ|S/Vm.<3v:E5[D&̌+@qs_=[(W6~.M`f?C?Ïzljk;8FzO< ԾSX" ֮,N7Rǹ  FP֩ϞOOnub _~ukasmul炤VW|)_<[E|Oqd&0vJcv1iQ_~|/<+[ x &Ve9'@?Vº/OXn1"c8 GݚpMk|0QKMWk:G4sjU*=z+xSz^5}cW\l)%cYcRv3Ҿw ǥTy9*'Z, _ĝz-kR4Hn4C%1O˜8puSfgU>%ū1on}XnCpY9O ËR~ӴMR8%j*8bA;k7G>J4;Zeqm['yNi-=|uS 2{1s,1*<b םxC^7|#/<[xz{cZӵ K]?0e,6rWkK] /M:uėPAu, Nr7m\zVΉg^e}k~{Iw f.JtxWTt=_QoGvB(.!ToRO|9#W[_x)m͐ =x,I&[VouoLrI-[]U>(b^lձȒ\uw, ^A;qwiiIk[1@]Ad~xka+[kZfumjQM2UIU98PF O\'$ -k὎qj:ur.vЬHgҁ,މ<T?޼'㏎|eyJ>c/[Xw%s;DuAUTx|)]zƷm2\%I21^ *_<Y,׾2pp80{#o1<#P94jDc N;汾FH,ogV•f\ #AoKxwO_Y-gLJ 9d9Jng%vmgwow=F[Le.qgjЛ4S^7Ï|@^j^'J5?%")XՖ@/ x^]Wڍh@߮$xSY% dMud|,}6[Eu.; u^%OiRIe[ Y^O$n ]^g?|o%8[_4T|YkzJ&H>\ >>~!k@gw [NW ŧ j0cs+r%r86'9vZw/KƳ>,Yhc;dX!W"zmZx3Jo˧27,~b9sShsÖ]>IԢ"L㝛9gVU =|K#ƾ~'˯[hqk7~3=1%lѰUIfߜ+t?x>cº͍~,6Fͼ9F+[w ҥ0n̿vR\vb3K᷎[Zxrϰ7(ȼ =2w]_w?ڦHi.n[hRLaI$ jGywZƅ¶IbחLL 1$9nmYmݼQihlaDKy|18*?|Qjz!4忋$-I5-ůijrq+o"o! Ú7 E/&IH%ݼܩNܓ}'}ZhZ_4*SY$#8x-*Xk45kX쯮y<1؍A?h]ſ>8~Ӷp'n4'IR[$KFn@$5 \ş׊6>.4"CH cF8|x:?ė:l,&Xlc@{?98cͯix^PV^%gp<\GuG[w:u|\,YBX_|:Zj^+ 3"6 ˜̼dht;xjY-gu-AxG|/λkBm^k[_/V+C'ڢ2 .A85;2Y[f1(WioMxLWj߲wtxMGyM ;&Xv@C'ß |֋^WKӵ8/i.;{Ha%P2 lV_|5w)O[u-Ѷi^=U$ƾO1p5[? i,ⵛT不VFq'<߲]NmR[36%hv`1` dZ|P|I<x[vYj2Ek[[,f&{U`os+u1|`cϥZY7%^af7T0?\BuٻᏉIi үURx3ěp0\k5k]OŞuB1 w71^snLvG'yu]s௄/bLPʤ`h^PCkk vР8bP85QEQEQEQEQEQEQEQEQEQEQEQEQEQEpUZk>$=#Jږ-DI+C޿eb(f[-!5 X|I]\4nfu9.$`Joc J_x{ǣOuj}t`R)2#rc~p>!WZ&MnܬqʬrNy l7t[-bMޗ-]Ÿyʞz,P]5[>)uX7B/<_]*8CX5R%Y4㨖܊̾X A+V4m!>a~IP?o-sX҉]Љ$/p2_}l}ɭHu AcI%XT1a'[_ ^;jeî7`g; य़s^/ZxZu=#C/-^<$iwD #{uIq͚tsYQiMhY 4 zڛyߍo?϶@0'cm7V\ҭ3kVoWѴk{7f)pPHT0b| _tV>)6uXhcn[~2K/xk?$m0 A_J#[>Xбk'Ÿ")gm$!(X^_c23&\e-Yl+H2l$99|_CjVE"G# CO-O*x#E"wq-MbO&]7º;:E]c;Wy~JeGJ~':{+!6lga8߃\|m I-1m۴Wv$B y> Uk$Z;[+ﺂ'qs3PH00E |0-/BY/Alr#Z ^_3 dd@` =E~v5&-/ޏpxNO\is ydLn?OW5o5om@dk`?so͵qfM_յ_}>GJ?C\_&x#Gk/?6RE'V%ߌk߅zU'O^,Zڌӯ->,|㗁5e7D:mt._;3ϹZ~'>v>zLgaiD 7)8/B008GMwzoh+i,2(ݏY<(Iһݕ~QE;_Z=p*Pe('`-q`Ԟ+xz5յ)_M "uI>H8dqHƊhKm 7zg7L@CѼSviSC#UUJO|gODⷞ e%ĻDRw@@1h380l8?^Y-缆MBNZt#}z}3@ E4J:uKr>aNzX"Vywo{Ióȵ1ɖݖq~+dHI<u") 942N$F]ԯKmo4sVR$&dA d;?N7_ XW6ے;fEqQ|hTF/-Ŏd]+!0@X>9^7u-:+[l#.E>Lm9ld.H\}lzu ݠZO<])%$0&c,ndMXڻG^ǿ94iU }FOJv&3+z徏xRhVnėZUܢƊcBO^M __7oeCK)|j(O D7ppWi_ Qvlc`$ Jb4 z+7~!hQk>-}2Fd[vܧ A)CVzKE 97MKEv'%"IkoDft]6ݛyIEQEQEQEnzR@iϾtVyZMkH]Z{bFFNدK(-l]H>j%1C}4~iZt;Vh2WԖVw<#xlO,շ}J;zpF}ڲ.oxOIZ}Sj,3&t,a0ە X͠8|7K/Y[|@H]}͘mLJdI!.J^4t2D@4{e1o Z=Qi4@bGˎ AԴ*>>MC\[*_w[]BфUAP,=8^qctNо0XS`iF'޾ܴ?,mivlVQ̃ѥ &<|F }K}yjeQ`Kp? hGl6[fbDY, urOBk;J'н-PGp:L/;0ޮmɡi[j15e/[nc2(߄E7J6T.^o7wJ!E(|1м+?ξ=mSӦ]Z^Bё}E~gx?Ú' m"_ {xwF]#P\:3̡Bn*A4^EZ–GJYΨڧ+$R[yr͒w.15'Ꮓ>Q[8Λԇv( &oLֶ,gԭ,-ѢS,Hܪ p=(友:wWV^,G)΁gy2Ng]J70VTD=UH#yOZ~޴ Z~B6Ce5'<x+}Cu"28+H8 znxLM-3UӠ!h#T`@l -| }3We%Bkn lmp+G (~6jt 1_h]ƛq&}:as5142FB,F~QS'Db.Mʖ[ Rp1P0ownӵ3vk ~D+*8%؜|GW0[ rmj"Fw (#rs5&|%.t]<2e; 18/sY֭5C]i_G$lhK .忾 $医!$((((((((((((( Diڵj\iΒFJhՇ*}oǶ^:Լ96ڔǪEu+OG+b*A\.toiVɥM{uuee}Ყ: *~ο |] s3ӡҼ'1t+m.o|cዴ)5y'+(<3گhK3ž$>"sXn mcY!^J(ݜ$}TGMU5]IZDf9_+3)GFh5{_<_L5]_C> >MaXi$mp ujWO^wdr d fcҾMi~Wx xCNMj?O,"aPyZHro^5j:7Ofk6yul./8:`3P--4'JFX,H8,(IjN]?WBI3L5B]' $]S6fd6vW%$rGZ~/i~4~ ^CPdHa(ARsuopU%,3F"HT*O (N.aZpσ"{E汪K]x2D/P 8Ϯk+7 cyZ_>8q4ix#H43 /XI^Eώ@R0k~Jޕ=M4~W*ŶOku>-n~ghX)Ui @" M~PxkXt? k*@ƕBu! &\ qg⦕Pߌ/;o 隔H]a%2R#v@rqQk!:~}9տg76O ,i >pUG5I)]QjMbCW𵦁s9P)R,HZG Z/sC o.OOUHcCHc pOq[nMzܶotnL 6ITPbGgAO;Wֵn4M.NDtK#n #hE? n8xW~87_~5]9Amks -ͤk\9#]w<%/Be=]O|CmT@x$+/I:'?X m]fK+-g*$UlnRp?"9; 0]lVWiMZ]Rvmu_gӦe7XIVGNdifH£/q.~3(k(;YZɻjz'/}RY-[XHB17gQӮŽ֪}Ms:MS$2ڙ]I/W&kCZXIZ]l~of8z1j5'̔[]okI/ʂ9dRWA#ڲsOZ`]Vm]U:|7.|})ƜUGy[_S0K JsTQJMu6f|l}"NDkۉrǐIlw\^6< n{^wg xN9 f7W{Hs4'1z6p.}3U<*s!*ufдg[CAH]ÖEVVיx;Q,.T߶M!ت[*LF7ŷ5-%rP`wQE (;_i:M5esI3iL{ʹ 9Q WZǴ?\?ŏm5+jeɞ9nVxH@9>QOUϦھqqip &_'95P"͎z ;Q5 c7W]K8*Vm.o㉬!x/Yi0mF,m14c 19ҷuُYwÏh#}ėYkFDxR !&LXoŎ[;__> 6k[v[Y(w(G^Ibzs[­#ĺ\k)xVIPE,C&Dfop9Ⱦ**wk#j wKet$%U$0s\=xa>%|Dŭ>6+[$ I+ƒ8bÜmf95Oǃdfܛ+a?t i!Ī,0݈lCÿltiYOyk,  V.SŰ85ïgī.t%I-̊Ȍq<5Y{eዟ|ExJГ2][6+kvlti pAO!Xg1}:ņq3Z]XIɆx|y5ݑxj-.NV]&KQvrS[wv p)RxYكhP`{? G\a#. \>ݻy{s3qk__u{ oCg;;[]F}M.c8ؘP-d;OD_tK;MOpԲX{-C#?!8N8?n#xY4/M15 kev=3?eMOMhNJ4 [Iu_iɪ}\%48a8U@GÛsQOMn^F0+ =My3%Y~{*>$lMe.FTG׬ 1}nVw~)6ޯ.udM dm (# sּBԦ/{oW}qqDn (=oq71vI-)i^56gi /7mw4UXy܀]giZ 7}Wz6o%ئtF%{*fh_μ|aWſ%m.ޕ,V\^f *n8hj( ( ( ( ( ( ( ( ( ( ( ( ( (<+=`<qU.?_&PoKjjCjhڶiEPڶi?.?_&@?.?_&oK#$ rĖbQ›yk6蘒64x]}6{y&H3 '+,x_r:g`ZDv(pIU09[¾/佼ӮLo4v!p x7sc7o1E_3ؽD,9^G&ݘ>YסO-GTOKH w2 f@+6}h ?ƻ)h|Z}\!$.d+ݜUG%]J{aRExe@$qҺ( ?Ə@<@k0tX؋=:kk}Ŋ yzH$aK?+t.ňH<( »( ?Ƥ7"4˖x: H< Vޗ /GշK(շKԶ7RlA.g~dV=p*hʥ(((((((((((((((((((((((((5EPH~Nۼ 3_9/+UƖR^xv*$$cv#^j6OY.gٿ,Cm3gWxs O("2KI› >ܲFϗGsڦ6RK~ ~WS;5Hѷͻnp:SucZ-Aex%,(5q3?3*:_٢[,z-޴.<=(XuᴚUc*G˂9Yz/i>7OuEfibxAwj~>xGα/+kYVSt'<(< }CgS,u ^MK f 9o-/SJ Q/q!IvU @ Ep/[xljk2$\tTa`pkjţ\Zms|2^“f@9f5x> |K|??􏈱\hW:-茞_+!.qȮC/ᯇ?kHwE'V7EݹS#vl%CѴe^% ]cT`ؔ!Qc5?SǚU|xA_ uu5[FbOY,kt} [yZ~'?x7%~%s$BNwC ~=DM5;RR_i { (hVC`ʐDh(I=Aj:mi `eT=]I 5ϋ;]/~"[[M^ Py@6&}ށ7M#[vX.Snp>m7:6]gwϬh(` "I;_nmߍ-&j:Vez2%Pb=#Ǿ8/^s^\pZ Wf -k'W;f>jG_6VuwƬo TRsqهŗ7=ڥ[ܦ;TXeL,;!8&KƵwſlj-P}YM}FQ-]o03 ܜa@9,~6~!~.]vZ0ō奡;Z4]IRn {xᖑxŚ}vQ4Լ(|UĒN{PQ_]xt fU><@0IhZbHfFPK,1's?į|$fzEӵ/,R=rIE5 $ F}E|uk[}qZC[oG\Z-.$OI~B[h&yIlȐ+JFCN( ]𾅨:}wW XE,@O.zec0'e2< |9ωߎ4z[u.-|]iFh혬0@[Bį׷%>x?E--w6͒9?iIl0P$J[ӴeeI. 9bs_%?>MyO5k˯[ڕMKī;SA8;xq@}QTT-A BFPشW8Cxx[TiRpppq^KWǍE>-x;ٿ-됖]o/wm~su?P:7Q3B L* 02=kmsOccm:D_:kDi,KTavI|?iÏx_s=../gI"CT;b7_Qú~^&QXF1Zg…wG^qHl]bME\['ٛ.~jW&oZ_MsBCh cho22)E}ꖺ&wL-"y癁!A,-r-\-ޝ} \[΀$l23#|kx/:z&?ÖajYL'r 1U$Լx<)?UU|c+m*s-#ð&Ѓ4|:x_uS jm~f0Lmx h(((((((((|Mu=5[WXHpWJ;||eLg,nG(0;JW^_M]3k:708=k[^'ɫOwk@{%qŮ.I?ޑ}7Rgu :qi3*ڴ@߼2GМM#ȷfukèC&knS)q1±&_^ {CC]xL&׵?h1s C&0I1`畺hOŢ@HִFHfMFQ-[ s +7ROů>Wαvef[359:v eE 5>*دS=Qy,!bJN3ֹ|aMSGN ]Os0ݺEB0%U[/?K{y3+1Ch^໭U %hOlg *㱬;$𦙩^[$*Ĝ09 Ο~/[XG SWz&1A$цH}LNo }E_ß@z,$'(-G(-^=LD׿ oM[_Im?FAϔWho=9g**eegֻoJ>`wGDpkBj(((((((=p*%|,ŝ 3VOukqm;qk2)PF# =p*+>XiڽȽ{nɍ1zm]ˎ*xGPԗPmj:7H  ^Es} ~,ҼGljeՅX( J-y=+'χSfm?ີ,gg+?wW@E?[I:|ꌍYjSj%@BS95wĿW‰;+L5-R[˩YXŽMIyc:Җ/cZpEwފS}[1Ksh꺥YJ^^G&% :J-Eևicf,V4P <Q@v>5|E?_wy:UjΔG;kHDM!ssӞwPx?an5 /u{ҞmsP̬c5C_7Ï joe^\fOR-h$Wrr6+٨z曩XXj.GNu9,YDbO¾'4k ӥ'v2ClcF$8'W@>;6+zEb~9ؕe `G keM֝yXfͯms)^V%?7ˎ@=Ez@R[Vf5IE*11qzRxR{⫫K-F:&Y4N{Cwk)h ͞ `<߳7|^6:FHͳBTe-}<ջ ӧ |hb`YN\0'plZ('to:֣˩/4YYDr/  H5yKwG4ɬkOɭ(pfg`m?'Ȋ>\zyEy'exUR,~q.ho-Hy <VO~#Nݔn}̐DhYg:(g|*S ·\_h}Klۡ,z׽swgk&wKI Bq`sZnQ*(e(|9߈}_SoVMcTg`CO0I h׿eOkHeΙGuf "nS^EcC:G5xBO46ORI[Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@qk:]r]JRrQ\~[G!exB)`Gڤ"$*n~~@ݜvꊔ=wڒj69JEv8E_Zj(٫z晢Z]l?-5$.V,s\O"|={Þu5{۝14gg{t$%|y!xWŭ^#xm Nzth@؆/tG3\=Vf19%sG/~>Վ#mw ol.$U%4I8N?^E/08 w_ :45};D)F}.wi~#+2Z-KN ew )*O;OL/)3IዟiRj3599mzĂ8?Xk3uVk@i~'{GomFRnne9yeaqZWqYγI=-`OSIzGcÚ/.}/\Ex@U8s_ |a=t!)cծ.cԐP y&<?vѼi:JQ䳲e$ UqP<9:=Bd$c 2d/#:ro>mGǖt^z`nsf"Gb 4Umu1G|E⯃zxށ zMgeL\;_! w5-sfCG&]CcbP${Y$)ُaM Y[[kG;%d\8'x|A;]nP0Fyj֟6 B҇{iio"VksY4o@ЛW_Kn}zMSι03]7Nqwxğm7EM~bWГ d(:W?f|9DiݭiF Qr͓}aZ6Zg;3ݽi+3}\.rrs5KOMZ9a8+7g<3A̚uW F85G5-VzWfA-$l&c zW~R55=6=_±d*(ye8HRiFVYmewusmѪT\Ğc!#Hj~_Eݯ?> փ!5X|/uw+dbQ' ^M+KFjcgC,ņֹ0|Qk4_ 8mcF}1 2Rݱ/׾)|/5] Yu hӬՠspF>fry ܜcY~ [Y-?ĺEחvI X8G:Z?ntB--"nlP3׍;5o^sGp"$di#ֹvڷSWZ 2P '3@1qҼGpu+o-n-" A Ox`hbU#TU'iVl'?Tc|+`T$]2 Uw=iwo{Vo~Sˠkn go#pk?P'YHgBʒX\jpG:-# xHxog|>t± -,IR2c\^Q+$?$n<s^_]پ)ҙvۗ#dq׽Jwyv[Vv}OxkFqxJ ZQq88\G?jWmORwggE,8mȬHq^5?'Kam}ibmmB$+}赫σ6Yᫀw/`@峷_fJmvl}[5>cG*K ob1\JN8v($棷[ hka?#w((6c3M}Nǫh^1yfk?";q8RJrMl|8wkm7>֯|?W_M/ Q1AeULrכCĚDұS}\:,6V|OE3x'^(m[[ˋ)$2:x%֋w (Š((((=p*'|k«&Qu]cVZI$#4*\w6W~ӞxKR>{kP~>ìi>;YgExo̤Ux5`t~~OD,έI46Z>K6 mM# [sȮ_V7>oeӵ_향d`1YM1TnX xw>񧍼54t8uXx,G79e]8 2 "ĺx>'eݼ7suif\Mu#,/ pM{vWWDVL$.y0.6*|\ \gIyFMJW sg__1mB?潃Uc;^r5 |]Z8 yElf'h׊OYˬKF> ״i-Vcr.\yȬVfZԑ]OomE"r@ufrQ^K/ 㗋u\OuQ; 7eX\#% | â't;]XZG*]jF1>Q(|Іg[𮕧h3&=KP֬Foi<4.LhO2x -yMiڇ?_iZQ|>4oT%𵟓7c̡G#5.GL/ֵmge kw,dC9i|_nL/Oxbm$y٤U!x01@[6z}wh[fKu#t388`qix㷅<xOծ.-.<'/5 М2$9x@о$x}l|9*Xm+:jćyTmc5{%⟈_ {?zVS,]Tť}$nǗ"`gi[!x H4ZMUXyo GHs+򞭅\~| 4b\KhɂRAڑxv5 mj?vӴWCJdF~L,6>|feX.] ++esXbi.GGB"msHɵ$y 0P@ng9F;o X_x^e|A29$e lXZk>xm᳡hwڥx[E I횣0ӯ,qK(,W\1Ԟ0㚯گ2lb45%4KwHv1^O~)x=>Y\=4&vQ7r(g8;p1jLJ"|f~:Zx>c}jwpIҥ[رVg, :f=/jw=?Mcܾȷ~Q`iw?.ַzѭҦ\ׁy+sOMׄh?__$q{b|&T缷kym2e+`T91i?$^/jOjSb{F-,Gi2NU"CskiܶioF7bҦm"YF`Ýn eȯ4/ٿ<uꍫKrreK}c v€B( ((((((((((((7سDǹPMI\<>5/5[V)!H(*rI'':ŷ2ĊøP I\> Ю[V5(#x6~W'`FK Ckf-hTiiY d'6="~~6ii_cw[i"BusZ/+z_,u$mW2p2I 1s _W{E~-aV}J4%1I2\_k;mSAn˨_%mڣn@ JO@Z@Q\Ï5mX-h]-brf Ѿn>k6 ət"R]G`M4Ěj:+|!L?ƺΝZEi몴- BUYMr>:x?5j!uˈ-m謬ȣ*p1E]AQ^GuOx6pkqꫢ6#_xF }m6b)>(MiiZ%P[(MlH,H>֕=j~^?>+ޕ6q$V _HgaExm;Z}\i (÷DWr1 C#xꋪx]6z-m<ƀNG'}sҨ;>MF-Bu%:N=~^"rjQRhX<i4^YB`Wj?hZ}^ko$v:fӐ&QIZM)z5ZSκlu ŁKq,=:o-uMٕmki%hy (k7_=xĺO5|lRtYαej-?O<W 4BC*#3(5mk-=).m<+E_Yh&4IJ@>Wu}cECդZ|bNӹ\=#w> tx2xG6yn4=j8W8_-\#pj;m|?6x7Jֵ4f冇}v((>~zͷ>!j9/մK@J55CH dqx5KX'ߴw0xCּ+]KoMdxʡ$g-1s_ /5C–"@$% FeE)P4W[~F O k@k(!gpnwrHk#mD?qYkMYUeY(nGL~Z|o|џEYo[f4$;Lq})*l-Sٞ ;Y]{9C̪1{/m៉_5 8>,I%2s#$bKۏxC](59K%湴a F:r(+µ/ڎ[!(xojOdkxn b@|@@Sx?j|[J&x$ph%V kEx=-C HZ Ns;A~BH k4zZzKjf6.Oj"{ß S?5gKվժxTnt8B_W!U " b1'ר럴eHbԨa<)4?kmoG ]m"+dFw82X/#EHg%ŵ֥[At!QIA#隷_MQb5K=<}qS)l\x>^I -?jGqp?;L#z+ż+Rh>*Լ-iƱ~NTI˻{gu 6$ڗB6"}R=GKa]_GCeK(h/j$o|7Z_%QX #9!Nb|}}O_%a51Q$rBGr((((((((((δjԵ9<1jQ\jRAsB[^x$p1sh#2.X}36XR|/Ķ_|yXxCMˁ [J:*:Ȅ.A9kom! h\jXĥ6"9jhK :[2;>(5k_":e9D!pH"x9W| C_/~xÚ.et]nXiwB g,Hk){_$]5MknwPpº~՘yc{7s!wkTѴ|4?jm,n!1o}-E|ߨTRKo>Lᶅ}#D_Βi/s&!,eŸ0bK7B;6[Ou=#G5jpi充P>TP|0}Nq"ąq%>g{*oFMW񽿈4kYGH@ ӿh_k?~#A +yJVlɏƽ.dP7 d.yo6xe¯Ix0 AڬA_M{zτ ]] #ͳwlhl7([MW-M<0Vqy Sk|^|1Ox+wU4Hڢ2Azo\Sw䏙fA_?hƏcN4OK#vW@>f>Mo[-?GK xSMnm?$l$v ׯ*U*ko=STsucsOFwW~_ |Aͧ?9,L.[o=҂S`rxohʥuQU{| w'kyml|c<7+i2 |V??ĸτ{[^vē]\0Žq }0=k.b7L*J6|-emxxL:vB*7_>|@S<9mx.Bȗ $yk(  kGuK1 dpo_ie lֱxC^X6XF1r'/"/ڌv准1͹S02T;pxO_[]fK7-j_b7Hݔ(e>[r>zoSquGuM \KImmo&B?xɼpJ^֑kZUg7x:YӴM"; Xl]#|O~AÐ<1}V\3fѐmf6֫j? ,*G|e^_kK 2Cl;v@5%69U:Sh?d/xsZԵ/\Mu\xjoNh& BsAS|T;㵧AZ?uAѴ;mG+^q4OFNB}|$ӵsh#; e.ܠ34_?R?9};Rk yng gO ѼaE&H ݋;wrcb+h$}aῈ J_$HmeU\H{g_%4}M?¾Ԧ2|s8Q.kz(?y7«=[NZ[ohG"CnTl|o%x[~+n^+{ i$c;Gz~/>&O?6cMVHwX^aw5>OjOni}YedLF⽲ˏzĿ|>4U|,Io3y1oz|?BӴsUZIo^)"c두Fb;WTPȱϿ[M.I{սhV ,MCHŊ#-~~xs:)ԴFo3[MkmK;nc|JEω|?\OiZ]a5Yum= d]׉_O;0I!`HAwxk_ܰuUISH$P6 q׎ x %|ln ᮉ~mn{ET6T2f%q{74 -[GF.ׂmžʍy5RHSd;]WLѦIfVb_W-Ēk=k.-okeh(8^)R f+qh>"ذK14'IbbO,HmnTk9`EC{[A7/nYZH22(jE:'!#5֡hvorcPALG9*g;_x_&B4u;T!dfH8# O_?צgn4Km/4 r=Q;dLUaܣ[Ҳkn^-vӟDmGG֖陙 Չ8s|}:ƐH4mmf)*f3FKٕ9<=EfhLj}gEƽ44]>m&ޝ_ oxwKawV̂6B0U!r+O¾k\x[~ub[Aa,1Lc0>-?·O~4ixR9eH̊@8 jS%/$KxR]:$ǩb>RF:mA-ip𽝎t&xQf:2 >I;}=r'm}&ݫz~i__/q_ŠwŲZUH FFBE Qt5=MlnLg9_~>M{ t eѴahF/6T9b1 @#l? $fcY5)xX0;ߌ[~7^?WSҭg 躪.*9Z 4 xGZ׺+'v&w+>_duk_RRH&eT HAcvNSf9;6o\\z>NkjZ8QL,\$s"|78kǴڧFZ{TJaD0|ۑp=>4>gPokjg-qxǗץq.o3x_Z}cGڷ4W?JQW/Z_ߡ?ua_:τĺ?#z=<\+^-5+{Z[/"Rd)^G?XI#W NZmч0"ldr:oDf_% 'Ga.%3pmM-!F1V_>3-P:h:N1b۴@JS89<ϵe+9$6LKcEY|;"Y&; E'֯߅_x -;G俴еHvW|rU7E^}4xZ]^ݨM檳cLr޵|.ajF>7ő s koe*W2,sX?V]>Ú*/$nϘPB5? (> :CP_޽vfTB7̫s@q/4lL>"߃$x5F]T[Ghmc`y+@s[ZxK$wVW%17v9#{SŇ?ևɯiQqO1+0hK1W-e&R#bu xR#mcZꚜi @lrTzBpj /^~| 4ֵ}_FݺdV;yv^g]CITgK]iv^i_%[$ʕpAIf'Ԟn'hW,YEM&Q 0?x</O\Wx›SÚ?$WOz]"'\!3N/^F]s@ӵk3<a+Fz}-Y5sIYɵjKů{CUῴ?|$<'i'Cluqxt&ȟ+qgUZ+_+|/um#V𿎮|X5b7I"[we_ dh#Q]F|G[Śwlw֒He,Ha`By5Oz}[Z9n;[fn%Gr R R8*y5z7guW7'4˫N \"{AYE*fo 2|Iiν]j/0[x\aWwCԒhmķ֗/6:em3[h&CP38Ao^χ/G$˫jdYKT3ʜ욽kZ#]>^iKH蠚[t Ċm쫫i&Lմ2V YƷs &rw`!{[C㥾i%CFO,̪6nF_⿆,,to,4K>C3$6HC|3 #ƾ2xmdѤxb,8l*^r:٩]|A4M/ZͪP]|?.s) 8$c4;LMmgzmnrT&KPat=:T.hU-yE N01ג7<^ A<#JaG)Q. cq<⽯{_ӟS=: SKZ[;[tr`Ur $qր<&g5oCt;%ss(-VwFB[$n9//<*Sϧj[ vx%Ie[!ԀvW3|[m ڧ5Mz H-OMX$aYJ%˱bQ5Nǟ|Y}iS&6 uY$sF9,s@Ï<[E#wH.n䲎o"/ fF}9/z/4R6;ذo?!8^5摾 _^Kv-m" &ov8kdX5xJ>(}'FҚQbƞyS@^Mբ/'4RM Nٴ xf23 8px~#|T.)M} z;69//lR.B.:cIWk? XE}MӅ"ap||9b(5i0{ago7~cn;g4 ~'G|%b=k 7FN%,xd)15=-5?t 7lc%Ȕ7 2ݓ]2;oS?KrlYo(3zt۟]*Em^j ?OUo3`i3݂FԱS"Rҗ˓˞8,a7v^}^*M*@6b6ɯ<Y#ޱD;Wc/T֣<;I-ΧE4<$.b, nM 2"_~",/t}7}͏M2NtB?@!2ZFHÀD;|;k-"=ۛQ5=T]]K`djorrNBs~'߄zW57SX}Tarqy~ E|kvo%Wi<"獜o,-v^ Z?gaQ[Q>)i-#7cԟz~?X4fc1+" Wv.x~xBMuzGhE^QJS$z<𯅴7n>*[xoa,bCn Ҷ~eyf8eu𐸎RQԍ/8'fw{% ֡7$N֞:#`h#OPos uO2<=kv}u%ٴ Dmd<`x>,h|'XxNksJP'l61p Njzs{-6 GDK䶼7)qpdV` NW[4ku @Pgdq@4fy2Wo|Pj <=ޏCjzy" ,q.N#XƏڶ?OJVλ,6Z|QH",,)+ /^xc['=4Ap#3FLn˶D.pzk^O7|;g=n!V'*2|˵oA!|Oc/t]7^VeG;=^q2M+vH9=kҿhofxC?Sih,t#p#1_r7pN+H4Kj:.RXujj]Ih˅ BT@;Pnd<-? bҼ9. RnK;HW;W$9|Ħyir}\԰ l[l(AsAw/|PգxNSm̊Qؐm+!AWO]m:m]j{y&@BX2}Pzg/G>x|W{=t-~Z4f29?@m/^xsFY&%~nq~qj RǠx-:Eޒ,[n$,M$HQns_B[,Nڎ,$`w$8s^17_5|1k&ǡA$t.SRC2x8 zg]#K圑ߥɸ`$eBm@~r$`/Ǐ>X ;[vi{mR $UǠ[_ O__M|A~0G^9xgğ xᗎ<_e,v˄Tc&Tzi|WxZLt~ֺޭquzI9pE^L>.|/Gh{TxeVz,‡]0x¿C O/x^4Ů+XErv\I;ᾠ | yu]C\kP.M\ݘ@WSM&ϜWeZYpxFt8u{hܣ\rr*헿?heZEPeZ>{@=o]0x6<^(;Ev}-G.Fj:ՁvAu\EБfxcZO/X%ƅ}z2&Lz>y}-_(}-lyXZůbKg~oc =2)=VpFjĿ4qa@I^> GhL*I2tOE=cW헿?heZE (}-lyY=ƕudJӣn$2쫐2֯xIt+NQbv3rܰ+ 9 f}-\2. Pe޹}/⯄5wOw_ZrMbD )}-fZOu?jRWᱲ J#i0ǰڍC|I]C}[=_T {@cԬ- "eMUdAEZxÖn&yKE` vħ}F-lyZtKFͷa),3 헿?heZa)លڸeRGLz}-CKoj-j+_Z ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (2xOvkˮjZTX pf]XD8NNw֑iIm/ݑsn* [msR'Ǡڸ '3j"_uO/\MwiZ<+Z[I0|P$G^kzArMS[AӼww:t^-nU´ d ZE1xwᖉ>Xˍ6GB I$< | RYŨy\?0.8O`W>8Ʃwҵ[y\ù n$~5 |(z# >[!~axҏor?FK_>xƯjnu=Nk0BX5Q(xd 0s{_h~=bRZ7V42Y)Q0{WѾ ^!:+Iɐ7L ҃G-t-;FQZ}Z*|+Yv`iwukBKK?C㏋jV朞x#.BF2BΡ[%_yh>?A-1aݏI jM<5u^ uR*u,|tG_ƻ/xoKnwk60ZUyS\.^#ӁRV~%S>,\L7 ^MKMhi9Hq%ifᎅPƈz=;\)dWWҾ~xO> k[a(J}£+B-׉ lkp[5w̹a$OMi ~gI|Q} Xm n-ve#@Wi,}*xgD@>Fj6f;nИ.I9|'5¯~ kSj?$M9n+d%̳R5[J߇coW' */k?/9Q|GH$Ak9e ~/OSr{'h_Z٬ Ka"t1 1ȄkVHQ@Q@ů{CU-Ek?KVEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPj\ʮҲƻsJ~њ?ot x캞  eCt/s^IڤIexKG|cySēj-(>HX[U鑍ӓQ[A-~EYoDӯ%ޯeypuwCMZs Oy~uA;L~uǸRL5ia 0q@*ox2?Ƕ96;yipnxh$#`FG*p >oW~KwIEgoi~ oj-;Kqs9¢{9$9>/閗_~)|Ate: ] dڅ$qhkmeo-ay{ko5TLdqɪ蟧 _~?i>;ml,73Yp"fHa#h YQ.at_ }kƥ~BMJ%SYaq*,\32z7ZYy7]M5Ia'Y Hd#.V-gis RTxWVƑ/mYh8pBOjGP| OY-I{h:=7`ĎFїU`VdhMZ4{o4m HIG\p.U]IS.qbO.]Giw3/ ϓNzVrvMXV9IC$e`zGVEЬ&˙j#rO$'0=kt__ ?k⩬V(HٕNdsE^CsOghhziě./313$dќ_2L<):ޡruܭ1E,@!A 3k&>2=RY8QeUX~4ևza}uk)n 6*csg{k^۔leelXº(@QEQEQEQEQEwT{CU-XQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ J}:[yb{GK"?xA8>˯kLDPy=x;]58=Dz5nd3O_f@!q:WIa{YxE6&>H:bju i(n.A3USn HԤԴ{t"2*C`@=G]Ɖ:O4Z[q8ݱKc"AmcLF*4H7+~]Oú sP{RFrF}G,z MwJzn}*@?ucm-ο†I%KuDP2Xuhf=:LI02O$uƁxSPXY\YևXMŴ$hdu#!G9%mŧ_Okx'9m 1H9ެKMY>xF7 e* pm_Pc-A5Oq LFBNIsַQK6*N&MRYφ4yں]ggڠY62 UrTy Lx)]*@[LxV99A˯gL? iEgx{S}cG$}۹X{dV^ykvlPwuHdf\,'L3h>˯kLDGu i(n.A3Ue5OJ +#@no$ԭ'`ƒHGc=+^;_Z=p*(((((((((((((((((((((((((#yZQ!~ޫf +nL^aXf)@TlBdMz_O@EfYDPpXχ'ݬSֵ},ݭܚ߈ZBQDloHJOTsZuB%YxYuעh[m _^D9g5-^_?;}4{OIYrFe \ێ>}_ C7L<*VKy &wÔO:/4t-|n:0U>׾jhfut9iwffiEK; Д4rwhF*&r|g|>YN<ͭ i<= }'6MT]6ͮAFsvCaXwú/YxPٽO6nnDl'pw n:qPi[_F^7<#W <7OVd;K&W,Q_ތr56B>^&ڽ%fHvbj:umS4IewGbU>QP|3Am3C^$Ӣ:]7-K *΅>MGæiVֿi3ZE8ĭH`>QjZi 쬼 i#h|۴:2b3a~|+> #|"շtFx7ěC_?o\q\sYJa8Y)ɓpupb H;08>W)I.7\ZC~w3olIl yMhs>tfC(uRq}Dr h)?|Aa= .B}Nt%uhʈ@rH8b0E~_D~+ݾкϩxI%WT<qjoŭsWD"H(ex ]h[[qi=o֗7tB TmiR+>*?Α݆k3j[\Ktt_r#$G>׍#|vlKm] U( Ilf^=3t,凋m2Ih-?& m3dX k{] [=,/jolZ~hSGa}j\iy-\2ce<s^G]b^!}+>&ּcym_BxAg:4%@zEw0))k-j _xM6K줶cKs5=5π5[xY=)|xp6fHZh7VOvy9 S<%-ľ)ڞkCTŶ UcTg99 392 876 0 C     C   :" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?GK]";{oܪ N SV?5K紟٣si?QQ@~?4}'j*(_=k{I}si?Gf%\O紟٨ ~?4}'j*(_=k{I}si?Gf%\O紟٨ ~?4}'j*(_=k{I}si?Gf%\O紟٨ ~?4}'j*(_=k{I}si?Gf%\O紟٨ ~?4}'j*(_=k{I}>'xnFS"_NnOa'}P-}:4]G5Ѽ]j{LJD2U))Odk&'h\Ob7eԮ.IKkW{;n6B?%cѬl<e^\Che± <rX4Z7wK>/\O紟ٯ5ؿX_zڬ<] ˧o3%qr+17^:X^:`˪&,NEWy14M_~'k{I}>?5O.?h rW_bR$k5F#rQzr_)[}>]uwtnp5NY\2GJ/bnͮ'h\Oڟ?5 si?Gf%\O紟٨ ~?4}'j*(_=k{I}si?Gf%\O紟٨ ~?4}'j*(_=k{I}si?Gf%\O紟٨ ~?4}'j*(_=k{I}si?Gf%\O紟٨ ~?4}'j*|k/Wڎ>cmcs}5ŭɶBI$,1sk{I}>?5x @R dRh8}ec\eK紟٧y;77n8[ﴶ</'-7j8^y'<7LsJ9oaN;J[YqU*qo>ɫyugk{I}|Qu{sɂ ֽG-|-XV׈eڼPk .kGf*v&o?4\x|i68ƏwƟi3kM;O4ü>4N?E[w ? |E}-@m;p?ӿgp>ix|i68ƏwƟi3h4_Kü>4N?G;O4\h w .4W?ӿg@m;p+owƟi3hx|i68ƋM;O4ü>4N?E[w ? |E}-@m;p?ӿgp>ix|i68ƏwƟi3h4_Kü>4N?G;O4\h w .4W?ӿg@m;p+owƟi3hx|i68ƋM;O4ü>4N?E[w ? x'7þ(_E:m&K,"ÿC|<|̏~\܈E⶿ w mV[~5~/\cT5R8?\bJqK2v3Z3E|ammԤQe!AA?wƟi3hx|i68ƒoF&3g+xWZEhdϫ moF̯s_Ҟ 7b%=H4_HmnI7IĆ c+ w yÿ4oxkGE4.bЎf'Gc]g53<цjϿ dmWü>4N?G;O4ZiWOe/6^)-|a{~s j qح ӭixoXި#M|m浚7`Qü>4N?G;O5-&zih FV|ռϧIzvLn+F@HHn}-@m;p?ӿgvrrhV4W?ӿg@m;p+owƟi3hx|i68ƋM;O4ü>4N?E[w ? |E}-@m;p?ӿgp>ix|i68ƏwƟi3h4_Kü>4N?G;O4\h w .4W?ӿg@m;p+owƟi3hx|i68ƋM;O4ü>4N?E[w ? |E}-@m;p?ӿgp>ix|i68ƏwƟi3h|D3+v6o8uۋyҿƻT'¾x|i68ƏwƟi3h4ׄ|YwY!gHo""9.GPM{;O4ü>4N?E@>g7K-n$Hto(4+ w .|;G#1m 3(H<{EעKT6_a(v ,` r:@m;p?ӿgׅ(*N2ϭʸQxx3Oq|\?ZugJw y6ͺ.qC5PO J4iEl|/W^x?_t+OÜ>_zz<+/_^\4׾"~ӭTu]CPȵ$QHNrqһW=G_zz?ׯgƓmxoQ<1x@_j:9DD !@w`{O~ٺ*ù~x_W}#TIՒ9qit %6z,z=_zz?ׯQcxI#|z彮u[^,{mdb ܯNsUۺnkh:EuYױV,VrTc36vmR\m?u k]?6}=?C׈j(_ ?:g=swHkᴊ9.*>v@^OM)Vtwr {t-H@hXz5{%>k[C=?||hguhMxVX4kEt.!!|0;:WOcƍ/X{zZ-^kR*,'*ao|tڵo^}?{'"+O_^C>|UO$~g6ytBS*v&kG^#f<) uעm/63%ĥK6M5+NǪ+O_G"j ; dhXPGg) uݕ=+?hk9F&v>M?TԮ5\Y8chڎ%d⒳ڶcֿף_zzgBҴ +VO ZmSTCEgh,v]/_"qVKNG?p1vC3Eu{6zw"+O_^S㯇~" _k XHi KHwMx5%%_Լ9xu=KU״5_PLEt) $$lpi[ IE!W^}7 xXl-oXP ydPUC %ҵ"4ɭ>ҵXݪdo1r~\7;_/{"+O_\6.kHBL(3q$ `X2@# \gz J:ᶻ 6vKK0f q srvms=?Cב~ϟux\Q.sF;:o>ne(1l r{'\}c?Wwky2\veȏjR1ѐ3N-Jv .] xW^E!ZğZi֏quqtP[W= rH \wG5]L5'5ۏZ$nco`d#d}݃isZD}ף_zzڴtR^YWGtWj^TN$U=i/!, OZG-#ֿף_zz|E~ 'u]%ֱs! -k˽kB𥾅IukTXaߖq-JOM/=?C>O;&=wXK`6bqTm}~@W^E!~V_zz?׭(C=?oE?*V&kҬktpEK W;>3]) 3%_Y~'|Iyj>dYFYPIĩe`b۶ҳkbPxɓ^!'kf_= x&;K?GmLdr=E\H4ɏ٦5)n^G=rhS>?5x,XupM=zq_L"Bv>c:RiT9Osh—S {Y] `$IYr]G1g}?:־R'VGVyǰԋ#6_(N1ͽN1zl~+πSG>asգh(ToB :#H@0FI2sR=jJQ| p?V)R;kVf?#OY"C3~+5̑`VF qcuX͸:%"%?Ao?<@꫸KnqqUm>lORAnm&TR2r 湗_5>tjsOLbAs7tYo]Jn?N ,:~u/KJcgLc3<>TIn7&x BCuWS7IPJb[bNG_>%lNVv?Ze.yO6Q*n+I|Fzpj^@O7靽? C~m!d&WO W~h؎?p]ÿ"G6~6TG v~r9s]ΧF|3Ѽv os:Zf:) `1 (%ʔv䊓|gq+]o5|W _h%߈iyCۍLak^m"9ӴU4  WmWq6ڀV[y'UpG>/ڟ?CD|siڭw6ӗ "u$+) dA3~=.d'o#1wm?.)^O_fO|CMBJyPKe^nb<)r"dm$!3Ě ꍨxS[!!V|W|o;Ga㟊>2m*oًé~ p n4~c8|,Ҽmic>&e-o$lRE.8`NpGqU ~"7izζ+BY3 a4V'x Q&Fs66q+y#f\\x]]4ҵeG%*qt>%_><%ۺ$-ϐ-m"M9 p7#/w~;+t_q3'?ElNe' ϗ;w( O {7om{h$Y@āJc @YxKfؙ`֟;?qi]~ lgfk(#7X86Tjh>=<}/#v|'T,9 gOPZ3~0QWJ>4̹ӬGce46=e1^|? ,9ENӅp , #&@s##q'i~|Wŭ6PkZZOi'R68{¿`WohPxww#,mtIM4g/1ԃ~֧buhWx~M \>c:#F8_xVඥLt/ aᛍ췖e$ i eA$/rx $_[xg:vqcf͔RKPXr=i~|K4 O#[h,tkC}SG+ƚ^fRӵ EGEDZT)m]m_ďkq?" TBMtЯDR9ǘvz_ ~kh(񞗣jFI;Yh'b'|L~5h ӹ?Ol<644 M='QrK*pBFFN{Eπ1JQ|EV^"DvC*3(PH5|T_W4徿đZGbxs#r~U\>|=ռ3[^4OvLSq>gZ.t,?-f5/X$UYWjP3g|1M{Sk]sJ,3KH7>]u~CDy,96K^(3)epv䃌VUdi<+%]U q9>LXFsEm=:/ Xk{apyMĦB9'9`YuGܩ πϋާM΃uccI` >궺{_J '$hjrWH>GV֭2h_YXqpcDzUʁ?gYʡs#z+E.GHgu[7 wI$ ޴~Z6u5Hg ^%@a2Ƭˆ~Wy7%|x1ojx>V7T"^O;[=WG|c~"CVm44;k'ڼ7rcvO|7i_u-Ƕ:c }q^<H.ZU)ͧNkZ_񗋴6(.d^A s[MߛsguHt2HŖѨcݷ a7L#񦕥ǫZ ,%P(WZu9񽗌L5[tշ6q,dwmWOo~ *v^>M)AZ&g 3zt|j𵮯sZwf񺵫qfU Fq{x;TQpqq'uÏ:[x[ռoS\j6>f۬j#IcIZogyVơMacዝ췐^mv% .VNK{tUڬl8&>g=q< V9/? GC]FK.c(ۋŒֵ~3|go{"-oC5V8ׅXQ$#9† dN3RGQiGxIP'šѵu490WTW5㿁? `v4<:VF#ik(vuRUdU=ss^-Լ_xsUɲYɎjOcL}էwqyioدC׬|__mFi^j0F{y;Ph5=sω\ki2"Z>ܧ 5>5]Jqkaiy؃‚O*>/XV_3GOx~oZbI3bo\ T2̫jߊ:x[V:}Ʉq_1ww2;^]~ Sh<=J֮.8]K0ߕN0ߐq^Ez!Q@ n)&t[ϙ7&R nX:gg_Ї1U:%X M_6=Ē Eeo8B<ҥwFv g²nƑNL> 6 ;m|@?O\=B[Ԝcu Dhޤ@у1ťGuh\` DhB ֽգD@ƳFoL0b–!YF2 9J5TTyVc9Lǥh,iOV% jA[xCZv< $<]i\󜭢(Z(Pp~f; %li5QF=15XCyx-Ut6jAJw{E4qLnaP~GU4]y:qcOʮی}k$Dds XrGbI/c>+yR9[ۏzݥR^rHAvh1jϒX{os+=qqy 3Gq],s4s/j.>AZݸ bz{6!6%W<6>_aZODsr:z֙s55khs`5rGx&¤A_z$m#GPfO2NkVştmcU&0HYח-xƹm%륽uĖl`v! s!WQSj=v,BUS*x<_Yyn M _!ca_ǟ+ }72DtḝeI O1kߴ ߋ;Ը1G6ԶV]C)V5zc/|CjI/W͚7 Y SSԥՋQR%X5 2~Iz7mWEoO][j Ѵͣ\Mo!ͦԴN>F?> xw|#V/CΥoe QpQdo :v֟G{pLmĝ(|fx⥙kk儁#GfRzIqY7č6Kkϱ[9l@t=J.Rq{unc%%M?363Y ieD|ح2Zڭ&,$מ͞7du >=]E5ךwYDO= c+k6VhM{5!z o~tD8gyMJ.5%>U+.i:FW˿ 55۫[ba"iL)3Ԟ8φ7$_? |EZ²حO-|Ȓ`w88T#7 zSF G ڪ[A?dž1Kw9qRugσ|gU-qTL.VWo+i*l2[nZO5? שH~ EoMA<2)Sn|t۟^"?ƭiĽHN_amiٮV<+Gx)Tn:]!gFKO=Wuۏڃ>$qzō嶜=bhɐnC5/+⾧?/,$*ھ!Y9= '֠q_-ޔ!tGVH䎜UOk? 2KԻ.#]6/~Z0r6''wmpVY_ m|Fr23/5p#|dm1ao$-(\]/nwߛ9}QPW2^x;mVHZucy©x'\?i?xCCѴK}Il[iQ(8i[A?|3z'yy q ;_?3_? |B/-uSɽH<)m (!կ%B$sGnvf`;s^_ ?g ֗'.uԳaw sѹTv^ߵi6k5rOڇlZdZ%F9gP ΢\5M]7שQ %JZkB էլ5M+ice (ǡNz o~tG5 1ˬ~ ܼ"|'V[j:s~6+fCYIۛeK[BX?c׺S~ǂ/ գEK )rHqW o~tKai"吅lXiFO>tK]wߩߵ_<{i^Pv6[G46 &|cy~&ƃqxWZ&Ʉpjb#ylf co&>:d/om-lm渳*v'8* ]i֖}kd$bY)^Ӈ}}[5ym~^".K+߰ZKilHq=FOjghxYL壟$oac .o _|[k~,KSl.nDU8U[A? o~tD_(:wm(3糽﷛}Ϣf}TWZWCҬ.FRGhY.<Ev<1y_~+:_]͜«*dJ?Ək? :㯛!Cpp\TOA4[A?\1OG_W~՚N&0G,j߅5[jmWM72?;qlqO*_x[Q:|chbF< ~<}_%o>4T#wFxU.#9ٌOA5o-[owJϰqJ%o5]пjOZ׈4XB7E# %9FC#=)p6r|y4UzR>?OK$l }J?(6x>SW٠"L>B8ϧξBGDWZ?y~ӏ0xݎ3+q$XUV$zYJHp'<OV- )s</bQi-0J|p 7O|sù;@@)4[Ec͑Pd`:~uak3admQsxZ(.Njc1C?zσ#>>bhr@ߒF>1KUP0IηelUpucʙ`j*ji+j2#cjO` گpE2&(=N Ҵ5{2\8i>mKJ:nys0RbEm:cI6mVGa:߻ 3Vl&!&$hHaqM#8-t€ӊ۵"]h "Ev' z ~-mbArZa%vH2sZD[-s۞08 jywt- ߼zm$T( s ȯhj׀CH@-\-֙h焴#{xR=QnBYٽ`@n})վ[{slNI>b˴r}r_7}0RmdC 5 $IvZՄk-X}.y *-bq0>dV(Պ-?`Xͮe׊Hkۗ=0< Ҟa5h~gS?-){ޞzzss Mm4/Evv^ܼiSIɞ=/uؑL@.;M(Nj{J{ՕW sG[xfym.lVdQ0~.hN1]:<=/ jK>9P1Xqk~+ik-PmH!z +<~_F!}4Fcp\y}3:gή3U.k~ַU<&*VNr'ubKe\[mui_a\ʱ/'J6#m]E[?_=6}W3E,|Lݘ_e;v_܏8cYݨwg;>99 m!`x\!KE*֭M/_wu6~5M&-6m3KdVD(>2I8ݒ a6to/1Jn @Q$hUI\vWex}GڱĶ7'ʍT*'N2{w~%I.-k[}re?ִ¤-Kw>a_9Ў75KCmA #DeMˎ OهU6u"-Z3JOƱJwYP|⢧]>UZ|W3/u.Ϭٵ0m$FslLPz u߉W'NX rn(Y7=8]6*IՌ-;S0ѥˁ ߳G_keyt+6uq $ (\+nkzgq Nnk_jnWе%otRÂqtq|X3)'XW>-S趚6O5i1Ǽj`-|M6Y41oتF beM>+dV1@3 b"3QՕ_3 RbH̏|c~{,0E0 w-4_+m"iz,l\1< zN)|Hc4X$0P~?ѣ43f tW͛/# 1NSZGR ?=DCb`}7Kz/|c}O1Q#{/$Bm{($ ֽoG%z9SJud[;А(EI~3K@W]FJtd̰ne}1b5g+oܟ.gJm߸ݫh:GOB&K1xe wߦ]!KG}7C;ˏ7>,ĩ$%m|%\W^G\0x+uw;%G(2W~_v<~miz ^PY@$T d$pk/t{i-!4&A1FW: ]fԼO($Fj+n(R{ERb;G^FgltH᭗=}~&u+tDhbnO'=kmv}+ = xY/DʄL-wYA}bI%h9?d*Oٷ?9:ɭ=VXML*gMy1QQwϱ&nTܬ[n:3=5|;cwI嘴&iAvtVO59|B09.`Y0&[<`$kQi W:b1.?7?j_GL?jb_C?0V7}3+b_C?ዼcA}ƨXϟ诠?ዼcA}ƨ.?7c?Pec>.?7?jAL?jb_C?0V7}3+b_C?ዼcA}ƨXϟ诠?ዼcA}ƨ.?7c?Pec>.?7?jAL?jb_C?0V7}3/ZOb_C񪷣>5u/PS%-3ȥNⳫ`9%Ul)x8Mz/$CKW梡;q##h>?-_QKCF#@CG r00Xp1~.ՓEKg1^Ork 97Y)FpHθKx&0$q!YxӂOM]\g8,NX{sF^&6G^-AĉmY$ X <lV$#JռexcGmo$Bd`lR\]YB:r茋9ف n3ӖنASKw)3B1wk)b'NܽD68;PzZQ}YL 7EF7:ɳ}bDF꤀9ב]Qnp7?coduUŽ4Vx*Ruy2 c]%a]e~k92N^;UL,!CȌ?7_KBQ=OӫYjW:䷺Ӗs4͜5-E^7$ֲ<9x7y03#5VR) !y!zM.w[n+i_G>!phMpA}k4HekxלqtT:noLEW9x+?V0m!_iHۑ׫_xwM)m/7p>Q{-mu.sn`k|.K93z0x*?e zXmᣤ?,4gvrsV%% f6xzO4B:r+?UZYL'h󭏔 r=Y=JHv21VwKܹ%ȣW$[fyr0J+TuM5畻L ;&sK8Q$_cda>ՏkTqveykKHϘ*#׊~qeMsdV6!T)r:\;Az$bGu?~W|y+cl-9e?Wn3{¿XES((((((((((((((((((((((((((((?o?SWU.0K('O=-_Ea!6Ui!##$ e]UD͂GMee=WOmn-͐^Ha]$'pEft0N*ƎՂcDT)BVi-4\=&J}[tRq}4ё0F3R[F#9OoZMw5g{jRf)'"'+3Eq8ol#S=YJ)m1ZDU1|#ϧzx, Kam}.Z@U?61y+f= 4yTzҸ+Au[h̜id6׷:uŶ`m9M:{)/byR# G΀r+iOT9q,`v8C i~<\ _#C'Y`T5R514M>dzLᦽj-W+}=OUeXW 1qx8 m#T$i^i23\c]|ÞO#ֳ=]xfyt|qE'%c$8K&h>͓@q_U `4\~Wfk~<+/_[hAES((((((((((((((((((((((((((((>|ĖSWୋy")aҿ{~;Df9U/ݮX M~ G f{Lj]4*~R>tss:tn(pF J14kImDm~QS_cýrKF9۵g{ht+ӦHVG|3C+o)!ʍ*) OU}>XJ$pֽ-q8y;F/O֝CBޗ#H'v5Zn>TMTӼk̠7Ti 3j.4$yRȌ2U!Y1ץY<ȲY:U"XMɒʬz i;kE},4a!WI}%R':ҧ5뗙/a`~h:Tڝ6LY;^ԴVǝ*woFs~qm,fÏְ#JLѷ4QRd?>+:m:QX]rg<N] /fdn;;}yi8dlT1>3F_%scK6ʍH7gx$m Fyjtt[ʹp8[Pyo `q$`c9ʿ6_ʏKs|c+ s^yskL-B-$і u^7:VWExuGuxYvQ.޹~RpNuYTc$Q-߉t3k2$>; e4@Tu_ `xW^?ַQL(((((((((((((((((((((((((((( qggFM_"Jz1d~gා? ya;LZ͚@4th'D\2K(#ij"z.^hג=AÄ)'+м39h;w7L%Irӣ=O,feq9U\G>i+q4EQU1[ʵ']7 p"|~%+Iԟgc$Up9P W`( dPP>Ɲwf6vuucvI=s/B{@6ocVTօ{slVMr;VZnE`pWfъZ1O8Jm3 $kr,k`=Olq5PtW<]{#/lP0qֶ1ss2pdo>W;^dy|uGktv<'5VWhmLL^22a,6qi#E%PN<uM&o-KG g~{zI{+b`.KÕ ܤq3fZ,Y!,;Yn621Wuٟf19dd}:y85qtڴl4U @C^}&XYc54(4{uY ]E4E&^B}qQJ<4oc8Ө/eCo - Q=܀0q_<~uCB|GapXYXh~wc~^4XY< {=E>+ympѱ8\fN쟧^m:n[kۯ?O5c}2G?>ч<yz};ס~ϟSF~r1׫_x/NuK>U2HlqZcХzGXzXڂ]5jY%ನj譭,32J@d=~#]X@6? i \!~ /yqe)NQ-Ow(Qj:yZ<{ `xW^?ַQL(((((((((((((((((((((((((((( SWUfD H8I z u珃>;ƻ5~FDs' JGM.Ah[D &*lǽoh:5սQ4Htr  tW9bhxmqʰY7M۬enJ?JqU.zpɫ|FojNn/4!BjRgHI$ <>~#ǴDN{s޻^U`KӥpRJ5Ww(!rlFܧӞe8X Wyjm5)5y۲M3Ws}Q+9HѸSo-@1mk:C2K, ʈ79'%A?; RPb$%r:kIs)+ط$!##?tև$~MAk%!Hg"_j1) |5BOsN\N?>0`9Q f3V|GŵEHw {\aIf|7&1BdQk{]eP'\QOLzWoQ\we,+c+[Ym3~}@Fw}RK84oH%o B\J$f*YjvN4 5Խ;c־)}&x{r8xőEcyo+)T(]mT?eYyx)$qy=kϼYqcLH N}+'V27n\=Grzt\vb?sp_uCVqًYKvvl#[_iVsqngV- {`{܈LL\~}1TK$QI>s[#}=}"}G>$;>eI;W~c"-Ĩaa<~{*> vc^gq5~F'm^T/Ɓ0g,:sJ0mMh %T1ҨJ2gޙ<$/!zKjd*q2@޹lvߡ۵@̊8BW;rx=8m4֪ t5Y ӝ,' ʡx8tVV$ *䨺XKeIcWa#!el f4T_3qV$Y&s?ǭxf~Kլ36>T8R@sWXMqB1:)+rqc*FÑP wKrn%73~*st 铒A=)읈I+L$H^$f̌G=ҴҠ5==Vx<^d!fcEu>b[̲[ N[iB_f?¼zʧ[۹Rvsk򣹞> 3{֏o5 j-DF(ApOֺO \xSOy0vs5x_u;|w8'fPY9$'hZ`WktE] bx k+n(3dМ;bĞPLהˠjPgA8c1Px5%o6XETq[`;r8µ})֫K3%} W>1FI.IbFR1GҼmQ8!D>y <Vu3Āૌιm}#Lz`aI{V&Y{̷yZ@VKJvx5G=C2 -ͼMtZ饏hG$H!wX)ݻ$>ؑ6 *HX\D5"(ӷ!wF;UyLGp$aѐ^xF>bVeY@Dbc8>]}|mcaGEG_I4\sǯjOqoЀ(`QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEx?㏂>>-jEŽ`0r_DM_=i ݎoJZ8-0F>+[=grp7,\żˁ3ӟm qq浶Y66`RW:`h,$(zwdjѰh@FN9FFx`qndhʖݥMp}5 uq cNէau#L6Oqeu7w/: sWKBgHpch1(^N"nǹS:+ (tD'ny"K=jȗj`~eej/=@uإ#'1⏡d&u)euYp +q]ŒaP~irk溞5!;W>!%{>7nzk:||v0\M5A:g5x_ՠ&b̞[x cp:cDd`۷asYƇoRF;аʽ.HtjsM'xtP2[JA2#탟\ּ~3[3f\&Cѳ3IzW1d9a+wn=IcƝ/z6o{F+:h==.JGt7M lrZ_\. $}k^I')V_Ys*r OZL.[K֥xgkm䳹Fl,c<:3Vs,ֆHDjԒ@P{5EcdeSj_*NWK'-N-vyC)~pJ]erg=WNsTFUFqb rOVr;ԯp<̌I!@`OYYcJ,>Ӑ9xrGSHqҙ#C ]F/nv^Iby =UUoHh|3swj5DZ6PH.R:ȸ;_α{q>m$BfOSߏZj[ `xW^?ַPŠ(EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP~ |a4[̏_5?$g:W @;G%IM_~T=E^$c$Hzx쮕!ctmuwc MـP u i2Z$hsqTM7Rk9 ikHbPrڱ!.d skvIDI 9  g?OirZXA?>3JY.!$䤛ܨ$$v9ܤ䃓4R[dyv,/!iR#w6q~U.6w{I1rCAGmpP$:R 8TQӬԌ}M8z)dHCBG$zW7kes)i' 'lI[77J &$'>³vze :Dq{w}Sm35g,c^yPFfI=+'D噜Qr><5*n*6Hrlu Gt>\iۚ9}9Tinl~Z?3y͌89Q1q!۹>==㚣 q\$ Jap3֝dg91F ;.BM?ϙN*G^ԑI]<rE;wmVgKa"Q:+MŴ,-V $J02qgUyB Fbun}}*{{PC#`qU9m œ~ (O_J_Odo<:֫KjNT|tn3V ZK>y,AʱwpNHsB%xZRwpAwN=оˠxc%όtKJ5Q@@==ilʍpbWu$t[h@P~bĞzssg(YXj;Kh$F ār=kwÅ|[VOC? ![x*;t)7F ÐO<:yk_HKėauӭOOqsXYFk~<+/_[ (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@Y]tu,AAjkKC6xA2hjשR@d%OX”xOC4m<۪jL MtR hI:E'6Ee 裦`??†ēy$ڧ?jQ@_?D?i ZPW"!ci?S)[º+`'V<)=h?µh DOi G"z6Ee'OT ? *j@im'WM"NC-ANN`OS+RWE=P<+=eEd~Ef 6dfLgץ!@H pٓҵ( D?i G"zOZ(}Q Lp:֫A{i} !b`20qS}]~ ??oJcv^@7>׮?MbSG%1ϻU}]~ ?4s-P(diJ24pOҋSG%1ϻUU}F-3O M3;TAE)}ݿV\u 45=!#?BGWu+h.!AJd J.SG%1ϻU}]~ ?kO1E)}ݿ溠=35}a?R*Z I2n;X3.U L>}W=Xw7)aem,VZK8w_>}Q LpkO1P]Z}q~nE( L>}Wwj}VMcs6=I0` ;jXLt`Yu#Ƞݿ?)}zI+5a2o&6\ Fΰx֍@Jcv?o 'R:ȚZc U.C L>}W=E?oJcvSG%1ϻUQE)}ݿ稢t?>}Q LsQp:Jcv?o(%1ϻTS\\ݿ?)}z.C L>}W=E?oJcvSG%1ϻUQE)}ݿ稢t?>}Q LsQp(2ztBT` vMĿUP 7?TMĿUQpIQ#owH%⨢MĿUxG/E\owH%?(xG/G$(|KQE??&Gt_*($(|KQ 7?TQE?&Gt_*IQ#. 7?TMĿUQpIQ#owH%⨢MĿUxG/E\owH%?(Pm[\ԞRh-Qc#D%~~)WgĭGGtB-;:᠙3$dW?/_ x:\+q+(Xǯww [[}cd7iwB@f5O]D岳2#$zdk |kM4HW1DaǓ:m/>i^"𧊼?o~YӟNofXش⡫O*.Mȯ/?HEuI4a[wgIu4 I#q8=*o.~+7; m:v]\C&ƿKa :Moc^8zF<赯7wIxSkں1FÖ9#o^:Kz%)+c#٭.}+ zWÿDnnm'20E0;F++~--_z_4x:ŵXǕaCǮvM_@9./;3^}F"ԯ$iKg$E*0є + f帹Ӳk^IIB ҳt1o~;ӵ x;QLM! G%pv΀wOTuviv0?ho~0hz:^h%𐲴Q,lU4;4okoc3c`i=xP|o xN!?RҚky;0ʒ6[  =LO zhwibL(a( sjuvOԫ{kXoe#_In]Z[۬ #=`zlV'Sl yֻoɞ %x& xNva~ۆ V|GG:^,Ŗ6kxzhNi>wjI`` sɦEEN-w)/@TjOkv9BHK/Bp((((((((((((((((((((((((((((+Zn9}SH&Eڲ\@zdҹnvT紌K ?U~gJT ?U;+S+surTc=.._*0U;+S(WtOx?QWtO: i7uG),VʬV<ǃz]\U^c=.._*x?PEO \I<%F,֨KԞ*/U~gJT ǃz]\UK ?U~gJT ?U;+S+surTc=.._*0U;+S*OWwhW*AnW_/G<!?_/@ŏtO$?“_Yҿ?·?_/G<_Yҿ?U~gJT ǃz]\UK E[ `uOT"Pj?_/@ V/uu$eY9"c=.._*x?P*г*BΕ?_/@BΕ«w :WV<ǃz]\Ua«w :WQ ,_ [K urT.Z6qY[imC UVj<ǃz]\U^c=.._*x?P*<ǃz]\U^c=.._*x?P*<ǃz]\U^c=.._*x?P*<ǃz]\U^c=.._*x?P*<ǃz]\U^c=.._*x?P*<ǃz]\U^c=.._*x?P*<ǃz]\U^c=.._*x?P*<ǃz]\U^c=.._*x?P*<ǃz]\U^c=.._*x?P*<ǃz]\U^c=.._*x?P*<ǃz]\U^n,l#'7I{"̽U_ ?@VT<O2.M̂8uCPrO6hۋ+Hf3^ȣ/U|O2.?EtEa/G *?d]nQXd#Qid=5$]F$(I?<uQ@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@Q@Q@׈%XEqiX @3 k4گP>Ki k7WkhK !֭ٷ oM>Tmi:޿ۍ&NMGEI&yFdH$U %;-/l}_OiZ=GXk颉n0dAA?>gX״-7IԈ[;ۙH s|3O&S!6iڬz\ڔ][]@(ݐv0%@!9=vIk]GCw 6hZ)FeN1 {!7m/__=wB9uXۦ_P>!xNQ𖱤[5Ι$r:J5'o 2?n;[uبH9N -=4NG_eeu׆GZ) GU1Gb  Ud ose)R%x{3MxA4]CuxA'xo[Ufa$p ?u/턣 5}};\|U@4dzBa*C,1::0ԚoͿ—#-P]c:<vP6u⡿K#~Ş#s ̳<>'ş2;Y}?Xmir+،Wnrp %1? j9#ZMf['6gfŵ%r~`pV?,|~9Ekz7{Ep)o4eϢ?'#2Kѹ]=.?n:|qӇQ417g7I$ wP; PK}Eҳ##$| E 1600 2560 0 C     C   /" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?q/4p8b;zhSJC=RN᜞ {O5F@ʖFVdd kxSL>SFtH\@#vo-sbk>_/ }jK@6`SJ1ؚҲx ljZb^\=L1sxWdw\Tvijl~M*YMHOMY2aqo6 ?½W=sBcxl+ \恸fIc*sޯSl2<]y{'W]4Vͬ4 KFjӷ1]Z=ҕ-cz^װ0xm!Kz.?C^]|⮍;Z7?,,c+ 7 ӿZu&e-溳S&27]E4ϴ?z^};zd6{+IV0L,td 1JA·j7Wx[onә=DldJzɿ}J.:ſ|mX-^t Zkp"铵 8ޗ+;OE}#GmC|Q}!+;^}ޏo[}k>lH?Wj>G܍Fo{zXaz?ȾhX6Ϭ?g}ޏ{WFo_3ɱ7+čojF*WQj6-!;n- zeh+=vF"B0j)^/W7Q,o$mѕr)g%pF 4] jJ)5XleEBa]\6Fzgm]x}+Hkۻ 4遉 =o{G#Ad:2}Tʉm-™!!Tm%u'Vf4v}B[kKEQ彿^3R:]5|!HtT CoqW8HQ8ḑe6n=&kXxE-nײ^G,[ΐKg4)@? jy*G:$t'#=:tgŭRO݊&`gYIp?Q+/)x&ܹq|l՛9 +N|iVg.zRRZgHbp935['|97qhkGgZ6A,"~o\;-[6ELjĉ-"j] |>^+xo<.?ɯ,f$ɔFCw=q[VKu}$1D +* <78\ = +NUo}]+fEڅS3ԚwƯ}3G>1چ'ckx.0bw0O?DŪ%tIſx_%%g'n5}_🉵ť=z}ރV0!Y70v `á&u#^4ZɸKk,&$XrWcLVJh] >%QvXz^>*5kmt&u4!AjRvN|k;W9-bdvz2r849[uR[jjzB<-zr:W5a~cFp_@]Ks.[6f?J|p^IC_(0Anހ(rжWq\=cKM A9GLpʃ,%ڹ1ETR9ԫӧJ-:I3uC{K]6QgXo)|\{[I o:)`UVݟMX[?oL=;Q亙B U"D( 5x{f,maX¦5A}Iۂ(C3WѤ2VEGG}kcj0Z@t^@JG_?2_Xqtk-y }xXVGQgn#؁Ȍ²5{FA^sWl9!=t2a. } W}׸#e~ϛńLcK .s1fiAEo=^7I9e⏴Z>}kvQ&8ô7 F|QGO}҃$'zvQ&8Yϊ>}hIPDQ҃$'z0GOi>;J(?PDQf>(I'־iAEo=G;J(,i>}(?MqǨiAEo=E\'֏Z_IN?(?MqǨ }_kô7 IN?asoU^m!߄r*^M 9`T`t֮'w)ukM xo?:Twv*I|me_`PDQ҃$'zʖMŚ,^Yk:;"[HF>b( 5c_5Pk^ީuP5#Wc7*A 2}G҃$'zvQ&8ԬU]麔_G.m"h NV6"6pShʀ3r^+Qf׭hh5E\䓖ǥ}҃$'zvQ&8ĝ>f zGME[vLQѦiK`8s7?l1ԥOM2X "V Fx5;J(?PDRi\l4K[=*(pVfݙqe E:ÿ&tz[4[O`^TpˬΓn>c(I'־iAEo=G;J(,i>}(?MqǨiAEo=E\'֏Z_IN?(?MqǨ }_kô7 IN?asss"HʒȌnR}տ(?MqǨiAEo=N.|gkkk,˲ԬqR}K]z&-An[Fsiu,Lc%IVI~5G;J(?PDQf>4֒{im--K\qOzirw;Tw>kABNObhfbf&t赦ms{:!|+Q]A,6O,8<QnJҁG28 N}+B{e(k;)S1rךϳMEђ2~eJp\r1 NV2'E]tG+A?(<Т(((((('ֿihewk}otP;"1݀ ^_2xsᎫ;\a=wúf["XQǛ,"RU~{_>?>IȲFє>Ե<#%ƛ OwEC)qm刼Q+2PW\e~3KO:o}xcQoc$-+@qB\RϷk 4>xWPӦ`IKD$t+t&ŠJnvyj7~X?w>jNzg b9PF"'k˘cFgtV+ ܠPՕח ݤN WoL v\o4dqe4^3x#z7M]WAnŗy!i?yw+aⰼ#bC>8|qۈM4@vq ,kaKz1ƱF,qUO;]q0F8P쟟7/z4=n} t[Y#eݱ$`B N7.zG_!ŢoF/-UǏ/aפs!x?žZk3M=Ν >}wsZȲg*H}y0oߋGӷ1t^is{n(bYInx]t5^[horK$i3ƮB##0S۝WRt6|No$KTҵD\ΗI$hm\koqga|_Wv3/~]kBG!7BKwHK&#kv^6Sjj5 G܈NzeR'r9 ʊo8ux^__+ ОTx$Ov]'T{%QPXQEQEQEQEQEQEWɿSzuy ׿E]=14Ŀ4qcWe2KC7:PM6tK4́ _|q|-c/G4zFhI!F#YZ6>:ѵ/[bܘ1hYsݱ*߈K'zλZh5~e &@;i$ү<;k6-ME&Sn7C ۳s\yKt-Qd[Im!#FUVR6#|j ~&4!ψ4.l^xR[UODw)V8cWԞ~Zk14h% k ᕁpEy~+K_ιF Ŭݤȓą H=28d/#sm_/ZdIyR[]#IW,BeHlskwy!ihg; >?#lt\ƚiŷoەӂ@\GPyMm;@\?捀!N0XjMv^h 46G"1phxmguq%'UeHՎ>^x޽gt.Mr5o5$u1yW2^*jV@,'˛zbQ9| {wZ\6W epV]QY^r5`*p((;&`X?KEPEPEPEPEPEPEK:< [d2M<u<@$4Esoיg _c?q5M {k1:d{A hE_i+no.~5+3+9(<Š((((((*j6(YKmdqz,מ~+=~|P6u0? ~<>:鈦rb_a))Jf-{jt|ШA֤..Ex3Kh$ЯtXX-f;=JTaEIoqh1iPX&;(Qn ۴F 6+;t^ ռ){h?g2^jdo9}tM3xrN]&+[U5j;š9]/ÚVLXԱ'dq <K xٗ5[hi$($RI Fzо~JlSJl[Xm՝-+','JtKŠ_\jj%̊ ;yI `21iӯo<- eu=B`{#noFטmָ%qj1Cdm46҅E~9: #rYiZsi4XJ7;l{@䧥nxq[-6/E;t!7+"+ &8_^$ {f6 Sb :xyg-|!mi6zV ͨ-M#lʟd%|*e 5ky縊ݼ-?~m `n8#>qźy5Z\$"ᥐOAdwLjntXu]LCgӚm"OW >܏ֶ~+v? m6@K.Ս0w8 Eg(-IcHg+Z <5ENЋsޭ\݈3$G y;-nci0D`Ɲz#V+yteN-&Hl]g{Ÿ!"8 §bsxw1q]ÏǏnb]{GqIJaF9 k}>$tFaR/?v:ץpbq~݆磆_z[ {J6اrwcs k۠T+ .,$1SЏAtlۡ<'U\7-vA%THHQEΓQ|V =vV߅M)P:?}ׁ$46-[$m} |-|0}I|S-'ƺZiͯ, x,26T+q9ӻGfPx6/uO$%/m|7BM{ >YAppslxo?5|SsiϠ~#C[FC̼nO,&Ziڥkq5ͻ+A! ӁvI[]wHcYY2[Y'.kzyhҮ4N]>gĎO+I$b]܅sxY|{#AKt"S5}qCReH2܆#_|s/|/o;\G_WbY^EnVbː:y߂|WKJIQ5 Ų\,lIE ^vk>>$P[w-&U )FoT!X$O_z,D~Ll)$DXܨPl+12+.OB)ft&I-#/:w]xuXvZ۵bIceT`Vz?ޫͥiIo/v T =>}6kMu.f&5* Veq]o|%x'EI,6g3f9gfbY$I$M%dB|F wF<}Z_RnTČ$ٗ~^SRq#=}mEzST]7Hګ%O5?/':# OKơ\@ɿ"~SK%O)8?׿j_N5$uG딿7$U?'?jHG5?/':#Q_j_N5$uGC {j(/MjO5?/':#ש~Ϟ>>'xT?^ݭ-VSĂ'!_Бֺ/SҼmkym>K:LCRXg+y &?Eӵ[oZGyi5xZIiRÖQqT#໛OhgŨ\Okzu<ȥFLxrO=;:WR$7{[^leI򑊨 B㜅99kz5? NYR 6+s ,OIԒr88 Ӽ ?^xM$^[j\'#96q~8Ƌ ~fӆsX+MH y㩥[ oZ׋OxDFԯ4lCjd?xHY09'oOfMv%8&npP@VN8JI|XUxҥ).-ǯ"ÚFjzŗt]l։ϱYŒg,q W>txCQ:ȱ\w l ?5?/acEi#H/T,4Cdۈ# 7 5(XԡZ/?kC񝝦,p̊vÌ8ZE]iVO9ݪ%@Q+ڋ"΃6aoڭL\`Rcp6+I_&cqi_HeN@afG,bhCsqE0&ag[R֢3ir{e]^۬V`Å[txv-}kogj.zoKLҠ4Hp!#3=#Fj%GM{/LW"]ֻokW>2FG{$ɟbEE#~=A)O|<;`A֩ѓx~崻{7_:&y*OQj?j $QH\=Gttڇi\CYmSe[Z3ў%?4|M!|o{4}N_'G^ (# ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( +?*|H߲F)E2CؼnG(Bp2;;4`KtWw.,Q2}3f8g\ńU__+F:P;ƿleoc#@ 75cXOrMmKJ~h¯~@ҩCKdA8>ARR(<9xo J:ÉKx]jFG0RZ?3 M9 LJ|;/ƿuĚv?I}rNY,<-#4#cW<)xt9 uiKL?P(,2ssRGF_u TYQ閣\+p@y8c<59eדgX2t/iM '󍤺3m! 7Öo$s[X%Y )!0RX-/z ItB}_ٶK.Tt F?k[Zx[O"ZZ%/2vA`W:Qb'T}sJI)U>%׺fB8zK~VOTtmvky@ ď0v4ǽXKɤHL߯O-vi$c#}gh}hrv)᭏WceAHK/_O8ʒy{Ax+OjLwk Q}RfkpE`x⮑xSW`1$H,ZEvuM‘$1+>ZV qmzQs.l(r~TrћQ|4uxA6zUy⳵r$/TaTsJ*}W4wSGձD&J}a//;_ 3#.{-MWqy(K|g$W5o0WMF.( 6ps$mpnU ZO#1/ڵ߈detB,7M$G0} I:>{|Wɑ'0} I:`|9uWGJL译?mO$?V>d}wE| {Co?N>$G#+O`|9u6s '>ϟ]_"$ßI?'Q|=+|siXAj!hEyX g iK+={Do#nm}DAǡ5 4JZG6rcq. p0q5wIBmB\˽)E9>ۜS@ԮSKQtTy@z@xNz y|?ʡ6s)ʉ^`|Cwikw<Y:l+?[ƚܚ>}WZY&mo nmT22=k3KL_N}kjmesp#1+8%qU!w :/bu";t{˻H9 ݜN:s xG-|;{Cg"X`1ZG?GO!94-Ln7{8X v8WAۧCCO'B9۩i?IE~HhxHR|nTX/.hdvA᲎Hv,`(` 5薾 rQUn:}wJD%;OHNoaJ'#ֿW~ 8|"L ܋"?s{J̃J?_=Oy+o2QfoVzV~ 蚷ʹM#CMywD͜gzo/7 ?.sjq=~ *$TA$i0A+G޵>15vJ2Oy0)渱m”KF89d|c?e=/|}y/-u|Gj~׼ZrZܬ{qq|i ~&?x3qZqFU>|aƝT<-s=՞Ϥ\=ųE;| œ"8牭:&௉7>.w4[ kVjiV&KW΋9 _ ^m-Ğ /.aqo[WWY[Q+2r0 rW8k5o |4Q=7I]R+ =+W sgVs$x9j_ 5k;&xܐ*6O9zg$W |`<eg$>;-XO43\B ɺg;Wk⯇~j@4Z90QծM&NE2ˇ`6rs6~˥C^K8*m'Cr9*Y[lNfMF1a~l_1WN4 /omPɨЙY]@ zq\8ӄ鯹}.M(-MNOT״Oq>f7wߵNtNXM`GzuVKxK-#Ry, }}+~k}m*M+DX}8uyOb4'9"yn0̓Wʯ*Oi R4Uc.3ӜkZ:YWWU촍;Q[EgmPd2 >x- Aq] 4gxU+7js-%Zg6G]*jݔ-uW\ē*xeXVRrT۬#^ޓ#5j4KOt`+s+m0j+<(((((((((Z26Ү׊Aa:O7ȱx6j`I!HFv<9cF;I1$jQEQEQEQEQEQEQEQE|L85Q;+3SOC+V|I?xin/ M-S͏xcc0_@094|m7ՈF as}Zuus]ϙ/?]"y+4nWZ&a[rF׸?E$gş믩I> ~9uoTѭ΢ |[ 5\h"\}13F|9h9/mPSuiJ v[*G+l RxkǺo4{}g@l*xEBF)zTψZeAgw%rXb:2WW]a._&)$U aMz4hV70fK乒kc6ly0sIƜH)!cpb'B_et~Yо ,׃QmfLsq0Q+'P3=kg[[,=RUUy3 'jM?Lͧ581|S5qy qۆU 3q6ͥڴ(ˌW8-PּgXiM"ZuV8rB nj޿־>ZeԚU% (<W;~&ּ=w?lhd$m1zW\)ƭj]6فMq 휑\W ^w~_pkp.Vw<Rwy[O$t#MR;ܣ8psK¸=o%q2ܷHKn^ǽ{d5Yd;_p̲! W7Z<36kIª+32}NJ>`%޴iv[I>35%vad$1_0;G9>;xwqy4PIp;3y-%c DcW=Ĕ׬SKf?Pw ck +"+YZ)c`è|HŶ+-F!ay؊7\ckݖs fY&*jQׯC_Ajڭ}{0 > KHI$ 5n獘Kק?@EmźhRx;zmŌI 7( ( ( ( ( ( (oo[\j:+ocfy!UFFYr@ W-0noh@n1iȸQ=Vni{e/*AVRVA4ER((((+&wHGNU+ hw*K`p aiYXec,>:뿳x~O/mkmN97IR$8#|^~+jY'R\L6]rO?(d[TW]p[xŶykt f&z ?;)vZ-Ais=8̿Nt>J}2R5WzqU]k'<>}*zr ÁKUC?ɠ,rվx}WAi[j 2x.K)=^7\8^?ËUoo5-5呮 o1doq\j|C/uw>JTӈ\tY/WRԣ_ϥhG n2&쿖Kwk֋ |2ǂOP}IbU`5R/Me-'t/ =h8ԏx/k>~x_[xJKhpMm 46ÐH)< \;yysp-p608lj|C7ZM_/5et$px3/O?CvbB:)1}(zRq8w͘V4&7oyi|Hoatmar\IF#YK~]$|1ۘUR%؈z@*{;hlm % ƻU@8dW] >򓼞^GaGF9)CR}dI$TIa,oF~^~_ZLcTl8 ^sbw.P鎄W.4HJsiןЇFdndfnn:vR)88|gst lU$&b0sҸ1쮏R>5ssZMܜ\_oD<޺ AK.$v X_z)M$Y<-Oh_v^>-xxB寢_N>g"ow Y∨[pw|`jv3¾,~ؾ%!:7| $L+xZMvj)!r~wj?i0["?l-TCO)f/ _Ia?m.һ-[]=jkoh2[sI$<;x^#|=jM?9=Oh-o&/1)#h$Kvkk:ŤkmMOŗZ/<_8<5bUKQ|?746,rdg "qgiV[r#kp'iˍDToU < ^~ x3COB1c. HH %NK oUQ6{?Fx=ǿ-iiׇe,*'!1IT+t^5oWGO;}bXKT|_>skEOɨTγ3̦U؄/*~ϞGSBh๸T[$I怙;@`zU]s'm|_{V5~,YOWZ4u^!w4DqY zGw@6AK]4k’jTo4sգ*8qW57'Z# MG֣~_1O/>JDQ?]z?᩾(:Rx}VKȟ%S|Qu=j?G_ZG)SK%O᩾(:jo?N#Gh딿7$U?'_j?GG57'Z#ִQ_jo?N#Gf|[-{Ğ!ӟIM<Dʬ{H.:W-蚕6Z09B0sȯX?2?+5GmM p8*'`Jޡ8Q:8TO@ɷ9\ ҆,;5_&@CJ֫4@Xɪ9X"wp5wp8mNo'O#䉛ouىkǻSH*l8˧ VA M"k\Q=3^{cktCbgLGC^;P)\^oD3 mLunks,iũn|/ռ:"zWGpu? &dSֽ|>GI& UI_I &T;N儕A!G7?0+0ߩjбESՃo'G߉?<OVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$h)oZ:jv~Ui%̬1ڊH)9<~b+~|>]YU|;߉??`IN~tY~ן-m !#|c[TԴE+k^ HѿX=&4j7b?iOyo5?fب'1/述,u 6a㑂8 ڰzM~$k OH,CU@[Y;_Ov&믜i'kټˬ#0QVo[*>լz)x{}ʣv>*xee^hĩ滑UndTymcϥw 4dM03s'֘<;pEk|; 9˷))5@=+,7 oWiWXN$ђ~,h'WԵ7WFz4"J?YQ]QEQEQEQEQEQEQEQEQEQEQEV6\N(!C$@'k3w_)LjSM&Ќd} ?G?~"F:TZ5`e]Ur,eh_-$k|F<&a} !(pAh]PB]q< k_9-/dy|'G?㿋P[ jτ'xkR\&[Ҥ =Cڀ:Hu+i -VfwvMRiQ^=+P6Vr=pXxu=7/+ .ai xWYMt-T,[j(r@4?. u@}CO.Ck6t[?= =NPk:efEXh>he~e#9#N>$w2YA)b gxPN޼)h]PB]q_ gi)ӗLZDLcmm|S|e^ygku}6XCN0/$p $gbMf[>~ApߠX_IR+3~iMiSTJlcTg5Z}/m)I~u?Aj1!4+8ST_h 4dK&xl C~4Bޔz;ϵ@ UݨPڜc(+S(oA@|4 ţ>9~!?3Os޼|dk/[0O5YǷ{5r?]M +n}E3B( ⠸]@:R\.G#$vw4kp² Wn=wJ~z~׾xnVnx+q\/H9]Q 4"N9ĕ|M6VЎYD}6Oի/OՅQ^Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@sw|J=tˑ 髬{źðe+ր[j{N/2S ;?xF3ƞԣBҚ};DWuY/t8.DoiJ̢6g/$"I"Hv;kLGHt_p|9Sp`s}}8;[M?j]uN$OXTv ]9z PfQۮZ?T^c14.y/ [@RxC_T]GA&FK`322E'|ziokPV5Y/JHH1+GYciiFة|KmmV-fK4u^_-i%\?kG?kT!5{? .>#hZnkK߸/ފ;ֺԴ?_xQЮ${%Hk79`ϴ4 p0 ~O 8?5A]?@w#^8*KO0jק7,z,|JS^$ŽM!}>wo$e^5n>\3;72*t!bs,qjKH/V֮"/mNJ%ԅ%Ojb4kZlĪ\Va_4f*PК>#k1_Iyn2ŷIWk.07|f[?5A?5A~xW~4Gc"M:{fvȅ>Y7i|C3>'I&(&k(|p {W?kG?kI{ zf.:' Ũ[qy}\ڳLpJ9N "|>J5욖XL6O55ĊmF@N3ǿJ3P/%o7V>+3i+44_sw3^A٫N?KEV@f8j%~͚_EvCݧ!J?f8lߥywZnrnErA f-/3_9vBOON \ߥ(]ƒ9ɚ<+hka xٗx ڼB?%ׇc|Ӕ_EV*ũ:;ֿMqNqjE"91OG4Q\TֱP:4da{GO DsϋhP??˽tWύ5$ϥg-+ iQ6pl֊+ORR>Zi߱|U&=m֯KMB.߷0'EG()ױ6􏃃 t]g";`Ѹh!X7d`@2Ey=~?[ ڴQ[œiEYujΫ曻1%w۹It(nQY>(D'G\eI G?Y5&QE[!l~ Mqɔ|?5&QE"g=%xsE~([?~7:7򢊙lӇ⟉70iLH5^⧊>vXE==Lϊ^?,>}ḗt{?} #vnTE\v&[%Mg`!ɒ'qqUi_ /Oq?M/d㴣⟈KY?VcOy,v|R4E0KY,v4v)t K??O;E@/-/wLi?C3Q@ K??O;G-/wLi?P K??O;G-/zi '@E,vZ^"?O;EOZ^"O;EU6? Y?P>"ݦ/Ijگt4. vEM8f8Ŧ?fotoxx-20.08/images/sharp-comp.jpg000066400000000000000000001650151362435004500171040ustar00rootroot00000000000000JFIFHHExifMM*V^(if%HH02310100Fotoxx:trim/rotate| Fotoxx:paste area|add_text| Fotoxx:paste area|paste area|trim/rotate|paint|paste area| Fotoxx:paste area|paste area|trim/rotate|paint|paste area|trim/rotate| Fotoxx:paste area|paste area|trim/rotate|paint|paste area|trim/rotate|add_text|add_text| Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 834 1465 0 C    ' .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQROC&&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOx" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?-[tX_~K5kQl~-_C^5[Y{ =+Uҿa/޷l?n?%6cn>#խ0zeҿ%Y]>Zޛ]wƫ~K51#kb}=ކ^5jJսG]T_s}O[lxEh*lGgŦݫZջ=vǏ[ƭ:cE}Z6/޷?n?%6cn:/Ҍ*lOԩ./U-nͮ_[U?n?%Q#խzzի=+VvQ}E>n]_Sr/tUYsW5kQҿcgq/սG],}\^*؏O2-WJ/z޳lxEj{ƈҪ{J_U-oM_[U?n?%Q#+b}=ކ^5nJխG]T_s}O[lxEiJxElOgŦz->/85rVzwn|f]4Y\C 5+J֟K^/ޏ-WU÷ʪdWV~ҨeFH\̿ϞvYk[Ym|\nUNo-vK>aXϐwͯrjk'[_heNa- =(WK?ln;ힵk^˭ZriFuZJigmjg8u_VmxRS.j:t3/ž|jZV-_,P_h:uyhsY|\֭|V/_!?pj ֯%g8%gZ6loܷZ#:Aԭ_NY46WVͯ ҫV~)zRm:wYӎfK>Y\֭|կp(sʴ:9},fK>Bqޮ[6a7կ~NuTW˳UytnV~꼺կ֚~gVcRYqګg+v]jmU^?p Z0G:m=.AZ<9pZRKR`k/װX閲\F+OkN~4ˉYi#5Zm#&zAe=;#j7 &^\gzεoc:e|)Ult%/*I)9^+VӡQ|իM m?)W5?ijoVZ+ZzwsQXu߄W٭ zWO:W,/CXwZr~C*ޣu}?>>լT^<Y{cBJyKAguq'?«uhK'DiT~.*{sfm+_j&տLwZ~C)FNW#J- GJJjݞ]FVT_}A}ݝ֭d N^E"Bg!zWO:W,_/CXwZ~C*֣u}/>>rW{ V =+Uҿa/U֭d 5֭/d{WȪa-ͩ WҺ|Z%LwZ~C)F^W#J-GJJj՞mFVT}A}՝֭d %J"TW3=+V+/[;[?!kQվdzjRνs҅_OFi_0*V2OVVЗ2O?ˆҩUHU0W/{=+PMu}_> guq'?”iU~4 t_]>ZnoG'ڭjIҫȽ:Peve[1o9ոL]en3Is>tR5Y7$cEB\O4~o#cY]8Ņ7È?4xFRY8\ƅXҿvxYnfQpu9WuS_o/x[}+V/'"QY|uts/xʜ* {=+WcqޠxUyn57 Ng*Mt6WҺ|ҫj<~*u9_]HUPӎfJcj>z~U{G?n:u9W:p o~ެ_N;=ƣ7Zq T^8U =+^](}PYj<~ֈөԅ_c{.vzWN*.5[VxRTrxt4/ߧ;jȿ|t|(trxtWo0ެ__N;Eƣ7Zq T^8UzWN*WcqޠxUy.575N3R}}ԺWqګ]8%~߾n^QISJt=7ðJH|g-Mޛlg5­+gmϹk'S~];zU['UL~VYLʥR\WSWSћQ|V4f~CҰ;gQ?VZ=(t/yM[f$_'zV ֳ?_Jk?d1[W{e:3e~OҪͣ7'Jcu]k?_J*A8Qh:_UюJ-ֳ/c7Z1*J^W#IƯjz3}/}*ݦ[X:ֳUKgiL~:Uy i6wzd/JwV;g?ty5}RFn>OҊ̳xLʊʥ*߅_f-<Ei?e2?} b]>Z_| k*T׼*:_U4l?#WI|CPY]>Z6/TJ3XR0:O%Lguj WҺ|J+u!KC{:֓#ի;'#Xջ=+CK{̪PmΓdZ֓X"-zպT׼)Bէ3BI2?y "ކҺ|^m+&DiR~*){{sjk'>_wZOFͥľ_jT_˩ ^ntE>Yi<Gt_]>Z%J"TK3;'#V'GK=+V+/tsyhi<G5֓/d{{=+Uҿa/TF.g2B6Lguj WҺ|J+HRƆu/5nI2?.E5jJR^*)}f~gsk𼶒-&Y1vk,-w~ 2q?oN5U5&e5FMIOą@2 NO+Ӹo rc6zWOv2`gcBNҬ[iY(jo~ڸ:|:i—:NXӼ}+fJ|:|2 ^^~NPKq}|j =+UҏMSԅ/c{-Ɲ6wwZ]+ ^^J%N+ ^>CBNҬiY{(jo~ڇN*Nm4֬iB~z=7QN>eS/a/xгӸyn47Agt˥7T?x*BْN~zU{;;-A.vzWOOƕ!KGhiz~zU{;?^=8Vm;P^—:NXӼ}+bJqޭӧ̽p%wwZ%Ɲ,Uyt >g){M.4ﱷWӸ_mjgt⒧OƓ/m{: Db2nOҪˣ/'J*|O: noUѰ?/ҪhˁRTxŸkjwZ7LOV.m1:zV/ڢjE]c>EѷV;'Mw[e$_/JJ: ~^cud'Uѿ%LNVF\UiteїBO8SQ]h'OJeuah/OJeIR ~>hj1>[ѶX:jJivҧȽ֗Z6}*ާu}/ޕi.wzd/JN: ~^eueh'_Jc.WVF_ ~N*t9Ÿoug=*֍UF_~NޕVF\/Qt_\O5u;Q'vZ6=+Sїq|V4e~Nޔ:|!OKiun?ҭjwZ7"LOX62?}*ާ/"zUTeNVF1?*+.F^?wQYN>oF{z[uoի=+V+/*<(}V^C2]ok[FݳҺ|^m+&DeCթCT]ϗ7{=vKɿƷfҿ_/5^JҌrt({h{ G]T_sѽO[oDj֣E5nJrȽҩTf~Y[M5oQo%oCV-[tX_Rν U(}Z~gqo7yokvJyKgRcwVmv>_KU?-&ޛJ@gtiFT9_]J==ކf}/KާޭY[M5oQҿҢgthr/tU(}f~[M5kQo%oCV-ZtX_Rν U(}Z~g_qo7y -n]>Z6 ~_P~Ta*ͮgo}j}%[i_/gtiFT9_iRmwjz7vzZt_Y]>Zr/tU(}f~[M5x>{չ=1<>J-u*7meK.Xg'E̗YS ~"Z ^$hWhO?&.>e.i\ W3=?p*žu'Jc261yg^.UQL}j?^bZ ֬zՋStcNa/tg]qW_lo/Zسj1}eBt:(He֮߸^^Z [2X6Wi'Kԝmw]yVm,zUY?2\9L }j?^fZ ֬[z՛SteNt}>Z U֮߸Ngc2ylbc|քs?tu'GA.u6zZٖ/̽*\|I:\.h jO'Jo]yg Ҭ_̽*ͽ^Y6rttG0m0zՋjO'Zoc~eV/b^M^9L=j-WZc~:͝\|U屋/ZԩGC&u6tzJؖ/̽* ;t_]IzHIdPXe@KFk7[Zk#&|gn.l-шM~/C[ғiӧ ^:^^xPV?d V4~dV=)>Ο{֭\2:^^g=C(hk-%8󢲛0և#g8Z~ [l8Ejޣ}/[辇޻e*8TY{ =(WJoYK5^mvB_[J2Ta*MK{=(nͮgs}ja-J2J+h{ GJ?jG*֣}/[辧ޭK49V^TWӘҏUGJ?eYK5oQl>-_CT[{=*?t˳ҺqUҏnK5^mvB_[De[U_TJ?5^JoMgs}ja-J2J+h{ GJ?j]8zan~zgqs/)V^TWӘҏUGJ?eK5kQl>-_CV[{=*?t˳ҺqUҏv]_kE#**JRQϗzQޛ]ϗ/{=vÏ[Ɣe[թ_Ce:YT\5jJn]_[{R_3NbJ̷|cv[=vÏ[ƻ_ zDRGnws}G*:aUВl>+BPpLWY%a2#6~Usc MW\}z|֬[埙z՛fOܿJoyg/Ұn*Ns63ynbo!>fVmO0ՋfOܿZ/tʜ{ {}|ylfc|ֶN?r^]fBuy'WA-coU{;u__WmxN+M*NϿOzՋ{7OZyVm_,Sn*GNu}o0֬'jžkܿZ T^eӝoa/tϳ:-lo͞k^ɬ}phN3GRu}}ޤ6W:ٓY~U{=f׏ܽ$tҥJC>o=>fVmf*k_Ym[Ҥ~y:zOZwD i=;iCNrT}jާIHy>\MUOzwbCz *3{{]&G'UeҤї瓯t>#Ӳ?&Vzwsu'S9Qz%ҤΗ瓧VҤ렗Zw|st>#Ӱs~Bu9_\S2=*O}jզ&֮j~"Ӿ$wҭxN(n*G N^KqV=*OEjݧn~"Ӿ{ҩʧ2L:^cI괺TuzvGߐxNїn'SJ>RTt:T #J3U!W[3i_/gtjo$*֭d J^W#J-ջ=+Xڍޭ$U;[?!:Uy YY]>Z|-iUZ;[?!6[Vs4ٷ q֪t)&䌰Ѩ˚I~$.cY]>Z뱜c7]rߧ;jȿ|V-u,VNNUWJ{՛+ {Gyu:nnNeS_a/xгҺ|^M+1^ΣQc~Sԅ_c{.vzWOQ~U{;G޵%N+ >B{U}(ygՑsV-u,P^¯:J{Ջ+ {G?=jΣ'SS{TWK/]8_zQU~߾n*u9:cu6eҿU-A-Σ6tw:jJNWR}}{U}+gY:nfQNU*y{_?/z~>^os֬j>B~:u9cN}BJqUҿPY<~:օN3R}}̚W|e~߽n^QISJt='@bѬZN@3ᇕ3񬯈r]Eڛ9Lnn0Hj$1ltf>OҫK7'Jewd?UeLnS9Ưoͣ7t'oJc/UinUְRTrM*Fjz3}/}*զOҰ;kq?vZ=(trB i6wz~^iwn?ҭwz"LTZӍ_c/xԱћ+~Zme;Ukg#1*5޳/cS ƯߛFo~NޕVFlZ[k:_Un./z8MOFo[юvNZ\_}*ݥ޵?OJ:Q5}m4f~NoSћ~+ ZZE{ҩө̵2j{ƭ'UeћF_7zGcUZ[gB_BS9ƯޛFowҫX ҫMwgK=*޳LʅN+ԹƯjjz3}/}*ݦOҰ;gq?VZ=(trGZh͸*֧7"zV޵?_J?d1SSje2;-?J+2Z1*+)ө},j5rI2/֭7:ObLijҺ|oQҿbTsy] t/yh<Eyt "^JyK(ҥ]HR0:O|}zgsUҿ_/5^Jҍ*\f!KC{:Γ"<E&E5nJR^(R6I2/֭7:ObLųҺ|kQҿbJ:*t?y6w:O~^k'B_ȾWҺ|^m+&BK̪a=͹t"ZI2/֠J|/{=+J4yR=籣?j2/j՝Γd_b:WT/SV-'J"TK3w:O~kQ~d_-ZպT׼)Bէ3JI2/֫MshK,-WJ/QTʩ ^ښIϗ2/Ut?"jWK]>ZQK̺=FIT_}SVt?"kQҿңgthtr/yJ?yxLrHewmּJsOݷөN WRfxXST[4|DQt+)"9?|?7÷y1=(p;\ƅw^f};?^=8Vmݞ;Vn>UӅ/o/xuw|j^o0ެ_GN;պteNBNUNc~zzWOɥ7T?xu!KmK>6s~j t6VҺ|:|.){h =?|*ż|+"J|ž|j:|8R[ϧy֬ϧ XW|SO{TKK4lӸygӾ߾^^JWJ?ln;Щ~BږN~zU{9?|QqګG)*t_]HRz~zUy,zV=|YҏxCO{ç ^^a7B~z=7WT^8R 9?|^Y7Agtɥ7ޅN3){Mg߾^^};-A.~vzWN(Txpt=DhNSLEfKhsw(< JG5]vFqqtҊ[JWʲѲ?shj_ZmePÙ}~[:_Untl/b~UVm;zU[p'ISiR}WSѾb}ڷium?OF_}*զOOJ8rxp?m/x}΍'_Jd1>`h˸*ާ/"?ҩӇ2 ~^cse#\2G֪˕2hwS3pZ7tҪ\_Īh/ҫYh~8YƓ?kxtov\Obt OF_[їi;zPÕ{Ä)i{í.tm1:սNF$_E]}*ާ"?ҩӇ2 ~^cse#\2G֪諕?J62(T)A5΍/bt7:6bUYeΗUteB ?xpc΍ջKiL`z*/ݏV4e~Nޔ:p^B֗Z6Zhe2?X6*?V=~^N9eNRF1(=x?J+)Ӆ#{zZu߲oի=+V+/*<(}V^C2]ok[FݳҺ|^m+&J2Ta*ͮgo}j{%[i_/gtiFT9_iRmw׿jz7vzZt_Y]>Zr/tU(}f~뷼[M5oQo~%oCV-ZtX_ʇ:NzU(}Z~go7y -nY]>Z6/TFT9UZcwVmv[Fgo7WK]>ZQWTzz{-zzgo7խGJJjݞRȽҩC3Nb^oz{X-zzgtjޣ5NT9׺aJL=vKɿƫͮhKokvJyKgRU]ϗ7{=vKɿƷ&ҿ_/5^JRԡ35zQ[F>j]oZջ=+CEJ9=vKɿƻo z"hcfl9ϭszWO7ݷUҿHNcf?ҴYS ~"Z ^hwHz;p|sAW\ jO/Jo\g Ҭ_̽*ͽ^YKtWoNt}ZX֮1y^f/!>eV.eӝa/tϳ֮y57l̵^[2:\I1zɭ]}p*s$m/Jga2N+M*NС\V-,zUY?2\9L}j?^bZ ֬EzՋStcNt}҅u˭]}pkb.>e ~NԆMjmU?p%_cozU{;t_]IzϞ^fZXOzU{/Jm^Ώ`[W^a7א^b/0֬'̽j̽*s%zZ&u;/7̽hN3GRt}}ޤ2W_co/Jg]qlb|ҫ̴:>CiI$Y~=LFiޛMl,V A=+?QqkC UzPJL?:62֚|Bg:>>ZoCҪPB tϗNVJL/Ο%.{X^=CqEjէ .sSғq|}jݦO̝=h~ϕh8Nr#7(zսOz"BUM)7:սOJOE~˙he 2LPiGhZ߱S#OΫK'2u{>g:^>R#?BU t)/̝=j_?:кczݫv#6(zUOJO'vRm?2t.U:^^xPСV?d V4~tV=)>Ο{֛|C*sev>#2PYGhKZ,tYtїNײzGG*=C:_(zUk/_(kJO~dUlt^CIʗej~#>?vxPСV=%>̟w֭iIևV NbzZP$_Pi'_ZIHd޵O- ΗdP +^JN>t쯱߅/f9 =,z~CV?neu}vJU׺qЩ_zvzY㊯6~/Va-Ul?%E_V~u*W0zf| WV%E_z? U_]J=ކ^Eի=,[uGs?ޭ+4*܋*JYszYV,.+5oQl>-zJ:NzU+Z~g85Zm,hKսgqs|ysDe[U_TK?`zYݛ]ϗuƫ+4*ܯ4RtjݞxխG]T_?s}ݞa-D[{R_3NbK_[gqs|Х[*Wzz~M[ju}OZl8EWi9V^TWә<jmֹ=vÏ[ƻ/ _ެMssCS[{ :dO/Scn>fҼG*T+m8T*e5]r3y7OZoe7~vV/O=?r*;iܿJt:^oe7~vVonfY_Xm߹~'WRԂ[)3tvSq7[Rk6coܿJgq^WR_mwe73t6SygnfY/Ҭ[6Y:}o0֬_M'}j;iܿZy V^eӝ_a/tγ:[);u=fӏܽWYc~WRԂ[3tvSq[26coܿJgq^WT_mwe7;tSygnfY/Ҭ6Y:}o0֬M'}jžiܿZy T^eӝ_a/tϳ:-lo͞i^ɬ}rhN3GRu}}ޤM6ۧW:ٗY~U{=fӏܽ$tҥJCmēO[vmUIMnhW sUF fx :KEiMrFb&cI?3UҤїu5XMDV_2MVϙY=MUOκ |EgK7OAUl|GM I.scz/wջM.Mtzߵ7nZn7OJ/8NrT$V=*OE>\MUOZo"Dz uBu}*J+?:6/<}kDVoiM҄u*(JiRgK:zcI?3]"D^"p~BgRu}}+S\_ջM*Mtߵ7jzn7OACsG N^Jq}jާIHy'"wI M$_}AM{TW2t25Vm*Oy'_S]ܯ$ߐxMїns~Nu}}ޥI?=MVҤOA7&*/$ߐIίejzTk}SV46IOZoD i-7iDG N^Kq}M[>$ަxM&V?idoT(NdRq?3EkMoQY ݅Ofҭz5ޭ[C*֣w}/WLU^<Y{cBJyKAgw 5ޯ/cAJ3uaW[3i_/gtjoKwWոK(ҫ]HUGJJjݞmFVT_o=VwzҫȽ](U2JսGJE?kW-!kQվdzjRνs҅_OFi_0*V1!Zk[B_ރ(*ET_cynmMľ_jfտK?«??”iU~.*h{cCQҿҢgtkQվ[A}ݝޭcC(t/yJ~?y Һ|kQҿbƳոL[nozjJ:(U_Һ|^m+&P]?DiU~**{sfm+%U-A5ޭ/cA^V1!J+uaWC[ջ=+XޭzSVn?CW{ȪPeVeYּV1xFkVs4۷ qֳSFxXT_44E]+Һ|x|;z8N+lu?zՕ|iM\ƅ{U}+gY:nfQ[j^N}m;Ջ+ {G?nfQ֩ө̽p%-WJLoCgs֪?lo޷Z:HU12i_mzWOQ~U{;Gޚ:4 >B{U}+gY7:nbQ[NU8UҿxxVo'"Q[X|uts/xʜ* {ƅӊ&/zQPKszhTs?xu!WlKګ]8d~߽n^QISJt/_OYҿv~^ՑsVmu,P^S_o/x-;՛+ {G?nfQSS{TWK/]>Z./zQszzЩ~BؓJCn;TzWOQ~U{;G޵%N+ CѴ8>ϧ,x$gmMޟngg5{o+Hgle|@-.csqG\0O۩8+I'c6GJ6eJcwd?UfֿeK~ԡS)Ưoͣ~NޕVFl/UynۧUְ?RRFjjz1\_Ji6ޕ_k1[ֶCNU^ZhqJ|XVz}*ާw}/ޕN9TW5,tcҪͣ7'Jcwe_?_J*sn9ƯޗF?﷥Vю*ztҪXRPgs_kxcOvz3m?'oJwҭ][Oct§*p_m/x[M?V=/aZ][cuz޵HzU8Te:1F_Vֲ?]_27_J*s=G8Qh:_UюJ-޵/c7z1**Y]HMOF?kV4c﷥`wzLoV.1zPSj_m/x[M?'JHO+KkqL~kS־c}Jn.3^g7(;k*_s {;+ȿZ_e2/kJխGJE?kiRνyT)籣gsUҿ%L{{=+Uҿa/TJ3u!K{s\_E}꽝ƕd_WJ|/{=+J4yT/myh7:WڢL{gsqXի=+CO{̪T}f~I2/֭7:WbLijҺ|oQҿbRνs҅/OfΓd_WJЗ2/U-WJ/R*|eU/cymMsgwW~^m+%U-Oҥ:^ntE5nJ2/ֱ5+*/?vzWOS^*:_Y\EսFJ,_}CXzWO:W,_/CW*T׼zPiѳҸL\i_E꽞i_0*#J3U)0:W%L{;+ȿZ6/?֠Һ|*|eԧKC{:Ε"Y\E.E5jJ*TʥNg1,t?"k,d${uJkKOݷXpQHST١]N/'Yϧq宻ıɏWg8gb`v4/6Y{+QgN*—:};?^f}?O/ZǷҏxVo':|1 ^^g>>,Uҏ*t:cu6O~zU{9?|QqګG(TxҤ){h{ =?|*ͼw|+J>zqڬGɥӊJ>WR}^b};?^~>^fJ>U)y{íVo{}(bJqީӧ̽*p%6s~jlo/Zg8iGz:|HR1Rϧ|*Z]+ U-%N+){h{C'SLk7/kFwA}-Sk+ǶLB3Ir_C:FLshZeʪˢ}+S3pc:7t7:62:.t:*~OҒ ?xcڝ΍#nF#XkJi҇N!OKisn?z΍H#Ձi.wzdTǙjgS2k#1*:7:*v?*62(T)(Nk>_Uk2:.*'B?xŸjW:?LջKiL`z2/}Ji.ޔ:q^ ~^KqLZtEެMwuZd/JN7ZS?c/xֱѲint#U,tU~UZmeBgO: toUѰ?#h/zU[p%N6~O5;q'ݫv:6+SWq|V4U~A҇N<!OKis?֭w:?"LV_JHw^Nn*pew:7Yvz*ʊtB^y%5oQׯ~%>jJխGJE?kiJ:O.L?e3,?-_k[=+Uҿa/TFT9iRV]ϗWׯxDƷ&ҿ%]>ZQWVCfj:ڣDޭY뷼[V+*>?gtⓕEJ3Nj^뷿ezgtj֣CW)P^JO3,?-_ko{n]8i_TFT9UZ==ޥY-{=vKkrm+_jӊQWUCCfj:ڢDޭ׼[Vu+*.?gtiJT9U*>?tl?-_z{X-6zWO:W,_/CW)P^JL=zKj{krJWJgRU]ϗWׯxDƷ&҇%j8iFT9_iRCfj:ڢDޭ뷼[Vu(}??vzX%**JYs6zikVq41dzn6gֹ=+hvfYxZ*N>ԌMF:$w* L,?pk$nP+kk<+|˩.PX+Ϟ^fZOc26yg^.Uӝo/t֮|Vo!?pjŽ^aXOz7K{TGK(YW<~j ֶ,"Z%_lozН.gI1z˭\p*sdm/Jga2N+M*NϿ֮|6ϖp*^z|Ҭ[埙zSn*GNt} ZX֮|y^f/!>eT.eӝa/tϳ֮xy57l̵^K2:\I1zɭ\p*s$m/Jgc2N+M*NС\V-,zUY?2\9L}j?^bZ ֬[z՛StcNt}҅sɭ\pkb.>eXt:c!Z~zU{=j-lKc^^.>e.WTmwu,(VFek 6H"YCzm5ıXdۋ(1.y)\xI69[/CUfQu-)2>dꬺRhK'_Z^˙e91Jx:_(UkТϗNVJL?:KYi9LOWk V̟w֭iIևV{i{1i; kSbBUM)7:սOJOE'~˙heNt}.zGUV_hKZ߲ғ+'UғB_:н3':>>R#ϗ Zw_(~m)?Uғ 'I{+= ':^>闩qEjݧ(UOKO'v\{̝=h~˕h8Ns#ТV?/(Zғy[Դ,_2}Z칖TK2GB; Т]G̟UKO ~dB\@Jx>_(U[E[iqg'OZcGI{+= *NEТjwO]*ާջ].0N?eʴNs#СV?/(\qZ,Xd޵Os- ΗeP *+VK?:+970և%gjޣs.tD%A ,.IT֎Pq՜ pjc}^ޯFD.}Gs,<͕:՝(GTm0^ݪz[qF}6xc;j84i,Y^(ڎ~SVk '\H6ճPʐ*Z3l:a,\ w0xy"jk'Hv<<ɘӫYP\ugg85^m,^ޯGE.}s,<͕:՝(GT"m1za^4mnopvph)YYlsUՌtHGK?j?VzY5\I<@61H/$:x֌+=,Ze$_ KsGYgms$ҫڸښ"8?'Ǒ!UU6gYg w^6sjH?Ӵ[`Nw{VXlkCQSqK_÷o>:oIm3Yh)Yaq*7CYJ+COzՋ{)OZn4$Ea ݩ#ёCǕ"Z)r9?;uSy _Z#gשn4D5Q ?ݦL*R,oΫe7nphQ1BǙΥWN1Q岛mOZge7;~uM#Bc9ﶢAH?>6h֨FJ;z|֬[M埝zkq$+ IoIʐBEVRўsoe7~vVonGF'שn4D5Q ?ݪy93J8[;)YMۯz:*E-M#LdG=гs69ժ*;\S}vUoν dhL{=P)B!fjdM=jͽYۧvM" ԱF O0*A YIFyŽaۯYOm4^hD`vL*R.loΫe7npQ61B!RqM6ۧW:ё1Q]dIf.u:.7E$=~ ݮ4UV#kAE< iP#f۱tjaQ*k!Qn+Str?y'j\2N5phŏ?BZ_0|ݶrSJGN18i?j.~f%h٢ݍî*(4d?١c!gRQԋOKq~OV4vIƻ 9I14:DQK됶ÍZ\$jާHy'G''-։*۴8xwZ¥ENQq,tr?y'j\NޯJF lڢ}6i|hXwrQӊQϗt:\5/Dn 8Xvz:H5=._>[y'O.(\8F1"(Ga˰V)8y}.:խOK~O]>6'Z$sĨnӜӺ*r.\I}UYt%}kclڡ}6i|vMciaʥWN1Ηt5ZK ?3^pE H׏j^;Tg"K$b8݁Ͼ9G oZXcXlD$i; 6{تÐ빺T5mb[֗Vmgwi2G`H!^Gii\7_9KRD-9ץD}"J/#8,\rI8'= wV?o\ PY% #⧠((((((((Ke"֮$)-Fœ6^8enrh,m46Iu#-Vݻwʫ]xyFt9BR.4GFQuh򘞎<: -ky̒(@-SQȺw N,q7sZDi &X 9PnYJ[[H/Muh̝oO}΃jS}uUS0hS4[Z+ CJ`g[D H,,[,H gB"M#n][K51e#cγ5uef͎8(((((((((KGԤYM!%B2;Vh znm@Z|س;zzI3@-%(Q(i(RfEf (QFh(Q@(( (J(i(J( ((h ZJ(i( ZJ(hRfEf(i(fKIFhRfZ)3Fh4QERfIuvNni$A5;w֦4ᏒO杨q/Nk}!^Vw[6j7TWiRhk]wN$ڡyIҍ\F2W,=odRJjWNEꭍڕV5 sN-rO旵rlyu'WT ; ͪ Gad/okwocv4ѓNNni,A5;sjk-sN$iڎyt?UqƼжmQoOv ڻJwn?ZֺIB\Ӽ$iF#]U+ϖRY5+$Z>ݩ^Y57kIZkw$i{\G&U׺Q]\^N(.jmPZ F!z4;AI?9>I>iʮ#M4Ϟ~i.ݩ>MY%&zNԶZ'Nu;·䓡k5wj~mFwƩA6Ұ:?ֵmtQt9I>k*yMU-URROQ;7jW;X5;I'Grl˕yu'W̃S ; ͪ Go܁oϮI|շtINUqh*u?tʹVp7jwp]<~4`ze$wWV `0*IhL*TH|qqum%Ht8=yS}N|f&cg6=Zpv0YƲ(MNdr7 C^d;+:oNfm7aWUyV]i.OƒU 9T#Rג+\Of5=vWB>SUZ38b mK^qjWPɪx]2N(=7سMͧ}}ZQUz1_/A& Fa\d ]Sڝijic'Jg6IbsXO{2@#<wjWVu =?|*ͼnS'aA\SjZB5+sSͦV !?|j*2љC>_ϵԵ~pU .B[si~:Uyf~NGWͦ|hXF?eI՘jW@nOիbY߱?VMuUy^x\K#]ɽrziobm4Y]A7!Ms^ ``ɸ+Ƿ6X}7s^<6&mI+Z˫0+ƪβn/O?妧7TS3}5FkJ9!MԯANLXlnOwԻ Gg|||Url˕it:S yuK085a%ՌNyo߼դ?t ZF*uO0n5ڥ}^?+fzq;V=/?yQ|ǿ:fqS-e՛jW;T[g ڗkrSki#^ތ(C V[=˩^ȝƙk>ݩޟ5wa Gg|||Url˕it:S wuK085eeՍ+̅7A5i5=/7?DֲъiIƲ{T?[}^?+fz3wgoԵ=/_3wo[fg>)N]YR?Yjk&RCnYz^8c|4x ZFUZ9!]MԯNLWlnOwԼ?>fi*6eʬ\ ˩( jo25=槥7A5m5=/φoZF*U{IƲ{S?[}^?+fzq;V=/?yR|i3jWok.ݨ4Vvxٞ5x~;=$qʝi#· g~v{t?Z{:U5tBIs[[coBWuuCScPGz='UBWzvK_{/KUܹ*XZ'GoIcO{h?o{OکSsRK-$`|?RG:՝e{~v{t?O*ަŮ6Ҩ[#}ڋ[_oBWuuԪے>ƿ(Kg*{|>,u/Sn\o-I4(VIbwұ|շ'TQSU%GOԴC򎇵gY^?Q|uy3wk*ky^:vs}4F[JrCScPGhGEP/5ilu%N&V^H~Um)>QkW/VXwNsR[-$`|xb2&13 k+_|עx>[tMۆq.`V!QVNL~a:ܑ㬈Z3$5DkN1nV1To9{On_'NZҎCkw>zOzo?nTvSu}(j!0T6sZ5!?ӯSU7_Hex'W,?p=꼺:Еnf=rk-EIWԯgtjmKHSK=[S?Rյ/}T[s(Vzji?/zTVԾsVԪ$54/4TvzGOyj_aizhPɹrU:HNZM'_5BVԾ:KjfOZQRU+?ږzjmKVԇ; Sn}Y\?٪IFյ2?kjjVԪ$5/iwY=>Zm歩}?'Q4=[S4+rn\-z yi?&5mK [M[S3ϺZQRU+?ږz656j·>QU-i?QY꺑h' pZlӸIj2t?1t=&F^>ANԴeC[:P8YnPN&/{FMpt2iF.S$={PN֋9tCF_ދ=?瘥J}˳˦h?L_[ytI>md J^4Ǟ~j]7&/Ο˦-p>AS-|~AP]nQM&/{62? Tmexҍ(kVy!w&.rYZc_uїRT*q˗*˦OF_Z}>'>9R*T<ՔvL_2t?1t?YZ2|CW?gqYtݿOU y49bǽEk.߸:zU }|4F5N|3&A[QM;+] ш#eE+f_ ʪN|<}klfuÛ)׽csPӑҬl< Ya/u{]:\ykPֹk5ZO:^ĽnOҠOYV3q7J-mTJer*G+ֳeM7Jgc6Gi*^*S}ޡ#Yy3y7Joc67Cxt^nOy*'+ֹ{Ρc?nNtq/|ܳt}k2oV꼶3fB?xu)>SOMU:ulf|ҫ͑7ZJS}桧t6|[J )ZaW:t/|oż:[ͼVu )S/xʝ?K69* 4܎gc?3t ԧN]>/>1ҫdtY&W#jJOİ骋&psOX-'suċcrhz6l$Z <I K/G$/Ƽҍ߼]Zq䇾tw|}G e|~/VU~ߨIR'i*qאt} ?+қ`m4gM9R*T'lBԡB, MimzJ<i7T ?Sީ&i~4Fnӏ$=󤼆$y*;8lЬ-pi*Q%N?X^CZ?!V??swS}z Q3~Te 8I8,_Щ(ls`Ymj]KKo{:QcN?V~YgB$iU+=([}?xQN<Β ?!LϏ} ɼ=QLқ %J<r/y ۿGi ٟ}B)ݎz Q+F*q˖pYxSjP!XzSzR={:Q^i7!+K V3Gf,1b?vFMN>.'kgys)?WzOڶr}Nt/ce:òp?Wj:ϝ,OԴ~c(?Wzmu?T-9G?]ONBMO94GUGG\WxA_괵 5>ƿw-^ǓcI:>hbϯh? *ؤA_>{UӓRtl'GzVZڎsC:-50:S5+}VקZ,^Ǘb{u>s+}UA_>ӵ[}5>'OM-I瞆sw{>Y$DVL`\+ Z:>ZI|hPYi#_JJ4yYs>j՟Vզ?!UGJo8C=}fy̟Zg'd*7Y4':8FTYVxQ'OAPKs'_AV,ҫˤ:УGOacZY~NV`U4&GJeuGT{넺X#<|,dɇ~5>ϥxMbRX>]i_c瘝Џ?[zPRM(}x }ޅUHhtm4g}N^ARi= {M}sG-}u{KgASjZPUePЊ_\ǑꦚЏ?ZzPI4}J>Bʇ$4 }~g*e^iC=QQ# li)PӡF__Ǚ?f#OQyUٟ҇M6Z {IdY>G-}u{IgASjZPS<FT>"|T__^I>hwUeC yL׏#}U$}:e8R^Ǔb*XZt(^k=UzJn: P3>/ceRi= =}sG-}u{KgARZPSGhVzP=L,{57iu :5gi|4G]|~I:ஷ)'NҭP"oEk~?T-7o_e;tҥIrC*7?c_|\7kOPt߱/_Qgܗ>_T^=MZ{ I77$[}wM$wܧeIs0l.p?&?QH3Z6ZK|SwM%| jK*\| {9&tV\?Tm7_e;tu*KSPM~&i5%,7)O4I{u\h?Dݿշ}>h?/V]~'ܡv^ԗ< "oӵ >5e$?Qt:^*ƤҥP_"oGk~?T-7o_w~T$=®as5H?7kSPt߱/JSM%R^{$?7o5m.~'DwϮK|Ut߱IKMV^4K~ecs7k< L;929+t_滏 ^A{e#ۆ c 窰RtZqWM*nFq"`pVwZG[~0xBs9oPO\h s+PԼtVZ['LVӶGX9{:^^[[[kPԾοn[ͦ?JO T/tƝE%vwZnKu}z}nͦrtͦ}Z#/yQ{Jj_bo7OAU,޷_Jޖ};MU}i){4Q{h BRU{Ka~UsPNҬM?J:9{Ky~UkPԼuvijiB~:9{2Q{ {Uޥ[WRk~VvUNk~:{Jث-ޥ'>ktwzG[n>&ts%/uԨ}Ά]֧[6Z\'Ӽ}*ͼvrW:u?/p=UBRҮ[O?JM?|jܽS.R͜zUyn/7[nϧ`~:Uyf~ ^BEcu*Ku}|=WԲ?zߕn>t,r?|T}·cᇚMv,'\q9}7ȑ;N?]>6 <͵khsDem]rv?2F%tVٛIi猥wU>H{Kſ|KYFo5=/?yYz_;4JΆ->%?մ[?Ltwzմ?t 9JV^Tsk?FԖ?#ZzMjz_f5NR RV~f/j-_&_Gg}J*IC*^-^Gf}ټ?g|||Х.OTƼ[ĽgH5nSgR*U''Ŀѩ%DօfڛR|jƤ>o_$[DxS÷TS3}4FR V| #Q٥Fo5=/?yYz_;4*:׋/AF"%\} ZMOK3OӔe IIvo_ڒ"^kBS5.9K2IY %7Fճћ?XRzZk)i#~TKI?'cڳu~?|.{zᮇ ]5[SbI~T-?Gڣo{yh+]]U[/J[M'U BX~ilu% ܻIVԞIm)>Qk{cA|Z{cR~~hpe*ԖI(ږ<~QuiC>QU5tBIs[[coڡo{yh+]UU[>ƿ/J,T5 ck[Kc{/IB.VԞIʭ>'5=^VX_n%[zYi#GIt? {V}iڍC·:*ަ6Ҩ[#-zP4 zUV䆦ơޖI|jZ^~+r\on'Im)>Oiu5muIݡµsRK-'ʻ }D2y͕k<%-̺|8`Sqyl**|A7:lNX3 &[{DGtŸehNJm5~Ν=ͶqYGU?of[GqխCJ>Bpz6kPi-'zQ=j؟:{zޔUnV]G_GЏPH8=*վu nOtV?Zkru})V )μ sVu n_tTndeMg^])rZGO֫˭ӯ VccO.~^I9r]nM=꽞s$r{mADVmj-C[BOzog6[3-\j֡!8=jmny~kP|GNmVFTa!zKqUқW,:twmotBUTu}IO؟ҫiMW%~ޫw9ޒUYjo$g'>~ks×uIb0>mN*VSW6Ƴ٪IFZjڞ?Ij_i|k8µަU~Hjh^i?w-%歩}uQڟs[raku{ O3j歩}tVV'?NPe*zjmKHSK=[Rږ~5N2gnHf&/ijXRM_RSx (µޥUU!~HA2Ho5mK=e4+rni%_ ^i?*i [RwD?OZQRU+ږz'SjZ8V*՞=#GKjŞcRM[RK'?DaZRм?~qQ>ZK[RD?յ>8O撅nM$aku~^­&/yj_nեյ?? O(VԚJ{=#R?VVlj]KVԿuw S8VzhmHvp}S {5jM;:v&CCUe/ j:2%5:qYt&.ުikzFM=*k|Qbo.?L_xEk.߸:U }|4%wV䇾ojc_g&L_ЬCF_>z2q (FnĚwշN1}QX o'ؤҍ*T?|e&OdӼb,e;Rї· W\a|޵N1tPN&/{62Pїoӏ$={PNilӇ}VV'})ldऩ._P]N襁c&/ѓAAVFOIMV^4O-YK`Ȯ*{WhɁʻ ZK P n?te~k%E+lk.]zkӢQ]2nDRs!\|pcX4МfOct+,nf}jK^N跜ZZ~f֡e7vT{2M {ݞN^]>?#fYS}fN4Jot}*{ulgҫjU/uƓ CON*վ8oPOU{):nӧ{m>=ǑVu >/!9^[O՝B}j/y{T^gEUӢkrk6n>vU岟7Z#K~Nc:YtܯJg#fKe?؟n^sM֒xҥ?Gmt~ztVEszz|}jյߝӧ{m>-VunmoέjS 7_ZK^:mUk:e3tۭ ԧN]>??#Wk6K3tvS|TTOѢXtEX4HK3~2ݭ? hrw7_`D7/)F=2ŭl ?*AgxVz[z[}SQN<Ύ ?!LϏ} ɼ=euG.T5o wm 3/W7y7ۈ砫I?r/xTO.g=MAgG`Ym6ztF~~ ?*AgxVzQi_4J7~uiǒ^Ag*;8,_Ь-ߨYMZJy>"N?X^CV ?!V ??sw[}*imgM9R*T',BԠUg--s׽S4gpYxT ??Sқ$/#J7~Uiǒ^Ag*;8,Ь-pi*Q%N?X^CZ ?Eiٟ}B)zi43F*q˶pYxSjPY*=sSjZ[~ީҏ:N?V~ie#Faiٯ|,?GKt?#*ѳSGMO: m%GcǦ}YU?Tm9G?] PS(7zTt9![P>ƿ#zM~}V'[=58I*<'Cۭ YAq+}UؤA_qoؤv-I瞆.p?W:ϝ,iڎt?wj:2wV_#.|@xt6jmBMO54UIP䆅mC?c_ԶZZc^z=98BTy^T=Ɵ_}UؤA_}5?շӓR}߻CTl)P瞆q?vyBMLOԴ5GhePЫk ?T-5G?]OT-4EQЪ$4*z{Kg?4Զzr{RJ&ƒt=ş_}U~'ޫ*)>ݦ- C _A_껯 ޽I9{-50>v WU/>*ww+:/jTm)e( }kl?s'+?hd!g0ZV/gϸj՟6͞?!Ut6H~QRGBYs'*֡٘2uZI*Ρ!>Q֩Ə21=Y̟%l߹z@ҫˤ:УG1-Ks'OAU.?s'*̚H|WGcGT>k6~z~NkV{!ZIzp:U}$l< h=}jy̟W՗N[#yEY4'SdeO{ gq:tUyu?dVlVIl|(MF3Oac[ZNZY̝}ՙt&GJg Z<ҧ CY2tjYq'*=2YFG?aP֬ k6~B~N#s|Hjh#*~H,>?s'OAU֬߹zH%G[hQS{lz͞Gd*ĺHWFG%<mCЮ#X#w|,^=1k{B[oڋ,AwvCN/Ҿ#}U%/~ZzPI4}D}ށVT9!^k=Gzeί^iC'TvzHI{M}aiУyۿkgB?t҇ۺ>ih*R'gu6~%=MiC<O *Vz?RM}>GgU4҇_( */5yGgGךHAS,<)PӡF_OǙ;ե~E>tm4g}N^ARi= {=}3G-}u{,SjZPS<FT>":}Џ=^I&>hЪ _OǙQ>u8R^ǓcIJ 7}h?/oObr^ԗ< "oנxF Y2 Ykn/]o W1g](M-8ث㆕<9#B\H#\5ޤ@>k~BI9gVm;ڟs؅˿ԼtfRzߐizfdVmӶ%7/u{E:9kOy~gPԼurm;yufm;T/tʝOK ;KUyn/%nlӸ}*mo'ZjKR1:%Ծ߽nYneN:U{9?|i).WT}Ά^wy*սޥ!V =?|*մvr\ usvzsZ./!?|}[M|gPNr\2Q{ {]ޥ[_mo7_A[siWm;De?ts}ΥYn/7[WzneN~>^m;G֒T^C/PtfS|ߐwiz~:Uytt/o/p- ]j~B~ m6*֡6'S*ueYXZ=A-ޥug>UMk>){/csR[KM*֧+vY?UӸ}hR_\/mp|6I\_qbrw#'8t3Dz2d+Ʒ6bnq=7ˤN*Z6Y?_$[LxSIi2߸UZ䇸T[ĽGoI5yia Gg|||R'\O c^-ۏDgI5nSA5i5=/+/pTKO95SK{zMjZ_sjν(ԟ՟g٭}I5Yz^8s|5Դ~QҪ՗$=>%?,[7zzYzW;4JΆ->%?մ]C/?j楥;A5m5=/+/pU%'̿ѩ%DօgSjZ)sq/?pϳ[5IV//?k橦sJ*IrC*^-^Gf}ټ?e\||R'i*:׋#Vo2Fjz_۾?vheJÜ/j}Ios/5~Sҳ-OK{ S׸e 572-/ݢp%׸RG ږ<~Q;c/Nԯu:/C5(ƿ'J,UBX~-/u?|%ܬJZϤ};UGؤGݬiu5muIݡƷ*%[ORK-$`|;RG:ϲ0?|?Q<|8FQU5tFIsZk/O{co/?Dc[QV䆦:R#5J5Yon'Ii)>_kcA|[{cR~~iBsRK-$`|ZHGCڳu/Oԯ:/C5N5UoM{]$mPG(ǵ-rP<4(eUU!i#k򎴶zHP/uim/u%ܬJZi#A_ҭ>'>cO{h?o{}O/5Q4oi=I,ʻO[}D2ykSXJnĘ*AT/3Ɛ>u?qZQ]{mC"~kw?**|1X\z|VKl9y=նqVnTtu}(<ʭjQ֡7tj֡:5[u*.~v[GN^]n}VcI7VҎG][Ozeq}%[Tu}jQӃҬM*P<GNf[҆ryKo<i-'Eoo?Vu nOtTndeMe7U[W,?ӧW[k_z?oaM.b~Jg85v]nM*qi%[T>oXX!Zp.:{ի}naGOֆryI~V:|OZ\:~kP<GNMVFTa";-)OJ.ma֮YwtU>քs0˥dZJ9v]n-=꽖q}%[TuÐ})#?&|wk;|mm~宴)F`l[G⤤Yng泶URM#%_5bVԽI_j~᮸µޥUU!~IA?/qL:| Gg\p% ܛ%_ ^i*i  ZM[R3t 9BIW?٩-#OU,}K<jmKVԿuw EWԷgTIIXյ,rk_i~VԪ$5/i??w-%歩}uL.8O撅nM$ak[#;i4g [RwD?}OZQRU+Yږz56j_; Sn}̢YZ?٪IFڞ:'TWԾ'?J0wUU~Hj_?OTvzOOWԾ:Y'BnM$ak[#;m4g}歩}tVVGO(VTi=JzG56}jgMjڗNT[s(Vz-LEc8V穅UԵg&L_;RNI*ȲѓS-<~AҥGNY4O o&L_xCk.BF_9A(ƕiǒI}bKg&?1Uh5Z,d*^#׾^N:ib&/4e~Umd /u{¥NJ4~ӏ$=wPNKi&?1~b ~ƿ Զ2q /uƒnĚw}UN1}Q\2h?;vo'ؤW*T'Ӱ?bڌqe.?*~':K^i7mdӶ]=jw1}ޢѓiJo/ ƅKWRy!ڄo&/)m$ӿ/βu >ƿ Զz2qʒxT׾]N:ib&/\2h?;UѓR|St 8I)4:F_+J;ї^ZXȀc2g.,oq&Ԯ/YtfG \Şt7스OZloΧjO[zҭ[l< )ۧYa]/u{o/|oż*Zp:BesT{2M {ݞwA.[Zͳ}Uilk֜i>gtsͧ&ҫr}+2[)3t,n*~J4zzU}:0oPL3tVS?3~t'ʽO˖t["y \ݽߝZ,nN^:oK69*|_mֳlV^R14|_boҫYrk6[)3tS|ҍ/uΛzfNakOUk)t:o.[ioZH->im9*imgM/xTO.Yi=MAiG`Ym56ztF~~ LEa:QzZqk_#j:ǝ,KMO:sȧ*VzutP<Axt6rlBMO94˩*Яkc_Y|GZZc_֖MN>${u?.?UmAq)?Oqڶj}Ohj*7COCj> @VvCz#8ʇޅ[]~oxު6 [}U驷zU }5< 4nʨCB QgψKPSk~zjqi%G'Cۭ iAq#o}OSivcʅIò.<GCU~MLOԴ STy] ֺ^ު }U驰ޕBMO94%F쪒 چq5AGR}VMOUV\n1?v* #z\ivbʅM瞆q?<7zRI$""+S[@-F9l|bt'ʵ*xtYP@'gYq?]oaF˯JH|St1Y\]CY2toYl?U $yVmqʍo"eOY5/!?s'_A5ZI?*!2SdeO{ e2tUl߹EYF:Ui4GZh0=}KskVfs'OA5^Zԝ}Չt6GJg֒ViS{hedV=0|i Ο(VmΟ(TGAg`~NU֬et#/Zh1>u/7d* =jxG*3GZJ4yYOa.:z6͖ÈdEV4ն6SqʇO{y͖?V f@EV>s!>Q׽SdcO{ gq*͟[2ubI-WIMuG1-K}s'OgYd~OXIBt,|hJGtKt%*#o|RX1ksEznjrMc\ewkΣ1zϑkB?x{M(U$҇ڟw]EYP䆁yZq:yh*;=$qKli)PӡJ_OǙ;մ~Iy[M(}QM6ZJT=2,yΦԵޤG-(~꟱2g#4/~ZzPp*iC/hк ?zήi#=QLG IPعJ 7}iYh*NE#ږC=ԖzH6;?cϱeCB;M}1GI_޵ PK>cwu]Hh-澟b3w uzJa:J|)PӡJ_OǙ;ե~E>ti4g}N^ARt=2l3ԺC=ԶzH.SFT>#א2?*՞1 +zWC٭v?iu:5M1-ކI!]'-2f8jq7[~5F +9'z<:?0v~V/HLx'=ORXWzR"/P,nH5.-rP֔iYLGΒعV~KCj7j}Ov =I nj͌~,zlWK ?1MYh*ud&pVW\O}vcuC?Cf} OS6D(cŽr.6q/b&keu>'T-쮼H5ªŠ:9bc .ޅN"/PM~&wWZ)P{oIbrS&OeuH5mn'Lw)t{s9n#<]+-i)ɸyeeuD٧67^t?t?kZ,n1?KwFCsU7ͱjظjqW[OD?jל}f\V@t1?BRv*Y8(^cu5H-fE)s!~KKbYe.M>7j}O3] =H x>*vn2F(xVZ:Sp<]! VWg&L=6Ahi']տ!^ٽF%kUakӅ'nc]7.?=?zwUkOa~B+,Ȁ=EOȄkwʴF![{[gPuٕhQSh2K $QTTy%ާ[m޷_A]rOJnci=r(X?z}v2hNmYz7 Ty^N_!Zn{[^h2 EOʤGh V8{kOy~Bjzn8<;*14%UQz"U:|+]~z~VzzߐK2 QSá ?ZOGh V8kOy~Bzn8<;2-Oy*AOGhQ*2\YxZ=WSk~Wem} ~St`QBz TEr~=-ާ&t^S~B 4'6́F=EEkPɏRX6z"U:|i4k,&~ Dqꬠ5kZ[/RR{ۋqW8k512F"i~#^c?q9»65* Cc[̽Ge&_qqm1 mORX|r/lɥuz5iP3O's= tB!ddSxvBZJsnf5565KvVxG.fOSx\3I*<}/j.I5h?c q3>Nzci]R 69+>2j;58I5Omc}OSm?IciعVRPw|Hٟ&_Y?K04xƈD,LoNTINms]C~eK-eF(<:h3%ހlc}I oK38ԟqPTo2F ~\JϺ<(XW{R 69ſ|KY2Fy#% mcq'),m.Vi{e.Moe?ե[?Lt>bOTˢ &S$bd*u6Y}R[Kv0xug*KٴOK= R^ǓS[3q/h ~VSSoDz|J4MWu+}XYyJo|gj`qr^ZΫq()2ʀCr(|[}qgI5D۴L. >Je[f X^\ZhaU%pp0gxg[9FZ[~:Vʘ|g%ukN[5X#̖, ۖ*BIUtHu& =tբjZ>}ug_jHc @6,ɢnۥHXNQsM-+Ɨ6qV6kq «*HEV@.DI[g'Oʴ[˝vY6[x!f A' P-PEPEPEPEPEPEPEPEPIKEa$~' goi6RKdVݍֱDFUy4׊5(`h;8Sd`T7j͡i""iF_:8Ak}sbriH+A%[,+0tޣ9zW>5:AV#\9it4J4-WRmlm#8 ܒ=qWI\#֯^EPIKEQEQEQEQEQEQEQEQE%ssxpkp22*wqںJ,!t$ӕO͕"xnp}ԋw,[WRL&;+*g}2 tH) 5 r0EImZ=J9;E2eÍu@*^[7 (xLO]j%Qe YD|rHԾ 2mM5[p.)kekq!a,ghOIq&q#Ȟc!4yI+v4:}˼-3ܼ0HA޸ ( ( ( ( ( ( ( ( (o +P#,=E~b!54U`>(((h(((((((((M9ϭ7Lpy#h #JZ(h['-PEPEPEPEPEPEPEPEPEPEPQEP@=Fh ((((((((((fotoxx-20.08/images/sharpen.jpg000066400000000000000000000657421362435004500165010ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 362 321 0 C     C   E!" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? { x|Wjk/(Y_VGڽW_gGϢ">Gڽk>|>}z>EY_QȬ{z,Ϣ"EEd}ޛ%` `4emaʒ%2DjJ]P#Kiη܈\dKc9[^C~= Gx#S\m"Y d|O\yo.<+m,YG$vilSBRe"Uu#b@!pE[.bTO\oEEk>|[~> _kaYݸq5Woan$j]мG\k2|O_?~=MR(Ͷ$I$3FFFЁ<[oi]>X<[xi|A$M:?ݱ18 s,Wi>ߩk>|>}$D6Ǩ$MW1k/(Y_VGڽWȣgYj^X EEIiJX}pq*=I?+^~ Bvֳ]Mv<yvF;"Xl&X1A>Ai##2#m(}& [S~mo-coV ˆ<p_Mωux%0jۈ`(- JY5 ;Y-ΡZ_v*NqT^Gv(K0wZN׵.nx>3c ֹXx[)ee .҃X+ԍ Rك.M/ x^EFKBGBwʹF2[EunGDW|TbP:i_P$ n<|Kw,r^\*p+fUSm̮}fkaz3rvյ>~}oQ^E}%F`8g;NJ?2QgAe?Z>}k3)1G;Sb0G~ù_?o(s32Qf?<}hAw?? -O[,h>gAe)1E\AֿC? / ù_?o( h>gw?? -O[,~j6{v.,l38~b nr~nI<,pY<@! X&eǷgAe)1E^>dڎ+WM隞wG ;p~i_:?-+^nt]?HUybBxa@A''#w?? -O[\?P,^<мx^6燮a$)ۺI8l8 uwe{ xcKөin+Οiopr~v}g;Sbw?? -as'ִ]hИyۋ*g ؞q_|ù_?o(s32Qf> |au5_>[,U ^M:YY)uoNjz1$"o.@YOQ'MErڮ]Kԯ?=Mq8fy|) pXgֺ;+,sF.z@QEQ@5' ^]ygM+QJ!h\E29((QEQE5F8PI+{E;Ox~y."H"96X(׮txA&k+_iwi r p+/o~!oz>i֖znQc!6$=zZ]yN xúƗx5":^3־|H/uxcN]6ŖzK֖9M2[ؼw֔WxL 0h^]YLՇ&9 ˹;V.4Wl0$?FR׎|Mտ?++C5槢>[Nӵ{Lqjaamp,pJ ,9ҎYƹo֓%,w Fp6'Z5%W?:ސEbϖy|SѠ/o6<+XZSf@4s@8*#'gk~/^$M7\մخRèS`RKy`|_(wh1WQEϖU.Q.<Qv%Tp:vL5?_&QH/G :k wWԣ5"i.YFR>|Ef{?uZ}/Ÿk _i }te1EX1+^oۉ-Gx^JM;VMB5K$L*>uL7M+xvz玿jItQo`6K{/xaqT ca״!翖_[Ka+1dt| .U0i$YblnoZ|?mӎ,i\.8$P??Rx]W}.x|Ixsڴ E#1\UM[ZmŒ__>+t{]BXh2 `q9а}+ZmbzEʼn'$w99[o ϧX6`7wlmʸfE4=/׹O> |U&4;oO.E N 2p?nF0NPMOJ`]KlqJfy#CRvj}uesi:lfNاYxOCMѴSb TO9q3ދk|iҼ7ZBXYj[C$K(=#kzg/%yitYeJ y%oZoh|[/? 6,uFԬ=- ZYg;#)!~Q͠x⇃tK/xYHivnDe-yg(=~8c7Y!}?ZE5Q.\U,uv/ bkúNu2{wʠw^|<>m0ѭ#1E(b1lrrIz/ڶQEH<⥯Hm]֋5>V8ZVy/W]&ZeѼAGkG>Dǹ⭕8zt_Ϝ4{Ŷ tsxwڤ7V~Kx]noEQIV8񞛫x-Ŧ ^KmOb>ؘmRDaO&)l=¢?*G[ԵxVV6ko(o\E#d, ?tP})skx t`oNm_ŎEP#.''> >LxGIm8DF#9\*Wo\U_ sUKE`zEy*Wo\U_ sTX5|Gx⮹{b{+Zeqk oeq+[ k?Hz!O ?͇|-'!Եl[eAˡYW<` *-bsV.uKc[Ӣvi"1v3ާU9Z,׼3[Rysc\29؍# #IZk~2?';izhŠ$bY[ɒ@'#OV{R6᫟ xV]byP8y$xV g-9ٶ_ޛ}KX}a1,nb`n^j?~뺿efc~J_`9Ds⟇ u}'EПXyK ʐ[$MrPwÜ c]3V.-X|?=W =D_"l8xChwh": 3yIl}z|<_Kt6 SVVhx́3@0 cd~_Eh/]|${W+Ϻ_o)}\4B(*Ư~|]Wo Nx̌7w6q[ JqiO4 VzMSN2džWUFSIGÏ74-#߂eխM&C}=! q^pw#V|@|w|@#Fcf*mwJ|Y*hum6Ȇۅ"bvQO''[?gſ ⏍cHK0"D2v-#(pp5tq2~2+WyeOWI]*@((((((((()7oΆPU*F=ψ=/IAjz-G#0 RN288vl vtOGÞ Ҵ  ub( ю$Ee 'srxI|~ώ4ϥhڏu&ibрUyfo[ ;?Dϩh|]oMc' XOjgQ +rD.IbIcqP>#_ է{OuSx^S^7bx+Aq6:7<(9|+6ďfZ{퉖Ib)a͞\~Ѿ4yq4 4y' (W>)k/-n i+\%ӼE/0%߇/RF$r# [h`kd-c N%b^_67O1^gOm},gᯁ~CJ{_E UFZ0s2I5u5J^'gShuo&K͕*RkiUA*y=sHG۳ߧ ?Ÿ zOEWp;4hiM1?\f}m^&MDlhAoV7%F9#*?P)j(+񵽦qeo꺴,ٶ/: p8#ր:*+vѴzlmnK#GVckE4:8 :z}Q@UYu[(5l$;Ѥ٥Q,ʹ#$tZ麥hVp_[32Y$0 }ZF`YU$Gms <2 $lXz:QEQEQEQEWΟ#t}úf+W2YYIpѕuy 858?)Ӽ;>qj:sjvn5B$zhpc^ǥ\ZW:~qzte*q)P> W+ޟ{sxF0.}WO (fyH{㨾 hcPKմQ,ݘbQd+ڝ}^j8.neżRϫ_( Tһz??/^o+15!^i 譚;(D-3Vؑ{}Sž6}zN5DR(ˉ䔫mHNF5{|kO+k ףJ-?m("dtic fIQ]IrpM4MhX_Ii![y|acmV}O^^k)4#.EW;zAg]pF;]NdQk2TV4uH{쫮Gqj5'|-BxR=$043ѻ#'P׏>O&,h'."Hca 3H$g-VA-zK:\@$R8eކǏ>"?Zj~4m5]ֱJʗ.ZP1G 9_x7O|JoG鶺/,x&C#EoRYerJ~w:]īc&v\~^9.,6[j*^/?? ^Atn[]ͤ.ؤe!ݜiw yK>}h/ O헺džH:Em,U[9X- a溫_?xJf]2+I㐽ƪ#o䞵:oM:/$zuVܚr^,{3!s]GupM +lERCq3^ @4 >t:K'[wi#w;rCA)BDwdV#dÚ*-/4;G;/#hWqdf?/Zh>?OKo#닦>4mò@e|/3|!ója*Gayqol nF2Dž mj袊C ( ( (>V?f|APE.Ei!Y9beݏ#g߈Z7?ϋth ڡI2>bI(|qb_(ikZXM_T|" Ee c&Bzw_'şxo,m>+w⧁$j3,6ꌁF䜑g5yG>k~"Vd'muYNqlP ) p_|Q⏌+'~&Ǣh]:=Osq3ѠH조MuC ' )-{4bc;,&Î3 H5?5 G?xLm,fo1de1vd\O^{wgˍ-)Կ|qqXgvlеOK~qvV<_x =u-KH-eK'sp"Xq Sniwe{-g$dKyMʞ+??BӴY/tW[Қ C:IlJU`$T>! |'m瀦K4-oP$I3bi-E%hW c?~YcAo Bډ8XGrwH'8xj? ?+cZ m #1#9U| ƞcX߆/4WQgMB [K3d87P|Ysx{k.%Ƅfy$ݱ\aC x;s=6ȝwG\NǾVO-"_,t $șR pz|A>"(tM{ž&cna-"qm2KfRqž34#_E3zׅ6^- 3ĬIc =kԼ Xu6Xjz־Td)7B#4~w^VIu[}5ΩE l-AB gךMJ-uy >xwX\k1K.`a=ܳ)]A85[O\z}B[?WYQ_o>|3c૟j:]Sk= fc׎( |iui /Wk:^.ai6;dڀߜrpE} gT|7c}~LU7@A#HY98##=q ky&|#-Pލ )f3'K]x1Y|Gk&iZ6c}^Ao/mXi!`>ll7xSZy~?G3g]'|]O:^>> IG=| z]#o?gcdznL--޾5`eḺT+O+,KqKT6j}u'ok~-#K-$ |H:]2y&@+ Zm ׇ;Z–k,ͮU /s#m'<ᏅX ;RMbty%Ԃ#NO6J}DݿW,~,x7֓xC^6)ui2m#^sG{pg߆\$!ra_ O㏏5𭧉֜}bnѣ-~]H%sm<M'1\ѭivm/y:@#mWD]wZJW>ڮg?xZPO?ٖoodY|$.nFk ^G߉<о_EZHaAIAxi> {ImrdQ)_&T^w+lk+YK4Ӧ_=6ja|I?3GHʵI BRuz#}LJ]nT `^]KV3eӆd 'cOúcQ}+iu+u:8Ѓf+_⟇=+^'%fW&NMmI).ŷg__jRBjR/4k]Sֺiwp'c"(G>ʧgᎥ7W Z-5~I0y^sSOX_V.}w;J֩ڂ۬lmpTBOQEs_xEiz0E}KTYLYvez⣭ׯZYM· / ho/_GEPsV_\+6]/LV]:C H*c;@S^\?zl5)0(ctO_{|Q]~We{5!Ye$gWy\f<-?4>`UR0VMvB #ikYiCoY‚4h*: T^f<-?4yƂ7dfYIzS2mo3SmmeW`_CRcgG(ҩj7@>mncsƤHcDT.w1Q?3Sl̎Yո O_֏3Sٮٛ‹<,P)b(+KgZuTӴM>mK]=H4&(Ċ'=(o.~xmF0(lH9nb32klm\HU9 O V#xë @X2G?v7{g5kTإYir"1s=&JƩe{-,$98k/SmZ\PM?LLX;st4V6AW4yn2Cu.sn}87!Ҵ/z<7 XdFONhET5[-n;:^RU72 VMh]vKqrʹB#'cRo |oR8<`I /ؖ8L`m\߄v,o&<;,ϫEufcBAJŋp}ga'}Otxoxj ZmCiYw $N}7gֺ:(C ( ( +gU^ {Z|@ITkuDM%E z9~,|MxgÚ>wbh76c-l{~*&cho_8}3Vajz;\}l @HQ2T'<2r\Asf ټ#ێ땉/EnM92L#4o#FEH̊D \RFXף_C? |!7I1yeߜt~r VE;ȶ tv}S+hsmg> k;YW(u2EN(H8&@hz i._ wXwZivRIiK\gk7 c6r)\*G(NH9h>u-?Wk7<}>;yCJq-> ixTOؾm7]Y#ԭXR*Wp=}EUoي to~3Z$F8Gv \yVCU]WK_ ݣd[VY`|/K)}qECޖ)i-5x~0+}B9Vhlƞ㑎z5oN5*+-|U Ҵ<7uoʨ{E~dSNSL啯|CjEơkG1yzg j՟& ?3TVkX_Z-p)7Y?MYRo4מYM C*Dah;կSV,gvgK4MyQXb~B*rzT5@QEQEQEQEQEQEQEQEQEQEQE[㯉^Yk:W&4zhߴvYv&1}E^W)Gu%淦x#iߝ7ȃmF{敯A3tWς~V^ZTjI5ԉ ($?g_xKὭ>& 7181VuY#Zó'ZR4[ik_zn!wTL YibC.$N궲k?o%ٷ 0yox&߄<=c{_X `PT#k޺GCQhQm?sZˇ^uK-" (Y1%īc\9$sV]xFX8cۘAnIʮF2+ ~BiZΥM5c7 ' /Ž UdkU{IuoX}O; fU,$*3=:QhQm?s MI陣)'__|j~_.0>4egtB @"cwdo"fQ0;?`k^3xkH[Kl]1idHxб̋w{5܀,s$y'v)lY6;c\EGXbpiQ@awυ>0x5+ω=f֫iqrw1 #) ^x~1m}2ֹ́~>EeٍNA< FWds856;/xK~t ΏbG(WwGn/<uGȠ /xK~t־kbc HX'xGS&뷿~T˱ @FnOYqE 'Ihv((((( axCnK|Y=ߩ^OizK?k:jk$rdt`+*1}']>;K2+FG9#Ghmzvn/ hm{*99 GtUo|Fzv^_}ƍ VR[im;yF2˃mavft1y{E#}zNUea$ȭa_BzmAq+ amGXL(1GҒR/&Df$rt JwA[ ^^.x5}#MW^N8f#q փ~ K k~f}gidu*F\PX5}{p/|5@DW:$ >d"Tg2OPNE24@!;_tOU=+bivkmr ~^#1^GJP>>У0A-*$c0Į I"sOӭc볍.:۝Cu$Q EŒR~_vKHXG~7~#"e5^H. `@q+mY"+|+6ڪ)(l%~V1]tM#SkrâQ}WWZ!<*+O 1+ɖA]i4ZS+>??淶񆷩BUt$pIHB"UP^I’$Io5\J"aH0p•%P"v>o< O~ |7 (e8[WVbg@4mJ|{d/[%Ԇ_)s Vcf:rOZ^[Y|/M/ F'vkΩe`$1^jHiom>'akioΖ4''taolaRdXVo3y.0+8?φ|kyS1*[p$I5Xj u+sZnK&vy. U8zPַ^O {|Enh\}lh47q=t ?<P4o ao=bFY&?3u*2J9'߈_h1zŸ쫩lc>_i+s[躴66|[}Xџk8 }Qs}pEE\\:͍pG(cđr7( +jLW3[jnZevj;}ކCoEQE((((((((((((((((((fotoxx-20.08/images/shift_colors.jpg000066400000000000000000000326671362435004500175370ustar00rootroot00000000000000JFIFExifMM*V^(1fixgnome-screenshot0230+0100Fotoxx:resize|tonemap| Fotoxx:paint_clone| 4http://ns.adobe.com/xap/1.0/ 158 302 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?9^UkyPaNhq+;Y hkҴ1 bS4;?+o#W>&-a⾫K̽cǘ3W,pe RkH~|ösĵOlA3\"='Q?t߾ѻJ[O<<7Wl#_&t9_d/֚=ĸŭZC&1xriJ=KSQMbN?rWspOkpV] ge3y;?^tV döNsϴV?$?Ey+"Ě}M5\cO+{wѼ?3IxRZ?ŧaorf O=/] j?K⫷Kt.\׌uK 𶧨x{j@&RV\`75I4 oײuI|:>kOJ⧎.U:ܰP: oogï|KON/ m- w?$?E14Lgx^;ol~#^ݦjV[oWg<]/CcK{)dO9Qj}-⫯AM'KySZ&?<)*hs'_ z>aƏe[뮉LODWsM/?!1izP b?OHg@M?!+/0/:ŗ7!K7kgwO'N⩼Qx(׬Rt}=A@}?!(?$?EV_t} K?:'D>[@}7b(?$?EV_t}OH?tO Ukn,`/"oUoG۠ ?:'D>[@}7b(?,_EV_t}wV"P[E>TX@䎀Ecx\藢ne麒Gi]!x+Bl*Ϻr9h.i׮qgOe{ۣ?خOO⏲G sx#>:}[?s4V K>/0n=wZ$)<)|Q`bx^'M%QGҒt4$M}z!?y '}CQtDt|7{{˛tD<3"C6?ʱkڴZ揮j={ϲ\jM%(7ߣoּ9Oް|yKiRT.m&Ύ[TTK.|8mhbD[>‰l&}>߾(| }GPŒMgss4t)2JfGytIxqڝ<ϳ*>C)YSPԦY<1'cOv'|j²\MYNe_fwޓ"l=WߣoԔs_ noRSfO~ϟ}y|Iu}Bng\\PfO|'QuFo,?| K?)Vտ}}_MQ\_P+,27Rf_Uo_WTR/A_cm[7RRڕ? OG_'\xWz٭&}oAyo ]HQ>|_JKskq}yxVIoC-mdo;D)rG Jꬽ##o ?뵢NC{}At½h+/ ?뵢8W1_uiV2m|?zEq_-./ x)FHR?+RY5/jW}Z'?.ò|w>}?Ҁ>MweeO}7\3Bў->!&M蟹tt}T5kwW:.EIo Xϴ!v|>}Q7}w)nʛ ;*b9}S/ƿ'?I/C`W<6Ǚ|CG(uKf&zB?YzcOzCTjζysȟ߯MnW4}^3tOzF;8#Ӵ8 #<һbM[@e⯊myxwZ;l|>]&r sb=O|Wo>Úm!ؐ:gN~&7 )vkUbT&x]>tGD $/%׼dnߢ]⧍iW'5hfx|폿뜱y:EK.fӓ{M6z,PkzƟhdZk.t~K{ñ~ž84su-|Kkϸ-vlDu~_Wz۫H^gKtG:RX|*~ti(Mv"C7ot_9xO{+{{xoG!ϳo|r_u]z/h{"X\Cl|IٱM eZZxb[jeq 4УDMܮ>< eY=yΏq4? ެj~u}?Je.᷽u't_@V.SXQcOh/J53G4_% <_w}w@ɏᠾ2(O_'j?f((_]<ch/J53G4_% <>W~pς|k?+ȴj+FM>ֽ6ZCɥ^i<Ϟx]KҪTU֯Q6[$1yPb~iC<<6}Q ?i7EvUq_6}Q ?i7EvP ?i7E6}WkEqQ "*JWNd |s?³xGԧ|Bn6Mwc>XuNJ5o jf[SLRٟf׫_ <](mus3/~ϟʏ|oWEZv<(!G?p"FߌOKGLK\lO_xoDimfŶ֍y;.ViNϴi [@_ wڕ}{k:y&Ćf鲙}Ox^G"5-ڜi3>D'ܤF>yx+cIյ+|[:yɿOY U-K32mkimps̎ނou+}gJ-ϵ&SS- %y?O>8O>8|.GKCY|]Iٽ:}Ѿ?>o ,$RmоAr5m?h QM5S[M +gG]Nޟ>Jź>}kt.Yo 7z>[w aٽa>;+Wy~&BW%k2;o}dZWxm]kP)^-}7/^j |{24ɽ޹j_tvP߽8k߅տ׿ ?1;K|??Fz?K|??Fzt_)^,ƭhYA[ G_ [ 7 [ 7ɍs/?>cVwQ4^|,ƭk-+'t9JexDŽ4}?BkKb}M]Mb ̐;RV^MWv]['pQ 궲MhO~m>"Wm.~_|,?疳;3Q?玳;3[ZW-??Zff',}x?#4?玳;3[ZW-??Z D:?O@O-+c?5;Ql 2 ~Qa_ *h"$H(5x\KZCuj~ Ϳd(;⌶>#<-qk朗w^^NL7U?­nz%|7}%koD߱yк?fscZӼ6t'ly #𖭭%QfR\}{ͳdr}?G5 tn;M/j$xmﭷodn#wMnGMM>ܪV?uտ5 v)ao{eᄶDGG3߾PWīƺcφm3KwaIM t]㢦< t[~*Ƨ͗y>JM{EDucH"&zGx%s_m|A;4 æ}=!Mr&3W/|w;W΅wxKO]?M~#Iǚ VZ֗ _?cSc}w t}oKKDӬum3Gy}mՐzQPXW/?|K$o ݣlOuQs6J99~|~IOG_1 x_a/q W/1_&=#%>OGG?:Ko x_bLUGJ};Ώ$t~u08ۨu>` ?SCXmнOs "&^V4;Ծl^E87Zk?U-.Z oM;4ZwѢKK>:~G؏8V0a>cy N|t'G)ώ;!GܮKBt(O.;!GܮKBt(O.;!LW |?i斉_iQhUN74>|Gv)WO㭪/_m5lKhv#(Yca7Fmoufx^mZcXӼD_nѷٲgO~A3Gӧ-|%G%HcMM"; ͳg3Ht=[ [e :G.~!uz(~!uz+nn^cE*卤w ?}?J+/)Vf[}}-Q+/)@<+/)GجZjجbKok˪bKo}}-P..}-Q+/)@誟bKo}}-P*جbKon+/)GجZtI]7Jɧ[ZXQYϥJ>e>(חGUm?%UдLΉ@]]sx?IAm@G\$a u=?憀7Z?&? x?::+4M hKY GԼtlni?=/ED4F?X?+B9"Ƒ^0 ZfaEPEPEPEPEPEPEPEPEP֥~֯}&Gĭh>yٿV;ڒ""owSGw/^>:.KWUB}r_ Z}Σ ?`t3o3Ym!:$cRC;>䧓6U2 }@TGF뾾,} ~V{P߀̚}Mmw&}͕UZŐV d}f|2i{YC fٚcr؟+?Fa\лss2<ɱ%}iQ-Ó],K罵;|o"F-W wnืy{9e/Gx%]+{kֱ:ޣ k ܊ dO Q>S@5d>_*`>(+X>)W?`M_S@5d>_*`>(+X>)W?`M_S@5d>_*`>(+X>)W?`M_S@5d>_*`>(+X>)W?`M_S@5d>_*`>O뗃V/cqs iyg`M__X>)W9cG' ??XO,ɫ|U|SC&Ts Iǧ'ySмC4ܒ޾g55T}2Nk._ڥѿww=o^!GcNw+X>)W?`M_T}1 AIO=| ?2j/G,ɫ|UXWoG'' ޾j5d>_*byQ'+sÚgPO%ExT0׮o[f\F&[(fotoxx-20.08/images/show-RGB.jpg000066400000000000000000000740701362435004500164230ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0230Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 421 401 0 C     C   QA" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?GL]";{o" N SV?5K紟ѣSi?QQ@~?4}'j*(_O=j{I}Si?GڧF%TO紟Ѩ ~?4}'j*(_O=j{I}Odм?;3!9ʉ)خ¥UL*?j{I}>?5wtOE:vu-Vg'lfS/x9;)4dmԳȠ]o; }_ծ3'hTO~;l|![/vZ_sYC4Nωn_3|-i2gUGTy*?ŝå6%gڧFO={ ~~w-@o/#r>1_*k ܄u$t 䲮ч.KzCmor?/WSi?GڧF9T֛my{l*1IZgm R|Cm{_m`Pٍ$2 2$I*vIVO7}'hTO׳~? ?5MúuΙa8'%3 -9ɞR/ڧFO=EEP~?4}'j*(_O=j{I}Si?GڧF%TO紟ѯL~Ru-Wđy* yS'{ך^x7od݌gB}'iVY =cP_6$Vƣlǻ͕+g@^<1_N2攝>$g֜9!yJײ|ZYT. ɚ'*־t6\ k(E߶O_1Y$yefJ2Id 5xֿMjpA=J+>\آwhF8ƏwƏi3i\+wƏi3hwhF8ƋE;G44û4#M_E_o ??/|E}/@7qgp>hwhF8ƏwƏi3h4Q_Kû4#M_G;G44\+u=&Ğ:O˥Or&;ҽo ??/񦝁C~:U|/ xc:דK]U%!fmv觵s.e𗌴?ÿ~ z^3 <ŋg7eݮo ??/Olev\dGQn* _èx}>7⽶]֗pXVaɘ.;;G44û4#M_M;l&/w?wf`Y7~p>Ӏv]Jx<=xI_xTmj]Oo $_8$0?wƏi3hwhF8Ʀ˕CH'>m-=G¾!wxP"VX 栍ÑVi 7 xxP=NJ"ۮ5政ۀ0FsZ?g@7qSwm44I$M~L.~7UIЬ,lӬ4F.7 c⾗whF8ƏwƏi3jRKbE;G44û4#M_N>hwhF8ƏwƏi3h9iz>\@JGOЃ["(5hRA*MmE0Og@7q+wƏi3hwhF8Ƌ/]ODCۣw:*Y^]؉&,"1#"[y&s=Iɯ?/o .gvb]3Q%ӮA-}j;G44û4#M_^v;KMBiS[szEgVMIYkoſSŝڏȓWU;G44û4#M_SRMXԬTUZ%>Pq|m6HƊu7_)Ogri~"Ѵ6gXAхE#|OpEJO oR,5=nKry傉,]Zkzea:]X—OeaAP4?(Zxv+xt?ۭ_'ڐvm/lt~#|Pm#^}Ú~j:O|님"EP sc`0%uw&3h5.u=/d P1VG|k?ЦּEipW?S_#-{Y_ͤۍrek x㿋7ShI8yUK*| t=*M',,(~) i6WX^~4 ΚzZV!6;&`Jh$CI/K4 ] ~{OXO-{Ƈp]?H^t_~\.gM浼R}f .D,.zW|M hWӵ;x9ZHL9ۚWeYѵkѯ5mR,8̳IN+k/z?]=K]3xwHXr@gp>O<[/tV𔺌~#Shp$=J7(&Uc0. z!C} PI"vy2:qڤf;CIt/x:喕6{j<YsX/DSwƫkI$n-ؕpAA )z$x(xw^LZjwXO&3.-mw2!Ol/ m'|45{;GA$dw1t-wFoM`,-_; `6v Ϩ;]iUsx-T:bW}$?{5[S fk ý@[rCmqwtfӯOq\ f3s[6OO :5/_j>-9i~scRFU=k<Z?6W WYK_cpgˌo' O]nKIA'4jq? ZꗇPtǀ;d|IߎpG9=Mvi [Wxl=&u88 ku{=/[{t lpI8rk&Q/:X~g_~Ϳ.:|o$ {bJ*hV0Qrq+]nR5sY\j:Ϊ7, UPA As}^!Q-p@)n AjvޙgL.,K%^ea.+OOվ xoxCMJ M<8ZYѨ>W:z|߳|WLj%o}Jk,c6+#/h GDK`H{ o4l+<9u`}Е Ö@Fxk>"Mŕ5I$b]k&#$K gO5NCU/|qxKΠy![9F[_i$qӚ~z?e5ֹCm,xo B>Q!l-kf{^[i)yڲ w]EJ@=zG wE|I7Vk&Tx8"J],j?d [x^◌.<762酭DsC1PAŽרxGCĺ_kn闷V$* py]}dQE!Q@Q@Q@Q@Q@Q@Q@s>5. mWMX~0f-m:9U<=k¼u0'e3"^Ij72 g ƫs 8k»s :? ׋~wMKԒ떺oyCn8R1=PϯEC?]W[=;K![_49.eWgګǾ5zn<.kzѡN!1cv0MzOxk@ݍ%yђn)0acv5~:N*HKXEKm$.2@Jd6G09a\|<3ָx`xGO%K.xC`kwlm <߁{*|X_ Zx{i i)]X4+]0M} ,ok/nu(uae ``tWۤS}ttx]NhZܝF,^`hQ~\dryp}?ůO! ?zSk3AXj [@SOמ F z:.ض|\oNZ}m&fDk@kx XmzbٓRx7&xvsΛi򨻊I&bYJ`zS[|V!jl$3GH]rh-5i4DFVU@ ~Z_zGDgl!zͶ["9 irpN;u/Ox?k~9yoǩkݴ͡c@炧U_i|_i:ZM[xf!`J@-jNZ=(((((((((((((+'^no#kZn82{ _Aʩt*~4ֿ}"oe*|Au{m͓l%x,0I*r0qʟC_1k|wcŰih> {l'VW;8 U$5wM>/@ VHC9UT g Tt) ((((jZ%ڍ彅#t7Rqf ƫh^&Qm%ƍX\譌%I+mL/|@ʆa^ޥxnoB+<ټ?jDmo`b7"ƍ!YT!DݑWnWOlx"YHpA^oxGWyck>4KMV[)kWfc zcvYob zHsQE (a6>巕~ݕƵ3|e^hqß7N6't`;دfMNOyjj+r oIM e#3f~;xa񆩭k6ZimF4b1EnESVW%;V_k|࿇*^ld];kBLNb| j64Pۻ xPhKxRRݖ ]j g-,24| ( \Q^Q9V?"hit+UѴ#Y[d6l'Ϡ袊Wߴi:s xkKTDj˖'.ZCĺ?hz/xriu& 1|.+=Ʌe`}kE|?Z.+o xP>.k˦=ݼN%bSxs:1ھ+S%F'Us2uSHem_G>XjV² p2;xKCծtۉmM\Z#;$ 2:W(.~.GX񾱪Ik2, u mm c JC1<%Qmjv?:MA$fX,` #( C֕OGD~#n$M`t" $ô{vixZGaiVZ5bOH" zkf'ƚV^N/5H IBNWk)f*1EXwZvx~Z]BDY HR0^pX<'֩頓VTWqt4 Y5 ·ދfwi m홱[OxwC|{ivw  5/$څ c'P&^~:G³1W)e #*'C=bפ-?RnߩEeܶńwgp*a[kk#6ĩIo rIϮkυ?oxIœ6cKy GD c(P]Tᛓ+eω(W4[_յ;ūۤ,׍2{-7ۀ7s>ykݏφl "O8Ȫ0zӴ~!+ QCr/cK{T)Q(F@a3\G7m_ Ƈv~jv:Tww H bFAx=Sxz?m /֦V[t|384_p>t{ 'M>n&{%I+ KOz^I6xWg5}j]NF֮$߻iH 09z6 +DŽu֦.,P}>p\ywd``< <+VPu_h"$Fx)g$}ىG 3Sm-/'zmih`6YBeӀc,FWs)!گ)j{z>{=Uy&|HDu0jp Ua # lH|ſ?s _OЄccmDk*,w9&xzv[b4Sy+#nc'@5o;Zx. |"`}*NHJG$QH1'cqǭzJR4Vs#sՏxvN_?Z_Oqag]6ä"nEc4CAp3^:sSRU H _JZd'IT7LԔyO|7t񞳬\O^-p7ZFHTQ;m ins)[[`1S d~#Av}@X]ڒ^!)61Q@\gCqg탦j^ 񷉇nеH4>)UTi$(#rNC}w~)1>j>$o@YT"PT`vHci)ĺ/ Gg S]0xDSo'WCifS.Ǫ&Tg$1b߅=x{x~bgaelNKP)hMh6:xi"} KĒ)unKۆ,pk|7 {L4EGim9jGxu`SψoK7o2O023~E'xM,~g q@ I?e|_Գm9tf#C7`޽BG|zqK.t5djI7IU3-!9u7%h:ZY?Yz-!9Bv;'o0o^f69,htz0;b.Qp+9~;xFb^)]iڕwAl725C)\12H|cɢ6VZrYke]9H#5[Rm[}^?Ys*iQ,Ln?v $t+f/"$.4Ёfc۷ߚ/|K?.5,$g೴AE$A 0p2k'4+2x?CSSVI̒}g 0M/὏ ɥ]\M{^3]ۤ$W]\GOkC×w魣jz}Ɨ0[dUsl һz`2Q+Śj6zBP] uYItbNc8kh|ms}uw-|՝] *>|M8߂s? ĚZOԒ3op#6q69 _|1K m< f-Aig{${I]'zxwD5[)gco%HDRqA4GMu_Ės<`Ńc( G6[c|/7oT5kwR6oi.ybA Dqf'%y\(uoBöb~O{cE)a2׊|`ψx_N/76Q2:L?(AEPEP^ C+w'ֵ|uhڴ7e1)kX%\W<}z\z5km"IV᱾CT ~9߀'q_+grc ԅ^Y^FMZԴ') V6$|֯~3hWZ 𮍯,踷сy f;@l6`|+įؕ"΀i`eA4-$UU[mG@j{maoΪ1*yL ݎ1ϊ5_u7NFw{G$G&L)"BN0B 8O?IoRZ*ӵ]2^JmEw>\JfK+4ݿlBA3#s-anq <%X|I xfKФK/"Rn=ɪ?>xڧÏ[?PQi"vNA˖~ټ'^t BWHR) T~©x?~(֭t[Ibim$)<>[ct k0gcy%ٻ dH:N K/>"JmQ+aY 5CĿ ;GCz.?Urdg*Zĩ!ƿiKg:[H ~ σsZ:o,x5+M5oCr+,he!W/hLJ𥅴mRH:|VodFIskxKyWi]f[ڑMsk|Ta ͇G~4qGԼcđ~ 1g˙Y?=q]sM-sONtgq$6&Y ;IG}/÷+7fTR@Xق)f!T ORvj>1eoKDӼ)y_1O ]NY4+iFFc^ᆽx:ׇ":5~<\Nd\IN88>z'4F-*3-K€\W*~aZOe=߅]jU2 ޫ̛#)QEQE?u+o |mA~ev5[k˹`(E?aǡ>׍d] exSĺ?|?g7EڗW"䌯AzEUE * Z+IwBMwmRx/"+2~\J+`1&a=Ug}Z=?XIk5σtiQhcC@fKr_͑K/z=+_ΜEY#W[vp@<`BbAoqDpv"z ) [ռ= xr^7YIv8Oф LOy|¥[m.dG4l!o_-+AdƘFѣ*F+_կ"|XiSN "Qf|pJgΐq$`gȂam>⑲#RԕMܔKYlR8thk%.OB=AQnj|C|z_跖7>m'IݛdW"Iggt/;Wh;ƚ5į4Z~5te[3,dG$j< [%cE$c!@AA=T(Vwuú7왣jBB[~Gs{~ xu} Sۮ[yxK#ITWKjV5 z2+(dP?=CA{iosmkujg"qY9A꿮 هگ>SWFIlc(h(f!F}^Y^ie𭽵C (ZիzA_0\_/N-u/HSm̒U|l6ソyö35_Z\Owgsoi,@%WtV}?P[=kb4"ΑV'5Иmuz8]Z9.nd#R=\Wo^Okmn> %kX^a; `#A]?<|9.49Ry2I,9y$G`ttQE!-Cx7'ռ!wK{JO7V!}ƂfPs Gk#xJZl5mV EMr&Txr2B~"-, Zj DA6F 铎௄ey#8ҬrG(F@@wkW>/GGCҴdsZo[g,by9dYICksᏇ',7~suFFxT5~_iq.Mqqi$HIc>sA=5rcVI_8mg2@U A s}n|Qc3e垳I._MskvyqHp+g]OS*x’^k6U0x񮮣|٘rgf_ ~̞𽎩dڝt_'T{'?eqyۅ6_ϦK_$1O^=G?1ڠV'WR,QE ( ( 7!]Crvy XcwG~q^^C(𭧓$)L'x㟁] ᄼb7 t,^Ns+_Q5i(>fݽܮFXgd%,?|<:?]3mu:?&Y,N%!`*Vij\w-W$]ڬf2*mp#Ҳ|k5j_j}̗$p[kAc\)SV_1lC]O/59k} ߧk?xCֳxRIԴH%HZм%z.Z~k'M%ߪ$pƒ!Fl1,d `<^|AXm~9қUyO-@Jė4Q>c]w"Wm"m[PO{aʌ@Ds*9&n>x-SK[ \ _A~@>Z|(!Xk^XVWH˜|[Ҿ!1 `' 袊ZI:J[Iz찙1* 3M|>+_h?%x%K l%b>ıq_CMxui&6r"i1e2@'|7~þ"vƵqZE =F8Gj:=ĭW~.|AigmxcSϺM.m ʏ|=;_ŭ_"<;׈܋y++hmVr>bF=|#-O>7:Jx7 n&}$ܒǵI_~'5vMBO/KrFkg} [R6pq"ş;x j_my<1q4iI\}HPW;튗{|i4[[/P,L$epI^9O5|ei8b2*yl7Z gSŭc___n]K_#]V%Pl-v>u%kVVpS]iP\HLʱFda@['WOQk4svoC}ԟt.li2E/XUa=lK k;|CuϨ$]G9El֢6օ؟0E-u~|w7WUr c̈́Ln0˞pyC9ᯅu5{IWSnOXVȆ- 2z5S(xw,3×~ l{x'fXȬsڽ?^wV_ jӂã^a\lXJ4琤1h[{_ |W𗆦!܋,aR+'(𧆵mjYInTcB| t/-gzŬ1iEE.˸ לVixg=8ZTadːpOK.nxv^kV5O:4g[ź}t4j -.K@F1{\ _M[:K<>drFQ91d_j^4x~{xNBM$^\UЂ~x_>-QqZZXxpITy&;O$37$櫩_/'QE ( ( |{GDs6\:YÌb௖I@Vߍ|ssK6ӛY Ӧ{8 YkXak>#xƍ+ŖxGÚ.q uˆVX)5 tx'm`,/NRcqD$PxxGϸ.~=xg7W$՜3rdP $*o|pߎY7k}^?= .rǁ׃^)qm0b^ }k~"+8_"9 !ͻ1u⯈~=m.-.5Li1]c,-7GJҟks(C8Z>hZۥ emKu$RI 5rj{bu!m4AjiT[\QmwxF;CA/-(HۀH^k+xU{xBhAsyef N885-⾗}:i6u6 6KpMAW >'Nfp֗6P<*We8 AV[ ӟ[ehm,-$ U=kǿgcZ_ůT 䵞82cv.*UwL=6C膻hw7ZfMs/hB96N cM} SSYQ:u-dǙ3)(X̀nmַS͝K<3r#U w|CGƣk|:  ԋprs(FHPIH"% N- r"bPɞO5oWmHN>R(mfv0OryƓaO|]+Co{i^:M,y?/2#8;n~hl">W{yP<<ο7Ex? 4@ ]$*Y $\4OG';x5^e~1ҸOZoGW4}aKSٲ]<6ҲEoK(qҼn6D>::j2C%ދ/9Y]O)]vW|wFZևpN chD;^9du  t}gWV^"Ԭyw&2F|n*@z +ͼY ΗpKu.吨@A,AIxρ4ir<;-D|nmbв>x)uC{V:G[ WbVDgVPH'+;ygE I' PEtbZ h^.9[UO3nlRwgc50['_i7j\ssncyRN~=VUNέ$,`#>eVß/WDn,'KY$]ѿ pq|_e_We=ΓK]Rŭ?aʯe!uWp^k<_]^m}&+jɭ&]a Ȉ?AE>)QEQEQEQEW>~ u+fSlLN!D 6|`k|u>kG{oM^YΧf,eoHQq4&>.O ;_?.jF_Jy<؍F%65_4{?~(3|3]>=&cGTV2]ǸÂwǵ ]}Q@ŠڛN ?x@׵2Htg*vׄK)/ Zz魬fu%ls+`}E~|M'+'GVZ߇|EoM2F؆[ x?~I6j L+8h{yhEW~Ӟּ[PVl,-nu7.y@E|*Y_XAƕ,4H[ 1;b_JLZ*%̺RAco%A{,P"EWJϤh=[߈l5y|Mi^ԯӨ,@/two +MKR{ieiRd#պ4-U  +/>槬E4X[Ir[*Grq_,~-[|w{}=hn-i!{{x kA#\X3i~eieO|^GїS%N5֠JhFC R&/=Yy㍍:'[8q7c+df(rcPZC"’ql~IP}*'p) (t@|E{qNEms텤K%ƃ‡!y/2Y\^6cl4 =O]&;s9yKMhzsU_wLQE ( ( ( (8OOckWDg-H6w:8p8zoxc}7ÐAMǖI FUv*@{ 9cῆj}ZK}F}.]K#9sO9w> ?\kIm2Fە[Mʎ@ 5QKaQ@%,֬#!'j>g#855)M4q$HkfVE` U{쨠'Ŀ| zYּ=mݦgFţmi9SWmEQEVO<-C_i aɻ)rCZP!#~ hV288˗|$_|o iZC}iA-$S$V>BTĿ|3Eo Y,#ByF 0%Nѕ$MwVYEoI  8PtTSQE!@#dV54];Z-#[-ᵺIEkEq!|^ rOs$x=1TW?WfХn#3\% oFݑڻ(>ѼZZnl,dI%=I$٢`QE 8oܞ3öGiZFP&U@߷wkgNFεkk>oPIGu(ox*ZVpjkȎHI$ c.ہZ_SeͷhJv;ϵB,uS)QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE͍ߴ[>P63?ߕ EQk~W(k~W*GM}_M}_P=7=7¯Q@ߕ ?ߕ EQk~W(k~W*GM}_M}_P=7=7¯Q@ߕ ?ߕ EQk~W(k~W*GM}_M}_P=7=7¯Q@ߕ ?ߕ EQk~W(k~W*[{Q*cr]qPhg}+j5V[I+˽m4>72L$'|mм-[^m=O^Kyg0MɹeFk nɱv>h??|3> .|S_]UЭky(d;T<N+kZ<#xoAׅΥiM=֑\ޫ"Io)g\|mYؔu>h?akj4r>^w~^t >{MfqvFvČF :+𖤚ƣ_D$ܥȳFѸVXFE<0ھT R 'o"Ŀq<0@~hH'Wo~ Yx{Nuu]4}`4L]B(!<:/ ϴE>h?H'-mR7q4b{tEZB0Aʓeý_]M*V9^ŹLծ#`Gv{ hg}+SG:Uyԏ(0pF5^'<hp:|}<6SmffTgHb0w5nV GHfQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@=|}xq`^m-n$墋kDXG=Y)_~Wo$ Zoq4h6Sgx^H)qͱee6f'e#-Ի< hti3.=k0_`EW?3Q+k*?]_7M1Sn[*)mV>txXkiZڀͱi$ASԞux w7]|4_˩]\II<`iXDzkz//,/D.B9'F"p"-9X^'YWCSxgCQ{&IYх '8ᦣkQqoha{HƝq8w7SekZC4gį|#.o-֩Q+$1 V&Hٻ*W5ŷѫIաUb۝N9A3g_J»ֿnb޵Ct7_>V~]h#QkGWgVУ+d~D1]pE [ľcnl,x=g»ֿnb¾[ Ԯ$$ADݢ) (((((((((((((((((((((fotoxx-20.08/images/sketch.jpg000066400000000000000000001743541362435004500163220ustar00rootroot00000000000000JFIFExifMM*V^(if%0231#0100Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?/էwD4=/ILPFJѝ\0b#_ mƫ57zxPҙ+Ws6id{6GnQ {[Uj^d{6GnQ {[Uj^]u[o5G6GnWޏ{vGd|^mu[o5^zCC|-xWzz46J(7RɜwT $}šd|^mu[o5\M_뚬45o-Cr~mr|뒀m\jh~ oU6ghq}y,簑 9dcx_ ?6GnQ {[Uk5}vϪirh^" lLC kF~ UllizZ\\}[q#;ɴ;Fak^A4uu[o5G6GnW!uSSuX/חNH@q``>6&xG^-cZKi6xIUcUPX wkamlvu[o5G6GnW!'~#u;{i[G{|d{A#)lc2H^y9h/cUF\.iLjcmo'/%bvEwWK~*#_ mƨC󿃾Ҽ}Hj]:L)ckr#v+An76t ۩4?ܽ#hׄۂNJ5`_"6GnQ {[UiuIkXiQLOtWYAlNk/H?OB++ MLE/ujDtu}UN~i귢_.a}o7d>lCVlټ+&uȳƥIUSf w`dmwiMZC?>/uj;,jrJwڽ]e\ QG%oc"NWVyl4Y%8V)c.Cq u; x/N L2[IM;Sʞšd|^mV_o/3gXwאxGMOkcO" 9L}  -z7x@E,W_+޽ѯp?>/uj~?$?=Hm:gIC3F݉Ɏ9 r 6? /I54tE]Ŷ+I$yLO\[3yX89Fr}U39B6M}_Oȳeh[x>0嵷(0q5G_Ɨ>gĚqE{Y,;A'5/gD[7S-cӢqg]8 ㌀kQmWv  ]5V3̰rDշ_{Z[t*r>_R;ouN 2s/N/?z}v:vqs7nLcd-*9+z0~^H)|Vtx*QYw p+9+kxMbH"Cչ{=똹X'V:Ǹ!Tn${c5Y!$wm_~8Q#O:~/k$H0LN=6?&͟Xv孼g9_N9 IS'`˓>ڒxJDݼ@~cdyI$'rYSƹp@ 1o-_$edsDGqpkKɐ.3RU:s3W h7 hnRq ITt2GD"ԥ!c>>^qb<{9eqPcPy.3rɖ hVnGyn1E ќR7q\w4RvwǺ)4#m#sVƚj8g1_>|N|7 D܄ v,xQO> |Sv-zUar©+xT޲:iүS8b<*~{QJu6A#"Rj1J7,#st$8E_5څvi䈩8noƏsգVNOB%)YR  <sRZ~бme9lYN0דWe#,REq$-*:bt]:UO<^(7b3ډې_>㼬, Ys Q*i2ͨ4%تDdp:g9_4wz"Kg`bAPIs뫹Rڌo;]Ŧ:m 6=jYmn泰ե7?V*I0:q~j~}-44,y|ʾl~>&,ij^&kT]ڝ9Fxlqx(!P<~,:u֗[$mt+FFVCHDi]XzƏXz?^ Hϧ/OghE'bĒ9PJ ˒ u:?O[6V+,9*H\h?a꾖_KƼi{1)ntŵ8 y9%eVϣJ+'^/>YԤO[b  M r#™.;QZϯ7%~5A"J~}k__ß|9}\,˿j>}_O ?asߵZ>}k/w'eqrx_Wn0ڏw_/]'Y/ xoW1ޓ42v:I܌2Ghrx_Wnw'eqفg#Eu=tCOܼs*\:J09jg%lSNNfj<39,A9^ ?OqƝ0.>%h-]H m.kA:0v19i:K/`ß|9}\\[ e <2(Я"6ڗ|O ~o;Æo i5wfnxQs+q^ ?Ow󿄼Gzmai$Ley{5H>`05߷xBt ãiyiZm)kX1DYGqw'eqrx_Wn=joJ5Uß|9}\,>e5;37& --t-S^ԞHLm^!s_O ?askvY}S>]h>N5ޏxc(:[/ t `d'  ?Oחi\Ml֌;e<ѧ2I]_`$ a"@̤w<29ۿf߇L_ /w'eqw^<5g. {,$w3r[ twa0STcճD5߼$x1VB%fܪǜ=9"[>1#.1čK{dgƖjҙ'eyKAOpӎlE!Y# &F0|鮇^LOT` H"O+J}ݴ qֺ{J4 2AsңӖD[b@7FGNlϖ^lb3yFA&'yF+n+݃ϧnXZ 09*0#zn90]cR`3=/[02J4U @ "ʢ.dQ)D XsscgHPO@D3:s|K[N6&7tr;ErSŹ(׏/50v AqZo_lt'3Ȃ9oަs/w۾q^->"wLre2,/̠`O= `.fܓvyc}Ǣa\Ϛ] Kj/3+, G,<~uvf otێ[!P@>dM<[(;mVrzIsٸ6+D ±`z=<׃(+=LL;ϖ$b1 @R9g5{(Mqcr2nrk ~D Ea@=$浮]sآIg LdLQPaF9,{Hs^m\73=J6{V8uI N/q#A8ON=kӼg䠽a YjG;`'%]7FGAv?VաKЩMV \ B#RWNV=LK[x4&3+|*'o\u'qnzm ^-n qU(Esd#↟d{i*^yrG<}==)x96T/.YIiaLua4g.9-⯄i2o{zKă ~5ɿnH>_ެTw5&o0X[P}v/I%ҩVTIrw/!qam''pԪ/p|G֣ (?u|9MmwxI'׮VL8ZI |ȅ aGaq[("l/7usKuss{oQx_ɊW99@sI,ON+ѫ^tޞo8Yߍ|qgs:C#w7x`k{_}i'3ZI >ϰͱ_6S2F&5;[;!gW͆eOL*%6 Ol{NZ,u++E;~^ d[~L7ҹaҺIO:W(w͌ڿOͱQI"y˸FTA"e B:ƃ1GEu(e#"$R3E%~g ػRDl  r\?π> Q܅%i|pp59# B+?ß'y/j1iXaf숽Y1/wHɠ?n jXA]9?0#?5\lw=7Ǟ0t/O_}N彎Tm.m O}{i 5:Gč]#uyĒA&`Gɓn*׈kBk_]vVqZϪYV:6"%Up,JJ`YSŗo^KsH_6%f R'FH#T]o|2^i5.բZ4.G$Dr;$M66am%4Ctw]jWπ\ @̇qvm 3?$iuwm-u!4 %\FI8m9t!&۷_o>.akv 4qb2I\G"HQi>IuiSzg7=ݘQMOiFQJٴNV4Cz{/Adi⯉i qci/٢Ǟx~|X-;3j}?*iǛ_/>/Eto]7ڣ]+qivx;b칖866$Fj c/ī ·ogIu^v~#.eMww:۲D 1qKUrʊ^*ַ![Ojz|:|]2ČFvwʪ0I ~\Q\x~}K@qsf6rm\LG8?>9ФA~(*W4/ͣ&e$I^_!B|rE?ПQ_!5?]ٵ~i2#+/hO_( +&ͭ?>K4'/WMc Xf?__  +&B ?kw/_}{E| XB|rE?ٵv/>BB ?>9ТBk~K[^AMus*o $T M`6V4м3dmIoc#̱jo|Rz't59=ڼ  $$Q#-늭Q$ڿOW:UhF4rZv,N$u0I E $uFTdV`Tx4=>?7Q>$)7~+ Yq.6;+_?Qq ?uEp} .?еfR+`-;2G.x-'A] i<_ljr|WBLrrOeM\Nu=I$vb^RBvcs~UGReA('z 쎩O-bXs@9l DU^8. *b8Nߚ\oBeGHaTm8ILӆ$!L\SϽS[w4$ 1w̡cw"?ĭ7zmMQR}rON~_o R7\c'o$}kߊ>/I-6GpapZBA<rpkc!Gi *ϙ3O-KϋTIRZ \zd4d,CmHU#!rdcl:tBi!4Ѵ'$hӆ/8$^jڼ ]8umI";%P ` dbGTu(.ml+yo,)J/Y u}s YEs`n Ó#$GNH;Sgưl5[j@P8%!]n4d4Y!C ,+K(Wov#<]mnmmf8QnGSӰM>D«/Ȫ6lqV 4bGfLu$'=ysrԣNNҝ #9Ύ6P[.\C D3,''98`VG{}:&(&w`t`q9'62ݘpyyaGBk*DCw0G%R,sU=淧KkAݑ*莢{%D$[fayc@ޥ1[ $ȖEA?ʕbE߿Sנxk@o47(;~5^5i+]Sp|$[urU;U~-6epv1Bx ECiZ#)nzTxsmxz0cI_~Adz5g{3kw^!]Vu1WKc=BHI!>nؒe"=xLV0}/>x{{V<ƴe_a 5j_>. 1!'x`YZ<>"é.vH%'{MSC+inS}x`?Z᾵bDH+[o'q(/m6ŷ&?<*)2)+ҒGMxy`[UUmcrAqZ l+ yQ8#>[eE =<: j#DCÓFoJ3s⺰e4{=:MIO:W,:WSC3މ[JJ {<‘PeM-Gpaʘ4̀{"GѴOW\]1{{kh 32JK3bȥz)ieɌy{b.8mDk2+m,,Eq+3o]q3.̀/gPW*H"$n@K{[ter<]ƅz}$y!YbX+0HU^3e9GulȒy{:AZz f.:P,dK.JB[hL9^O2V'-uy,6Ck2*Do2G~k,|O _ah.ܛ@R nUW߈t}&-[Z]=\@ !U*1⦧g=@$0AY`5Ur~fsԌQ\B@.dt%s,{m93g ??:Q[>4߇ YJRSf8??:kEo ϦͥhsrY`Q`G$je xEy?oy|Ougo;hPhFKMCDkb)fOECsPr|#xRtnm5T1`.6`PF8cOMF37+ zw,z u4SnI%cԾ #WkK ]@ײ8Iaq/g pyLվ x\; ian-- Ò| e2Ir9ފC8T |B<5,aUpP8+`QE ( ( ( (rxG5_\۱IL1MT3GҼYoz}ݥ拫D#FRFLldg(fwqY#3D`Pk^,ЂxV_`vs+ I )j쨠VMvv1/r7-z\r썣M'vpa qpe nEtZjS20٭*xœ=;Wk#!F3ٮ_R$G#b4silUOh\k:Ƨ Ƒm`L}O3kW ?V`R4`tS$ w\G$6_ƟztKYzlB lN8 5? gSxnu (6ؘ,0Z'ۖ!06灌ԩkQv3Krqfx{jrH$\G}k]i=]xd g FpN2<{kᯊOOs3hqjve T g U?Ue+i #$QO=+|*Noh/-%Z)e[A8DC+})#,}Jt!LXQzOM|Qiz΀V[֙G4[Ҧ$TV!ݳv3kdR<ƶ6yx{M:#)}<*}#`@_, w9 Ddood5 ,w:gv쩮64^æ[L$H$$Dqu[s)bT1 n b{+(5k7¾!]kP {q"fv_0o1FNN@'`>JzY1kmj=^и*zݹI<0*r `׎滯_XK6;?{$ s9UB=}+5x}:$EeX 8gLAl w-υ/5_'63kp#֮.)D[rU,)?7|_5?VMݞM*|M+b99V k'Zבj YjH"Bcv*ۃ7>\׈,<-}_X݋Wbb#UN񵕀9FTfWS#n-pI'=**]ğy\Ag'&Qcn UI'(ͻtWaI__įOBk ]dT^PwgyxO?E¦+:+х|,bg[.ܨ3 ?>M _|̏a*>Lc}4_*mxO?E¦+:(|='3 ?>M _O0+ۃFΡg54;^x"Ė oTQaN:Wz]XF!G={Ry5Q=3q]D3pˑ{=s֫mK ז _r\pc OpFMq֩Jr(r[gU(TIs?K/+x+y37=y7E$cp \}[MhдHA*u:X,oltg`noo!hT#az@ֻN޿Z78Zgt?罇 #ފ@qta{G]?罇 #=?avP WL0Zgu@G-]3{:x\ᲀ#ɸ n<+oqi? v#L]&=M>@ <W3}mKs%K@HِǠ~ռpLJ+ʣKݠ4$|(?)y~<|GQizM̗GwA# IKkO7Hl)POH3^+ĺGfnV _[J\Xp{\(C`jYxkGn᳀16q_p+VkCi0Dv鍪8X ii.$ۥCLǵE>/|Yq\+ w!$*j~ԓ׫ zΑ]>8X$d@1D7v?C2~*ҢӣTUXrzgq[$!]z{՚:[#`Mes8ٍVH=Kz$c1Wv!r67=F}k2\M7ӡHy%E+x^k4 w>Hc1Ƥ$=GZR~VT"?aC]T yiwvwi#pp?>0OzsQnfP J*Ĉ Y6rbGPs:҉"3 j$)|xNKg%kGkr^/'wǷw/4V!Ԣ\!Rx#F*+X@u Bґ#)?t tY59T1 gZ YtVT'!aA:-,:џNb+z͢ƐЧG}I<%UMF/{ju֢Nm׿7?uF;6u*x$t>IO:W'(s T'q}>y7;mQ|##%"\Igx77zLsOyq*Z覊7C*Jb>kx#m(jWSϺ|qI FP0Tp[ RWGk^sf r]j"Å!̇@8+?-#^4QMKfƛʨEӖξLf1" !,|Q&p:DC|JLTE9CuV MwZ2M5ۗ&]yG9lQ~.=Vy9lP1Q<E})<t񷌼=$i41M?ŰB,T[)a'G5ޡ{xu&spxGhՆ⥏4#Q:|:.{A6[oLBc *&·BoSJ5iEYe6bC1prƽ?M6v0Bި7ж9?ɮ:IQ5{khDs)ya@q%GX m.+[Khc =*%(Q@Q@g|SiZ:t\$_B hQ@Cs+xFi|4[ĊdH5$0sA@#?#^1wC6{ꖱiװŴ)$l0GoCKhټ  #S~1ZB1\1֥K(pcS;xɓLXj-* ֻO,\u6& p7 pi(o4xW]mⴻn4( tSȑ@˅N:93VGiRϊVtCۜJNP~t-CTѼ[DcHpp~|Y@4ˍVC4KW4x Q/Vf{7=JG j~I.i5yi@@ܬ9%S?j3rx$mVRn|Yrhq]=\Fu$gDŽt/x+J[:yP$œņ ::\+Ƭ~"Z6I>Ȯ&GQ^# wxM~ gAqR}w ?ޏnj7Zۦ7K3Q}@ៈtϋsNfgԭ.d܆*#V5pjiyZx5-9=gF ,[IQMt}kb`(E*ȡ=EyIxvo\[/lQ4+y?He_\:Ҋ5{[G @tW? #Ͻ?PAEsϽ?Q #tW? #Ͻ?PAEsϽ?T>+)e:@p < le6ko4`~|Ŋ/9'+A ּDN)@`1'P=kO4YgTLvq+!FK92*czI/c >wIСIn\Jxgՙ )燭< s-ު#)quiDݍz us/6e۰PL`r}^?>'z!h`HU[ t8|,4,jv{Jqȥ֥HHmdc3[E rďï^{Cݸ k|r]fabU71 jFmĒ9q«8UgK? %dv+gHsCCsV 2DUsN?JF]I&l;KBYhC2yʟV0c77aVԂ!2&9kGc&֍2Il2Whw~Q@Ns='Nl$#[2w@lb/c4kV@ GQϽudnĻq@^ib{IKrs+)]G.u9HQF=Ot*nsJ+SZ's|Z߃kj6i9W ?e٤>;9В# Y-R^ԥl.s~lL,U,`֠GޕߑGCJ@xԀ\AVW1X3רP %td6mzu eeRmY 6+/O'u6=c$ʎk"QƊ3ׁۡ֟ ZiuD#в \e*kbbs^H$=-Yk7ƮLˈ qGOFizlr\xoX\<^[9/m .'qtr0H8Q>@@n88gA|G{mTu5"Bk+gGZQ:6< bgq2=j]ʘ44,y`&-qzTxV 728mDc~G N)Atv4wfmCm`X|1?}Yf~?댗Y?BOxОA>]b E2/ "8<9mƳ176!.L_hNzmTc#ì˙l}E?;Y{(Vo+*\g =OҺIO:Wu+'YhʴM8yq燴-TcZ#&@J _c6ҼU/o1ZbItشQ[]Gbv!n%B ӼI/gDu?~^&y'3eO0 v'>$x-Zme7[J夷HI 8 pHg!VPW,g$cߠ?֫JwIn]4"(mXyG@dS$6 piQEy3n5LL[hMzY_Ceg"(I< oq~V-ӵ qi( 8'hcs8W~|on"|y <`pHQ0?>G۵HcuVM\0LDwc >v9$3~8,PK2ib6l(޾ۃ 5#XBET+{?D|A.:KCpB} I",r QMh3ٹVx)W u:;P8W;a׿g0nM\ykZA,T%X&Gl.~BU nd+dQoMiڜx&=) x㊷ okzGEsu%贍1A@+#,p$`:x_ &we}nv$g]WRA`jh~*e[Yì_[XL֬&t T32)ę@l|-jV&tn"OgiLxܕu,$O-}ӞU6ֳ6T9lsy3Vtiɫ 1L6Xdt7NG_HgH$gFm\cN=EKs[I!W`dH[A`L7-½nƇ bxW]K P=yȌYz\FuG'~ϚσŮ_R }[TR=IsILy{[jTg568C?^uڟm<*Hdsr]Ck%0מ=_bwީUv'kq6?N_p`$0Tʝ5O-'5+_i)ygЖX0Jf"z)YZljI>km;zNjlu+]?RշA"-'[o\?;^<1}[KmsZ}e2ɖ (V kp<guj?<3}u+M=ƕ ,K33$I=ICOs̿ᘾ2 hbED'M}9#/f/_L'x??ᘾ2 kh}rG__xO41|"e?צG4ˁ=ik0 !Ui@vZOmݮ6-'A@|Fx^<$s# |w8^=GEf%Q*w<9Qd#WQG]d1q6 ܞzcҼĝv;/ g$'~8fԶEKھ]|_,%ty#;w$qȸo khX85OK/#5[& `A;V.PL %T־.G;Mqqg$a$q-gIPzk#z9„^ UUǮ Uԉ ʕ uK8]۶Cs7"Şi  OZ%R`A\#NMxΔs1~=7@c,e#h)4HGʫ3|7o[a ']r uM^\Z_y1tgs;[fR35wOb;8hB tOx PR#'-ni3 J̪Ć({z*vIꎸ 6|C09Ϯ*O |څK%ΣpQ-NVI>AZj[iфRȹ+F68gx^Hbnz-+m"{qg|湯ڏ*K4ͳb;N2k}WvRϨo-V4~dcќju*F>?h >NRCIWn9ںֈ'¸ .,(eW0xǿ-<;:,_2mO6e8,<'w>\z~u_3kډutxm\SO^c $#{Zçj9 ?t׾ ׂd1#275-/u5qmV4[ƨ3^o"W5_,vjIѯ4[Y~FA0_\s-  tp89{-4d1t_$Nw.__,i"k{G,s:upUQ,b[i}tw9a0 glΊ^{RG2ӯ9<ӈ[ßd%Εz2dP R eX23ׯ<ӣ]*[yL;CtaPH鈌i.gĪ2sv[\Ty"Gk[a9ק* -]fFiu#dr:cAuɤh属KY6RN%yW|uc`#Mg q~32J:9sg&}-C3މ[JNݓҺ!1f9ִSr8d{*ςlź6\"K9zM;_IKUhq@M`Gn󼒡BTU(8|OmM#xwIN-R>IqYa i35'x'( U m\NAq .3Bmc+]xz16+Y嘜L…uelrk!e k_ZFM1NXXUSSC"چ-rU2O@OljAR >;ٖ{ [i7>e3:,ZVK4czbO>٤3R S0(jzAyvw݅jȮvColHpNE<[0c|sZw=ȸn$ k&$dC#+"Ǐ 4\zUھtĶq 5ǖhܡB; gw3$jVٻ5_* 4I0D tQZ].=QI ,w,k4˩X;W5ωoͿ˧m]GeENwH BEg\zx^(!;ÜJ\>>Cev9Bꖛftus"}E@qS@GAgjԒȷ1F3Jc瞞a(4m#ƺ6ԋ Ida$1fkrQvQX8 NɧiURmab&RO8*Z yoŸ3Q^)޲Z\ޜ㞥 WoEl0QKHD6iqAiomDQEilZ g |g[qsRx[/ B52@*A ~i_ um^YmfH +Z(AYFܻZϯ{t4WNi6N2|^kԲp2p2:J+B IM##Vli^ƿ15:Z'>hVaCAq>p \ܓh́2E}:|9/ k|g*j~Wlw*%s;0n awş xs~ZjqC#.BO_/C7ok:Tm4r{keVr`duϻT2O94fncYj 6e*J1 }^\3Tqsy]G1;dƿy*X5kд1Y\:`[OUE?4$cSOHɃ?{Pi4vpvF%Te=?ASU`IxJ7l@Cm|΍09le9~΍lhyr]̉dPC*8*U?¸/ 56 )u4RKΤА~bǒ22J~KOٗᵘ{J}oU3>KFd*\7! )0?Bc?Hu?K:6ukWE5^2⊹"+p%Ved5 ~f2Jwq]ב3־!7vSi0Z/,P Of>S1c 1Ǯ_to^C@ɪci:֑ HE) YJF9\s=Ptv#>8xcc75F/{k ƂX$n8S4z3j7߾-]^$ ~Z9u«0$#!>yk:^M)8at,EQBT#ğ x*u%ɨaդİ,F7QHʤ+:K~6=>9t^ٝ+[oX n l*f; cX? |>ԗORX& tDo&Ak'?|M}#^,ݮ6kOi_hXvC.TxYx{&ua_{/*I|ѣLcTwd.wm ꗿ|!cR49m;)~@BfPtx(;UmC\tہ.Vv( ,RlIπeҡ}-$2\)0""A9- mƗiM*U{빯yo$y \,1(w®pv~G/F:[_ď$j(fb9&*> B?cU{y5-I{Gg8 FeRA#]T(ߕ_s_KVSR:OiR<3(ƹc0DC "Hn@C)築?Pug+c:OiR<3(ƏiR<3(ƹcoğP]t|=$‚'4?)_G4?)_\1O(.?፾_Awɂy}Û'4?)_G4?)_\1O(.?፾_Awɂy}Û'4?)_G4?)_\1O(.?፾_Awɂy}Û'4?)_G4?)_\1O(.?፾_Awɂy}Û'4?)_G4?)_\1O(.?፾_Awɂy}Û'4?)_G4?)_\1O(.?፾_Awɂy}Û'4?)_[մ?o6a[k"8`]9m8eN:n'=y1O(.>w -z n/vOD9'9ұ:_o%jNI/'h:K,h?vaA{eR;r+ҼI SBuH>ӧȍosq G+$gŻmi=V軚Y5h:xUIvv^fx]ߵAJ76?3cjŏNAiZA"8 ©a=6_*𝎟{5̓E{.A&5i? v촟MĖ[$#|{#AyoQ8sv8 %9l-z6''–+/|TcHLNQf3q̓5Y_o(4Eo U,V+!H<>u :8"MP:IXd\."Aq[ͪ,qXİ9xy |LM['~}$)B<>zTH:CA?eI/ns=r"/P{|ޝkuf[ ]DڶW$ qNx"A$DyH&XߡyC˭2~C|.u2=c',rz-GMJuEd@m ^AxkM$"ߣZeUOqW{'It= ;n==kSY#) eP}l\WxWe w8tW]]iT_$dQ[S.fK\_Vde-y!}0и*ٯmh4-Er2l~׳7|a>7#PV;PKS WlSlH?λmsEOH`ҸZ4ic`$^eiņQڸ0MY״%WɭW1Vs< sO=:sW4}#ށ,ޫ,2۝P|}<^*ƱDqsY)hmF>QڅUT8[檲x$UM2[()?0zttb@>r61O,t4}Zy(9_#*PZWtx{R%LlHѹ`zמ+C3މ[JSψf۪KHD5emڥ`=dW.OOT &Pj3ąf?3bd<+_7ďh]N\l6"'(/$T˫Ŗ_֭m-io.,$lômQ괟|mOQמYC̲ ŝK(2\,g-?0j_~#TҼIqjYၡF]NRXSpZxSՃCu,q?J3_w6m;=9"$hgW@ts8%r~ooյ 2+LԱtgP?d9|1魭{-#v[}&lj2YPBY?Z7$GԚHE±HШm\nxݸ [ |^mF]7_{fVEQsWو:|Tc,haT$Dfp]!ԬoY3KtF4H.ճgX!dEh'Q1cYyF=Am(E߻ #ϼkI+-tŎY.`txH:J.A !VՖY!Dr Z-cH.s(@BWm{RD}Y&<&8?ଘy@{B{ٿWV? I_kr\J:-ޫm-fPi1A\r@c_KnStfk0v!+=ck˿(;u>e>2<4no>+=v-bTId6eU #s i5je i< 3 H$2*yp|Osj[K,ok%r4lci"cbg< $'[xYOOkw4v!R_]_\R"Vw F[ntYe#Xd]z(|m"3#GxʱA A*߈|{ z?l-qsqvf ,!~| "ouEԼAsv2 IF*w{O隔--` sǸ2z=[4? Hlָ!kXUBbO65B(UTp*D'};o跰l7 `X2NzW_I4o{/%J{K \`x&?RCl1\Z90> rJkp;NX]ǿ 4K;H,5 ,1 b< ˀ+}FMt{ge|Y|JԵfiD,h8lcЖ;O75&j7SE F2X%Z_AX 'gZ[S[%ǘѤmNO=~\q}N"/Gj_idxۛ V0rd -6%SYO֚GD.Jj)m-FI4TK3JҾ-[[լ/Gl^\6;QL7*{~;pɤɭ=הC7^kR4Ow 1Xh:jx)ݸt5Hy5/ I7b)'pz/$е$mu FRS_L lv1( z+|.zBMTԬɦDꭙn3Z7Ưɾ_xf$YŸi(~>Y ]C_ѧ`if]f9͉ 6>,>6zm;:u\Zj[M  KN蘪2~aBEQXiԵbOd 2x2#xA,N@ [RlK}}{H $8;J~n94ܢO<)oM\E QD7*tW~73%o -* u $ +y{/>ѼOG$6z$Sc|yc8M;1'utQE!Q@V4]V-⺷qd Y9<[̒ 67(e%UI cں008PEP] kIm(.SO\sN}Ig#* b6]Rՙ10 ]Zoѡ̑.y>Τ4)m;(\r,h]."{>Fiи+ZJ)A){p_x?D\,q$p|gK]VX}"~G0kX[IFLA2 $4?xh.BV&2=}k֚KԬ6).}mB>3>FVw_,!lƸɭ="P9#L"&۹`c&˙%EjyYޱ98FuDhAJRw <9UݎZB~ass5+n\C"1x{2b(IPJ$kAF$s5X<]gNA\5e*zSV?7mgKԵ;` 5ǃ`cWuU5+/NH go[$vYCd'-R ?]g#os$6>QկUlϥڦQݑL1`F젎\׬~v7V?wje'ծ63cw8_:\XFV+f⾝.5o7Ӭrqiˋx⸈XԜ>p˔\u#<WdTsT{I[Þ6*Xy.dOru'ڶ@9"oКho&?i\_sUS֭c:upe#lW10!$9IJM ·ki{ $-ō}Hb\K\h9+ϝ.;Tգ׭`h2ݛ wQ)l*^m|6ోTn4vE%8$I,οw~[Rs#]4b4R6B$כR_{<, T1<~VpqO éuem_R1,2ȯm)U 2s ^Hψ~*m[Z֤X~KRyͲ1% \cdoYxOt8u\[=?ÓZE<\1<˴Gc#b]CZmMo Zo$yoO\4qD +QI?(> ~Κtt2O`$vèj68#f0!)3(N5)š;-ݶ1ȱinZmlFB6,Y@q/M+=0Q&]&aF8uBQ-Fτ,b bz5xgGjU#9Awn>p8ZBC jTz:PQo3 {ПPlΈZ^3 \*%tX nHּ7~ ~$]uk;Dk12ҽ:KK.Uw (Ko xS9B]YK+mIzzU)6$;I,pf)T''MteIiZC{k qta U<)VU91.~yǽƚ׆Gy R9I 4*Kߐ3 +|N_|?;-t{m3WVM=ʔ ʟ1$`ISQB1:n'imn-oYkDBUQ381z袛wwVV (Š((((-'A] iuP09kژϦM^4H@!듁$xzy%i[b9pK'#jon^B|Uszd6;qr[3 ))4hV IY q+6tţHT*_&Zyuub:4kkb ]Jjp`ǫԢl)uI`U{-#8WGE2=WҴkc\x]CU:iq’+n'`lSzrjs5&WnG$\ Oj'pnֿ"UJ=gPHh:)Pyk{ֱ\>oZ<|Ȏ bM=8ߌ.2sּ9ǧ#$xt` dn'zxn䓹n`]thQ*brkՏ!yIP aCZ"%x;sd*w0* .d@۳JU|-YnXu_Nk2}*٧ʀ ǞsF3U/cjQGjLG %V$,J\ 3ק\}%dHא?θ;=y0Y"`H'zZvKBRYs}S.F?~}h&@?E,{PO5;G5)`P.=lo׹ĬMg-z"K,⺒G(?r1+ 8uj/ ׬KZox~u re&\Wv%2^C*dnEǸ'_־=u<3p~Z eq8mG ٓ*Ҕ[>oDӭrűݺWC3މ[JJxlr,:䌩^aIy*[--DG/* pEzyOh^Ҵ}oz͖XjQrkA6Jr0 J$25_5`uv 8 A?0Wcm5-"d[)lB<+ᑒ9$ս&/J>cb캝qY"fRBFWڠ`vW[<:O;'5E{ҸTA J!A-=V'WvZƵe? VWXZY5y&gKk=>wq.Na;X<nOG5z>jX۫EolFYNR};eޔlux/oq0VJ~Ыêoea-y[3% YDsˬg[4I4o6}|ư(Б3K ~t1 OWi^}BDVno$DhA \2]#W@Ҽ;qIm<h̀Ic@KJ.2՟q5fIe$kZi׿@R݄)wfʪe l`1-^}cQY.$I6?dyQ!{hepč ͻ{- 9X1=dLInn*L/e]3W>{~u 3x]'vF9'-Ǿ)x ǣ[sv7j,p\$.IHJ*0WHNc)cWY"p;>.xAo.ZvMBL+ d,"+oXf0S[mt)$cq#9K0yT+HHUEݸfWKRm.Z^jPi`bV7>69籩{xL-.,/ʙ_z)9z֕ ( (>|Śn[8Nby͏<FQ9jLUnA;f5T-a[~ܾ>~$!ҵ}p/5j`,V#NkX#=sg 5ѵ `eXL#qk>*k4E&qXms2j ŢR26IZ<3=K_re!fmlgZ|MZΩ:\Iʵ,"!xʒ+|kcǺ\NJ}y+I,lK1#_xCÑYմk]j:=ۃAn?1bۤ9x5oO>X%6O{xz +}NssYEy-b^]sc/xO4淲N<:UOp\rL҅M +6A_eO诘|a85/'f>V6I=^jZ\BDQ,E"̤u_]sN:/&񥦃;KYZM"EM|h+k\>ņ iG+Zʲ*ȧ 8`x#.x/:5ogX.'Ӭ-t +q+cЅ23c*[^̔cU4+'ԯmD 5ԫjI1d*Ұ` #|ĺ3zώ|Q7txuoD%0Y*$D{I)={ ~0|5 I]5tb6DHK+:30{uÙoϤhb;F=姑u,*`(6G5uTxw𷜗76iO2"؀T~f{ßq諊_!I#^#Mj9xƫ~?k{O/T4SH.oW9gc` 0pzpAbq}L@]芺5. .+;k[.]uoFf#^ ToyFwxW9KEWoj<+ sui-( (>OW'*K{!I"RJ!3qһ/]?u/. ՘1]:D! Gjxߩ? [3 c]%uI574Pxv]iw0UC 'd1真*ٮd[ Xx$ XȊ&_|7Go%G%X)bAmt3:c8'՟2Xj2?bP(׾:UGזu+%-9y2.G=+웑0 <)2o(bX_Zj0AT#rsJQ&{3g\>}xy&mI%2[(pǯ_k-r Cuּ Ma7z{H&^x~,F}+ 3WYopiBþNq~=.k$j2O8ZS咹O<^ea5')8q^X^`Q^S<{˲f#9N)4Y\$̌} {ucBy-NzUj*:L@Ѿ2 ]k^IWPџ#Y?c/.iY#Y"'«__X&yfPv5_O@ũxvqF:uc->jm[YXhPf!@SRNJ3Ӝ *rj!uS)6s޹n zŖcJuT .Vlo,d%U=FxbO1RF=֋qzv6'[64q}к +8 `e%GݬmQI'ʪ\*wjNHX:cCc=+OoDm8>ǩ]av*1,y{u_':~Žw{A3RAhRihI.g+O%ƛ' \p˹c_ ]p} N+$?hpFVˋ, gW<^s;4~~L7ҹaҺIO:W,:Wd3du20J__71{BQqay0[FG11\MԲ!c`V?6:xBQ[hsec+ImfT dwOvJfC$*$P (H$5yGѾ-"-[2Bؗ,d̗,9güyk |I DҤԠ&"ƎT1^s( .Mje=ݭIFs(h2DcbP;x8"]YҜFgGNr c5u?-ּ-2,!4|XghSFGA4PnGʬGR3F?/&Ĺ9j隬 sԤ`(A.yU*\1珺3_<paGCI<_.g^0-yҵKk+Auk* )ȯftQ!Qz"rsNFv@4m c`YD ̖Qkjo1mkc4rG[vK,&a_U1\o]ni4IYa%! F ,f!߅^F4sګ9%xF  a}9$v]Q 4>OyS˗2+2I>ΥkX_]iB.,_t޲h]1[XuvY.Hn~tE2\Z6,>⿄z?GOtd}oՀ,9;UEÓ j7SޱMMd1ˇqq(f TkA`'Ὲ> g3Ai͵wJ ܮlx&/g_ l-vij -H2e (f=B0{g5_ ~Q q*lSI .b_)Bʣ%-!:6e&Lӭ` *8RDz>aYZY[0Yۭ 8>UAAZQR (($rG5i:6[HWyT,O<_goj1avo2B`sɦ}{ ׎B3pG1]P@:^;g> ׎kr"|T?_gc*[@j_܏h'<-x/Bt-:ImX,# @bG$\Fu@NOy]x/Bաhtb9 +w\'ŸIsNi-J@qWUEx?_k67vi}tO è5e'BT'ʧ`#D>BӮ ҭ^m| xD̈́U^{ WGE>X|G@fmjEZO5]6g*]#>OGӞ/ k[4:F񤀨0IAb;aEc־xĖ6:4]J)E 1}V|/xJ]6JyᠴFG;8I=ɭJ(0_ 4}.]OR=܋0xW7+?Щo& W8cI:hw *k.?T?]7_%GԼB:·Hԡkufp[Ab0Y7sI[rf%Pr ^iBdb@yχgmu=FՃwmĒáV xWwTNv|BVV,&- I$QL&Z{ohVjRHTr ʭY> i94}cW6nb-1scWQH0a'IKu5_[wm#Kx@.KR'%o"z7% ƱcvqshqYM/v>k Qw+I%h䍜9%&I$oSZ=ͼZtQV a&g`Ud0 +ЅH~g#~ ^xoš>vљ8rAeʀpJNs_0ދq63?0ڀ9eNZHFS,5>ў7Roq½QX7VG{[ jp!gcbXz!d9 ]ũxW;AzG"Imml$~i? v hMKp̒˹"` ub%UAd#h>+WZFۏe@秷3!iE *F:4<[#GWTm圳ڔi3E<*`A8k隞}T3 FBF:BFVޕ=kk6eb28XkEXQnn#n^O$!QE #d?0+isTv^Gž*Э5mwD7hlfVy%w**w±mYt4qfY08L{jڽ͆j~ѭYoYV<Cӓ^Ы 02 2c`;wqT3Z3WR|w m&[ɐ7 Q8Ɱ<1eRC9hQlR+®Dp8?Î1OUCp`9-康*F=W:ՄTm)Ek^4 EYe $yuQ45 e"s{S]b!3۹ p1;x­ܦѫ4@0rEeWmJFkFɹs#zVE:$_OSYTe*I  A~@DH'$OŤb[M7Њ2HFE̒TgU+mdr@x>-ͭMPvfUku֛)tɣ($$x|'xћrÐƞ|YtfT<uOWѵ^'Q,#yPO?mR:nҙS4n{w]xyQ˛{]kiHGwogd_M-8t4mC澨kOӴ[rC>Tmot?U']VK)2=%%5pxM~k|lѬl-PTOOH(@}: aq ~> ֗6ڽıkl%Z8,lաC$:kO٫W;qYO{ Ky?3`[ewrFNe#u7~5i^)kPwSula:ce˂AR[BӴ.X4`!*ȡ(?1 XKm-|D߿"<TPT$7=Hm!@m <_Z SQݠ划pr 9l]{_|{e{[\ +.uM+_<9{;aKZ]=2yNL@> c D] ͧ3Y-rEi⪣h.NRT.ߔ>{[_fkjJL@eZgσ~|)LrK6zEO 2" Z k8,e݉Ҧ및|+ÚV|Ku9v4L-_%!C8*@(?ɼi7^M%;:HCbGv6OE]kwj%eBve,>E'x/~j[ֿimnIu24ʢrj!ی!lB$*,mL˱՗*(H9犻]Iڍ&מIF~RXԓMZ,&ʨdޅ@F#[~1Ͼ6˥*|/sᏀص:䒟4Y%V|W3E}-spmY6_j?W/ 5ZTPoύ?VeƨUEfc<_|l1iQ@~>6_j?W/ 5ZTPoύ?VeƨUEfc<_|l1iQ@~>6_j?W/ 5ZTPoύ?UJK2w\Rp ''&%2S?XTycOcJ\GF##Q@<_|l1~>6_jB{nť֚&YsߝzOcJ\@|_|l1~>6_j}_ҿW/$QOcJ\@|_|l1~>6_j}_ҿW/$QOcJ\@Zev均cλ'AכiZ. bhF@Y {:Omݦ|EnFy*o2z ~kk#SuZ/,+R9*d̀|SN>fETn;qZݯ@˴+=ts}) 6ah"VjU6G'< Y z;hyb\,nwCp`p9PO xGoUԮ8k o4ɶJ`D(⾼oom6O;( 099⼓,K/rHR>Fl{+*D:(Aݟ6/i+{GDm`AyXIUd9Ʉԏ@kAvНo^8[V) T@om̀|^'W^[jtwOM~{W\¶ SP1oTOێȮK }iDK0 zU,,uS=WBU\z~G/[!s{&M)Ypyo[m~QsUHRĎ uP%jNr/D;്5]p[l<)ysh8ܡ)'8nτOF]# O ZZLqS.~kTlr8K\Lۉ *1xÓҤc_g?P+:Ucz/#~S_>pR-5 Hc^r}Ozw@O#43ō/P^k{9L>\ :GbC˟%nQlԑd"yRtUb*a`@;sKm:?_YxliWwQki#d9#\b@r __5%{m&A"~c0'9YnLNn^U?%[!Q2\|8ֆtr]=4/Mkun11Dے)24C%GAߎ IOFca?pm6k"aH6*NqO ojiWk4w2[2R+U|62.@ = 9%ykXyg*c0;X#G2@UF[GاtkGdٵLʏ;slq!ɑ9P|>(ʲM4v!Sܩbsv+ogopXp@FD`E$߇YFШ8WY`\b, l~Zuim h!@(XQE gm|S\YOH U'b@,*i}<#A,kº񞙦xԞ'h,3.G¶7`_ |S;WSե3Zi|y<ɉ' `w |Uj!qq\AGCŒkࣞ6>u[HVY2%I݌F5.A<7'Oݐ͍ PՋHn=c}{k~-,mϕ%DK4J!On|+ӴkM6-bȃw^"b|֞[蛥+~_I)'=-ռ#wx6& ܩ2 rኜٽ{avA!S$`m%CVQéRAt4y)4FZ#j4LѸk?' |x{)۽D',qa Y$rri )*#>V *sr;%:Ö] 4Mi~mct7!ch?u'sX)Z7)$syʱ$.>c]7LހfK]KMg&x']dn63_:vm&쓳ʘI=uRU'/{Ts9ΚJڙ)73VXB6䜒}kBH_#A=Xs;(U#ǐB/ 1^٤h[99 ڽEMޔ\<95}b2BNCZ-Mln ~d>?,.@r+n}:u 9><`[ڸ+{:Iw3=/TzxHr퐒=9{ýn-:XPH\c|&q7}WW  #1Ih{jL5($r>fvG-'Sbp_)Qx1ˆ;]Xٿ#Xgo8ǭJ%ȩI^Y |$ jwҌm 3ps6▭|=fݞ"zW|5j4M2Zdw*nί3RTޅo 8[kHA,*OSWks9S8qؕ#KypiZ6d;V*ұJ7VRPY 2pO>&]X2𶒚onBԻ#;9~"oך4#G,Lr7c In[m%%4Dhg;V8`zt1%ioSMG qm`lQИrqM~ 6*6*d S__g!o/ ^|Rռ CSe}K6ݙ!MGH1a@}1wG텘ɒ酼֯!I#lO ErW|!Oi{ I `@셕Y$+6 U힐wyrBm y2$()-.FOGEEei>*5o" }EY’2Gjoe(9$=2x>:ydy|l}O+K [mRtk4c9?:-_ۛj][ť۸;(!O05qhxPmU!E2Om *~]I 8?pP4|jϨ Ӭx[L> i.BB&1H,r"bv0zςtkZOXCȵ ]|@`Go!33qͪL"o%}דoA5R;bf'@?<7;H'-$[JdovIb `e[{aQXcޒG"7^Sl3u{ےy2y AE]hpWqE(=QE,EwQ`8z+]X(pWqEP`LRFpp}rq]iGasO^E+mk%mk%{EXv3k_-jԺSԤ>X1mBF yFOZ׌/˻ VkFPdWoC6f,»4»5Q`<_ͭͭh:bR\]\^.7tTU@';mM.ٕ)^9b_)`<^5]JQ(U9\nBDDW"JT۹z3:2# . ?FEIS"urר?JQhn~ c^$f3(^M.^~vfeM<(n A %H*!BYM',!3ңt zڽu4i | q[tY=n`θv/@}KW |;Ww;"@">su{Spd,cy FN1eQրW& 5XFA,^sG w\G^EO(XZiVo oFs~hՏ]znڎꖺ\͋t%Z) c%*?)`xk\ӯ4CɪͨS|s+ew}j~6~/2k+bb)I28{gܿ8'?ٯAռM{PkQimtK܋ˈJJAt*%dBT+|h{ .h/6odE&rX2z1[#GFm\]Mqn h,+T8$Cr kp_k+z1j^j0k75I%w+nUȧk>ЄD ɽ& ,} QZR AtS`oQln#t"3dWHs n9ڛUHa -'[4s\8 9sRXCJ<;s߈M#F?$r~_1sFMv߳_9{MGrX$%nuČ1%T~Q_\Jc|?#zr&eة[@⴫(,xQ>j{__u~Z0{لP3 ]ƛk' 1200 1200 0 C     C   \" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?ğ|MUjWr*tK=_5IFK=_5 Whzj:(OK=_4}_Ѩ >/F$DG%}Whzj:(OK=_4}_Ѩ >/F$DG%}V1D'M?saOM+ \uB[d2_Rii,p}Aź 't_ڕ6Hu! rwXP?uv7=S6S(Hl*mw.VjVZ(*PY4Uf< $?y~i0,<7xwUQ]PH_&׃xH|OS b{[/"6Mq1E$dN=Z'~{6_Zgҫ/tPhzhDQ@}_ѣWGEIFK=_5{t~52ᶫ}M8ێ<+ }_ѣWtbƚbj"٭ >y$ٿ-iwN?,t[˘W.%_-\`Tk1xgZ{G]BM)ⷌ\:. r_bzеnT?=' ?5D5 FU7ݢHIXI8Q%wc/xzOX_xCWa3nh[Em&%\3ŹdS|?ho^PH^v$ʸLӯT9M>7)9F=ٟo⟉ ԮY7:7Q:7W8$gCԣEgxUG$`zl<7P46<2$P+Ђ=:7Q:7W'&TgiuB=( O O¿yftS*"rѬ}H " pH '"w~(w~+YS=o E~ `j `j ۧ;ȭyN#J~ǧjߢ>AQp?? O O¿ʎy㶅E$(&`j `j k]EkppOrjpAsE^d>딐 1jIi~|_=\%Y^ IQ4ѧkaakaQ_5\/ZO_pC7`zq6]W% Np=:Wqmg,q2N"I)V(ϟ@G@_e\SuoSuo2.:7Q:7WEw~(w~+ `j `j QE|5?]߆ ?5?]߆ (>AQp?? O O¿(ϟ@[<_4t&91b\2WuJtGTg UhMKm?%-u8oh=O$=ՈimdPq5^jӊgVIԩ&oVe(1?Ϭq??ne>!fmI?ޫñ z+\Zic| W5/~KQ_?o^"?|Et\j+k_1_7G;ׯbn-E~ñ z+c| Wp?%|ĞŎ?Yx/:'#)ҏv7_o^"jV&ji1h֯K[1'oZoMx52dˡ&.;$ .~qO~ñ z+c| Wo!ºI~KcVKV7%_ ]1^U3㶹b#״hkbJJ,x8=k_1_7G;ׯbn}u%Gĝ_oh,LQmlyW3xs qm#$1 _o^"?|Et0ωj//AcOs%i nS:mga93`㧈~Zv6FNݫʤ#r2y%c| W5/>/kz|),}>il"pɰSOoiP:wu;SotQ(I_v7_o^"l'sW*^Miⷍw=~ñ z+c| WדO,Ѯl}|¬%I:-Vow_Ó=o ;sh'Umth "vt-\|jqۃ~|gm^-GSUҵ Uι[PG6}7=M"2\$0`?JkoZ~mgLj:~q1@y$`pO+F ]7C˩O/u-B9hcEY7L)N+/CM'Jρy.<5yMv5xyFA"ۿkW_ t-nWY_] V0.`>i&nmm<-0p8*k\麕Q&fİ6Ą yjkGx%|OZk efmX LEJ;sڽvխ)%D2!\ qn&:U#]\C>z=OM4Ȅַ1RI*ukR|kzܚ慬/t S:Hќ3*Gt_?^O^]$-D֤ӯs+iSy+t?<5ݭ.>4[ov=PΟ-=uyȖJ^K%=Ԋrkc<+?/<7E>]^T]ObcZZتڟO!.4oKЬĮ,K2xS-S^EF/_xHtkYkMRKM8ٽxT0?:t oxĐiNEٗX$}?7] E -l$ NOg%X\g F)O0 *.ekD5Wig^|w|It`.lҚ_.0 i7XLrxF>OwOxXƧ]o˦ I&s"OIvagKMA!K@o XmMq=Rm Q;ħp Ӿ!Y/˟\3\wqkבO%zsi9QѴ Q~cYNj? ul5 K; $.B#*29j] 4ikbFg-#31,Ğ䚻*t}V]ױn ,˽Q ]7LYF:kW D3#c<y?2-F&V^O+/d2Kslv~!b.t &;s#(<9 h'6zWv㐣d`@={U{˥c~.+X,o#?u5տцr67ו?>.|aSĶWpq٭coC>oSB].ssfG_*Bwg$s|"=!ņ{{J| ;r}+=zi?=Ct/m4W甠f?JSZZt7V.ўs2)%3!9$.9B~߭[-F;>JeF*\_f9kGP17z ?Gx xǢ }jy ou4K#BO^^ Cjך MF1R\K=е?_F,k$wiŹ6Ns/ ~05}F7ڶ\Z ?2GBnrrzy./XФy?hjbK13d•$l3G2xJ}.E p%:7#R}##^om/p}<Jox7Nj-umrfnl[*_! Dq*ǝIPv/xk()um'Tt{cI9`cp1ږנ]G i#VmK.TEpFs0s5x?ᶩTUj+)89W>}o _ZP_5goxsp1:\TAᦢ?e [~Ƈ5^[K $Fҫ u jbc}|!Gh]SeeFEgxo4=-]NNvF"+OlҖw16kEthmg^NNa52oQʐrZ>|CO\݀%S89Cj~&>&8'¦}&GӦV,J2Z@g8RHQQc?<77e׮PV>kDa.hw|ݰjĔo㆙kNHkݭcu1E-3G5[¿5mk,>ռ x~[QyeHa KR1]͐뵻V|/Dj"k/{7o;v>xcVXQ;8 r=wo/?5xztU*[ۥv88'қg:uI4U݂ P=r1^cSᏊ|Gs^tM$W7yŀfx35[xF:${+EHdTp%Hypߡ//=.M3Z}jQdNb)P #N7[ōڏ ~qfv ׎<#xWRѯ8_-7vWq X8 $hf>τmskJ:]-_Jy-)Ho;rmZ&g='U G[j7`+bDOKp k.NJ"^'Mʘ1I `#-[M6pjZJŦuP6"go:l60q?6ϊ|H}XռzxILFp @T9lt]AqsV~%PW'^YYhWCҶˏ)$d`1g}q5hV::z:c5vh;2B*ȯ^j3_Ck6Xٮt.&!f*1<,xaxutE6Ȓ"y[h׌>$|2"ekvd.Hڼ?Za ŭᓡkW&%6 dWDc@1|υo^gt[{n "ka22]>߇u{^(SG|HYJ7H׮!+Y^Z%%bRFs*zp.V a4Ӟ m8!cC4,];|i.<5[j{ %۴%p``TdQ_vPӾ5hf%%v{5n]4U*=sW|Iߎlo5 B:U֡tne_)A| w;}k3Ys@x_<% )|I٣M)k/xPΏ5O&]KDMF%̒#cImךl}+ iȲP?dV0 9ߥ3|NN߉n ep2`zWgmMFE_ZZCCz%B"r[?]?:BBomąַw FCmbvko_f⟧}!ETQ@-?Z?(!A$|`V&cm:Ty`ΠQ ?_j|GcSl/ 7s &<'g$whHȐy8' wx<'GuKrjWhYZL7A–zmԮv#4uS)wc$_Y]iZYe>[yMKMc,;/=h>bݿMD:m,MXg)o7r6_xQsB[-^s2ٟ1;w֪-kVz\k;TZk5ī}UyY'-Go>u/iWzsm4,hKa^|ë]#v!EӴn.eڻ$c sMF;eKåX%196g+;ko Вm=6[ ?eʌ&_c;{g¤,Ѭ%]T?*u|el  coys1Q,[N*KOx?Qּ=\K/[k.Xq>i<<J.o Ĉp}+M?6Ңf{ɢ=̹X\-:LI,/-ZvmnWzGFWVpyZ+T9Jh!=$ΜxM ᳁9?W}N;9±I4j~!B06vƥ;y3Gu3+H<?/|Ahͨh:59"f!ys1׊*~&i^ *)oM=4El)^;_-=(W ^I'AT`4jqkFpq(o?-Mŝ;Fb*Ki 'UO^ m .i#iy=_קc'U:X^G6!c.{ޝV+޶gt<= =3Y iO qחxbO|B_[;R]IL+r<N*7mjGAam5URmھx\H  W7gAu K(!nM:[xdȉF'*H8;{r)uq_[|/ma s[3ivE42HΠ,095V݄7w澳nH7.gR|o[Ksh6WAyrxX&$+2|1wczSobK_AsY1{5E+/xz{=>t 8f2ZG9wr*]w@Px_AES}:爵gKy/8ʢ8_'|l`ƿ%ώAi;X:=MxIJYHcsRZ;cOUl/ {V8Ԓe2H܊_(qg}࿈W:v= [Ҏ -Y3y#Fʯ|A:r!!4ٮHd$uՠX #2cZ?(T4PQNǸ{~+|AWkwSj0 Z܁$u\.{ׅ1d=UVֶА-g܁ޚMsWj$6q_DJbXrGC[Rl۱vwW7k/un|jnW;u>WừxE}VI`2,y 3z "x#h P(+I+}zFJA h vrmr?>| s~nU)dm$|'@zg ظaaּgLì\Ldnl5 >̅"[H7c+kW?=R'<)G3't,q }pQH(Yk~*q¢ĠN۠ߵo?T{𧈭5-iI 6T+fG AkǪ ũImq&Cc9z%X.qmih :iLG5QJù|cƚ1J֐ÎL|~QNsn<xW}WK{WzE2XC'# cΊV4AlgYӎ9h>+$cM$]Xw8JqsQ>ƛi?λ:)W<_XE=ݏv1ΒgF$\uOEē:yD( q^E &g3Cn~W{E &gC[yqu=.{Wc(P}=k; Zw4u}0?λ:)X.yGoU,e˙"Xs} jöV$qUk붢|Qێ71Ջ-oHfm/e+ @zWY\)y?K`햝{{XuAɪ?|67wc[/uP68RE$ >ՅJpxMZ^yo2`G<-IΝc>Ś .Q\Nluk[Ca|Һə .8"xoĶzj\$ylm!-7l!X!Gʨg'Jo!X`G8UP=?<-j$HR$.p9-9j| Z~kk;2[;̈p2F)[m}^B[t+$ve5 Y \YKwpBrX71C@Qv&gȥmСP9'*^h@QEQ'Oh>ѥg^hxy!ѸtɍA*N0A|IWc#O23Ywamy&grȁvohji6P.nnV8}YZ"/ٷMƘEhYmγzVţ0~I8zt~S}K}J }}sr 3D I#8;sv "X2a GNӿP# k HF.ྸyeiuxYi H1b7zV c[1jz̮o#’Up=NM[ӿQυ_,U/M;|-ؚwZߕ ]bik~W* եzuQX (^υ_N _@h}J-Fi0Xķ"8Ի*2Ibik~W(KN _G&>€.T4+bik~W(,: :GG}:4ZK"72RFHV4+vυ_N _@h_ؚwZߕ }{Q;#: i΀:+:LnIHFVIυ_ U/M;|-ؚwZߕ X36.=?[yODp Bibik~W(ӿP(1qa[dFo ߐ8*K 喉q^\Y\j׏w̗F_9r >0qӊ쿱4+bik~W(R~?ev-ߑnjWMPq$M[/uR*+M]E." "<+Nyt_ؚwZߕ ?4+M= %B&R4*v& ~QZ'iU`\i."S($JG %YQG4+bik~W)ihiܪU (_<+[swu=hFR΍!Sz~7]Z_.0M2v $$_KQ@+ZZ-O6 Д0g#!ӽb<7V:iW:+VI )Eؠ$Ҿ_0nxxƿ3v×D$eb^ܦKB7J]^(&W &h_tK r)k襻LiYXR!GVZAoy VVqxgunaYݳzv#xW4-1mlxb @-G`lv}E5Wwe8mI(l] =LjԭҾ'w{oMҙX*=Đ223u`Kw?rOUofS/o6𾩢h~5[2MP[ww^+WÓ%iMu[\,rGJKԿgs(~/oWv%wkƹKZ]2-4MwRyh#spy`΀z1?T]?~uO\o]z:oG:>!Sio~,cba!9 C徵}h$-ϋ'"i"2FPPad5 4 ƩK9@I6V6%~=k:NkZ͹IGcY'5zmiFi e(ƿik|C[k[hXAnƁ|=x__]>%Ԛ^^畓j2(!5/|/xڞۛ'Q~dl0˹ai4x^Úѯ-Z{WveWp;w=oo(۬,,tmljZ@4;Fn6rL#FP崊dY65co xfY:HKrYVy\~~ BH6Mf@6|fMW4˺6I~|.ϊ|U 3"4qo Q TP;K1 cɫL5$ 厺>iwWzlfܦ.K,m/&2Uo |\}kZ5㸤km:LoYpڅ[*$^$|ggZHVEƲÓ|/4Zl(HOC[XLNjFmEcqqi=@zL}˛uiYU 9מּxBöPzl\"C D!<  &噒o˪]\A`,qK+$`(㎕WW}5u]"-o2onunk/;$"ȡ`0=?74ˏAcqdaeqo,HIl|m|KA,gm^C_."0@?ľfT o>)J买ixo Ck>=` s׎}ᏆxVm=A>KQs*|Nqj|!hcMlG'~L_[B]m#6KƦu(Im0%a W~8xniun WZ{yF"3"l\۹$#p^|Fq\jjSn%UYYdUt E!N:WG?tZ^:||7׺R&N.Wn?%g絯:^ЭjR_{_\jymlNLyc>0_5E_KY/,aA ''g-[B톩isqsipjw6 RKyI^S}of1i6 /d֥.mT֍2nlr^㿋Q|5ymصB['KYFTmܗF;׭xk|wUbϹ:MPҴ:] Am[QO!D,BĒXe~!gvߗxLxO OXYZ/ iڏ, %ӥȸ1U'>Rk{oZEWEzLQI !=-f1, F9ץ{  xEׅwنF"6`@#fm~x3O{^MtWQ0jvyټY0Kv0)_T_OH!$iv[阮e2)S4nOK;PB( r6 &؋]%|4)s#vgrYٙĒI@8]CSE(|ԛRZjIVi޶Cڭ,ř9$$sV (((((ihQx@Zu":Dn-+P뫓{-#h\.er{VX[Jbkx+{o@5}03gb8/;ռ 7R{)"Ɯn䁮-E,/AN*?gOl]Ij56m7~T[1:;b?*~V7aU2ʗ~@Rqڧ|BMh5jEѻ,3(c5}"ZޟuhEr#d6 Ec7-f~._xNs7֩j1[ɭ-yy]_MeK(g6s4VH>kF1һ+ګ3GzEΜuU)_~jkEwbwm];] ~*kj-Ji8&,ɐ H};jZk4Xiz^kգF}LɀA1^XxWJSoĐI2LeV= =ݯhn_ ޳mwem L.Q CWsl鿳~ Լ)xYmEkoYJ2Ȉ{>m VtGڈ-/[B>ZCp =7>=:7*Ɖ*lt 9r_~I ϝq "BbOPm8B_NvϤ{8&/&I#Wh󝄌袂(0((((((((((_?Q_?4&_*'eř>є>']|.JbymVK1e]6K:]Jǹ_G,6nM S7:V:308sJľl}~,w47u1E) H;TxWD5? Yi=wggLF#֟+s[%gӼyC{k6W6L3[7 "~/|:<'u`4][Wb"Uf2d`F 硭>+6x/ioأb|`pTxl6r^4 zfchۤѢ#EK`ȯ0xW&յs63'`,۶ $ ?>Ӽ-gĹm}i`b"X؉ Y6+g#Wgk1NO@H:ay2r;|=7'~"ӵl ep 88= y¿iQJTa(:tVa Į_|GOїLT4&MLP%)WЭ G+P itu!p&hޥ0ě1; c|1?-|Auh.jo#$YI 6p5_*%Ɠۈ=5[kە4yNO[+~+QO?}绷/GO#q?1ϗ͎h ;.<]ǡ_`>Ҧ)*bGl'Wn/4=X{İ'"4h!xr\ I/~kX|Q{qm]@$&,W94]Wz_ǰk~bM95_hQ.m j4Lp)6zU ]O &|Upk^ovw9~nAWqG.m`l$qf]wdzO+gx[o|M&o[[baFyQè8lOK]׭N[^6qotye^G\t}? K׮l(P=;gxǃ)4MKĖ:ݎ]^iِY5B"n''$`3^/;i-Ú,n faH6gU%Wi{I'14 jZ,Rjwְj:~p}L(^N_œb]׈ ,$bFJ=Gqw|zKGZcx/U~ڍ\CFRA10 w |ij#.7r|9`W\*w!kRfUv ~ _Mzǿ6z6OM 7Sg[;.VrQVZGo'P׼Wi7Wq,GwrF 6zWV~9>6|eI3Ku,{E4o0J+6T:W:ov+G5 b(h~7XuR=ޟs ڲZ}r8?;~56=e_"ng693^K't[iEպ{;DKHB;4GtM6 //ڮ"YC¹'M$OG⏄4[xbĚenJnUfb*6w_j. K$'MKw^9[්O}xW滭qj|X1m4YYl18EuK" ]`wQ_6Nև_o/ηuFȰPhy\Wx_ZGt;mgB tMݳVe _<^>X4v|_On(Z啾6PEs6@ط Yᇃ"xwv3\`ӒCdwJ '-;54p5 _? .,4Ȭ|R7 )BʻFO[>= L8±QO!lmK=l˛#Ev5OG_UX~QxE񢠢A7O1lt! uWx[ò>#[YYf嶭H蛆<x<qxͨu+A0=+"2*/D+_}=E|/']!'F8L@ݻَ OY ռKy“im A֧#鷼Ki[R p@EM_-{ViWzдN4٬qJ0+mS?g@H|7oi\[h<8^C;[n&Mj0OT ϭ)bp5 |Opi~##{euj\` 4$Rs;I w3a..YeRC"/'ZotM O&H2HSKZ6xk>!Dtn]b4|3 itu>ԿTh::S]hо7EƑMz%ԚFxZs/–SPH9$v_sfNZ7/+,}5bx<z!l=W[]?]~-ryDd*^+a\|'}j1j 5m$U Jr:&#Ǟ&||5syyvlnvaaVv-{PK7k[Ij_*OV &LIPF=^=Kf1K5Qhql:<7ŚΩ SY6ee%i\ys.w~z"]_Ձ;3[ X"$ PCC0 ',OAMcC"l|'6:Sc& =r)zG_9xȵӵ})X9HV@IH7dqڙ|~Uӎj0odD_ElZE@yI4CU]?/'Z?/'Z~F}'E𽦷Aqls<vF7 O<ևh?O=cG>rKm-yc2`ŭ>e\^Y- @2u(NIVưƖqr2sԎq^MHh>if;$kiY[1woo#bHX=p flo-mۉl @*VM?>ԿTibd[L#e-C_1|5>'E[|)=ޑYir[i*#u7dʨxy@E U񾜶!cV7L̒[e'c$8=IX.{{TdVTZk-,8H*^֯jc:urok~9.]*Yx~$ZoL|5B<ZM6-g>w#>A1wّ9N_} E|OvşkC6k\z$z{䣵2횯㷎k}4hֱYrݬn]# x$t=kH9vGt5࿉|[#+A\CcfЈ>8#a'O֗e{uksqi6^ibVxX XD6FTff8N[)>6s,!7_&E0Mn4_@I*J ( (1G}2%+ \[% }T?k?.;"_kM=|>0X=/Z፭NfѭԆnk*L`%y4]u쏠\wEU4k5-/|eR۷SadUm[ TWs^>䡌ɰ\q_?gQUZWIOGEƊ2FFeaGyޛ?xGYaah2wlXn~TgXz+:n4:Ky#f˱6ryz=dyc Aap ^hXhY$𵁰0M&oX캓;w:s]~maF+eYw>p5P~>:$K3YL[靡xѱ|^E t?~yik7ڴid$9pK1p:W2y:tܤS#5?6:aլaKdXTOy{0I|1ѵ izׂ[M̲DWYC {װQJe?QmJP_$"߲`gVGIׯ q77.Mwq'<9{χ]૷PU& ļWQHg|!дNש*Lj*bLjrπxR|?6Η7WW~|*{3܀+haEgԮ~&o >w{`Uh;e4Q`<6ﭴwNX[Kq#)9f'Ö֞2&og#>P97_S $N'+kH2hoawфb+qPʥdcߘ'$׬Q@*>H+_J2}HJ7V&kZO]I47"VF][mH ڽbcGAt$5%[&33Lu̦]9wF77onu f/"s#_p)h ( ( ( __Be(_ZFX\2GBV &m/,!'[&{I3o+`b jSSxCRG$Cs2x!t!g ӒO^jvĞ՜S2w/_?(犻e:.Mm*_HaE18QxnGEKT6=>-bw{yËKjGy4[iំt\K[mrOKe#amdd A⿄uiw>c2<#>nw 7l㊹k7dkvr]I3G̅}Y|2?5DO W4=?ÖZ'yeAw{R zo k^sZ\$|8ُ1eܓY7/G&ruK{JxQؿ3)'9Z o^!\KFm@0NG$u~+: IM:?Tcř=IRucʼ;:jV=&aw[WқdI- !0Жn#F)Ci6O-%tY39;q$sך/XU3)5.U$ #Y~(GXeφ,|Ixwr2h=FFGQޅH/4{Zis> 0&>'v+{HHNb=nI7s棓n\0+S|#;wO\g HsڡӾ/x+V=LJ,Ok̶ 8Wh^#/ |6֗fXDLBF8O>xWT֘Cou5\mF0.<|l? Kom kV@;gqtu/ jZ,3jZ\09Ms~^Ҟ>&uR"ǪݭO"*ۉ|f>HLkh +ik۟˦s7۽9s;Ft|Y?MM6+iswGzόft_iz6 ҍA)[2ogo_iEmm[-Rx'q#&Nv[>yi|,ิ2= RĖduF'%y(ޛ>m7j:-,w Һ({Sl9>_Pwg({W[hfEQ 37AyeJԼLc}BiᵼT,n$QV#ҺY~xrodtwc`-:y#85.zec%nVŶXwW«S' >/Ҿ_Fd"UًJstoUJuYo_y_neBq[o^fԼ/k ҵk)!4 3CtۢL6$9;@-< /ҼSOxG3ͽŜ${p6AE|S־&h5 3]K^iLk{Eq$jݓ$i4lсVVk&ω ?ഭ 2IqynYl(fpv|+x[PE֜ s6g$70Ѽх)4]*+K+hl"c0(XnB3xWo-vV^$̾+:]Ǿf\@Y|Я@n[oiGF^_q6}k9·\yDEmo=jv&y!!#F2d.9Sc5_'=;מ^po"]$X|kXn&$m%dz3jZIia]ϒĿ 5~ҡ+]X鬻fYCW&4 \ĐZxSӧHv Wh˝}+{{oZMuwy?vkx>mu4uf  "[. U@Uv`7+ϴ'|] w^L7Al7.qy&vWvwpOuhY%gb2uϙ SN \kόﴻyaLC lyx$I/t?UI紶f \AcdХ/[VŠuXm6I@|ڠN`j"((( {Ii_W>$eKm6K;쭠@dȯ,.JB#rdKtV-HKo?ڨ\wE5Oa`.E1-|"23gVW? ~TUƳ<*_imq}k u+u6EVN;#T4Qih(EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP?0G+]_>i=swaql b])+;"47I `Nzo^=޽wgyy~|,b1fBW[VDڤSLLn"f.EY)LCjj4XOkn I\aGS QIi|ᛟj^%՟Y#tY-n<*2k׭eMD`կu^iqTMlŌ!#.w$5ڒ?5ud}*}N6P’ (5~^ҢmB6UCC:o\FF Tv_\Cd}5` MX1 9M|0Ń:_:PY,-0y^(^ QմMBš~ocgX\x 4vх'q>@k ?w?lL3^=?NyGc=NNJ"r19PLk⽊8w ]ý2fj@Bxú L'Ҭ⻱k.Z$C,}{aՖa<-VғVy❯cy5TxbڬxZiq5Lmt˽N2ѠFH'95#G׋WuCH*N~h׎izKHNx#9 U^nTiח藞{"nJp(@aۧZ_I'k/ YoOzS2F܆mʧ<}tOtE(#-hZxKful܉ e[##xW9+o?MmnG|5={ oUWU+i:V ۂ+=H{ HH[*#q_׀44/fSWA$9IZL#wcڥhWZyyw:}{"XV+tIǷN]2(@G.osZn۫}V.Z4QZ]ZPuHՆ95x]j]SH\ĖHnfV/.812 rIV?ռڳovC.YEI^ח? h/|H@n#j8웙3_\l=>2s1IIh2/Nt<;:jV=&aw[WқdI- !0Жn#F)Ci6O-%tY39;q$sךKK67vݿC&>)M joŎss_4Yd#Hıś'|dw2:A𭇉d͓Tkh+b=mg׫~"_[^z|oAŔ&9h̐HTvFyR? )O6 x n`~n:Www%]\k%uI# #L.yyRكb@ΓaK}K@?ZXKkc:Haq{ |"""55lre3sKm–z:cq/!nE,Kcn6HhY|l׈tOIoM*m;OX%K欆g7Tcx п/ LLޯ\[$>l.ܣ `׼)Em:&q؆9\ITn d音πd"n/f5kϲeE7ˏ>G8#4k4xg8ᗈkv:# b2Z0xy~Hl+ψ dԼQikeű^Km*"X#1&I^9^E{}$@ -̿abA~rQ>s f]i\X6)yEIܲ+\"|qUu{_!'mS5 xGҰK7lt^[bZWO_GpW'7?[\)t}LJ'0y#cJ~1ck\񏈬u1;M6iiXyowqX>0ҴaR:B")d&XJ z_Dk |-[/jZa@em8Tňx,R+  yߌe iO-7.V`25I$q11Sk,Zq?|G=^6jگ{_dl)b#x̒rC_QJfB0]C^Aͺ:fwwsqyu}ifFeRF3k؀8IE%Q@Q@7O^1BƺBG"_זF𽬐:H^KqENUB{.'I[ϲ$@L2FQz?| ^?5]R [c".w2ub}0Of{ax@EL_Np1?VZ=n2LmUI؝O@iIOGEIGI"~+Ůǃ%vܼy$}:' ?FOy>w2>{N~WV#~'{~Qbh`?h὿g({1G7E?O+v,7E?O(' ~.E' ߳d=(_߳d=~'E~'{~Qbh~{~Qboo2Wdϊ>5ͯ4}H&y5HG8R$ɒ0MyFc6sgr.-hdU`2H<↬?f὿g({1G7E?O+v,7E?O(' ~KiIvY kk ؁+`S H+][.N{;[TFVUsCxc [pZ߳d=~'V4 [k+X۫V 2x$uwd sk~'{~Qb+>"~h&Ѳ![uo{mvNIxc ZE ?FO?὿g({1_  .3LKOX[[hwԹ&<qw0,cr9SQk' ߳d=(_߳d=~'E~'{~Qbh~Zݟo.#Kzgh@m_S47_Q`?mb>L^b=x$ tЎG7>3a,Tor={+r,7E?_?Ἷg(WXo/$&y~ϟQ#5(_HM)޿1 1`{1_Q`?h>gJgbo/$& UM?NI3 XQGV'Ӛu拦IyR(r2HZ୎PTR}[t^'(GvOχOߊ"Bǎ-5 Z~!٥>Ɓ9d__߳A#Y*/J*5Fx^wͻv;b (<>x;R%՜׶;he }ұ/h/_E. p+8QsdJpC+*/5,OvZeo]Yy~iY36S*Qc5 IiYytj5͕ͫD#<fV.VKgo^zW!J/|5]SJk:ݺM"?Xٔ6l׉5ǤOw~.xvPK Vbܑ3NO9%rg+g~;[zO3L׆<-xgVЯ-d@K+ǜHpkm~MߥʍP^~ AӢV%p0R]мO=}dw:\2oRb-((Lu&n=o^ŋͧ*܉ qTL55!oC1RV_A2ޖ{u p*Z].7ŏxZ4iZb-m)+_oCC,2f8 g$pyq^m߆5|45E\E.TUXH`Lz֏?($:utX_əſÝOf_{[j6WYF<,$FVR8 jp5x7=j+o=$ʒ1 R1NI&JETQEQEQEQEQEQEQEdvV[A|ۢbb2+ŏ;+ymam6*"O%C^iľ7R7H-xKŷam$z-kc\Xب?;HAqդK{!~qcx>CqkN{e71G(8##8#V? ~TU@Ɗ-?÷PWe<-.uɡ;6+"4U \2W_&"&\JD_%|em/j:+?ᒾ265_xxDg/:5'6\W%`W4Wm|CmNira,k nK,+1V 83+/oQ [~ol}Aҵo?ou=E.Q[QE1,V98杣|YaX@5]HY_2Û-ȂDϙ w+/oQ WX3O xsGck[ ij9..&Vm qPx'ⷄKOE^'-|e^1ڣ%X/BxzJD_%|em/j?ߍ߫Տ>4:F4svi*Is& K.J@jwL ?mY?|fO+/oQ gk#*~^4/ z6{Vw׉Ar$b($ Ê|5Ch*>-kx㺳f5طo/Bc"sJD_%|em/jKD9k;~ VZTƿo뢭HC [Rw [/_cҴ۽䷆Դ-GV6ird:]u^>^2W_&"&+/oR֒O{Go:ω!>iz-0Z_ch1d +oSOjhj~-EehMstګmp@72W_&"&+/oU7}}$o*54] >(SwV5RV@{f~|a'Z~+˛zW662 ŜtW0rq+/oQ -V=f{7Igh[^zwjuUNE|^ JD_ԭcc\JD_%|em/j`y_xdMEM@G[UӷZƯi~Jxx(yq]2W_&"&+/oP^ JD_{Sou]C qg/tBM7m0 zw\JD_%|em/jgZi&[ue=r9+o>>Ѯ<8ҌhmԎcl}#`w2|6#MV?fzi׀Ss}ՖF |;&xGk_nϡW8yRoeumLe~ʋc*y{+scꚗzN&sss5*E\{$W} h>$ifKb'FpFG |[>Z]i)4 #X.H⸝d d`LZ^E=]ƞu)svVomlp)1cdfWZϋ;D>*棤!fZ[Mh0-̧MFnzSfCw_eefZL /qv>vj ]mC_еK? kz~wo> р2`J{Fe&-bYium̈́Hu:8E/φ ?gi0W:߉>3oQmgo{^0|K"Mw '1//'|K ClUڛi-70d̍ >S Yc=~E+k/ؽ8pe62%zs >k[蚫Z-׺Pqs* ,@־h'Ũ|U饃iԑ<mHXQHRJh| DZO{lMw+oxnRnGj:$Zk$/p";̧~#iO_|C>zE藖nމ>S(_+v_mϦh?-_UE9Y4i//4"&HXpu,3xg9BQ{PmmZ8ktB?R70>%۶ДW$O -QHf?7/A}ķEa?kaocgy{U x{I>9^+|R/<%uiopd $S4qǷ||(Ө}Ƴ<*_ioq}k-պݺ "lu bһk-E*GH5OG_USVwؿihQxRQVog)xG|AZE/ey)6uܧU?P+x;Jw\t?(>?@Zy.l@8|[\Fs/;ԭxZ4 ^ftuy%xr'̈S、9|A֝pxoxOcZym[ v`"n7m.}kwk_VO,inlqF%PKjk{ ]`wo9_N>)׵+dh6a6s #rI 6!@y.c| >&'ϧ3]YAih1ӎ֓|E*:Rnɾ'vzxEu2%ݷG,l2"yiv4 'MצM6ӤjBPc ~~_S+gCRe!(²Z}?ObS^vO话ψ05꺞xiQivsK5[ܻ#\DBYJ5Oׁ v@׮52GRג Kv=]? 6zeewwwGv@gG>-މx©hF=#xm%RpFp:5?o>%/F5 C z[ 1lͲ~kʷ"B)@F$Еo?Ij+,hf ="kw(N>w+R'%^/hWQXAùơ1f$.I+jg3QjΕc{j'Ӗ[q5[[3@dS ]vG2Ұ[Ү Hei%%b)x%Xu}+sP6k gO*(m KN-']~ķ'I_ 78]&<< ϣ_[Ѧr&N>(N>w+? n/xMmR{魼yK\ FiZ6B=]20~*JפK,'[͚\b0-A@2Ea枂>ӭφPZ[^#Q^vj[]by?*IJH)Fp3E/4QE"(((((((((( -?l5|G𭚤K (( !v@n';`.XrC\kKkg [ci=DcޯM;dR@aɮĮBZ99ep+O5o:/~TKnK=gϒND6J.H+<xoWУ};W*kJaiv|_XhWzDZ4iwws%Bc* `cfgG뫣'Mq#CKh9W${lئs_k'JқN)+ŻJxXdxL͊z-6:MO'u(׮m]ξtla;Z}g5vyOe-%s2ƭoe̊`^ak@9 a,^|*-<]~͙we ]O_n|9Ad÷rrIu-՛+|xl[UlB 8\Ԝ _J}wn~h>Z=二ڌ3l$u>.(}QHOx#Y @Љ;{vj:֣w5ń2 *%tLݱpk_^–] i~58c[t&3yn0K[r+Kzvj>mkh4WvEi }fvŒ&վ*,ХP[[I.m&06I.gCI'.=d οm =gx'<1=OZl;4esg'(kC 29+O7%jnaI]p"/\KL772K<n'i?4=^}FRYn&{yumfy Kۼ&ݽ ϰW?k=?ć]YBvkkiivC)u]N@Cy^5.u7+EnZDQ`Tm<Qa\<%t5Mj- ]q h|?k[hLV/O\վL1Qx*:^CipEuoug -$PiR2EocҮ_{w'aŷΩ-YvFYBTFT6Fo~:U>d:0&c~~OYxX!/7*_00=ɮz k1O%26T1~ѿҮ5\A嵄wp мB@z9]y6o>OPƘA]C{''|yb6|1. Mv `V|wxST^X\fU3F9 hdpd! A gkwV+O [Q\vAi=E"ˏ [O<-  @ɤBKIwЛy[ mrЌfwo$th&/.-t˨H\_6Ъ-sKqsuxFѵ ٶcI{idĤ6[~@Rq-}>B涿2ޗ8xHѮ&mZ#VWO-%2C 1e3>|>Ϧj,kpQi,Wiݕ1]nYgD+ HyYcE,"̀ u/:)ԛ]u 3fkx6yA漌#)dV_<si1hIԦyodkY^fJ.1qW\[1θ_v1h5<xڞvգ/"ơqSOwJ&oeP;[jF$vi^gS?t~_YVtGH^UD*ó)7C]}ڧA|o|Ͽwlտ?hx=u[H&XgHli' 4 XԒe/E:ly`#Ca,--K aݝe$bEP37XwCk-nG mS#{(y/+[RѼUhuKx5-;S m^1ro.R 8[!O*'%U5՟귚դ6um!gjL픞zZSHihQxHeYֿƹ-btKCg&5WZ883],,@Iz'^u5?Uh%[+zaax>"_G{_ q+=v{Л熮m(%m$R<#(cYLQ=*Nj| m-ēF#,=qݒ5/`+{{ <IŢx;NwM nUŽq2~MmJ-$ A (x=UWT_ A*8b ?.w~3u^7ǜs._>q4Ml!1N {|5Ilrt*KMk$IFNv{tA:EN}ߎ~C8'-lsx:|UHt0wj2pEmxVOЭ|;Z|+BKH RI]g=ߎ~C!.w~3o_e?!fs FZt#۵q$iϧ^ -˝B(.x/̹SZڠ+.Xu5P$V,Hs9$v4EAATKmN¹=#Rot}R[-V.ƕS y`3~6,*q&fnCclSθ4MTd$rhΆAVtox<kCl7JqvAw"\oe$-K|{vYm̛X@rFqEu ((>#xVc[ ٪D)(UGf車^!8Hxo?IA&=.ķǯZR8vq$&)a5'daEbǙUxw>~ףҬt?$pZ}>~(ƢBX=iY56k96F%Yg=k??oڰ6>Eෙ=?*r6o$y?߄ |(bF|?y_'2H!SG92+KHAy}6WֳksR-p)hUg?1ef{M&pO__cz f{KUly<;#w5_k6Gsdb:ͦ4Y nfKYu-u7mx 6:9}tQ]-wUy#|ԿhUi,cżm9|Oas$zޕEȶ)Q#yd7|[k^&^-K–zx?_i$jѲFW D3D3-4?wupe\qbdaX#޲~:g ɣiڦߓcwyEP߼ 5v ѭfIco+jޟ/,cm/׍|67mjzֆ׏x Q埝!c =Oோbu,_/tͦiXdXvB*sҴQ f;zxNԵɭ|74S<(o4L-] ӵ[/TԼCou? E w83,7`q'(Uekz~6E߉6u];HuGñm'^i%1&pvy-[6&>(来_]D$7ŝԯ;,p-"cU@m?~g_i<)i j61]HR]C!S!\1'=+hbj;:wzM-%8_5_sO\FQ1-w*m̒*N H#AϒOC_RIoqO_Z}Qgyo([GlvE}Ō1b8!X\m~1^kw.~LZe2[aI+FIe#5,r? iמvGm#0'$)ҵ]t"[kIXź8ۼQ:ލKMG_x>({ 4/3i7<$IxPЃ7$4 .RZH,rz4-.-|^ \׭;5qjx-Zk2oLi˻OxǞ*ï xO.#$%ة"ss⾗>zDXbX.-[YF!P{X c !pjDWvmg{ȁte^bP«z'"{oZxqaCqk4ۮI9(e8*x?A}RRmNmF334x*ό5cEt ؋-#NՋ {(=Ndԕc?xo|?E?x5fHft*yQCF +0I=US{ƺ{oncq6ϒuc45'f o>[y1})7Uedw}s_&oCš-ԓCo{(rFF쮬:2p++ТB q\;dƪ>v r4 cj+[߁L?⿈2^B_I$9y+\`B8`ڥ '+Z?ufn/*:(/$iz д_}/9ٿ۞qfkl,c$ۤrzQo|[A+;]όEƯpXHi 򤎇 [ )Lֻ : >Kλqnǘe²`8z\z6tEn7:GhzsG|4vE n$M( ryPQ8$\m S}CIVO_oaNz[;|𾁠j=%vsl+_NO|sKMI"ѴIҺZ Wi8|7u>? h,A4\ 9ep'ZkDKWOg/x~'}(na:ƝsA3yd,6KDaT85ݥoM@Weh Rx'I;]r]fOXP n?Wn[MtXr4KQd~7uac?foz%uxDuEi։dIRdcǂx~xkᖬu? <ޅoYH"%IAUxnk ӿe'mY$JNŧ*s ׿O1Yw^]]Gki3GnS4˺'Ģu1֥R>^x6sū}$5<˥>k;]Dh90) (>#xVc[ ٪D)(*njKh5;K/FG2CйRͳiKx^< de6ig08݂ԗPGuʡu=>|??-)mp.\YD@f0Ğ[ۧ[ gĿ #K}~mZGҴ״=utӐҙu"f|Th 0޻ AZ]?tRf5|I)\ * ?\Լ5qE 3o$d6C^?]kΫ6\k ,z }pm.DBgY[f:6?<a\aKc< ֵ}8Ẇaf(;Wd(O_Lki. :UKDVxbV,XǤ`לv#_: GL|e{s.y6t07M.UC^O7wk]WaDEq,`o t57ytc^[KvyTxfy̑e'97ǟi|i)4o ZϤ)^Դkm[_.1!8Vʍ‹ X(EQ@Q@Q@Q@Q@Q@Q@Q@7O^1BƺBG"_זF𽬐:H^KqENUB{.'I[ϲ$@L2FQz?| ^?5]R [c".w2ub}0藳=ⰼi HJ/'US8{lYޫyZEk}7V&x6NI觠4 OGEIEYֿƼ?YnAַ=fhy1^7xZg}x&BoV=Qn|눲He#4-M>?|9'IHQ ĵQ: Tb2AvOGrxKĂQDaP6 Sg6kDú^p [NH1$aOh/5Km;_f^FE ʡhؐIҼ;;gQ(trs($X>ⴼ5O״x͟E!s0`ߊd%6־_I+PAR2X :ͬ,Z}ild劅whՆ-FMÕbUq +¼]D%6xzvqAohfIcEp;j9( |VѦ[f;eֱ`Kui—c H[ Xg<՟|JEo OE 'Nvqx寍'iZ<i-gskVTBYN9 >xX}7RM2-=gֵM>HÖ[P*A/ u=[Ԇ_ LNcST>tz1h5[aԬ yDQiއ+~"ӟXaX@Y ^/t>{.o.lx0wMK|*OEkyqb5ϋ ֋j3S]Ws] qs I >xAOP^xRn`o vw;~dg㊴%zNLt7AnY/h570ڹ][\{mż<x千+ޱ ڬ:uKp^hֲHY9C\Fz7o^¿h> ׎֖p9mKp]mwg!G=q׉$WIcZJ@tTAqp 9 Sz8}hwz׆9]x㖱n -*mJ𭸕E@I #^q![?/\' "KZ=Z佲xI1ۖ`+sez/ڏe( VYƥo! @\ []=EVɤ煤iߙ?'OF_xIӴ2t;WA1ڵZ‚:}k..770,$dy犥o~:eKF.[7>PKYYHd}m!K?;p;^ _KXkj'Dè]5y#|i:{AI;M>K,guO#ʆ,J}m >ۧoC7S_WװRpi \5xO<)icz^&=_q*cX;__>NudͦGq Myo#*nU8fy*MkGfmSX4W3bFBXf8 l7zŏ_Kյig5ʬo^W~"3+m/ޑinO]>7HadL#]Llx<zTr[=M>MyΧ77iZ[\Yw&icPr |F*O;u_kLյ[spc?e|›gp7KU|UᯎƹxV:'t?O/<2oiYvl ~R1\σ1"QÛekvmkyy=/W[z~kdu1I?xmn3b~nj߃>#_$Sxg^#qc:23ߵ|/274}-,M+&.g~مf$7(. rx+WҼIZ};T}> ?^$B8.`iewiևRpdW?֧6Z1^Z@ޤԊ5m:>o4fg/ xq " JU))mV!r8栭ib(b+!G ]É\bI1e#lXdd 12|f$Ӯ7Kp'i.JJ.Oj^\ӯt9.N{_OaI38NOS/ o=?+ɢn-Ipgˋj_b(b+)QI\Vڄuϔ"F.$UF4lC ]??kj̶ ̭u4)"'t[i)[ ;OA+/"OA+/"ߋ!Ua鬤5ht^$Hctv2$Q#lU95>6kSYŪ^No[fIf]FJJB_O>$,:aR5fm\ǿb_]i㰴[ {R+ٚ11v2A4>?>?t> f[ ?>?M:if L&Urmˊd"F'eеtL:-c?>?5ύ>޷a:ż[g\ylYX*X8k=KK=OJSxr{g|ȱUr\GK_{O ^߅Tkj/6wnqhڢY]]C녶A2)Gq$ r1"ß|%xԼ?mx`t%vI"( )Z 4H״_5G?t?? 5Vm>]*on%/*v^«+]PEoi[Ew~z ŻC $+8&> G ;{O ^߅Tkj/ -/Eծ]>c(YbAlģ)p@'i]#)jVRjzl0ܬyB {S]iAj{O ^߅Ux7_jM~wv"7%`H^HfPBі?ViZ N),hRTcFr~L~X.hDd$Ppp2*'\« j"1@PɮE/OA+/"OA+/"@?>?E^ASih 67xhqCA[=~:x]XF2?HU[lap&ڎP>/cs w]G< o-RdHp{]-&{=HwZnU+{XY[8eHJ r<̬%o.ks{Gյ;Q, 'ċue?f0`ʕemcg"m/FQ_8x㏍ouiZvoxCH4GnѴn傐#*Ts_BiS^\i_9PWpџJ:_ ź)=_ :߇~y:W'ޑGEEapaV6gb`SnP NKkm H]1T$uXt'/ukǪK?- yT5ox{^ӼG-ˢidivqF\4ͼE֮nɳ~ݩл_=G۵/ukǫsUiwv2FӴ,${RV/!uT Kiִ|A#\?JG FERQ/ukǨvBmy'uMsL S[Yʸ1,UD{lwc\ߎ;ֵ߈ğfvOΔV'|F3MF2*Mu;~] Z+Xns!9fxYwg#ݪ[ 9Zo&sev(|AzͶ֩æiz!);CHAbppxτ+׾%:\xva}W΄M-EUgdP11נ7cz+ſo][‰:~{`(f;1qqkoUlt-';d1a07}i+oh++mcőY_!,eyQQL״=7Nmͭ^eQ 'Hzfuy(f& 8VOTֿo7/ukǨvBms~Y:e1@ "0~lIo#>?4hޥ[X@W3(y"W-ĿnԿ]Mx{QXK1-jj `wA6"4nӲmFTI{8,|'weip[HA`$mSIEi^6eX4.k Io5KUD+7 ROJ]}RwQz_.?WDCK/MeE櫧ebbS/:_ <)'ޙo~ϔFɞ$U[/b9{o" "J5!t/@ZCaESQE}_Zx?ºt}N!*-`A/U}s{ĐI9Ɍ | V?{nmcώ?wf3iWh4XælP; f9Gе+_*O?HMgks>am| | -?44{h<&º\Zàom%,~̡|̢F3]6:mĝ3L4GF-/ڙ[9N >SOzǎ0}66_+ +?ϟGicjIo h_i?tg^u}Eu}E{@I%o+|T  ѵ-N]OZzmn# In; w7t Mwz'˭4:5q*Ol";|X `(}yu}Eu}E֗QQ~ Ho94i+Ϧ[i!L-4I줖9@H2ds"?z$.=QGoGEME}躔$Qv'%ely-jZfhbxH\nS(.51~=ӯ{B4NjM {44i02tC@-Cm0193_ڦ5ܮyz}k>-ŐD>]˄:H*QZO k4[\LF7HOLT#ᇇO۽ᇇaUvZ#S O|O'8o{q[|?k{"(ږ7>ܙ @ۯ/.E*mGm@h_o__]o__]l?)i&ܒ6s8P9ֿƛNki{Z.w4^mwϘ)bW٧gx@S̷Riehhib$x T wkW^{qqd\M2\%(%F"rA]Gb:oj>,AVÞ)VuHfU G`6 NAZ?.5Z֓pZ[Ai<РXW CgWxSOu "ͤhd#E*F8$V:e2[ʅUO{Y~yc_6kv 4jXom͜Z} ++G!c#B4{;KFm^%#cU@O\(T^&i3i sm,,ʒр>էOm}D+u]:Ւ H#C7n'$q_ΠA\HA !hL|S\}啤&p0U4VTIM>b.D&ѰX8 :V*}JIMRak)9RFp=ȼ-uI/D׵#y$oq\#L(xq&o/ukkg3xտG5/ RwD=7&//G&//P9m(sR*QhQ֬ic򢜹.vc$%w!$nӥKl`G)*aT GrೌI309$29IG+}7- m_+{?9Y?}8vّ>D<#5?_oJ_G5/ 5uk]Rt:ƽcj:VsHQU<'#ԁՋ 9ZP/&ZUɑ{?+g\i O Z}֛qkudTMkqnIp*q!k)tj-յ=sSm6OkuoƉPH zװG}~7j"mZ_8.#vHJ prMPE[[5Ƴ+M>˷򼽾_99jyKAc׵95-|Wayo6U6l9L"V)F_ ھ/u}k(3yolG pLUA!A~8V]QtwZp-<_o]=3S\em-ĩ!Id`I$s]-AmogxsLf|YxJӴL! 5ti#^C5Xa۝oZ4}6RY̪.cEiorN>SԬ໴;YI0du#!AzoKct_O)g^K-&E`N1#S#U]8EogtM K9;h1 v(0FN\_G{ex,3 fH * Σʳ,Qۈѿ-R9DJఎI) r95># 3lVS@'hg@3N3Y^+V k Y*vZ~]@@ ieB 2##(8BƩ;/ne]麞.C#_3;U8;X5kii$G5/ j_oJR:^Vf2nc b8"q!rOl9m(wR*Uk_ȫ+2ak*W]\%4&ryY ̠ܚhCaESQE}IPXQEQEQEQEQEQEQEQEQEQEQEQEQEQEW5xnP 4>n*:Tk?jg4%,>J| C kR[fF= /PP:Wo/ >\-SDPUb/?BHI>$m[Mk>y'⿽Ȑ[nV9 [y*ʖ}%]ϫ~è]*+ot[ąwDЪ9?@<VozS3m9%w i#u-?Z?* *4ҟ7xh~6ft譭ueR HKu;fXQᏈzk}hoj}Ʊ\J`)-M@ >d8D9&[Fm"O2}ŏIo IE*=FzYF-ɏnxSZ^{/ִ/A5īs%{)>Bw@-k|-2 CL|Cj֗"S"eHYYrۺc[>C:toH?=*垍Z鶶6r!lR9[ 7?UҦfoZnu+sq$jds8 *NAio#HG*zC)k^k|9:+i|>b?IJSDPc(9 Qk6P[\췒vB6~\;?V_ <1m^] ً3}I95Q-6GQ\@;R?52Qpã;&ёTu=KW$_]4Ŷ1>-_0_YAmrH;B6~Cc|~RQ G?0iv 40ח| tT]?!⫕]@ ydɝQW?sK <^# Eh1ߝBO5`fу}zý>+ԼO_5,Jr͓/Txki\Wz-ЛEXS @u-.Y{MB W 1,8 W4A:iCR;UWz~7Z'>;ex~\.o[[R$Bc>aU[V>6(u7-r+mBFl0+c7u]"S[vIDM껁> Z1pČɷq$u__iV>HO/ ,|++]qեi?bwKTЎ?&E}Q-JW8.f9IUWG<溘< k;H|?Ek~ۮK(.Oy=sWOv:]ev[ZBF98UjܮJVIv.EggW@u*D<1y{gs^m (xCP¾57u'LrID@ܒqI8Ic W3WKg!/8џ Eм3zU+K#$4I?2Zm/&52T7wi<zrynѐ#QNc7T{w`F/ڛÞ kOxQ3i%O-7435(fPd)oJҥƘsٵqM=<}Gscq_]-JghTY5fU|'n|[e$v>Yե䮨oW>lpmrK(|$o?:$j~Y!.ot DԾNIќis1-9;$뾃U֭HѴE pG=꿮^ wBH? Iiz]Z[xb*{q]=QE1Q@lrĎFi+vV*+v,0(\¢hs ݢ*+v,0(\¢hs ݢ*+v,0(\¢hs ݢ*+v,0(\¬WJnn YA$pOn볢>m/į]1V[M tN?5z ;ztUzIFD.cԵ+ICj̓el=+CөbLIֿƹ|Weq0Yd zt.Eo_ 4i5,̒3`{U8ux[[K=SӮt+k2'FQ&X7O;Ox~e֪~tK-`rMҀpt\cWk4bn.}3V&,!m-6 t&t e 3mt{RXe˃TjiWwF͐1P,c8ƴA[GU&n=M*$޶&m&Vm_YޢXUn"Y2FEpzgŏz)//+GS[Y_ IR:,6Ӵ38Zׄ2Rëx>.ECl-&<E!15Qؙ;&Ϡ? (/??) M|Iidfc,>Ni6:mRbqr2*?aQo^Ȫz4O PniTjО7[jzZe#c:p*l뜿-OOP\Vc.Exuut &Nhq<̈g ][99銷'@֩zG.llO5#aT\zTg<[x.[%޲hWs,eTT,gxW )x^:yˢ)u-P[lʨCmdPtmߌQA_x]_WAL1˓!8+ɣ&_ɧ`bavڼ׎x<&$sک@|dڳ@㣕S.V]h1Z[ZdW`UP 98z+i3+7 .K ϩXZ6.嵲B3X԰~85hiWmwVǶXC#Ƽ?5| JOXx;Yu<#m{ʬR9 cρ> u_ʝKHm,v6JaWez]{*Z lZ+WzA-u0Mo5-wXK+N{:Gjۭ$D8]G h|1+tbV]߇'x(*= $=;ğ]~WQ@XQ=;ğ]߇'x( ?߇'x~pu]EcG~pt~НO.+,~~НO.{oI';7ShvWqY,ڍ# ץDk2.6Xn5VI4)762xt~pt~НO.:~Dum;Kfmj1 Ēx[S] K37BKqzI]]߇'x~pufj<)gE׈\jp? ==jfoh%=3-྅-ވpvZs{oIm{Bw?8?Ѿx:F.)/o.ِeß*:ң~xH^[Uѵ'T)پV#}Im{Bw?8??N'_>15㯇O ,W=嫠r}՛l/EzZMedn%e\g#&X=;ğ]߇'x]'jx whX]q{gqvXMmqCq G4;߇'x~pu]E0m{Bw?8??N'_P?TN'G= $m᷉|Sios3^Cq)VQ PH,r@րTO$=;]~XA E^kֺu}Aܓ'𩔣I"RYpWm.ퟨ?χ'xGm#{HXǩUrH Yo_;Woss t[jZuڅ}#؂^n1dJWkɭ;{9I!>gm隄֛iXγ“*2 Es?xV?NWx&_MM5?!*#o145 "E1PN5&c{iNf9KHh,@_?|X#t?:Y^T--:d]VDzܞ'C_TVy #EB#I/vg##*)/}m-x7j;Vuabe~mr|+ cܛ5gJ:$ֳ'ѐוO_/ׇ;r޼&K Ɨgl5La0dPZL.qMj&;xλ7?p?*)W8KZ:vkr}>]9~8j]f(խQ,"BO9X&@yҵ-\i}? O뚖0@QZݘ普$T(H Z^GW˷8n5޶Y2/7@ =WP0!mkav]W#$t81?6tI5 kM{bEQ,aTlG}c )hKL fP#:ERW-/x+ c>?$!/_#n9J"?h\i߱1x/,sT OJT{w`F:j>1<=qxŚ4.{kM{rEQ,aTlJzϻ7?p?*>$\tҴ(x?Af=VKyfHmȞT~C~/]B)C',mr^a!u.H]q⿏.v>·o4I&To]B5ſ_ [Y~+O/N+;7~|9$KYe^-mq]r{li/ OfBOMԯ4'Fı7y>?ؿ&9/_f-+ƻ|WkS'7)4Q B|߀oC_ϋMf[JV+2K(ʸ_bebnv/_xw-lR]oÜaxKx4oIo#$yY*Z?XזW :b\3'w9#];/;tñ~2{ÿ1o7Wͭr[tWߵm'Wz~q_Y&<2p3yKą^?~OYE-Zc 55)ݱ:d!S[_ [ſJoa__ZLWR׾4]-\c9<ע|T# M6iZtp-FȻ,`gbebnv/_xw-~ WrֵZxE5jv~)"qmI$b8;IǪĺO-t}1%PH8eaſ_ [V_וyd?5 _n<'seXxFbͱtcBGpp+]u[&[=.kdC#rQYI `d?ؿmn>%Q_Zñ~2{ÿ1o7G;/;u@|àjZVgҞ/#\^--ىH7=7M}[ſ_ [U~ ~/ >GRKMZ;hH=Sn{;/;tñ~2{ÿ1o7@&;ovn9𯋴Ju-L&HVRsW_ [ſcVkS)m$zօhoNf<?}oy{yx|uQxچVU#POk?v/_xw-bebn|)e{+.M L֊ȣ;6kwIcE]{xcH?K`ܻ%V2 Ykkip& uKM>z[\H)\60Ads^7xj <~tBcֺW5G拡k3k6}wՔOtM$Z( X-yv"x_[Oh+ kUC%|qy;W-?j}=ނ.bC)W^>)[K[X:_-'EG :+HQqp*ЭZGojqpt5 ]'ˇQ@H[2X?Piwv $}&M>K.>wrH`8 Ҫzvo,5Z6 "de8eO[i>)C -e:*Hݾ#?y/.i>%蚜Zo5³-ێ"KvB$iN2װ\mcmx,kifvUsATWNg&E(E*G5/ j_oJ5 7՞-/syTS!dDY0Rr8{t_iz͌_%L*ۂ.V]qi&f$@=>(o]&%_ X4V 2dvݫs^>&Ě <\5eyg+'2'ȘfKmKAToN߯1GX׬uGB=i*7ʿgdz?q![?/P_ZE$~@ y28 t=l뜿-!k7Zu止Zi%䫈 x9dVS ҪY.aj- [C L`/񞙠gϾ".s\xc OmmċArw:? ?JB}kPv__܈ xU hTvM&y߾xT@5KJN8ݲ`hFGLdd5 cQ淫hhMq 1<)5U  jh[?Zc-Z_ Zjͮ[xe?enJ !}*8g{}6 o.c^@c4r##lc##h z6kEh1j.dTY%iW$ >g<+fj:T]#11RA;|"l(M";=¦R&m:TQ<,#fpHMA쥹vL#U+  4Y^+V k Y*vZ~]@@ ieB 2##(8BƩ;/ne]麞.C#_3;U8;X5kii$G5/ j_oJR:^V5Cy})䵛8 GCK%M{ o9j}ug{4kH4F[[=L)RY+Ү;'1פ]|B:ϏlZsJ6HRє41֢ ϩtV-P%k xFv w /pj %iV]1\9*s+@55c.e UE핸vc̍G`>t/ ]_'|7׆aZ]}3Dbp̸95R\ik1m7}xW/<$r>gixLds(UQWS_x=wC|Ge~Tdž㿒Ind~+9qX 𞁩W \Y!-ŌQyp #wOJ:}k /&&K^.C0*񮦾ۉt-/:4c}ٵZ}˖T&v8@'爵B@BM)G,)ht ʲ(d6M['}+|5&{Y/`] `zΆ91zǦE|Q?7|=6mci&ka:.sҽyѤ Vm-ύ6Mhxs,n@1!sծĥ{]=S^oɣtCZ-B|7o͝| 7^{sot h7^7$t硭o &_(vC>-Ñ_>2F0A|㟇0м)OKkjA"_l^:Sz,'qi- Jji6r\Y!MHbtIdg8 >Ѣ=~4Ǣ)QEQEQEQEQEQEQEm>QQVA7xϿu-WsYX\hUsiȤs_AM5KPlua%ˑ~G ;Ȩ||B|uxZQ͎}ΧlcvZ[&7K$ЂM. ox]5ŰZ46IA b$! ּ%[iu}Od;{T}T; |Ѧ94#nnR 3CAQTL-l|O_M[]׺E޽qDDXTdqX \h/W]:}:Fڷ*7f 澺Zι6ˬD1ncHFZO ?Ҿ<4b̑YnIO"ov|ǥ|U5}ލjc5tKAorୠ]֡$mTåt5_i—VA ϨȸTmI#(8O >.қU%R rBE]fOoY146Sr;_pj/;/:b#І5X~{&ixT})lw|BF퐭鶺U;hG"8*Ug#xO! _Eɋׇ'Mv&eC vc/]GmAmsH;,nd`Aӭp:gh&OY|4ŶMzw''f,dϑ$FWdiEsۚtI(׿O1EüA#\?JG FEP9-^+dqt3Ƹ)k}e[y x.,nd`AҤ}g}J GS5_X!*~XtŽ{^]'ImS{v#VViu-o?'&uE\Ϻ0+,'x3X~u <Ձ;FKq+ÿoR> i~ Բ.+YO6N[s |y>5dmix>?_rڵVvvZNӳCv~DsڧseQv|gLjMkW^_ \47Vn&2%M}Y׆4, 6:ȅMp8>J]zŮ揜|9>M/z\&}_ZIy~\NٱalXYxO_zmDʝ5 .uKOW2XZخbV텲yp `°$rMߘmGG Ww8xD>Ɨ-n`U, 8,(y}隽 CQ%nt93q}b/Ahpp ֶeS[ZAo5ԞmđF| ~%>ܖy\]ݶ5b y=g`g8E}i8cׇ᫽VO\XO\͖-]֡^ wr_w;-SH4Oj;uj:՜,&ly(+y| sZhu261}YrO=󞇊 -56In3iJ#Ld[ߧg߳]MwN/|Kj:7ڮ_f,f rjkӼ%Σai:kTY֪n㊶Ҵ=6lc̆V W55EkUrMOE,*[83!aR{ZZ+m;Ú>.74sN(2ޑ֩:eL"f䓌2IRMs>oRž5[=aqvox^V.KҭAX--TXy'ImT}\A5'NbIӓ͍ww ۿ5~ "/|1-z)ZdᏌxRsxsIMlwM$77eE% >A_VZukrI6wfO.t#OSV>]څ~υ\68Wg[DEiƊQ5@<_2͑"/Ȼפ_\dž4.R-1DTP=)Cy}*j=SK.4y,zmD"YPA$`qݺsI?/CɵvCy43ZhCĶx-/4#Zmh/IzK?ry1vU y|L/k^GJ'(S]|\''WB+K4%_˒EEdS2xjOKE|O~icixMվɨ뺽n ^+XJ Q0鴿J M,n5=;Y&"Z&tR**z\wDzQ^9kOxfOqx;2  }>C]>-o/g7GK#0@CHp9g}M3(tAu_ jz_5(tGݥib+0P3.)NhV iwF.O+G2)Y&` *АK8&J+4ߍ>.վ)Zm/&o;oɧI576&[O6R26(k3[sm௉EƲUds%&A lz+?^_zꒉ>xOƗ -=Fq+Ox.t}A9}wMJ Yƽ հXv 0]X{ oZEy  > [{? # Ym^4"H$eW8,*<4Mtӥ6t?:wyvgn3i-mooioV=v~"x|>6WSJ{--H2s@럴k-ѭuY҄r(~^?zW͞ oZ~Vg񕦕u}?0ڳ)I,z c:χ4*+yEVY(e y[_kc +˾5|D<&ݠ%lR02UB 9'io-WU դG Õ G{f3#l|H ~j:6yir1 6UxedWVuXJDc%h %.mI]$%Ra=]E|/△φQQVA7xi6'+ѼMkC%8ԓ2{W6M(horl3?8\iuo/Em4 k폤^hn[fX2ȯ/ok?F6gīZ%-ŬLH@>Yn8>_v|yiT>._Qk+ӤY%95y5m"GuVf<z#5_<K2 uOOKҒMb '7 .ۆ2H#EC4ޣХޯyXojH niK!-Qr % /@; o|5kI-@lt8 ں >}[ĚεxM5Y|I]iOp#-AzS\ŦuJF-wūI*­9r#'h5ׇF|DғS5K}WO"ر/"FW'=+/nyܥ٥0E,nI\睤¹h0hipmeZ"褎]%2fluo٥%V%)!dW|Xwx’BKRMu?54#9Cm;I>g#xO! u/|:|#x4[<-l49YLrjp]{" U]l L8B?@-3 آ?Pд'ĚKN\kx2Q&cx/Q,f*,O#"xF7]#Dğ]^4:r}ˑcxB2a\8o0|04 7@&KotO&Ulfa\dW f~ni B; {K!|cq%?K|WCOk:vK[mpuJ֭ ݿuiZVMeYoL[6>?\V+4l뜿-&3A pk%ķ=d* ؀ rq3_{2ܞ!Հ6钺Erf#-] UռWf ɯ.:\NGg : :un%A_zChg] P847Y?v_oſ:֥XK[Xn^/d7E\YE[ʏ_WϟxB5ķ7Zma~5m5H^1<܉r[֏~^ ~,n]7KqȮm p9V%R@ xWD͆ci[4&Čx[yqwǦiZIvy`i]99ntk1<n28=z:Wԣj-V1pj CQ#ۮ{h"wF*9((k#zڰMmZ5EsS.<OqckaT/e%YAS x5~-6ekBҼ@ܒxK{5_"UYe,\H m?x{GKk=3R?Uщ AryKFoTiiUM[iW!r#>MgA@n+t&~0lxONY.YFYc@p9SIEK=t-NH#$IC]I_';iu_^}NWѴ1w-$(ƥ8$A@дoE+Jo㺶=| 5KORxϋ5GH[kHUfR' xSW| eïƦTZFigs("Upp { -H PU@U8ba\]пk)Cy}*j=SwO5qM5i:{U^UB\9,8`3]'t{ ,3ݰraC 0$F9:nzW+ ق#irIf-?J^^c>|0o <՟]m"!:->ZFSs`ަ|{SR=_]?G&)B1"ze夙ݬD5UD?+nq$[;a>cHcH3}[tLyf>~|v}ў߀ZM5oV "&& >iHaSNM{%+Mϛc+;.ͯ}9V-(icp3⽃Pp߮"Y?#&kdxٲk է-㹾,i-xvG00 xLuM_Cw-i0߱M 3mrR`S/е}Y͎-}&2G@%L`ētE&pMS<3gks/ˎryTQwUlyL[ŝ#6QdF[ 3߿?c5G&tK 4C[LgV_O&++[|~\>OGè"-6|Al\ ٹ<5]ӯ4 4RMV[]G1\ߣH|dR1\L{u-Ku8 Y$[/(&ZG($n瞹(?c4-ϊ^GԞPvRL<ӰGKO_D\N)+j]rkſ xDk-5OēsPIwwۊKGu48~Ϸj'OծGmK M󣴖+U<"P߀ھ|A][ŖZ4;}"k4@*~y6ץ&^[Pɐݑ~݇,6:bhߡ]|'ӵѵ BK?ƌ<;r ogP=JWvx5KivuĶ6hRT2_܁;zvjsm:#>K3mPI tNZ, 72Z]“쌅2nIZՏ4{Uӓ%WZ"L>:)ӕO|5xXӵ.6LV>ooO=7vxJ)}Ē[~ʑi>xY[h,QX@!10IqZ? |agZx6)3hmo>dls(^E.Gk;]?o ~ڮs)|ia#>^y͝F9J)݊(PQEQEQEQEQD_EY4_MM5sž*|%mk NKC\t-|V5w?xv (Εeچ I7g\ӭb+[h$ptwgiȃHԼAgwaekak[^v̱؁6} 15BZ=և|Gqnp.>ɸ$awɺ~D~ioUscqɥ.US3#=FKn;["Gŵ+5Tefc4u!s/gҔS#fdF BCXޓ~7yey,r}EEapaV6gb`SnP3Z)[DK-FȐV=ɻqPvz OJk?- yT5ox{^ӼG-ˢidivqF\4ͼE֮nɳλ7?p?*xs-asjַ-Lzcƥb}ZQ$*trn|+S0dp dU%?'%Qw@oUq_l]ׅ53K<*4Nmgk*8IJ1UV?ݍr_ ~:xZ~k~"G_~Y9Ѯ<k9uM^Yi䌅Dofg: =kloQ2C2m; gwyMڐ'%Qw@oUfs|c~mg+<間j֚(X©';@5njS_ѣzowyb^0̠G't<"[_'%TWV-}~HB_:Fs/^5SYEp`jb<+rE#bb!!^Y oG֩g]ğ8F$op.kDž+xAĖ}KTS,@;-c857g]ğ8GuIUc#^ịOxO/7 z&{mZ+`{[v3G3v *`~Ua+ោX-5&S>sy,sj]by/MiZQ0DaBK*mA$1kC_._CaPǴJi>s7}_'xᦿc#ա%x鄺c>

    Y} ?@jtm;{\y .XQe˻[Ѥ|)KtؽI)R?|r|N΁=OI8k7;pDRu5.L'qvo^VqɬD>ao-`h[SHʱ['=6w1J*O-yln]:6\jVogOGEe&| 3q *k}:7\tVdKF60M} +#VtRi# :ë]hmΩ.kHtr\EѓkV>Q?i_RO>5Coon.Q [G%q2![Gr|Kyk#Pa4$ZS1}]#B `08]<i[H4)Ng6V<4 hS\!2<7O_ZV'YPյ|_iT|&A-H#osw1^FP_y4rcj:KV3kV:|>(4}h_iSHm)C8Rqx[xV?jZG,"8'5E MvNk_M!4!.^9Aq$Ha`c@׾ZoI6FK6JjbE: fҼ%WWn\\ZڤRM~vP sN.]/t_Y!5 }Z{] 'ZFcmub`F7J;VῄṞç2O)ıZJnSyg*^4 NA=O6Ŷ[Kk<@7Ӵ h46324fKkTeıZbF~5xwv Ӯ"կ/b{}s0R0ZMor#i! j:6.:wkAl|,HA Ce8 x|3-V%eIxubaq־%úUexSrQ! 󃓜_A-hHZG"@ qqG6y>Zֵrq/Ǻ,tqݛX.roFedU/|m=NZ͝߇n!ǑxPG^XitK)l2Vf [t7-U{m9(on "GÐ f}=Lr3`l(9,Ğv_=_" huYcui)I!aIL )<_<.,5ɬPɓpfQU~_ע!:WNm5d<{PFsۆkI>\W$yi!ǣ*RQ&ɧu=ڮYZIlہZUSNmD۳'Ibhᕀ##n2ԭu!$nʼ^_4MN-{R7MYZ!4ŒGbk.V]q;yA*р9[sR*U+tKѓk k #RG5/ jX9*)ːf2H]NIMrak)P9RFp=:X/f rB }M+y{ 8ʹ3C xԔr./ +i?2JewpMvw9/gxbMC߅.Ӈo?ˍL3^ATKmM]Z_ԝ5_bX{x8To~0 ~5bB_4(I+hxdpA*{ 9ZC. ᄞ7McN KmF&aDd;T g_wPVoKMoPҖ f1M,@3Zsᾩ]k^Ma'=ڊmǮ+:Mo3 čXy<UEBHWcU H SN]e^eMh:i.u- ݀$ WOϵs YG*ZT[k$M3$aaHMzech/|Ck]O}NdAȪ: g)XgE,UƕMgS> ek+I@u4$zsٵuj.t!-!İ(m[yWzp6|tЬzi m:I_pcp?u6%/j5ԼWncH-mb0; X'=}g{^Vk:l>[um+Fї2 8 ޴(wm GJ;K\f"1 *H8qYoM YpgXTXcԳ>ֵmJ'WvLNIɨ!0VIdjv;C:qƐJZ[XURlTdj/%J[K*IQ@g5O i~8t[- Mu.@,)7zDƟxXՐ>O{H9$m@|h9m(sR*Uj&E* ]23.7%O?KmP_hzhgڦh43uH11`|=kiqN4Yn ֓D#~/9ΡQr-pqaǦkGKmSk5F.mfH(w"U st3_| =1\OM#88(,åudmx<Ď]z`n5>xwdЧ3kiYxѸ*pyiFzΏVZ&/@4u6/5ՏFU k|}"Z/mƟ-iaS](%_ ۳8 mx^t [+Ghd[O;AF+ή|3 M7u.,}IJ̓a nb, )g4P/+6uoROXt{' X |G56M6{6W4|u[v8 K^/K{k[zGq,\*JWsJ\nXlBMoP}:Im=07=4[?iݯw:ρL@n/tѬ23ۧg Cγ x 6dhu2xb1ɱ٠duX|dW|pSᏆ{R⦎غb^K BtU%6FN? .w@]4v}T62A'TkL>ږ?>n4[[IhgdT2F0쬤&IhZvm[Kaq:=C9PC/9= R E V~n=#Vq[ A&owMO^xŚVM;Tӭoc#q?x.2)i|wg~%k'[iv,cv@-㍥ (K RI+# }>iTX%@g|skW~\PZk{GvD«erۺ筿 %Ik="EmzEe2HdmAupv#G>5M}N)`n'!u}y0Z.iV._O%!."#+[ &uoZDz-4A%C9byp nlu^zŻ~+,=Ru 6O&!![s.N_hh=lDZXx>ͪCH-N%xn&eXQhĉಁs<ٯuI OϚ3$Q,|׍|sIxQGH-izg搖\F޵|AgxL.vzƵ}*[ %u$nYKןw[Tui7#y| &]qNBŏ٦@}g>MZwl9 u<oM5IA[t9A%x݁3Cݶtt(mmx4[׸q$?)n ' k7[x4ˬZyqjkB;+Nڼ+}*-nOxH6f('i6e<{s:o mjYE*, Ado;;Z/"m[:(aEPEPEPEPEPEP_EOdyjJ7,gktPO._cc܈q^7x;.QK8.A4 4J'I)uO~etkqxs|@lq}^-R8u1o9IBaǖF4z/~*xz]\{}F;8b1"b,cn,F["w>Bۛ8rsֹ]Sெ:ŅW6q gZhyJ#1j붿G/SͧG/r,o E7տ:h=1]#Y`֓;૽ri.]C2_HS\}Q>]Y]Mcm-͎k3¬kydWdqQ>84=8\9ai9ʹ sKp-GȺF/Ǻ?//.)0UW :F>\9HjZ~#]Λ ,!T[3/ˎkڴ:iu|KHL̠N'zJѴ2k;T_k>h1xZY][ądrT\{(-v[q;Ě!?puL +/V.Ō>$J얛M#ڨos^N)?ۚtI(Xw?aQo^Ȫ:ZZEl/.fb{w֖/u/ o$Ě!?tT7/jߴ2Ɖ>#(WV&` LHc*9tU[?!⫕]@ ydɝQW?s3otO'~b[K[ ً4>$ý>*ԼO_5,Jr͓;$&>WMQ%m/<0/ (vO4 2ˤO;A~so{DӤ`烷=xM M[Oy/PGt :q 9>֙h~ҭ-RuH f_ P ;7QFֿuh;_oM+xkPuψxP_ܤc3[IW`o[/$zϫj22x .,mD8DkƏ 6bkd;\mF H)omp$5j+)v8iί-`Tky+/bl.SZ)[VQ,d]B"-ż)s )>/E<"8NfӼ9#sJ˝aw42!N[ !|?a_ j:[_$" VnI8$u$1?+[OX_HghVgNk^*EH%ܒi-6UMgAXۚtI*O^9<7hqp=[^}m6^0ԭ4ievCrF0\ygi8=W_iRx^LFO9Y ܞAKkW6#Rүc1\\j4r^X.s{/7m%ŽB?#3'07PsZ<-Nto%Ssc, `Uw;_W>N/bmx]bFDmUV'IMו{$#K 3iPƊI=?S['/lL@,xd3x_\W=υߴ)q{mZOX__o 0[̐9yf69 x:^7:mkyR X0-s74Զ=oyÖlu{nU0}j8/K#AfPj;%wd 51uu?j e-9ng훡 {s%2G y|_|, Hmn!-/wH1OOjk gQ_4x6xcš'<uy5CO+,Ŧ7ZjA %Dxɸxcb[? 3!-a"m.j:u Y.y:?{+ty t,q^Dz|K.o=6.<:xbv794tOL/_JR}(/IF4_X'^]W^t6"n$ lM5WA/ uo..-[YP\ʆ bRN0d 7pyqL𦺺Ki SVi-E0sX})th+sh* f*@=8~Ǟ)xu/;gQC{guui6Š-\>e;<^>%³kj}fY7x.mhOm.2OKd߿G4W 6 ]`߻m9Y9mq]k_XO]MHc\'|~~7ѿ XGf5F=Յљ w[``ح|-Ij m Qk>ҟdB#-A mEw=~MjQ04>[;+VU[k9>rFNk7Hk&jD/m5ͱb^&E%IN;%;=+/nyܥ٥0E,nI\睤¹h0hipmeZ"褎]%2fluo٥%V%)!dW|Xwx’BKRMu?54#9Cm;I>g#xO! u/|:|#x4[<-l49YLrjp]{" U]l L8B?@-3 آ?Pд'ĚKN\kx2Q&cx/Q,f*,O#"xF7]8~,|;?\V+4l뜿-&Qv(3W?\GSDXܫ2}*}ZVzvr{}K<؅GSJ\xFOɪx:HoPoCK%Nv"y¬ 5xNu CRѵ'TRFxw[F>t8 N0kn>xBƁ_] tK{jxdLnQ*hPܕ#=c3ß,cpgӖkQX/8$9$koT{w`Fit.}KS&,rqPWEnfWo]iS}Um ]kd .f 0` $pk4->$l5}3Jү4x#l:F/B?xk?┞.v oy!"G95Yr 2u|}࿁1:Ym%H\®R."T'PEصW!t/@Z-aPǴJi>xWgk׶)ԴK]}-U.|yd<^>+洯>^xǞ}R[xp8Pʜ`e޹iZCqU E!"c 3MjΏ75Oᨍ-嬋s,JWVh!vߜnW_O|*Pum3|Cia23&27Tt`LhÌo٣z)AZ4p`v1\w5D7kkMBT} .RHE[Yc8Vg/]ؤ ŵkg%ciRI\Be g(,r84?0_ >i? 5|M AdK0cw6D17:52AchE 15 *}R4UƑoPD=RNO )Y.׆}n.naKX Ą trgim&xT\n.;ۣFc@![5_?SǺG.>(3O 1+{Hmx_?B7k&`7# d;=/+ugzfRլus&8P$Fxʨ)5 ּ|Ee|bHaySeR*I<53Fź]zUWPo14lyjty.pp3BV1o:7|si6Ϗ.W%0:r\繮ğE;[<5MgJ?M]^Xm"Έ5gTXExBWwyuHK1,U&2npGw?%|Lt/ kYs$s :{!Y 3CZ+]w>k=GOpQ}ɰOjm-B-Lp@fXs9~䚟—EۢONAMf,K aھzKω4=Utu ŨiP\C6˸PVI_:àw7^կ }a*dcE8}~̧-Y|/65|0?e{ wdaq^w%<9m;O׼E{˛Qv8X/4 ._IyDWqybs1`Af~ICy%ZxT]OI6V $h̊FJI+W~O`t年dW 1,Nqלxwo3EIcSY!ڽ(Lvrwp[H5o^k]2;3Gw>{̐O ۥC&Aю]ˠgĺz\GuhOEEC$ah# 퓞~N g_%H,,GUm/^-d$oO25c]8̆?,ܐ7d8o~gخWIVe_Z<ʣt0d;6{R]`mjOu&'cy!I6u&Zd:]S#D %'EofD=֫j7Z ]H 3G#P< ?oKїant]ڽ`ʸu:Vo?xMѴ NR-e[KE`(RJ9\sлlDg2V#$}֡2:kYF0"TX_)H9-߄ h:Ce} p jQ$y PҼ7.]G Ǘ}#U09@ 9kx?Y~xCU%SMpQe鱗xU9ѯ YjP_hWPkiZBT@o.3`uVU}ǡ_xOÞ/t6+ y+q"4^+WƇ[xCzzk̏ylM2 !R nk+APuegdzm%%K? gQȿ⿀WzOx^!M6;Cd}ͫZLC$EpF$? 1G> VwuiڶwO-8R6?( 6Z|x<gx§ZӬdCs4wG"&$bXpk"4SB!`Ϊ"$X(94{N V+G=vk7+]sykonb]l # 0+/}?/`ծD,YQnVwfꬊ;uK^x)<-k6Ksi$e QU.Gr U4:'"1\'jP''(jڿ?cM=gxcZEf}3I h1v0`c'6u?=B:bxWޯxՒK9$hJ3;F1o|LHGӬndzt֎I!v;fD|ۃ^KϨ+z>PֵeDCjT.=r)ƣ&xSе]VJ5+ lJ"g5Q~__|?ƼWtG3޾$vh@z~_G؊1^ XšG4X!^QB}3[PV-͸'Ҋ"TJ*1Zm:oUt$ԟP[8ЉD)|d?~F=ZIMn}BNK+Iq:B@. OW%|#Ӧ6;-朾,_ 2o۩~Jֻz {7ǯ:'A1x{H,4ۯj&ͳH@K(up]ßluioy}SZx%ci# - .Dkcko1s-_ch617t^B.=XO1I^6?n4Q^/jt^Mg,H v:dpnǸ^uOSj7"ӗEϷ҃o6vc`>:E3Z)[DK-FȐV=ɻqPvz OJk?- yT5ox{^ӼG-ˢidivqF\4ͼE֮nɳλ7?p?*xs-asjַ-Lzcƥb}ZQ$*trn|+S0dp dU%'%Rg]ğ8\u'uMsL S[Yʸ1,UD{lwc\ߎ;ֵ߈ğfvOΔV'|F3MF2)tv ou]`tyk2{y<';ָseW~i 9ZC.E e.p%"bQ+襱ZP+aCH SrEƫn?36FG /m+/k=.}i-qx _L,8qf~>=î4,bk@ٚ"&vwvk*Iis+f|A?IҮ46\i0@s9-}mjQE!DN h~#[ 6K,u#ϩp `L־WTUH[TF0foqӫ5zG[HfH ɴ0qW'*k_7jC)g]ğ8GuIUQx@~ZOy_kZk޳*c d?3_M_FZeex2y*n)g]ğ8Q]XLo! |qzWMgAE5=MĄ1yg uZw@oUۿ5Qk,/L߉4wM6+^Fm)'{gԨ8q^j?k-t )5[\]ݮ<0h`\Gf xQZ徛k%iߴX"u(++ҼG}m%v>nn;Q[_ o7Nso3C$ڋYg) m^I5yڏKn;Q[_ o7Fd??tzOZi2p^v3z@p;FxWAN"k xne\qs88[j?k--?=w8|Ge>$}Է~=ceq&2X'w$.-g럲 i=ԢU5å/_BXbp]႓3WwOɨ.RQ|% $Ceem)[j?k-K5|h>߈-n RyҀ߂p j?k-tvj?k-tvj?k-tvj?k-zҁ- vIو @,_EOd67x'##u)"3D8.9* 8Af?CѬ뛹ԭ5 K=2b+o-Ov)ԼiOWR3R@P2IE9g;vs_Ade<m qr? +O |M[ \HVY#TN2ç[@¹qk x I|V܌ =T^+oJI>\:@L62A@t1Y777 I68TVTY jmD88qW(*}JIMRak)9RFp=ȼ-uI/D׵#y$oq\#L(xq&o/ukkg3xտG5/ RwD=7&//G&//P9m(sR*QhQ֬ic򢜹.vc$%w!$nӥKl`G)*aT GrೌI309$29IG+}7- m_+{?9Y?}8vّ>D<#5__oJ?Կ6 խuI[(QYw#FW RV.?+g\j ]CH҄⺹h6W&Gse2Q@¼|(=tjeI~Z>]a=gy0*yz"sMXTMcYeYT7 ̙M;BaZBEì+\%J!KRCxڷu=ŷnn^+;k Od`Ur3(aY3ͰZ |]xP'{me7p?"4rDQ?y6*j(,QE35*k.rĀ g6 &g]aS)bRXZmRfu,"w8$jG&SF;v#]i ΀g4Y^+V k Y*vZ~]@@ ieB 2##(8BƩ;/ne]麞.C#_3;U8;X5kii$G5/ j_oJR:^V5Cy})Ȣ* ( ( ( ( ( ( ( ( ( ( ( ( ~T5/;SI\JH޽/tW}ZN& K4fB`3^N ΎyOf+>֣cojjzͺv9V i)elxZJS\Y Yo#fhc q6_k~AHu)#U_,(s+"yH'| xHxsWuYiIdub$8|񐹧nMۏ k:\}Nvmri!\uˬ wu-:_쫽QҰkݲ crY?|9o lrB}~|0V$^!i6z_-SLZ26Z%btzUo|?|=|Wgk zr^MK!x_10HO؛|ڏ6tD~ixOlwc|<ƍ'ӣ⻓oz~IfH#+ [ ~1_C⋝bR$dy/@2Dѱ`1^8i-Fմ˭/PWTXۿ7A!Ԇ㱥?} QD_EP-Bnn%H- $UdO`|/⇇kIm3V4SOybmpJ*OM+ۅ+Ix[EТM,4Xkl,, ǞoޟGw?c/uhVƕSΆImإ]ؗfs 𮿡&c~zٖ0o0@ {]bxKCBm4]=4fNr ^6U~+x7_75oSRfglur{7V s,P[,(1v@w w [u W:-4ΥayKXAu.n`m%Ls:g=};A,>e?%vM6P#5zFlXy%`$듚.dחʊo|u/ _ȌxAGf2 e:ω+^xKԼgiMvYtIR$d]lz&ui:BUGP:@|#hh}Z6ERܐ8'nT9~(|ks0إ(_2La3]WN TB՝;N,a b8Q*=g;Cy49sRI8++'Qd+fimn Ȏ% f?ß 3~Ҷ&/ _^K+u4L>ؙ َV2uoey x'h#gN!G$SU)]i{Unk ӿe's^N)? :?s*;?-o@SԵ{;[KHEL[ocܥ!m㸸h#gJ<Fe[_|8?·~%jײcieU;xNjߋt|gG$SZwxGZ_xcKwZRyrۘ[=jұ-7s_}I.?+g\j յk5 ffԈ%ǜOq![?/RYv(2.O;m}@/{H%A0I-Sm3ZZ*8Wo嶌s_X^VZ]Im %syI)Fkֱ7D-0$c$#62WBbj>N񟏵? |AuNѧ^9渼\͝I߉toiuLfZM-nZ;F 9_Bx6\xR :i+Ɗ9_К鴿iZ%e-mknžU@Vgmx|,,%dFIm x8ԅa] ڜW=x\vWz.<` ZV$c5X :eúMn,bEˀ;7QZeS[ZAo-ԞmđF| v![oܳETdC7'sէε}?ҧ[ f5SY0t}&OgZ<Ȣ qY>.SZ)[VQ,d]B"-ż)s )>~ԾV :N,KM+.vFӰ 9l7|"ᇅ|5jo:Nogq~S'QUVf(\? `կG WZ`߫_3Z:O \\8fۜzT~QNq8=)ěkiֿƛPY|C -O/MBwe$Έ^3ElDŽt&o5D5lfSH$|ÚCpFd.tR >Q+FKYגix_~G7~2Jx[YBX|Ҳ$c՘ݭtK_tr>$NCh y[V[.OJɉca sgW֪tOq5?h ϨBs^sӴhtmue.Jwch 9MC:w;tl-fA3lCf0D0'#mw$w/'[|F_xrh oyY32}JFuwִ :Uuod\tqTv8[ׄ2*t87|S>v6:ux{.EiUs94H\/Q<-WmR}=q \_Ql5.WI:y.`lg}-.o%Ó/$q+Z'n+J ;9) tF٧ tU 9Zo&se])  :oi˺kF8~+wa>%Lz ӵWrF x͠KеfXtm.D]&I]22^K{⥞zu;/.졿Ky^Dn*U0rFNE Q=kо&S?:IcyCa8:'Ұ{jDž>-ܲ+80bNC`W)'j-4M=[Y隔 żQI 7o"M4mJ*I$R<>c8 RХkf Iku'zM-w*m#]~l$zxGĚ-iZUoյvq'PAHf2|AxKOk+"K-RfW p2 ^~m-{|'큢{=N{ V"]j(RY-$hͽO @ Wtn/+üe_ N+u)Ԭ5I!`L:{4W|AhZLjn͝fci ֢}VQ$*trn|*xZG _FC^U?~$x[㷄;^״zf,7]QGt 3ohAi05l OJ:$(a\,`K}j\ڵG'%wcܛ5oJ:?s*;?-oIFO}ITg]ğ8\u'uMsL S[Yʸ1,UD{lwc\ߎ;ֵ߈ğfvOΔV'|F3MF2*Mu;~]ح WN7fn3-r~Q;?8k 9Zo&sev>&|EC~x]kKI$[jI ^n6I#kkK:<8P.#!FH8jnWQ_2_]/9m ͔koVDp+[:K&[>Ÿiw ! rzGCȪJ%Xw@oUEua2gѼG%m?^U5_M ֠#·-4R>&/圀rv}juITjOn(_MG:5@y{izP%*-9I]['%Rgğ8^5 ⟎ÚV<_ǪIt4'3E).K2i9uK;ᇅ|\d-9Hՙ3j~Ȟm̟&ٴ(Y"0!%6NH~W!t/@ZCaESQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE7xi6τ7xQKXm>0L͚K庶`I꧐z]5dOdI⿝A#̃N=PM`9`I 8 jڢJmr$4NpЊ@SVOl纐 YNʒ7o^E/k Oz&A&x㈭RݐaF#S5+y{X^ 8Yhƭ9m*Ӻ%ɿ5 ?5 KmG#RCBgxKFs$Q.m{ 0#v8Ɲ,^cl9IS v>mmfIA!OjJ9[IiB }d̙%2&ݻ;wjO1&o?h^C÷̉&qqwR*R9m)[ Q5cQТxG 9Ƭ\Vֺ} 5ur?J=RB4iڭF, 迳:[i7'uNeiehD +q^XxI0xY$Mg\#7sssBsofTכYcZƥj gkn :Fu' WAw.OUU:O<-SV,PJMq D$tY?x~ΐIi/cNmB "8X^Ir"xYAeVb1R׮N·<7u ռkxݣH-mb0; X'=}c3QҦ)H 0qV~`Bn!q52.X,ŏ5V\(gR+[i'}JƬ@rjO{H9$m@|h9m(sR*Uj&E* ]23.7%O?KmP_hzhgڦh43uH11`|=k돊DŽ-tLu.\-?bw +o9ΡQr-pqaǦkGKmIwC}wm[z,)Zh.Eƥ ^4"i ]w3e[#I[?>ouٖUɚ8lXVIP*c/ÑYzWo?״^"I;f{m^at,@Wkp8| 2("iYD=M/885vUؼecChۢx<N҇oJ {Vz޹xgoᦧ=w/H2m1yn)y|Ch%[CXi&;_ܫ#pN{1f4Y.;_^_ٗm曵Sw+ÅaVK>R|{/~Q/u}~x,d42ݷ=;WakW^04f4b 'P R '*rzYE|/-(\m:L:# ?xs[#Q4b1h.?S鎂~ iug uѐ/g޼1TӴ Gݫ۶s^D`w9w_@x95:Y>ѯgy4)sHrE-C|>]*mFmfVR7FG>B,-[B9āT~QNG _FC^υֿi[MsN%U&` Lʆ@+EE_xYeLC(T&Nڧ#G_i\[$~O>Lꊹt`W2XO/gGmr+Fy0v#V/~^}Ad]Vl}OZj%Dw}M=Aq![?/P]>^uggi;47o+7@1(<= 5u#?+O/zK5ҵ{=-ꚌIU.c1zoP_Ev'Vfx Iqn3DNLdʒ56C|5/h> ߉cCѴmuծ٬Ӎ&tv8R_^*h>ǍnnoaԥTV]X5ܑWi3+I<;#nn,ݚ h$DN9@Izrx/!Kݠ!+~x7>&k?).M#RA]I%ȕDqq1^A!Nt4j{x5t%\0i^7 ;l_oYh:fqM:%m`To)~d AT =.|=\ؼe2!R{Z/uGoi0G]hzmޥsean$-C!`Wo"/xT SԤԬ|C?$/.!󡈹9(g`۞{/ kFo͚cB´HMM%]EDqߌ&.[->6njObPAru:^f,JN['<'r0|:ĠvPdqZ4dC7'sէε}?ҧ[ f5SY0t}&OgZ<Ȣ q5k'*k_7j֪:匚YUe%.pe gۚڗ "_xP3iIp鹥e0vA-^>0𯆵MMGI-/ܒg+7$dqN:k}Ō-'U/c|n 3g+{3'Bt/ ^j im"`I?4O̖h* o{:w  槯]O^lld8SX?-Q>g]]_V]ȴh!}h.hc_Lp\,I1$Ha~1W5_u-nI&ծ?c5̳}B]۩5Co0ixZk]B߁ujմIZ6hUQpU[L]؋_._1)4/KKo Q9C%Tn+Š( (((((((((((((((((((() l}3U4+~oRӿQυ_P/M;|-ؚwZߕ ERӿQυ_P/M;|-ؚwZߕ ERӿQυ_P/M;|-ؚwZߕ ERӿQυ_P/M;|-ؚwZߕ ERӿQυ_P/M;|-ؚwZߕ ERӿQυ_P/M;|-ؚwZߕ ERӿQυ_P/M;|-ؚwZߕ ERӿQυ_P/M;|-ؚwZߕ EsNI齇$ TIݑХ/gk5++|U𗇿"4]4xJ[=[ng v=jźt.-ij&i%@?/4֩?{ݟХ/gh RVv.M#F[?i5zRK54Vr&n!S<qY?[iWfaWmn6rLȐdo16rN9YZilѾ€ ǸIR8MWerMM9 MvꭎOn<}ޝ&wu=A.z%A qgv4x|2S,wWm,)(1\1%F@KZV?#J_׏Kӡ%Q>|JąY MI"@y5vמ(_ xr/!["^Da 6ޢ½=d)KY DOvnNjla 6somj-V,ng;Y3\ gHӏ]u#N@ ĬdWgCGt?~tUC^ ]WIqsdmڛٙ)xX chRV:?tOOcx`#Ɋ8!8D={t?~tgCHxcsP Z[jr7bGMDb?yz* #<{?~o|gCN}+.^K b,Ÿ0fbz::?k`8߇;hlp.M9R(!u:WyU$ea޵f (((((((((((((((((((((fotoxx-20.08/images/smart-erase.jpg000066400000000000000000000411061362435004500172500ustar00rootroot00000000000000JFIFExifMM*V^(1fixgnome-screenshot02310100Fotoxx:resize|sharpen| 5http://ns.adobe.com/xap/1.0/ 195 314 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}XjCSc?}'?±Gڽl>|?z>EaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Yvq Mw/p̙`gxgR׼ aZ[J.ޛ&lW-ҷ{ ?DQ 'i=#?}'?½_F#^>Xj:..wOx"$r.6#H|1ؾx[]x7Qi76$2Ewm`a1NvIMZ1'?DWxW4;-25{đ K^vr Tea:gg@վ MoZj7GR0#T;qg8Y^~6%aOl>|𭏉? KMš:ޕ}fdUya:kWJԦl}'?DV?ڽW;aOl>|{z,FE HjGAϠdipM͍…ԩ;UpYMFPm$ECՉWt<`Y]seX {qs$DQcTQWUWGiyO*KOO"|Ici4rSiE +5mpx;Yb>ݼCGiKmm<;AsRY!o Rw:19Ȩ_no@%iA5vO5P1(~E{OfBMrxfPZa ^ڻi-MĶ0p#k5.\gA)'񯢾.^ŬcDեJ&>nVtp7`d5wu-c(Xt#7^Uxv~L'>}k/E/EN~}}_/1x-/1x-as'֏Zq|=?m(q|=?m( _i>}/; ^)o1G; ^)o1E\I'ֿA_bO[?_bO[,Oi> {C"Q{C"Qf??u)nO.x\I Zc~?ӿ-nv$nGqKn5W; ^)o1G; ^)o1E\Ti5EкX.،rWZ=F[MuUK.i2Kpyo" ePxZ+/E/E]_כ>~#mƟ_jPU"(Ls,"7zv#YD<660n&s$.^YqvWܿ/1x-/1x-;6/uoR^M%:B9X’2+^<.9WUX-8cBRz//E/E.|1: R=G_KԈB%0lHԞՃ_/1x-/1x-%l s'֏Zq|=?m(q|=?m)مϯZ>}k/E/E.|w:=\[mH8>dZ>?"Lc̕ W/1x-/1x-as mgxe+q!=s뜜jlo;Y*$~; ^)o1G; ^)o1E\T{K(qUvz7ŋ+X- 쑦Ÿf}H_høøËFom[.ͱyUGS >[?>'.O4>limit97çbֿB?_bO[Ow Ao1[MRɊ֕|DgQEuAEPEPEPs\5jvjdw2%1sX_ $<.|A KkHf(6|P' uDpExomjۇ<;ikNsRE3G2Qno/ 藯'~^nʹ-[^Z +ˆ`mx#Ɲ-GYuO ZAvURU7G2H8 hWmLiexnHҮo{ G nN=p aC@>.g>@#J;3482$(yA˟A.:yE'm|AO[n|ˏQ4MYJc<?*zn6] y,vnZHbT,x!ِZxGf/|;z%Jak}lmgdVRJYi~IOB>.aK~@܏Έ YhIUC$SБ^=c^5Xu \NkDdmAmUXP0%ևÏ /|)}x:xtm.d>H TGXWٿ_ﭿ}-B$s5 XLaCP=7zg޾>'SJ xvJk==-0j ں+$pG6dU;Rqo_Yx B@XLL"bNIbFUp0۵LIcI5[( H%iT+c񏋬<MK:~\MPu >8:O|Z.k0^ vE9IO1T ]=_߇ҵ _Vìiڝw`#B"dwC\ZDMrQqf%(C`gVnಈq4pD:Gkozᶷyil:~NZAk2`a%gI<=@m>Kxjs ][MuXlUy4n[Llg7+n47RSoVpG]Ar}i9>(wVM--Ɉ[$RGD_l:o.4#W@ȒF4?LC0iVy`*̻p:/x366WOM5$ }u;M6hf;.f+~`^ _0VڋmI!sĵy㻿 DڥZ1H̩1:sgm6z{"?熖I!HX.DS/mO^$ѬcIZGͩ^ HaxHr) LВݿtTv x8I.FpA}J((((((((cG~B_Kh#qƹ e  exe߈Ux[]~#m`{oS[Ω"HgԂ; {]7]x?F\ )"2Ws#Em323^oQ𥾯7$;_s5e # ZcēN:R{OF; i^ ԼC6 ƫ IA.cqD÷Qot:'<=Lj#n£<HU~|=BJ.vՖVd5Ķ!Td# nqPKqR8pu-HЮM1Eg-Kυ><״k6^ӭu{dNq'X]Eғ0 ʌnnI/M?6|% x{ xH.}Fv%]ureOqrVvܑCi!#y }9ڰosI-Zzї&[F5 $X(8$8E߆=5{I3Z\Y\2[A34lTvl2 k}(! _$xQZ'C,\l88 Ğ}Ww\\jWKe`ucLs,ĒJZA,jx⧉~'3> Ŷoq>0,o ߷y|3  EU5;xuFU/-he,NPT9kĿ:5{f^f CG{co23dCyו kY4K[_ G̖5OkufCUX-1;/>+x'~14vaxơAuFɴpj}gO|;i.Lu Naf < ˞^iot6M6Kz_)3X|UmSw4*s~!ן[iw:dztq1aO 2iכ_ğO>m^ź>)~DRo[ m=:VO_ɧj~loWNU䀬B|;U,9/_NSgKl" :+[hU3YSm_ |M4=O:-ߊ572Gl^i9c,QUl;=O~D|UxayTQESҼw7>(K-#تO4EWϨ\XGXITX~͐ \qcˢǥZj:4ٳo7ur^O~在Q`;:)tZ?WaȴXQYZMΛ}ƧsGi>R"۵j$ԛNĚBj6k脡U)9jY\\CwL*n]W#j([zV_O5DT8߄5l:čSJgײibp-lI b \Ƶgq [M$.%I)IAf!I$օW<:v.w WӢ%NHQbq8 sw~>wm4mޗq$HąfT$A>[ySWaȵ_x[IQŚ%MV^[1FVzhQE ( ( ( ( ( k'^M~5C5nB dˌKWԵxSH5]cRXt rȂ4-BJMhտ ֟_n[o Rk mGYZk?!T"`K>)~6h,oh[rX}9\OTkrZ%O="X=@QqW'dr_C<=v5Nj,+vgX3*&XXXqzxcH_k[ jV_736ٶmT*|̶@mWuEGy5,vK6A>8բbFq1 .QF>c&w?QJge_&XG2\ 2Y(!9 _ǖ_%aCGUURxs^WRԵ>^10\>6'8'8ѥ)h>l8j{mP..#Pu).ԛ{Hغ J1Ut? xSe{{;]2K=8*qk$z`ҶZ;:mx~-_\(u;uJƲsP5G4iyo8bq,dMk|yNh5~%~?{#C\-OѤdG-OѤdLf;*ؿ Y:^ex}7^Dzx:6-`py~R hK+? x_UfuMWPn:v@CTnuoxo2C ^?&}&UI %NF0ɯ]g~$֯5}W.I\ګ.O`jjm.;#+-3B6)!q"R~s_4?o^Ѽ_U|;|]iem|'\[^'󬐰F`<[iweV^F^i* OUO|ckg'mmdQ)DOЕ#85.qAGm??M04ۏ ߈?n?7y~!hJ͸6w&4ۏ ߈fy.m+yX]}˷o Q;HҐVnw,S[E/rfP~\sGm??M04ۏ ߈?n?7y~!hJ͸Z΁o׊VF̑d7 (G Hw61FLdvn?7y~!h͸qAUv\/zV܉%U).ހ6A櫩 lB)t@-\# ʓqAGm??M04ۏ ߈?nG#Z'h!ㅠ **|ږit5rLU']M hHWg.(v@9㜎qAGm??M04ۏ ߈?n?7y~!hJ/, ; V+!m qb ?[W~ѐkڎ:ΙIm#DEuR0v{W< kicٵr|Kx{S.4KXUF;RH0uƺ,S FoRX Ę̈́ni??fqxi-SNSu8>s,HS0DL:[]࿅/xևMYY7op@ʌs`i?m^ o'WkFZ:4ZΡd./;@| t OZIx0hIɈ(V #@͜06Ҷm..5kғ40vKYWHK.|m_Q%ec+wCƾ,Ѵ)SUeIt+c !L'&uH|ac4{k2jqz$!<d-,t!L 4|#ѫ¾-^Ma=x{yo FT}>wp+ pFz u)Լ3i?k~'Y^% ,~P-h3>>߈ ,(sJHaSr2a(x|1ڬV>MJMKYkˊ GaSOB{&E"i MdNۀG&>fN|Gaikܽ5϶<t:pئMw"V?O'BMA PZ=-}ww)y1$6F,#̭.u6z]J̖A9?|//4颷LRkQl;U +4xLH۵-?J4&) ?L?sCĿ-ͧxWTuh fxJKiͫρu{Ik msOcnaLGThp.D| gMgG\!D}CrZ:_|DӴ6C5(煢vdH "\2$: VjW^(//4畭.mhBy*\~vռB~[9dwiUEGdH\s9};5+cFӭo-Vui;q5 O %a9?~.k>:-SìZ#RJ|f_ F?_v9S> ›R?Loz mK+57yY?Loz mK+o~|&򾞷m,+K{qv;=F?_v#W/;@."xPe==EqOoNć#"=tF?_v#W/;@) _ME|7=|A6Y<_ s|7=|A6ohys&~`5yY<_ ](!A1DA_~,h-xrK+$#|.GqZ57yY?Loz mK(3)/Hjeoh3)/Hφ篈?Կ"#W/;Gx{:OInne1Vf<95T SYTwhgTeb=QEQEQEQEQEQEQEQEQEQEQEQEQEQEV'o緛GgmF5j(Yr\+Vs^/8ռ$}5G+ e?uCF|5xF㟎9Xxt}<6o43 BׁX >!xXf.t &_>[FN/~5tI5Hˋko 귒Qy f-gʎ\;kDZsN#.u(u"FQHhs[FoOjJ ( ( ( ( ( ( ( ( ( ( ( ( ( ( lJ]xnNɩ1'Z*vOi{J*rAAphuCS #k3ZE"F@Qn~zǚׄ.X 4l:F5b92͖Yhw_rVZGI_ mqokLZլQ,㉢H1 kZԺ:5)xv;rG;FsYhw_rVZw{v'hXrUe ++DƝ/ax\oyfy] kVfotoxx-20.08/images/smart-erase2.jpg000066400000000000000000001106621362435004500173360ustar00rootroot00000000000000JFIFExifMM*V^(if%02310100Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 8 332 1000 2 2 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?KoUC5.0g\hA3i.wK ?jƝGŵYm7OqGO)kt$?Ҕ.zR YwF+ 5ͬr%BsZKu{u^^LT); XaI%@$Cz~I*ӧwBZ[{2>LD>m:TU Wrȧj u'q})O@]T2aJgiO'92~s=HgBꤨ=j[ rGҷ|?`eY7P6>?+`z W`~*؉Vݎ8W:;=G?Ҽk:<ZwZ+juZ8y%C.N8'8V9J0MI傧$x#5,{&G(x׌g@|uv*<8O Zw'Ҕd]ކ1e=gܚAH3zQzN#hQϥ*iIҕF[P705+FѶҘ fCf+XmDN]-es_1.dNJ)`dMQv!),`EspA eH>eNoJMF>U*S;#t+58"VV`R&|D/VW<;U mA#Sj ?v)I>I7ay\=癨KUpsUB/I❜] Cqdvԭ hמ+ %b`.o*vݤG˪Gʙp<nqQ_*I89XJf9'*T")OK-@)95vYkb g;qTvۧ L n*[6eEeOQEX$Tg)NNペ,7q@m$j,vNNpVQt&(-FQ6pw:+ A?W,x\w;dVPA 94x orq]_DuaS5EnYIWG{Kqs+*柮]##@sR|Ⱥxfn>%{Ivɺ:4'/4K6'kGRYʻ$jaFI)#=i4\lќ2Rgx/R|SYi`ps6D5$+Lr?&ډCj  *>wNuqҥv 8"~\Pdz}jQiJ3Lv# F:PWION1ҧQX{=idT9=9\iXnsG#2qRI㎕ QXRۯIsNۃ)\z!,K 銄o֎W 㻂T6;*)_r$??ZR9Sd=~ӘUv6U;䓎.)Rmu.mHPeT KsM vOB[m|apȦ7#>*TCiw(qm'=ȭ-k&QTɺ9?3t=$i꤁*zkC n{~Zc; 9<J}%]"㺸$=j]K yr:j¬SWEad̏UwKlNu7': Km둔nvkף--R'dWXz8x#Et]4KM r*y2+–a:JG *xm3[nsX<9}pcX_D~ Ҭdcyo jecd/]N#5B$?&#5 Gs?Z<,|2 mZ5Y)dsЊHH٣TUMg`T<ƾcl`zy(Lϰʲy.\qoZO /r2XtBuLtҞIFީ?9=K.Kv|wi5zz&{+ŶDg'yװDF#r*X4M2]_2,k~=浖f-oxϞN[¿ 4V_3pʨ>½WIomM\2392{n'i[ J[taذ]Ή Uhy$l{ WQ#գB5et_j" opGӏZXxfKdm$l9=z:%L!&d,Hx'’Ԭ>tꯓא*beGԚxY_*H9VF /ֽF}}JSnq-n1eWs~(Z,4Դ;êᙑ 9ߣSljB:q4uSqAJ.#yi=i;pGYg&I~lhGdQep#֜G^Zhۥt&v˜`iV)-hUqWk̰.,WAi*3**Xb#npzqS,Q{W1W<)@&p:g>BK+Ci`Sx`R:t֏97g$<T I q߆Z枷RihWw Lk4\-j7o3؏u#sϓA7pmckZ99-9Iq߃Jb-K˭Ss. m%ܛ4l.hJy+8OQN>GH-0M4;!Y?yKT6RŁ^$Ԕg pN<|7ām9t9לM"On煷FЋmz!tk(@@E't]/km-Z,g F@q@9+] E5>zr^~x{\hl4mDd b+u iyʲ5QҬV*jRW{c@Q9һ21h#ϥ).16sE'EIcqjhY]'X3. ? _>4gs|`]{fDnpWĸ= 8h7j:F $&6n9⟥QdkyZ+&Gea}4v )J[隱I'P8x:ׂ*{})- MxM"?Zg#TYW]XZ[J"L>Xo`+C^cں UWi#x`hx/~ϱ+K4. paG+oz 1~a?^q)D;yo,:8SN I̯H/GKy[NB3j_Gҭu8TX ֲA-͘)yȓ:xN=طK]Mc=Z79i+Dʍ:*Eqz`QnLzfSiF;UF\yڼz !'+D$mźgW((_VxFl;b;/}kV AJN\H:.DHqܹKR4&̊;~㼟PKf% s8djzk6-&QLp6>"KM2+-NiP|[qfk?gt kw0SN3u G5栮ګdO?Y1WxfԂ[mʑ@ 4a'khwooMi,+#\C*=wsNjo41#2is @-Gvqm4RۜG K5է,7>Γ:|^O]Nyru]&^O?SKiibhɎI ֢Ӌ<NĒyiͩ+`횩-[Qjr B0>j]2T9PrSV$rFlыxxq"/BN ŋH>:{LPܞJuOBܔ& J-~+:{$hVޑ.M}HIMs> j:a4WG̨u0?x+BZ4lA%ʧeݭ¡=n1ZrVVJ[zdd_~<,e" F?G8kV=JQݯD#wlg zÙmuˈnY$鑏·!|*~2H,78ǧBNv%9nyvŭm+[b rw``y~o &rlUD pW`=]pF9b=++G3tSCc'p8y-U]2=7PϹ3qjrN9r2iԭ^RCF+i_xc,\rc3V47jpBQσ..'$pw(#>WIM)\#Vh%H8?2ՓBU7H*ъ0^ݤ1?uqU:d{ppSP{YHEݜ?ʽ]ېV%?yvGzbOXX|%9H> +6_u 5#|Wr ԉX]e0ּ:gGt,о n$8ʈW5foNH%6ڥ('Qsv=zd]-PO,Gf,}4(n8ҹ7x P nT| tN.EE V jmVK{ N %Ks (:>XB=QxbZ-Z=aZP7\kcQ=Mu>4u;kkyLQF\c'H?ֵ]"5m 2 <=kZr;9݊:C^J]!G0F8x9mXW*T}3߽yg6d`Vozk3M(JA)|u4jZ|^oX5]q=XO$w}Ѭ"sHp<>c}Ş%Q=U|׊ZH'5i kĘ4@rwhz6w0`!p 'Ү/"^Fڋf5IL3F]~RIǯOWYeXQH: Oz"}! ZkY ʫgcZv=D)d]I803;JQrkSmrκ}ń ޳I$#T a"]#IF+Y]A+ H7$chZsRa(HORytso=4!E*~cL[RdG'YS*α?3=i1DZjYKoGQ0\3Ga튶NF:ӭt0nWe;#8)Ym$ha9PÎMsd]C$bg]yq9+3t!-ERI}\$^dX`r{ʺ#;4rʚii~' =>HTBӊ?vvF2Nx<>|oXv:C"RSkٵ9#:q6Z \̲ >תǝ#I3'f,?at}"VA 7\6\ND~bGX@f<ֽvamw k,["RW`:I=zW ŖK{8A[@{b."&xJI,mIm`[}wPy8#Նh6Vsz!$};cO Giy0‰Ⱦu@c#5xkQj>fy\dFI#cƿV-3{i~\SYocT`VT'̋e暭>GHGJB0ps;׸.2ڦHO~zЂ+׼ r?֩ܠVNSK"5ʔyQ޶(T5(Kcۼ9a*XzU\E#Px!ۏ^kG}ɭ:I-.NIR-B4H*N;N s s\= pzg|[6K3C;}NAAa9zNADd ^_-$f5 ˏ&Jr'۽sUF$tӝFz]/ZoĽ{|uKφVw/tNA\]qh ȑC .!rGZI]4-f#}1 h8#5|Y CmSdXFw|Ҽk[=NY6-Iɞ^/f[}FGӮ) =ƣ_mƛs@[Ҽ\֓=hf "XWkjVHI3>I1I3ōOL;#pn#Ȃ28VG{>O }B5c@8{^Ml46RݣB3zo{] 7چ#iKfpr1_29|#^5֖mH$2 85"+])9 GLlnKȔD!\(@GPx{7ivַ:Ƈ#lV(e.Hǥa/D\4p=Ӑ<@I$f,]ׇkkQM[edYFA洫>vDr3WqZBR2cq#Ծ#Fx<ֽ3,H7n\;Dpǡ6~/m[k;R۞m$#ppzl:UTn~TK6mq1$yQ |įM[Y?aQ:|נhvZI*YN>'tUhΑnHg5sҕJs^m^n㱖UV2z9z] eWy ?y_-o7-c4? =zQKoh9L`wZMǚ&n:aifMOjZ|V~a?xp2KnYO1e pW^xK8u[gPX6A đ_i6=:3py>¼t+IxT矺@=G)r8jۙn`ͭjZK޽ew}O{uްɴ@ 5y[\Ri+A## sWߺW3|o/ṹ]76_QfǩkX1Cop1|XgQDH_n@VB~VǨֻR䚝y^QǨjo1.{ w-S1I'xƹ/'i;gҿ<-~Ν=u- yPgF8}mi0%Y~!ݖf m^M-L)qpm[G TAn}:`]ݧJM0]ElH?hց>Y`pp Ќ]bB&3MlK}"u'7sьbsC,m .<"h\ 9?dxᇇeev w66\#Vi.1R]$9/_+2&, "1w>ƱQrz6Իo'ZFXU''~mt|9$kqo(\0&q>5&ѣo. "yVkޏ=݌JHRM+W\+3w}gkiqj_dvVZ_c|i|(=sLkK8.66+&mnFOzm47ks ]t? #(*U>pAZ~#Z 4+V6KDks[y9@Z'2:ZP>W C/XmG=xW G᷊P]7Q/o! "1s g{fBGC5etcxJ2pX=Ğ*u_chdVDŽ_>FLY5f4_C޲.>̓gj71d~kvqU%8]k\/e-.-a ѐd9ޤ׌k7][#s_$,ǏDp@O\de5yh) ;Mgz #k6˧Z0HrXݘއ܎s]ެÒi~hq߀<'SԵ ۲&r79 nz,׃?liѬ p*>]"uf]sA+kDcFJ~Nfm#"+jSdh \娹U8 |mվx+x3u.>@A9^Wt65͔;fc?[_ 5V=1cGoD@9r^kmɓOAEv,JXWmJ2j;ݦowi p{rƌO9½|ϋu :m"s&x;VG56!K{s 7xwdžOZXO-O2ʌ ┥ST4g{k.tĞoZͭ\[ЪFvmޛU*;OkzuCq-`x8+Ė7xMRdElQU xM]vOMᵱxi RIֱN,P һg4v+v_4XC,vHnQ?qXƛ?aR"CKʞ %˻ݱ0+$tޔ5,0e!dR8pfk;Sw#mpֿa#"…#9nH4v1̧T7vk|3Ao,dpBcz|Gh) Ð  Yg%݉w/m=_&Z('F+b9! =bR2E-e8x3xQAE0IeP~e^9Mɜ{t-V۞4^'(pM>I !`Fp{׀XP=ݶ{Zċ H|t!ф [V!#b)S=ռ;_Y6nv8q+Ëc-8%Frk++\@;6q8bW5?MkK[:%.UHd~UZjve:AzVqoM oxHDtcu|UZKY}N޻hq͖?oȐ{?LV;{!Ñ; zTJ*8*FGsdMr+tl<Y\oqRqY_> /:uìnc@uR^y^-V^h.۞( `J7Ƶ{{qF B0,p8l0GZj~HKyaE I` ֺ_x_SFi"jWc)Dhڤ9yY_Gx>@xnlL#+iv'7jq4Mo ~:[Y[1tvsMo|DŤjvWVWO(+u X''+<-+94k uԡDamȣ]!j*R"\> pr@9֊pkVq~.ǥEڍ .o%.Aq^=+Gnu?+:~9y5>NTI0²ۈ;zv.[K4FgS2u0ɮinֆɭU, GoDWˎF#NHQ~ x-I!ҬQʇќ6 zQ4K,+EC%O+ƒÚfnwqgIn~ss*s:[^+^.}%T2Ln鸻$E8+}ooERƱo~3XYM7q3ouXƗS%<k rR_-j85qkPQ#7@_Ʊ_64#RpmA r4ZlR`xb]K4Ȭ>hX.T0$5}6P- }E1g!m n2q\wЪdM LFa42`{r(*m{[,O T~=|lL/RHT9_K$Z\ _Gh&pPmIxU1wZZ/6GO4خ\1?R:u5qӥ+OU*J>JL-Ӗ'bn;NH+V:}u1\i?f:V?`.~||`5|S ͂dGC 5a]_[ST0tge`.f'In6=;m-iK=^[oyc2Vt2۵,X+SXOW״)BK` +(Oϑ֊NڦG#oSis]h<3w"F# [v;KP}OWydirsk?m~5泅W6LXI0ZɫxYљtќţ \GR}9ڼtFvgv6AܱFT~?U&y\0ߺH T3oa 0H%cL}d+m}kj6y21I5mPW/ef /{'"=pWgC@0W(L<!}-K-$S{>\ǃ~e5wIY[E:3pmC:GQ->$%}dTVJG9c*jSj:9C`y]p,}ASY60ռ[6e{HIt +OHIדY;s[a?*u^M:~}(P{GVszTz͜VWt_G|$;0Nӭ"@v`}gi_SÑH)ʕD.7ּAq8H1;;z롄塍\CQ^+-:,C:0Ŏ Ƥx㝢 ⶲS iP` |ߣvRVSm4XJC1cG^{imJ>Fyuy`VGC8PkzH7c*xJL?S{}1^3&sEtڷy(ɻKA㋻Fin#O3O֬̒?3E_|Y^ FBBqW?Fso2e w^#(m/-nmڍ!*F'8-7:[9} |Z,^o3uo@>&.Z5d8o|S|tOKľtk|mzl>MpQHʃN=jm "X5fR5`e8`)_B%SݼG!>U|v9^Aq,8ہ<~j2Gk!4{{ [z[#B!qM)/<`kSVuSQxą #JlF -jC1Tc?@G^ktou]#^m hү||Z{輻a&*O {ׅ|WhZ Uox{PKQLα0rqw8/'eg[HZqoc9lg>]߆]P)vh1@c:(H.}H02Fr(6cӤ6xo~MW y*.d"{)nP:/zOBi13s_ >"ƞ95F.Qm" cp ޷~$V{Lj6lQg>_5^FC_(0iQ[ov@Öͧ]HwMZf^V}Yeu%EM&ݭ&AE`1^?45Qmsi>AE@q.}=Y.~o&-_bԐe/z6rJ r+F/x;{D&8 ԿPs=Ac?txUcEj)jcj)C#\Iqkp7-Ιz(ޓR5 Axv{;;+*A{wLXq>޵+{+y_u+󠕘aO]JX~Mᛋ{:8QGX`$g),ŲvkZ~!N+5l>1Y eRn#3n<|t:Meݏ ߶hV_aEpk'KU4Vg`b1ɏbdb[Iu6iD$]u-b\X\Zq ZuDxkQb@6ѱ]%9H4V֚+K'cEЬ|s+{ZMC޾oVs_\\]Zo ׭zՕ]@?,hVШ5qx#LzEp9օtM>yBזMYb4Gclb@ĸgi@mVZZ..rO|Kh TبndG|:Qx_[Qax%GzKF)UJٚFd϶>L\N-w9ϥ`à|TڔpmBQA|w'7b;5_ 2iu N֒DRȹ8yk&TV}gTV ^h(2 ԖӃ0WE|}c,Iѡҙ..O~zjvź^,nVg$rpd䏨W'g{ ȼI%v4zNrڽԊKs$? 4}-~5z[综;xQ+PGIcO:!#y{B׼YixOVKu,a0wg F훥i cgqhy#ڹJU]ͥV0^=\՝<}*}koKJb]y ߚ;3ž0b4lzo)O;בHR 8kާJcty2w7BuHRT|tlk_Ul؆(m,&]с\/VuR[OxQwA8P)ܤ`zb?4$Cmaj7xZKSЭKwkm" $uE"FmyY䟯j-~gf`dջM sD x@D2^kY%x{ 82S~88Znoz$ 2Fp"K0?2nޚiktI~Uvt%d:Yƈ`xeqii}-*_UB Fcmɹq,;һky4;w)b2H?Qt-C )<Ѧ,H{jH;ڽ)&}UZ@ P̯rxMi/z'-QADecF[pc}f[- IʺOhH*k~e"C+dmpFo _)I< x8 ȝ}pk(~ٞ7K@F1Ak`q YN0z~创JV[hڦsc.v[{.E8نj8m@OqM,!?N>F~^3ğv֋HrK4z9˂:Z?kR-Nc!8T.>7u%{JL owoFEE9s^GԴ&PEcG34c%XÚo÷0;G]˒]%5_`Qy!OveXw$ ?y4"`xAWቘ]"灝u1jJ?U9j}rϹh 9&I.c$8ыͅU88#] ,r($ֳIfU֒? f#~I1ٗ:e>dC/W޺diB㚎;[p%v85qsyrH#,[qpgPeaw?TK{C_ :qNɢ`,e$:pwDyǿ5>ˏIgR7V1.+4r1v:WMNv;&){6-bXI V~lj.tO[&2[Ŕq$#56rn1cݍ7RVߎ?5-:k ȥ%x>p@?Dlu= -ay."21'+@z̯$#qgvVWIm7= C Mgg(cu.jkKE,,*czgg5}u&[ǴN xԮ-<דTaC}۠ڪs\Z~'WE}5>بc[H9lHј*N~ճ[%3 ̵-ڝkU8`~Q8E8TmȚ{kc;m-ysys#K]\G}5z/>, ]J2y_ҽ.%A'wlv:?r^xLhd_~I+so nkXiב䓌uqV=/: ?ȫ$vLTqڼ#TZ݋v/hY|Yv|͸ 9WQSN Yk֎yCt%%Ѳ9% {KD}cⷆ8~*xiF!DmԓUrsơskpB99=}**]/gp6zz$tFv,l9ָZ0wPSQFvv=~=xUoghZ}?P:֗~.:K}tIVbx8(/2\}Q<&j~&GgpZ Xۅ'Ԏ_o@>h)FXq\l$pNsOڶ/V GwfʢRq؎e3Y[IЀЬ&?53Ւ+UUPX c5Z6zX}<x$y=(md F*뿌4ԦXWmeP>@zS֭^" Ҵp1^JO$[M5X (H=2?*s]Gȥ#׭4aCazlF唂2gq{h xݛR3 &HSq5rrD@xJHC}=+U?H<=%Ri#S$-*,!Qt+K @ET~_ww,ZobC\Wx OӴՀypaJUCSܴPHddm$zǀUmV?6Aoa,qN8,2}:wt4۫[xn$ ~?oյJY<3ȣXN8Tt=y\:vEƜRs)tR[e4ԯYq(;)u/z׊K =NK?ɥx_Hӧq%%9`T oV[_X~wJh"LA$$s:CivVI[_GqybzAl{ PnZ$HnpIR|%4Fwje|bh@$}x'{N&HhH)FÜzzfgRjvTi>G؜TO;}O$cIPmt a^Ryb*ܽİHHE c9涍JV\bg sp 13zށfk%J~r3ֺ^.V̏F5um;(J=xKO/ 88 ziKAG<O:DB<0 d U$I]2 +Dl(N|xSg,[hѧC$c  Uρ1H5y4mdUUt0MǧaRnґyt:J;,,0{_rx#] gh_e#|Kɴ]$#Һ"C,DN\#fEZ"l%.*ِ74iwqa)M̌}˖"ޚ:SO:ڛgq(.FC6m̖ʎB FҴ[@`Py^ikn޸$ڢ3JizVtHkKPrP\OFѭ~# \e'}O^M=7>y%,s`~YWaӤc.&~i!٭]YjM,p,#8S15>Q1Ydc!]iK#Ο6F}:v!,7!uz*^-]L#˝$OE<%hUJp{8h1Ee Y1] Ŝ Z=~kAaqˤM&m8^WG iJ-y4 6X,vpGVҒ "9|`SһwCL֋]mdrU\kSqҧsVikj͚A7+,gJ!zs]Q\XΪ%%s5_mE7P7n2 VҴŞE ӷ@ҨW^xbIŌs ̻ݳUYk&b`->fxs$أ-{tnj0H'ԍR0vVoT~OҭYC-N#,8Q Fp~lc_ʵsUʗ6ͦ Nd#ewk.IZUfxw[tc-'Vlkz.eA:ĺ;i'a1V(+5K[NV #8g?6G9.[Soq=ڦI-Njf%H=Ժf;jو3+{g3Tedձ,2ǵԎ7Nѯn _.؜%֪cgK?o\_X\K뿰H[ ݐx`s\_b]IDH$0b[H "mW x$t5gM~w|;{?<Ӿk}mcG<8g;9^=:wi2~uDbHi9d8㟘oWM_QTګC5\Ny?Z9Z˨h?dW&\,ȒF,NKyL{W[8#gLڮYH?!qקJ:q[]Ln h@ANOx24ojlHՊ>pkFI4: t\sirrفzp LcWx7 ?t-BI.h`9lُ Wn^:h72>-"RLy9º?5"JqƲ yͫM<#2o)\yhe?y.9|2'.E2m,I'r[xh V s#=Iln淂I!3f[r(@s/'=DvkZŨK-.&2,k=uM!f[{Q{NI )~[=ɀ)C6~AqWHtY̹*GpEK YjgV,VnyAG|3 O\׊X|DZ˘ 9uYc$0x^DbdʂkN0pyխKlN\9F6ַı,|ֻ?F+}lun!wu+=.kpRd\:qcҮ%%e:vxa"j܂6U&A8 mΆI>d }+Vfd"{K92b@K"Q1e$H3>༶O2U89z׼mkkךI$D8BisC{ TuivNTߍz\^g-mh]Wv< scw pk Гe-gB$,9qfos|Y9cշK2I"mWlA@@_n&йs$',@N>. #gL$ r8T9Y>@![85|Wc&stmUpÊtZ\Eq۱PopBT.I<XkibQ*9XKN:7H.BT 99_]ks?=yJr8¤4w\N?͙( ѸVU//c7 Ç-^3^o-dБ#7IlΊ";QF mr)6JNkV{Y4e.pJjְ][iwm(n#,vqJͧ%}o^hk)KGFq2d-q]Vzm0 -E5Ies *jxbF:t3CC; 27mnͦj$A* ~yTj3jy^[O1&*14`u{~U%IJy1șrx֢l//tjQ7Mw3'&xb `iN[?wҽZ< Wn K$fK6R5Z49wfw}߼G5xOjV=` F~?*|I{nE{%&l#IeyybDcaR>٭u}c>" \'/5R?$Gr rgQ9-d}L elg%=7ҹml%λ9{fz.TH g|~|~URSVKҖ{`|8Qr@kiXub^K(*2y{j){Kdfu;x1b|ud'RW+H$"6='MYT3HV,?Zn/jz:h[:ȳ_Eq$ljAr0s9]4:Qj6۲Gd ˒%V*ɭ3WZݍ.۰D!Áןַ%1yym"hמ@|ǨHt%яz`~beyV1j03Ndaa<jmKBZ2NHm?uiSo` 4+$Xp'ֲMqF]N^PӴRܲ%P9VfO1^4,p.@qtV옹v}O?c/O&ԕ@$܎>o^=;_aXKiq\:dZ?i8)N\&NQE;1;湱%}ER+w>s&thi㻡tQ_(c-ĥ# s\Vx/K*={k4oM蒨o۞ǥx5?tԦo<+qA9ḙo,ee;qY/k"$b:7_5çH$̝I J^2( ?s*oR=a4[Z4C,UjDmSӚ\25oi/+X!shx g8ե8?y]eZ+C4e 9Ӄ 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-20.08/images/sphere2.jpg000066400000000000000000001331561362435004500164040ustar00rootroot00000000000000JFIFHH ExifMM*V^(if%HH0230E0100Fotoxx:resize|trim_rotate| Fotoxx:resize|trim_rotate| Fotoxx:mirror|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 856 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?ƏR k2ۛ8aifE]@#+P ̏k.Tu=;П!zSY&$mn8޼>x?#ubD #Eu}Wź3P]NcQ61?Sw-2,sk_E#xf[xŴ\rzOα{gDfw~]Fu{-ëq/߀}0r}zTv>x[/@^` Z*΋ LVze<03'|/|?_znXVn!8c _Ý*mxi }N B!L4C֥NME7~W6KԴY]Bّd@s?}Y4ZѴ⡚+J>?S3xaq3K0 +{ Qj|}$8>A.}yh~\[|?eh*6^NN6+qo$l qP: ;/>e]ֲҺiWͻ-_>-28/)`v/7YqpxKGa#:M0u(I-o[Qd?Ҿ)sxU \#z'6; n4#nY@cҺO.,K3XEC =9xN;58'~%cAa#nW/4۫2ΐų n bj7YFu)[&qX*>ڦsVS⇋D*=sc|` Nvޟ5| Ƕp JژhF:v:#6mve_<^Y 7&:]_t[-k*kVo:廒(Y*_9lA>Id]?k/1BEAf'fib98r&MZ,aw[ O5?U?E݋*0'>8?n4VZSE$GɹS{W|boN.c &+UmIRi# hj}-a} 5{7֑Ͼ0#Qlk7i{g ^JDbf YOr+_+Oqu+|##=Gyp{>[y?Un}M+.H9!vk:=ݺU¹E~+?W.|O[e"LۃlQTgox5*XvOpAu ~|3|/峺Ux)+'}NzI%~=xwőYx;ĺn; kЫk.dV{/ZO&/66ye?XFc2Ocp1ٚx֥i)yvBS8_0*{*ʑJzqh#SY,ʲ}x5}?͌|v{W.]<دY2Đ࠾9An,wb2rX~UM6Uh}SZ[MGymgʸ^}ͼG-GG/"ź 'zgwv,5Ŝ+Xȧ>cuῴxRx&K_"zՎZ4_WGmM#^1 36_r\bMf=GfAjR貛..#X,eS:ȯrWgy7Z TA稫3xNд)Cָ]h{K2*85A.?^@Zi%eztוhi3]@Z{V8b\W|KOuhtM;Iwzqx~ E&ַ@Qh kj13O˕?M?jw7Zvl`X8W!x~+I&/<55Y_rI0xr8|{cIouQx_RB'Dn =ֵ~%wAҾúUv>b |9w [Cu7L*&9>#{PѼ;w6{mu-f ssc}il&#'N_S|/o=XloZ8#_pZU)1rYN+_0y;K'$9ziQK:ȶȾh$ּ-׍$qx:4v7נZM-լw9QW&ΙG<;ZW1]kJђDu=f-lo#sO8zuRWQؘ$Wjy/cŝ.QEDR) S%eE @ @ޛqn/~P[LZm _)ɠ=VCe+/J\B˨jvhqe ǷzO~ <,sM6r8ҿ/MmM{{{%ģ=9Agޮg xGAw#ҴT:>ֿ2jr1'lr$jG+TjEZ%c\6@֚/Ϭ#~(%]:QjV,E8 2ꤎzMҞ}:)lku/nһ#Gկn^𔉧nF:R Wct_:At׎־?j^\^~Ոw ?A7;N{m w7}>%^6s B%!t3l--x}?iq7M7^^Jp▞K.^I&|Fn6u][ΞzG14]zUa/nke "7ȮP=r+cwm/imdMu6zq~ Ҿ"|v/5++yom 4m~k2N"XMEY7.tdҖË4S`ݧQ^/ޯ--& mŹI`eyE}ܵg%Q@ )O28=zG%hdԵ'IZM4;}_ZѬC kI_P⣧m= \1zzbuoWmV[od V `^ =bk~|l4]fD5kq(8(?5g-M'N0>ĺ5fጤJ7,K}< cjwItvkcOY\`7ۛaǨ[{'h> \wᾯvAj4Vު{|bxFs~G5_.k;8;K:e*/j^YFMEcR$@U'W>gkOl]x{KM&d0ϣI,pS8GEūhN_\*9І#V>/CYQG榃O0Y\VF0c ҠJ74ȭ& qlX`: _VtB{OM^89$ĒO=}bran1UcrTʼ)2+h:~Þt.m$vkO'wmcRBF0#ұ>|"A|@5/'#G&Fj 6zI>[Jxx#Y{b!WҚ2"L4HbH}N?/uu}:|*(}$Y~WÏ P~$k}@zY>2A2ǥxL8%oğb+AST:Amƚt hܠe5OO|QSHN+8#kle-z ү,5 ;[[;_`玹?KRkCwqi~q\pv:,]wu}Kw\mָ!o?jz.-ZMŲs½jXZXդ+ԦZ/G3賿H| ^j̳͢0d9ū_d6jQ|ѺILWO:Narα]2=;\S>I2* d~4ּO|azxHF.- $ YOnzנ˥v&[)QE흨\mBKg?0z{5|u[s{qm*lgܚ Lm[QĶYyNk̾xCZЭ4mA2.XH$y8ȷ(8ƿRw<)9-x0@$aG_[ῃ}Y'ѬdŬXĉ!ܤ_|;ʼnNdq<?Dg}i|;ǨXizou[ݼ1 oa4D?f=Wn77co,R2$d{|#u.c,0rz }+ɺeivE 8' f^4iO h#8g@Xliz*E3x~'6yX\{9OTƛt//ެq坡x<soĻoAѵ_j1<:wۡam)j2$)u j!B(JGV O0Ūi|7Wwe]0,u!KX ĠzdWt 3#\m\UO-{;yn0Xߥsj)Cae3껟༤⨙RRxz~xkk$lOWX+__ UN1$WbC=Ǩ<צ,oỨ Zě}*8vii͉T跇nOO&Mw5scWmΡM\H0!S󎥠x^Oo`Ebb ݪ6|-.kUvY|;2XK:s$w<pHI\o_uΟm4s1&yq ֤n` =pxjV,b{T̾6Q6ϧ&FxIǃ+&}=e[m־&f{/ Y?9cd=~XlCg}jxQ>7EݬMde v־ '|r~tJjCpҽ*Xh,c[:Z?-J*Qq;Oz֛7—}z\c[7Y6;+E\tݿL Ҫ(OKJmyakReG<5[ϲjӠscǞ;i IXL/=O5yww0.G-sboX-I$N}߅|nQgTN"Ɔ~7 JCXvևp&fj::}3RŽSv7j'97>—ZƣqJ.Ì>2">%.>'1Yn$ci4k׫d.;՞8-Z|3GIw+9g;׻KplD34me3&&}0(S>Ծ!x^|OjlHfxvn+k(24PVgf$qs\ K[HD8$.{uDgisL JxrH>Ʋ%~kS(JvMh'Ӑiw9I=OּW}v}΋tDo־I.bN{"9tFb$+睧R ;S1~~3osȱE"l@9=VMzٵ(#9;Wڍ_oIcd26rmdG< >+xLS}nX"WgïGែtୢE2JԆ@\WuIh gq2p>vGQl9ݴ 9߽qfyս؜>YL~& D>51ͻ!흜|]W@#YIUKMm{ԟƿVZ_&[um:偎2p}x;Xx,;GhS*fR^fm=~;uuڠ[Y3z= xmZ^tA ٷq_<>7&D7v͵d֩"а@ߝ}t%(9U't?C ޞi>h~$Xy;Rzط.tf1<[bO v_RxsQ"dpwGTKTQmt`9S^&oBu,yF"->s~k~Ϛ^iZf5hu _Fa{+o-7o42߫D.-/u )Nm7<:g#|Sma- ÷Nڃ"Tk@<үJguM"C+BB*|`׻}O-:(xWOiJmDn܎?Yݤy wiGCҴxKty_>-g$͵-H I-#~dLjn;_@.m2(1qZdyl~}}6]F8||=Ty|JhZ3,+q;7gW4.j<$`9x) [K,HJ2k|?}tȄ'? ~dWk)#eKݼ_<#ۑ}ۅ z^7,]̳ژ`u#?z 6$Ѻ0cfeԄDFS+9X}3%ۼ!u`}1ֽLXbVBKAF&G JbuIX.qwצx j0; vƓ8|@&ZD({XTga+ۿjHao և[x_Ǎn#[O)iZ( {5ןNLɶ7cS.\?4 X4aEci..Zf׽~ossK ]I@~W߰OkWmd[eHۊsX;D]mab" m^K=ѲFߦ9W}+]m--J>_k~uid:hWB?#ȯ[/FVgنǕhBA[M enbd!#vq^Zy{FNH?Hҵ]=ϭkߍxH}m3#5r?J_@USy7+?;r:WKo⛛4P`M|}8>$UVRfx=p4Ec*O~Zg|Eyu-?\ß)ٰcIvz%{w;w*ccOB~5J[QWym{3lXrcBszF_LpE ϔO-1ֽ]'~I WME1pq#B7c Zic0ڞ2P;8 'ZߍS_snB 8 |w,0t}ֵox2uin3:ϥix]|]Ows\H5 3 Q.5WPQ0#.Mˎcִ'>kB ىբBArUI'͚mRT' ;Ik<=^[|O_ |o-;ԭخPz/kN[_z{.5 W|<.8 .r 8Ҿ =%7wc2 :Q;=Lby/s*`7E̷[v2+Qt+-Ϊ$hẏ[ M *, LV>J' +kfe Td ;8bxqnZ˿ wfq+NH}#/?ȱDF'!#e<ۿ5',ֵM>QwGT 1^?Z?k]:5żP%YY#VTf͹)$[s׈|d޹aѴ۹#mwtxS#({/Q!~7#$OlwOƹ7]ޛ,Q\ݺaMRsqi@%մ?T*ïc]%--YY$z+05ehZ3_ ۡwycgXjoMZ(HWYv<>$W|,L>o+x 7R5QC6piM:PAC6:5ޣ OKczuQD#A'}k-fX. Onc ͼ|+GI:15is7mKğ e0j ܃EǎZ.%jޙ#\.{;?|--K{$ A;dc__x_vJ[\^ĵNpq(]89sGrOo洋N?im#ekepS eU n+O\a!cN{b,U &6rE}N58g#b>;w~<-wAuo$$BvC3֣ewZͣ.[ 13 ~Jh̩P6]š=ׇdWYv2`+c(**P~~GĞW+y{mدίsk srK-Y{vtDAd)ʱQ;0c~(}Om%]Kg$hP/9ɩi]ysɾ lO홭x*\$褩&]FƇka#9c;$l=_zJ[l5*\mf-_:鯨}EWs.A=JFՅWkXċGcYfoCcoZZjh"xG7 b89S֮_xmWƚsCڱ>ꧏzRNW5g~GGiZt;&cC>xŠﴸ u %i]F4g K/5aF ֋$Mķ?Z=spxAAp`-M0p?^,ZYB:K_ȅk,IM#'Ѭ:)DRa@+(-O (6PsDžM`*;u`' /V[i 1~^[ X%4`K;W>yR)PѷvW#w>Y5Uy Y(r$5/zӱ LI^O|Q}-XE3d k>ė|Io<+@{tQڱMRi-O+Gf -!s>koQu{ǃ[Y?[m; Ҭb(#}}/՝BKm` ";֟'w|?w vщmxky7(>kǎYQ.XGFzu:rW>@gZswn!Og2 7QҬ-}xmטfRxOӴzG~yrhcSF1пm5d ϺnNNָ_xɴ K]3E^LIwz|]xg^)*mq?^7]/2346laOK)gwstyl~~ϫXC>D2jَXWY1QNyH{RI?@N(ڏŲce6sȶpN|۠{X'{umUZYUgצ1=!5l8o/Oe3+q9x]W|[,TaM菭J=vͥO0Jz+Ӽ]X>׍02y tRxLH+o ,R}XѴ}*M:SI1 [a񯓥]=yM?C?ࡺ~1ܼ*:P}dy CvgavQ鷲 gU&SXsǵyoȯ,HnLleV}Hx 9^ž5 xIB0ő֩kO?vѮ]80R2p?0;4ph=lN{ƜG5yN2=Ez%Ѣo7/^g~#CoxzR9&LcrԳ[-x:n'ĭC^7c?{+n?"J4j4(yC}Ay#x21.^Mz3vg˃2\"Ys)`*-OZJz 'DŽkcRGXfK'+sɈ +Ox{ƍ>%5(cݷ7jߌ-|F|Ex];PۡCw>Jy[ol#2Yn&Wt(,No+6껳W\4o/)kxQ#S=G#ba+}iyG:Z7Ye]/G 1jWM3FHx7~k ^^-\ \ȀCx$r8FFֺ)NߋXO,(>bZ K_`jK~On=YW?LWzdW` (?WFti-Z:`ێ_ٯ⮐ri#zo'kH p>yMtTFA|mwJՇTd8pSJHI'{0p dγ[ 3/\`E[I_x?\Gs۳Oj:l6RI \gN]Hjjd'*J2A :2N3VLHrrKc o<yvyS<$}~:|( 6@یW)RO6,[ZEj5 |Ýܩ_OOGx_R#%VpؕUS]>R:`d(`ފK>rO^{h>Y/!w D%_+gLּ=|Jhe9sdFW%Fd"iEgëOIs"ϙlc2k X㽏lxN+ :j6mQ uǨw0%:Y?ioN4˨Rk{o=U߼)+{~5xCMkbKX^tl9r2s>N\i[IncUǟ: (>']ؽ]6; Nwyۙ. P5e_/) <ǰ'񩎘_ gm$Go#>&[.񯛱V2F }o6r^Ikt7V5~?,|R־![jۤmhBgқWLkkkrv둓_:BEHnf(c*x~qJu`YagOs~ f;&|;Sjl(~l+;פ iF%goZ<`ͳ ;ye %Er^IRC),.Jˋ'%AS^ ?[xcmF5tf،dӧJ,E7yIǗ%Z-x[­3-vxp߈ vYzWZ+45 )߀9 ;Uτm闶Kq[c=O5O#JxÒC#,cg״~]?j<cvt_ Ww N!rY_r,犧mMN =cxʾ{ZTT<\vYK BRmEēDZ;v^zs}U=GP֬5{ gF,mf0АV?U#Śn;d/Vc7#F9ظ."V %Nj$jŮ wztfgf.]WNԄhPH'*kџL|Km\i-yHDu$soV potPFֺkKF٬ fkby;TpNWgͩ:ӭ?uyՖSǾ/-5'ۉ Jr~8-?_AθҍjX*ꋻ>'Ƚ'~*W喧|HapG^u|:Unkagc߳:A\]$^7>+mo(Yњ#KpUN1wkB>H9_5rBYSXw_^ ɮۛrJCv藱h@_O|9ᦒ9W\%a,߻` ;jƿHîb GP0:W%GKuH7ih*!W)Qu?}~/q ᆩᣏ3ɥKWl?S#kȀ L 1mw]V23H5jve u#Tמ(.L? '[ݒ8ark< ww>⛻|R v?]u-mrlf=k[u՟Cx v$s$ &|!OM>5ͻEs{\!שeY;C̰ 8K|vYL( rg<ǽg0oRO,N|bx G-ҬPc ikw"ȖَG@mʷ$qZbtw֦j*Z{7# ʪv7|Mu"W]Ħ4kuIIc&<c={7Y {|/,Fi O|^ka 6m1DV4p=5~˗V?Ci5᷐Gt F9K(5&j~ 1(p+Я#( ӎ#!aя~Ϗ' 1q" c_w~־S8~|G|ox'\Ggr 5(\j3{YcЧ*|=gȷNMkF&!y!k]&5z8=w|zd_|=K߈4avH~?ʶ%+XۤZl%A2`g3yٗr$Ҍu<'L񎣯EzG}20+]3xG ]>{xo,B(9ץ|9%ZǐUU<)?࿃^!$Ӥ-ʜgSrwJgFHgOο$R#s| ;J"m:~3j%Lw {%J'? ~i&ZMkm^hxo'f%໱T2ɌmSx=_O igHzUݽ_Ŀ>>s[!i%aZog\ OA~'-t hHfl3cEt~c}5.-9˘\_$YhY ||sU[}͸%9@O^jcC'S$zHֶ^ƿ%$&l康u,%N`b JK4mf hkiyVv~7mrNs;tǓs'wc2q4 Oy|@!n7~PG~w(dT0~Gȓ$~)c<xi4&>P{"5wJVU1(k:IA hrҺTnǾkCᗇ?f? Kkf.#;(sfJ*%RPjP׉)e⑭<^$h4[$ƽB4o[-B 3ː*>]Χ_\[r};~^ b/@IXsUЦ,~y)WW>x[}#/Z|/juu7~S^+cZ8]]^ oԴfYW|5+ST cX*sg}xN]nY%w ~oկ,9e$ P`a|e={.k??^!V߉ȫG ''2j俰',-f[e$8ѥ~xuxG#ZumjZ1[3n*q^ϟt-vkK't-ۃ]|7\&ٹ;W-\xDɵf/S{/k77Z~m$@H=+a&K>"~x_i3 6qZ[O9vWVv<:R9\ΡgIKpsV/+ixX+oz_½4~?a^ZZA >l/ʬz`'?q 8LlPI/8iښ<'ExRʉylo SڸM;㯏4l @蟽c6]kr5[jRu+H9HμaUB;uoͶswekϵUJpA~Z<Wy&+ZS Eݫ ȫگó5zŵyi(;HwT}szFC?.." 7y]Fcn}دaO bvԮγwfL9ʾ%T`aW6.hM[waiծt99~,u=>9 p2z;*MS,Ŝf]OO򷰼ox=Eiko+i7rLrvצxúǃ;z_,li mNǥ%fiJw=>-5~WV>yn] f4ֺxTAkNտ_Y|°S1\MZy׷ +Dwg<_q aGm%v: TI7+>ӧPeg>?{.)-Ӧٲ@U[o5hϬZEo+QL(q\?r fkZ6O'?zm?ڭ#G6 S󘺵!VыqV6-x:⇉uڞ==ti}B]lPJOω/bA;y''n+̿oExg]\HNrߍoG+lZF,ZKD- /Ͳ*>1?7G:3džQ[CT \ʪ"\YsY?.ɨxZ+k& Gb>GhrΚĐ#8єO8RyKì1[쎤0^:70xPˁk[ACa֤dʜ`¹/W>)hG,4+]Ju9|2:Ts%ՎpDؤ^ʲ[i42)PVEXc=krUR6y?|(6WT:{XBXV$ⷄ7x~>MՁ٦(`{M}fX$ψ")nHKi]T7v px~W=hRd?/LP+#Bnt̒%cxWĈ}sW/Z]iQ 8$Yڝ󽎹o{" [uvQ0gR:ֳ}n'K}28E־mW^ Ԯoh$vc*d~'ռCZiH,@R6%c߁?! p-I-K'^OTM#-MVE? xƑXI7JgqV!X34V'X7Y:xnI kxP̍> |}j0[BǴJpd(>Wd﫺J;|Ay7Li=w;w'=MEO\khun,YVI>bX2}}?$մNO$xeQ~7n|P \up8e%mȨJ01i_xJM1ʯnkwK:Ɨ:Om!$@y'MfRTIzڷp\ܫ+y>&XxID1p@wS~p4{ݤC!@qҪxV{i%kxIrFGNs:9}8ԖY_7?v(][k Z[PMӫ2O-ǯ|ۦ`C 6s9ONkƗh֚ΜHg9D=Nj\)y6P@p3|8TrgV';ކ6-6-IGgNdy85k2W<7=ŕMnZf}F\䑞ΛDk{y9i+,>Ugp:lnoي4OX;&Vw>6ca9.sJIkwBÚ11{D n5+;} e km^WIZ?1O ᰲΧ}j4] W\qʏ_z1i"Yu?# V~fd6ۚA ƾsMN!6Nx"[#Bmo=E5ĶX٦O(3_c/V GYtӭ$fx';A'NWgWwAa8\ı@1ʱ8߱g⵻,-:e:ev$ p/P;edL._.Hn㝭QObU%sWs˱7kK4vs Y~cZX,N=Qœ!8ܿPt~ `"{5\ج%1(rG;~?7RUcf~IF(k -FN gzjo -e@:fſ6 RpҸ<9Dr9 \,Z[ic# ҫ|V<~^757L5lN>5?wyao jݷ6NxRQG͸Et?'Ѽ4j帒޼iՀڣGi&%$c^K-燍aFt{O;}w2ma4{i{&o_kw>1ᾶ6[ w`GAkGObH) ZzzzWwۍ6ݦt7o!f#Z'? XL4ˋv"H"%\zS+6ei|; 84_@u&4BҢ-w>ziix`Yʞ`?s hqQ+N8~ʪe̹x|fH @\F;W WXjqqP3E-D?pc }U|"MF;׍xn$cX -Omjz53 KI&` 1/ܶ/ ܷ_Zz/'7JmaAty?ǿ^UaAYJ\?cY+9?g].eoH[h*?ɣ[.bhIhZLu*sʼZ s7nDZYs?&O1?w/^=<;jF%&;Yn~e灊|akV?ӹ|ֿ8C&_I.nMifw>cbڪ#-sEp 2(D'ur95o\iWyo #N+GJmlBKHrl]Y5{;iR6l? )˧\ҽN\Ҽk v =|TqhWGòZZrKZ݇VW࿇7dnYn`"~"Q_R_ ZYH. ו~τRiH_oN2 Mue8x|dS+!Wo|9]gZѮt[moM\AMzƪK|BGhe\u" j q'-еUj2} gIC[0W8#'Syv5Gݓ>7ˢGs|4nia>[^ɍ43}ZP,ee5D/gwzxEzþ!֮mGLьW8hYqZ !y)b:gҼ K1toF<;bk o웫w'G;&Oids⻋[tnY' }k( jo?0 >zPgl؉6$ |-v4UF4|h `Ǟba\DjB' 0x,т ˞&_>dKR6U}kF_x~l(}kXfKiROhi]2DIMjO57t<@# ^Oֽ x|=xGU"ir=AQjLKs^Ica5[wpkVլ'O'2~I7G>k_it֩!Ugxye;eQr\o5x{MյH"\Ԟ)3*HwCGx;U[uX߁_!.>s~ө\C5sE91W\3O(&&[Xfm)+WN^wv1ޓ0lupbpN_ FB˸¾'8JQq J2jGUHčl]؃ ^v.<9Y}Gg IܟJW|AG5`Gԃ^j:z{^w -F93ְ1w/+'Q7rTG]AwugE} ?lgËgM]=Ag~zuRdu琉+~ +⍡lȅ <cB ,8 {O x~go~6?!־Am1Ƴ.ǞB~ߴO˙we|?{J\tCM.lnm$`Jд?v5Mxxgl-|+Vr3@,ڼkuox_^7xP9ok?^~+*=\kgxNojƾ2]CuҮCL>N'5X2H1ڽ ?~$ ՠ[4VHdhoאEr$6zI,sW%jUϮ¼+B|mybw¯ ]gzŤ>]vG|36sK++ {UV_ҾN,LdT*ςnoxŎar4%&IR DZ_+K-pGѱ$я9 :+x?޿>uǸ+p=koJ8Ϻm?k/ W6Mr08Oɧh eCȏ9n?Ks ?gYIu t+AfmL$m#RK/c^Sq=x{[.pL]UF=n:Ffv8^o٫^ν:qqzĜzJ3zVakg,(rz+@c[Hm!HUW|*͙ԼI[ j3 g&P:/o c*vsrgyQEtRLTe[2zeƿFm~/qLO w0u'ßEIg_|~Ul1Z0V'odӴJdIV7Y ^wq"]DNRcC;A >jӬXQp5^4UfWUrqmx˨%$|w4YxkT{Al~c>خz[O+f>^m# ̒[=xq:w&$Sn MY|o^\:q˪&6AsݴGqs_6w^񮍣+[}#i#az ܋rɌW Խw%3_;|mͩX4}eL?y^st$qZ[^=gO&-bŲ6tl>ʛl@_ܱ_Mmq w6*R=x7f} ^eyקj}u?Z<]Oݏ[ 67/Bד:7_SQO)BgLطШ.y>#xIXi%F|'CAٮC]{ZX~}bҜƄ}gK\'mi@= a+5R>–]8'?h|]OnDJ ^K0++> O5ۘ̐EXپIm}?i-Qšf=*6u(E-,skc*WmMh|A4FAu;#A4- ztk'Tİ[d6j.OwR]=(k#YyOtw`zt+M8ϙӔϮf,Zj,Q0ANXWռnJcAD8n0piX'5-9@𦡡qm.a zZVZ&>Ӽ?/ޣjw)ig[\i1 pö=~ižӼPj[nő_g|dk %#:-Ęd F),u[UbBynk NqX\$}ש~? |=?F]]'&~Di? U dw BuX/Ww*4Ÿzv1^zP58 Ѽw6Y%1d4.k[:j}$evHRW6IZ3}hVT8duCt 9w?Pdz>E>J1,5؎&iFUSh]#x ?:͎ylx'I-N)Wk)0p(c Y,>3r"3|ϗO x̯?DmvIqV"1t{7s5&\JFű7y/e?݊_.(z!+}_;(Djcݏ~ԯJbLSZtUrrw{)S /%e6V,>u=Ҿ5kMH'8 :BHj/Z102ξY񇍮+ [y E>[x~HtOkc+ӫtaќ܎GVF6Zs[7dj9nmZ-Bm繶vƖ6y]Hwuin//Z6P:|k(5)򣕠s,.K?qbxo.t1Zʁ_T#u\o6$I:!US'S{+rJ0t5d|9vDžӒP6}Ba8Y%۷O Ƹ@5(7g'H~8hPBJpq^1+zV]X{~[(y:fZ̼w ij+:iQ'=L\zKaW"֑џqɽ{/S˦/' iˏVy)fmB랹G̗7@v]yGAsj3'd-oiW@8漧ſ&׺'Ѥs5/gҾ\5wj.s(K]v8;Oz~3LAKܒk?MR+ݣrY<| Yyc1?>GOTЬDՒͭwaPY\yGKղ!m(;ذ'N{רXMVO2ӌvz:FC XfާcLQ͒u9]\\+|U}pڇV]58KW+~wl8 ǕR71?MƲ>7YJ$qdvm$mڽL‡i~\qVj ޞ[Φ\GrM$+?`4>d< s;c;7FLryt?>:Cup'x85Ɋ_V:y uS9ea0Xۻzڟ.cӖ0I ^1_~7X5NnYEy d%&15mAٞTa]Ԋ 6e@FoW4v_D.0k6\{_+nñpYաmF+ÞI4osHgqQG>3Ųo AOi lm5MS\,_53C?|A}egj~MY7=GoҮ>02(휞+pig^ SΗ1-=|0=[v~ Wq䰸v;l*1C,33 rvO\$Ha-:׷Ib!:pO6ۻGj( ?GQ^#B~vN5- Vf`xfuqq9MJQwE'cV΃qkAbq6rOZ>/).<3q ~QN]On~,V-~ڍљ"`*ן$Y>#ď1`Qֽ+KAuE׬#4.YRA^:]w+KfPl䍜GڿVeK1TU5*]_cG1us|'([__AQ~eI<+w !fmzx/:%Zg9S0@}xl+-?kjfRbr%^k7dEؠ+cKKofE9G5?T8Egq3kXhfoO>+.c1ESFM|΃ۢ_YQHV.HOlGWk)' 縊Z^}γᯊA/?>#xTumF[EW53ӭl&f#Jr짩ag<׷_ "N~ZQȥU=NkXSgRSwcii[%jf(g6lqkm>q}~z޿>g"K֕6hG=־ɷRc2_F7_6~+{u{$kBB)6R(oҼq[>?,ѴJ,k飸g+cּMkљSVԴeTpwۃQ随xcvo30WDyYf>j '_y|K3jzH46@a@4@<jt5Vcu."Dv(*xTzy4 ABijO84(dW1\Pϒ ހo޾W+M^Qx=߂wϋ~9x/XGӴ+) omѥO4v(e\W[ޒg>~6L8xKkm.y6N'F=+8*xy<3⿇?-%tTyu5Ś^CKn $q* AN-AmVOp?AxwOwdB&,>](`cﱸiJ0Ϟ|es1.$_\Ⓧ]k ~o,3)S_z85wLNX^+s*2}+gٴaOkƬMj[jsuj:%]pJ%H^WjowY|&^6N$S/"~P¸l}kǵtf$%`FYT}s֕JԕW0i^'ޯ ?hCi^'ޮchQ@+/ii2IoVcuA(~zYYO#ب>\7=e-t٪~S^Q\yxhUq]T3R ydxNX;ɍ־]ۑ6m7pO]gC7٦Ob#ߊ]cbޑ#KRUɿb^#x222|9},,Z[]/4+{`@!Wj<.w] IJ0q\ޫ_ъCsE>.O{')Ӽ?N3 #op2}ோߴh~e%SW˕o+9-` o|#=mCxl,6$|Dӯ)|݂HUQXƼ}iSs/-9Y0q^~ΏnǠZI;!~ugs7֖Zkm.$҂mlm|GGƒlػmWWaF<dX՞3@F[=_7CF#~S(s^-_[ qfwzMƟ컣;C}%RTP[w_`*cA(4FUy~y?Y+ +}6G#+{ DszݤxM},Oһ*SU.=G%%JV5WZіhF~bjog'0?*?k?vv`Ɯߴ}|U%>++mopD{8˨y^:4ju3} Z̟0k3ޝk^=cY7+1 Qsb~mjF4|!h͒kƿ]G x,&=q&>/?uuϦXPq(mP'bf~|}jjpXY]fdhr4oQgŞ]\<1K2=Q_HdOԭd8#pO_|m>AW<\>_g}5,5ig)7Ghp#9 2n=} ~)Յk$వ9ʞo>[=3MeS#6IV_|}侺ZNpH#R|v&RT9|a(COޫy/k=JT]-A׍M_AzWZ=snc^׎OdYP&AdWa[KG 206 307 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?úM4,l P 2=ɭ?!XK屦?gV/]ܳbUW$MY,DMCN[gf xF"}pZ-gl*P0yPsv^_%xS~W\՞-輫'aOF ink~E34qF䑂"2X麇u/T6(HWV𗁼> M2'_-{<<fEQ tý'-jz7+ŵJJ]ĥ.JI ?ԫY>>9&dhi5쑖=X5| RTGR,=&6c$eB7C94|$ΝZY&nhV$;io 4k?B![I}B;ɒIm*i"% rT}OG|/N:~ױ_Zw J=+|~_KԮ;f+6}.T{5R~?::xR=[R藶ַ$n !~5M6m7M:g$vY[r})<6 _NN7bKN|Xtin&c3M#mHr}{ e]\i`Q(G/=z]Ɓ2͵tI\g9ؼOv\yjIُcg<21DrXV2ͮ4erԁgOS[^Zck"}-[us JN}~8.r50[Dn'K].ZQ79PsQ|+mm|g${Cz.1ҖK|u>ך6$o&Y5'y6h.:}zq?*6/G[r\GXhXkbt~Tl_ʎP~amBm7P!.-k.FkK_:UtZW0mn9t=Q?*\sz_khKjsgZ5z|h4U5jzjao;'{ؿpSm|e'Ak.6@~:կFoes\J\no\ "f/GFQ\o>-Vծo5[ag}$IrLS_⿊$˓w5\k^?tx~:82[FC,|Ӿԙ#qLG/OwD~:'/G7]81u݁3674Cn l~ȯّv]_Zc?ff]n+?u>%׭\\mơoO*Ư \-%( ƣ c qJAؿs#'_M?}#5G,F1ɧBxzwl|K[]o xK-^Q$ʺXi%FYk}|po[" LjTֽnu W$$LGZJc,tGWK:jʡHUT[:dl'[+jKYkzt#8#O &;M^Ȁpr_CҧўRn kU(aq ,x(4ڽũx`ip5#%O;JJSXS Ig;t1q>va ^M|~,/%l[[j{{Y؅g#;TzK;t$f>,nh>_6[TE(ś|Cn;PJLHe8 28v⋽R:g<=sTxIY:},[Oa >g :ua$ @$5u ,tkuV5 ;Pq <_k={P  8 mJ`cĘFʹێ2 yÏes\ZtYÐ Q:xM׵mF@7heqɐ`C\ω>/WFM[)&o d;JyNT#4þHwXik-;Wi;]#gp?-uxķwW[W $*[̘"*1q #w hUKn.&ݲ7\^T&{nKc+m byc덿֩O`u#Jlpb#Aⵉam.mhp; 4l.-QRH[#!؏ˑڸ p'M/Sr$,pM^i&Ӽ+F7Z[7F| @ BNHxV7#Rh.z}Z}VHefZ%ڼ-ԦO )~KO[ ep2A=z|)x7JM7ENV/B:9,II&Z=7څH# Y76=23OUzEU״54eqOmb"C2QEQE5cFv;UFI=:kV>#mu=2/,.I SЊO`)["jq@8קie? {\{TO-"#HP3X8ԔW̼sGy< }*P?Ěr_Qٻk4t1F)X+yfv bH_Ji\]Gq5gDCm>`H((((( ƛ!i?1\W9#IM]MK8 Wj:Νsa}m5͝m нta53\kQ|L*^/4H<6eJ 12vf Ӟ95;/.sq3&,z1'RJFI4+Zyv8jƙ믵ySI!M&">1iQO)/}p\+8ѝo#º0(['l??WmΑF#mPgk(=P 9˚NKXu53476wu<czoi:jwӢ]xqmf{+mx?<JUꅹfg .2 Xc#֦x횮5EY~꺫p=[~4AxM'VuլdJZM:ǵ[uOg Mk*_[ƒ-^ '^}=RK2il ",9=*̾fѧ{$ipqq?/ҿ__q-7yE~i1u/l n+fQ}0.WvO[Íپ g<~u"OIޕ6r\=ԡ$9@I<׵5څY\',/:qI [/%-'vxEO>\xZfuҼCp S,O˸ 2jZMk%ˬ=7DY$6~aҴ{WL;腤,v~\ڪ?|Io+KWX#$A  @A#O<^2~W5HIuVevu_? kw>Xծ]^a^5QyZ1+N9TO-OfsA&, cǡxF_ ɡɤ r_}ܓmJM$K~zeSWk lf< ^3Dmf#@8ƨk({GMKk fO28€8=֦EEI\}4i2$#n 7Z=*O5 kiRSwV ىEuyx-Aյ!yhp&3HY\:/']!.dkF0͖ǭuZ_k_hVҦZ3XʐJp>S[^Ɨ'EOӡ.EY63Xƪ_먬Xx i(tjKCt1DxPPO=-| o߶BIa\Y| Qޭv__y+>~-Ծ#@h# [Tq9᜾;0GCۋ7id. ^Cl8Ur?$]fLiHZGAd$d0ʗOqv=J7N+Oi1jΐu5ws !=& v  j<+OmۍcHoM3pHnތ63ǨQ|ܯ2\/2o>k[3wsooݶQNجd4?[iQ6Ze j>!R>snUہĞG)^vUz_;omqqYb_ `0Zj_VO²4 v(b9Rw濴5|ិQu_ZpAib&gfbJ/FݥrVKR-K6 r߈G?^ek>ү5KT7hxHc2+*8' ּ㏍?_A 7d>bܴxrwsԣmo Gv-%sD/+?Կ#k+~k$|6|Gu(k}sOu[JKUଧ*+&4.>ɞyl2ʲi#XK ;8qST0˟5@Š((((((\A$DJ=+ý?¿,y5RFA$c-tyN=/PϊumN INbX|VڠGy֦kM;Ԯ8oܭAcWQUЛ-X7V~2WEq3!Pm4URv9Ú]揥Gk}ͬ)94v@JԨ.V6dq\(f=g>p.'.nnv%I2nBaUowR:{saKMFV$3r^E//_m4-'h: PIك4r+)V#tn JyaV8Ozu\稬ok~ZOVҴNRኖ23˯[b|UXڋInAd%IH5Sq<zZ[[=1V9!U||/㟋:߆'kxwM5;}f k ${nqqDۭu7V|"nvL?غ[ߕ m=-*99j^]RgL3Y2[.2u5߲30Ч$x^JěE8=먾m%K]0+ V? m$,Ǥ_NomUC-} Ou_ ^GUCa"̱9,Ř98?YYh/" w{ L?z> $I$P#TKp>aEPE+I*$é FGq@EQRiZT ƶHB`"+g9_i/fIɢ ߤ nm/J]JI˄hQ`*EnqWY-",(YG@>]odeΐug?1Nc2eX6|}@`A5ReY5ht9d2ɠEȺ{rG@vR'k}:ww~ &jvz*KK-bY,IJӴۭ_5 [Z=h:|~On̒b!rd5x$F4Kk K"ҫ, ;Juc[<7.bG.lnۦ=e-g9<ֲjJҟf'㗈O Ca@%G}wfF2;ll'Lj<'hCZӼG=Lj,IeUhςBaVů ZQWC}|57]%n3&GL}6R5eQ]BK $_0iٯ'$u_F/J31۫!̎IQ}IMO LE vN-1R &9 _7=r,}ܷU2xG\մM:ͨ_4 b9L\ ֮JSmu]~3I%ߍnQZB һoQ3$2#\=zEHu5]S5GkcpDpv8}3H|Yi7-hg)e,6(=y|-vc^fּC%HI FpMzE4WvG=ܙL_xKY'ѯET ZzlCLq<w(hq<w(hq<w(hq<w(hq<w(hq<w(hq<w\e?M3l5f_6&yEYZNa#>A洒)@hyR0%q<}&? <O?QѾyEPG,_GJfY#g(+Lip`©Ē~Zq<}&4Mn$O }FO"O?˜"f90BO/?M7ʗ$(? @;l4mxC}&K? q[[OTL[jʻ`v)+wQMO:7(l}E((((((((OXw*,?XypA^^oG|Ab׶L,ʠ UFA2=}s=S^L'izW|wö6^u YOak#a"v+ xC$r `|ل_j0y?wslWkxCw{i o|ViD`=" :?&oT8b¤RL, '#=jLj~ >6صmѠԜBK#Hܺ'W\Xk=l q[[м)8Q^xJϊ-;\${DF̡$@9Cx9 =lYтN>k%Zi6dauX*e=[Ii'k6Ԭ5;UgQA`Cwi Ea#|8mumH&ͽͭq $o2Kc[w;J_[Mi$kHY<${Ml~ PD!I':!xʖ_I#ڮhVw0a7qZ}9s1{M+|&Q5O=6<kZaTŠh(((((((((((((((fotoxx-20.08/images/stack-noise.jpg000066400000000000000000000321331362435004500172450ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 139 258 0 C     C   }" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}Xx aOlb@U1'}g6OM4:xdaٗri.J>氾h|m߆Vߝ%?N-6̀$Yanbs x6{?ڮI^)nK/"HPʖlwo Nl>|Qdž/ᯊr×ZifylNe=G=%Vp1eʒ>zO~;xGKR.SeOyr2,Fҿ"D1#Ғ>Tfm>Փ}uy >}^/t_ & }^Ꮔ'uo _joG.ۛ8̒yO6z+ |7֬<Wþy޶uo4 9*7BB@ݏ:TDG։u4&W8׫P^u [Rlv7bIU!~]]DBh $prIZW:'xsP35R7oqrEy勲yP^|W/|Jl[ A4_*1Չ;N;1t<; v8yQ=n<3ĺ6-kKa9c>TFP)u=Z+0#5?^ ڔjGf|/ ,%y[_qNj> 緞)]2FUdt WG;,ǟx'GIq/,YRXM]U??P+t#$yjO~9+w7oxw7oxNYMT =2RhO^wIL~?:Q_oW=F+DxsIn}QTu=^,Dq1C8h?wKgˑ݁մKDU0OLd}VnfkqoWB?`oٷH~F+6}i \,!Dd F{~"W~,^"񍾋8mcoj̖?l4kyca"fy6a`7/xpF4%潯GOqr.cVkkYPĉH*v1qR?kރVުe~ yK`r"G T>B0>%*jz߈m2Mb뭨W6IDHΰ0 )TmvbGWw-vֶگ"-tYO ~%HhYfX"[pcl ڶHok16;R{]5/g {9dX#,+ [OG&fy Lnkz=ZNkM:ۊx!skž5|?ohކVoյ.-=g[֔}`]2f`\.Ko:_uMzXkmi5Q\"bp 1BOjNv[hqf27Iݒch0\{F> ɫ%<ڭux΍yڦ ʑ[*] c}w}En鶚yj aчROTdM9%uKvX0HUUt-φcҬeF Ңu*X{;;{9%6H {'ò[]@q%$n6pڢמu-,Ǔ:mqjNvĞJ@W8j 4=Ku>0{[C]YmbrC5ՇՅ֥?gԒ(fPQQcrK:)[Oӭ4T>6~p*PO5iKxs$3แK oα#ԁu:ŘlIDqm bk}.6D= 'PRWD[`'Qj*Kd{)~5 'ۙZZFЁ$d?lQH }".E&89|wu:((( _)6:6m[Ǩ[$y.\]}qOjZkYZہ`cD{/L:z`uo x3ëM&H_?~zmeiif{#˟ 3s ]1B o~o5m -'EfpVyޗ`9hB=N@ Mu?>Ȧ^(.'z09Iw";ZvoN+Ky*J:WoID FNzIk- qh,PqOsG)^_|?~!4-o\xi|=ZOwx\ŋKtOM[v64 Mgmsy6vpy!HWdUX'9:5/-5=up)!rF9n-|@!ӼYuxoo;8Vl aFV9 z~Z]SbÚc;˛M:+4.R9YKhC 2c# ^w~)EY@Ҭm즏Q`MQ';r4=RW 2+VYb1%c 0 cEmW72ONGO쨡Z _|;C 烮M .utV[{Te.ȀC668nmhA}CZ=ދ=I&,D,XmN@ RR~x _FidWA]O'e购 6xGmy?$~ (l-;"$:j0u,0UP\ 7v^!ҦZ.uMOZkǘ^ ~R2xᗌxSA|٧q<Rsee V5袊Šwߪմ7* Mpj#O?€4hG4:i? t Ѣe9.(FsI]Q@/O +;'v_G#O?€4hG4:i? t Ѣe9.(FsI]Q@/O +Mng,Y@mk|;`? ?uW%OkRLn'.'X# b6hoPMK9ϗpaFU ?uW%Oh'w4\?ÿu_/<}kiia-Ü[7vz%ŽlI뵶m?ԢԥkKM2stcˆUׂqJ}ki]%ݍK<$lV V5e=SA.K? [[x?P;H+>#!F쨮W¹nĖt^Ef&{8豰3oҴk޲961Ʊt xY6@Tg_(;MS 3Ǿ0xb PxsTe&F]?薃WVZk kr2pQ>ʠ}o ~741zvUҮ-#ڻD vqq\M?MaWuN!/ W?|ge|#'OLD~cf$/GCB7mGƾ-tOhl#vVߩkخ`e.f$p|͑s1񇋴#L~vۖd=hoЛ}q4݌5£T5GY鸕n:fP5j8lu->ŬhW-|kH22M@%MvQ񦚚_M?"KY }YivFyY-H +h@nךOq'CVy}($cy((ۢ((((((((ewjQ M-, A$~VʛM? (WR"]7^U=@.K5J.ߺ(͏)A?Xu56̱Bw E#lKml>|rC]Uvm7S7SNXM 8 435 1100 2 2 C     C   &" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?R8ᔶ|:qz~opibYl dsZpF@;T{O9\$JJ82~|V5iwcB2i?|1xM_ҵMDbVJ璘Q޷o4M:mB~j&i"c"g 09:|ժh^-72]6x8ux/Ρ<" Haݚ/>{+Դq$aɨ"Fp~$ֽ)Vp }Ug yh9 sji=cQ<~1_ LEHiKe~u.yC2r4V_tM":\.W:u#ܼ`w@F~@xQQPMe:f0o8 z}meqn`8 FI\8ixT{ojw5ҪE 0YN>c~7_wn|`~'X[mq l/ яkh7Һ䶖+"$@zg+/Eմ}KD/,!K`]]w);##Fͦ#G$`F.s,fxWu_վXxRMkjZX#=U%Ngs~c\}J~DϤ=۴"*>] {gSm#$'H=A#ƾdҵX.X4NAshݣcW=wM^-ε ^[yωb- [&Ub1}Y.=Z)';HT9ڥ /Ov 8I $x:l3cg=XΑFD4u(`|yaO/NG|g>%ƿ~uoǮ]i7PZ]\b +<;<4* 6rg1Gk '*3~Zg%Dkr;S_?6Ni{k%ۭl+[]Vf󔈷(aM.txK獥G )9ppY]wePrk(AB:.[!X0p,3#Njyk/hvO~I=}sD k,rH?O_xs @@;2|R )@K$=yOzd[]v:v*c[rxP31׎իKg%vv`8$v4VdMIm(̪rr17!ZY`K'SMJ[ / q`9laDn+`,p8^>֜n5Td֍cU#t@BP2,뎕-g41y>%];VC<=se) RqRXg ovlnN3ߞ+J+7[Xs0p8PDMF2XrH8[Ygr@N? )7TlYps!-'ҢF>cIkݳcrdgyoҩ,m @ nI0e.e c#9_Z9>UЎH<'[t,~EpĒr;gߞjXm¼HƑd(}ź+ے3^1ed`˷`-I5.ñVDzg< Z|3E %iX7#8PxqkAx8d\F`q^ZUOwKVKyUJ$ԦXOBx=g47eZUz#¶y993NK1_?9=F={2cg#+=]rߊm,•]:.qBH;sۥp:; Z&x]RtӼ9o7\4iLU@D)€E8]Q= r*0c0'󩙉1NT'&_i6v&>Zv{e1 |yk/ h|^5ޛm|@:=[3'xSZI$P^G{ڕS^}d/(hē/Sҳm>6\{ .h^>:N0!ipebg8؋hz7 c"^0S:=՞.azD#Z$;m@,IjZ|Gnt$rR#H#}ᴟsRv(kۀA1c:6p Wh=+ijMMqrVBd~X ymŀ)]V;SѼP5xv-mfBG*J.1($%X$c":T#i%F@Dg6޹?h/{ ŠjP\`!VUAKzȴ/r6w=v] ЗlF dvo}b nFx 15'wyƼ_%Cqaagc&)5?*<}א’ IU}mb4dROqRR 8ϯ׵9m,{uv'V2B2N?X\ 8cOcX(PǦ1 5#+!+M+$LԞaޜ$hj#W. 8Ґ.I32*oTm!;B?[-<́d_L)irCIKshو ԋ =A.&hyXH߷QA?Ҥ2?̠xA3}mY"L.T,.8KRc?K~p8N}4A*6\)<d0}V9_d׫|+JHmKj_?뿼۷ !xCWDԬte;Wr"GC၍֬[x/w:]ׄto@#9rB?t[SEp/Ɋ1hu#p"p| E\CӰf(R=}U |V7qܒIg UGG |1o%'ӌ mͺ-,e8)ǰ5l8e8$v\1HyL.͒nԖP\%]L3q:~4X$QmG JA=Gr3iJ&M9;' &h 2}ǵr~3u]SJ&uai.ލNA,x A$gsۡu Ljv7iiNrqT3܀@\ەcnT&KKO}7[^<-rd}Hz7|7T xz;8U]N_ʹNj#?+e-I2ĕP8=%أ{;3'ݸO^Hd@ڈco]-b }KPU@;jF%;OC֝Z%m9l۸?ҤHn\P\?U+H@aӹ݆S=OcR#'͂2`*0#‚pMrz3&ؒfg!~b:[r E]sdx+TF7`AQ韯Z&Z)xN=##VxԑK&?x1Z"Y%I\bW8h.al:QCc{cp\ZgxH|d8 ]xU4HojC}kk.w[$C?/֨Sda@P_km?,4Ckw,L@ H#sʯ4T5)r4&n5 u 5;۫{7Bsh(rEmS_i2mx؈u,1@p!T5lp]6#Ģ`2Id8$v v):{<6sگ}#Y'}[K:5 ;?r9=}46ޝqY$ݒ7BqM!9WKiw!s"+ v?.sҙqj)\XH#@=JZ8rquO_ Zm.Ė΁)ۻs[ҊkrdY|텽ޘKKx'[bRGqT9N+r;J{|G9 !;>s 2a\Hۑ:f\I9׶?Uڔt dgu+Y I>ЖL$$ L!F9"8bG'rLԺY,:FG80rN?*ޡq#b|vtN}T|N6ӨOh41H]xP.aKr/L𨦉f 8B2玙?5 L y `z|ݽ=+('M:{#LPLmb:Cll-̣]h2Ke[e TK$ֱmPe.O{Rqn誺;UJT[:횱@s4%e$8ϧLZ̅YEGңm?$BaQEP3Iƒ$&~v>jؚ$cFZF(201ުZs?syקyҍU%-o"" u7vDIO*CĿ3`_bNe;hf|ЌSnQ\"|Â2N9翵H'سj zr{W\&+EfF"a|N㟔wv$V 倲v{b.o"V#U}gSV]2xݻCfe!&Vs9{;`g5I(˙߿+"7Kp\1%W,/nRN2 2z㌑O;.% ,ۧjvYHD2tg7 G2It MmR9wH#Pc =p1PGr Llv7``2yJgY!dL{v=stMS>HPAq=;0VY2[cJ/ô l3qRC1an?bdB-]m;hULB9YXΜm>i3#M9@q֜T C==} b7Ȏ819>dey\vFXB4n[jvRQȕFI߷-մƎ8$A8|U;P#G;)Ar??ť[[I4B :KcEVy]؂3d ٫infkpWnT:O|`$HbUW8sWuC%ț$xm8>>hDhZ{kGI !%F&rH#w4ۂ|98EQЎ}|f( y1W| Fj s>ּ"T\-X1"?^ԫqmv?2IH_ϧyghm|Iki4´\s{t+\OnaġҰv:N8%̌O%xX7( #`: Ն-BA[bĂ10?*uW)`F#z~|ΣN/Dr|0|AhX 25 cUFܭ0kWę]RU|xg>Ր1؞HJZΕa7p7v<4/gӚ]pҖČd*6~Lmp:c>4,Wjݺ_6t5 ښl=@)X-+b9I>X _n)Ж;J + 8nl00ʞJ9f;ZAG0FsEʍ$ÕzA,rju)$ c*X(D܆!F{c4 N`ҩf9=4C=sPWORs0/_L5Ʌ<|4]F;zUoae"0pqJ) >ݩ 2 P{w]~\{T-[Ťe:uKrFpU&,Jr{t,,Ғ,jY➯#ք.W;tM!cyQ6f9ПƣmPyq̌vC9$zV45CYEc*7/rTzX-&9ȹY,S=zބp%|rRi?qgK++CP~Q)ƥ*E EDI`oQj31؜uϭ>"Z9Q0(%pbH'9%x[l6N@-_T:N;T0>3ˆ/Oơ;\ n aLwV.e`A=OTxV%ro9<h*!R\0|9\NsϩҬZ.ݻv9C9U#4;] (bAxIZ.G.7cJ)A>mNϘeˈe$acMX7ىFg˨2I?Zkn"!ePHP-^Tr͖QEa'gZ\Qz+.#6FPL?j|1rzp>t 챳A=H.f6yF1ʊp-:ⴊw咹οvs)k)RF@ nY#e% C?7_jʵ")@.c۩W$qC`sׯ򭽊BRM' HbK.['<~ΠWƩac@pNsӯ?J4LBOG~3LӖ$lw}*sLy1,no@䃑8L"<+H ȒW0 ~\tNyg$J Vy;+Wri-\5CzaF qқcb)@v ? w^?%d0zUԮm!E Ol֐U55Zм4Raٟ;J($O i;w,wxR?4fG ggqE$=6^W}l%=UI{;I-2M|7iU޶r| y֯]7H6AzOW7-զV+\HWg:j͜aF>;KGC;Gqc<3YܓDiL?.jH[wJ,r*$OUI;GBi`cm ?ΩyHL 3V6,$X'#9*m4XW` c9۱.E rR0~^9&/> KZ*%%[L534-<.m9xަI=ޔs>fmRya0jXK?x7vSzX )9>*gӋ 8Ua۵SVǪ 1׌sM|@\=:;08Y%qøzQ9LA>3 ӗ V|wj.xc ҙ9,AZ\ |jhQo*Iy0˅x$b~px<2'Tu'wby" E07W*99;~ 9'mܵPo3&U1NߞHc7cu Rcn?s {Uc26#+FwuqP#C75\ͽICrwѻ$03sD<|pO3veh鞙 ]GbHiLw)i#OFZ}BҼ\Hs1|+ŧ[AVkmf_%v0 `rϥ|+~$zTΨB*7g^x^t3j:vI ;HۜZGl۸-H_2}:&vvİG}C3.M@x䍹=Z)Sg)}JJz@6 ($5x{⏊|rK֮R m&#<#{HU%Opj)f9DDf}/GIWc}}kƏYڹI]O?jO{7i MfHyF)#! wY_J+rn?H$$g.?NVdiExUK czij @;V4%Mnr /$$Ic(.[nNO;~$IRvpg֡HcQn MlpqNӬ\}^ܑ5iVђZ @'ܔF}ZkplmVXb&"7gG2i^CqE*nWj H;URwIo))+S7sng Vep\}i1g (82  !_NOZʝM,Q,GI"DT0Tv 2/fMwCӿP@棎t++ ۱c8퓌)4 )[Ɯl1W;A{Mk$$c2˩88H .GRy# Tw6k11ՙA*"2 ŋK B¦xpw/}+9 q4h,Fa{;XH`l@lzuEk#:+5ffH6XG0>U-fx,dr8?_OdOzh'FYA?"˰,1=?KpI;7D";'UO9tNq\ 3ǽx+_WyD W F>?xlisoIdHW( yv R&嬢}orѼn =;3jS" )͞Gȼ m} ABo<'䟛 "X7T$p~-9FJ1_C6so%06˴88=X?(u'i?_Ҽ&HT|Mώ|Gu< Hr7v5;C9/sEuӧ*pI)KCaPT6㚂㷜jm!Ao֣" 7B F:nj8x-3*}rvf:s|%*,q,3X4хB jVD;S*?/sCGI2^珯zfXq,e768?]mA}:>NPrR?▥F\Ɲm-BHĎbX/wzy d_F<1@9 G?L͢DUA!sڮ0sZ;{fO}jֱB2xUI[*^S"rIRy>R>Vܐ)ْU' ; W׊2Hs\v 9ԤWIܟa9#5W$ا6oZЗw`IP^~}ѝ3"YE#+N)vI`ggP^d̼g#8*=H08l z4<;|(RKH9;qҪ< jWMboI&F{r:u8qjT2G83uVwI3@c OҙFq+:[>k#WmS*0IQp=vy5GDqm J#x=]#\Cm-(A9c6m菷~+/rOLT&r˜ : \F8cg=Ikk2ԒɐwOʙ%9!e_cJ#\ A¾X D=, /WiڥG,F= ~\G<~uwaMs$}|)g<뺒cfvY= Z) 0*?7K:&U{sZWϭ;uR~icg )2CTlvʣ՟9t8-:4k D|N3Ҭ_b6n_%lRYm%F_^zWƶ{}V[`6; ?d;FхbǤeHFz)$~Dˉ}Yd 8rS ̪FŠ<{\S[LwHm*?gڑ*9$qۑ=@ qlpy$O" GS9F5ܒRE ,Q`mQ;~u#"8RKI ڛY\) gJZY%A;ԫ,>dIdu{ WA+I,dr,NqSQ(fRq[bP]F뚊[dR sǭ6+YLm3=?[[M$ZI3)ALr?u' >TtN|K<0ycA"_qQh4XԱK߯#ZVF 'km;ѴA2;ܜmэLcJ;V&$F<QC Wnߎ (_̓8*ci'Kݣ#~N7tL)1YΚHrG20"۞q21r-=>li.MlC#i A޲hؼ_kGunMʜKA>kEBRliI-$8ULm8 WW] Ī(p>j xcX{IyB]$#~Xpq.~yw;wZ(IHFhV˃ȭ31ЮӵB0TLʮ>Ǖ#z\?JI/Ycޠ0AⳖOHc$|d^xŚ%˩$7EN@5\ mcXb\>\Rmpb@QI+1- Գj>#ֆC\\C͚},)̷.Ɏ7 j-IRD2Eh ,V= d2}kI.,HuV" h}sNh3ֺu^A`HO\6tM ztLjE 8жXqRkq/~XAԈG?+`d̖Vk1$F~^=;-eم\KQT+Fǚ$ybe 5V?8H蛖?@+m_ Qt _ YAeii-UȨ>\><=Em3V-H#Ee̹ZؐMټIU/X%G܁2;YY0#l=F=5"[m`2B1ӌkxٍu ܪ~dc&LRD9q{U}6R*$ E8%#$uY-6˻\}]rH-rٓ40ذDžwȦ LdO0˝'8%d1ʥۇYGAJ\.T@##~Qm{l`ֺ7[`>^EKxq#Țt\YxcU'n U€ 9Sr? $GiyEWHK3{@2A~JHo^\,yӟn5]4"#8<~\͂Q' 1n5اlx\<ҴD c.om4O,AI?_JKy}orng$wֳKI$$7PNG&v[AD–䟦HYy7D7X6HNj$ƅ1mU98Y m_9~6j{+.Bu78 `ꚬ+,*Ѫr9c. $&t'*zCg 8rP*̻!Kdc?;ӮnLPb2Hn#sX2`Ag3HtuҮ3-VpNzJ#R7-}*; tYe-x"$t3Ij9Sv2I̅U\*'ubZ6E"o ]Zm i>`6}+E(DpS dS\\)8cw1 #^V턟/;rE_NaD#$W dR2C IlhٝSm>sO֘nc!#bd64[ >px#d!r7*ZGE!67MD%.ӄEߚ]Hʸ$/.ׄ ?⚼<%r,|ێ95$hYNsr8Hep~x!SoQk nt hDÍ?c(u1,yStkTjWWԗv]W'ڞI2dg ~V#NB}@y{ZM ,Uy8)ۜ I{sP‚O^4{#Gܖg7mf0H%߃cJ`Ȥ7LN 2jʻS+iǀ=Jqݪֈ{pߵR@`֣YWSRK;_R_1xKR K*j zzW͇6F 43U4֒E=fg`W*zp % ȶ%Ԟ m?` qJ\gmSkVskRm~֗*^2W=Pܦ ܕV5Մ1iiM:ҙJE2zOgq\\FFKE*: "8wpwb!ץH4RT~ZyUIq$̠㑞=)csTc?"mgi Eh%_0;wp<SɮP^I,n0#O~U:["*DҰ* >E]-I!ON?ZpλI}Aɮ˝s+r2Hq<(ˀHF1gbcaJhB8=}R|ʻDW=֬K8ÖDwU nNCsS]3/lV9ڱqaAF[ |W#$Vp=?Z6.1$$/IORwT#m',N2ݸF 1B LU^De;1c?[4K[;3`N{{n]1I$Ί5#)7%c.噚ّ$2n dq~{;F̅u;ui VpNqXA*7۴WeXM_r%.IkZ[o81y *Bq qivHY7+Gp}_]m|p6?q!xA*з;?rnU.+ ݢx5_(ԭA2J7q9.\Ucƫ-(#}O\&yv4%O\f+c,nr`+>Yt.# Cc9#z|4".sGts2އEez)-&pn=[W؅eaNfLN%S^к#I _Ҩ̥&218 m\MrWRI'68?@mfg&V/ޑ:B2'۟V'8폥iǨFPqvi;+hڈݙv2FN?#Z0ӭH<'۴3c;w֨fHmPc sl[wkCu>U:Pj:m1' 8=:-J>HVS1g;j9!*ZJ6ܸsڅ ǯ'PX?U> M0qpyYR:8"T5wZO;37WCmcêp?q.qon^! <׎~ɞj|71%8mZ]0ygҸdJltNqMPw1#%dKyXrr3^}oS {&^{&$g`ީt򎆑zKk[nwK7O f8PN9?uRj2'܋t2Q6dLq+[G& FRr: VGFVLdq݅, -9'ֶV,lJ2]ʢFA`1xOZYC,G$QF!t1SXaC/V1PrinRp[)|cn0TXM]B798'zQb~<7[UzTwf@cOk hz2Dd@I`{F1sU~ԠJYs=j\$G?trKVkƽO0EmB~ګO4i0d<8?޻4{A] ̸%I=}~Y#ft**3N1:{sI3~dCW{a ޘ 8U>$$pp0 cs}+8p@'Er%6*1JWM#Y,I@=x:W3yk\w O8RƎOuܟ^K__eh*2r}GNJibӂ:p6a)}kͺ@-wؿOzn(Ar~S7R ^X~=EfQRfՌ1ܳ*V=S}_OX=؈ȟ(q>v8b%ʹPhC\%߳',UjTVDo۔sA=#W 㞟Zd2DJz=*)!,zm=dF݂JeVu`b 88=yw\sQ9o+c5zTKXNf#[iv&7ORpJY!8=QөG1#9~T>`hy{;6Vfc}mioI#xpS}YO.(N㎸3\GxV688%穣USM SGֵi,MKV\ oDE\0ۃ`+NV( lf@2>e>๖RFQ% (=q\d|T$I!EP", 7ޅdѹ<Lӑ?#/(N.ߝz~Nd^S2>Ĥ񃟶]vY=>5y4yջު`9,FOLTW!y ʐǶO*,% $B l aSF۹;$r'qc2 4$WF94~iFI9{'ufSt%77&PŪR$ɆRg,8JJYH‘Njo#?֬[~@TSZ+L8;O~VTnXZQ|Tno\VLN$<:OTW271~jl1ך鯹V \`5>c9֩hZM 9X0\qZl9d*PlVKhu7C{!E4Uú #i=x#޳vڰnbsx&Hlf&6=AW֬ndO{V:R zY- eQe|ca$jgOth8=hmZK*qE.Q].\r(BKrl㍑Fw#1e9FI 3:(݂Q5%6QAǧӵpYiDcY"27)9`TA2}{R%ҟvgӂɫ=Br~'NbGV#V+Vt}3bWA0mYv9MdnGyr~`/+oS[BFPR,T~Ό N>^q)46*Q32$"5mʣ$cn%"O4#ӿZ !J8Ud&y>c:pRRoRPCe2;.[`Dv܉#@jrIEtm9DZr}HB%r63^kHIs3HrY_i_g$?)zs\A,lN9m*s׽nJ\:E|k֢XgDG;ZRG^<瞾Wm& vQ[[$SAQu-b59ZtyViE(\=@B4h^3؁Ved$ےW` =ͨƌ$q\efjH#ێu{#x+9ÿJכʺQ&H `y+>y8u`Zөʮt֫Y'x?Z@@xnp==n-ak4D3vRopa+fw 8M$i#m?f>MeW.Zʧ ֯ 9$ \C2nי-rII|LN dQ.p7zRh/Y|ާd?♵|͊&O!I>R.PLjH8$#sYH#5qKތICu2ny=L $ Ty5ag ʹs\7^gY,_V;[ $^qMZė*$(9V7e' Tt{E,A'J҆.>2*HPHfo3'jvX\qW3EH\DSnL݆RTG^kE^9XPy.eΟZo4K9󤖧DgQ*b!X#RmֳFzƨI 5^[GCcJflkc>__q b5&,:ҷc3pp1L᜹8gHǓMyoSW{ӾAяKŊ(Бc?O"ā!g/dGWˢor(^nrm#x-Ʊ#ĨH.C$sU[Nk1 6& pEUJWD52^[ۢC1LNi\0D&Y\~l7;t)#&$( :w~}i[کyA@>*%J:7+! [s4"={}j8!̸ =a& 0 u[Evsש氯7OhT<8^rYb3T $_A̭a* g8ՋʸY^H M[]4ᝒNu8]Ԍl2TP@\ީtIDeXHl*G:)2*0$ѳP2O<O5麌Xt_z^M.ᡏٹx'5ٍ'fڤ6ݤfS@~ZxO[$mֵaHTQUu{-U4̕\AG=y>QVARsеX7۸o,0[IZ|=v'KMuNqji*(v31=G񪖊n|.Y:䞟Zljh + Ժ@]Uw6YN2 nşO$nykeͤnʎ1ztEڳ n^զ.KgdAퟧ+ч$ov>M W8]W$n|3qZcϦx鷞+Sg>8X74{;/)MB;`;Cv9[`X=)m7UQ\lܲ tJŻ+wR> x%z w<Ⱦ x{])׬ y>&yipIA8) dP#sӓH+yaֱݍ2 rF24,#;x'Z=@ 8c@HjXF{ PɵBWǰL2Y~Qq}n FaTـ@ӥXi ?5\"\6u  8ҩM&eUgL x)JJ:`V]+ xrk5r;ӭRܨ3\yC2H{U@z̻bWe{,H#5a"vЖREv;z;yv7xbfM>>5<| OUxej|A^̲;OJ鉧Hg=Uˣ9JZ5ɐ1< yp3ԚM %Y4n?L$3W7͢ߥZԨ#dlj9oIPh}1qd !rr=Iz/'@ rZ A{Vז9B.ӂzc:dB#g F==s;6)Rg#46]i T<Â9?i$~ -๵~p~`@kR*%{tbt\_o~Țs?ˉIc鼂cN$uOhza{DIml<%TqϡSY7=㼬D&$?v{K:F+OΒ-%DD=B/P9<םz~Rv2qu.db\ @1֫#N'V/PCk99ץQv $!՜p1ߠtg缑@eCP |ҳ m>}8h^&O'&5"OG8ˌTH4!=AۨQES\m$̹2ThO|=jD1b[q=¡h#ޥI ޸S /`dm:Ur>gǓPOSWM&3o6IsTъ1Ej޾천g͐Rm3ۓgVpUq#Vm sZ^7`rכKe߲۔Gs:#p\5zFӯzKe.Gһ=DfE 5kA6#Ʊg銳NUNq[6Q)R9-ֵzU=jcgvyMw#iH\?@hE=8#qޱotP.neefpFN1IJ82V;bq5 8Pn3F1ǸGO^N񥽦0zuf c <]IeXŻdfZ|E,O>[xRtLyԶ'sythfPoB#\~l#@իe[5u?NPw9=)+Ε%cJĦGeUa@=#~=EVq?DG,a ~s߿5ϧJqo` *pGڲX g[ԷyܮJ5̣z[0$By ѱ(ǒp?j-֭ H29ֽӓ/kF4C2Y9Oa?:nm/GQ?(S|ךӮ%K' +(s#MO3ЉJNhQ[^ y=ҿJ~hiV dp!$EkG=kOi.Wwy?ֱՔcs)n۸KkYH=*G7[.v0?A늻2of#{p)>t?J-׋p@9s]4soEz=?mj<ި<2㏑pN 0YZ?E8݂QzϨXOZG(Ht?ZTnlp,g;zsܑm[sZq:4-sQ© ;LI$Ϸ?gT?>mAo+ ?wu=ӆ]N_Ԏq?T>Wsay~Sj%oǫk"m켞dZ1:8Jƫom;mGl# >=_N`'5AWҥ;MsyY\ɺXdP??cCPHUoRyUN}'"F-g ̝֢5cPc%pI*?ҒƸ"'(FAzY, ? p<泮J*̹݆eVm !r1ӎ+bGc2.;}iwe$f .; i\W<j܈h=R_ylHQrNx'ś[j$g_C\wוn%<=&ӷ8atݙɽ<1r y6BZ^NyPkm 溍+ OJ4u'TT;+dRUzų|81*[&?hNOYs}Emn\<)yt Fhl` ~OtsB#=c(]KbQj] -kE|M/}Eҵg#"sГ!-{q}3S9.+;;⺝ Xws;ڵa8(zAYk87:85LmsG+6ݏt?Q(nQW]0j-ɄO=h];Z*.]fotoxx-20.08/images/stack-paint.jpg000066400000000000000000000374601362435004500172530ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 221 250 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|| 6~3gMZ KI0$zF}Ez~">:x? =#o},J'ҀYR2jڼ^4PDau;{fyd`ޢW#~}GO"y< &ރe&ɠO }&LI `u4ψzw}էu=a{TL%TJPJzIVm_(aOw_<xK=*mJJԡ-cYɲTf$*c z >R~5Ɨs Ge' nï9G{]?̭3M)MC VZ0=pk~xW~^$ ֳ6'Zri3G5 TXpǷ=3+-[m \iq Mg|#=^e' }M-3t@{k2i7*CpZM6ief(ц:֏FMgkQLWPԚm?*̑yg!FӎVj7iC# !>}go¶ͯ[,WxMK1`f^Q~|4O&U;iD2FG rM5oקaOl>|Atm͎[#w&7m#8k/¯j_O]7H1^Z=痶HLM*Ml2[}'?DW <7gi^%t]+]e-lK2c1OL? '^[G⋽fuO\x;NJ@yE`r(DW5_46nO?)aO] k;I/qʷ1Ԟ}H]/x]Jw>MV",8P tKZ=j?jC>f1@~$~ziv9 nە,e( }G&]dvqŹIĚſ Jx"$\7$9 N0Yv҅VhvdNW'MOJg.m(=ة;GC&F9QFK\]h]=ŵ[f|ݺ4=GCllĈ, -OOʱM—$a^hRYms3O _yb-WyÚXϜ~s-`a;L#8=l?J{ߊ?Koi@&7;Np7 <-mFo`gEy@ 㢼Lj5)WVO͸v ,M\WV}ҿoQ^E}Fh8_?_Z=?gZ=]7> ߋuOk΍zo3OlnjtIK/&(mV_ "c>;YVG;YVEh|Q/>5e}/tŕ[$2|K7JaA|AҵWZ4:5+W@0`}?gZ=?gZ=.|U|m5M_QҼE-ޯ9+mn)$'Vં;b3BIIl^H%>mrWۿ?gZ=?gZ=.^>"o|>' --2'p0(o.k.lDZ[;><9Ge;YVG;YVN'KXsੵ t?M`څ]/٭I.4$XNT gIJƵx|KmzܵߘfIk)\pJ,_G,_G&kۺi> _i>%__&_B&'q|$zcںmCߏ=ON.g{A~cn#D/ɭ-G׋5vYQoDvظ y.E08,_G,_G_wjzơg#rai2\N%1,@ePF+ID W[ajךԦnlⱺ*ۦY"f H&=1NøşkøşkkoOwH_ j恮ɣy%źA yŷ+ Zm|m[\9bgUbYNdҾq8?*q8?*(Crσ5jlęlN8u'R=)*zfA??? z??? zvb*vi%mǔǐA#o= K;.)u jywϨ9 zq8?*q8?*e4%t˅IS>>0?وJsN[=J5=jYepɁj,_G,_G; ars 7o:qxK^۟iC?_ ; z+ӳ<UNWumQt+qdT{m _jov:fqKsv2HBm9 9դC7jA5 ڍ㙼:S$ $P2G~0xNԴcR<;Χy,$v@UPːH-_ ߙe׋$>t- 2]xPm(n>`Ig!J_,ylOĺNKr:[AIi$c1 |TF z6Asu{IVy!4}C]&("} OlM}{iּ-H8-[<+ 6u#߻IjQ^}(g@(=ԑKLu,m-3glkj/qI|;u Ir~Kݷs7W1Y^6El.KDe;vO\f{whM-MYƻoBU{X-m2K3*w"kZwtqѠ^tig1x݁|3~وU<`MW+O]&T6wwu((L< Fyc[1f9}~"3÷2)-n! vx1xsڏuv\: }oWU=GD_.` ꐬq!|.*/s .OU5bSQE# ( ( ( ( ( ( ɗZIi^K4w\5XldB֢2k!"7wT߆Y~q4q+H'dd3ȓ ?^O5@Y%x{JY鐻hHTHP9 )V INJiNkb3+Ii=̷3_޸7nǠ¬=퍵&"W0::d|Q\ 7mi-%=p}AֵbYxPcm;k'TvFulnFҀ2%܊5_jr-dXFm c[4ii6:n$H/dVXI{kYEm׿Kֿ$P>W 'Btܕ3:;7oݱFd>Ym׿Kֿ$Q{kYE05(ZfڊN$x%0orP6чCWiQEQEQEQEQEQEW/߭&W O:<})ݷ}6+>=I_ûZ][k.Yc0OAlhk 藚e,|R~vb T)_Οx^/fԵ٥!47Mǘ$m2+,6nW-Nq~4~mkuZYZK5zTX "-rG߇~k>^'έgumB->:vm KvݩyLhrLf8]\^{M*M:\QӎSKZy,2N202ڭ\E5/{uMJ|;tntHD*#)__|6Ήh_יe)mgY- hy22$F=*VmLoe/=¾#n;m/K㼁pa_,|?&p0keKxGMb_dԏ#wYPB[ 2I?&?_8\Pwbx?]z kVNҴAr.bhUUBq885_?炵[~ ПAm絆1_Ih{5e}7VxFɦ__'$nmJ--R7 -`1s\/r"O4E 4k6AkjmFe $ Yo7%{>]ߧ{^-յ7KxWTm WG4nb.u8l55>k cOjV|S}TnoX#$< *_}y.x7Pܚnx#O1Ri"FrCeԊ{]XV^ԴM^Iq5ȶ+7Diei_fc=jο}/YqqNҹ'O@_bj>agW6PncC$pI*3%),l7Pø<@bj>aիEQӒs-ơqq)[wl5U(*R(((((($$PWEcGU;8LYOpkƛiwsoq=3\[aH1`\埌o-55MZehϑFUýAS}˟ܯn~>cO`*1Efbv £@8K؜#I-^?;ŚֵGľN㟲C+;cCtmWSΪwf88?|JgחׇW&D ŷB'6UkJ+բέiVWZE:']6G!βI! Ǘt-5^Nt >~"dҬ[t"۠htU8qU'gq5ucJ(((((((((*äYcwMGU}YMI,,/H;Tɧ&GtW~k|j{O6jsyܪL׊/'xDto: w"N{tQ+|pqJ7/_~{>ܢ>6xObC^TaDm _ˆGJ7R動KoXQ _?B֥Z7,+>]Q_"WOе ?+ZԿ FP//KoXQ _?B֥Z7,(}qϹ'TWȿ-j_Ji/ѿaG}>EkR-WOе >?$\__ /% ZhKoXQ?a rO N_#WOе l) mZi RhrR}qS, oCŦ/Z x<N+.$Jdp랠].xL|L}Ɯ챝cNq (,QI8 L"i6Bħ؄( fѷUOMa'61[<WBͣxyu9Zv7:%9Q@Cwg1$H6r B*j-wNV$)ff' bHI4<ks{5]6=7}ַ;hR(cUEz :!tZc>ZFn?9_Xa˫E"3#n3fѢK>♵"]S4p̠d[y xVT: vE"e  ;({ķYzC;}ԑeO{] T4]jk+VQ~7~َrzX.#_ǣIyy54ZD&nnXZ+Ė0I}v]'F+O0/O4\(_LzRCL'{W  }>Cx.}ܛS1'v^4량33nxx[P`k4h<̐ N~S ,FhAT*0=$U*[~~mݷ 6_w>9k6_r=>K=-ɿYŢ*̱_ . ߇ծ>שKA%S9Rz>VYꡠ(Mҋ` >6_Ҽ9@]G1{bmї9t+$%%%8ʿWMʿWM}qEz?5??/>GUE {hUE {k(ѫ#A/ _| K=z$ K=z$QWcG4_W{*IW{*I(Fi4)?2/(UG2/(U_\QG_iR3e__Q׿&e__Q׿&?QgʿWMbbj0@Dٷ59h+n0 ⊙c4?54)mwnxЯaت0}RFy2d` ɮb}׆iq.s vL./CI[ᢷH?|F %͎د:ykV5i՝\{phm#zj(EP{i.UDwsZrL! ?]W*7n 쫯 ?3{|AJʺ*7n 쫯 ?3{|AJʺ*7n 쫯 ?3{|AJʺ*7n 쫯 ?3{|AJ"[Y.nd !fr3TKh7,vYyd:P;~+|{'4-3W=L~kkXqv3.M_OzDžt]!u Ab{~#Ȍ&ӷ .qϩk ZmSZj3FIssCQNOu#vv64{[~i X"j0}ޞ-_=_xl>Vxx> S R/o, $@1|w ں|X-9nb<=[ilͤct"2o&7۴_'>u? ^C6bi2qlQ"`B85x-vMƳ͗D  J$1U5v -b09doj:~6}!ѭ5tK`UtvQX5 <mq _HheiuLݑW`Tps5R|"*G7xSBѵ[>ZMc6wdєôxfp~[_|ogm^-Xl;>M*7+I6y$AkZ5ޑx˹kU;+W$E'Gx⏃?# >ͮX%ԍff5[2EX,T2'7Z@3že$Ӵ}>;Fvyn<0y-rNI,0x,!~ KV΂(((((((((*;}BAd=Eb½ Z?E -hE½ Z?E -hEvEXZF,`pTZrDRҬAZ%*=`qEGZ/&WkG(Z/&WkG(Z/&maH-XPPQEfotoxx-20.08/images/stack-paint2.jpg000066400000000000000000001533531362435004500173350ustar00rootroot00000000000000JFIFHHExifMM*V^(if%HH0230,0100Fotoxx:trim_rotate|trim_rotate|trim_rotate|NE0Photoshop 3.08BIMfotoxx,  2000 2000 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((Z" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?U^*4ZY9EySSKm(p4J(xi€)˜)@Zz@!OQHEԪLՈC:"X㊷Tjeq{Sc aJ-s5Ҥ[AEs{WG+_+p:#*_J𭿗FT?uA5B >׿_ɈCTZCTR*M-<\:4Oi˜)*E(H4*R*PV#JbSU$QUj$Eon+SN3E}zVv|tJ,9kN{WB` ?ҝsCM3:o'?/ spq󬯎?n_K)4GSGTGSFSP(4hLJ 4-(~i9E8x4)P!žzԋLQR((UiES"SLըDIO9Vb=v@SH:TڴMYXJ z~[yR LNjz?_+f^W?NֿOqP(,u*5fDsiҐ4 1R`zQ@ȱJIQ@GPԂ(0OT>N =PҊh JBԋLT5LEb0=*$: `Oү@y*B9c~O֦Hװ"@B}*Fbf;r{UI@U,,HnW8X|:DDV[h6v\O)< r=zӰ]d:2+\~g8_ul>S#|dRG?fe?*z*J⠫1RJur,Qf=B#})H)€i 8SրPԊR-1JHA@D:((PjWU0/`́Q^y'`Vor)Ԩ*fEQ*JSR‹[Ij)€W'Ր)€*ETž)`5*OZiՋS,^-JHLD2=qVb  { ^]tjˈVǵ1c#E@Oj$bUV'f2*)g!TrI8Yj}erxKH"Y#a2 KPBĠ@SϚSE*5";8ޞb|Z5$:-J2Uk4XX:4j7_j sMΛ$@q#5ocv0$+%ĉlyN5|5H?43!IᲤ`T̚ Q(mMLW_ 4D [yrzoV1?-FGӠq1Aq +ω!uSie*N?xW>è#w8w ciuZbsoh;Y2mx*sª1ob7b@52,'*gg]*}4dYKpv۸{֐'׬%#V&86-[H2!)vjŝFeI~C}x"7,Agִbѣ(Hc#ۊddDg$M|ԢzIVAtF &ECE7"E&(JO|⋁&ihHit56qw9S| p7Sך> WOU,om\ּ~k Fg)*OQ[:m,mBm&5ۃSr\Z=VD$Y#qe9{k$ i*OV⨒E5"T&+13^yuDq$K(yP u"R+[I1?-1(iЯ3b=U5qLfeeXA;ENM>U"ߊ?5ics@ݿ=>N!4&β>QhB:\J1 l#r tQF}qqvdG 4EUFI*xj|(%B1x\&Fkx!Jږl:2?z|.ż&kdZZiXZ}]JlqVsW"d|LBC,Kk~kWe&ݴ/HSBV#&/u'JbsT ⺭j6I8BQv ԶFi6*㰫^d =Ii>ͧD.JO,%݆wI8ݨt_3VI- ḖV*SEv$Fv"_X0F}i GWXXK|\ z`rs?#Si+ǯSOO}j>t8'Q %yG-V{')"T:wW&:x-暵h1֤1h"šMs>&4.>qB>]N: ZrpH? ƆS^*~)jMK}\Q?p;UXQLG  x$M4rpYƥ<ū='x}˨SWCkC\<'ݾ #` +ATWIjݑ-s9UTg'WMe);]w 斛CZv rBSگ[hQ jHe?V[ET=KQ%ީ}SU+ԥ._,j'Z.YAŒU$]֓B4$i~S\7UUmnV?y|C꺞{\t۶Es;6%bxKQE d1O ַdTUC<^٥jg5QMO*POJIc ȇ8;$ujώdĶD'AmTtM$f)3U{5'{T8 1asZyq%ݦ4tq+ĂkK9Ԗ`V$5Ca e ~c?QTuqTzķLD;j桩^HC4x!YSd%/n׍~UgTTVRSDn8 ?w`QX1Њ>2j?J>>ނQS{O>"˗@5]Igq/"47Rf {siҭͼ3*8 0FGq֨~Z_=eq3#\pze$w*CѰ ;޶8 (,"E Xք֢{TJzIVt*A=wT<DYQ囜8֛ءC⥖jV`t &o^\ݺbB, Z@\0*><exEcdM|_Gx˟ 'kt^*@v1,k\&`YCMHoL5YWYɴiԪ:"d5^" gJE8v*^]E|\d? H%?U^*+F'ЏSaЖh$~┊EAC͕MFк]X"hۑ2#犎Pdh>'Z_~hFܪA[\ȗhdzƎk(fiaȮú .|} :oS;V=d$kҴcL$_:Yf"Sp^0mMa~\SZ KD\`kMTo-ˀIo\40{38 _& R3> "ѩa@$zUT2`*g#i||re&Ѥfy>SfseEibb,c9:upŹU2>NB|:c?'C5VykP3s{֊]pCO0O`-lJ)֢/ Vl&FEۂUHmiZ,ZnL ұemJk"NBJKS%S]nkְ,x{yT"* xvO^AoHyf[י#&gIeCѸfͺe?۩pAsMxsg.6mZMSlVRkmV?\;_B~ha; o>/$B++rA)ܿT,+[ u8F$+#'ǽT]D}ailVNOF};}!]j?xbV1F$vl͸4 x}8­:=N -;K"bSvz(N2ʥo5lGZmhsN;f#q~[A#m0=9#ҨC_0]zgFzP}2?>Ӥ]X_]r\Ͻy~ǪFWO/w:=mVX0\JrwP7i_S׼+sm}\mvc]BzP PG⺶8e?0>BvrG6є3X9U*ڊKR >ZyxJqBoSbqq=ȌwpXUȭ?x zS\d[y$\Eq_X  +h=Ie,k;$$dj]ЭSs4pVڄVY2TUo,r0SPImecnVoU-@OJʳ^rU&Q:tKQa,yӚo𵛞Y>'ϒ컜<k]Z2~cޠᖰmm=y zS*x\YTP@z=rJSb`c3;kDh#(w ?.YMPvÈ#Z^3FLg(2wrG+z !3Z6ǵ9a۝>PNǎ%TyД ⵇáŊ*U\dt_ 3nbREsڲq›s$ʹj<^h]]ܴɵp@Y F*28繧egu}v~zFJOZqsX^D?lʌ%:+Mc(XoЪI} V!evm\G{A+:+:dH662ø4Cd5#P3S[ XGz;[YYT0A*@iy|od[kMB1v\ zg9^\D3.^R7|ppzk2 j~{E(d1/Y䎹9H{ObŒ rsNՑhD0"aB`U$B",@zlc$n.l>QힴmSO塂IL}?iZt*iWviHP|Jqǵym^V϶jg :kɠuaFƛ L%(TO?j<.Q BYeKF:J<#-ӡ/m%\$3+}HRCim~BFHaȮOX*a)IȯC>+:HGF8^mdfFQrAɧa >Zi*gb,#bE,t M@r)-LI;X1pi]A8VS[k;DU*I0 \V1\$jɵ戤MF~sIXj71oXF2.f+ՈXrdFyױcmR4 :MP7lȬέq=?Zӡh" }sZtg[:vxyr灎t[|9 W(EHRz[qgܾcoR]X9bQA&xYISjXx\3j_)قBMn7HBw1*y#zVO}$@c3ӑȲ:hRtj4z̠=wu J<5<'$uHjN|:d鏯e~\~u$o*q`}3hun gI`~H-Ɠ2FcnE@&!siړZTWEteS*`0@@*G s޹TZlH$}kv+- "&:3cWB+5 ,Wj}嫗X*X^jU+jlf)  t51Z&Td9Qѕ#= %[%ET naDcDүC! (lSBeR?NE:KV}iER.wc<Sk-jT4 hO'cU*X^zu#W2t8;lI{c u?3]caG^mp Z ;5;{g Q(ܴk^xj{9w2]* X(brM8p/T <]9KSm^vݸ/~BFp*Kۨma.W$6jgDfTݺ3=M?YKS>_tɛ3]hꩂjI|o+[2%Ct&\)m.?Xǥ,VR~sz _!đ`+dXdV/m]Ee|R2 `[AN:YDwabXu?fCbKԴżȏF=tzũk-W6 7 𬓳= ԜVvQflESTuk}n_J8#?|_ 3P]4"Ac9Q,%7vK 䈂T#D7ᇡ={+>ue#6'jLLc[i\_l!m^Z}iV(H/63y$WWciGX2Lm[z2~dX(>l%sMlY-.Λrc"z+~?θҧm~ܐVd^u)GszJ}mˊ_jͩPI0q2X}u3ҜʳYx b?Ʈ&)*∓W#?Nzl\\'d&\F󇎎G3lFdztp+3m6 6EVh^}|r i;g֫\bw"q4xNӼ; O(fRr~80}?u4i*Cw3nB׀'"qL,x>saoszmh8 h=/59/csm#~Upsc3dducoxϏlWu=y[]Fk*%x9 9y_N\@ - Mn:ͽk;Y7< P+,5=ӋXвy39?ҼTS<1/3sz1k=iob©hW,[mzi$U\Sl73#g>o ,p͌7%n&0ddWh6B Vr #֔7V8IIn|oigqV dr+I ń*A]N.K2DQQw:ߞ.RATDf}J1Vl^$ X*R]OWG[<mHP`cPٯX2 Vo<2\N$,ʅQ:wmXN3Px j'kc^+Y"^&!<5ۉkm}h"Z}jFZX k9- #)-Bβ EP#o?iUk;xō69?g\Oj$ ;'ʹҟ"P)3Gh/ 0|㨮4n dןW>v\B/m+;I+hgRnCߧ\BLHvg;rEcv[}pI`xגZ0OjVzۘ{.J-̪: sxKĂh@SLÚ(#yIc|=&Gc[k/&?+]8y+'ơSk'\v:kڬaoFw!$(%ٸY]uͪ\ Ƌ$zqE|7 {q{<0LCV#zCuD6ylQ^u>LPI7`ہ-tr$1Xg3c>^cz^(sJ~;k?o/v;mW$栏ƻ &6 c'ֲ"Y^iL:Y>6'mCКm)ΒwbRx9ҥ `X9N{P4: IJIv8ݜ~ⷞ SjWMݭ3'>FMzvcK; mlfdBpV@ݫgzZ[]̄HUR$:ųۚl0l;U)µ8 o^GӞ1}Odnl7iD`*b@ zO&:;{:'D8ʹB=*G?)1_SYjdPQbŷ_ԍaCEkSh ڈwB0(@1z"8e⥍~Z[*ci-"(35_md?+)iG<)$o)7(S~5皿u5 bK(.I4 #vr7RMI>Y\u_&E޼WqP609Yp$J;Wj|2lmfzDoU<|˜<>vԪI=_4}?OȗpxaWQ닠|?om|.x@'@~Gϗy F*TbLnC{qV4w-|OmmoTȨ?}9\jg9iV-:-,1PJߡ\t:f;Ȥb;W,՘"9@uc= z?EQdBdxɮ#+5? tVӍG#]szRZ*n ݝ q yq"EŠބ`b)&jUyjmXQRF51*x(nxڛ8Ujī:dcՙj9*2*(Fr{n3B搋~L.}M\k%#gf-V-eB|x5TԖP/m9?:> }6>WX+=k{R,^0~.>}k?+{YE8;1k/짘UnxU]x)'VvBAڽ;+؉Z/v`[#05Kšj:Ʌ]?fq#ζ'̑g?QSxU|7C!L~Vm 8#ׅ} JRpշ+8"M 0OkBxA'֣T5+np7+QԮFʮ삸D<9ZLi` 2z K-f㰌Xqrߥc+;u>q7Sٞ_A5Qtuڷ{I訉#mMeFۤcZc#I$)) 6S\=JpjF#uȫ$T.*yK,J}#uՕ}71vbr*I"ڢJie0wcoz`o1 zE_p"k#WI,1 &[ȊAd#9<{XQ7du']Qi9wӌ2ӳ8][CoowRLI zKNʼJgTIdpN#91޻}q$pǴMu9OezEn\j@1SBFZ']cu$)!lW:oS-Jgh}QhTh2*r3RȪď'sm_ AC  bh@&v{$&S4? $_9 j?ʾz2FEăw֝#+)pI,}M4+HiӡDCXյo[+O  %+q8xr^;QTmaN:ʽ0{[hH8u<^<8Q.p/7fjW=ᖥ.xdX<^A0v=a#XzVcBxw[l=YzzntC ;֖OoY as?17?rɺMF3ҪƎq,QW߅%mAehv&de8_04dz>%̓V獁d[iwy%rlE)dG̵;sOU5-;5Whe!|R#<צ~6UxH? ;+}iȃ#_Mt? <!DMٽ?X)-zƣ-/?ՙI.դ`&=,bi2*XԊM\iؑE;oJ4I r}+;5èP?"z^m;rĆr V$!54mm K SQ>nibg;XK-6?B[f,}1Aȍ2V+Imdcx "0 cn?y;32C$yf,?W0Crc=ErIwPaO U|M;7װ&)|3Y ZxwGԼ?y72ĿiYFビc5֧uU3tKP̖6"f^ zhwDĝ '^Jk[Ú^ ~S{>9+H17'A{>γ O/\։Mq.N\B&a émȪWh\zX50fԑ=qm.)fEh\hs 8FǠ5gxK zxsϯsSy3[i6kYzkm*oAM_̅V=۩}f=ypVsIЮ|ݙxPOZ߇@YGOBQ,T"ݔSzװiPq PFB mn7ed 55XT#Pߗr\c;$bdRr?1LmVw.0 lcX( ˥rL}͌}j/mx-U6%x}_S7NKN/mq?ezdumAѵH=8[b~ \h"Jс?VёDH>39UgDY46mQ-IY:Q4γ;3 }#w1I5/,q,n¢H^LBSve/"[k'#b0 _:G:gyо۳~xym6oHŊ巄h^g/ƫ\ igv2qC%3Χ,xcQT=+P+ҵ{FDi%i΋['으[j! .Mq,~PБYW*,eG\Ux5s7Bi٘[OJ+8P:$vhUJP01ާBv6=Pf,l;+ .vV I_PNxU`py? e.`b8bVM#9a^co-@pHaʣ f7BEUX|3B]:R[Si0\Fܼ*դwFKG_7"@ 33gҴYjOfg(2[}*x/PM"`)^ > EXL !c&hhӾ믧h{zi V%a"['G,nu8VwI]i;5-"9dHd)}mЯNtFҗ v-&yw"8EvFo4k'`3**ps8xg2V?*1+X0ry%^?Y`Y?Mky`Y M2SZ\FQ}AᤑlCo]n0F7o퍠K~Uicc?ʉc!`RW9=n]'wK$8??+֗& :2w]L[,=sҽ r׊n9WvPBe4Q;ZN;YK{x"5SH,׏I,)$#^Ꮇd$1+˿feŷ}]S̹>Wd{ʉF{UIdH^Fa5y 'P9R5u={~Hd%#7h;Npʘj}#xr)dbuAw|x9֥(HPr35 ӭ 2rss,d&v>+tc~!Ac'?ja'RlT)|9}KXXOb9 LA߄u$P0Yڪ2_K#" sںa̦ﱄpM谫t+w t~`f @=+lj7q.zpMq·7fvR8㊔ci9n\E ypfy}]Ɨ$t&naCҹ_\\L3xL yOQB]`gM1ZCzzC$?ƒ|߲3Y}| >`n81<ӷJ8H#9U=i9s3K74cik0qCZH4 %"P(9Ǹ$flIku9HPgnfY,sylhOLi#Go^ Qo5ͻѷϥB;Xv8WM+U1$tӻ/Q]×ĶC h56 ZFoJ=ƇoZ_0u(S׎y%|D},fM|xQJD\Mݗ,Yj `}i`ZjɦFB} c+4ݢu+f7$FL_wN.t#&{Ku$6bE{ R}]A/֓&jW$٦3qKIw#؏x1·|?9vb8@W;ϻ'5kJx&n2H {~:7*ګ:f/FW9GMJkd:ĺ hv11OIN-XhŨ WCm>b3Á !JjE ta,rvz/Ttq"e)Ȑgh|Vde :~`F2>>[Iq=L gAq \rIb>"+&_Z>xR{ [1;}q^3?^j3;7M1 ڴ 7'd*pиy5.9=JRU5ݱ\-Q<^ƵdԚ%>g #}k~BUāN}Au1xneR@ *]9ҔJ*Ȟd߾`{(Y‚׃//0EGNެlWrc.n!3?T9r.nT|k7ִpʜsH|⼎1,@ֽ&ͺoW`Kg5bKy؏UeةTtdWג+.J+VdTwwnhI,;cɍ nikNA⸄: _ZT#:jf(oYDa*վng 9 y*䜌WcHRVKM{yi}6/Cʵo8kD_ r28)odԓ=a<{FwG>UU՝do75l5sxl立SRjJ jL\DEZ7+oD7h|A0f e?i&n9.c9F>U OSړ#0<WxO3N%Ex}Rud?ꦀZΓy896QU#.Y;9(5t/W mk.7J"샊_ڌK1=V}AωoNbTtb6+ ?+W̒K.@:R(nuHFKFHϥp_&Gy?^$kEOK 0iUO 9гNZy?4.w8=T/xSۚy2xBcbSM[ a=xFGLV0lbbġկo"~jy/#SoR |V^x8%ܟ>C ڕ!m9湖wz4dbyy`cO.\uhb%9C!4Q+Wa[X AګPO<9YGZ> tKK,0lS\;P~'r=+ha-3Fv75=GKe@2#'=8-֋]]iWq[\@e8(wSZ0w4UgeuF^"6JLe8/'6%@9geu*Es1?/k/kAS(vG_J>4'?]x,aPtx4,aP(P+EجzP/)r '? ӳEX\”|cN9(ßa^F 8.ǭMy(4i݊ǬMA?o'Eqo?ɿ[_ɿ¼SzP_) _yrV>+g`5S<& EZ= |Q'`5S?yҥJ#Ї?8|J'a3WJbLC#& _Y?3W"{P+,GL3Wz rP;/Y @+C6m_W:w%?v0=+Ŷ{V`{&MP6'oolg;W r3Oo>Z^&v2OxO˃]xjryV3G<{ԃvG;c5:qOʏj0t#Ap?@sӇoa^6"C7@sP/+CSv+ _9RNZx9,z.?a@şa^F)Eac?p& _\ɿ¼S;C@oP?+Ʌ8Qv#?n& _[ɿ¼Sm& _[?ɿ¼SԇŌ& pa^^U7MG?ɯ¼VU#?<|L'a3W$u2G@O?g'& R!V.:R+&?_Xm@kX9O{P+w,3@h ɏƹ?-*Jas ɏƏXM@kZQgE R*TȕhT(UiT*iRSI@ĥ m.hœ)ӁҊh4LBp( @)RԊE*jUJjeZTJD :41@Ua!E95z;Fcڬ$}-TEmjAl=+Ea#ҘŶ"ۏJX8E@߳WWh٫ E]/ڱhHq+ιc9{!TT(UcTR( JZJ`&i٤(4iQM QH (!œ)8 QO xZ)UiꢘE * *UZTJV(*j(ĊHhVVqʘ YH 㨩;PVzVD*A#0[sHҴDCҞ"Fw[°׭[wj1i~03?iFiWtq>Ē,3XKrUQOXGje}hjfE8}3֧ ;A{O1Ҙ\T O-qsҜ&M(85&PwP(iԁii(JT!ԩ䎧HJTQ/j>4QтذFhAVcVZڧX1LEVzUTagJ *".|A,TjԂ4@(XFA"q uIE"ǵ>.DR> iE0#jpޟNՏޞ#(OQH)zT1jUrR-5EHZ*52 3S*$a1r{*Xv7(?Jݎ:hC~b8PԪ:AS#n!y[dH2~Ok0t .p>$U 'Hӽʏ& ʓb#UͷWJ|ZLi|~d6pORnzd{WwgJlRW&+6Uijܪ dQq:ȱI5-[O;i)EFRH)E0#jpޤJ)h0җ5%(Gڜ">#zӅ8PǭB-T}k1[rfO֩p6i(HqU jeZ!>:C@V O Wjmϥ1{-2Z,7Wwem.U ^ev3atKE[uP riʀ>bQRH4"tOTS7RCH^^Z}.j.bZQQW%I= )j6}x3! AQr( Oi#\J7rEtM,uDJ`Z3\Nju;xn|b 8ԏκOkZ;]ܢ$RLhS֛J(AOjxa@JY\zԋ "Q2'ABNjܧL.7ʊ= `hUaINHE8iQG5fW/ʾVv-d~Rw;*ƘS,RM @gfw*lKOCB0{S{ ڞQo׮{:uqYFN:-Ƙ^}ѿH)EESER7HoZHe'uc4œ+oǁ9>*!v:5 ;hZQqգ\^/g7pqutZŷ,`׮ 2NS 6 x@ RVW"ȾijEe:c b.%LAnc*Tc.iF*bcmea_Z6qFS{phb%sjSX14u͢MR%QbF^EX=dW#*$EOZ%Ƿ5pX(Yn]!bp M QѤ&vCbN})j[-"˺W |kנ<~bڍΫB3֜ ::@T ˪*@)TSEQRߌyg^ U6 &48in76*YHm-J9eXuh*u* 9Cȥ^GıkG>{/?^/J=/,\1mYqVSS94dӧY2 29sR:95 dD[xΟk+:!Yr:@玕+;e #8>i- ;sbԥ[@^S:{D=崸" p+ԿL6VѫjL }?e3[k2Ids 7sqNL̂Ik<_fZ IB lu >v>T jRjnƪ@3s6=bjW6HlE,Akٛvck %2>YιW)GoQFӽc+l⾣qM V:BNq<){-<lcpk/ _zn-4( LM:Vk6TQZmҹ/XxT][T{VR4mc<_1ihSo/C_GA˕[QgKFhI eڅg2pE$O2:>GP״C$+ HA"-᰷ohd},Vr=."W3$9V>%8c`#vFRd )Up5_e`+3S褮\-dUv?џTP'H!`~&4JE0ԆzԌm; 4ŒE$VbTER}i+ξ ģUF E~em5EYD@;WWk2w9m+L{P{آ+;r Um7[J7<ҴV xSz8eG1H*Ѳ6,F2ïRNZZ;*=Է˭EI|-C֏f|S,wIe*z%lZoqvA^x 'MIȀ/OM8pr=!8TbBk-GJHo&6>H6s_x )I']B_TJĈջq}i]mwu jϘnӁUInOL v]B|3ξԱ3HI$ gˆ3NA"2 ) jfE>Jf141Kr~?>̽je\ͲhKxDUu0zOSͬ˰#DR8&($wɭ  }E&HRd1 `p_\B79pI+'v)4IEko1xJܒ $~Fm[U∅?wVSE#˨ɗU#:sXZۣ$o^ LC>k?a8.[^s0nty,2 +zn+n@yLCHg7Bb)R7|J}$ҰdUԾ` Y&`Uyp ˩^5i6?Lڼ졵+[ӹkqZ]^3:nqԶ(iۊg:?sI<~7DIGFXn+/|8ұ&UȮw-jSYjHZ\zTIɯkiCH(N.T1K= \O!^ ^ğ&H۟^XxHTefeU Yfuu8?qo?:QHj3Npk.=nX,eme~ RzYK+Kyp̓NShNJBB3xTmrO?bj>*M1ޥbYB`RCfT?dPbpP7vm/Ȼʦ| ҙFa!3]vGa%4wG[[XF,k#Ԛ~G$V6*NW5YW`P>J# }wWU_&,,Cs?QW/2CH] 5X?j,r%Ty;h~A_Shγ6 7+Gxf)%#D?#^C 6Dr$yoj3i-/#ćF׊ie@;z,۽1LtBlpmoe 42"h Kb[]Jq9!3vٱỒ%Nf+RE#m]+b:nch=da5}iWȿ >~?0kM&,O"ݲ_,6H=5Hk}te"t#o)ۢ8&FIwIש x<#mZQ>f֐'懇$WerLW)HGL g7F 1P8EA0g"hI-ZA̫''C~Ɯwmj#җ@:xhZ70jE0d;r@Zn?Ռ}!I1QcʌAf'p^\y=j'b؏3S褮\WWDw/˅d3:k7k](~EK˜G5)*FZXmܧ-jt-5=,0ǨM }iŲdҡן|N83伀=5N:6YowPd1* Z3ӗN"ew:|}jDŽ9ky~ig/.ecF9t-##lsG"CjI]Y+o+5d+IS=EH"+Hcʧ3skv&t"'_;]KpB~zNSUX%U75hiv!Q[=+jAs~))1\EJg1FԳejv=,MGcGc\ůU~{[T*JacΙHrC4L7㑊,(-[L!^@XqZlHl,} ,._gR7gtfC8LӖwc?/W5h_D FϾ"r[}65?|N W:4H&`mgnQϭ4KW:GM>:RI(x*oc p1 n+XKZN.ͫӯ<=}:qıA# k]G18V\CԏiU}Gm\bX;]9ƚ;¨SWWwL3*XL=K}x$ kB-O mQʚ0K%'@ 䬑R+'?{R>twTJ QLXzT,+no5=EGxЛD2@*x'Njvzƽ9Gs vL9㩄:_ yȖR"Jv]퍪qhىss Q;Wi\aCdcq=追pљwK 5@2dGF÷\WRQ6216+5Bi!܅Ҹ=`f]ǂPp8M5ھ^n9joYIuy (I=[bqѐ7YΙoIkvoOպ:wluHFkY^,;; M^a[w\us9Kd1ds+ 5ޘxjS֬>[}^jM VCqYLJ|# q*׀e6#Ӧmw/?>˸튞~'@89G:#6ZUry lC9[<?,G|O־1pSޚ<D>hBdr+c\ܱѼ>V8.K ߐlIGNH9q¾nV]K.e FNGrAN{npGFۜ穯?d=W>wHW[+ vq?-tjQ]vV7ۓ&i*3yfc+.\_HIȝW'jb 9y/{/hU\ 5%Ie8mhd#GK-Ӹlm?Q6Gz?1HʎA8pFg1p*KQ-QRymp<:f9?qK/aGjcG4E" gBasRmyԙlek+^qZR;D;0+ܴ1FOָxhwI!AP=!W6d$뽎ތy6hCI`31ڱp"<ZuI# œ f0Mۛ5Z‘Ļr[8>y&q(\5A|4i"(pV[d$Pz~Uθ3䶷9nOz xB蛆юYJ,gl(@7&| x¨`\ H“MF¹|:Sy!\~[&'z0[|<9~2wqm/-w)#0ζ'X [$1tbd]; lEVOq>: #ʹ8Uݪx4Lq4!"Y5Po7^ƺMY4!kVHntZiؘz(8`0~ °맒hsq*D`*0g`2Lՙs}֦6O.",t08#7_Jt>mB#嶷DNA%-]0Pr{[l9crGVwK†2.3v漱'\g>Fe[r>S!'Aw5L([l* zgh,<`~Й'ۚ?WB^ũ-ř6-HT`jo ŭٹH%f@x=*SF'+%޴f*1Cyr>j/E<3˼|Xq3Yf_#PoAw{`zYjKk쓼͵<"ob>KKe" r<,F7* .sFDKAOn+N>1#~'S_)+QuzԿJi85G"1,J@"""EIDtԤS-a%P)6АkSCmi 7\O<0i 3*D!axrAznPcV{gPH98_f[ (Т[G,y'랞uāŝ6]aZ3!K([8mß|T:tj1e!O@v>?zOq$J$hQJRZn7#VTbtZea,6F ҅ sֻ!ӥNxsc(iǦnOcr[xF] c?*OxM%I<6s[^ Glv('ɮc(.xտgG /~W¨$I/g!zp+:vUۿUs1f,5WaDg$"6|ݻg 8FP@ D tG9ܾyWvHa\ᇇ;Iqɖm֞,{Hu~6*9ߒAso#*0UIk"eXPЩ8bz'ID<.N{WsfKx]%FSy%W& ^4ʏv.=O5ZD,P R"i-$G Ԅnyұҋk'l)(nG, ̢ PYW=z~OcG0u;DMuv}i宇%(CrvzXVVr˸qJ1L6[%XNT"YYv8ݟj\OJ2FWxSpHZ_5ҧV&]%:[KpλNp;c)ut7,|f:t5{;I]-,˜*4$ ;sRto=H<kӨ<+soB"\!ǡ+Oz@VEe 9u}}Rx|7質 .3(zgaג+nfnR=a6o q^aiz 2)?+7eLלV翭>1JWSè>Ֆ3QOClp*9FFk ZΘvlxvo@զ88ICmGj n?yWMc-`Ti?*Hlκ'nD*+iťǟ67^-At􌘣ʏV<^<4ı'5mQc|G,YnC{W躼ժFI8pS WYTEw"CXyhc#ӗ/ sCcGN=,!QfBA;97ŤrZI.ZEԌ's}aq m.sYsUEc$sjf02 }%an*'Zt IosAY"Ԏ;B/}C 8r@?e?P7$,H8  msJð,Lx*:ui hҳiL*p8 %'z5g~\EHz(7cݘ@+e7᠕4~F|+seps#6ᅝ-#jyjYxUj`El9jY17MTM_])#wx7R."gc9Up+~S8O˧ jm+FM"8Huū /LN3]m౳{I# ±Drq|T@H* > kGw4o+h̊̌b _-drxkKQ4<bS̊Kұ=<,R_C~J#&lo؛W?oxOj[W;$=EBvgdiqg#3,aw} l.ŧQ OGྏvߕ1ѐqӐ}Ǵ/%S[cdw]1I ^ jPZ6 .&C8sRI,XI,UV={xhZ=Ưk6LHi ^+-w^b"lr#9Bs*d kt 1?zuZsΰ[0\HLӮz'+Ma"OΤd];(`@2:)!7vȊ2jUԣAF՟EhCTc3Hz|ۣVH)Uר]e:?#9;%%*\V-jh9C61RٳMm#3AqV)V QhGAU)& 4+Бү]{l7qL< DR=kGLSWxķdYmRNsW(UI]BIUqSϡG|Ȝ*+&+}Zq=?#]ǎgBuϭj8"A~=yէke w.BǸ'r5yov x$ OSg&1ּ~$0vUk^8m)̉_gf|د2Ej*Bp7gVr4o!]$Jŏϰ:͇f`,SqW\gW}k~lŋ4u5F] {(R*}m} ğMǦ~Y1+Ñ❶GZ\ &efUU~?t;Ɩkwy%Jή!߼p2~\c5濳ZmY-`?jmHG:e@|Y0o tfY@j3- "vB=8Լ?,}ixqΥc{?;hcGӃWak˫tnvl*<9?Z i1\i 2Krg˃œ7RNཱུ%YmO>Vʮ#??Mc>Rk_s?ԿJTq]Ŀu/eY,B+"E̿Z5p/Tȫe(}zsV1YIjm^S8ǒ]VRI+βLQl< Q<*H$SIẄGqȇȫ[U JQ0+DN=:4ڐ_@8:m޻cx3E{X,w^775w+mZ! ȫ)kcAl(֛;[{Nk\Xos~IŝYgMh!ȱW|e~{tFㅰ.Ēow垃ڰe⣮Enwb>#aks ixG"k ǣLb.T&p̚/ĒK(춳yE*ACMg8?/W>VK!Ei]-6Ztk\F38ҟǧZN1IG0ƨGZnKG,@y3PKY 9"u`Ğ})sc$͛v wgi|`HҼ=L0wNqJf+#4߈Gn^;}Pr|Q玙ѐTaEM9_=AQϣOOhgܬgZ/f[+p"̢UI6Agc=u2 kxmP0?:{c 8EߚKtyc8Dr 1=䎻G{H<[ۗ58bF:/*?GDN=zY*)yOw~mм/+,IHv!h _3ĉVi ogP6 ~,OJF8)Imy㶳.19z#ukK۷) mI޳-p<ֳHjnoaWEMJmomè sNiwernYW~2:c=AF_ ZYŤ`y'߸4Rp<ܲ>[kV^]̘AoKdX 0o1UHOZֲtv-XΉ~!:6q/GE\2r=m7B[2_ uT?UzjaTO6sMRU8G)?ϥ{M KE :kIIψn1Uc̔p H=*c*ˏ\֒ <=Ml>PR cʓ[VN*),p08OLဪNd ? 85_.<9Gּjl«}  Ңɰ!qX|w5rՎ=:ܵL:)Xakaf%1"n2SRPj🍮.%զvu bw:]i-Vq{`{W=v_T='kcižv§ v>rT zmޥ;3Ӎ(I[u,m^ܻm3ng' ґ6@ $*np:au>YƍǕ}܈d(lq] s<0,ϵQFzn9~5⾟-!M-2l3n@H>F3˾<5ޓklDG,G9qW|E~ˍ?rvyz#h$cU>,޴,ê#PI_^ďk3P.hNF+?WLǩ&kuX#9:WY6qewsw6~M2i璤 t~.L]dt/YX GZ:>Zܟ[N rbZkkOsZ Gh#`vl׃h>Ш*zY/Hf4Z뗓K/6f)Qsg?hV;%RWjN67nSs\^%3}MZ UC$zԲ⇴nTRTu#OKM:`{ħS&O[2ݩUՂ{\QP֠1LqX2E~g:WHX˙QKUv4|H?8zEMrx"c5rB/dFUϭZ-,Ԍ`trH8%,_;XJS!=@mTb[Dx~5})U\~T2Η~tP6Nœ&Dj뚂LSqɩDFblF^t( ==*=S!PJx$LaZi&3iЈR@pBC}}Ex' mCscOyJZ?:zƙWVI.BȻ#9cXGez, %Şs}lIlL uGz4ֵ$"[_y'%Fq֦M %~ 0Tr+OGVZc^YKmBO2I,,bX@meSx tz?FHΌ_oxԿJWt?8lRW8g*|[f2 Lhv)JE@0cCgmDz !TRʟ4~֜V8,gs̾1x}?C^3/[e_DZLiV gU.a )2 ZW9;Aҕ>}HvKH7cy9['?*`l֑K \L1= "m.4#݊,1 b'eី{TЋ[H#ɁGlj[*_:&yُG wW_-Bgƅ yT{3:]:,LHcݸ1Pv(IZ]֦V 85_IqT/Ӂf[.}GO%+j+ͼJ_Gl$"z ؊ii`~e MCMS|/ڮXʠpO WSh }BG;zmsúVHf} s?+^;o$1Y-ܒKta:vӹ|Zk+=}EuocqGEc*\W׉aQX(wIr~jwOk+tc E4F>ܰg&Kg?ytn?J<\5wȻ!K6HNJCY|g-"e-0QNNAtR5cR#:x3|UkO+FE[& e >pk Nr%&P@)9FaPκQI\8].(NG$ެB װ ;]ˆppqڜUTtY/dtnW*vE=]|e/N5\G%.17~=q+Sht* i(wx4XO]lg0zī/h&pm86{ R0>KBm5f "5 ޙY1 9Tӣ [] nڰJnN6>t;) }I?>TR3ʁ~5š~v0n{kI-Av4kxI8t<{U@dbTI2yfQB5V@h+G}7Z[}xeP2Żћ>xun{)?`/}7E=jv7́^5c-| _&xk:|HRX\䪇 ~1XJT4ϕrEXQ` 4MX,Vl+mRzf .h?g YxD%e[#ټ'e`~U>)jSZ)#l#W_/es/d[C؁\mg=½ix/ű,&'*v+ 3ѩ]sZTl8apk+R_g#cme){ c⽆OM I.cx7px(լfc-ni1(U$i h:[JMYE'`n<898ט cw͜cZٽ@Ţ9ڹ- O7W2|ffi2FYwSR>%wUf6T]6A6mF3* \7UHZև>8mJQڄ upEfpfSp!h%ixm#ݢOۑt ;+mmR*?xzX|34#kcq\!IRǚ~i>wm<-|ۓc:~:uxkhm3 1+6WsYq1_kE-Q3'oI\>%6rI\^VE +|Ee5nnR/楂-⠱y$d]962T'h(FULH&&R*`D?8N=)Ѯ?!MXa<դIb 8fg[П6m6?^N(@*)sP15&RQK(sZv H3YF[4j0qI%*2\Ҫi-4Hd\dq֡R@9b0jVw[ǵxtbޭר/E2kp dcOU}?F`\fٰ2z\Ip{]m:@rZzq\vTނ2fzKU5It+pk@2㏯]Q [m`z6K+{lT;a[rFi~Q4,8>kt*xX`Eg}>LpQ*_ZZUxw}Ҷ{[MC\yw,eJXGf3=я_ք1ܙw=dbsдiDڟߏ2BpqזxoM&k( .Hu9ן^ Fo:7bʌH9#$W "[=|T:OR򓍤d5:`u5!]JJ_Ԓ nssM;]Zi ́P4BEܛQP.:yDD\FhI܍xpk5gU8} bFֽ†Ge};~!}Stgqf((E;Vrbqqэ4_. Ʈ[$ơq'cMNV\ő9"qhj,qqLoﰑ^h_9I.FF^h:YcIsD&pWߓ3Z*1VhEȊVxK%𥶍߮k& E*棑NaB|̚UP6ɐoQ{8?ʭRKrsUn>J15cV6+6OV7UU3\ՋdRjޕ'1`'J2X 0<iq"S+6ZFGwʞNzWÿYjvm{Max9uy6@ʳ8_[A5>KKoc,%p9 dv]7;4 CGLW'j*#slֳlKfZ4ow2rͽ)RT^69^̱6##qkh.!xiy7FP:=h||8 d2!yk`eŷG{8&iUecr, s f[_C?GUdC=Nz?*E\>ZEdQjs2† U[_$7FoH&y$dr[MCtnGֶלxfF$E0[ ;du#ܥN]#H"b/Μ}]E+x(li{Xwcӌ)S&c?š5~@8 =;c]^tV|U}q'2c`2@z~Jm{Ql/ϒ֥E}^G3&"D#j咺s}DXwq5KO|I\PqvYSP9Kk)lQrP¨%!84B* qD=|;y\4(onxMh`SisfVӞL Պ(w =ԮAوh?:s8T$b`bKZznu{ l|ĎJ<'#.86RRJ94> @J ʼ|1Cp77ːq¥Wtg"nlo8tkAid}+K+}SV W6ƻ>E )ciCns^"6($c «6]I#(Ӟ6 jR+GQfmFJ3L}:U!VOӔ B2LK].Mݦ\+(OOkKCD##]9*I*am9xA^q,/jB [˖ IP~ο ֏&eΗqF+OSZqN$# epHS_)+t?W:IXH+4[)^ҿ$%lTM(?xg9j!=XUȮ'Nb&i@-Wv^U#ܧJkyts nm=G>1 NUø?NTSBA#L|{̼:7Gq;NJWO xXc$AYrxWTT"ˎm_bƸOlޗ xxUԬ;D/0[M<M\*)Λ2*#"u+E]KKK|ѡd9d~5WL4Pt(` dɞ0 ô*9\ڶV'if>_ƏhىN:AcS5!K`8?34f-Ӑ2 r+hHjID؜ԊB>ÊWt&[۟.`m<)ݠ#J &8ҜxW'0W r@9$s9O ƽ#eG2(b]AUSmY,=Dh&[kkeF۳RU #xIx;sҨc?1ttP%b8 P>=.p *+>xo$!R$|`}z5#..8GoqTͩBY{s+FYzڣU*1f7ylOD9V#҆THY;9*q+jٓD{ꓰkJ&͡0Anri3I Ù. 5YڶqSLo Caա<Ѷ5r#]J7׵zƦI+ۓX=Vټ9ĩ^A$}8+:tb  ywO׎s@2Ս̖1O 6;_Gh:zƋm~e_5^ĢNIv89>J]Uݑ+B8Kqx<ߖcDI Yk޳@`֏H5R_ORLUdd8 j;>ȱ#}k~2¢uc; rQcxY-K08O[ԗB²if(I^ʹyf§:~>>Ovhs/W?>3IP=Ԏ/^mwvڌu K8; sTcft 20pG6Jf>4? [+J7DjPDuA'PECڋ/ v̲i`@Ugs=p8  yj0 ##©''nf̋7 SV2,/=ְr } Gd q~LV=$cFE4&7O$v3~x호]mm@WkzS-̰H@#'W͡=NBYƽmXI*>V75-L ySڹ)I|8yձ >H&Qo'Bv ;@dSdҳ5ݵgDUDp5`1XhH Ͻ^:3`G&rxM ߵ5=cobBI SJ$Ry` Up' afޮ$z ecuL9榲aVmD+ι3[Hn<|٬Ę)l3?[sY6yֲSBwڂƳ?y%p[5S ZCZ{IE6#;&eg"5$:j;š$:GeDÏM.zP}=܌CulFl{dž2qFGk{vg֭Bw=ቄg,w)sVMBX9Wg\G c.~5?Ef:.DJ_ZM|ܙA:d1 V{ J uٵC($xǯziCԆ9xeu#VF$VףA 2?%x7ze.2F*gւ/f|O۩r2@>^`3z?hOW/bz^r8q:#ӞO27ekZm݀x#^HwF w{(_l]XpAȮZ$uVoa}5g1Ս:9 _Z*GEJ;MCl*FK^|XQUiJ*N6^ukDӬ乀䖏(}G=+? ZCVZ|Dl"c?xn ^yMOڨ|}uSC1zXM|먭5;9zpH1x\s׉ nez:°[e[|n;346 \1pzKFg,4SD()-C \lGv߭i[ V{ sսq~+fn1=9aMMe)躐IO=̍$6;r2in; Pyw4N|rZCU)郊@Aip:zS.yw#q>t|Кd-߄ZclRD[?*UWeM^g8>ks4 0[hg0p;Тyۯ?K#'Hmڕ>-U% 0v j0T|x}O_MF?''(=s{)H*pӄRF͏z쒧< DΣo9ryT0}WhG_Jo\~V,iP61^0BO;xY>Ԓ X?;bbF#ʻ}0@D4.[sqּz rݐcG隈 Grp #niWi5`AJ O:&ܜ~u6Dpcm w+j ׀So+mp A^V;O|~Ѹ>9ɷⶺ":CgvItSg]5/'M 81 ;Ӱ\")ԅcvXU6֘$I)u異2N-UzR*t?+I x,c*8l:¯>QSq샞]̯\O:&Id硬h" Ց|ʳiʿi%dc-Y`{" +ӊI8nZң@9X89ce-("Rw۲;uk!eRCV|W>lҴ3ZGɼ੆$MŤr{.;Տ5C @qW=i5˃v#li`s>ZO7%KFcUv~c}~4rGzc8Y &VBsFECdGhr|ɫ4cp3֋u) 3'MNNFsDOʼOzȽP.[܀ɥ-%R|0 >ըCҚ=PRw,7$ SB_ ¤RI\yOyb/rKx2$Sn>SӚiy܊ 8= PYyJPcҭGCqqŹ&1iMnm?!;IL֥b;$;E9?r?ty;_+= e)Ft{5ʬyxnV+awUXnfd"GwH*sZ'}Lk`^C$8ǩ=i"x\K+\ܮ=g{V5kQfotoxx-20.08/images/stack-slider.jpg000066400000000000000000001075461362435004500174250ustar00rootroot00000000000000JFIFHHExifMM*bj(1ri%HHgnome-screenshot0231Ơ0100Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx, :http://ns.adobe.com/xap/1.0/ 537 452 0 C     C    " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?cV =ƙ1#+EνzיG[Y,7}#B?S^;riPlQE!g4PF3KRPHhڴ;R1 ӾƟm{smV[/ۛzI gj)M!d<=Fl2P9by)8aX~2%ú򡲘 ,^e&k<'Ak mQ]>Bѫ 4#=XW~7IO(--#VR4$"z sY_韱G_%{vB#1,Jԟ_xg1$FD7zֳZ=F2G6B6a#~~`w.V{OͦkA8ER#2Ve 7q#Xu3]ʡ WA8Q$.R(\?hj).am덧K9,YY^2)GN\F:|u8co  p4((94R(!GܤAqS-[t{ t] (cjèj )QEFlTw7QK"g8ݫ! xQ ́גO湯Bږ4얏*~/ld,ngeKxۦ^y㐡>8~ީ?񧊶3iq[)>^Y>T%u]* 3PAdm.Hۅ^Igu%6>eķiVs$ėy'w%`>+uxkHFmOQ&YՄV啕r-wU#oß [lk[̏u+yyXq\YhA럴;+5-ZaXGڋ#!m@ #_]KEV #z]geI,<,Kׯgw'koh!ԁh$7ڿ!fFspS[1=ku=ZtpC̫УKp$6+S|M{hYu_|" ~7Ѿ+xEeIFx44H iˎr@+/|OZsM͏I]C[t hw#;c?M|xƾ$kmwR !~ R1Y}KAw„r#cnIϪb&qgMu--Fk3;'UDPBI ALhhZ<3c]6f7VH\0@l 2MvjVw][>v V€,QEQEu\mvޛ0q$spk5qkS Fa"i<7m0v]Df%P;H5&48i[/)*YW$)leZsSGZiV_&ba[F{ u%7v;cD]weѼe*l.uaU& rssYD! "~T/Eu_n*Ήk h1H vTpˎ}OGase /ͿT@ʠ,xSwb>_>{SXi6rC*n'!ORjĻo1YZY<鮚?1@s)>NEn)俸g0a* GlJ?4MԂUFepo(B@$3Lڄ?{Uf_ $?q%ޝmQnIEUG y01+w < %jK5+' d~Ynp<ݹ?g3Zwˏ tmsmIt|@U8f| W:[?xDynb9nx!,wYdWu#N&;qiKQsῆ麂x)uK!7SJmc$@҆KT1q$Dzߋ?i aM7+ ,N$0I!EY#hs/O<w7~7_LMڦun!(|b0; !Q$G+7j:">l|?}i%{g=ԼQoYIcZy кI Lm,x˨ccI$xu*cX 4A߆[÷HCr!_)U[0I?UݾC!y uU$aUCyƾ!u 9l<_i^Ԯ.nb lC#Iađۇ'xGQIhn˖'G(,HjEM P?W]=3pc^B's) @+o+ZM;Q%Uјp R~WG쭪rZlb ePD@HٴٓA{ ťԗQGʤi`rW5,Y6Ϧ(V 2G/j:͟KPm"i"(5w_on^넊ؿ:1G`vrWՑsSxZ4+h<*-JRܴ0V7~2TgW[[a/&ʍQ ݈ϊ5?mV.-[cpI.N p*o^-GK/xyhOT66HBGKtS^EZL~3|Kmt]Wx{LjUK& yZإ浫I$<dƒxq5I:K{]2OE 9l%&Xq⋛gKteb0p kAEDg-9.\l-#gX .(#xoLVMPy93~Oҫ|;'ndi)uӖeżb%qm`x@ҭ4/j_ʑ*kw;:RpvIbHE)JkWrlK –S:ݬw%kc8b~g :ɗN" ʨD\bRYpo NJG5ԑZK<14ͅqg2oCU2yŝE>YaOUQºDSj&8mC$1;Pr@ xOFQ-ަZ`,q?Z=_߇_M9Gy%Z<1n̛7ʡL;r8Pmj(*תC~&[~q5:?*xx_I4ְL."F4fI}KM]2MMѢ:FB$mVa_&^ <1ϫYGxRlɽdHc`TSa\3n$>tMSohR6[ Q.ʫ ?#|zmz/Z__'{?]YuoųxoxR,!gt;T8^|kxk7G.{i]èl0a:dVncŚ-|%a$ڕ(D0\mCWw7_Q[;%e2WqϠ',x_ĿwGPm$LȢǿ*Y$?gںCֳmzO:s,l1$-.c(4i'|-=\kuӥha$eh n1. Er>Zfz/y#yQ9=hWNir[El)$l7ϔxS|[|HG{i n.^17-mxIc'~^x#ƺ'ai܌Bb*jC&;=z%[T{kW!P+霕0;s^xQ\~~մO^I٘\ N3տKkk:FZ܅ 1ܐ++nyok -?.^-ՠ2zFdIV?юV5=>|/l4vCaQKsIx=ZVx[Xm-ʍ!1de9'֮湸Uoe}FW?*" rI&owMռAkVڋgR 9|brei~o'//$ծ1L! Fc-zJ*Ʒ}.eeyo-j`Vd(,r23ʂhy׈l_^kk,%*Ѣ,1&;_t_[y7^x|iy\$M"o<Ғ@ڸ?Ŀ |+ICOi ͧYh ]%g`/%t?֞mojjV>0CgiZ;?9REve9\|2o-ZYW2D..LQ456qagϚ|I_i۴xo +$ F G!s>.еO]/oό4 °1FN^)TGd<1mΗz. `bF>i(_'%KhX͕|>#qנ#He=54YmeZW0uMGF%3ImeE X"I"(P"I$q$M&'c|Yɯ>T.,)o8cx'6~F@_WxsS4VQ:iim<3)yq=&I6?nFTROa_/^ _+r[+mSVdU`som9]}gVh.܂]ƌs9,݂x'íxeS\S?fMCTYJ31r!zb9USyS/~WmV}#"En#'-ݰOj7O^c i~-ok.f+]M b0Y#䑹k㟏=Zڥ7VzEoHv Yp͓mu|Ak.ANdTuYyvөڣes"~=Þ /KlcVIek ԑ95 ^,n.3-WwC#{@B(R~ /3I'0C{E+(vb|1PJzO /67jWw:s"Q\DAܩ?11\uZ7,񶖺GVgtRXݣyy$ X@]ޕ'|OnwR )^4/-垽ۭΥm2V!R1eRJ+t&bxƜ:މ;)j]>bic;=Ey/_E!kA>7D3$o ɮ&LЦ5&˕@ FIc{OxFm/Jiu=[6 )1BastS4ր?Z,Ae.޺M7&\zZ$cdڥeQj,~gYZ[l7ix pQT`cc2-Υ/mѤiH|=Ma*Ij7T|?|T𶫣x>,W&i|5&CDHJv /׎<=-Z]>k-/"-"BX29 O~hy $Z5WZeʬfXTNp=WS3G'񅼚VuVa-l ΌхBi~HoO.G~Z_xsTE%ՠU죏FrۥPyRT. Exco^(lVֱ/e턱\02X-cX~; ^s]gNҼCkxgMvZb[U&hRW8*q]?kwx躄YtM8I :kuXӮ-J.M16:YR'q@KKr|["1ҏwiYƿ$ec%P0gXuM6Γ:z}q|/##\AqpG_ɵ_Z kQCqtKi]g1?}sx*Wwd0i++Vt`DBcTTxxBp$'zo[OeKqo H6Tn IO/''Zf+\ɎskS)'Sņ3=͝{!G%~9%sӼm:֚j6%6+\61AN_ʥzTMmy)$R ܹ*@,O;F{T^Ev2jv$IaG/2|܌9Vޯ-pc2P\@$<m7kA5 rܯټ?`c*y|ۓnsk;tZkܭkk.#jH'ռrZ[;۫!5H O{W=XRھ7ZGe+X2" r~fr3U.Ъt{7`Zkiu&-n0A:„eޣV8nxP)xH0\}ꡞ`B>D\#55u'.5+MHt*ʿi#_Rz\ڿċgZ%_ef 3S #.੸JÆ#Vor/Pjubѡ"X'2>F9S^ξ9🆵[g[Xխ !'eiV++P}yX_=0yتy9 %gx,>.k\iorI$dc=b:ړEN/y6+5-:K[(B3G)>dҿ KLm7k7۝ϓJ3F 8]WÝD25Qb>o>d#ex''hggѴX"Kw6`in+ׂDKv}G_n[Y > fo.ͱۨf 󼍁苦xr1l~ֱ|?!|_+IѮuT-f:NڥxpJ//Z1e c6>^w|ɋ:(T󲜥NQ>Dzp${iRDdKz'h4=Dn #=xX]x5g]4 baXɒF`"6J@pzSL𖽠\-ZN`|PN$2NW=FfoKs"E$%L('z]H6mkPy1@U,zuQX^4NN^X-CR" H$` ۃZo5cv H19怽0Ҁ4MՖyi ћhc@ڧ$`rkڟ1|ION$ncs.M-IJ.wpK BI޿ Ϧ>d7 l0p͜sӿ\y¿ixuqZ`^LTI?/K[_?7+{ KKk#6opQ̛#5k%Σ1x6^3 Y_iDL\ 0!#!9R >7_>HU') )>ҍn)%cnUng3kkf {ᆳKm$96 *TĎ~`~(}Y\Vky]dcIɲ9bXrO3xǺehsCl5-?Q]AJ$Է~K{m6sj_|EuS%eV}z #pU-b"qeSZߍ/aOZ&us ɛ,JHKZ";yLw~i!&Zh\\D7: n`A9#'z?M>#>X1ƿ2yC`Ď9߄>ҵcW񾡣٤^Ook*^qvK1}jt(eˊ]9pF >tMqYeI|QN{r|Cs}UԢUC'呦̸'+IV=qI\|+qu#ƗzEqj/qm`8pX𬑴Ӂ^|?a+EkMӗ&G $L * O|,utf@!F-,pk'x4>s83Krǃsب.fzķJbF0nWe#"IY0kI1îfmWXUZM۽3I$@arIwQoؿGյ $Ӵ0h0ueXjTp1QY WPx[g$o6yp>DgV_$V?:[VlZye1!%CH@fvNJ_GsY-eЬg|b !'|LV|N},TT'Yƣͦ@erھrv;5Ԥ.7 mvZÌ\K?|i뷳xmopQB*؜y8|_a|5>k!!H 8|_bW[=KMk x }Ti%Pe]&zonk{Z7m-|Ni%;Ho!"yq#oF.4-W/C=9XHZ6ͨ ݣK0MȤr#0H~ {M6Y|Yu.-XGp*]bK:?ڭچ>ͭcPֶQ2vVǙ(`ߺSxׄ'ĝ\xN^Ǭm!yy)#{t! {sU?z;l}I —E꺡2*H{pΦM$g9i|c]i:톡]m}y%}0S$T|ʣ xšotUYFl%3p0q+n>Ime/5ˋ>ԡcxpss SKC Ny4M#LӅfsG+F˷`mg89I]HвG0Yġ<$ט'_nqb,rD(" X#g\D׷#sǞ|kƚj,(.4ꏦ?cBv 9R̼Htn^m 89SԼ#m*8D_fCC Fp][Fub ^KP,fPgrR䃝S^4ᇋןl,dd$|vj[$Au#oO.GWgFx֌5 ${D׮-p ̅Z] pqonKW⇇ xX"ooi%xѣ8rrHr?kP񦗪xv'[BN~S>܌o薚&^i2ٺpc‘C5U^@sk{º _:|-Lԥ{3Lh y@Rb5!!9) k±Eux6ǭd7So*FČ#M˹Nm|&?'|jΟۛȧ£qp%H68|OW_ ӠYC ku /%G+G?>uR Pcú&h*<ZqH1 $ A(' :Kr6oa^^4Vy(g6Lqp#-ؿ 7HټTP_k1qjUEHܑoq[xS_kp+g9f"@"(+? TmնK77]Ca"1ڪ7~#_~+ ŭ햳{j$݅Da$g\mu=[eA&Kc+g]ǎTꚄvHþ6DYsRϵ||Yd+{&mkK, (Y km'F=sF2YD?HXc\(1&kx:/ [_ue)|D#NHwT+壢s} y s2`W3W  ^T|9h& ѯ.2 8I9 uJDa4iֱ)fw** J^C-eu%%.",EzG dA_k7NwsBK8t$[qcN53x{]ZY4T+7G)`\ /$y&fۅ%;{ }v}-nŖaGǸ,F:{[HV5m']c4pnV,qGn$i9+_?L]i%Yb r-ӲFC=&#$n9)>*D6g2$G LjpDcJǏ@{Eݼ5F|p@5*i5͹-:oI[ #<1_$vGF)^[Of"dJ-rn!h 1 zd tu𯌥i6Vw0BU<(L`5humjL\gp$67Q&r gGP:WɪI=. p>y|i''vBGqpp23(f];|k? xZ>uIяzs.N*8SK]SC!2$o bDdq*WnմgM̎h+&w($ f#1.9"lII4>Ҥ~BJ ӽm:ي+E|eD/bX[i B:I U;sIkxSL趵[?앐%LDb[8Tv 5wIV7F(mcč4m9mӖ-dBI#s޸TՇR+"~~hѼ_1 By?0҅e--j!c>(X&-ymc=nDPw`W*r ᳿߃_|uN[^h>?Є7[h\sBUe#0Jl' >hе/ĝU!ֳ\Zv+2̊Wq")q*㽾 :x~W,mE\Am B1wx_:-!^k6zt6!{[J~id@S`;1_?#|t|@<m5M̍2w3H";_< sTK3xI [[-a{(#`+s9f+pQY_F1|7BpyduhZEܾ-|E%iP2HAol(8ٹz/6"'U1[iLK޺??n#.JR)Ēy᜹8''?5p|q_hyڰ yIzQ{>*˘Lj>) jTނUP#ydeUֻD]e[WW /ZSS $KJvX:w$^,[%~_ aAc&  [(sy#TS(9'iPIʳ 8QWKcvoEo ;֩mp,A~ɒAk& ?|TM[In-1vDPqx|767 ?g1n * 'u{a."oe獏=ET5UUQf9?0rVZi%҇&UI˫] m1Y`Gwly;,V+hNj}#HKdY--f (@+l6ji8N3θ=(AS)G$j0U9' .A1ĞL$ew2X?gIG;9'&Ѣsp(X  %Fy9} O-/.oc  ;9r8oH^?bGeR{RFrYܻaVž42gtÂȼsTm#-F$ 7yy[h:w 0~SRތGcSKOT=G]tM.&y~XLeB~헯i:,)q-4>ėZ\-o2+n_}jokZ4Əeihfg$y19udAJeFA1jO 3j-|sHuiMΓn`tɸ%EWJ6 DɡMnY یc!K.6QPֵ-WFM[IJ[K=%{b$3ƛFh#q\r@f?~?[ϥCP}* Kܬhc-'&w? ߎ(a;I[)4y.f#VY$H8UP<ӌ gW޶mπl-RFѼm^'\M'5 G3jZ+THg4)A ioxOuY<7$%k+|XةJ*`^͖?x/=DՇşx+y#CΊ,c#aM[写e5;Qn5i￳heIXy7#"11NAt~(g XxKvIn6K۫xXc8wnҥ*_?lWĚ5eMY9Lj6 2 %b(?~KgMwƝV\i5Vb~% F؎`c ? B1oek0^ [O (Ãp0?J)sW#>h!4WR[i7jȎU V7(o-P`Ҵ4ap~rO~kd4_޷-OCmk ;.J8䜜]{OK,5k ;NSopIXBHz#8> ƃs$K5ֲWbȶcUN{u#a>kUo:ֺiw)<Ʋ>>RBx֎7MflG"&f(6zj/5ms{7Zh}ǩcxwYM Vު˧Z <2r@]m yfGwlo]2D]P[ 9"2 v:xSÞua6w:pz XCUR9?u{XRKu+֞I&HPsn>W^8goMKIm ,Ipxϵj9EBc6⟉qyocgust4h^(f nzW.",T4>Xsr|/@\~ J䷅c Spzl[Xoap$HE;sM9|D^Ϲ&Gw.wI5&_gxmd~MsqKjUTyaeۆ:i 4SEoʫ\WmiEsaQ8/&Y9;H8Ͼ+? ]z^d +ө-I%"rv.<i0#rL֋&+;~uqNG, /h*d&n ÿoZNe!N"[8 #cG"xaZ'֙ 5.. lSusF0Q).ʨ_,xkoKqkËEd{%o)]s# @D|"6]A\/7=ڟ\ٮ4LP&v99l B$\^g;j, ;-'>+ľn䀯;EB(Q,h*UzWa`̱HQMQTgKse:7!Xs۹|zcq,4) 9 8[kpt? xWzn;GJDR$,9T?k*<5wuzh*#1Bu\3Fܶ%o/%jjJ"G0pP{? cwKI KMw8,'m.|NrH% !jU&kkzehscb`ݍh><hFWt\[v^"kg䍱R^iܜSV,6{{?woY|?C>6h2l D; = Xixdc>p .$Q蚌 o :@GfZq"n@8 vk;+J+iyn%@! '5sn 4p8volyIв/9]ꭧ Ɓc}p2F߷ox"V}fkvi'-kc ;H 2G$&lV_ Z5 ~&zz$+%5d0+G\'{xUMΗu?٢id+رKbBB䍾42ox^/#CP$f ʹ&P4T/_~"Y=,zJ^pQ8D+vPv?|!sbK Xt2|9dՈqXd_os:?/tӬgj:ӽVDAgfHt2Q23_Լ7M3~y ?j[ßC#ab3Xe4f] ~ԧQ77w7fKծ-b1Yc bD < O'Kv^*VXZN\$)L\\&dv!_|e˯4<7=ZjZil$EI {L=;|>?m457-t.mukqz"hʭf+/$vV6Lr&r8N}qKmpÒT+ OBl |k׍&ks,zı[7HVF7I+/M5M/l"]E-eWnA8 tlt'&'m>vmZ)eUNp @WW~Jnf{5 $^dqM9\3}t:.,t}3B ܺmmV+ I%pO"l%{V0 {iyvAȘ+3*Iyd/d|,"m+hEآj"#8,9W k^)Iu Bg }'pxfk5OVmSNҴR@vf.% qVD;9f;PwsݹOb?]sFu?n!FE#.@}+o|GmcE*3'5Z֙XֺLWyNy?)5%<_jkȼs2,npvC{qsָ(-4nOl / -HU!Y)CAkՅHYvQ{/m%ƽ%ZiH U 9BrW;{?~{vtk\t6jXLɵcHNW۹`1;#I¬BES\gְ[1;ʹ\߳?Fxo@&%h,%~qsL pOk[c=t'JdmIFɆUS z{ Ѿ7^vڮk#͔gF֬eWwjCɸ]KKTp9jxw&τ|Qk}nu Sػ~а%Mt"8|\YFԭo%ފ`:rek&ZH`Wv^i #x>'&o5MF+9Z]0\mE"y LWhϺM\|\{>kTE5+wfuLdfxm y Qw:ݏ"jzoeo=1I:]$~ake.B^"owB<'Ɩz'WHiE쭼p.?F@+pI hmH־_tGoXc'{{Cr 9;Ɵx4Kv;O-$ ObE. 7 R^oŖZ-ûn-_{^$Zᯁm]_V$˩[iƩ)jbi1W'[hs"| qh>(j[Z>W^(`xp+#{>^<~#xfxjw]][OorC+gF#{?Nwƞ']WQ[&w[Y^HX@UF$9=|ƶ뤛V{$u x}{ePV) -NZ#jz70\jVdU%6"`g|Ly;m;F1oy I0Kqy<]hp]O^mi[ G$ #**mRTg)lt˥'ms|KUң]hQ}I@>'@ԴFo-#X^@$D;UP=ox:_^,U4@@'Cp,XB^wWNN$xLy I>E%jL6+h y<'vcc3xVo9̲ȬѼ).//?H^I&J̑|lħ95F~<`&;}:@m/HHR+~ğt9gWtlf s$bY%ž),sg]^=?55[ؐŧ  $21^ ֗W䏚oO.G?GEỏ uuIFa_[ܭ.c, lQ`v`(~ƞ?(^iv~"Xc-$tXbd$nr6;SOtCYYxoCm.4KK%NRKddSlVe9r _ςY>$ hXXy1Fr_`]&xǺiz\Vy"1N0Im/)WXDž<1ciOq 6DZ9vFrFߑ"nֵk='R<xL&k[;k]72!c>4~4xS&k u=4O1|p#$J~bG3[y;o i _M,vI^Fc(݈ M{%b>, .KE5')< ż%ϫ{'xK>;)}6d%yyr *k+n)++ľW8N>oڑ"c~3z}(fYwsxyrFj^M#O!΄ Pc$+R9>w].}lIYk]|'5:>#&vEY%"4 K6Ѹ 9ZeKR γ  4>K*G2ۈe;SI8=jφ>^%߈MN`:Dʍ\c~ MH*!4d}FvzxQ}z"pN|6ͩ6mPm-y!4c?)$j]ផkI.Xw#MK"F9b@XUX:;%F아Y^Pk.hdjM\־=kX[C2Ec(Y#QQq Ȝ3(G !mݣib $Ud+?F(ݦ݅F?b:g 'DxMq$𩴷 Vm//>+{:"Kq\Ghu:IUYya hVMVCtdxm'o*3?񏆦 l<%A=WwR\I>h5Xa#±nA!Cfbxש,mnyּE|={$욼Aљn0AWQxk Cpo*$7w#$3ЧR-ntZ5 K&i畔BZ #v;yn5WIHk;FUrI3;*ջDMmu#F9989X*yn5>qiC!XeT+ .KV4u Ԑ5CPnkWUm9-5fȂ8˓n3S9y_:{OF=iE>=JxU1Դejmb[˂1,mhj6r[cREH-mQgK3tl?Wi myx[5P>(^1; R~K+Gq7q#~$|RHqn$t$/5!y<%I"nq{?+㟌^!Ajw/2vf~n 4{<>Iwk6ŒC6{rz"&Vf&MYz>"uo>Pӯ/fuk|*`&Ϥbj6^5/᩠Ԭ/=+X%-3I)7VDlƭBgU?g <[<+qg/Aw |Dweضf(K]547#|~+3%- L2WɶwWCW w^ejuAԵMZ/izİYIx-T$r@م?ljW<3-Ci-׈|%Li.;["i?ic׃^5CG[kT޽-epPN򦌇=Ɵ@7*㋺|s}φt+bMr[I`,ŦU12sgq mdcv]x>}ú=/](IAw|^ܹ.˰<[pKG7? x WW>>[J/g\*H.f[%Ƈ̌ʻ/-x_ZT1u;]T,᥌Mk HH 6;|_xI9##JĵMĺdȿ20+KQ֕o 4BWwpxnp<;I_GxLҾ5XZXkhZIuR$N#.(~T<;-0LU,̶tqoXdMniĺky6yqLWF?6ݘc}WrFA4TrkRĕeH#4W9GđxV`KMZ1[;ymub;;sצ{נxo[|Q&~$ice;N }S67уlߕndיZ\؉mc̽fPr:J|fSEC 1]=4u i%}R&E\<#-p 8;^V=Ik{8N@7 ?3ɮP&{xw-y"JLwF+F#*?x3PnżVDfBH$v|+ L[/j*vg : @<7+|iM'^Yll-2RY"!pn;WkOBR`AQnirpO#+{=h%dT AYLh[˩#H D3 p2s5I4[s>545Qlq:ׄ,_ FeBUCIp ~y>_xYl_xJNk-:[#%;ypdlsdW7/%,l&lkhYb)NONM3Z>x"aj~춖#| ]]AjZW}'όOqIȠrNGA\KX06C $k1]/>$xzMJKmNמ%$Lzq{4(FG lTFש'|O4{SiyG)6'y`2FW#+|sf4hֶ Ώr X #GJ?*ӳ#E*Nҽhe jb'Vާ| ƾ$Vvu+O$rZd`bXl?!>7t?|IKCUҵ=CJ] gFq p̥ ݰ$d ~ |CԼ,t(4,lX|$y\ P>GtmWAZw/lI&AtEifwr1 /K+G=ڟ\5/_ #$K w6Lڄ`pEO6I/Fk6kCм ævttz-io/wy 0w/ofg\jJxR꛻flHzZ"U[O" qco_<_]>i{oO5$ WBp 7|{;-.Dj<9q[Xt⼳a>eR$#5/!1A?-J5m~o `XaEb2b7U(Jx$~ s?~ /t{Qo%$ؕ1w=/7YE|Kg_"i}N|$A3hLkś ûභ Hҕ5ȭ OM"3 l1?# Fp2L}g٣km:{MJ+J2> v;*)nH*dgnv~/>(3 ih,MЬua U@ۙvΈ,kOx[c߇6֕ sX#\* rq#dr|7🃼mj>!i 5 >+\_ 9{i ,B<y@2R= /4KZ^ 񬮒{]U6ʝ=WO|w+t/ok ؆kydpЫ"8$_7ƭ|4ƳEmAgn=!i0 NNI~~ OPΛ4hvfxly3G/2XF*o^ Ӿ$xfћb'YK7p#d]F6Oٿov%k+ĺ΅>sgCk,QB>`8l>W?0/|!G`e "pPQqW  ~nnu?:6OՐ7 ֊҃x^vVilb lEQ9$+Qfotoxx-20.08/images/stretch.jpg000066400000000000000000001253361362435004500165110ustar00rootroot00000000000000JFIFExifMM*V^(if%02310100Fotoxx:trim/rotate|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 600 1000 0 C     C   S" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?S((((((((((((((˿]oW@CCc};N66FrŘrXQ+Oڿ\68ᑟut zn_-u1tGSdO^7s_u x( _+lK+18f:V~~ڦb6 RX$b7?Ee𿴻V+8KuY[i[N2=VhCx"JoW?m#+/ )̨!~گ#GPc$pZZ' ; gt\]̻CFo^>k< |d>:8Y+rRkzHyn|tm|Exf5'RjUP}M{bj_Bu_y'رj+/caawUu]Kk q z$4Weo~6iVWo^C֣Cjغ/PrtU_S<\^wsXca r._C6gC3gzx񿜔_^t㔷F@/ݩyjUPEG~گ,MS(K?7_+c3MY|/.~ccwUuœi>(>(-m6-я7m21x:W|p ,qZcS~>#<#w#čsâ fE)3pQkg7:Ɨ۬ĠyOc_e_$Z]\j(մt~U۹/?/o.OK[Q\*`*3QAf,8#woA{`' &zċaϱ$F C1j ki"J:`_Sq.-,g2i^g q% <5QVi}/%0k׭u>P 6$,DѰ 5ui0 -_hw|HUeNz:y_e|=j>JI5-0vkaڿdgZ]RybCW-LMNӒLT}O|M#ZLZt#s]W=OSڿ9?l.x+Ek1߈!-'8XH97] 95=w?u˽T[m[H@ϩ5-Źig|~X+MٻF=|O6R|. [jk2 4Լ_$;@>0&?޾X4o:^.UM͎}Y`_jx_5C]x-ͳlXuW֚[gs llQE1W+N>=~"ml,Fa; ` Q$&?᷾1g?bsHV~_qo?1-4V~_qo?1-4{DQ+1-4o|bM{DQ+᷾1Ye@w:m g] 3G6/[lG󂟴A+{ 3Moۋ0VmNGo—A:+{V-1G6>0c,(9YE~Wo|bQ m$E~Wobſ[chC9YE~Wobſ[ciŠYfUGSc@ gSv>Wb:Oԧ3QrF_۫c_#8# qpomM/sF9|p9譧؂'4?[vXq-0h+"ig~m~ǀmj?ŭֶZ/: "$)^WS׾S֯N^V77%NIUĝWM{ڣz6_6(WJ,n뽞:5#BG,xbt/|->⨳Y?匿?V/)#^V?;ˤ}Yx.yW vTe>Qg~%)?QgGXih YEzK,eb>"~#^kODԿѿ_d'~n3Lf\)? ?uEoc/MC.E2$b|>z 7^dܩYL۹Wkc۴^ YQg'X+GEFu/,_c/ ~/'5L_/kS~n3G\!? 3U}匿E&5/,,eIT{>/5./I][}92L7\v ?_,h YƧEy,%Xghѿg5?_sW2Z2} l?5L!X<,nO,__UO1w3[j_$O-eϞ_&]Hؚ/U_Z:O~/QerVO$z'77Z:O.s \[hw۹mbڧ5L!Y,̿E3ZXUz?c/_*_7;Lֿgz7?kYLۺ>e__u]}j<@|5é*<yYB>Q_F`bn|< x|,ܵI5{5STdugO'FNQ{7Z kHBB}ܠ`>xsM~q|15H$Ӣ; :qNv8_Ή/SٮY؟ބS@c ^WaJԽVM;_O/Nj|nMWg|J}㈴RI÷4jj2yR$\g#'t+/hw[4P,0H#gڰM? 6f|gQʾ'^ItMOPҤS'hӽ~+ &K(=]ϯK Kq2]htYh&0UV qwkiXnn8M9@*P\ n*1 O\~?ۧkRi4Uv#}FuIGˮ; -tT*FWH?PMCFS/Vɩh+lV{]A+1U/SյZh.R1&3]?>6"Xk1}Mi:'\^ݨ(5VO3OKhI.dbOrq|SN2ީx'Do d?C޼' M'-xD z۩2gi 2xMYlL>xUCz쾗.'/K9r([}o/3/eм;hL$q4tI,ku??>!sQi,knX?S #joQgxoJ'59'ڧ꿉>?zoD]<­2 qCv_Zw!“W]4=?pO T_Tww9?.oKlI=k> BdXVG'tφﮬ, 8mݑLnvH뎂=o_R|cN~ZتVbi`pDirWS׾*H}z8Wn\<8XS _gZxsO{q2]+?Fg6m8ynޤӿ> hM?KnZJqpXq8+ Tr4)УZ^|[NIG~~zψʐpFX< }1j t8cE4P0U_8<4p#F=|S2OSˢ]C<_FzxQI\/ڏt_G!RW\f6>}hQ>h\QGֱhQscGֽ%A?O[6JR+ټy<0~j5X𾝮|c_+@}:LH"Cx'<Ǻx6Í 7 {ClDFYN0O^E`Mþ+|+oR7JtfK0v1qmLW<9[xLO5K=Zk'z\\NC Y 7ß |c񶏡xW-[ rDfqGzKtߘzi__:7ZEsˢE3ȶlYFu/;Ԭ5u7早`%;o18% ץy|5oj6zƋli_EkwF,F$kj4P[x[Ə.uqy~HbʼyGo(i%kX |0ҼSki3yy{:HGQn~7 >Iu= 3\a+ox7RVor߈lඟOEah"囸Rh[n 3 iv)|4fi&`-6x-dבS3*|?? tՓ[5i@`"$Q tzGi"~My}+WYi%'e a$^]O⎛xNO}ė!&bW}|<j!7~N`jo -ؓGhp9A3 o,aiNJ5hrv6@;IU;Դcĩ3Al&i? !ݜjΗg[YmOs;د}V{kh$ϒG-6``1[$nǙt>4]v]D{g{.[ (`6Hruϱ߇P\Bc[ڤOx ֮l'+2FGy4uZdQ%fYSe,vIv|C(Kr@ڻvJt-vM98\rsc=ϥQ'KIs>kB:UZ!g$h07/>6}gc"heHo㉁bNz ^3ީ{KNl@ed@p޾WmI* ))9roc u~F?78ѭ.hh 1`9%:Q^kgk[*$ܒZG# `Ga)WǞՠkIG"Opy'x_iǏ:k37?ǎ$ҼKVzD6«2r~s%2ji;]N( .dget zn^ٴk )?;Z}ı/:VI EoGC~ ;c_b?֟?U{H>VB?AǬ?W^2q 3k_^G|>~ /|Kb] ~ /O}B꿻_uoXP]a)v<3>בjy&ceC> ?c_?n]#V>%Y+qi|CoGo,pZZ' 9ep\1q s?0/#/FĿ+qh'Fu/|KrVBAѿ⃳/o+CFoFu/_|+S~CwS?=<}AXn?nAc^Vy'u|=l%7M?h][.%X]ʞc ^ؘk?^Fg=VѴO?X<CjZ/P_ihߴ6y/b}ch~} ig5ybĽ+qzz1[Qտw%_o"گ,MO(;/7O+R~?C| O{K|A/|7n?ۭ #E_qۭ?6 e /+KHuoEO,_|o7̩?V!^Щ8z_|>~ +kZ/XV>%Vy'u|=uo eS5UeIw*Yg|/{#O\/5?/#J_ѼQjZfNu$8;wgB|@ލ6Сۀ"7 :GLW_UV*2E=ՊR=^(?^-r*0o}[X~5?%𦳥d#]K ;(!Y8<BS(vFr%MVס+xf4A C4@{#V2x¾)z0@xh%+%z"xGG<=Mx]4 %Vg#~5]WZ6-jιi<=tbOB=j[\4[b198$)K/MlY'1q{4xk޿eZ!hn",IJ|!apT*u m.njSgV4c>h>}>k_lv+VJO|u/Km{arF:6FvϭGuPӮOT#7 $M?UN0ɉҮ&eO5A=cKo+غBR>Tg=*?fr|-j/ړYuh2#Zo>;OUN@>qKb1|3;'BE9󟝧ڦG}!SCZRx_>LI}Tzkkyt~dqK6;R?.xn# }ߋ>6 S\@2L;ҲM;jSzK%ہn͇v=[Ҧ/:M)̝&;z7),R5*KEۗCzD ׉HGrnaCg7%_i-օi̥ld8_]Eo5tXY0KscD:VB{>Q'IJIhދա%T3tVTğ H jG (烹RxOgӥG𮗦 M!=è9(3pi;LmFo Lҕ(jmW?t/j~o2w;'oOJTe>io'cф(*֧ Kzm ԓjZ1dxlێpvؗ~|GuoY9} =y; /rxqiO O 5x<{!>*G Gzy4'/Ğ4iӴr@'?hS-? ۏ3_pLE2cU#dqޝ8Ŧ4CO ۏ3_Ǐlv4Qa^v5xr |]zVgc4Q(@8%N2|La[Yjjl$Bܐr={薝 oW?{ڏZ6_6޻l_^^kµ^n GO5#|CGt,xb4?|,wQ=%2pX?goz Z:.>yo-UW vTe=GEQ,e)QeXRk![ RϬ__UOoO߳\]O?L<3i3L ?ǷiCFa/?ܭ-? ⨳9儿?V?$:77ft_)G]% s*yoա-}mb?5L߇!Y⨳2w? ?bjTYX<~?_vqu?n2JT߭O_ ibaX#{~aTY2tZ:7~/QgsV?$z/7d֎?1˜W*Z2} l?5L!Y⨲Q_B_:~~1yf=1ƾ_XH*2掺SsI7(Yވ)ΝMnUyc㟍4??4-Sڄ-K# UYrzW7 TnӘ`x%7{u|um/B ?+q.|!e/]/|YJ[~s)*ʢ=ŁM*Dx4z [_: O)18hQQ:ukM,cYzǁ5%Rс =* +ᇁuo?)kI(a "0TZ[>U{Ǟ7Rg*Zse( st*{});\5"E+f9xzTtCpOMPC~0gw=ym{w-e ki-ބq|E&hu=N-.= Grq?U#ԏ۠҅ v~x5P#ީE'=:mxkAo7W?¯dh#o/k, _?~ x:Oo/t{)%g}wU#˓/N/>//j?c>E? 5׊46EViy7mDXdrǿ֭x>1ӄ!ixڇ݆!˟}i/?B⛝Eh/Bgxӗkʂ%̲? ckuoƿqcyE.S I%Ÿ[&ōHmB۫X&':Dʶ-5gW"O FdUY*V1m`aX]2]6Nsֽ [kotOw6m7Q9lc}|eAUg9_//>1֥ڞ6 W&:n?{2ӴuX3BAr2mH#tR #4eIn'k:2L?R* K0 &y^ㅾcǍJYU]Tۼ;M< `_`&g%,&k{s$O"*YܳU@$ǥ=>B? mDj d-ps͟^j6xރOt IOW/of x0 Fb7m^y>-VWQ_iFi5e* w.P( Fݽ%v+ 6=kn'>)oXx7EYOf(v8'sEZ]/tڇO[{KVϲDATyox+R< _ž|n$e 7^Ff&0FZY:kAVp=fߊ,gj2eln @)6ǏzI 築_1oƖ<#߀|_oQX:?.4Okq> uYaBr oӾxĶ*_~8_<`d&rw(:׫#߂>^^x_nsq,0˦Aqd(#_?}7Gn[8n,=H5v+;#A?hh~5/JI&Q/e&7_1 =+nߎ:lm5} AtC:$[$+Ewʪ䑀$Ҿ7C௉SO l2+c9njO񅎗gGI-+ulmF;j> m VGMl@RF=zXI[>yϦi o\ŬK(%uF&rdhT&22*:$t_ hSӬjPLs6$($H\wϲ<}T VZVol_*sx=i|%W[煴_B`>Y3N;*V<= OM=_@{F{9UG!I#?(՛il4],4V kX(E+Jdww!_?i^%4l|{=BhO|U9?RkN}r_+sɨxSu8͖.dget zn^ԥ%B N;Z3:R|IZִO EoKB~ ;c_b?֗?V{H>VCj?BYǤ?WwQg_*<1c_txE RĿb?ۮ (K.E oXڟPv__[y'?Y!S0/#/&#+uhԿ/z1[K ? eG_?hm[K(K/_yoЩ5 /uE|Kb]&Ac^Vy'u>uo e_׿hm[.%X]c ^κcf|C~ GĿ+qGE~ 3c_b?֟?VѿℲ/+GEuoF5/,_|o+S~CwU3OG=?OChsV>%Mt_?5?|K/$nߴ.⃲) ⃲7O+S~?CS4S*^3~!o|K'b?ֆAcSĿ/n?֟?VℲ/ihߴ6y/b}ch~} l?٩ /yE/ n?fAc_n?]GV_BYu) ԿℲ$bU,3>.ceE~ FĿ?/y1[7uտ4Oh6%y/b<7Jy*:ceU4FZOc7u2NTeh-):AAe"icy7٥S`M|+?YqJ:8=+(=dž4/xi!M KRrpdu?vns 2ZsJw;rM`0|"x9b2=zU x_h`5t[?!͜ i/[zg OΔj.z{XZu0NKk[<)7~ԂåS(?ڦ?|%7u6z+ j0T~͠)%ʹ}R2ڠ'NGZN6_axjtpTְ֔]EcwP?ګ:-<>1~W_F?'էi}@&/ojj%"X{+YC}O_:O?/GU7 XGm[_/U?4:N6Sx_ I/-?P/wߗ_Wy/u.Iuςx|ڂoSE NF$cspw~YOǤzI/>cZj'"5^|{Y}ِCC7<<1"`_c/Ô"y} -<=|cĖ6!bH\:X)ܮJZ!Vp^֮?/#tO1|@m5?E5'~*!lui+r]Hr#qk)?W G!_F` ZC8]~o#RSb[WZ3yKny`ys%ġw"YGkE_*~w^յOG'kIZCiaܨ"+MS՚n{coIJ((8}GB?տ˽ZOpWFJH !s][#_|ӯ־q]k_Wz/CEj'Oƶ3ǔ8)%jDngH|3oR[4ݮ2̿>8m/&}2NI9z_X՞5,Y5mſO9Gj fE';K)Q{]o^<|13C[yɝf\iێ|}Wix~a6yf,I,B03ls> MW'[qn:W2vaq[? ~ѧB}f)0ax`ߺ-Q x[հQKj&u~XmY*rB+s}~oWY\M>(k mVDu|#@M.[׭56~tE!eCnHR9\ƿB4%B;i0mO*F?|,>⨳,ebO,sR=%2pX?goz?-~sRFY\-?eϢU2s_,U]儿E&u?,,eIT}>Fn?ѻ?+S~?|/>u0s,{v ?to,ih!YΥEy,eb>!!ѿѿ&t_)K\ s*yoա-}mb?5L߇!Y⨲w_ b_TYuX<~?ov~p?n2JT߭O_ ibaڧU۴~!YQg'XK+GGF5?,_a/o+'=^OZ:7.r \[hw۹1lڧ5L!Y,̿E3\XUz/c/O*_7;L?g7E?+S~?|/s]L ?Ƿi/QgrV?$777kKH"ڟ7?5syoա-} je8 vC;~~*?/Q~![ TG,ekV}>Fibj5gf2YΦWjWn>/}Z:C瘕!|"xHA7-5 J>Fe sFNQ{R֖Ö>#gH?v@0cdaA"X zU?ٯ? F?$vOy63Ӄ־ag<ģ=@u#IM75J~>oү1Y/5ccisxs6_Oja?_fDaP>Øh+ 駵)ZuGU= ЫK=[io 3/4 j O5OatW| 㿇97f?ڗ;~|3O\]iNUP>^_&}x?o?/UOxxP _ݯ[h/5;W cZJXֿ֑|_̻ j*wU"{6_*O_-o?7e?y) ws(??۩|Ilh9?r!Ү:SF^wW]}2 _PZ ]?]wQ[M GV_ꁼ74SgG64o~G4 W_C(Ӽ*_iy[Z<{^q?b|-:7:_CN0o N֡?(_=Nm9}=kH+ӫ8FTxU?9]~z/i'X|W k_0~:߄/u|9,MF҉^.70Xzfl=LBѭ#oi)V@|+VF<;iB՘ U$%|Gm5agUH -|ќn*A994GMH$6gx;VA;9I0󏽌JGl~c,[@};Vrſ|X=R6dIL=VLrs8PIYKsZѢ3,h#:_A#\ִh ,#r839O?/^j0)j4PSO/W1k&]oO&ʷ&$BX&M4%B9iߌ?jPxJSe.YY׵{9lkU)>WuqR~i+Z֗sʾ!?/}Vihz/G5|KVCjA7пhmWG(;/_Y 84ٯl=?Uy/otxE RĿb?ۮ (;/ o"__XPv__y'?Y!S?0/#3#+uԿ/z1[K ?eG_hmWK(;/_yoFyz_>~ +IhbXXVy'u|=uog_׿hm[.Ivyg|/{8fx_jŏEc_?nt]#:V>%9+qj|Co(K/?rt_UcQ<bU<7%S4S_<1c^|D/X+q<~ڮℲ/SuV e/|oWzj4,ψz/-+Ou?/y1[7uտ_,whmW}O(K/_[r{<7S4_<1c^=oE SĽbO}Cj߻럴6 TE/{4*^3>"h?tO|K 1[CH~ -c_bͿۭO6 e /+KIuoEO,_|o7̩?V!^*'/_8+k:/XV>%Vy'u|=uo eS5V eIw*Yg|/{=ϙbU/#؞_GZfu$8|N3q_L z>::/ٚRfʎk0+*Ub.sGQ⛫Y]-);cYxZOZxHs4+[h>u3}} V"4Va^q⏆tjkm=lI& 1^9|1Ӽ jw[Xz_IK )N%w}4[N )h/um4?|'x{2 8֢Oį C׏̛m 8AŸF5r>"ܨ/(p?=*?Tѭ'=/؋IFevS]׌ۡ=eGG(V'BJOYO40 2Vv\dž)4~E~L4ڬ~Y[wܱ=Ye?io3Ɲ^ZL) |?|A:S /{ogO?2|>"vΦ׾$x>;i ׅ58aqxƯ-cǙ:a'PjA5s⨸aqk'u+~?G|{[\L1h|HR8sextMQ[PHHٲ3@ZtUl>09$Wſ`ZJUCEPB5G[,%;[D5}{N@6Y$(2 ,a`fܣxg;X{deO ޹j+_xKt)tHtXhUY, l<_:KV "3% ܏RxL#=+xfc]c?z,%t@WdMk-vu `uchzMǀl{mCU7΍Ǹ97$z4 /^Ji|0 Ano#WQ6|oӴIpo:xx3Tp^jC>=Qg__o#U/?=jgxsPFY]-esލLf\*2xo⨳,e)`TYtZ:/~ _TYǜ_+^kGEԿѿ_`2Z2g*_#]}*?/M|A&EX匿?تgcFibjdکZL۹+F?,E|c/ܭ? ?⨳9匿o+=^OZZ7.r \[hw۹JWko3~fbϨo? bj_TY匿?تgᘿ{k\=K_LU<3iw>weظ_v#{>D>XK+GHE?,_c/?ēEFkGH}S\<i>T?s|,U_"ּAEߏX<~W5.&q2Zyog/gL ?#bN!U<Ĭx}ܣֽW~ǁlr{诃{5VJѳnz2rd8'<ZuCq(9$sU$a\ J3ğsxJ(,%ʙcڛ( `rޕ|@3FF%
    pW%=!:nڦ5I;<EsI> vv0OE_RAN>z厩-1Qԛ$JYm[W ;7GokJ.l x*{N[񧎾h FIn@3\%h@2 S /"==]}S.>,|>6bcQy"Xޯ g^?UpK (7H~!燡$?8o,c"_?KW6٠zB[9_jMwǟσ4Yh˛85ry~ב\=5)+Qc|O㏈IOV?KDtܜ%'|u(iZ&tNg$sUB5^ mˍUn9/,G9*?Sϕ*I>7Un@Tl7ϕlPiC>_S?qtRSϕ9Q?RԵ!X#5ԾL1 3IF= ;ϕ9Q?/7W ? ?n7W ? ?n%lsT}O?OO ~'\Um|*uַ೻m3*0S P'\Um|*V{{x6Lv@TaYOOu/d+sϕ9T^n>n> %lsT}O?OO ~'\U>+@m4Y'ۏКjg(,{AuVtW v{ PiK~kgnjɥx\ ?РX&J4t?^A__MCŸ<'kԗ,nOMھdžc^X#ּ{&f XwIZִ] EoKC~ 9c_b?֟V{H>VCjjBYǤ?W,g}ljf5٩ /#ξhsV>%Icj_XXVy'u>o e_ Ⅎ??خO1q/u|igX/iy_??V>%1[7uh/zI1[K ? eG_?h][K(K/_yoЩ5 /uE|Kb]&Ac^Vy'u>o e_׿hm[.%X]c ^ιؘ_jŏEc_?Կ/y1[O `hAк#P_/7ʩ?W!^)Xz_>~ +'XV>%n?]?VBYv) ⃲7O+S~?C| O{KAѿ/|tZ:>O+ǜZ?h][F>O7Oڷ"P_/7yաn*y#/_?Ŀ5މ&~_iuh]XℲ?S5V eIvYg|/{<ba?^F_M+qKI~ /c_boK bh||oZ:G - erqخeO1?q >O4_<X]3Z~ /ߏbۮ (;/ o"گ,=O(;/7O+f?X!.W,?c ~HjŚ:ԓv'8JJw#\k )ZO3p~GLv3UQcj*Q[l]GnTeetm`(|Ws/(w-1YI~MG %1^_~Bx{zO4W-8cه'>+x%c9޲կu𮖖pj͹E)㣯k܃]}in6=I%]}B:R߿S_>" #A>OkRxƀ͠xz@<ζsKRN9?اx㤢Mc/0x=K@y%LN1}\Nއ5߁-X=\rxlCşt@.?sr < 2x~K 붧.dQO?8=*aԛ~dp_\Pz?(|_BЦFx M!SxWmDha&0l]o~j~1f++FjuB:z֏x:}+:=]!1 }s -?dJ7[0G Ն(2x3YKI!RO AѶ|W Fޤ֯dp* cxXʄy\0\G:&=ՌFR-m%ShK3ceZMKk#bG,Szu_?VO=?UVq#;Ty=qW畏~%6UϪ[E;Ug+Vhiw7V *܌:qN][Q̫հU\*:ouk7s둜ӓ1O=kta:(B5^j?W(((((nvHԛDwKp?'<_~*c2~!𭥖M<,Ȋ Lッ_.wU7anN,pcY*}q^}suo[[0uMv{K.xʪy 'c{_V RxtSmi"0G`Dchb,5}-G[oV{o gMimIp#8Y^+ aMx) KH [u )]*8~3,.Ś)eQ'KT 66񖓮Vd69/ 5$6FvmMMKkK (E *% I$Z}?m{Ǟ$}#VXw;G!y>jǃ,&9.tH41 E0j kkjqZ5 ҫyc.2vIhos>?hZKմi1=ꗚLbY[|/LW6hϦj o ̙3jſ2Xߍv=\xa-W̡,Ъ1l۹Nkؼ?6^<{><6Z̑ɧL̘eF329;_v𷇴f_k9..ڬYQvppNj :7wZf$RF_-wܫ{׈'5{|>o=i=PCO$"opF53~"׉|'DvwzettzqѲMNܦ 0A砧ũ>*5>]GR|Cw$ᐔ6k4=IK^YƾdKCψ-7_ִV}3ĺ/o0c4H( 17Wjw^Mk5YZ3c̅l7ppz:X>zw!_Кǀm?hŭZO6 "!ےW񯼴K| E|no͟ͷHWWphKUb&4t>g>xx -W~Yhc|CG,xbt?,sQ=%2pX?goz Z:.WLzlkoFW,U__"? OmO*?c,eIT}?s7q4}I]-eϜU2L/\ݤo|B *??_+KDG5,Oc/~HF?gu/_u5߫C[ϢLf\)? ?uEoc/Ixg,]O*>U>}?~qu?n2JT߬_ S+F?|,ѿ⨳,ebto|,gQ=%2pX?ghZ:/Υ.rU<isLk?f>E![ RϬ__UO7],=O_Lweؘov#w~aTY2sCO'9儿o+-^OZ:?.r \-?eS+kCTY2Q~ Y TE,eIU}?FiSF(%vYog/|ʿ0s,{v_ FOg ,eo"T/`DGK/̩VL}@e8 vS7~*?/LּC.EߏXK<}WzTF8-v:] 4?e'es&b{CMown!X -z5xOGuUۢhfy fk_FxguŷIs$^Tj{`뼧+è{I[;3%m[:ʄyW6wϓ=ƿGs%u {^]6|WwzN 6r|h7^ 6bm#iHlיfV]﨧o>"x$g|@e *8-wFgJ߭!ę-iRK՗k[>IwyiKHeb\f@:M\ਈSN cY =뮇f}5r'Q%^<|![ܤ+Oozf_BWei[46D ckiQƓ##* JsΨ5ZSUT*|u٫G 15OO Lv c3= :~kc-h[PN8uM}쟳զe92x?2i~/,\eUʇ2;ƾ fgVtԗk;fUpʔ[GH|a6i߆.Iq{}w~wz7|ck藖,Ȯ*3_Ku71?:ϑ׆x6/|8ZwJNB9:a $('ug=tL8̲kk7x. =\z ălG[|y3Ygh\aFjfd}+ocE2:lb?_;օMj,QEY?W-5L䒃&:}b:}b_V_?(=gZϴ?Qk>߱E:}b_,EwֿgZϴ?Q`8zq8A,S6NW#?+׿zouC~!m[XONx!^#?1t( +[+??(δXإ`#1Xॿ":}abO,xO =OSElxkwzD\_j ZmEO˹8Mzu~)ӭ[XآxNK7Tφ;?u HBsHzd?>ןhyIi>߱NxxWOX@WO7$WgZϬ?Qi>߱J'1Xॿ"BGC:SE{u~֟X+y$vSEtЦѡޭ >R$޽C:}ab`BG(Οm\WCx_5>/*P( U9?RkT"Pj5~׵۝fGKJo9@[j^P:vK^9k'E7͵֚=n DoGC> 9a_b?螣sڭ0]h/ΐ`H>xJH#ѣXBl*FO{%O+'ŘT:|[ktD/ot6z1[鎝WZV>ˡ:~Sڋ ԥC?u{V״Ou?!h?tOYE%Qg#ğ5·C?'ߏbۯ?࿂}C,t$|q}&]JAyv'8" Y< Emt-[RfM `3 Gh|;ǧZ>7bsI6A$IJ"xeX*fh$̟+ZhD{ʲIͥm_%1#'M}sῆh: ;C6r|ռw'?1~>5p[oHXpFJ0+qةJ쌻*t?7~h 0+ъv)`Ph`Q@  0(bF6#z*E4c5i+~ [F1>_˵|$ Y^"#ȯڳ5 '&hvPA}&Sb2wz峌X7&\"vͽO*~AZ5lc,EG0;W LۏG?g>z}qgizVVZĻR( ?BӻϴCɠ TG@,8u fQ c󪇁pf`:QV #.cN- [F֋_V LwC`K3!7ΝkڅwPa\f fI pGM'b2~:`O&1E210|ݷ \$) ^qg@O E2W2 3N]: r*<|@,h! P~̵Ì 33/P%p)= Gdx#3!Ŀ) zJ xL}7Wh$If0T,*gW d́8wgT32FS/`@32LT .еiCn?~g`:A  Tc0 V V O?a8_>2 Cx<ãOwAlSdԬ& 2L&Hz XP3P90@@1 *Ф҆>`&0[>νGHP_*6cW'0iZ$C T0p|!qyE(! (~ڹA3\]HCB<  W^z=ɳE*p,0@eb~8{OHA LjHLl ߀P@ t5=H 숀`R@=@6(e`Z=`È1oz1 TwV7I^ e,@Y` IHx2C ثceoT0C Л?}?@ P (Q`~\F Kʎ Xx"fׯz Һ ČaXFma!Y`?E*,-<\$3*'Ï̖ 3pp,ٓ(b!ᰴ1P׏rVfJO=Wh?~r;d&C Yn.Ϳ1@fpj`5eac T13}Dv@t8rXX!-bMDXaj0@On0m23`'ZES#;7XrǙ /<6Eex%|l$@u<&ZtO%O IS$Le`-9LG`:uó Tڕc`Xs~oA55dcb$H&~LU ~ ?X>3}. n BF6`nCfb3L:¾.! u >a~ï?@i` |.`7m:'')dG`??~CSݟf>lbxP[ HԆ3CW CA@"kcx./`G+h8eap5b3[o~b~)}=?헿"ŲN_0oo̼Sэ 6K8ˁyn@KB)E-%!&a 32p, _p߁<2@C,`]AZN=''P(Cb%ûW_K_8J:(_bx k` ?dge _|Bu8.#y, >ɐ]p=2 PGb8foƂC_9CWp ö:1|<6 KYvyp;0C 8QVQyeC}>c_0ٱa ޾v/9)b b%[a -x=2vA] bC^3pӘ,CZ~e>Cʼn vmax)xt|9d P=pDW6ɾ=al ggdVo0to,~B̈Ӳ?^}xpSo2T˾gT;/ "X`ͳnB>a0UcCݭ w2ۯ?aXg}^AZŬy bbE8i 4s lN?čkk|\Ч1ai3$;Cv PfHSmdܚe_ 5 E R++?<}o3GT1d \9 x0N H  ޿PlOMϢ]Kn>i`}n€ (f3qM=yTnj@;OSBgz 5F3Dz@E5СK2vJIENDB`fotoxx-20.08/images/texture.jpg000066400000000000000000000253341362435004500165320ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 125 242 0 C     C   q" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? t{ h<>}XjCSc?}'?±Gڽl>|?z>EaOl>|{z,(aO]k;<<9o7*2 r(5~y7IaauI=uH61Y@>]ݿM]_31K2 j5Κ6*\bلd,Ī'nC:Vo:Oͪ3r!m|q rVO/}m?}'?»|=s y9%D4#.P|0}f5[x7 ֵ]RSK5Kc4W 7dx˭Vl>|?M}?K<#Mowɩh鍥 :j>PjƳ_tE͕8 }]GJe4B?i6u}Myb`Ew8Mh,Gԛ]ySBBc?UdM1PF]jd,ڛ/Z zzߥW𭵯'0Dv<+rP=S[uuxX<gܝ(9Yn-Eg@e=Nx]S ,@GP3U Co PKw:j!3G*3CW/;Ccie G>%Bz-{j"vgwj`8>ס,hsw5s0aX 8\ƾ{Vxnu(hnjή ?08;5ՀŬeknqkʱOՓOəsK+Om+ӳ<;i>Q_?m(pOo1E\I}pOo1G;2x[,O'W;2x[?>Qf>ߌ_Ou]K䵑R:d29XxW<;hViZKuG!wBH R康hgoxBZt;1o5FSTvY|QxI&]_I;{+x1QW8981_mø|C'(ů|J6GZ7bۻƛ\Ow?d?ø|C'm>, "cG[ Kx FS{lx$'.M4>dikiopzd\v=~n  'bw"1fIY#*sIϬhbI=< טiJNMSP%[^hW(eQr$[r@znz29ohHV{i5k ȧˊC3@nQT4-nĺ-X³# Qe`QPx{EּU<E%#(z+|kV&t^ ui %}F+fZF\LK&? xcM3_]٪]Y< EuJ?ѿUhO*PjExKfS+$H8$O@Wꟴt[N=>ۻuΘ$4 Z3w󷒸i.ݣo8?=O67\dWIi5 Gw3\Ckx9"]w0)%pBoWi-WSp&o5im:isB6=qqE'ʍ dj Y~|IԼkuoC:]ouTI} '+8ѧG>5Ե]i3>xTOi팚nGimO{6(mI<"^`}gKWumT J[9fD!BTF3'4=MT;;xY]Bm8SQI~jro;+|ף??^_6'Z1 na+6.AS3⭿ōNݦkڍ\\()*Hϰ)wky3/"<9|M񎯠DB. h>U0KWܬ>< ^#ڴwziI|["2JXKncFeC ~* HBʃSZ?n^ $OM^j#I%iRh%S+D·5lUDɭW/4 hS%̑NYYVuYu#8J+ L\ ӽ? m_g-aImG}'NjZWGRK^+'-FQ|H|!eixg2]Gp[ı%+"Y庯/<}xSG-1l[y9>Vpkc"} ~"I+a| E_֜=[m5ȝ5wg>Z ݟjE9[J~`wW+9mSCk-igI. q$_δiQP]*(l%U_TѢW|-~>vj4h?U;? 5Gϝ +;QgoƨF_Ty[ѢW|-~>vj4h?U;? 5Gϝ +;QgoƨF_Ty[ѢW|-~>vj4h?U;? 5Gϝ +;QgoƨF_Ty[Ѣ.Ҩ#r@1ίPEPEPEPEPWS[i : 2A UteC$2:oJnJt۱L^1»8u{OvXg h>4Iɔ}?0i"++ZgR֖$ψ|5v\/ Q8,%xȭ[V÷jvziֺIn<2p5k"(pHgnG7s4Iɔ /.ON"gw>?J/|n`G<7mɪbSzah&̠mzWWrZYEgxں\%yDUn"qZkn𵮱@hHٸ N}*3eIbH~3SM2@Z(I>PkQw>O(JݏR`E'&QOL~%~* $:ܚ:!nԘey>:@ yyw0rHO:wMdr {;a!N#m:)WTʂ'iݷ=[>!KKul/ -B@{VCrf7N}kuNk- L,漻m`\Kl4|i`(U+T"x[%Ӄ-DYʹ$3G:\/U:c=C${u @@Lçz>!KPoƚgl_2޼{_aҼU(|meo&D-V4Iɕ7nG3Cof\Ifnji)Gc `ao[JYi585X_QhxZ)G0+6ϤmUtBkkfVUedi$kYȫ0(((( ד֕>O {h5Ŵs[_H4IN3ҽyH20A+ou^!ď UDfCR " #N,7M>O]8ۥ")O>)Ρ[FVOǔpX yCrivgO)^0̷Å$1Qp>ڥ\'?lj^;v3}y~_vmCך^n_:O=GKhv0FLzohoS}[ws> mmoDHm漳ݍS]MrUѕJe5i}.+&vyt1%$·Z]<}Ԛ.8A5 uuc[ƙ:^,(Y] F\i庒Oxpasse4Y=(pQ)p;v_V1_տ'rӵ ٞ3&!([C7»ii5_Zxhc r06 1LzohoS}[wb 600 1000 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?lI83DpҫnMEfִ[k=POtPWnHcAgreRiS|&h1e'HZƐyQî1X>X!bA _HwdO,;C̞֕*R㦇=Dgc,E`-Y; * [=w)n2.p ^1Ѩ%JtݺIoPF-Qv}V[cq#Wi6 UYX%}3W+/z҃1+摌+򣁟wwZ|9XKN*]BTbf$<1O`:aѹVI ULQ>$}.>,z1Ͻurm=}ƣXxg'@{.M/~ZKyŠY2~zBͻIv$*2X׎٠"9 0G,dr{>JKvg9ٜюzG$K1æ<0i:MBF+-ۈOX*9o|PjRͽB8ޥ[m8In8G"ķ$ۅi068=6glkAi;W!ːz8ϩSSy#+1t`|*+Դ+vO$aG| `dtߵn)l(6 c~ӭ [f& `1$c#zfy.HQPN<ʫ}$-0q=zus׋)ΝƿjmE]P ϩ氫QA];#sz!PCzN1?^3xB$3"dx6o݃d+B;h tÌ3q g>:`v_VκO:' 9sVcjuࢳ۽?J^AO1pO,;"wbUC9t^0p\k4F256o2LAXj1jFNrqtZk &&H Mֹ%q:隆p~9M ed O ;H8H#ҩRjvns;G4vGyܜ+ub@z~լDV ^=1]m3v:uԿdEX,"h2r}+|S5r;^\[!%LBX`YZL2æ!=x[ȝ@_PcS$5G=8_z.l[@ABya~wjZR~Q#|lm[*2-g$GޫxVk"y,pV1{כy5N;_F+5Z9Myo0L9%~fKusgk9n$3 98RܢW.Ygޣں?Ö]3v`X}yߚ)Qz"-E-Z-4G\4p=@`8qZy ˹bZ?_Ju-[ñUK03[vu3@{P+:٭NƧ\ Oz۽lZeƗ$@~J)hrq8Hs2yobTpzzcQ> r_KH[h# p20G^;tv<%$.G$zW7kmkn>pq;$*8)L4zcGoA+H g'Ҋ 뫻Đ©$_@N`GEJr7J(d;֝@07FiL һp>GJO"Hd}1#Ƶ.L.@ߕ>{:׳^*`_C,LaIry^Rħú+YwIu..2o|$0cx#{7<=7+X%e9zK%R!i,X+SIsKQ;#Ⱆ]u5.Wu-oPP1g-^gGG(h?zʐCǥvEĹ]'smӼ$ֽ\ZgW$HSF\q1Xº,2'$˜ʰ etOSi9UHTIS\܇c}z~ b_ʤ5 x= r=iZy5B%3OGAO{}RٔK:c,q#ֲ㶾Lq,9i }^XMj@*ZƐ+0Px|M߳_Gkē^4Y2 B.oodɈc=8:E ~nTΊ4cuK KzqX'gIVsٳˆXGL:Ik&:j IbPF9*r}ZK+ug rX#qN9n(Kh @ 0`y~?ϭhMx-0edirO2d^a3\) IQϡ(ERkR!BA=q_ìHt;t^daH<+yl]g$wMzKxt|IPӒ+Io=ljZbk#Kx7*N/ƤG9w#喯crI([WM=L5XlF_CYfP9Y:qP)5ZլѬ7[RpT8>֠IϿM(|p3Mo X-A8SӐW=Ji֥i}$:dq"|ǻx g4~ĭ Mnr&w'm(UuB8'x#i,>7ZDp=(ko&=PsLt2xRٔpK`x(Z}}{Y+g_|XyK/=kˬu8đ/ <'OyxjV|%|! A'՛#$9*'ۥAO_ƺ]5H^8:rpVg6mƼqNiV2NFXw#5 21+wK bm[C4h5L Tƅe@&ỏ<ӧkv@1Z-^2Mj*G¹Ɲ"FzӢʓ 鄔6>Rqs'W![q^+DI.,.6xϧEXj*2nǾ3ZΕ7)I!!wan4 |/zaPb* =ti\< :J7d.crXdqWoE].7xhR[oJ_'JfY ~=G[j{J9&Eg5%iZ*A ƈ(w1:2>ϐ}'SӛdEcS'h_ ,zV/4.dQ[$p~"[As&ROT;o`yo-`+p c]2U= ezd՚d<~+ބi"z&St.j7WO="| }) +5GֹYʢ %N*I''s*>[o~;ӏso ub%ē*Iw8>V6_ܡFz׫EE5Rm#ƔueӦ̽uk,) o||>{}qNu iBrUpcij] .!{[TD@ϩ>}gЭ<2mʁ x޸{}vMwTeT:UvHxV^s5][Ar0w\wP%[ӭkTk5'vfwHU+FD4c|Nx7߽ea='ON.Jv睸$ҳ5/DZ&['P^&oApzwESWŒb[rNx*zb)i4u?f#75m\gbSy=\J#nO rc=;#Z­GX9sӟJ.E_[Js bb8bc-"5t24oXcgxѻo;F: ?xxFE%\ n#ʛIqyhpYtqWV4ZB FυVp1Clo&JlI^N|~s?zke3QǀdUہ>ֲӚ;B49cg<3k&(^BҜx^N}rK{)T|6 }{j PѴ;]\rǿr?J5MV˳7" ;(St=qW]Q} [7`O¹4zӉ%Np9`cy8ڧc&z$t0`/#=΃nc2:Rq}{}^-FȊ5-Oz$>;dCus,@ ;)&h FW`>Rtaӊ)toeom5,w17b;T4nD'09 y/) gc<ֵΏnZyR -U')"= Cw^4ݭcnFM}4S5䅾U˜8 ǟ㊹a;[(И['籫?RFGna4#$788>jOu(jVqK[3D5[=Ђuwkm,Τ)#FFE3$",c*zF!]aZt8(advO jQ͙ !?">:Ҋx ^fZ$*r:vmUsPInw>ny93\׎h3ƪ60zn'R#5G1GuyrŴ](I&x3:b;>³ {جFb\st$S$ I+3 Wkv ?ZcUcӔ-@ZKKoЊ|R7ѱkjɬ_ J͛ ?J.> %}Q+v]nG~:,UJn?3ֺ k;X)HfF{mYs\^P6Nyk4QԞnJܲ5t;k3neX}G#[-Tq?ڗBHg0.iTI>qj^jvVEHЉֶ4̑\0'>tb𭭜DIJTGZȟ0TUԃCNF~#KwU~Mu)b]++WV!ԎWHo'mw@iTQT>k* L& Vx?ֵ28]k4kKrZcT!ZPcXJTFUa*2~%Md@y$vCi[co#\#>^#ay׊xR`s^}_\bmR'Iu 5Y]UU8?:lNg8vR;H%O6yOZƺJ#̺-U+}skV wIiq#(0\cztCY˾'v31t7sSи'kU*g;-&rb]W5 =*Ѫ)9x+/QY?w0 63W| sNQT8+:8)oJ<=jr^Z̈́5݌YنuK\m)b Gc55hn#5,^V <?˓UV]ү翸>-qF ezvQSk[Xt:XepJ*~7=zu\BRd@O^kɼ!|3G,xYI`c Nݫ|&>(8*-hySLxVc8tY|K4:P$gR(vkT\۪I0ŷ~FOlgX )"L͖*dbs~ʴ0z|;i7:T0۔0@*:=O~+KVU+e>03jimb,lxU^;*Huק)IYDvGx$t'>".ߠYhnEx6f$cOzlo-na1'ɟ%w{ltեqFjZj kn$(¡IϠ$WG%^^[^絵Dfܻy`#1w+ 3iֲ`%sTGBxbrHy5iA %$FqOHǨNr+V5EH۱A־aMtPXrp7g=Bnt֎R퍐qWߊҥXtmZuDcl}Zr)_HЇ $k~cd%x 5j`9TeH xGoj9wK" 3+ Kidw9ggZҧ7;yM912>ӭsVQ܍6yUWP`~9v;sT4Wd{Ux#p'1~es^=TbzUũZ]x98+7RHӔBU>\OҦI=- eގ$Y-q>\|^3cWx7s@Ƞ:׽|4ǁyh(鞍*Q4¾lwIm'=?RVt)ZɍO/dsҡKo\#=?jφ<0FI/#ۨs(RL?Fofv 7`[]Jј;= >DS#Ң N r4Q_ u@-d^Q=>HG>:iJ#V&ecV6A85ۛgj %ddp+@3._3#٣\\4=s4A=c~̖A'wqXgI|tX]F3O5R 40%i,n$FCO yaԌQ@<z󥿎!\mcMT41 Ӟ=낽R=kkd.끟θsÉ-arI"5xKmtDԒ8Ib!\=)Ȝ&i3<ض4;Kc_XG$Mm \\wr^xkPƨѡp Qָ4#5sWc1uu)Cj#atVL?,M:5-Y8 ^GNJɴGDϕjKrzg$x\h\g?"zz ז$vȵ tdHr y݃@86`iYCX yA5xIo|DdOs[H̹ > Uf\iELj}3t\ӞKPWh?8Wld!zg*}M찦qUI#rK(fh?Ŀ yNu_|6Ȫ.8GN5z؟U bqdG& V;Z8.[9 iukg ̭$r`o<ة)4mA!c9b\CuvRŁ8 WSH.؆Ԕd {$hl\0\5+$9Ey߅-C!Uf={bRŗi/ș80.H֝YX G#GR^֑O>I7 Qz5-:&XFZ ,1q U|u$j0#A<<P1Bn n<əLOpĪ2PF:p+|]3IM]th%M¾BN {r==Z@]M NQAYBkFҏ+Ny “PC378&|d ƺeȮYFtE#ƎNuzxK+ ֺXErn\3ZƏJrZX:֦ivxdp 8'5jen ~ny[f}kMhCy5]|S}$jsUAifȞf6Q=l)BQzj`!Wȭ{`j *Zg[e vھvZ3 r>/ WNrWV 6{6Ą'9=\[E(ݲ~b9& ۲`I[wѫm8*Ev;Uٙdz]Fivd j6 }O14}\}zWKmAy蔈XqּKqqG]5efǷ]m>5=3c}bgq]Eȸl1dw$:{jeMݩ\I(>rqv5YQIJN۵DCfN]N4A7B c7ףQ^FbZj)\ |d-Ny}*ժ<1]$L pF >rPȢ *Ԑ[kRK 4g=@;c&[lF{󞇚uA-0ѫr$e?Z m>eA*5\yv&upFWIǨ|EIݜves{l4Αf8G`(ǧ>h7mo#,]`7!*bM+RK& i3Ȉ톅\zs^KgʤF';k.Pvg N;\Mci1KUJ̪@8sWo Ԭ=ʴx tp\Cqkg1XZ_&kBO;9Ǹ55?z%QY/l Kfi0.XG t8m>/A+[EChזJWV@ޱ,bB!HU5h:+9ePF`KW=mi")4L6Qj?%y@*$+-t)9+"jR:!H*@gNxIZ׬o=B&E$H;9W[1H@H^(&8}j֌JX fVNR;pyG\F+hv}GKsm6?cۧ\mOv@\o$Rќ-RtɎXX03G7`׾N0?CB[)VUy+`~^֮}$7qyڛT>V"w԰) tm_9oQGos^[}'.#oV뚼b[y9#?Z#), >L)N@p0G^=9]Yؒh}=7OQ:GSvyZVL[CPtۂX|mJiuU籑ɼgNGN%4RRڭ,{pI㞵)JWtF(-N[/41vʃm-ěαFml*q~ SIٶvn˽Tra$G`TIf8^#9Ss}i{-5x\Bpq=sMh.FaRZ6n3܁8Fcլ.dzQY92|Ϛ&w$q,q11JH|f^FWʞZ[$f\I@;hXtр'$d 4I FkV[?olHOA_q9d7x[%sṺ c(LXRArj4dI6{>IT|zkgd(>S-?y<*\\y)< '^[x,X:\+ (2J䱯C$wqF6ՙ(_*$YK,hQI+ju2N%Yϯ>lGf|)ڞ1SwJu}#hJy13}q/ " #`982NDyoh>VE8e8q8ø.9tx~Usi*EfP#:ϼ𵎭.G#k\%9TTPHtCʷ2yrGaɮgTt[m.6# Ii$aUP`pGFZUi-Sتu:ү$(|'?5zqҚg[ zm~Ơ^M$:D3^zENJFHŐݹtQl!Jn $*y ,8NUE۳[,~~Dn~b|̫<#2=~bG?[ӬQ$3; dtCEum"oB@R-:[BI(|`Nӎ?=Xݾ%wm#8*& +4pӂp:瞸Wis])! O5I,o,nd\ Xuy7RZB=mCd!rp=?|AO]E_5!d1B7qak-ΊRj:Sn/c{2.8;ʴey~8Y둏{XsYj6 Ia2H^ojp$;?x\?}EpNqG]*^EqocͥM)tݔ˴bxHu9ѮJYrI<+7Z>-(e'a%O~k廷t6PU)NFj4;EҮ'ִJY^@=+ΧoBmX.0 ǭY`-Y&IeG_t> ,&>V1b2sW沨⬶4 \GhnնmPW?VnOeȳIvB 3{JGl4M" s~5-TTQ( ᛞV5OWs+ ,@PbxQB浫^q$ Mκ+=YԢdQ&sSjď: k7R89>K!٘(mY7aʓW[mK¶zj L#Yc;JQ#T|>˙#v3<:Fa75hcX A p;8Ϲ#(ʵdr:Mmk3ZwF'qךj 4c>eLa8#ֺx}umn4͢TH{} ~e=rtW?tY-U<8M圉=||9kSY<#|Y9u<÷U|߾~E$sɫv^2,#${Ҽ)6#ф5O qadr[ qߨV{ɇ !YW <YZ ymK8Kl~q>bwla6 v{5Ii߻6\qg.nukv2^ !{-|)Jv$6r>rK9ƥ57Hm9sf5hPRQpG|ߥg&>6Jdzu/H.3:Ob܎GbG[m>EdTp W V'.:;k7O@z#>;Lԯ4{V.RMzu9G4ZhSsK$oFǏqҊܷ쬠1ohd@T|?w:qz+RwLTCq rO&zgڬJInبd#'^l  tA iF2*p1LLl|nCǗb.Xn9O`kys!Ozϋ.Y,fWguh;7nwmW4w3k)npX_zھ^Ǎ-Jkx3+9*09=09=$X"tI}?:.-ܳ=щ$`>i02$~y#x!,f$7v3:dUGnY2 Opp\4YນV%c8=~kMK{[]#`cy>ONo]!Y ГE,SyYreʧ<@u~R6bdI82I+_ddc?{#=;\pc 38 NxlA\Tmt;E8Y.ڝ:* [ v1S^x-##@:PW8#?O^kLW#HN?*MaD^~ݐ򧿲;wo\B0F2~koJ< q Ϊ*Vro@xyr89r?:1:Ym `c=SFtfv~օφ9`?Es<5*:;?4Vu)LlNOJUp2>3ް].]bǥ}d +حOxz#-VVYӭyN_Uml1ZE;`~rwRM+4䜞khZqPSr9W,C֨r,^;jr%HR?,bbr?Pxn0Ǵ9ÉNuV:%`g\3'W'!!b;Uܨ6L&gKX4fFa'nִ5HYq ڴqRE5$d=];0lG=ٟ+GIMgdpN3kҮE8YJ济35P:k;^]?L4,*kɾ;< ^ ZG3Gj Rԯ.arDYks÷IRq\ρ&PTGº3c؜>=I9 {#9:;K:&yIfT`~l*WN{`a!NY~n>$v\@A<>Xwoqt+C {{Uk 9~WRDhn5Vw1u] c?c&el;5$C+~"\mH,qJ7 ML<a[HYE :=X4:JLUAsmvbJqkPq+!6)KK8Qa8C|ZGunB&QXߡ֘ casnKFr)GMMe^$њ%7˴' n+FtʼᵏTDb"Vrw\{~Ihy܆6XyCM~1Nko a#szi^R[ v9$dQ˓jŠh]r:&#Q:NZo<[K(Ǔk|q,}k;L'w0{u4˦2 ~=?J^jv6iHmuDf%³\+VY+Yʜ>XĖD,ye QǥmK>MӨq@)Πe)0ށcP{S*e2*1Vq+[TaӾ? ^?(~3`=kX+-lr\AUda1Fp GVZ?gʵM*e HTYo ڛsZFߕiYFu*LR!mTsr@WRvG4|H־]#/6 aGּ h#mi R@8F׭oxeӴ_ΐ),ssn+E/Dl}*x5S,4&1R4Z8̈́B;z dgCuk~yR.nq*ο?ΰ-kdAXr\5xN.S4n1 7nT=~ڴsЅtK>Yl9u]Ce}?MQeې g=6Ieڳ CAz3=208zò[[gV q>PS6){;.Ofo12 8!r}r=; Xw,tAwylw3ӵ7EOIbf\CkRVF/FFO8*:^1ŒY>G{8c#s H^88=yU謍0ټidpN9ZB'#+×Rv_Nyb [i&8 wzZ"YQ E z^xkLɭ% qpTD~0GHeh_3cz^ cuXM vğƾg0)qG5bo"G4CA-UEWymJB;9Z;KSsj%F&[Vf~] _i5q;[t3!rxoÒǗJ\i)h.&)\H9oh3P&]"1cMNTۼ=8oޝ fEgkk ٤) Sj炙)(P0ݵx mf5b#` ŸѴOʌïPI56'#RWv/He^UObcI~W9 OoWH7,rկv:hj+B;YyOןC̃I'WRms]YH#8;OL0=}@Qt/UIrWvUɵAcA9UIH*GPhxY[MmmԼ#lrLV8(hV$g%)\Y 0sW[c\5n Ñ?v:fbL`qS(ԥ+a3VE#S!%'?GDB[ye W zs]dd}ݚN璹Mt#5_|4|ec|P1q^^_Nj %U\ d2q8\BH43Y[1+NLOA-?Gax\oNJt0#gc/|1%ѕрVd|/vO:%=~U珬ŧZ(,H[̽"Nv泌,ւss{g/. Wcnbzs֥[io3 A$g5z6FgAX+Ghj2\"2?wΌ4zN'u{Ix>9Z0==xn@@enpGUZ y <,G5>Uk!rN+K 8W@(0xV.L7p!*qFJfg,5+O4P3/'|K6m6Nbs[˦mimԁ4[K߀&TF>R!#ÝֲE,>'IsT=:Ssҹj,qGöv7{*oS'8-yOu':3d JtK8k/p2x^+uq*+OeXJ 9#pO9^kFqp-AOX.uʷZ֕E8q= MĮF*/5dY1柛y~QUY,dzW.-g R!F7pz]Pw9NS>I[o0!w.B +ɒ8éFHc׌o^jwũX!i72lO#-^g [Z6plR +c+H[R5`n]F28Q哝 v>ح NZz'e==yUœ ÜcZUY:Lj'>rc}{Io}unI)akͷ%ҟ0gg=њL}*{X-o^rIQrq}5 Nr:ug]ɇHS^žnފtGoɸcZ+GBלU0Ij\,Ldq|'i'99EՊl:)kݕ# r},b1E潎[9=QEq3sEhnpM_k3@Mr8+ PQ--K€7sOZеU‘Fh3Wm-q,Sc7=JԱ_B+ФE疕;˃ȩ ݐ(CbjCo2&H4k4Q]+sVIvIIAVR8#u/^}F@$}h#*nnAdVmD$(g/ͬ`zZ٣7F!(;0 8D,}Ҩ-ԢCEq-QR)9p~hxc}G_j(jj̚5̆h~\#$>Q]M{Pݗtl-V2'V烮Q#V+sGV8)/-X؈¼ga=mV"."~8?9;M<<- ' NF?Jmx;9I܏CӠERr-vT$ZW3)gY\2 1<(dugIlYԂ[· [Z>6qڰ&lQ]"Λ,hhE#4gޠqQEqfotoxx-20.08/images/timeline-report.jpg000066400000000000000000003433731362435004500201570ustar00rootroot00000000000000JFIF$ExifMM*bj(1ri%gnome-screenshot0230+Ơ0100Fotoxx:zonal-flatten|resize| Fotoxx:paint|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 601 762 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?8JLjֹ{߈5SʌT.?R3VO5i0 1۞>GU3}OK-bQMnȇQ֏5"T.~F O{麢׏zޣj6$zeŰ@7\^<$@Os»p؅Z癏"T/9èC6## QL:2l%Nƫ}2Hj}ZY`8|9u)8 @ p:9OZxMc#ynjN ƺnpXfC*DJu?!^yyW{[⹑3'Pu9ֺSyQ7ҟOQt}CP/kKꚮ<nk;BC-9jO?z:eo+)fzJJW qi|>Sh q5ӴN-2="86eV`^C0Nx>Wuc7v:ui=fUK[\95~S}/+WV<Oχ!{2i\"Q[񶯫>WHV o0O#o?{ҩQb"E|2Zu߉2iugpO5`J+xoTst$EKo-ȣQu?_^O4-wXA8by>[03j.WU<;6 6:6,E5A|gr/Qu?Kȯ|ysczx/ &%e$e|NT'5ukqK4W7,90'4&K>T_OQȯu?#7tuX7HnT6׺2A}WU, kesmH&xUSϵEEj|7$dWߵ^hmZi, bKB97^9(Qu?Gڢ"=xm;XORN2[i^4 cXAš^IAkIcզD$r:3NĠ%ED,iSu ʗ> 5MF[,@/$FbǒO5 \]Fk庼--o_9n9=(rvik~l.,H jڢ"WkU7ynӈVv ,+~4Pkjiɥ-jwhȖ2 3,1(]l}mȣQu?\7|O%jqߦV'IJ9`Sz>G0XT_OQȬz9ڢ"EE`}ޏ7S}/+w`}/(T_OV}ocQu?Gڢ">G} j|>SzO{:EEj|)~G0XT_OQȬz9ڢ"EE`}ޏ7S}/+nsQu?Gڢ"{_7S}/+}ގ`ȣQu?XmsEEj|o'۽cT_OQȬz9ڢ"EE`}ޏ7S}/+}ގ`G,')~bH|Go mc5ޫJ!f"q:GSR{ k$XV\{W4=[Rkgۚ(TlT׵CQbҸC㧄xBF8 :3毅V7KՇ*&[kxcB%{"ܖ><.W3s Cx! # AǦM`} ?R'I1)/ֲUpKJrmzu ls$TN%F?ghTW9Bo#I , 23[uU660HIMs g9Gǝv8v19IZiOXvEv?j_ӿZzF5/-o=G}λ=-V/_ ia,sչ>J5W) s^ƥA;ǨgSOMo=G}#1?c-Dy/ܱ97.s477e|?ޛ}͡Ie}s˘YN%9=s^Φ?!kTPF_MewoE=yh}Ý/ɨ>ʬY *q뮦Ɯu4`r'ex|^z5Oicq\ƹɥ}wᯃOsOԓ,3|Y9^s BNQ$(rk,.4Vn,K+GgO3>ѯ/,tkk _Ȁ?ҳm~*g_ZzkS?}(}qoW%-NC\lK@T7?ex|>r>o#unGa1"G\G ֣(,??Q7}WsXȣG#G >]a1"r>pn(,??Q7}WsXȣG#G >]a1"r>pn(,??Q7}WsXȣG#G >]a1"r>pn(,??Q7}WsXȣG#G >]a1"r>pn(,??Q7}WsXȣG#G >]a1"r>pn(,??Q7}}(,??QG۫,??QXȣo#ug^^XȤ6c(}ßߍڠtyb E|SKSq7ky 9`<==+ϋi%6Hr+[Yׁ#JY>3 ׀k4' u|kVZ?\\h_jcx|Ʊ7P?ZEw8K@x8 51ԭ>#~>!bNm$60 w p7_9GvYGc-0Frs_)WPN+m3ڌUd_ 6Ձ6r:}Ky_,:m8Bd83:=ӑHqz`|I- cfCn\8:$x'zgMw[a;-O\>yo4ECoq4,l87rU2nx=S~VzG~iZ455bbV61={GmK[ k;KV]:)w\n4_uyy, %β(lblB*Z:nњLsu%cR?3ە0$iU >V4u6O"V w^kI>Ӡ; ;!=7hڵyV ipHBJ'ɌԌGqk_nuht+E죴`'ܶ["o2&_C=4Q f@<2wj5M4ꖰS#rn^H/v˦-\mbrd[s/3zӎW@߈|fM橨k6,Z9C f9o$Ӓm\އxOVmSKw"}ŝbybNjk:ݳm!m!\䓴zR76뛻=3TԤ*xgJ_VOc3Ds>>&si}AgdW?ջzB89 ?߉$3O:a VA ʡfij)=#ҾPTսy uݧjMaEd,Fr~f< 6F*Kt犟*[_ kmI{zW2ϸ$R~@&iV|)mͣ[*7Q4yLnN6M3V# rbmx$"ۊJ]ҮyÖԣT˩ixX y4tfqYnh0WډF[q# ax+㛛&{{?}MT.0'`^"bhB^w÷naq6y (F 0@#Ÿ AF\63,P[<>-?/&ZE\𾛣vYh0"'Q1Fiߴ殷rxJ{M}Yq7_=Dž4weJ䵴o3ϖ=cZ|&ޞ/ ( !ծ76cE,DSCO?m|rS\[7lZm"Sgi#VeVZ59;ʂcO&:~}tb15R4[1Qֽ.gm9 vHc1̻YsV&7 .m8ɼ6ՕFUnR didRĭGU=.I>MnܐYSF vxEzrg:P^Mb[JD ;YTH=uT"QHaEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPџW K49[jg$WYGSS.IR5'h==q_9Ox=XٗZ-N:VFំܲChZHcSHPp+kt쎚?~Peُ]RM_gXe'"o|GƟmbV@#RD^ǁǽ}3on_JV wn+:߀?B$o.t塖Ί~wR3[[dXT/<1ZH'ŗm${`(X猿>猿5\%25ql.ąbRU=Iq;;y'gpUQ@$+}!4}!5"9':I2AEIoa\?/?Ə/?ƼsoQGޣ?_ƋcA2ChA2Ck?4m?uh\?/?Ə/?ƼsoQGޣ?_ƋcA2ChA2Ck?4m?uh\?/?Ə/?Ƽ_,|!k+cDڎFR2}-ī+MZKPC.k)5Yr[,8(\/?Ə/?ƼMg`m=wm c;dacvŖ\&YO6~'EqIF j,=xx7os猿>猿Gz,=xx7os猿>猿G'ޣ?_ƋcA2ChA2Ck?4m?uh\?/?Ə/?ƼsoQY o;{x=̰Aq_#z gڕsݾ猿>猿ew|aioyڳyYoV,"jZixK% ue{pz6 vvA2ChA2Ck?'-0jY,u1p7`h?_h?_xޣ?_ƏG kE׷sLr&PqvP5~,</F"]]RIa"L|r6ZPHdгP3zוUԜynSr, Ρ't웾/X=s^>cK{wfHis}!' m]?_]?C3YoO,l-]rMa]@ T33%w)~^|E=C7Xok?jKm*ck:@ǹUr'p;7|vou‰%I5e[Ouy}NtgV9I mV%\CÏGe VohOi4O%̪QpX(?xF0Xg8oxQxzֵ66RyZg4O rT|4$q1_5+?CgږKiy*Op")6*@v,} xYc`̀O҄i ֊;_>o]6I~Ӧ%29b1Sb{O8.!$Ag[EPEPEP|AgmuBakIeF1;FOrk*YYt]Wַ׿aUI.t褎1J~Q" 7J|V=A"%uԮdb!Q #2)ko-.<wi%K,1γΑ(y$UXy1pR_'cΩmXi~$# [lC)`1[ּx[uG{7^ ::ܖpii_?šH߂$쀃>!knVyݥI@A(U/cֶO[+[ ti7G*449޹-Lj[AOn-/ZD17 s;i'`4h+)ws:l)PCo.+c\ͫZGŤ%͙.[F!(NAěv~F_֧+|:CMk_zĮ. &}3Y@HAڸQKI_k}[L{캤LF4jxSd[QR,|;p:êO祲|̪hMr֛{+TJ+Ƽ-ZQ8#8mJ5ϽrHmrqGo ke;[y]\qNºgGѶC U3Iֵ 9Gj'IB:׻Wc׵g'a-#; `F9R'zjqW>Q%ٹ^MXYG߶F>.r gbmSk]!K(dIw(xķ'Zլ5t{I㴍YVib@UbiHU{gd~ _2]GPP]bDN5@ӥȓm|׀yy Z\Lڏu u8H]m4`9(m?1z<{45FI/um'10β2QPTָ8Fմç&cuFn'h^_S^sT/|1ty֢Qj H#;q8z;ֻgl%6ӵ{[{5ɸIJ#D[;qTޭ]BROӹ{h ԴÇTMӮi$"s=F+w}BPLH̋w,[*# `bYE{$9tfg(m$X\qҹ7:=EɪaEi ,E  oKg` GQ^AqQm-;0M4lXYfQoAkou_ JӮud/ +O$M"4Aa_\ +A=C~ԯZ^mgĐ슭) GwJą`HҎw:$:g9qhﵽ{Q-.,ab Cn ^MEP Z=:=8-dKnk)QȭAb=uǿ}>@O}R-6D2< aе=j_s1X,b)hdxݏJCY6=&Wl5yXC$ViB@Kv |Nছ?\p1;bu?z? κ=Xí:jCndfDaeAKmv%;ÞEx!YᎧ%qYeۮtW?0Q8@$c}27Ƣ+)p-|-GN׍^%K8^;)`<'N5M{F#ZXn.R9_jTl$shh]cL_i:Un4r_j)r-kX | .iLC ⺨>8K'dBHtUJ_~1\ tҶ_7iV=RmS ]?xgɞ %V)R w6~?|WW..-ygsӚ6~v; +O-:k-2kZޓkPInNVhTHV6)*\烓^Wfً"y$G5.(:Ѭ,as#,oF:'<; 5? K旎,d- `Q -m9q_-g\۽o/t" ѓ]s!xJdJ)~5Z-4kz@_ 61,9ݻT!=s>4mks6ӆȞ  b}dpʲ y|%mQ5k㤶_G56A p,J#b>lv\iыOHϙ3 t]2yjSm4kz\Ui"-I{xrV0dV$gk>x=[VVY͛얲M$DJ|:meNy `Qo x:4s{`uX\8Aʮ䞕ᏄAe+iz4֜IiMa#*[<+Y0>UH_N%72#l!٘#WY_V\dCwv@I7:͜ `^jGx:WDM֠vYLdl$V|֮xnW>d֒DX䳍'87nO&uΧMV.%y5#nQij[#2C"~I6($Si_(okEjڵoZ+g$smj"Ž䟺>3k|AoX'5{[o+ZK(3'L<Ih_KGiW2 mmmthhX, V׈|9m#ݪj=ՠxL >`Ķ6$!_xwUм/_<0*ppX>VmK(4xD/2(Ffb1zV>_]-u=Fk}&_jwܴWV#BI#@ ,6qtq{YzԓPXԨ\I 84;WZhտ=3\<>R,>Oqa<P񿗀qN+J𿂴7DҢMrdVdcsו¢|;y5֥Gy< la sF@ۜ׶idӭm<cT2AUt/Ȟ?~%wJ[xrGDZG cqgpA95'!h9{g[r7V;$ Ã^s~w72UŚ1]AvKJg f|Oh_-"8mhFiI(ahiG[\oK ?+-JS~=vH/$crk}tB.Ӗ̛0!p%q=q+K>2, yvY!1n2!/m3y[b\l<ج[SZ,smZ;IrSřvd1R]i"=Ś t0 8ZZG`;úm]vb9mUfg(V0FrYg;kMZC 啞yQC|D[yp9jOv5fx_J:fo>%~R0cS0*Fwި5>ŷVe#=v $Rn2SOYiqVW6o4%NL',3zם5K,+j:%ԋዩRx'H\LeU޸B~lt8lx ޷O qYtBVȏ[K{U:t moQI,X,3Ky@x\ KeC;x_i}Ιr#3"6y³M!t]HOfڸ@P.6~j7OhV?ZJ;+oPk|M|WuHXYKw"ܟ8K%~S9o m!|?qa577@i,)uby#f8gGzeO=4#r~>a\boH҇?v@[+`Ŧ8ȖMF6~gps(迮՜I|-n|Uj֨^U=>H 3y`\u'nuvmG`<-8'5 9eiKX =2 t|F^eOm'ÅP~Uk IψֺU{T o%x4 ģtW|(|=iζt(..RT49PD%r H'K_}̫|0Z{m峘!$0=|x`Z}V;a}bDr`82>u`{ L,-%U[ՖTr>^5P$jZд{[qh./M#FtY|$ەgrӿ_[ʶ>$uk;-45KLsia]Wfls}JM#@f]]KhclqW_ Ie%֗$.ai18),p =uA<hauuenzy K!yُNB9 x#Ut{ѴJLɹ]D%w0;H&mw૿_sO|٣ز|nWSes%z-Ʊq%݃^M(CE ` gfT4 iwq0TȊs'v01No>yrizuܩke M%^\[r kO>'<agir `h/"Eb620;za=֟}AiXa4|VF…eR:פ|8=n2%ܫbK19$䚭ŪMs[I}祖Pts-so߁^6Ma}CNўԭDϗuf'#S7\|M+.a54I-!81 v>+Q;>, ]g|G:?Wœ̻ml乐8 0Xc/\Cc0gY4k^k \YZ^Hq= X:d#H'{68=K]G%<\GjyVV 6@j#.:]F^OKdvvGB?t[V#c-Zz,z%Qj#7 y`HCᯈu?2.o RSv g9|clZhLCcHE 02oZgx];^dđ-!,(|PgU*.:mmcBOEO'beVpʤ*z6ஷwڬ )Me$Sz:#ׄKo 4Zuͪ_Z_JsMEo⏇}a]^Eu5a#-)JblbWU\>k}Y԰4W$[!\@22G9oAF%݄&wشLig* 7'w/_,^4֦ʲ-mpVixI4LQgrpW9o v:j3u_}}<9j3_iY.ۘ/%y] *0c+?az? B[GT ݞ84gK] T}2KHe[fh8ʹZᯆZt+ej'Q}CPQIŁ s?O:I$ѯat?K~'|,`awxG_D*+[6{GhWb2̭qY4~ᩯ4+ t/>; 1j-{;J1% T 2|3~  xa֯nfOf cC⍭@ZX<5O{=-b+pP4T“;/3iͿL|ݻg^_M[mF_&K[el#T7~2prk hl6By~~ٝ{9B  KNg(@mm;m4Os'ڌ!MnG*3)['s7bYI' ]$ s=q?'e4  J^Cu(%eޟv<R ЃG1}*bT5|cYƼa-YcZ~ L#VHrpt,ZO:wgk{s1~k}8QB=iIK|5odXTVhUx?/NJ0 {|?:?C]w_h:}wo:ATpq[_k|%"^"C;gy?ȷId&Wg;' E1m/A<+jHE4WFpkӕ ,=&ybj{񋱻xSf鼟Ulady? 2[1d=IG{"kH4]_?~l?O*?ýBM3\Oipcu8 5*s75j7 ?#Rµ:O#N}9kol8}[GMG赯8rty=EBw+)v;`|VhirLO79=Ƨso6>pt>+fg|;z>5(0-# c?)# 7mfS`G9Ѭ翊XhYﴜȁ[R"[Ry'܌zaGJUrJ2էmAE;?C^xpϼg~ǿX۾XTZ6s.Sk4ͭ[ʟesF1g#+mVt9Imv\-kjwZWůgKssK*GEj,OLf+M9%kfhP?#g`׍X.kir `.#bR\OHG`&sx(P5fCuώ~2 WOVh[2)gjoK<)hzk]}men?_.js:.&wvmY@#=} L4;1ۢt#>^KvmBp@yz C U _qX۲Y\vڻ>5ӣX'$gl }3Þ'ڵ6Qɵ"+Os_zĸ^MZ0 kb"mʼnԚjˈ8-קŭvq(ա/J 3|nˋ)7ؐͅg8 H&O˻Hi/~oMr4;ĭYw:dWKkBqzEsN3;_Zk|Uǰzv}lͦm,@#>`|j^*ıⱾ.HzzK"E8j N:uT%l,;z}ĿռKɲ{Qon#ѥƖ5 wP~No+iJ)G2WRPĊ2o=|_1ğO&?& zm׀Mml?/ 5MPmi9~ %UsOAi``zTFGY}W ?J?}? rt3>?1#j|GwsS"/71lj$_M4x??&[V 5qeB_4CN[kBj%oR `-0~QqZ,6܌"󿽊|eQ1/]gsT/PB)A-t+]o&Ef…a(A'軣^;:)W`2}&z-ťK=R*A!~^#vMsG-#!*Hھ&𽅭:{M=6ZJ269n9b,9kl6 p֢eqh{ f?,}JxՃnKb#-¿|[ld3e{6;KeRTՐ0K{w:}Iu| V0?O8/NIZ>>|vF5tTGUFsauG^p˝'ǚnuk9mM~jٟVY73d}/U-BzO?cYoǂ̓r9+߁? z)K]iOk;c3W#4Yu]}1}cS*!hz!>xsSӵM.խ2Fzן ܛ;ҍ&6)_.~?_.o^.V|3ǁxi/4ne."񯑿moZ&W>"*:Hϒ6>{]UNjQdtm$ԵK؏ ?ehk1}+?#+[|$󔏽z/J񎻨j ka \ȇ.H ?~x^OO;}0T[=8SIEjv UҰtP90hly}Z!GMG赯hVqlZ8]%S~p>+31<Zgm.ζ0ՆJבIԕ?z$*J 2H <>Kxq$AE\ҭҦR$HozVq,zYq cǭs:w||BJ d!{>w*u8@>m*}KxCFvS8_SK hE.`|z~U完Z .D6Tz'z<k_xG(?Y,Tʧ^ol%wt"oup}Hši-3[*#ONOy mk-.Gi q=~-J{Noy!o o"eϫ6?.C*mcq֧z딿Gh2|qGLWu_Q3iYiYǓ3珥hOh! p:umKqs9Ikf1wI- Q5̷fD[Y,NJ?(+|Whz>I1'=yVm Nt~n&hr cYI w 6 |^ ksԼii ;.ъoL'KQk}4dYp$r=RKesneg8!OGtse?f/7?yO#۬1ǒ|O7Me&5rO1IlDRq/ʤz naA{h$W Z0\Sr=Kְ.x1zRd7 ;űelTdx}2y>Z6aW6QA񯢿o_h ಐ·B^:H<3Fua,adkC8 &OýjOG18o/=bHKa'vu@lo c㿭y}[LV4[鬭!g8{tɮ72o,A¯ڔo9*N88,/vrKtI绩G ˁMtNd8+9J+#wB[+{KmA1\ Fq|J|-.99BxVx?BLR+:k.\c;\NJ 'i/]]]ܝC 1F01ȯ g ?u5Q/Ѯ؆ a^!{ߥy$x7X ߒ>q^!MQ|{߈fӮ&!F;{.?l{_AuyX A< RӤfQddS†WIY~b#Ӂ &c[%mxTH-y7ÿ4h3?D^k0Oֿ/1 xUѮ,#>ڟ>0zݲ-ݽCl}k0y1 FzKJRnw0h>KE|mX7Ўd_tTysV3☭ #uU*+HcSsg5=u`~gs =YVAE?l>%?,Hx5K쫤]" NFz ʴ)Wn(?__ gWMYj".wdSz;>/Bnm. Tp R]ydicalv? SC|8ֲYFM4@5Fwd;)wg76wng +M8g eAlcNts0䕜bEH\,iȯTGz: zW??WtPKqbG|eAxǚv}=7MhX.~n::b%q^K7PK#Mis9Ǚ?fm8:#  s9K֕y\{:6'ї!r3|@t[FYtX!<0ﴃ\Q:xR\Mi.mO0>ENy&q t"Z?BD$ 6`w,\2=]oO$_2_[ͫAT HL(N}ҵhԫZNwD6]GlOj[:s]0RVp YS>?ūWR._NF#Dnc@?EzJ̈gQe[w2j/Tޭ #@2k^[jz}ƒE3tbx'#8?Ξ+5OQv}y?-{H]|^ ];o\ס/98FqV{OB5kIVIgjNi?cV檒Mc"D~r8\iy'j޽ sI53]"`uܧ>24Ӭ49aGmIorq\Wߍ$335+Nݦpa0Oz*0nHNSJ,I]XVr\DFRA^7ž4ѾXh))մi%B ) G9s$kzw*&X.\.H qٽx; IԮvM2Pg%MsilMlzo < oK,.4 PWy={Ś]=ͅf<1LpF\޺ Gh7躛,1NNs^!|31!X,6|"#(fh@gQK4M.>b^_ū˿ jKc;-$o9 Uv8z6Uǝ[Mo)06yݟN+c> Լ;<# QzRS1>'x³xϐm˷C_L,H5ï XQLz.JA\\;ong#_OmJq2?ׇU/u_+=`Y( iw(las 5/mlZf[>1o-V=mI*Ou.Z<_-76>,5MR:ir#iy01AdD<ᯉa%4bK¤k @$=^t=A.E{4nާ3GӐO5:wχz6Lִ ym-Jpsں+_xS^-}h)2kTc1Ϸ$5&%{ધ)I| @dX5CVvost-&;R1d~?SGHIw(ŗ'pqMDΫ^Xͯާ {-Ȍ9Wf2͎1Т 57IMnzφxz"Xyb<-&XhWR]2SMM4K4F'h*  m ڻ_5okW~%mMDqŨK[}`Pw1d'=juvyo=GVM0S"d%AUq]d>W sjPrm+{3ltKR>jp;DD '`֭"S|Ll@'9+ׯ5S|EgޝܰYىk"Q׭Y>i=BOk9r8r\/97kS5ө/Y[ZI-юGtrq~$^k[+ WDF{(prķ  u> w_[8}o ~P* r95ZxŶ6%̡I`%@A {uW>"SJ\wkҊZsWi0j٭>x@ s޹8[w7{ 1@ 3ɭ/o亰Ԣ |p2xl@%:k kCAqZ|yrz}+ȡ 'k mj=j80Hܤvd-Y5RT3 'WV1.K{d`UÖ9Qz(zؿo|7#9k+V&[\I&j3ϖ,韵^-e?wM${ǁ/>͡ʒq'$⺋mEOW~g;@L yi%gHq(M5Gj+wUU-|c3jύ#H G<Fv~ &'fI\á?5OZ\\+A("Pvy{Wѿ>*kW7cKszfZf*6 ׿5 T]nQOqL)! y_~".MH$ :0 .kw&}r7 ~3Ҽ:֝u#vAv_S+0]޾e x/Sғ@ԛV ]H1 9#%vv [SvvJ+Mϼ|*Kim:Xghdհ^!|5^_:q yI! NT>~IҮ4vQҘy!ukIxW%FxDZ8n4'֢)Iث=S8grn9vZwt{m>i# lך_PJc_)?$dctLpiǗG(wRO |iZÑo,҉<( zu ]6<{>FtCB$w=3@Ţ9Yr3>_m4s+(1N F%3)ҸE}y+m f@ilMF:ҽul8 eG'?wv*ŧˋxaQYPmc+9N=%}QaG/|>C$[0>^ {W{b,,YZ c ӓ^5otsp]g ׀xǚ犥]sU%L\`ڽvv/e ,g?Z_zMeh"Q^]R1ukT2Y q׺^ 0g|)Լ<={xf;Eʲ[\mLkWp2695bQGϛh6m*7`ھM?Mwy k;Syk3n29Wj_X.,ZA466>̅ 3J#!ۮx^T˭?Z~9zJܸDc}O B&yV*:{u=?C^/A8uuC_++<ӄө^##R|P|fnO6HidȎM,bkSZnZNc`;Gk?źό~xKVfUYˑ\Nx^\侗c{F~#MHKn 2HVF |җ&WXq^/Aз<%Q%5dGMڦԡ 51B||Z(ۯTɨs2\N|~}|݆}w 7M_7?lNְ k-dxsV%Guф!Cuo>"Q{/_GuMjxCY"ۉbCP/CAtf? ng)#v8lvC~hZmz^7]vBH]/i4d!]ٛz^VGFMThvZl:^tFYnlEҜƻ9!jNj.c\ѡ'-m(猕$' ~$ <'jG;Lu>o㹽^{T?mq8M>W9O{ԭmVDudP2s־>np2+9oxJՑW>:fP;dyl%}zξy}|Ss/RFcW?¹hS\l4]azQ|7Ƭg&HG ?_aM&;KB[ǝ$53Y~+98ظ^2ş%ْI(%pcy.9ߵ~w~ӳr噲rN{x&`mM`S5?I}^Wm~pcIie2ࣱd> ;z汫kKi,v)dV#{/o?o{?YkZCKe}ky ag.\+AN隼LLXBVIrXx_Ե=^K#m=je6s/ KGJH-rB0FG==ʭdI",Y=fA$%EͦiPM3Ix%3M)3_>ex? i:dipĭs5YI O9q:XK>Xo"^s6E8?>E lmo;~ss3E <~S|VYWp&Rs!=J_ Go;;0h%d}YI>a߂m7vvA?}px\Z^? *惱>}ω|MG?L{s)Qmip'i$ZׂW>yiV? t:7SF^>x4"ѕqi\MNK}Mc3̽K Z98OR=+53c z93_ HUcHGu#?VrJ[_*~J~~j wk) q^k2k~O Lnxb6G9E)P{5̇fυ=9Q;N2y>Ք)F5G83=*v\-녏 _=Ҽ_7uI$hG'x5&>hBG50jWj ?e?D.,Vؐ asτ$Zri=[l:~5 ?M }?ӡJ&r>3~4rOR+[ t5P~Tާh?пh_ƻQCdbv[k; Yl,|w޶ 'gU(,yӵ~ ϣ?пkh+h/d~^߳_ŽolkYC QNУaç$g>~(YkV7ezfR?пh_Ƴ[۾iҔ.X?^֖SMg H3^kx=Jg$œsUh_ƏA4/X.XzQuJדw.7e?EпaoQ*Ye0<=? *_ `xr_Q?R)vf !.m4f3|y ĶQPMo$IЕ#r̤3^uu'- QllףORќ0'EצwiT@.q4{[~ޙ#ˡ4-DūGqFԷF־=cV $v%*pie5'vdzt?a躯?{e YY5GoũG:nfʤmb! >z筼1╜hګ࢛7M @?=O}}#;i˭ZAgu+>m%m1?LXb xZUyUx~]1붽gTG;`j _?ks"}W5+o($9KI 58ށ\ƸqXlݷާ*8'sI({[}ϡs ?5_HX^]s:܎_k|%"^"0~!GǛtWMhmѕ=Wl▧ZȾƾ~Xsc$ 1}بW\Wg|@B."u\hmxQ=kkOJ+ D]5W~߆5ω|;X>˧\s[ɱ#ڿ5?e[ F5co\DY9#;~}gzxh$y!TquloD$~F.~2Gc*ռEs.пwQHzot,g*HΕlc >8捝Јmg9cryѻLzDd9c P*״";4sa8]ٷ{RMr} ivN1КQ"-3\滪[jiaeq$%dI[ v?{U+ Ayte/X%P:}v{h".A'ޮOvn&i{³{$4˛t(]FHϥK}c+PR[ tkg{rcVgOWln-. 3.ە`Ϲ.'mwU4ӎp_Zvw]$p"OJXuě :[K[TevFYI?Z{Cnmg fmed#8ݜ^n+.bW˅t ǥGx:]CBXycjIi#`0 OJ4x{Ķ&帵6C _꤂9X:|Y6[`n^Ao'([?Z-J;\5.dh2Smtح|Csv{u16sZ"Qai$J3[\w\*! sֆSZum+$jp`&̭m\v}v1Xd$}:ѥCo[ԣY-[6y“ZtGIb}K"#"; Ս±ilXDƥǩgn$,eVe\qGjO@JtGp6w:wimo2 'we[m"Lf_51f8 ϯho$HcB=;mty4U?4ަo`^i4\,]`~i>.y5}jh󨸬\̪֗~mm <<ʧqعQU?6622y\,\(*GE22y\,\(*GE22y\,\(*GE22y\,\(*GE4Q抧{..0U?6ͧpsATĦͥpRmޗ'\'f-aX?KsvBH'W洹qO-SpP:E Z. ((@QEj|aD?ڰK _>&5Kaayj|`Ԫ'mjV,(ފEx2Q%F|Ϧ1<=xu>dk'w_ ^5+SZ76k:@N E=^0Wb^˵vMקЭXc@Q^^'.՝YSP\nJWo$w+A ȩ׬0קSEM'cE}i_b"ZP>LœgߌglrWrdey>VBgBC$d΂L7Z[Ro0QmF>d_i uͯ>+?$q( ,`$ r}9fYj~+ԣӼ3ē  9zԛP674Ef :u_?][-{Xulzҥ%&Tbwg¯=F71̑Aq3yQFLa vghKω Ѽy^8 uZUyn>v^kȼ#1{&M귏vL̸1J"60J~:Vak*Ii'ĬHN$15kW4ŧxƯ㫟WW mH kKce]ٿs_eoxv׳kԼPjOw}l?F6ZtW+Wg~i> eɶ9%p:XgID~)x ^,AD$ٱ/%jnz_?/Vz|]ɾ0l,뱃(~]͖O6qEM ˻B@%mNMlsLPv֗l叕rm,2;\=2Q^\ZY !er!-Yq9+CnlǶw& oc1T8cϵu3~ϳ[xSOX;9.I_@t+>KHv<;yJc2Y=}+l_xW)k3`[aـm+m i멷_=KmJN$Cpms 4`!2Gt| >k DW3=Tx#{fc\io'EpXS\wgyNd ־𥵧|/n8m/u?T:o YX$$RcͪOmb?+{iI40;ʘ_N~ש$/1uYd "{Oo\xk4[O{a^7q3ϨG4{yqjvG*.RkX|ke\XXM^<iy .O=+ʼ {:ow+k j3MD4%{/o28G>k|y-%duD0ʲ\^ {-{Oh区ۯIq0Xăairn=ȥ'dWG %džM yVwO5#;;Wvo^gcv@_5ŶP\Z/cRz>Se:kٿ,?)YyJG|o_mIs4 :}$8$M4zĖkgxc|Gt`;;N\Dxf-oY=NiӑIu#FUHu`@PX#JCKoR[YYNO> KV~_'ϵ(=WUu>C_ Ev׈Y,qO_坽k?ҿ7~xYѿh6+u[Zޭt-bܶv>Đ@y?l?֭%'ψ^8#IVᛍwTsaQǚNVo6^'!{A'Ɋ+"oxġ64y(W gi Vƥ.Ğ%} t=+5: #UEٳ\j0iKr.Z_ĻrI<5)$m}[sºĚGlDw61rK˼o8EG*.'NmmHrbr7*{. O&Ɏ-3ұ~kl)kosmjmrC)v`rr>OY1JwLkʉmYGhkyhv/'l;"ֻWe&^]"HgQ @Xo0c}Qx[I ~WrΣyaBEnꟲνCxݼcZޮo`#yHu.Um5)_~'xKOiqٴXPgjm~Rɵ[[ЮzdRn3żz{?4^Zi:\\}"]4^Jr܍r0x[ #GԤ!W.,ż3,1$e)(|WO!.A֦mad)mɸKo=+}૯񖝥x+kL2Gexon(]-X onI+c#T\Hxڍ'Gx l'cԅ.WVg1X|㑎 \Gyg2Iblھ\[BX?Vk[|hʢ\Zz汫Dl={j#*Q/nQTnZgNtUwoFsakVp_-Es`RT] xA_k5|R{Mv)Fw ݌JMg'm%4*ee\2WCH"V} ٓ#0>F)E2sчG_ֿ]O5Wqr4&Y, ,@'cqN--\B\3g9fkG73?3i_yk'N޷hڔ/ܗᯈ#Z::׺̉㙑6.=|SV@7O/XfPH2!6kvO6{ >Xu4~:]Y Q`&@`' =OI֢y->7Ih23eRGyȭU~F2=Fxn N-Mm3FŶ L9zz#T ғO&[Y-!4X1b d9b@}tC$WZ>}jFK]ImÙg _=0*H/Nj=]7t,zirjql"L.lHlsU>.EZ͟mlķHR_7(|jzft;ź5miCoB6;]SY)*s=u=)k"]A>z\/|>~T$R$g$Xqe<2g=΁8|[--+búp6Ų+<3c2-;)YUP8${=xSׂnu^YuKYg(PОH0yuV־fOj7mCZWAϵw]6w:%Օw$/!bTA5/mJN2]I7ik$l0Llzgzq_[{㏆&2Qv:;!`;gnq]S܈샽y?]ah i\y 2naS,GLdz^5>&ծs5:^&!9vR,RFA)ˮ CzE$k{BѴLJCGƦ:&umAPqnɍM^OK_x⶟{#IwvV\$s4,cU '-+J?|-ÓX?ȭncX̄G%y tSEyguOtD*ѭ-l#hnHm̼ez-93^W<gky5ԞeSYnbS$ʸs_@ok PSӭmnkx?d rĚK⿓~hc3|Aeyh}mImM*-RQE[ifQۇq%]yP^s_APк̍8:Oa>)Eo=֡a% fhwGFD%cƾĶ9kwyΟmuPZ&^<gO&76z]Vf8,fgMd3WH$Hde!dӎ\R{ ny3Z5}SMJD[m6!"C,v '^sW[ֵ_\Yu=/g6r\^m>(x/A>]*;QK^Ws@tU+>jd o_N+9$e @Pis-Q@Q@Q@Q@<)x@ѵN(d%x(C eS^Go\#jomިp mHxPz}&J`>|7Q[K[+,lRS*̛/ p}k**T"bF)k3^ºޯ="ܽ"PDdnQTߊ^DŽ_jWwVZrX W#=FHCBۺgk&CХW6r60gkؾ|#ϨOt$^G%E 9x XΔqseRъX ]JҊg+^2NL^oj zͼ6Z&x49l[D6sKϺj׀uK^T6:|%C Ʀ[ nz _Ƈozci]BKy`ƌgꋁ0jޥ!SsS᷹``mF<Ӛ>waO dۈ`y$cSeQ#OS\|a~Z_lk[fNHXj{_gn=:g+j) }N,x69Pv J=Pվ9ixkXU/k!eRrHU{- ݞ]>:>0;Qv@)(FWy2gu[|F,,8ncYaRV5'U\;Fkj7In"R;VyդԞh͓b2l(d iv3j(³{;:qnjH!;v5VdGWQ>jmQ x$|eOzeyGxK$^oijF|bss8Z#cE 0:S,(2c>%Q+5M RPżjEa=ȼuq7;[n5;tʍETc+<)_ƣ]xK{-fs[W0ʑҾz=?x?ǖECG6&=ctKNHU=rO-Lr握{,PY$c#fvRrH==9=?x|{}q-ǟG}^Nk;%KS Bw8DBx!^U{׶^:~v^k%MKHbPj[-ߠ=gQEKJti>XHX? d|> ɼ$LN[2$txB.lm^+wx- 4QN:¯ Mi7:~$%#uuQE<6`;vLv7P rHh")M֡stdb;*|/^#!hzo5ѵ"5;rTXI"na޽g>4+v/Bd=d`>g>ciײ%( Qυ_JNS }.y^En[WaJݠ WhGҳeju>%Q@:E:AZ^QEQE$b_}!KFLGj͏ݏzAxcDյ)i\*w[ˏ?c@XneCr͌޼Z\/j;/%v%8?#/LѾ ؉,&*K9';q]e~{Swk776vKG1l_n|.}wWʠӳy6S)kb:5(G~OF̳_kxJo^({_Z7]kL"wm>c*N+> x-4uΘ4IԢ>["YB:zfvq C@?]jyr.>6)\8XmUQhO Dk2­rMsO'C_,/KMTT[Hф_)I&?.:Zg(^׷zCMYz\X͜Rkf<ᙹ'{ּMr /&[ii=Ůng +>4]iW>džG:Q4r0 ,3'n먒}Z bm6wa"ͮ~3x=%uGVH3Jbe @&==Eo|4wZ\Zf\Z^ܕKo;E0P ar=&_>0K {gYm4Ǚ.YKulH^2|3e&o3[Ae-Ne`GRyR~!dōGĿ[B[oEpbne$el`$NJKOy<3 4Yſz" dt_Q_u_DEcYou8txI. *-WF$ڷ"KkcGqw>>N𗇵c?.Cm-g#pG%FzsYW~ P44i5En-!n T.Ckm|Cmw+mzH kl2?9KK~¾.+}vgWqp@mb ½2z [PwIEi~c>LE"7\3W'ZA3Y- 2`^イ0oKŵ׼hZ9Q N9M{97maN.!MmwK@rnؒ.p;h [}2(EBLQV~G5oltOWx8ωxb}KO,|?4W֦J…ܼs3^o|n-dկӏbK Aqǔ#N@d)iL,Cx^Om?W>`*8YM; k^ۥ$ 8THKk];6ñxCI'6|vq*`mcО:B4QE<Ś4-594Fi'O".vJ +j>+I#ñAyR&&w_5z>1HL+Mt"ZH92qX_ )/jzcD gt43o N >RO_\JQN(:S5 X5K_C.0'" p~LW _ejMk /%a-t8Dy uՅrj:]qng6Mr0T-gᖳmcS{1q)+F HARo@_~x?4n`[UQlُqN>j$lHn6"E||=/+öZ&ch#1f9$$IZΟ^Yq5WM=\V3U'BZZ]x9Nխt46*V&+4l8+~<]?S@,ZĊ]Z,FO"x'}~> :EIj֥A7"`g$yA<[b:YJً;K3IkݛtQE>~#R^[6F sטpWQnxx[].=R;Y ߓ whv.Չcmyg|e{ይOң/k4c.Imt[\{WJgMiu;I巽$+FWsČ,:#m\09ZTLjCP}+1D~v,B "'ݫW\ ޭ]Bh^ ි+y ,$g`7Yx[:_c3Z̛Y6ʠ zFEk^Iwm7޿}gujJmD<ƛfFOq]T%v\[eAq/huQ\Wשbڋ{d0E,,9F WR2Fs^ŏ V5 n/ K)Ya ̍EWLRA<~_J@QEQEP3c5?OzI3 |]93{gᧄصgѽYTn2C6o.^EPmJ "?geF )]?=Z}@xgV^U[pu /.e- 6VYɩ J|9PL&gʝLn+JNy.>X۹l!dlD|1gWUC|72oF-ơdvaflH%7|%Z,ct FOy1eRGnZ<6ZZH[m8\2):LkǢ˻/^ϭ\j6z r,by "*!p$sb?.Ӵ}3Juo/J[ `h9|e`r2W=+n:xX:Umf}6-e-{ %v!dv1$qY/Tj7V^mGegs[N,47H]HTٵ.jm-[!g?, ⴶ~$_-ίdG;ɹFX:¼cw f~wicj7iBi$D @#t^~ςkNci6-m%;JX8C9_+kd&.u+t3iiR]< \ /.G,XN> 6#ew "xʾO`9뿋+šMݤ,۾hZm8ڻc (y=tBo#=ޑhZ}ޓqeFO)I|X@3?cM_^$5ݶ59#|[wU$ȑxZj}zG n㌳;c@Yϰ֍eVȸ#G<dgFOqI5󮅯_i;/.~ѧ\xrSE((icZmO[m@ՀelAo2o5X{KNW[kBbr1Sɮ}Ż^{w>?`lgHi"FmsrHЫzwFᦹ֢H *:b=7Ob"o6 =W'S²j~u-2HOK+L"CB ^@#jۻBZMt [ZM-GrLqYUcp^~4 -,Ft/ ${"Qm_-?}槪/=0 Zcqm[:)$tzJwWBboMvmk-g]D8 Pgm"=cX5yI4ksYݝ&x1!uYd O҆sYGSsk6-JlDyJMLwWyU|Oi.x \f˩}X1{"L)\ } }G4zܷ]dQ/_kv,Pi 2$  gU{$M Zv]K}p1y(O@;׉'X/|k5k[Fhrh6Ƭ$j%l.ː3>7WsxfkǚMEjM흻jZVsqn`T6.xRv!FL?~_GKs14!gfoƺ><] Qu]U \ڽeՊR;[+<2.'8}BZG]{QL]|1s:}ֺEĐgeM[*{F>2k4ņII-i"w.2I GXyV{-#D`n4$;nbnp8}#0\icwtC6n#(#)n/t:`||/sI{Y\fl/goH4lP=rzW~TԭtJ&i[LvȒHbѐ2#B ho{-^lOEDUM,)^G"RtJ[%&W)SF?烙+r.8e9i$dC%U,τݛo\sKcGN)Ɯ Z(;ol쥶I]丅P_Zxj: ]?Tc\$=?שBړ=?FU׈5gcڼFqi@ nDbUJU38:z~ܯP˧jLyu='bһO Ӏ5F&tEmV'u=g~%^Kjg~ygq C_&Sc#ndn_ByM^ȌUIӊpgG;#%x煤'8v6]_캈@#/ |UukK{9`B ~VzïOxuHy7Paq+ѩ7V38g1/!<g?ۇR-嵅*m$B.9Q]JY%(XӌbvӜ%fnEx'J?oJE5F1jr֒'4˝6ݭvERCIBEydDWZ6Əo^k4[QvU/ c{px'6^g}mxVKbw1B+&ھ_gn>eӼYuiswsm wygeE_3$ᘅU'"վ5ꚿZYvkgo0pp7aB ^Il5<mi>$~Go Fy߉|H~ ֳ.> xRbXN[iF&23H$0# A;UiO\k: I17( d!c OAx3VM.WşM:#Lamq֥mU~|"+;K׊`OtnJI,D]R+2l:S 2*縻Ғ +TA'9Opg>m/ms;M2BȬ#К髞݅/Ȧh-~铀=iHj%kríD𽭥$ L-/DǦ}멯!ӧxg]Dko}ۭhBBFqs^x}QKkk{P-,I,eQ4r)@y@ڼ|C&M3EҤln_8"±8Y\^/4g:0-f`ܤRT" \7ӡ&ڠ @Fym.iLWTm }xmdE%fɄ''Q+Zo-*QA%wL\ (h?[gcpUiH`;F桧i/"+JyU G'j]i6UF]z+Oƚnv݅HNv|kG^^'nl-"9ou]Z{[uA/`1Avє,( mVsgko;nY\Ȯa"~'Cjzq{&ks[7R;(B(WSti!6Yjk !Iv6q:nzm~ũRXO0I ~bgq\k۪w<\V.^Cy.m18M,bb0I99Gu=W«i-#;dRH*OCp+}Αo:]M8UlnijP)lpb~˶weݭ4ڻ^iIjַLQf%:x)#QiDJYDzW!B(!wlQʅ!9' =y8NOO6tBa7kAjO쿴?uM;-98'h ɧ<$Lr_J?wS@EiN޹IsW9u@(^>YԓH&u<R#w}B5ox}n|<`Ey?)xZ/x;;d q[u=N"1>qP>g?{Of)^-:TqIqC:kiO.I$yIZk3}ߌLTedI dbV|m ~߽'7ެ]fM^Q?z}&Ui/C堂IfS~+kb@ǘ~˶|ݦMN52~gcU5S`"|\J\:F ;|UqT-9QrG\+FJi!ܯ1%O:۝?졆 s;}9-*BAm7Ne,ЩdinވS~8;z״~ZQ>;if.q^Ě1h?r2G8 d)ʔ䗽fvaUirZ\zĺQ_=Fq&vj=&LK)--.of$*)+-ui/EmiWW[$'99gp-,](ZZj$s~<,N8Atuj{ԗ|M6FMw 2B,nj"ɜ5=+-F U5:#,ZO2PwH2NuTBw?mgi`v j3ɱaiR7F@5D&1yc&n6FT|Q߉t~( ݖqHѤpH$g#oZeX|Qq=h [ƚ|K>,kd(fc|-wMvF-,moM ѝ ‡$ 8) WW2A|<5Zl5HiYCjgbNOݴ!^+33jvEZɊSDgsHd;Vs:/M=GJӵ=uu5t$ $rFFsj]k}O4߯YRHӒ]&{Kq42XLq3NFX95[~ҵY-d{X_e x>kl;6et4D`(R 9;rO m/  %KmۥrL۝dZt(7{=mᴖTkxs qEzq |eqm>iw&bYBFF @`URдYkH!#JlûwኲkFt+)k{8 dY;Ik~īP$?xN1$ڛ뛝AX~sl(y[6;%Cj+&rғJL]7>兤֔EreIPhTAW  ^&=ͨZۣX@8xkټC^zċ -+v/DfIq"߃4\]agw%8B3x!iYc5.N2I5ᶿ].R&ii;Ě\.P2 zkFyjy7q43r0Rז~&o=wVb@Lw0|r:*;]u(>x|EjM VV:}pK&V>%rƹOkK}Oa,i$5I,]"2xQҺ%sZ6+y z8~lVcy[ ZE.{ZXKyuP1z4CSeKΑzJ` Qu=e>S[ͩq>r?\I{@zujhz~hV`FYA}kF5ANm]已KxV̪^+R*W{׺p`WK(U$Үxč/zIeٵEm?L?^|BҬ Nm qډ "\pqqu:TSXq]b! {dԭV4(Qhu-4c<Sv7ec3mĎX 6A(?ha&/i2iQ&eUr$hF9G.Փ||_o&nxߋWy IxxÑip\ePM4]c0)%gMEP3)>+1zrq6ܚ%P2x$g}k*U JlHnJfX1=!#ugDΗ]_Gm}?!k\Ok[ NW8fFd@1 dT`79E8-+Ĕ4,$jXŨ^]Y(iZj+hفHA"䶥Ap08%-*>KUW.sꮉ*$ݞڂooӞ8R$dpnj׉EѨ`V#RD\m{=&Gy `IV^Kӵn&degX5r!IHdQy_^}j-2Fxr`i&DTǿrUi+\oD]F#ZZZ%dy5iƎДY!^oP~WwH6(NA=hMg$IycR}?PLCoK#z*Iy|zّWU&Hn+#RzqNk3eqcieuާtuW[`ѝ@?kPC)i O"nhƉ:ۉfY3ȹhqޯhl5Y.gO [ڼ^XAA= > ~NiǍ tn0kq]ߌQi-s]v ֯riߡU.$!|͊s@S>خO? m7j]+J1f$6[+X)Y-9mQ9^ J1jQ,PyYXq;jGW|2\DِL6rGe^:;*KC_=r)etG|qVJso4vVVf_KBkV\HEܕ+|s~O xKvP7zlM\`C&;?/{ sE7RYo&&6)U8!iEkmu%?gދ(l- P>e2{U >,'[;˸Xbi-sI5\"^*ۢtW*M} ^}FմO:ŃLdԡYegHZRFd נ-7Mx"Y]r*,̬!F wm{yߊ x1t>Mo5W[l,`>-==qIn+k¯h c^#4 ;E+[5vy\v}cjj| Z&uM y[p'IP7 N#ki^Aʾ'ѭ|oMݒk L-TMpS6<㑑_UW^|g}Qކlm1d ؙ}~]c2zԾ+3<=oOn&i8"ų4R*Ĉʒw8}<,$Ji!ټ00zsqWIM:TSO;h-K,/*J+ `,8n3OGt;+.눖C+C 6}+K?EcojMKwkǕ9鞕 KX~xi;ZƞV0FX}( pq5U:A#Fc%W8{ 3~-k:UM[@L$UYMtzwN5vKWh6d[Id*X_ hiYAz -$_np3mV٫!J^UΛqj#xH,褪` +kK?gѯ|ƋoZ9"i1CPF }]k65[649} j ƶ܋MSWkya1Py3c%ݥp!fUPyc^E։.l мkm@ӯf!О9'ӲTB ̢ GxfwvrqqoiUC RPTJ_x׆-ƊvŦy&v\|<V8Ek9pX*pc! 1z5[V%4<toEyZxHR"BywrX_:Hn/hnd?7#q|\о%KHtK? N _?ՌdҬ0y$bhS ?_('xPKM]Zޙg PK"~TfP69߳5Ūi^"Ӵ4K+Ⱦ˪i x^Ré5Ꮛk?/}Z[k%ab#m|]-7=>*44 ^Ϯjvv+ 97+FF^Nӕ޾kiollm'%`GU 8n >yK 5Цն[BNe{ ^]iCEǛ>PsvS[?堞]z(Uu/|)\=Y' k+qW4c8P} {O\|>eo,6|@>}j8j,B2GJ18zu1[z5jZHT*˝8,S0gSw[jh V+ _ 9ɮGBxnKaiV)2{ei?Ps\kFT gk? 5i}2*3=+.Wsg6涂#+)b䌩 ~?tcO ֯> ~Q|i|?mc6Iu\2K6c>OZW/NMsb*ԥD9OhX JiK3;t[ QMVS]ݙ=H9+N]ӡi46b6;wnh^+Stۦni"Ð< (xC~ Xk#̖&򬁊lxQZ~P (l}rrw!B?imkQuhtV+8^mj$ҵKEQ0ٲ7.߄GH׊#O!H?6:J7 Oq\Xèq?ڰjuumIEHHgv$qL566:m,,w}=ėRev,Pw1 ⼒DJ:_g_*3jkK_>b/ o-O,3 䎦޾ΟzG~9xu lo.l,Yēo>bg6n9&g׌xFFhYjP2im>ٽc8 c {>y[!~-eҢl4r}LREdZߋS_ӵM&6v|FI3yyl ~^%m-"֭{)m/NsuLUWq dy8_|C5MCȻn;>;(V9L`(|K?S##&AoڥU-ge.7 ~Xmp&3<͟Ρ#xWi`&arckZBg`?¼M0| ڕ}-pa jna i~u2 :([k: nG!5꨻W$c&KOtWzViuYK+wIcWLh5.dRگ~l4K޸*izBc$H| 򌞘^E+5:ljM+O xM]5IT 尔H/`t^'g|n?.fRЮm:DN~̠Sk˗ɏeOs^.,CwS#sq6MF5UNnKrI> t.ZhT{X'+wm-4g\6:ޡ$vvN7-[*b_ub`+,2k>+|=m:OhO+u%UyT#j;`J{I-YNs`c{WʺD~uZ_i:W%Om]#찟#spFj,o/軴xkPotm R-K6;Ekex@J4s݂{ O5fj [(?oo7R\? Ѐ1zDlC4hMjOu)ZN7r(8F|0"}sZ񮕤k__e@նyZ^TMkvo)UG |;G<>X]i,wUa"'v7R.ײ>v߄km=m"7Xr:V7)&s>5AX$aHۖcH %uGy\$ZZYALacaKwcܭb4R I9to~ԃMO:ơvr%ܘO#']k ڥG X 7m 9}3m}WIu[-sO7ZuY\ȖC#|[tͮǟ|S8tsOcu6[9Y#gj<ӑUu?>"GX^,AekMe30xrv\d^_@:;ιMm+JTixȟ@Gw) :.qauj:Ɯ{66Atf _g~x{EAO(-ÝwY֥P+UNkX!6f 'v8 (?Q-kSQmJ\(4DݗY {W|M? [MULW}BW0ڰi#C55;kmAKU4j7v1a#F10@O/n䃃KH@ 3to~ ]{ĖYOikiaM+0 kGM$}Q"|=Wu1|(zn~Ei~lOV--u L|w|'KP\DʮTg= Ċ[9c&FIۅ<b[Ꮝ<1kvAuYuA$IˡCvI0C9kit{m^}DYD"VIFڀ8\ k?sZ]qvA$R@Q`oP7M{h_o4leVQ,[٘#c 8S֚7^Dw:( ~ߺƋw! p\}k5Ծxx"00pk/(> Pne>dr=xUOfަfQ2@YlKY'Hڷ q9=[.2_ڦv3wE >IU:tF4FR9\cviOF>H9TTLHU'E'M%"O2"y5/X j|#?Q饿/(G2rz\v*}k|Dhblֿ0z7|8>(B +Nd-) !=1_7|^eu RgWʐ7zWË*-샬. j;Tbݹ+oR~ծCk=D͌7R*XgO+ܼ;SR}-c/oA-yֵ쟤DWHS_@6f8TMezcs?7(=6c 9QCu1_ĭzM'XuE>FF0UF~ҩiq֮.Kv?F5%V~lJ3Ҽ!?⯈I!ћNԬⵆ,r|84xNđ,u;}-xQ5 %GRH:͏QWFO\%/Oje@ y5W?l53kVvŦk1BoʜA d012 槯WCxRN9]HdhRTpTQ\WO]|=lz=Hs<aojHΰEўà][%ik} r#"|ṛS\N;Z/B-?\s-% ڥ=+Y> ѕqqmwB!%UuT&SoMkwzGO ˶h] k|SQb=KMKMJG!a$*W|gks^2z[MB=<Ǫ"+qc!H; wN$tt}OJ5j13=bBXp8S+V'e5KD?- Zmnb yj.ڼDo%.Cƨg$Q`p8֏o.jdcp085B}-tQE>>{Pn| [(_1~όk/#E;` υ_JM+5$7<ψvj:(& z\_EjtRXHw!L<U:=ß7'F}*}V6>'̣ΈqA?o~)# _NO.b| C@H>x5P8FF(=}Om'k~_\ͧ$1o/G!q^ElFRL)@ij(Nu jbƽ7t+־c4H$W ,/Od*I_XWx-Lj55M6t P9Y}{ds_)euHmw3{+?sᵍ1Yj>l..o#=uȄvBƟRT<Ͷm_O9s+<$nV9{^6]f50tD&C\ev~7|+C`*J^^oVZ7ޢⱛuMY#X'&mOPJӖv @$^T}*CV|(dtf썬fpnϺ#o*dܹz$J솭Z{\֮fZqw2(RĀBJֳjgS5n C!Y$ͅlz>+kgk*b}L\xF=f7K˹ #(e;\jR_lI&]/z(&!} \kՍׇ<+/f\yeBʼn @95o%[}GK4Wdp-xfH$lvxV#ֻjٷ%[^07MHsri j-dDʖ6~(MYw,I64g$d.g4+J֢ٳfg4Ox7:ϪxN4j?^HJ i CzAR}0Hx~N "{F]"W <g9ek_64pxׂIu 8.u(gXwfSgkkK? e凇4H}!좵hu}e cj[dXRap2pAޥikkkj%Dz䙊+H2nB_R K91I8Wsp=y<<j^<$aEXX`݊t_CO|յ_wQzH"tLymym<קW+^G;K 6{Ah91ʥYHF uUlrOyj}))gi-#H̆OXyPHH+>xÞ,/w8>m+XK}jRzȾǦx;X5("b:P@by'o~!Y~>|%6q,M۷n?MQ}VI V.-w ": < MgĞ0lz^t ZkFi6ǤA]9g!aU$R#8=r(`x'Ֆu+h˫\Aż|o IJ]+S-?O}WWGoGvЛvƠY6ؾ|E <"YX:W;$6RH|o&?]6RwMcjRmق+mޠ:}-t=*Z+Uеm>8V#.J:9>gε|l MtrFIeI-V@IAfbKxjψz0xR}ry[C67v{eEPvapH$4o-|ci}o,~dSx=w&Eҵ}.T}B8cra:[\~vG0 xr {ghm/X8QAt׶jOS#ΞۮT8ݑ+q6,A_kC\eEIHV=jzȠfLZʺj㭏?#fᴜZ  6*@U|;"T֥ѵfݤI3hiPz<3kVۭf9vkkTp]HVڭ˺'%cտͨ~UḆwiܩʀ:Wz׈*_{/mO}6<:XRM_) AaԾ8F:Y ]P&1)0"`UHk7]q{q Xˢ,lYr0 X<'Tqiǁ/5k7I-:㞂~)O&}t6\xX8X~ȞZI|T+KO6m_$v>u{]'J̶VG["g&]Șp9c= zjvxji#P_K%yϐLdlq״[7zk-giWMTZǦ\o 0La۰g GS9u/Es-G%@) j^- [^it(Zյ9ͼ*J$VIݤ0dfυO 8}> yv v||o2bX_j"qjabXJFnD,)A`ko W/ 7זK3Wlj%Z}yχ=>;[ج/~q^^Kf+岀&YЫ0n8zeyǧW_ڭ{c{69+ICT;u-j43ƙze7VYGXp ;IӚDQx/ S~oyul,c p60!]xzFB2*vGeWuAJ֐Z{dm2V<F`2HR|fS׈;xfZMO&|ȩp=3jc4=4:2Lv,*OR?[ZƋIS?n<@`WQ`mD9&yBiR yf_m5+?G𞝵S)c<}g 7=O_9|@|%@4G6gkA gL_7y<Âw`.*o"3< K|M}gZ5$cj=_)oQ>br[1|-'X=oIy-l#*̪NJ8Uc^kյ-N{]SJ7dG 5)&pkT~VD+ shWoۻFxZiKјA N3}ggoksjm4O`\D%\G~ }w &JhA* +AsUuEmtx.Xa18G*=Ÿx_I NZimb bGvզyq% ,H۷h\^.SԶ2&"j_4+$)8'^5]rڭƫ-N?iڬM:V#>I!Iv'A`wmfݼǣx3~#]ҴU`VIx0 b.܂JESL-.[NZW 1Ѫzh%PuOhM4/en,뇊,ca)2Aֿ {hah6z6%Kvb$`r@'~в)5=3WRlf 9IO ET_\lKQEWY nqRo= ʼnwxuҮ! ź’nXآLdWMj.v<Q≼)u\6IlHwȡa9ԑt5B/xMνiz坬REV}2rٰ [ u߭ƙj*eM, p^iƫ36rnYfݪv܅+A[$]y?kK[[Rivwmq!STc#Gֽbsō'Ŷ>'o&ۻK#%#kM:H#u>񞩭iMe<#@2xcDJnNi3aɯ7ſX~0Լc>K[(D>Y /_%r]}h9Z9ծ/nmM53fH̾35$dCVw?ٿfc>Yt*t5[PS[fJ<ޔLf.-T]_Y:476˧h<Ĥ5>w#0+ʾyuySTƢ:pԮs>IdV=M^*%+^Ղ(QbP񁶹y=iGӀ j?v[F@+B/J *ɒ;ՕJuQ@Rih ( (:YJPM4VʛОTrz-eY"R"ġVr@{ mM9ZXԝ$ ѹĎAZ_4" H-C}y?qRXRֵc5q|e޿~%^OIq vFFT~Ϳ|X<_}]>F᧛da{k$XJzBq];]2O> qtQE.^VnnQYEf^tjM޿4ZԴ\Zc* m~|Q$ Z[Y$l8 Ǡ<.&ݴVӦkX䑼 NkORusEI}rr#@Uڇh<+on#S×7:5o>2KC*IE @9v'|(,O WFΎ` =i~Z;? ;χVMa$;V;)- o @=AK+~.lԮeu$2E9]qS^ڎЧ6x7жᄸք -No3vp\(9k%IxcL{6Qqyh &!^I Km|六">t h^ڤF{I-d-?𒯂= w6ci鶱YYYWjf,ך|(n:𝆩8KFP=Ϭ[!uaX>6Ү|/.bڎkdg09JުZ6]Զ\$dlenMLˍ[+h uFk4$ȖS'ך&sO=B0"ncʼnvgDԴ)ugnhۙE(~\orW5xZO.eȤ̫毸gQ\ šYi-ҧ+o -4Y9}+SB?ys 1y{߅ G)DKuFA'rkew_lmmV;ۏ-!lכ|L>+{}ufFz$k~6h "zڿuMNڐy$t{}Wᴿ""_MH8V"ˍQn:zZkG`Q]3Llo lG" 1~85]=X χV^.a[[\K"6 M1 $vȯ_=+ W?)JBXmZ9 BْN:)u/Ǟ ֍E Y]oltIĩ)~]sd 1MZ'#8X:Whڧiwv^3mnQZ}ʌ~HQzօ{De cO >eEʥK($c+|j6Xt5/Z>*=⑅,0@{|2'Ҿto~-OE}ᶵO>`BFٍ䃐3jW?QgA}qx~)˶S6B (KϙbEDP@ҾzoacCP\ڥamcICFnGs_A@$1bJ#?'q{u}4l2YnӼp۵AҰcxjLmK'L,27O~0=bRiF6a^\JPs*w٦Ygֶ"Xh?oQK{t"Ѵ[حi8ڻ1IjP3ɵCŻe[zZVY7>wW+1]x5մkMA/H.΍r+zyZ}nYiw:H!yb) #5i/_p2|9]+GԱsyߓZSdO1sp oj:N BI$ڼQ޲2,bMj Ez xσZ߅{^_٭ kz/xeb,G^!u`*i:}ogmM4*"Yպ#fOx Z S yu; VTRI-2ӭf{fDGAw?׵oVF%evK!wM8Rv`:nmjQE"0fb4>:<|6 (U8U(Ʀ*O@(QEQEQEQEtנ6 1qCqjtN텟W̆-:V|z:HE?z NrqbTci94f`i:jmfZٞv qw].\)񏈼=MN촲qZ8Xio0opu|{oŋyonDwo#wnȍpss_*}1+*tRϥC6 nߕ rqrx+ԶѝkĖOyu=ܤK}Nr:UCw ]JO 1[E5ʍBDEm+؛q T9,ti/s*Js*h%zu|I7TKo-e ,)$О*죽r񗅵Iþ[C[>qgr4iPo‹ˤ$WQ]}tb/$ٟc/k #)~&V4u1NᎦϫ(a$eğ|%o }o * S98e.vSڍA5đy&w;Q|Ռ$&hno2.3Q@BQ^ wc?^cQ wн{(ߨҿ]]_. +]OiwMjoqڻ)Tn#'G}zBf)\emF@ApMTW̗wtb.KR~UK g Kb3ڙ~ޞY4-*kI*Ge I'}c+承n [jSͥ]ZtU26L5zi!Us9h᯴tIz}E| GqsP/ZOojۅ."Ff`͵Y&dRx>mm kao-`xVRyJ #5vѮh?|RE U8=2hZ+{K wл{(ߨ]~ǩW(.Tmv6(?n _5 ]?hlvlwz5۫^Q-wxU~cFX]xpZ')$N4]MDaʺVR:+K5 x/=kXPkPpӿg3~?Ux?CzGeRష td۴3ow>"hL|E:}MiznwggH \Dx!oSJCjo ,Soa3bej)!@ǁGtƪQDAbH'"mǫ5ki~6^xogÚI-} JݏG3](]WK?. ?moxn]j-<$ Gw=QTz:}?oMqc:Vu-BhE2M۹!7>lc7~SomAa9TE,@M]?`]Xy4'f/?|uo|I'B«;,˕j,[ 2{]*x8#|+<[Stn]bpːo#iÂ:t#ۚ\(>DH=3RL֕X MbddO+ɑ]Q@j{M'x{>%&-rmۛbbV ]ZXXn dB`znuTUK]V ( T0 +ۨ"nQ~tQEQEQEwݩ! ˽ܧh@'}3\ϽX<6F= }@`A~3*_>Ë^x_beeuCEUծYʒ#8__>]ӵ NKHA(_㯪4?:6"$v1 fbcFZu<ܿ :-ס]u? bMktF8;6cv#ulx>.[i FK2}\gv8ͦho~4QnF?߁ִ|KcCvuɬi^0OXʟgxO=g cr:R|xK C"άW7Чc*S c#VWצu@g}5+CkYmō3}bI'[`d _|. t2IΦ.#_WtN|f8 |y#~WJU~saΡqc," yC2pvkյMgWtĚտ'tY~־~ԒOڡvZ-}>_gmF äi4.[9-gr>C0n+g>Ml&oܘUU$W[-I Ȟ@R\m [ >]YկuƷG\=Ύ;cG9NZ-{;}ॳrzSES,N[ؤo E52ne;~P5!y:mol͍YcGiS/ANKZEu4èFQfT aHa'~BEOj?s|Qsj˭j7W7k76&g%0*"1yM fx%81!\4M7Eb&eӼZʤg,2=:|#_WMS듙u+Z2$c(@ 5+oOj,On,.ngy̶0:*.ߟ;#C/:燼j5+bHIy~e]6T,8=ix ET}FS2]N+ͳtX&|]II\|JsLU632\\ZqlUܬb& @P2-+cxXMڴHy$ьĀ=W1?[կViRPUbH/*rhg]lj/,to?K s}s%05y!Yn Tޜdb]k@::\J3f7d:c.w;qgNss]jHQKk9..-VC_AiZ4Zew^@D&C7Β0+O9ݻ8Y5~6]7XXṚx XQv0C^qF?[;hoޝmM_tׂBcrA2}Mk|HE^`t%$4A05tًZKuڵ #G. 0O71oLukv7~⏶v+>.s|'.Ўk){  um)uM+˝J]=idnyz+KW}Vu9-yQ6#*Wv8kގszƣsiooui VZKo*ƁASjMwQfFԸVe&rdہ&wn14_z&㤶M톬,DrĮB!fc'kuC7kR[k_% <6x%/aw>1_TE-v֩k&,|PFdW7o:2EᅼF͸ʓBU ( "/ O$^}ww`i47`Odps^ Wg.tKs{lI!Wa9jm<-)t/R}|O`KuV1{C)8uoW+&UY4#:}'yK3 Ƨ p fxqPҴwt2xbU V)7*i't]}?f_xRdu &8'o-d1cxwv>A^nq?z5ZA5g[[!!LĎ='KS'm.,m]3kr-KҔ*U WsV5++[)/tmBI\мDv7Ϝ`G[1UXgmH\ 1$TBW`!o'=NkMN&Yg(z+zlg$kcs }HEp1"~p_^K߉|3i/緷>2XtJſX1cڸg.#-5}QLd-АTH jIꥭ|A$zj{sV²$۷rcllX%owߛ)hfo ,Bq'#l|-<ɢǨh#Ki۫ZXQʒ@Q$֟ŸKA }WT,gyuʄ !eNrAqEYZ&:w- mNI_%{|'y֠SMvɡ,H$_dx+Cf| 7*xt-.m.62*V^'1I 1%뚦=έy%[K`S~RHԹR h?|Q Ms4WV:%cP- ĮA.pi-,DmQkSVRx>o$6"QB\bq+'?hw.sSZ4 yU&j"d+fj~-uCRơc-lYNy5oxwNk MUlf!FynW9h^/s[~'(K_>u~ӊkzWyVM&ʽ"yTq^ 6yvOyrL{EE!Q@#ј5㿴u|!tӵym0 ßc^7 ]iaz'ŏ]x3Ķ:t{\Gsմ2Q.XקzJ6c[mu(/K$Yo@HkBD畷-nJM~ZtoA_rFq־|y!TKvq,O$0sN+0c:iE-V[6gA_k͛šEΫn6 g~7tzWo]%Zo5"LpӃYG͸Ͼ+yjIZl>BLy#wU]$ׯ7+g܎xm,v7v-i4Mfh} => jmM Cx!pmDŽhUV^|^y>~ҾU1蚫?cZ0X.Ngvm~v9Wk?=r Y-nEaTU#x-(mNmHXN1>^ ZePC)MYnz#v_s3#T5G-`"DoEg?<2ֶ͢iԖ&I9%/<]+Xi?ٴW_Qao1+,x2c-^5akImg#򑢉Tg;F<⮵15\t;ez8]LDZna95j:mƩ\dJpI9*'xEc`s)C NPRw<]Hͥm x<ka=^ 4GB(u[Y["Uذ%(ǵrl@I6Pin牮0 ԑTmjF֞ :tv26_KgӇJ>/}컗|'fޘ1j_3MixWa>lyfr3a[޺>͋|&ki o[C` ABm}Wfּ7s`leX)g-_ی{W/&vNu5[vGF#`t}0ث?n3Zvt"; b+/^Av`XbڃR.>S:n?"‘Td.8<թp/zOtndoK FmBlQYsjw<;3H.:,VqOz'˸[VT=\:z]%حv,:3z>#'rߵ/mcL=;1_6IIW$BTyzr.&jNOk۱qԔcd}aB-ҭQ[;T`eD_Q_m3A\_/[+fF XU,,1rmphpm-:ʘM:5,ޯ>߲>鐥{- qEʈBa z|*֋e;oi.#Y|k;Q4ǏWnWvC~ꖛ]kmck%t%ɣtI^%FxJ/ Xè,6JXM:U/v=.is[s"ZiRD}3_Mm%R#}]?8W3ڕ_e}_-~RTg;FҥױhqM¨MrJHmɌ/5|!^bPbaNRMTkK1)$HbUD\*:U/E4oO?o5oMk42V k6=pW}k0➶'a ݗ=n?|?/!E$~5cN炴{ExW$CmfѠ^^y A%.\l׫I&eV#M:G 5[ .EvWtgea^*4lt{M8U QE6[& }>^3k%hlÀ>Շx6RH6%opG#͵_D5.T!F);ˏ2Epρ9lS,8$ddSt5rGB''!pWs:Wz]W¶-<穆 tB= QEyXQEk!?'2:Y(ß |P|e7kݬ'sǾǾ==Ė6nduׄ؏IQIft߅Q|򛌖\㮦,a̟g R֑A62n( 3־hzUMh" $G̳ 죦:g"׮.evuZGrT ^qz8kUvZ+=<-)S߭T'ƈ&5XäF|oC_⧋!%:5Y7cmdI)#(rWjk=>尌tܤqޖC ʿ#U|qT,wQ)4 c? W0 ,UUnn a5vݍЃU4 w)af/ 0݆AT෸G5Ek}#z{y9,q_f?|Kދx0Pۓ"f|mo)15xGυ5)9 ;y_ hTFyPtgG) |M~^7ӏ{\F0RhDr. u$ӯ!nΎw*xǴ}zR5_:?7a {Lh<g8{vi@-44(%SksҚm<kx[Z6zΟ>6ոgD>r?*U+yd =XusPwE3mUBUBU}k04#zY|9g/COu˯+5Gw[^(n?Jļ=> lzoF:U2ĀW5\؞KU cuF di! *v,J, q.t>R>;ajвf 7zg2[sw})sr=W fi 0\r@pqJC+[[,;T#p9f8J^bq ms5T,1Z0~o֮y>7{rx_ZӖdV13|nFy-MO&nD[T#]W*8\W|Uso7K0y弦6b?21_YoS.dY'm|n+(ʥ[c̤ ug]xz{?ߨc浜qaNNzLדlw ou$c?}E:<u{!oxt.yix^dݛI<׷Oβ_\|H'~lpfX^ta+`MTI--Ex{*Oe?Xv؞y>=־&cz,F$][:Ut*+jN.~0%5| t|+a1\]yxSPb Og#~> F~F>F[oJ״/*Fu<9bvd"f]_ĵ&\DkI!㧭0[?JY] v[]2s,Ŏk=k&PB#W\n:'5'x"E6P2*W)-MG*}k(75)VL3w?d0iqd#XpcL~f;ёtWQExQEz]Ru?w:minI%sU`X'/%{U.4KM՜1%Ŝu.p\c85݃ ~FjO?86ֿ\m ?5A {]^wG 1ƾ3I*]jW7qtFS.9qP5ʑ{ޫrmV9n9ȳA9"BT!hǯ1,[5 F( FuqPƮ Qաh:z^=l38\IzI ʾ3JX&:5I_`=HB}}{4jo.̹ImJՈV o9WzN| n:~k?úßzϋvhO_2y ͅ챙yL[oN;`V)Ugғ 1Jt/RsS[^,.Fr×wyAG9uށm+3«66;i!¢gZj\HD2ld v1!kMQ8f GAZX$ʇ+t9z4)3ZLʊmr^w~_cβA׀zzkZj٣y-#nr3jA I(*TZepjtFSWVhqDCjvMg{2FMbu70=8?r? !>X+Ce_,WG,Ty&FT 'c717]Рe%N8?e A_^.<5UxJsm3FCPx#t'Md/rϯq׭zdD:;~zi$- >q1 ͑Lf#σ*]JK2‰PΊǮu~fm[sS^;%e0j$>XZ L|#ÍKu+\[E!+C5Ci?a Oq\;P3!|)&$Y bןxVlgr[JCQ{7~#߈I@S乕' إfOR/0?z nчFkYگl; m"_ xwJ,FF{2i$"m?_mz_A&e󢕊i:xOeNjVPOXyO{]L]\%E:sxgX<~.K4uk]nE1 +Uf}#K Ҿg߾x4pcy'O4qk}B2P $W}:m^/qE~ n.~B^?>vmt {~5_3j>v鈌\I ( .K0@5 xSUmé0iA|aqjeQga_oƖWpxr7Gko*=SMl}~'xeU<}H>-.5=R:2JzЂ2zWx_C޵&&m۫Xa#^ u0#5y\B(2QsUaǯZb+5jw;Q6dz+Nu~85)DARƼ Pcq {/985߈7 |TZA-TWSJ9Ӗ:zSwc9,*.Yv=zבxGuо-Λm" VU~Or3b[MCNݭ/H4<}}M>U+cҡñc$栝}ǡ\ږ {k{ VOݭĶ"x빈#R{j~wo$$!A^:+ JV-સk\yr@y9$ުEmsNV=BU,CF 5eoeZo-\忉xb3u)uRN5=SKԥ!*8a2+C+~kCt ZI#4LExȬztɦ\`(%4Z^;ӣO4g vS$LOjLfC1Z76/mcMem72! @'Qh_~%9N~*Qcۛ$sNk|+iM6ouygr H_,]{hu#=:LrƳc:*xvyͪQ F$,>G3ue8.̥c:MBѭƓe C,DsUﴄcO+@|eĽMRi]W@X%,/xyl&FO4dmU$s*@d`U+U?haQ붞 O_#&]g&+f6vT_\̞XMD|8s&~~,AWן$I <M8򮧡6+>3 xc?|Ij6y;[+)Pvģi| ǣG!&ff.|z @u}d!Q\Ht.%~Zx 1+Zzcn"E E@oʖf9sCbekT34`xm#lJ*Ѽ cunIJa=W55KrQ\ 7_6͞-E$>U9TsTVg ti eXwz]شA gKi&r4yO)-qҡ XNr[?i$gv`A= *O \1΋n۸ܱ?Jۓz.𫎡H*_ĀyEM lb?fn3$JׅNFɴ kzYaXL) CtؾyXrm4?>VKXc3q)?5U} t&hV#<Ƹ9g#}z3Yk<-b,-˸Yy'pL'ły>^@5YږBP{Xke) pug\^}Xj2l㴏/u39bÁ߭23Qx͖U{%#+#@Fjvm\+GRGP9Sz6+xO|k2+FLyœssֽ\VnX5yhNcJJ|OM1xbqYqG§0BA \ q11_GRϽ袊~QE ?:Ѽ9?_]:v]#hntӌ!CLHZ2>j>)mJ;}.㽍b>#quxt=ȺYͼFB*NӒzW-I7m؆gW pjp23帔4,J߳}m.`%fjђ8qZmg}#[ OK0jm~bjI~͞5\i!D{$qb3xv={k(I攈 7np'{M2MCP.-K/뱦$ܠAێ)hQh2Cv<uJ9ԦM s{4cS3'<S|y GӵOաRRkȄ/(KGj~!±F/6nec ho 5mOLU_I"(f@HE+ۓ~*aUrkߚ/S>C-x&2/";g(+kO j NsqpܡY2Ar#+¯#~ o5Rp,cWe Lg7‡@G.gZ;&Q#Bp\TCO8T:OiAm*j1,sM$r}ko GQ 댂21zf5߇ 1_r4ۘ;N1ߵcW ?នmhqݥ[0dTjd޵5}U%NM;73LI&p.W1ҭxVԾ+][1ps)铞I?_;}sD΃VF0l;kǼGx>xt i; >H99?6 TR/bqRVsv>|W4h+kb'}ygp}s<*,n' T_^: K65]Jz%[E +Hwͮ[#XGߘP\Gmu1[GJnLJJ29&K#N/gd&Мw_:W646KKL 7a$q޺#PvCJ׹[f0\<;lWL$k;xY"$BY@FkkA&1@$ :1H4V5a)bN\JJQ7q\Z(Š(K+|W7ጚ|u_ۋK*<[:6ӷkK_n|7L~ ݽݺ˾t ף׊V~.I^5xS:@Tg KOV,񻷩* 5Y\y:|2wncHm|5cѬ` l&B3ߡةJN5m{?u8(|F1ó F~8G3VY'&xU1Tݑ,%fG˰x[R+4\qKuy+0MGZe*Xe $J벑ўxA"س u0xG귷0ڽƊM;^f#Lck*kcχI?eO :3O#!Uiaϯ|ZapB[ЊcP 'ھ_W`0:ۆgR*x?V7z s>VZϯ3&m ;ỬSa ;o7.{Ƹq?]zχ4+ :~jIme[\$3Ǡ30SB1Fz,5Jn}O<7pf7 0c?qqeԆGr*?+~#ao3jax]i 9$4 9Ę?!ʢݬp90~Vwuj VyĠo⸝'ndR!Uѽ&mj/<!;U*Qn,sr5$r\[= L`urfख* L ۆ3W[l<ij1 Yc}>#T,w** ;z5a3W\2K8ݹ2ȑrWwT8~oxf&jd/#7"Yi#Ӛ+[70Ta ㊧<GPb&Eh~x;gV?ʣ6M2ZJ瀙lԢ.m8Uf;U*aȋ#Udg.2|x LNR$<,W2%wrߩYxnn^I³ZbdH(~T먬Pcqv'W'67~89~o-lKz%xU>9z/D|iS]xK-6n$y:_y߂>[* d 3U '2;SL Ӿ艧Gso?loV$c }U8U=P4j"e+.=rZ|˙Vs+o:,WE|7}c)_9a|O!WW2@W+:U$ou }`kD-~W@?x8-Ԕܚ(ǖ>[Eğ[=8_* g%Hϡ+Kz]q-FiAn:/-c8,WW~'O[,eq"v?SZF~>i^5fLJ$A8= 薧f b]>yW@Z1< A- 23ж qψ?t)JMjKnqkPrGڂ7 ӭ9 B;{w /fy_V+ ˩)'*]eAW|x~]BX劬T4$$85.<7O^_tדd2`)mDΙ=s&XǣJ/7o|:u%"*c_24c9x–1$*Z>q5sxI6(_Fd6 /ڮq#5 @pt%(;HR0=঺xo^S:H 2v#;B^NyǰKO.[.ך#vÐxm<7n{6k xsǜNgQ1ec05*j3].!cV(w`~yS$Klr8keQ~虞2ULe1 <>Mo95- J A Ҷt8ƕC=ډYtT ]Kgh$mŽ;\R+9EJm>' =!ۃrE1XwAw-Nĺ޵-Ν<F_&@H褀⽯Q-7Y&ʮ7|͞U)W$b}!ESQEd^kz/ ͦ_R?B^U氂59ʜqџ^ z/gXE/`~wDe@ ]]M9+nA#~ۋUŵ6]l>:um ?5Ǝr--(N(ZuQ@Q@Q@f[)&u:62NY eI8 ~xu\'e?g ?ÿ 8pL?_sᯅO+(6jMDŸ'1kOW@6Wa4Ÿ'/z|7i5P<g_~`}4?_?!-wtP#a 7=<1+8 ՋÚU zmpE@?+FwI =w!No>g4z3+3.Uإ6ZVZu66qg˂0$(dJԕ%=~eQ].(Ԟy!r9+xvb@F> $֭Kӧ5U?`xM2\ϧZ;$ udi{Ƴ11fV;sUW0մoQM,WLȱLN J%E6)fKhvlӊ;.yvaB&g 9NU 6wE_KfN|LܵѷNd{VY5|[ܷ^0{R7! 6=@=^}+Ykdkr *F脭3Ѿ_ye E1K"@8orp7qwOr/lvuPz^?xmص$á@; w~# ?Tđ;ZB-2OGUu  +TQ!\~iqDN }5|-L|G sUpkgp?~ @)c>lQE!;⋔4=?TH+-ݲHʄ$t8SO6/&Z*\b@q[/t_MnV+-"%@kQBV K")WϵxYw>5y-PU+r{?|U>#MY2̤gEy]H!Mڻz(mx?DoM 㵽a'JY鶶HUTgVn_ٖ*vG VQ$a;F3X+w^#4v;f- j68Wp8OJޢ-y蜺|;9%`A[7FVʷPtphBt~k\l"_4 pť!=3+Il:t_Mi|1ϴex{LӾo$~b`Q} N8{ f bS@;Q s|Y[|ccP~U(zUN]-!G 0 ?Z`)0?(1o"N[E)z@pޣާ).څ#DcvsJ ĘHrh((HtI`ƥُ@5:/QKFKWSif4:;sJ͡钰9L]]7pTC_Gc:3ߩy"QN[۶^.Vk(^1/<.K0ʭ0Z]jR[\Z2X\j: mh`'%<4Z!w~nS?.+XhbԺ*<Ո4kh}o]<+0ahrSK@ :өhhhZZ( ( ( ( ( ( ( ( ( ('IK|e|MZ$.!6⧡ϱYޭ%u .6|<3v ۆzz3|\v5 )d|rrGcҽ7oYlmcy[_CwwC- ŽKHd,ϥ|ZX|ki[m^.T,#9RzͲkڧM 巹ԠM1<#} hT^FInYBGc`'GZ/tN}$k @nD9$`m!b |?.D򢎆0 U+='Ef[nC̤H'YGK|hmKlD#27v&qu?g0!60;׾Vn$I#20PN9 l-i (((((((((((((((((fotoxx-20.08/images/tiny_planet.jpg000066400000000000000000001750051362435004500173610ustar00rootroot00000000000000JFIFExifMM*V^(if%02310100Fotoxx:trim/rotate|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 600 1000 0 C     C   i" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? 5!-a":XsK屩GObҰm}xXktX.m}xᯁ?y7㽌d妘]Ya.%oE>\=kO?|I6 Gs;B6T,qW;{׬|B_o|K/Yi:EhC@S`O]uٯ%ONO+ۓnEe%I<)OA}b៉׵P:is]|$|jƨ8=+|-/N%߇/? <5Z܉.v.QrO 9+ox-["үhINۍK96 p*KVMk]<D|u0o7zn/իjW ^*$bK3,*^'9 I9c_>et ڴ~/׭ }byFmѐޠ',NȪjט>{K-F][Ki[FmQ*s1Ri?:Z|67qx26bgi"2ܝ2 q $xtJH$>~ǥ^%̥\HҷBFr: 4~j~.G-6 B &}pOOzH2Z>~5ψ~>~ J0z@%Rٟ(0^A9'eN7;hMsMMqiĒy+j>gopGnx{x.ߊ"Sl4y#l}]GNjwOVR hT<3RgV-qɦC+v_su#hƚ}?xOf|d#I8YǰCͦ$:boirL4V<'ȮN=b m#U95YS{q.:Sqq^F9$Q=+R&6Mya\=B5KvdA^-[\k{%8;v$ܕ׎mkHX6jS]8;׶浪 ӡA.9u3|1q;=SRWIWYqT` sOX>Iu=q{0KK+7UK<ҡ|iwg%)˔15<-^>ỨV$d(^?xVn.\HIM; 1ZsoX%<ةͻUnG^sᤘTsTFCe48u*[򳅏1&g/Zev}ĩ0 ~ \=bhx5b5SNFj?=b%4`[Fm?}5ྜྷg0#%#ֿ /* 3׭kC >,#P|zedk vg\Y,)J}jqXiQ¦Rp,O]nj3 bEX" wVۓT}s(-{WZ$hO, N]b[[䞕otѭU%vэ KP+OAkԙ4wվesıaXe;f]#]'QK{-06!&qjv\e Mx?fƿr FϷfGҽKFZE<7|Ye]Ƣ˅ՠm-e bi+R3 یΩ^YRXDoFY<3o%@ VAsYz^:X#-ӻP~A Lnv ~}Q8+˿im{NJ~_d~xrŝT2}n'&9iqBWOo?i_*_T>ω ]|\&EP@Zh=x[LgjKKE?|rވc7k x_ߨ^ŔCL/זEf|PhI~b?mɌl}ǽ} º}ߏjCIejO~\}w} "M.%XUGnkTin̔KC}s_]=œ-/{oֻ}G^QM WY6I\,*o'gPI8cv"Ҳ"u<]H?R<%DgOye멳tTTG%f|*L>_ \͜eFZ~|u NZ }pDzqr`iqe4Ua8OOxKwỻ髒@{IW̺w^gk5Gl\FQyUk [ ~|<2M]* CᎩiTon1#$[N `dtԞR/_!?x;)<˦]aвmǸ՝ǺC- M:,PvSH, Jt1~%ϊu\)&KG_\0\ҬY|/æ{?x0]Ɯʪs_twJ|ъ#xß_篗3nLW{LIlK)d;{m>6Qs_t-m_P.{8XXO^HmgIүbQkezl]I@.~TY|+,~ O'?֩X|RĜt{_MX4-]Rq]2UҮ%:'F]"?4Ct]߳6rO~OW?mq%.A~NM+]i$'Es ;)|x7G)p%Wr''Iܱ N+Û~X/nzf?iƝuj0ͳ<˹2-K9$)v[l||H_} U/)P7<ۑ58o8+C^+}I dAHuqh#,+ 1'W\е [j%-4j =qRcy12lqj:;E"'K,|n$)!3 $ӯqoi4z?*E"yR*7cS=i&_6vaКw٤@KFɴt5,1/QsGHg][bjh=i7"vV8dtYڨk=B+lZ׷Ӂ (,:`VpJҷӁ(ȆڴtHk^M/O^Q i%SʐxݕZ>Ñaim_.ifu n]{XԼ[XX,a+$vV'9=NejkT5PfEd|s385^F=Hk2=NOB<;o-cKH繚MV'<;=@\|N-~5O<-MJkrwGs` 9n7 :3^y7wVl˴ag'FOղ+[4(m65XL`:ν#%Z2=疲F036yZb`9?<jw6Ziܙ!r31[K%m~g6?\W7s bifidh6Jd6z4zغ|{#:r15+^VbVz[Qe1p>}k>6h {äłH%@NQﺌ,bڋh F"h; ۥy75-wS3xHMT ~c8֦ev)Fjn-GPg.Fvmi5x;Rcie ۑ}\ wɩ9O\D1$z׈%B׼F kk{x$d~o9=OzaW/VyS.ޣ7lommagw^$iK "808:k°P=4pdfE^ IKx~KM:RTyұYe\nhTϚ|w>#j>]YM+¹k;T$_+^Nq /Zt4ϸYE>aiKe.!3w.JTQZFWg|%Nqs;P7n<1@[h! dY6U1SsץĺfjMm$SGt8ʑ\qqַ5eEKG@3#SJ嵋'Ԥo))?yƇ%b' 5U@JtI {%C:i+N/'*@r 4k+ećv$9`3׾F$"]'u I^k=KzX.f/O_j)N3QokM;KF(9fv\̹٘8#^ ៉:=LhqV~:IVH;w8%w5*SlS4[٢v='f}j48 9t}ŤFQq+8Gt,C_US<Kop7)li=/C[$%eD񝵜Yi`;^=щyyyy+7Xvՙndoj~}{U-4xOwe&(!c\o;k1# zt2KXBOnvu:}*anA,8-rHY lF-)r@++ Q& G@y=tM:ɵPH*M&S[v:|^@kR91]6[a8;nO*MhiQeZ "A9+Ӵy&\rsf+DL~!iqJu5 22Sqr Xc {5* ˮ>^ G|@) X3N&e)8bӡZ9Lj:x*T?%Hʪ1ַ6ygQZ{^/%Ҷuh}LM&ox@Kzڶ}hseo2" ?wM[z¤k-ϯ__ԗ]g ?<1m+*?&}57Dß ]:p@g#ZZ)ǹr*?gҾU}JW, 0?C\XeCk.˛KxÓ >\'_ bKG*xvŃK J3k[ͦȖm|-08zIǘu}3S;mD,l6.3z$;όR:^pϛ3$]u&vMayWšjS֜27׽!DuOu~e6UC..TM̫v zfeh?np_LrAtA8 9JUiEɗJctk^ # [̌BsI~v0kMY"ڭy0\@k iSk4llE̋9lƫ3G=|=j+kFM{ڑ{kqD20$g9V_?,t'zr\jCm~As^rܓ?q^2Ư\6ZLbV4ȲUr LkKK->F$Li3<.[AU,~+^߁L}r21Z>m[2Y^h=<,\ H1 !؅Q¶~(I4ԾӮx@guaqq9k8*GjӹeKZXDLcym1?ki$|O=z,_x-1$~X9٭N Mk%.I!5q AKWIn=$ǥ}ͧxI{ E3p%^ӯ3k''$8S$RgPz)+aM kW:@[Mø$p⾗2+εᆯ>{=Sy<{;q9'9Z6[C~'Nvo ix.;uktg"~i B_~5 >/HmVKpx!8S6eGQ/jPD.Iwxt'еM?H :g cX,p;f[Z_kҿe[ZƱ{s\j~2)I0D#Iq|C#'$n kx;pMw$!H 6;:|+w=22typ;fdTyɨM׌MsN%Y6!AM3ş>8.zv;7]EI"sga9e_*$ 2;׳i>SN,cx°GERߊž|yOL4m.|lU/hz[i #6~ x{]қ ZZizs'v3;\J'׎x?x#QHִcvZTc Y-1ƕI?1޸#?\xÞntC]]"^X\]>>ҡԒ;I |:ů}P0e\l}99~|ox#^5mgҵxn[28d[{x_1򱐮{px/5>{[)<shAHkx I ceo?.m{ 5p"yK9ψk"/s=><_ o//m3CȖ{8B =SV,+?Zcx'lKB0r'_6X[ |Uo-|91F+z5U̪vPQ^1f+"TN9*ֱ4/0gHc#+ōO[cfG,{wj:/Cϫ:osaP{WRX:q]j6E<oQ^_ڧ/-#gA9m͜=8MRP=OӢ,cݓ<ȥp7ֳmbWj5ֈVy'liOW%$d9$ =OX$ālikYeQ_#VCj'fTwSJ 1t;F?ϵaGVNbP{7< `~GHYoӖՕ?=xpfk4}Pڣ̚<>pNzYw[1Y.xRv’ϩ[5xa6eް)w A~n2j(Z^Cw5ے"8%ۺ SȐ#vRe aRmKCu9Jk[ gqݯoʹǵr Ԧm#< ۸H#+OLk&hbbBЂIYfYZ bn\ngd`6 MSt]OҾ>">'N {poݶ`' 6b@|wǤxKV[&iF P 0acflM$ϢɣI#t?q %CUF?ҽ{힡- ,,/u1fLZm9$XiXvy+?RFa,/|Wqg2IZSq 䎃s(8΅k3|֒GO#2 3RB|&@7SoIMMk>gYq'k ڪʇ?JƼM !4$F rʤi3xť{ j'@<;WZi~U_o{[ 7'ߵuhoELಯ]O\D~-ҹ :kK&G|G}ͭαc`vؐ .N?QO=LӤ_J/!' qXגOesC1, U2 UaTﵓRKcXZ̭޺,nZd1u2 ⟈m6Jde ^a,󒨤QҤ繙LOCMGs4=lW?ʨ imi1Fkm$ҝ~o-q&* c$dx[2Y O&FۚGׇphW+q*Jb$ݏĿ*?gҾmuNv/ OW_To>ω e{wzY,'jQfB^Lp o}rٳ>^(Y 9/չ,t[|ZLn4Zti+-&W=_Ԛ-fx/xzNnq k'$S}=lumGUvNVN\WxW_[[iXY#$'}^K'J7#m;A 7ds\](jxL-J[ug[χZ^.3{? VR99G_ҡ^jԟdk"jlYcbgefH^t8!Ğӵɭs Nɮ[9FߔPǠ@s]KZC;pU+m?xwLm*sQUw_ڟ~kWڌ_x [f`6:+>$jo[xВ#0<)f`?W>+Iʅ.&ڤ^g} |64f<+i&זGu/Yof /s2WM.K}RI7d{;٤ y :WS ֧58FW粷e"#*7 57X.7$r9|^ Y 5hZܻܳo7OWc_7S jO۵d+zQZ>2!~ƛ~B,=HJ;=x#۶E]ake% e%q \(9y8gmkKY2I,GLA},5Cb**-t;qi>9n,Y\W![_v$88!0kem"|$&69aj%goABJ|vcO[vxI&֏gu9}n!I»J-a鴜mI֍ce$/=bhܐWvzbgyAa]]e2/*2Ș#~S~7tjlql<OWVwC%]Qk tIua`b ̓ퟻ7E'FkSWA ttta_B5')& 9z+ 2[ۇ%c 1 x_=J^]%| iQR\щnR@u5xk[Jռ;-cHHɟ.d*Β#l`ftorֿ6k|TӵEYdB8bi6n!'W+>7O^wu6^vH$2&g~I'y/M_?;x{NVk}v-RS\vX`l4i1ld 4hvzpڮȮיC\8#X_ExͲS< >GQ``dwZl?mAKy]5LgDf;8p.yP3oi!E'&O|)1ehavP?g(I_?S%. ?k~A =."Mu3?"Z/У-vP?g(I_?S%. ?k~A =."Mu3?"Z/У-vP?g(I_?S%. ?k~Uǚ-~d^K9W'$WCY K#;+^; A0#_0n𖏮S֞^V7~03_Oh Y/Ĉ=;~:pwNĴ[߄ZwF6ְgmQo%x\n# נAq-*(ác@4io1$3]+V?hу>n;6ڌ~fLH85:锣IV\_Pk~D ?nr+6r~6"MM̯I%WF*NWNn>{F5֒E#BA22d_\3U<9pUmݘuIB*%9Rv8ЫHGڭEw! :o*" N198Ac)E-,cθ)hOVgg.stwcfe!O9^[G#k]cVycki`,q!qyI©ʑ qOE;FIJ^I}ӣͼ")qdgV}k2=\D3>Hdb9*WⶵGcO˧[oyБ //<3Ә%à\sa55vV`˕e8ԌNIFNg#EL7xixI!b#p9 NL/yiuA0E-O9bNE||a}oX,ۃ=d6۞{$0@UOV ;U#3仂N z zV?B߉[*3MD1;=r3UխŌ (].IE|Kkvkٿ Psa~I5~>&Ȟdsn.ltӽMCI侾XDPmV2T:Zڊb1@HNJ6~>--Q亳c,wfVh%pkg_jie_ڇEJƛ+v>4!kY&G/ qGQЃӚ|BC'tsF)B裁Ж3Z9`{26״ '<c }/Ʀ3*U8]_i%gpݏo\^+[/a9̦02ÀIc-K>Ϙ\8PI5,ʫm:_=ߩWi'ρ5I؁y ,U pG$W%$H~k}lQdOL{ffUI2Q=늽g:cF;8/v>oӚ>x^8ѵ>氝$`?:0A>QYCOXRM>V٣P)AET,i/֍k/tfy픪 m, Y9/WſH4y.n# =д\z]1x<_k~'4 ^Iy5g{U>8\6Pnv 1סxkf~{Ѿb;-,dc B=GrYy`ō3nʕGˋ>wv*OdcqW|,Ox^KyǣCeh+,FtcAğY{~*5 T+öS=A>8LA"YK>@q;Wf.S2W*|0^gHk拤i_ZZEƐMn>PCp .kzt{7oKms}w #r02o⇌ZhpjpZZkZV1k)XGp9;,?fH`+!IAF +HdX1 '݇2Gxr xbP&Y+!meHùMxW??GF#l lcq{4ru&FNA+݀1a⹏kڎo yP]I} ,FH?w5py3M,JWcm,![cԑ-aJ'FH5~Ů|JԬV'{{J+fA܅j``,OBX࿍}iV:|B`UXa$\J>Y[xD[ϰ*쿷"h&)4c-bd[=ܼ+i޺=}[n2\z WR+Zò +f0T1>CRV_ GsnNUJ?^k/= /OWMmmMJ(8? ߆@'?Y]'WG=>}Αl ඛf3g5/HYmE#uFK繯>7FĿU\[M]6p\閊\W>-YF Udg$ը'~Zyky|շF4Q3 NZt_L`ϫ$TER;;WXN΍ MNHd"2Ai9[[NúT2\HN& N^VY'W.:k F{Q$sW,˃_(_K}a;Xj%Ȉc ~uxJy潚Zύ^8ݘ=Z|GX*g{κͷ0~,A>%ΪnUɑb6 $ޱ4 d6pW$t$ֺ]âi'wCZfsM Ǒ=7cҮ{m{Ns1qOSWY 8,;*o:' ׿|iӪܒwR87x^tk3;3J<]χ׼Su ̦M)Gydm| }#q_ZRgKss$rG"%rI6>Mj[WA d%ۣrzZ.ZGkʔ#uIm]1 9$__={t>5Q"#o"5 9*A~|Q Ybki%e2ZKB?{=k̭><jQ^\ELY>d@쫓ckS,tm2 Z3웾҄o|r2#|bMxLY)Z"]ҀHgkZq}WRM4ZxyX`sGz|hDŽíc` 9}]smPSOmYNOjzt+q#8E")0C5֙"],qNxDI\5qZ ;g~gzJ?x<6֞CTyþ'kSxXݭEimuxT |υ ?xGԭ *j Tlz8ܚv'gGtoW")`C~W,ՙ\ᶕ>yj'sjp*[ b&`|8Qo/k.F ^' @1APa&kߍZZTH`:@L/75ߊ5xX`uT61Icjg+^uwK;+g(D#yb|6x:LJ|bŏygmqse )ieYIIc6I]kC)*vK_S۾ i KMMns͞8qp w{H'0$y%0KIz? wFlU':;+j2j6DXJI+wq;׏J׾z?%ЭNjV_~ LZ$@iǡwQ𖍥YAi۫mVRjk8|iiڠ 72&Hzd :b>=L,uskp:I,ȝ l8=qgRyj{kGS$Jk|>8|ңkN*.6^3ZY}3P?q lld(rsՏ5~A5Ʃs<"Gk!n70I}xjfZ0MeWial##Nޞk 3ZZL;c g׾Ehjܘ?8%]J34m4rrmc=VlAuaTG+6?ߥSbsn8 G?]p6-f1lBnjMX^R>]~t;3>r?zqզYDh@`"sdO5tyݴjN^1<`G\!]L;_Y yc'B\>ǰVP{F7 ʱSgY1vԴFyPngr Q$'V. YodkeE\s1:Jc6HaF@>W֟jFŇH8)Y=Vp.=1( /N{ӵy-.3å># n_+,;ߝp sxom>Iotu*{K`R8hA}zu`9E៊4x_ Moi9cG ʹq#⵰"]qqqjS>cw,ʫӰPi? xL4`Ԥ:ucom8^C>--VO3]\Ka ҕP$ۆbۅ,kʥUeT8z0U^tZwThPZ4#iT:ֺFi/[]4p(.I|s9<&/&Yu16ZS 7RsG5x+~(vzjSF.N[95_ /R{}ǟ e'Q 6}} 铴"db=sH`<-x~'ɰk3${ awg$;y= ?|)edE{Vt w J|7Zk%m0<~g#fTNds)#9>O-pvz/ey 2ɵyF2]qW5kQQm&&ucc8n{حMF҄W(W Ǿ;?Üq5*Sn&Ti-N>XaZ,R$8k5vokv>[TX EY2bG s@;+z mGMdHGPgv_#K)NQNOxgzŜ'pxSn9=s֭­B\쾉F8ʰ[UҮ縂5 *ըIgajAC3$PG᦮ݯڞL,UvOlVFrѐI5A/5MҲG&!L2 wdtZ_ŭODvA_'SXdu$Fiʐ@⼧ᧈ&֮.Bm{ռ AO+i+uqs:Mm>JE>:EwY_ .{|T 2%@8_F1ծEl-~jx^tOq[H&FcRs}+;1cO9+5ٻOrmt5fP+Bޝmt˖u82ʾ6Q?Yw S6B߽yB+|?qmo = QBt-2sWן |i>q{'5q<۝P׼^h| @f+_Q#I±83)}kĒco#ҧE?S^,7:J/J;8H>ץdk[C=0G\~a_Fюrzf4ZG+"LǶO_߶o|K<9c;šhS:uٽpP?za匫ȶ[7^ԟ&defxexsm^Vt_V_KTL!G|Xɮ]cᧉ"ᢑ b4yw_OS'|gK65~xwMxZVqc<'; àec_1p|)񭇇fDB* w >*cLuOH~Y`O)n0>5X-aFk;Ȕ&L3 c;kwbӌ(o4գou Z(A*B8s֭xcZdfׇ|)f% 8 YTr|~8m,azNLaL$w_<]xSgX9 dROAk%FM=,zz B&?4z=c<[m. m z,/m]eN!q\FC4k%IRL X/2z0x㰯[--EқaS >^\dWXJεMG؊JYAo X0D^w$W>ݟg?x׊}T3;[v# qνSSvhX,ol$kյNc|92֋$m4sdᕡUꂾȯˏ2|`yW 2~_'?~bS*yOJ umixvTy|潗Mi3Ŋs߽vԑ$LXdFGz #U~ڗ~ӗkxzmm, حx.mo +LA@ 1B7=Fׁ4[x?E&VN;1ߌ׽G{ Է?k6%yqgmZ-iV.om!DsTP08>hV|QZŧ^i$wv̪>V!rTFgo!]EجH SRYn(#}hfC ‘xj 2Ao cU Qu~'(HOR<\[Yx=< sa ;bSHMt>𽧅٥Y.eݏ~Pu~'(HOP ֑#_?@V'ZG"NEoXiߺ ?:?uE`u~'(HOP3$E.S\d ? B5q[Z7#]@ .Zɭf D#!8΁ ?_OwVUQq1K?i?u X[8I1_U3-lv-Jh/Xe-p Gצ8SU.Z&yTn8^%|ܳl'W|x?yWrDVViZگ/ii:"-:J) mǖwrK GI.{@hC" 1N:!^gbͰU\gg߽tz3Ù xC S܌}-N{ұ:^YlW2{}bVIvrm%ql8w|1g.P[T'i*@f;VuG"RRx Hd 71eQs+*rj[\ZcJ4akbӷ,G60S`Rn-(˵@<h hP꺕 ҋn{䁁־y.G fUX=A#5}M]ϛ_.-5R ]r@'=\̫svUy8 8ҽ!9-(݋b1 ˤ 4Vdb(vwГ.kDFг*o&FCws߈o{Sb >c8w:֣i k$XS]xͭ,G׿֝<p2֯5E LsWΕ Q漑(Ca@Xךhͱ`Uל#?Zg0KoNP']li&1\19ڰ8&TUާ!m4o+QWVZ5jsH _bH3;/^Rk*GS)r1)M]O}"K|ϱGDİ;~B|t6ܠƺ=N";ʖd:M$#/AG+-oL;jHs0?AgM(B|ΠfOю9cXP&U𮥪9b]V?ki$\/~8+V$FMu 5Aۋc-c`@:3t/F̴TBq`Fyk)U7.nx~;b8l09i6'2(q^;I9+oN:ןxږjی0f#^*󧦛aමkk1_zK4nxB ^PA;|`nym}IJ%DcrKp25 OPᶹ!yA 3 9#i%å{|Eۋ8c7sfa(J09d=jyXF3uz4=R 8&-A|-]Y =M?wvi][Kf4L~ok F4Yn/[hÀV \Ms׹;K%KO6L-w38BqHTUNRN=?o Imj wL J!'<+<-auKsmbHS$R@&]Hp9OJ>/jk66s]7Ouȕ_ʬv <'Ui}FIn[h6T> 0c+wƤG}L}kH2Q$uz+R>ZOr eMԬ~ txIo߲D 1@AXn=r8uWS#r\Yd Fsqǵ{\<ҏDtyӦFQW6y0]SN,mUG=+0\?596I|è?B1^)Aj7Uj9JAt&+Pǩ*f Z9Vr78H"zB3>|m \ţkHY t-k/wپ,Iڔb#LRyoKQr @E|j&ּ?xW꒾ǏԳM1!gSYU`>u;J!u8<W*Kvv5zv޼oK:wcYW's㟘ZrE,%GgOJ&TyݮlZ@d3{&I4cŪEa%H9S)pӓElv9ܓ>r Y}??&/~őc0;|XXx{ź&xo^HP!S'Wִt=3iuډH" tIO=qҽg˥ioaPTE\#+RS'IAQWn<1M% V_EU.>cAlW)nuˤ otI v|`EX]VщJ,%F89gĪubtsѥZilϻ|l#uO1<`6+9-# #6o6M, D Av)Fb4,9l⾧/Oב{y۹SZWa/ B?+ax"y^Q ȡF3O>;Y#?aü@W/@k Hi#6\!GcKAv__ MKHʼ ˂}+¿)e>%ws's;.Wqcժo}F)'biu@䁒3_<Ch~"ee{#ǒ{guO5䖷\GYO\:^_V~ϊ|Kkk.62gA-x%GLOxVT'WXdKlaJ/zyrG<[t:7Ovhφ&ڭuy}&20K0bOqgO:Ιg˭蚩_Hn.!Y jwX o<-uCǂ&`h];3T dI8ű/Ӣ|BXYaO,2@X>[ ڸ# #z(#wpH5GFɴ-'$O ~hp+‚sֺd߅ *qizR@/G"Z/j斀2e~(KEU_j)@/G"Z&ʲ ߋ4KZV7,mgB>du0Eߕ$`sH D_Y߅ ?e~+Z^jVy]]Cln%&'!苓4K-V_D_Y߅ Gd%*/Q@/ZPApfpA=+^(? B5#z^i }^ݠ&kw.Oǰxs:߈5`H}9eR:b1|sw;0v|O=Ҽvf,HsX,䅤GvicpH<ҽ5gOyסϿopZIv^l[]P*g5ҬΑ:>:7vH U#^j- *qr8,cWz^&ӷ%G?Gt $Ư'.kq=q?o#R5̖ǩC ;qg'N nMsꖶl K8 wLdu]zH>"=J{$-E I53|4o/ZfMw6b9 wz> ƙ\% ^gٌ+ (b)SǍ*2s+o/+YĢ93\φ0/4yk|3yk<_|) P+LckbAq٥k/ڧO+إxZ/ ӽA[ >(am C; 8=뇳{imB|?b=fen{u||.-2;kX. v $sr+a4**;ODxٜFQ#\!-/峻#BӸQ<&ܙfp+烁\VFنMEeO?bGpy'tlwak*rlߜIvz0;EDý ukۓ ,!S'w=k:DAmb5 \;$`9ȯGtTKlG ,.fx屎]r7 8Xc0 =r=ER1hdΖ;ِN0;{GA,c U1Y q:qCSJOqi1C(~gB隆܋`ܕM//vAcsȐ RD%B'ǧ10j d<+d=H[G'>|Nck/%$8:rqPxRkSۀ^? FN2X݌8^A O^#Gm58`a/ J(gҶ攥9AˢOi/!aّ1O#9$9x[׶Os7,Zfp{9헄<%KZimm`4&8!@p9|L>ײEghdB>dN+ 8tx򌹕g[w~h?ō#G1tc :?#mheW}CTՠHmNKy42@y=q_5 ;[6O,(#R}9?-mV7X\ExO-l*rc+N/@"[TT38ULdx'o?<_ez/ͦ3_BeB$˓sְ?GsˬeXKzTΐH@XHzU4../!.~RGOq'J4֦4Y:ocF׃lblm"Ck ̲&q;O}zWFcOEF$dBO^1ӊmNm#GiI<08l8瞠ӚPX\ t1+!##^~_^{FmJE'ȟ+dW}]I2$|^S̥A61[եߛ ;b[{K5V z8J.zm.FrSVGxL:[A<@2H ac##!}~?F?-p` <#3`zž(&Bԅ72[^J.$nr(TfH$)$lF@3Z_}wDY)9|}D>WB1$")*AEXUgמ$]SPkDh-.&hL+}*de+g -/YZxJfnOI9A#@8|d18tl1bHznw5 _隝#GB*f t$<?T{Junx~(j>!PReIaVY !=?EbmRgcefO ppHn;xW}gOӵ:A״3gv?W>/=ɧ,s#k!1/Eq Eխ4쭢Ӆ-<[}*{zGukf̚DI,ck6F}Ϩa\EùQR~zZe[Fy'-J|Ydg+}g B>7.2~+[Y74\m μ@8#}ļIͮYm#g0F%`t#_2ݸ4}G=so|?j1i6 u+;#xgσAM;Y.&@"N8ڿzȹoc DjDA^_*jyG~::3扵,{g7~xg}Ď,Au<O~Eq|MoqkڴHDvى9y2{j~Iwsc<犋 bvZdDb@F'Cc8(}(w huD.r.G[ON KX 3-;|#9"5Oǟ'RGҢ(3<Wy;v+Ye<>љKrJIn*)shcHc%y7'=xv8$}<7wm#-=5|n'[j~R.Q 0$ |O#m6])$WQvsk4F=gLcd8 0ǖz2}!%(c)>i6x1NsjjnnLi IO濟x7hĎr0?¿eڌg8]R(F\˺,rIdH‚N{XW~$[_޹.sxU6O$a GMn\Qzgs9#ɋm$ No{-6? _OΥ\ Ҕa;(= >W|:|՚{T#hX LV#vBjܮ#ogypnNf9ךt/X<4 r$uq_)n|=ڿ_.bdO2爂hnq>ƣc7dso )KVuA]>4}--&Ds=;özֱli^/ѵMRBҖM uWx+seo KgikL\ui7'[V Ѷwڷ^ ּC_XkѴwWzB-i*Mǚx&Rt}/>}YjK(Qbau42OäxJ\|#}m{I+$pBy9^ӿg#_Wjz Ww$޵Iv(x VmgtMW~9-KLΰ'C 4KTI1_o'/|[Zݣ^O4,?9E9$c~::_4k!!ЗV#me2!X% S:xľ A4(Yrv<⹣1FOᩮ5T:כPePQt(d nnvW)|xztշN 8a$F CW|p|=^$Mֳ<ms پp i M6['|Sɯ f__j% $d kK}9.Y#3fn ,'84րcRr\ڮ̶nr]B7,Ѯ9#{%q ~ ]k߱jzڡSZ?U⿅ƻG6}qZ=w"2ǾMd^HbHoZe/.qrUR Դh[l`Lʃ޽Q}k\֥q-tZ@`hY=߽c6Ak/-#=8- ѹXm @\g,G'}bZ4[75=_.osZixn愱aFV+dWu^85a]CIkfJn60B#Wk/ >2.uևpu4F?.C9VGW[ඏV.j:Ωx徟X7. ~Xpr 'PUrwJM$1EIga&k/ߴoG\Qi? B5yk|f ėrC$B >` q>ּ<\x_U3ƥf97|_UOj]`Qg'6Ҷ>yr+̼W2m'PkXHAUREoflvr?UZ8rJsޱ= TgOfr[Z/6d>e;͸6$qɨ|-KxCn10 `z%-,{` 8{>`^D᧐HSdjx +q5ǏLd'CO^EقSYčO,)rrrCojQ;bK~W-};zDW 4H$}Y}8U_ -IZQ>( Ѩf6d|@+P Mխǹ^60J9&Ծ%{G&Kq&eDQ[<}&~.E#n=>l+Dm"|>-,˨_FDm}Q ϒ:zZ>lWP,rcq[5s}YRQiHc6'o}ijW<'olWNFVFn 8/': xcakW>\~ >R^;TH+01^3_.E5޻ymkA4pqxgב׾1>}>2?Ү!'lYɿwz#~Cz٣wXVHQ nڋx\M,*'rJI^zWȾ&1+I.'L('{`rtah$L;9 `WڼGDܻJS! !ق sS+uGY.-)ccC1ԯ6Gό_c] Ri#F9ScĖZX^* FIyFX0H_a^hw8l]rll淩h+$aާQe -%j@˕+ю:g![vK*dٰ('뎸5is*eNFwW-ydFX0\prjKg7iiimr>uěqN:ĭ\9dCKmѐHy$zϜlfa!U¶8sRDN 3yjP^E'f_hM]l32Fqc{WBn?fD#vvkT(L6r nb8$i+\Ve,d6dcB1j+qm,eNce c=OnY_Ό-77LLb\ ofi)0 y cu;^45\Go:bU ;AUyRxF%E`g8%UF3zoT`Tlˌ2erH뜞92Yċcywkkv&7Dm HF23VwrYe Img$xUv(݃*IO6o岞K$F ok]F;C+g'㓁[#`otd.3s^:`pj.Mov&Mŀ7P8EYt?e˱L+rG#NrjKk[MnmBF \rxM[^ GKll7MK,QR-(1Vaӯy`VAw1!d r :8|\jFټV6}u$[55W$) _lsـͻӬ53ڏlm.X Aʧsh#uMҦr# 18 gΩev_IpJM!e Urw )YKts^!Cy\ض,8;8+xIř9<}OkܥЭĿfpFqNIp+&패v:[`-Zd>Z*02I8kKBkZʅUgs96lS\Σ{d`A+˹=y<`Z2~fo#<Qծ/"6E!P$8w q!7l]#Ycj!>\(arpq`~|=C˷[[Ɲ;6; Fj<%MQ/ePX9T1ϗ.0I^䞝+tS*zҍS4+BIqo偐pW A$=I?ݾ}GX[*EN1ĕ ]#95ωOM[]]c$0ߴM"KgIb ARp =xzLjma#)}E;XIcx\7g=IZMӯ%nPN62 pG"ҕ43WtMV;s!F2c pI'I'կRbqwdyw-rKϟ!q{,"Ox|=V[ϓ뽼qz=+ _© f8K7B{uǯr֐oGHP#I0{vt,G*9兯'^hJ* y2^#<*=# }s'_OsogdX ;d^ڗM֬:k+Q9AIrKf;{4g첃_B70>J*spgT?V9_gUsyc"T?"5~S^ pvIcBj_ KeX4v< &Rhk CDq-|o|g.b{ >lj^=[H_ 6w #=GZen|=wc<6,kǿhğXȺE,OֲtԙJc6Uă<2̿Ҽɑ"}~!{#Omu;㔜1:=OMI74$.BI8&Mi: G*/<8SSUR$w q $ӛhއ J,[I(th!^uJ\=M645K4#sB샞 q T2Hy$ڴ n-20$F?Aֹ [X݌ ,JzyTݰ^,I5PyO5cOֵ Z -9TW(;x]\YsI 0\̀} =jYɉʹ;" yONvga}*K QVP9݇q~Zx"KZY&ԤQ6fQ?u:O7tm)-ѯoqG\ݫ|S)fS,&*Vߪ>3̽~?j+9-IW` G,Gqߚ1IkP]Qʫu9❮|7ԚcV=W 5үc>gk̿SLkp# 3ɩQYvo1t 'iϥ"6}- \ch)9KH62 \dTͿK?Em[A*^e??Əe?ƀ'oNI} a;6Sez8~̜~Zx-/Pu|Aߙ€ :yЍ|5K>J}֤$!a9<~#+]A0#_^?4} HQ4+k{vݻ#~Yo .g~+ri##inL]/aO<q5Y w⼞?h[}N[xDDd|$_Lq],8o* 9>uf{[(Gx n-c~ǧjKrYh-"@1]r~xw2,K$Ktprz>Տ Gks$d`go+^F\wVdb^ |A8=94kviZl1luPyں6m6n?^xg7 (vaA\Xc!'9fGQE}_ֿe>'{;*@#;wk KD l eI"d z⼒ڡSӠ]$Kx5sEA)\Զ} ?ƉWޚZ-̄xٌ hW>.ď}.ϩ;Fq#$;C)>q9Z x{Iأ}d'pq+&ͯ4vo{QԄIgynq-&,d~uzvƫu[(*XJuWT xk;S`n?7z+Uadc#!U *n$}ῌͨ,}68#,1ɞł#yuu -5awm03ya3o@Ob8tZ=Ze1!,[0諒FG ukZWvxe#+:sO 3nV˔Rt9Htf(6[#cF.8s\n <2k[T\q<RYZFG2:#Im/NӗMJe $;#zַt4}RӖQ'o.L&s9a;Uqj0K?X+n.Dž.9{TK*OwS P#7d>b2+wSHaFUpq5} ȱ_4KV:dBR$pVfQu 9ew"GWQW |Nׯe]\-,j^oՄ)a ߕEFz7=}1BLEV͍~e'J&eXYQ$Β4gAP8;G9ƴZ䳰}+rN8bk5m{XCs},x8}a0)7}J]OuԼoxGWy+U=N-ŠחpOk+&Q 9Zck@mOX߂zw5q]AռH&RI׉3%`A{dg8~ͽ9Pm1g,R@%܁Ѹg{] L3#*WOGSgIsTn /W+!6jٷ̈CH~b]}O;Î'mѵGSI$`'Ҫ"K&M92416|̰$&).,5m-a.f2PN92@;oa+ˤcynpxUҵ:p9& NF[9$S,VVGWRi܃t?lg 3''#r;qߞ9t-akD%WA$(e1l`H'N2kk܆sBբil:%`0UKY$ +.3him.[ .wv c'$E_ܒ[r$'$ vm6@©p?yIgt^o5p{T۽0)p7|bZ;w:.1b#ʯ#>l 6kOTYWKHax}(rd:70;0pئBHm,Z> 1#{2ԨݝP6y.[vVήڤVs,HC9-9?68VQТCg.k9 e P %kis¶p1~wM< F.F2.Y/{o,SC)+͸$"Reawqכ RRw6ad*铁1l56FA걢JBi'$\AA"ƐڴI@v6N@p <'B݂1Q/WЩow{qb/*L+y3WECu%X0)$fݜqycYx @ ӾsdWzQ\`J08rA< tD\2w FU\`1\(Yh\AWh:c=sDTbLjɝW™.uZYIi`QNm$`UA8SѴ|$ah,c̈ade@<-GOhE * 2׀kD읛V{=GkRX >,:`c%wfUIh|\rZ<0HY@ ``dW῁R!_]weӥcN?jŨh-Gnc>!=a $Up9=J-[ڿ!{X!"I`WwafGLaw}rr2[; F7Oki)߂ mot9UhdB9*rr>ޅ[廉"3V ~=|7$HupfG\"]Le;þ"e + ޣu [Da~Fw_tKë[݋=vF G^+дovWacx┮Tws*e-lyį5 %b,sEF;)㧥|5EwE4IܓyPq~k֝{a}/6l`:fKѴ+=oKLLI-y<&ՙ\QI R؁ɭe64R)TL ~]kK`"f&Bp8l*ߩcq،c;j5&=Jd׶䢅3swwY&x:tmQ ހR*xda]~.\j\y15]Tk +qx^V[#ޣK٭w=WN7c_g'Oo=KN袊 ( ( ( (1f3tӄ-)բ=B+'p(Mk%5[xZK(IC^?|0{:=ެd1]6+oX`UbO90-[WoK߅"Ϡh4_?i%ѮvFڡw+y|(lq]Wğ#_X<qm;TK>7x'ǽwkx>Ö0^JͥTY\$@93o|,hO`6%|˴@P$`F x>Si&Kk;x㷸A_Ϗ>0^e=F)lY4hE7%Ot@p ,pvۇuֱZH铘3j(WP:6U@ڹ"u o %6M_2"ǫހ| :2ߟ^kxNt-1=ꐤ,27SO/檡|0yi]O"BqݘՐ0mx=Ey4_EE]RkLk 0gI"@l8+{Ћǐgnk9o|+ykxN{gQ؆/Ʈv<54|LSWtx^m5j n+-IٞwpJEz>Q"~-+$Q-L.zyz;˿~03[O|*uK YSx~5+C#ۋ+*CϽyj^ w~ lvyY~d'JXԟJX2]"f'ݵi8$.C1wh:-}}1X Y B6$ ŋOy^gi5fi3Hԩ@vA>5.s.+}KQxL@dBAsڼ鑕VY/9U(%Pĝ̍Cnwv=:*P5qoyK8Bܨn=6+ջI,c{{k2VsA9l'gS0+SXjJy^S,v#78'8QG[-^2UW\}N3ih&i;KΥPJ< 9=E{{e[m֥vn8RK*q|#$4\ErMNLY,4VL6pB%.q\ou=cU/E۽|ѵ]O^V97;$}>+Pa|y grX=]f COM<X Ǣk[E/A/` tme~Sjkcx+o%y@*6Cdm*1egfks};Ku[oKv8+vy8#w5)\۽E;YMW.KT':i13OmCRied2T,@ >\="5+@]Ep5"G*;qO]Q9# WQF@`~^6r H&%" K#YX2p,P} E%9C#>b 1QgƹmҽM2mD˴X1kwpIY HRO3DVvn`m2=@W3q,`$99yԞ, Mhgr LNUT(Xckn a6s~W1LX=ylwG?Y.^T,H8=#N;{WeῈ7oXGn'RwҜY=OLχmnlt2Cm br#AU'ƆX7pyc%_m0`6H=Yc,yh- !Pv dbjVL}*h3+x,+W$'=ypqt׷:ci-$p2p }-i | ͌dn=Fn$q_X|F(%?&"y$VA.8N޾ q%myB$9U3ty2OnWЍA|Mg+Z '7kk jz̖8x0 T>l]`Z,4[\7Gۥ}$Ԭlшt`(G>WH85_ ?k/1Ӣў ren드jmeO֟@)$v*5`$8zi1. I cq xVR}t*TlEMo_@j/Ư.02ehXx־v'/ih/Pǻ}6Ӗ% \NMG[wg[Ⱥײ5먞{lھ\~oֲA_$V]kUS+{#G}q;?^/o<[aWJ襇㿉wx%gǰkZ]JY$c &$i$vF9,$e|'?Q`E?߿z(z?~QE?8b.O{?EXO{?G"^("^E?߿Q`E?߿'"z(lZ}87۟I?ּs{ka4U۸ ](#=hjX(VՎaI\pWśIshVo4Rc`A TH3Q^6}/ο,W ?Im=-Ca<8dc3ꉨ1QZ<>\qz䢏l_ٲb_,ͻ@8gۊԿdFGh-v3u'ފ(Y6k_Xux1 4L#ǡ=s,N]<?qNhW_̱Oy y4*>"@:cnb O!lPOA99&)?K >oe8؋/=+3n4kFܦ ?..83E,P_/Cs$EAPW$#2nt2 kY|vg`C(:0@9Y֊)fa?<_ KSH63SfT/M ߭V?`*.XpYy9׎MT,?a9t;P3=<*]/q9Lzƅ8\薫_B=zF\L 1'Cqrz9c:}: QW_c/W ?V؃;y<6t:;-39g$iG{lOێQE-?/u0qtAvBtYqç,aPrryRyf8i_F!YKwvU^,1™611Qc-iN\Fe`nOq/^nHVX|y;έg寚`w|9r3fEy'(n,PcAchʰkh~/G#cO.PS5J,vX gxNUPuMPn17!u'syy,I'#(T?aoMwEdLm $V|a7|3ia/ZT jZ{#C9 ߩ#(+~nM}_Kks *'g&WHs1eoe}s;[S" ds y qQWOib_W 4 ^em>΢BEj0 R=O& 2_+XA$D| ?\=Ugῗ8OR=ωr (_9'QBʰkh~/?ȚH|$ѲWHߎkR%X{mB ݥ=xZ(w_3nV̗.-)ٹ8I߹e[cz_$j1yFM3ݎ8z( 6Ntcgg=lm|Dy*}_Go/K?Wߵ&[s*lSh??[???[?(cU-OcU-Oȏimz'ÿ%?ac'yif:AqjXdd(>govG}KPWZվdj_\5ęnnͤcEo ),T=џ'WEV ,Q@'tQEm#?RbM]V2,*[Z)|2+Ů ^Fx;H"n v=8MVs֞qN:r/5i -'$ m7cU-O6qV?qV?(lShlSh:FU/[VZؿy_/nݴ9_TQ@fotoxx-20.08/images/tools.png000066400000000000000000000127461362435004500162010ustar00rootroot00000000000000PNG  IHDR@@iqIDATxZ xTU>^Ie_ YX d !{B@ADulGnuf[Dn[ݶ:PLHXCXC![%J%朗JBHf}}{9=] vmn#pFf8G(1 3fysRjYtOLpƽնoz3 y6/b٬f 1 _xI(((N_j;,-AnQ;T/*XSDHd?'U6yLLc_@uuMlDH7̎/ܣx[o[(.>\~!\`}@x_V=6 'vIH03+ )8[',X_ʫ/%ԋgۭ]Q12/8o)iBLlD).͚38C3w>坍o[q˺jމ ĸ9uE7cJP+TAb&$'p/o/ywnkkk9~x 7hht ƆE'W9 dRFm@߇Z]T"9u ﷜?FBH~~AS~.dX3߼K@l H"**j2+mcپ._.Cɧ---׋-f낼C% ú^zBsY1/gCCBHuU$%RqI}M&=3g@Z^fkw!C"%>:6%`h;R!'稥f0g۶8eɯt jYEnn R .IY*Ieۭ%:GӦSw:9{w%^m۶÷@Kj$q Olg!}w tveeԹuB| i{PxjS[S'eYvro!e oG^#=H^]K37dv.&\yE)ZcPX ᑾ}$l6.c9C1hdKR 8'  yi!Jx{wx8 ?)$jjݓ]F(_oB ~b?a|]]]Y$<È'L`wx޾camwfqgعkMM$A#޽KM q:5nӦqVe 2 ѩ\ tn0͡/+g OqS;|~Ʒzx8ꆫA4i"SSwLh^FD2=BMu\'wPWS(--_:"#0fLKY``޴|Dja2~7=5?WR&p]b;dP7#Y{VYd1L %ڵt۞8q2h/_3mZTt|ԄT̛7o'E 1rkLXnТVHO+`/)apiXYXqr5k&dgg3g -=Z3/r8f͒J4L )<GF5 Wn{HY& ς46BlqQH:A,I$eNjN >zPzMD^N#O^ f9ўޙ=&ݕ!3r| 8i1;҃q?7 >ݱ bqBĬGq (&-M",;sׄԄF:Ej+hiC|Xf)+i0h?Tb(qV2{ϜCn^a4"6D9{&B~A.[,}ZSY0b'J:Eb< gюur <(J%gҞ(> z@ @ Lܼlxfs&\T1j JpOF H<%qbR"o@ɡ!ŵu p4zk^`y$&AИ8ׯ5Q޳# ` bT]o>21xgw5…&pAР&9-0Ǝ Bs̚˕#-EޙvSy(Gr8z"s j`j49c:M&q*D̂A.WL sH|L14j`qݰu g<6?%bju4,:~* s]b,'o`hlv<%X `0;dUjms2DQwO%>> dAo0V-Zd4ԅ7!Yvk'-ćȊ x)Ba޼򄌌_➘YsjðPy1< ?xD58I*I3uw DYaV-

    a3lw@IT,=gM ].40a= `!x88}LwwR2 Ͷl6g| 0@(wP`N@!/A PÒ^ӿIϒ(m! j.sGyЅΰԕ½@<Tξo엖&CM0gXd< U^@(z (B~Cy %{Ӛ0(w`E;Db\3Q3w'/ڵp|f̌wNm Y_s!lfMxzDB n _{?0x\v ",wԯGw`AT]3$c@3ۡ躮sBUuӫ5L\A>EJ%UyDeUW.PBaSpQ"FɁmoQ3,xC 1dJմ?՞Z+~=̓@=/ugDGGC׹W!!au<eh ec>ׄaÀ@.QȗW3m>[7C8μ{"гok;Pإ?nlՠ\J"MpsS+ 2i,f_| G I%>mK(JJwxWfv᧣ zW&t"M935W\1۪E?pqo`v+q5&ڀmCi~:0{M |gk#LI?]xf^dÚyG^ }W7"1 ]rcp0+ rsro)1”rf۽| ?wVJ˝_Z|8μ#ZymMw_losRlww=}&O&Ov7xv>hq@xeEw~btOw7`𯱳? UN~]M0eF$̌^ ?yˬooX mIFKi܀/'mk C-z-4$~`іfs g}TGXo݌XʵB{PW^ՋP S"`%sde5?9#[8}d d^16.RY!V0"p(]<]Hxd1oz#xp̅=XjrYyhHDFFX"np 8u۵b6j:gg#܁l"At7h43hm-['?V.S2^SN:;:G-0`@[m@DD]ȩ,FY8y4v{A=xdbU,QGbe' D}Bc4K$}q73w9zh%g^]~3O9W)tK ﻥqGd6ˤk܌3 V|n,wq>,GP*]j ܙ&(6L <μP}vGBD\`r1%= +ˆ$86^϶TQ6fp+KYpn!r5OBH3#\ztbtO; [2ˮnXkS'B4]F.}Ϡπh8X98(aw6!M?W,]U&7^G cIkeO+jiTXtXML:com.adobe.xmp 64 64 ^sIENDB`fotoxx-20.08/images/top-panel.png000066400000000000000000000422451362435004500167350ustar00rootroot00000000000000PNG  IHDRb.QtEXtSoftwaregnome-screenshot>`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }Zǀ2iTXtXML:com.adobe.xmp 876 1239 0  IDATx}`UX:`AݡR6*懢R * (*]d#Fouo_y /o=q}ιG6i5QMh@(D& ko55ޑ:2h{wos(D& 4Q_|Sߛ(D& 4QMh2tZG~гGϿf'zަ5QK n] r+;ZC@r$6p2ݳ~S,/vok&Nk :~ٸ Fpqt>]67݉uNCvZY+0rMӺ}&ժ \+?{i!Woht 6,Z}JjK c2Eƍ cܿ?mD7 w{ؽm+zO8^ҺcΆ#~&&&5ׯfΜ9X~=^ 3gH$&&sXxtn߾#Fرc/yhԸ{78| IQZ.ܛcĸ1tyq&vn݆37Q^!=w@_AR"l,=eqtG^5*k|b?} EG`hfk^s;چ+wWqŠ.`[s\x&L+NFzBZvİ?z(/cpy;:uNC`O85J\8;BUu{}ϼMfJg L#F0S\ϛ6.-ºˆ~=$ZToZkأ=k֑ wonD\CapL{/$܀o~:]a=}Xۻ[Ch<K/2䱃`H|hb]y;IFNպ4uQ>8En7e2B\kV~E|68'I+1;ͽ(S;b8qn=Q~^ol#(-ęؓp9ܚ[#C ztn ?w/* 2pLV*TC;6 ^ #agx"QݛXj%:!24::sn#wkTJ9ʽٰ CSvĝpRfcrv.huԉ#}mn ř7wxDSa=˷cwa*BJ)èՒV.Íaמ6~  _ƾ}o/$oBX=%%KS0zhq7מCs[S y2+5Bʝ0 j^aQ3 T*ˑ/?_o$]éKzգyU) wX&(m< ,\9+ҭM可ISǶ`z6К\v8|̜4 Vq?jd8s'!5P m<~vz(,-~#-2ֶװoQaVmJy9؆]{@GwvTdȼ{ {DkpwDNC\>q /\A[bxtul9(3t3c°JMRbْ\uawR`źq8qiwAXlfEƁP6߀m'dT aET$RGfF 5yyIk~/}[1 .>H~5spqFW GN y :?lMo5r9L=\h_YݡIw2q;aepit7v'42k_pLT"|lbדfj5r`붃dzA[Q'ὧ;Ct|1Uq;sbm& 1&= 'iB}J]v_+"B~ dk׮… aFU /n۳gBCC1k,_}4k gϞō7 FGn#iZ$¼ahO!yv^oԍ˧p,6]^gb]]kƢq]y@9ի~πp\.=nZe]jK:I[%"%ʩ\QIz R,|1zN5<oBj;oHe+s~aG5mCКX# c=55ƶ('/1QqNA2W yǧ ] I6ޡR^FF|MT*u%V4{\^ڱ_Ğ37Bk` e4_boy#qKkpUVrK<_s >n^qry{! T9^᪖a G]gJʣmE}PEFnJFԤ4\cb&j@Duᜪľ׫! '݌H.W93}L~ <7n [atp3ʨ;D%CRЮK q);]w ݺ>`Wut-2"P7Qrv1.r1-&\/ǃ)T"oLrL=[KwQXx6747~=C##߈F1)4]k~#!CB;ucȶ]g!dRNp9 SKk2CPPVjaN/LBrEriqve[K w$k.e>0uq1 F]Ăh.^hO"4!0$lkawkQQ^̺qb]x5h+a R2׎SzT 3bC0 ѝ"a@6?؞-XgJ͑= 'NN8>'/'{ Bts n2bR=vKXz g07?F:PW^ŀhO|"l= *B32ňi/`56@iQ8CgtVAp82j:_Q"((- ?KoKKF?S >gѡ}[şĽ؄?B%{7|HcyX[zPO!DF66h$M '[i^̫ qu*T40uÈ S0ax؛ DC:fӓnl8c t7h/sP^zöPv=G&BIw웘xQ+_+E/[dg$aÆ@^qױZb”g1s$ t)cÕXw(*9n_:?+wBà -3pfL_7{NV26^7B!!-斶, T6K>@4Wy7KaOl<*t9v1}dcH#v똲, kBd4>3 ]>NOcX䗔w# \{Nϭc{D_>QVVH|:Cq͚TT ! rBڍC͸8Z\Quh(~AR!SC[y61!pwiu*W09h|‚`&-|||i& %-v  Us޽غu+87^#avލ̝;!!!7Lxĵ TnSf^ۓaXm>ٔ Eq">c3ؗ KnRd$?gv} ff[+/ME@o}52P Qy2Q 29@ Gaؼ/"f{1'2݋JX]$QigxKAJA3Xf(IK*:Ĺ+[XN<.{60!*eǿ i2K  crg䌷>|/m", Xv =3F5;ǯ7+4ݾ%tK2pt,zLGSn|SAz76TVtq >nHyPSQԽhwGWp4p }&@[G9⽊ ҙZZ ti[Fƹl-twi [3ؼJh$S)0vJk9-C/ ׍?ԊيkY[p3R81GBcOyQS{ǨG~ރ&xp֘mRܳ/Mc5 I gg稤iPv?Y9&5yz퐕;NN-mٔ .t>5Ejxe1CnA)yaa 8m=WHE*VU (Ƚ䷎+"Xm4IcWafks r?* ҳ@BT4mvxnD/Q8[г{ n:`i GA~/Xϰ hg.A[.n RƈɏH{7 k2q8~'*'Fcl#͉B\?w?ԣ-4 ܲ {pL0L"Kz񟟊/=OtlkB>L`1BEZܺzICh+C;'p>;#ʨ\rv_Gx <\.w= g*8e_,f=GN@PO}5^Ì󡫫OFkW}piu9 g}&aTڝ§KCvˆYջ8k+x8$7eNغ57+ˑsvS3g^abN,.T',É1rDLsF^laKh b@7 {)ظg;X[S'?w2KJč*Cc<q#e (yڵgţ4^kNh&qn^~s؂WM][wD+$on=bm:{{Б"3#%f$՟<:dQJH6w hudȚ\ɅπTO^eZ޹7͑:nnF(˒UtBtF8!aaw wSm2Wn%)k M6 pX]@+/mYa4w8/l%ػ} NÞC#Ϭ=̊k󝏢_e 宲W8>3Ğ}DF wxLCvaҍ7ZEۊޤ>{IO EK^z3[f)iF~s2rK&hjuf}«o؛bL~іJkZ~D%o.T[4nΓփ!Gi^t8 %g9N9r]st2Q Ν)ҨRH3D0O|R=}?n $\=;Yp՝5crs $J0PkHyLUjsiO a Qx5ć߲ 730)Q DC=^G~x3l?F ǷJz qI6s> V4Ue]i: ) ǚ =6m襜4v B|a^zγR`&S̅$~s VUt⁏>goG_ѻ׮XL{? on!..Z=^_wأTT n CtL7TQOG5vNxB5뾉@1x`fmza°^p||hIѮ 8{Yy>p;(tF90IF:³?=dh縸.\&DÑuiKOFZ>r*3p?5-mr41n^}<+vdž}DçQXR#7@i B2Dwif0q=_R IQe>3jFt q9< b,OS`,=#Ȗq/5c)5.KuΤ䢈Y)bTR!4M tNT^Hxkhťx$$_`{YF6ХOW#i #fKMj qʄ^Npf8FGh$qZ42CzL$veIL(Xs`h?85^,jO+"(8z;Hȃ?D\%(Ჱcl-% Ʌ>ēhcѰllذ]732 V^|zn"3zIvCeNR o 1!eW_2:ݸV=\a ×=U(--8ZQ^c_[Έt"*?A4}#БyZb͈;yc)bҿs$v Rcb}cK)D6}Cc&~, ոkq(E ?}}pv=t]տ1S(eywWwXɥ,],; CG7zeHgOkȬS^+ވAܸyӘԂa>raR\0p@cL?I~kDOzE`3gi혿x%K24͛1C$#I u=wbQmJf/Pƻգiq`;`Ѣ'6((>`g)Cȩf ئb*.%,}R'W1)}x1a{ 2#8yB@k\&9[gGbHC9,Mcs^%-nQl7ʌcQ #T{7 ި6uz+8hyy[fV!c4Tt"LsSybI[ Җ@b (#ҐoHM Wo0qCa2e\ۊsJ5 fb5#ޙCagI.3hsMq]Ѳh;x1|,@bKݒhl2uɴr)LS3Š /,cڳaXʋPt';ce!(he")!-j)r yق*){ԣE:c񱴨s'}[nڳ0aA87R-TC+Zg 3NJrI,ޕ7qv"X#q 5 }<$ ?hW2 1%c.ׅ"jީ(}'G3<"}`K|R+bn*Eh"&2ehw?ÁC\/m_l\& vY{)RVhp.e -yxW- *6~u5ewyLo&wMÐ,s*'Hpd Gŷ+|KKS@D-ҥn30Rd kW_t<N2QI WE?o2.[ŤrL;C}iATWqE=Pk{~$ZbO_"YEpIznv=}+WTb.k*u^hli.};K,sqqSXaԠhmȓZ̘`ƌK6z4"$y+ɤUÛ;2C4gՄr*d앤 e"W)Ѫyl|ez{m2B!q"qq_p{zKuskPp (Ȕd#[Z(dw1^Ub,ES>cF?2 yAA`q9fiEfH#HC-}CCE!Ju$.h6s% E `lLL, 1OlDtzKT?.t)P.*Zi#Xƒ%tY\B g&ţ!\q53s*=NprR J9ƒc J}-I(T "^Rrx}\/l/B5+y,6,[ݽbGg9}1w2Ю8~6J鼻<6|ߨ9!p63D)VڿUzUPRQcEVc|eR(%qTO$9Sr&fD8#ş13m~!ՊJMaczr֖mgP_VTu޲6 `UGjE1 ԛ<1iK.]ıS ( ?^" bR)ʨ fJKLy ǡԮ?Z2A%Kۂ0 lae"KSNGEG%#^ΈVf?_8ЩD) !"H%Ûz lUDfVi}f"uysHPGQyҞ'2ѥm0Ley\[,hk8ͽdk=net܋݉q'Mvg }\0/CGNğ:Bb,^ƺN/ .eV$G]y;#u*JmZjydk):rNVێ#݃JJ)h3aνM]Jb$s6];t5=2U[לUM]0L5$_&?n&|dMZ_mڴ̙3! <׭[ Y;v`"ËLn%d C<5~xH XΘ/&hL۞?5lTp+\9 ix玔]`1u1v/15N6L(2>`>c~Ƌ>?C=l  fZf ,^f/[PXc@ޘ 7bԡz٣ G#WFEV#%ɻĖQV*m|hc+Ii{@ Nij.jWf{,92 se%[5G~\"WsEag%>'7@ SgNc?y(kZؙxkLD3/4 2+*'ah]k^,dV%Z^;'DiYzJƾT>"%+Ѵ:j" JOdddAb L#q(2~lHB>_v(jժE=܈BL*q|E)qaaݥza䂈Hԁ5PfG-*N,iϘ`/7\{Sc:c ӧb8Oz#{BԥRRV,%SFÙEX Zٺc:G1]3_ {<,-| pϟCfA9 ^eaHZ֡>Vc]Hy;d]. jg}v*QE@Zfm v#pF:۬AV@$zEt}Dv)ehύL|q9)FėT_JLX;b$[J}e#_|+Tin5x$GyQ]s߅ .,G&`@ ٓ!axgV_H(R :ZNW“wx1DYVBp붬LKYQ6p =PSГ`c`xf4N40=LxJ)R*pֳyŹ3.Hx=QX( z`2O^} K&A,o椿=H%|J۩hO=5ktzu@$)W;X0!&uۇ+ks}IeK}L߯w kMA23Wo'^P̤JŽ}}gqHR۶7F )ʦKpc7Le!RwW(,gbV>y5XGPtuw`=:4"n p+*XыgN91hk c5iF@֑o0"ik(IA[FG~Zں{]g42p@)ʟ}-D~hӡ' cz%dFvNGϹx!873n>)3߻@d=~peE#ZSy94=ŰqG@ c*zCcܠTjdN i)e_ #[AH4rrB=ѳS4": #nADPkv>jl5lO7!:ePo/9%mDf@Ė6܎H\BĉFf<02Q"CkfRd1d27`,ѭ 1|̿c8F60(<6ۣ#c<:{1P '+ S%'(FRJ$^1FtbCẓTx^ٱ)[,F:tZ2^ږ= gt`#g}='{mjMh@(D& ( Eۨ{uG?e!MŧoNx"V1=19ތ&9?r̟Gt& 4QMh@(PMFn_ŽG/7z > zܑ֘CERebBzJکMؿF(D& 4QMR|GG.Q :gGgC Cɦ֎h Ml Ml:M½1X?IDATh@(ЀyeǭIĘk땚JšO}T fV,h[?um<>:eXIfMM*bj(1ri%HHgnome-screenshot0232IƠ0100Fotoxx:trim/rotate| Fotoxx:trim/rotate|sharpen|resize| Fotoxx:draw_text|NEm?IENDB`fotoxx-20.08/images/trim-rotate.jpg000066400000000000000000000663221362435004500173030ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 459 316 0 C     C   2" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?R!oo`>ާLQADizhLQ@>7F$LGf}3Wizk] ]mx_VEԥdkpaFqzTŏx iPjFwy-"۬<%d ǖ#'7JzO"{}Fh4ʒpv`qU7~/eKiVG-+^op JDB u95SÙ|9toZ]Ǫ\ܹ7Y$bW[MK_cz>D{Q4t%0%Gd~3FA>/߈z?ëK4SM%sM4сpdf<-6 fy+]V(WN󫆊SM/Ȼ ._4|F=_5OdZ&x Im{@! 2w W5K]izE伅"rU۰F3M&}?F54IǟizhL_|x_~+~оҵ?Hѭv2"Z @rL+[M)/4:M{{Q# h,pA{ϸGW]8ZZxU$Wr??|YWU ՝_4}{4r/(m\I]mO7MC/ew$N?> +?J (CO;?WE~¡' ^TE| BWUE|?P"T>+ÿ*~_| BWUG*ЕAQp?0e%X ֤+gmcPkWr=ӖA,p89+o?J (CO;?.$,-mY!-(Qbbq- GtjQx췳$,(Mtj@ܟT>+ÿ*?J )Y ՙg/ 7u\SkR-֒4^`?p ϋ:$Zi{5Kى1LWG3x/çqAQ %xwP*Y ? `nkɫìg}wR,ힹ?ZxK\i_3j/$f9b˻Nr9oT>+ÿ*?J )Y<[KgehΠ.$)nás#b|Ax\ڕrhvq_?| BWUG*ЕAQp?T>+ÿ*?J (W*ЕAQ %xwP\PJAT>+ÿ*?J (SgQs ̫g +Ԯ庺7ޯ,ΓE'%#1޲>xœx?KFn5!1:2DRAl^߇c}uj.@ cPqVZzq-β%!D98lBeLHj[:SiXmp4`x? \ԖN sxu " ;Dq* ߅mc*K ( ( ( ( (<#|YMo^zn}{%]zgIǖ118O<[_xWÚ^hѼKsnV_1d@jy,<6R4uȯifMm\Y?ـ,#"eW<Ydh4]:e9lcYb;I:_k~ko?MkmV4˗ƐмB0# ,0F +>w:aypjI) W1>6>xXVvoƛY&c#y,[_\_.uɼkcfssjW6hέ:"zHc#S~Aug .{&#e6s[rcUjWy W|My+ohqSOhgnOr|񮂇²牢4$ΛtK$Uv0>[DxZK]^K[̖H eS*ƹc>']N+$O%đ^Mv"o-BxϩY]W|LOI!Qj\jZ Ζ *<щ`K0Et^ {hfM7D} Ec˸(ͷh2{?|?ZZ]6WZƓ2i$bݍ^wPZ\d5=VS)Gqgv>E\W 3x/W~zFilFܠAe n_>6ᶱ\ }6K-l1fS#yle[*bM־xW/mݔ\H+]BH58TC1 H9#5[|.bPH!-u4[ \AHwwsB_`~07C@MDMbty$>[FM<۵y_⏈uOާot[*]xcRڶgH< *q\x7k"J( - JŝWkHm`v7~xB+mBhY]GX\j:i]r[Y`R9ols‡v? }|_c'+#Ү4彋Q>㷟p nVo9JqE{uqGᏆK[1,B&mR)edD:WAxL^jױX[<۬${5MZ4QEIAEPEPEPEP5Ş<ƟwƗ]umogfe V+9oұ&ֵ h/4Oz6V'$YX”GTkۼm$umXdWtSCu:V BfZ}-?͊0ϗ&kWח?u?x6h"_Yi<>\\?laG<_4ϋsx{[/:<6;!1)F>H?C;%EnPH u?տM{V֞K;A2(љb[[T־Dy-KZ㏊n± ᰳyB; \T Jʦ6Q־_C/tjvC=ݺv䓎_?|}J C^𖓪C—6yU'=*z[sż{ G4m|{i[}ndgR{7mX=~cxr;k =87?qX|HCg6lUo HO(J ÿ4y?kWy|!qIѴ!s5D6D| |b5O߆wIA,pVB_s'[;> ^˸^AZ>y?k__ Q}#NhK8Ѽ 0s+kR/? dc{:dӴmI_-i%EP:[?Q\s&?뺅l$.)y`}<;rqH엟V^AZǎWd{ef[n@>ϰ>x|3|#|;=78/ڇ=G엟V^AZ}[uN\-i~>q4/:n\Zz5 "$RmY#vhWF/? dÞ 4 |M\oki#ve8 ~''iFv[HsawD%a3fgT c J} K+\~!YxF7L}xwɱ(+j9&nt]7ᶨmt54kqY7!PvH+ ~y?h%*𶍧iZ }gXmss8 dpMkP/^AZtVK"ޠPjQE6Xh7P)A|3ǚ?5{t, AKK$_>h9⾼uF|ۦ-JN~wylIlogqj<CVx!7c 4jZ$Cm ncc"`Ȃ@9$Vo|Sx{QԭฒY-rќ:px"%+lOx/7RD;S72j6sG4KcqKIKm@bk5? h0PW,&VIۓZJ';A Am{6!(_u,:Sk= HZYJ%.*pI>¼we\|^Ob! %֭_:c7%"/M]3MHVC~~?\lRxJRYU}ombytc(/&_iɧby\n<ΘgP&y7!&1EFbE-&׾A㿏:-.lm,uYmΓ\V HqN;וw3zVi3ki6bAu{A*p^'j~y'~ӼAҴVIUw*qEhRZW>Kjj6zt.|&k\9lː]A@KU־6^|X4-5W=gpq|إ1(d`p d Olt1oqxS25 llNZ[[$**sco$toY66:枎`X_ta786_VcIҭtˍ>m>աx-H& ҪGe^|/|Ss6CLTv:HN.w;<_~dPJ?E1x݄2 37>ϮB/4 iV&ʲapun0'D~x٫Y~_A?+'%:g7E9\y ! F&Lv455hF[W|#e_Ov6qqk&F* ]Gڴ\L|/#[ҠDw- -!y$'M-XSƾ]:=gl`ZoR!07Sc|S~>0%kwJlx--m2r4 U7(O>_ |;=χ 7GndfU'%N睽=;IcAnm*1^ -6f،x=;^$g*ש(5^Iy-`">`H† bLqie #)F}vJ/'xHmXzxĺaex 4f 3 *>c{p^7{XAH^9JWci۞(IO`ksR:RPQEQEC^otk4JHOo1rp6_y|xĖZJ=>N%Q-t)#\mQa=3ҵ#6|KGcojjK).,M|d[7t㖷w~[g׾0\xkkDƧIki!,Dne!ӻH8]jbៈ`KӓYE&mbI(Q >'.><;#1_4d͟1sn9gl߈my#~e$E|ߏs4t5+Ɵ:~캺I)lK YtFC#O'.m]x,nO* nQ@o5/ xO|KmjV\ijuHbT'2kQէl7~#]\&"f{P&څ`Ėbs֫Dݶu\ZWߍMg]z4!k[kR QHGpM zĚͮic}[mpm`ْ9Y m%b};bf&ƚnjx|w%-̲Is*& e#` WIo~'W9ܝN} ٭դym<9LpЭ/1=lWG>< G3 #mOECAk+ϗJT*HK`&Ww)kD/6&,RH rv*G55koxR[7X١b )V#r?0u}oRj%iVmgjФyYȈprY`5.jMH5mQ!l^m-jwzOI}猝 N8'мAxq.&}iH7ξa[n#~-7GN5뢽Ih~$0T *@ g8wZ|-_ׯ}{՞xq4ylm%r88$+ۯ]ӕ&ׯ5/&FVKb7 TU$Wq2$eaʎѤTAebSּW~%җz6Z&q]Q:J? IRzе77 IuEuX0|Ic֨㖷4;$ڣĈ7ᣒF o^\|-\ៈzeo,욄L,Ijې$_|#˩6?fKUxM]@AJۿo"eo.Uy<)\i: =T-2WDvB`y|KzOۣZ# '$ƼYmiCXm_ VnES<pc*Duxqz6\)"d{e .VS~-CskM?Ě_m~Vխh!E8i#-ޱ|LaT5)L|:ZZk >bu8𖓭xw:ޯaǡk:"G[Ֆ#P 8 7vpܮ0:&þ _|WC᫙y>j~0G%Y5k! p7oA1yC)}ˇFZgq@VxX]3jrVo51G8-p?6z5G×pnxS5xf0C'PZːA678գ}Z*mhʹ|[.o`X`ei?=^Ÿٟ^"bҾMo ;)Z.eMFOw04rh N@}??ѾaVZjIq$6s w+#>=Ѿ!u]zIm$/4f[;X Sp8`=^)}|?2Ӣi='AV(  6樺 k*[HTP$d`s_,( oᛵYѦ.UmXi)peieH"y$`,Z>$N\~%+o~L{i/ßflơ?i{wHGM0A"Xu^f[|>dJ!OJŞSd$2F<=^ -?-unSP." kqS|Ar4Ϙ3񮅩xK-ᄱӾw$3ZYA ^Wa._ZhGG4CFѼ1y[_#I1o:Ep%, n7+]!CK]?>T&cj|!ЮtoAuk[!kͧ9Iv2y4[h]ūŜsjjIKʨ%"$׼Ik> n?hZEy oCAAjI?OYIV{;Egv/UA$_'i -Kc+*+!~o|HHѪq Cr=2̪\L?-o"5 .}^ :j(# Q:!e(+H叇5=#L5+_ T2h<01Y%ZFM+fzީ2ܼ3v/& "wW `}/ɂ.4 u 8:RN}/[3+unmWvZUSglec9#溍O!mm[.V9&H3 %0OZ.g K7B ԒO'ڳaAmCSt_ m5oúΕKK+m0YKkK e/s'X5?Ej>}v)R[yO8#h>7-{&?E7WR6x|@~S2*#(no<_Jz|%w╇ՕJnC&P#-HqVll|+M 4} jW~-y=^IKX@hnU>B<_G#znכFגG I]X\P63̱ ET*<{d$3ƾ۱\ƨs ֡+hQE!Q@-b)x}NeĞ[hmHdy8>$D%/! ?JVZɮxYbPOBD$Jd J+OgcK|a4l/b#$ePG̈O8d%/r KG6ZZ%:񇂼ABoSӠ.cMLmEI{͞b-`׶uϬ}x׉|k&\ϢcŰyY3ܒ@ q^መAwpU.n RnQy۟Ɵ@y> ί{5kvC\O$S NxV<d7qvqƁŕ`A rPPj3mjQr"cET (I4m%vտ |o^)4xĖܿSxkUഴ&e[+HV6擖bE$v?^/x:Iu_ƺό[itn ؂(]pzrk4v-`o*GDYGo—,40>[yFc01w˰$qFgƗ}hZ6 ^LrLB;<[i-<5_Ϧk-I-댩Isk灼=mwQV_NM'X$)jʡ2D6IJ. ὁ]?-~'į>1A,6ɬYGi"!.gȇVv0*/7:;`xė:d:X Kk+*fӵ@%r,ZJiڵZҠ+hel1# B+`X/^?o\ou5yĒ;>NWfwGg7)&Aj-^=oAgAkK[Km>90MIK+;dg$naukUKT8K}0g"6e*[no4|:ޢ) (((ss$ ZMpc(Tmw__]^E6w`3;k7ZA^cqG>߮8"e]J/4ˡRD]Qtmw__]_(}_Qt u}EڎC /T,3( /..i:>ivwOyIJ`P[oS*Sa0/.?.憎OG#zRby憎>/憎ާAT:N$np\K`P1@}_QtqK(ǻ6X8`U9f=t`Կ0 ?.憎of]Y-ƣs8Uy(̑+giezR2Se]J/4˯/.<Ɲ $_#?o/w}$k*2:F2#7 4^ 9〰أ4~L2o>d}⯉|5ؒ[o^X_hZ I$m$ #d߹qKZ_:Ǐ O]栺րoP[v !)'FI9sŭKik1 GmpB7 \G|ALjgjRh!Gb{x d n x'|=97Ʃ-FQ BFKr${v<}ψ#Ll/A%6ei]C3JW~rs_[[h:eoZAmep*Æ$0I$MTOl4i]Lg$a i];P4?j׮c5> {Oij2]Ax J{U*a\U]6%IwíCIկq*1 Klz w ;Kf3ž n|lD7rGa$̥v QkwI?f74YMF^軜K(!#F9%E碁ں_xQ"U#Vi#f@#Z~vgh~4&?*%U~!%{y?ks_F)?ۚ4I),tN?$V@7R񊯥Mm=64vo!)Ə~={3ũ{ \֭5e!G&(Y H3zVޅk6dlnL1&̗ j7́^kjӍ;GAg6\0@N8ctM}f> Ţ o4% mIn56_5~v7[T.1ϽkG6 ƻRMBQ;(:\]Ic$ `i& m#&8ڽx^GWz]GZdg|2'rr\$x$Aa]桫ŮߧR~G|"Q,.-l KVQGSmke*[ *3P'8A['.rGQE%Q@p:4 C5Zn2VRzdW}[V^lKk 6;K\A.vBɕ@@ ]nkMa>%K4Nuvi8>|e|,7~{I,t:6D%6ڀEA+ 5-Ն 6gFcx1ZSQ$~ǧ UI$﫽&H@O-+xm2q܅^[dLKIZw?so\vZ]Kx/r@8 :WmIt-N¢w=FYrvx&=(Y AV_1hpx@t(ŭ12FW(ڮ`U$xw<7ׅ^"PGfZI:>/dc"2#!j<<ҍ5Е A)J*Z_MWMSx).4XQ10V~cl!xA}KEIWlz_: kK= \YLu G®UwdEe `C>[KtUJ+e|\)Қ|Iڵg{=RezP$Jʩ#WSծ ]ˎ8WLtUW ??)?7k矌ڶqVЍ*m mjEp~)́c3vFFOxh'KQZ]&ݮse|_o,95p;|z]mxFdlu;aqgu{<N aǯC^{@|]4 4OxKȟBf- ˆEn'~mᆿkZJiUx[捤!}n3]Q8MFm5~|T)Vu#9(${8.}g7VV$jZpxc40W̑Spq?K"'*4O>DuL8zɎZKOSr3 ? |%͕JᡂAkfh-~^G}=r*m[5Tzg$Οi{K)r3_G/ B l/j^`&<Λh ]7('PMJj~+m,Tl/Bżrjdx?E=kԖQEQEy:Mαc[SOSz*kDrT@[wxATʑawߞp1յ|#"_K~$ z(#rJ;^70 K<.F2+ȊӺC%BR*Fyռ6t:YҌb'_{~UhDY ]]X2%PA#'yzy&"? PXL9g&w**jꤓbztxbKOiʻdHX )#3|Mm%uKxtʊ|eegI?ZA@=i4;e.w k\goLj׾!|U# ec4PrFqv^G@P u,LEъI=Wcϖ +6[&].7"-6&N"U#T|UI<6NԕE5AS<\¿xwW]bJܓGlwQuo~ k[K{[Ksh`2Bv 8:׽pQG@j~ S5X <Ѡ!@ 漻w KҴ/FSkΏ& M)F_IypQGEy^aDFH4Sq%FI-`Do(՟[(ڳVV>o(V>o(՟[)jH *ڵ>O1T5[_"-je3"đQHe=x=@Q@Q@Q@Q@Q@Q@|dռjsm]krZ$1,[HۻI$Udn^J 5_NG v&>cȯT𗁬kso47<ζm yݾr[7 ði :=V6mg(4jֿJ??=_nĒx}C=&η$Jсq[ᶡS[R}$ͧMjM !Q*mf2A'4/7-`t8#p*|+<'4 Ӽ+u%ez ( [jQi+CZ\]6KaAafOkne-[Aw-$B162H'9zMW`gPF gk{o0F"DH xb3RM\Oj>ϊu/jMSIE DBI`\c v>㏈(,>0k yY[g}Tb'@oxX؝#lg,jbD@S5o>E?4% '##!$_OǙ/.?m?YxsW Eks O-Ddu_)Ba{l_OxJ[^ԴBZDrCwıc?&遊%m/\am4)k*^fPc^Nu/AEQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@fotoxx-20.08/images/trim-rotate2.jpg000066400000000000000000000277141362435004500173670ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 160 325 0 C     C   j" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?R%mcQ }ϽOF>7F}oѣ3WGEIF=_5(әPeP=I5o~#Լ!XZ=j2cn:0#5֩-gx;%5mcMr}udvL0%s֗7WROVRm9 ѰY-t![sW3W}g>NOj4=VDF<,Ysp+biT|>Etum^Hlӷ5y`+ڥTI-Z_~k5וCq082ƾ/3o;)J>F4}GW ]YH~/q:ڿRO ޥ2Gwokpv-16.20V՚]RM73c>dh7̒t x?X?.!fgҠ;.FBie99{]g/Yk vVoȚjb%% ۭBwW^_ߦ33WizkXx-U}t|WKkiTBdR+7cv(<q{mgFմ:F.n^9&uBU9-?&+⏴=_4}oѭhH-d'h-2IvYF0 䎴h~ &}[>@;du81Ҧ/&k٘izhLLeN J}oѣ3W{,JhM tڹd"X4F7}*^-@y8Y$cU$}K^#,yw~5~ֶIµVŠ G__xC>Ξ-潑odKRWEFfYL!҄QI]_g' 0kVRj6Inzݤ~wD5+Y-3,N0Cz*vuN9Pq4Tq} tW+ Э&V[E|?M;~Q_%B?[Km4\J+ mo hZ`dLĒ䊫E\j+ Ћo%) )=KQp?J?Ok2j5^Epᡏ~U8t|5;yg6CyBP@')¾ 3~ 02? |C,Vzk0Tw@%@q,~+X=gPE]񻿯zG_"?"_"(ѼKxttVL2$6w:giƛMiZM%7.z`rs_K[/Q;c 7e?S<7vg9J.#:դr-rg~oƚ%$}V=&@áEQ_K[/G">KQq2YMZK.^XTfYO⦷-% u(dV|w)A+C Ћo%)|')<enf-mK`dCEf.ŘbrIKQp?ޟD|%B- Ћo%(W$^EA,.)PneU=_D|%B-~izދ}msHEuڿ_sYu1w0= W">KQEW BJkf߇1xZrBni3ZnoCOiUn4trP/Eu1hj1-WFMԠyWP,#VG*tV|5hwIekFy=!g ߌoms6D+c2$nHʌGin>!wRv vmEhc@ c9<[KEπd<7M3vuyHdfѸ"4 nEa&a/-͎mYK5eK[6g[qlPI\7m)|!xU߆|+}6emYI& H0<8U tUo_ 7 /]i\Ao&adFTpi*imevR%;+U.Rt뛛 ,31OAY7_Ꮛi|3mC{-ƫyPc 8ڵG?=VH6q4Qȑʊ;g8.c!Lkoc״& 2!Oe<ⴛQJCS= bM j%1eZ#y~&&Q[xrG,f Nm`g;?υxMKޙ鶶[=EnH>{PQQW?K.ëjՖ%mB!VG21@^1Jm>_țׄ-;J?xTԮc&QjO@&|k"n^趮gQ.+'ִ.J.;x$e3A8=+7~O/KkxP<ɤs鍣\qp@ c;˽R88Hw(* pMlx~$xDÑKCZzf2jMZ^g `ի^?+;Ong_6ujYHkTݔ=:LJ5u.Og 0_qA[iGIxbk钭b!^lp+?/f:4zMJKy}9X%@r!w#9Ai'Ě̖UhӘ/ ysiPRWW'45x/NԵ-cC.U]Ghc.O'C|!{mus}sq>DFiyb'da\+{wIBG;x^ 6Z~!գ'[h]܃2@5auᯁ QZi+ %DE^ 1*8GhN$J5f O^O‹]_`#m$ 议) lJo|Kh [uumxFAٷGx8 r1R|/B=Dž9tEa}Y$1$FE;,  F*ÿ8ma [ [k-\jws4&R,W,:Njo ]U:#xVK#4--˕=s;' 0_KĽ_+/\W^O:g|[wU4LYxL\K}nd!eؤ,?Ho^)8e◁jw5v{]l,J6$U[=_[g-_i^ml![q X*CU1_/𾇭k}ω,5=7R5(Z(.Z&}JnفW[>uq=קw4іQ٤וHEaz_ "d-2Ҿ *K\Ht+}bÞ.iZO\Hy\FBC=*;xj>X[ai/u LdWAl{Ӵ2xQѠnng"ݗ>%a1 p}: ziLoYm0Fy/xsE7zixbPO!V \Zï;Z3^^KjOg>P ř s'?]S>ej_^>xƛhZ4ZƱu 2fƌ|UU᱌Y?ƝK=|9'5aeokq&xUVev3M+OasZƜxbdm )gytIUU@bj~ˣ>٥I|WX']/M׿K@zx-i`,jY=f<9B"QOл/U~ M=czNM2[ky.Ȇv(@`9°'|{9h Ӽ/{iߛX,fIx n{=!;jqshc'?]SwS_b򿲓^߲e?ݟ+ݟ/ӽz p:kq;>ejxsE75QZswvukw3x~9O.jW$~!g[Vq`vחO6:Gj-iIt/s XO7M'?]S/ 7[r~%~DŽ>X5 ?-JKhY |oZs'?]S>ej~ҷZ?|kږx&5,᳎as~lٌrXt>Um/^3Ee,.& d\JlF[R3}CGsÚTެB'vHّp<⸏_[R/nmOޖTo|M]?c?Gt_]W3m˫BTRJ+xD{'m\+/g+-?Tx.%ӥc+e U A"B7KeVG/=ImIgoˢ g.?1}SMk3ƿL.?1zw7O3ٳv۾zRGP[OxP0kZ5oy6{PQx3>3o&^c J[ڢ|3qv`te3t}EдV_? o>|2\\,c1f`Vb~J]tO/\.Mm^K g w4۸%cSBᾟm:m&8y!w@ } x~~,>|M~/F'dרTH#.y\k"С#X@Q/UP:jwt_]/8em=OM 5]CV'E},p@6n]Ǩ^qG/ٱ>[񦣧xsFu (4 ۼvVvFݱU@(]?c?Gt_]4 jқ_~._ž!eKZ֞ɭk^M4(Bۖ"3zQ6D$dJ'O9h.?1Wˢ g.?1{+y_[}oF'eOKk+:M\TH$/OAEHZ(Q@Q@Q@Q@Q@Q@Q@_weX=M%ISr{Qk|tWxz'~1=u2V5 arj5xKYkmZh`F,Ls^Wx^,xC&^OZYxm͑՜y]p1ּ|_gĺmwwcS\p[mD-VD!NFHm_KǬ[;QFVP}(/ur"KMmQPXQEQEQEQEQEQEQE2]xxY𕟉ci-fE1+qdTx&_  704`s9k裥;/XinxNC$H\ QZ]: c}y#pj5bԭ֫kEaw=[ą#~ C^E<Ιx^թgS<ہ_D/ot:m2[<&G&< q5P3G):U*G A*ץQ@fotoxx-20.08/images/twist-image.jpg000066400000000000000000001112271362435004500172610ustar00rootroot00000000000000JFIFHHExifMM*V^(if%HH023080100Fotoxx:trim_rotate|sharpen| Fotoxx:trim_rotate|sharpen|NE0Photoshop 3.08BIMfotoxx,  1000 1000 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?N4% V97nj&7<*n& *'h(:1G:dW-|Bvwa0o޶Ksؠp|RGQiC{dEzoM0DIJ,ry)rG3&n$dajv*HJY`N0:ӡ]VW] ^I7tEKah8 5Ĥ]ʰ,Kir3s]C0IFtyӼ-}y{)l*(UR*Yzdv>ZakPieg%Qͦ;06IeR Ujzu{.+X^b& r+?*Wxoh x _Qh`iery|JzW~Te}^H;1k.i‹< V2(c'iQxw[-mh2ﱶݝ`ʌͣxgۥm!WT%‘I3:#.i5ZbrגOf# :q[9_AQT((+?*QQp-}FW~UṢ̢[ʌGE_AUO22o+?*2eeW~Te}U<<.ʪyy\ y_AQT((+?*QQp-}FW~UṢ̢ZʧӢH_/+;̭4_SLL񯊴!ҼKE.XLDNXI^k 9xU~]MnKyUP??r' 'II(??r' 'II(??r' 'II(??r' 'II~.>ɠr˃$v8+=\k5SF_hϲqA2#׌_N7a' {sO#we {Vn| g׉iwQ-N8M9+{źkAQ 'W\_I&mE(GwLdVkM’Ww]NA"m?"L˴v$Oۿ^%9(>Mm'|Bpr~UO^^yFym"8a%2J> 2H)]߯wY#>!j*]B8FÊYjLc?s63^xw]ka9KT p0@}\VR%_2;k<1[]Z؋&jnAyc_FzN-R gXz1^of6@ې { kTžw.68RUdl1$jĮWy]Hi-?#Ҿ'Ϩ-ڍ v+uʰzoN5l[[=X~d` xk^ZUDmrk"m"xdF1イgB"'kMJJZ5/4ԯ-ND/Ю2=N"i*bEu%##;ɥ6,ϸ9+f뙙>}sk,w vh)w]<0izԷDVPq,xu/>ՅdU\UbGAӥnk:Mg!;8(x q^e̮6>t>!N3Rk{q%̭7,[ dv% N[q#Hw; sIϠ;⇇S'`x9 ܒ7C.@<.kjir#[  ~RUd @{nYC^fњVxlPR?cC#7}Y|45 -N|X+rn٩ЯԤfI[<*bAdvŚ6*겠a!Gс<{Ogm[+R̝Hxc={ ٭w&IK{NJ31R]єۥ%Q.2px'W//{y,Ũ@pX@Liuv9u.[4qZNm"Dԑ\v gpWϥy6D[qmp.Gk2X{ڴ M%@Wi+$rI$DĚZPq @{`g͍{V _gkON}!.O6 Y7 tҸ/_5y4Gkڧsԋr.KN/ 61ӥGİ?k>SFAQ}7/z᝙N$d~UxwF ƓXOX. VIѤ`o)6z]ʢY17r0H=rZ1۵5c-: け=kь+S"Ig|mO_J4ox$EX2R7;TimJ*-VwXn% +HzCGz+݆rH 8h=?g76x$ӂG^4w;ף<owXVê;ع|)wEQ@hN;]|Hi!zw#<owGy^* yb,F&XsTDGiQ~=1үg?ֶg>+qi^]Mosp6(P6ؤUQ4H-Aiio.ђI' 02IZ{z?Y^gs)%5=;]2]24C.81,AVl'לvӼKqiç]5̓Zڻ={z,tE{{Կ{z?YGRg?fD^ggK;ף<owE]yy/y^=atE{{Կ{z?YGRg?fD^ggK;ף<owE]yy/y^=atE{{Կ{z?YGRg?fD^ggK;ף<owE]y6g?+3<owZL& wVp|18q6|Qv7,UU꿵oۯ^UVHQEQE>(YF^M}UL2ؑ_V>7\^tu ^ ..-R^Gސny83^_]Ι"ɣJQ~^F&|6z8iZ k/y$p{n'5Z"FC"yk$߇j򻅲{{h9FCnhly0뜯kiqZZr[$ r:q:ܙ«}&,eʙم;@86(qʡ+emB4d^Ifss5h,n Y32BFsqmjG!h}[UFsּgVo4BM7U9%A`}c|LFU;vl5m&T+%0 01toZ\Хx+tȤ`IiTupONvv%wտ/$YR)$β,Eh].pQkZLɥx%l2@I̿ g#޽\Grkm[oIdm ,&pyrqZei0H8潌!Umf:;u8߉Q+o4{_ # Ϸh#PҊ]'䳜U=sUu-wgV#K& #98>J11GHQqyo Pp$c$py_%ƺZ/k ii:~SV |̪XU#M/R/.q.؍2:s8>_iJBU%Rˁ1ܞ2H ߴu B oG)Al7^{e[%)O !L2>ڥ^[y1wo"r2RGԷc^SP}I,M?LOhg0G=*V:8[11]r~$W<x^j.--U'#rKe$G*񅜺k8<ۺFį5m׶M礷V2j)-wR0rk>)M[de1>Wֱ:uͼhwl0x 8'euc[vujaʣ|ǯҖdL I5[Ky'ԙ993_CxC־M2} s8쎃ד]t.D!sx zə"cn"%ZǑWwf0/ eLl.HO$ǿj ڝҭފ=>%[m:50`J793~kȕO{CAyK1ѺZ8d9u>h3S,<(H!AwcEjZ6*$I^_<=yiS"Ѷy` ҉݊֨`m7_Gc?Z[ƶb;~Kěܘ2(yc0'Ҿ't? Eas|锷9p3[C.Vu+-mǧh0iQ0Cn䏽'?μ)Iѣ+Ve2_;^-rXK7eR鷍}RX^Nߞ T'?grSKا^;(mcUiNt'x#NMXr[>U$8ݾSW-J7쒲)v}EWfQEQEQEk:-SjSYVciGn t+n{Oioo.+2*6RT$$Gh7E\^jsHaU`  I : >,VY.5;eŔCzxaEnk6t襃^T"wIe8T xPDf9ln/ܠ[13C'oZw:s jk`r=]}I #<J' h5).*vs\1T۹n\ڼ:ش]q1>g PX`yw2X~mlWr"FHޏ;ƺ{Uٯ<7qTwP#,E2ŎvWao=:e+o߆&!i]2#%tF_9#i^x[\j''LM}&ۺG,@9[7 j^-Ѵ"ʎEpP[JRWЪK1n6%+[idSr,PNYß$!(q\ֵnFIamgG}2!R2AUfFwyxwKuN,C,D@uG-ov_c[,~[7ΓJmV'2qKhZzo'6 S$#i8g C·D"{{Y1O5r#  #+_j|'u+${J[o)D",ɕBsOyNN5+ۂdW6rT@Nj+[idSr,PNYß$!(qU<4"VEo PED`aWT:{W;54;Ļ[%w1bd '@U˯9:Cvw'M2fkk{hd;@6gs0mSzf~7SJ׍o$8B)' gC\օSŭ֓iużӹ47dfbWAJJ,]yharQ[K FeeeF>i<]KEZʎߚAJe峀T1# d]FюjYORiQIWv2*-X SPcݣǼ zdZ45ޙq*KEdᑓ50wHrұTVr{0˅3A'Yn-'b6֓LTAt$+޿_hڕV%j0wm*AbGZHNogGu,,4,: /y;*(AEPEPEPVTvր>x*_ڏKIb* -GڙR($8%>J+[ +茋 >[DΩB~$޽ÈJ^IhK?`Wiiz]vk٬p!\zvVcI4e?J^eJm= ^O oxP5"дŎ(AsEi~'1hVH jb@.~B>5yt}cs ?y&:^Wf&ѥW7"h,@; Tg_B|YS9l+)z2/ $bIvȷ|s r=ȯ^L1EnghIR#:|աɦL9"8kh·`v_kΠ5 y5UI3[,Qt1{օ?cRU[&Ub< ,j?*&=6ya*C?ZXӂ]-afAeAj:n[4,XzzGӯI/O:UC43\$]:['5ʲ)98#ӌ׮|'ԟR}MN!Bq8^;-h>p۹}Peֽ?/ڗM GYP PpӓMFKSWL_zw.͡gjN>+_~Q?3]߇\*$~x% IFfcC,n88WE /%+~t^FoT*`0 2S唕t~?υ-^t 4ʖ!?$rsS\_ukVfNy"!a;ћQd<+qx[~>5ay-qK[JMeu h7(6(dz6*ԓwFm3ݥp$q[4i+%Ё!$͆\qMl9#QX^)Ե=9-J7cDnx?/AG5O R/͝G4G5Ziݗ޿(?_?_T5/Q\$~) ,G$~) ,G֩kv_z3?HS@YƏHS@YƏS?gyEp//ZQ}#O_e?#O_e>O>[G4G4}j}Fe;c;_k} SYAyW~#ѯniaYU(0 `rkVPT?.8VFL*ѝiiEK+FV(#G` X4˫EkM$T7O? :<^%Oڳ#?x~-?KshW?v:+W?vx~-?Ksh?x~-?KshW?v:+W?vx~-?Ksh?x~-?KsjťοEi2(p|Hfr8?tnQJghڏKIb*Ux* Hukm7Lm'O"φmbӕ!.˂=\elpIJ1f8J7ZCY]C1 ƷyZHQ0#)T7;Emj0zTךʱKo۟%B¸~ucAcԮV8X,rY s=BGMiak`6 r]:kiw(N"-C~8#bchHIlec.>=֒5k{[Qˤrgt y7zf,pdnD{ZZe-}jp@^DXaGӞiY'魌8l#@2 0ܤ0z^Ƴh1Ssw>f=߆^Iٿ/jRTdt+ɼg .L&A4,P9g5֯/f Nue8}ZUegں ?1,v}>Y6w+,n#2rg]V;u ɂG<98?P+b#i{:՜_#9/y;Rہ|'~a'k]Ñmm,Gx `iշ8N:ZtGuu"T1'sCҢ``Zuo~'۶kiiӢ'fOѵn?W/VC v*F oOT،>tU[? pV_$ьAԼkpfgR$@'.y@ɬ;]wU@q2䃀q8ȑK<>+¿=$}k[To^2? VjJsPTUٓ7DBfY2ڳ}:}A-mdf#'@Cgۯz\6 eWV`qv3܃̷W&He^=^VTjJ.^K3⤖Zz]-K̲1MMu iF:5&٢ ˒m5^K]D ZV?J>bCdq3 a1Gx=^Fv9gxw:h[H{Rp0:=f?tHTF+8q9lⲚ;Pe{#^X(i.|7wڷFcZ\$㬼JσG˯CXϠ F/ JsWQçg,7Z|Νp6bU73\DXrT>QWxo"MT8Hn.ZI̶UG q:W'G8Ysw: BE-XEp0v+˛W"8i6`lTF_7- bb';u9=MhkחmXj^&oQ%j`c#ނ8JO⾆3Q[XfcEC7 ҢMqMK6B, 1ҭCi@gnD;m@?d3IVM7"( g=uw#Hu(yԺqԸ^?WMe.hy?,6ߙ,Șk8$`u 8>ƼKg2' ?텯B.9\ȹ`1I$h$2z_[iۅ]@*{gk3JdsA8ި%CrNI"gY̳1>@X^{(-4,`9oIV$ю2.{sgtiVBD=zԟS\$ަGC+=z]h<ЉKpW'qq^u冠!H!IfUgu8!<#5Ԛ6, 'dQӧ_hȿ<۲sOC&KeZ-ٓNח/w I]UAy%Kľ'㸽wv%6ãHSw{a4ڵWm̌TEXvRG<Ɏ5&}<ڂj^yo6k`#MyriQWRZ:ض16S$qh}sg=IuMm$n\X63'$V$=hh}ϥKͪ[yYWnvYyW诱<?v_CJ_'<͜q\/C[{k;$%貙\EQEQEQEQEQEU?GU*ݟ?j [ljUA$Pzw |5B= }Bv,}p;V~?xŚ+YkMiŬ'qAǥr;.D ֑>'|Ⱦ5Ɋ{/Y?]:^]CS3^ݰf+6N[ܒ}굗\la%!q.:/=H?nu]DMoJ1#rO~?InFĪC}@;i H:Ӎ5o_S6[i]k:]WY@ {ҽ÷ZnvqdDՉ,- E%baT{Ο.u+xgKhclpʽ vɯ9Iz*zs\i.H0.)~8"sմY=OyLDmuvJ~oO~̌$2^!1V= +.ǰd.9s u8>XCf`d9^jX:ō"MP@9B97O!-`カG95xEK{Hӧ+FFq߮A ISZ3+-+ djRI2ky21Pˡ[Zڐ?m*>PJkm6ֶ򳁖.I\{jZKwgv\71~G=iBSv*ߩqgu{f2p|ɬeW.gм7VL¬J$`p8ס?|)yLM#>ǧ\(ݿ}+qCgoj]:glqRԖP?z3Ӕ,߆5߇ik"툆@|90zƷԵɍ2yq Ǿ={޿> Zԥl>\]H2V vힵVd{fL"3(N 緧BU*|8B;M"ZLk_f>³,ro Uhb]RYcIݥ17<*qJHhڥ+C5A}ɝa"WX۝v+Xۇ1ӯ_Rjm VHDrpܚqr{*0bj:ܳ46壓,x>8[yOLc KޜU˨iے'(8I޺i}r|Iy,fStHw!}sԱ7e\k%GҚ"WG(@Œy9eu DrjFťf;_tqs-5R24rOʯqȣ"LJB?ڕ i3ڵYQ#MRdR5 Zɥ^iwڧ!2^D;QY:V+5!v6ۛ5<&-Wgl85wF:if[]>]Jn-QF&70r9]Ȫ:G.l`.=NmBvϷN*ծ<K`i]4VAn9ۏz]<ŭuƺL66)n2\U~^E 2vx [K{{d+'+mGgE*~ < o7W6uwv0hڭ)$nDE!JA9.R+k{K;G*K+ 'x$玂K^Ĝ}+ޕk^ҵ=SLIn!YXc@WDFF+-5eٖVMH2 L@!'7QI/QmF >Lrmx\em:Kۍ6^Kᱳ%%Q$|=g(𖓭[H-e`˥WGG48m&v|ep_''o knn/mc%aϴyP~OTM+TtW>rlOZ‰of!Q`'ױDƷ]NkQ$ QNnQ}z_%ˆVGrO ^4\ e`=1Jsn \{k֗O$H.\gj+@x+O ڵɄQo1v=q5WWLZtoi&K$kXI$q:F|I-Ngƞ/=YF?+_,K% H3ZaI$` ;/uC,,VA$lGQZ%72eJ÷ҵI$Yq#s,?U}o>j RgyI=Y}+V'-FV2N]x'㷂᜻Q.A{?JKn-L9{~5&fR4,AxA;1]Nqlo.5-a\+e7-{s6v3c@+ܒcm@?YSGx$~Q-nNg};5t{MK(BBP23'cM:qb>j93ֵxuKB"Ȋ2)'~Im"̲\rJێ2ўEu 'ΣhΠ"պ$Iyw 8^FNq\^ s+sw'y=VX%g lgA֘*p U,d]BtK&z'7>¬VRѬ+#z=85Yw:`NvP?R:_Kjm 壎fyC$N<W% jٯ멥XI: Qv6wCi{l'I=TH#co%FfCPxA`y^  I?Oz!^*)1r뻭On#6[  ]Ķ3I=G^"Cifg(4BYL$ >A[mc,,Ն'a߻gG%K52ps[}mGR)e:el\&5 2i jzךꍧʳ]mԻ(RY#g_dc[SOѵ n[Sw5 ۰NU⹹[~a(z5~η*6Kk*y{DodA:O5M &Úޗ.}qiuq]YMɊYa2# 8)1b8z?Q)2\cZϗ[|>wavCLcΎ3lrT[>lw஻uY 6gqzl*UڎŀU=諫N5,&n}1EU $RH̩leQ# 8,9#=9TQEU #Uk&fm\nQPj@! ( ( ( ( ( (:[X`nn!3u£w23z6A7ePHYE?CVȵ >ܓ K2kGhQ@Q@Q@Q@Q@Q@Q@[T~R?Z_Oj.Ldu>Rc*׎t>ibx̛˱>R61ՠF?k-] }(㹕qԏ)7Wc5iO{lĄq' p> ߍsZxFjHЙ0X3fǠ^Oigof#3I!n=c~?*59%~zfP(1FT]cW-WڶBp r1g ~b8_]>Xa,I}k.5o*82-D[6H@y sr$vݗSqө ; lw &;Мuޙ pQ^[鹱5-Ĝ(%T`w5w22ܲ44מ?iw5ռV\W}])}9 k4qy8ݏºouf8ӇC}VоQ[ VڒYUKZ[Knb`0?u0hGC_?t;C#qȑnJeβini)EL ;A=/1eu=WdYy(i:`wna Fr^)᫆Vlqz:Z9{v7u(l!w&@ÐkoecqZ7 g ʽ\ԠWE³C cr^(-TC ˃s9tsY4қu 8k(?"0;' 5kE-^OwhXZ)!#Vk6D%ě :qB]<7.創[x3lO8-b9uGZXXx{NI$%eۍIck¾*hů6.)1(PKr~} Z\rY%i$`: x|̖Go-g7+ZvfnGIk[Po H3!TV$v"]UC 4ho+]6KeeQ"$=l{8O%u˻t@̭JӁ:V{PaߦU rr;Hȳ&m:nW?.=Aی@h"yi}JgR}@wFNzׅ:\һ=hϕYUyZBYmc=NN2p1=+S\ܖzV*LpJb7l#`Cň;Jw9j֒Xer9;V'oᙀ;皑oDGm%u]Y$FGU߅NzX; \֩2ϩ\tK'nڣK-iM<{`Uu j  4mpOt˴0-\xk嶞7W[Df}P{Vn-JXZIHi;Gr @I=Mď[3Y,`[@OSa"M}OZ OWnsi^ie!PS`-,/'g\qWg[U /a֯c}3SuEHS,o~Jlt:|W,imt l ZF# '55TQt)XɥHݳTg* Hӳ^_?ț;?r kovۏũ{Co,x#оuըZo<wjY$IKEojZ'Ԯ;WHӸ*0sO:5QwI?Uso_O q?&_&+/{:>? 8?FUA?Wek{?q?&_&FUA(#o_O q?&_&(gH(Weho_O >>? 8?FUA-_H4sPK(+wS 5S%&цXdU*.{"x8z$6+9[d0w8'=$WxR-OIn#+heM^  (q[~mpaսp+r9Oyw/?](^?;~.wyG"+9?EQw(Oyw/?](^?;~.wyV-dm9`LϘH`ݟp== =ґ*X ݇'dVFx7!ۄۆb_(liJVv=ú٭#41wC|889ѫ7rq6u9 2WfbAsò3Z5¥viPV?Jt{4ZV ?}<ǝY.vt2W ;x~!jZkY1޻_=1JG>bN*Kfa_x&|%fMP!y'ޏZ_Oeym5xwp ?1->jFs lZQ9Ea^<(ʼ❟_sѝXRRfuk"KLFH98#}amXhwj6G4,Dny#twz -<`k8 e3Iq i~ϬG` ,7Hֻ=5c{yl6"/e4^1Ŝv$cz`햟Ga io40O`0O^z 麽wp7LWɰMV'Y$Qh=?ZMrKfVoz#&(inetq{ t^0iD8ӎr뗉qz)HB䓅<5w7pUcOZ\wc ㏽굱k;iZy$" p1H-:w'ui{y8In0J.$V@.m.%Wي7?U/ 7mH\s,y +s8[ܰ9,9,˸1>-G drz\zjJJ +x S1?im{Xz)>IeHqd *s::7Gf"u[jHv61S,ʠ'h|Kv3\[#|pga[z?cjzti$(t=Yz}5f_z\(SFu\ג*?6?C gs͟|n* gs͟|n* gs͟|n* gs͟|n* gs͟|n* gs͟|n* gs͟|n* gs͟|n* gs͟|n*?UlsU6G|gs<gs2=h6?<lZ2=h6?<z(6?<zL>lsTyύ9Tdz>lsTyύ9TPK$ŀ6w ;فFQJgh*k d6P~T"dFOK 8ڣ zCmaF|0 eF2<V%vOkMg=H6:|׊NU&T=GMҬ[+W LQ*޹_qsFd77sfۗ`s73 A*~g^k 5I&۹XOJh[dGC³UUSYzvb MkhN{ɴ刮;+:}"[u-G)np3j 'K@kLRdI g޾nKò\,J|"_O\͟bϩ.NKBQ\VZOe wj&k1,ሊ02 =kᦘ'U$iRļ qۥz}GE/F˪QF{3u%s忎𥏋t&( c21gsz>/_GV2[?kCn5߳US$#Vv=I$ mϨOG|!x7-[7I#BLgTw'Mlje߹|2(V7)-4LsY2Oy)4-$A=p;vq]#pAye-DJhb?FOB6N:N![k%mf$ zZ44LϷ9b=8-i5[hb˵ ̋z~5͍K- A/ݤ_l0<=v>>w fL2"&AWW0Γuʠ8$'^uz՝^:n|J眶;=kZ/9ުFt:5ыYO9\0MDžܫ95xӠhHBɖLH9 p21:f *`._J~цsںW.7GJpӧJowuk*n{WXvm3!l.K>en9aORRep>i-X###vPgY皦ۺf;=Nb[6Qۦ: qáVF 11_Ŭte>#aFOHjHK0f`ĕ3TU#tyb-I%T̺ni99u5%/u P$8pH8s[~2iyS\a|=3d\-ET<;68eAJb%v3l=%-d1渚=7a `` [B&:L&;Lď]UB񐬫nf=ko[inY}}Vm-\ot&}^љ²<@0{lczTȽg2 Xr8-{ȧ 2F9sլ  8#w̴7m@<3Bҿ?gҿ?}g5Hhofc-hJ,Ւ9siq,R=ؒj-2JBq-"S=? JHgдפ^q}Ulml-)ayb.-?̻#"V`]>9Y͆{IUo-靤NZ%χ$Iχ^yk 2 z{m.4ԋS0A$ zxKA]VVunDOjmCr ^<е?.z;oIXd[ {HYpA\~G4kXݰ IH\i Qw̱$woBcAS!"^PYk\\VwX9Ǚ6}5hxg㦅U4$0 ͠?iχ:vxSH5xmA/|NrH* b3i_ Qχ$^T6ֆ]O v3j0Y/R< dq֮帳}v[ߴIݼ! ")Pns5Bnקχ$USGԞ4ijKmuBHV# VOkvzލXk;&?(,k慏oXGn+6TX \Ot ˃f@ݎVX±Ep[fTgs[ְ#9uxZˊ߳T~1uE6 )|{+ުQ*~ֻ>$Xu-q4-c18Y[N=GG< &).&ai?ѹg8 O~W62.TCjj|gUm촫r'*jkl1j6Daj_F ]$֓G=Ȼ)Oix"1n9RxUzfh^vOzW wC*$(,ͅ+xƯӵ`V|~uEJ1fz2 oMD] E5#6 k:4kfvoۮ n]pzr -@0sܮ0q ՞!Mx]]d!0ː 烆]a %,!91r*wFz6>-C&&?,Ҽ.uu6=+c,劃-`R-v2wyf>,_2vy5ǰa@VoG2’|,{QGp9 I$>e,h[+JA8_鎮;}"ZMml &!i=ڻ!YpsU<%ίPKhN$kǕ5^;D¿q3'+_ϣhwm]2|XOgSXm $-գ2vGpO;qq\U*hUH'I#I t c,3 ~p~x#5aIMXkRQWmF.eIDP+*wdF1\#\K-pf"cs%v#^ǟ2gP;.p;}+amG# 8#8q58NO-1.5Z92-A=ڻ_\j\X]Fme͏ uݜq^kJ6nIe쯖Urk.F8FBazg5A+^H֗e)YXFC m^ҷۍYmKGp"i$JGn ֮\1r[Sn jK;f; `d{`cjPMq"vsT rxm~+m4ͅp'{^5h-gZ*}2C**}YFMVQ4daPGv}ҶާhVZMN;Fq|fmZ6/-x佉ۤξA]|?]? Gy%UFg>h"00 ^9}fQ,ysIi *g| xq| 3VuԣN4?&{c.yG𮕧ΒC cW.Ӄ}3WL/+IX rqøm8>j嫍q^ 6@N 'tQ,ma֣|ܺیz珛ރ5!2,*}>Z/|]Y,1Yp͙{Oұrqב*=+Tk ImsC7ob#''F8f[{JvV$gUfwgv,rI9$kקMSI#QZ((B) (((((((+ltEծciRK9G2d1N Fstqɭx ,-R%OnȹI#5?Vi[ f t3=]P-="XI'- )q);+9n<#KXXm sT^7FR푮<) p1v8A@Ɵxr|ꁝ,K>Osɭ6A0@?P?[j:ns4-]̙W$^e3nzn*Ůnme<2} G<ЎB8Q(#H^$G4߳{q1c]nQ_ǿ r#Z"{,?>8OYjWSd/ڏKIb*+~ Je-,(cXHFVn=Ƽ%MJ-eGnFA##e`~GߠշBI)URsОVY1IR1#UvMZ(ѱ1.IZu9Mٷu9W8Kx:c%שmms>!M:+xc!p9=k4=Dj'(E\D$Khc;@?z2^Ȋ6sM6r6+|y5STџJR2>UܝrGs\?f j`ad҂.IA׬L{;;;yΟz5Ezrds hilV$gtrk2W9Hùh~4wEIan~9ePp~b;u=Ԛ`e2pL`6lƭ|>!>Pr#W:|==X*g9x5>vf{<}3\} ZhHrb<NO[^2!=֑#eYZ_;!8azE,GpK- v^:Wp!er ~=a̓!Xxr{s{|UұxIMwIXiD F}?yTViөg՗#8bƧqKʬɶ( L?oYF ce?6Qdб^^..!'A Fq]k=M~ JA(L=9tSʍۂ07@gϵ^[GP[±&vsA\Up-N.~톭ne4q %[g51>S[eM` FzE"E H9U.xA=t wHZF9G$~X1K\,tg6mc<۸*Gw Ll, ?yHYMmn\~GFvB2p1LiW-̔ۡhNܟoO^өR[3ʝ7NVy!sngki-Ȅ$ qt09Y*#Ok 8g2ybH<{*sEwRwH BAN21μ](z-BR]F|ȺV# qRG$`fjz}\O[%|"gǢG׊<gm2y63c8>f_bܟp{hbv%%.mԭX6GB@sxb|NH; Nh c2ȫrA!` 6&-[kHknq[ӫpN+%dd (Q@Q@Q@fotoxx-20.08/images/unbend horz curved.png000066400000000000000000000247751362435004500205350ustar00rootroot00000000000000PNG  IHDR]ٹ. IDATx]IeQQ-,[my" ˜v&X,X;`ˆ 8  ؖBB$#uCUuWߗ_<Ivݓ'/^U^x?/NҪ: D0]9j8NB:=ݥ,0ةNww!3y\Ӝv L4dl5D) ݀MSIN$: 8o0&)Śxy ѣf4Kq' |||/ӟ ?>أ1TE*Q:q8{5sis4#Sq?haB!9,{oe RaϚRRvR`,B'Y8   YQStE`ՙ-h=cW-WmC\#y92CIlc#̹5W1]jhS&DяFLQxttto7k~L|,GGh{^DBU\hcߣb `lhbv4{mzaȐэ]ޡp#,U&@Bp`<S s_طz.Pw^Wn}֗SgTQCɨt롣M\1os>Kk8{ڐ5辞1qNx kL o)R I,N5DjIm+W?cǻ(r"A! " "nAg&PJ8a@{ ' GjIXZ70{pp@_2;TO5ӟ/g<)w(|O2.HuxhB(ym^>̊kNښaKƸ[jjZH2|W@9+?ы XbjV:Eš|w.(:xsΝ;xŒ3ia;ߦMPEQQb*`Ti G/x>KB\n݉ƞ@=yGMf*LJ3`3;Eev@i٭ЂSsUxC+_ru#?yO٭&9~x/֗9V"yӌ-'RӊǏ<~!ӈk½"Xˬ /EeSI}ufJhmX3 zkiM#?g8֭</M>L7O@Y1𴊯`J! tq_}"Wt9/VGx׏ن`d{PJ4-<6gJ;C<Օu2rD~"uި`K>mO\ j0RM9ko|[_|vc'uJRڸsȉ7fZUg-Qu nsYwvϪ=~W!}̔ ;rYa3tWPZHoR(NopOee23tpPqFO"CلHP<-ȵҧPZ<ư[s?ztt._l헾w?C~>yy'AE6Bmh#VyZHGlʳozN,+;6J+0dn?B)cƤ 0sO޹׿oK9>ޱoh˘CI^2ibf}9J^y8_N*TVq-zɿBl媹[ !;Jݹs~|۷;qaNӨCWϸ9 Z,;nAM 4+9LnSiaקּ[R !jxj?̍1wo\Y:;pzxxp\/p=I_#TW㒚!Ԓk§^HOJ6tFZbpsJ[4cP]_:>k"γ&3)uRW Ʃm820 uq #Uͅӷ{+9ڶo7t'|1wy[\%de98P56emc!xkM{[lrW贪pӝn-+> +M8GP#gjkC_9}p65G+Y-‡n~\ʣrT{`{?=Y|/> X^, iZsĎ땪jXR>#]Qn=S¡4W#YCݨݶ7>VYLbW&JӰM:L-M/yS;-.*nIV̹>x"|s o%.U{|0SY\GGaxVL"JP$L"s a'[k39CKVT*˾R\>X&%[Y?p[oyO&lyeP{r%oCmݑNJ1L&N,XM,%BkslS?xZ|J|{wT+W_LV.RUx,ZO^ϚS\IC@W): q?9B7ݤs{o6[ՠ͗[g(a5MXXj\+f4)D\gE`RVXhV#tropz(faTl3V>#F0D>? CD}xSNQTzx\{,f8MޛˌZPdGڥ(x"аO0?O.<)x)`.|XMP/GR$y.[2uvࡊO z$]<@ݙ5(Ʌ!{|5x%~qkk5a~O&@lG>qFA'8ԴN,1]A,?Y' T ?1iy)9ZgęN`NMpmtO}unYQZcidXe5$xypM .>jBI[j$ -qwKl@F 0:kB)/Kj1(Q‰6šTN-xȓ V~P墷4LbQ9N+|Gi!VXfɁ>9hTmkc&*,#"F{-H c^ [k I9 vRۤĜh=m߷ٶϯy=dɲQ0%Udl#lf˞B~\igb\ik(|@Ù/ڕ&Cj[N :,MPwB> hq<=B8a7y{ V6W@gPy$VZکIaPaa=JkbM]LKË3t;zjv[(63-c:]@~cV 4aMJo[p>!8Ow6SoɷjjB+#WCSWbSALZz PjrPe1)'ZZMW~eR}b;T?ȾzBrݼF[i Рk #GqLE"vLUUƶJV9 lB~NPu}dcKqPL$>tkU8wDVcJ3Bׯ_>x .}Oqݝfx; [E-{9kG~[;nSO%*GiQ-ᄯ6`9y?÷7Ϝ=saWY.>{o,1P:0$k9) ՙN7rvu(*fapRoWF*J/|;00;ij/?oW]`,>{gg|6,&OP7Ϛs?txڦM[pgVmR.!kd YQƽ6'78'/EΡZ,QM$;ƝХ+QF%>1^|>s6,ov%c,2RRLb&p΋NsHѹb|mIy1&M)Moq$ľ6;gΞǼuM%d,jg 7aRozj#c8*Þw#p*G u&㲏SVxp?&`2Nϥ&,qP"Zwad"7(u eSMb[<| {SxudZLx{MaU64 55uJ,*1#l["FOD3s4+=6T"7/p%ܛaʵS4?W/]4nĔZ-yRlm*e/'IkMY59379k+Ϛ.N5✮ڱ)k'r}{+䕫W0GI0鹃vLI*#l9զx 9(T;1id+z=Y(\8Pk6Ue囓7^<9tm lؾ1zIIYRɪt7m:ܶHs:@W?^FxE/gտcA /_ϟ?wlb | 1ArocOL ΰI#uR"ABDd]-&U㲬 MĻH| ukDCt#˺г+/3cYcB-1Z=EhKp2IדVt\e;X GzW[qr!_5Kק+vL"mVv4IUl,;l \8Sml MJhkucү gɤ"1+t*sG+Yㅋ|+Kw@ѓt%\D1) czx_B8& 9MX1 4i Vֱ.p ^eF>ջ|/_tS쮜 >r5 !էFx& kA xBm ڗMAi C=Wk%&PɫjŒ^@³͛7nN7S|.^|bfۘ& L^sڻj|(L̦;kƍg^/k`?O: j@\K2&En'˦Xag x%:&ZId}?nJ iPA¡t 6E0T>dsFϊr2"z&O{jLVhu $2wZS3/o=6o{+R]r^>,n@Α-PECP~6h't9t:WOu6Z|KFFldj{d(^~Bj);;xXw7ܼOԕ;WnC#é#ezЎͬzk#.| [} IDATdGC/ C")džFBq/|Dn-Ywv_]}胟vFTr,g ޞIř ))LwLa{DqH;/ZlCsJI%/Km}:(V^qVg?[z%2rY0t|{Of@eXs؉JFQgG؎W̆6YF(ӔQխlAJj Yi 0N;M2h}BΕ+'(7Q`yl'Sm#xw+?޺e/p"-;Mtbt==@[vNcp-jxl ˠ͵Zi&v)sT%~ a\+ fqxgUrq^_O祐qWr/tb$i37ڲ$'VqT^g:ӟ :8k6,֯1'rN^(,}},X>p/ۑ"Md(|͋x'>?߽x⓸x ') RZi8E/RvbW:7:lo]z#paEOjѺ[IrcJ!AXQeU&P1 C0‰á~nU<4s`>F< (#IlK]lLvjS5O1)P%ݼ&))AM\\kP2r x nü2u`' 0~еEi ^7*)Y|sV Q>ąʘ~FsJjڑ@C v}=#6Z 2a3m?EL^4-A21QUvK?LjU(wpa2>?~#w G?ifk00SL59 Yb mWjd Q8=_n@+/A>&(\. ,`l)m'18RǪή*`s2D)MS %|YִAߪ1mMa䅰N5]gJFΜ]R`C&bZ+FkљLmVjmz2 b2m?L6zTXtRaw profile type APP1xPQ =Y4Yֶ麟<_{}e.{yM@DG?"RƎhйr鷳6ȧZ3r|eQ%E >^z{3#03&6VQ9ȁKOdRo5rVd:ɸ"6]", ⵜbt>|l![_iTXtXML:com.adobe.xmp 128 128 IENDB`fotoxx-20.08/images/unbend horz linear.png000066400000000000000000000164401362435004500205050ustar00rootroot00000000000000PNG  IHDR]ٹ.JIDATx]ےnyum 0x)nqLd]{?~p0_epJUdS^xe_~sNnvM~r{}?~e 瘾1k.Eo7pG 9b}2p`Gjt jb̽ O'͏W {~ | ^% ۫o`z_E8aRү^]LokV ~M7A׀qt4:-A9W1PAFTU:TȰ: g4B (Qi4O?ډzfZf.59EjmtnG|G0((e6*8Vzv.AɞQ/㪜4Zkφ}ZEfr@$.nM%$qrC6cFm% 3+ ͘Wv_00#r|-=:oB-VJ|0[T!5`*#(G ܫT H`#11vg\;qԜ=~iK1hhN]^0ۯG; |͊ ,6OG ; 濔X6Sa9n$>:dOs+hPF.B#]<:+4A0bf:R,0ڂt۬dei'Y+F:(s=ᛃ;/lv"֏_B9T U?dRFtlfm~Ƈo? (m^b6 Etk;{u8aX9}阝l t7r($H~)m(Ar5?``7kz񎎫680!zjĜ054KM969xAـI h̘?N;`̂Tiـ xjسN]'^O٘K\- YU;1ƣ04~0!ϫǩY' k-樔1-j?Rk:c>XcWnǩߵ:vPmOiIyoހk"O c+IF#řQ_m9CiǛΩĝWK;HW%gt]`D,R=jؼ=8t{w!:?\X}|癏з^Ǔ cG&&e3>HݸBTKw>+H/E;'[ (ys /Oz_PrKԚh'^b Jjf`RG:æC}_\rY7|F},!oJ1S_M5|d(9nNcxP +u+!&g#kku M*;rNjkn֧9 #.9^6_'i[m+`.`1e r19/<'z0uv_ZV *0[n]q߸lj5Qjp*HnY&}tq4e*XzpxrBiILo>vpjBzpșI՟iM1HqC<t뾬o =r/5f ӿݾq9^4ߵZ֞,} =FVn|uVӾ_Z\jj6]3qZ>i0.81{o?X6)!S FsKh% UAis' ǜ^ީ>懊3(,܋ -vJЮb";t.l"m6߯QxKBs䴾1ő VVS]]L9~[x@{EIG+E=#66}pQ_ܚIJf1]Lxsnf?a] <.LXXBq}؊'fם33>=4s0߾ESw7Zq|N1n f"M(""x+?& ꑿ(pX3l4ҷ 0gjֱ|C酪]d%ܶYnZ@-ݷ%Ύ1՟ʫZ<OwB])Vozz[~-0=X;.6/=0{8-RFIi<1f-W1x›pRx{/re m&hu*},a IR&XD$8xOQG:6㸐eOKn K P6C>62%X&X z:vߐGtSJ4P0|ZΓfNxBPBp S(^'P$m:cn-RM< *Q,9z[Qq`LwmVȣ KGbYe۷qJ׾0'7A ?CGaOAfhvh 21G Kp+Jt4Qs", V~ăW^։ekQ9[}-`++ #6g8A1BV{x2gQ)C ;X ՋY0+0dg A3 j.`(\ 16!ж\! $Oh/'nЬ8ǙZk}jn®^O{Uo6r\D$ M> ry~~y\V]w:;跧v{A/m4)m`! k"|6"n se"7 1fSdTU ]RX'o$xIj(bt 9?qj |P g: h[ᙓ=6o#rj$ND.2ti(8t2G_YZx %Eӛ)M6ku'q2[S }nLT/rX,Լ~⧹\;HIWG = &+?e 7 u`ZA\5fצVק3~tΟۗȝ&069`unWTO>-s+ذ-/ެYږAFAF޴PB'z-H .UFŅtYB?,B(w<}A*^"]S3b`͗Y<%<K[ Ը]*%0z@:|D!pAk`?a.v)=i+!_  M.wH2||}޳=֜vuMey\T|C3_Ox:|NX'ߗg'"f}˙BCQycNG8j=K~巊p gihӆ Uy*D=#^Mp=2m~1>h^jѤf+m]/W,şpǓtf +w k[AU6 r"3/߹˻ nBq~Zv1nt ?]z2d8FڡޢHS?5\; X*7=R>G֠qƙ)[,ߣ4r"GIܾe6ƥL -"G4gXK?Q&'UFhÑ^>G8bǙiޒʁc?qN.0B\ "XMnsщӱp"GpW`n+὾R;`+<U~Fѿ^f t)rC:&ԋdR5 EϹoS~WrBklRgܪff( OXwqQ.\vO7ߚ+lN~t#},9{dS134Zi(h-{l_#6XLW+n*lתO7m&QG}ԚLK۸17**[Ǿ [\fh*{\Qxȏag)3~7s4 _|O7qbkoЛ\nLzXᑯyp0yw,gAq Yu7O<*؊Z56Yj pM=6֍4@vq*5jyvU-ǡg'Aبk,ƂZ?)bѼ{J;, 4zƇB*t m!g]*z<`aWng +v"jU mMr Xf/C+t$m &Y ³@},񡏢4Uy!b~+2MM^zR4] ;9 dEcӥCD喫7M0`/P}?nzv9u,Uqb+#!X%/ R34s50ܮe!3fYdۍOSbg%Zs>Kq zTXtRaw profile type APP1xeP[ => Ĩ?\6'Y} ̽<˾彿(fi c] @ y٤۵n;ȗ7B],Mq:\|a8r 30f}lhFCn3X_K}$p+dE[$@ϺPy07[itiTXtXML:com.adobe.xmp 128 128 IENDB`fotoxx-20.08/images/unbend vert curved.png000066400000000000000000000176201362435004500205220ustar00rootroot00000000000000PNG  IHDR]ٹ.IDATx]mfWUwXFNK "Em1D-hbli&`S5FԀ1R,Ж(UDP hhL&MʏB2g:23zZZk3*{쵞g9=vvYY oGO>zq4T777/:y-Y{+.SNwlg2XݻwmݻxN>n|neK.{-7?t;u9I; llllx/{+{Yض9=󖷾w[k_{~|1,5؎}nW`}}}*^rzNl-h@sxrl5)%PTB].1<{ۻG^*䰶ԾooEw[_%Ծxv1c/m[FvדX|6Rn:䀬)38_){'&b G738z۟r#ЗtصG~;o.xYil0D̜2 +m~lzq[O+j XpM^,IῈ`>Gm5{>rk_4K@̜[1ɐ;:oDhΕ"+Qp8n>ҋgD4=&\DqC/o~QT`:a^vOqsld351mc>aԬkڦ>2%G6]Ұ|lڍ:G8Ђ6]iYr孳1w,IJN 7,H(W^20daSs4 t6M9_1bLkr@ ||p8ǜ 5N2RԀXǝ\E=@jy].Nu~Vg|Sa:CEo yŘ= BI:q ЙyYZeFAV>Z|[5#;EVQgZm3+- X| 5XQ( rT 6<"qL ⵾UBO<"v2NQ{#S?g܁b+`}zL]Qu+"\~`de0,\yq5ɳd6bG-usSc>0}1 2&V'p 1,/&IGrd|T琻`lX=lAᝁC?V<#ķJfݿI9SE N0fDMtvB]$uK5O- AFȝEsX̯:=ưnjۘPF%upl'MQ#R\ uEW5%%lNn7SpɑM ('!(^!>)TG$O|nQԀY9B1a6HhkPM1$⧪ .fG"%b^!gm$s#GJ̯İE z<ߑ&ʋa>t%s.st@1{4 7b)K84䅯T3բ]+ڕB&Bj1PjϐGQp `$7K3u8r7LSbs噭OK6Ug? L6QWŕO~B( 9q"蕨-| D ʨMfZd[ɰ'^N7dK q7=wke;h>Xd8Q0 ڞ: ^Ύ 6qq=_H ͟#k>bIN,-:%ƘuFa.¨Jk3N?Jňq$a]8비\ \oAEVMJ͌~IviBɢ5dt+k̍}UQf6KE89x.d_D hW;Js+oFPYT5OB7<+q-Z#|Ĩ7e:"6×d涘ڨVy a!l~%&[ bp]UZnyݣ:`Þ8Z<Ӓs4Yڑ19YZ+3岯>)4:|ڴLj\uŜ@KzIaJ$ӢN"}jWV55'#U/uNǾ"q@ Yp 32^G3;;|Pag^.ٶ0(9F@e(v]cծbsG>TjC E£╯@|OU|jx>m4=-A sX!T*NҍF")ia5@k@eR1MayQWg+ՀsV4Q/2HR"/j_1ηz*SA#(2GPm)LqP)%j淏1r]wdq2=>c}#6Dͣ3+%=Q2p`Z~;"i^21i[7p"| Yev wBѨ>MkS PU(v1K4M*5"E/&=o0sَP0 8+'lr2OKkiu!i؜=5!NGeuS>+[vj;Q&U!càƸ* ͣ.;dX,uPT >5s8IζG݅k,pk\zwZB?zU;7eª@@0PF*Ɉ!Qgkaωj_ro)?O)S..˓ٷb}`0d޴F4Y9I%3D2g&7Lj1v<=Qq9!}c`qK :ՇOH U Ͱ"OER Ҏ 2:ᐫuy^ӭ|KOM#CĽPlUcޖj2-_hۆh 3NVHî>F=RhQo 5tF-!^*6TH1iE#8Ѓ#C펨YbVj0L!sP?*#PEB[g2k 8 )ƫM qN -of {V cڣGi=kcSx˴V.S|8s6b*ס ~ۼ}ƷXmT%pG3Xro)NkȨY}|y'm߆y-_QtTkXJk.7 Ljj ?2&#0!.59+A0aTcӀ>HkatDQ䭒˦8o~-\Y|lw3 Pm&.ꕴ2Q$ %[ 58Nj~#"4).Ig`0ɱk@Da"+Գ:w.euQM׷'MIy&Ѱ#FaIzH!QrD=q!h5 /[C')i|PR8fU^Q_@.dĈW$ɓF{Mjב.^ϣ>^cyN^/_}0!YL֤9I_si639NqXXUiUk.oKpKYb}p$Α9|L\Lh$#8%2SC JOiœzѱ&YY]aF' (C lz%P [cعOiHp['^5jd!yMȃ ƀȋ,D,8K/X\an`rrx 9|}?;vPiC;..$tD\>*)ybʚ/4H &b? n׷9%jʡ1dh+@:9YSkkO>ue"~ƻ_s+~pŴ m{|eY.?~đDv]ŧ<۟{Ͽ*={ۿ߅VqXk\0O:}/>MGWoWH=/E׾bcccscAa*i 7XzGOȋQ}{V;C=k%?#L׮ {26ɓ_$bԧwnȟb; |sov|<寸xӛpϻpYYYYYQqFzTXtRaw profile type APP1xePQ =Y],۲ ڴKEwoYhn@`$̠s!iDlF~;8"xt5CO}]x]!c5:"׆B+1g8;=K8c4$*Wؕ5*) $Q3WlZ}/]'|:iTXtXML:com.adobe.xmp 128 128 IENDB`fotoxx-20.08/images/unbend vert linear.png000066400000000000000000000166071362435004500205100ustar00rootroot00000000000000PNG  IHDR]ٹ.IDATx\ksm߼e18љ4(if֎D y]/>3_myyk6c<-? `Ś?jkitEY_z |?lbRnr|;Φ_/NW8d\慤֡-<:k6MX=c8F}2ߘY=+\hDԾ!~Wf7>d|JL;$vDAL*/#RDl$$2o CSu;?ZWNC׼z:YM]M ~YclOmc'yu^nɬtt3ϭ{}@xZVbZ'ߩWqک8o~ܙ{byDD }:^|D^D}>qH >o{,դ;4͆*mX7V,PtSģ 1#ВkA$!-? ګvMW&M/2F 'WUQe|$k5X^^z8a|pӅ=q?| >)_j)9,;6Kn / CrWј=w4. {sXOĴv.%s;iZI.1_1i)YyAYp}/VMHM@ݤP#~CO? I))W'f(Yc]r9&J!'!PZrV ^ܔgV㭨P.>@ $!$I)%v?Hq!Oư}k|mM9mm=bĽ .*^u`~Û.Ts_e4}(Wx~jT.\ &!>3f.ma\y\IKX傾#M 5Qda*;(ҟwJ3fs5h|!CtLe$V6uED@{ӝm'uF[Ϳ_ iEy+~ &zӬ"Vo <0RVL]$4l zJ5Oㄤc*| y*hC T*Jmq-JuZxNh{Ni  "pC1<8$p^Gܕ=n[!ⵙ?Rs?7Z?}sh7$7Kn4aK_H1؉X1gVzdUcFs| w$<#.UjQJ hh_gՎ_*%YodA!V60e.;"p$Inq5z# >!i$-6Xr~,' ^uofǐ(΃6cl18ȟ7Sk͐S<trR"JI{@n.ډl}o nbuw[S׽4/Tamd1q0 FKAp[Ԡ)]ys Csb:60L6z`Nrʪ QRꬂ87>$QƋss[@!ow^ +̡;Cbrߕ% e^m`/zoΡBPcȍJ)D(w =tÇ_c w>i wéjԏbߣW)EᶃJNle0撋:S ST( n#6L)OH@nn+[fM= N?K8=(-8Lv+x5Õ rlyx%a>q#(d' %VfIA֢1JgjQÿgpOO0T9q_(ODž둻cL95ܾxDy”a:M<z_}1PTTsT'@sW-;qpGU<\gqӃUl,~ztSTJ4rdUPFJ_2XJŸPU@/Kkq[V Oڻζnoѝ҇唨XF2_L{\c f|i)J˴MKvſ5[閅ŢыMWKq@jA=RF:;Bh4nqbHro y >6p{j1eZ  ^Nؠ+d-lGŸz^#O#?[X.vd}ʌ4jU :餵Ҧa+3\ҰP}.I֩߄u9Ģw1? u-`*e;*|ۇRus{ua ^oGy Y bJȘ1n8(b:Lu15pw6b]UFV;Xkp죐,3)> wtNNTm.*ag5Æ l9A|4Ra짚ػ6׊#$qNAxk9'(H*֦/\-.{jrt%DoGl`F%gIfH8y:&3bb_bdt! !%7uZ ĬҜ6Z+UهRъF'(v+0.%s6sj#?17bW͙ e4( +Y{ ZifXI&-7KJ<7-6ڌB):D9M'#8؝OOsf?+_{o^ܷi&/85BN޸} 0HGzPsd!tq;^-B{72 5W#t@;oTkQiU)Yz2 CWV|m1rճ/D xez2N阽z!A6[e w>o"gn}vRJPxgX$p_p&WdثGBD8-"-|׹ E}xz!/U%7AK9.ދcx'x3bXS sD19#p_%jy<2ˌDAd./8>(:;SϏZQo]Vp^Qz]NN.|FNwCQW$Uܞ>I^MݸDAE<@f̀+*{1} |c1 Y8n|߻ `}+̍Cvd_XS|*`?bsH;~M{h3̠|@;2̄C^OT!o+*P#:X!pTF |/ 5On)L<0icy6!mh ދBDx{{-  31]y w3.9OrhH[הA+~ZF~kd>3cfjVϪ[߹Ͼ7E8V05sH'C:Q̞؏YA;|xjE-Ѫ_K?ш yk~D7i*L["_c Z4rIvÓb ?ɌIYJ$ԉ"˔Y`;~\C~-7nQQ-p퍎cR䤉4aI{ũ[qžI6ύAC@|&S]m¦f wARLO9|MP?iD3`/mK{Y8{OJowG;ދ̪q@Q:aQwkԫHng.z}mLOwn>L?۔=nafzҿ_$\d rG[ ,̬ s*X£> ~?. _SOzg2B\bR 'f63qgT\̡PQІ@^#MZZeB&Hv4#Oh%'G^#ȏ>z8zVf?qh!ͯ3PN}~k(g B`duKKå*Wk{Zr-s8LAgJ,wwOOMqt2A'Q?NPXd1#v jY6CvX )&^} wރ?S$fPg| Wʥ:a߬!ky5)5\ Q&Q"${XBŎtH}%/CgC)5 @h!A3v=8kBqF{'9D5 _8I`G Q6i& DRU`'`lKOƔD%N֡_e0lo+H)F_A{N>e7s"IJ ڛ6휈xD[Ic)MW6U߬[R6]>pI/PY}A1[˜{ P,kkq|҄FxG7 Ƙ CM' 墣H_z_H̨D\9+ &AZ`!ɕ) R]*4b/{(P9:#mmG|rHLSnu]b \ q$fd_E%Z\7"q}]}ٹb Rp\NqabxL$n! Ft,T=&tUV`sd=56;pA1'Ew<(/x9ųgk4z=avm=#V3E,36yOi`; 8Uő,nl0M $'"6.]x$rʍ|x),fw>v~Nv͹yH.'D%]o!?9|~;}"9i)%j Ŭ /_^mf7Oرo~ޞ˿~{%y~?JlzTXtRaw profile type APP1xeP[ => Ĩ?\6'Y} ̽<˾彿(fi c] @ y٤۵n;ȗ7B],Mq:\|a8r 30f}lhFCn3X_K}$p+dE[$@ϺPy07[itiTXtXML:com.adobe.xmp 128 128 IENDB`fotoxx-20.08/images/unbend1.jpg000066400000000000000000000276731362435004500163760ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 299 161 0 C     C    " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOc޽9ɪ}KTAghQ;fvD\'?“`ۜngϵ{&zEìO,Eg`-G1AT_ <Ou-PrU%*~nIi?3>}G+<1~6 .GyƧof} $S@>Zqj/1foW:)m{-b%WB]N3nC to4 k<ސ:/)a6S]ݎZ`/ ׅ5xWo#],3Nҩʷ8UH,ԯKSSkh+7# tQedk o+<[6m t-Jdj95E I!ڑs&k&5Ki8$ NO W+Wݟdfi>oA]#S;ڪ"D!j/mW'f߯vdI'ֿAw|1Q!v^ڜIoA]#f?=~}kO/D>7L[pΌ;|WvG;|WvE~ZoiY hVh.M,cdnǯZGis_[Z.&bȌa-1$q1_hù~wmù~wmY_ג|M^7}~-Ιi%MBn٭Y2rFȭNUGaRki%$*7Yq5;|WvG;|WvK.|Wk`g~$ezqkHxIWg5 oA]#oA]#ӳ .ij6Y]vyH dH3БۭUNbP欧PrAoA]#oA]# iJ %8;5}|`& ,\J5-jYepɁjr7.r7.l33~gN4U/ks>Wo;eGڭJI}a^g|!;2um%!RvHeXr;q[QEoIk+#Ҽe-k5'y;H80Uiqgi ZZ@"HK#lF@jk[}?Bgw{w_m_Shy~+AZ5e#'V@Q@y$ -s-HN~(?=R,>/&D^~U,@eoGR }YaJY$@΋>C?kZ[V]6n첕F#?+) 褐 ͳ;[hIpHGgr8f'*(SRKS>/?tPOKA-O>_*@,4شpѼ=ľtO+HUrY=GV袀 ( Wik7kf$TIp!y{3>]IIn?:?"^YR7>_:?"? .>_:?"?E bYm^iemWR1Gc8ps]|uJ_MA'iXe8q_bCAEbI(Rdz2=E--QE|A5 Ce~6Odr1|Ҋ?*dr8U$Q@Q@5 K9D@#E}K_;6yuˣF8_DSE ߆ֵۛCúU䚮)$l^Y Ҩm|5Ԡoau95 UmOO=˼;}EPg⎷fi:Yym4~8vdbB*0 +=0~?΅>cZ;hba}2@W;LnWqp|U-q; ּшm⍦>ly6~)Ac I}-DU$qw+,!v;`ſMB?W7- 5Q@)дM#W56jX%4G*$5Y_1mhQEQE׍->jتTڽ+m?ĐǴ2ot sEr$Q@4QE}!.Yy~.py{uy?_hLsȯX,+7Z=&m9˅ -̲8?uQ5E0,ý^-b躝uju/5) N'8#F LE K995E,;"Q {^.n{^n <ӒkJ)QEQEr_a:W6WC=7B91A toI, ,sD''M[ߥ |T z4 z4+ddϕ_&||m S1 S1pOM[ߥ ?l/W|_C<_C hCVɲ(%v?Jlx~7FOFH ڡÒpx{ NŠ(((((kiufܺ:pƽZvuL֢΃5e;ᕭ;;IuX }p#ma]xW2\xwJ,Q3;I$I>ov~^Sk2jWXeVOۆV v.I בº 7 hu?4o\G_΍בº 7 hu?4oҸ6c#_h+FiMmm>%h4p պ@QEx’xW = sGzr??Əo@κӬ|5Deүb)L:xSFSP]eVE#訮G[OhoG?ÿ ?tebN#xU(Q@0?##ZxA#|WѾ5:^_3-i%qq8=G<֯}mG Bm&LZf'V(+a =y:]= tOϪA]Gȑ_i[][30 93TԵk%m:dh✴k hHAE| W)N.ifXtM\~_۵N߿Ѵ^$մsgx,ෞk)4Tވ Ҭ ^#RzjnN憭oE3ǽ~(t tqsOwq-c/M?z 6$?f.[@"Ky+  d#5o{K[\/b8c.-#۟\nl}MS/~ͭxY*Ǩ ‚ٱ@Yj[62xؓc |Y\COkAs}I6J&66 pZƗk|?tO -J-oTi~u c)I4F'&q\ t~/V]/U|i 'ڴ׍`&-7; JAv\6VNגN='Z/7Sm"n~=sRqwP0a>&iQkqh>&Ӛ]&k+g8YqRFRX**+O kM}6wlݢ*FexKavJ#Am{]+]FnJ($k=ssR!tyH&^Eyn5q隿1 O$PO$t~9鷐-nb?G'@~KEyxhjiW@񔑒WLהnzE1/]Ħ@ fkOUɺH9Kr^+Hb \¸;Y ]/b.cIb*v)d؊ (dtxcEk1ױ*^Ey&JNOJnߺSIK:nXto쫬^Eyx95Ӟnu>9N=̰Kln2Mj=3?n@Q@fotoxx-20.08/images/unbend2.jpg000066400000000000000000000576061362435004500163760ustar00rootroot00000000000000JFIFtExifMM*JR(1Zgnome-screenshot 5http://ns.adobe.com/xap/1.0/ 306 596 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2T" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (,R?Fo6l/_NH*ڣtӮ-NǍ>1O|(2cZ#<_Kh&S\_Dlj4Ek+7M>]=Sh*>W73Q(M"&(QV[Lܯ&ҡx&R(QEQEQEQEQEQEQEQEQEQEQEQEQN9%m9Q@ `qOuGԚ' M1_H]w[Hb?xLWRBk8).yhB?LWQkqF( xKVnFY%\w88M6JxE{ȇMvX@x"^~Ӈ]E9A#?~ڂ߯uP| /kgi="?U5Q@Kx&}ۛs,?F פsP՗QAP5dfzm5}RX!?ʠ{[A*Wl10@?Z+_W}NZG 8 K4K#"S?WB_]ʟ\Ҏ(QAŒ }?hE )@ܒm*)x?RO5j='MjPHc_T RR!ii)h{QEQE-RQ`--%P!2ɣ>Տs-02/Y<ݎhݐ\AEUypoR=#o+▋ xX;&?F k0}:c^KEMm=42D}Hu{A{>\Ͱb{`sK9$"z4v)a?CsYӓm~IPwGEt7 ɍa?+c MgkT(((GXgcA&7\m=!5au\V- v7qF=?Ҵ& 1~?tSP.c6úEܰЎ$vƊEAҊE"{T-'QWh=hGb< 85 q@Xf(--&)1NQKI@’b\Q@ E-LQKE6S01NQEZm Z(R@ )ԔP;4ZZ3IK@ E%--QL)RRKIK@ E%-1QLLњ:Lњb)4Ϻ֬N$g .^{きy}_k̜Cŷq^,9!U9&[ naEQ@Q@Wo.:'_w\oT:RȠh'SP1OU_w^Vnr8ZV-F[m̏.4M~PGo$ 8\7c=CV1V򚉢d8BX[hUe()1Rm,2v)1@N&(b\Q@ IKE%RPFhRJ(H- )hbQ)ibKJ)أSF)RRPRъ)Lfy/-bpLDV^6`DZFo1ر΀, \߼a\/ɨƛnw4s2\Ƈ݀r/%l1˪]j@Hm1t( \/UZ:P4pm?D'O8a85\M?FVsQEHŠ((_w\p:WkTi~ZiiS1ej1#kSP`W+MeeVD$@#5Qtc!'$ ] ]7>WHN$4f&'-+bqUPI69Qʌ~Y+gMw\{r#=+j\$6Ϙ=NAiE%-IBRB+ǵDAj9S)M)V^<}ڌHD;i1R4"6JMҘ DNYMr:Hb#0VQ=-HU[y˿ҧ+M"Xm%)T 5x/k&g԰G+ETQEQEuλrq\G?کKqyO74)1x)+a6P<Py^皳 JQHf,to3+>A?(zP!f?#ڏQpJLS@Ri2}M> 4@))i:})m-0ʿ^CX#&MlP+>%COQH|Mr=4\n-=K@X' @ I u`mSkyf6˜`Y\.i4~isQ/0nszb0Max[-)"nR͌;{T\a)lu¤R q(K%lq.?Yo ru0٣L4Px)hNҫ>y:jqz5oTSA} 17Lke`ІՄtc9I]W`Wx,\w ꇎ$le!hHaJQEdQ@Q@Z:@=kGrMec2譚H4mP: $uA8:Y(htE㰪 ֓.[XƯ8"ScG*x_(Prnin]Gb?>T{,{ Eաl g.ԩϭ;4NFʡi W*YC٥f;*Tln?D>R. +T;Y&|]B4}i-j킴Q7yVTڰ-'ֶ&.wyqnzS,栛W;ohCi"Ӻ4K*@>CM3rF3N J(!(Eޜݾޜݾ #jv:'^Mu \xlg{.T QNԒ4qe)Q:ÝB0Ka $['^gnNrr1KZp[qßVoܪUm6XiہX yFFI݈\źh䷷Li'Muz/U.,-D,5 sH]j.A7#Vv.Q)(;ӍW?oa&OKcU#I[,}$U{)5 M#B> k9j7 7xV߹#ޙuāǴ*[$$5IDG4R[dI!qg#5leҪ=(ʍ#ZqٚQKo+|^X=3Lź*Anj0WQBDʤgxH"&w *ۚZ}hAE -wjO]_ 9GG|d?R  秩E>C9$8Jd4QXR?1H,|j6Q4xWKrֹ";Wa,fְ)6.O!HÊƣ:F~0L=2(Ð((Іe[;k@oEnb|GD>V9ɩB`: /&oUjaIq(R҉<ֲs[!lAY+ݵVbu[3BʼnD^fy KFNs eOZmF ]Grk[$+3W}\*Ÿ|!O]T ۞6jnqȫj(󫨴dRz^Zmɛ7iЌ2i.5?GDv_\ t;YxVn4"F)#-XD$#3^m oW?R/%B$`6BNas21<61RɥZJ)M% QE/zsvRSPMXԑGl@(wprǵosԠELЪ-ƥJˆ_:y%l,{ip9dYW }=r=jK"WodJ}QUD#'ʢܥsB Moiě'9=h֊QE&h4:cWjCԌe-rF|Ly'?¶!EuUX֬khd hz~uGTzۣ#co'qһ$1߷jdlFlъfU*mwT[tH >:ny{8hi6tX# nS-eUB縭uxS+8^}nY;֣{jhPM‰˶xV XX0OltbD i]GlΧeYr^gj֏Fkt_) x]^OEF xZPM.M(HVm1 ҕ!a\ζʷ˒~fw.wv#66cjtcQaݙ2^,"X3V* ؇] iQXg8꾼 VPz i'v>H})ĮHs)`3 5!ISؙÏ7iQ'.*M( ^vJpn;Q`8Fu]d7?vqZ lyoo#?dIq RZ+IUXTFyUve&RKBuK}h3F) )h1J^І|GgJ]_/_p9<-:YgY&19ȌAo_&Γ!+uC]0؁-\3@?ڮ6b{G~U2 2#G*"h8TLf7֣[\uO-en o@zP?z'*:I[5COM;d0+x@ܣoק!tS"ac@BO -%-=q}i]Fx`lfERC w5q?'IjJ}z?znꏏSoϤ?Obq`_?_k/~_!Iz(((s3uхssTᰁq柁z1dh EOUQjٯ֓إ@u^u x5!YM61:tkz;3:I$q!@=BK `AqcX<6;)?Y24b"'ο龟ւ̐P2Gk+Tbnܪ!Olxm&KJ<_9&YrsSD+}Gu.9 i?j?OiZO.\a*(laʙQOPާ¶wicMbCؑt6Gin%Hzty-xe\:F )rXepʗmL;bI(?m(Z,xKXv#Tz2a2o8S'#ڶ-$9>{ǵ@M %fy/]pGeup{«F%+{X<:Loy$k mS1*[UF- `9߁\fy&;fOݩGjK/e] VڲUvΪ%R8H#?A[@JU 1\[Ώ m$ϥtEЍmem%*sTnt.1+,3/TB/2R C[=ӒY3?'V.!Mj[.t9Ȼ&ycֶ\U%r'6no-Q! Fr},xa9@=AkqL6ҝ)r}))P$8O]슰r:W-2P>Hﴁ]9lgtN} |WGkKs"As~vmǥ%fN;~ <Mo YFIpg z'ǥTҭ.1G! qNLV&eWd޻ &-e+8<<Jθ}%xfV I(mme|sӾ_Zh:k%Arg9x_M/ºōR\ʋ@I} a On)O|ןj_0!m˺W֧[5<$)q2.L&7982&@=MxM$rيo3Ѓ]xImXd@YX,s\Ԥ٬nC8ES~ g=@\QSqњ -4<ʛC8?*f4ue v#6iYgrRB͎OO4+k̄!=w'9ߧ׽66 +JBpB}c]^z\ƢL3<ҼQj_^q71 W;Zy^kXm`N}O"M'4$Zk>cqU Ymw"2XH$5›YUdG.@zna%Σ&UYN{ȓrxq9U`rs! Qgs,m1ɮb;*{$zj0X\ː:&Ζm#KwXTc\Z^LKji.Oxա*p8$Yp. zW=G8Y3#1'͚Wcz@?ZUEhMQ'f_M,zHRU`:T3"VǨKS|@?58_qxv@=}*h4jљRCtq*+(M9+'n-3BOҴfH拵Cqm+^r;M{8%mOcR]jrI! ?XqIs;udՙosR`[TIo+m@0?ªkrHVܶAϦj5yXI,{o* "*}[DqFυC^IY$>kˡ'}PV5NUFms Y1N~cH*HkPb(OTI\0Y+L)@)6sSq))iiW"\/ n\H 89'WV1EX!X8Vuwkd2!WuǾK?)>Ea L KjS[ .0s9{5뷔H̛>d yªj:^L>A?gA%կ>~v=c񨵍ٜܘN j7{  TcV4ly&8@CH6gYW̔=& iÅHOZ-sXt %,=y^,n`w汭tBSyk4A"v>bm uB+y]- VoQuj7E㠍v܎=k5/]$f0py49ݥLq8뗌1Ĩ91ǟeQ+UuBjZrI$KdsXIrc)]t$6ޭIl?X}?WpHĚ2?NP9{ɤ]F~c^)>u339VBIח\ ŻXw\q, 11[q]8jv6Sm;NsӌqT|?e*_mw t72F-+Sk jN:˴WɱL%yn)T^(RU{XFP<[Ǿq/$wwL߻aG[7Ou>gt#  8Uoc[L,lUQCW W+ -no{+V=E R%g!hV#h6vz)$ҙ3>W&; .-5kM@BOcVQrG +FyEat5vwǂ[P2 Y==Vδ\wH#J|FjʖO-8g9xC\4z~|[jª~͹c'9O޹K94Rۻ0Z94J5OXM29<~@P: ƙ-ř鼒Pt[L W-[Sh[&f J8ŵ{ubE.u$ȭ(tʮ怦OżZ  Һ sҷFG_iz#dÂTT[HQLnX TMlbr:?xjswa9cuZO8 Vksa>IWin^t2od1kUNiԉsi4xQRPQEQE: tK\SQr? k ) |Ǟ fنJVi ؒ#V=کFzyqRy4tdE.GSL@ZȻVX }kl>X?:#7sXC0s=됽ihUI$ { mcos.mu)'*<~`9S9tɔAԼXmȣz:J 5Ӗnf?fΉQs T;vR95m̡+;$oKsiϹ@ZĐ͆ǿ$# 'z+za<  9>ln{?pi0OkOۘf]L~<~5= $ԐLIt6JLTkڦD"V"E1*R)1H N00KƜ[ 2HJ+Թ>\U'i>[3W888fKQSmVV|hy ZxP=N:]Imc->\iޥx%j2H:Z]ЌpO5KVԓP(:z~]znݛv9Rj XtVg*ˏS^nMrFa]b$cOO+rJE(9OI=i9_x8>~yotqRGc׭ MA{ܴ$m1elc#-5K\(f&@Ǣf nkQ¨ dltfH.([ 3J$?gH/P@)|s3sO:\kl,*1ڻsОe˖Oi z=TRV%)1^j3>)p)Ԍ#TR3֮́U&;xSRZeYIec'Z;_YI g?@X?jy>9UV_)]āprm^돩.u}J}F+S{*)*@n9l;[2۟26G;CmISFA+j[;!5wL+eJ"2jmI Oo#uY]D|}b ?Na.c t\ge|Y$,zV[ғ{T)S[" WST$,{T.)bȤHEiSN @ L"y.E1u+^Qk]\mF:9iks47$O::ab#uSgڜ1J<##<^])JW{Ilм{$R %pj#R\烚w(Ҧ3}k ({ n펽)EFZV+dݫitEC[PN})qM60?{vT|m*<*?}wWY= l"ŒSKsJU$uLly*mĆ8UU ..(I25rISOs֘)d9V>Ơ`U>t}S<~[Z)I^3K@*5- q^ՠ̩³`+ô 18dE{mlc)|UQDH Ɏ\ !H.0gˎxj}<AJLhJfjֲF?OzGP94):HG2pGz0 :d $Hoj,W-nb\c;օۨSzsY\ϭXV>>M=æo$>+援Q &3#gQZ>pL4\Z&v*j;MJK5d֛rkB*zLJVb8U8#u&W$hpVY?>voB.EyTgi "5vie8}Foq մ 37 $I'Z3.c(RPQEQEx0[ƵLo# ק5e[+Z"YfوfȪqHU_: 9CXz:"o"/^W'ڜf AKs"Eڴ0 w=)tSGރF"¥Rz.vdonH̻uPG T.LVL,o75)uyvƝ3܊aF#9 NX%mLE2>A.< l#sJzFQ*vsG,}O5\E[3'E9zKnݻןڷ|SkX_,@_Q\|*4yָhQԡ23a*x*V eI VL0OP1]<ܪÖ_SP408JJIZKUU0&ڐLSih /Sq~sP.vCzXWlaQ0I8!ڨCg^Wlû=+9H+E&CH)?{U2 wu'VBY2}ʩ +GirUG&eLU\ե$ZE(IuQOc£*"ڿ;~6wTp*uRS\IǵfZZ=WucV AҢ9sU&К3򎼞i3V #:j 8'cV"RdcV^sAbpi搊7rLD5n-RXl9EnY> \-$/x10cln¼ ,s?wnwon!G3REl7JOsz;o{wpp:=X*Qxp_Nkvtq6^Zz'iF9)JsFTqT.-8uV9cu^w-`r1&|c׭B<4 ='NI'w4mxB3E `1fw~5A ]#&kwK]pG"ԓ%U$/ATpfZmH ++`!W#sW .dβj8=c\]fr}R?gԦ?#H¯SE EX LxO[,V ]'1\y7YPN3rvGw:tFy!b@֢ۢһu:賋`}w>J7#9敧{Tw=OeNSrd*(D5\@I_I99Vu&Ee_O5b;QųT/KbOji f~jQR}ҳ̇54s˸#]cRxIFql$ZY' oŪDZ#=g/+9'eaך$Cn]DХ)V 5E;gQ̀s] ډd:ͺ2Z[f$6aR8#_PuyA͍q[#YF7jڔJJjCFY\`業 gUFr6Z>^o~-g{۔, 5ԋ/ 9xciWȎT$t!X?e" i*?85[cNUyYcT5FPԤ +Vrұ B?Ua2H<cWs7ߚGyɦ}ճWC;ۍGN澶5A&#NsBsŘdSŻrI3oÀ2Qj+Zy>jv&[(|ˌTĮVE?u'Qo{$:ڕĶq'UAօ@8Tܦ1җ]~vĚaQ(p8ѳR5kMLdn=2VMoof ڕ2v3;@WPkthzD2nY ֙{^fȱґ%-AYMH )FIHY?^9w{a^OY\zeNy\O皱r>j. ^-Ï]z~iحzv?59< [ F=zM"ւG3\;j (1V^BjeO}(VGR~‡uz&*n8sizN v)Kti&[E%ߜp:cNy0+<܁ [q?R9aˣ/P⺛Rv.֏||:}}+}n87J\35i;o9֐QEՎW+_;W=sbvοKJ-cMԢov靭U|Cen5 hO0Z.+8TG#y1R]k2~M2ڿ3_ͺWbڬ(`lEt~nJWtlJGƧ`,8HΣk[ˆ#l;Vl~)J-·P 4GouGNdFHN>RGכjHC(Qy jv7KȕUINSIijwKEa ]c#^R\_;C&Suiy4e{`}cV'@<T)pZզ=\WUЕ"Kkok1-Ų W#,` k͏_n:峕[ۺͥ;}MYKT͑Lj+N1tmǖ5Q ;zj; fܦN̵@@[Zvҹx fMʩ#ԱSTMh[7T,+t8X Oڄ䀌`flnf.I<ܧ4)J,tJMzуMt$@gJFʩ5]jwȪD^jpK6)')hQ*S^5*s42qAw+qP!8*fAڨ/7QiZE^%#3 jZn?I=.-Ts)r|#wAS 4eB/,ܧRd:L—$@qҥXPZ?,3P?`g?'r(j:FءS2I0귒1rGP\Xy1{djL8E\+K< 3aA y%7>hu8O?#Lp)^sǽh[HГS( Aʃ"ss7x9RCRLO'N?LR+cNs`[✲:q+UewqT2>_Ҥ)Ar>oS;Wv-SNh*Tu4  R'|Z1ܻjw} Y@00Kch!+E#&>>3UUrHؙv.zkk3m3#&-7_dq4\{?p3m?\rۧI[رhЇVVcqfogxWs]ZsgMԍ ?Pz%!NK. Ud#滑R}J0_R+TJH>y]6(QEQEQEQEQEQEQEV>/эEEZ]N?P6ݻnW_5E.TDn<4Hל~#pl X4Tq$tĶmfPN@#ӓ^bw;*OI҉Jئpi鏥L5{]GϩqRT.c*xڡ='3>o'ϝ+.},,MQYFO_¼ƊǔJs+7y1uVP u7 1Noƚ;V%X9WGZc~ˢs2ҷfdcQ6pc5V,du;uDEQEtb>3?f'#[KMWݖv?RVtzJu;_tT9=d/ۮW9=eji7QQ@]}nsF!#ƙE=I?%PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPfotoxx-20.08/images/unbend3.jpg000066400000000000000000000644141362435004500163720ustar00rootroot00000000000000JFIFtExifMM*JR(1Zgnome-screenshot 5http://ns.adobe.com/xap/1.0/ 309 593 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5Q" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?/LUC6Teuj(5?'>ы=?ޭ3OIVWK?U*%[>2~_jG}(٫89ǣQo\j2@zxUo']v(Ž:_jߎ;wWz)B;~&}w\[ɿƣ>.h8>"A]& ~41F@\?2W KQ e:_*gm -i.{ e:_*LUC6UԿ4]1CLnjeu/ͫtUm?ܹ}@x dfo&^*W?3W KU]7UՉgu/˫tUBՓH?ʫG% f:_*LUC.U~EG@?]__2/XP&~*W?3W KV5 e:_*LUC.UElgVaA+f]\K2ݓp\YS񽍪[Cș(~NygRǯsD_o +^} 06] rtPY/?ukvFw knE?Zʏb'E :v5@iI0[o|L⯌UfCO:(p 'I=}xҹ;wH#yr+ˌgEäŪxoz_*$)?aEGmfKOXVw)٫jvVvKV]F>!/,+yEVԘ})mS?֪QL /ƩA+T"ƫA+GƩA+ThƩA+IJ:Zb-l4l5L3\GѠeֵacSIk^* G$__PX3~\j$TMLUC.Tguj*J63W KQXPEPqa:+s-J*8 AK@(!E%8R) xÎzg`@<*.-7bIJLSE7b@ (Toim/#oSQ@hz\zx[H]\آ9frISsUdDK]]%qrx&VO H֮HFͮۮߑB_$)bxmRVF';lTy_ہVP["SF- J@qդ|ЯRIK@ EPERRp4fEZ))iRRKIK@ E%-1Rb4fE;4M-Q,W";FZۋsWQu=|Mc; #SNܥc\Qwjl9Fŭv.Gl`W\ۙvb?ZLzE(( *o+TfT$R!8TR=4I86*u}k3c4݊6ѷpM$)ux5oq[=E"N2+P A%?[5km!\ cR: SM0HM)NkARC^ZWծ롩cE:(Š((9s롮=ƓɧDO44NcZcS8k.iN9Z2:NXmYQN}(ޔJ~IP!ғi4)&@ pZ6U8b҈X1>F zXeCrMO6muv PmQhb )qF(QP(P<-iiB(д*Q(0(([cGokxqVM1,Kִ[.zD4s/fٺ[z_^?t(*3^c*} yu[LOYKO'<*;fxAxNn"1̇؎v@Щc:r] (V9C۫d:Jȯ)UAO(쁜I=#&Xı +"|B{ xmVDہMuzLO$u #ƹ[[Hb-F@(J`r\H@jHcugoα5,MӁ(1)!i -!ԔJ(AA& LE-P1RCDJ+P 4H: CDJ+P 4ѵskIKH)jEP})v6//ҊO-}()€䯽8BNhS"< jR>*Ul/u%wqVBIŵ8[ijWڜ jxڋ<[Uڞ p@[Sſ_{S\ ڤtCNWUmF'#5^ZhT3qDELBko-9؂r+%eX(ʓm(i[˄H!rjm_6n~FiX1M4\sI@<Pj=ԅ@.(׬] S_gb;vlR W/*% FsH<]k9p3G1~}S;X^Go$eg(=jU֤Z]ME'W~egdhfRڦ[3ZFOas0YJc;X<@x9╂jK9 .i3¬bĆ7p?ZNr8&M3D>e5!AQSyȥt2O&pS dUJ^?*Fv)\ # 9MX\vwC607F((GAX1sֵu j'J+2m0?cKr+XqF$j-gFGf+7&4uy]G~#XM6/'u &ŚX;L9QS;\(uAu#ӮEB{ӓ IN9kq&&qcp=#?ʬ?vqU4k]Y ݚ6?[]bXX׵o{%̸\g<檹OE$rHB5!0SSU\FiJ(PiJ(G֐үQjKҸ&o'=>K\;\H#<44\[14p@jՏkHN늗V8{}_ʋ2D+XA-*]!3**Dn)a`3S{̦B[oϺjllvҹ{-bQ,Elӯ_.7]GRf(Q0KsUAȀ Yc ySdu) d5-]O.Q[?^*GX 9..Ѥik'$sTwm˺vl'E6.5$/h*"}Gr 4`Q=)p.Th+= q*FvH8N'b'Qsk vQdqZ)^g]AJWV"0*~kk_IIzR`(P:~Cd c5^;ŐѺ¢VjiqA4E+}A(nQfJy\GYEr?ʒg)`V=lx5[g}/4 AaxcS{*ӇL|V'ƹٗ+ ٸ\!9M MC- KIK@(:?=ӹЖ5+?{7R2s,[@[ad_?m!IET;Q@E ((; =Ҙ ~ngk5s4oWt.[+߾O~RH`@,H n (@9法Ix[Sס4iŸi^ӯK_ĸ2X%z&6sQقpյݶ3TڼҽeץcJw? \ #?:qV_6 + lmguG7sNO]Q|W}=l PV$S8TV36iE346Ӄҽeץa]٢SyX,L|uf޺ 4o4~zuk]x}TV:I*+ 'gǕ7(9vF-O[.-;>FW2%[}ZI|c]%H`krK}p *1qzU:tݹO[.-;І "hS5`?RE0$֎OO,v;sM6Bri^mqoĊW4$H)/ 2 *+C &[* o{2E]wZu{_̤-vy?*~>U X*#uP+(WgX~@٫rzSy{˯K_ñF)C<|ufxfo? lmP|W}=l#+;UZ7L|ufj~[w::my{˯K_7%̑I(dzT9Y˨ImɕI =C%'̋lb(7}+eץc:JzWC Gη|qT5}?"J/J吝^-Gη+ʖH#[>rrj~C(>[+߾]zZv!yvz|qQo$|qPWii^|8{ k4:Ɏf,aڽ6^}:Թ=tiEi^g>&p:څ'9iHrz7V*9:˿t?mjCge~[?\Զii^GjK'7Xٟ3f3}W/L`tkZ7WٻFY|{0:.#N/J吝^ gkrbSu훼e1cΩx}2G~`+ێO==hMN/J吝^ ՕQSmS# Y*mSSp1* 3*pQm?\9SҿO~k$s\tpfp=8oIo#E6>g -:ӌ/4{qT[Eh6ʋY_?8»*'F_-O[.-;QEF!EPUZjZJkT-F8E*Ō|­ܮXU|®N>q!Ďi@d$NOIxG_YŰ7WM&L*EsIA<kj2ܶ`&:~, ,mۑvwic skRCƞڦîcmqvI }*I$&0UEOrrl?TўfNl/v0{F~?vSBh?x~iVmm8&6 t恣YJs; Yy?ZC[.@fͽ~!TmJBI(ImRGK;>:ԻzRE*qmy[{{zʬNFT5&VE 98:envru`4K$-u ] z~^kWS^đ TzVgh^[6C6ʎyR}n{+LOs]9O}+ !城2BBmc%ytvL[SoQ7c"̬nh'ҝ:.q(~,*NcOܹj,X?dUc+ٜmIqᣧ,238i`=kN0 iNoGI<䴂$,aǪ1f@91RW{:]B|sJ<7פ[GBUK,&W Ch_ #g Efoc,7@r"d3NG-LDҠd 6*:WNeHܱzQ?sf;:_lc0ZwuP8{#-1߸f^iO_V_?M (N˩a#PNO+&]{?rPq*zbGS7kmaԱ,WLUḓ?C^w3^9 ~Z=p>uz+ à?/'%%cҊ ŒQEMQE @ ?Yo>Vu7^-1BNJr@$gZJ71su^\1NjCޮ_Qj&6gXGOmM[{u;rzOkbW>Ǎ:{KS>EUQEQEuSZsYBkK5rOgDv P ;Q+|roTbW $(${cd "֍cT+Зcq]Ws~KdN.l"{y0K;sOȲI>^IrbژXt7 6IXE4;Ijٸ x5mcXm&bB.Ob?¹D){bR|1''QYh፽\RD#2mt6y8D24tsk:_]ݬ2Zf/)@bH{z4Q=J#Q- 8]wHɸ}E`F85^]5CxofpkbsqjXR80*|&bhX"4!;O^1{PPC(%uUw:zhWJda5!)ҙR +@;-Y|crIZ=O f$˞_VR٬&ƎOK"{苎S TDWz\⨙ḈPX#<[Cg:Za q"8LIx YCŒFznXNd?sdĢXARzWOmk@6vrH…+y`yE8קx-4[aNqްJ,qλs2)Q60]0FvPtu#*Aes=jјە eW `3ִ۸<{G"u9SSQw;~PH.'9皅_-ź3+(g J˪ȨQww;IP2#=kW\:Q3]  Ye*Y/[Hm k`pye;3Hbԭ'EzVqZ:t5vj˙\Ǖfn+HoVVքQxkXfu!_T1PRULEbҵd0;h S-+R䊔-;m; iC,LPppнaGtq^ɔ#ּ^%1ʎc*Gpz5mErM P$FQ1ÁM>kaBF@!횏>(Jd.D?1I5[N478H9d1|?\\I(~!_cֲ|{saܳmdUAr֘P**L+rJGM4tcrq#xr8TAnG#bgd-b[w\>iL39C=>2~zٍue6D(ފ٨"ojf9PP?+3Px'hX!Ru*!ki{my-1\g5>nE2ȠV"׎K1x ׯ_VSaKf̖I\WR9ߑ^[CsՃrp2k_CZJ K;W-C>ۉюUq@m4Pc?[Z}{q\M_-:0v땺gI5s?:,.c)0BN62EsM]mFFKq\3"qۊԶ ݔ09`bY m.5Y<:~v18#Ftj)IB#˚,"%9 }.Yhh˅;gjgy†֜Z7Xx{˜*Ğ+kw4]`)8$Զxpn6?Iu3Z)kZƣim ϻp<Ru+He#9?9Gɚ㩹 O EVMv=fgֺյ%5$1-8@G0+vJʚc$kxLڗGUWCc_SQwDk;X,?Zַk7󧷳]O3DyQ[Q@Q@?O:5CCmր湪|L LZS 9&³4ZZ*X'ҡiDp ;&}w c" v޲mPk笵Qy6vJa ڴQ6,Skkv'^CMscb?EHDg J0+ML띹Q-B^yؓSUjj6ѕ5i:ZrQI$Ev8QH9["2G5iW ua.:zzκ)w.Tj.,dK%QN"DDSJԤR@mJE&(iiSLG%rV|zןO0q'=okOc\ny<ՁW-JRnsDVS=K[o4̇%Ǧskj^l"6 7>t[#lH5XM0gIjoV$^[!$]谻NN'֪# {S ,VҨRan /"6ϙ'[}#k[G `I)Ҫ fs㸫7ZMݕҡB>T'M6O:vjQ#4"*IWN_w9gE$aj6Issi$H?,1$V]Ĩ7 WdP: tnB?\&pHݼhvR;-$k 9 JkDH{ŜtΌfQOX,VАQ1 X D asTh mm;:- wݻ{ԭ!@))]Ȯ)uPi9 @ބt[; Xi]?jt$W𩷥Gןpnf2* u[]fBk** 5Sϳ=#QYS-o^{̒GyeqTxHx +*5)Qt68aӽ\Q1G(柨vZEk468S"59;Rjr~YoiIn,k-Z`l@e SWntA/"fÏ[F`UEcrqYޛm^"kpEMWH$ı1QsQ\G~Z )>S"Kd Rݡc,dR@B8vhkIPEq޲,t>Qmqi$!bnKm<% q H:nBtf юmah7q^M'0$=Zۤy{~5}ΧdǢZkB^AX5CD:}@ۊ 褽Ԍ*|G{Y>(eЧUeeFԊC'¶J2oC̥bhQTo|$u9"ܜ8;zⵝ rՊ5Dm̺]sk)yڹ߉%f۸?w60Ǿ#sHiBT9\(((F4;_Oæj1upW=_(b)SOZ԰8>Z3?1%C,-9?Ln`2Uk)h$)r[. =r g {X#9%OJϸW8z=O'+k7cZZ(F̕wkvۺub â5L޵FsH85?$5a Ya@=kN4 qj*J+D:q{VMI$' tTɐd$;՟~1WYfUo?J\!;Q#8#޻gnI>=sV^BY$]A;]sɭ~~#?U3$N .nsi*\SHDXR8 P("yZ%I^+Xa~pXڛGx9Lۃ(7nyyxxfM3×3])BsEYtm4E;%2 ?ɮ>Iԗrı*I,PM"2#@URLB@ǘr9=>^YjHb2ʬ u棔2bB ™PWF}蕕vEڬāIv]{]o]"=I=@8hhuCBm%=MmSHX~`7krS¼?yW8&?)4yj>3ݨŞIp8 (ǩRtkyy<h+ZbZE7z1]?n ʵ|qk/t_WODg IC@5AXop]ֻQ\Ck++XxH`nced Zi݌k"*e[pu.Pz%1+9%{_/I܇T%y^(PEGF_T]RkU=u3ꉶ=A&北0VFFEd%KɅf!9#OJ%ݥ[G d8nv|w5rZct߻}/L[[|,ʃ{=&}nݹ3VrY8#3dT흛rgs_o#Y}]1a¸+q=6QjՖճo@YGMCwA5މ$0[Kyֵ=x8 uERcNwŻMȊ2<kt;v] ouք[̍)l]`rMj:2VyvL 2Q%"FUxhmOj };[;\ҭݳ3s+#@(Ph6?VM/Vs\~#ZS_;kZؒ>sU4Vi2ۑXLrP1$ֲ èYYUH߄ŊmT ,0OZ{6wkJ+sp1u5vٌ!'GIm'۹Xi.08Nb"'N]|=}+rwm *HS|*ɮ!}Vl٣N\ ا;l-V5f ~lVxH(#`Vbb-{իI>Wn8zp0U3Wξ `kTs+I!}?Ƹm^#BhnM,~smڣ<sX)"yYzTT%3ajtPkDK[kPjub.)GP@ N N"G:Hj dÊb9b%p;{:5^Lȭw>xCC^o%G8"K5̙ G##aPǖqǥE,,a SU(:+9MHVO"#"A g++j%F1W.ogKyNVVj$ tiwŮۮߐ(xhcHu#[pG#+ʗF? *sTHϽ3[ebFQQCfeAe랣Ko5 1$wU@'<&-jLH<!ID'!lVGmdlHJ+6l}gjh, H:ȩ qߊ;(m!$8~ C^ڨ2}Љ-IHK q&q\Lr? سwTQ-t~VMf[9)?Ӛe%Z4 pVn#QOJHasHpj,U5fD]j=+D:pqި MM+/jĘs k|e]JWge*q:O5-j|k~|W~QqN<z,港еe଱4&༏;wyN T- zիW>Sj 3i Pz< Lá4jVIJڹk2F9 =6r$r9[dKxV%ۘdK+mf'.zLsWs1((( ;i3lPYxsLCO-ްp~qHO"3J[%V~EVA&z E!m#~U^\\\ '殻W5od2NkXULt(i{7ZʡFGu4ns,LO!+77vm24,ݍPԣShX/O]-\mpQIܙNWAhUyXOdaJ7?|?hcY:4i"5"+9m'W 1ʰ|'i:&p}Yצb QƸ *xr2YjwD~w?qu퍱?*x?l أ:?q4Yӗsew Te?@bk:w%/ޜ:/ky{`S ݅?lQ61N6~9m#8zS*{q[I}'xndATE>U98n m<ϩJ+F ~_vXVD ly;M:Ue\Je1S#>c$R 5>jKJAh&AT5b2_$~a}wu\W3pWiq4ڵKk%jU|(@MX&k?:>[K Ilt_}N]u,`T~Q\#-[jx4RR݊PQ;OF$PThZڪe1fz[-IKS3 q`C]WWviڣq)99.iSy1f9⹤+3ˏcC I*U%H~ʟVFYT2==1㧥UO2=Q֠J '!S>'ʭkagKm۲uyNJmu(zNNxU+1jIDW[>[YX-i%n+m k۩e O3G5g df;ʁ^|472Lރ4SFMZC6Vv1 |qV m3GP -f *WɍdtjlR<֊( ( (;cGc֛7 1ZZOBpmNhVZbKϽ&gbbXNn!Q7^8I㚩,.3vWbcV'iYt #{v;JKؿ,Ci5 .htʸSQ՘wTJ""/z?:Đ*Rt&pME˰j/.ume=AsO/H \V@\9=.i;勱!{ 7m d$ sni;=mrC!N$tv\AI@4=)(o«ȥL0'/i'PԮ5H) H@Nvs W H#9AZ9ɪe&Z)7Tol1U75E,l&xUDE?qXR0G,n9ǘxm=[wMn~uc[1!e6{ԑ"ٝ [/hR>qWlM= 0zT`fQJi<яdVGjNsiN<9}0[.8RY QVOk Қ9Z"|TrjsbZ#'ӁsY=6!U4߳CG Psp }uVyz'ߖC]踘ʥ5h3pOď^a@ԣ<`~֭c Ñ-òFk>VX}[ ?ƼIQΘrGPxݖ=:8?ʲ. |{WU6c(6z"0i$ynz(*Zg:r{8"? 8/󮭎|?♗Dt+s(((괨tLĐYxsUsz{S:i[Pfa ߒ+t޶-2(;X+81޲U\W*T[:zUFv.{W5)e -6d(v9l5U #N\Womin"   i/ǵwg2F ,'We($rՓr*91X>*9[^@Q]>8]p'Q]OA]=Bw"- ,]G ^+mIs6#6d/'\ڟdVƫ-q,՛;@733#qӚcHri"y q'wC'I9-裒=C)%B"?+4Q#Pk"pb7Pr3ToHM,hJb03⡷t7w",Fy\Hg%W*ם_vƫ~>/ҍZԇtUY]uR f\r͢ 5WW?=zm±E13yukRy%G-vm3~%H~gtw R: stScT$Z#":;~tFӷְZ7Ki݌?JJ+ u OS.0QHqs#~9rNja/&qA՛"&4\qzm! @T"FFʒyYTJ>eeoP8\t9/ ۺyfLz)c,71IsZq(mͩ<їBV 9TmWi"\= ;q(R1%O~ eEutO:|Eon#Rf9]UҖEIya<`g1s`ýe@CLF8ʗUszke<uͤMkkQ^B5]Jasg?sJ(Tuj r Wj]s31c(ˀ⦱]ېzEc#T"N*$p$;T7$QRDE< j^HE2\ZoKۨ@Uķ5EkLA7 ꊅ}BOw&=3EvFwd upzieIb(((((( = UFhUKPD2h#CEVQEQEQEQEQEQEQEQEQEQERh%+#:zTɨrs/$ fvEh<ꗆ%,0_㢊 IGfh,մ~8 z.AX90)EdKWv8B]J6+X7ډHNEE(AEPfotoxx-20.08/images/undo-edits.jpg000066400000000000000000000300121362435004500170720ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 156 341 0 C     C   u" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?KM]*-u|n>կM=EEo'hTOTP2_M=j{I}Si?GڦF%TO紟Ѩ ~74}o'j*.'t?S[70TL`:77+]_{j{I}>75Wÿ9*6VZtn3x`09c,N:Vwe k3x\օߍ_7[F+g@h=9$j"BtѴ14ߵM=}7 k?Եě]P֭㌋l~&~Ͼ|S)u}GzVf+)@AՆ9$}?'"n˯F|紟ѡn'bBO5W x{eVr mWQo-ȻDV4FvO@Sς^}F[[4-#Vqgqۏ <*\zVmf|뗯>IkTu8*ĂTO>?^*VQo>/6w0:H&8.Wy84 +|XG0[PM4Z7Bᑠ\qYE[_O4ߗ1o'hTO׏?|gij6Yܼ)< p`SMI]Vvdj{I}>75\u D3z<5mzUԤ:;Q(bKfC,|5⩬ߴ oksHJkx̪18(紟ѩm;>yǖ? >}MEdq !|ӌ@p[~c^Fe8ǚRvKm}Ozӌ.i;_O$|rRwx$월kM0ryeá6+I[yڦP7c-|F[}~(5OZ5ki悊 Ki?RoPvtE& kֹA?m'M p?7tE& hKi?RoPvE~å)7(O;G:[I{\+-IB?O'~oaⷈ>5O w~S "h.!qD?yHm'M ?5+jz3w񶹯KQm{}:f-so7DͶGdZ\BD?R?#?O'-IBJog/o]4|om!,d4Ջ\HŴmČ  LkSsz66w+y60%o}?m'M ѫ_֋i}k^Ӽ$:k_/o .W%\rzg.4Ni]k%vy-c$~:[I{å)7(O;UͭBlcPx%[-|L,- }nwWϮu2 hYA0$Kgm'M ?%ekt+~I {;gsLY"`15kw#u;oǙ<͹(/tE& hKi?RoPv^㬞 I m jF)Ll _ƾKi?RoPvtE& k+]br +=Mvhg񆎚F)h"n""5/^C_m'M ?L%?gEY~,ye+{|Leeddh6+C<@?ZiºQj 2]H/  c~u~)?wsXx_Xs$n:$T~͹oq[26›)庳ўXgdY|o|Qҡִ*}v+n.\HM#tU u<;i%ėk1돽e;x|clWeQrͺ+;'{]902H氼Ci?ĝs#L]?M)m0j6U%:V3K3w+O|1o}:G\F/!ῳ.i^i:} ~m,k$g FkZMY'\iˡk0Si#;VRNârj/DIV AERQEQEQEQEQEWowvel㲰ԆkS+ßhOVOvlo-mR9bȺuP> <7x[LӴ-Cp`nݫ OGmo<I^ Nyu([v7>i0۲+GjzbCwN߮ [|K@'r=;8^ "OσE]]uuu))f һKZտ<7IG_?3ZP5}laI$+$sby>%n9^zQN[f{)A) آ) ( "1ǥ> ([Pas (zѢy>%n9'Ŀ g/#~O ze77zDXN-PjR.CJߟTn?@TU'Ŀ g/#tB is2d J!zb4k;_W w?ٴ{Bw-n fT0r8?w_xv~#t I{M#qNKbЄ/.o:~<>5ּ/'PO$&/x2^p]@qT|c[]_@C߶k_xẑMV о[gua3[(mO0h5/ KWMyh6PS7n5M2> EtKWD#"w.: xtkiw :~-АmOV28ҹ~Z`{o j|Ce79nm  ݣRe)x[DRs $ql2FNxB9+S5ox%.ka͢An.J7>^0}I$og`WX>.xVTׅeԦWI4a4,duר꺔:6yqȵ`mU,p; n  ntm+u+"cyǥuuet[kP0 Țr>|T_WӟQ5Su<-wW;+R[ULjl%ոYfE+&dlܤ+|鿲joSIcmϦ۬| n-me%q(2շ^x+_ψ'KL}:$ Kr0d!A?$~_ϼAm[Gfz׃'P<+~pT~?xO ]? %RA^,yR;w9@Fտg vójt f@7̑+IS^ηӯ;Hu_Wn-n9oB̠9]+9 u.bW5%䴿^!~t}3:Gc Hn:K?0  + >&l'M|􉤖y8*Ts^w|7<=$Zni-] (.:trK]r0 I>MMl D P]r@v(:gCt(mF eD>&?r&npA k|u i7jz3嶿qZ~3ǿ'Cőx1ZVk7e=؊J  C7sU[ov'$` IXQ) s\n8,{B=z(&,"ΕY0v"v'Î%yh*ѵiךmi3+ȊQ;rs^] GM\hjZvhIgu n@Ac8#>3-?h5k]2XJW; J˛UeoƏ0&:SnXĻ4)-x#Wy4_ZX|C<=:滝k,:*Ü,28jWxVNj5OˬJJb1"2CH q|o|]CSYSEtDX݌nY:smb,o_odB񯃬fmG 'T{F?QQAUnFܟM5y(Rnk_b{=JKH3r1ە+ѵ,usPG9ZPySm;PySm;PySɵK+ -̊p2V/|etEi_KSN}2_"/ )>?2OoGN>2_"Io$lmhW<0u:ns=FMԊ11Ĭ0}Z((((((((((((@6|wI.bTX] m^Hdi 2`G=Efk9]]jWFկ{iK!/@!k7?VX=τ/^MZ v/̌dh_ w"W4jnƈ#SN'=e]_^=AO3@3!Bx xtkGp\&h㕤PUYK(^W?G߉RG-sSxoZ[iVwPA 7@[f3Ze 6|ORhP:5ڼZ~ٹv@4瓛:oÿ> wO\mvyo1G&_^zM9B7/gxǚ߅;.Zs%*  }U\[2ׇ+^u@xj ."7r٨v,bAvbBq^DV6{0ʲJqKK+ ^TQE"((((((((((((H%A4DQ@DQ@< (fotoxx-20.08/images/undo_redo.png000066400000000000000000000074321362435004500170130ustar00rootroot00000000000000PNG  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-20.08/images/unwarp-closeup.jpg000066400000000000000000000537511362435004500200220ustar00rootroot00000000000000JFIFHHExifMM*JR(iZHH0230P0100Fotoxx:trim_rotate|paint_clone|sharpen| Fotoxx:trim_rotate|paint_clone|sharpen|http://ns.adobe.com/xap/1.0/ 600 1000 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?~iR{beqqUi'+Iԅ-~+CI`#|K&;ʹqE.%;%0>S+cOy#E޼ž!Ym*۰N}qeC,Tp$xuZ7] %wEJ=^2"ʳ բAnIQzw,~t''fnJ-hUze=$)jKB;kH!bE{frv1.xz~y U$iMߧ"ʪyi>emO {Tm$ '4_[e}FW~Uz?5 _}ԮԚ5ܱ2+A4ZFqqWlձjBwn6v=+?*2:>#[I yb\}'ޟՎNHB#HV1*QpY_AQpit7G7Q@"y DZiK[m.ݬ>e QqXܾ;!?uU<ʳmφ^I{v3rVaTq\j0{+*LTQlW)LuAA"R< jw񁊆qlnA]q+$u pE.?nH3s,#oҚ٣>evWmҢmsW5o|2I"ϓ MTQm0ǵpW=zt3n*ǑVE\VOSeUkpGAIk>LxQas` >-.^HWjo?)5*[Q91hwh x ~c5NVUٚ!HAf&5Jrv |Kfψ'cZoK3텖"dV;F8O\s3;n49vVitrgc0B0k׼/s=k2,7!z(ӈy^<_w\# \8/ xj[[qxRXF䞤]ՋG=Gzz?Tcb12ϞfxBp1Rgv}o<_wGy^91%GdsĨp@Nn1E/ J48`~;ף<_wE]tfm.xSe(#޻=zz,7Jt[Ki]7 '## B${15‘Yv.3z K۽*0[]l pKp}1^;ף<_wE]Ztkoi"I4n:T,4-LEu)i6 g#$]gy^=pǦi1AK}@j7ǽI紵ձuu9&:Wk;ף<_wE]|>a$wrr+.__,񷎙 c,p.c"ȼ g ^YxOĎnյEvUER{*߇fԓVu)/EBH}?;עi6V?+3<_wZT>E.rsvoBA|,fI|¿~Ez8VIDim"Ew!2`bN?x* !$_TRl1mwmG8g޾OAK.pTjo{Q~nOП\DDCs(#jFU&W[&:e~an񅄣JPc kQ\:v7o}#*(1lI'2p *u;Ώi y?h_9T+ᕇpTtj>/]ZIuf&Kam- {Շ'-.m=TMOOf{Kı3)RzE^ '>,7 .XI ; ;ksHԱE?WEP.ܼY< 0Q nMk)VWѼW0"H3Dº2;Y&wnFeKc*`\5s~[Y5,f$ѐѐ'<\j7~9ӯgè)t0TN8K=Šۻ"j:EΡ:tq^(ke lcZٚ.zZ`~%H%yNŸd}~3R@P2IP[_Eucq ͬ9p02:^ivڌߵꪘr3;G;W2]ru2ڬwBz_wy"3\]J}ĦV{pKolU0$(9=O{g=nQl=O | m۸;gZ> 2}qXmlgAUn=ίcs8,ؽcydPܻ)PK}qt;VŻmSW[+b`df叐#$ 9W#֯ml ar3]@Qܒɫw}yˍhƫ +y [d1]lju+J%u-.!p5iP3b9gTVqFt/q4j7 ZXqQBʟOcЊ~jr_iZyvF~רcʬ`r8;WY p&e--q9Uym?~!}mh{eZv^֮`$PCyh Xy~a=)څljR[Xn,n{vHЍ0]YzP{EGݛ H  *k0.p<26ȇ )EBHaڔfsI9HsuB/U}gJ[xDv*ȧ >>][Nuu*959)t=GzZx +esIs򯌠m:+3&ndDҥ)9GQތez%0+У6ЬgkcY6tWE +|B9vkC8cR7.L`5K XKqWAkj㹫ϵimqfSҘxH ^sy9Em?δ+Xh!e _Q #:jIO (l|W~H-v"/5k/ɦ3n}G';FZѻHz_6]]}\4^9|зx҅hW;AּVm[NuP*+9){V'̯xijZtpET1dF9>sRjEёm$ ~nI>7Z$`̓ T[2+ogž3w8)=u?ҹZeue.^^r'^[E0Z[l'ִOu(OS/~.+*ç`dEsPد! B+>>sT.&JȊk=7N2mz݃Y5_hr9joDi fwPqA( VM܃S6{|iq+stjmG=Zn }Z-NRz&`Ip{7 tdϘ:3v\T7lx"ȟgs$ N}0^pJT,dK&0]}S;;X;2 b3tG8*T>$枹WK(̹Ykk>ܙki\)Ԛu;SzX娵3ksmhL6qbIɗu,W< WO\6Tt9Ki/2Jq#)KFr1re\]%}DΗ/DGlO"o@|l4>Y4˻ Ȯ`{tRɾ!bd{YXů//X6-YTnU9j5V>]h/lŤmu$"/"etwvw\%ݽHab9%[UluPմlCr$X8;>?G/,ůdx།n|drIݻd\-bDlCC$s eF=b]s/*8'eΑMZHtmgM&e%d)8=c`'3h;y6+o0 b6xEyv5 Homf[> hܦs¨,[X3|`tpa&>&-.[5]6H-2C?.9뎕{J4^]i7ַ֤[L#ܤx_W癬,$L IϘa sS'MŴՠ%Vo5 NrУ}.l/tWk]_NfVyBaU8b =*_SJMQ%tǓ[yx m ~SJՠ{–b]1qqp͐Ns֛ vGG|NVG*džgԚԴ,8R9tR Nݭ28cykƬUQNWDOދ~Gjת'+F {LN儠cgz׀~[jOpÇ#,_{u=*[_}RvQ!=ϫZnzꫩ+CnxQ[e-Hƻemu#tֺr>TPQi*Y`K{4F)ꊫ[ݹWr-;党MBD!f[S$@Ju/ $z ,06)Vto9(,+mr#sHI pA"c1< T~9y䶾_Z~^pO rGU_ \{6@Nlv;P8T}ND ںz8R)=^i8'< lJ15{+Bz 5&6p lȥA);[oBlJ㤲g>ҫYj3,SB>jF{K:0wGpczb͋iK#@]c5vtajt{Aswcungia(qg%'^FĺՉ[d `rOs^j+|vSY= IGl'[W[# k6ZKkOٴ%5czTՍE:ѻ3ծd_{.zO[浝D2!GҺxc̙(#CYֶx~m2*¶BkJ*s0|TH8⺝T2opVw6lD sR}kS3/֊Tg7hxUPˉ]a+f XL\oCJ1yX׹r! Y,@Ï2cN;WaY؄/[8aJKɸ1'I9WU(gw7L-GZ?*cl:c *Ε2mh/ebFIz=+B@4 vQñ4S9?dӼ}Ӛn<#>zCirƫ&LWY!`wE:O-RMM%k$i f-!M JIh6Qi $qźABڛ[wiv=N[hv,BΪSО3Oêj>lkfjb&FPYHO^Z苦\Aͤ\ſ6e<˅ 8CSݟuC{kn,(evo,[KQc0fDxHzeWY5([WHA+2"wC`pzпWh,Y.nK5K=.&K R <砞/q};U څ[b$@pZ]׺i-ҴZl& |eY/ARMF}ŅQy9?B0wgsVy?;fCn[8Ь>Q|ɵ4O[XKE UoN޵M (Š((((_åw* ] t_jJODʊg^.kzRkF[1⤖ڽkëH[8ng :ێ}2{tVHd|hu*+Yfd܊rAؕRX 6 'зW5۾{WtKCZ79EEisգk:^ӵۤ Rp^}D#ޣi|S֢MMŃWCX^V\f,0m~w6Tv1bx+8aUz9j3z2i9KFf17aןZKL1V) =+IMcDdV F*MBe4cs(IȪX`2k 8(KBP5hZƃC}k#?62ts6FAy37Wx~⛍6h5$y>]FGOj|nAr]R5O$c^!RE 5Χn$ko?_@|?]gZd䆖(?N? 啋+nņXw$[/ +]Â9&Bca@;oZ md73N+NgbfvVtIږeXL-f[4qZaӔ7)"y^r w t݌)AjvH SCpi~VrlRHk5&\1^ 4(Tl䞕4<\ [;edl[J G$pzWr}E|n OIfŦ$-I :+hy͢)(((((((((((+GO2?V%:qgt A/FAI84Qu;N20#akxH!HEHBP:*Sj`VUJ/7-d ^1.y\} |a'y-=q ccoa׆#{NA5yPS<ң!eVeWIJPzؔ>4 <=c.\_Ƈ28*WͫXbVtj:Ԕ籄sԒGԁH'LnU^cC򏘟jmHT >Qֹ$ȼ.W5MNK(# 2IGmwv+HܷQ(l2ԯ@<3@*ke@(lIm(UTIJih<hqLzqw. B j @I}H]JۤNQI)ri2\hLXGpx(%>X *7+mh5)XRG6j,dS%$۞i2#A[LߐoᇁfgٜhvrH ܠ$CΊnc<5L[i@pzZF?y ЂIvjVFW'u~OL\ֽu-9{oKi33Mh& /r+Hُ,I3BkYFkԤ|W%qw2[ L\cA~$wWtTW; % mǨ_Q\$.ݷKuPEEs\лvzI.?]o=@IqB}?$wWtTW; % mǨ_Q\$.ݷKuPEEs\лvzI.?]o=@IqB}?$wWtTW; % mǨ_Q\$.ݷa4-Ƒ[,  퐞v ڷiR?ZJ:!P)$VCSȻr7jrDOkLz&8]Z 220#vnJ?Qk $ß?\gIsIA rݢU憲Yf`:JqnC 56>B0xj)i8Qֻ=iḿ#5VKX8QOY,c-$F1i|X͟24Y}o,t҇/sg°tQGej͠٭/80[ + zQM5$[iiw3/fR:G<m$ =p\~`TV\s[\'+eQ \G GQ\M=3^&AI)tQ^cҤ\ n HyjRTQ1(Qwc_4#qjܚ.RsBO],JT |ҟgJjyʰ:+;j:UŽx=]DYL.sX~>3GU91M" "E*qdb} 0۔֦SY!!%V2j]2C`|3gVMOS~ A_kW3\^۽Znw+CMi#pLrYʿ\W?58#ҵ 8X2׷5iQX |) oJgIM^)K;c2ڡsTn]z\$^6{ V{] C2Gf W)̓.[Ys{yM~fˆ  گ .ۖ*~mTyW_m~[?ۖ5Rq}P}tː?]_XC{egcۥU3%"9>ڴ-5M&js\X7&*NsZ:Cg%p-Ѭ8qŽkFكDeMEiTܞ%8<,5'xKZ*\-sHRwLp2YLq}/ݥ$\i]5ip Xሰ,͌洌vbS~φaidW$>o¼wo8b {>do`~-ׇ1p4$9elՆk+.{KsOWGC$gzp {潋Kq:3^ehThiIJ({"E{ ! @5ql I(\)950:Ӛ)Af:5*|֩8Y6iԍzɬIK&kֿg4 {Ei8,T(5{&]^r^Swx}Oxr 36@)( ~f$W*|T.rS 56% yPN7k WLIt?R:a˰i_'$0pn#}IvzlaVXcFp5g~/tNœϥjH~uaHpsS(FHvmmi Kd~tw][Y\-̨іS،e[֯R3YZܰrfU \/<%*}wR;&UK|k>iԴk+u.<= OV7;khQ5OvVp(HB,rA_Ej ǂ mq`ۣY&=?\ҒXIL8_Km:ޟW?E#}x-O\1\ÍP63ZbB2]4frpǜ#[Ue!Wz>hý$jAT) JqҬD$PmR8$VRb/#'u?,0@[=WikƤk減S 5jIWNss9qs9{J| / Ίד ndvk> jΡWmFJhSEn$b屖Im3,";w`*^g>;q3,LŌHqx5["bP0+%!c z&wi%?NmkLFYy$FAރn01W!"@oϿOR/.w69m: }MKg ܳHn>ɾYdS={[+F'TLEOÌkM3TZRDX[xҜm+#ȁG|6?y;KF@hXHI8&ewx.R~$9g%$ p' AߡڣmFm ZU'Q ܴT~ϋo8ˊߴT~13BԶ7Rj9ϖxSqU*r7eYZt7wNpqmh#η?Uo1>Oj`e[|~8EiOd#\}!ir':^aj>}Ce ')C zĘ, EIy:-%u +2du~FiCmb.p⽷C15#BȚb#\KNW1,:NW\UEթ15$b49L0m ےsғ`2d/\1hb] v^W_A[-(RkŞ&bAU2INKlENHn71G?L!GMW<ɫ[ۍA/ ی yb{_ƶS&dL .yV>T/Wٵ$xn;&.dB>r[ڿMux^oZ^x7s^;ٞѕ4YYm5 1C HBµ'=+/^kXޥ''dKVq1e >}c~u?0͎Gpvzq\iy)d "&f1galQwNN{z?Q.T/۟s_1Rh?%dq5}a'Ld'5S:U2ޥ["epF[,qGҿg\SA M,.2¬H#a4a!TՌTfU$QSV?TfU?ٟh3AEOgZ>@U-btagf$RIkS}V9}N#ޣ3gd ȇЩ?H}V9_CMsYEۚ Gٟh;]b,CIq8>lm,pƱ}fU̖4. 6KI%ybn5?}V9_CMsYEۚ Gٟh?T?$SZ_Y\ym0?)ufUAtnn/)n_dj89gn["o:51tcښ N[XnWZ~ZٹtY8Bƻ{ ^*Kn@v+7L^e}bk3e{"N,F`CMM;~^6ĖTXnu 0?#ֹ#L6WCvOIy_eu(YA+5٣PT~[1I@=?Z-XrL#7N\:9ʯ?]&mo9S-ۘWesoӃ+W<<=ZxIj>ERME$%cc Ɯ_(i\}Ê_| ]/uoI!e+>5m*K8u=Ox@F!/^I's^˯|tf>T5`=]WsbMO[RLbfh?$$W שF?%]ҏDRz]\yJ~~KgWhG9r {t'7vb>FxR6ro-\\7U<<v 䟳ƨ ?5pgF-^{\KhXΣr[)`á{ RձT\Ucfotoxx-20.08/images/update-albums.jpg000066400000000000000000000741121362435004500175730ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 257 532 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?GK]";{oܪ N SV?5K紟٣si?QQ@~?4}'j*(_=k{I}si?Gf%\O紟٨ ~?4}'j*(_=k{I}si?Gf%\O紟ٯ?g|O|Aa-Ԑᕀ0Q쑠x㷌GIl!~\HHkH+ 9'([\I7oc䏵=k{I}=?c)-,/ ;Ko uy423$'*X1]CG7{]ҼEgoZؘI me$ʒT)Y7>9\O紟ٯyA<_V7U5V|<*ħ8هMG@Nx skYġQYJ'q_khY6;_q?k{I}>?5?؞4iM=:'wHp[uxuÝ^ZbηtE D[~TՉ_'h\Oߵ? ?:p1Iy{d׭7tKڏVt_ L$k,v/`r˒x篭JwM&k5\O紟ٯ_L_,߆r/V\Ixm/$onpxJ紏ٮ=_SYWv`qL[p1Mk7 m/ɿ3~?4}'kꋯkyq/XR—SM-Ss(o8ps^]% 4"X5H5!qϨ {Ӆ5h$b_k{I}>?5AaFo4{OM/@E's"˕<;SY=nAhhBw+':Ts{oQ}7f=}?{^}v_IotN-;C|0;1|F SN=P-b%\O紟٨dk{I}>?5/f=EEK紟٣si?QQ@'h\OTPk{I}>?5wZg|/ts!%n.!MѢUyyfwt zuG紟٣si?[>çcxDۡvU v}+h֔6g+?a8:' Z,[˧ݦI$s*0W; p> 7K(9լ'c V$x&?:\_+Wҟ/?g@M?p1\S ?_ .|E})@M?p/?g>ky|i&8ƏwƟi3h 5_Jü4O?G;O44Ϛ该?_  tWҟ/?g@M?ps+OwƟi3hy|i&8ƀ];O44ü4O?@\<5AxWՏ4٭M?Đ[$jTFb  ߥz&i|W?]\C4qDı$aj;O44ü4O?MK~KVV?~^4xz7Z6 a5Ad}G]_@k-⟆w>5hͩ=N fy:|g ?_ Mt6Pi;߯% gi^ ·M>Z>%X1r #W?~Ԯ*}'+lHV?|;OyV 2{I){g(4#J'נü4O?G;O44֏[[b[Wy|i&8ƏwƟi3ii|ק"psğ gg|74X޵N4RK)vSk/V Z&yʐ1ެü4O?G;O44ӵwNjej%No HY|>Ϗ+F?ݮ[lj?h~)bpZWڷcfߓ:뷿J_  Qw_\nN/gmO-iY4ԵoWӄf,j$WEű}N펦n-H8o[n3V?_  I%Eߗ n{k3[eܡA ~y|i&8ƏwƟi3h )K|CMaztm.g- ~#JÐ:ztZy|i&8ƏwƟi3hoWZOtimVY:i6⽗ ?_ OR֯3y)71;zVe})@M?p/?g9']cmv{Kz.Gb $_v .8x.QV[yi%sv,<x?/?g ?G#CӰI?VZ#?]Ǻdm9Yh`E'$d@EtNCyχ^N&`ֱiq$KR$Ež&?\ ]#Ěq^‘!u߃(?1\3H 2s1zs?4p|}0{ԏθ_x7>6zd$ [V@I`W;:V=_^G1q &Hd/6ClOzS8/~",qzsP|C֝50i%Ԁ`2{>)Z/|3 miYŨcK ŀ:20t$wP{ԷdbKRxN$w(]:{Tc&NNq/\޼W~ ? C=^^%j^ 4 $K"f$1^צ~<7:3ۍ2#$'RcA~6!;[\H*2Fs'ހ”FWw/p^3/j;u[|!kauyHa0gX,9-JJ |y&gnek楳lk)!ʞ*n=wn&ݜcj9^>x]V6 ʜñ2= xUA[i'[E͆8lvr w{k/m-Mmo!q$D Fg4+)$?;/ d'} O V@F8σ<,:cmYnm7 ёyRZv=-<#Ve13(|'_OuEZ^Hne 0ٻYЀXEioO xP<5MU^-=Dp89-vgQ\xL)!Ķq$|m9A6o2Ƿ|QӯNu&#ee9wyTUӯ~_%:Eݗo^0t]ˋ(Y^("giLsF½ߵnms6xWͭMrYi v(ͱ[ZI'Z=ZuSq{mM&/6ܻ4w{L xO*-WCHe<.[n[jn8l{%"4?W4&񴚆"AQi'cJ'#ZOt*:׃!x& K],eү4mB&g"RmFH;< G\}77ɸ`d !89:cn:y>~4‹JMc1/5x<`l|C"\k:Dka@<ʝvI_w?#ź5fŇ̉'UIz0<@?vE=s}I~~о +ƖAT\xXmcG{4;u~<=/j6DgI!wBgo';׮ŒvU1~1<8?t"Nn}~M[|K_ /`Ex;Dn,#bduFr5m x[X12xŰ"<-~2mzWm9s?BGN߯b|h*Wy>?BGk'k-MÂB¨ E9xox{:Շ?/Bܖݜgi:*]GO+h^"Ӵ{+]&Snb1`qG%odBO<OXx/>_t;`i `J;G|K+??,|__^VYf. 4rBrH_DQWcW+VzlGYp6T[ųxr=隗O x_K}NRޮM7h a,Wab~_?ϑ)|dZF+Yig;_ :?)L V$H?~%'+V~#YxM!a2}‚22s_]QU}n+-/K^BE𽧂ᶊMyeaʫZ_π5tO ;MV{ǒ(YT,Rȟ2]kho[X(C ( ( ( ( ( ( ( ( ( ( ( +h>+Z/-*xmW#О)ڧt}Z4F]OV2-1_h 3Es7ῆ8|QYZsJ('cYs ſa]RN"c1ހ;*kuqiuO рeaAxYb[əyȁ4L J*-`]Ւ4xu'Cׁ> jw:GRO/\gGE^oG+x?7,*+x?7?[AcGQ`(W[AcGSeym o$,?%SGHt ms-5}"wymqG&*>_8 ¶?\AA_GQ`(Y¿V-KwLY7VwQ P>_PkoEE^o\6⯇^"Mψ7="p~yW[AcGQ  ?"F  ?"VoXX 4UVoX·zQi|UjtGs$#0 [_ޱ({?Eޱ(hޱ(oEES4xXִ/ڎ26k#(eG  ?"F  ?"nj<'/Ė..cm!_',E^oG+x?7,*+x?7?[AcGQ`(W[AcGVVSN?_ [4sVB"|IgylϩIMPgw5ϥ /5;jKX.V[&]`m&VcK?m^;eCb|s'iGfaD寃u ]45LK !+AkV?N:jgy dpyWcV#徇Rocm%p0`(Mm}$^ר~sl$~(~К~gIS_|9TF I*! Dƪ_)Qp71<~"owo'xYww}ώmCg%~[>-C#4=B ȭyqO3úĞ="OwIRBNI'瓷iݯV+j5kS|R5 ZM𲴘<=F0뜺E=c:Ƶ x9/|IwMV[ BVtfkZ0h^Ҽ)[mJ_,D]bI85uoY5 HHmI{Yٙ%ea1Эʮr }7[i~1?z6!Ɇ?3K<k읢? 4=TrOaeon׭q[[mVɮx~;C|#R? -WvVVRCl<_d$=<)Lgd}w~+mD8d~Ip1qZ hx+Ha!uKE]9i۴FV8W[QMt_>AwRZu2&tQLo%~Ԟ W33ww6%oNѫZddW;N>ZS_SԮkK|e#-/4{W[o)4ٵ@yJ7!k_d֯lnm*JtZBH'rHKR/6M_χ>7}|-^K?wGL_n_K(#q>&Bnݵ/_xD#HH\Vwcm##!fzmYm٤%.:+!VM\ w _{~:<ß=K:΍i>OKHe%?iG=(fylv8> O3kKgOd|ל#^o꺇"T׬uOZڍZ"@cVPk7u)_xƧ.~Ve k]NuJe&+lƊ< A$Mi |S'?SOU񶋧޴>6\Ѯ·lzMvtQto ?dߎ$-9.m}Qh$wngi$&H]@lc;ObWiaeaCheFhHy?ٯPA᳌E|S|'GW-:h-Md#U,0z]}KZekh_|Y[> Go-cE]nZ s5|khẼ|qh֚T64ieyRx%bQ`yI?bS_s_^/KVV5De2ؿNmh6{iֺtAm5H(:;duQJ)9[_-U7|0[k-IkK |҉Y$Dp#x??;#E㛓Y*xzEt)E7;u_m<sTG9n`.bRxd%%J$w5|n/O5>$_X׷𦗧,e˱5D|{ڟuo+~i#WI G$,[^%r7$`Nk;VIr{O?m6Oڶ'^ ķ-ƙethdCK(+PNXաM;{i]|){?zO"i~.վ"k2k(5;khy6ۘRpb\^B&ቯ&쥲{pHE*YC23޳jn1ﻷcJRrs )o7Q4 $ zGψN<9uO 5Z)yd0&Y޼a,f ;i>5C4em$ ھi=k5 jīKcR]hVә6 7FrEiR\՜Q9i(F_YLåx>Nik}|؋wNUr:Y5Þ vZχvVlX2qң'쯠h? &K{R|S7Ј%6jƆ6Q"x_٧_u_'w:LrBie&D-P'+JM)k xZ'ڔQE81aFKW>"I.jK>9gPoc󷞋vsLRrռWh7Vڞ7i,+t!J4Nn ;xqU$}j|L|rگF1]6vWvF0svi,('Y|U|faH%9e$ >)jš߈,AgBE0`g`2IcRsi,('~ğsO#Ēiֶ^*7vc@v2 냋x/<1mVDf;ˇ$(ؓ;Gzv;i,('D௱L41ٻ]۱GxVƏG'Osuu6Pf?"A,8d<@?dW4ꖞ5.miEK#iЏ a;Kx@z^Wtwq$c8*G>X*' F _X7E爵 rcBark4Nu> t#<@[w {mlr4]@8ox+ ? >i,+,i,+5xsC\pC¸o>6C%5>jZNJGd*F^y9O#OgaG<@?z k}/Vq+;xu,dr}iG xQKjWK})evEx+ ? >i,*!@jZEΏ%nUd+5Q3SY7t+kK_l5I8%#0csOK/x+ ? >i,+fo.ŬH@M= H<|Wqf0w w?dW4}_YUC-X4U=֑uz#,1DQN%-ӰBǾnm,u={JYGSq |W_dW4}_YW6業]s= 4Ϊ2B"u&iWh?> Y.^/N`t Idni%wa}_YQO#Oga]|W=/⟄[(tB 5u+PgZqgsIG?L&m/\j<}(aO#OgaG<@?z j;M_:Vu"H/ob}BEmE*OI !A(9E8u;_X_30D1![hhn3]E\o$t+C#DĺOng֖?ZȦޤs_ alK[cD(0Ԏ6VS}-?>蚯?B4en ESu EE:ZIX ㍸pFGO>_jJӭb2qT7g{iw~$753G+S~:-Ã`t<8MQ9p9"i&Jpind+x } yD5o8dBݷ{S"O58 V6Qd +.G}_Ǜ6Vv-֡RGm[~П\xc>;/ĵ^>eNWF+N1R8S乱v9eV݂㌑_Ch7'5(ݲ:+01v܆_*K' $@%*L?WCL.M#ZΧ}fmf, ";%k|ϗ>j^oxfO|NZX+NT<&+y S#1 c =O /hYo}"ԄbOA1y\HZ 7[x;S[uܸqןJ h?~h>&5+YRqYTgo[w>,!i=nf>V[=6i[ko:s+Re$[&a&z1D뮟~ox 6^-Ԯ4BG~+Q#tۀRHT60z .`L5v c&EH(}kۯ.:E.O~W﾿ϋ>Ao>'Ӧ}.x;J ݊>-0zG~~~5|?i'񵭝aiSxʀ4J"ă_p(&2mB9N~tKyU) PuE&`YmC+&)^IkG犧[a?I-/j!dodmJi|~ٿ&<=khs[MXEq-IF^F: yYguqBibS㵊y&Oj4MfLsODڷ~uS8ik}iכP7ٷxd۸Cd;6P|'㟉o&:_risI 9܍C +݋^C1["|oy;U/0+eǣ~Ʊyv[[|]o>& _ׁM;(7?Mڥ_Htf6`Ϝlخ`^O:>w%nUMMUs9]*~'#Wڪر!c =*!QwD\w~}wyrowM&]~p_4~Уž}VulX1lcaۍަO75߉`?ǥkOj2Ac1PT Wϟś3拯_J$Fq *X&zm*?N]W>8O9i9'b]oܹ3 ;q3/72^Yˍ.x|5 ,FW͕:ǹ@_RI,ccfTL}T6i(_C o;8<DܓmD()\/x>>|񇆧MSF4}hٕm6\0q=}k_|2Ph@[%[4TjyDP/rG _aMsojO@[Y\h/i,V$.OS]ý¶5vU]N,@%ghN#g+|-j , cm^OZ,F,͋y8"C>xX(eko 5ƮljkB#彎.VÚ=3Ʃiֶ6֣!7? FޟJoů1aFKT\CDе oZYh_̀a%bN]c (K*CI#h,p&i֟ ϽyM\@Q@ZS&eE@QU((K1啂RO&)fU!ʲA5MV\Y]CynIQ-ԐpFGEPE@b681|( *ƣiiqoo=0rJiH!AKvKp@2 E$@(##MT'GX@Yԓ@i~ I4JR3u)pJkB ( )3E,SPjwVw]IBѰppGhZQiCuVB=5f ( ( ( ( ( ( ( ( ( ( ( Z_ĄVYؐ3k/#p_&x4ozFx[5mfXad&\4cR:zV>{&c\ I$p:R|@msOx\P {$f (9pCڛՠ[GWjpz<9/!?P TUGcOs^C⟉uoZ{EtZxJ}bmK;V  ȊbO4MJ-_IL"d07#rvR28n pc8>]\,y9҅dOP^&}.کZX_i/%墋paYUC29iu+m{x#Rt4/m[ن<!~;ɨs}NK+{Jl.U]Xl`w|dkqᘵ.~ow yyȲ*F%pW= y#^-,{&om>OVwS;oQVoSUxs_ .i)>!  Km:%]lEfacד]Oď_M-]Quymlbn|3`q^NO^g_x2fFP:+ ^bn߯V?׋!X5_^,&Mrdc5hbrpqW|T/WKV}˚VH4v dp7ӓYv uꚾ%߇t+l, \psƻS]?6>^淼"E8=qC@z:Oşx<=i".[]@fI[vFTן- _߇4OզX{{;K85fgi$PNO |~?ſ|[xGӮ4&I PYgp*x +^|mx\6M%֕+y.Շ#%w4V&j {-)5;{{]Mo+IH\ʤk* 5gu{=gTufyl-Z$yqUY#={קU>[(((((((((((H6춖9ϔPc빅A?.憎EQП__]2 U u}E?.憎EQП__]2 U u}E?.憎EQП__]2 U u}E?.憎EQП__]2 U u}E?.憎EQП__]2 U u}E?.憎EQП__]2 U u}E?.憎EQП__]2 U u}E?.憎EQП__]2 U u}E?.憎EQП__]2 U u}E?.憎EQП__]2 U u}E?.憎EQП__]2 U u}E?.憎EQП__]2 U u}E?.憎EQП__]2 UXo%UVOF@ҭPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPY֥5c7gb7O=qZUvo%,m?M2Uc5={R-uVV7d2@P>/+>ږ]6I[3GI ݅ŲI5!C?ustmcgܯ&u0ds` O$ގ.u̟Uu̟U|%]Ğ 5 ͫE]O6Uh: k+?ߊo5xÞ0K_ xP4I-+`!7˫ kQj:^5O WWRH FGjkFa t`K`PC޼ᖣ{z.gGT}6+ODr:{Ɨ)fFfr 0A'!Nn?on?o>z?%cxcxU۩Ǧ/$}7'uc6xS&z|8G,*du~F[ar-̦tMHi[yY:OS :&$HE,ĶMZXδ{T$$lp} [OP/ɴ{ O'CYo/!8p#bm95bxr`} Tߧq_Tcu=TO5YCnɍ6'S銟n?ow?jZS}gRyShW9:I.Gxw^7Kb>jwhč,Rw\=n?on?otڋO){ĺtKmcN-48[͘ A 2>Q֍ /DOk>c%1դ|Qt_s~4'Uu]OPۭCP4;Xi&EHFY+|$~$xRei|BNdm 1q\>-uGK- 'FKi#@3$rUx6Tm&ϩ|/xm/.ğ.XF}x E^Qx(׫֓\qD_4SQEAAEPEPEPEPEPEPEPEPEPEPEPEPEPEP\Ѯa켎p%Qۖ񮢣xhH>_]j}=bﯴXlmKXXBgv5o>Ox[B[r 7"Re .'8Wѿh d ?c]ož!׌/xD {{GS(,qY6 .sxy<;svӘ7}_M1&(cGM'Pi}7G3eEç*S.UO!' kgKIKVd,TX1]gu[k [yGU0*[>QE[hnUNBp?:%^A{Ѽ pi0XӠiDh1\υ= ~!AZظ8N6^Ə@O6? C݂D|z^xr^} ;?d;7cYP!YzԞmgjT3wٌZhgS*FkF42h d b?ÍCO~0_klH9"J)m^;;iᱰ[};L N@ݷ3]#?l?­ZRd'%a '--CF/5yo. ;sFO|7B! .<[[EG7 Fӭ~4s$<Z5 G5և^en%=^XU$T_h d C>b=oxĺM*[泳0UڑI#x_gVmׇfH'1boݎh d ?cX?>mSh^ EvQIh E<YP +sþum// 115 327 0 C     C   g&" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?%ӠsK5}M*o-H($gsQ@"|Ň#zWŞ=[}K:cs:}#WGׅ>Oe+0JVm{X֚>e#B1>]k+ocqIĂ5!f gׁ{e w6Gzsש"K->AYoA[$5׃,]U+\2߮_ez@ A<{]A}e eq0'~'冷}iZfHf[ba+czW +CӼ OܑjR~w(BH3v\l{%Ԣmf`Yb)`:A yxWm[oIK.m3#}) OL5j^cgO( VOڟĶjb>X_n D[bO}̾KvZF//AX5*+6ý݀Jno̮]_R $!\_'? WMtE'̑燼Q&}y{J{;fHlf= (ZOu߇>7gMQѥ%IB8ݵ`ggQz?O( =xS|ZTon0^2ŃX|q5WڬwOyײf"]N}ǀF =G>m}'Wy|~xjMpkHhK$pOړƐ tЯu{+}=<gbP}沿hÕru!G_W^;XoR_Em@F6F\^N+_6~"Ǥ_Y} B'(X7 ҝvb> G0X ru!G_V7ڽWcgO( Wj,.j(=(.sD[aJߊ/sg}%Hۚ }+~1"(QPq7R,,ȹأL w>٠ey-frJ}*s.u"Y%GC,It'G8?ȣpG#3_wP\AԮa];3 \ne${>#rxJӬ hW5Ɲ"mgFIZϱ"| As\޽5j-g![ kؾ}(aλpt? koaMZ4*9㓂O">xBw+O}7MQn.ghg!00>@z/1"h4;(5iVhr 5χM{wgsD-%4hg^@[_c+h:ս]h \EnЁԸ+ԺW}tNtVP[8ʹq_2h hfZ zןk6w-[ n<?^ZxU!|! SmZ'D85M$SvO3m/,i֟K y%r2.q N{GRSX[d%ލ|&7/@ѼR;kdtd9K ]mRWEAUѼ;O oS>~]J tf9r<hwbM.x4ɬ'd p$l T^yrj-7!cI񟉵 UԬ-㺹 $H^R?ŏG:?ԃRZŏ>QQ36λFzW;@;o3ErWhϝ3c$"E34qቾ!^_š2WSѷSoc?5OTݮ_ώ5gy5!Ҡw+ǁRž3 ._ ۜ|7ĺgntR_kp4;XtO8լL0Nme\69qτ5>mt/Mڹ{rm>u2>kŴm*v.`򡺂=ya~zg">*G G!x8sc7{s u$Ҏ_/96oGU_- 2MLĪ"G+BpA(iaWgǰpz-#>R]Y(uKCPCK!Fa,o&B`.0_ŝQ|ORMNᵳaxr׳*@c=Eh=u~ki~O~f5K-J($IU3TqGxD׮5?Qw6I+!I gϋz|1w>Or/@:I@#YW?߂wu7sl|gm-͢ɉ}qwQ} C?})ETQ@Q@Q@Q@Q@W)-aysl۔UwNҀ:Zq6^.m]GzvcY=լ߫?Gx_~O΃Y&핧62]u[;CV~Goj?՟#Goj?՟# Zz@tW? Zz?XV@EݿB Mou*8׵uQ\z/4u+.+KX'j3HdiF <XV@;CV~Goj?՟#AEs.io,}RF BZе=:FVPtR?ZE`ח/i]Ԗ"9{UE՗H=)լ߫?GXVG#5k^_=u$뮀50)`dxz?5j Sjz\pd]\Z6J p ۜZ}ږ&ug ƙ$^CH6𿀼9঺mE^詝bd0|T)ԦtMR]Aj%HX#9˳z`t4W={Cf~3f~:+?6juM2Kﱈ]n&TW`ޠޢEOPD=F*VlZ,5t 64C*դ$v]p8횹+]v3}udY#`֪t֓Hk.<`ےkx~𦇫Yf K%=3,QDRm9*8jsR|Cv?+5*`Bq\)ȰOo/}JnT8My qNk ğ^Z÷ SQI9oer9+O6E?eͷ̯OvSuUhxO( bն[;ߎXO oX]NrAVʠ0%u7x_a0|p Ins89xWo L [X4=q5JV#˅X U7Zvo}}OOUpG4oN)f$ռWv_>Ƿw_eWcEl?a}$? CfC.I߃u)?ļa@HŤ֌5Mt}m {5P !̧R;zVKxIr;]{딛Lduu1?*r:g8~^赯 t+#ίp 5naa9$_22rHWx7hZ)MԂV8H1u0:kK[;|iNF>Ԟg|gnyǮ+o4NJ}ÚI겯owIo3g2bH<Kv .8uJ|[7f}l>&kڝj>Oa.qw=C"pXNJ^nal5#O]^7kh%`r`'C'+46CЬ0*X,6䜒{km̱_j wHpA}Mzq/f׺Ǫ<+8.{?/R֟9}?P8sk0ۼpV 8*kcK^>4?k:hjl*4mUbtV$bxϮ>3Gt(ds-Rv{tYH-?j/nc2c0?C/7~b[i4ѯbYἋU%#| Px_4|Eޡc qilcsJd/II +խTQҥ'kY;<>IGB8EIJ|MZʖ.S&5kJiM1gQ]wxG/_1vx/:umkJKFM}mVUׇNx;_1NHΒg+vCɱQ*T~RߎGqL"$W> ?[U{(5}[ f0ʪIV\(%~3氝tXX@3z0$Mhzn%hܞDK;}א ~.>hjm&sݎ$qaxFAEd^؊nTdv #^Qϫx ccA˨:{ip*{G~+|Bѽi\<+a :d2g̣WQwm2FWSpNNTJN(9¸ L"__\ב] wvXi{s/wp1Y&yb2 9}z)2oOÖXdr8|v fw z7Q k5GR?Zj&v۲Q `; a*1\e[mCWO;^8⾕sE(8Z?mx;o±㟂pKӴ4Mk[Hn4.&bz*΄.1m u>k iD0-ޛ.`88#=*-ף|_UC^Tw_Yn^W43NÖOeBQ|ȉ) {Wa\-ף|_UC^T W0HU:+Gm:݆{%,Z\Y^HBi<C+^F[;$zmG5x_Ӽ)♮{m2Q;#Xx޼{?-o]~!V($-,^fuŌngڲ|E-;*lmr`xcfS'*0:V'-ף|_UC^UjϮ~^\zF66ZZC8bP/ DŽQeZi>(R7Ě])D wrG($+ə+?5h_Uw~\j9Gܸ{vS bh'?f}5Cjz& L#*ߏAq#uEf?6G>MI.̙F3N2WL[MnMk l@@0N+ϼWCGmĄI` I- ݹַ-Oף|_UgڌzZ&rzg%? Jg͈익8qNJ/j^KOԤ֮] 0R3q`zKo?[qfNs]o}jmWT;Eq G*pG#F+,UGRJX 0;mݳgƣwuaqڪ\mVm$m kIcEbי~)K[̀+5#Ic\|/n.n,Me3Y_dw>SJTXe1ɨxr2upT}w -#c?'-ף|_UyARj>96ӄP?e߆:]ީ$Z6ѩyO4:j7ǚao?mrC^U{N_o+[[)kYDBUr8NiAB;#b*bʵW3( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (?fotoxx-20.08/images/view-metadata.jpg000066400000000000000000003125351362435004500175640ustar00rootroot00000000000000JFIFHHExifMM*V^(if%HH023170100Fotoxx:trim_rotate| Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?|Cڤ<#Uf$ '!TVg}LIgdQs> <运~gg\OB4/*G@<运> {{4/(OBys> <运~gg\OB4/*G@<运> {{4/(OBys> <运~gg\OB4/*G@<运> {{4/(OBIl罷9Ud.y:Wizo [hl*<$Pxҝuť¸%_4/+4k{.5{6fgp{mwڠcdE;RԵ;/5ub%'#Qml+sOB4/+WtOh%4 𘕚C csgfx*) mZ농w 61׽$!G}xb[_A5S_ު H _UKv~L݄͝1 v0qq4}.qi_Q 4}K\Z(?K{!9߷hN χm"Mv}"hщ\p; si[ap} 7Ww6ŚΦ "ěw ,bF8z'm,5mbēh Y+eA8运> 4o ]Zkm]f22%T"y'qk.TQ-\OB4/*G@<运> {{4/(OBys> <运~gg\OB4/*G@<运> {{4/(OBys> <运~gg\OB:1gv,T;;A Z( Fz `sq˲Eɂ? to$yvuaWG2\Ams>0FYt9-fiI3sr? ,ZX$Hz  CH%bꈅݎ2I/]Bk bd?*oʑ* ՘d" "6u`URN {8ė37@@8k]G+%D|Fdt sXG.5)bX?s8?ym śc9W=eY8f~8xF( ( ( ( ( ( ( ( ( ( ( ( ( ( (>q֍־௅`ͩ걃нCPŸo }h}kO7ZCQ ?*"ϭϭ{)kP?O7ZCQ`<yy{? jG)kP,o>o>AC(? jEM֍ֽ5T?EAC( Ѽ׽Ÿo 5T?E7Z7ZS ֡PŸo x&F^ ?*"S ֡PX}h}kO7ZCQ ?*"ϭϭ{)kP?O7ZCQ`<yy{? jG)kP,o C]Ե+K;ǚvU19k? jG)kP,c+wS ֡PŸo sǭ[i:٬M ayC,C`WŸo 5T?E<yy{? jG)kP,o>o>AC(? jEM֍ֽ5T?EAC( Ѽ׽Ÿo 5T?E7Z7ZS ֡PŸo x&F^ ?*"S ֡PX}h}kO7ZCQ ?*"ϭϭ{)kP?O7ZCQ`< rz4hU2@{o)kP?O7ZCQ`<*[VWMV1Gq%\{)kP?O7ZCQ`<"K1v߱@-6a;I\ao{)kP?O7ZCTΚ+JUgFj7f ,x<#؃~6~*iOܽw_AC+ @%jpIcx*sOzΆ46ckbu]zQ]' QEQEQEQEQEQEQEQEQEQEQEQEQEQE@嚸 Tn-fڜY_5%a2n+tLZRxK!5!mjKZpw墫@ `Io csF-X{K^B+*Ɉ\7dR?1ZX]"B&*ON Ik,4DYCIm XC{ k[qEs"!r فAGTw^׼=H}PH,ݕg[gyG|~2ھ}>irOkm& !ϖgw5ULH(GQ"}*J(?<EAz֖Vs\I1QP2O*fxLޫJ+A9;,]M}iŧ V3u f{rqeNܾMb?[I9UہrabݰGsV:^iOhل[Z2Hc,@ 9< jZF-ZGIm 752.Kn''nHS2uG]R?~E֢ᢳ qyꎡ.6%_"QyV5)5;rxrkpOu I .ΪGN{';FN-[h>:'zyPI2INrFЮ5R,pW!XdgU]{X7ŔI5ɴ"# Oҭ|4/o~-cIQ>R<Mrt-WVԯEZzAySrT`BtkiKJvFh GO}~kS;X,[=)Sۀ{qzݻ/ltkOMU"Y^,mikz^>igkwo=F1 H7~^o^hiy{ ŶhcQb 4e +gz O6(ֵoot(anO6~v7Kv;(UFIyvMnJ^2/LO"x`^('o%.szF[j`Szg/>t6WMB{k@kdVm;cSk )@(0NTp'cTO'cY-M4JP1`T)ہֺ{_ϋRY,mJQvLeMCwGu9ӓMu}OXI5Y70!7@C]Q"QCcBѭIum.Z2OB:Wtך*àH-.Inb@TNv_DQ`5+O.R[HnyBB`cӷҳ IGo&}R_2b3AamE/hOuv׭y4+ TቓWu5Cu=Gp_t5˫;w-nFm*Ǩ9R[_:? S۵4n|'Pqnc*JYxȯ&τO=o4A kt9 0vNs(8<׮vgyG|>%Gxȣ(RQ@(%Q"<EIEGxȣ(RQ@(%Q"<EIEGxȣ(X9o5xI e}z~as"Y#;|{FI꧄J~-ֿγMtKVX̋q F# qW-/b8^mfǏ|[i[-A$חT<nzxx[]_$qn5.ןtUˤ4_y/՞Ey]E]w6q MثGSL@:So~.Ӽ54&;8\\0! n'idD,Z}5Ys]N &`L 5~#ҼH3/ x7Er,.` yUgbVpqמ)|c:eIA__k'NJ}7NK8tX crcκes0kh4WF&_6O {aޗM&FN2Vvzm +ʵ?:We]j7ZfyUX݂@ v:)NtF0 N9D?I}tz+G/jzo=-S\[IHj@޳o|_ZxcŶA2[ H}"jX f_k#g;x$wXKPI4;/m&ACc^|KB{0Ibeg^ P[I`tNɢ59k\I6(N ((((((((re:GpˤKa%ٿU{{iVsKm!n.ʳɗW$'5U2PQEQESW6U;[wحIl:ҭ]UnL[(hE|lp==9xmۤww:6H;rnS< xW.Em>pIbafr`3Kk~5֣ {I<=/_4UӮf|1kq<0]Sl[Ph 0\麞aq#w7Wb2&wP6b @*x{:?í+C%Nђ20Fؓ#qT_šW2\@LaKF x =sD#kǻk9 ;BcnsU56Z{hn:v$~j gI=+þ%mcKC[q_1?w12Fx׾xK}V:Αm]_H*e 1`2l0Z_o&B\,r.~S^>Z#wHNmN}*;8 Iwgʇk`'x?s /RMAOޤxUQL6G.@5WO!^-26XdpP|7 sD|ߧꎷu{g>FDszzDڢ_ɪFbՆnٽi KڣZtǛOtbn"f6yAM15/.Z(mg`;Whm~t?1G=83IU+\h΋r[Cr|!U=xJ>(CMOfmy0jzÉug56=LV&&\` i~ǣ-vwj;}bHC~yN1s#RO3ۨQEQEQEQEQEQEQEQEQEQEQEQEQEQEsn\ZQ5!$n8$q^]x wZ\n5ynbe!`t#j5p3G#7R5;#B-6t~"hߍWV&l֬%֦i~ Gֽ$ս0*:jWoǖǖǃ.*+vK/8tvN3@];fI"ж0p%ȱX(im91t!N,w}_lyߏ[->uYŽq\ |EwZ.و !rISOW$*lߙRiko][O?kmoNf ,?p<<~($Q-4Ɔ6;FF*2?Kkt=^|OGm-]d622ݶd vӒpxk~8]+Bs,Hat[xpq `cB|C[!n`o̐m(WH]LqGchX?Z^Wjm%OWoSV/dԮEה̏ Hmk}kEtXt9, Qٻ y,o(] pŸ{_C5|+us|>9V?V]Im6uw"8 rvLuԩ n>(9((((((((zC_MZԭNc&WҢٝdy3 x8#?C_=#Zm;{MVU']i rqg%\æcz1]_=:G$sqyS+z 6dgxCxHӭ즺Wm"L$N9c!'3uͣӖthm I$$J .4zchq= IL:df^Ojjvc>3=IH1Xmrs)\n+͜˥:[3 "9z\NK}VOXk'dRr_\6g+῅<)C'rӤ66@\a!x3xqLZeQ@Š(jWka]^H"[ҲGxEYxgW*acB~CQ>Ú6hqIkZޗm}_GVBu 2 7EjA'LMԥ,Kk;%%F5 A8`%?׵Z-#vhm&K崍0';v `Uu[mhtϲ%I2㌩.8SןV/~$<uMGmCL$jml `ABkZ)B֗Sib$/pP r[Hn-]SP:Lj,25I$<Uh{}zOG6$0aO\u-qE}G˥2[d°H%WaoDރhz%f?4i%]SxĞ#u3urhzV0| qNx?sx 9-5)tfJyL#4L l:غ粅;X.#:?$MKڬ6-lV3[vw;K*;k\iVpi6{i4rqM !\N1M̗]=B??{}8<ϵ@M OFwmcYiV\)C":g,goRrz]sZ ſmt TgrCK}K[|Wddʎ.-aͻ4dda ':RxOx;P|M|nZ .9MA]Ŷ>)Եi|q<~Z prH<V귺W+5]Bi\B4R`u>KkHΟ}&nw0DV"A0r @W״]OKerr`}B4rB ]w3jav6> =?Ǚ!spy8 #{Qx]=*5ڦ-dp ^Go{d^o ]hiGKđWX<1^#k72\Em K`3 U^ J=,i+mejbX?H<ִ|E=>2i^E~]qB=N^_ֿ(ZMBB-.ڐ{y[iALP ,@Zn'Ywk !$`\|_5x`Qӣ-m<@s >xkC;ˋ'4(呜RNݎ/?E(|@|E;SNe,mMwI\ȸ{bԼIhڼK}3~*/xoĚbu֑fuIXKp *l#*) r3B}]X]0t4rta?"F~'Q74|Sd#W$>[cr;OjCi:Gm83\>%^Z.-ml7q+WZt}K^4mIs]'r:*;H]Vid\Y qn'@?:jv|)^j7&ђ|QT[UasAMkRDk;Xe"Gn z{VX#Tƣ=۝:c+onJV ČK[x[4|_ivRjUݵM m>c2e`xeZ0{hҪ#ce*FN+|%.=ދ Ew C M5v˖!y!]W5ڝ鼔YD§D$&Wv.ן]0ۦ܁<䎧?o'4EPEPEPEPEPEPEPEPEPEPEPEPEPEPEP=k!?/Xb:lQEG]k]PeIⷑf]0RA#jUukV-A $+)e#83"gaW<{$JdԦd)Qs Zvt#TUƟ=2[^8I ȌW9 F>_?u\,!ۻbyj—ܺơ@7O8O쒴wjy.b[C,+qo*|:ԭ:Mg$3]\67bpA<)+_o#y4o\X=^ԚBʹY,H I :u[hEŞuyZ[s^$Cm?1#k=x2NtoT=IGi ao~DFXjBQ V2#J'xHcۯqߧ^MKj~)iDsO $WJvV]89laq{-/Bӭ͕Ƣ%E$-'s?)8Ex_\񅖹Ėq0)6 -+_5m]^5ƞbF27 )oXm'95}-O;"j///!QzroO ^V׋c-n4&ܹI<~7~4l]_ug*M(mBkC$: z ڕ41X38 08UûoԵsz-_8X=`ؖ%'?Rt.u=JT \ZiEHb1ؑX1xi]i[ /t b9*L0=kt'ėk7>sJ5#$Z?\qYXMbGFݻOWo,;K˝9bϝ{lٓ*s}i)b5]cUF[K^KamEO=NOmuM^Pkv7AHʜsw'g­~X> o':}$7WH#CUGAf/<+ZipmU()k~WUe2JUFvU@Sg|MzWwJ jS0q}._zкXh=B <7COxSIּd1nݰ\gt|2]CS;dps9xWC^kI! rNTQ;@]ƭp7կ&ӯ5n٬eċT.q}kJ_,_u"C ېs꿮mcSe] 8a63^H@](l1yK/xHުƮ'KK`f] $+c_²]u]FIo3Igq!W *+>u3:=kStp;Sb(SsOEk6 \\i_\i|`̄aqk6DVHETGzc4fbkAٯ?:#N[9$|9`X/o/. I#UT=i3~ 'E -qu1 |!lXqR+OOЗmӴ* ¢:Dap:{g- #յ8FXZydF㌚ķ{aa4mKT&zm$e}rA#q3K_(OYWS .Jqytmy[9'qz T0}+3]Gwkk7aM>{y2\ƹF,w.}(ڠd dH_Up{HmKm6DP"G;Te? fBQ+ C$#Bx)k{2߉|eCuZfvpMuv,Dfd pP{IxP,sy P4j6bI'9i'9?wZMgS5.-W8yU'i# v[ĺ4O eRͨj6t73O) ,[U9rHL@x} JLs5Ќ;цҨ? 1^#xUl;vpv77<к8ïkӴ/5/ɝc,HUY\zu]bH/dYA#5K[;X,5=_Oh_Nkic.!vD,C ֻ]6 7NGmmU_k|NuXZѬ\]Q}XB0Ky=]&N_RO-Y[Z0e FX=6_G6"\hwl6>S4/]y?<2}ϓ(A9_׎mx]vPY 5pCVNҽJ=PޱjV5`F#v6N1'sQVQEQEQEQEQEQEQEQEQEQEOkjtmQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEZzKoËMwwsr#.?:BZ EEZVR쵝oJ-ȧd59qat?יouֶ7l[V9dSqZIJx!SM>}=!O&w6嶘ąXRwmU9m_?oH"C RI rlJ>T6 ?iiwg$ҴQFH8Ka9Q7 o5')uimwZGs.G"K"`H Rӽ?<׷mk~/(F>~珟w'H}Mk`K{.wtM~f$r:T}QF&BiX.;V;3{;_͗],^Gwip$QCyUP-"8?W[?h[Wm&uY e1;玦Z,]Ʈbv{kԦP(m+qV,<Đ&HcӟJErVl9.SGK\֦5zO,^s6?ZҪz.o:eqkg[6P'W)U5rFzUX@eb|G պ  1S1m4<^vM[^07I gUݻZ V|oSt_ͣYh$G}qXO!tO'!-J!zyqM7ve佊쫸#zD.Wa;O O X" kV]mf6#GG%Ys̰cgAU=o`]f/x:qeHfX|2K ~n;0{|K/t3q|U̓|:s[vQvpA m^џK4TyL;C$1%MrG׮+2?^!]B-xX Q|.3ĺxrOJ+.mf*ٔnX.QT_,JNԵ{/4;}72*r[xDҮ%I,^H\3eKdĕ=W_ hzE_7RIrY|T121#O x}4;5n­pV5r2BIɭ ( t8$R촇~SfQ\.\qK@#? (cc^i֑\d.Hc|1* t潛]-m|n`쌄2y3:D+=J}&iDlŊLr;ctiW1ivwP."f H Sb"#BS{lrKm4p`ЪJ1BG \@;>ޕ诵XoHO^ fFa8O,9bğGG0H?-U.).ˉPCǨNռjvQ n TO#*мy:Z( ( ( (>%P/k?̩JQ7k7̬Qṣ̪~ee <<ʧQQ`ṣ̪~ee <<ʧQQ`ṣ̪~ee <<ʧQQ`ṣ̪~ee <<ʧQQ`s̤2UO2񻠑U'=քs~RI?#Rwb0mcE[.j5V&9MILqq'36-2+FtlA]sEUfTZ6aw.%8 A`P_AxrV+G$|JgEV>a_ϰ,r?X")bAhмe&m]RYM1prdrzVI_IuQ Ç$`;qt;;b\]Fi 1+eQr|48?ġϿk2:߇G^%FgV;ONzzi,}/si%rd$rOpI}Mݳʑ;H3I4zhk^ohWv WckidHTx=ς==$%Ɵ=$ģ\ⱥ;ͤ鶓is"mzKy;H:t?J}㛉K Th]:7 =/FamօAc*:|qV4oM-Jy.Biae{)6nR@9MK_3H+®r ϭ+i枋ALսq &|C t3fR-uk{mR[{m*|yL7eѴ_auZbkeNTa#Њ~tK(tGKL.JDl<Q|z~W%o%RI Hd_蠜oX~ >b-3w=XWA[ Q@³E3SX![i#y.1*A-6h}A-dME`{# p e1ns)5t5QdG##ޯEP-BUٵA4&%718}(Cƽ_4ʛuZ߆?D 5tQ`<NA_G(Cƽ,‰?9k~hr7׽EOQ:-uZ߆|QiڊX^_J[ aw3ڋuZ߆?D 5?ı[nLC"w;8L2լbxd]ѵ!!+qNsD 4‰?9k~koAm51$l&@\c#۸8Jmt:Q$VpѼR(7|ȹ sD 4‰?9k~k۵]VI](26ʼnO`fh"jdNJ>P.r;ҰI 'PoNA_^/ޡۍ*OIo3yTӍ@q^7n8q C 'PoNA_^c;-C6z]7VMuqHFJh9#WUE‰?9k~hr7׽EOQ:-uZ߆( 'PoNA_^E?D 4‰?9k~kx'(CƏQ:-{Xr7 'Po{uZ߆?D 5tQ`<NA_G(CƽcFw!UFI=x/(CƏQ:-zm43Cy?`x2,^b ˖Fx oao6k Doo* s˿D 4‰?9k~k4DӼ:CefnCnq?tq?^i:2uFrUH9s?D 4‰?9k~kشɷoyv\+(Ad$Rkmԅɏ:qu܈9!r>S=(1 'PoNA_^7tuPQp,[ʬ\\df&r<-Pm%y#ҋ r7 'PoL|sO\Z&0 /K)юos( ?~6g9`uZ߆?D 5:t{K=QnV f9v}) sohVU40Z%$Fvz30Ї,He|$#mKinemJK*r;yEK d >$hXQGeWlVHuQHaEPTTѯ*pj#)+> Ҿ}jt$-K>U7T6:c-5KWm'A. sQ*X̅I$aP $qG4E=B%{>V-< 6Ks5qjaF3KlWz~?;\iaiL :|'r?5{F!#'S1Fd)>PzMV =j8|=?\˧Ae+\ܴtrp'm^m$$I<RNm_ILz70M{Ayr0vP"m_5Z7 ~Igk]Rec,v $Y/bOh6Zw2YJ$k(D \q]~g4tMCL2Oy9Y~5kOZ} 1g~}Ga= cVo#?I|wcf@쫂1cѦBcBSzPaПG(V< zx+m7Wܾ*[-nX!&SÜc![~3_z6]ksvd 3%X ۊ%r?f"#Ef@i]&&uceVK{u[Hx`{py+T|VsZݾdҪT9f8\װBHՄ-v^DG/1 O5mGjV/zbԍR,B,Ѭ+`NN3/&M}U&ķwh8TW2>6}zATQaP@Q>T)= ou3|̵֛vr@rY'>d{CsyeĐud%X.N ](@>%"[{iFVUz*=꽵՝퍄ͨؔpe\g9ߕh͒yo_mOj"s V^vYOdzwQ.RF)YJd"81I @늟HmmtM:Vs-㒰<| 95+e/0 Ķ;qe(3FXCcq`<|xզ޼(G888z߄?ˬ[sg9#FN1 ת\YJ>n1SM:oّ_̔>S@X#կ$żP[I F| `x8=3䎁%ѵ XFWct]chE;0׏Y bEiܭ) =pvT}͝yo_mOj;cF=ރ[[n^L(Ub? 9m/K[DTFu10B0NI>XʊVR}2v~T[Y;nJ"t_?\>xU"Q^!A& aW9ìCȯRj2euJS!Ɖ LXT:_aA lvn?Zi!l,Ki`f^7dpH#rke(3FXCcqqe*c'Q7XSC,O-`xt!k(!k(j:?uȮ42; lt|"w*Sld䓏NkZWۼsi]IB$zGrGrXk j?a1BKGU! 0O źegE"@FXg@k!k(!k(jkGsޏ$TD6Xg睊WҫxAo'DzM:R٬yf'_0r0rqWd?Fd?F=wǞIO?İ1oK;Oh6}0c~>JiWr;֖ VO3k,. OI(3xGrGr%cʤ~-gWO6u#1cDI2տ#5jfɫ]l̦P>ήx'$A"/'Q7'Q7<{'C@-`[}JlY;bsGz4p4[%5ī$[ c֏NA _o9GNA _o9@XmGrGrN!k(!k(SuZ#?uZ#mGr[..๒)Z 8 +t$Ai(=QL K_Y)Zkre;.uk>uI&k944\.19J@utW)|@5ۼZCnK%Üa0; ZJ3*7^4Es$vimzʳA Gk"3)#d>fmMȺ6_0lq@hf+۬Rspy8gwm}I I\q@EQEQEQEQEQEQEQE2NQX+&]!=nȵF ىr{'hbln$]z0S Š((( G\ZZ-V-ڣmow$~k)Nkԟsj9}JI/&Hij>▩hgķ,[NO+KGۣg ClD;f=OV~.$_ [3B:N}˧2%YU +K+%PQ3NI  JimZCkH0 9v?3~f ERQEQEQEQEQEQEQEQEW:Կ贮]SEnZǴJ(hP%딿-r> ܲxZMjy!e wy| ppsZzKXd<4MX\@4 m""Dwm?Þ:TsCǴ j>I}팳N%u""\Kme֪x~-:\i b )9) zV/#}H5;c[v<#r9V:YoĨ.`-zaBW*ax櫯|,˭9O Z$q vp+:K6i("zj}-t B(0((((((/xKTK+vRH28@9n&8"ig#FYh`Xúbosk ޼WuWL>[[ӥŬd2p=>f.JnN}N7Y^6 2 Q\[.졹[]#MHbEUٝpK0g55Mu!DkCDY&W#?uajb$O r7S¤Ks&EnmFF̡J?v$uTQE!Q@Q@&R `[}:4ڼyF7(N{q^P$|4-m>ta n$u;QK`2q3ȩá ڌ-[@H _;x-nGmoj-Ou$ \ 7y8;WP\ij2~"t{eWpQTw[DžhaC;=sj]Xܼ] >hS`Ns_Y~"xFH;ȧ ,e,a]i4M"@U=7[ӵ+,Img6#W gr29:i_7*$Ô6 `v}( \SFJq@EQEQEQEQEQEQEQEW [o"Y7BM.1HSw?wUbp2L񌓵|Wܩ?y \e]C:'}?^ECrkfΉ_OPĎB ӋǤRĬ{.#}wwX.O kjwPޣo)Y]"/WrOdD|^41}%黨?7[m#Mk]>=ytMsľ.#!hMyXE=k꭮ei4ZA42R=)ЃЊމi -,d[e`Nt$ן!RjS:HY~v]ۚuϏ,ii.GŦA /$o뵿!M8infH,dT3կ4|cx]t;/M[|&\}86"l G4_K#[km$a$E۸q{c4i5H5̍R=ʼV?G}>m*/h%̺j (s@^^ItVծUmo I"dpsަLJokKW;yXG,y܌v34-!w<5C@έq& ^'_*Y;wK ^WBX"mbnQ^xJּ7Kt6gu%[i"M??#c  (,qmcIe1E8`8|mJlXnsQ֭m:]Jag orY*~`02:9yF=2ݢe'dPQdz.Tu]bWI].Z(Eme=m7vƿ6kqJd `YJ2)܄<{t8Iho[; iCe:r/[U@YH!=Ethz{zF/⳺i3W3wqɯD<1_ Zxv=wOK.!R3dt8ȫ5Хl5қȏٝ"7l'8ɪnb];io@)ocX\$zD(PJ:}]cN}[#_xEž6ܠBIs{?žgyfXmo[i~2!;XCZV>+Ӧ'u7EcUkqݎ0r0s.>)mi~;.(<>K} B$F$ 䢰ۜlipOIzzy.m9=*#+zu81u)˷S?jFY#QK=k{6;{i!,e9GLZj+27oe4IF1bFw qWR_Y~.5ؙl e`(FLw[_A]@[sJnynAp82OtZ]sIWJTTwf&u琙GjIY9{NJ7AFi5g4WdK}#P:袚EPEPEPEPEPEPEP\WRҺ*_BuOi(=Q@Rxg~)u@t۽ZyJG#/b{_Y)Zg5eBj#B@ݒr'q-m2/kzAw{kq}Z\J#`w߈!c}k,,Q0p+ (=y_L5ɭ.eoQr^7;d9cIӯeҥԱE Z7pI?ߒ6-DžodDn,-Vb(um?tjD661leܢWVl8M)vbӼ=_k`!RnV.׾ ZiuXxsUPV˂ 4u]-w9?ᖩixϋwA}5:u(P{sW< _r-12,v8sn; A_GCk[q*y{7 +q+)Erm-[QE((((+?_ͣ]ǣ BnA1hx+Bkڤ:.u$ oe79ý .Q鶩4-x(B9q^9].>.d# h΅vmzk{qʂE0v<u-''HѦҭ[b&ܬ0&\<RP^Fhx[LЧ&V7"**c c99Zow(IJ JfxICH6;B:|/ǯoKR[Gr]?ZԪVqC,Ek.4ӥZRQ2Eӗ ͌lV}zu&ׁt!|H ō4>\Q#Fc\',y5RZ QEQEb?{m3O+OZeͽޑOp"HIebv/Oiz?u;ldԦYn .C~.?k~j:r<iiPX.5g YOxk\OEyIȾ < ́ws~$hm3[r\Pyg>b?=ύ&2 sd )$m)RFGLrtCvSKRK}+Ē d#$\=*tGFD:zZ53pY-Iދ6gkQqI>.CF%PGZHET,ODg8Έ4]SϿy.Pb`;Br82s]^:ZgL:}Q\ wmY r@s z*ׅ4B{V;VvduRG4(95==6I[wm_(^G{j/ ӂ[ y8wqw@t9Ƭ "I{l`J 3CA<|dƾfc$qDt,̀x<.|+6_[ZxbmfB2T `xb;Gq͒Zy+Ggd2Lc/"KG޿((((((((+ _?ZWE\WRҀ7-??ZǴJ(C_?kV74u+|ZRVLU6-=g\iCmR]›:uj}݆n L$hTFr|J㠮+Bƹԭ -NLw7A*K߻/l/\ {%o]Z? 9֥jVX b5q;rd'cd,?k_t^%lfOWcnV_`aE0<ޜt?_XYK-.pdAqT+8ri=+^6EX\5;J>pn#M]XnɴS×gQT4*e?&dy?v>\Ԓx[Q&h:V1j2C-^~m봲!먦p:_/ZG֭Qͦ$(v.,Ÿ]H;r((((((umj JcXS Pm2}:۟GXp/pL6\&{2Hf3zW\Zu xLUд i^TUUS$ Ļ=ѧ6HX%wb&wfn?w:Y1Mp]iڵEi X Np\8s6.$2\jIo sۚi?r4 ]I=9>ؑ ]Y1گWΔ [ӈR.s;g/(3g;|_z^kr^~EA}^B+a㫝RGu}J]B;]FXbBF|;IKdoFƻ_Ѵƍl᷑bP RA 2;ZQ~?((((((A7-4s;GvDƻkk\&hI1 c3o!̵gCy7?2׮M!I桉EQ@Š(*_.-qdy_\zphMY(QEQEhu>j}WIglm"Jʹr=޺zIRAieżd!G\(KSmoٚ52Payw.Wnn(Q@Q@Q@Q@s!]KJ諝 _?ZP{GOEbZzK [_h1Sŧ$n05؃T7[E5u+$>9SV/[@nX_ڗ?-ڗ?-07h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKnTzn5el.m|y+O`1^+`̡Xqg].=bLVӅT Tvk`qK<[CQ4}SC-lo2(3"H!]U9lW-|,Ru;Ӽ˘^V#Y|dw gROPG3"ECc\%#bx4=;y 걛2`osXd8m?[m;ʸ8*$:IPxg<~YKiR%j۷;1ϵ?Jڧn[k &Ki]%[AL,J7lzZOD;6rF>gO}hKp>j)H0K ӚQyK ~]m}ͺtgjwiu M+EQ4jXu2I\3ӂ+Vڿu]:3dOe WXVO5xӅy@8Vf 7*NT+W0Evz׋uOh-oiݩL窠JeXx69T-u,gZ|wEN&8dMq>~Sz 5?#RI'V۩^^0um..ȶ}vvqJzkw<hqmgdHq{-78Z,vѴ,_4O8'yI(X,raBDBi)=\)˻[]v7`I{KMݖe`ai`2r}}[Ş*a}N6Hasǯ[^m|m/2]]Fʑ(Fr@䖨|/Eh#imnb{s介r届mmw~ϛٯko//>k*m{,H[NOּ s/dyB`z%mE b( ((u+Ti]s!]KJܴhQi( GMqiYԔR?,GΤ#Ώ,GJ(?,GΤ#Ώ,GJ(?,GΤ#Ώ,GJ(?,GΤ#Ώ,GJ(?,GΤ#Ώ,GJ(?,GΤ#Ώ,GJ(?,GΤ#Ώ,GJ(?,GΤ#Ώ(I?:T2N;ty?S=͒ceE;Y>2 &k 2mym,- @gѢ\a{_ᙵ˝:Nfu;'qI(VV{XxKo XЫM|ݴk{~x[Ѭct4/ufv%pgI=ǽnkzuYigKwS]]U #g$x_ǿ^}i&6 x#oyP0O֓O (gg__ snx\sXC7F9n5 t+QK "fA\l=M?U^ kkp1E61={/_6atFӞ큖)1q㪊7M{X{F5ETPH9;B1^u8%;[X,22Fj-[mTחN-D!y$*XsR~yvS6uc{J!pS6Gkl^-[IfK2 *P1YiM ذqǦ#x^O1EzqU>.F]CWw=Ă1̂CrgvzyZj):knoj G-C,̌6l7~l.wWx޳qXkZ69ه#wZqZ\ZvMn)R{s@E+C;M֦mn- :8]iVF)oxDw}A{sm{*3URT`&W^|S8I^:yϳjKnO#5n]jCwLS|R2ozassڶot)Ut.<H % r lxVVOB }.yf?iwGcn2 Nם|JKoŞ!Oќ}iPC;Fc{=}hϕ&zP3RQ@˜a%1P+)EL9R>(3O0I|04# EqE*V(@ *i3i4NGZgT;һ*eͩV6ݓsN.s}s;\L3=+qJ*Ȋ%Vnr݅QL((u+Ti]s!]KJܴhQi( j(Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ x}iFk[/ wio5q_,8,Hs׿jE\ ]RMR+x5i,r[ځ9eu8s{7M-3:w7ZbH.ᵜFQ 8 S_2_:5vS '*T$bXNh'i/5x 9tZψmZѴ^JݧAp\Ӝk{]Z\ sAAu[jjvHfhٜ1vb-|-mKR{"iF#vр+?-nn_L:%>p?C YLI1G^)_2>!hBm*Pͱl#4/.h36o/K- ž2+OiZ~.v#cH`@W9kz>"_[&u}+P8$!vy 7\/om+OMLm"OJ鼓f~ϵO2m.ux ֵBk=A5(廝mL  r?imdKۛh%'Uϖ2 o|=umNkMs_1-F4yQ|N,>"rS1i&k;Pɝ_`9߅x:wwM=]Y@\JE}mM{#P)&~ֿh"2J v?(\$MqLlxcӊk_ -0x+k#Umyk3|=5+xOJ:\D|/vp ApV(gx\[]Im0AXb ~$'Qh"zj6Ķj)lNA&mqwo{|>j6%G.w $4T~'m晩giw[Fo# %X;NkѼG^i1^I =b>7 YxN}>6_H"+{tݦtS"eG ob ovdž.<5ajw*4*y|p,ܒON&c&n֩/Xك2Hg+ox0`Qf2Z@q bg +㿇tzuڜ&]47 S!v&к3_S'! Bm -3jB}VR~ۧvWď}eF猎+g; ]nahL2W2r_n3!',=o=?R'uV]F_LI/w4I+FAFtG `rAWO^#'gkwvp}qlC+ w潺{QHaEPEPEPEP\oDU0lÏwmvUbxسM/_񯊼G}is44jikk4Z9ڿښZ?45W}hOc_M?ښZ>y֎`?ֿƏM?|UZ/_񯊼G}h jjikh/^i>s44jikk4Z9ڿښZ?45W}hOc_M?ښZ>y֎`?ֿƏM?|UZ+[htxṁ-T(ARG8Qұr>]\%ͣ+At=To: 짖S_&DTJ:\eOB=w8ϥ.`F)0YN;=U6"4Җ1fCAv0+4Z9LJ-dcҴy{lȊ͌dT.awe)R"mdgǚ}hO>`;7Kq~Py9 '4>ׄz1¿&8dHOi>nk?C vXpzLWC_񯊼G}i44jikk4Z\c_M?ښZ>y֎`?ֿƏM?|UZ/IdNd",+5ך}hOcX<JknK3/-p,|y_R+4Z9ڿښZ?45W}jKh%e S hjikh/r4bᓮ#ifʷ擮װ,}_SO__\աkx QU;ª}.`Jjjikj[{kE2#_Ojy 2[lp #߅EA3KrX{QJ5.tOIz[EQEQEQEQEQEQEQEQEW:Կ贮]SEnZǴJ(hPN(*z7e<<ʧލṣ̪{@<<ʧލṣ̪{@<<ʧލṣ̪{@<<ʧލṣ̪{@<<ʧލṣ̪{@<<ʧލṣ̪{@<<ʧލezlmC 0` q]VE'c#m(7"\4k ؓ ,~Xk^b[D,$ecmaخ$W&B#' k: HuGNЦɧ%ŎˇGq".A*+ZW67mЅ)G4UQBs.27cc"#Qm{mq5:T;Ng'pF9Jv>"F:Zt30&d`{|WS[Ypkl]cy]Nt-Ë?8\yCl=wvך5Ҵ!Zs$l62@'=>m7$"5l#vrN Ϡ7zfXx6mB|F$M8cײ641[If@ppi6u-VNkM巖vI6lrxyW+0Z?[<צx[jw^Ikv%2+8=/(g2K܌l``<-Dk<= 76O%BJ Jkm;MLJ/Uhxw_5CO{ϴY,Eskźk ZyBFvIҽG q%$ѐlX.vdz#KE=h'#KGF@;_dzё\OF@;_/v#ָ/v_:.mFGq?_:?4(Bv#3kreWO^FφϩS)s4c|qm gO`kMJ߈O]}2u3*yc||v;[luy} ,[5*ޤQc*I>Y9{^'G{1[u`UfV߉Ucnx;3][T$iK EaN')i/ݓ+Jt}QHȜPv*7p:\姅;- "kYfڒe2;v|Uח6s.k5=OlQr9{ZV?Yo˛K)BI/n*:. ^]-Ã}s>skzu֤y؈L6JW3+E|+i^ VZ^j]}]ȎG2q۶r5/~'x~_njŢ)"תΣHo[oJws"?V5OkY@f`RtA2;Y5&V1ƫjw:Ű(С JSנ+4}ONefE Fk)}/QÂYRFc>&]ZB;-uC<'t׺<6ۓ zoH(>Y$Nj=W~'bԯ/+K 2;HbaǯQDW-^N[=景x5HuInm`w 2H>doҺ(++QEQEQEQEQEQEQE>-F&ֿ4?MݨY1gV5E̚Uۈ̢ 7F5|4錦l%Ln09<xc} \[A I#]̓pxoxo.{ٵƋqݢtq@pQ|=xžM*^[4߹A$o88N՟?- V]M4Бޗ}щD \(xrOz̚mv~9 A#j{lNG~Q&i:no4ҧo/keN{ OzCAfڅg ,1cc^V$ӥ.4ai- Ւh"7_bsI oayR8t~m캝֡ QZcr@rG9 :+lWR4jOH`qfkfun^E4(łrp3p:+P^uٖ9;ZH%Ny GlZ5ߊW6Iq\v]5ɂ$qm;#$zPQ\t'3iMn a2 mAOCӿc@WsMinشۭBH |gk~)uSxEOCS[ 36#D",egFx{^Ƿh+W{JX]+Mut9Y00sֺ`QEQEQESU@2lmVa*Sբ,d [&<ǿeКc OA={/b~QE!Q@Q@6YQH:Y~΀uFW1}GX$p\$6q~^1眞 .,*V _9Hx˩XH_<64?\xMkq= [7 %D,7`H͇"dU:x7M,)q8?5$G|n嶀>pA$MF)B h9_֣uWFm{- Ku1B|&R݉wm7_fD8&[3E.8ι# g P7]Z_֬ OiWIkvIe ##e-Ӫʙ~`) F+۸IHqu^nH$Mp !uf$]G{g۲747($uymuC:OEҊ`bZzK xY]pi{KjQrYFzC_+!]FmU4t{bH"GuG%FvMi*񆥪jv")J-%wGP,:YWDGӿ5;kgi T|zn_Vwmo )j;Kd!G6!qID֮t=NFI̷4UbY H c#Q&-IVOv$Do1ڸUHPJ[;\G=BcXaS >@ tVb]68k2 a+@)KH##T-񖵦/Ul9uo{fp18ey -NJ>mG:Llza(v* ªB뷶qfuWuI}YI<i 1מ7iV^LV<8HP/̣g,8j~Oҵ Q,nplm4ZZFh%Y,0KzvTFg *{ ڂ<P4Z:jQڢ٤k no^t@M徍q]^Kx3챙e.07 p2}+ƕZ6K]G_kx-bQ-ݘIv+g+s[-4eKsؔD$۶K}0$w N]jh\Au+  U dd]_yK]rkI:[%FOcNl5KǺym1u{Ѳùkom:èMr * 2pqEH~^GRQBY>yj4ҵ]Z[&v {b}x0epIrGg> }_LujZVְ)xdɿvb6hZo[i:TvF&D, `3dSJ]B_ x [Xk%7QyO@( wztOdi~Ž:*3|Ca3gZ|TnOΫ5|WFXv4霮`V#g[^1}MK5iP]-e*jlRgr|7ytFDM>Qc0e$*ϔH>w/ťGy 7P1mlsT|Oqi+}B. !kW3}rWC2cNJ;GR *j KGd򘋿&G,PH >᎛Xmt?W6- -zd֝ԯ4 א\Ki%mJʮg߉_YK.`{|Y`rzeN1y]P~a<"5}4٧a]*pZ쨢 ( ( ( =?<㍦-_ؤǦӜBWxoA" ާ{m=¢!nlcM:8 HҢ) ((*qa1\ժqm,%R\xJ4x/%y:lF;-zikjm( [v:WNlO?+ciKY$H|0hH ʨ8'9c.i=}tD[$dr Ȯ+\ek^~٦cKB!Q+V_G]?K9 i"YfKx'Ҭj77MWQ[̠2eRe?r:ݕڕZuJ̒yk3>ͼ6 *BRVW(\Z\yb2HFf[9G/xT7ʵ Kj1~ L@NO9o_5{tOPZyYY"tEN(i{wjV.bX&Җ,N\J~v.wm;Im8kKVƱxK]ѵri5Pdb9?xu'UtF= ku˧4dtګPI\wSJyq^ӵ&$[shoMPk1k:bsfUPr+Ҽ iSYE{2[>c7aIktY [5 EeěI# =[ivdewbڃۤ(3 2d tہ8$z6V$ 'ď çh`5!yclӞ]fkX^0'@ʆqҴQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@zbu=U!=ommp~c5G: iYPČ=[np=Hu,5 ?=o hys $ԯ4.qK\l ?0 d8xR+vS%w1"V6gQ<;z槧iuox{FW*H`ݹUm'P}R/K{KE8ۆGn@O^$FRH(kwGf_ݙG$vWox5D+dy fC DZ +?zmQΞ%m:/4t.Օqキ=PUns?..lIps/¼~?RWnEI"y7gқ]\|ƏniD ytcmFR6 -;5ĺbUզt, ½y.@##޼<{Cb=VY C _u@/v"򆴳 _KAfc[i+2o*FF㞧(pѴ!Ԯ,lcPk 񞆸M3:֟zGgj ;Je/y|Ðg5QF\ B{"=_ʥ}濕gyR@}濕gyR@}濕gyR@}濕gyR@}濕gyR@}濕gyR@}濕gyR@}濕gyR@}濕gyR@ DT@j)P=k!?/RxgBZu;/QU]@ 9z3/#ث:\_|_SNmtۘm.mcYĐ:)%@AKx]_Ru/m"ӭ5V;%sN׿W~_j6Qeg'mrDafA##[]S"{ 0f=ŀt(POb\eX&^EvH݂\abˏNIiZ \bMRni2Kʰ (_]m6ki.%xcTi9yY*k߈*=."'71:ǜHJ{ִ5_jznѵ1Z[^ZF9Pg-F=TKL]ޙ-Gb(O, _ @5j:tG9X<~@2T#4-UFR#.g[Z0&iVF\?NjΗ%sAt7Vɸh:)ܸ^FA 0@;z( ( ( ( ( ( ( (2|?ͥHϨ}K>B1(=*hRxBLSVnk`_V4+Jښ`ݼ6N%D#e M7RiGq"GfA`@E'N]Bך4$ E TF;g[NVڅ6;td,$9{im5-jhl Œ[` meHi|u5KMCCODE#*y.:sCwM%V=gZ <9 HV  9bʸM^-[|B% ;q`|Xnj2$VvVFs#!K?u}Yu}*pX˪Zm-ʫ@Q*F\g1M{;%cdq2wQew68Nbz-[&:d Guuv5W}[x qYo>X\Qq FYᛁB|]J?ׯEr}/LƍZ6mqcrgWF 6טxö:˨jĺKF׶.Bhs֕EyۭB;_Jקe"\2RєU2 0k{Vmϓ7zi.m(KJyĚ5K; &y;DEYmPAfe$pOQ݊W|4F]xidfI@@*Fri?5_Iesu6r\ŧHcSi} Sz^vEyq}kXM[Z֕m] W^Cۤ)C) *km&qkEous(Q:$A q{q˭+\_ɧk\i08yX37+?ܜdglL4񽖵}kȖ"#m$Hȭ.Uq2xŶ~3x{q [晌{UbF8% c&?xfy%Ʈ5b~d_k呁$>uۏDVVO[\Y4ys7Au* 4zUZiF)!]$2FYN1 ZΡ;CYnD7GI;s1OlVGK-?UtKJ+?][i 2PǴps5/ܟ @zBI$\Yv zc j_cgk%.gg*w069ɮ{>-fwMƺpKwptUF\ TSڴu/];O 65[|eL;F=Og |uoI}>!F 1gS3GeoKCjv6^jWnfitfi_{aC'^AD[*$`_\y5L(NO__u V-[_$6ЫE˪c!xN03kmxQemtm޻VY! ^@((((((((':!Ԍ7wn{CLz^f&}u{mm++Hm$H?);Ӽ3s\WzG},v_gl19xCzޛeOmrm*rAqSfK }$%[MpkYS*lHZ]}qtd$줜Uk i>4_]K+c ۉa2C9|G\nMx_Ru*fi)8M_j~'_Zຶ(ܡJhIsmR5mdxPioH۝@$UzW?5_6DխXkx<¨e*{’ ^%\@-&PaP?W7s\Oѭƹ}Z^\#?@YI{A޵ 52bl VLs^5ۍbʾ_̒\,\; u0|2NQ@zφ|xPl7:ۋH4 ^NI55׃tk `%NywޒyfzWWzV7od7c8wsFT\}C/lcpVwd4˧.gdiUeWsb:Es[:bS4Ӝ8Y8NIm~! >(V;{gi$yfhcsW#>UvN1b l7r"#qoqu%Ƨ .S7gY$Vw~RsArڷuSK5yK[2'a'/*nb֒z_]QL̐6($zqOĝŠ(aEPEPEPEPTQ8[o=NW*eڬ?hQEQEQEQEQEQEQEQEQEQEsֿRH,4Ok-3XXn'h Pre69]\7%!;tF{{o-#ޙۙ|v#Kc4-:Z5eR[޴3d&0- g&xBk _.̈́[rpZ8иn5zjZpY/}TƮF0[$ j/uH-~xjh q)c;9U.i'~5Yl5C]fi02Fpx\Wzw:,b|lC!% 8RkMs0y"սFdٰFrA{ծ+Nt{InMI%2LAEFCt$t<04ayAG.ˡ^=kɇW:$ /@ h4dCych)ɮHҴoxŗ1tdY+XL|nc#)u%. }1 <ަ+Q)}faTUSc14f9u9Mve5BbY`c)x]hoOWM;5c,i,:#-Kww6Q1ٹP~lFf1O$i٦JtX{nؠ@^Z$];2Mkḑؓ =z{UO^g]Q~.&5#Fy9ݖ{NJYtYfx+<?>RAIΞ ɭr]BU 찦÷C{9Kv x動p\KI^Sucmk(ҵ?=ž=Ȗ/%go ́ʌN0@AYj5翴K+#:WU (PލoKhšL-C3Il}ḟ0ǵr~#=j[R_~g]..BMq$2]34 ^}M2=%~y(v˳͑zא77~X4X^MҮ+H_hY>pz͊!յ߄ɫZ}}R8&&e1Bp%w _.Ia4q0K녒y$ɜx@M[` g Wotit?XciK--c{ 4Mg? KJ#*ڬmQ$B_v=p:QO4MY}B1+ާ*t_UMI'bQGEuƮBV^if!Vm>D[<$[ "*F7S[5Ӵ)`mY;C^*C4|8$qJIO>f3 Õ>wW (Q@Q@Q@Q@Sբ,d [&U-]oǜq |7O+Zt>?hQEQEQEQEQEQEQEQEQEQEsֿR촶w.--/kiaIW͕K9K_Y)Z j0eZ[MWXD)ʒzk]NN< ifmȒ<6Y S2rfxrš4K;ߙ+o @}+ڞ%7XY_hZeŝ~z`t+~ڋ5Q7p O9Ӟ38ɫōnUL63Mž[WEӣۨ}{kYHK?zJ8A?DWZ)4%nV񌧯tOm׷pGu{\ʨM|duON𾃦si\2En$^~Vr9<{Զ7Ěwuzɞ;ƒI9dқwwcc/[ָvscz'9vYC:B `g>sAubZӭo㉷D+z5Es/SMiYFR c8j/hXYŦZ%@I 'j(žu}7m՛riF>}_R̐) g(rjKe?&K{I~Ux+dneCt ] xgCeMHEmJ:Jyil}ݍH`NNW]9OKM>ٓG=X.0 ћH:Mc6GOޝf<;ko k]BBPd aIxoEo-}TɴpHy((((((j^zsTvugpA^,ך3 YqYKҒ@{_v6rPzpi($gK ԟ0O!2s-ƯڧIg4Ze,Fܡ`A+k=Xϲm ^yI H=>AMO<\99Y?1E ꗙ:Ԟ]4 [d*H3x# F}M#R\AJT6v;[5  Mg[ kxsI,nC({LJtKMybZ|:rxZͱ_ھm.}gYz;=j~qaʞA# lSz1-QV:;$lg~Ѭk=#O,rElɞ qӢcLMs*}2X̨f%;}rxJEEkm"+Xb!#@~(((((((iekg}< Q6:y-M>4;\c ++uSէI䟳H62~MjW,nYV+ zn ގgK KAKop!fe1p{0 Vq^SE&etWG]%'mu=1*IVZ?]۫RKXк+Hsi1_[]OV>,𞓥ܝ_Jg07O5[H0# SUdծH ΄,}71d ba>\|xk]콖|( ~ _A}/:*(Q@Q@Q@Q@-VL6QʰC_2F苞}*qoBVKSMecQn{~&;SgvWo=DfDg8^%,[omַ[Kk7iK[SE,#0Y9FŞ"_ht I.i&x]YW*cn0wdAO gѻ]^wcШzĖU<آgM?! w\Sm|=35 ݮr'?֩'feO:u#+Σ_?tYTȑrVHh .x֍dk7eUCgg9$;|Wz%Zx^u i6v߹s"+!_ ʝwP/ivW+v=E $~TI[{mi 2k.}toZ=cHEwm>dEr AȨ5O:υuIO&o6HmӍsNzҕ^_~=( =E-r MUl5[3\oYӌb/&rVJ(fQEOVc&;SM\;rܬL8կWCᾃA\+A]?GSEP0((((((((((rexAVMQ#P@*\Gu995bC_OzOxSQ֬-ƻ4fG ҄wɹ8*@8rR@{^gly[Ej.5KNX6@$sޙoC->{-mD72m>Fa:3ZeBvXjIj oKhݑd/% ub9>| YZjVԗ IK,cݞA#u}rI7Mq$EKrv%cW=<ĝZw`]#)nz yy_M!ӡ!m6Iepp x]|Τֳêr }@C:Rf o^GRpzeϭiHu`3kW[Ӟ?aJοkyw"cHX4bK5FUn7= 9_c12OyiydQi'葼L+r$1pZߥ#A;'fG߾p3`RKd z+r)%lo\qQu[*0FF82q1km67hi1n _ڵooiX)n~hZkRމ]l%_KKi6[\Xɿɻ GjOZTXr+m-V῁/]VJꖍΤ6U2(}7$TxQuONtX5im&B|V!GP){ AG<2[=ѭ淸hch7N(C ( ( ( ( ( d":<PmfAGNߞ#9kU,%-axá# ךM\:.o}%֞&*s@WX4$#[+!u>jj$y`sN(45 ᔟUh,~-mۀHCm~\z\IsUT斸,? <8}vIZjŠ(0((((ʧ[ՠ'*k&."1]Z3Y B2 >w}ioraq$^tjtefXj+8.PrOJTRSTj-,Ӷw[i$ur(e؀zֺ  P<]\z^lY0mW4?aKſX_1IRz*>-B1iDyf{o*Ca}wqHv t ?_JkV EAtKPAۑ@#VRtm6Q≢?hd:U? z׋,{- OddFr u5,]N.uZo}}:׻& XͨG1=XD(4']C+ze[0ā~p+w M^7ye݃4 ϯְ|W^^`7el99'X:RQRU*+7w_~a CÚ>w18$!3@O>ak^'5,-c w:ninX[[;0ѪhfPV%>0~5~Wsn'^"X~!V69e嘢(f&Yuu Oxd, ۹TbCe⣹tE]뀬K?7qӧrz k6ͬI(ojvzt֫0KCaUc2: r85LWCV(_ ]_=GEa^wzGMyZJviwZrmR| uBWZx f,,uwO[H X6\.$PȻ:O6|85%-%)oȊ3~mVR*[i6&|6o0aH#6#[U4 i5/MhZ$  '*[V\Zd&CZyQ<ˌg M'Wεn-.bh^7y,0 |˟8Wu_ rq37:kj0Xh3)t_2)g_+.3lI'V6/Cɷi+H\)xFItXz<_&I%!|˗B\r\UtH̚Cc?/:.'jumRSl) iP^Y"HEʞrk׃4{zq_vdR,ݼy<Џx^@ (Š(((((((( ]fɐ?*ltay}>uiϨ̘Ŵ ,@uk&FҷFS6pJ׮.t 5 KhH珼ɤm*7^a㼏X܂3qUlt@S&+ɡ~_ˑJɐ>I"\ZlLomHCT=\Au+1ss'fI`lnzP@ m_B-4O⸂X8.VA9==kԴ駸խ.InήccK) Q,AY~i7zvckusAn󟙀kJC+YZ։kam V(P".I'q$M-̥֗$de<jz(,YBYHP2pϵ_((((((((((((*OWj?Ns@+A]?z?koP#(QEQEQEQEQEQEQEQEQEQEZzK?xS݌vz$6b/YaIQ]딿-r5_.g{5C`87B_`{intZGMZ{+{{9 ![lUCpI3~,u飞ThGp+Dfp$*0Ga>t?xIꚸ[v0q ¨ĨݒFZ.i|ۭ䕣cqР#`=RC(^Z|r&OTYghG[j~ WKῈ045QE%-)I A\cKmuuyʗv^N@ ~^|/hmTNԮҬt JǔRO;ӿo :;=Bƙ#Ei)Dq*pR qKǚNodVTM6wxK {֛iluK{{7ȒL,n*T1Ԯo1yVv2|$+89J_6CVke~ՔPz$bMuzW.~ j7tmJg<z{/'~L}[,}I]euVӯ$_/IrPYƝbV."K9cm{ˑ+ؚ?D!5S3ȳr呹|߽tGGY4 K S&۵[n;\& Uctj;|kܮY.32`r1X(握\cu˨IoIki7±SeQ=3]~m2LbC1 I8>t wV]eawiq -E Dq*N0ik/fE>}GЄx=>]WOhxkAj֓;|f2c$ɓ c?tsRbju\lry,)#E8p~稨GkZ߇6-1 >xf"o8Gԧ Z ߧcPad-*ZE+P'Qqt+oҼ7y{\j-(ׄYUT2rGs48Cq`\)I.C6 {?w%aYSc2:F c'7N+۴{KUg-7I#ܣx=>\i|'-ͽʹz2ő"NF w=[kk OGdJ>ԒF㍃1f?i_/&[3: g>qp*D1j~Vj+i:ޣ4mU,2 78_e'ZrOmo {*,k$az8ۖ̋ އyhFj2oXq#y1Vۂ }-?OC]'Ox.n XQXw+y.簺HJC::Wb{UksIӵ[il<֎GF/q=jV.xBK>,A Mbщ13 S+}rV}/SyϞdHH̊b63޲tk6#lGyɶgɆa(P>Or>1Ҽ]1 ȯ_ˡqH*wc fS|I8Hg/fFi9ii_袊C ( ( ( ( ( zͼZuhօ2GY7+yk=pjk_[i[,g"K,A)qJ>F-)})kw pQ)8 c|/F`Hՠ[DÖRCdU$Yz?5mRY4mV5+{9#8X|lu \>~O5K*[[Z!pmc9uQEQEQEQEQEQEQEQEQEQEQEQEQEOUGc.W;<ʧ-`U8կWCᾃA\+A]?GSEP0((((((((((re"Ӿ$ccc%F,'‡@@Pאkm=g\kG?H}@[j?p P<^3agRX4l'3o3JX.SrelBQґX}" %x<)jYI]%ƣmk$֡]vM5;H bv 8<m#OLRmN0C\>O~Jëo]C?i,p6`(g<C}׊SzPS,jto["2Fnlvױ-%^i ΒMw l%T7^ !յ?ŲZ\_U /rG-Ճ\V)vn/ҖtOkuk&qz0۲5,BhHx_Nֿ5}B;-F¶JspyIeghJPඞp`o|{IiAk7K`4%QZr6r KE7o_/#Pյ NQRHc ȭܰg7BB﫚C5MFGLcxĞ- (Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@SՄfO0miSUE{w60?hQEQEQEQEQEQEQEQEQEQEsֿR[k:|Hɵ7 #H+cvm=g\k6\6.RmUˠfN͠:T3{4N]t5ƣȡHX**O^z|Jֵ{䮋{n-er 8>m~?t5h@]h;9R ti[g/٭ H BX`X#<վ%=c&O n5[Aii<$Ψq"+;Ԑu䪞gIq##u MN-VQ{y%X`P*n$&M@tѭċJYg*W Lѡڎwa pE <3oO*5eSm斚Ejr,O$'bv|x>Wutң_٦h<2ldlŴ~\) 9in4]G*վq,XarēQQk~gCms4egGpjRi&V]; ɼnݠʹ_NwoNMi< ga?3~闚Ԛ׺k/nlT\O$qv~T$+%FEJ/#~F6f]LM*}rEI̹ɵʑ-:-.qͭ -vZ몺_QEQEQEQEQEQEQEQEQEduIH .lrxSRHPNVVt<0j=FA/~v_rwcqFcrzV5t-cgJ nVIꆷ2&[܍s9n8S}+,[hd|p |ˆ#kdcba-k5ONV(X) cSzwBJ > jNuʛMmuPdGrX c5CZo /5d;mŭEuW,UٮQ4SW"+BHgL+ ӚxKé:ii.Y^cs@,df,r %tFh$hm ,|Br=+}Wź>[>y$R$!&9oɄ_9n|]XZ\ tea.8{x^F2; l?ӥ㙷( &;tnJѷ⥽|]4*dxam#OSNI4˯v[Ŕy;K$Cǻc<ARm~mzզ:q.5aXL/7o߃zsEhuSmĀ l`9dύ.ami[_I4v2 ~@zuC[5?¯5 RPE(nx\8_MյMF/Sz\wa#X]D%,7Ͱa/ Kk+!u6ͳp[( 2)SSi&hⵖY^PD櫇;󚧻%-~_j^/kvewz5Vvmiop\5Cw`AERQEQEQEQEQEQEQEQEQEQEQEQEQEQEU-\?؟COZTvu} |7O+Zt>?hQEQEQEQEQEQEQEQEQEQEsֿR~񖟨ii[MF7̗ct؁eA,~﩯dre6EӤ}@e`;4[y ߇拤[Y\d2D$1r@:4_Mw.AbeLHrO?(+z~z4QE0 ( ( ( ( ( ( (1;yuxڟۑ '-.zH;qSxk;{ɬ[ͅ#vHuh6 Z{gg9j/iF`6K")tF~vwgjzuҮukm HDh%v ]AQ1# _AI}`]3gGlmP|#Ì+|#[fcZ]-W~RW?^j烼={ɦؽ]42&1%I</$}=lu|VNkjB2͔pT.~gbxηkm.UEIdErg30qUs=BmS qX$籶Դ*Y-a[{tWzF *R]?5]{_&uc][Z}{v,rJQP&Bdwd׆&dڨ` 㼶᠎FLj:uDQE ( ( ( ( ( ( ( ( ( ( ( ( ( HcU=VA\V_y?koW= |7O(E ((((((((((=g\km?>b޽,WdfG?6\mPreu_NË=`hC/x )-yuOTs\kݍR>߱2 c`y派b'kkkϪZHpAP7u5Vi{=孅7Y$MOTo4{ };HQ'+fE  b3xb<-Zힵ~m5_zΈ^"Is1R,BA}(R %]| ^YxVMӴg_M ]Ymv (ћ,k-6 >iEMhC@$@~ntyŞukv'k&Wib"UCg qW<\u7ᴎKĺՍ<7ߔ3]7tc_Bc3}1B<v>`֦ZCQmF'&\aGFוEI<3˨\ݽ.-|S6=ժqcg=A3MeRȘb NnwKM(0(((((((((ӶQ^* X>ȼb2Ws}3kW!`$r' p{eAt:}YVo&%O2FrO5}Xiˮ\X?R6ӻI+A\i ok&9M A0H5uu9k?\E7mf#z=kw"נ42ls)VZ.744ObX|?ww=WjA5uOxWOnf0}1G*_A^GMέ.%eWGyV 1x>Ě%ݬw[$$s0-jo_hZ6,[Y+8qǫť=CQ#2[AӾ55A}u9y$2ܚ.5 {{{~ʺick{uirbpÑֵYeaA"Ƚ.a%_;*(qc)xET]6m_OQbmZns@QYKKd5CڙXS9 Ҁ4(z&&iڃơmnRR'i8ֵh((((((((z"I X/W*)<ÏvcVA]s_}tMQ@Š((((((((((zC_'NѤFѵ׻k Ap!9`L׹$[6r\KI7;xƺ,\y*q^W~bSmSF5>W/O?m%67DNK\8E߆a)n-&kɮ4G$AО}6qj7v!Gg(Wq &61 ܞ;_=ݭHe4`ᔶA*dۧ^-^X^lp1t}vGZah4_%?ڵbIđ*vl,co]RIOC  y7+YtM_Tb$~XH p6gV}DzBDqѲD7p>@Aa$Ѵۉti`./ ͻ2?p^E]0s^I 028$P8I6"jۙ<ffwֆ/Ư5M_kc4g7moI Ȫ6@s[!nqco<;=8:;RJ1w#{$(k8vndhKenǭIo]2kh~n$#nW>ϵUVE`u$l^o]O89;*,\3GBHf&dı~fhdzU;/[,Ѭ e$xՔd@VmƵeY=BMG,(ZHhY?6@TVh֬ψ7cjHx#/ oEdԼE𺘢T6{N@&fđhGlih<$Ju|;{Y<=nB׷{K ad6;bFNNFj+˭2?d-.، 8a1؊-3Z>hntffl85$H&5=l~5K=SyG5o&4kvrU ?cJ>CׯZ唞+|2k_D-qa\q+n<[Y錓3\ !F~v2pE܊sr2=+;Q|3{aVSDA1:Mj  v?fpZ6_: Ώw֛wm%+$QSX"F$F 'MuSM)FxWP<|#qrZ&v(4y?L6?ZKmnmIcIhިmOčb["AlpLc'# r7W{L񆬺ms\xz[nʪL\X59d*G1u3pc2FhY(j-]vzn,|89<0xlq6S-hҖ}n'UIcISw%+tVmƵeY=DsG,(ZHCv=Qfp!TVo՗:-Ώwֈu)"gi⌎G1iQYݔ8Gƈ59deicyݙ1c ?塚=J$,q6S-hY(R̟[RXh>aKqYBRhQ Ңe֬xTY?6G՗>-Ώwր4u)"g3E'l~ݔ۷8Gƀ459deicy.fn fHG3GB@OUE{w60Gx٬1$lZ gΩfO,9lG~  F @UQ@Š((((((((((+nYy0Q܁Ҽc#e<~w(O>MG:?{/Lu?hSƽc:_h=WZ.<O?1o|X=WZ>çU֋'SƏLu?k>çU֏ﵢc1o|Oﵣ:_hXLu?hSƽc:_h=WZ.<O?1o|X=WZ>çU֋'SƏLu?k>çU֏ﵢc1o|Oﵣ:_hXLu?hSƽc:_h=WZ.<O?1o|X=WZ>çU֋'SƏLu?k>çU֏ﵢc1o|Oﵣ:_hXLu?hSƽc:_h=WZ.<O?1o|X=WZ>çU֋'SƏLu?k>çU֏ﵢc1o|Oﵣ:_hXLu?hSƽc:_h=WZ.<O?1o|X=WZ>çU֋'SƏLu?k>çU֏ﵢc1o|Oﵣ:_hXLu?hSƽc:_h=WZ.<O?1o|X=WZ>çU֋'SƏLu?k>çU֏ﵢc1o|Oﵣ:_hXLu?hSƽc:_h=WZ.<O?1o|X=WZ>çU֋'SƏLu?k>çU֏ﵢc1o|Oﵣ:_hX}ڜ6EF QH+^VI;?EMK??@4V,XqdZd >c=Ak[?Hm,H^? :-Gu}~? -` y d?4 袊(((((((((( AE0=(`z (Q( AF0=(`z (Q( AF0=(`z (Q( AF0=(`z (Q( AF0=(`z (Q( AF0=(`z (Q( AF0=(`z (Q( AF0=(`z (UwXXpv3V,ž}}+%.嶂I&B9$H=$/}I?bi,}J;-KHfVh(Q8Ki#/,K,I\@ =$/}I?VBwD !O$ 3B¹o/-U"שпo;G?/mhNj/i^oK.6W[CoI^- g%\GjvD+kKX$̧{*8>8'שпo;G?/mh=.K]Q {YtyF]T;Wt;}nB@KoK]. [Ag T@IX訢c ( ( ( ( ( ( ( ( ( ()g>?PڏQ@|j?QE}ϏEg>?PڏQ@|j?QE}ϏEg>?PڏQ@|j?QE}ϏEg>?PڏQ@|j?QE}ϏEg>?PڏQ@|j?QE}ϏEg>?PڏQ@|j?QE}ϏEg>?PڏQ@|j?QE}ϏEg>?PڏQ@|j?QE}ϏEg>?PڏQ@|jTDuevʜ(Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@fotoxx-20.08/images/vignette.jpg000066400000000000000000000731261362435004500166610ustar00rootroot00000000000000JFIFExifMM*V^(if%0231#0100Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C     C   K" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&^7⨵ ٥ӵ;H1N;2O'aOn|m2WzzTl}'?DV?ڽW+ >}Xj^X DQ Wjl}'?DV?ڽWc?}'?±A+Y pAPz@l}'?£]R7W@ Ys#{'_/=j K|@ٗmY~ᕵ%uxkkkh-a T@s)$~Jqoĝ5 >}^ំCax=J[m縍mm;u->:?q?ìI VgpҎ]@GH@?m#"{q躍4FQnu7hvS#D9Hdq\oûj>ɦjShv5V0W/)VKiP8DQ 5F[MhZ.e, ,@D\`H5hEyVwRۥݻuG*H՝\kUuV-}'?DV?ڽWc?}'?±Gڽ]srֶyr"" =$SL"4.0϶s]?5H4/ ErK=䥦0&gVƱ[xI!-ΛBpyܺn@,jFIUM:\^hvϟ$*TS MNK_8Uҝ\>T"ۀ9@;W\MiN#կab-J*īlNxz, ڀȞcT%Mqe[eP#*,[Wl_MH "wΫM2њV`JT1>?1xZ#wk"?,Uyݒ7/Kٚ caו_>š-dk($wn5$Vv}jp]B! ρ:o"81dϞR˱sM+lm~$x[]Vb+ɞ?h_Dggx_cⷌ5{޹Ts_}Gh|K=Ahc|L D|Tly[=f}pasKCR~%_rCQ#~0ywzTښ9f>*Y0C|Ta9^v)Tt%]G?7@3 />yWT{ޟ{%~+M7/W&tD}N'TC|V?S|c9^yQSJN"SsE;??/ O'?c J4;$*~R _)1sgoaA,8i04@S㔇)15ue?~*NWY1gN*7&gp Wow(?G_)1>HQ*phS㔟_r&دk.+\S?>*gJ_?}u+9݂x9hϊcj_}u+b5ơ]_h6uw3K,)fgbI$5Z{d6_.X蕯+0#e0rnM6~Nr#o Oj?U%qi>9'}/?5x-?/?5x-?asZ>}kwKbwKb0Oi>; ^%O1G; ^%O1E\'֏Zee.|Go7ĶdZ..>.CK;ž`(C':^xrQֵ]C]]K*Ȅ7.=+wKbwKbM;|3. \j~ `Mw!0b 63^}o'Ėi56ФqeePN$v_oü_ ü_ *-l6!}b_i5MC56n#ʍA9S>?EI`\FPW_/?5x-?/?5x-?χ; ^%O1G; ^%O1N.|GO}cCW2QcCW2Qf>qcsk?wKbwKb^]׍~GgXIa'e-Փ^> u{Qev5~d7y'?m- iiHطc'G KxwKRinR+u}d(f'  4F*VKcɯZ"Օ'v\?ugƯ{(|W_^ WƍjGrA Ҿ? >J[ML4EN)7dKqS!Wު=)bOZ`=t4Bkq2#ӥ*&G [CH#9$jJaFz _Trd;sާDG452r"qAՅBԊ  ' J'k)CRԑS:爪Qlz/zM  JO,\EWOJڧdLr0f:RHE=ZM66C hI&3M be S[(C+Ne[bZj~عcV|~7Xi.ȧR9WPAܽZεhت-ƛa# +gZϴ?Qk>߱V( ֿgZϴ?U(uC~ֿbk>߱GuC~X+gZϴ?Qk>߱V( ֿgZϴ?U(uC~ֿbk>߱GuC~X+gZϴ?Qk>߱V( ֿgZϴ?U(uC~ֿbk>߱GuC~X+gZϴ?Qk>߱V( ֿgZϴ?U(;Z*EDߖdu3^o?G"+Pu#t*bO~3xڽ\p?ϋO⧌zU1 q}3@Y7^1N5qRFG3XwTXJ(Lf)j6c]6FcR*`sXc-SDbj"tDՄںG-HR`EOF8c\^;~'5e#`z DH[(e8'HaV{TP#}jȣ>1@[R4+W?J|&5ii"'ֵDˊ̒1p([ثE9HNG4qGHmo"U\l.8 \r)M`tBā](\~Ty ʓ60H|xOUIZTYIfA%W xWpړFL@g*1U%,d* RFKUZn3Uy#% s#.HP"{Ui#iśIIPI#8Ya UNtH'Ӓ!U$NMrN'\g}T(5jUg%{f6N*ՂFj@OJ&xx܆L㟯*;??/ն 4,tWuC+M|]B:D[fovxÙ/IC)UvHq捹hH4oƖRyg(N8cO\3~>*CWƺ WrŸO <_ =f|-. xktEOBA܂ހ((((((((((nJ.`CGY׮ȯ ?uzʩW7Oo?{)$;9]_Y6Sle9'aHJ($I Ec*)gA7!H8aq5S zְ99}kXwZ"X2Oyuf#A9[VP kE-Lej0i>տgJvrhʻa. 6gjȿwX:x8ZU"G\2*{kR )4ap=<3[MI_Ɲv<XcTJwD@y`U MQNjr"+MQ Hyo4=EkMRbsOPma@V&!D9RɺBzf3U+AqH9zUWAґqTtsFΖ=RUڸFNJxiPf[yS3Zm F90"MU9ʙ54vPQ4C#4>f$$Nc! 뢼Q޴S[ym&QI?n/X ýPd4 IIO%Yf^NV@ɦ^+g{ .ƛrK;VzJdR3CZ>bm{<3GsK8 !4}Q@Q@Q@Q@Q@Q@Q@Q@]Z]QW_E:?R)zQOLfo*|S񊌓y޹v=}Gdh3*HG$o2sV@oҬŧ80*A=ia[M1浭R@W*EhG!>s[FMXK}<*vp1QCs6RkN]d5fs=EGhۼpVm;Y:DbdRv9$ZiY@4' 3ZtRFuԥ+25c5G^9ԂZs局nvj `Ḙy>K'KC?a&qjO59^<+ȏ"?vʫDr;?zu d&*$pJpsM5xB$ l)œ jnK+hqg~h3޻/Df)aNG_BTSxc5TecPNqY{6rj>5.@\^TY 9k31T#ֺ뉐1YW.t+s:s2ٰW6JnYm9YVF c9RX'bne9TYWt{ $sL0sՙ_ x!qP2*I7i E8OM?%kwe?6.蕯4}O[?BNj1HkB[!7GEZ'QEQEQEQEQECygoMkuw6!Xf@"׍\2/P[:ps63MgYb9?s(oc@erT[jv,jZ.ӥ#"9o Xr*_[kwWf;iei7(H)2Hy{IϨX[Ht; 6)tAm B;YSب@QEQEQEQEQEQEswQt?j:νvD^E%CVmGUH#^r96ֺ5Ny޹20x(Y=Y:R*3TcJH#)pa5q\sV^z(9ͻw**haV2^dt [сZF-gImi @`Z{+1նPI.Ox sX( N$'dez|{¿-`.oqirsͤC,̄ǏLko |WQM [O~@N,mD@ڗ]Eb[-)]#1i^%7bc "\ccr8QMGY2eaA"@Q@Q@Q@Q@]Z]QW_E:?R ?:e!fة&3r!(HUʿc/aWG%I)*ƖE,o~j hQOp3'bщ Ϛߎ[w+:r6ljBu8 0kgL cMP?8gt ,0qPIMO/ZW:P4Vs1 ~~m ^+_`&/}OW/V [!7GEZCqKa^a9x e}?q7"Q\ۼ]@M-nw4OMȴQ\ۼ]@M-nw4OMȴQ\ۼ]@M-nw4OMȴQ\ۼ]@M-nw4OMȴQ\ۼ]@M-nw4OMȴQ\ۼ]@M-nw4OMȴQ\ۼ]@M-nw4OMȴQ\ۼ]@M-nw4OMȴUcDӼCa5cm̥$dGR0AR"~h8hw oE(~zGI=]pW"BWӉ`?$yvs~f>w+Ba];w!'bBh8hw oE?hxx²Fʃ"oᮽr֞7-1>$*o}?q7"Vjn|-#0Lv%+G)EM^߳gK_.5ޏOc?=q$V5-tiWRNNW +l~A"'2\_X]y qӂ:Tg?,XRzzm =} dF/\8Pqo>:OƖEg_w1~1J.`CGY׮OU|u-7vB鶺~,LF3cƾQU ?.3y޹aֺT_ 6t-PWTn;#5ZAY~t7ԙcQԭ֣s)\Jȅ ~~m ^+_+OWlcbZ3O>^\Fu> {'<tU5bI5`Rd!nQ$ׯ7 Ɵ m㛽W7ѼI|iڍD0Ip (8u-W!w|_\:EǿoQ׵k=)VI;J4nA`WGZhz%[-^Kd7Ѥ9HF+,L|Of'RO YwhzžidStsٔ]FA"aZ7.'յ]*]/eջ1YT)Rq/-I/K;ў rz֕[x_Wm*{J"q)(ޕ]|e%jxFKF&E8٣9ybSxW>ŗ>da&zfe-n8$OYLyYN@ko[Ҿ YԼ/-fSnx|Xa(2)$gmKz[ZQH(((((((((^>55kw]ԮRf^xDx.&c=q?)KKzkib+ RLexl`dz/|W{./4;5.&Ҧ{,ǛC/it=hw#|?𷈼Qh!nTbd '4gN.cjqgg-K-H3" F:4['Ÿx_[wv`%MI(ЉdD\1a0IrNO[Q|UM|ca̅º|QN_̡k)h”)4h IX^}*u82WeaB~z1Ѓ уf@܊[9&8`Uf+B/9-[N\urKBaҟPUǵtC0y[>^ńx$޶v>5_%.N{-rQbźmdQz/f^Nmgh`F$^o.CȓXTFcgFX4cO2e\'~6+ #U'@[=,xCݜp}&CX=gXokM(yA植Cr>4ANs_]4ڳdL1U\f7^* 3!"֬U<Θ"jt+l)OT.:U˃T'~+r:R^F㡫Tf9&&"tJ'Y=8gb"9n+CYnebZ1+Ww&/܊=㗡ѿAStoToo ӭ%aKp'OEbũWq-Ϳ#rba#2P*C[4nVF1K,Ve>˜ȣ\c+4m;KH0'E$$I'O$zSKg|0A⏃{XVhWu՚Hf#'k 跷pk'욈i$K{ks[cܤd#>Z,-po"m#$>s o&K>00ϫh|w:4#ڪj$B@ȁ@P(c#*IIO-Wwo\X-Zb@VpDAC)m۲k5;Z%m4#Tkk9GGuo(LY$UCI$(c©EG zSmV;{|S "'u< ch`-؎ Ys(fMd,f1\gAskWx|Gemioީk&Ǖs+gi?}vWbL^D^S.қ=1O64) 0kG+Tmk>2*Լ]v"Jko 8JZEfN;M6}JH=.Oix{v~i滽}ԆId@I:d 9~ xo'ZZͭfK,1+@6zfۜ97wumNo7ltnķ{dgg]$L~a!;M}w5W bIPqDJ: ALw{=nËxJ?궷mcsm_ieB̔Aҥk÷/iҼy]xo_ڝnQ]*enw#2ma1_`9mѡlm@cE @Q=*Hvf`3f8}ξ 6|rzgQ-I0%X.&mOF|h;re@ X~e=$c_`P?[]?U/1ƿ&U|E3_qu??ʽ_}kO*~6 iHmxXГoq6Fohպ_~Ik3[vƩwwL-%]xYgCT= d@rڿ4q>޳g~/ZM/!W=6[,vr7dzL1C:,0Ht{جuLJzs^EͱD_\hnrOn+ʹ949ckYAg3Hϋ_Y$\ۤ#Kݣ_Gw^6# G`9[?p==ks6Rքyqp1Sڸ2$lMnZEci<}G2\~I&"?09oZwH W;ץ5mg9RHOK&ajmj{pq$lc8ANG缊2hl`)9. Iߚ眙j(wYӸS?ZϝN1InA3cRMY)Fk ɝ+]e$@9O8}kmC_G术.{]ef5UT< iR)1_&/h9ܿoM_%k̾_ۿΠ7Cƿ&t/[<`#YZ&ba"2M0: (AK:$s^W% ghUQIn FJ{\FuxKtMNw YT\0"wgk~?6K7< =WuȐB1N!@Լ]i/}S6| W9ԗu$K",5l #L1R^8M@H To% )@Y^qZV/~'ȷczV&iNm->,˱yJϨZկ~}[xMH J]AĬ4oɷ*o!+^o * Ki. 9:'=jm/H,Nk@ooP>y,> ͤ4:qxU3Z]j_EIȌIA3#xHu?T۫ ϩ\ܬ_c[#$5X]CcKL4[co[X[.bcBǩ€2{mmYmhGl lř,I#$}atw7zi z^ic>9na-Ė'Wy+|l@uqyYvXd"%c/ SO1:I+ߴuyRÖQh77g7qݘJ_XV $z"a\ğjP/@NL7wbEN?k *6oG,-p\E~*c Wz93VE}oO+x[Bkj>}~յC]6"S%V[;67enZgit/tKk+d,e[ !e5g[y(Z+]?ɉuxD~#gu9Go=fti$S>Mv?=sy6si^;yz f#㊲>" Ko"~Y-QP=)<5;F;iK2Rtjp=Ϲ}-uX٪ģAs1Y__O;#)ڈHqM"-)KxB&oxoǺzIixhC4RB` x}^e|&/tyu-=EBVI߁$'F!xQ)5 ^M1s-#/.X+"O<"{}??:^_י.^>Uw=߈/W1\4:o^¤%eJGk"1': nQbmk[RV-Y.ĐȢА{ʒ(((?p\q1 Q |Nx?\7G8ݰ+"r)4&;Tѯ)AbCZTNVN͏1іS +hQ3rVbp=_cb+1s4TzՈof^:ԓ_},Ozm-o*ٶvkC,x?js3bH穯Vu&}~2nƛI_J6WvGq^z;Mm*K$l6pªcԱm1} kX\M-ݟ-r}+%yo .J!z-Ք2KtG6wWƿ@gGcgbI$P=r}co$'=RmZ 6sW*)\4Kqbގm HdT$Q.@1_*HS7.1CHk) `T7lxJGI\U+(49lhOy 4q=*Mv/aHfnsy&՛i7`3P) toc|g6ԣUG*TмGuk$K60+kĞR1_aɮ{Ikv[k-i5.:V=u˙r*xr+2N1t_ JP qםĶMXIw]NkN(|K4*k>")/Ҹ%-45[pM?x>(O,la/W }y5M.]V0䚻qX-˞6f>1^=LLzщأ]E#tKp+BJln{khQfB7m*ɼ ʈc nZSr!Ep:J{w;cӵ,҂{c=i]ە0ȬE+_guSHcOk1c0_{!UJm-O- GUѿAStoPVC ( ( ( ( ( ( ( (2𦜱iGne"![izMnzރ :=h~Դۤ m9A)B"Vе Lj|OேZ޽i2i+s+] BgAhݵ@JԵ^#m3晫=Z}4ћRQu n1ea7m\.[7o]ߊWo Ы֚}M6Jk>IHFI-1T=y|g/V5+ţkPǦaD|S}h%id 4|\jRrE_P:cyF48vBIu*N;2IVc`7~\.Ҏޟ_}7?UUoQ7?U?UoWWĻgj/6vڧ@nPFFKjÿ~xLN_%ܱj:׉uqHGk+2 3w-l?֟xS]VEewtMSHӯ|=yLYGy*3F@U$t5F?iz wN[`كI|״yd@_Gki-1.umDKۘۿn㷋חK~QxAcQ߬p[#MB kmGU>;OIo^o?G~ |P,bG=sJ>'\A0Qr݉? .+T h0)Mry)4)T(3*1H:ӨSҙNV=j-'ԗLЯ&݆pG_?pp廜ר|Tvob8n\漱Ӝ^.<}{R:Q!R<הLkMnj21cUrVFғmB;Tf١pd^+8_iUFo4:hم 'ߧ^ErסYa".K*d9FaQW ]Ϳʩ/CJg1y޹2k[r85GPXWw2\JŘ[q5L5>-ʏ]k-_G-_G'O0k|2g=86~xo{& ~&11]#gA]#'FG4ֿH 9 z? 9 z=Ed~pRۭ~÷~sm7s)_Eݯ-I",<2Fr+ !I7G9-?D p6%%x&&ϠH( $GfŜx#"V(Oh +^Z㛯"H8Mq?Tep㧽~#>O)oH\mAm#2ķ"Տt2>"S>V]ygxT8UQ4(7Mq/z`I*xGY-)L"?1)=O'FU*32Jmֈb}?nOE -#$?E$$!hUL|# $78?E?'/OO6P~ wo$6*?OJIj#iȸ̹~vb=3ڿXK| ƹPK /?>(A *#jn1U~GlKミ[?D?ҿ3Hj?jc? C!'5,3y /#쭓?gȔ xJdXGo5H_JSpƬ=M],(K&7t~WL̀ki\|K|_v|&5?~quu|66^(UUUI ?fotoxx-20.08/images/vignette2.jpg000066400000000000000000001504401362435004500167360ustar00rootroot00000000000000JFIFᎲExifII*   (12i%RssPanasonicDMC-FZ1000Ver.2.12016:05:15 09:32:04PrintIM0250d  ' ''''^''''$Zb"'}00230j~  /|po8728728720100`@Js  @2016:05:15 09:32:042016:05:15 09:32:04)_ [ Fotoxx:paint_clone| Fotoxx:trim_rotate|resize|Panasonic@ !`l "$%tj&0408'()&*+,-./0123j456789:;<=>?@ACDEFGHIKLMjN*jOUX^ajce@fkf@kg@khi&ljkH.llmvlno~lpqlrtuvwxyz|H~mHmHnHVnHnn \fono vooo`osP s "s0148  6sDVEPDB AFrY;scc sn??,,`bdfhjlnprtvxz|~y?"r $&%(%*80 24T6> n n n n*XXcAEhjlnr  UK2r r  4 4 4 4 *8U$>. " 0&:&kk@>kHMk6 "$&*>.Z(,0 2<4F VZX!DBDU|~JUFHNP^X@B xzk \ dbfLTk U:p *2*WB6 f`9d<e@B DF4  hEj,"2@r$& "4(8*,.0!2# HJprxz |~ tv  XZ8:^> Ld~]\         6            <FD` b } ?   5   @ B D F H J L N P R T V X Z \ ^  ddST " Z $ ( * p. 0 2 4 6 8 : < > @  N P R T ` d h b f j p z | v V & X l n `bfhdjxlnz|~vr ES:      YCB   0 (           " $ & ( ^ * d, w. xg0 E#2 X Z \ ^ ` b d 44f 44h j l n p " $ & ( * , . 0 2 4 6              qzATB< " $ & ( * , . 0 2 4 6 8 : > IA 2    3    2 A     0      DSFWj x   DIS`bDd}rtvx{z|~R&~~~~5~  (.~RCN@B2D+FHGX "$ & (*,. 0NCM  $0p DSCP'=%[;EeuU ~`/[7wlWueUqU41eSQ[e]vE DISV8888888888FFFFFFFFFFvvrp~zy0wklkjffgjkl_`ce:OR0VX\`nlmmefjjjmz`{`|`^}[}[~[}[|]{_C\B[C[B[G\G[F\E[E[D[UCTDSCSpCS3>W4AU[AUBUCU6DTlkjglmnmmm;O+F$I>-78 -0 ,$YOMPPyZBW8LALG.Q>04/SZTC8S@GEP+-%& "$!'XzzdPZTf`[(Q0')51&6,%%'B$&-%!)60250(,(   -,7%3(# <7F;CE,K9>,&07@=5)-OISG~~~~~~~~&~~ ~~~~&~~~~~&~~~~~+4&~~~~~-5'&~~~~~'~&~~~~~&~~~  2       ~2      ~~~~2 ~~~~~~~~2~~~~~~~~2~~~~|~2$~~~~z~y~x~~2$~~~~~~~~2$~~~~~~~~2!~~~~~~~~2~~~~~~~~2~~~~~~~~2~~~~~~~~2~~~~~~~~-~~~~~~~~-~~~~~-~~~~-~~~~~~~-~)~~~~-~54,!~~~~-~ ~~~~&~  ~~~~&~ ~~~~~~~&~AEBMT :>Ru*/\d^1n.e.YJ"mb_!i3Ga,J!w @m80Y /jKOD&&G26hNJPRSTu A_YGI]us15T{qu_=ޝWv]M=UuW7_Qq_UUCQUG}u6RHKQ^PY]MP]yק]_QuET5qU>Z,Qu\uu}iG]iM]DqQQSSuWVwF\yvֽte۽UP]T8YqUVQ4eUWDV=Q E?1T}UWct_ЕvБёq&vPoW4Q ߝEtt]uY#^6]q)Y}7QOůU]\uqyqxGOW'KL7]X[_z5EWQWEcP{u=EtMUIU-Wҗ%}[W5Tm%}TUacSuuYeJO'U=)GݥTEV f2uQjpQwQ09J W^x4-r9V3tX:Q^=Elw?^Hew_euzEy}Z.257e-_]DC EVw@qy^ ]'}vGAq5Q_Ywo_Y}OGO=SyUZwDt_}UWY]7QYF\&]ٝUd]U_U|z_U_UKB5_Wtt][ U7EUDu$!FCCVcfss%*r qT7?6-U}>U_g~ ^74I 7 fa5d D `m u6Q 9. 6h V? lm /N ^; F k pI tm%G>T B _q8+ p S Sm0T<^ J +t  ` m?L`5\ [Z ZNfz Nyu mH=^ Yl E!~ QS xomB 68 (`yvM eD 3? HTr=gA\SQߝ}]N\EUUUO%uwOY]We~y{atPGt_=U \Ee{ZtQGnYld5TIA_M_qTiMSM\OY\V\-AM]BԔU|EfAc\]{TUqy}W]QUUTVE-UmMU&ԟ׵U15UU3Q1@OOM1WQWtISwU}[xpUqbUw?/ML$WwYwWreedu|uw\Y]]IPe_uZX{0:YUP\UO}}Y-%V5r/@[1u]X;~^%g]5 q]4Mmd}}TUڹiŽ?T"SQq_Y}q4_=WGURUeDUX|mu WgSWc_5/ܜUR; u]uT@}U 5TQ-}=_Awwӽ AYmW_9P?=UUIEsYmuKM}u{]]]GtT'WWTQuUWU_wutUEeU7=~{1wb{"oD5?_Wm_ST06ՔciWUޟ)4˱WHmntu}uXOUjs7-_U_]_V_DVluttqu$i%L7WtDe?5EMtqT_6]_'W6UwpeՕur'uMTEG*]=[D|Q;sCWtOMo=DsM%7-qQ\guU]m VGU[]ַWSioG7uWOWp10G_Wr=E]Tfm%M_\U}EVuQ>Q]EwAWv}QESwTV[]16WEy :_V5UZ1mtl_rU\CA=EUW Q_1E3eP]xqWx}U8^uU]TNdyOW^H^{[vAt?wwf/ W ]_UCOTGSeEpUBW-Wt?u3VqL3u}e~Puwu_|'cEp?UUw-wWuswG\{Q]GwaqYӶWwOUMS VC\KoWZ_tږQ -o}R3%uGVU6N\A%1Uy}uOUW kDN^U_U;\ud3gUP]e9wh=חPfRUY5=u7U]UUoSum(ҭWU!}'x]t9nVM?W!5uq|]uSq}Җu}A]EU+W_GY~R{pv[cA}pZol 'LW13QmWyUBTuS]W =.A%AuuwUFvGx[>G,7\]U,PNVU}W/EuqU}V#AT_\IW[EooGUMUWfeqW{tWWc~wU_]u#|}mU]Q^=U]9WAӵQ״M$q|vMEUUUWtmR%U }[u:WUQ}UDI]U^uISeUUS٥Duѯ 2]]U}}upIQQ'U]pp]|UsfW[ k`]CMSde5wqT~v?O Ls&W_U}shw%uSWw Y _u?UM|iݶEsP6_ECW[bQUS<Օɯ7qqgWVVEesu_AI]~UE1EuSzPUDUYUSO_] _uFgU]Ua\1UmיUGT}]uO% WtVE^|ݮWey_=Vur-UoYUsP4vwe\EUgW|,Us:N6E5EWMU[G^]'Plu}hrUP}U5ps}Ww]; n[+u?ؠu=RsU]J}?_=Y/u7wu}UToeA}Fz}TCgKMG87.w5טreeQEqUv z3_}TU_e8dXQY}U'!FUAW]UMNW%U}tSU7TweY4tUY7pUYV 9Q}ǩð|UUE5]CxSij]X]4mEfgUQV 6X%uBUW-фUTE]qWT5WcqzT{UY_[=W]~_seck]wPyEwVOww ]zOozu{zUnQuU]OGM=3U-PuIQUR_msOUsU}y%u|O}tL]թ:6T_]Q)TA_O@}WeUTVE]F ]G_WTP5]q\uUvmuT?M_\Нw]mu U]qeutNv/^}]q__Q#@xU$^^Q)YluQIgeU]5aWuyc~|qu\G7TSWWUGAP\{6Uy/zp]MMP.BR>'7ZmF'T}TFUUTwuTUoU?v5 VUq6Im۾yT~?SE]WUOG=PqE]URr} ]A'E1{3ÈUft{\o-%CTו}Ttuw4MS KOpNߝտ_QuQN7e>Y]P]]@ k]Qv\kTEv@tyW)vmVWTvT2LQU-l{U!o^U=pU{M<5uUӕwWuT_u=Mm E_{UDc7q~| QUUWGZST4C5iSuUJ]nf} ݇qwAE!uPWq`|DG,^OUX]}gqvyS'nMum\UUDEUFSYTYt/wL&uU_QQ(M~~TIuJgW;Wfm tV]1دu)~OI[tA5UT_WEWTHP{hz]u\\YU n\FEeyZYqPA[]L1}S?IU5pV#M}d;s}!tSe=UQV$]]VC4QWutPM =Uyw_N''Wn1{WUמlLe[ emSU" U\oSqv,rG ~m\_EW$4=E1uY}VKUUG F DE}_AP%"Eu&p+Q^3dύAt}Q]Q_[9d$D1}]P57-aՕ9BaP7UU5YEaUwCU__%}VWU}AW]dWnWQSN55}]YUWW}U7U @ QUV6VuQ9ՕNl=EuEU3T?&T*WF78tSWgV}&~ݽ<4WTsSyAA^SZKUGM05]M]t5ֵPe@pV]W=sUIOWSU\ _u5?UYRS[ 7M;W/]E }=U=[}U_B=U7?u~mrN-Z\ @3UhTZTU__ U5w%]Ӱq|se,kq};+NYWED[Mgu]ZUydvھ7]\7@\5 PRu! DA._^KG_=Y~ vTUqTP_T-_UЏgEKmAT vU?\Xr|p[|OwM_AUmQQU]UU۹u\V"uLU5UWU73]Q@ē]7: !]/}JypSwWtuzUWߖM=__}utW0R`UVg5UqS]tTuo%<5SLG_5]W^Aut?asywUu10XTNwu xY]|PߞU@UUGU}OӅt%_]+eŵ4 t_qMX5U]U*2WmcuWõ%m7WQ5sIB_D+k]Wv%wQwepU{_, %Vg4=eQ6GW4+5-Wkv_zq`]}Xg>-AvUea-W]߱UQqt{tU_U QCUU)catѧUtU'W H>GaZ]4U~0U7XV_sf[tuXRwUXݕQWUUO O}?7OP{Vf-HSUUU5<U]W=#U:OQp5Ќ7[R߷DS We[Post]մGC?GVF{5U7t'_?IugS4QqWRBEU>2]ovTUZܕuUu@V5UWŋUU DYYywFpg^T= }EGaM7uA U҄]t(q]\K]-u_\uOyTn[sXE_y{QcuOkGGOQ{7UUDuYguYS DWb]?TU6[75{]^U_5U555T}TE}5!_?MM_?UuEPْ8$QpQ|KQ×WW_XUՄVmuTdq$u/WG_^qQGg7GuWQUR5RQTU^ U^pScj6X1\`w?u4_auQ%׵WdQ_Uk[UwU \v,vw]SuqwQC=_o\;_UGCi)RFaZxYVUU]_]]5]{_OS]4%LqU5xU}O/GwSpQMА6x;D}5Oe u]W{syM__ =^r6E@qV5ՂUt{VwU]uW\@w_s-? UUCEtt\UPo^ue_eEuoV\QD}]3QrUQWsu^~u֟]}vTr2}]WO-wmE=}Z{FKTUQUwE7]|Tig.GsW_Uu[iyQY=uTM_Wq%WgBKGф}TTW_,WtUt eY{E{WuTY1EEU\A\\Ǖ}q;Eut'Sřu]4uWu?TlET~\QuOv6V$]55u}UTU$QY}uYW/O:U_`'``Oafcebg,k^tY-sxv`vy|뀤yxuZ-+oi 5@ywR)qJE5aC$#mvgmpq6k&TI]]X4BA7+5w.I ?N>p?F#5jڞ:QTW wsU,F-#+6)E.3..J k Vp{!09v;=n40g1-n `)^=;Va|"1-x - i!*{ U * D+J\ l90!<  ZE ?2'EMMtMNMMNPQ$TIDX,\rZZIYpy@[\_ZS%ducN>HV=bdQEO0C+ %I;:@306_6K,  %w .#x# 3! #Jpp]#3?mK~LJ]ןM/+Бi{=/}<_M{BGOai+Гh|_MƯsmK~?_x-G+Гh5{Z_}¿ >.[&H4{=/}<_M/+ЏhUW? {¼B7Uo? |cM/+?"o=_w_-G+7UokO~//88}ݔ "[{TgjHi`hC'Ea|jӃ eaY6A\xSKcRّH*H 4Gu)]ns8ރ&Ƒ*Ri9 )+RzUzTbOiR+O$f()zukgYJDcѷla0'sp4i,mϗ6IDYY{ۅϝ[Sox8/S8RF3(s-waԘѠ-$ISG=3F&r@4\$[. ̷ d̰)q\䓌 {yKy4H;YKھHoc ǖI菔.=ߍi&Q6;6>@F!zE-ʰ?*z{N"<7tEiK(NxBקJx[RUKY.g.$RФB'Ϛ֍TZ<_W2iZ4$@vqa^jL$]wxю{z䟭V*If Z oțc'duG̞b궪wK ?+;> #XKu]>}T}ܔldW1^L>Բ3)f1ʘsӱuqԨ;l}+k_Z%*nVqH OH{D'{=EN\y0@$.- ~ߧZ%ȵrk6H9sʣ4(4屄T>g.KOyKJ2e89zDEo4JcqYU\ }dU|Wqythg;2FW?C^ףxRmoG-p^uC0!  :Us)_mLJ.xgS&K#,s^,|I6Ljܮ<^*gFQdXVy]: 6=T_hwDƸ~Kf%U6Yǻ!d#E&k,|ʶ`RῌPmi]NsW-YpVo Rrt->e ;ei_Φ1F;יjJc=Z#WA m"i5]nEH9e!RH'8 c1^i_ui wGd{z ᄭzoKnRF%3}JcL|x*✻/<Rv^G"B@ae_>6id/r"f4t t?REsE߹L SCR^J1m&wf2^şA19k z9z[ƳL7EĠz7oi~O r) l9QX4G4WRF+nľ(`koA!.U46λ:y:v#)<:s7t}*gZ;e ^L=qӄJ<Zd*s3+4T<!ۃHH}:Z9 ЯۤLKLd^}ǥzMbMFS?~BjՖs~1=SS12n;;Y"OHX.V{)#Lq+Q&"-2u9dI;߫\F!PO?{?|k2,BpJU|%s[TcO,fB[8'Ó]Wom?Q` :eY&si(9dvV~W-xq"Ϡ,}vwq9M 55)\ֺ;{pOҚlu Exz'B0ۊI2Wc?]H{;*?5PfLxH@GZؔ)[q$W5IuOjabmVPsn$?iGqɶqˤK(iHaWlmfx ]㩭 =,B?v['$W[Wķχ-ŋ[G %`@Y|(Dzv-)s7-'EnBӵH4qwI#F6f-5?ZL8,uE?+xu>:zj^S F,($3$5xFnJreo¤qE#>$d*w5ŋuqaR\u^|A&=ZG 6U=ʞ?ZO~i$Onܧ ϕk2Dy3c~ÿmF>ͶQ|ѷ^:p;NIKTsBV=ZB]$R5 CDE$W5dμ3z ut\- gֈ;mV)9ڦ_Q1QL@r!^mCM7O Cn#p㢚¿rBaIE ep?UXKĚi]Oqp 3׊t\O|=F[w_Ʀ9il`w;$hF2TEkÖ3Ku5*뻃]`%5KaI2X)Ϯ UI֦QL&c/7@C5Nz]c&F|xmɿ-E?8W.RWzWLggx\JT2Þ#mRU6RȪZEb̠u&6Cs[I r=v:OA\ 97|KFH\S?0g< Н +!1Ld~+Z"NWJ{"nX Qn~:YW$,BgS*ΗMͰ=džxšn2m+)aY?@+otS,%++ p:q]lĮGxS^k)USռBFʣQ л]ZތudaU=:#q&GNkZvI`>$KY!O3 5vOޑ]L3K Չ0.}~yixumRHԵ"YE*}AIt3̭/T`AǞ9SӼg=x )J)'.j~!nDy\I'$>`r&5\؛+ u yRk{2 ;:`ZxI O1,[Ϲo_CŲG=٪o1x\?Y?Ɠx-w\x-w\]QTx^9ǝqu/&>.ouR@Photoshop 3.08BIM# Mike, Rosi, ZSulinavhttp://ns.adobe.com/xap/1.0/ 1 2 3 0 4/1 False False 2 False 0 R98 Romania 0 8 6 3648 5472 2 2 1 5 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?P((FH6ZB<̉˕o .l7S9<5ytĞkޡMjxU 3aL]`W8yG<ljy,s[Z}s\u],K^]s\~zAnk]ڞks JVŽ,O5{c 3*/vvrOA] ·&[ȶ<ׯWK yTqR6<SC+!uҬd.]O;C-y$ 8# pG6'iEYUHW<S_9Jk)4`vom6BtsQ+3tsZۆkҦ8^k}_/^Z3DQErAEPEPEPEPEfšԤԸu젵8kQb' fB{5H@$k_hc\M4Pg`NF:rvQB7}TIÚ6f?o}wD͞ۃ޹?jzȸVWкn*ݻW> 񥭖ss}ib=졲s}k5|Ui1񢴝0'y:ٝnorC(5i&ϩ|yt8 y3p;|`׸[1J?oaC&D&tlI} WDzxBhoY?ٕz^tL%O81w-b`dڲ$եʌS$Mk6+*[)ږy.'IA ;}ɯ7%k_<3hq*=+֭!W҅MὍ{ǯn]96űҊJ+;Š((((Ӫ 0w{&QXwyQr"Y @x\I ט^|KIeym@ Ԝ~SOZuh=2EgHЅ(݁~s/u>ͩO,L& d c׿|pL^(YRI_b;z}FG WOğ\2O#+<`XʴZB^-q=FX,! =xፄ^"n/U!YY'rA@}M| :tm {LC쎆Bp~k2o|ΊdR5"\u09]Z(Š(((@`lvyS/7i=@O7XiR#m5/ů^jr6wo 1I{ڤcah&;JsGBC^~^ԅx}¶1GlNy>TI|:Kտ|psdqf==MEop1a` S1֯:͟ vyc**EԞ$xnkFt}3sY_4 0GCA|_βw76alT9PP㪂S_|n/uiڴ7ϯ[\9K+iKmd:eĈ$Y)qԣKTA%#x*X-~kmy ~\e;w*TV34* 1" c~K| }(}ϚGg]ǡï:?omt$wNKY g^biCeNXz/Yi[m :A@e/*>0Im&C1 H<8xK~L+H>I 9`y^gW7PK<1)bV.hͦGrZ|:MZyDF0l_^'uUeN[X.'|̸1v=G|AyŬC>V[gmgA(X0AkͿjOן/ (p_raG3[OѵnH# {&km¤nRNs־(9G;Bլldo¿&op5XČ;z'~d FI,R݌L G^2yci/] vID18? t{uU T~~\_gZNYt;7g!#L9mc^7zlBGߩJSaEc>|H@mj >`f ;};WZ'VR6UHm)~@#I,L8"T__G_Zk olOցc,l89ʙGrWmT5Tn4,mٗF15c+Ox^GVUL\}:5^ݐ* ZM5̏S#W^ݢo k)QkZp ++?Cԯ}ڮwhת-ۗ9m`p@'`g>elh8p!M>a4hߊ ZMtY4mewOݹ5f8:jMjKχzc&m-Ps Lk@u0wici[\Fq2(>< 09* ((( z^1']{$(W][21}Gѹ,Af,GNįn6b9VIf ~Wuy_ xT5v1fA ]3HP?d{kmҝnF{WU࿆isݖ6?wֻ(ە{LZGA9ijgWNJ:l4HHEsYhz'KI%\?LWu_-n:l 2f<:|#ǖId9hҊ k9fHWJ ubQR^Pak䰝wıYz2=_kǿeOi^׊PGxBWgj03xKN[s۹- >akQy$ГOhUMZ ٟCƷ>IilV8f'$e޺)0iV#-?m?tE 0Y6>Cʔ9+35͹AЬ^][˖X̏eg9c=O5O_s$k``+Ah ^u{gi>Ӽ9.^fQvp>qNޏjF*~+1;&`{wmkJN{m80g9#~UppWOIhĖK3m5w>!Ѵ:H/hϕF{sμ>!h6[̭kđ1=⦴o~+|Kq5iO:?<>׸x_aף :T:]I=Z\!8@5I6px%#851~gЖi@HZ}k`t (Š(( ׹+6G^v3>TƎY`gpk▫ot2?ݤRIk5} Xq8q j5`{fJiPs |&+w]r9 (WsMөי֬,zc7ѵKǹfqn.^V} LV=泩@Y|c涧32O11c{ _(z>;[ye4wݿA_Q2<^f.$:1+/bB \qzWpoao+[ǼK]ռe%G/WbPO5ؓ6{^Č}65x־o_\_o5 R~sS~bm¶R%LVVKMJXmQ$?:/\[ht>RLM*0K\9滏ҥIx2 >T</h:khFKԺ6~}pʧ1ɬfi(Ǐ|.E}uˎ7XJ]ɦ鲞 EXC۰WП/m1H'?ּY-Eh:W1wBy΄-QHaEPEPWS.빻lDkc|wwci˻{GJwIvg3 k\-}G.%v5Q<<_a'@,W>8Ӛ18걡9oӳn#3c]F xjK]L,mrjPT>)', ck^ݝH'^~sW%%i>s .gX?, F @5+xگ'5 {YBh;V$,'森xWLѼE)Y#%\`JeTtݮzr5MM{ޑ%'F{Ru2<{~ϟy[7HBMo1n=p.BY_QJ~:#[sSfT|LVC(O'[Lsڢ]ŽI<  v$~k 0y~O]X ȌVN\H;fDeF[ -U<ˎo\g Oڀ3'Ыz _@@򺬛ؖ8^نiOQI+Y]BƙnS _<1}ŗg=w̫XU`T/M2bq_ͅ3;Y ѡ#?V:Qƚtޢ2U~{@k=w]"sp:Wg.XGwCsU럋%`CFkt$<9La7p{bKß@~簍bD EʛKiî} ^]ţYf%IF@/{V؉r>>,i?v&Pp>65g]lC A !~T]Ei_ĥf8kVw/CeK$Gܓ>Վ"$՛i¥T+Ԯ4[rK"Chds^@_I,muȒ0zt3_|x/Q[$0TѲTHi$|AKmV1hQa%jĜ SJwt>u7 učr-1/nq\I_»Zy<9fiXOu ԯgv%so>D;V>+լ!Z^=Q^f1 ͓֨EB ( CҖ{m^;F/F1~Z?޵|ujRt+&@"MQ/+?-:^VF$#&p}0#ɃO%4[.dWvl6G\ $ B#фQ\q2ώFH~xx.!>ƴmfh 3#vK)sҨ G4!'пgXxN֋˃D*ZI1çr+~&b-#C 1ӾanoDYG; ZRvj7_|S+`|-Cڑuu~?ׯx=nQafb~Ҽ8=+W0cidS|KR~+.@kB: ;O^' R>^G ;-bԶ:tfk/x?+SO95C\c\P:u>|ou{m{ `\L;)=BIWJeiflK_lI$tKsmmiЁTg یrryHJBPG5@=t0YF:3j1VCu-4{ xcJH_d>ݷ޼\齎>\`Wv@mjT(`QE-s"V2<{GM^-2YI(ϥ|+nh`;|Wܞ'9oj&ͣZƍ~O҆]4dF{MK "3I:C2\3Zvr%]GN~kƬf5.L 9Q?]2Zi+q= ux NG_+g2c9 E0+8inC1֮{¬HAl6DJH yvҹ;]*U ݜ&eq^ڟ[|5" ɦlw\n?JaVMvY+slL uXq^'4kf] }ko~ׇe\/>!~Zo BN᎝_CNqiXFWnG*- ul2R ]߃&.+mLt8ǡ+B}kGԜ;6}5)s2zׂ"'Wlth^qv> {r<߆+ۼ!o?=gZ )UQEQEQE@F"cZ}"B"~[{?6/IGWfeWA9IT>n'$r2稯H!R] G)2W^T1~2AVsjZUP=H?'̮~^6 v㞇ҩ$r7<޽wㅬ6|CPF(@$w9?x̌D8x+~2=ՕR RɯW, <!WB>O^ULDGH=>nϽT6|G!HƅGIMmR0q:sᖇ^?nn4YcIYHȯX"DhaSswZO?fhW$Ԭmj W!Ϝ #x?aA ((S)*0:c_ I?$oۼ:(}$8ĭn}'8?Νg;_ƫe=36rJt ~9x4O}jXؑxu]X{JݭA$Lcjp@0ɞkK/E͝[/H ~T<7Gf1qvKbU~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-20.08/images/warp.png000066400000000000000000000167311362435004500160100ustar00rootroot00000000000000PNG  IHDR@@iqzIDATxś Nϼ3mHcBLK¤"*ZHѢ}/Ѣ=*Q!! I c13ITν<ٷ{%-^8#)))DQTlKgr8|O딿:q{KDޯ{~Y,3Cgc$(6i ?u$"Bܚ5kO?͛,YOKMM;VvePVPfM#$o n_gH"ż0a„ا~j Dx SĤTa<]sפ;Onذ-O>cgd eH'|@}$)|G4'ge=G3ėf/hv\Dzmݳ|KIIۢE:u߲eP\PPP`ZaÆ/?€ĽT:wwD֭ s1)z36˦z#G]f.BWdj c"^bEX|$%avI˗7fLy˩)L+_{iAwq3=f)< x4F/nڷf\!kfhӦM^z_Œ3Lmׯw_=՜\ZZF `L SL 4h4i,X^~e/fk֬СC/Ԝ-f@\znxg/!El/i Txᇇ͏4OniӦF W_}u裏?cGaذaᤓNyh1j(:w-ZyiΚ"9,4O=E8IIq_ጊsïA3θobn_399*{a}r)A&xkǏ?03={v@ݿ ֭ .4_z%3¥h"s2y ֻwIz &[yZ^9~yy2S?p zIorUV'0<Мk׆J*K/nfDsn޼yX~={k+V "4_ "vJ81#8?}>}M̀V W\qE+rUU+WN z2{FRw}wyh9\`R"q9L6 pED '+}].p6!C/?MC3о}{Rg}fT8}HY?rcP]Q tAaS_ΐ8.]YfHC9saOhϙ0sAhr%E)zVvBDx޶m۽)[>uARY2O{Z(c*dN䯿6("$ ca}(wyLBE y$H'NMW1 $H=/ 2Ȕw!2E?q&lu / FbztQGERHERHڵ+Dlt5P$M߬CUb:=Q*U"1 ⷲHIH#U$SD 0A*з $JU|$B~޳gy$M{Crnez:p_~<.Kð͋G=>N6Kyo/{0 `96n_(n0Qqqts{^q2ݻw7O/ $> 393" Yfv$S Ty5j|M9U3g~߈=7 qzڵAZp+입A5:q4ANKc,*puum L9 I S3QV^ms`ʕ+Wꪫ,"@?A&r&-1 IwA@1$# uG8@0\~m78<+9| }Vm.0:A+`d?F` !>-[^0 lWBy?]u< ǧC.@LFfcBƑr҈gB$GVa[ $Ko=pk~5 fJI4/T`!D(jGHvEvOXKpKdžG}`8~#=gpMűd K1`qS !0L/G|ђ4^9kǑB4YiZRIbH6M;wvRo91%>/ ;3<Ӯm۶ᢋ.2Y3|(C-М @8#^iȓRL ZR#q%WcҜd}iLQsh8ើ~f]fT'\wuh뮻a @\{Cl#C];֑41b{Y qa>E#9 Fu8@.pA؇k3LGl]v*$E|Q?,3".$ɘ$D ͱQH0D"ʶ̱'|y\ 9=уe˖Q$O M?B [r0Q~t/kg1̃9άFZc =$}Z㩵Q+EzcBHt`G#hN<%є)S,Ta#m z&M2{LJ„ #\|o!Y-{:-p$HMHaX/fXޭ ]xᅑRkH5}we|ಇp!HE" j8ݦL\M6mbܢeTPob&gK3JUGF+ bkuAGC6|$s% 8|H$)fGgO:,!tYHyK3mn28<+ q8 NΟR]CF+6}[ڦֈĬ- JPJckZ^Mƒ}9)'Ub3Xw)z^%E<{1'.ɸS$MN4a*}?nqNیPZW?Ԕ$S,Lu֫l[G~N-ÉuIi`YOBIƴ&L(LrJ}) qFZ&?K3BVVÙIo'Ols5ubN][,[<{;,$Jq0pHw\`#X{*3\N ;e"^Kg?^nIQ|V 4` T)f&@1qGB >yoz*ᦐI]xuMXU ټIrs@½n"&%$ C'PѾCX/D)뭷JZ7OuAgL.xWe#<+s܃ 7^ށG<$@&ةB |^bq9?+㌝m ՗3Y{Vo # 5$&nHG,Q"ԃ5q)W^-bB$vs&oO?5CxBϹgw%a MRoTD桱h#of"MQ;26ė.lc# t} q'KGI=H8ׯe Ns0q4&xQR/OpV8_ {RZ8MۇE @(PoT0G6#6a!MJg2O*D7?_< Q $ MܹLXu'c>̃ybjnl@X BD`h?adI$Qk!ғ@#|xF)iMTH hиt|mI,R1黻UVM֦DK+B"'>K udf:#(91?|PTRf>GcEQU~59R'%Bꔆr_1 @3 p=\!C_rQң%?<s/3˓D^ZJޏ}C v-%yǤ1h`L_RW#aġnoW+VC:B{&z Z7Cu$%0+ur r. kMxzH!"?>Ssݚ!i>'LRV6kjx<EcQvnm `߬Y7FZ%LAP%oDu!2-7..!}DBCݷz앐00N MH{G@iowqhސ!X. 7ZOﺡj$&ty_BFzu+gE!;9@u1 @[hө aHv5@҃&0A=bOٍ7,h A<ߖQ `呫J+H?edTf^a4NJ)"%ƻC8M=A8R*+WҘ<=BMRRe#s 6B5H1^RU31'ԝxtFE@ ='5k"L Tפ4Yd|*s{ f5@n?cPļkU",Iq)Jv\vQsbhpb 0Xy Dutz%^),'ֻ8 zW* [I$K&)*TU^F99+oUpt)y(xn0T.26J%)Wg3!\Obq޺ҩ !9 Z4Y=s963)n;Ŷ$}eXIfMM*V^(if%f0230Ƞ01002015:05:27 07:50:58Fotoxx:paint_clone|paint_clone|warp-curved|paint_clone|trim_rotate| Fotoxx:paint_clone| Fotoxx:paint_clone|resize|resize| Fotoxx:resize| Fotoxx:paint_transp|NEV6iTXtXML:com.adobe.xmp 8 1000 1000 2 2 0 4W`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-20.08/images/warps.jpg000066400000000000000000001221431362435004500161620ustar00rootroot00000000000000JFIFExifMM*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^[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-20.08/images/zonal retinex.jpg000066400000000000000000000360051362435004500176110ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 185 226 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? { x|Wjk/(Y_VGڽW_gG?uZ C7•| nm?ÖbR/s<1'8˶Oȣg^߃w^xZ]es}$*oMTq֓> ^_F.a0kd):){O7Gi.|žS~Z'2ܜsk #OMo}j!@Qԙˌޜ+m~"}?Os?ALQItOepFkxGG< ]iWUoQE+AtP4ۗ|`Q6Aֽ3<1r8$dQȜ=)Hq9; ﵟEEk>|[Ox5R-&(Y$7y+Nʡ 1J A{7:Y̓ۤci9mVex+E7o18 c8;@5vN4cs}]Z[ZgJmM:,(ciׁ|Ű;n[[DRp *l:1ӥ%fٞX8˱E@2X+F-]QhW[H |?og3jUZy%6\WX+1:w NJ4h<1 d^4sJ]dAAi8^m'F`F*f5 b#4ψ [H\qR]"_><𝦝g<1c]>c@ă½S90<!W(c M[o(t|3?2Q ^_6NkYXĐºua' (99}gAoe(1E:ohAU宑?4`HIprI8B$ |X^4Q& m\ysul؊(1G;Qb(\Im QxcOhPڋKvdbVOAY:{zjZ8:ݢ~΍P8o-WG[? - oޝgzN6C{Y6hȶ#-qs㧎!˥%M䑭 /4g`$&t|3?2QgAoeO.i.;Ltp.72PO8VHY|Y^d58Ŭ $cfN695';QbwG? x-as_5]m8[K$!ysN~ú>[o(t|3?2VpT+$rbu_.i>Q^+Egš5II.4&`&o @Zz+E|{KgÃWOHNdKJ"1#1EU kO5EY}h;'_f~qko?z>oh?]&o xb]NC[ID^:ҋȇ$g?"_=^ ޭwukago|!Y%XW_'F>¾;+Ƽ]߉?uO_EӮUY$6ʈW]Ď/?zmk67S-1y4HL238=r(J7mɟAQ_>tOkNimYza d`d\A  9$ZV7Ѭdt:m-LeRH A)sIkooCz_GCRk*E؉P_-ۍêՏ^x]5]:lAg!Gݏ-me]27n!іdZ,W2ӭ^_ٻĚ&-ܐiVm'g~;|/I}jwri"Gʫg Wof@z+_֧¹kz?\CN|Y=|;-$k_~xQmvSॊ"fM'"1w;\|) چxVҴKi|7`ČIuQ'i$uWo¹kz FJa?޻jk 'fI/m"2=*O3x{kuZ^\w$r$6\u\WE _ZfwWMYou gpC>[{ÿeUVMXbQ:AV#p4>'7{K+_nvnA`3e_n|xgKFuwff(,ޫ3)"O*Ft/IP<KRu,Ӛ Gl@#)K'60J@[KiXERvrp9]̺eq{3a#یbU?o9 t'N?QM\.|0rC줃v[Ysytt3~зVYus{D=N4wK,Fp ,B`~G({MӴݽƚoѸ Kc vD鵙ΐtuaVDo-!\d _iAѼCuh:3{<6^mHT͑H*A61.T-iip]Y,ڵaq27ʜpM=zK %a'Y`wI˿o:6awִhz։>wo w蕶D2ȏ2CWgWWh7zvhf*iZQAХkdcBi oZ#Q oZ#V3']̟ Rt_/??{3WWkSj?WWkSjdcB V_.ox~&o5 MjG5 MjZ_̟ Rt2|1JK|PmcA]_ OcA]_ OK V_.fO?)Z/J +55 +55i2|1JK +_/EC?"g &H腕5%qm3xռ>T#w&u bf?‹מ\~ g1[xٔc jX I4>Z0BlK_-|fqx3h+5-VR/39۽ ps_FxRڌZO2 +1b 2ɧI+l_ӝ/\ ڴ4' vW @?MWR-@i*O(UT qPkEq_j n??4'[Cӵ#RyIJ/Esj n?l5V6#J ?.@>/Иt:OKXV0+Bw2鶷m%wZthZVti}Χ#38؛r*RFޢ< I/t[f%+y5 aI,v#EH!Ti/ kz^%ǧ 㳎E+$qG$^\m?@W :֕h% ?.geR$1#$hQT/N2;ß|;ٴKk-91"Ez|Gu_M[siQHa,K#,k *~gǺa,<]^Oi0Etf1*}+xN`[dWbBeX-빇OpSO0i]-k-7V[hS$eڵ|3>guENa<[hf 䎀2x.Kmu%[ L ?yfFKSrgEU5g!.fY"s\8eݓivēȡpUA5xþ s̗Ė~L[zF6>U8X|SQlu mwNQ13HP@ |qҁ&ii!a#V#pQ9>QNI8usz.3IGkZGek *F;((((덢@P[ZZ2B Pb{ixYK .&-.$هAL +rZ+ϴ= ~Xh#N] ( ~_m׊mb\-|Å5hhO~s_3Pxv :W}[ɮ$IeY@el|3x5;ZgHԣ oȂn^E 8 Rw2RG>[6ˮhzVozYf"(\3 9;/W.mS~5 ΚՕwtB|,ʐ{o#Mk+UT KF%yE;olRq"pPI b+G5dty.!c5ͲnШcVqh ኞ}+E6o`_O>FW~7qmO\zM9-mX/#6Ua诒8=@5VÉrG%~/rMRSMw}I!U,xJì/3NIh5+ UFDHО3Wp/i^'4=!A$>$3 Y o D[-! 81LNmoohVbYb+H.#f$Zܧ{Z_ _?Sdtk ?$Th`mc tk'ͤko}`q#ܬ-Z+ZUeF~6O3MU$o?_Wk?]~»Y +N_'5Z g_Wk?]}aEu??w/>OkCG3_ůŠ?,g/ _|nj>|FuMkIY,`YĘ*n UR9lWi>hz= qk2xC\;h_^u+n\Ձ>Zҭ79%$Jn/ImOzEV׆:Ĉz'5ӺaMwq G󮊰5*V閰\nn#RIW`9oժ(n-@fqdDT1[ P/옿Q?ERɋzLU1[ 4;{F 3BHր4b_'GL_d⫞Q4ӤFxDA9϶+cL ?1[ &/u2U[G#/,?€,d=n&O*옿U M3×Cma\,nbs HuSzu0Oo=Rxa-^.I%DrJ뀫PL_dɋzLUb+/X>MM olww<5.uoi>oyLEjd=n&O*옿UoG_X:u ??b_'UcWCѵ%BB\G$RO&/u2Td=n&O*7Fm͙i.QIPY$w&эƯ+ -rbW֯_^E=F{+WDStBˍحre,^' 1鱛9M8_xsR.@T%Km TtJz SK~OWS$ Lj!\xC5{oF?O sق̣"gszxSCs!q7tRMFvS/d&qcM;[_ޚst J|zu8Quf#"۳`5:QѳW+<.DzlA=LGR4u0tK+},d[YK4(C"\3\kZZڶkkps4AG)uj‹qPӄbIF@z88޿ׯKO#}[9G-[EZK:O;DIQ?*OiqOn:D̿$H20W'v:Ьl/u hBee=k/M +<6Ɠ0JX}nxg|{ 3>k-t>Ē 311 482 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ??~ Nc&'99[8+KX/|u$3&2:ܸx<6k63FqD}[k[ú7$&oVI~5 :zC$W u#9a!ʢu֩yy0ZHl kkL}e7iP-;譼)^x #5y9%̮Ѣ'p+&/X1ڙMkp y+kNڴUC't7h܋9k͵n?k|%xio,}2m޽PLT$=A_5>yNo R.*YgcO#[`a#Wʺ}y4g="2>knp>~GS}?Oό4duXm{#? Me81\ѷkIw't~IzUD0xʃ*ڰs9#0??O Ҥ j CHF;ʭK EUzӑa?PIy?JJXb= ,Dt'Vj,x( N 9dC𕺓< teיKq-^&PG?i9( Q<_xCP3#C .' Sׯo'e D9 ^ʿƟ [:١ی>Lvb.&F#~aF%vԭs~2xm/Lҭ"٘-w|Ucu dה|[Vn HcF` /[ 7Eh:f^w Tc'Np]9)}/ᶧu>]t?Zs:5LZNNpܚ7Xԭ,L"R9ԣ`AwV/˭ts>[k}=揮Y7}C2-u8 |:HNш l{ܤc zgU` \:=)7JzRӊlxSK?M$lOJRqLh^,zPI*F]IZW գ;ՒSzßi,VZ&=cٻoUW6ar3\p-d t\wQ^yS9 L--vV{YX$1,0@ 9 [Ls[Gp+u$iJp2wu縯C'n^9X *R\ft@\Ҫ4E lei72";I=%crظWb636_`)ՒMܙhlP2sj<[w}5(W擖8i eSH87 O9asѬ|K){np^ Rzll^nmuK[kvdCr?yأqtRfulYtm q_>}|`bC50F^Ǟ|Cm4:+` F?:e[K-#QX[gy+|Mᡡ2"ī, pZDpeݚyJ*r:ETnac攳6C$sf'Bk^N2 ޹#uHN QZEB5x Ǣ\OU53?½LJs/fcJaOYVkN"fM zRG^Xm`Hqӭz!T]NFXJZq${zN\J7Z ֐֢O ^,&ڇ)^?rWdcEzg쮃{)_tSBg^Oy4QY#D˸w8p?Z_.%Ώ'@ˁkbʷHaFs<5.|SZ߼SZ|_t>9| S#_hڌ:E ~$|#Ewu,V6 q''Y9>:WVLmO^+Նqy-\K~ A} ~joi>/ѴIn58n vOKucJ.ηZx,R~>kAh*u"r${ZINB'$"R~W?ߘi-*Q晇XǧfZj^)9#bPO#s=5m1C"8#QcXƽ}kVQQ*]áͱ, :ۣ~f=¸Q4ieZ7' %9,~#A|UYb:`.~T>r{ ?l2@Hd#+8ۛ=G-\Gs4\lX?q`{Sm@:cE.}܄;j,\8"C$=u}N}^AmRE ͟ʵ3~"|ckFɆ12g%*ozMF[¯At*-Qݹ=G_kkSuj6|yg|uOs_LQ]kK-Krcn?Ȭ}'bBV4%6q^+ԍKØ淽cOxG]N:y]g} EnQX[ʒFr1\M JROJ:-im8} { ٵ?6?]xY4DE~Mi"iqXPl$r:_b/5[˹&yIwfVEzGCĺSX]I7/Lkٯa#+!496@x#[AbI^,bȨ2:gF*rq֢nh:~؝4Q*9h?(G㪏]>+#4Ζb(Мːs^zAzcgadI' 5z*S7~mVqڽ&k?^0Pqu,gD,W`y!,%p3 i]DР-\1rq T+=Ӛ6qaqumKzb~ 4 ih\\hO0$0 w~ d i|[)ۧ5? lZ]I=%+ Pm*G] O:$d~Kä,"K7\yw.`3*5-V>oΟAK|K,"gH7M͇9%W5jjJ(rz+ϒǡs_^?qo-&[:YoI0 FT\zǨ,,:E/*H~o^_GD0l.}3D+;?0+&r8ڹEJrJ÷S+-;Hd@f!LDnU+=%Yٙ {@JjWPe± ?"*c}Km)&iH1\rE]iy9[cnV\WE+ -l=U-+H{PAEf^{]&HY'.":3$i ݱ][{ކ<Ŧks,=ȇ2LGrGdE.I5>å <1[ګ?\V>[5ݳ椷I";_nZm`o5o jei'E%::I43™.cKNnaoa(%6_u=?_{ F״-ȱݷq~Χa_߶&qfGTGތ8a*4[ЉYnZ)W\c?O_¨oYֽ:6O=2DB?^3 )NGZ¦]2m&.H dRr+?e39pJν?R6#%@߶xP<;T `/k٧+Ps>jt۟۴"KcSI\_3 (]g'ykKF`<&|!WM~u3MGQ0A$3;! $jm9 "2eE"r$lv8'۞Շ&mbHI צ|#ƱCǫ٥e2+n]sY9x`n'O1{xN P[f>MVT6xPþ(g2ɨ49hN1ڱ~gI3$c2ycMƝJe?t{WIӵ;ЗQ6ֲYc\uEG^*kx^$x|a>SS뢓[sfʒF$]R}v+ '5OK^pnmN`|d?:W5|+9$߀mw%bBpkҴ E#S<lbP|ɉ3נkV Nr1?_}o_YݝR k0|ExĿ#H-8ͫDOAu'4bRpi@G$c֝\4=*:x5 G?o>?C(a@?h?( 1s^qܽyMp-`fXQzW|u` ;\wKʪ]作Ws~׼wchu4P .7=ױڷf[=F^L>T>jVBB8k;Tox m"[R{YpFGjoG6iGk=[@C$[r *n<%ZJ\dk;u$f ۊפ=kkgM.Ao\B䎇y?/-&KuLgkA&0e/K eڅB}I)b%Ȭu|@.RK۩T;zEt:Ύj0m9ᛡ>v+mz5i9.M#1)Yh븡׳w}Nm,MNjLJ, Fٓbp A'm5G{n !}`*ċ-NKgɠ gL#h!?)ԟ(]a\?hSy%jsٻ=zdFdhY!f]$/u[{5Cs&1rpxךx%ξvk%,1d9.2v[[Rf#iNro޾o5 Y55ki sq+c'zWzrK>Yy<׬|7[JxR];--#8 :sBn?vI<7zd֋kj8BIwYZ.imkl-mY"('$W! hlYò^Z/\緾}QN JwDMY%O)<WMi Ozc岻.QZq]#YE i묋^歯>]6Zyhތ j|DY^"JG%b(e_vtj/xrG$u{FщJO aʶ§$nmoRzKpY377בv+⾼z1-,W8+ԙ|=p<%x*?5r-Ǜx}ma7VYmo$n$q#\HEƛt (@=Q+~3 |ED j7hk8b;tOq9sJg?V:'T* >S'ӡ >4@tdUʎ y޿aK٬QB8-ֽZkTPe`T2x[U$59F?)?nKٵE-H P;8}~x~ C[+ Wz;Zc5ս|6=mxYtU7z׏Q5Wz֚dyV4a8 kOY_m"}g4,$H[x#Hdʔ@6o{ҹ\by1}KW}7lv*w8޻aDM<0v Keip\ֆFN'.nh׎*=+mFS׭-[KLe2[y|Q{ 8,=0Ό<;d= yPu|Et -3z j\}^ٯzT|CqoLfvFV6^w56W sUb}= }aQ\Ũ[D@t3Om'm|9GcQ-AokK?gqYSo_F|xt:0][tbEA(5JQR:P} vГ;ZOCo>; )`^TW؞9u85=xv8k+_Q;̻l{>R\nt^yW>YDl@o-YĽ=/ n$' cjLO+ߎ?W|]c[xb|;1Υa$:B#$Ϟ>46v)IEosUt/iD@H;GϏcs~fS3@]2Ky`-)r;Iִɼ N^K]N-jPNSȑBƁU:WhLěIZ,@SZȓAH=NKc^[_$x[%[6wH̖DoHb/Ꭻ!xൺwk*g$}kw[6~!1~PѺUt<0<n=_ge/lże̿4Vߴ# <3a8;]~Eut"$9u;3۫FPYy,>jظ{)u4{3!tj8%bq J;;8[>xH4mbSމXᛞ\mUsBoV;-ƛds u;3]G[/SCqކy\iyQBh6lS&6lO1"xw/Ӯ&;?F屺BcxRRAb9MB2I~oB>c\wD%/ ܆u${wJu4K ["I8 ׾1] <]iMkz̪F)|]&͛6InWe69O?ݭ\i&q=ŌHڬm>.2wʎPPoOs̬X{K,Jg*\&[RR[$ngizsNqbce2{ӱІb̘! uW]N'vbMI kzEgKZi#mp$}*vkDznPյdEn1Ǿy%S=odHϗl:n=kcj&]-ױg!W|I`2W^]ǔS#'j~ B HBi3Ց>&i7,|SrGH>)Z-(mTEvD+rw~HK~b}ϚS3^/aH´bFB"*+⻜tݫ\_[ܭΙo'I9Q# w>4k[ ~IF*Dhm,Ku]./* &VtB^mO NBwa9+R9G?ڿ_u[;[XlOO$z"`T؅(lч' / Pj:I.-k#(*AN;rgoi I"&紃WE7)ipg"W̩o l Dے&QKgxȅd0K x ?@|772ݪM >ZH m/# k¯gdVl"kt45x[haDQSz  bP'W,s ľM#4ʰ3',׶f:σ5VmJdFQ6϶%8MTy>|3 ݭ;]9#W`uAT?o7P[i{E%;)22+)Ȥ]un ԯ^Z@w Gqּux_Z>q=ͮ|K;@@u9?ޯ)LN1`ݲBEŬMst0 I;𾢚OiZV,s Vmk楫 au}p-ŹTMo#4,Z~ça]%TJydtcSykgJrbtiVLH+z2RhWk "=Tżv(Ue^ 3O_YrkQ9 P~Ww~.ӴugY՜e!9nP]mzikR0?R1_XBِe~o1# ȣT0˹4L[$u ^˙RH]3օ ZA6Č$j01|={럴[ʩF5o[ud0] Wlvя=>ZYXa]-|1 +nךqОMfPL}/3)bRz֮h2[?CҨC$ه8 AVmyj?xF^jge6/c;xSJ⢝5߳?Uҿ*)Q5/)z;&OhHϸckoZ#4"F0|?7w7<3?$֊l5⠾(˜Ԋm?kEtFmq+K+--A\Nj|Q\Ǚ|[խ7SًȞmjG@V/ Wk`#B*ɂ:+Z I{92 p/,P@gE]EYI7=Oㄲ|gi#[)L gRQ"E,%Xà~M}`MDyܷ ŷ* GybePbI.]׹_fx.~aXlf՗i I\<v_($v<=YE{\Vf%mmM+KhRf<(c=*99Vy[YY& ++o:͞E>EE!UHۅP9_¼o\EC[ZN1Hd`)"?쌟jsLڥĶ׶$<#kfCqBʹD|ʼ#BAbEW76&gXE zk dy DYO=?>T$FXjf2 !=1wڄ* VN&zH󬥡 Zeڜj͓[z.":.,0otq<r3PR^\ZȠI03WL0ǣdpO6aX6qQL~؝4U~:~Ӳ|_Ip@.eఽMU{76t⹽n_Y1'd_lҡd%.} v書Uti$?ܐ~=kӮ4.sn5#ʷ+fSGnln uXݚvىp#n0OzD[gE =dh,KTu ߾nOJӒH 4&:VA\g1sCr*2@?~Ϗe#լ5ڞ:AFܜ?2מ[HsWkXiV:3' gqG;cyXRnzT-{y:5Mg1[uEćKn7gk~ Fn&IӧYBbR4~ &]VqA'vKZH,d[ĺ 徛i#,vL*<]JIFO <ÚjݻT{W\Y,af+{oFA_\ֲ0f{K)Cv+Xedi4C(=j֛Ʊ<6ѼsYa[Ha& ,zVnh :)VsϦ:<ѥ[%ג.3ꟲBKJtҤd-!502W׭~QX"e%F9 d?N 7o$G!}C/axY,81_Tat([8<jo]fKtSQEW (&9rDʄW<Re@:[>'++IuK!cC.zm U7ž k7:bE52oS'rZg.xٮCYEKX\Pr٨%WOUG[xt۝sڳ,][Zt 4oT #yAQ`y|-ܮLh3@z+!{L&s1qϥuZQ]X3߅:jVmw ̊ RkvWZE-KmT' ֹ/aդW͗PֺZBgȋw*}zt;s9-4]Ruo90*Ў';ȹmZN"plMy 'kkصLA@~VR:_K3_mqpYCgn|+T-:%8-O'ڬ{H_bsw§iw߿1ڻ j>p/#:m\~pAϏ F3 .SbW$A],}O\:KbiOr[G[ X3(w^[KJK$wmp-xscwW:dPHK&BY=5h\[h U13l99,i)4z]o6c$\ݛff;cg󬯈:dRiRy6OT;W3FG(ɵ3u#źCyl&Rszt";Y'>8yzUuBDu Kˀ\ǃ]V7)Lu֚l|eY;`}!;#\uI7>~#:/*Y]մ02f&XxƯ_ >i0о"2ޥtO6#:=N1|Sh|%+k 8'_g xW/V ,"d {|3m hǜ)@י|Y98 5?mF]h@ЊtSJ>=$ii]IlsEcx:Z^ [뫘lxPUֽK~ u{ FDS[ݾ bd)# {BTm~؝4Q+~˿N)[Gў<`9 I6! Kk="VO&0ygqL'’hio.?SEyL^0Դ'SSOgoq'r~s'!Ӈ>;o xn#Ed%pyZ~b~%Ce地:*}a'{Rv <{m $MP7%fz>R5;Fe6miM3?y"ۋt;r})> (`L' +Dw6l\a ==Z +lZt{8-[q1kՌ6#Jg @P}35YcUuֺ\˛mH_EmjɂK{ۅ?Fr3蚕q\WHy3+Y,dBG>~wݳ)t7&܉P&;a WPFHf;#YO&K~|5 ޾O-ō/tFGujw&8fVڣIݣ2yR*7>0O\veyc$`I- MMWzZ4Ji#[ybqrHbC7l!# ͐*͚OJKaaPQyp2;޼ ֊%f#F)O%$yertsIJ8]XS7g9w4v9#nq@V_-wƖZU4råDvܖeCּT$G[+ 6WS^̒;s)&PTw=Swf5?U5ym`Lr7¾%k{āDLN2}*ƪcmɅqDeU.n^_amy"23ۭfnBglc߽z4"MqZJ%Oڋ(%Zo'%xѳ"1ʣ?dj?UqפPB#Yk.W2Xp1q#-Ʊ_ta;i./bKu8Rr6k*oEQW#=Qֿ=4MkPc]{:Bͪ\o-p@or:~Psۡi+Ě~2,2r6^zWj˫o]F̒BZ% Vt˛KKZJI`e*.cRs|=IݬMl.F ; Xou$! :e+a:&_Ni[Z7~O[4C=>ޭe_ҬIpd=k56X.MVL.:_!K⸴+/ehwKnpsۭk|7[}SWi5]$g3#2GZkT|8'xUoyXj [Yّ\1^i6w\Lһ*F,yWxq [lCdSe۶O*-uiT=\’:$&?k(ϚoR*F]}@I,#p©8 𶗥(&{au_b=+n'D#ԶnVc,(lUAj0 +E7}ub5X{]imm$5 ݌r+T+qksg(W7k(Hn՗pUa9ڼg$u]b^hlKm,+|djӵ=.[[bbma0.&Tdݑ22ydIV`t^/O<nBes^KkuK (ɒq 7WM[+|`J9\Lأ!z9*Oi70o#E>좒ۻۚ7?"z?ʯ$z\xYB4q"|֣9j;YoAKztWMĻ_=pՈ|k>i.ƍ 5<-b7zb ag-Vk%̫%+}Oֹ 5\k2UK}t=᎒-6&0ݎO9^c-efQ"'AK F]:t_~DC^ nAK|V%d2=9nFFUO# 0*\0pDKɿ vSxݤ6Z|0J#sn.jeޒ] Mg'F7 Gc>Ƒ)hYgpȏ(X6 W9s۠#209 VeКn46`8^%ħۂOQup!D } ء`1GHה4VGoz_`}._5\^Xt`я½$f%%ZU7RC\SHnz/׬$w:⛁}c{m][e(XOOlyП[5#\2r\~ "#Ջ$gx.jwیbYﱔu,F򘤋Zb?Sѣ[kUXf2a>v.?(}c VFcmSU$Gmw]OڰH̸5%\'O[hoֶ/j5o29yJ5LG#eQk2^M*soqԒ*U^;Q_9 ݦލ+֠oʭq{{8RGmXwlK)mLSз\*2kem'x*햫vU6'V$2|OG4&!pW3SrǗc ꬉ !nSIxѳ(e7δ|SYM`EQKx:< ).^g{TyRZMn7̷yjq9n9Wf$?V?Ҙ2?݅erĥygqul xfOXefGnidR>MSS2LV)$:n*邓眢i3?Tt& F?OqO={OqXj2"-vZ';дuMYԲ  MHno-`+gkLzX ea8oOhZ2[VZϙuxDo+H0V?T~? 0agѝ׀ILNt]oړ&7i㱝)ޟXMKKxTf^N3=]|1K{Asa=#`y++s|U~:+Gهxp %m{}n.Hӟzxh9ӳ(|aeτu* /E4C*&XmY#I3֮KP쟸XSAc= [95@iګ/TMai85n}1d`ߗp+*u'.gdʦަRhfOnQ&%"o]:{f[O4;&& \Ÿs Qr3 GU.D!x0_CEks7OcU7.,Qt 4^rrյ}"m>htas fP~jo4wLז]͝un[W?b[H%T$|@Ps2^^7T$P__Et^?ukʪԯZ79v\DW| #>#ی?rkv #OozLJߺSc o+В2E6n-*KqxC*ZI(ݵAWOG0ꔹyL>5PH$rY7,^j u5++0hʙXq˚uc­xD?Àkogi.MNMcRgnX>%mƿ$R1?Vǻ5pbTq&ydmmKL-I?*!)FW B3ܧq+%M46MG\CYLNrzmոu]2^xtW9o 6]\{hcSԻb*bA6dOȘŞ*|iӠ}?D*wx]WOMs \L9#WW|%?kQ.ʜ7|o<)$Ъ,xTX#Styb& d84'ZP'*žյde0il̻`~]%rڅkqݾ-KkͼM⛫(.XV2DF? m]FRUcoVJj9>A#E"_'*8QU$lşS0>)h~I`0x\#=ߍGqsΊc!hc75%հg[O-3o\m#۞Z{CTl#SJ)'uy<;Sj9-负%̋]֡$+cuKeEŻAJL|庍0+ɥGv&H4$rD?/No"n eRDh~S%U$W ڃ.>VkrtT/"]Oo$7*ω|Wm?G &tpAvJN/ |mwSj4?Xп&]_kڌ j m W +i>+Zi6zӿKF#yh2e0ʸXWcğFiS"Ed}AT[ }kQlvm4) -n1\mRg%.mq$vE)GtsyPD_~`yQNEQmc棟 (|+rMaY?\|Ē 0;9V=O0ZؽNkn 42n_C*!n5&{)~-ZDs?GNVp29b<T KIo-#H|vu7ZmQmhaCAzCC8ϡͧS*xchoGnIYkc b)mљcvm{UWT+ۘ7cĝw8iK*P:fO(S$V-XʱMIi{r85aj-ϯS{ۂFǞ~_֡UXic3ǻݚ /LNR޻IݥCR-c?j}kOլ~uڞ;C~b4w4gL,52^g_t?GD9ҺT9KĞ"R^jM"vbNt> >X,i~k2~x["^Jҏ>Y- c{Ze-!pl:B ?6-chq"UX|+`JI$?.Le-_#'*iRSK쟛2HUgZ5#iFVK6+{{hKd>P4,^ycheM 7g6Lâ>}s]bNn_4_ŗZFx2ˎ7VA ]8A~`ǕHݾْ},t2I }MXݽ϶f~ y/Z+?Oÿ] E]O7! P~g 'cfotoxx-20.08/images/zoom_menu.png000066400000000000000000000035101362435004500170360ustar00rootroot00000000000000PNG  IHDR0-J`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }Zǀ1iTXtXML:com.adobe.xmp 128 128 0 ` IDAThc|H:FL 1Qb.G.\zY@}63[=.IիPAn{~ ?L7o=$OQe?1##}0j=shC>F=%Uh4X,1n aN!ׯ)6ceS1$Hڵ-,''u:${[Y@&2́d:I <@M4҈AImkS€IiAfz 3[xӦڿo` g?YX,&N3*?r8^⍿\\?~a(/TaC'P9?d߾}66^ˀJ̟?{idxrI54>*/P LL_6 ɈDf `d2L8_@@w`. UUWw/ [Չ/\8Sm yɠtyFF I}| LB،Ca a|bhC>|WT 2012-2017 # msgid "" msgstr "" "Project-Id-Version: home 2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-12-25 22:15+0100\n" "PO-Revision-Date: 2019-12-23 22:43+0200\n" "Last-Translator: Josep Antoni Miralles Puignau \n" "Language-Team: Català, Valencià <>\n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" #: f.albums.cc:59 #, c-format msgid "max. album size exceeded: %d" msgstr "l'àlbum ha superat la misda máxima: %d" #: f.albums.cc:70 msgid "" "Right-click album thumbnail for edits: \n" " - add selected files at this position \n" " - remove this file from the album" msgstr "" "Clic dret aper a la miniatura de l'àlbum per edicions: \n" "- Afegir arxius seleccionats en aquesta posició \n" "- suprimir aquest arxiu de l'àlbum" #: f.albums.cc:73 msgid "Arrange files with thumbnail drag and drop" msgstr "Organitzar arxius amb miniatura arrossegar i deixar anar" #: f.albums.cc:102 f.widgets.cc:458 msgid "Manage Albums" msgstr "Gestionar àlbums" #: f.albums.cc:113 f.albums.cc:232 msgid "Create or replace an album" msgstr "Crear o substituir un àlbum" #: f.albums.cc:115 f.albums.cc:381 msgid "Rename an album" msgstr "Canviar el nom d'un àlbum" #: f.albums.cc:117 f.albums.cc:451 msgid "Delete an album" msgstr "Esborrar un àlbum" #: f.albums.cc:119 msgid "Files to add to an album" msgstr "arxius per afegir a un àlbum" #: f.albums.cc:183 #, c-format msgid "%d files added to album %s" msgstr "%d arxius afegits a l 'àlbum %s" #: f.albums.cc:213 #, c-format msgid "max. album count exceeded: %d" msgstr "S'ha excedit el màxim compte d'àlbums:%d" #: f.albums.cc:234 f.albums.cc:270 f.albums.cc:1537 msgid "Album Name" msgstr "Nom de l'àlbum" #: f.albums.cc:240 msgid "make an initially empty album" msgstr "crear un àlbun buit" #: f.albums.cc:241 msgid "fill from pre-selected files" msgstr "omplir des dels arxius preseleccionats" #: f.albums.cc:242 msgid "fill from current gallery" msgstr "emplenar des de la galeria actual" #: f.albums.cc:243 msgid "select initial files" msgstr "seleccionar arxius inicials" #: f.albums.cc:284 msgid "enter an album name" msgstr "entrar un nom d'àlbum" #: f.albums.cc:292 f.albums.cc:1569 #, c-format msgid "replace album %s ?" msgstr "reemplaçar l'àlbum %s?" #: f.albums.cc:312 msgid "Use [Select] to add files to an empty album" msgstr "Utilitzar [Seleccionar] per afegir arxius a un àlbum buit" #: f.albums.cc:326 msgid "new album created" msgstr "creat nou àlbum" #: f.albums.cc:342 f.meta.cc:7832 msgid "gallery is empty" msgstr "la galeria està buida" #: f.albums.cc:396 msgid "enter new album name" msgstr "introduïr nou nom d'àlbum" #: f.albums.cc:403 #, c-format msgid "invalid file name: %s" msgstr "nom d'arxiu no vàlid: %s" #: f.albums.cc:424 #, c-format msgid "album already exists: %s" msgstr "L'àlbum ja existeix: %s" #: f.albums.cc:435 #, c-format msgid "" "%s \n" " renamed: %s" msgstr "" "%s \n" " re-anomenat: %s" #: f.albums.cc:464 #, c-format msgid "delete %s ?" msgstr "esborrar %s ?" #: f.albums.cc:571 f.albums.cc:584 #, c-format msgid "%s removed with no replacement \n" msgstr "%s eliminat sense substitució \n" #: f.albums.cc:589 #, c-format msgid "%s replaced by ...%s \n" msgstr "%s substituït per ...%s \n" #: f.albums.cc:664 msgid "no selected files" msgstr "no hi ha arxius seleccionats" #: f.albums.cc:877 msgid "Replace Album File" msgstr "Substituir àlbum" #: f.albums.cc:879 f.albums.cc:1004 msgid "Choose Albums" msgstr "Escollir àlbums" #: f.albums.cc:883 msgid "old file" msgstr "arxiu antic" #: f.albums.cc:886 msgid "new file" msgstr "nou arxiu" #: f.albums.cc:889 msgid "replace old" msgstr "substituir antic" #: f.albums.cc:890 msgid "add after old" msgstr "afegir desprès de l'antic" #: f.albums.cc:936 msgid "no albums chosen" msgstr "No heu escollit àlbums" #: f.albums.cc:1117 f.widgets.cc:460 msgid "Album Mass Update" msgstr "Actualització massiva de l'àlbum" #: f.albums.cc:1125 msgid "Process all album files:" msgstr "Processa tots els arxius de l'àlbum:" #: f.albums.cc:1128 msgid "Replace all with newest version only" msgstr "Reemplaçar tot amb la versió més recent" #: f.albums.cc:1130 msgid "Replace all versions with newest version" msgstr "Substituïu totes les versions amb la versió més recent" #: f.albums.cc:1132 msgid "Add newest version to existing versions" msgstr "Afegir la versió més recent a les versions existents" #: f.albums.cc:1134 msgid "Replace all with original and all versions" msgstr "Reemplaça totes les versions originals i totes les versions" #: f.albums.cc:1136 msgid "Replace all with original + newest version" msgstr "Substituir tot per la versió original + més recent" #: f.albums.cc:1140 msgid "Process album files matching selected files:" msgstr "" "Processar arxius d’àlbums que coincideixin amb els arxius seleccionats:" #: f.albums.cc:1143 msgid "Replace all with selected versions" msgstr "Substituir tot amb les versions seleccionades" #: f.albums.cc:1145 msgid "Replace all versions with selected versions" msgstr "Substituir totes les versions amb les versions seleccionades" #: f.albums.cc:1147 msgid "Add selected versions to existing versions" msgstr "Afegir versions seleccionades a les versions existents" #: f.albums.cc:1149 msgid "Replace all with original + selected versions" msgstr "Reemplaçar tot per versions originals + seleccionades" #: f.albums.cc:1237 msgid "Select Albums" msgstr "Seleccionar àlbums" #: f.albums.cc:1296 #, c-format msgid "too many selected files: %d" msgstr "massa arxius seleccionats: %d" #: f.albums.cc:1535 msgid "Save Gallery as Album" msgstr "Desar la galeria com a àlbum" #: f.albums.cc:1739 msgid "instant" msgstr "instantània" #: f.albums.cc:1740 msgid "fade-in" msgstr "fusió endins" #: f.albums.cc:1741 msgid "roll-right" msgstr "enrotllar a la dreta" #: f.albums.cc:1742 msgid "roll-down" msgstr "enrotllar abaix" #: f.albums.cc:1743 msgid "venetian" msgstr "persiana" #: f.albums.cc:1744 msgid "grate" msgstr "enreixat" #: f.albums.cc:1745 msgid "rectangle" msgstr "rectangle" #: f.albums.cc:1746 msgid "implode" msgstr "implosionar" #: f.albums.cc:1747 msgid "explode" msgstr "explotar" #: f.albums.cc:1748 msgid "radar" msgstr "radar" #: f.albums.cc:1749 msgid "Japan-fan" msgstr "Ventall japonés" #: f.albums.cc:1750 msgid "spiral" msgstr "espiral" #: f.albums.cc:1751 f.area.cc:140 msgid "ellipse" msgstr "elipsi" #: f.albums.cc:1752 msgid "raindrops" msgstr "gotes de pluja" #: f.albums.cc:1753 msgid "doubledoor" msgstr "doble porta" #: f.albums.cc:1754 msgid "rotate" msgstr "girar" #: f.albums.cc:1755 msgid "fallover" msgstr "cap abaix" #: f.albums.cc:1756 msgid "spheroid" msgstr "esferoide" #: f.albums.cc:1757 msgid "turn-page" msgstr "girar pàgina" #: f.albums.cc:1758 msgid "french-door" msgstr "porta batents" #: f.albums.cc:1759 msgid "turn-cube" msgstr "cub giratori" #: f.albums.cc:1760 msgid "windmill" msgstr "remolí" #: f.albums.cc:1761 msgid "pixelize" msgstr "pixelitzar" #: f.albums.cc:1762 msgid "twist" msgstr "girar" #: f.albums.cc:1763 msgid "Xopen" msgstr "apertura en X" #: f.albums.cc:1764 msgid "squish" msgstr "Expansió" #: f.albums.cc:1765 msgid "disintegrate" msgstr "desintegrar" #: f.albums.cc:1766 msgid "interleave" msgstr "intercalar" #: f.albums.cc:1797 f.widgets.cc:462 msgid "Slide Show" msgstr "Diaporama" #: f.albums.cc:1800 msgid "Select Album" msgstr "Seleccionar Àlbum" #: f.albums.cc:1805 msgid "Caption Time" msgstr "Hora del títol" #: f.albums.cc:1808 msgid "Image Time" msgstr "Hora de la imatge" #: f.albums.cc:1811 msgid "Clip Limit %" msgstr "% límit de tall" #: f.albums.cc:1815 msgid "Music File" msgstr "Arxiu de Música" #: f.albums.cc:1820 msgid "Full Screen" msgstr "A tota pantalla" #: f.albums.cc:1821 msgid "Auto-replay" msgstr "Auto reiniciar" #: f.albums.cc:1824 msgid "Customize:" msgstr "Personalitzar" #: f.albums.cc:1825 msgid "transitions" msgstr "transicions" #: f.albums.cc:1826 msgid "image files" msgstr "archivos de imagen" #: f.albums.cc:1827 msgid "KB controls" msgstr "Controls de tecles dreceres" #: f.albums.cc:1841 f.albums.cc:1908 f.albums.cc:2141 f.albums.cc:2450 #: f.albums.cc:2786 f.albums.cc:2803 f.albums.cc:3039 msgid "invalid album" msgstr "àlbum invàlid" #: f.albums.cc:1936 msgid "open album" msgstr "obrir àlbum" #: f.albums.cc:1948 msgid "Select music file" msgstr "Seleccioneu arxiu de música" #: f.albums.cc:1983 #, c-format msgid "%d images" msgstr "%d imatges" #: f.albums.cc:2008 msgid "" "arrow keys show previous or next image instantly \n" "space bar (blank) is allowed and shows as '-'" msgstr "" "tecles de fletxa mostren instantàniament la imatge prèvia o la següent \n" "és permesa la barra d'espai i es mostra com a '-'" #: f.albums.cc:2027 msgid "Keyboard Preferences" msgstr "Preferències de teclat" #: f.albums.cc:2030 msgid "blank or unblank window" msgstr "pantalla buïda o plena" #: f.albums.cc:2033 msgid "show next image, with transition" msgstr "mostrar la següent imatge, amb transició" #: f.albums.cc:2036 msgid "pause or resume slide show" msgstr "pausar o reiniciar diaporama" #: f.albums.cc:2039 msgid "magnify image (loupe tool)" msgstr "aumenta imatga (eina de lupa)" #: f.albums.cc:2145 msgid "Transition Preferences" msgstr "Preferències de transició" #: f.albums.cc:2147 msgid "Transitions File" msgstr "Arxiu de transicions" #: f.albums.cc:2156 msgid "random" msgstr "aleatori" #: f.albums.cc:2159 f.albums.cc:2175 f.albums.cc:2179 msgid "time" msgstr "hora" #: f.albums.cc:2161 msgid "set all" msgstr "establir tot" #: f.albums.cc:2173 f.albums.cc:2177 msgid "transition" msgstr "transició" #: f.albums.cc:2174 f.albums.cc:2178 msgid "use" msgstr "utilitzar" #: f.albums.cc:2176 f.albums.cc:2180 msgid "pref" msgstr "pref." #: f.albums.cc:2280 f.albums.cc:2322 msgid "invalid file" msgstr "Arxiu no vàlid" #: f.albums.cc:2394 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "error de format d'arxiu: \n" " %s" #: f.albums.cc:2456 msgid "Image Preferences" msgstr "Preferències d'imatge" #: f.albums.cc:2460 f.file.cc:1610 f.file.cc:1995 msgid "Image File:" msgstr "Imatge:" #: f.albums.cc:2464 msgid "Action" msgstr "Acció" #: f.albums.cc:2476 msgid "Play tone when image shows" msgstr "Reproduïr música mentre es veu la imatge" #: f.albums.cc:2481 msgid "Wait before filename/caption/comments" msgstr "Esperar abans del nom/subtítol/ comentaris" #: f.albums.cc:2485 msgid "Show image file name (overlap)" msgstr "Mostrar el nom del arxiu de la imatge (superposició)" #: f.albums.cc:2489 msgid "Show image caption (overlap)" msgstr "Mostra el nom del arxiu de la imatge (superposició)" #: f.albums.cc:2493 msgid "Show image comments (overlap)" msgstr "Mostrar el nom del arxiu de la imatge (superposició)" #: f.albums.cc:2497 msgid "Wait before zoom" msgstr "Espera abans del zoom" #: f.albums.cc:2501 f.effects.cc:4023 msgid "Zoom" msgstr "Zoom" #: f.albums.cc:2505 msgid "zoom-in" msgstr "aproximar" #: f.albums.cc:2506 msgid "zoom-out" msgstr "allunyar" #: f.albums.cc:2510 msgid "Zoom Center" msgstr "Centrar zoom" #: f.albums.cc:2515 msgid "Wait after zoom" msgstr "Espera desprès del zoom" #: f.albums.cc:2521 msgid "Transition to next image" msgstr "Transiciò a la pròxima imatge" #: f.albums.cc:2524 f.albums.cc:2654 msgid "next" msgstr "següent" #: f.albums.cc:2644 msgid "click on thumbnail to set zoom center" msgstr "clic en la miniatura per situar el centre del zoom" #: f.area.cc:93 msgid "Select Area for Edits" msgstr "Seleccionar àrea per editar" #: f.area.cc:94 f.area.cc:1890 f.edit.cc:8198 msgid "Press F1 for help" msgstr "Prèmer F1 per ajuda" #: f.area.cc:104 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Seleccionar àrea no suportat \n" "Per aquesta funció d'edició" #: f.area.cc:139 msgid "select rectangle" msgstr "seleccionar rectangle" #: f.area.cc:143 msgid "freehand draw" msgstr "dibuixar a mà alçada" #: f.area.cc:144 msgid "follow edge" msgstr "seguir contorn" #: f.area.cc:147 msgid "draw/replace" msgstr "dibuixar/substituïr" #: f.area.cc:150 msgid "Line Color:" msgstr "Color de línia" #: f.area.cc:164 msgid "match level %" msgstr "nivell de coincidència %" #: f.area.cc:168 msgid "search range" msgstr "rang de cerca" #: f.area.cc:172 msgid "select area in mouse" msgstr "seleccionar l'àrea al ratolí" #: f.area.cc:175 msgid "select one color in mouse:" msgstr "seleccionar color amb el ratolí:" #: f.area.cc:179 msgid "select all colors in mouse (flood)" msgstr "seleccionar tots els colors al ratolí (flux)" #: f.area.cc:184 msgid "Area Edge Blend Width" msgstr "Ample de barreja dels línits de l'àrea" #: f.area.cc:188 msgid "Edge Creep" msgstr "Límit progressiu" #: f.area.cc:200 msgid "drag mouse to select rectangular area" msgstr "arrossegar el ratolí per seleccionar una zona rectangular" #: f.area.cc:201 msgid "drag mouse to select circular or elliptical area" msgstr "arrossegar el ratolí per seleccionar una zona circular o el·líptica" #: f.area.cc:202 msgid "drag mouse to outline an area" msgstr "arrossegar el ratolí per perfilar una àrea" #: f.area.cc:203 msgid "drag mouse along an edge to follow the edge" msgstr "arrossegar el ratolí al llarg d'un cantó per seguir el cantó" #: f.area.cc:204 msgid "drag mouse near a line to move the line" msgstr "arrossegar el ratolí a prop d'una línia per moure la línia" #: f.area.cc:205 msgid "select line color" msgstr "seleccionar color de línia" #: f.area.cc:206 msgid "size of mouse selection circle" msgstr "mida del cercle seleccionat amb ratolí" #: f.area.cc:207 msgid "required match level to select by color" msgstr "nivell de coincidència requerit per seleccionar per color" #: f.area.cc:208 msgid "select by color search range" msgstr "seleccionar per rang de cerca de color" #: f.area.cc:209 msgid "select area within mouse circle" msgstr "sel·leccionar àrea interior del cursor" #: f.area.cc:210 msgid "" "first select the checkbox, then \n" "shift+click on image to set the color" msgstr "" "primer sel.leccioneu la casella, desprès \n" "majúsc+clic en la imatge per ajustar el color" #: f.area.cc:212 msgid "select surrounding areas matching colors in mouse" msgstr "" "seleccionar les zones circumdants que coincideixin amb els colors del ratolí" #: f.area.cc:213 f.area.cc:214 msgid "area edits fade away within edge distance" msgstr "les edicions d'àrea s'esvaeixen a les vores" #: f.area.cc:215 msgid "move area boundary in/out in 1-pixel steps" msgstr "moure el límit d'àrea dins/fora en passos d'un píxel" #: f.area.cc:216 msgid "map selected areas and verify" msgstr "mapar les àrees seleccionades i verificar-les" #: f.area.cc:217 msgid "invert area" msgstr "invertir area" #: f.area.cc:218 msgid "show area outlines" msgstr "mostrar els contorns de l'àrea" #: f.area.cc:219 msgid "hide area outlines" msgstr "amagar els contorns de l'àrea" #: f.area.cc:220 msgid "clear area selections" msgstr "netejar les seleccions d'àrea" #: f.area.cc:404 f.area.cc:570 #, c-format msgid "exceed %d edits" msgstr "excedit %d edicions" #: f.area.cc:1471 msgid "" "Fill selected areas with color for visual verification. \n" "Method 1: left-click in each selected area not already filled. \n" "Method 2: right-click OUTSIDE all selected areas. \n" "Press [help] button for clarification" msgstr "" "Omple les zones seleccionades amb color per a verificació visual. \n" "Mètode 1: clic amb el botó esquerre a cada àrea seleccionada que no s'ha " "omplert. \n" "Mètode 2: clic amb el botó dret a l'exterior a totes les àrees " "seleccionades. \n" "Premeu el botó [ajuda] per aclaracions" #: f.area.cc:1499 msgid "finish area" msgstr "acabar àrea" #: f.area.cc:1528 f.area.cc:1757 #, c-format msgid "found %d pixels" msgstr "trobats %d pixels" #: f.area.cc:1546 msgid "" "Method 1: \n" " Left-click inside an outlined area that is not already filled. \n" " Area will be filled with color for visible verification. \n" " Gaps in the outline will cause overflow outside the area. \n" " Repeat for each outlined area that is not already filled. \n" "Method 2: \n" " Right-click outside ALL outlined areas. \n" " All areas will be filled with color for visible verification. \n" " Gaps in an area outline will cause that area not to be filled. \n" "Gaps in an area outline: \n" " Gaps must be closed before proceeding with edits. \n" " The Find Gap function can be used for this." msgstr "" "Mètode 1: \n" " Clic amb el botó esquerre a l'interior d'una àrea delimitada que no està " "omplerta. \n" " L'àrea s'omplirà de color per a la verificació visual. \n" " Els espais en el contorn de l 'àrea provocaran desbordament fora de l' " "àrea. \n" " Repetiu per a cada àrea delimitada que no estigui omplerta. \n" "Mètode 2: \n" " Feu clic amb el botó dret a fora de totes les àrees delineades. \n" " Totes les àrees s'omplen de color per a la verificació visual. \n" " Les separacions en un esbós de l 'àrea faran que aquesta àrea no " "s'ompli. \n" "Espais en un esbós d'àrea: \n" " Les separacions s'han de tancar abans de continuar amb les " "modificacions. \n" " La funció Buscar separacions (Find Gap) es pot utilitzar per a això." #: f.area.cc:1889 f.widgets.cc:487 msgid "Select Hairy" msgstr "Seleccionar irregular" #: f.area.cc:1899 msgid "select the area first" msgstr "Primer seleccioneu l'àrea" #: f.area.cc:1975 msgid "select" msgstr "Seleccionar" #: f.area.cc:1981 msgid "deselect" msgstr "anul·lar la selecció" #: f.area.cc:2335 msgid "" "Click near any position on the area outline. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Clic en una posició en l'interior de l'àrea. \n" "Es trobaràn possibles separacions en el contorn. \n" "Prèmer F1 per ajuda." #: f.area.cc:2356 msgid "find outline gap" msgstr "trobar separació en contorn" #: f.area.cc:2451 msgid "cannot find area outline" msgstr "no es pot trobar contorn d'àrea" #: f.area.cc:3285 msgid "save area as a PNG file" msgstr "desar àrea com un arxiu PNG" #: f.area.cc:3407 msgid "position with mouse click/drag" msgstr "posició amb el clic del ratolí/arrossegament" #: f.area.cc:3467 msgid "Paste Image" msgstr "Enganxar imatge" #: f.area.cc:3472 msgid "resize" msgstr "redimensionar" #: f.combine.cc:174 f.combine.cc:828 f.combine.cc:1436 f.combine.cc:2066 #: f.combine.cc:2510 msgid "Select 2 to 9 files" msgstr "Seleccionar de 2 a 9 arxius" #: f.combine.cc:196 f.combine.cc:850 f.combine.cc:1458 f.combine.cc:2088 msgid "Images are not all the same size" msgstr "Les imatges no són totes de la mateixa mida" #: f.combine.cc:560 msgid "Adjust Image Contributions" msgstr "Ajustar contribucions de cada imatge" #: f.combine.cc:564 msgid "dark pixels" msgstr "ombres" #: f.combine.cc:566 msgid "light pixels" msgstr "llums" #: f.combine.cc:568 msgid "file:" msgstr "arxiu:" #: f.combine.cc:1036 msgid "Paint and Warp Image" msgstr "Pintar i deformar imatge" #: f.combine.cc:1044 f.edit.cc:6054 msgid "paint" msgstr "pintar" #: f.combine.cc:1045 msgid "warp" msgstr "deformar" #: f.combine.cc:1648 f.combine.cc:2594 msgid "Select and Paint Image" msgstr "Seleccionar i pintar imatge" #: f.combine.cc:1656 msgid "Transient Objects" msgstr "Objectes transitoris" #: f.combine.cc:2264 msgid "Adjust Pixel Composition" msgstr "Ajustar la composició de pixels" #: f.combine.cc:2266 msgid "use average" msgstr "usar la mitja" #: f.combine.cc:2267 msgid "use median" msgstr "usar la mediana" #: f.combine.cc:2269 msgid "omit low pixel" msgstr "omitir píxel baix" #: f.combine.cc:2270 msgid "omit high pixel" msgstr "omitir píxel alt" #: f.combine.cc:2532 f.combine.cc:2844 msgid "Images must be exactly the same size" msgstr "Les imatges han de ser exactament iguals" #: f.combine.cc:2605 msgid " Fill " msgstr "Omplir" #: f.combine.cc:2606 msgid "using selected image" msgstr "utilitzant la imatge seleccionada" #: f.combine.cc:2825 f.combine.cc:3095 msgid "Select 2 files" msgstr "Selecciona 2 arxius" #: f.combine.cc:2902 msgid "Split two Images" msgstr "Dividiu en dues imatges" #: f.combine.cc:2903 msgid "drag image boundary" msgstr "arrossegar el límit de la imatge" #: f.combine.cc:3092 msgid "Image Differences" msgstr "Diferències de imatges" #: f.combine.cc:3108 msgid "differences" msgstr "diferències" #: f.combine.cc:3110 msgid "X-align" msgstr "Aliniament en X" #: f.combine.cc:3113 msgid "Y-align" msgstr "Aliniament en Y" #: f.combine.cc:3179 msgid "select exactly 2 files" msgstr "seleccionar exactament 2 arxius" #: f.combine.cc:3408 f.combine.cc:4562 msgid "Select 2 to 4 files" msgstr "Seleccionar de 2 a 4 arxius" #: f.combine.cc:3483 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:3485 f.combine.cc:4639 msgid "no curve (scanned image)" msgstr "no hi ha corba (imatge escanejada)" #: f.combine.cc:3486 f.combine.cc:4640 msgid "Search for lens mm" msgstr "Cerca mm de la lent" #: f.combine.cc:3487 f.combine.cc:4641 msgid "Save lens mm → image EXIF" msgstr "Desar focal en EXIF" #: f.combine.cc:3547 f.combine.cc:4701 msgid "Pre-align Images" msgstr "Pre-alinear imatges" #: f.combine.cc:3551 f.combine.cc:4705 msgid "lens mm" msgstr "mm de la lent" #: f.combine.cc:3556 f.combine.cc:4710 msgid "no auto warp" msgstr "no auto correcció" #: f.combine.cc:3558 f.combine.cc:4712 msgid "manual align" msgstr "alineació manual" #: f.combine.cc:3560 f.combine.cc:4714 f.process.cc:1395 f.widgets.cc:504 #: f.widgets.cc:829 msgid "Resize" msgstr "Redimensionar" #: f.combine.cc:3561 f.combine.cc:4715 msgid "resize window" msgstr "redimensionar finestra" #: f.combine.cc:3569 f.combine.cc:4723 msgid "do not warp images during auto-alignment" msgstr "no deformar imatges mentre s'auto-alineen" #: f.combine.cc:3614 f.combine.cc:4768 msgid "use two images only" msgstr "utilitzar només dues imatges" #: f.combine.cc:3644 f.combine.cc:3848 f.combine.cc:4034 f.combine.cc:4796 #: f.combine.cc:5000 f.combine.cc:5186 msgid "Too little overlap, cannot align" msgstr "molt poc solpament, no es pot alinear" #: f.combine.cc:4112 f.combine.cc:5264 msgid "Match Brightness and Color" msgstr "Coincidència de brillantor i color" #: f.combine.cc:4158 msgid "Select image" msgstr "Sele ccionar imatge" #: f.combine.cc:4173 f.combine.cc:5325 msgid "auto color" msgstr "auto color" #: f.combine.cc:4174 f.combine.cc:5326 msgid "file color" msgstr "arxiu de color" #: f.combine.cc:4180 f.combine.cc:5332 msgid "mouse warp" msgstr "corregir amb el ratolí" #: f.combine.cc:4182 f.combine.cc:5334 msgid "flatten image" msgstr "aplanar imatge" #: f.combine.cc:4637 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:5596 msgid "pano tools (hugin) not installed" msgstr "Pano Tools (Hugin) no installat" #: f.combine.cc:5605 msgid "Select at least 2 files" msgstr "Seleccioneu almenys 2 arxius" #: f.edit.cc:106 msgid "" "drag middle to move \n" "drag corners to resize \n" "drag right edge to level" msgstr "" "arrossegar el mig per moure \n" "arrossegar les cantonades per canviar la mida \n" "arrossegar la vora dreta al nivell" #: f.edit.cc:209 f.widgets.cc:501 f.widgets.cc:828 msgid "Trim/Rotate" msgstr "Retallar/Girar" #: f.edit.cc:224 f.edit.cc:1483 msgid "ratio" msgstr "proporció" #: f.edit.cc:226 msgid "Lock Ratio" msgstr "Bloquejar proporció" #: f.edit.cc:229 msgid "Trim Size:" msgstr "Mida de retallat:" #: f.edit.cc:237 msgid "Set Ratio" msgstr "Establir la relació" #: f.edit.cc:238 f.widgets.cc:493 fotoxx.h:1371 msgid "Invert" msgstr "Invertir" #: f.edit.cc:239 msgid "Customize" msgstr "Personalitzar" #: f.edit.cc:248 msgid "Degrees:" msgstr "Graus" #: f.edit.cc:250 msgid "Auto Level" msgstr "Nivell automàtic" #: f.edit.cc:256 f.process.cc:186 f.process.cc:822 f.widgets.cc:502 #: f.widgets.cc:830 msgid "Upright" msgstr "Adressar" #: f.edit.cc:258 msgid "maximize trim box" msgstr "maximitzar caixa de retallada" #: f.edit.cc:259 msgid "trim transparent edges" msgstr "retallar contorns transparents" #: f.edit.cc:260 msgid "lock width/height ratio" msgstr "bloquejar proporció amplada/alçada" #: f.edit.cc:261 msgid "use EXIF data if available" msgstr "usar dades EXIF si estàn disponibles" #: f.edit.cc:603 f.edit.cc:1550 msgid "rotation unknown" msgstr "ratació desconeguda" #: f.edit.cc:1479 msgid "Trim Buttons" msgstr "Botons de retallar" #: f.edit.cc:1482 msgid "label" msgstr "etiqueta" #: f.edit.cc:1692 f.widgets.cc:503 f.widgets.cc:833 msgid "Retouch" msgstr "Retocar" #: f.edit.cc:1705 msgid "Auto black level" msgstr "Nivell negre automàtic" #: f.edit.cc:1708 f.edit.cc:1714 msgid "sample %" msgstr "mostra %" #: f.edit.cc:1711 msgid "Auto white balance" msgstr "Balanç de blancs automàtic" #: f.edit.cc:1717 msgid "Click gray spot for white balance" msgstr "Clic al punt gris per al balanç de blancs" #: f.edit.cc:1720 msgid "Click dark spot for black level" msgstr "Clic al punt fosc per al nivell negre" #: f.edit.cc:1723 msgid "Click for RGB distribution" msgstr "Feu clic per histograma RGB" #: f.edit.cc:1731 fotoxx.h:1326 msgid "Brightness" msgstr "Brillantor" #: f.edit.cc:1732 f.effects.cc:4049 fotoxx.h:1339 msgid "Contrast" msgstr "Contrast" #: f.edit.cc:1733 f.edit.cc:3279 f.edit.cc:3311 f.edit.cc:6777 msgid "Saturation" msgstr "Saturació" #: f.edit.cc:1734 msgid "Temperature" msgstr "Temperatura" #: f.edit.cc:1742 msgid "Settings File" msgstr "Ajustaments" #: f.edit.cc:2250 msgid "choose a better spot" msgstr "escollir un punt millor" #: f.edit.cc:2555 msgid "Resize Image" msgstr "Redimensionar imatge" #: f.edit.cc:2571 msgid "Previous" msgstr "Prèvia" #: f.edit.cc:2589 msgid "W/H Ratio:" msgstr "Relació amplada/alçada:" #: f.edit.cc:2591 msgid "Lock" msgstr "Bloquejar" #: f.edit.cc:2840 msgid "insufficient memory, cannot proceed" msgstr "memòria insuficient, no pot continuar" #: f.edit.cc:2936 f.widgets.cc:505 msgid "Adjust RGB" msgstr "Ajustar RGB" #: f.edit.cc:2942 msgid "+Brightness" msgstr "Brillantor" #: f.edit.cc:2943 msgid "+Red -Cyan" msgstr "+Vermell -Cian" #: f.edit.cc:2944 msgid "+Green -Magenta" msgstr "+Verd -Magenta" #: f.edit.cc:2945 msgid "+Blue -Yellow" msgstr "+Blae -Groc" #: f.edit.cc:2948 msgid "Contrast Red" msgstr "Contrast Vermell" #: f.edit.cc:2949 msgid "Contrast Green" msgstr "Contrast Verd" #: f.edit.cc:2950 msgid "Contrast Blue" msgstr "Contrast Blau" #: f.edit.cc:3268 f.widgets.cc:506 msgid "Adjust HSL" msgstr "Ajustar HSL" #: f.edit.cc:3272 msgid "Input color to match and adjust:" msgstr "Entar color per coincidir i ajustar" #: f.edit.cc:3274 msgid "shift+click on image to select color" msgstr "Majúss+clic en la imatge per a sel·leccionar color" #: f.edit.cc:3277 msgid "Match using:" msgstr "Usan​t coincidèencia:" #: f.edit.cc:3278 msgid "Hue" msgstr "Tonalitat" #: f.edit.cc:3280 f.edit.cc:3312 f.edit.cc:6778 msgid "Lightness" msgstr "Brillantor" #: f.edit.cc:3290 msgid "Output Color" msgstr "Color de sortida" #: f.edit.cc:3310 f.edit.cc:6776 msgid "Color Hue" msgstr "To de color" #: f.edit.cc:3313 msgid "Adjustment" msgstr "Ajustament" #: f.edit.cc:3828 msgid "Image Markup" msgstr "Fotomuntatge" #: f.edit.cc:3829 f.edit.cc:3894 msgid "Draw text on image" msgstr "Afegir text a la imatge" #: f.edit.cc:3830 f.edit.cc:4818 msgid "Draw line or arrow on image" msgstr "Dibuixar una línia o fletxa a la imatge" #: f.edit.cc:3831 f.edit.cc:5499 msgid "Draw box on image" msgstr "Dibuixar una caixa a la imatge" #: f.edit.cc:3832 f.edit.cc:5700 msgid "Draw oval on image" msgstr "Dibuixar un oval a la imatge" #: f.edit.cc:3871 msgid "+Version" msgstr "+Version" #: f.edit.cc:3895 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:3944 msgid "Use settings file" msgstr "Utilitzar " #: f.edit.cc:3949 f.mashup.cc:2438 f.tools.cc:1668 f.tools.cc:1677 msgid "Text" msgstr "Text" #: f.edit.cc:3954 msgid "Use metadata key" msgstr "Usar clau de metadades" #: f.edit.cc:3973 f.mashup.cc:2456 msgid "text" msgstr "text" #: f.edit.cc:3974 f.edit.cc:4847 f.mashup.cc:2457 f.mashup.cc:2795 msgid "backing" msgstr "fons" #: f.edit.cc:3975 f.edit.cc:4848 f.mashup.cc:2458 f.mashup.cc:2796 msgid "outline" msgstr "contorn" #: f.edit.cc:3976 f.edit.cc:4849 f.mashup.cc:2459 f.mashup.cc:2797 msgid "shadow" msgstr "ombra" #: f.edit.cc:4002 msgid "save to current file" msgstr "desar en l'arxiu actual" #: f.edit.cc:4003 f.file.cc:2432 msgid "save as new file version" msgstr "desar com una nova versió" #: f.edit.cc:4004 msgid "" "save to current file \n" "open next file with same text" msgstr "" "desar en l'arxiu actual \n" "obrir següent arxiu amb el mateix text" #: f.edit.cc:4167 f.mashup.cc:2563 f.tools.cc:1961 msgid "select font" msgstr "seleccionar tipus de lletra" #: f.edit.cc:4446 msgid "text file is defective" msgstr "arxiu de text és defectuós" #: f.edit.cc:4786 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 dialog \n" "clic/arrossegar en la imatge, clic dret per eliminar" #: f.edit.cc:4826 f.mashup.cc:2774 msgid "Line length" msgstr "Longitut de línia" #: f.edit.cc:4833 f.mashup.cc:2781 msgid "Arrow head" msgstr "Punta de fletxa" #: f.edit.cc:4846 f.mashup.cc:2794 msgid "line" msgstr "linea" #: f.edit.cc:4875 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:5474 msgid "" "drag mouse to draw box \n" "shift + drag center to move box \n" "shift + drag edge to move edge" msgstr "" "arrossegar el ratolí per dibuixar la caixa \n" "majúscula + arrossegar al centre per moure la caixa \n" "majúscula + arrossegar vora per moure la vora" #: f.edit.cc:5505 f.edit.cc:5706 msgid "line color" msgstr "color de línia" #: f.edit.cc:5508 f.edit.cc:5709 msgid "line width" msgstr "ample de línia" #: f.edit.cc:5674 msgid "" "drag mouse down/right to draw oval \n" "shift + drag center to move oval \n" "shift + drag lower right edge to change" msgstr "" "arrossegar el ratolí cap avall/a la dreta per dibuixar un oval \n" "majúscula + arrossegar centre per moure oval \n" "majúscula + arrossegar cantonada inferior dreta per canviar" #: f.edit.cc:5712 msgid "oval" msgstr "oval" #: f.edit.cc:5713 msgid "circle" msgstr "cercle" #: f.edit.cc:5988 msgid "" "shift + left click: pick color from image \n" "left drag: paint color on image \n" "right drag: restore original image" msgstr "" "Majúsc + clic esq.: agafar color des de la imatge \n" "arrossegar esq.: pintar color en la imatge \n" "arrossegar dret: reaturar imatge original" #: f.edit.cc:6024 msgid "Paint on Image" msgstr "Pintar en la imatge" #: f.edit.cc:6030 msgid "paint color" msgstr "pintar color" #: f.edit.cc:6041 f.edit.cc:6970 f.edit.cc:7548 msgid "brush size" msgstr "tamany de pinzell" #: f.edit.cc:6055 msgid "erase" msgstr "esborrar" #: f.edit.cc:6060 msgid "include transparent areas" msgstr "incluïr àrees transparents" #: f.edit.cc:6063 msgid "drag image" msgstr "arrossegar imatge" #: f.edit.cc:6065 msgid "zoom image" msgstr "zoom imatge" #: f.edit.cc:6592 msgid "Color Chooser" msgstr "Selector de colors" #: f.edit.cc:6594 msgid "click on desired color" msgstr "clic en el color desitjat" #: f.edit.cc:6929 msgid "" "shift + left click: pick position to copy \n" "left click or drag: copy image to mouse \n" "right click or drag: restore original image" msgstr "" "majúsc + clic esq: seleccionar la posició per copiar\n" "clic esquerra o arrossegar: copieu la imatge al ratolí \n" "clic dret o arrosseguar: restaurar la imatge original" #: f.edit.cc:6960 msgid "Copy Pixels (1 image)" msgstr "Copiar péxels (1 imatge)" #: f.edit.cc:6979 f.edit.cc:7557 msgid "paint over transparent areas" msgstr "pintar sobre àrees transparents" #: f.edit.cc:7531 msgid "" "left click: synchronize copy position \n" "left click or drag: copy source image to mouse \n" "right click or drag: restore original image" msgstr "" "clic esq.: sinconitzar posició de còpia \n" "clic esq. o arrossegar: copiar imatge origen al cursor \n" "clic dret o arrossegar: restaurar imatge original" #: f.edit.cc:7535 msgid "Copy Pixels (2 images)" msgstr "Copiar píxels (2 imatges)" #: f.edit.cc:7541 msgid "source image scale" msgstr "redimensionar imatge orígen" #: f.edit.cc:8118 msgid "source image failure (scale too big?)" msgstr "fallada de la imatge origen (escala massa gran?)" #: f.edit.cc:8197 f.widgets.cc:511 msgid "Paint Edits" msgstr "Editar pintant" #: f.edit.cc:8203 fotoxx.cc:3576 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "Àrea seleccionada no pot ser guardada.\n" "Continuar?" #: f.edit.cc:8211 f.tools.cc:3751 msgid "Edit function must be active" msgstr "La funció d'editar pintant ha d'estar activada" #: f.edit.cc:8216 msgid "Cannot use Paint Edits" msgstr "No es pot usar Editar pintant" #: f.edit.cc:8243 f.edit.cc:8485 msgid "power: center" msgstr "força: centre" #: f.edit.cc:8248 msgid "reset area" msgstr "reiniciar àrea" #: f.edit.cc:8428 msgid "finish current edit first" msgstr "primer acabar l'edició actual" #: f.edit.cc:8433 msgid "no previous edit" msgstr "no hi ha edició prèvia" #: f.edit.cc:8438 msgid "no current image" msgstr "cap imatge actual" #: f.edit.cc:8449 msgid "This edit cannot be incrementally undone" msgstr "No es pot desfer incrementalment aquesta edició" #: f.edit.cc:8480 f.widgets.cc:512 msgid "Undo Edits" msgstr "Desfer edicions" #: f.edit.cc:8708 f.edit.cc:8756 msgid "Edit Plugins" msgstr "Editar complements (Plugins)" #: f.edit.cc:8709 msgid "Edit plugins menu" msgstr "Editar menú de plugins" #: f.edit.cc:8717 msgid "Run as Fotoxx edit function" msgstr "Obrir com una funció d'edició de Fotoxx" #: f.edit.cc:8758 msgid "menu name" msgstr "nom de menú" #: f.edit.cc:8761 msgid "command" msgstr "instrucció" #: f.edit.cc:8959 msgid "Plugin working ..." msgstr "Plugin treballant ..." #: f.edit.cc:8968 msgid "plugin failed" msgstr "errada de complement" #: f.edit.cc:9004 msgid "Raw Therapee not installed" msgstr "RawTherapee no instal.lat" #: f.edit.cc:9023 msgid "RAW type not registered in User Preferences" msgstr "Tipus RAW no registrat a les Preferències d'usuari" #: f.edit.cc:9038 msgid "Raw Therapee produced no tif file" msgstr "Raw Therapee no ha produït un arxiu tiff" #: f.effects.cc:83 msgid "Convert to Sketch" msgstr "Convertir a esbós" #: f.effects.cc:161 msgid "Clip Level" msgstr "Nivell de tall" #: f.effects.cc:165 msgid "Algorithm" msgstr "Algoritme" #: f.effects.cc:170 msgid "Foreground" msgstr "Primer pla" #: f.effects.cc:173 f.tools.cc:1680 msgid "Background" msgstr "Fons" #: f.effects.cc:609 msgid "Line Threshold" msgstr "Umbral de línia" #: f.effects.cc:610 msgid "Line Width" msgstr "Amplada de la línia" #: f.effects.cc:611 f.enhance.cc:3929 msgid "Blur Radius" msgstr "Radi de desenfocament" #: f.effects.cc:612 msgid "Kuwahara Depth" msgstr "Profunditat de Kuwahara" #: f.effects.cc:1057 f.widgets.cc:538 msgid "Line Drawing" msgstr "Dibuix amb línees" #: f.effects.cc:1070 f.effects.cc:2090 msgid "black/white" msgstr " negre/blanc" #: f.effects.cc:1320 f.widgets.cc:539 msgid "Emboss" msgstr "Relleu" #: f.effects.cc:1326 msgid "Depth" msgstr "Profunditat" #: f.effects.cc:1541 msgid "Simulate Tiles" msgstr "Simular un mosaic" #: f.effects.cc:1545 msgid "tile size" msgstr "mida de la rajola" #: f.effects.cc:1548 msgid "tile gap" msgstr "separació de lles rajoles" #: f.effects.cc:1551 msgid "3D depth" msgstr "profunditat 3D" #: f.effects.cc:1780 msgid "Dither Image" msgstr "Tramat d'imatge" #: f.effects.cc:1783 msgid "Dither0" msgstr "Tramat 0" #: f.effects.cc:1784 f.effects.cc:1848 msgid "Roy Lichtenstein effect" msgstr "Efecte Roy Lichtenstein" #: f.effects.cc:1787 f.effects.cc:2083 msgid "Dither1" msgstr "Tramat 1" #: f.effects.cc:1788 msgid "pure RGB or black/white dots" msgstr "RGB pur o punts negres/blancs" #: f.effects.cc:1791 f.effects.cc:2481 msgid "Dither2" msgstr "Tramat 2" #: f.effects.cc:1792 msgid "RGB mix with given bit-depth" msgstr "barreja d'RGB amb una profunditat de bits donada" #: f.effects.cc:1795 f.effects.cc:2756 msgid "Dither3" msgstr "Tramat 3" #: f.effects.cc:1796 msgid "custom palette colors" msgstr "paleta de colors personalitzada" #: f.effects.cc:1852 msgid "dot size" msgstr "mida de punt" #: f.effects.cc:2087 f.effects.cc:2485 f.effects.cc:2760 msgid "resolution" msgstr "resolució" #: f.effects.cc:2089 msgid "RGB color" msgstr "color RGB" #: f.effects.cc:2091 msgid "random position" msgstr "posició aleatòria" #: f.effects.cc:2488 f.effects.cc:3196 msgid "color depth" msgstr "profunditat de color" #: f.effects.cc:2491 f.effects.cc:2768 msgid "error compensation" msgstr "compensació d'error" #: f.effects.cc:2764 msgid "palette:" msgstr "paleta" #: f.effects.cc:2818 msgid "palette file" msgstr "arxiu de paleta" #: f.effects.cc:3178 f.widgets.cc:542 msgid "Painting" msgstr "Pintura" #: f.effects.cc:3200 msgid "patch area goal" msgstr "mida de l'àrea de color" #: f.effects.cc:3204 msgid "req. color match" msgstr "correspondència de color requerida" #: f.effects.cc:3208 msgid "borders" msgstr "vores" #: f.effects.cc:3799 msgid "Add Texture" msgstr "Afegir textura" #: f.effects.cc:4014 msgid "Background Pattern" msgstr "Patrò de fons" #: f.effects.cc:4018 msgid "Pattern File:" msgstr "Arxiu de patrò" #: f.effects.cc:4026 msgid "Geometry" msgstr "Geometria" #: f.effects.cc:4030 f.widgets.cc:544 msgid "Pattern" msgstr "Patrò" #: f.effects.cc:4038 msgid "Overlap" msgstr "Superposició" #: f.effects.cc:4046 msgid "Opacity" msgstr "Opacitat" #: f.effects.cc:4052 msgid "Grayscale" msgstr "Escala de grisos" #: f.effects.cc:4478 msgid "Create Mosaic" msgstr "Crear mosaïc" #: f.effects.cc:4524 msgid "Tile" msgstr "Rajola" #: f.effects.cc:4532 f.widgets.cc:540 msgid "Tiles" msgstr "Rajoles" #: f.effects.cc:4538 msgid "Tile blending" msgstr "Mescla de rajoles" #: f.effects.cc:4621 #, c-format msgid "exceeded max. tiles: %d" msgstr "excedit max. arxius: %d" #: f.effects.cc:4628 #, c-format msgid "only %d tile images found" msgstr "trobades sols %d imatges" #: f.effects.cc:5088 f.widgets.cc:546 msgid "Color Mode" msgstr "Mode de color" #: f.effects.cc:5091 msgid "reset" msgstr "reinicialitzar" #: f.effects.cc:5092 msgid "black/white positive" msgstr "positiu blanc i negre" #: f.effects.cc:5093 msgid "black/white negative" msgstr "negatiu blanc i negre" #: f.effects.cc:5094 msgid "color negative" msgstr "negatiu color" #: f.effects.cc:5095 msgid "RGB -> GBR" msgstr "RGB -> GBR" #: f.effects.cc:5096 msgid "RGB -> BRG" msgstr "RGB -> BRG" #: f.effects.cc:5097 msgid "sepia" msgstr "sèpia" #: f.effects.cc:5348 msgid "Set color depth to 1-16 bits" msgstr "Ajustar la profunditat de color a 1-16 bits" #: f.effects.cc:5377 msgid "Set Color Depth" msgstr "Ajustar la profunditat de color" #: f.effects.cc:5547 f.widgets.cc:548 msgid "Shift Colors" msgstr "Modificar colors" #: f.effects.cc:5824 f.widgets.cc:549 msgid "Alien Colors" msgstr "Colors alienígens" #: f.effects.cc:5827 msgid "blocksize" msgstr "tamany de bloc" #: f.effects.cc:5830 f.warp.cc:3096 msgid "amplitude" msgstr "amplitut" #: f.effects.cc:6089 msgid "" "Draw a line across the image in \n" "direction of brightness change." msgstr "" "Trazar una línea en la imagen en \n" "la dirección del cambio de brillo." #: f.effects.cc:6132 msgid "Brightness Ramp" msgstr "Rampa de brillantor" #: f.effects.cc:6605 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" "arrossegar esquerra: afegir transparència \n" "arrossegar dret: afegir opacitat" #: f.effects.cc:6640 msgid "Paint Transparency" msgstr "Pintar transparència" #: f.effects.cc:6648 msgid "paintbrush radius" msgstr "radi del pincell" #: f.effects.cc:6649 msgid "strength center" msgstr "força en el centre" #: f.effects.cc:6650 msgid "strength edge" msgstr "força en els extrems" #: f.effects.cc:6655 msgid "gradual paint" msgstr "pintat gradual" #: f.effects.cc:6874 msgid "Mirror Image" msgstr "Enmirallar imatge" #: f.effects.cc:6877 f.warp.cc:3098 msgid "horizontal" msgstr "horitzontal" #: f.effects.cc:6878 f.warp.cc:3102 msgid "vertical" msgstr "vertical" #: f.effects.cc:7051 f.widgets.cc:553 msgid "Custom Kernel" msgstr "Matriu de convolució" #: f.effects.cc:7055 msgid "Kernel size" msgstr "Tamany de matriu" #: f.effects.cc:7072 msgid "multiply" msgstr "multiplicar" #: f.effects.cc:7075 msgid "add" msgstr "afegir" #: f.effects.cc:7079 msgid "Data file" msgstr "Arxiu de dades" #: f.effects.cc:7099 fotoxx.cc:3845 msgid "Load settings from file" msgstr "Carregar ajustaments des d'un arxiu" #: f.enhance.cc:505 msgid "Adjust Brightness Distribution" msgstr "Ajustar histograma" #: f.enhance.cc:544 msgid "Low Cutoff" msgstr "Tall baix" #: f.enhance.cc:545 msgid "High Cutoff" msgstr "Tall alt" #: f.enhance.cc:546 msgid "Low Flatten" msgstr "Aplanat baix" #: f.enhance.cc:547 msgid "Mid Flatten" msgstr "Aplanat mitjà" #: f.enhance.cc:548 msgid "High Flatten" msgstr "Aplanat alt" #: f.enhance.cc:549 msgid "Low Stretch" msgstr "Tram baix" #: f.enhance.cc:550 msgid "Mid Stretch" msgstr "Tram mitjà" #: f.enhance.cc:551 msgid "High Stretch" msgstr "Tram alt" #: f.enhance.cc:886 msgid "Magnify Gradients" msgstr "Augmentar gradients" #: f.enhance.cc:923 msgid "low" msgstr "baix" #: f.enhance.cc:925 msgid "high" msgstr "alt" #: f.enhance.cc:928 msgid "Amplify" msgstr "Ampliar" #: f.enhance.cc:1312 msgid "Flatten Brightness" msgstr "Aplanar brillantor" #: f.enhance.cc:1363 msgid "Zones" msgstr "Zones" #: f.enhance.cc:1370 msgid "Deband Dark" msgstr "Eliminar bandes fosques" #: f.enhance.cc:1373 msgid "Deband Bright" msgstr "Eliminar bandes lluminoses" #: f.enhance.cc:1851 msgid "Global Retinex" msgstr "Retinex global" #: f.enhance.cc:1867 msgid "Dark Point" msgstr "Punt negre" #: f.enhance.cc:1868 msgid "Bright Point" msgstr "Punt brillant" #: f.enhance.cc:1869 msgid "Multiplyer" msgstr "Multiplicador" #: f.enhance.cc:1893 msgid "brightness rescale" msgstr "re-esclar brillantor" #: f.enhance.cc:1896 msgid "click bright point" msgstr "clic en el punt brillant" #: f.enhance.cc:1897 msgid "click dark point" msgstr "clic en el punt fosc" #: f.enhance.cc:1900 f.enhance.cc:2531 msgid "blend" msgstr "barrejar" #: f.enhance.cc:1903 f.enhance.cc:2537 msgid "reduce bright" msgstr "reduïr brillantor" #: f.enhance.cc:2523 msgid "Zonal Retinex" msgstr "Retinex zonal" #: f.enhance.cc:2527 msgid "zone count:" msgstr "recompte de zones:" #: f.enhance.cc:2534 msgid "reduce dark" msgstr "reduïr obscuritat" #: f.enhance.cc:3058 f.process.cc:189 f.process.cc:823 f.process.cc:1404 #: f.widgets.cc:524 msgid "Sharpen" msgstr "Enfocar" #: f.enhance.cc:3066 msgid "unsharp mask" msgstr "màscara de desenfocament" #: f.enhance.cc:3099 msgid "median diff" msgstr "mitjana diferència" #: f.enhance.cc:3101 msgid "dark" msgstr "fosc" #: f.enhance.cc:3102 msgid "light" msgstr "clar" #: f.enhance.cc:3888 msgid "Click to set center" msgstr "Clic per establir el centre" #: f.enhance.cc:3889 msgid "Pull image using the mouse" msgstr "Empènyer la imatge usant el ratolí" #: f.enhance.cc:3890 msgid "" "left drag: blend image \n" "right drag: restore image" msgstr "" "arrossegar esquerra: barrejar imatge \n" "arrossegar dret: reinicialitzar imatge" #: f.enhance.cc:3933 msgid "Normal Blur" msgstr "Desenfocament normal" #: f.enhance.cc:3940 msgid "Radial Blur" msgstr "Desenfocament radial" #: f.enhance.cc:3954 msgid "Directed Blur" msgstr "Desenfocament direccional" #: f.enhance.cc:3956 msgid "Blur Span" msgstr "quantitat de desenfocament" #: f.enhance.cc:3959 msgid "Intensity" msgstr "Intensitat" #: f.enhance.cc:3964 msgid "Graduated Blur" msgstr "Desenfocament graduat" #: f.enhance.cc:3969 msgid "Contrast Limit" msgstr "Límit de contrast" #: f.enhance.cc:3974 msgid "Paint Blur" msgstr "Pintar desenfocament" #: f.enhance.cc:3979 f.mashup.cc:1577 fotoxx.h:1402 msgid "Power" msgstr "Força" #: f.enhance.cc:3987 f.enhance.cc:4914 msgid "Blur Background" msgstr "Desenfocar fons" #: f.enhance.cc:4918 msgid "constant blur" msgstr "desenfocament constant" #: f.enhance.cc:4921 msgid "increase blur with distance" msgstr "incrementar desenfocament amb la distància" #: f.enhance.cc:4923 msgid "min. blur radius" msgstr "mín. radi de desenfocament" #: f.enhance.cc:4926 msgid "max. blur radius" msgstr "màx. radi de desenfocament" #: f.enhance.cc:4957 f.warp.cc:837 f.warp.cc:2277 msgid "no active Select Area" msgstr "Seleccionar àrea no actiu" #: f.enhance.cc:5139 msgid "Apply repeatedly while watching the image." msgstr "Aplicar repetidament mentre es visualitza la imatge" #: f.enhance.cc:5175 msgid "Noise Reduction" msgstr "Reducció de soroll" #: f.enhance.cc:5215 msgid "dark areas" msgstr "àrees fosques" #: f.enhance.cc:5217 msgid "all areas" msgstr "totes les àrees" #: f.enhance.cc:6195 msgid "Measure Noise" msgstr "Mida de soroll" #: f.enhance.cc:6196 msgid "Move mouse in a monotone image area." msgstr "Moure ratolí en un àrea de color uniforme de la imatge." #: f.enhance.cc:6511 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.enhance.cc:6530 msgid "Red Eye Reduction" msgstr "Reducció d'ulls vermells" #: f.enhance.cc:6984 msgid "Color Match Images" msgstr "Concordar color d'imatges" #: f.enhance.cc:7015 msgid "mouse radius for color sample" msgstr "radi del ratolí per mostra de color" #: f.enhance.cc:7017 f.enhance.cc:7022 fotoxx.h:1397 zfuncs.cc:12681 msgid "Open" msgstr "Obrir" #: f.enhance.cc:7018 msgid "image for source color" msgstr "imatge per el color d'origen" #: f.enhance.cc:7020 msgid "click on image to get source color" msgstr "clic en la imatge per obtenir color d'origen" #: f.enhance.cc:7023 msgid "image to set matching color" msgstr "imatge per establir concordància de color" #: f.enhance.cc:7025 msgid "click on image to set matching color" msgstr "click on image to set matching color" #: f.enhance.cc:7092 msgid "select source image color first" msgstr "seleccionar primer el color de la imatge d'origen" #: f.enhance.cc:7287 msgid "" "Drag mouse to select. Erase. Repeat. \n" "Click: extend selection to mouse." msgstr "" "Arrossegar el ratolí per seleccionar. Esborrar. Repetir. \n" "Fer clic: amplia la selecció al ratolí." #: f.enhance.cc:7314 f.widgets.cc:529 msgid "Smart Erase" msgstr "Esborrat intel.ligent" #: f.enhance.cc:7319 fotoxx.h:1408 msgid "Radius" msgstr "Radi" #: f.enhance.cc:7321 f.widgets.cc:525 msgid "Blur" msgstr "Desenfocar" #: f.enhance.cc:7324 msgid "New Area" msgstr "Nova àrea" #: f.enhance.cc:7672 f.enhance.cc:8054 msgid "Chromatic Aberration" msgstr "Aberració cromàtica" #: f.enhance.cc:7711 msgid "Red Factors" msgstr "Factors de vermell" #: f.enhance.cc:7715 msgid "Blue Factors" msgstr "Factors de blau" #: f.enhance.cc:7720 msgid "Find optimum factors:" msgstr "Trobar factors òptims:" #: f.enhance.cc:8095 msgid "Chromatic Color" msgstr "Color cromàtic" #: f.enhance.cc:8098 msgid "Replacement Color" msgstr "Color de reemplaçament" #: f.enhance.cc:8101 msgid "Background Color" msgstr "Color de fons" #: f.enhance.cc:8105 msgid "Color match level" msgstr "Nivell de concordança de color" #: f.enhance.cc:8109 msgid "Background Proximity" msgstr "Proximitat de fons" #: f.enhance.cc:8151 msgid "255 iterations, cannot continue" msgstr "255 iteracions no es pot continuar" #: f.enhance.cc:8413 f.widgets.cc:532 msgid "Vignette" msgstr "Vinyetat" #: f.enhance.cc:8802 f.widgets.cc:533 msgid "Remove Dust" msgstr "Retirar pols" #: f.enhance.cc:8806 msgid "spot size limit" msgstr "límit de mida del punt" #: f.enhance.cc:8809 msgid "max. brightness" msgstr "max. brillantor" #: f.enhance.cc:8812 msgid "min. contrast" msgstr "min. contrast" #: f.file.cc:601 msgid "Rename Image File" msgstr "Re-anomenar arxiu d'imatge" #: f.file.cc:608 msgid "Old Name" msgstr "antic nom" #: f.file.cc:609 f.process.cc:141 msgid "New Name" msgstr "nou nom" #: f.file.cc:618 msgid "previous name" msgstr "nom anterior" #: f.file.cc:619 msgid "Add 1" msgstr "Afegir 1" #: f.file.cc:622 f.file.cc:882 f.file.cc:1629 f.file.cc:1998 msgid "keep this dialog open" msgstr "mantenir aquest diàleg obert" #: f.file.cc:850 msgid "File Permissions" msgstr "Permisos de arxius" #: f.file.cc:854 f.meta.cc:833 f.meta.cc:1615 f.meta.cc:1818 msgid "File:" msgstr "Arxiu:" #: f.file.cc:1000 msgid "Open Image File" msgstr "Obrir arxiu d'imatge" #: f.file.cc:1019 f.process.cc:654 msgid "unknown file type" msgstr "tipus d'arxiu desconegut" #: f.file.cc:1207 #, c-format msgid "Start of gallery, preceding gallery: %s" msgstr "Inici de galeria, precedent : %s" #: f.file.cc:1208 #, c-format msgid "End of gallery, following gallery: %s" msgstr "Fi de galeria, següent : %s" #: f.file.cc:1355 msgid "Create Blank Image" msgstr "Crear imatge buida" #: f.file.cc:1357 f.meta.cc:1909 msgid "file name" msgstr "nom d'arxiu" #: f.file.cc:1391 msgid "supply a file name" msgstr "proporcionar nom d'àlbum" #: f.file.cc:1570 msgid "Copy or Move Image File" msgstr "Copiar o moure arxiu d'imatge" #: f.file.cc:1615 msgid "New Location:" msgstr "Nova ubicació" #: f.file.cc:1620 msgid "New Name:" msgstr "Nou nom" #: f.file.cc:1625 msgid "copy (duplicate file)" msgstr "copiar (duplicar arxiu)" #: f.file.cc:1626 msgid "move (remove original)" msgstr "moure (moure original)" #: f.file.cc:1676 f.process.cc:608 f.process.cc:1574 f.process.cc:2155 msgid "Select folder" msgstr "Seleccionar directori" #: f.file.cc:1718 f.tools.cc:1488 msgid "new location is not a folder" msgstr "la nova ubicació no és un directori" #: f.file.cc:1737 msgid "new file extension missing or changed" msgstr "falta una extensió de arxiu nova o canviada" #: f.file.cc:1771 f.file.cc:2075 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "l'esborrat ha fallat: \n" " %s" #: f.file.cc:1845 f.file.cc:3226 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Sobre-escriure arxiu? \n" " %s" #: f.file.cc:1957 msgid "Delete/Trash Image File" msgstr "Eliminar/esborrar una imatge" #: f.file.cc:2029 msgid "GTK g_file_trash() function failed" msgstr "Ha fallat la funció GTK g_file_trash()" #: f.file.cc:2048 msgid "not a known image file" msgstr "no és un arxiu d'imatge conegut" #: f.file.cc:2054 msgid "Delete read-only file?" msgstr "Esborrar arxius de només lectura?" #: f.file.cc:2056 msgid "Trash read-only file?" msgstr "Arxiu de paperera nomès de lectura?" #: f.file.cc:2272 #, c-format msgid "Kill active dialog? %s" msgstr "Tancar el diàleg actiu? %s" #: f.file.cc:2322 f.widgets.cc:633 msgid "User Guide" msgstr "Guia d'usuari" #: f.file.cc:2325 f.widgets.cc:634 msgid "Recent Changes" msgstr "Canvis recents" #: f.file.cc:2328 f.widgets.cc:635 msgid "Edit Functions Overview" msgstr "Vista general de les funcions d'edició" #: f.file.cc:2334 f.widgets.cc:636 msgid "Change Log" msgstr "Registre de canvis" #: f.file.cc:2337 f.widgets.cc:637 msgid "Log File" msgstr "Arxiu de registre" #: f.file.cc:2343 f.widgets.cc:639 msgid "Command Params" msgstr "Paràmetres de comandament " #: f.file.cc:2346 f.widgets.cc:640 msgid "Translations" msgstr "Traduccions" #: f.file.cc:2349 f.widgets.cc:641 msgid "Home Page" msgstr "Pàgina web de Fotoxx" #: f.file.cc:2352 f.widgets.cc:642 msgid "License" msgstr "Llicència" #: f.file.cc:2355 f.widgets.cc:643 msgid "Privacy" msgstr "Privacitat" #: f.file.cc:2358 f.widgets.cc:644 msgid "About" msgstr "Sobre Fotoxx " #: f.file.cc:2366 f.widgets.cc:665 fotoxx.h:1366 msgid "Help" msgstr "Ajuda" #: f.file.cc:2414 msgid "Save Image File" msgstr "Desar imatge" #: f.file.cc:2424 msgid "new version" msgstr "nova versió" #: f.file.cc:2425 msgid "new file ..." msgstr "nou arxiu ..." #: f.file.cc:2426 msgid "replace file" msgstr "substituïr arxiu" #: f.file.cc:2433 f.file.cc:3083 msgid "save as new file name or type" msgstr "desar com un nou nom o tipus d'arxiu" #: f.file.cc:2435 msgid "replace old file (OVERWRITE)" msgstr "substituïr arxiu anterior (SOBRE-ESCRIURE)" #: f.file.cc:2486 msgid "cannot replace RAW file" msgstr "no pot substituir el arxiu RAW" #: f.file.cc:2491 msgid "cannot replace HEIC file" msgstr "no pot substituir el arxiu HEIC" #: f.file.cc:2496 msgid "cannot replace JP2 file" msgstr "" #: f.file.cc:2668 f.file.cc:2727 #, c-format msgid "" "file: %s \n" " exceed 99 versions" msgstr "" "arxiu: %s \n" " excedides 99 versions" #: f.file.cc:2821 msgid "" "Transparency map will be lost.\n" "save to PNG file to retain." msgstr "" "Es perdrà el mapa de transparència.\n" "desar com arxiu .PNG per conservar-lo" #: f.file.cc:2831 msgid "cannot save as RAW type" msgstr "no es pot desar com a RAW" #: f.file.cc:2912 msgid "save anyway" msgstr "desar de totes maneres" #: f.file.cc:2980 f.file.cc:2982 msgid "Unable to copy EXIF/IPTC data" msgstr "No es pot copiar dades EXIF/IPTC" #: f.file.cc:3096 f.process.cc:1387 msgid "jpg quality" msgstr "qualitat jpg" #: f.file.cc:3100 msgid "color depth:" msgstr "profunditat de color:" #: f.file.cc:3106 f.file.cc:3114 msgid "make current" msgstr "fer actual" #: f.file.cc:3107 msgid "(new file becomes current file)" msgstr "(el nou arxiu serà l'arxiu actual)" #: f.file.cc:3110 msgid "permissions:" msgstr "permisos" #: f.file.cc:3641 f.widgets.cc:432 f.widgets.cc:821 msgid "Permissions" msgstr "Permisos" #: f.file.cc:3646 fotoxx.cc:254 msgid "owner" msgstr "propietari" #: f.file.cc:3647 fotoxx.cc:255 msgid "group" msgstr "grup" #: f.file.cc:3648 fotoxx.cc:256 msgid "other" msgstr "altres" #: f.gallery.cc:1699 f.gallery.cc:1719 msgid "recent images" msgstr "imatges recents" #: f.gallery.cc:1700 f.gallery.cc:1724 msgid "newest images" msgstr "imatge més recents" #: f.gallery.cc:1779 msgid "no albums found" msgstr "No trobats àlbums" #: f.gallery.cc:1789 f.gallery.cc:1803 msgid "Current Album" msgstr "Àlbum actual" #: f.gallery.cc:1806 msgid "no current album" msgstr "àlbun no actual" #: f.gallery.cc:1828 msgid "" " Reset all galleries\n" " to file name ascending" msgstr "" "Re-inicir totes les galeries\n" "a nom d'arxiu ascendent" #: f.gallery.cc:1849 msgid "Gallery Sort" msgstr "Ordenar galeria" #: f.gallery.cc:1853 msgid "File Name" msgstr "Nom d'arxiu" #: f.gallery.cc:1854 msgid "File Mod Date/Time" msgstr "Mode d'arxiu date/hora" #: f.gallery.cc:1855 msgid "Photo Date/Time (EXIF)" msgstr "Data/hora de la foto (EXIF)" #: f.gallery.cc:1856 msgid "File Size (bytes)" msgstr "Mida del arxiu (bytes)" #: f.gallery.cc:1857 msgid "Image Size (pixels)" msgstr "Tamany d'imatge (píxels)" #: f.gallery.cc:1859 msgid "ascending" msgstr "ascendent" #: f.gallery.cc:1860 msgid "descending" msgstr " descendent" #: f.gallery.cc:2759 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.gallery.cc:2783 f.widgets.cc:648 msgid "File" msgstr "Arxiu" #: f.gallery.cc:3589 f.gallery.cc:3593 msgid "Image File" msgstr "Arxiu d'imatges" #: f.gallery.cc:3702 msgid "Select Image Files" msgstr "Selecciona arxius d'imatge" #: f.gallery.cc:3768 #, c-format msgid "exceed %d selected files" msgstr "excedits %d arxius seleccionats" #: f.gallery.cc:3780 #, c-format msgid "remove %d duplicates?" msgstr "voleu eliminar %d duplicats?" #: f.gallery.cc:4229 f.widgets.cc:457 msgid "Bookmarks" msgstr "Marques" #: f.gallery.cc:4229 f.gallery.cc:4337 msgid "Edit Bookmarks" msgstr "Editar marcadors" #: f.gallery.cc:4311 msgid "" "Click a list position. Click a gallery thumbnail for the new bookmark.\n" "Bookmark for thumbnail will be added. Change the name and press [Rename]." msgstr "" "Clic en una posició de la llista. Clic en una miniatuira de galeria per nou " "marcador.\n" "S'afegirà un ou marcador per diapositiva. Canviar el nom y pulsat [Re-" "anomenar]." #: f.gallery.cc:4514 msgid "unable to save bookmarks file" msgstr "no puc desar arxiu de marcadors" #: f.mashup.cc:212 msgid "Project name" msgstr "Nom de projecte" #: f.mashup.cc:216 msgid "Layout and background image" msgstr "Disseny i imatge de fons" #: f.mashup.cc:220 msgid "choose an image file" msgstr "escollir una imatge" #: f.mashup.cc:221 msgid "use current image file" msgstr "utilitzar imatge actual" #: f.mashup.cc:222 msgid "specify layout size and color" msgstr "especificar mida i color de la capa" #: f.mashup.cc:223 msgid "open a Mashup project file" msgstr "Obrir un projecte de muntatge" #: f.mashup.cc:238 msgid "enter a project name" msgstr "Introduïr el nom del projecte" #: f.mashup.cc:269 msgid "no current file" msgstr "no hi ha arxiu actiu" #: f.mashup.cc:296 msgid "Make Layout Image" msgstr "Fer una composició d'imatges" #: f.mashup.cc:403 msgid "Edit Images" msgstr "Editar imatges" #: f.mashup.cc:404 f.mashup.cc:2433 msgid "Edit Text" msgstr "Editar text" #: f.mashup.cc:405 msgid "Edit Line" msgstr "Editar text" #: f.mashup.cc:406 msgid "Rescale" msgstr "Re-escalar" #: f.mashup.cc:411 msgid "add or edit images" msgstr "afegir o editar imatges" #: f.mashup.cc:413 msgid "add or edit text" msgstr "afegir o editar text" #: f.mashup.cc:415 msgid "add or edit lines/arrows" msgstr "afegir o editar línees/fletxes" #: f.mashup.cc:417 msgid "change project scale" msgstr "canviar escala del projecte" #: f.mashup.cc:419 msgid "project complete" msgstr "projecte complert" #: f.mashup.cc:421 msgid "cancel project" msgstr "cancel.lar projecte" #: f.mashup.cc:439 msgid "rescale project" msgstr "re-escalar projecte" #: f.mashup.cc:493 msgid "save Mashup output file" msgstr "desar arxiu de sortida del fotomuntatge" #: f.mashup.cc:513 msgid "save Mashup project file?" msgstr "desar arxiu de projecte del fotomuntatge" #: f.mashup.cc:528 msgid "delete Mashup project file?" msgstr "esborrar arxiu projecte del fotomuntatge?" #: f.mashup.cc:588 msgid "Open Project" msgstr "Obrir projecte" #: f.mashup.cc:617 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "requerida imatge de capa \n" " %s" #: f.mashup.cc:674 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "requerida imatge sobreposada \n" " %s" #: f.mashup.cc:980 msgid "project file is defective" msgstr "arxiu de projecte defectuós" #: f.mashup.cc:1010 msgid "Save Project" msgstr "Desar projecte" #: f.mashup.cc:1151 msgid "layout exceeds 2 gigabytes" msgstr "la composició excedeix 2 gigabytes" #: f.mashup.cc:1221 msgid "Click image to select, drag image to move." msgstr "Clic en la imatge per seleccionar, arrossegar imatge per moure-la" #: f.mashup.cc:1222 msgid "Make black margins transparent" msgstr "Fer transparents els marges negres" #: f.mashup.cc:1254 msgid "Add Image" msgstr "Afegir imatge" #: f.mashup.cc:1261 msgid "Current image:" msgstr "Imatge actual:" #: f.mashup.cc:1265 msgid "Cycle through images:" msgstr "Recorregut per les imatges:" #: f.mashup.cc:1272 msgid "Scale" msgstr "Escalar" #: f.mashup.cc:1281 msgid "Stacking Order" msgstr "Ordre de pila" #: f.mashup.cc:1282 msgid "Raise" msgstr "Pujar" #: f.mashup.cc:1283 msgid "Lower" msgstr "Baixar" #: f.mashup.cc:1286 msgid "Base Transparency" msgstr "Transparència" #: f.mashup.cc:1290 msgid "Var. Transparency" msgstr "Variar transparència" #: f.mashup.cc:1291 msgid "Paint" msgstr "Pintar" #: f.mashup.cc:1294 msgid "Bend and fine-align" msgstr "Combinar i alineació fina" #: f.mashup.cc:1295 f.widgets.cc:661 msgid "Warp" msgstr "Deformar" #: f.mashup.cc:1304 zfuncs.cc:12896 zfuncs.cc:12905 msgid "Margins" msgstr "Marges" #: f.mashup.cc:1305 msgid "Hard" msgstr "Fort" #: f.mashup.cc:1306 msgid "Blend" msgstr "Barrejar" #: f.mashup.cc:1320 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:1810 msgid "Pull on the image with the mouse." msgstr "Empènyer la imatge amb el ratolí" #: f.mashup.cc:1826 msgid "Warp Image" msgstr "Deformar imatge" #: f.mashup.cc:1832 f.warp.cc:1209 msgid "warp span" msgstr "intèrval de deformació" #: f.mashup.cc:2264 #, c-format msgid "exceeded %d images" msgstr "excedit %d imatges" #: f.mashup.cc:2406 msgid "Enter text, [Add] to layout, edit properties." msgstr "Afegir text, [Afegir] a la capa, editar propietats" #: f.mashup.cc:2486 msgid "Text File:" msgstr "arxiu de text:" #: f.mashup.cc:2490 msgid "add entered text to layout" msgstr "afegir text a la capa" #: f.mashup.cc:2624 msgid "click position to add text" msgstr "clic en la posició per afegir text" #: f.mashup.cc:2630 #, c-format msgid "exceeded %d text entries" msgstr "excedit%d entrades de text" #: f.mashup.cc:2635 msgid "Add Text" msgstr "Afegir text" #: f.mashup.cc:2744 msgid "Set line properties, [Add] to layout, edit." msgstr "Ajustar propietats de línia, [Afegir] a la capa, editar." #: f.mashup.cc:2768 msgid "Edit Line/Arrow" msgstr "Editar linea/fletxa" #: f.mashup.cc:2823 msgid "add line/arrow to layout" msgstr "afegir línia/fletxa a la capa" #: f.mashup.cc:2915 msgid "click position to add line" msgstr "Clic en la posició per afegir línia" #: f.mashup.cc:2921 #, c-format msgid "exceeded %d line entries" msgstr "excedides %d entrades de línia" #: f.mashup.cc:2926 msgid "Add Line" msgstr "Afegir línia" #: f.mashup.cc:4171 msgid "Image Montage" msgstr "Montatge d'imatges" #: f.mashup.cc:4180 msgid "Frame Width" msgstr "Ample del marc" #: f.mashup.cc:4183 f.mashup.cc:4191 msgid "Margin" msgstr "Marge" #: f.mashup.cc:4188 msgid "Image Columns" msgstr "Columnes d'imatge" #: f.mashup.cc:4205 #, c-format msgid "exceed %d rows" msgstr "excedides %d columnes" #: f.mashup.cc:4301 msgid "Optimize" msgstr "Optimitzar" #: f.mashup.cc:4309 #, c-format msgid "column difference: %d pixels" msgstr "diferència de columnes: %d píxels" #: f.mashup.cc:4367 #, c-format msgid "" "column difference: %d pixels \n" "Make columns even?" msgstr "" "deiferència de columnes: %d píxels \n" "Fer columnes igualment?" #: f.mashup.cc:4389 msgid "Save with unique montage name" msgstr "Guardar amb un únic nom de muntatge" #: f.mashup.cc:4391 msgid "unique name:" msgstr "nom únic" #: f.mashup.cc:4393 msgid "create image map" msgstr "crear mapa d'imatge" #: f.mashup.cc:4410 f.meta.cc:9151 msgid "supply a reasonable name" msgstr "donar un nom raonable" #: f.mashup.cc:4421 msgid "save montage" msgstr "guaradr muntatge" #: f.mashup.cc:4441 #, c-format msgid "map file saved: %s" msgstr "arxiu de mapa guardat: %s" #: f.mashup.cc:4486 #, c-format msgid "%d max images exceeded" msgstr "%d màxim d'imatges excedit" #: f.mashup.cc:4560 f.mashup.cc:4566 #, c-format msgid "image frame is too large: %d x %d" msgstr "marc d'imatge és massa ample: %d x %d" #: f.mashup.cc:4871 #, c-format msgid "montage map file invalid: %s" msgstr "arxiu de mapoa de muntatge invàlid: %s" #: f.meta.cc:245 msgid "Add Metadata Items" msgstr "Afegir ítems de metadades" #: f.meta.cc:249 f.meta.cc:1609 f.meta.cc:3190 msgid "click to select" msgstr "clic per seleccionar" #: f.meta.cc:254 msgid "click to unselect" msgstr "clic per deseleccionar" #: f.meta.cc:476 msgid "Extras" msgstr "Extres" #: f.meta.cc:476 msgid "View Metadata" msgstr "Veure metadades" #: f.meta.cc:661 msgid "View All Metadata" msgstr "Veure totes les metadades" #: f.meta.cc:826 msgid "Edit Metadata" msgstr "Editar metadades" #: f.meta.cc:829 msgid "save metadata to file" msgstr "desar metadades en l'arxiu" #: f.meta.cc:838 msgid "Image Date" msgstr "Data de la imatge" #: f.meta.cc:841 msgid "Time" msgstr "Hora" #: f.meta.cc:849 msgid "Rating (stars):" msgstr "Calificació (estrellas)" #: f.meta.cc:861 msgid "Caption" msgstr "Títol" #: f.meta.cc:866 msgid "Comments" msgstr "Comentaris" #: f.meta.cc:873 msgid "Location" msgstr "Ubicació" #: f.meta.cc:876 msgid "Country" msgstr "Pais" #: f.meta.cc:899 msgid "Image Tags" msgstr "Etiquetes d'imatge" #: f.meta.cc:905 msgid "Recent Tags" msgstr "etiquetes recents" #: f.meta.cc:911 f.meta.cc:2081 msgid "Enter New Tag" msgstr "Introduïr nova etiqueta" #: f.meta.cc:917 f.meta.cc:2087 f.meta.cc:4750 msgid "Matching Tags" msgstr "Construïnt etiquetes" #: f.meta.cc:925 f.meta.cc:2096 f.meta.cc:2556 f.meta.cc:4757 msgid "Defined Tags Category" msgstr "Categoria d'etiquetes definida" #: f.meta.cc:933 msgid "search known locations" msgstr "mostrar localitats conegudes" #: f.meta.cc:934 msgid "search using web service" msgstr "buscar utilitzan servei web" #: f.meta.cc:1384 f.meta.cc:3800 f.meta.cc:7709 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "latitud/longitud errònia: %s %s" #: f.meta.cc:1441 f.widgets.cc:477 msgid "Manage Tags" msgstr "Gestionar etiquetes" #: f.meta.cc:1441 msgid "orphan tags" msgstr "etiquetes òrfenes" #: f.meta.cc:1445 msgid "category" msgstr "categoria" #: f.meta.cc:1448 msgid "tag" msgstr "etiqueta" #: f.meta.cc:1455 msgid "Defined Tags:" msgstr "etiquetes definides:" #: f.meta.cc:1605 msgid "Edit Any Metadata" msgstr "Editar qualsevol metadada" #: f.meta.cc:1605 f.meta.cc:3185 msgid "Full List" msgstr "Llista completa" #: f.meta.cc:1619 f.meta.cc:3202 msgid "key name" msgstr "nom de clau" #: f.meta.cc:1622 f.meta.cc:3203 msgid "key value" msgstr "valor de clau" #: f.meta.cc:1815 msgid "Delete Metadata" msgstr "Esborrar metadades" #: f.meta.cc:1821 fotoxx.h:1317 msgid "All" msgstr "Tot" #: f.meta.cc:1822 msgid "One Key:" msgstr "Una clau:" #: f.meta.cc:1908 msgid "choose options" msgstr "escollir opcions" #: f.meta.cc:1910 msgid "caption" msgstr "títol" #: f.meta.cc:1911 msgid "comment" msgstr "comentari" #: f.meta.cc:2057 msgid "Batch Add/Remove Tags" msgstr "Afegir/eliminar etiquetes en lot" #: f.meta.cc:2070 msgid "tags to add" msgstr "Etiquetes per afegir" #: f.meta.cc:2071 msgid "tags to remove" msgstr "etiquetes per eliminar" #: f.meta.cc:2176 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " massa etiquetes" #: f.meta.cc:2191 msgid "repeat with same files?" msgstr "Repetir amb els mateixos arxius" #: f.meta.cc:2331 msgid "specify files and tags" msgstr "especificar arxius i etiquetes" #: f.meta.cc:2535 f.widgets.cc:596 msgid "Batch Rename Tags" msgstr "Reanomenar etiquetes en lot" #: f.meta.cc:2545 msgid "(click defined tag)" msgstr "(clic en etiqueta definida)" #: f.meta.cc:2547 f.process.cc:815 msgid "Rename to" msgstr "Re-anomenar com" #: f.meta.cc:2564 msgid "old tag name >> new tag name" msgstr "anis¡c nom d'etiqueta >> nou nom d'etiqueta" #: f.meta.cc:2620 #, 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:2774 msgid "max tags exceeded" msgstr "máxim d'etiquetes excedit" #: f.meta.cc:2843 f.meta.cc:2978 msgid "Batch Photo Date/Time" msgstr "Data/hora de les fotos en lot" #: f.meta.cc:2851 msgid "set a new date/time:" msgstr "establir un ana nova data/hora" #: f.meta.cc:2859 msgid "shift existing date/time:" msgstr "canviar data/hora existents" #: f.meta.cc:2885 msgid "test: show changes, do not update files" msgstr "prova: mostra canvis sense modificar arxius" #: f.meta.cc:2910 msgid "please make a choice" msgstr "si us plau feu una elecció" #: f.meta.cc:2915 f.meta.cc:3276 f.meta.cc:3467 msgid "no files selected" msgstr "no hi han arxius seleccionats" #: f.meta.cc:2945 f.meta.cc:2955 f.meta.cc:2961 msgid "invalid date/time format" msgstr "format de data/hora no vàlid" #: f.meta.cc:3185 msgid "Batch Add/Change Metadata" msgstr "Afegir/canviar metadades en lot" #: f.meta.cc:3270 msgid "enter key names" msgstr "introduïr noms clau" #: f.meta.cc:3353 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:3450 msgid "Batch Report Metadata" msgstr "Informe de metada​des en lot" #: f.meta.cc:3456 msgid "list of reported metadata items" msgstr "llista d'elements de metadades comunicats" #: f.meta.cc:3599 f.widgets.cc:600 msgid "Batch Geotags" msgstr "Geo-etiquetes en lot" #: f.meta.cc:3640 msgid "location" msgstr "ubicació" #: f.meta.cc:3643 msgid "country" msgstr "pais" #: f.meta.cc:3676 msgid "Adding Geotags" msgstr "Afegint geoetiquetes" #: f.meta.cc:3785 msgid "" "data is incomplete \n" " proceed?" msgstr "" "les dades són incompletas \n" " procedir?" #: f.meta.cc:3873 msgid "Report Image Locations" msgstr "Informe de localitzacions d'imatges" #: f.meta.cc:3874 msgid "Group by country" msgstr "Agrupar per pais" #: f.meta.cc:3875 msgid "Group by country/location" msgstr "Agrupar per pais/ubicació" #: f.meta.cc:3876 msgid "Group by country/location/date" msgstr "Agrupar pais/ubicació/data" #: f.meta.cc:3877 msgid "Group by date/country/location" msgstr "Agrupar data/pais/ubicació" #: f.meta.cc:3880 msgid "Combine within" msgstr "Conbinar dins de" #: f.meta.cc:3882 msgid "days" msgstr "dies" #: f.meta.cc:3994 msgid "Image Locations" msgstr "Ubicació d'imatge" #: f.meta.cc:4263 msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" msgstr "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" #: f.meta.cc:4655 msgid "Search Images" msgstr "Buscar imatges" #: f.meta.cc:4659 msgid "images to search:" msgstr "imatges a buscar:" #: f.meta.cc:4660 msgid "all" msgstr "tot" #: f.meta.cc:4661 msgid "current set only" msgstr "omés selecció actual" #: f.meta.cc:4664 msgid "matching images:" msgstr "imatges coïncidents" #: f.meta.cc:4665 msgid "make new set" msgstr "crear un nou conjunt" #: f.meta.cc:4666 msgid "add to set" msgstr "afegir a la selecció" #: f.meta.cc:4667 msgid "remove" msgstr "eliminar" #: f.meta.cc:4672 msgid "select last version only" msgstr "Seleccionar només la darrera versió" #: f.meta.cc:4673 msgid "original + last version" msgstr "original + última versió" #: f.meta.cc:4675 msgid "original + all versions" msgstr "original + totes les versions" #: f.meta.cc:4676 f.process.cc:166 f.process.cc:172 f.process.cc:180 #: f.process.cc:2058 msgid "no change" msgstr "sense canvis" #: f.meta.cc:4681 msgid "report type:" msgstr "tipus d'informe" #: f.meta.cc:4682 msgid "gallery" msgstr "galeria" #: f.meta.cc:4688 msgid "date range" msgstr "rand de dates" #: f.meta.cc:4693 msgid "photo date" msgstr "data de la foto" #: f.meta.cc:4694 msgid "file date" msgstr "data d'arxiu" #: f.meta.cc:4695 msgid "(yyyy-mm-dd)" msgstr "(aaaa-mm-dd" #: f.meta.cc:4700 msgid "rating range" msgstr "rang de qualificació" #: f.meta.cc:4708 msgid "all/any" msgstr "tot/qualsevol" #: f.meta.cc:4711 msgid "search tags" msgstr "buscar etiquetes" #: f.meta.cc:4718 msgid "search text" msgstr "buscar text" #: f.meta.cc:4724 msgid "search files" msgstr "buscar arxius" #: f.meta.cc:4730 msgid "search locations" msgstr "buscar localitats" #: f.meta.cc:4734 msgid "enter cities, countries" msgstr "introduïr ciutats, països" #: f.meta.cc:4739 msgid "search other metadata" msgstr "buscar altres metadades" #: f.meta.cc:4746 msgid "Enter Search Tag" msgstr "Introduïr etiquetes de cerca" #: f.meta.cc:5060 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:5067 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:5135 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "cerca de dades no raonable \n" " %s %s" #: f.meta.cc:5160 msgid "stars range not reasonable" msgstr "rang d'estrellas no raonable" #: f.meta.cc:5718 msgid "Always reported: date, stars, tags, caption, comment" msgstr "Informats sempre: data, estrelles, etiquetes, llegenda, comentari" #: f.meta.cc:5750 msgid "Additional Items for Report" msgstr "Detalls addicionals per a l'informe" #: f.meta.cc:5757 msgid "Keyword" msgstr "Paraula clau" #: f.meta.cc:5771 msgid "Match Criteria" msgstr "Criteri de coincidència" #: f.meta.cc:5904 #, c-format msgid "bad number: %s" msgstr "nombre erroni: %s" #: f.meta.cc:6184 msgid "date format is YYYY-MM-DD" msgstr "format de data es AAAA-MM-DD" #: f.meta.cc:6188 msgid "date is invalid" msgstr "data no vàlida" #: f.meta.cc:6226 msgid "time format is HH:MM [:SS]" msgstr "fomat d'hora es HH:MM [:SS]" #: f.meta.cc:6230 msgid "time is invalid" msgstr "hora no vàlida" #: f.meta.cc:7328 msgid "not found" msgstr "no trobada" #: f.meta.cc:7329 msgid "location and country required" msgstr "és necessari ubicació i pais" #: f.meta.cc:7586 msgid "choose location" msgstr "escollir ubicació" #: f.meta.cc:7886 msgid "Set Map Markers" msgstr "Establir marcadors de mapa" #: f.meta.cc:7887 msgid "mark all image files" msgstr "marcar totes les imatges" #: f.meta.cc:7888 msgid "mark current gallery" msgstr "marcar galeria actual" #: f.meta.cc:7989 msgid "choose map file" msgstr "escollir arxiu de mapes" #: f.meta.cc:8133 msgid "" "fotoxx-maps package not installed \n" "(see https://kornelix.net)" msgstr "" #: f.meta.cc:8138 #, c-format msgid "map file %s is missing" msgstr "es necessita arxiu de mapes %s" #: f.meta.cc:8142 #, 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:8468 f.meta.cc:9020 msgid "No matching images found" msgstr "No s'han trobat imatges coincidents" #: f.meta.cc:8562 msgid "Net Map Source" msgstr "Font del mapa de xarxa" #: f.meta.cc:9098 msgid "Net Map Locations" msgstr "Ubicacions Net Map" #: f.meta.cc:9104 msgid "map location:" msgstr "ubicació de mapa" #: f.pixmap.cc:3227 f.pixmap.cc:3279 msgid "HEIF files not supported" msgstr "Els arxius HEIF no són compatibles" #: f.pixmap.cc:3351 f.pixmap.cc:3402 msgid "JP2 files not supported" msgstr "" #: f.process.cc:134 f.widgets.cc:588 msgid "Batch Convert" msgstr "Convertir en lot" #: f.process.cc:145 msgid "Sequence Numbers" msgstr "Números seqüencials" #: f.process.cc:147 msgid "base" msgstr "inici" #: f.process.cc:150 msgid "adder" msgstr "increment" #: f.process.cc:155 msgid "New Location" msgstr "Nova ubicació" #: f.process.cc:160 msgid "New File Type" msgstr "Nou tipus d'arxiu" #: f.process.cc:169 f.process.cc:1390 msgid "Color Depth:" msgstr "Profunditat de color:" #: f.process.cc:175 f.process.cc:2053 msgid "max. Width" msgstr "Ample màx." #: f.process.cc:184 msgid "Delete Originals" msgstr "Esborrar originals" #: f.process.cc:185 f.process.cc:821 msgid "Copy Metadata" msgstr "Copiar metadades" #: f.process.cc:199 f.process.cc:824 msgid "Overlay Image" msgstr "Imatge de capa" #: f.process.cc:202 f.warp.cc:4547 msgid "Width %" msgstr "Amplada %" #: f.process.cc:207 msgid "Position" msgstr "Posició" #: f.process.cc:219 msgid "Make constant size for screen:" msgstr "Mida constant per a la pantalla" #: f.process.cc:227 msgid "" "plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s" msgstr "plugins: (any mes dia nom-antic seqüència) $aaaa $mm $dd $nomantic $s" #: f.process.cc:371 #, c-format msgid "file type not supported: %s \n" msgstr "tipus d'arxiu no suportat: %s \n" #: f.process.cc:507 f.process.cc:2116 msgid "cannot create new file" msgstr "no es pot crear un nou arxiu" #: f.process.cc:554 msgid "updating albums ..." msgstr "actualitzant àlbums ..." #: f.process.cc:732 #, c-format msgid "invalid plugin: %s" msgstr "plugin invàlid: %s" #: f.process.cc:739 msgid "you must use either $s or $oldname" msgstr "ha d'utilitzar $s o $nomantic" #: f.process.cc:744 msgid "$s plugin needs base and adder" msgstr "plugin $s necessita una base i un increment" #: f.process.cc:749 msgid "base and adder need $s plugin" msgstr "base i increment necessiten plugin $s" #: f.process.cc:763 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "max. mida %d x %d no és raonable" #: f.process.cc:770 msgid "specify overlay image file" msgstr "especificar imatge de capa" #: f.process.cc:786 msgid "specify overlay position" msgstr "especificar posició de capa" #: f.process.cc:814 #, c-format msgid "Convert %d image files" msgstr "Convertir %d imatges" #: f.process.cc:816 msgid "Convert to" msgstr "Convertir a" #: f.process.cc:818 msgid "Resize within" msgstr "Redimmensionar dins de" #: f.process.cc:819 msgid "Output to" msgstr "Sortida com" #: f.process.cc:825 msgid "*** Delete Originals ***" msgstr "*** Esborrar originals ***" #: f.process.cc:826 msgid "*** Replace Originals ***" msgstr "*** Substituir originals ***" #: f.process.cc:827 msgid "PROCEED?" msgstr "PROCEDIR?" #: f.process.cc:983 f.widgets.cc:589 msgid "Batch Upright" msgstr "Adressar en lot" #: f.process.cc:989 msgid "Survey all files" msgstr "Examinar tots els arxius" #: f.process.cc:1029 msgid "file cannot be read" msgstr "l'arxiu no pot ser llegit" #: f.process.cc:1146 msgid "cannot select both options" msgstr "no es poden sel·leccionar ambdues opcions" #: f.process.cc:1187 f.widgets.cc:590 msgid "Batch Delete/Trash" msgstr "Esborrar/paperera en lot" #: f.process.cc:1192 msgid "delete" msgstr "esborrar" #: f.process.cc:1195 msgid "trash" msgstr "paperera" #: f.process.cc:1258 msgid "Purging deleted files from albums \n" msgstr "Eliminant arxius esborrats des dels àlbums \n" #: f.process.cc:1338 msgid "Batch Convert RAW Files" msgstr "Convertir en lot arxius" #: f.process.cc:1377 msgid "output location" msgstr "ubicació exterior" #: f.process.cc:1382 msgid "File Type" msgstr "tipus d'arxiu" #: f.process.cc:1406 msgid "amount" msgstr "quantitat" #: f.process.cc:1409 msgid "threshold" msgstr "umbral" #: f.process.cc:1413 msgid "Fix dead pixels" msgstr "Corregeix píxels morts" #: f.process.cc:1416 msgid "dead pixel map file" msgstr "arxiu de mapa de píxels morts" #: f.process.cc:1419 msgid "Fix pixel bias" msgstr "Correcció del biaix de píxels" #: f.process.cc:1422 msgid "pixel bias map file" msgstr "arxiu de mapa de biaix de píxel" #: f.process.cc:1727 msgid "growisofs not installed" msgstr "growisofs no instal·lat" #: f.process.cc:1774 msgid "no DVD/BlueRay device found" msgstr "No es troba dispositiu DVD/Blue Ray" #: f.process.cc:1797 msgid "Burn Images to DVD/BlueRay" msgstr "Gravar imatges en un DVD/Blue Ray" #: f.process.cc:1802 msgid "Select device" msgstr "Sel·leccionar dispositiu" #: f.process.cc:1889 f.widgets.cc:355 msgid "Create a file of selected image files" msgstr "Crear un arxiu de les imatges sel·leccionades" #: f.process.cc:1915 f.process.cc:1984 msgid "Output File" msgstr "Arxiu de sortida" #: f.process.cc:1936 msgid "no input files selected" msgstr "No sel·leccionats arxius d'entrada" #: f.process.cc:1942 msgid "no output file selected" msgstr "No sel·leccionat arxiu de sortida" #: f.process.cc:2044 f.widgets.cc:594 msgid "Export Files" msgstr "Exportar imatges" #: f.process.cc:2049 msgid "To Location" msgstr "A ubicació" #: f.process.cc:2060 msgid "export metadata" msgstr "exportar metadades" #: f.process.cc:2091 msgid "file type not supported" msgstr "tipus d'arxiu no suportat" #: f.process.cc:2173 msgid "location is not a folder" msgstr "la ubicació no és al directori" #: f.process.cc:2225 msgid "Script Files" msgstr "Arxius de scripts" #: f.process.cc:2229 msgid "begin making a script file" msgstr "iniciar construcció d'un script" #: f.process.cc:2232 msgid "finish making a script file" msgstr "acabant construcció d'un script" #: f.process.cc:2276 msgid "script already started" msgstr "script iniciat" #: f.process.cc:2280 msgid "start a new script file" msgstr "iniciar un nou script" #: f.process.cc:2301 msgid "perform edits to be included in the script file" msgstr "performar edicions per ser inloses en l'script" #: f.process.cc:2318 msgid "script file error" msgstr "error d'script" #: f.process.cc:2323 #, c-format msgid "%s added to script" msgstr "%s afegit a l'script" #: f.process.cc:2333 msgid "no script file was started" msgstr "no s'ha iniciat l'script" #: f.process.cc:2341 msgid "script file closed" msgstr "script tancat" #: f.process.cc:2374 f.process.cc:2395 msgid "no script files found" msgstr "no s'han trobat arxius de scripts" #: f.process.cc:2418 #, c-format msgid "" "script error: %s \n" " %s" msgstr "" "error de script: %s \n" " %s" #: f.process.cc:2440 #, c-format msgid "unknown edit function: %s" msgstr "funció d'edició desconeguda: %s" #: f.process.cc:2452 #, c-format msgid "load widgets failed: %s" msgstr "carregar complements que han fallat: %s" #: f.process.cc:2468 #, c-format msgid "script file format error: %s" msgstr "error de format d'script: %s" #: f.process.cc:2494 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "error d'apertura: %s \n" " %s" #: f.process.cc:2511 msgid "script complete" msgstr "completat script" #: f.process.cc:2553 f.widgets.cc:603 msgid "Batch Script" msgstr "Scipt en lot" #: f.process.cc:2560 msgid "Select Script" msgstr "Seleccionar script" #: f.process.cc:2561 msgid "script file to run" msgstr "arxiu de script a executar" #: f.tools.cc:93 msgid "Folders for image files (subfolders included automatically)." msgstr "" "Directoris per arxius de imatge (els subdirectoris seràn automáticament " "inclosos)" #: f.tools.cc:95 msgid "Select to add, click on X to delete." msgstr "Sel·leccionar per afegir, cili la X per esborrar" #: f.tools.cc:96 msgid "folder for thumbnails" msgstr "directori per miniatures" #: f.tools.cc:97 msgid "extra metadata items to include in index" msgstr "ítems de metadades extra no inclosos en l'índex" #: f.tools.cc:98 msgid "force a full re-index of all image files" msgstr "forçar un re-indexat de totes les imatges" #: f.tools.cc:99 msgid "" "Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Acabada la funció d'indexat. \n" "L'indexat és requerit per cercar i funcions de mapa \n" "i oer fer les pàgines de galeria acceptablement ràpides." #: f.tools.cc:136 msgid "Index Image Files" msgstr "Arxiu índex d'imatges" #: f.tools.cc:324 msgid "Choose top image folders" msgstr "Seleccionar el dirctori d'imatges principal" #: f.tools.cc:325 msgid "Choose thumbnail folder" msgstr "Escollir directori de miniatures" #: f.tools.cc:326 msgid "" "All image files will be re-indexed. \n" " Continue?" msgstr "" "Tots els arxiusd'imatge seràn re-indexats. \n" " Continuar?" #: f.tools.cc:442 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:448 #, c-format msgid "" "Thumbnails folder: %s \n" "Please remove." msgstr "" "Carpeta de miniatures: %s \n" "Suprimir." #: f.tools.cc:451 #, c-format msgid "" "Thumbnails folder: \n" " %s \n" "must be named .../thumbnails" msgstr "" "El directori de miniatures: \n" " %s \n" "s'ha d'anomenar .../thumbnails" #: f.tools.cc:454 #, c-format msgid "" "Duplicate or nested folders: \n" " %s \n" " %s \n" "Please remove." msgstr "" "Carpetes duplicades o annexes: \n" "%s \n" "%s \n" "Suprimir." #: f.tools.cc:537 msgid "specify at least 1 top image folder" msgstr "especificar almenys una carpeta d'imatge superior" #: f.tools.cc:542 msgid "specify 1 thumbnail folder" msgstr "especificar un directori de miniatures" #: f.tools.cc:649 msgid "build index" msgstr "creació d'índex" #: f.tools.cc:778 msgid "Top folders have no images" msgstr "Directoris superiors no tenen im​at​ges" #: f.tools.cc:785 #, c-format msgid "" "0 old files found, %d new files found.\n" "A full re-index is required. Continue?" msgstr "" "S'han trobat 0 arxius antics, %d arxius nous trobats. \n" "Cal un índex complet. Continuar?" #: f.tools.cc:1286 msgid "Cancel image index function?" msgstr "Cancel·lar funció d'indexat d'imatge?" #: f.tools.cc:1447 #, c-format msgid "" "Do you want to move Fotoxx home? \n" " from: %s \n" " to: %s" msgstr "" "Voleu moure la ubicació de Fotoxx? \n" " de: %s \n" " a: %s" #: f.tools.cc:1449 msgid "moving files ..." msgstr "movent arxius" #: f.tools.cc:1473 msgid "new Fotoxx home folder" msgstr "nova carpeta d'inici de Fotoxx" #: f.tools.cc:1493 msgid "new location name contains a space" msgstr "el nom de la nova ubicació conté un espai" #: f.tools.cc:1533 msgid "index_config file has no thumbnails folder" msgstr "el arxiu index_config no té directori de miniatures" #: f.tools.cc:1541 msgid "update thumbnail folder failed" msgstr "actualització del directori de miniatures ha fallat" #: f.tools.cc:1544 msgid "thumbnails folder not changed" msgstr "directori de miniatures no ha canviat" #: f.tools.cc:1545 #, c-format msgid "new thumbnails folder: %s/thumbnails" msgstr "nou directori de miniatures: %s/thumbnails" #: f.tools.cc:1556 msgid "Fotoxx will restart" msgstr "Fotoxx es reiniciarà" #: f.tools.cc:1572 msgid "Recent Files Gallery" msgstr "Galeria d'arxius recents" #: f.tools.cc:1573 msgid "Newest Files Gallery" msgstr "Galeria d'arxius nous" #: f.tools.cc:1574 msgid "Specific Gallery" msgstr "Galeria específica" #: f.tools.cc:1575 msgid "Album Gallery" msgstr "Galeria d'àlbums" #: f.tools.cc:1576 msgid "Previous Gallery" msgstr "Galeria prèvia" #: f.tools.cc:1577 msgid "Previous File" msgstr "Imatge prèvia" #: f.tools.cc:1578 msgid "Specific File" msgstr "Imatge específica" #: f.tools.cc:1579 f.widgets.cc:434 msgid "Blank Window" msgstr "Finestra buida" #: f.tools.cc:1604 #, c-format msgid "%c of scale" msgstr "" #: f.tools.cc:1647 msgid "Preferences and Settings" msgstr "Preferències i configuracions" #: f.tools.cc:1650 msgid "Startup Display:" msgstr "Visualització d'inici:" #: f.tools.cc:1656 msgid "Background Colors:" msgstr "Colors de fons:" #: f.tools.cc:1658 msgid "F-View" msgstr "Vista d'arxius" #: f.tools.cc:1661 msgid "G-View" msgstr "Vista de galeries" #: f.tools.cc:1665 msgid "Menu Style:" msgstr "Estil de menú:" #: f.tools.cc:1667 msgid "Icons" msgstr "Icones" #: f.tools.cc:1669 msgid "Both" msgstr "Ambdós" #: f.tools.cc:1671 msgid "Icon size" msgstr "tamany d'icona" #: f.tools.cc:1675 msgid "Menu Colors:" msgstr "Colors del menú:" #: f.tools.cc:1684 msgid "Dialog Font:" msgstr "Tipografia de diàleg:" #: f.tools.cc:1703 msgid "JPEG file save quality:" msgstr "Qualitat per desar arxius JPEG:" #: f.tools.cc:1706 msgid "(90 = high quality)" msgstr "(90 = alta qualitat)" #: f.tools.cc:1709 msgid "TIFF file compression method" msgstr "Mètode de compressió d'arxius TIFF" #: f.tools.cc:1715 msgid "PNG file compression level" msgstr "Nivell de compressió del arxiu PNG" #: f.tools.cc:1721 msgid "Curve Node Separation, Capture Range:" msgstr "Separació de nodes de corba, interval de captura:" #: f.tools.cc:1727 msgid "Map Marker Size:" msgstr "Mida del marcador de mapa:" #: f.tools.cc:1733 msgid "Show Images (F-view, G-view):" msgstr "Mostra les imatges (vista Arxiu, vista Galeria):" #: f.tools.cc:1735 msgid "last version only" msgstr "només última versió" #: f.tools.cc:1736 msgid "all images" msgstr "totes les imatges" #: f.tools.cc:1739 msgid "Image Position in Window:" msgstr "Posició de la imatge a la finestra:" #: f.tools.cc:1741 msgid "centered" msgstr "centrat" #: f.tools.cc:1742 msgid "right side" msgstr "costat dret" #: f.tools.cc:1745 msgid "Image Index Level:" msgstr "Nivell d'índex d'imatge:" #: f.tools.cc:1749 msgid "Fotoxx started directly (2)" msgstr "Fotoxx s'ha iniciat directament (2)" #: f.tools.cc:1753 msgid "Fotoxx started by file manager (1)" msgstr "Fotoxx s'ha iniciat pel gestor de fitxers (1)" #: f.tools.cc:1756 msgid "RAW File Loader:" msgstr "Carregador de fitxers RAW:" #: f.tools.cc:1762 msgid "RAW Conversion Options:" msgstr "Opcions de conversió RAW:" #: f.tools.cc:1765 msgid "extend dynamic range for dim images" msgstr "ampliar el rang dinàmic per a imatges fosques" #: f.tools.cc:1768 msgid "use embedded image as a guide" msgstr "Utilitzar la imatge incrustada com a guia" #: f.tools.cc:1771 msgid "RAW File Types:" msgstr "Tipus d'arxius RAW:" #: f.tools.cc:1776 msgid "Video File Types:" msgstr "Tipus d'arxius de vídeo:" #: f.tools.cc:1781 msgid "Video File Play Command:" msgstr "Comando de reproducció de fitxers de vídeo:" #: f.tools.cc:1937 msgid "Select startup folder" msgstr "Seleccionar directori d'inici" #: f.tools.cc:1944 msgid "Select startup image file" msgstr "Seleccionarimatge d'inici" #: f.tools.cc:1951 msgid "Select startup album" msgstr "Sel·leccionar àlbum inicial" #: f.tools.cc:1976 msgid "rawtherapee-cli (rawtherapee) is not installed" msgstr "rawtherapee-cli (rawtherapee) no està instal·lat" #: f.tools.cc:2006 msgid "startup folder is invalid" msgstr "directori d'inici no és vàlid" #: f.tools.cc:2016 msgid "startup file is invalid" msgstr "arxiu d'inici no és vàlid" #: f.tools.cc:2216 f.widgets.cc:372 msgid "Keyboard Shortcuts" msgstr "Tecles drecera" #: f.tools.cc:2222 msgid "Reserved Shortcuts \n" msgstr "Dreceres de teclat reservades \n" #: f.tools.cc:2223 msgid " Z Toggle 1x / fit window \n" msgstr " Z Canvia 1x / ajust de la finestra \n" #: f.tools.cc:2224 msgid " F1 User Guide, Context Help \n" msgstr "F1 Guia d'usuari, ajuda contextual \n" #: f.tools.cc:2225 msgid " F10 Full Screen with menus \n" msgstr "F10 Pantalla completa amb menús \n" #: f.tools.cc:2226 msgid " F11 Full Screen without menus \n" msgstr "F11 Pantalla completa sense menús \n" #: f.tools.cc:2227 msgid " Escape Quit dialog, Quit Fotoxx \n" msgstr "Escape Diàleg sortir, sortir de Fotoxx \n" #: f.tools.cc:2228 msgid " Delete Delete/Trash \n" msgstr "Delete Esborrar/Paperera \n" #: f.tools.cc:2229 msgid " Arrow keys Navigation \n" msgstr "Tecles de fletxa Navegació \n" #: f.tools.cc:2230 msgid " Page keys Navigation \n" msgstr "Tecles de pàgina Navegació \n" #: f.tools.cc:2231 msgid " Home/End Navigation \n" msgstr " Inici/Fí Navegació \n" #: f.tools.cc:2295 msgid "Edit KB Shortcuts" msgstr "Editar tecles drecera" #: f.tools.cc:2302 msgid "shortcut key:" msgstr "tecla drecera" #: f.tools.cc:2303 msgid "(enter key)" msgstr "(entra tecla)" #: f.tools.cc:2304 f.tools.cc:2451 f.tools.cc:2554 msgid "(no selection)" msgstr "(cap selecció)" #: f.tools.cc:2445 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Reservada, no es pot utilitzar" #: f.tools.cc:2672 msgid "Brightness Distribution" msgstr "Distribució de la brillantor" #: f.tools.cc:2866 msgid "" "Drag mouse on image. \n" "Left click to cancel." msgstr "" "Arrossegar el ratolí sobre la imatge. \n" "Clic esquerra per cancel·lar." #: f.tools.cc:2893 f.widgets.cc:612 msgid "Magnify Image" msgstr "Augmentar imaten" #: f.tools.cc:2902 msgid "X-size" msgstr "Lupa" #: f.tools.cc:3210 msgid "Find Duplicate Images" msgstr "Trobar imatges duplicades" #: f.tools.cc:3213 msgid "All files" msgstr "Tots els arxius" #: f.tools.cc:3214 msgid "Current gallery" msgstr "Galeria actual" #: f.tools.cc:3217 msgid "File count:" msgstr "Nombre d'arxius:" #: f.tools.cc:3221 msgid "Thumbnail size" msgstr "Tamany de miniatura" #: f.tools.cc:3226 msgid "Pixel difference" msgstr "Diferència de píxels" #: f.tools.cc:3229 msgid "Pixel count" msgstr "Nombre de píxels" #: f.tools.cc:3237 msgid "Duplicates:" msgstr "Duplicats:" #: f.tools.cc:3567 msgid "too many files, cannot continue" msgstr "massa arxius, no es pot continuar" #: f.tools.cc:3647 msgid "Click image to select pixels." msgstr "Clic en la imatge per seleccionar píxels." #: f.tools.cc:3690 f.widgets.cc:614 msgid "Show RGB" msgstr "Mostrar RGB" #: f.tools.cc:3948 msgid "Change Color Profile" msgstr "Canviar perfil de color" #: f.tools.cc:3952 msgid "input profile" msgstr "perfil d'entrada" #: f.tools.cc:3956 msgid "output profile" msgstr "perfil de sortida" #: f.tools.cc:3977 msgid "Unable to change EXIF color profile" msgstr "No es pot canviar el perfil de color EXIF" #: f.tools.cc:3979 msgid "automatic new version created" msgstr "creada nova versió automàtica" #: f.tools.cc:3988 msgid "color profile" msgstr "perfil de color" #: f.tools.cc:4036 f.tools.cc:4042 #, c-format msgid "unknown cms profile %s" msgstr "perfil cms desconegut %s" #: f.tools.cc:4144 f.widgets.cc:616 msgid "Calibrate Printer" msgstr "Calibrar impresora" #: f.tools.cc:4170 msgid "print color chart" msgstr "imprimir carta de colors" #: f.tools.cc:4171 msgid "scan and save color chart" msgstr "escanejar y desar carta de colors" #: f.tools.cc:4172 msgid "align and trim color chart" msgstr "adressar i retalla carta de colors" #: f.tools.cc:4173 msgid "open and process color chart" msgstr "obrir i processar carta de colors" #: f.tools.cc:4174 msgid "print image with revised colors" msgstr "imprimir imatge amb colors revisats" #: f.tools.cc:4315 #, c-format msgid "" "Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/" msgstr "" "Escanejar la carta de color impresa. \n" "La columna mès fosca a dalt. \n" "Desar com %s/" #: f.tools.cc:4330 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY." msgstr "" "Obrir i editar la carta de color escanejada. \n" "Eliminar qualsevol gir o rotació de l'escanejat. \n" "Retallar AMB MOLTA CURA el prim marge verd." #: f.tools.cc:4362 msgid "Open the trimmed color chart file" msgstr "Obrir la carta de colors retallada" #: f.tools.cc:4495 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:4535 msgid "Color map file to use" msgstr "Arxiu de mapa de color per utilitzar" #: f.tools.cc:4555 msgid "Select the image file to print." msgstr "Sel·leccionar imatge a imprimir" #: f.tools.cc:4596 msgid "file format error" msgstr "error de format d'arxiu" #: f.tools.cc:4620 msgid "converting colors..." msgstr "convertint colors" #: f.tools.cc:4720 msgid "Image colors are converted for printing." msgstr "Colors de la imatge convertits per imprimir" #: f.tools.cc:4769 f.widgets.cc:617 msgid "Grid Lines" msgstr "Línies de graella" #: f.tools.cc:4778 msgid "x-spacing" msgstr "espaiat X" #: f.tools.cc:4779 msgid "x-count" msgstr "compte X" #: f.tools.cc:4780 msgid "x-enable" msgstr "habilitar X" #: f.tools.cc:4786 msgid "y-spacing" msgstr "espaiat Y" #: f.tools.cc:4787 msgid "y-count" msgstr "compta Y" #: f.tools.cc:4788 msgid "y-enable" msgstr "habilitar Y" #: f.tools.cc:4909 f.widgets.cc:618 msgid "Line Color" msgstr "Color de línees" #: f.tools.cc:4967 msgid "Darkest and Brightest Pixels" msgstr "Píxels més foscos i més brillants" #: f.tools.cc:4991 msgid "Dark Limit" msgstr "Ombra" #: f.tools.cc:4992 msgid "Bright Limit" msgstr "Llum" #: f.tools.cc:5117 msgid "Map RAW Pixel Bias" msgstr "Mapa Biaix Pixels RAW" #: f.tools.cc:5124 msgid "mean RGB:" msgstr "mitjana RGB" #: f.tools.cc:5210 msgid "select at least 10 RAW image files" msgstr "seleccionar almenys 10 fitxers d'imatge RAW" #: f.tools.cc:5230 #, c-format msgid "" "cannot read file \n" " %s" msgstr "" "no es pot llegir l'arxiu \n" " %s" #: f.tools.cc:5235 #, c-format msgid "" "not a RAW file \n" " %s" msgstr "" "no és un fitxer RAW \n" " %s" #: f.tools.cc:5249 #, c-format msgid "dimensions do not match: %s" msgstr "les dimensions no coincideixen: %s" #: f.tools.cc:5366 f.tools.cc:5449 msgid "Pixel Bias Map file" msgstr "Arxiu de mapa de biaix de píxels" #: f.tools.cc:5494 msgid "invalid pixel bias map file" msgstr "fitxer de mapa de biaix de píxels no vàlid" #: f.tools.cc:5524 msgid "image dimensions do not match pixel bias file" msgstr "" "les dimensions de la imatge no coincideixen amb el fitxer de biaix dels " "píxels" #: f.tools.cc:5610 msgid "Map RAW Dead Pixels" msgstr "Mapa de píxels RAW morts" #: f.tools.cc:5614 msgid "gray RAW image file" msgstr "arxiu d'imatge RAW gris" #: f.tools.cc:5618 msgid "RGB threshold" msgstr "Llindar RGB" #: f.tools.cc:5621 msgid "dead pixels found:" msgstr "s'han trobat píxels morts:" #: f.tools.cc:5658 msgid "not a RAW file" msgstr "no és un arxiu RAW" #: f.tools.cc:5664 msgid "cannot load RAW file" msgstr "no es pot carregar l'arxiu RAW" #: f.tools.cc:5727 msgid "choose a gray RAW file" msgstr "triar un arxiu RAW gris" #: f.tools.cc:5858 f.tools.cc:5903 msgid "dead pixels file" msgstr "arxiu de píxels morts" #: f.tools.cc:5955 msgid "invalid dead pixels file" msgstr "fitxer de píxels morts invàlid" #: f.tools.cc:5975 msgid "no dead pixels data available" msgstr "no hi ha dades de píxels morts disponibles" #: f.tools.cc:5980 msgid "image dimensions do not match dead pixels file" msgstr "" "les dimensions de la imatge no coincideixen amb el fitxer de píxels morts" #: f.tools.cc:6048 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:6208 msgid "Available Translations" msgstr "Traduccions disponibles" #: f.tools.cc:6212 msgid "Set Language" msgstr "Sel.leccionar idioma" #: f.warp.cc:110 f.widgets.cc:556 msgid "Unbend" msgstr "Redreçar" #: f.warp.cc:388 msgid "" " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle." msgstr "" " Clic en les quatre cantonades d'un àrea tetragonal. Polsar [Aplicar]. \n" " La imatge és deformada per que el tetràgon sigui un rectangle." #: f.warp.cc:405 msgid "Perspective Correction" msgstr "Corregir perspectiva" #: f.warp.cc:638 msgid "must have 4 corners" msgstr "ha de tenir 4 cantonades" #: f.warp.cc:763 msgid "" " Select an area to warp using select area function. \n" " Press [start warp] and pull area with mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, select another area or press [done]." msgstr "" " Seleccionar un àrea per deformar utilitzan la funció de selecció d'àrea. \n" " Prèmer [Iniciar deformació] i estirar l'àrea amb el ratolí. \n" " Múltiples estirades amb el ratolí fins a quedar satisfet. \n" " Quan hagi acabat, seleccionar un altre àrea o prèmer [Fet]." #: f.warp.cc:776 f.widgets.cc:558 msgid "Warp area" msgstr "Deformar àrea" #: f.warp.cc:781 msgid "start warp" msgstr "iniciar deformació" #: f.warp.cc:1182 f.warp.cc:1494 msgid "" " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Empènyer una posició de imatge utilitzan el ratolí. \n" " Múltiples empentes del ratolí fins a quedar satisfet. \n" " Quan hagi acabat, polsar [Fet]." #: f.warp.cc:1200 f.widgets.cc:559 msgid "Warp curved" msgstr "Deformació corba" #: f.warp.cc:1512 f.widgets.cc:560 msgid "Warp linear" msgstr "Deformació lineal" #: f.warp.cc:1825 msgid "" " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Empènyer en una cantonada de la imatge utilitzan el ratolí. \n" " Múltiples empentes del ratolí fins a quedar satisfet. \n" " Quan hagi acabat, polsar [Fet]." #: f.warp.cc:1841 f.widgets.cc:561 msgid "Warp affine" msgstr "Deformació afí" #: f.warp.cc:2174 msgid "" " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n" msgstr "" "Usar Seleccionar àrea per sel·leccionar una cara. \n" "Clic en el centre de distorsió. \n" "Moure el control. \n" #: f.warp.cc:2201 f.widgets.cc:562 msgid "Unwarp Closeup" msgstr "Desfer deformció d'aproximació" #: f.warp.cc:2379 msgid "Flatten Book Page" msgstr "Aplanar una pàgina impresa" #: f.warp.cc:2380 msgid "" "Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: " msgstr "" "Tallar la imatge per aïllar una pàgina \n" "Assenyalar les cantonades superior i inferior \n" "amb més de 4 clics del ratolí, després aplanar : " #: f.warp.cc:2383 msgid "Stretch curved-down surfaces:" msgstr "Adreçar superfícies curvades per sota" #: f.warp.cc:2438 msgid "Top:" msgstr "Adalt :" #: f.warp.cc:2441 msgid "Bottom:" msgstr "Abaix :" #: f.warp.cc:2830 msgid "" " Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]." msgstr "" " Sel·leccionar àrees que no han de canviar. \n" " Arrossegar imatge des de la cantonada superior esquerra. \n" " Quan acabeu, premeu [Fet]." #: f.warp.cc:2846 f.widgets.cc:564 msgid "Area Rescale" msgstr "Re-escalar àrea" #: f.warp.cc:2869 msgid "select areas first" msgstr "primer sel·leccionar àrees" #: f.warp.cc:3088 f.widgets.cc:565 msgid "Make Waves" msgstr "Ones" #: f.warp.cc:3095 msgid "wavelength" msgstr "longitut d'ona" #: f.warp.cc:3097 msgid "variance" msgstr "variança" #: f.warp.cc:3108 msgid "perspective" msgstr "prespectiva​" #: f.warp.cc:3285 f.warp.cc:3321 f.widgets.cc:566 msgid "Twist" msgstr "Girar" #: f.warp.cc:3318 f.warp.cc:3609 f.warp.cc:3835 f.warp.cc:4060 msgid "Drag mouse to set center" msgstr "Arrossegar ratolí per establir el centre" #: f.warp.cc:3565 msgid "Spherical Projection" msgstr "Projecció esfèrica" #: f.warp.cc:3792 msgid "Stretch Image" msgstr "Estirar imatge" #: f.warp.cc:4057 f.widgets.cc:569 msgid "Inside-out" msgstr "Dintre - fora" #: f.warp.cc:4065 f.warp.cc:4314 msgid "Center Hole" msgstr "Centre de forat" #: f.warp.cc:4266 msgid "image width must be greater than height" msgstr "l'amplada de la imatge ha de ser superior a l'alçada" #: f.warp.cc:4319 msgid "Cut Top" msgstr "Tallar per dalt" #: f.warp.cc:4324 msgid "Cut Bottom" msgstr "Tallar per abaix" #: f.warp.cc:4332 msgid "Reverse R" msgstr "R inversa" #: f.warp.cc:4333 msgid "Theta" msgstr "Theta" #: f.warp.cc:4545 msgid "Click mouse to change center" msgstr "Clic al ratolí per canviar de centre" #: f.warp.cc:4550 msgid "Rim %" msgstr "Retall %" #: f.widgets.cc:105 msgid "Album" msgstr "Àlbum" #: f.widgets.cc:107 msgid "TOP" msgstr "DALT" #: f.widgets.cc:167 msgid "Rename, copy/move, delete, print" msgstr "Canviar el nom, copiar/moure, suprimir, imprimir" #: f.widgets.cc:168 msgid "Thumbnails, bookmarks, albums, slide show" msgstr "Miniatures, marcadors, àlbums, presentació de diapositives" #: f.widgets.cc:169 msgid "View images by map location" msgstr "Veure imatges per localització de mapa" #: f.widgets.cc:170 msgid "Custom favorites menu" msgstr "Personalitzar menú favorits" #: f.widgets.cc:171 msgid "Left/right click: previous/next (also arrow keys)" msgstr "Clic esquerre/dret: anterior/següent (també tecles de fletxa)" #: f.widgets.cc:172 msgid "Left/right click: larger/smaller image/thumbnails" msgstr "" #: f.widgets.cc:173 msgid "Save modified file as new version or new file" msgstr "Desar arxiu modificat com a nova versió o nou arxiu" #: f.widgets.cc:174 msgid "Metadata: captions, tags, ratings, geotags, search images" msgstr "" "Metadades: subtítols, etiquetes, valoracions, geo-etiquetes, imatges de cerca" #: f.widgets.cc:175 msgid "Select areas to edit separately, save, copy and paste" msgstr "Seleccionar àrees per editar per separat, desar, copiar i enganxar" #: f.widgets.cc:176 msgid "" "Left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit" msgstr "" "Clic esquerre/dret: desfer/refer 1 edició \n" "amb la tecla A: desfer/refer totes les edicions \n" "clic mig: anar a qualsevol edició prèvia" #: f.widgets.cc:179 msgid "Image edit basic functions" msgstr "Funcions bàsiques d'edició d'imatge" #: f.widgets.cc:180 msgid "Image repair and enhance" msgstr "Reparar i millorar imatge" #: f.widgets.cc:181 msgid "Artistic effects (filters)" msgstr "Efectes artístics (filtres)" #: f.widgets.cc:182 msgid "Image warp, unwarp, transform" msgstr "Deformar imatge, adressar, transformar" #: f.widgets.cc:183 msgid "HDR, HDF, panorama, stack, mashup" msgstr "HDR, HDF, panorama, pila, muntatge" #: f.widgets.cc:184 msgid "Batch processing, custom scripts" msgstr "Processament per lots, scripts personalitzats" #: f.widgets.cc:185 msgid "Image index, user preferences, shortcuts, utilities" msgstr "Índex d'imatge, preferències d'usuari, dreceres, utilitats" #: f.widgets.cc:186 msgid "User Guide, recent changes, log file, about" msgstr "Guia de l'usuari, canvis recents, fitxer de registre, sobre" #: f.widgets.cc:189 msgid "Current File (R-click or key F)" msgstr "Arxiu actual (clic dret o tecla F)" #: f.widgets.cc:190 msgid "Open a parallel Fotoxx session" msgstr "Obrir una sessió paral·lela de Fotoxx" #: f.widgets.cc:191 msgid "Cycle 2 Prior Files" msgstr "Cicle 2 arxius previs" #: f.widgets.cc:192 msgid "Cycle 3 Prior Files" msgstr "Cicle 3 arxius previs" #: f.widgets.cc:193 msgid "View a 360 degree panorama image file" msgstr "Veure una imatge panoràmica de 360 graus" #: f.widgets.cc:194 msgid "Change file name" msgstr "Canvia nom de la imatge" #: f.widgets.cc:195 msgid "View and change file permissions" msgstr "Veure i canviar els permisos de l'arxiu" #: f.widgets.cc:196 msgid "Create a blank image" msgstr "Crear una imatge buida" #: f.widgets.cc:197 msgid "Toggle - blank or restore window" msgstr "Canviar a blanc o restaurar imatge" #: f.widgets.cc:198 msgid "Copy or Move file to new location" msgstr "Copiar o moure un arxiu a una nova ubicació" #: f.widgets.cc:199 msgid "Copy file to the desktop" msgstr "Copiar una imatge a l'escriptori" #: f.widgets.cc:200 msgid "Copy file to the clipboard" msgstr "Copiar imatge al portapapers" #: f.widgets.cc:201 msgid "Set file as desktop wallpaper (GNOME)" msgstr "Establr arxiu com fons de pantalla (GNOME)" #: f.widgets.cc:202 msgid "Show location on Internet map" msgstr "Mostrar localitat en mapa Internet" #: f.widgets.cc:203 msgid "Delete or trash file" msgstr "Eliminar o esborrar una imatge" #: f.widgets.cc:204 msgid "Print the current image" msgstr "Impprimir la imatge actual" #: f.widgets.cc:205 msgid "Print current image with adjusted colors" msgstr "Imprimir imatge actual amb els colors ajustats" #: f.widgets.cc:206 msgid "Quit Fotoxx" msgstr "Sortir de Fotoxx" #: f.widgets.cc:209 msgid "Thumbnail Gallery (R-click or key G)" msgstr "Galeria de miniatures (clic dret o tecla G)" #: f.widgets.cc:210 msgid "Gallery view with thumbnails and basic metadata" msgstr "Vista de la galeria amb miniatures i metadades bàsiques" #: f.widgets.cc:211 msgid "Gallery view with small thumbnails and file names" msgstr "Vista de galeria amb miniatures petites i noms d'arxius" #: f.widgets.cc:212 msgid "Gallery of recently viewed image files" msgstr "Galeria d'imatges visualitzades recentment" #: f.widgets.cc:213 msgid "Gallery of newest image files" msgstr "Galeria d'imatges més recents" #: f.widgets.cc:214 msgid "Jump to beginning [home]" msgstr "Saltar al principi [inici]" #: f.widgets.cc:215 msgid "Jump to end [end]" msgstr "Salta al final [final]" #: f.widgets.cc:216 msgid "Set gallery from current image file" msgstr "Establir galeria des de l'imatge actual" #: f.widgets.cc:217 msgid "Change sort order" msgstr "Canviar criteri d'ordenació" #: f.widgets.cc:218 msgid "List all folders, click any for gallery view" msgstr "Listar tots els directoris, clic en qualsevol per vista de galeria" #: f.widgets.cc:219 msgid "select input files for album, batch, script functions" msgstr "" "Seleccionar arxius d'entrada per a funcions d'àlbums, per lots, per a scripts" #: f.widgets.cc:220 msgid "Set and recall bookmarked image locations" msgstr "Establir i recuperar imatges ambs ubicacions marcades" #: f.widgets.cc:221 msgid "Organize images into albums" msgstr "Organitzar imatges en àlbums" #: f.widgets.cc:222 msgid "Update albums for new file versions" msgstr "Actualitzar àlbums per a versions d'arxius nous" #: f.widgets.cc:223 msgid "Mass update album files" msgstr "Actualització massiva de arxiurs d'àlbum" #: f.widgets.cc:224 msgid "Save current gallery as album" msgstr "Desa la galeria actual com a àlbum" #: f.widgets.cc:225 msgid "Start a slide show" msgstr "Iniciar un diaporama" #: f.widgets.cc:228 msgid "Maps (R-click or key M)" msgstr "Mapes (clic dret o tecla M)" #: f.widgets.cc:229 msgid "Open Internet map" msgstr "Obrir mapa d'Internet" #: f.widgets.cc:230 msgid "Choose Internet map source" msgstr "Escollir font del mapa d'internet" #: f.widgets.cc:231 msgid "Internet map locations" msgstr "Localitzacions de mapa d'Internet" #: f.widgets.cc:232 msgid "Open file map" msgstr "Obrir arxiu de mapa" #: f.widgets.cc:233 msgid "Choose file map" msgstr "Escollir arxiu de mapa" #: f.widgets.cc:234 msgid "Set map markers for all images or current gallery" msgstr "" "Establir els marcadors de mapa per a totes les imatges o galeria actual" #: f.widgets.cc:237 msgid "List a few key metadata items" msgstr "Llistar algunes dades clau de metadades" #: f.widgets.cc:238 msgid "List all metadata items" msgstr "Listar totes les metadades" #: f.widgets.cc:239 msgid "Edit image tags/geotags/caption/rating ..." msgstr "Editar etiquetes/geoetiquetes/títol/valoració ..." #: f.widgets.cc:240 msgid "Define tags (keywords) used for searching images" msgstr "Definir les etiquetes (paraules clau) utilitzades per cercar imatges" #: f.widgets.cc:241 msgid "Edit any image metadata" msgstr "Editar qualsevol metadada de la imatge" #: f.widgets.cc:242 msgid "Remove selected image metadata" msgstr "Eliminar les metadades seleccionades" #: f.widgets.cc:243 msgid "Show file name, captions, comments" msgstr "Mostrar nom de l'arxiu, títols, comentaris" #: f.widgets.cc:244 msgid "Find all images for a location [date]" msgstr "Trobar totes les imatges per una ubicació [data]" #: f.widgets.cc:245 msgid "Show image counts by month, select and report" msgstr "Mostar quantitat d'imatges per mes,seleccionar i fer informe" #: f.widgets.cc:246 msgid "Find images meeting search criteria" msgstr "Cercar imatges que compleixin els criteris de cerca" #: f.widgets.cc:249 msgid "Select object or area for editing" msgstr "Seleccionar objecte o àrea per editar" #: f.widgets.cc:250 msgid "Select hairy or irregular edge" msgstr "Seleccioneu una vora esfilagarsada o irregular" #: f.widgets.cc:251 msgid "Find a gap in an area outline" msgstr "Trobar separació en un contorn d'àrea" #: f.widgets.cc:252 msgid "Show (outline) existing area" msgstr "Mostrar un àrea existent (contorn)" #: f.widgets.cc:253 msgid "Hide existing area" msgstr "Ocultar àrea existent" #: f.widgets.cc:254 msgid "Enable area for editing" msgstr "Activar àrea per editar" #: f.widgets.cc:255 msgid "Disable area for editing" msgstr "Desactivat àrea per editar" #: f.widgets.cc:256 msgid "Reverse existing area" msgstr "Invertir àrea existent" #: f.widgets.cc:257 msgid "Erase existing area" msgstr "Esborrar àrea existent" #: f.widgets.cc:258 msgid "Copy area for later pasting into image" msgstr "Copiar àrea per posterior enganxat en una imatge" #: f.widgets.cc:259 msgid "Paste previously copied area into image" msgstr "Enganxar en la imatge àrea previament copiada" #: f.widgets.cc:260 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:261 msgid "Save area to a file with transparency" msgstr "Desar àrea en un arxiu amb tranparència" #: f.widgets.cc:264 msgid "Trim/Crop margins and/or Rotate" msgstr "Retallar/tallar marges i/o girar" #: f.widgets.cc:265 msgid "Auto upright a rotated image based on EXIF data" msgstr "Adreçar verticalment una imatge rotada basat en dades EXIF" #: f.widgets.cc:266 msgid "Adjust brightness, contrast, color" msgstr "Ajustar brillantor, contrast, color" #: f.widgets.cc:267 msgid "Change pixel dimensions" msgstr "Canviar dimensions de píxels" #: f.widgets.cc:268 msgid "Adjust color using RGB or CMY colors" msgstr "Ajustar color usant sistema RGB o CMY" #: f.widgets.cc:269 msgid "Adjust color using HSL colors" msgstr "Ajustar color usant sistema HSL" #: f.widgets.cc:270 msgid "Draw on image: text, line/arrow, box, ellipse" msgstr "Traçar en la la imatge: text, línia/fletxa, caixa, el·lipse" #: f.widgets.cc:271 msgid "Paint image pixels using the mouse" msgstr "Pinta pixles utilitzant el ratolí" #: f.widgets.cc:272 msgid "Copy pixels within an image using the mouse" msgstr "Copiar píxels de una imatge usant el ratolí" #: f.widgets.cc:273 msgid "Copy pixels from one image to another using the mouse" msgstr "Copiar píxels des d'una imatge a una altra usant el ratolí" #: f.widgets.cc:274 msgid "Paint edit function gradually with mouse" msgstr "Funció d'edició pintant gradualment amb el ratolí" #: f.widgets.cc:275 msgid "Incrementally undo prior edits gradually with mouse" msgstr "" "Desfer incrementalment les edicions anteriors gradualment amb el ratolí" #: f.widgets.cc:276 msgid "Edit plugins menu or run a plugin function" msgstr "Editar menú de plugins o executar una funció de plugin" #: f.widgets.cc:277 msgid "Specialized program for editing RAW files" msgstr "Programa especialitzat per editar fitxers RAW" #: f.widgets.cc:280 f.widgets.cc:281 msgid "Fast auto enhance that may work OK" msgstr "Auto-millora ràpida" #: f.widgets.cc:282 msgid "Edit brightness distribution" msgstr "Editar histograma" #: f.widgets.cc:283 msgid "Magnify brightness gradients to enhance details" msgstr "Augmentar gradients de brillantor per millorar detalls" #: f.widgets.cc:284 msgid "Flatten brightness distribution to enhance details" msgstr "Aplanar histograma per millorar els detalls" #: f.widgets.cc:285 msgid "Rescale RGB - reduce color caste and fog/haze" msgstr "Re-escalar RGB - reduïr dominant de color i la boira" #: f.widgets.cc:286 msgid "Make the image look sharper" msgstr "Fer que la imatge es vegi més enfocada" #: f.widgets.cc:287 msgid "Blur the image, different methods" msgstr "Desenfocae la imatge, diferents mètodes" #: f.widgets.cc:288 msgid "Filter noise from low-light photos" msgstr "Filtrar soroll de fotografíes fetes amb poca llum" #: f.widgets.cc:289 msgid "Fix red-eyes from electronic flash" msgstr "Corretgir ulls vermells produïts per un flash electrònic" #: f.widgets.cc:290 msgid "Match colors on one image with another" msgstr "Fer concordar colors d'una imatge amb els d'altra" #: f.widgets.cc:291 msgid "Remove unwanted objects" msgstr "Eliminar objectes no desitjats" #: f.widgets.cc:292 msgid "Fix color fringes in outer areas of an image" msgstr "corregir franges de colors a les zones exteriors d'una imatge" #: f.widgets.cc:293 msgid "Fix color band on dark/bright feature edges" msgstr "Corregir banda de color als marges foscos/brillants" #: f.widgets.cc:294 msgid "Change brightness or color radially" msgstr "Canviar brillantor o color radialment" #: f.widgets.cc:295 msgid "Remove dust spots from scanned slides" msgstr "Eliminar pols de diapositives escanejades" #: f.widgets.cc:298 msgid "Convert to simulated sketch" msgstr "Convertir a simulació de'esbós" #: f.widgets.cc:299 msgid "Convert image into a cartoon drawing" msgstr "Convertir imatge a un dibuix a tintes planes" #: f.widgets.cc:300 msgid "Convert to line drawing (edge detection)" msgstr "Convertir a un dibuix de línees (detecció de vores)" #: 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 dithered dots" msgstr "Convertir a punts entramats" #: f.widgets.cc:304 msgid "Convert into a simulated painting" msgstr "Convertir a una pintura simulada" #: f.widgets.cc:305 msgid "Add texture to an image" msgstr "Afegir textura a una imatge" #: f.widgets.cc:306 msgid "Tile image with a repeating pattern" msgstr "Enrajolñar imatge amb un patrò repetitiu" #: f.widgets.cc:307 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:308 msgid "Make BW/color, negative/positive, sepia" msgstr "Blanc i negre/color, negatiu/positiu, sèpia" #: f.widgets.cc:309 msgid "Reduce color depth (posterize)" msgstr "Reduïr profunditat de color (posteritzar)" #: f.widgets.cc:310 msgid "Shift/convert colors into other colors" msgstr "Canviar/convertir uns colors en altres" #: f.widgets.cc:311 msgid "Change color hue using an algorithm" msgstr "Canviar to de color utilitzant el retolí" #: f.widgets.cc:312 msgid "Add a brightness/color ramp across the image" msgstr "Afegir una rampa de broillantor o color a través de la imatge" #: f.widgets.cc:313 msgid "Paint image transparency using the mouse" msgstr "Pintar transparència d'imatge amb el ratolí" #: f.widgets.cc:314 msgid "Mirror image horizontally or vertically" msgstr "Enmirallar la imatge horizontalment o verticalment" #: f.widgets.cc:315 msgid "Process an image using a custom kernel" msgstr "Processar una imatge fent servir una matriu personalitzada" #: f.widgets.cc:318 msgid "Remove curvature, esp. panoramas" msgstr "Eliminar corbatura, especialment d'un panorama" #: f.widgets.cc:319 msgid "Straighten objects seen from an angle" msgstr "Redreçar objectes vistos des d'un angle" #: f.widgets.cc:320 msgid "Distort image areas using the mouse" msgstr "Distorsionar una àrea de una imatge utilitzant el ratolí" #: f.widgets.cc:321 msgid "Unwarp closeup face photo to remove distortion" msgstr "Desfer deformació d'aproximació per desfer la distorsió" #: f.widgets.cc:322 f.widgets.cc:323 f.widgets.cc:324 msgid "Distort the whole image using the mouse" msgstr "Distorsionar tota la imatge utilitzant el ratolí" #: f.widgets.cc:325 msgid "Flatten a photographed book page" msgstr "Aplanar una pàgina impresa fotografiada" #: f.widgets.cc:326 msgid "Rescale image outside selected areas" msgstr "Re-escalar imatge per fora de les àrees sel·leccionades" #: f.widgets.cc:327 msgid "Warp an image with a wave pattern" msgstr "Deformar una imatge amb ​un patró d'ones" #: f.widgets.cc:328 msgid "Twist image centered at mouse position" msgstr "Girar imatge centrada en la posició del ratolí" #: f.widgets.cc:329 msgid "Make a spherical projection of an image" msgstr "Fer una projecció esfèrica de una imatge" #: f.widgets.cc:330 msgid "Image scale increases from center to edge" msgstr "L'escala d'imatge augmenta de centre a vora" #: f.widgets.cc:331 msgid "Turn an image inside-out" msgstr "Gireu una imatge a fora a dins" #: f.widgets.cc:332 msgid "Convert an image into a Tiny Planet" msgstr "Converteix una imatge en un petit planeta" #: f.widgets.cc:333 msgid "Generate an inward spiraling recursive image" msgstr "Generar una imatge recursiva en espiral cap a dins" #: f.widgets.cc:336 msgid "Combine bright/dark images for better detail" msgstr "Combinar imatges fosques/il.luminadas per millorar detalls" #: f.widgets.cc:337 msgid "Combine near/far focus images for deeper focus" msgstr "" "Combinar imatges enfocades aprop/lluny per obtenir major profunditat de camp" #: f.widgets.cc:338 msgid "Combine images to erase passing people, etc." msgstr "Combinar imatges per esborrar vianants, cotxes, etc" #: f.widgets.cc:339 msgid "Combine noisy images into a low-noise image" msgstr "Combinar imatges amb soroll en imatges amb poc soroll" #: f.widgets.cc:340 msgid "Combine image layers, mouse select and expose" msgstr "Combinar les capes d'imatge, seleccionar amb el ratolí i exposeu-les" #: f.widgets.cc:341 msgid "Compare two images separated by sliding boundary" msgstr "Compareu dues imatges separades per un límit lliscant" #: f.widgets.cc:342 msgid "Show differences between two images" msgstr "Mostrar deferències entre dues imatges" #: f.widgets.cc:343 msgid "Combine images into a panorama" msgstr "Combinar imatges en un panorama" #: f.widgets.cc:344 msgid "Combine images into a vertical panorama" msgstr "Combinar imatges en un panorama vertical" #: f.widgets.cc:345 msgid "Combine images into a panorama (panorama tools)" msgstr "Combinar imatges en un panorama (panorama tools)" #: f.widgets.cc:346 msgid "Combine images into a montage of images" msgstr "Combinar imatges en un muntatge d'imatges" #: f.widgets.cc:347 msgid "Arrange images and text in a layout (montage)" msgstr "Ordenar imatges i text en una capa (muntatge)" #: f.widgets.cc:350 msgid "Rename/convert/resize/move multiple files" msgstr "Reanomenar/convertir/redimensionar/moure múltiples arxius" #: f.widgets.cc:351 msgid "Upright multiple rotated image files" msgstr "Adressar múltiples imatges girades" #: f.widgets.cc:352 msgid "Delete or Trash multiple files" msgstr "Esborrar/eliminar múltiples arxius" #: f.widgets.cc:353 msgid "Convert camera RAW files to tiff/png/jpeg" msgstr "Convertir fitxers RAW de càmera a tiff/png/jpeg" #: f.widgets.cc:354 msgid "Burn selected image files to DVD/BlueRay disc" msgstr "Gravar imatge sel·leccionada a un DVD o Blue Ray" #: f.widgets.cc:356 msgid "Export selected image files to a folder" msgstr "Exportar les imatges sel·leccionades a un directori" #: f.widgets.cc:357 msgid "Add/remove tags for multiple images" msgstr "Afegir/eliminar etiquetes per múltiples imatges " #: f.widgets.cc:358 msgid "Convert tag names for all images" msgstr "Convertir noms d'etiqueta per totes les imatges" #: f.widgets.cc:359 msgid "change or shift photo dates/times" msgstr "canviar data/hora de la foto" #: f.widgets.cc:360 msgid "Add/change/delete metadata for multiple images" msgstr "Afegir/canviar/esborrar metadades a múltiples imatges" #: f.widgets.cc:361 msgid "Report metadata for multiple images" msgstr "Informe de m​eatada​des per múltiples imatges" #: f.widgets.cc:362 msgid "Add/revise geotags for multiple images" msgstr "Afegir/revisar geo-etiquetes a múltiples imatges" #: f.widgets.cc:363 msgid "Build a custom script with multiple edit functions" msgstr "Construïr un script personalitzat amb múltiples funcions d'edició" #: f.widgets.cc:364 msgid "Run custom script to edit the current image file" msgstr "Executar un script personalitzat per editar la imatge actual" #: f.widgets.cc:365 msgid "Run custom script to edit a batch of image files" msgstr "Executar un script personalitzat per editar un lot de imatges" #: f.widgets.cc:368 msgid "Index new files and make thumbnails" msgstr "Indexar nous arxius i fer miniatures" #: f.widgets.cc:369 msgid "Quick incremental index update" msgstr "Actualització ràpida de l’índex incremental " #: f.widgets.cc:370 msgid "Move Fotoxx home folder" msgstr "Moure la carpeta d'inici de Fotoxx" #: f.widgets.cc:371 msgid "User preferences and settings" msgstr "Preferències i configuracions de l'usuari" #: f.widgets.cc:373 msgid "Show RGB brightness distribution" msgstr "Mostrar histograma RGB" #: f.widgets.cc:374 msgid "Magnify image around the mouse position" msgstr "Augmenta la imatge al voltant de la posició del ratolí" #: f.widgets.cc:375 msgid "Search all image files and report duplicates" msgstr "Buscar totes les imatges i informar dels duplicats" #: f.widgets.cc:376 msgid "Show RGB colors at mouse click" msgstr "Mostrar colors RGB amb un clic de ratolí" #: f.widgets.cc:377 msgid "Convert to another color profile" msgstr "Convertir a un perfil de color" #: f.widgets.cc:378 msgid "Calibrate printer colors" msgstr "Calibrar colors d'impresora" #: f.widgets.cc:379 msgid "Show or revise grid lines" msgstr "Mostrar o amagar línies de graella" #: f.widgets.cc:380 msgid "Change color of foreground lines" msgstr "Canviar color a línies de contorn" #: f.widgets.cc:381 msgid "Highlight darkest and brightest pixels" msgstr "Resaltar píxels més foscos o més brillants" #: f.widgets.cc:382 msgid "map raw pixel bias (camera sensor, vignette)" msgstr "mapa de biaix de píxels raw (sensor de càmera, vinyeta)" #: f.widgets.cc:383 msgid "map raw dead pixels (camera sensor)" msgstr "mapar píxels raw morts (sensor de càmera)" #: f.widgets.cc:384 msgid "Chart to adjust monitor color" msgstr "Carta per ajustar el color del monitor" #: f.widgets.cc:385 msgid "Chart to adjust monitor gamma" msgstr "Carta per ajustar la gamma del monitor" #: f.widgets.cc:386 msgid "Change the GUI language" msgstr "Canviar idioma de la interfície d'usuari" #: f.widgets.cc:387 msgid "Report missing translations" msgstr "Informe de traduccions que falten" #: f.widgets.cc:388 msgid "Anonymous usage statistics" msgstr "Estadístiques d'ús anònimes" #: f.widgets.cc:389 msgid "Memory and CPU (to terminal/logfile)" msgstr "Memoria i CPU (al terminal/arxiu de registre)" #: f.widgets.cc:390 msgid "List files included in appimage container" msgstr "Llista de fitxers inclosos en el contenidor de la imatge" #: f.widgets.cc:391 msgid "test crash report with source line numbers" msgstr "informe de prova d'errors amb número de línia de l'orígen" #: f.widgets.cc:394 msgid "Read the user guide" msgstr "Llegir la guia d'usuari" #: f.widgets.cc:395 msgid "Recent user guide changes" msgstr "Canvis recents a la guia d'usuari" #: f.widgets.cc:396 msgid "Overview of all edit functions" msgstr "Visió general de totes les funcions d'edició" #: f.widgets.cc:397 msgid "List updates by Fotoxx version" msgstr "Llistar actualitzacions per versió de Fotoxx" #: f.widgets.cc:398 msgid "View the log file and error messages" msgstr "Veure l'arxiu de registre i missatges d'error" #: f.widgets.cc:399 msgid "Fotoxx Man Page - summary of capabilities" msgstr "Pàgina de manual de Fotoxx: resum de les capacitats" #: f.widgets.cc:400 msgid "List command line parameters" msgstr "Llistar paràmetres de comandamente de línia" #: f.widgets.cc:401 msgid "How to do Fotoxx translations" msgstr "Com fer traduccións de Fotoxx" #: f.widgets.cc:402 msgid "Show the Fotoxx web page" msgstr "Mostrar la pàgina web de Fotoxx" #: f.widgets.cc:403 msgid "Fotoxx license - terms of use" msgstr "Llicència Fotoxx - termes d'ús" #: f.widgets.cc:404 msgid "Fotoxx privacy policy" msgstr "Política de privacitat de Fotoxx" #: f.widgets.cc:405 msgid "Version, contact, credits" msgstr "Versió, contacte, crèdits" #: f.widgets.cc:425 msgid "File View F" msgstr "Vista d'arxius F" #: f.widgets.cc:426 msgid "New Session" msgstr "Nova sessió" #: f.widgets.cc:427 f.widgets.cc:453 msgid "Source Folder" msgstr "Carpeta d'origen" #: f.widgets.cc:428 msgid "Cycle 2" msgstr "Cicle 2" #: f.widgets.cc:429 msgid "Cycle 3" msgstr "Cicle 3" #: f.widgets.cc:430 msgid "View 360° Pano" msgstr "Veure panorama 360º" #: f.widgets.cc:431 f.widgets.cc:820 fotoxx.h:1414 msgid "Rename" msgstr "Canviar el nom" #: f.widgets.cc:433 msgid "Blank Image" msgstr "Imatge en blanc" #: f.widgets.cc:435 f.widgets.cc:822 msgid "Copy/Move" msgstr "Copiar/Moure" #: f.widgets.cc:436 f.widgets.cc:823 msgid "Copy to Desktop" msgstr "Copiar a l'escriptori" #: f.widgets.cc:437 f.widgets.cc:824 msgid "Copy to Clipboard" msgstr "Copiar al portapapers" #: f.widgets.cc:438 msgid "Set Wallpaper" msgstr "Establir el fons de pantalla" #: f.widgets.cc:439 f.widgets.cc:839 msgid "Show on Map" msgstr "Mostrar en el mapa" #: f.widgets.cc:440 f.widgets.cc:840 msgid "Delete/Trash" msgstr "Esborrar/Eliminar" #: f.widgets.cc:441 msgid "Print" msgstr "Inprimir" #: f.widgets.cc:442 msgid "Print Calibrated" msgstr "Imprimir calibrat" #: f.widgets.cc:443 fotoxx.h:1407 msgid "Quit" msgstr "Sortir" #: f.widgets.cc:446 msgid "Gallery View G" msgstr "Vista de galeria G" #: f.widgets.cc:447 msgid "Meta View" msgstr "Vista de metadades" #: f.widgets.cc:448 msgid "List View" msgstr "Vista de llista" #: f.widgets.cc:449 msgid "Recent" msgstr "Recent" #: f.widgets.cc:450 msgid "Newest" msgstr "El més nou" #: f.widgets.cc:451 msgid "GoTo First" msgstr "Anar al primer" #: f.widgets.cc:452 msgid "GoTo Last" msgstr "Anar a l'últim" #: f.widgets.cc:454 msgid "Sort Gallery" msgstr "Orfdenar Galeria" #: f.widgets.cc:455 msgid "All Folders" msgstr "Tots els directoris" #: f.widgets.cc:456 fotoxx.h:1425 msgid "Select Files" msgstr "Seleccionar arxius" #: f.widgets.cc:459 msgid "Update Albums" msgstr "Actualitzar àlbums" #: f.widgets.cc:461 msgid "Gallery to Album" msgstr "Galeria a àlbum" #: f.widgets.cc:465 msgid "Map View M" msgstr "Vista de mapa M" #: f.widgets.cc:466 msgid "Net Map" msgstr "Mapa de xarxa" #: f.widgets.cc:467 msgid "Net Source" msgstr "Origen en la xarxa" #: f.widgets.cc:468 msgid "Net Locs" msgstr "Localitats en la xarxa" #: f.widgets.cc:469 msgid "File Map" msgstr "Arxiu de mapa" #: f.widgets.cc:470 msgid "Choose Map" msgstr "Escollir mapa" #: f.widgets.cc:471 msgid "Markers" msgstr "Marcadors" #: f.widgets.cc:474 f.widgets.cc:816 msgid "View Meta" msgstr "Veure metadades" #: f.widgets.cc:475 f.widgets.cc:817 msgid "View All Meta" msgstr "Veure totes les metadades" #: f.widgets.cc:476 f.widgets.cc:818 msgid "Edit Meta" msgstr "Editar metadades" #: f.widgets.cc:478 f.widgets.cc:819 msgid "Edit Any Meta" msgstr "Editar qualsevol metadada" #: f.widgets.cc:479 msgid "Delete Meta" msgstr "Esborrar metadada" #: f.widgets.cc:480 msgid "Captions" msgstr "Títols" #: f.widgets.cc:481 msgid "Places/Dates" msgstr "Llocs/Dates" #: f.widgets.cc:482 msgid "Timeline" msgstr "Línia de temps" #: f.widgets.cc:483 fotoxx.h:1422 msgid "Search" msgstr "Buscar" #: f.widgets.cc:486 fotoxx.h:1424 msgid "Select" msgstr "Seleccionar" #: f.widgets.cc:488 msgid "Find Gap" msgstr "Trobar separació" #: f.widgets.cc:489 fotoxx.h:1426 msgid "Show" msgstr "Mostrar" #: f.widgets.cc:490 fotoxx.h:1367 msgid "Hide" msgstr "Ocultar" #: f.widgets.cc:491 fotoxx.h:1351 msgid "Enable" msgstr "Activar" #: f.widgets.cc:492 fotoxx.h:1346 msgid "Disable" msgstr "Desactivar" #: f.widgets.cc:494 fotoxx.h:1333 msgid "Clear" msgstr "Esborrar" #: f.widgets.cc:495 fotoxx.h:1340 msgid "Copy" msgstr "Copiar" #: f.widgets.cc:496 fotoxx.h:1399 msgid "Paste" msgstr "Enganxar" #: f.widgets.cc:497 fotoxx.h:1376 msgid "Load" msgstr "Carregar" #: f.widgets.cc:498 f.widgets.cc:654 fotoxx.h:1421 zfuncs.cc:12499 msgid "Save" msgstr "Desar" #: f.widgets.cc:507 msgid "Markup" msgstr "Marcador" #: f.widgets.cc:508 msgid "Paint Image" msgstr "Pintar imatge" #: f.widgets.cc:509 msgid "Copy Pixels 1" msgstr "Copiar píxels 1" #: f.widgets.cc:510 msgid "Copy Pixels 2" msgstr "Copiar píxels 2" #: f.widgets.cc:513 msgid "Plugins" msgstr "Plugins" #: f.widgets.cc:514 f.widgets.cc:838 msgid "Raw Therapee" msgstr "Raw Therapee" #: f.widgets.cc:517 msgid "Voodoo 1" msgstr "Auto-millora 1" #: f.widgets.cc:518 msgid "Voodoo 2" msgstr "Auto-millora 2" #: f.widgets.cc:519 f.widgets.cc:834 msgid "Brite Dist" msgstr "Histograma" #: f.widgets.cc:520 f.widgets.cc:836 msgid "Gradients" msgstr "Gradients" #: f.widgets.cc:521 f.widgets.cc:835 fotoxx.h:1360 msgid "Flatten" msgstr "Aplanar" #: f.widgets.cc:522 msgid "Global Retx" msgstr "Retinex global" #: f.widgets.cc:523 msgid "Zonal Retx" msgstr "Retinex zonal" #: f.widgets.cc:526 msgid "Denoise" msgstr "Reduïr soroll" #: f.widgets.cc:527 msgid "Red Eyes" msgstr "Ulls vermells" #: f.widgets.cc:528 msgid "Match Colors" msgstr "Concordar colors" #: f.widgets.cc:530 msgid "Chromatic1" msgstr "Cromàtic 1" #: f.widgets.cc:531 msgid "Chromatic2" msgstr "Cromàtic 2" #: f.widgets.cc:536 msgid "Sketch" msgstr " Esbós" #: f.widgets.cc:537 msgid "Cartoon" msgstr "Tintes planes" #: f.widgets.cc:541 msgid "Dither" msgstr "Trama" #: f.widgets.cc:543 msgid "Texture" msgstr "Textura" #: f.widgets.cc:545 msgid "Mosaic" msgstr "Mosaïc" #: f.widgets.cc:547 msgid "Color Depth" msgstr "Profunditat de color" #: f.widgets.cc:550 msgid "Brite Ramp" msgstr "Rampa de brillantor" #: f.widgets.cc:551 msgid "Paint Transp" msgstr "Pintar transparència" #: f.widgets.cc:552 msgid "Mirror" msgstr "Voltejar" #: f.widgets.cc:557 msgid "Perspective" msgstr "Perspectiva" #: f.widgets.cc:563 msgid "Flatten Book" msgstr "Aplanar llibre" #: f.widgets.cc:567 msgid "Sphere" msgstr "Esfera" #: f.widgets.cc:568 msgid "Stretch" msgstr "Estirar" #: f.widgets.cc:570 msgid "Tiny Planet" msgstr "Petit planeta" #: f.widgets.cc:571 msgid "Escher Spiral" msgstr "Espiral d'Escher" #: f.widgets.cc:576 msgid "Stack/Paint" msgstr "Apilar/Pintar" #: f.widgets.cc:577 msgid "Stack/Noise" msgstr "Apilar/Soroll" #: f.widgets.cc:578 msgid "Stack/Layer" msgstr "Pila/Capa" #: f.widgets.cc:579 msgid "Stack/Slider" msgstr "Apilar/lliscar" #: f.widgets.cc:580 msgid "Image Diffs" msgstr "Diferèncias de imatges" #: f.widgets.cc:581 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:582 msgid "V. Panorama" msgstr "Panorama vertical" #: f.widgets.cc:583 msgid "PT Panorama" msgstr "PT panorama" #: f.widgets.cc:584 msgid "Mashup" msgstr "Fotomuntatge" #: f.widgets.cc:585 msgid "Montage" msgstr "Muntatge" #: f.widgets.cc:591 msgid "Batch RAW" msgstr "Convertir Raw en lot" #: f.widgets.cc:592 msgid "Burn DVD/BlueRay" msgstr "Gravar DVD/BlueRay" #: f.widgets.cc:593 msgid "Export File List" msgstr "Exportar llista d'arxius" #: f.widgets.cc:595 msgid "Batch Tags" msgstr "Etiquetes en lot" #: f.widgets.cc:597 msgid "Batch Photo Date" msgstr "Dates de fotos en lot" #: f.widgets.cc:598 msgid "Batch Change Meta" msgstr "Canvi de metadades en lot" #: f.widgets.cc:599 msgid "Batch Report Meta" msgstr "Informe de metadades en lot" #: f.widgets.cc:601 msgid "Edit Script" msgstr "Editar script" #: f.widgets.cc:602 msgid "Run Script" msgstr "Executar script" #: f.widgets.cc:606 msgid "Index Files" msgstr "Indexae arxius" #: f.widgets.cc:607 msgid "Quick Index" msgstr "Index ràpid" #: f.widgets.cc:608 msgid "Move Fotoxx Home" msgstr "Moure inici de Fotoxx" #: f.widgets.cc:609 msgid "Preferences" msgstr "Preferències" #: f.widgets.cc:610 msgid "KB Shortcuts" msgstr "Dreceres" #: f.widgets.cc:611 msgid "RGB Distribution" msgstr "Histograma RGB" #: f.widgets.cc:613 msgid "Find Duplicates" msgstr "Trobar duplicats" #: f.widgets.cc:615 msgid "Color Profile" msgstr "Perfil de color" #: f.widgets.cc:619 msgid "Dark/Bright Pixels" msgstr "Sobre/subexposició" #: f.widgets.cc:620 msgid "Map Pixel Bias" msgstr "mapa de biaix de píxels" #: f.widgets.cc:621 msgid "Map Dead Pixels" msgstr "Mapa píxels morts" #: f.widgets.cc:622 msgid "Monitor Color" msgstr "Color del monitor" #: f.widgets.cc:623 msgid "Monitor Gamma" msgstr "Gamma del monitor" #: f.widgets.cc:624 msgid "Change Language" msgstr "Canviar idioma" #: f.widgets.cc:625 msgid "Missing Translations" msgstr "Traduccions que falten" #: f.widgets.cc:626 zfuncs.cc:5885 msgid "Phone Home" msgstr "Reportar estadísticas" #: f.widgets.cc:628 msgid "Show Resources" msgstr "Mostrar recursos" #: f.widgets.cc:629 msgid "Appimage Files" msgstr "Arxius appimage" #: f.widgets.cc:630 msgid "Zappcrash Test" msgstr "Prova de Zapp Crash" #: f.widgets.cc:649 msgid "Gallery" msgstr "Galeria" #: f.widgets.cc:650 msgid "Maps" msgstr "Mapas" #: f.widgets.cc:651 f.widgets.cc:1145 msgid "Favorites" msgstr "Favorits" #: f.widgets.cc:652 msgid "Prev/Next" msgstr "Ant/Seg" #: f.widgets.cc:653 msgid "Zoom/±" msgstr "Zoom/±" #: f.widgets.cc:655 msgid "Meta" msgstr "Metadades" #: f.widgets.cc:656 msgid "Areas" msgstr "Àrees" #: f.widgets.cc:657 msgid "Undo/Redo" msgstr "Desfer/Refer" #: f.widgets.cc:658 fotoxx.h:1350 msgid "Edit" msgstr "Editar" #: f.widgets.cc:659 msgid "Enhance" msgstr "Millorar" #: f.widgets.cc:660 msgid "Effects" msgstr "Efectes" #: f.widgets.cc:662 msgid "Combine" msgstr "Combinar" #: f.widgets.cc:663 msgid "Process" msgstr "Procés" #: f.widgets.cc:664 msgid "Tools" msgstr "Eines" #: f.widgets.cc:815 msgid "Popup Image" msgstr "Imatge emergent" #: f.widgets.cc:825 msgid "Add Selected Files Here" msgstr "Afegir aquí els arxius seleccionats" #: f.widgets.cc:826 msgid "Add Current File Here" msgstr "Afegir aquí l'arxiu actual" #: f.widgets.cc:827 msgid "Remove from Album" msgstr "Eliminar de l'àlbum" #: f.widgets.cc:837 msgid "Select Area" msgstr "Select Area" #: f.widgets.cc:1105 f.widgets.cc:1124 msgid "Fotoxx Image Locations" msgstr "Fotoxx Ubicació d'imatges" #: f.widgets.cc:1173 #, c-format msgid "invalid menu name: %s" msgstr "Nom de menú no vàlid: %s" #: fotoxx.cc:257 msgid "read+write" msgstr "lectura+escritura" #: fotoxx.cc:258 msgid "read only" msgstr "només lectura" #: fotoxx.cc:259 msgid "no access" msgstr "sense accès" #: fotoxx.cc:465 msgid "Please install missing programs:" msgstr "Si us plau instal.leu programas requerits:" #: fotoxx.cc:594 msgid "Index aborted" msgstr "" #: fotoxx.cc:772 msgid " Defer image file indexing:" msgstr "Ajornar la indexació de fitxers d'imatges:" #: fotoxx.cc:773 msgid "" " • Fotoxx will start immediately \n" " • View and edit image files will work normally \n" " • Image search, batch and map functions will not work \n" " • Thumbnail galleries will be slow" msgstr "" " • Fotoxx començarà immediatament \n" " • Veure i editar arxius d'imatge funcionarà normalment \n" " • Les funcions de cerca d'imatges, de lots i de mapes no funcionaran \n" " • Les galeries de miniatures seran lentes" #: fotoxx.cc:778 msgid " Index image files now:" msgstr " Indexar ara les imatges" #: fotoxx.cc:779 msgid "" " • Initial indexing may need considerable time \n" " • Subsequent startups will be fast \n" " • Full functionality will be available \n" " • Thumbnail galleries will be fast" msgstr "" " • La indexació inicial pot necessitar un temps considerable \n" " • Les posades en marxa posteriors seran ràpides \n" " • La funcionalitat completa estarà disponible \n" " • Les galeries en miniatura seran ràpides" #: fotoxx.cc:784 msgid "" " Indexing time depends on the number of image files and the \n" " speed of your computer. This can be a few hundred to a few \n" " thousand per minute. After indexing is done, startup time \n" " should be quite fast. You can change index options later, \n" " using these menus: Tools > Index and Tools > Preferences. " msgstr "" " El temps d'indexat depèn del nombre d'arxius d'imatge i de \n" " velocitat del vostre ordinador. Això pot ser d'uns centenars a uns pocs \n" " milers per minut. Després de la indexació, el temps d'inici \n" " hauria de ser bastant ràpid. Podeu canviar les opcions d'índex més " "endavant, \n" " utilitzant aquests menús: Eines> Índex i eines> Preferències." #: fotoxx.cc:791 msgid "" "Main memory is too small to run Fotoxx. \n" "You can try anyway if you wish. \n" " Continue?" msgstr "" #: fotoxx.cc:826 msgid "Fotoxx First Startup" msgstr "Primera arrencada de Fotoxx" #: fotoxx.cc:981 msgid "(reduced)" msgstr "(reduït)" #: fotoxx.cc:982 msgid "area active" msgstr "àrea activa" #: fotoxx.cc:983 msgid "dialog open" msgstr "dialog obert" #: fotoxx.cc:984 msgid "blocked" msgstr "bloquejat" #: fotoxx.cc:1040 msgid "edits" msgstr "edicions" #: fotoxx.cc:1958 msgid "Show Hidden" msgstr "Mostrar ocults" #: fotoxx.cc:3168 msgid "Exceed 50 anchor points" msgstr "Excedits punts d'anclatge" #: fotoxx.cc:3397 msgid "load curve from a file" msgstr "carregar corba des d'un arxiu" #: fotoxx.cc:3464 msgid "curve file is invalid" msgstr "Atxiu de corba no vàlid" #: fotoxx.cc:3478 msgid "save curve to a file" msgstr "desar corba en un arxiu" #: fotoxx.cc:3549 #, c-format msgid "" "File cannot be edited \n" " %s" msgstr "" "L'arxiu no por ser editat \n" " %s" #: fotoxx.cc:3562 msgid "Too many edits, please save image" msgstr "Massa edicions, si us plau guardeu la imatge" #: fotoxx.cc:3567 msgid "this function cannot be scripted" msgstr "aquesta fiunció no pot ser guionada" #: fotoxx.cc:3584 msgid "" "Select area will be ignored. \n" "Continue?" msgstr "" "La zona seleccionada serà ignorada. \n" "Continuar?" #: fotoxx.cc:3590 msgid "" "Select area not active.\n" "Continue?" msgstr "" "Àrea seleccionada no activa.\n" "Continuar?" #: fotoxx.cc:3900 msgid "file data does not fit dialog" msgstr "les dades d'arxiu no s'ajusten al diàleg" #: fotoxx.cc:3910 msgid "Save settings to a file" msgstr "Desar ajustos en un arxiu" #: fotoxx.cc:4297 msgid "This action will discard changes to current image" msgstr "Aquesta acció descxartarà els canvis a la imatge actual" #: fotoxx.cc:4298 msgid "prior function still active" msgstr "funció anterior encara activa" #: fotoxx.cc:4299 fotoxx.h:1372 msgid "Keep" msgstr "Desar" #: fotoxx.cc:4300 msgid "Discard" msgstr "Descartar" #: fotoxx.h:1315 msgid "Add" msgstr "Afegir" #: fotoxx.h:1316 msgid "Add All" msgstr "Afegir tot" #: fotoxx.h:1318 msgid "Amount" msgstr "Quantitat" #: fotoxx.h:1319 msgid "Angle" msgstr "Angle" #: fotoxx.h:1320 zfuncs.cc:7936 msgid "Apply" msgstr "Aplicar" #: fotoxx.h:1321 msgid "Auto" msgstr "Auto" #: fotoxx.h:1322 msgid "Black" msgstr "Negre" #: fotoxx.h:1323 msgid "Blend Width" msgstr "Barrejar per ample" #: fotoxx.h:1324 msgid "Blue" msgstr "Blau" #: fotoxx.h:1325 zfuncs.cc:12913 msgid "Bottom" msgstr "a Baix" #: fotoxx.h:1327 zfuncs.cc:7950 msgid "Browse" msgstr "Examinar" #: fotoxx.h:1328 msgid "Calculate" msgstr "Calcular" #: fotoxx.h:1329 zfuncs.cc:7936 zfuncs.cc:12528 zfuncs.cc:12680 msgid "Cancel" msgstr "Cancel.lar" #: fotoxx.h:1330 msgid "Center" msgstr "centre" #: fotoxx.h:1331 msgid "Change" msgstr "Canviar" #: fotoxx.h:1332 msgid "Choose" msgstr "Escollir" #: fotoxx.h:1334 msgid "click thumbnail to select file" msgstr "clic en la miniatura per seleccionar arxiu" #: fotoxx.h:1335 msgid "Close" msgstr "Tancar" #: fotoxx.h:1336 msgid "Color" msgstr "Color" #: fotoxx.h:1337 msgid "COMPLETED" msgstr "COMPLETAT" #: fotoxx.h:1338 msgid "continue" msgstr "continuar" #: fotoxx.h:1341 msgid "Create" msgstr "Crear" #: fotoxx.h:1342 msgid "Curve File:" msgstr "Arxiu de corba:" #: fotoxx.h:1343 msgid "Cut" msgstr "Tallar" #: fotoxx.h:1344 msgid "Deband" msgstr "Eliminar bandes" #: fotoxx.h:1345 zfuncs.cc:7936 msgid "Delete" msgstr "Esborrar" #: fotoxx.h:1347 msgid "Display" msgstr "Monitor" #: fotoxx.h:1348 msgid "Done" msgstr "Fet" #: fotoxx.h:1349 msgid "edge" msgstr "vora" #: fotoxx.h:1352 msgid "Erase" msgstr "Esborrar" #: fotoxx.h:1353 msgid "Fetch" msgstr "Extreure" #: fotoxx.h:1354 msgid "output file already exists" msgstr "l'arxiu de sortida ja existeix" #: fotoxx.h:1355 msgid "file not found" msgstr "arxiu no trobat" #: fotoxx.h:1356 #, c-format msgid "file not found: %s" msgstr "arxiu no trobat: %s" #: fotoxx.h:1357 #, c-format msgid "%d image files selected" msgstr "%d imatges seleccionades" #: fotoxx.h:1358 msgid "Find" msgstr "Trobar" #: fotoxx.h:1359 msgid "Finish" msgstr "Acabar" #: fotoxx.h:1361 msgid "Font" msgstr "Tipus de lletra" #: fotoxx.h:1362 #, c-format msgid "gallery truncated to %d images" msgstr "galeria truncada a %d imatges" #: fotoxx.h:1363 msgid "Green" msgstr "Verd" #: fotoxx.h:1364 msgid "Grid" msgstr "Graella" #: fotoxx.h:1365 zfuncs.cc:12943 msgid "Height" msgstr "Alçada" #: fotoxx.h:1368 zfuncs.cc:12935 msgid "Image" msgstr "Imatge" #: fotoxx.h:1369 msgid "Images" msgstr " imatges" #: fotoxx.h:1370 msgid "Insert" msgstr "Insertar" #: fotoxx.h:1373 zfuncs.cc:12917 msgid "Left" msgstr "Esquerra" #: fotoxx.h:1374 msgid "Length" msgstr "Longitut" #: fotoxx.h:1375 msgid "limit" msgstr "limit" #: fotoxx.h:1377 msgid "Magnify" msgstr "Augmentar" #: fotoxx.h:1378 msgid "Make" msgstr "Fer" #: fotoxx.h:1379 msgid "Map" msgstr "Mapa" #: fotoxx.h:1380 msgid "Match Level:" msgstr "Nivel​l​ de coincidència:" #: fotoxx.h:1381 msgid "Max" msgstr "Max" #: fotoxx.h:1382 msgid "Measure" msgstr "Mida" #: fotoxx.h:1383 msgid "mouse radius" msgstr "radi del ratolí" #: fotoxx.h:1384 msgid "Negative" msgstr "Negatiu" #: fotoxx.h:1385 msgid "New" msgstr "Nova" #: fotoxx.h:1386 msgid "Next" msgstr "Següent" #: fotoxx.h:1387 zfuncs.cc:11875 msgid "No" msgstr "No" #: fotoxx.h:1388 msgid "no write permission" msgstr "no hi ha permis d'escriptura" #: fotoxx.h:1389 msgid "no images" msgstr "sense imatges" #: fotoxx.h:1390 msgid "image index disabled" msgstr "" #: fotoxx.h:1391 msgid "no image files selected" msgstr "no hi han imatges seleccionades" #: fotoxx.h:1392 msgid "None" msgstr "Cap" #: fotoxx.h:1393 msgid "no selection" msgstr "sense selecció" #: fotoxx.h:1394 msgid "image index not updated" msgstr "índex d'imatge no actualitzat" #: fotoxx.h:1395 msgid "opacity center" msgstr "opacitat del centre" #: fotoxx.h:1396 msgid "opacity edge" msgstr "opacitat del contorn" #: fotoxx.h:1398 msgid "Paint Radius" msgstr "Radi a pintar" #: fotoxx.h:1400 msgid "Pause" msgstr "Pausa" #: fotoxx.h:1401 msgid "Percent" msgstr "Percentatge" #: fotoxx.h:1403 msgid "Presets" msgstr "Predefinits" #: fotoxx.h:1404 msgid "Prev" msgstr "Anterior" #: fotoxx.h:1405 msgid "use previous input" msgstr "Utilitzeu entrada anterior" #: fotoxx.h:1406 msgid "Proceed" msgstr "Procedir" #: fotoxx.h:1409 msgid "range" msgstr "rang" #: fotoxx.h:1410 msgid "Red" msgstr "Vermell" #: fotoxx.h:1411 msgid "Redo" msgstr "Refer" #: fotoxx.h:1412 msgid "Reduce" msgstr "Reduïr" #: fotoxx.h:1413 msgid "Remove" msgstr "Eliminar" #: fotoxx.h:1415 msgid "Replace" msgstr "Substituir" #: fotoxx.h:1416 msgid "Reserved" msgstr "Reservat" #: fotoxx.h:1417 msgid "Reset" msgstr "Restablir" #: fotoxx.h:1418 zfuncs.cc:12921 msgid "Right" msgstr "Dreta" #: fotoxx.h:1419 msgid "Rotate" msgstr "Rotar" #: fotoxx.h:1420 msgid "Run" msgstr "Executar" #: fotoxx.h:1423 msgid "Seconds" msgstr "Segons" #: fotoxx.h:1427 msgid "Size" msgstr "Tamany" #: fotoxx.h:1428 msgid "Start" msgstr "Començar" #: fotoxx.h:1429 msgid "Stop" msgstr "Parar" #: fotoxx.h:1430 msgid "Strength" msgstr "Força" #: fotoxx.h:1431 msgid "the area is not finished" msgstr "l'àrea no està acabada" #: fotoxx.h:1432 msgid "Threshold" msgstr "Llindar" #: fotoxx.h:1433 zfuncs.cc:12909 msgid "Top" msgstr "Adalt" #: fotoxx.h:1434 msgid "Transparency" msgstr "Transparència" #: fotoxx.h:1435 msgid "Trash" msgstr "Eliminar" #: fotoxx.h:1436 msgid "Trim" msgstr "Retallar" #: fotoxx.h:1437 msgid "Undo All" msgstr "Desfer-ho tot" #: fotoxx.h:1438 msgid "Undo Last" msgstr "Desfer l'últim" #: fotoxx.h:1439 msgid "Undo" msgstr "Desfer" #: fotoxx.h:1440 msgid "Unfinish" msgstr "No acabat" #: fotoxx.h:1441 msgid "Update" msgstr "Actualització" #: fotoxx.h:1442 msgid "View" msgstr "Veure" #: fotoxx.h:1443 msgid "Web" msgstr "Web" #: fotoxx.h:1444 msgid "White" msgstr "Blanc" #: fotoxx.h:1445 zfuncs.cc:12939 msgid "Width" msgstr "Ample" #: fotoxx.h:1446 msgid "x-offset" msgstr "desplaçament X" #: fotoxx.h:1447 msgid "y-offset" msgstr "desplaçament Y" #: fotoxx.h:1448 zfuncs.cc:11875 msgid "Yes" msgstr "Si" #: zfuncs.cc:1836 #, c-format msgid "" "create folder? \n" " %s" msgstr "" "Crear directori? \n" " %s" #: zfuncs.cc:5880 msgid "" "If you permit, a message is occasionally \n" "sent to the web host for usage statistics. \n" "Nothing is retained that can be associated \n" "with a person or computer or location." msgstr "" "Si ho permet, un missatge és ocasionalment \n" "enviat a l'amfitrió per obtenir estadístiques d'ús. \n" "No es conserva res que es pugui associar \n" "amb una persona o ordinador o ubicació." #: zfuncs.cc:6743 #, c-format msgid "cannot open file %s" msgstr "no es pot obrir l'arxiu %s" #: zfuncs.cc:6766 msgid "save text to file" msgstr "desar text a l'arxiu" #: zfuncs.cc:7936 msgid "edit menu entry" msgstr "editar entrada de menú" #: zfuncs.cc:7940 msgid "menu text" msgstr "text del menú" #: zfuncs.cc:7941 msgid "menu func" msgstr "funció del menú" #: zfuncs.cc:7942 msgid "menu icon" msgstr "icona del menú" #: zfuncs.cc:7943 msgid "icon size" msgstr "tamany de la icona" #: zfuncs.cc:7946 msgid "Bold" msgstr "Negreta" #: zfuncs.cc:7953 msgid "close window" msgstr "tancar finestra" #: zfuncs.cc:8000 msgid "select icon" msgstr "seleccionar icona" #: zfuncs.cc:12000 zfuncs.cc:12896 msgid "cancel" msgstr "cancel.lar" #: zfuncs.cc:12489 msgid "choose file" msgstr "escollir arxiu" #: zfuncs.cc:12494 msgid "choose files" msgstr "escollir arxius" #: zfuncs.cc:12505 msgid "choose folder" msgstr "escollir carpeta" #: zfuncs.cc:12510 msgid "choose folders" msgstr "escollir carpetes" #: zfuncs.cc:12515 msgid "create folder" msgstr "crear carpeta" #: zfuncs.cc:12522 msgid "hidden" msgstr "ocult" #: zfuncs.cc:12896 msgid "done" msgstr "fet" #: zfuncs.cc:12926 msgid "image scale" msgstr "escala de l'imatge" #: zfuncs.cc:12928 msgid "percent" msgstr "percentatge" #~ msgid "" #~ "fotoxx_maps package not installed \n" #~ "(see https://kornelix.net)" #~ msgstr "" #~ "paquet fotoxx_maps no instal·lat \n" #~ "(veure https://kornelix.net)" #~ msgid "% of scale" #~ msgstr "% escala" #~ msgid "Left/right click: smaller/larger image/thumbnails" #~ msgstr "Clic esquerre/dret: més petit/més gran imatge/miniatures" #~ msgid "" #~ "image index disabled \n" #~ " Enable?" #~ msgstr "" #~ "índex d'imatges deshabilitat \n" #~ " habilitat?" #~ msgid "" #~ "Right-click album thumbnail to \n" #~ "cut or copy to cache, or remove." #~ msgstr "" #~ "Clic amb el botó dret a la miniatura de l'àlbum a \n" #~ "tallar o copiar en memòria cau, o eliminar." #~ msgid "" #~ "Right-click thumbnail left/right side \n" #~ "to insert cache before/after thumbnail." #~ msgstr "" #~ "Clic amb el botó dret a la part superior esquerra / dreta \n" #~ "per inserir la memòria cau abans/després de la miniatura." #~ msgid "Drag album thumbnail to new position." #~ msgstr "Arrossegar miniatura de l'àlbum a la nova posició" #~ msgid "Choose album to view or edit" #~ msgstr "Escollir un àlbum per veure o editar" #~ msgid "Select files, add to cache" #~ msgstr "Seleccionar arxius, afegir-los a la memòria cau" #~ msgid "Display cache for drag and drop" #~ msgstr "Mostrar la memòria cau per arrossegar i deixar anar" #~ msgid "Remove cache files from albums" #~ msgstr "Treure els arxius de la memòria cau dels àlbums" #~ msgid "Clear the file cache" #~ msgstr "Esborreu el arxiu de memòria cau" #~ msgid "Replace album files or add after" #~ msgstr "Reemplaçar els arxius d'àlbum o afegiu-hi després" #~ msgid "Image cache has %d images" #~ msgstr "Cachè d'imatges té %d imatges" #~ msgid "fill from image cache (%d images)" #~ msgstr "emplenar des del cachè (%d imatges)" #~ msgid "max. album size exceeded %d" #~ msgstr "màx. la mida de l'àlbum supera %d" #~ msgid "Choose Album" #~ msgstr "Escollir un àlbum" #~ msgid "cache is empty" #~ msgstr "la memòria cau està buida" #~ msgid "remove %d files from %d albums?" #~ msgstr "esborrar %d arxius de %d àlbums" #~ msgid "Process album files matching cache files:" #~ msgstr "" #~ "Processar arxius d'àlbums que coincideixin amb els arxius de memòria cau" #~ msgid "Replace all with cache versions" #~ msgstr "Reemplaçar totes les versions de caché" #~ msgid "Replace all versions with cache versions" #~ msgstr "Substituïr totes les versions amb versions de memòria cau" #~ msgid "Add cache versions to existing versions" #~ msgstr "Afegir versions de la memòria cau a les versions existents" #~ msgid "Replace all with original and cache versions" #~ msgstr "Reemplaçar totes les versions originals i caché" #~ msgid "use gallery" #~ msgstr "utilitzar galeria" #~ msgid "Wait before caption/comments" #~ msgstr "Esperar aband del títol/comentaris" #~ msgid "Show image caption" #~ msgstr "Mostrar títul del diaporama" #~ msgid "Show image comments" #~ msgstr "Mostrar comentaris de la imatge" #~ msgid "select color in mouse:" #~ msgstr "seleccionar el color al ratolí:" #~ msgid "use previous size" #~ msgstr "usar tamany previ" #~ msgid "invert width/height" #~ msgstr "invertir ample/alçada" #~ msgid "use previous settings" #~ msgstr "etilitzar especificacions anteriors" #~ msgid "Amplifier" #~ msgstr "Amplificar" #~ msgid "Low Color" #~ msgstr "Color -" #~ msgid "Dark Areas" #~ msgstr "Ombres" #~ msgid "Max." #~ msgstr "Max" #~ msgid "High" #~ msgstr "+" #~ msgid "Bright" #~ msgstr "Llums" #~ msgid "recall previous settings used" #~ msgstr "recuperar ajustaments usats anteriorment" #~ msgid "Color Balance" #~ msgstr "Balanç de color" #~ msgid "Warmer" #~ msgstr "Càlid" #~ msgid "Cooler" #~ msgstr "Fred" #~ msgid "use previous white balance" #~ msgstr "utilitzar balanç de blancs anterior" #~ msgid "Median" #~ msgstr "Mediana" #~ msgid "Color Saturation" #~ msgstr "Saturació de color" #~ msgid "Add Text to Image" #~ msgstr "Afegir text a la imatge" #~ msgid "Add lines or arrows to an image" #~ msgstr "Afegir línies o fltxes a una imatge" #~ msgid "Upright Image" #~ msgstr "Adressar imatge" #~ msgid "Lever Edits" #~ msgstr "Amplificar edicions" #~ msgid "Edit Function Amplifier" #~ msgstr "Amplificador de la funció d'edició" #~ msgid "Cannot use Lever Edits" #~ msgstr "No es pot usar l'amplificadció d'edicions" #~ msgid "minimum" #~ msgstr "ombres" #~ msgid "maximum" #~ msgstr "llums" #~ msgid "image is too large" #~ msgstr "la imatge és molt gran" #~ msgid "Click image to select areas." #~ msgstr "Clic en la imatge per seleccionar àrees" #~ msgid "Local Color" #~ msgstr "Color local" #~ msgid "Metric:" #~ msgstr "Mètrica:" #~ 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. " #~ msgid "Color Fringes" #~ msgstr "Franges de color" #~ msgid "Color Drawing" #~ msgstr "Dibuix amb colors" #~ msgid "Bright Areas" #~ msgstr "Llums" #~ msgid "Simulate Embossing" #~ msgstr "Simular un relleu" #~ msgid "depth" #~ msgstr "profunditat" #~ msgid "custom palette" #~ msgstr "personalitzar paleta" #~ msgid "Simulate Painting" #~ msgstr "Simular una Pintura" #~ msgid "RAW type not registered in User Settings" #~ msgstr "tipus RAW no registrat en Configuració d'usuari" #~ msgid "outside top folders" #~ msgstr "fora de les carpetes superiors" #~ msgid "no more images" #~ msgstr "no hi ha més imatges" #~ msgid "exceeded 200 albums" #~ msgstr "excedits 200 àlbums" #~ msgid "Image Pixels" #~ msgstr "Píxels d'imatge" #~ msgid "select thumbnail" #~ msgstr "sel·leccionar miniatures" #~ msgid "Mashup layout and background image" #~ msgstr "Capa de muntatge i imatge de fons" #~ msgid "project name" #~ msgstr "nom de projecte" #~ msgid "supply a project name" #~ msgstr "proporcioni un nom de projecte" #~ msgid "montage map file not found: %s" #~ msgstr "arxiu mapa de muntatge no trobat: %s" #~ msgid "create tag categories and tags" #~ msgstr "crear etiquetes i categories d'etiquetes" #~ msgid "new set" #~ msgstr "nova selecció" #~ msgid "all versions of matching images" #~ msgstr "totes les verdions de imatges coïncidents" #~ msgid "images added: %d removed: %d new count: %d" #~ msgstr "imatges afegides: %d eliminades: %d nova quantitat: %d" #~ msgid "no changes made" #~ msgstr "no s'han fet canvis" #~ msgid "Set Map Source" #~ msgstr "Establir origen de mapas" #~ msgid "file error: %s \n" #~ msgstr "error de arxiu: %s \n" #~ msgid "file format not supported: %s \n" #~ msgstr "format de arxiu no és compatible: %s \n" #~ msgid "use dcraw" #~ msgstr "utilitzar dcraw" #~ msgid "use Raw Therapee" #~ msgstr "utilitzar Raw Therapee" #~ msgid "" #~ "script file: %s \n" #~ " %s" #~ msgstr "" #~ "script: %s \n" #~ " %s" #~ msgid "User Settings" #~ msgstr "Ajustaments de l'usuari" #~ msgid "Startup Display" #~ msgstr "Pantalla d'inici" #~ msgid "Background color:" #~ msgstr "Color de fons" #~ msgid "Menu Text" #~ msgstr "Text del menú" #~ msgid "Menu Style" #~ msgstr "Estil de menú" #~ msgid "Dialog font" #~ msgstr "Font del diàleg" #~ msgid "JPEG save quality" #~ msgstr "qualitat de l'arxiu JPEG" #~ msgid "Curve node capture distance" #~ msgstr "Dist'ancia de captura de nodus de corba" #~ msgid "Map marker size" #~ msgstr "Mida del marcador de mapa" #~ msgid "show last file version only" #~ msgstr "mostrar nomès última versió d'arxiu" #~ msgid "shift image to right margin" #~ msgstr "canviar imatge al marge dret" #~ msgid "image index level" #~ msgstr "nivell d'index d'imatge" #~ msgid "Fotoxx started directly" #~ msgstr "Fotoxx iniciat directament" #~ msgid "Fotoxx started by file manager" #~ msgstr "Fotoxx iniciat per explorador d'arxius" #~ msgid "RAW file types" #~ msgstr "Tipus d'arxius RAW" #~ msgid "video file types" #~ msgstr "tipus d'arxius de vídeo" #~ msgid " F1 User Guide for current function \n" #~ msgstr "F1 Guia d'usuari per la funció actual \n" #~ msgid " F10 Full Screen with menus \n" #~ msgstr "F10 Pantalla complerta amb menús \n" #~ msgid " F11 Full Screen without menus \n" #~ msgstr "F11 Pantalla complerta sense menús \n" #~ msgid " Escape Quit dialog; Quit Fotoxx \n" #~ msgstr "Esc Diàleg de sortida: Sortir de Fotoxx \n" #~ msgid " Delete Delete/Trash dialog \n" #~ msgstr "Supr Esborrar/Eliminar diàleg \n" #~ msgid " Arrows Previous/Next Image \n" #~ msgstr "Fletxes Imatge anterior/següent \n" #~ msgid "Stuck Pixels" #~ msgstr "Píxels retinguts" #~ msgid "pixel group" #~ msgstr "agrupar píxels" #~ msgid "stuck pixels:" #~ msgstr "píxels defectuosos" #~ msgid "Load Stuck Pixels" #~ msgstr "Carregar píxels defectuosos" #~ msgid "Stuck Pixels file" #~ msgstr "Arxiu de píxels defectuosos" #~ msgid "Open, Recent, Rename, Copy, Delete, Print ..." #~ msgstr "Obrir, Recent, Canviar el nom, Copiar, Suprimir, Imprimir ..." #~ msgid "Gallery, Bookmarks, Albums, Slide Show" #~ msgstr "Galeria, Marcadors, Àlbums, Diaporama" #~ msgid "Favorite Functions" #~ msgstr "Funcions favorites" #~ msgid "Open previous or next file (left/right mouse click)" #~ msgstr "Obrir arxiu anterior o sugüent (clic esq/dret)" #~ msgid "Captions, Tags, Ratings, Geotags, Search ... " #~ msgstr "Titols, Etiquetes, Valoracions, Geoetiquetes, Buscar ..." #~ msgid "Select image areas to edit, save, copy and paste ..." #~ msgstr "Seleccionar àrees de la imatge per editar, copiar i enganxar" #~ msgid "" #~ "left/right click: undo/redo 1 edit \n" #~ " with key A: undo/redo all edits \n" #~ " middle click: go to any prior edit" #~ msgstr "" #~ "clic esq./dret.: desfer/refer 1 edició \n" #~ " amb la tecla A: defer/refer totes les edicions \n" #~ " clic central: anar a una edició anterior" #~ msgid "Trim, Rotate, Resize, Retouch, Sharpen, Color, Text ..." #~ msgstr "Retallar, Girar, Canviar de mida, Retocar, Esfera, Color, Text ..." #~ msgid "Image improvements, Paint, Copy ..." #~ msgstr "Millores d'imatge, Pintar, Copiar ..." #~ msgid "Perspective, Warp, Unwarp, Transfigure ..." #~ msgstr "Perspectiva, Deformar, Eliminar deformació, Transfigurar ..." #~ msgid "Arty Transforms (filters)" #~ msgstr "Transformacions artístiques (filtres)" #~ msgid "HDR, HDF, Panorama, Stack, Mashup" #~ msgstr "HDR, HDF, Panorama, Apilar, Muntatge" #~ msgid "Batch Convert, Metadata, Scripts ..." #~ msgstr "Convertir en lot, Metadades, Scripts ..." #~ msgid "Index, Settings, Shortcuts, Magnify, utilities ..." #~ msgstr "Indexar, Ajustaments, dreceres, Augmentar, utilitats ..." #~ msgid "User Guide, Recent Changes ..." #~ msgstr "Guia d'usuari, Canvis recents ..." #~ msgid "Current File (key F)" #~ msgstr "Arxiu actual (tecla F)" #~ msgid "Open a recently seen file" #~ msgstr "Obrir arxiu vist recentment" #~ msgid "Open a recently added file" #~ msgstr "Obrir un nou arxiu afegit" #~ msgid "Copy file to the image cache" #~ msgstr "Copiar imatges al caché d'imatges" #~ msgid "Thumbnail Gallery (key G)" #~ msgstr "Galería de miniatures (tecla G)" #~ msgid "Bigger thumbnails [+]" #~ msgstr "Miniatures més grans [+}" #~ msgid "Smaller thumbnails [-]" #~ msgstr "Miniatures més petites {-]" #~ msgid "Show current or last used album" #~ msgstr "Mostrar l'àlbum actual o l'últim usat" #~ msgid "Maps (key M)" #~ msgstr "Mapes (tecla M)" #~ msgid "Mark all images or current gallery" #~ msgstr "Marcar totes les imatges o la galeria actual" #~ msgid "(Toggle) show captions and comments" #~ msgstr "(Canviar) mostra llegendes i comentaris" #~ msgid "Find images meeting select criteria" #~ msgstr "Trobar imatges que compleixin un criteri de seleccio" #~ msgid "Adjust color balance" #~ msgstr "Ajustar balanç de color" #~ msgid "Adjust color intensity (saturation)" #~ msgstr "Ajustar intensitat de color (saturació)" #~ msgid "Upright a rotated image" #~ msgstr "Adressar una imatge girada" #~ msgid "Leverage edits by brightness or color" #~ msgstr "Edició per nivells de brillantor o color" #~ msgid "Edit brightness distributions" #~ msgstr "Editar histogrames" #~ msgid "Flatten brightness distribution" #~ msgstr "Aplanarhistograma" #~ msgid "Adjust color in selected image areas" #~ msgstr "Ajustar color en una àrea seleccionada de una imatge" #~ msgid "Reduce Chromatic Aberration" #~ msgstr "Reduïr aberració cromàtica" #~ msgid "Smoothen edges with jaggies" #~ msgstr "Suavitzar vores amb dents de serra" #~ msgid "Convert to solid color drawing" #~ msgstr "Convertir a un dibuix de colors sòlids" #~ msgid "Convert camera RAW files using dcraw or Raw Therapee" #~ msgstr "Convertir arxius RAW utilitzan dcraw o Raw Therapee" #~ msgid "Change user preferences" #~ msgstr "Canviar preferències d'usuari" #~ msgid "Change Keyboard Shortcut Keys" #~ msgstr "Canviar tecles drecera" #~ msgid "Show a brightness distribution graph" #~ msgstr "Mostrar histograma (distribució de la brillantor)" #~ msgid "Erase known hot and dark pixels" #~ msgstr "Esborrar píxels calents i foscos coneguts" #~ msgid "Version, license, contact, credits" #~ msgstr "Versió, llicència, contacte, crèdits" #~ msgid "File View" #~ msgstr "Vista d'arxiu" #~ msgid "Set as Wallpaper" #~ msgstr "Establir com a fons de pantalla" #~ msgid "Copy to Cache" #~ msgstr "Copiar a la caché" #~ msgid "Gallery View" #~ msgstr "Vista de galeria" #~ msgid "Zoom+" #~ msgstr "Zoom +" #~ msgid "Zoom-" #~ msgstr "Zoom -" #~ msgid "Map View" #~ msgstr "Viosta de mapa" #~ msgid "Net Map Locs" #~ msgstr "Localitzacions del mapa de xarxa" #~ msgid "Map Markers" #~ msgstr "Marcadors de mapa" #~ msgid "Color Sat" #~ msgstr "Saturació de color" #~ msgid "Add Lines" #~ msgstr "Afegir línies" #~ msgid "Zonal Colors" #~ msgstr "Colors zonals" #~ msgid "Anti-Alias" #~ msgstr "Suavitzar contorns" #~ msgid "Brightness Graph" #~ msgstr "Gráfic de brillantor" #~ msgid "Edit 1" #~ msgstr "Editar 1" #~ msgid "Edit 2" #~ msgstr "Editar 2" #~ msgid "Cut to Cache" #~ msgstr "Retallar a la caché" #~ msgid "Paste Cache Here (clear)" #~ msgstr "Enganxar caché aquí (esborrar)" #~ msgid "Paste Cache Here (keep)" #~ msgstr "Enfganxar caché aquí (conservar)" #~ msgid "user guide not found" #~ msgstr "no trobada la guia d'usuari" #~ msgid "save" #~ msgstr "desar" #~ msgid "Inside-Out" #~ msgstr "Dedins a fora" #~ msgid "LOW MEMORY - performance may be poor" #~ msgstr "MEMORIA BAIXA - el rendiment pot ser baix" #~ msgid "Map Locs" #~ msgstr "Ubicacions de mapa" #~ msgid "Map Source" #~ msgstr "Orígen de mapa" #~ msgid "Choose Maps" #~ msgstr "Escollir mapa" #~ msgid "Net Maps" #~ msgstr "Mapes en xarxa" #~ msgid "World Maps" #~ msgstr "Mapes mundials" #~ msgid "Uninstall AppImage" #~ msgstr "Desinstal·lar Appimage" #~ msgid "Run Script Batch" #~ msgstr "Executar scripts en lot" #~ msgid "Dots" #~ msgstr "Punts" #~ msgid "Spherical Proj." #~ msgstr "Projecció esfèrica" #~ msgid "Unselect" #~ msgstr "Deseleccionar" #~ msgid "Sync Gallery" #~ msgstr "Sincronitzar galería" #~ msgid "Replace album file with another file" #~ msgstr "Substituïr arxiu d'àlbum amb un altre arxiu" #~ msgid "Update album files to latest version" #~ msgstr "Actualitzar arxius d'album a la darrera versió" #~ msgid "Quick Start mini-guide" #~ msgstr "Minni guia d'inici ràpid" #~ msgid "Completely uninstall AppImage package" #~ msgstr "Desinstal·lar completament el paquet Appimage" #~ msgid "Convert to dots (Roy Lichtenstein effect)" #~ msgstr "Convertir a punts (efecte Roy Lichtenstein" #~ msgid "Reduce Chromatic Abberation" #~ msgstr "Reduïr l'aberració cromàtica" #~ msgid "Open and edit a camera RAW file" #~ msgstr "Obrir i editar un arxiu de càmera RAW" #~ msgid "Open new file" #~ msgstr "Obrir una nova imatge" #~ msgid "Save and recall named map locations" #~ msgstr "Desar i recuperar ubicacions marcades en el mapa" #~ msgid "Choose a map file" #~ msgstr "Escollir un arxiu de mapa" #~ msgid "Convert, Export, Metadata, Search ..." #~ msgstr "Convertir, Exportar, Metadades, Cerca ..." #~ msgid "Next page" #~ msgstr "Pàgina següent" #~ msgid "previous page" #~ msgstr "pàguna prèvia" #~ msgid "Jump to end (bottom)" #~ msgstr "Anar al final (a baix)" #~ msgid "Jump to beginning (top)" #~ msgstr "Anar al principi (a dalt)" #~ msgid "Reduce thumbnail size" #~ msgstr "Reduir mida de miniatura" #~ msgid "Increase thumbnail size" #~ msgstr "Augmentar mida de miniatura" #~ msgid "Sync Gallery, Albums, Slide Show" #~ msgstr "Sincronitzar Galeria, Àlbums, Diaporames" #~ msgid "Quick Start, User Guide, Recent Changes ..." #~ msgstr "Inici ràpid, Guia d'usuari, Canvis recents" #~ msgid "Fix Perspective, Warp or unwarp image ..." #~ msgstr "corregir perspectiva, deformar una imatge ..." #~ msgid "Complex edits and image transformations" #~ msgstr "Edicions complexes i transformacions de imatges" #~ msgid "Trim, Rotate, Resize, Retouch, Sharpen, Denoise, Text ..." #~ msgstr "" #~ "Retallar, Girar, Re-escalar, Retocar, Enfocar, Eliminar soroll, Text ..." #~ msgid "Open, Recent, RAW, Rename, Copy, Delete, Print ..." #~ msgstr "Obrir, Recent, RAW, Re-anomenar, Copiar, Esborrar, Imprimir ..." #~ msgid "Net Maps (key M)" #~ msgstr "Maps de la xaexa (tecla M)" #~ msgid "World Maps (key W)" #~ msgstr "Mapa mundial (tecla W)" #~ msgid "drag mouse to set center" #~ msgstr "moure ratolí per establir centre" #~ msgid "" #~ "Completely remove the AppImage package \n" #~ "Press F1 for more information. \n" #~ "Continue?" #~ msgstr "" #~ "Eliminar completament el paquet Appimage \n" #~ "Presionar F1 pwer a més informació. \n" #~ "Continuar?" #~ msgid "This function is for AppImage package only" #~ msgstr "Aquesta funció és només per el paquet Appimage" #~ msgid "" #~ "Drag mouse on image. \n" #~ "Left click to cancel. \n" #~ "Key X to toggle dialog." #~ msgstr "" #~ "Arrossegar ratolí sobre la imatge. \n" #~ "clic esquerra per cancel.lar. \n" #~ "Tecla X per amagar diàleg." #~ msgid "Zoomed Image:" #~ msgstr "Imatge augmentada" #~ msgid "" #~ "Duplicate folder: \n" #~ " %s \n" #~ "Please remove." #~ msgstr "" #~ "Directori duplicat: \n" #~ " %s \n" #~ "Si us plau esborrar." #~ msgid "Invalid folder: %s" #~ msgstr "directori no vàlid %s" #~ msgid "create folder? %s" #~ msgstr "crean directori? %s" #~ 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" #~ msgid "stars range" #~ msgstr "rang d'estrelles" #~ msgid "Last" #~ msgstr "Última" #~ msgid "First" #~ msgstr "Primera" #~ msgid "Page Down" #~ msgstr "Pàgina avall" #~ msgid "Page Up" #~ msgstr "Pàgina amunt" #~ msgid "Sort" #~ msgstr "Ordenar" #~ msgid "file type not supported: %s" #~ msgstr "tipus d'arxiu no suportaat: %s" #~ msgid "Quick Start" #~ msgstr "Inici ràpid" #~ msgid "Open RAW" #~ msgstr "Obrir RAW" #~ msgid "Convert Image to Dots" #~ msgstr "Convertrr la imatge a punts" #~ msgid "" #~ "shift + left click: pick image position to copy \n" #~ "left click or drag: copy image to mouse position \n" #~ "right click or drag: restore original image" #~ msgstr "" #~ "Majúsc.+ clic esq.: assenyalar posició de la imatge a copiar \n" #~ "clic esq. o arrossegar: copiar imatge a la posició del cursor \n" #~ "clic dret o arrossegar: restaurar imatge original" #~ msgid "click on image to choose color" #~ msgstr "clic en la imatge per escollir color" #~ msgid "Color Palette" #~ msgstr "Palta de color" #~ msgid "" #~ "1. Drag mouse to select. \n" #~ "2. Erase. 3. Repeat. " #~ msgstr "" #~ "1. Arrossegar el ratolí per seleccionar. \n" #~ "2. Esborrar. 3. Repetir. " #~ msgid "Edge" #~ msgstr "Vora" #~ msgid "Click for black level" #~ msgstr "Clic per nivell de negre" #~ msgid "Click for white balance" #~ msgstr "Clc per balanç de blancs" #~ msgid "Rotate: degrees" #~ msgstr "Girar : graus" #~ msgid "Level" #~ msgstr "Nivel​l" #~ msgid "trim size:" #~ msgstr "mida de retallat" #~ msgid "Minor rotate: drag right edge with mouse" #~ msgstr "Girar : arrossegar cantó dret amb el ratolí " #~ msgid "Trim: drag middle to move, drag corners to resize" #~ msgstr "" #~ "Retallar : arrossegar pel mig per moure, arrossegar cantons per " #~ "redimensionar" #~ msgid "Area Edge Calc" #~ msgstr "Càlcul del límit de l'àrea" #~ msgid "Edge calculation in progress" #~ msgstr "Càlcul del límit en progrés" #~ 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." #~ msgid "select all matching colors within mouse circle" #~ msgstr "sel·leccionar tots els colors coincidents amb el cursor" #~ msgid "select one matching color within mouse circle:" #~ msgstr "sel·leccionar un color coincident amb el cursor" #~ msgid "select ellipse" #~ msgstr "seleccionar ellipse" #~ msgid "Steps" #~ msgstr "Pasos" #~ msgid "Zoom size (x)" #~ msgstr "Tamany de zoon (x)" #~ msgid "Zoom:" #~ msgstr "Zoom" #~ msgid "ALL albums chosen" #~ msgstr "Tots els àlbums ewscollits" #~ msgid "All Albums" #~ msgstr "Tots els àlbums" #~ msgid "Update Album Files" #~ msgstr "Actualitzar arxius de l'àlbum" #~ msgid "%s deleted" #~ msgstr "%s esborrat" #~ msgid "" #~ "Delete or Rename? \n" #~ " %s" #~ msgstr "" #~ "Esborrar o re-anomenar? \n" #~ " %s" #~ msgid "cache added to empty album" #~ msgstr "caché afegida a un àlbum buit" #~ msgid "Delete or rename an album" #~ msgstr "Esborrar o re-anomenar un àlbum" #~ msgid "Clear image cache" #~ msgstr "Netejar cache d'imatge" #~ msgid "Select images, add to cache" #~ msgstr "Seleccionar imatges per afegir al caché" #~ msgid "Album to view or edit" #~ msgstr "Àlbum per veure o editar" #~ msgid "" #~ "Right-click left/right side of album \n" #~ "thumbnail to insert cached images \n" #~ "before/after the thumbnail." #~ msgstr "" #~ "Clic dret en el costat esq/dret de l'àlbum \n" #~ "de miniatures per inserir imatges del caché \n" #~ "abans/després de la miniatura." #~ 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" #~ msgid "Select 2 files:" #~ msgstr "Seleccionar 2 arxius" fotoxx-20.08/locales/translate-de.po000066400000000000000000004433251362435004500174340ustar00rootroot00000000000000# German translations for fotoxx package. # msgid "" msgstr "" "Project-Id-Version: fotoxx 6.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-12-25 22:16+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:59 #, c-format msgid "max. album size exceeded: %d" msgstr "max. Albumgröße überschritten: %d" #: f.albums.cc:70 msgid "" "Right-click album thumbnail for edits: \n" " - add selected files at this position \n" " - remove this file from the album" msgstr "" "Zum Bearbeiten Album Thumbnail rechts-anklicken: \n" " - gewählte Dateien an diese Stelle einfügen \n" " - diese Datei vom Album entfernen" #: f.albums.cc:73 msgid "Arrange files with thumbnail drag and drop" msgstr "Um Dateien anzuordnen, Thumbnails ziehen" #: f.albums.cc:102 f.widgets.cc:458 msgid "Manage Albums" msgstr "Alben verwalten" #: f.albums.cc:113 f.albums.cc:232 msgid "Create or replace an album" msgstr "Album erstellen oder ersetzen" #: f.albums.cc:115 f.albums.cc:381 msgid "Rename an album" msgstr "Album umbenennen" #: f.albums.cc:117 f.albums.cc:451 msgid "Delete an album" msgstr "Album löschen" #: f.albums.cc:119 msgid "Files to add to an album" msgstr "Dateien ins Album einzufügen" #: f.albums.cc:183 #, c-format msgid "%d files added to album %s" msgstr "%d Dateien wurden Album %s zugefügt" #: f.albums.cc:213 #, c-format msgid "max. album count exceeded: %d" msgstr "max. Anzahl von Alben überschritten: %d" #: f.albums.cc:234 f.albums.cc:270 f.albums.cc:1537 msgid "Album Name" msgstr "Album-Name" #: f.albums.cc:240 msgid "make an initially empty album" msgstr "ein anfänglich leeres Album erstellen" #: f.albums.cc:241 msgid "fill from pre-selected files" msgstr "mit vorgewählten Dateien auffüllen" #: f.albums.cc:242 msgid "fill from current gallery" msgstr "aus der aktuellen Galerie auffüllen" #: f.albums.cc:243 msgid "select initial files" msgstr "Erstauswahl der Dateien" #: f.albums.cc:284 msgid "enter an album name" msgstr "Album-Name eingeben" #: f.albums.cc:292 f.albums.cc:1569 #, c-format msgid "replace album %s ?" msgstr "Album %s ersetzen ?" #: f.albums.cc:312 msgid "Use [Select] to add files to an empty album" msgstr "[Select] um Dateien einem leeren Album zuzufügen" #: f.albums.cc:326 msgid "new album created" msgstr "neues Album wurde erstellt" #: f.albums.cc:342 f.meta.cc:7832 msgid "gallery is empty" msgstr "Galerie ist leer" #: f.albums.cc:396 msgid "enter new album name" msgstr "neuen Albumnamen eingeben" #: f.albums.cc:403 #, c-format msgid "invalid file name: %s" msgstr "ungültiger Dateiname: %s" #: f.albums.cc:424 #, c-format msgid "album already exists: %s" msgstr "Album existiert schon: %s" #: f.albums.cc:435 #, c-format msgid "" "%s \n" " renamed: %s" msgstr "" "%s \n" " umbenannt: %s" #: f.albums.cc:464 #, c-format msgid "delete %s ?" msgstr "%s löschen?" #: f.albums.cc:571 f.albums.cc:584 #, c-format msgid "%s removed with no replacement \n" msgstr "%s ersatzlos entfernt \n" #: f.albums.cc:589 #, c-format msgid "%s replaced by ...%s \n" msgstr "%s ersetzt durch ...%s \n" #: f.albums.cc:664 msgid "no selected files" msgstr "keine Dateien ausgewählt" #: f.albums.cc:877 msgid "Replace Album File" msgstr "Album Datei ersetzen" #: f.albums.cc:879 f.albums.cc:1004 msgid "Choose Albums" msgstr "Alben auswählen" #: f.albums.cc:883 msgid "old file" msgstr "alte Datei" #: f.albums.cc:886 msgid "new file" msgstr "neue Datei" #: f.albums.cc:889 msgid "replace old" msgstr "alte ersetzen" #: f.albums.cc:890 msgid "add after old" msgstr "nach altem einfügen" #: f.albums.cc:936 msgid "no albums chosen" msgstr "keine Alben gewählt" #: f.albums.cc:1117 f.widgets.cc:460 msgid "Album Mass Update" msgstr "Gesamtes Album aktualisieren" #: f.albums.cc:1125 msgid "Process all album files:" msgstr "Alle Dateien im Album bearbeiten:" #: f.albums.cc:1128 msgid "Replace all with newest version only" msgstr "Alle Dateien nur mit aktueller Version ersetzen" #: f.albums.cc:1130 msgid "Replace all versions with newest version" msgstr "Alle Versionen mit aktueller Version ersetzen" #: f.albums.cc:1132 msgid "Add newest version to existing versions" msgstr "Neueste Version zu vorhandenen Versionen zufügen" #: f.albums.cc:1134 msgid "Replace all with original and all versions" msgstr "Alle Dateien mit Original und allen Versionen ersetzen" #: f.albums.cc:1136 msgid "Replace all with original + newest version" msgstr "Nur Original und neueste Version der Auswahl behalten" #: f.albums.cc:1140 msgid "Process album files matching selected files:" msgstr "Albumdateien verarbeiten, die mit ausgewählten Dateien übereinstimmen" #: f.albums.cc:1143 msgid "Replace all with selected versions" msgstr "Alle Dateien mit ausgewählten Versionen ersetzen" #: f.albums.cc:1145 msgid "Replace all versions with selected versions" msgstr "Alle Versionen mit ausgewählten Versionen ersetzen" #: f.albums.cc:1147 msgid "Add selected versions to existing versions" msgstr "Ausgewählte Versionen zu vorhandenen Versionen hinfügen" #: f.albums.cc:1149 msgid "Replace all with original + selected versions" msgstr "Alle Dateien durch Original + ausgewählte Versionen ersetzen" #: f.albums.cc:1237 msgid "Select Albums" msgstr "Alben auswählen" #: f.albums.cc:1296 #, c-format msgid "too many selected files: %d" msgstr "zu viele ausgewählte Dateien: %d" #: f.albums.cc:1535 msgid "Save Gallery as Album" msgstr "Galerie als Album speichern" #: f.albums.cc:1739 msgid "instant" msgstr "sofortig" #: f.albums.cc:1740 msgid "fade-in" msgstr "einblenden" #: f.albums.cc:1741 msgid "roll-right" msgstr "nach rechts rollen" #: f.albums.cc:1742 msgid "roll-down" msgstr "nach unten rollen" #: f.albums.cc:1743 msgid "venetian" msgstr "Jalousie" #: f.albums.cc:1744 msgid "grate" msgstr "Gitter" #: f.albums.cc:1745 msgid "rectangle" msgstr "Rechteck" #: f.albums.cc:1746 msgid "implode" msgstr "Implodieren" #: f.albums.cc:1747 msgid "explode" msgstr "Explodieren" #: f.albums.cc:1748 msgid "radar" msgstr "Radar" #: f.albums.cc:1749 msgid "Japan-fan" msgstr "Japan-Fächer" #: f.albums.cc:1750 msgid "spiral" msgstr "Spirale" #: f.albums.cc:1751 f.area.cc:140 msgid "ellipse" msgstr "Ellipse" #: f.albums.cc:1752 msgid "raindrops" msgstr "Regentropfen" #: f.albums.cc:1753 msgid "doubledoor" msgstr "Doppeltür" #: f.albums.cc:1754 msgid "rotate" msgstr "drehen" #: f.albums.cc:1755 msgid "fallover" msgstr "Umkippen" #: f.albums.cc:1756 msgid "spheroid" msgstr "Sphäroid" #: f.albums.cc:1757 msgid "turn-page" msgstr "umblättern" #: f.albums.cc:1758 msgid "french-door" msgstr "Fenstertür" #: f.albums.cc:1759 msgid "turn-cube" msgstr "Dreh-Würfel" #: f.albums.cc:1760 msgid "windmill" msgstr "Windmühle" #: f.albums.cc:1761 msgid "pixelize" msgstr "verpixeln" #: f.albums.cc:1762 msgid "twist" msgstr "verdrehen" #: f.albums.cc:1763 msgid "Xopen" msgstr "XBlende" #: f.albums.cc:1764 msgid "squish" msgstr "quetschen" #: f.albums.cc:1765 msgid "disintegrate" msgstr "zerstückeln" #: f.albums.cc:1766 msgid "interleave" msgstr "verschachteln" #: f.albums.cc:1797 f.widgets.cc:462 msgid "Slide Show" msgstr "Diaschau" #: f.albums.cc:1800 msgid "Select Album" msgstr "Album auswählen" #: f.albums.cc:1805 msgid "Caption Time" msgstr "Titel-Anzeigedauer" #: f.albums.cc:1808 msgid "Image Time" msgstr "Bild-Anzeigedauer" #: f.albums.cc:1811 msgid "Clip Limit %" msgstr "Kapp-Grenzwert %" #: f.albums.cc:1815 msgid "Music File" msgstr "Musikdatei" #: f.albums.cc:1820 msgid "Full Screen" msgstr "Vollbild" #: f.albums.cc:1821 msgid "Auto-replay" msgstr "Auto-Wiederholung" #: f.albums.cc:1824 msgid "Customize:" msgstr "Anpassen:" #: f.albums.cc:1825 msgid "transitions" msgstr "Übergänge" #: f.albums.cc:1826 msgid "image files" msgstr "Bilddateien" #: f.albums.cc:1827 msgid "KB controls" msgstr "Tastatur Regler" #: f.albums.cc:1841 f.albums.cc:1908 f.albums.cc:2141 f.albums.cc:2450 #: f.albums.cc:2786 f.albums.cc:2803 f.albums.cc:3039 msgid "invalid album" msgstr "Ungültiges Album" #: f.albums.cc:1936 msgid "open album" msgstr "Album öffnen" #: f.albums.cc:1948 msgid "Select music file" msgstr "Musikdatei auswählen" #: f.albums.cc:1983 #, c-format msgid "%d images" msgstr "%d Bilder" #: f.albums.cc:2008 msgid "" "arrow keys show previous or next image instantly \n" "space bar (blank) is allowed and shows as '-'" msgstr "" "Pfeiltasten links/rechts zeigen voriges/nächstes Bild \n" "Leertaste (blank) ist erlaubt und wird als '-' angezeigt" #: f.albums.cc:2027 msgid "Keyboard Preferences" msgstr "Tastaturvoreinstellungen" #: f.albums.cc:2030 msgid "blank or unblank window" msgstr "Fenster aus- oder wieder einblenden" #: f.albums.cc:2033 msgid "show next image, with transition" msgstr "nächstes Bild zeigen, mit Überblendung" #: f.albums.cc:2036 msgid "pause or resume slide show" msgstr "Diaschau pausieren oder fortsetzen" #: f.albums.cc:2039 msgid "magnify image (loupe tool)" msgstr "Bild vergrößern (Lupe-Werkzeug)" #: f.albums.cc:2145 msgid "Transition Preferences" msgstr "Überblendevoreinstellungen" #: f.albums.cc:2147 msgid "Transitions File" msgstr "Überblende-Datei" #: f.albums.cc:2156 msgid "random" msgstr "Zufällig" #: f.albums.cc:2159 f.albums.cc:2175 f.albums.cc:2179 msgid "time" msgstr "Zeit" #: f.albums.cc:2161 msgid "set all" msgstr "Alle setzen" #: f.albums.cc:2173 f.albums.cc:2177 msgid "transition" msgstr "Überblenden" #: f.albums.cc:2174 f.albums.cc:2178 msgid "use" msgstr "benutzen" #: f.albums.cc:2176 f.albums.cc:2180 msgid "pref" msgstr "Vorzug" #: f.albums.cc:2280 f.albums.cc:2322 msgid "invalid file" msgstr "ungültige Datei" #: f.albums.cc:2394 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "Dateiformat Fehler: \n" " %s" #: f.albums.cc:2456 msgid "Image Preferences" msgstr "Bildvoreinstellungen" #: f.albums.cc:2460 f.file.cc:1610 f.file.cc:1995 msgid "Image File:" msgstr "Bilddatei:" #: f.albums.cc:2464 msgid "Action" msgstr "Aktion" #: f.albums.cc:2476 msgid "Play tone when image shows" msgstr "Klang abspielen wenn Bild erscheint" #: f.albums.cc:2481 msgid "Wait before filename/caption/comments" msgstr "Warten vor Dateiname/Titel/Bemerkungen" #: f.albums.cc:2485 msgid "Show image file name (overlap)" msgstr "Dateinamen eingeblendet zeigen" #: f.albums.cc:2489 msgid "Show image caption (overlap)" msgstr "Titel eingeblendet zeigen" #: f.albums.cc:2493 msgid "Show image comments (overlap)" msgstr "Anmerkungen eingeblendet zeigen" #: f.albums.cc:2497 msgid "Wait before zoom" msgstr "Warten vor Zoom" #: f.albums.cc:2501 f.effects.cc:4023 msgid "Zoom" msgstr "Zoom" #: f.albums.cc:2505 msgid "zoom-in" msgstr "hineinzoomen" #: f.albums.cc:2506 msgid "zoom-out" msgstr "herauszoomen" #: f.albums.cc:2510 msgid "Zoom Center" msgstr "Zoom-Mitte" #: f.albums.cc:2515 msgid "Wait after zoom" msgstr "Warten nach Zoom" #: f.albums.cc:2521 msgid "Transition to next image" msgstr "Überblenden zum nächsten Bild" #: f.albums.cc:2524 f.albums.cc:2654 msgid "next" msgstr "nächstes" #: f.albums.cc:2644 msgid "click on thumbnail to set zoom center" msgstr "Thumbnail anklicken um Zoom-Mitte zu setzen" #: f.area.cc:93 msgid "Select Area for Edits" msgstr "Fläche für Bearbeitung auswählen" #: f.area.cc:94 f.area.cc:1890 f.edit.cc:8198 msgid "Press F1 for help" msgstr "Für Hilfe F1 drücken" #: f.area.cc:104 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "In dieser Bearbeitungsfunktion \n" "Fläche auswählen nicht möglich" #: f.area.cc:139 msgid "select rectangle" msgstr "Rechteck wählen" #: f.area.cc:143 msgid "freehand draw" msgstr "Freihändig zeichnen" #: f.area.cc:144 msgid "follow edge" msgstr "Rand folgen" #: f.area.cc:147 msgid "draw/replace" msgstr "Zeichnen/ersetzen" #: f.area.cc:150 msgid "Line Color:" msgstr "Linienfarbe:" #: f.area.cc:164 msgid "match level %" msgstr "Farbübereinstimmung %" #: f.area.cc:168 msgid "search range" msgstr "Suchentfernung" #: f.area.cc:172 msgid "select area in mouse" msgstr "Fläche im Maus auswählen" #: f.area.cc:175 msgid "select one color in mouse:" msgstr "eine Farbe im Mauskreis wählen:" #: f.area.cc:179 msgid "select all colors in mouse (flood)" msgstr "Alle Farben im Mauskreis auswählen (Fluten)" #: f.area.cc:184 msgid "Area Edge Blend Width" msgstr "Flächenrand Mischbreite" #: f.area.cc:188 msgid "Edge Creep" msgstr "Randdehnung" #: f.area.cc:200 msgid "drag mouse to select rectangular area" msgstr "Maus ziehen, um rechteckige Fläche auszuwählen" #: f.area.cc:201 msgid "drag mouse to select circular or elliptical area" msgstr "Maus ziehen, um kreisförmige/elliptische Fläche auszuwählen" #: f.area.cc:202 msgid "drag mouse to outline an area" msgstr "Maus ziehen um eine Fläche zu umreißen" #: f.area.cc:203 msgid "drag mouse along an edge to follow the edge" msgstr "Maus am Rand entlang ziehen, um dem Rand zu folgen" #: f.area.cc:204 msgid "drag mouse near a line to move the line" msgstr "Maus nahe einer Linie ziehen, um die Linie zu versetzen" #: f.area.cc:205 msgid "select line color" msgstr "Linienfarbe auswählen" #: f.area.cc:206 msgid "size of mouse selection circle" msgstr "Größe des Mausauswahlkreises" #: f.area.cc:207 msgid "required match level to select by color" msgstr "Erforderliche Übereinstimmung zur Auswahl nach Farbe" #: f.area.cc:208 msgid "select by color search range" msgstr "Auswahl nach Farbsuchbereich" #: f.area.cc:209 msgid "select area within mouse circle" msgstr "Bereich innerhalb des Mauskreises auswählen" #: f.area.cc:210 msgid "" "first select the checkbox, then \n" "shift+click on image to set the color" msgstr "" "Erst Ankreuzfeld selektieren, dann \n" "Bild umschalt+anklicken um Farbe zu setzen" #: f.area.cc:212 msgid "select surrounding areas matching colors in mouse" msgstr "Umgebungen wählen, die Farben unter der Maus entsprechen" #: f.area.cc:213 f.area.cc:214 msgid "area edits fade away within edge distance" msgstr "Änderungen in Fläche schwinden langsam in Randbreite" #: f.area.cc:215 msgid "move area boundary in/out in 1-pixel steps" msgstr "Bereichsgrenze in Schritten von 1 Pixel verschieben" #: f.area.cc:216 msgid "map selected areas and verify" msgstr "Ausgewählte Bereiche festlegen und überprüfen" #: f.area.cc:217 msgid "invert area" msgstr "Bereich invertieren" #: f.area.cc:218 msgid "show area outlines" msgstr "Flächenumrisse anzeigen" #: f.area.cc:219 msgid "hide area outlines" msgstr "Bereichsumrisse ausblenden" #: f.area.cc:220 msgid "clear area selections" msgstr "Gewählte Bereiche löschen" #: f.area.cc:404 f.area.cc:570 #, c-format msgid "exceed %d edits" msgstr "%d Edits überschritten" #: f.area.cc:1471 msgid "" "Fill selected areas with color for visual verification. \n" "Method 1: left-click in each selected area not already filled. \n" "Method 2: right-click OUTSIDE all selected areas. \n" "Press [help] button for clarification" msgstr "" "Ausgewählte Flächen zur Sichtprüfung mit Farbe füllen. \n" "Methode 1: in jeder ungefüllten Fläche links-anklicken. \n" "Methode 2: AUßERHALB aller Flächen rechts-anklicken. \n" "[Hilfe] Knopf drücken für Erklärung" #: f.area.cc:1499 msgid "finish area" msgstr "Fläche fertigstellen" #: f.area.cc:1528 f.area.cc:1757 #, c-format msgid "found %d pixels" msgstr "%d Pixels gefunden" #: f.area.cc:1546 msgid "" "Method 1: \n" " Left-click inside an outlined area that is not already filled. \n" " Area will be filled with color for visible verification. \n" " Gaps in the outline will cause overflow outside the area. \n" " Repeat for each outlined area that is not already filled. \n" "Method 2: \n" " Right-click outside ALL outlined areas. \n" " All areas will be filled with color for visible verification. \n" " Gaps in an area outline will cause that area not to be filled. \n" "Gaps in an area outline: \n" " Gaps must be closed before proceeding with edits. \n" " The Find Gap function can be used for this." msgstr "" "Methode 1: \n" " Links-anklicken in einer umrissenen Fläche die nicht schon gefüllt ist. \n" " Fläche wird zur Sichtprüfung mit Farbe gefüllt. \n" " Lücken in der Flächenumrandung führen zu einem Auslaufen der Farbe. \n" " Wiederholen für jede umrissene Fläche, die nicht schon gefüllt ist. \n" "Methode 2: \n" " Rechts-anklicken außerhalb aller umrissenen Flächen. \n" " Alle Flächen werden zur Sichtprüfung mit Farbe gefüllt. \n" " Bei Lücken in der Flächenumrandung wird die betroffene Fläche nicht " "gefüllt. \n" "Lücken in der Flächenumrandung \n" " Lücken müssen geschlossen werden, um das Bild weiter bearbeiten zu " "können. \n" " Dazu die Funktion Lücken Finden benutzen." #: f.area.cc:1889 f.widgets.cc:487 msgid "Select Hairy" msgstr "Zottelig auswählen" #: f.area.cc:1899 msgid "select the area first" msgstr "erst Fläche wählen" #: f.area.cc:1975 msgid "select" msgstr "auswählen" #: f.area.cc:1981 msgid "deselect" msgstr "abwählen" #: f.area.cc:2335 msgid "" "Click near any position on the area outline. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Irgendeine Stelle an der Flächenrand anklicken \n" " Mögliche Lücken im Umrand werden gefunden. \n" " Für Hilfe F1 drücken." #: f.area.cc:2356 msgid "find outline gap" msgstr "Lücke in Umrandung finden" #: f.area.cc:2451 msgid "cannot find area outline" msgstr "kann Flächenrand nicht finden" #: f.area.cc:3285 msgid "save area as a PNG file" msgstr "Fläche als PNG Datei speichern" #: f.area.cc:3407 msgid "position with mouse click/drag" msgstr "Maus klicken/ziehen positioniert" #: f.area.cc:3467 msgid "Paste Image" msgstr "Bild einfügen" #: f.area.cc:3472 msgid "resize" msgstr "Skalieren" #: f.combine.cc:174 f.combine.cc:828 f.combine.cc:1436 f.combine.cc:2066 #: f.combine.cc:2510 msgid "Select 2 to 9 files" msgstr "2 bis 9 Dateien auswählen" #: f.combine.cc:196 f.combine.cc:850 f.combine.cc:1458 f.combine.cc:2088 msgid "Images are not all the same size" msgstr "Bilder sind nicht alle gleich groß" #: f.combine.cc:560 msgid "Adjust Image Contributions" msgstr "Bild Beiträge einstellen" #: f.combine.cc:564 msgid "dark pixels" msgstr "dunkle Pixel" #: f.combine.cc:566 msgid "light pixels" msgstr "helle Pixel" #: f.combine.cc:568 msgid "file:" msgstr "Datei:" #: f.combine.cc:1036 msgid "Paint and Warp Image" msgstr "Bild malen und verformen" #: f.combine.cc:1044 f.edit.cc:6054 msgid "paint" msgstr "zeichnen" #: f.combine.cc:1045 msgid "warp" msgstr "Verformen" #: f.combine.cc:1648 f.combine.cc:2594 msgid "Select and Paint Image" msgstr "Bild auswählen und malen" #: f.combine.cc:1656 msgid "Transient Objects" msgstr "Flüchtige Objekte" #: f.combine.cc:2264 msgid "Adjust Pixel Composition" msgstr "Pixel Zusammensetzung angleichen" #: f.combine.cc:2266 msgid "use average" msgstr "Durchschnitt benutzen" #: f.combine.cc:2267 msgid "use median" msgstr "Mittelwert benutzen" #: f.combine.cc:2269 msgid "omit low pixel" msgstr "Niedrigste Pixel weglassen" #: f.combine.cc:2270 msgid "omit high pixel" msgstr "Höchste Pixel weglassen" #: f.combine.cc:2532 f.combine.cc:2844 msgid "Images must be exactly the same size" msgstr "Bilder müssen genau gleich groß sein" #: f.combine.cc:2605 msgid " Fill " msgstr "Füllen" #: f.combine.cc:2606 msgid "using selected image" msgstr "Gewähltes Bild wird verwendet" #: f.combine.cc:2825 f.combine.cc:3095 msgid "Select 2 files" msgstr "2 Dateien wählen" #: f.combine.cc:2902 msgid "Split two Images" msgstr "Zwei Bilder aufteilen" #: f.combine.cc:2903 msgid "drag image boundary" msgstr "Bildgrenze ziehen" #: f.combine.cc:3092 msgid "Image Differences" msgstr "Bild-Unterschiede" #: f.combine.cc:3108 msgid "differences" msgstr "Unterschiede" #: f.combine.cc:3110 msgid "X-align" msgstr "X-justieren" #: f.combine.cc:3113 msgid "Y-align" msgstr "Y-justieren" #: f.combine.cc:3179 msgid "select exactly 2 files" msgstr "Genau 2 Dateien auswählen" #: f.combine.cc:3408 f.combine.cc:4562 msgid "Select 2 to 4 files" msgstr "2 bis 4 Dateien auswählen" #: f.combine.cc:3483 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from lower edge." msgstr "" "Bilder zur Grobausrichtmung mit der Maus ziehen.\n" "Zum Drehen unteren Rand links/rechts ziehen" #: f.combine.cc:3485 f.combine.cc:4639 msgid "no curve (scanned image)" msgstr "Keine Kurve (gescanntes Bild)" #: f.combine.cc:3486 f.combine.cc:4640 msgid "Search for lens mm" msgstr "Suche nach Linsenbrennweite" #: f.combine.cc:3487 f.combine.cc:4641 msgid "Save lens mm → image EXIF" msgstr "Linsenbrennweite in Bild EXIF speichern" #: f.combine.cc:3547 f.combine.cc:4701 msgid "Pre-align Images" msgstr "Bilder grob ausrichten" #: f.combine.cc:3551 f.combine.cc:4705 msgid "lens mm" msgstr "Objektiv-Brennweite mm" #: f.combine.cc:3556 f.combine.cc:4710 msgid "no auto warp" msgstr "kein Auto-Verformen" #: f.combine.cc:3558 f.combine.cc:4712 msgid "manual align" msgstr "manuell ausrichten" #: f.combine.cc:3560 f.combine.cc:4714 f.process.cc:1395 f.widgets.cc:504 #: f.widgets.cc:829 msgid "Resize" msgstr "Größe verändern" #: f.combine.cc:3561 f.combine.cc:4715 msgid "resize window" msgstr "Fenstergröße ändern" #: f.combine.cc:3569 f.combine.cc:4723 msgid "do not warp images during auto-alignment" msgstr "Bilder nicht während Rand-Ausrichtung verformen" #: f.combine.cc:3614 f.combine.cc:4768 msgid "use two images only" msgstr "nur zwei Bilder benutzen" #: f.combine.cc:3644 f.combine.cc:3848 f.combine.cc:4034 f.combine.cc:4796 #: f.combine.cc:5000 f.combine.cc:5186 msgid "Too little overlap, cannot align" msgstr "Unzureichende Überlappung, Ausrichtung nicht möglich" #: f.combine.cc:4112 f.combine.cc:5264 msgid "Match Brightness and Color" msgstr "Helligkeit und Farbe anpassen" #: f.combine.cc:4158 msgid "Select image" msgstr "Bild wählen" #: f.combine.cc:4173 f.combine.cc:5325 msgid "auto color" msgstr "Farbe auto" #: f.combine.cc:4174 f.combine.cc:5326 msgid "file color" msgstr "Datei-Farbe" #: f.combine.cc:4180 f.combine.cc:5332 msgid "mouse warp" msgstr "Maus-Verformung" #: f.combine.cc:4182 f.combine.cc:5334 msgid "flatten image" msgstr "Bild abflachen" #: f.combine.cc:4637 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from right edge." msgstr "" "Bilder zur Grobausrichtung mit der Maus ziehen.\n" "Zum Drehen, vom rechten Rand ziehen" #: f.combine.cc:5596 msgid "pano tools (hugin) not installed" msgstr "pano tools (hugin) nicht installiert" #: f.combine.cc:5605 msgid "Select at least 2 files" msgstr "Mindestens 2 Dateien auswählen" #: f.edit.cc:106 msgid "" "drag middle to move \n" "drag corners to resize \n" "drag right edge to level" msgstr "" "zum Verschieben, mittig ziehen \n" "zum Skalieren, an Ecke ziehen \n" "zum Ebnen, an rechtem Rand ziehen" #: f.edit.cc:209 f.widgets.cc:501 f.widgets.cc:828 msgid "Trim/Rotate" msgstr "Zuschneiden/Drehen" #: f.edit.cc:224 f.edit.cc:1483 msgid "ratio" msgstr "Verhältnis" #: f.edit.cc:226 msgid "Lock Ratio" msgstr "Seitenverhältnis fixieren" #: f.edit.cc:229 msgid "Trim Size:" msgstr "Größe anpassen:" #: f.edit.cc:237 msgid "Set Ratio" msgstr "Seitenverhältnis setzen" #: f.edit.cc:238 f.widgets.cc:493 fotoxx.h:1371 msgid "Invert" msgstr "Invertieren" #: f.edit.cc:239 msgid "Customize" msgstr "Anpassen" #: f.edit.cc:248 msgid "Degrees:" msgstr "Grad:" #: f.edit.cc:250 msgid "Auto Level" msgstr "Auto Ebnen" #: f.edit.cc:256 f.process.cc:186 f.process.cc:822 f.widgets.cc:502 #: f.widgets.cc:830 msgid "Upright" msgstr "Aufrichten" #: f.edit.cc:258 msgid "maximize trim box" msgstr "Schnittfläche maximieren" #: f.edit.cc:259 msgid "trim transparent edges" msgstr "transparente Ränder abschneiden" #: f.edit.cc:260 msgid "lock width/height ratio" msgstr "Breite/Höhe Verhältnis fixieren" #: f.edit.cc:261 msgid "use EXIF data if available" msgstr "EXIF-Daten verwenden wenn vorhanden" #: f.edit.cc:603 f.edit.cc:1550 msgid "rotation unknown" msgstr "Drehrichtung unbekannt" #: f.edit.cc:1479 msgid "Trim Buttons" msgstr "Schnitt Buttons" #: f.edit.cc:1482 msgid "label" msgstr "Kennung" #: f.edit.cc:1692 f.widgets.cc:503 f.widgets.cc:833 msgid "Retouch" msgstr "Retusche" #: f.edit.cc:1705 msgid "Auto black level" msgstr "Auto Schwarzabgleich" #: f.edit.cc:1708 f.edit.cc:1714 msgid "sample %" msgstr "Probe %" #: f.edit.cc:1711 msgid "Auto white balance" msgstr "Auto Weißabgleich" #: f.edit.cc:1717 msgid "Click gray spot for white balance" msgstr "Graustelle anklicken für Weißabgleich" #: f.edit.cc:1720 msgid "Click dark spot for black level" msgstr "Dunkelstelle anklicken für Schwarzwert" #: f.edit.cc:1723 msgid "Click for RGB distribution" msgstr "Zur RGB-Aufteilung, klicken" #: f.edit.cc:1731 fotoxx.h:1326 msgid "Brightness" msgstr "Helligkeit" #: f.edit.cc:1732 f.effects.cc:4049 fotoxx.h:1339 msgid "Contrast" msgstr "Kontrast" #: f.edit.cc:1733 f.edit.cc:3279 f.edit.cc:3311 f.edit.cc:6777 msgid "Saturation" msgstr "Farbsättigung" #: f.edit.cc:1734 msgid "Temperature" msgstr "Temperatur" #: f.edit.cc:1742 msgid "Settings File" msgstr "Einstellungsdatei" #: f.edit.cc:2250 msgid "choose a better spot" msgstr "Bessere Stelle wählen" #: f.edit.cc:2555 msgid "Resize Image" msgstr "Bildgröße verändern" #: f.edit.cc:2571 msgid "Previous" msgstr "Vorheriges" #: f.edit.cc:2589 msgid "W/H Ratio:" msgstr "B/H Verhältnis" #: f.edit.cc:2591 msgid "Lock" msgstr "Fixieren" #: f.edit.cc:2840 msgid "insufficient memory, cannot proceed" msgstr "zu wenig Speicher, kann nicht fortfahren" #: f.edit.cc:2936 f.widgets.cc:505 msgid "Adjust RGB" msgstr "RGB einstellen" #: f.edit.cc:2942 msgid "+Brightness" msgstr "+Helligkeit" #: f.edit.cc:2943 msgid "+Red -Cyan" msgstr "+Rot -Zyan" #: f.edit.cc:2944 msgid "+Green -Magenta" msgstr "+Grün -Magenta" #: f.edit.cc:2945 msgid "+Blue -Yellow" msgstr "+Blau -Gelb" #: f.edit.cc:2948 msgid "Contrast Red" msgstr "Kontrast Rot" #: f.edit.cc:2949 msgid "Contrast Green" msgstr "Kontrast Grün" #: f.edit.cc:2950 msgid "Contrast Blue" msgstr "Kontrast Blau" #: f.edit.cc:3268 f.widgets.cc:506 msgid "Adjust HSL" msgstr "HSL einstellen" #: f.edit.cc:3272 msgid "Input color to match and adjust:" msgstr "Bildfarbe zum Angleichen und Einstellen" #: f.edit.cc:3274 msgid "shift+click on image to select color" msgstr "Shift+Klick ins Bild zur Farbauswahl" #: f.edit.cc:3277 msgid "Match using:" msgstr "Angleichen mit:" #: f.edit.cc:3278 msgid "Hue" msgstr "Farbton" #: f.edit.cc:3280 f.edit.cc:3312 f.edit.cc:6778 msgid "Lightness" msgstr "Helligkeit" #: f.edit.cc:3290 msgid "Output Color" msgstr "Ausgabefarbe" #: f.edit.cc:3310 f.edit.cc:6776 msgid "Color Hue" msgstr "Farbton" #: f.edit.cc:3313 msgid "Adjustment" msgstr "Einstellung" #: f.edit.cc:3828 msgid "Image Markup" msgstr "Bild-Anmerkungen" #: f.edit.cc:3829 f.edit.cc:3894 msgid "Draw text on image" msgstr "Text aufs Bild zeichnen" #: f.edit.cc:3830 f.edit.cc:4818 msgid "Draw line or arrow on image" msgstr "Linie/Pfeil aufs Bild zeichnen" #: f.edit.cc:3831 f.edit.cc:5499 msgid "Draw box on image" msgstr "Box aufs Bild zeichnen" #: f.edit.cc:3832 f.edit.cc:5700 msgid "Draw oval on image" msgstr "Oval aufs Bild zeichnen" #: f.edit.cc:3871 msgid "+Version" msgstr "+Version" #: f.edit.cc:3895 msgid "Enter text, click/drag on image, right click to remove" msgstr "" "Text eingeben, mit Maus auf Bild klicken/ziehen, zum Entfernen rechts klicken" #: f.edit.cc:3944 msgid "Use settings file" msgstr "Einstellungsdatei benutzen" #: f.edit.cc:3949 f.mashup.cc:2438 f.tools.cc:1668 f.tools.cc:1677 msgid "Text" msgstr "Text" #: f.edit.cc:3954 msgid "Use metadata key" msgstr "Metadaten-Schlüsselwort benutzen" #: f.edit.cc:3973 f.mashup.cc:2456 msgid "text" msgstr "Text" #: f.edit.cc:3974 f.edit.cc:4847 f.mashup.cc:2457 f.mashup.cc:2795 msgid "backing" msgstr "Hinterfarbe" #: f.edit.cc:3975 f.edit.cc:4848 f.mashup.cc:2458 f.mashup.cc:2796 msgid "outline" msgstr "Umriss" #: f.edit.cc:3976 f.edit.cc:4849 f.mashup.cc:2459 f.mashup.cc:2797 msgid "shadow" msgstr "Schatten" #: f.edit.cc:4002 msgid "save to current file" msgstr "in aktueller Datei speichern" #: f.edit.cc:4003 f.file.cc:2432 msgid "save as new file version" msgstr "als neue Dateiversion speichern" #: f.edit.cc:4004 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:4167 f.mashup.cc:2563 f.tools.cc:1961 msgid "select font" msgstr "Schrift wählen" #: f.edit.cc:4446 msgid "text file is defective" msgstr "Textdatei ist fehlerhaft" #: f.edit.cc:4786 msgid "" "Enter line or arrow properties in dialog, \n" "click/drag on image, right click to remove" msgstr "" "Linien-/Pfeil-Attribute eingeben, \n" "auf Bild klicken/ziehen, \n" "zum Entfernen rechts klicken" #: f.edit.cc:4826 f.mashup.cc:2774 msgid "Line length" msgstr "Linien-Länge" #: f.edit.cc:4833 f.mashup.cc:2781 msgid "Arrow head" msgstr "Pfeilspitze" #: f.edit.cc:4846 f.mashup.cc:2794 msgid "line" msgstr "Linie" #: f.edit.cc:4875 msgid "" "fix line/arrow in layout \n" " start new line/arrow" msgstr "" "Linie/Pfeil lagemäßig festlegen \n" " neue Linie/Pfeil beginnen" #: f.edit.cc:5474 msgid "" "drag mouse to draw box \n" "shift + drag center to move box \n" "shift + drag edge to move edge" msgstr "" "Maus ziehen, um Box zu zeichnen \n" "shift + Mitte ziehen, um Box zu verschieben \n" "shift + Kante ziehen, um Kante zu verschieben" #: f.edit.cc:5505 f.edit.cc:5706 msgid "line color" msgstr "Linienfarbe" #: f.edit.cc:5508 f.edit.cc:5709 msgid "line width" msgstr "Linienbreite" #: f.edit.cc:5674 msgid "" "drag mouse down/right to draw oval \n" "shift + drag center to move oval \n" "shift + drag lower right edge to change" msgstr "" "Maus unten/rechts ziehen, um Oval zu zeichnen \n" "shift + Mitte ziehen, um Oval zu verschieben \n" "shift + untere rechte Kante ziehen, um zu ändern" #: f.edit.cc:5712 msgid "oval" msgstr "Oval" #: f.edit.cc:5713 msgid "circle" msgstr "Kreis" #: f.edit.cc:5988 msgid "" "shift + left click: pick color from image \n" "left drag: paint color on image \n" "right drag: restore original image" msgstr "" "Umschalt + Links-Klick: Farbe im Bild wählen \n" "Links-ziehen: Bild mit Farbe bemalen \n" "Rechts-ziehen: Original-Bild zurückbringen" #: f.edit.cc:6024 msgid "Paint on Image" msgstr "Bild bemalen" #: f.edit.cc:6030 msgid "paint color" msgstr "Malfarbe" #: f.edit.cc:6041 f.edit.cc:6970 f.edit.cc:7548 msgid "brush size" msgstr "Pinselgröße" #: f.edit.cc:6055 msgid "erase" msgstr "löschen" #: f.edit.cc:6060 msgid "include transparent areas" msgstr "Transparente Flächen übermalen" #: f.edit.cc:6063 msgid "drag image" msgstr "Bild ziehen" #: f.edit.cc:6065 msgid "zoom image" msgstr "Bild zoomen" #: f.edit.cc:6592 msgid "Color Chooser" msgstr "Farb-Wähler" #: f.edit.cc:6594 msgid "click on desired color" msgstr "gewünschte Farbe anklicken" #: f.edit.cc:6929 msgid "" "shift + left click: pick position to copy \n" "left click or drag: copy image to mouse \n" "right click or drag: restore original image" msgstr "" "Umschalt + links Klick: Stelle zum Kopieren setzen \n" "links klicken oder ziehen: Bild zur Maus kopieren \n" "rechts klicken oder ziehen: original Bild zurückbringen" #: f.edit.cc:6960 msgid "Copy Pixels (1 image)" msgstr "Pixel kopieren (1 Bild)" #: f.edit.cc:6979 f.edit.cc:7557 msgid "paint over transparent areas" msgstr "Transparente Flächen übermalen" #: f.edit.cc:7531 msgid "" "left click: synchronize copy position \n" "left click or drag: copy source image to mouse \n" "right click or drag: restore original image" msgstr "" "Links-klick: Position zum Kopieren abgleichen \n" "Links-klick oder -ziehen: Quell-Bild zu Mausposition kopieren \n" "Rechts-klick oder -ziehen: Original-Bild zurückbringen" #: f.edit.cc:7535 msgid "Copy Pixels (2 images)" msgstr "Pixel kopieren (2 Bilder)" #: f.edit.cc:7541 msgid "source image scale" msgstr "Quellbild Maßstab" #: f.edit.cc:8118 msgid "source image failure (scale too big?)" msgstr "Fehler im Ausgangsbild (Maßstab zu groß?)" #: f.edit.cc:8197 f.widgets.cc:511 msgid "Paint Edits" msgstr "Änderungen zeichnen" #: f.edit.cc:8203 fotoxx.cc:3576 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "Ausgewählte Fläche kann nicht behalten bleiben.\n" "Fortfahren?" #: f.edit.cc:8211 f.tools.cc:3751 msgid "Edit function must be active" msgstr "Editierfunktion muss aktiv sein" #: f.edit.cc:8216 msgid "Cannot use Paint Edits" msgstr "Kann Zeichenänderungen nicht übernehmen" #: f.edit.cc:8243 f.edit.cc:8485 msgid "power: center" msgstr "Stärke: Mitte" #: f.edit.cc:8248 msgid "reset area" msgstr "zurücksetzen" #: f.edit.cc:8428 msgid "finish current edit first" msgstr "Erst laufende Bearbeitung beenden" #: f.edit.cc:8433 msgid "no previous edit" msgstr "noch nichts bearbeitet" #: f.edit.cc:8438 msgid "no current image" msgstr "Kein aktuelles Bild" #: f.edit.cc:8449 msgid "This edit cannot be incrementally undone" msgstr "Schrittweise rückgängig machen nicht möglich" #: f.edit.cc:8480 f.widgets.cc:512 msgid "Undo Edits" msgstr "Änderungen rückgängig machen" #: f.edit.cc:8708 f.edit.cc:8756 msgid "Edit Plugins" msgstr "Plugins bearbeiten" #: f.edit.cc:8709 msgid "Edit plugins menu" msgstr "Plugins-Menü editieren" #: f.edit.cc:8717 msgid "Run as Fotoxx edit function" msgstr "Als Fotoxx-Bearbeitungsfunktion benutzen" #: f.edit.cc:8758 msgid "menu name" msgstr "Menü-Name" #: f.edit.cc:8761 msgid "command" msgstr "Befehl" #: f.edit.cc:8959 msgid "Plugin working ..." msgstr "Plugin arbeitet ..." #: f.edit.cc:8968 msgid "plugin failed" msgstr "Plug-in fehlgeschlagen" #: f.edit.cc:9004 msgid "Raw Therapee not installed" msgstr "Raw Therapee nicht installiert" #: f.edit.cc:9023 msgid "RAW type not registered in User Preferences" msgstr "RAW-Typ in Benutzereinstellungen nicht vorhanden" #: f.edit.cc:9038 msgid "Raw Therapee produced no tif file" msgstr "Raw Therapee hat keine TIF Datei erzeugt" #: f.effects.cc:83 msgid "Convert to Sketch" msgstr "In eine Zeichnung verwandeln" #: f.effects.cc:161 msgid "Clip Level" msgstr "Kapp-Niveau" #: f.effects.cc:165 msgid "Algorithm" msgstr "Algorithmus" #: f.effects.cc:170 msgid "Foreground" msgstr "Vordergrund" #: f.effects.cc:173 f.tools.cc:1680 msgid "Background" msgstr "Hintergrund" #: f.effects.cc:609 msgid "Line Threshold" msgstr "Linienschwellenwert" #: f.effects.cc:610 msgid "Line Width" msgstr "Linienbreite" #: f.effects.cc:611 f.enhance.cc:3929 msgid "Blur Radius" msgstr "Verwisch-Radius" #: f.effects.cc:612 msgid "Kuwahara Depth" msgstr "Kuwahara Tiefe" #: f.effects.cc:1057 f.widgets.cc:538 msgid "Line Drawing" msgstr "Stift-Zeichnung" #: f.effects.cc:1070 f.effects.cc:2090 msgid "black/white" msgstr "schwarzweiß" #: f.effects.cc:1320 f.widgets.cc:539 msgid "Emboss" msgstr "Prägen" #: f.effects.cc:1326 msgid "Depth" msgstr "Tiefe" #: f.effects.cc:1541 msgid "Simulate Tiles" msgstr "Kacheln simulieren" #: f.effects.cc:1545 msgid "tile size" msgstr "Kachelgröße" #: f.effects.cc:1548 msgid "tile gap" msgstr "Spaltbreite" #: f.effects.cc:1551 msgid "3D depth" msgstr "3D Tiefe" #: f.effects.cc:1780 msgid "Dither Image" msgstr " Rasterbild" #: f.effects.cc:1783 msgid "Dither0" msgstr "Rastern0" #: f.effects.cc:1784 f.effects.cc:1848 msgid "Roy Lichtenstein effect" msgstr "Roy Lichtenstein Effekt" #: f.effects.cc:1787 f.effects.cc:2083 msgid "Dither1" msgstr "Rastern1" #: f.effects.cc:1788 msgid "pure RGB or black/white dots" msgstr "reine RGB- oder Schwarz/Weiß-Punkte" #: f.effects.cc:1791 f.effects.cc:2481 msgid "Dither2" msgstr "Rastern2" #: f.effects.cc:1792 msgid "RGB mix with given bit-depth" msgstr "RGB-Misch mit gegebener Bit-Tiefe" #: f.effects.cc:1795 f.effects.cc:2756 msgid "Dither3" msgstr "Rastern3" #: f.effects.cc:1796 msgid "custom palette colors" msgstr "Farben von eigener Palette" #: f.effects.cc:1852 msgid "dot size" msgstr "Punktgröße" #: f.effects.cc:2087 f.effects.cc:2485 f.effects.cc:2760 msgid "resolution" msgstr "Auflösung" #: f.effects.cc:2089 msgid "RGB color" msgstr "RGB Farbe" #: f.effects.cc:2091 msgid "random position" msgstr "zufällige Stelle" #: f.effects.cc:2488 f.effects.cc:3196 msgid "color depth" msgstr "Farbtiefe" #: f.effects.cc:2491 f.effects.cc:2768 msgid "error compensation" msgstr "Fehlerkompensation" #: f.effects.cc:2764 msgid "palette:" msgstr "Palette:" #: f.effects.cc:2818 msgid "palette file" msgstr "Palettendatei" #: f.effects.cc:3178 f.widgets.cc:542 msgid "Painting" msgstr "Gemälde" #: f.effects.cc:3200 msgid "patch area goal" msgstr "Fleckgröße Ziel" #: f.effects.cc:3204 msgid "req. color match" msgstr "gef. Farbanpassung" #: f.effects.cc:3208 msgid "borders" msgstr "Ränder" #: f.effects.cc:3799 msgid "Add Texture" msgstr "Textur zufügen" #: f.effects.cc:4014 msgid "Background Pattern" msgstr "Hintergrundmuster" #: f.effects.cc:4018 msgid "Pattern File:" msgstr "Musterdatei" #: f.effects.cc:4026 msgid "Geometry" msgstr "Ausmaße" #: f.effects.cc:4030 f.widgets.cc:544 msgid "Pattern" msgstr "Muster" #: f.effects.cc:4038 msgid "Overlap" msgstr "Überschneidung" #: f.effects.cc:4046 msgid "Opacity" msgstr "Deckkraft" #: f.effects.cc:4052 msgid "Grayscale" msgstr "Graustufen" #: f.effects.cc:4478 msgid "Create Mosaic" msgstr "Mosaik erstellen" #: f.effects.cc:4524 msgid "Tile" msgstr "Kachel" #: f.effects.cc:4532 f.widgets.cc:540 msgid "Tiles" msgstr "Kacheln" #: f.effects.cc:4538 msgid "Tile blending" msgstr "Kachel mischen" #: f.effects.cc:4621 #, c-format msgid "exceeded max. tiles: %d" msgstr "Max. Kacheln überschritten: %d" #: f.effects.cc:4628 #, c-format msgid "only %d tile images found" msgstr "Nur %d Kachelbilder gefunden" #: f.effects.cc:5088 f.widgets.cc:546 msgid "Color Mode" msgstr "Farb-Modus" #: f.effects.cc:5091 msgid "reset" msgstr "zurücksetzen" #: f.effects.cc:5092 msgid "black/white positive" msgstr "schwarzweiß Positiv" #: f.effects.cc:5093 msgid "black/white negative" msgstr "schwarzweiß Negativ" #: f.effects.cc:5094 msgid "color negative" msgstr "farbiges Negativ" #: f.effects.cc:5095 msgid "RGB -> GBR" msgstr "RGB -> GBR" #: f.effects.cc:5096 msgid "RGB -> BRG" msgstr "RGB -> BRG" #: f.effects.cc:5097 msgid "sepia" msgstr "Sepia" #: f.effects.cc:5348 msgid "Set color depth to 1-16 bits" msgstr "Farbtiefe auf 1-16 Bits festlegen" #: f.effects.cc:5377 msgid "Set Color Depth" msgstr "Farbtiefe festlegen" #: f.effects.cc:5547 f.widgets.cc:548 msgid "Shift Colors" msgstr "Farben verschieben" #: f.effects.cc:5824 f.widgets.cc:549 msgid "Alien Colors" msgstr "Fremdfarben" #: f.effects.cc:5827 msgid "blocksize" msgstr "Blockgröße" #: f.effects.cc:5830 f.warp.cc:3096 msgid "amplitude" msgstr "Amplitude" #: f.effects.cc:6089 msgid "" "Draw a line across the image in \n" "direction of brightness change." msgstr "" "Zeichne Linie über das Bild in \n" "Richtung des Helligkeitsverlaufes" #: f.effects.cc:6132 msgid "Brightness Ramp" msgstr "Helligkeitsanstieg" #: f.effects.cc:6605 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" "links ziehen: Transparenz addieren \n" "rechts ziehen: Deckkraft addieren" #: f.effects.cc:6640 msgid "Paint Transparency" msgstr "Transparenz zeichnen" #: f.effects.cc:6648 msgid "paintbrush radius" msgstr "Pinsel-Radius" #: f.effects.cc:6649 msgid "strength center" msgstr "Stärke Mitte" #: f.effects.cc:6650 msgid "strength edge" msgstr "Stärke Rand" #: f.effects.cc:6655 msgid "gradual paint" msgstr "Stufenweise malen" #: f.effects.cc:6874 msgid "Mirror Image" msgstr "Bild spiegeln" #: f.effects.cc:6877 f.warp.cc:3098 msgid "horizontal" msgstr "Horizontal" #: f.effects.cc:6878 f.warp.cc:3102 msgid "vertical" msgstr "Vertikal" #: f.effects.cc:7051 f.widgets.cc:553 msgid "Custom Kernel" msgstr "Benutzerspezifischer Kernel" #: f.effects.cc:7055 msgid "Kernel size" msgstr "Kernelgröße" #: f.effects.cc:7072 msgid "multiply" msgstr "multiplizieren" #: f.effects.cc:7075 msgid "add" msgstr "zufügen" #: f.effects.cc:7079 msgid "Data file" msgstr "Kerneldatei" #: f.effects.cc:7099 fotoxx.cc:3845 msgid "Load settings from file" msgstr "Einstellungen von Datei laden" #: f.enhance.cc:505 msgid "Adjust Brightness Distribution" msgstr "Helligkeit-Verteilung justieren" #: f.enhance.cc:544 msgid "Low Cutoff" msgstr "Niedriger Verschnitt" #: f.enhance.cc:545 msgid "High Cutoff" msgstr "Hoher Verschnitt" #: f.enhance.cc:546 msgid "Low Flatten" msgstr "Niedrig abflachen" #: f.enhance.cc:547 msgid "Mid Flatten" msgstr "Mittig abflachen" #: f.enhance.cc:548 msgid "High Flatten" msgstr "Hoch abflachen" #: f.enhance.cc:549 msgid "Low Stretch" msgstr "Wenig strecken" #: f.enhance.cc:550 msgid "Mid Stretch" msgstr "Mittel strecken" #: f.enhance.cc:551 msgid "High Stretch" msgstr "Viel strecken" #: f.enhance.cc:886 msgid "Magnify Gradients" msgstr "Gradienten vergrößern" #: f.enhance.cc:923 msgid "low" msgstr "niedrig" #: f.enhance.cc:925 msgid "high" msgstr "hoch" #: f.enhance.cc:928 msgid "Amplify" msgstr "Verstärken" #: f.enhance.cc:1312 msgid "Flatten Brightness" msgstr "Helligkeit abflachen" #: f.enhance.cc:1363 msgid "Zones" msgstr "Zonenanzahl" #: f.enhance.cc:1370 msgid "Deband Dark" msgstr "Entstreifen Dunkel" #: f.enhance.cc:1373 msgid "Deband Bright" msgstr "Entstreifen Hell" #: f.enhance.cc:1851 msgid "Global Retinex" msgstr "Retinex global" #: f.enhance.cc:1867 msgid "Dark Point" msgstr "Schwarzwert" #: f.enhance.cc:1868 msgid "Bright Point" msgstr "Weißwert" #: f.enhance.cc:1869 msgid "Multiplyer" msgstr "Vervielfacher" #: f.enhance.cc:1893 msgid "brightness rescale" msgstr "Helligkeit skalieren" #: f.enhance.cc:1896 msgid "click bright point" msgstr "Weißpunkt anklicken" #: f.enhance.cc:1897 msgid "click dark point" msgstr "Schwarzpunkt anklicken" #: f.enhance.cc:1900 f.enhance.cc:2531 msgid "blend" msgstr "einblenden" #: f.enhance.cc:1903 f.enhance.cc:2537 msgid "reduce bright" msgstr "Hell reduzieren" #: f.enhance.cc:2523 msgid "Zonal Retinex" msgstr "Retinex zonal" #: f.enhance.cc:2527 msgid "zone count:" msgstr "Zonenanzahl" #: f.enhance.cc:2534 msgid "reduce dark" msgstr "Dunkel verringern" #: f.enhance.cc:3058 f.process.cc:189 f.process.cc:823 f.process.cc:1404 #: f.widgets.cc:524 msgid "Sharpen" msgstr "Schärfen" #: f.enhance.cc:3066 msgid "unsharp mask" msgstr "Maske unscharf" #: f.enhance.cc:3099 msgid "median diff" msgstr "mittlere Differenz" #: f.enhance.cc:3101 msgid "dark" msgstr "dunkel" #: f.enhance.cc:3102 msgid "light" msgstr "hell" #: f.enhance.cc:3888 msgid "Click to set center" msgstr "Anklicken, um Mitte zu setzen" #: f.enhance.cc:3889 msgid "Pull image using the mouse" msgstr "Bild mit der Maus ziehen" #: f.enhance.cc:3890 msgid "" "left drag: blend image \n" "right drag: restore image" msgstr "" "links-ziehen: Pixel mischen \n" "rechts-ziehen: zurücksetzen" #: f.enhance.cc:3933 msgid "Normal Blur" msgstr "Normal Verwischung" #: f.enhance.cc:3940 msgid "Radial Blur" msgstr "Radial Verwischung" #: f.enhance.cc:3954 msgid "Directed Blur" msgstr "Gerichtete Verwischung" #: f.enhance.cc:3956 msgid "Blur Span" msgstr "Verwischbreite" #: f.enhance.cc:3959 msgid "Intensity" msgstr "Stärke" #: f.enhance.cc:3964 msgid "Graduated Blur" msgstr "Abgestuft Verwischen" #: f.enhance.cc:3969 msgid "Contrast Limit" msgstr "Kontrast-Grenzwert" #: f.enhance.cc:3974 msgid "Paint Blur" msgstr "Verwischung malen" #: f.enhance.cc:3979 f.mashup.cc:1577 fotoxx.h:1402 msgid "Power" msgstr "Stärke" #: f.enhance.cc:3987 f.enhance.cc:4914 msgid "Blur Background" msgstr "Hintergrund unscharf machen" #: f.enhance.cc:4918 msgid "constant blur" msgstr "gleichmässige Unschärfe" #: f.enhance.cc:4921 msgid "increase blur with distance" msgstr "wachsende Unschärfe mit Abstand" #: f.enhance.cc:4923 msgid "min. blur radius" msgstr "min. Radius für Unschärfe" #: f.enhance.cc:4926 msgid "max. blur radius" msgstr "max. Radius für Unschärfe" #: f.enhance.cc:4957 f.warp.cc:837 f.warp.cc:2277 msgid "no active Select Area" msgstr "kein aktiver Auswahl-Fläche" #: f.enhance.cc:5139 msgid "Apply repeatedly while watching the image." msgstr "Mehrmals anwenden während der Bildbetrachtung." #: f.enhance.cc:5175 msgid "Noise Reduction" msgstr "Rauschverminderung" #: f.enhance.cc:5215 msgid "dark areas" msgstr "Dunkle Flächen" #: f.enhance.cc:5217 msgid "all areas" msgstr "Alle Flächen" #: f.enhance.cc:6195 msgid "Measure Noise" msgstr "Rauschen messen" #: f.enhance.cc:6196 msgid "Move mouse in a monotone image area." msgstr "Maus in einer monotonen Fläche bewegen" #: f.enhance.cc:6511 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.enhance.cc:6530 msgid "Red Eye Reduction" msgstr "Rote-Augen beseitigen" #: f.enhance.cc:6984 msgid "Color Match Images" msgstr "Bilderfarben anpassen" #: f.enhance.cc:7015 msgid "mouse radius for color sample" msgstr "Mausradius für Farbauswahl" #: f.enhance.cc:7017 f.enhance.cc:7022 fotoxx.h:1397 zfuncs.cc:12681 msgid "Open" msgstr "Öffnen" #: f.enhance.cc:7018 msgid "image for source color" msgstr "Bild für Quellfarbe" #: f.enhance.cc:7020 msgid "click on image to get source color" msgstr "Bild anklicken zur Quellfarbe-Bestimmung" #: f.enhance.cc:7023 msgid "image to set matching color" msgstr "Bild um Abgleichfarbe zu setzen" #: f.enhance.cc:7025 msgid "click on image to set matching color" msgstr "Bild anklicken um Abgleichfarbe zu setzen" #: f.enhance.cc:7092 msgid "select source image color first" msgstr "Zuerst Farbe des Quellbildes wählen" #: f.enhance.cc:7287 msgid "" "Drag mouse to select. Erase. Repeat. \n" "Click: extend selection to mouse." msgstr "" "Zur Auswahl Maus ziehen. Löschen. Wiederholen. \n" "Anklicken: Auswahl zur Maus erweitern." #: f.enhance.cc:7314 f.widgets.cc:529 msgid "Smart Erase" msgstr "Smart löschen" #: f.enhance.cc:7319 fotoxx.h:1408 msgid "Radius" msgstr "Radius" #: f.enhance.cc:7321 f.widgets.cc:525 msgid "Blur" msgstr "Verwischen" #: f.enhance.cc:7324 msgid "New Area" msgstr "Neuer Fläche" #: f.enhance.cc:7672 f.enhance.cc:8054 msgid "Chromatic Aberration" msgstr "Chromatische Abweichung" #: f.enhance.cc:7711 msgid "Red Factors" msgstr "Rote Faktoren" #: f.enhance.cc:7715 msgid "Blue Factors" msgstr "Blaue Faktoren" #: f.enhance.cc:7720 msgid "Find optimum factors:" msgstr "Optimale Faktoren finden:" #: f.enhance.cc:8095 msgid "Chromatic Color" msgstr "Chromatische Farbe" #: f.enhance.cc:8098 msgid "Replacement Color" msgstr "Ersatzfarbe" #: f.enhance.cc:8101 msgid "Background Color" msgstr "Hintergrundfarbe" #: f.enhance.cc:8105 msgid "Color match level" msgstr "Farbanpassungsebene" #: f.enhance.cc:8109 msgid "Background Proximity" msgstr "Hintergrund Nahbereich" #: f.enhance.cc:8151 msgid "255 iterations, cannot continue" msgstr "255 Iterationen, mehr geht nicht" #: f.enhance.cc:8413 f.widgets.cc:532 msgid "Vignette" msgstr "Vignette" #: f.enhance.cc:8802 f.widgets.cc:533 msgid "Remove Dust" msgstr "Staub entfernen" #: f.enhance.cc:8806 msgid "spot size limit" msgstr "Grenzwert Fleckgröße" #: f.enhance.cc:8809 msgid "max. brightness" msgstr "max. Helligkeit" #: f.enhance.cc:8812 msgid "min. contrast" msgstr "Min. Kontrast" #: f.file.cc:601 msgid "Rename Image File" msgstr "Bilddatei umbenennen" #: f.file.cc:608 msgid "Old Name" msgstr "Vorheriger Name" #: f.file.cc:609 f.process.cc:141 msgid "New Name" msgstr "Neuer Name" #: f.file.cc:618 msgid "previous name" msgstr "Vorheriger Name" #: f.file.cc:619 msgid "Add 1" msgstr "1 addieren" #: f.file.cc:622 f.file.cc:882 f.file.cc:1629 f.file.cc:1998 msgid "keep this dialog open" msgstr "Dialog offen halten" #: f.file.cc:850 msgid "File Permissions" msgstr "Dateiberechtigungen" #: f.file.cc:854 f.meta.cc:833 f.meta.cc:1615 f.meta.cc:1818 msgid "File:" msgstr "Datei:" #: f.file.cc:1000 msgid "Open Image File" msgstr "Bilddatei öffnen" #: f.file.cc:1019 f.process.cc:654 msgid "unknown file type" msgstr "Dateityp unbekannt" #: f.file.cc:1207 #, c-format msgid "Start of gallery, preceding gallery: %s" msgstr "Neubeginn Galerie, vorherige: %s" #: f.file.cc:1208 #, c-format msgid "End of gallery, following gallery: %s" msgstr "Ende Galerie, nächste: %s" #: f.file.cc:1355 msgid "Create Blank Image" msgstr "Leeres Bild erstellen" #: f.file.cc:1357 f.meta.cc:1909 msgid "file name" msgstr "Dateiname" #: f.file.cc:1391 msgid "supply a file name" msgstr "Dateinamen eingeben" #: f.file.cc:1570 msgid "Copy or Move Image File" msgstr "Bilddatei kopieren oder versetzen" #: f.file.cc:1615 msgid "New Location:" msgstr "Neue Speicherstelle" #: f.file.cc:1620 msgid "New Name:" msgstr "Neuer Name:" #: f.file.cc:1625 msgid "copy (duplicate file)" msgstr "kopieren (Datei duplizieren)" #: f.file.cc:1626 msgid "move (remove original)" msgstr "verschieben (Original entfernen)" #: f.file.cc:1676 f.process.cc:608 f.process.cc:1574 f.process.cc:2155 msgid "Select folder" msgstr "Bildverzeichnis wählen" #: f.file.cc:1718 f.tools.cc:1488 msgid "new location is not a folder" msgstr "Neue Speicherstelle muß ein Verzeichnis sein" #: f.file.cc:1737 msgid "new file extension missing or changed" msgstr "Neue Dateierweiterung fehlt oder geändert" #: f.file.cc:1771 f.file.cc:2075 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "Löschen fehlgeschlagen: \n" " %s" #: f.file.cc:1845 f.file.cc:3226 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Datei überschreiben? \n" " %s" #: f.file.cc:1957 msgid "Delete/Trash Image File" msgstr "Bilddatei löschen oder in Papierkorb" #: f.file.cc:2029 msgid "GTK g_file_trash() function failed" msgstr "GTK g_file_trash() Funktion mit Fehler" #: f.file.cc:2048 msgid "not a known image file" msgstr "keine bekannte Bilddatei" #: f.file.cc:2054 msgid "Delete read-only file?" msgstr "Schreibgeschützte Datei löschen?" #: f.file.cc:2056 msgid "Trash read-only file?" msgstr "Schreibgeschützte Datei in Papierkorb?" #: f.file.cc:2272 #, c-format msgid "Kill active dialog? %s" msgstr "Aktiven Dialog abbrechen? %s" #: f.file.cc:2322 f.widgets.cc:633 msgid "User Guide" msgstr "Benutzeranleitung" #: f.file.cc:2325 f.widgets.cc:634 msgid "Recent Changes" msgstr "Neueste Änderungen" #: f.file.cc:2328 f.widgets.cc:635 msgid "Edit Functions Overview" msgstr "Übersicht der Bearbeitungsfunktionen" #: f.file.cc:2334 f.widgets.cc:636 msgid "Change Log" msgstr "Änderungslog" #: f.file.cc:2337 f.widgets.cc:637 msgid "Log File" msgstr "Fehlerlogdatei" #: f.file.cc:2343 f.widgets.cc:639 msgid "Command Params" msgstr "Befehlsparameter" #: f.file.cc:2346 f.widgets.cc:640 msgid "Translations" msgstr "Übersetzungen" #: f.file.cc:2349 f.widgets.cc:641 msgid "Home Page" msgstr "Homepage" #: f.file.cc:2352 f.widgets.cc:642 msgid "License" msgstr "Lizenz" #: f.file.cc:2355 f.widgets.cc:643 msgid "Privacy" msgstr "Datenschutz" #: f.file.cc:2358 f.widgets.cc:644 msgid "About" msgstr "Über Fotoxx" #: f.file.cc:2366 f.widgets.cc:665 fotoxx.h:1366 msgid "Help" msgstr "Hilfe" #: f.file.cc:2414 msgid "Save Image File" msgstr "Bilddatei speichern" #: f.file.cc:2424 msgid "new version" msgstr "Neue Version" #: f.file.cc:2425 msgid "new file ..." msgstr "Neue Datei ..." #: f.file.cc:2426 msgid "replace file" msgstr "Datei ersetzen" #: f.file.cc:2433 f.file.cc:3083 msgid "save as new file name or type" msgstr "Als neuen Dateinamen oder Typ speichern" #: f.file.cc:2435 msgid "replace old file (OVERWRITE)" msgstr "Vorherige Datei ersetzen (ÜBERSCHREIBEN)" #: f.file.cc:2486 msgid "cannot replace RAW file" msgstr "kann RAW-Datei nicht ersetzen" #: f.file.cc:2491 msgid "cannot replace HEIC file" msgstr "kann HEIC-Datei nicht ersetzen" #: f.file.cc:2496 msgid "cannot replace JP2 file" msgstr "" #: f.file.cc:2668 f.file.cc:2727 #, c-format msgid "" "file: %s \n" " exceed 99 versions" msgstr "" "Datei: %s \n" "mehr als 99 Versionen" #: f.file.cc:2821 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:2831 msgid "cannot save as RAW type" msgstr "Kann nicht als RAW-Datei speichern" #: f.file.cc:2912 msgid "save anyway" msgstr "trotzdem speichern" #: f.file.cc:2980 f.file.cc:2982 msgid "Unable to copy EXIF/IPTC data" msgstr "Kopie EXIF/IPTC Daten unmöglich" #: f.file.cc:3096 f.process.cc:1387 msgid "jpg quality" msgstr "jpg Qualitätsstufe" #: f.file.cc:3100 msgid "color depth:" msgstr "Farbtiefe:" #: f.file.cc:3106 f.file.cc:3114 msgid "make current" msgstr "aktuell machen" #: f.file.cc:3107 msgid "(new file becomes current file)" msgstr "(neue Datei wird aktuelle Datei)" #: f.file.cc:3110 msgid "permissions:" msgstr "Berechtigungen:" #: f.file.cc:3641 f.widgets.cc:432 f.widgets.cc:821 msgid "Permissions" msgstr "Berechtigungen" #: f.file.cc:3646 fotoxx.cc:254 msgid "owner" msgstr "Besitzer" #: f.file.cc:3647 fotoxx.cc:255 msgid "group" msgstr "Gruppe" #: f.file.cc:3648 fotoxx.cc:256 msgid "other" msgstr "Andere" #: f.gallery.cc:1699 f.gallery.cc:1719 msgid "recent images" msgstr "kürzlich angesehene Bilder" #: f.gallery.cc:1700 f.gallery.cc:1724 msgid "newest images" msgstr "neueste Bilder" #: f.gallery.cc:1779 msgid "no albums found" msgstr "Keine Alben gefunden" #: f.gallery.cc:1789 f.gallery.cc:1803 msgid "Current Album" msgstr "Momentanes Album" #: f.gallery.cc:1806 msgid "no current album" msgstr "momentan kein Album" #: f.gallery.cc:1828 msgid "" " Reset all galleries\n" " to file name ascending" msgstr "" " Alle Galerien zurücksetzen,\n" " Dateiname aufsteigend sortiert" #: f.gallery.cc:1849 msgid "Gallery Sort" msgstr "Galerie-Sortierung" #: f.gallery.cc:1853 msgid "File Name" msgstr "Dateinamen" #: f.gallery.cc:1854 msgid "File Mod Date/Time" msgstr "Dateiänderung Datum/Zeit" #: f.gallery.cc:1855 msgid "Photo Date/Time (EXIF)" msgstr "Aufnahme Datum/Zeit (EXIF)" #: f.gallery.cc:1856 msgid "File Size (bytes)" msgstr "Dateigröße (Bytes)" #: f.gallery.cc:1857 msgid "Image Size (pixels)" msgstr "Bildgröße (Pixel)" #: f.gallery.cc:1859 msgid "ascending" msgstr "aufsteigend" #: f.gallery.cc:1860 msgid "descending" msgstr "absteigend" #: f.gallery.cc:2759 msgid "" "Use EXIF photo date or \n" " file modification date?" msgstr "" "EXIF Aufnahmedatum oder Datei- \n" " Änderungsdatum benutzen?" #: f.gallery.cc:2783 f.widgets.cc:648 msgid "File" msgstr "Datei" #: f.gallery.cc:3589 f.gallery.cc:3593 msgid "Image File" msgstr "Bilddatei" #: f.gallery.cc:3702 msgid "Select Image Files" msgstr "Bilddateien wählen" #: f.gallery.cc:3768 #, c-format msgid "exceed %d selected files" msgstr "mehr als %d gewählte Dateien" #: f.gallery.cc:3780 #, c-format msgid "remove %d duplicates?" msgstr "%d Duplikate entfernen?" #: f.gallery.cc:4229 f.widgets.cc:457 msgid "Bookmarks" msgstr "Lesezeichen" #: f.gallery.cc:4229 f.gallery.cc:4337 msgid "Edit Bookmarks" msgstr "Lesezeichen bearbeiten" #: f.gallery.cc:4311 msgid "" "Click a list position. Click a gallery thumbnail for the new bookmark.\n" "Bookmark for thumbnail will be added. Change the name and press [Rename]." msgstr "" "Position in der Liste anklicken. Thumbnail der Galerie als neues Lesezeichen " "anklicken.\n" "Lesezeichen für Thumbnail wird zugefügt. Name ändern und [umbenennen] " "drücken." #: f.gallery.cc:4514 msgid "unable to save bookmarks file" msgstr "Speichern Lesezeichen-Datei nicht möglich" #: f.mashup.cc:212 msgid "Project name" msgstr "Projektname" #: f.mashup.cc:216 msgid "Layout and background image" msgstr "Layout und Hintergrundbild" #: f.mashup.cc:220 msgid "choose an image file" msgstr "Bilddatei wählen" #: f.mashup.cc:221 msgid "use current image file" msgstr "Aktuelle Bilddatei verwenden" #: f.mashup.cc:222 msgid "specify layout size and color" msgstr "Lageplan Größe und Farbe angeben" #: f.mashup.cc:223 msgid "open a Mashup project file" msgstr "Mashup Projektdatei öffnen" #: f.mashup.cc:238 msgid "enter a project name" msgstr "Projektnamen eingeben" #: f.mashup.cc:269 msgid "no current file" msgstr "Keine aktuelle Bilddatei" #: f.mashup.cc:296 msgid "Make Layout Image" msgstr "Lageplan-Bild erstellen" #: f.mashup.cc:403 msgid "Edit Images" msgstr "Bilder bearbeiten" #: f.mashup.cc:404 f.mashup.cc:2433 msgid "Edit Text" msgstr "Texte bearbeiten" #: f.mashup.cc:405 msgid "Edit Line" msgstr "Linie bearbeiten" #: f.mashup.cc:406 msgid "Rescale" msgstr "Neu skalieren" #: f.mashup.cc:411 msgid "add or edit images" msgstr "Bilder zufügen oder bearbeiten" #: f.mashup.cc:413 msgid "add or edit text" msgstr "Texte zufügen oder bearbeiten" #: f.mashup.cc:415 msgid "add or edit lines/arrows" msgstr "Linien/Pfeile zufügen oder bearbeiten" #: f.mashup.cc:417 msgid "change project scale" msgstr "Projekt-Maßstab ändern" #: f.mashup.cc:419 msgid "project complete" msgstr "Projekt fertig" #: f.mashup.cc:421 msgid "cancel project" msgstr "Projekt verwerfen" #: f.mashup.cc:439 msgid "rescale project" msgstr "Projekt neu skalieren" #: f.mashup.cc:493 msgid "save Mashup output file" msgstr "Ausgabedatei speichern" #: f.mashup.cc:513 msgid "save Mashup project file?" msgstr "Mashup-Projektdatei speichern?" #: f.mashup.cc:528 msgid "delete Mashup project file?" msgstr "Mashup-Projektdatei löschen?" #: f.mashup.cc:588 msgid "Open Project" msgstr "Projekt öffnen" #: f.mashup.cc:617 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "Lageplan-Bilddatei fehlt: \n" " %s" #: f.mashup.cc:674 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "Overlay-Bilddatei fehlt: \n" " %s" #: f.mashup.cc:980 msgid "project file is defective" msgstr "Projektdatei ist defekt" #: f.mashup.cc:1010 msgid "Save Project" msgstr "Projekt speichern" #: f.mashup.cc:1151 msgid "layout exceeds 2 gigabytes" msgstr "Lageplan überschreitet 2 Gigabytes" #: f.mashup.cc:1221 msgid "Click image to select, drag image to move." msgstr "Bild anklicken zum Wählen, ziehen zum Versetzen." #: f.mashup.cc:1222 msgid "Make black margins transparent" msgstr "Schwarze Ränder transparent machen" #: f.mashup.cc:1254 msgid "Add Image" msgstr "Bild hinzufügen" #: f.mashup.cc:1261 msgid "Current image:" msgstr "Aktuelles Bild" #: f.mashup.cc:1265 msgid "Cycle through images:" msgstr "Aktuelle Bilder durchsuchen" #: f.mashup.cc:1272 msgid "Scale" msgstr "Maßstab" #: f.mashup.cc:1281 msgid "Stacking Order" msgstr "Stapelfolge" #: f.mashup.cc:1282 msgid "Raise" msgstr "Anheben" #: f.mashup.cc:1283 msgid "Lower" msgstr "Absenken" #: f.mashup.cc:1286 msgid "Base Transparency" msgstr "Grund-Transparenz" #: f.mashup.cc:1290 msgid "Var. Transparency" msgstr "Var. Transparenz" #: f.mashup.cc:1291 msgid "Paint" msgstr "Malen" #: f.mashup.cc:1294 msgid "Bend and fine-align" msgstr "Biegen und fein einstellen" #: f.mashup.cc:1295 f.widgets.cc:661 msgid "Warp" msgstr "Verformen" #: f.mashup.cc:1304 zfuncs.cc:12896 zfuncs.cc:12905 msgid "Margins" msgstr "Ränder" #: f.mashup.cc:1305 msgid "Hard" msgstr "Fest" #: f.mashup.cc:1306 msgid "Blend" msgstr "Beimischen" #: f.mashup.cc:1320 msgid "add images to layout" msgstr "Bilder in Lageplan einfügen" #: f.mashup.cc:1557 msgid "Paint Image Transparencies" msgstr "Bild-Transparenzen malen" #: f.mashup.cc:1575 msgid "Gradual" msgstr "Abgestuft" #: f.mashup.cc:1810 msgid "Pull on the image with the mouse." msgstr "Bildstelle mit der Maus ziehen" #: f.mashup.cc:1826 msgid "Warp Image" msgstr "Bild verformen" #: f.mashup.cc:1832 f.warp.cc:1209 msgid "warp span" msgstr "Verformen-Spannweite" #: f.mashup.cc:2264 #, c-format msgid "exceeded %d images" msgstr "%d Bilder überschritten" #: f.mashup.cc:2406 msgid "Enter text, [Add] to layout, edit properties." msgstr "Text eingeben, ins Layout [Einfügen], Attribute bearbeiten." #: f.mashup.cc:2486 msgid "Text File:" msgstr "Textdatei:" #: f.mashup.cc:2490 msgid "add entered text to layout" msgstr "Eingegebenen Text im Lageplan einfügen" #: f.mashup.cc:2624 msgid "click position to add text" msgstr "Position für Text einfügen anklicken" #: f.mashup.cc:2630 #, c-format msgid "exceeded %d text entries" msgstr "mehr als %d Texteingaben" #: f.mashup.cc:2635 msgid "Add Text" msgstr "Text zufügen" #: f.mashup.cc:2744 msgid "Set line properties, [Add] to layout, edit." msgstr "Attribute für Linie setzen, ins Layout [Einfügen], bearbeiten." #: f.mashup.cc:2768 msgid "Edit Line/Arrow" msgstr "Linie/Pfeil bearbeiten" #: f.mashup.cc:2823 msgid "add line/arrow to layout" msgstr "Linie/Pfeil im Lageplan einfügen" #: f.mashup.cc:2915 msgid "click position to add line" msgstr "Position für Linie einfügen anklicken" #: f.mashup.cc:2921 #, c-format msgid "exceeded %d line entries" msgstr "mehr als %d Linien" #: f.mashup.cc:2926 msgid "Add Line" msgstr "Linie zufügen" #: f.mashup.cc:4171 msgid "Image Montage" msgstr "Bildmontage" #: f.mashup.cc:4180 msgid "Frame Width" msgstr "Rahmenbreite" #: f.mashup.cc:4183 f.mashup.cc:4191 msgid "Margin" msgstr "Rand" #: f.mashup.cc:4188 msgid "Image Columns" msgstr "Bild-Zeilen" #: f.mashup.cc:4205 #, c-format msgid "exceed %d rows" msgstr "mehr als %d Reihen" #: f.mashup.cc:4301 msgid "Optimize" msgstr "Optimieren" #: f.mashup.cc:4309 #, c-format msgid "column difference: %d pixels" msgstr "Unterschied in Reihe: %d Pixel" #: f.mashup.cc:4367 #, c-format msgid "" "column difference: %d pixels \n" "Make columns even?" msgstr "" "Unterschied der Reihe: %d pixels \n" "Reihe begradigen?" #: f.mashup.cc:4389 msgid "Save with unique montage name" msgstr "mit einmaligem Montage-Name speichern" #: f.mashup.cc:4391 msgid "unique name:" msgstr "einmaliger Name:" #: f.mashup.cc:4393 msgid "create image map" msgstr "Bilderverzeichnis erstellen" #: f.mashup.cc:4410 f.meta.cc:9151 msgid "supply a reasonable name" msgstr "plausiblen Name eingeben" #: f.mashup.cc:4421 msgid "save montage" msgstr "Bildmontage speichern" #: f.mashup.cc:4441 #, c-format msgid "map file saved: %s" msgstr "Bilderverzeichnis gespeichert: %s" #: f.mashup.cc:4486 #, c-format msgid "%d max images exceeded" msgstr "mehr als %d Bilder" #: f.mashup.cc:4560 f.mashup.cc:4566 #, c-format msgid "image frame is too large: %d x %d" msgstr "Bild-Rahmen ist zu groß: %d x %d" #: f.mashup.cc:4871 #, c-format msgid "montage map file invalid: %s" msgstr "Montageverzeichnis Datei ungültig: %s" #: f.meta.cc:245 msgid "Add Metadata Items" msgstr "Metadata Elemente zufügen" #: f.meta.cc:249 f.meta.cc:1609 f.meta.cc:3190 msgid "click to select" msgstr "zur Auswahl anklicken" #: f.meta.cc:254 msgid "click to unselect" msgstr "zum Abwählen anklicken" #: f.meta.cc:476 msgid "Extras" msgstr "Extras" #: f.meta.cc:476 msgid "View Metadata" msgstr "Metadaten ansehen" #: f.meta.cc:661 msgid "View All Metadata" msgstr "Alle Metadaten zeigen" #: f.meta.cc:826 msgid "Edit Metadata" msgstr "Metadaten bearbeiten" #: f.meta.cc:829 msgid "save metadata to file" msgstr "Metadaten in Datei speichern" #: f.meta.cc:838 msgid "Image Date" msgstr "Bild Datum" #: f.meta.cc:841 msgid "Time" msgstr "Zeit" #: f.meta.cc:849 msgid "Rating (stars):" msgstr "Bewertung (Sterne):" #: f.meta.cc:861 msgid "Caption" msgstr "Titel" #: f.meta.cc:866 msgid "Comments" msgstr "Bemerkungen" #: f.meta.cc:873 msgid "Location" msgstr "Ort" #: f.meta.cc:876 msgid "Country" msgstr "Land" #: f.meta.cc:899 msgid "Image Tags" msgstr "Bild Tags" #: f.meta.cc:905 msgid "Recent Tags" msgstr "Jüngste Tags" #: f.meta.cc:911 f.meta.cc:2081 msgid "Enter New Tag" msgstr "Neues Tag eingeben" #: f.meta.cc:917 f.meta.cc:2087 f.meta.cc:4750 msgid "Matching Tags" msgstr "Passende Tags" #: f.meta.cc:925 f.meta.cc:2096 f.meta.cc:2556 f.meta.cc:4757 msgid "Defined Tags Category" msgstr "Definierte Tags Gruppe" #: f.meta.cc:933 msgid "search known locations" msgstr "bekannte Orte durchsuchen" #: f.meta.cc:934 msgid "search using web service" msgstr "mit Webdienst suchen" #: f.meta.cc:1384 f.meta.cc:3800 f.meta.cc:7709 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "falsche Breite/Länge: %s %s" #: f.meta.cc:1441 f.widgets.cc:477 msgid "Manage Tags" msgstr "Tags verwalten" #: f.meta.cc:1441 msgid "orphan tags" msgstr "verwaiste Tags" #: f.meta.cc:1445 msgid "category" msgstr "Gruppe" #: f.meta.cc:1448 msgid "tag" msgstr "Tag" #: f.meta.cc:1455 msgid "Defined Tags:" msgstr "Definierte Tags:" #: f.meta.cc:1605 msgid "Edit Any Metadata" msgstr "Beliebige Metadaten bearbeiten" #: f.meta.cc:1605 f.meta.cc:3185 msgid "Full List" msgstr "Vollliste" #: f.meta.cc:1619 f.meta.cc:3202 msgid "key name" msgstr "Name Schlüssel" #: f.meta.cc:1622 f.meta.cc:3203 msgid "key value" msgstr "Wert Schlüssel" #: f.meta.cc:1815 msgid "Delete Metadata" msgstr "Metadaten löschen" #: f.meta.cc:1821 fotoxx.h:1317 msgid "All" msgstr "Alles" #: f.meta.cc:1822 msgid "One Key:" msgstr "Ein Schlüssel:" #: f.meta.cc:1908 msgid "choose options" msgstr "Optionen wählen" #: f.meta.cc:1910 msgid "caption" msgstr "Untertitel" #: f.meta.cc:1911 msgid "comment" msgstr "Kommentar" #: f.meta.cc:2057 msgid "Batch Add/Remove Tags" msgstr "Tags zuweisen/entfernen Stapelbetrieb" #: f.meta.cc:2070 msgid "tags to add" msgstr "Tags zum Zuweisen" #: f.meta.cc:2071 msgid "tags to remove" msgstr "Tags zum Entfernen" #: f.meta.cc:2176 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " zu viele Tags" #: f.meta.cc:2191 msgid "repeat with same files?" msgstr "mit denselben Dateien wiederholen?" #: f.meta.cc:2331 msgid "specify files and tags" msgstr "Tags und Dateien angeben" #: f.meta.cc:2535 f.widgets.cc:596 msgid "Batch Rename Tags" msgstr "Tags umbenennen Stapelbetrieb" #: f.meta.cc:2545 msgid "(click defined tag)" msgstr "(definiertes Tag anklicken)" #: f.meta.cc:2547 f.process.cc:815 msgid "Rename to" msgstr "Umbenennen zu" #: f.meta.cc:2564 msgid "old tag name >> new tag name" msgstr "alter Tag-Name >> neuer Tag-Name" #: f.meta.cc:2620 #, 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:2774 msgid "max tags exceeded" msgstr "max Tags überschritten" #: f.meta.cc:2843 f.meta.cc:2978 msgid "Batch Photo Date/Time" msgstr "Foto Datum/Zeit Stapelbetrieb" #: f.meta.cc:2851 msgid "set a new date/time:" msgstr "Datum/Zeit neu setzen" #: f.meta.cc:2859 msgid "shift existing date/time:" msgstr "bestehende(s) Datum/Zeit verschieben" #: f.meta.cc:2885 msgid "test: show changes, do not update files" msgstr "Versuch: neue Werte zeigen, Dateien nicht ändern" #: f.meta.cc:2910 msgid "please make a choice" msgstr "bitte auswählen" #: f.meta.cc:2915 f.meta.cc:3276 f.meta.cc:3467 msgid "no files selected" msgstr "Keine Dateien ausgewählt" #: f.meta.cc:2945 f.meta.cc:2955 f.meta.cc:2961 msgid "invalid date/time format" msgstr "Datum/Zeit Formatfehler" #: f.meta.cc:3185 msgid "Batch Add/Change Metadata" msgstr "Metadaten einfügen/ändern Stapelbetrieb" #: f.meta.cc:3270 msgid "enter key names" msgstr "Schlüssel-Namen eingeben" #: f.meta.cc:3353 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:3450 msgid "Batch Report Metadata" msgstr "Metadaten auflisten Stapelbetrieb" #: f.meta.cc:3456 msgid "list of reported metadata items" msgstr "Liste der gemeldeten Metadaten-Einträge" #: f.meta.cc:3599 f.widgets.cc:600 msgid "Batch Geotags" msgstr "Geotags Stapelbetrieb" #: f.meta.cc:3640 msgid "location" msgstr "Ort" #: f.meta.cc:3643 msgid "country" msgstr "Land" #: f.meta.cc:3676 msgid "Adding Geotags" msgstr "Geotags werden zugefügt" #: f.meta.cc:3785 msgid "" "data is incomplete \n" " proceed?" msgstr "" "Daten nicht vollständig \n" " Fortfahren?" #: f.meta.cc:3873 msgid "Report Image Locations" msgstr "Bilder-Orte anzeigen" #: f.meta.cc:3874 msgid "Group by country" msgstr "Nach Land gruppieren" #: f.meta.cc:3875 msgid "Group by country/location" msgstr "Nach Land/Ort gruppieren" #: f.meta.cc:3876 msgid "Group by country/location/date" msgstr "Nach Land/Ort/Datum gruppieren" #: f.meta.cc:3877 msgid "Group by date/country/location" msgstr "Nach Datum/Land/Ort gruppieren" #: f.meta.cc:3880 msgid "Combine within" msgstr "Kombinieren innerhalb" #: f.meta.cc:3882 msgid "days" msgstr "Tage" #: f.meta.cc:3994 msgid "Image Locations" msgstr "Bild Orte" #: f.meta.cc:4263 msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" msgstr "Jan Feb Mär Apr Mai Jun Jul Aug Sep Okt Nov Dez" #: f.meta.cc:4655 msgid "Search Images" msgstr "Bilddatei durchsuchen" #: f.meta.cc:4659 msgid "images to search:" msgstr "Bilder durchsuchen:" #: f.meta.cc:4660 msgid "all" msgstr "Alle" #: f.meta.cc:4661 msgid "current set only" msgstr "nur aktueller Satz" #: f.meta.cc:4664 msgid "matching images:" msgstr "passende Bilder:" #: f.meta.cc:4665 msgid "make new set" msgstr "Neuen Satz erstellen" #: f.meta.cc:4666 msgid "add to set" msgstr "dem Satz zufügen" #: f.meta.cc:4667 msgid "remove" msgstr "entfernen" #: f.meta.cc:4672 msgid "select last version only" msgstr "Nur letzte Version auswählen" #: f.meta.cc:4673 msgid "original + last version" msgstr "original + letzte version" #: f.meta.cc:4675 msgid "original + all versions" msgstr "original + alle Versionen" #: f.meta.cc:4676 f.process.cc:166 f.process.cc:172 f.process.cc:180 #: f.process.cc:2058 msgid "no change" msgstr "Keine Änderung" #: f.meta.cc:4681 msgid "report type:" msgstr "Anzeigetyp:" #: f.meta.cc:4682 msgid "gallery" msgstr "Galerie" #: f.meta.cc:4688 msgid "date range" msgstr "Datumsbereich" #: f.meta.cc:4693 msgid "photo date" msgstr "Aufnahmedatum" #: f.meta.cc:4694 msgid "file date" msgstr "Datei-Datum" #: f.meta.cc:4695 msgid "(yyyy-mm-dd)" msgstr "(jjjj-mm-tt)" #: f.meta.cc:4700 msgid "rating range" msgstr "Bewertungsbereich" #: f.meta.cc:4708 msgid "all/any" msgstr "alle/irgendeines" #: f.meta.cc:4711 msgid "search tags" msgstr "Such-Tags" #: f.meta.cc:4718 msgid "search text" msgstr "Text durchsuchen" #: f.meta.cc:4724 msgid "search files" msgstr "Dateien suchen" #: f.meta.cc:4730 msgid "search locations" msgstr "Ort suchen" #: f.meta.cc:4734 msgid "enter cities, countries" msgstr "Städte, Länder eingeben" #: f.meta.cc:4739 msgid "search other metadata" msgstr "nach anderen Metadaten suchen" #: f.meta.cc:4746 msgid "Enter Search Tag" msgstr "Such-Tag eingeben" #: f.meta.cc:5060 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:5067 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:5135 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "Suchdaten nicht plausibel \n" " %s %s" #: f.meta.cc:5160 msgid "stars range not reasonable" msgstr "Stern-Spanne nicht plausibel" #: f.meta.cc:5718 msgid "Always reported: date, stars, tags, caption, comment" msgstr "Immer angezeigt: Datum, Bewertung, Tags, Titel, Bemerkung" #: f.meta.cc:5750 msgid "Additional Items for Report" msgstr "Zusätzliche Metadaten im Bericht" #: f.meta.cc:5757 msgid "Keyword" msgstr "Schlüsselwort" #: f.meta.cc:5771 msgid "Match Criteria" msgstr "Trefferkriterien" #: f.meta.cc:5904 #, c-format msgid "bad number: %s" msgstr "Irrwert: %s" #: f.meta.cc:6184 msgid "date format is YYYY-MM-DD" msgstr "Datumformat ist JJJJ-MM-TT" #: f.meta.cc:6188 msgid "date is invalid" msgstr "Datum ist ungültig" #: f.meta.cc:6226 msgid "time format is HH:MM [:SS]" msgstr "Zeitformat ist HH:MM [:SS]" #: f.meta.cc:6230 msgid "time is invalid" msgstr "Zeitangabe ungültig" #: f.meta.cc:7328 msgid "not found" msgstr "Nicht gefunden" #: f.meta.cc:7329 msgid "location and country required" msgstr "Ort und Land erforderlich" #: f.meta.cc:7586 msgid "choose location" msgstr "Ort wählen" #: f.meta.cc:7886 msgid "Set Map Markers" msgstr "Karten-Markierungen wählen" #: f.meta.cc:7887 msgid "mark all image files" msgstr "alle Bilddateien markieren" #: f.meta.cc:7888 msgid "mark current gallery" msgstr "aktuelle Galerie-Bilder markieren" #: f.meta.cc:7989 msgid "choose map file" msgstr "Kartendatei wählen" #: f.meta.cc:8133 msgid "" "fotoxx-maps package not installed \n" "(see https://kornelix.net)" msgstr "" "fotoxx-maps Paket nicht installiert \n" "(sehe https://kornelix.net" #: f.meta.cc:8138 #, c-format msgid "map file %s is missing" msgstr "Kartendatei %s fehlt" #: f.meta.cc:8142 #, c-format msgid "" "map latitude/longitude data unreasonable \n" " %.3f %.3f %.3f %.3f" msgstr "Karte Breiten-/Längengrad nicht stimmig %.3f %.3f %.3f %.3f" #: f.meta.cc:8468 f.meta.cc:9020 msgid "No matching images found" msgstr "Keine übereinstimmenden Bilder gefunden" #: f.meta.cc:8562 msgid "Net Map Source" msgstr "Herkunft Netz-Karte" #: f.meta.cc:9098 msgid "Net Map Locations" msgstr "Orte (Karte vom Internet)" #: f.meta.cc:9104 msgid "map location:" msgstr "Kartenort:" #: f.pixmap.cc:3227 f.pixmap.cc:3279 msgid "HEIF files not supported" msgstr "HEIF-Dateien nicht unterstützt" #: f.pixmap.cc:3351 f.pixmap.cc:3402 msgid "JP2 files not supported" msgstr "" #: f.process.cc:134 f.widgets.cc:588 msgid "Batch Convert" msgstr "konvertieren Stapelbetrieb" #: f.process.cc:145 msgid "Sequence Numbers" msgstr "Sequenznummern" #: f.process.cc:147 msgid "base" msgstr "Basis" #: f.process.cc:150 msgid "adder" msgstr "Zuwachs" #: f.process.cc:155 msgid "New Location" msgstr "Neuer Ort" #: f.process.cc:160 msgid "New File Type" msgstr "Neuer Dateityp" #: f.process.cc:169 f.process.cc:1390 msgid "Color Depth:" msgstr "Farbtiefe:" #: f.process.cc:175 f.process.cc:2053 msgid "max. Width" msgstr "Max. Breite" #: f.process.cc:184 msgid "Delete Originals" msgstr "Originale löschen" #: f.process.cc:185 f.process.cc:821 msgid "Copy Metadata" msgstr "Metadaten kopieren" #: f.process.cc:199 f.process.cc:824 msgid "Overlay Image" msgstr "Overlay Bild" #: f.process.cc:202 f.warp.cc:4547 msgid "Width %" msgstr "Breite %" #: f.process.cc:207 msgid "Position" msgstr "Stelle" #: f.process.cc:219 msgid "Make constant size for screen:" msgstr "Feste Größe für Bildschirm setzen:" #: f.process.cc:227 msgid "" "plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s" msgstr "" "plugins: (Jahr Monat Tag alter-Name Sequenz) $yyyy $mm $dd $oldname $s" #: f.process.cc:371 #, c-format msgid "file type not supported: %s \n" msgstr "Dateityp nicht unterstützt: %s \n" #: f.process.cc:507 f.process.cc:2116 msgid "cannot create new file" msgstr "Kann neue Datei nicht erstellen" #: f.process.cc:554 msgid "updating albums ..." msgstr "Alben werden aktualisiert ..." #: f.process.cc:732 #, c-format msgid "invalid plugin: %s" msgstr "ungültiges Plugin: %s" #: f.process.cc:739 msgid "you must use either $s or $oldname" msgstr "Sie müssen $s oder $oldname benutzen" #: f.process.cc:744 msgid "$s plugin needs base and adder" msgstr "$s Plugin benötigt Basis und Zuwachs" #: f.process.cc:749 msgid "base and adder need $s plugin" msgstr "Basis und Zuwachs benötigen $s Plugin" #: f.process.cc:763 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "Max. Größe %d x %d nicht stimmig" #: f.process.cc:770 msgid "specify overlay image file" msgstr "Overlay Bilddatei angeben" #: f.process.cc:786 msgid "specify overlay position" msgstr "Overlay Stelle angeben" #: f.process.cc:814 #, c-format msgid "Convert %d image files" msgstr "%d Bilddateien konvertieren" #: f.process.cc:816 msgid "Convert to" msgstr "Konvertieren zu" #: f.process.cc:818 msgid "Resize within" msgstr "Skalieren innerhalb" #: f.process.cc:819 msgid "Output to" msgstr "Schreiben nach" #: f.process.cc:825 msgid "*** Delete Originals ***" msgstr "*** Originale löschen ***" #: f.process.cc:826 msgid "*** Replace Originals ***" msgstr "*** Originale ersetzen ***" #: f.process.cc:827 msgid "PROCEED?" msgstr "FORTFAHREN?" #: f.process.cc:983 f.widgets.cc:589 msgid "Batch Upright" msgstr "Aufrichten Stapelbetrieb" #: f.process.cc:989 msgid "Survey all files" msgstr "Alle Dateien durchsehen" #: f.process.cc:1029 msgid "file cannot be read" msgstr "Datei ist unlesbar" #: f.process.cc:1146 msgid "cannot select both options" msgstr "kann nicht beide Optionen auswählen" #: f.process.cc:1187 f.widgets.cc:590 msgid "Batch Delete/Trash" msgstr "Löschen/Papierkorb Stapelbetrieb" #: f.process.cc:1192 msgid "delete" msgstr "löschen" #: f.process.cc:1195 msgid "trash" msgstr "Papierkorb" #: f.process.cc:1258 msgid "Purging deleted files from albums \n" msgstr "Gelöschte Dateien werden aus Alben entfernt \n" #: f.process.cc:1338 msgid "Batch Convert RAW Files" msgstr "RAW konvertieren Stapelbetrieb" #: f.process.cc:1377 msgid "output location" msgstr "Ausgabeort" #: f.process.cc:1382 msgid "File Type" msgstr "Dateityp" #: f.process.cc:1406 msgid "amount" msgstr "Wert" #: f.process.cc:1409 msgid "threshold" msgstr "Schwelle" #: f.process.cc:1413 msgid "Fix dead pixels" msgstr "Tote Pixel beheben" #: f.process.cc:1416 msgid "dead pixel map file" msgstr "Datei toter Pixel" #: f.process.cc:1419 msgid "Fix pixel bias" msgstr "Pixel Bias beheben" #: f.process.cc:1422 msgid "pixel bias map file" msgstr "Datei mit Pixel-Bias" #: f.process.cc:1727 msgid "growisofs not installed" msgstr "growisofs nicht installiert" #: f.process.cc:1774 msgid "no DVD/BlueRay device found" msgstr "kein DVD/BlueRay Gerät gefunden" #: f.process.cc:1797 msgid "Burn Images to DVD/BlueRay" msgstr "Bilder auf DVD/BlueRay brennen" #: f.process.cc:1802 msgid "Select device" msgstr "Gerät wählen" #: f.process.cc:1889 f.widgets.cc:355 msgid "Create a file of selected image files" msgstr "Datei von gewählten Bilddateien erstellen" #: f.process.cc:1915 f.process.cc:1984 msgid "Output File" msgstr "Ausgabe-Datei" #: f.process.cc:1936 msgid "no input files selected" msgstr "keine Eingabe-Dateien gewählt" #: f.process.cc:1942 msgid "no output file selected" msgstr "keine Ausgabe-Datei gewählt" #: f.process.cc:2044 f.widgets.cc:594 msgid "Export Files" msgstr "Dateien exportieren" #: f.process.cc:2049 msgid "To Location" msgstr "Zum Speicherort" #: f.process.cc:2060 msgid "export metadata" msgstr "Metadaten exportieren" #: f.process.cc:2091 msgid "file type not supported" msgstr "Dateityp nicht unterstützt" #: f.process.cc:2173 msgid "location is not a folder" msgstr "Speicherort ist kein Verzeichnis" #: f.process.cc:2225 msgid "Script Files" msgstr "Skriptdateien" #: f.process.cc:2229 msgid "begin making a script file" msgstr "Skriptdatei neu anlegen" #: f.process.cc:2232 msgid "finish making a script file" msgstr "Skriptdatei fertigstellen" #: f.process.cc:2276 msgid "script already started" msgstr "Skript läuft schon" #: f.process.cc:2280 msgid "start a new script file" msgstr "neue Skriptdatei starten" #: f.process.cc:2301 msgid "perform edits to be included in the script file" msgstr "" "Bild-Bearbeitungen durchführen, die in der Skriptdatei zu erfassen sind" #: f.process.cc:2318 msgid "script file error" msgstr "Skriptdatei Fehler" #: f.process.cc:2323 #, c-format msgid "%s added to script" msgstr "%s zum Skript zugefügt" #: f.process.cc:2333 msgid "no script file was started" msgstr "keine Skriptdatei gestartet" #: f.process.cc:2341 msgid "script file closed" msgstr "Skriptdatei geschlossen" #: f.process.cc:2374 f.process.cc:2395 msgid "no script files found" msgstr "Keine Skriptdateien gefunden" #: f.process.cc:2418 #, c-format msgid "" "script error: %s \n" " %s" msgstr "" "Script Fehler: %s \n" " %s" #: f.process.cc:2440 #, c-format msgid "unknown edit function: %s" msgstr "unbekannte Bearbeitungsfunktion: %s" #: f.process.cc:2452 #, c-format msgid "load widgets failed: %s" msgstr "Widgets laden fehlgeschlagen: %s" #: f.process.cc:2468 #, c-format msgid "script file format error: %s" msgstr "Skriptdatei Formatfehler: %s" #: f.process.cc:2494 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "Fehler beim Öffnen: %s \n" " %s" #: f.process.cc:2511 msgid "script complete" msgstr "Skript fertig" #: f.process.cc:2553 f.widgets.cc:603 msgid "Batch Script" msgstr "Script Stapelbetrieb" #: f.process.cc:2560 msgid "Select Script" msgstr "Skriptdatei auswählen" #: f.process.cc:2561 msgid "script file to run" msgstr "Skriptdatei zum Ausführen" #: f.tools.cc:93 msgid "Folders for image files (subfolders included automatically)." msgstr "" "Verzeichnisse für Bilddateien (Unterverzeichnisse automatisch einbeziehen)." #: f.tools.cc:95 msgid "Select to add, click on X to delete." msgstr "Auswählen zum Zufügen, X anklicken zum Löschen." #: f.tools.cc:96 msgid "folder for thumbnails" msgstr "Verzeichnis für Thumbnails" #: f.tools.cc:97 msgid "extra metadata items to include in index" msgstr "in Index einzuschließende extra Metadaten-Elemente" #: f.tools.cc:98 msgid "force a full re-index of all image files" msgstr "Voll Indizieren aller Bilddateien erzwingen" #: f.tools.cc:99 msgid "" "Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Indizieren abgebrochen. \n" "Indizieren ist für Such- und Karten-Funktionen erforderlich, \n" "und damit die Galerieseiten annehmbar schnell angezeigt werden." #: f.tools.cc:136 msgid "Index Image Files" msgstr "Index der Bilder erstellen" #: f.tools.cc:324 msgid "Choose top image folders" msgstr "Oberstes Bild-Verzeichnis wählen" #: f.tools.cc:325 msgid "Choose thumbnail folder" msgstr "Thumbnails-Verzeichnis wählen" #: f.tools.cc:326 msgid "" "All image files will be re-indexed. \n" " Continue?" msgstr "" "Alle Dateien werden neu indiziert. \n" " Fortfahren?" #: f.tools.cc:442 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:448 #, c-format msgid "" "Thumbnails folder: %s \n" "Please remove." msgstr "" "Thumbnail-Verzeichnis: %s \n" "Bitte entfernen." #: f.tools.cc:451 #, c-format msgid "" "Thumbnails folder: \n" " %s \n" "must be named .../thumbnails" msgstr "" "Thumbnails Verzeichnis: \n" " %s \n" "muß .../thumbnails genannt sein" #: f.tools.cc:454 #, c-format msgid "" "Duplicate or nested folders: \n" " %s \n" " %s \n" "Please remove." msgstr "" "Duplikat oder verschachteltes Verzeichnis: \n" " %s \n" " %s \n" "Bitte entfernen." #: f.tools.cc:537 msgid "specify at least 1 top image folder" msgstr "Mindestens 1 obersten Bildordner angeben" #: f.tools.cc:542 msgid "specify 1 thumbnail folder" msgstr "Ein Thumbnail Verzeichnis angeben" #: f.tools.cc:649 msgid "build index" msgstr "Index erstellen" #: f.tools.cc:778 msgid "Top folders have no images" msgstr "Oberste Verzeichnisse haben keine Bilder" #: f.tools.cc:785 #, c-format msgid "" "0 old files found, %d new files found.\n" "A full re-index is required. Continue?" msgstr "" "0 alte Dateien gefunden, %d neue Dateien gefunden.\n" "Ein vollständige Neuindizierung ist erforderlich. Weiter?" #: f.tools.cc:1286 msgid "Cancel image index function?" msgstr "Bild-Indizieren abbrechen?" #: f.tools.cc:1447 #, c-format msgid "" "Do you want to move Fotoxx home? \n" " from: %s \n" " to: %s" msgstr "" "Möchten Sie das Heim-Verzeichnis Fotoxx verschieben? \n" " von: %s \n" " nach: %s" #: f.tools.cc:1449 msgid "moving files ..." msgstr "Dateien werden verschoben ..." #: f.tools.cc:1473 msgid "new Fotoxx home folder" msgstr "Neues Fotoxx Basisverzeichnis" #: f.tools.cc:1493 msgid "new location name contains a space" msgstr "Neuer Verzeichnisname enthält ein Leerzeichen" #: f.tools.cc:1533 msgid "index_config file has no thumbnails folder" msgstr "index_config Datei hat kein Thumbnails-Verzeichnis" #: f.tools.cc:1541 msgid "update thumbnail folder failed" msgstr "Aktualisierung Thumbnails-Verzeichnis fehlgeschlagen" #: f.tools.cc:1544 msgid "thumbnails folder not changed" msgstr "Thumbnails-Verzeichnis nicht geändert" #: f.tools.cc:1545 #, c-format msgid "new thumbnails folder: %s/thumbnails" msgstr "Neues Thumbnails-Verzeichnis: %s/thumbnails" #: f.tools.cc:1556 msgid "Fotoxx will restart" msgstr "Fotoxx wird neu starten" #: f.tools.cc:1572 msgid "Recent Files Gallery" msgstr "Zuletzt gesehene Bilder" #: f.tools.cc:1573 msgid "Newest Files Gallery" msgstr "Neueste Bilddateien" #: f.tools.cc:1574 msgid "Specific Gallery" msgstr "Bestimmte Galerie" #: f.tools.cc:1575 msgid "Album Gallery" msgstr "Album Galerie" #: f.tools.cc:1576 msgid "Previous Gallery" msgstr "Vorherige Galerie" #: f.tools.cc:1577 msgid "Previous File" msgstr "Vorherige Datei" #: f.tools.cc:1578 msgid "Specific File" msgstr "Bestimmte Datei" #: f.tools.cc:1579 f.widgets.cc:434 msgid "Blank Window" msgstr "Leeres Fenster" #: f.tools.cc:1604 #, c-format msgid "%c of scale" msgstr "%c der Skala" #: f.tools.cc:1647 msgid "Preferences and Settings" msgstr "Vorgaben und Einstellungen" #: f.tools.cc:1650 msgid "Startup Display:" msgstr "Anfangsanzeige:" #: f.tools.cc:1656 msgid "Background Colors:" msgstr "Hintergrundfarben:" #: f.tools.cc:1658 msgid "F-View" msgstr "F-Ansicht" #: f.tools.cc:1661 msgid "G-View" msgstr "G-Ansicht" #: f.tools.cc:1665 msgid "Menu Style:" msgstr "Menüstil:" #: f.tools.cc:1667 msgid "Icons" msgstr "Icons" #: f.tools.cc:1669 msgid "Both" msgstr "Beide" #: f.tools.cc:1671 msgid "Icon size" msgstr "Icon-Größe" #: f.tools.cc:1675 msgid "Menu Colors:" msgstr "Menüfarben:" #: f.tools.cc:1684 msgid "Dialog Font:" msgstr "Dialogschriftart:" #: f.tools.cc:1703 msgid "JPEG file save quality:" msgstr "JPEG-Datei Speicherqualität:" #: f.tools.cc:1706 msgid "(90 = high quality)" msgstr "(90 = hohe Qualität)" #: f.tools.cc:1709 msgid "TIFF file compression method" msgstr "TIFF-Dateikomprimierungsmethode" #: f.tools.cc:1715 msgid "PNG file compression level" msgstr "PNG-Dateikomprimierungsgrad" #: f.tools.cc:1721 msgid "Curve Node Separation, Capture Range:" msgstr "Kurvenknotentrennung, Erfassungsbereich:" #: f.tools.cc:1727 msgid "Map Marker Size:" msgstr "Karten-Markierungsgröße:" #: f.tools.cc:1733 msgid "Show Images (F-view, G-view):" msgstr "Bilder anzeigen (F-Ansicht, G-Ansicht):" #: f.tools.cc:1735 msgid "last version only" msgstr "nur neueste Version" #: f.tools.cc:1736 msgid "all images" msgstr "alle Bilder" #: f.tools.cc:1739 msgid "Image Position in Window:" msgstr "Bildposition im Fenster:" #: f.tools.cc:1741 msgid "centered" msgstr "zentriert" #: f.tools.cc:1742 msgid "right side" msgstr "rechte Seite" #: f.tools.cc:1745 msgid "Image Index Level:" msgstr "Bild Indexstufe:" #: f.tools.cc:1749 msgid "Fotoxx started directly (2)" msgstr "Fotoxx direkt gestartet (2)" #: f.tools.cc:1753 msgid "Fotoxx started by file manager (1)" msgstr "Fotoxx vom Dateimanager gestartet (1)" #: f.tools.cc:1756 msgid "RAW File Loader:" msgstr "RAW-Datei-Lader:" #: f.tools.cc:1762 msgid "RAW Conversion Options:" msgstr "RAW-Konvertierungsoptionen:" #: f.tools.cc:1765 msgid "extend dynamic range for dim images" msgstr "Dynamikbereich für dunkle Bilder erweitern" #: f.tools.cc:1768 msgid "use embedded image as a guide" msgstr "Eingebettetes Bild als Leitfaden verwenden" #: f.tools.cc:1771 msgid "RAW File Types:" msgstr "RAW-Dateitypen:" #: f.tools.cc:1776 msgid "Video File Types:" msgstr "Video-Dateitypen:" #: f.tools.cc:1781 msgid "Video File Play Command:" msgstr "Videodateien Abspiel-Befehl:" #: f.tools.cc:1937 msgid "Select startup folder" msgstr "Anfangsverzeichnis wählen" #: f.tools.cc:1944 msgid "Select startup image file" msgstr "Anfangsbilddatei wählen" #: f.tools.cc:1951 msgid "Select startup album" msgstr "Start-Album wählen" #: f.tools.cc:1976 msgid "rawtherapee-cli (rawtherapee) is not installed" msgstr "rawtherapee-cli (rawtherapee) nicht installiert" #: f.tools.cc:2006 msgid "startup folder is invalid" msgstr "Anfangsverzeichnes ungültig" #: f.tools.cc:2016 msgid "startup file is invalid" msgstr "Anfangsdatei ungültig" #: f.tools.cc:2216 f.widgets.cc:372 msgid "Keyboard Shortcuts" msgstr "Schnelltasten" #: f.tools.cc:2222 msgid "Reserved Shortcuts \n" msgstr "Reservierte Schnelltasten \n" #: f.tools.cc:2223 msgid " Z Toggle 1x / fit window \n" msgstr " Z Umschalten 1x / Fenster anpassen \n" #: f.tools.cc:2224 msgid " F1 User Guide, Context Help \n" msgstr " F1 Benutzeranleitung, Kontexthilfe \n" #: f.tools.cc:2225 msgid " F10 Full Screen with menus \n" msgstr " F10 Vollbild mit Menüs \n" #: f.tools.cc:2226 msgid " F11 Full Screen without menus \n" msgstr " F11 Vollbild ohne Menüs \n" #: f.tools.cc:2227 msgid " Escape Quit dialog, Quit Fotoxx \n" msgstr " Escape Dialog beenden, Fotoxx beenden \n" #: f.tools.cc:2228 msgid " Delete Delete/Trash \n" msgstr " Entfernen Löschen / Papierkorb \n" #: f.tools.cc:2229 msgid " Arrow keys Navigation \n" msgstr " Pfeiltasten Navigation \n" #: f.tools.cc:2230 msgid " Page keys Navigation \n" msgstr " Seitetasten Navigation \n" #: f.tools.cc:2231 msgid " Home/End Navigation \n" msgstr " Pos1/Ende Navigation \n" #: f.tools.cc:2295 msgid "Edit KB Shortcuts" msgstr "Schnelltasten bearbeiten" #: f.tools.cc:2302 msgid "shortcut key:" msgstr "Schnelltaste:" #: f.tools.cc:2303 msgid "(enter key)" msgstr "(Enter-Taste)" #: f.tools.cc:2304 f.tools.cc:2451 f.tools.cc:2554 msgid "(no selection)" msgstr "(keine Auswahl)" #: f.tools.cc:2445 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Reserviert, kann nicht verwendet werden" #: f.tools.cc:2672 msgid "Brightness Distribution" msgstr "Helligkeitsverteilung" #: f.tools.cc:2866 msgid "" "Drag mouse on image. \n" "Left click to cancel." msgstr "" "Maus auf dem Bild ziehen. \n" "Links-klicken zum Abbrechen." #: f.tools.cc:2893 f.widgets.cc:612 msgid "Magnify Image" msgstr "Bild Vergrößern" #: f.tools.cc:2902 msgid "X-size" msgstr "X-Größe" #: f.tools.cc:3210 msgid "Find Duplicate Images" msgstr "Doppelte Bilder suchen" #: f.tools.cc:3213 msgid "All files" msgstr "Alle Dateien" #: f.tools.cc:3214 msgid "Current gallery" msgstr "Aktuelle Galerie" #: f.tools.cc:3217 msgid "File count:" msgstr "Anzahl Dateien" #: f.tools.cc:3221 msgid "Thumbnail size" msgstr "Thumbnailgröße" #: f.tools.cc:3226 msgid "Pixel difference" msgstr "Pixel-Unterschied" #: f.tools.cc:3229 msgid "Pixel count" msgstr "Anzahl Pixel" #: f.tools.cc:3237 msgid "Duplicates:" msgstr "Duplikate:" #: f.tools.cc:3567 msgid "too many files, cannot continue" msgstr "Zu viele Dateien, kann nicht fortfahren" #: f.tools.cc:3647 msgid "Click image to select pixels." msgstr "Bild für Pixelauswahl anklicken" #: f.tools.cc:3690 f.widgets.cc:614 msgid "Show RGB" msgstr "RGB-Werte zeigen" #: f.tools.cc:3948 msgid "Change Color Profile" msgstr "Farbraum ändern" #: f.tools.cc:3952 msgid "input profile" msgstr "Eingabe-Farbraum" #: f.tools.cc:3956 msgid "output profile" msgstr "Ausgabe-Farbraum" #: f.tools.cc:3977 msgid "Unable to change EXIF color profile" msgstr "EXIF Farbprofil Ändern fehlgeschlagen" #: f.tools.cc:3979 msgid "automatic new version created" msgstr "neue Version automatisch erzeugt" #: f.tools.cc:3988 msgid "color profile" msgstr "Farbraum" #: f.tools.cc:4036 f.tools.cc:4042 #, c-format msgid "unknown cms profile %s" msgstr "CMS Farbraum %s unbekannt" #: f.tools.cc:4144 f.widgets.cc:616 msgid "Calibrate Printer" msgstr "Drucker kalibrieren" #: f.tools.cc:4170 msgid "print color chart" msgstr "Farbkarte drucken" #: f.tools.cc:4171 msgid "scan and save color chart" msgstr "Farbkarte einscannen und speichern" #: f.tools.cc:4172 msgid "align and trim color chart" msgstr "Farbkarte ausrichten und zuschneiden" #: f.tools.cc:4173 msgid "open and process color chart" msgstr "Farbkarte öffnen und verarbeiten" #: f.tools.cc:4174 msgid "print image with revised colors" msgstr "Bilddatei mit geänderten Farben drucken" #: f.tools.cc:4315 #, c-format msgid "" "Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/" msgstr "" "Gedruckte Farbkarte einscannen. \n" "Dunkelste Reihe muss oben sein. \n" "Speichern in %s/" #: f.tools.cc:4330 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY." msgstr "" "Eingescannte Farbkartedatei öffnen und bearbeiten. \n" "Schräglauf oder Verdrehung (vom Scannen) korrigieren. \n" "(Dazu Funktion Perspektive Verbessern benutzen.) \n" "Die dünnen grünen Ränder GENAU abschneiden." #: f.tools.cc:4362 msgid "Open the trimmed color chart file" msgstr "Zugeschnittene Farbkartendatei öffnen" #: f.tools.cc:4495 msgid "" "Set the name for the output calibration file \n" "[your calibration name].dat" msgstr "" "Name für Ausgabe-Kalibrierungsdatei festlegen \n" "[Eigener Kalibrierungsname].dat" #: f.tools.cc:4535 msgid "Color map file to use" msgstr "Farbdatendatei zur Verwendung" #: f.tools.cc:4555 msgid "Select the image file to print." msgstr "Bilddatei zum Drucken auswählen." #: f.tools.cc:4596 msgid "file format error" msgstr "Dateiformat Fehler" #: f.tools.cc:4620 msgid "converting colors..." msgstr "Farben werden konvertiert ..." #: f.tools.cc:4720 msgid "Image colors are converted for printing." msgstr "Bildfarben für das Drucken wurden konvertiert." #: f.tools.cc:4769 f.widgets.cc:617 msgid "Grid Lines" msgstr "Gitterlinien" #: f.tools.cc:4778 msgid "x-spacing" msgstr "x-Abstand" #: f.tools.cc:4779 msgid "x-count" msgstr "x-Anzahl" #: f.tools.cc:4780 msgid "x-enable" msgstr "x-aktivieren" #: f.tools.cc:4786 msgid "y-spacing" msgstr "y-Abstand" #: f.tools.cc:4787 msgid "y-count" msgstr "y-Anzahl" #: f.tools.cc:4788 msgid "y-enable" msgstr "y-aktivieren" #: f.tools.cc:4909 f.widgets.cc:618 msgid "Line Color" msgstr "Linienfarbe" #: f.tools.cc:4967 msgid "Darkest and Brightest Pixels" msgstr "Dunkelste und hellste Pixel" #: f.tools.cc:4991 msgid "Dark Limit" msgstr "Grenzwert dunkel" #: f.tools.cc:4992 msgid "Bright Limit" msgstr "Grenzwert hell" #: f.tools.cc:5117 msgid "Map RAW Pixel Bias" msgstr "RAW Pixel Bias auflisten" #: f.tools.cc:5124 msgid "mean RGB:" msgstr "mittleres RGB:" #: f.tools.cc:5210 msgid "select at least 10 RAW image files" msgstr "Mindestens 10 RAW-Bilddateien auswählen" #: f.tools.cc:5230 #, c-format msgid "" "cannot read file \n" " %s" msgstr "" "Datei nicht lesbar \n" " %s" #: f.tools.cc:5235 #, c-format msgid "" "not a RAW file \n" " %s" msgstr "" "keine RAW-Datei \n" " %s" #: f.tools.cc:5249 #, c-format msgid "dimensions do not match: %s" msgstr "Maße stimmen nicht überein: %s" #: f.tools.cc:5366 f.tools.cc:5449 msgid "Pixel Bias Map file" msgstr "Pixel Bias Listdatei" #: f.tools.cc:5494 msgid "invalid pixel bias map file" msgstr "ungültige Pixel Bias Listdatei" #: f.tools.cc:5524 msgid "image dimensions do not match pixel bias file" msgstr "Bildabmessungen stimmen nicht mit der Pixel-Bias-Datei überein" #: f.tools.cc:5610 msgid "Map RAW Dead Pixels" msgstr "RAW tote Pixel auflisten" #: f.tools.cc:5614 msgid "gray RAW image file" msgstr "RAW-Bilddatei für Grau" #: f.tools.cc:5618 msgid "RGB threshold" msgstr "RGB-Schwelle" #: f.tools.cc:5621 msgid "dead pixels found:" msgstr "totte Pixel gefunden:" #: f.tools.cc:5658 msgid "not a RAW file" msgstr "keine RAW-Datei" #: f.tools.cc:5664 msgid "cannot load RAW file" msgstr "kann RAW-Datei nicht laden" #: f.tools.cc:5727 msgid "choose a gray RAW file" msgstr "eine RAW-Datei für Grau wählen" #: f.tools.cc:5858 f.tools.cc:5903 msgid "dead pixels file" msgstr "Datei toter Pixel" #: f.tools.cc:5955 msgid "invalid dead pixels file" msgstr "ungültige Datei toter Pixel" #: f.tools.cc:5975 msgid "no dead pixels data available" msgstr "Keine Daten totter Pixel verfügbar" #: f.tools.cc:5980 msgid "image dimensions do not match dead pixels file" msgstr "Keine Übereinstimmung Bildabmessungen mit Datei toter Pixel" #: f.tools.cc:6048 msgid "" "Brightness should show a gradual ramp \n" "extending all the way to the edges." msgstr "" "Helligkeit soll einen allmählichen Anstieg \n" " auch ganz bis zu den Rändern zeigen." #: f.tools.cc:6208 msgid "Available Translations" msgstr "Vorhandene Übersetzungen" #: f.tools.cc:6212 msgid "Set Language" msgstr "Sprache wechseln" #: f.warp.cc:110 f.widgets.cc:556 msgid "Unbend" msgstr "Entkrümmen" #: f.warp.cc:388 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 Viereckbereichs anklicken. [Anwenden] drücken. \n" " Das Viereck wird in ein gerades Rechteck verformt." #: f.warp.cc:405 msgid "Perspective Correction" msgstr "Perspektive Verbessern" #: f.warp.cc:638 msgid "must have 4 corners" msgstr "Vier Ecken sind nötig" #: f.warp.cc:763 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 "" " Fläche zum Verformen mittels Funktion Ausschnitt wählen. \n" " [Verformen starten] drücken, Ausschnitt mit der Maus ziehen. \n" " Mehrmals ziehen/strecken bis zum erwünschten Ergebniss. \n" " Wenn fertig, anderen Fläche wählen oder [Fertig] drücken." #: f.warp.cc:776 f.widgets.cc:558 msgid "Warp area" msgstr "Fläche verformen" #: f.warp.cc:781 msgid "start warp" msgstr "Verformen starten" #: f.warp.cc:1182 f.warp.cc:1494 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 zum gewünschten Ergebnis. \n" " Wenn fertig, [Fertig] drücken." #: f.warp.cc:1200 f.widgets.cc:559 msgid "Warp curved" msgstr "Gebogen verformen" #: f.warp.cc:1512 f.widgets.cc:560 msgid "Warp linear" msgstr "Linear verformen" #: f.warp.cc:1825 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 zum gewünschten Ergebnis. \n" " Wenn fertig, [Fertig] drücken." #: f.warp.cc:1841 f.widgets.cc:561 msgid "Warp affine" msgstr "Affin verformen" #: f.warp.cc:2174 msgid "" " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n" msgstr "" " Gesicht auswählen mittels Ausschnitt wählen. \n" " Zentrum der Verzerrung anklicken. \n" " Schieberegler bewegen. \n" #: f.warp.cc:2201 f.widgets.cc:562 msgid "Unwarp Closeup" msgstr "Verformung Großaufnahme aufheben" #: f.warp.cc:2379 msgid "Flatten Book Page" msgstr "Buchseite gerade ausrichten" #: f.warp.cc:2380 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 gerade ausrichten: " #: f.warp.cc:2383 msgid "Stretch curved-down surfaces:" msgstr "Nach unten gebogene Flächen strecken:" #: f.warp.cc:2438 msgid "Top:" msgstr "Oben:" #: f.warp.cc:2441 msgid "Bottom:" msgstr "Unten:" #: f.warp.cc:2830 msgid "" " Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]." msgstr "" " Ausschnitte die unverändert bleiben auswählen. \n" " Bild von der Ecke oben links ziehen. \n" " Wenn fertig, [Fertig] drucken." #: f.warp.cc:2846 f.widgets.cc:564 msgid "Area Rescale" msgstr "Ausschnitt umskalieren" #: f.warp.cc:2869 msgid "select areas first" msgstr "Ausschnitte erst auswählen" #: f.warp.cc:3088 f.widgets.cc:565 msgid "Make Waves" msgstr "Wellen erzeugen" #: f.warp.cc:3095 msgid "wavelength" msgstr "Wellenlänge" #: f.warp.cc:3097 msgid "variance" msgstr "Varianz" #: f.warp.cc:3108 msgid "perspective" msgstr "Perspektive" #: f.warp.cc:3285 f.warp.cc:3321 f.widgets.cc:566 msgid "Twist" msgstr "Verdrehen" #: f.warp.cc:3318 f.warp.cc:3609 f.warp.cc:3835 f.warp.cc:4060 msgid "Drag mouse to set center" msgstr "Maus ziehen um Mittelpunkt zu setzen" #: f.warp.cc:3565 msgid "Spherical Projection" msgstr "Sphärische Projektion" #: f.warp.cc:3792 msgid "Stretch Image" msgstr "Bild strecken" #: f.warp.cc:4057 f.widgets.cc:569 msgid "Inside-out" msgstr "Innen-/Aussenseite" #: f.warp.cc:4065 f.warp.cc:4314 msgid "Center Hole" msgstr "Lochzentrum" #: f.warp.cc:4266 msgid "image width must be greater than height" msgstr "Bild-Breite muß größer als -Höhe sein" #: f.warp.cc:4319 msgid "Cut Top" msgstr "Oben abschneiden" #: f.warp.cc:4324 msgid "Cut Bottom" msgstr "Unten abschneiden" #: f.warp.cc:4332 msgid "Reverse R" msgstr "R umdrehen" #: f.warp.cc:4333 msgid "Theta" msgstr "Theta" #: f.warp.cc:4545 msgid "Click mouse to change center" msgstr "Mausklick zum Ändern der Mitte" #: f.warp.cc:4550 msgid "Rim %" msgstr "Rand %" #: f.widgets.cc:105 msgid "Album" msgstr "Album" #: f.widgets.cc:107 msgid "TOP" msgstr "OBEN" #: f.widgets.cc:167 msgid "Rename, copy/move, delete, print" msgstr "Umbenennen, kopieren/verschieben, löschen, drucken" #: f.widgets.cc:168 msgid "Thumbnails, bookmarks, albums, slide show" msgstr "Thumbnails, Lesezeichen, Alben, Diaschau" #: f.widgets.cc:169 msgid "View images by map location" msgstr "Zeige Bilder vom Kartenort" #: f.widgets.cc:170 msgid "Custom favorites menu" msgstr "Benutzerdefiniertes Favoritenmenü" #: f.widgets.cc:171 msgid "Left/right click: previous/next (also arrow keys)" msgstr "Links/Rechtsklick: vorheriges/nächstes (auch Pfeiltasten)" #: f.widgets.cc:172 msgid "Left/right click: larger/smaller image/thumbnails" msgstr "" #: f.widgets.cc:173 msgid "Save modified file as new version or new file" msgstr "Geänderte Bilddatei als neue Version oder Datei speichern" #: f.widgets.cc:174 msgid "Metadata: captions, tags, ratings, geotags, search images" msgstr "Metadaten: Beschriftungen, Tags, Bewertungen, Geotags, Bilder-Suche" #: f.widgets.cc:175 msgid "Select areas to edit separately, save, copy and paste" msgstr "Bereiche auswählen zum Bearbeiten, Speichern, Kopieren, Einfügen" #: f.widgets.cc:176 msgid "" "Left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit" msgstr "" "Links/Rechtsklick: rückgängig/wiederholen \n" " mit Taste A: Alle rückgängig/wiederholen \n" " mittlerer Klick: gehe zu einer vorherigen Bearbeitung" #: f.widgets.cc:179 msgid "Image edit basic functions" msgstr "Bildbearbeitung Grundfunktionen" #: f.widgets.cc:180 msgid "Image repair and enhance" msgstr "Bild reparieren und verbessern" #: f.widgets.cc:181 msgid "Artistic effects (filters)" msgstr "Künstlerische Effekte (Filter)" #: f.widgets.cc:182 msgid "Image warp, unwarp, transform" msgstr "Bild krümmen, entkrümmen, transformieren" #: f.widgets.cc:183 msgid "HDR, HDF, panorama, stack, mashup" msgstr "HDR, HDF, Panorama, Stapel, Mashup" #: f.widgets.cc:184 msgid "Batch processing, custom scripts" msgstr "Stapelverarbeitung, benutzerdefinierte Skripte" #: f.widgets.cc:185 msgid "Image index, user preferences, shortcuts, utilities" msgstr "Bilder indizieren, Einstellungen, Verknüpfungen, Dienstprogramme" #: f.widgets.cc:186 msgid "User Guide, recent changes, log file, about" msgstr "Benutzeranleitung, letzte Änderungen, Log-Datei, Über" #: f.widgets.cc:189 msgid "Current File (R-click or key F)" msgstr "Aktuelle Datei (R-Klick oder Taste F)" #: f.widgets.cc:190 msgid "Open a parallel Fotoxx session" msgstr "Parallele Fotoxx-Instanz aufmachen" #: f.widgets.cc:191 msgid "Cycle 2 Prior Files" msgstr "2 vorherige Bilder abwechseln" #: f.widgets.cc:192 msgid "Cycle 3 Prior Files" msgstr "3 vorherige Bilder abwechseln" #: f.widgets.cc:193 msgid "View a 360 degree panorama image file" msgstr "360 Grad Panorama-Ansicht aus Bilddatei ansehen" #: f.widgets.cc:194 msgid "Change file name" msgstr "Bilddateinamen ändern" #: f.widgets.cc:195 msgid "View and change file permissions" msgstr "Dateiberechtigungen anzeigen und ändern" #: f.widgets.cc:196 msgid "Create a blank image" msgstr "Leeres Bild erstellen" #: f.widgets.cc:197 msgid "Toggle - blank or restore window" msgstr "Umschalten - Leeres Fenster oder aktuelles Bild" #: f.widgets.cc:198 msgid "Copy or Move file to new location" msgstr "Bilddatei in ein neues Verzeichnis kopieren oder verschieben" #: f.widgets.cc:199 msgid "Copy file to the desktop" msgstr "Bild-Datei nach Schreibtisch kopieren" #: f.widgets.cc:200 msgid "Copy file to the clipboard" msgstr "Bilddatei in Zwischenablage kopieren" #: f.widgets.cc:201 msgid "Set file as desktop wallpaper (GNOME)" msgstr "Datei als Bildschirm-Hintergrund setzen (GNOME)" #: f.widgets.cc:202 msgid "Show location on Internet map" msgstr "Ort auf Internetkarte zeigen" #: f.widgets.cc:203 msgid "Delete or trash file" msgstr "Bilddatei löschen oder in den Papierkorb" #: f.widgets.cc:204 msgid "Print the current image" msgstr "Aktuelles Bild drucken" #: f.widgets.cc:205 msgid "Print current image with adjusted colors" msgstr "Aktuelle Bilddatei mit angepassten Farben drucken" #: f.widgets.cc:206 msgid "Quit Fotoxx" msgstr "Fotoxx beenden" #: f.widgets.cc:209 msgid "Thumbnail Gallery (R-click or key G)" msgstr "Thumbnail-Galerie (R-Klick oder Taste G)" #: f.widgets.cc:210 msgid "Gallery view with thumbnails and basic metadata" msgstr "Galerieansicht mit Thumbnails und grundlegenden Metadaten" #: f.widgets.cc:211 msgid "Gallery view with small thumbnails and file names" msgstr "Galerieansicht mit kleinen Thumbnails und Dateinamen" #: f.widgets.cc:212 msgid "Gallery of recently viewed image files" msgstr "Galerie der zuletzt angesehenen Bilddateien" #: f.widgets.cc:213 msgid "Gallery of newest image files" msgstr "Galerie der neuesten Bilddateien" #: f.widgets.cc:214 msgid "Jump to beginning [home]" msgstr "Zum Anfang springen [Pos 1]" #: f.widgets.cc:215 msgid "Jump to end [end]" msgstr "Ans Ende springen [Ende]" #: f.widgets.cc:216 msgid "Set gallery from current image file" msgstr "Galerie vom aktuellen Bild setzen" #: f.widgets.cc:217 msgid "Change sort order" msgstr "Reihenfolge ändern" #: f.widgets.cc:218 msgid "List all folders, click any for gallery view" msgstr "Anzeige aller Verzeichnisse, eines anklicken zur Galerieansicht" #: f.widgets.cc:219 msgid "select input files for album, batch, script functions" msgstr "Eingabedateien für Album, Stapel, Skriptfunktionen auswählen" #: f.widgets.cc:220 msgid "Set and recall bookmarked image locations" msgstr "Lesezeichen-markierte Stellen setzen und zurückrufen" #: f.widgets.cc:221 msgid "Organize images into albums" msgstr "Bilder in Alben organisieren" #: f.widgets.cc:222 msgid "Update albums for new file versions" msgstr "Alben für neue Dateiversionen aktualisieren" #: f.widgets.cc:223 msgid "Mass update album files" msgstr "Gesamtaktualisierung der Dateien im Album" #: f.widgets.cc:224 msgid "Save current gallery as album" msgstr "Aktuelle Galerie als Album speichern" #: f.widgets.cc:225 msgid "Start a slide show" msgstr "Diaschau starten" #: f.widgets.cc:228 msgid "Maps (R-click or key M)" msgstr "Karten (R-Klick oder Taste M)" #: f.widgets.cc:229 msgid "Open Internet map" msgstr "Internet-Karte öffnen" #: f.widgets.cc:230 msgid "Choose Internet map source" msgstr "Internet Kartenquelle auswählen" #: f.widgets.cc:231 msgid "Internet map locations" msgstr "Orte auf Internet-Karte" #: f.widgets.cc:232 msgid "Open file map" msgstr "Kartendatei öffnen" #: f.widgets.cc:233 msgid "Choose file map" msgstr "Kartendatei wählen" #: f.widgets.cc:234 msgid "Set map markers for all images or current gallery" msgstr "Kartenmarkierungen für alle Bilder oder die aktuelle Galerie setzen" #: f.widgets.cc:237 msgid "List a few key metadata items" msgstr "Wichtigste Metadaten auflisten" #: f.widgets.cc:238 msgid "List all metadata items" msgstr "Alle Metadaten auflisten" #: f.widgets.cc:239 msgid "Edit image tags/geotags/caption/rating ..." msgstr "Bild Tags/Geotags/Titel/Bewertung ... bearbeiten" #: f.widgets.cc:240 msgid "Define tags (keywords) used for searching images" msgstr "Tags (Schlüsselwörter) für die Bildersuche definieren" #: f.widgets.cc:241 msgid "Edit any image metadata" msgstr "Beliebige Bild-Metadaten bearbeiten" #: f.widgets.cc:242 msgid "Remove selected image metadata" msgstr "Gewählte Bild-Metadaten entfernen" #: f.widgets.cc:243 msgid "Show file name, captions, comments" msgstr "Dateinamen, Bilüberschriften, Kommentare anzeigen" #: f.widgets.cc:244 msgid "Find all images for a location [date]" msgstr "Alle Bilder für einen Ort [Datum] finden" #: f.widgets.cc:245 msgid "Show image counts by month, select and report" msgstr "Bildzahl pro Monat anzeigen, auswählen und berichten" #: f.widgets.cc:246 msgid "Find images meeting search criteria" msgstr "Bilder finden, die den Suchkriterien entsprechen" #: f.widgets.cc:249 msgid "Select object or area for editing" msgstr "Objekt oder Fläche zum Bearbeiten auswählen" #: f.widgets.cc:250 msgid "Select hairy or irregular edge" msgstr "Zotteligen oder unregelmäßigen Rand auswählen" #: f.widgets.cc:251 msgid "Find a gap in an area outline" msgstr "Lücke in Flächenrand finden" #: f.widgets.cc:252 msgid "Show (outline) existing area" msgstr "Aktuellen Fläche zeigen (umranden)" #: f.widgets.cc:253 msgid "Hide existing area" msgstr "Aktuellen Fläche ausblenden" #: f.widgets.cc:254 msgid "Enable area for editing" msgstr "Fläche zum Bearbeiten aktivieren" #: f.widgets.cc:255 msgid "Disable area for editing" msgstr "Fläche deaktivieren" #: f.widgets.cc:256 msgid "Reverse existing area" msgstr "Aktuelle Auswahl umkehren" #: f.widgets.cc:257 msgid "Erase existing area" msgstr "Aktuelle Fläche löschen" #: f.widgets.cc:258 msgid "Copy area for later pasting into image" msgstr "Fläche für späteres Einfügen kopieren" #: f.widgets.cc:259 msgid "Paste previously copied area into image" msgstr "Vorher kopierte Fläche ins Bild einfügen" #: f.widgets.cc:260 msgid "Open a file and paste as area into image" msgstr "Datei öffnen und als Fläche ins Bild einfügen" #: f.widgets.cc:261 msgid "Save area to a file with transparency" msgstr "Fläche mit Transparenz in Datei speichern" #: f.widgets.cc:264 msgid "Trim/Crop margins and/or Rotate" msgstr "Ränder zu/beschneiden und/oder drehen" #: f.widgets.cc:265 msgid "Auto upright a rotated image based on EXIF data" msgstr "Gedrehtes Bild automatisch aufrichten, basierend auf EXIF-Daten" #: f.widgets.cc:266 msgid "Adjust brightness, contrast, color" msgstr "Helligkeit, Kontrast, Farbe einstellen" #: f.widgets.cc:267 msgid "Change pixel dimensions" msgstr "Pixelgröße ändern" #: f.widgets.cc:268 msgid "Adjust color using RGB or CMY colors" msgstr "Farben justieren mit RGB oder CMY Farben" #: f.widgets.cc:269 msgid "Adjust color using HSL colors" msgstr "Farben justieren mit HSL Farben" #: f.widgets.cc:270 msgid "Draw on image: text, line/arrow, box, ellipse" msgstr "Auf Bild zeichnen: Text, Linie/Pfeil, Kasten, Ellipse" #: f.widgets.cc:271 msgid "Paint image pixels using the mouse" msgstr "Bild-Pixel anmalen mittels Maus" #: f.widgets.cc:272 msgid "Copy pixels within an image using the mouse" msgstr "Bild-Pixel innerhalb eines Bilds mit Maus kopieren" #: f.widgets.cc:273 msgid "Copy pixels from one image to another using the mouse" msgstr "Bild-Pixel mit Maus von diesem zu einem anderen Bild kopieren" #: f.widgets.cc:274 msgid "Paint edit function gradually with mouse" msgstr "Änderungs-Funktion allmählich mittels Maus anmalen" #: f.widgets.cc:275 msgid "Incrementally undo prior edits gradually with mouse" msgstr "Änderungen schrittweise mit der Maus rückgängig machen" #: f.widgets.cc:276 msgid "Edit plugins menu or run a plugin function" msgstr "Plugins-Menü editieren oder Plugin-Funktion starten" #: f.widgets.cc:277 msgid "Specialized program for editing RAW files" msgstr "Spezialisiertes Programm zum Bearbeiten von RAW-Dateien" #: f.widgets.cc:280 f.widgets.cc:281 msgid "Fast auto enhance that may work OK" msgstr "Auto-Verbesserung auf die Schnelle" #: f.widgets.cc:282 msgid "Edit brightness distribution" msgstr "Helligkeitsverteilung bearbeiten" #: f.widgets.cc:283 msgid "Magnify brightness gradients to enhance details" msgstr "Helligkeitsgradienten vergrößen um Details hervorzuheben" #: f.widgets.cc:284 msgid "Flatten brightness distribution to enhance details" msgstr "Helligkeitsverteilung glätten, um Details zu verbessern" #: f.widgets.cc:285 msgid "Rescale RGB - reduce color caste and fog/haze" msgstr "RGB neu skalieren - Farbstich und Trübung/Schleier mindern" #: f.widgets.cc:286 msgid "Make the image look sharper" msgstr "Bild schärfer machen" #: f.widgets.cc:287 msgid "Blur the image, different methods" msgstr "Bild verwischen, verschiedene Methoden" #: f.widgets.cc:288 msgid "Filter noise from low-light photos" msgstr "Rauschen in unterbelichteten Fotos ausfiltern" #: f.widgets.cc:289 msgid "Fix red-eyes from electronic flash" msgstr "Rote Augen von Blitzfotos entfernen" #: f.widgets.cc:290 msgid "Match colors on one image with another" msgstr "Farben in einem Bild an ein anderes anpassen" #: f.widgets.cc:291 msgid "Remove unwanted objects" msgstr "Unerwünschte Gegenstände entfernen" #: f.widgets.cc:292 msgid "Fix color fringes in outer areas of an image" msgstr "Farbsäume in äußeren Bereichen eines Bildes korrigieren" #: f.widgets.cc:293 msgid "Fix color band on dark/bright feature edges" msgstr "Farbband an dunklen/hellen Feature-Rändern verbessern" #: f.widgets.cc:294 msgid "Change brightness or color radially" msgstr "Helligkeit oder Farbwert radial ändern" #: f.widgets.cc:295 msgid "Remove dust spots from scanned slides" msgstr "Staubflecken von gescannten Dias entfernen" #: f.widgets.cc:298 msgid "Convert to simulated sketch" msgstr "In eine simulierte Zeichnung verwandeln" #: f.widgets.cc:299 msgid "Convert image into a cartoon drawing" msgstr "Bild in eine Cartoon-Zeichnung wandeln" #: f.widgets.cc:300 msgid "Convert to line drawing (edge detection)" msgstr "In eine Stift-Zeichnung (Eckenerkennung) verwandeln" #: f.widgets.cc:301 msgid "Create an embossed or 3D appearance" msgstr "Prägung oder 3D Wirkung erzeugen" #: f.widgets.cc:302 msgid "Convert to square tiles" msgstr "In Kacheln verwandeln" #: f.widgets.cc:303 msgid "Convert to dithered dots" msgstr "In Rasterpunkten umwandeln" #: f.widgets.cc:304 msgid "Convert into a simulated painting" msgstr "In ein simuliertes Gemälde verwandeln" #: f.widgets.cc:305 msgid "Add texture to an image" msgstr "Textur auf Bild zufügen" #: f.widgets.cc:306 msgid "Tile image with a repeating pattern" msgstr "Bild mit sich wiederholenden Muster fliesen" #: f.widgets.cc:307 msgid "Create a mosaic with tiles made from all images" msgstr "Mosaik mit Kacheln aus allen Bilder erstellen" #: f.widgets.cc:308 msgid "Make BW/color, negative/positive, sepia" msgstr "SW/farbiges Negativ/Positiv, Sepiaton erzeugen" #: f.widgets.cc:309 msgid "Reduce color depth (posterize)" msgstr "Farbtiefe mindern (Poster-Effekt)" #: f.widgets.cc:310 msgid "Shift/convert colors into other colors" msgstr "Farben in andere Farben verwandeln" #: f.widgets.cc:311 msgid "Change color hue using an algorithm" msgstr "Farbton ändern mittels Algorithmus" #: f.widgets.cc:312 msgid "Add a brightness/color ramp across the image" msgstr "Helligkeits-/Farbverlauf quer über das Bild addieren" #: f.widgets.cc:313 msgid "Paint image transparency using the mouse" msgstr "Bild-Transparenz mittels Maus malen" #: f.widgets.cc:314 msgid "Mirror image horizontally or vertically" msgstr "Bild horizontal oder vertikal spiegeln" #: f.widgets.cc:315 msgid "Process an image using a custom kernel" msgstr "Bild mit benutzerspezifischem Kernel bearbeiten" #: f.widgets.cc:318 msgid "Remove curvature, esp. panoramas" msgstr "Entkrümmen, besonders bei Panoramen" #: f.widgets.cc:319 msgid "Straighten objects seen from an angle" msgstr "Aus einem Winkel gesehene Objekte begradigen" #: f.widgets.cc:320 msgid "Distort image areas using the mouse" msgstr "Bildflächen mit der Maus verzerren" #: f.widgets.cc:321 msgid "Unwarp closeup face photo to remove distortion" msgstr "Verformte Gesichts-Großaufnahme verbessern" #: f.widgets.cc:322 f.widgets.cc:323 f.widgets.cc:324 msgid "Distort the whole image using the mouse" msgstr "Ganzes Bild mittels Maus verzerren" #: f.widgets.cc:325 msgid "Flatten a photographed book page" msgstr "Foto von einer Buchseite gerade ausrichten" #: f.widgets.cc:326 msgid "Rescale image outside selected areas" msgstr "Bild ausserhalb ausgewählter Flächen skalieren" #: f.widgets.cc:327 msgid "Warp an image with a wave pattern" msgstr "Bild mit Wellenmuster verformen" #: f.widgets.cc:328 msgid "Twist image centered at mouse position" msgstr "Bild verdrehen um Maus-Position" #: f.widgets.cc:329 msgid "Make a spherical projection of an image" msgstr "Sphärische Projektion vom Bild erzeugen" #: f.widgets.cc:330 msgid "Image scale increases from center to edge" msgstr "Bildmaßstab wächst von der Mitte zum Rand" #: f.widgets.cc:331 msgid "Turn an image inside-out" msgstr "Bild Innen- und Außenseite tauschen" #: f.widgets.cc:332 msgid "Convert an image into a Tiny Planet" msgstr "Bild in 360° Miniaturplanet umwandeln" #: f.widgets.cc:333 msgid "Generate an inward spiraling recursive image" msgstr "Nach innen gewundenes rekursives Bild erzeugen" #: f.widgets.cc:336 msgid "Combine bright/dark images for better detail" msgstr "Hell-/Dunkel-Bilder kombinieren zur Detailverbesserung" #: f.widgets.cc:337 msgid "Combine near/far focus images for deeper focus" msgstr "Bilder mit Nah-/Fern-Fokus kombinieren für mehr Schärfentiefe" #: f.widgets.cc:338 msgid "Combine images to erase passing people, etc." msgstr "Bilder kombinieren zur Entfernung vorbeigehender Leute usw." #: f.widgets.cc:339 msgid "Combine noisy images into a low-noise image" msgstr "Rauschige Bilder zu einem Bild mit weniger Rauschen kombinieren" #: f.widgets.cc:340 msgid "Combine image layers, mouse select and expose" msgstr "Bildebene kombinieren, mit Maus auswählen und dann anzeigen" #: f.widgets.cc:341 msgid "Compare two images separated by sliding boundary" msgstr "Zwei Bilder vergleichen, getrennt durch verschiebbare Grenze" #: f.widgets.cc:342 msgid "Show differences between two images" msgstr "Unterschiede zwischen zwei Bilder zeigen" #: f.widgets.cc:343 msgid "Combine images into a panorama" msgstr "Bilder zu einem Panorama-Bild kombinieren" #: f.widgets.cc:344 msgid "Combine images into a vertical panorama" msgstr "Bilder zu einem Vertikal-Panorama kombinieren" #: f.widgets.cc:345 msgid "Combine images into a panorama (panorama tools)" msgstr "Bilder zu einem Panorama-Bild kombinieren (panorama tools)" #: f.widgets.cc:346 msgid "Combine images into a montage of images" msgstr "Bilder zu einer Bildmontage kombinieren" #: f.widgets.cc:347 msgid "Arrange images and text in a layout (montage)" msgstr "Bilder und Texte auf Lageplan ordnen (Montage)" #: f.widgets.cc:350 msgid "Rename/convert/resize/move multiple files" msgstr "Bilddateien umbenennen/konvertieren/skalieren/verschieben" #: f.widgets.cc:351 msgid "Upright multiple rotated image files" msgstr "Mehrere gedrehte Bilddateien aufrichten" #: f.widgets.cc:352 msgid "Delete or Trash multiple files" msgstr "Mehrere Dateien löschen oder in Papierkorb" #: f.widgets.cc:353 msgid "Convert camera RAW files to tiff/png/jpeg" msgstr "Kamera RAW-Dateien zu tiff/png/jpeg umwandeln" #: f.widgets.cc:354 msgid "Burn selected image files to DVD/BlueRay disc" msgstr "Gewählte Bilddateien auf DVD/BlueRay Disk brennen" #: f.widgets.cc:356 msgid "Export selected image files to a folder" msgstr "Ausgewählte Bilddatei in ein Verzeichnis exportieren" #: f.widgets.cc:357 msgid "Add/remove tags for multiple images" msgstr "Tags in mehreren Bilder zuweisen/entfernen" #: f.widgets.cc:358 msgid "Convert tag names for all images" msgstr "Tagnamen für alle Bilder konvertieren" #: f.widgets.cc:359 msgid "change or shift photo dates/times" msgstr "Foto Datum/Zeit ändern oder verschieben" #: f.widgets.cc:360 msgid "Add/change/delete metadata for multiple images" msgstr "Metadaten zuweisen/ändern/löschen für mehrere Bilder" #: f.widgets.cc:361 msgid "Report metadata for multiple images" msgstr "Metadaten für mehrere Bilder auflisten" #: f.widgets.cc:362 msgid "Add/revise geotags for multiple images" msgstr "Geotags zuweisen/ändern für mehrere Bilder" #: f.widgets.cc:363 msgid "Build a custom script with multiple edit functions" msgstr "Skriptdatei mit mehreren Bearbeitungsfunktionen bauen" #: f.widgets.cc:364 msgid "Run custom script to edit the current image file" msgstr "Aktuelle Bilddatei mit einer Skriptdatei bearbeiten" #: f.widgets.cc:365 msgid "Run custom script to edit a batch of image files" msgstr "Mehrere Bilddateien mit einer Skriptdatei bearbeiten" #: f.widgets.cc:368 msgid "Index new files and make thumbnails" msgstr "Neue Dateien indizieren und Thumbnails erstellen" #: f.widgets.cc:369 msgid "Quick incremental index update" msgstr "Schnelle inkrementelle Aktualisierung des Index" #: f.widgets.cc:370 msgid "Move Fotoxx home folder" msgstr "Fotoxx Heim-Verzeichnis verschieben" #: f.widgets.cc:371 msgid "User preferences and settings" msgstr "Benutzervorzüge und Festlegungen" #: f.widgets.cc:373 msgid "Show RGB brightness distribution" msgstr "RGB-Helligkeitsverteilung anzeigen" #: f.widgets.cc:374 msgid "Magnify image around the mouse position" msgstr "Bild um Maus-Position herum vergrößern" #: f.widgets.cc:375 msgid "Search all image files and report duplicates" msgstr "Alle Bilddateien durchsuchen und Duplikate anzeigen" #: f.widgets.cc:376 msgid "Show RGB colors at mouse click" msgstr "RGB-Werte auf Maus-Klick zeigen" #: f.widgets.cc:377 msgid "Convert to another color profile" msgstr "In einen anderen Farbraum umwandeln" #: f.widgets.cc:378 msgid "Calibrate printer colors" msgstr "Druckerfarben kalibrieren" #: f.widgets.cc:379 msgid "Show or revise grid lines" msgstr "Gitterlinien zeigen oder ändern" #: f.widgets.cc:380 msgid "Change color of foreground lines" msgstr "Vordergrund Linienfarbe ändern" #: f.widgets.cc:381 msgid "Highlight darkest and brightest pixels" msgstr "Dunkelste und hellste Pixel hervorheben" #: f.widgets.cc:382 msgid "map raw pixel bias (camera sensor, vignette)" msgstr "Raw Pixel Bias abbilden (Kamera Sensor, Vignette)" #: f.widgets.cc:383 msgid "map raw dead pixels (camera sensor)" msgstr "Raw totte Pixel auflisten (Kamera Sensor)" #: f.widgets.cc:384 msgid "Chart to adjust monitor color" msgstr "Grafik um Monitor-Farben einzustellen" #: f.widgets.cc:385 msgid "Chart to adjust monitor gamma" msgstr "Grafik um Monitor-Gamma einzustellen" #: f.widgets.cc:386 msgid "Change the GUI language" msgstr "GUI Sprache wechseln" #: f.widgets.cc:387 msgid "Report missing translations" msgstr "Fehlende Übersetzungen auflisten" #: f.widgets.cc:388 msgid "Anonymous usage statistics" msgstr "Anonyme Nutzungsstatistik" #: f.widgets.cc:389 msgid "Memory and CPU (to terminal/logfile)" msgstr "Speicher und CPU (an Terminal/Logdatei)" #: f.widgets.cc:390 msgid "List files included in appimage container" msgstr "In Appimage-Container enthaltene Dateien auflisten" #: f.widgets.cc:391 msgid "test crash report with source line numbers" msgstr "Absturzbericht mit Quellcode-Zeilennummer" #: f.widgets.cc:394 msgid "Read the user guide" msgstr "Benutzeranleitung lesen" #: f.widgets.cc:395 msgid "Recent user guide changes" msgstr "Neueste Änderungen in Benutzeranleitung" #: f.widgets.cc:396 msgid "Overview of all edit functions" msgstr "Übersicht aller Bearbeitungsfunktionen" #: f.widgets.cc:397 msgid "List updates by Fotoxx version" msgstr "Änderungen nach Fotoxx-Version auflisten" #: f.widgets.cc:398 msgid "View the log file and error messages" msgstr "Logdatei und Fehlermeldungen zeigen" #: f.widgets.cc:399 msgid "Fotoxx Man Page - summary of capabilities" msgstr "Fotoxx Man Page - Zusammenfassung der Funktionen" #: f.widgets.cc:400 msgid "List command line parameters" msgstr "Befehlsparameter auflisten" #: f.widgets.cc:401 msgid "How to do Fotoxx translations" msgstr "Wie eine Fotoxx Übersetzung erstellt wird" #: f.widgets.cc:402 msgid "Show the Fotoxx web page" msgstr "Fotoxx Webseite zeigen" #: f.widgets.cc:403 msgid "Fotoxx license - terms of use" msgstr "Fotoxx Lizenz - Nutzungsbedingungen" #: f.widgets.cc:404 msgid "Fotoxx privacy policy" msgstr "Fotoxx Datenschutzerklärung" #: f.widgets.cc:405 msgid "Version, contact, credits" msgstr "Version, Kontakt, Mitwirkende" #: f.widgets.cc:425 msgid "File View F" msgstr "Dateiansicht F" #: f.widgets.cc:426 msgid "New Session" msgstr "Neue Instanz" #: f.widgets.cc:427 f.widgets.cc:453 msgid "Source Folder" msgstr "Quellverzeichnis" #: f.widgets.cc:428 msgid "Cycle 2" msgstr "2er Wechsel" #: f.widgets.cc:429 msgid "Cycle 3" msgstr "3er Wechsel" #: f.widgets.cc:430 msgid "View 360° Pano" msgstr "360° Panoroma" #: f.widgets.cc:431 f.widgets.cc:820 fotoxx.h:1414 msgid "Rename" msgstr "Umbenennen" #: f.widgets.cc:433 msgid "Blank Image" msgstr "Leerbild" #: f.widgets.cc:435 f.widgets.cc:822 msgid "Copy/Move" msgstr "Kopieren/verschieben" #: f.widgets.cc:436 f.widgets.cc:823 msgid "Copy to Desktop" msgstr "Zur Schreibtisch kopieren" #: f.widgets.cc:437 f.widgets.cc:824 msgid "Copy to Clipboard" msgstr "In Zwischenablage kopieren" #: f.widgets.cc:438 msgid "Set Wallpaper" msgstr "Hintergrundbild festlegen" #: f.widgets.cc:439 f.widgets.cc:839 msgid "Show on Map" msgstr "Auf Karte zeigen" #: f.widgets.cc:440 f.widgets.cc:840 msgid "Delete/Trash" msgstr "Löschen/Papierkorb" #: f.widgets.cc:441 msgid "Print" msgstr "Drucken" #: f.widgets.cc:442 msgid "Print Calibrated" msgstr "Kalibriert drucken" #: f.widgets.cc:443 fotoxx.h:1407 msgid "Quit" msgstr "Beenden" #: f.widgets.cc:446 msgid "Gallery View G" msgstr "Galerieansicht G" #: f.widgets.cc:447 msgid "Meta View" msgstr "Meta-Ansicht" #: f.widgets.cc:448 msgid "List View" msgstr "Listenansicht" #: f.widgets.cc:449 msgid "Recent" msgstr "kürzlich gesehene" #: f.widgets.cc:450 msgid "Newest" msgstr "Neueste" #: f.widgets.cc:451 msgid "GoTo First" msgstr "zum Anfang" #: f.widgets.cc:452 msgid "GoTo Last" msgstr "ans Ende" #: f.widgets.cc:454 msgid "Sort Gallery" msgstr "Galerie sortieren" #: f.widgets.cc:455 msgid "All Folders" msgstr "Alle Verzeichnisse" #: f.widgets.cc:456 fotoxx.h:1425 msgid "Select Files" msgstr "Dateien auswählen" #: f.widgets.cc:459 msgid "Update Albums" msgstr "Alben aktualisieren" #: f.widgets.cc:461 msgid "Gallery to Album" msgstr "Galerie zum Album" #: f.widgets.cc:465 msgid "Map View M" msgstr "Kartenansicht M" #: f.widgets.cc:466 msgid "Net Map" msgstr "Internet-Karte" #: f.widgets.cc:467 msgid "Net Source" msgstr "Net Quelle" #: f.widgets.cc:468 msgid "Net Locs" msgstr "Netzorte" #: f.widgets.cc:469 msgid "File Map" msgstr "Kartendatei" #: f.widgets.cc:470 msgid "Choose Map" msgstr "Karte wählen" #: f.widgets.cc:471 msgid "Markers" msgstr "Markierungen" #: f.widgets.cc:474 f.widgets.cc:816 msgid "View Meta" msgstr "Meta ansehen" #: f.widgets.cc:475 f.widgets.cc:817 msgid "View All Meta" msgstr "Alle Meta ansehen" #: f.widgets.cc:476 f.widgets.cc:818 msgid "Edit Meta" msgstr "Meta bearbeiten" #: f.widgets.cc:478 f.widgets.cc:819 msgid "Edit Any Meta" msgstr "Beliebige Meta bearbeiten" #: f.widgets.cc:479 msgid "Delete Meta" msgstr "Meta löschen" #: f.widgets.cc:480 msgid "Captions" msgstr "Titel" #: f.widgets.cc:481 msgid "Places/Dates" msgstr "Orte/Daten" #: f.widgets.cc:482 msgid "Timeline" msgstr "Zeitablauf" #: f.widgets.cc:483 fotoxx.h:1422 msgid "Search" msgstr "Durchsuchen" #: f.widgets.cc:486 fotoxx.h:1424 msgid "Select" msgstr "Auswählen" #: f.widgets.cc:488 msgid "Find Gap" msgstr "Lücke finden" #: f.widgets.cc:489 fotoxx.h:1426 msgid "Show" msgstr "Zeigen" #: f.widgets.cc:490 fotoxx.h:1367 msgid "Hide" msgstr "Ausblenden" #: f.widgets.cc:491 fotoxx.h:1351 msgid "Enable" msgstr "Aktivieren" #: f.widgets.cc:492 fotoxx.h:1346 msgid "Disable" msgstr "Ausschalten" #: f.widgets.cc:494 fotoxx.h:1333 msgid "Clear" msgstr "Aufräumen" #: f.widgets.cc:495 fotoxx.h:1340 msgid "Copy" msgstr "Kopieren" #: f.widgets.cc:496 fotoxx.h:1399 msgid "Paste" msgstr "Einfügen" #: f.widgets.cc:497 fotoxx.h:1376 msgid "Load" msgstr "Laden" #: f.widgets.cc:498 f.widgets.cc:654 fotoxx.h:1421 zfuncs.cc:12499 msgid "Save" msgstr "Speichern" #: f.widgets.cc:507 msgid "Markup" msgstr "Markup" #: f.widgets.cc:508 msgid "Paint Image" msgstr "Bild bemalen" #: f.widgets.cc:509 msgid "Copy Pixels 1" msgstr "Pixel kopieren 1" #: f.widgets.cc:510 msgid "Copy Pixels 2" msgstr "Pixel kopieren 2" #: f.widgets.cc:513 msgid "Plugins" msgstr "Plugins" #: f.widgets.cc:514 f.widgets.cc:838 msgid "Raw Therapee" msgstr "Raw Therapee" #: f.widgets.cc:517 msgid "Voodoo 1" msgstr "Voodoo 1" #: f.widgets.cc:518 msgid "Voodoo 2" msgstr "Voodoo 2" #: f.widgets.cc:519 f.widgets.cc:834 msgid "Brite Dist" msgstr "Helligk. Vert." #: f.widgets.cc:520 f.widgets.cc:836 msgid "Gradients" msgstr "Gradienten" #: f.widgets.cc:521 f.widgets.cc:835 fotoxx.h:1360 msgid "Flatten" msgstr "Abflachen" #: f.widgets.cc:522 msgid "Global Retx" msgstr "Globaler Retx" #: f.widgets.cc:523 msgid "Zonal Retx" msgstr "Zonaler Retx" #: f.widgets.cc:526 msgid "Denoise" msgstr "Entrauschen" #: f.widgets.cc:527 msgid "Red Eyes" msgstr "Rote Augen" #: f.widgets.cc:528 msgid "Match Colors" msgstr "Farben anpassen" #: f.widgets.cc:530 msgid "Chromatic1" msgstr "Chroma 1" #: f.widgets.cc:531 msgid "Chromatic2" msgstr "Chroma 2" #: f.widgets.cc:536 msgid "Sketch" msgstr "Skizzieren" #: f.widgets.cc:537 msgid "Cartoon" msgstr "Cartoon" #: f.widgets.cc:541 msgid "Dither" msgstr "Rasterung" #: f.widgets.cc:543 msgid "Texture" msgstr "Textur" #: f.widgets.cc:545 msgid "Mosaic" msgstr "Mosaik" #: f.widgets.cc:547 msgid "Color Depth" msgstr "Farbtiefe" #: f.widgets.cc:550 msgid "Brite Ramp" msgstr "Helligk.-Rampe" #: f.widgets.cc:551 msgid "Paint Transp" msgstr "Transparenz erhöhen" #: f.widgets.cc:552 msgid "Mirror" msgstr "Spiegeln" #: f.widgets.cc:557 msgid "Perspective" msgstr "Prespektive" #: f.widgets.cc:563 msgid "Flatten Book" msgstr "Buch ausrichten" #: f.widgets.cc:567 msgid "Sphere" msgstr "Kugel" #: f.widgets.cc:568 msgid "Stretch" msgstr "Strecken" #: f.widgets.cc:570 msgid "Tiny Planet" msgstr "360° Miniaturplanet" #: f.widgets.cc:571 msgid "Escher Spiral" msgstr "Escher-Spirale" #: f.widgets.cc:576 msgid "Stack/Paint" msgstr "Stapeln/Malen" #: f.widgets.cc:577 msgid "Stack/Noise" msgstr "Stapeln/Rauschen" #: f.widgets.cc:578 msgid "Stack/Layer" msgstr "Stapeln/Ebene" #: f.widgets.cc:579 msgid "Stack/Slider" msgstr "Stapel/Schieber" #: f.widgets.cc:580 msgid "Image Diffs" msgstr "Bildunterschiede" #: f.widgets.cc:581 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:582 msgid "V. Panorama" msgstr "V. Panorama" #: f.widgets.cc:583 msgid "PT Panorama" msgstr "PT Panorama" #: f.widgets.cc:584 msgid "Mashup" msgstr "Mashup" #: f.widgets.cc:585 msgid "Montage" msgstr "Montage" #: f.widgets.cc:591 msgid "Batch RAW" msgstr "Stapel RAW" #: f.widgets.cc:592 msgid "Burn DVD/BlueRay" msgstr "DVD/BlueRay brennen" #: f.widgets.cc:593 msgid "Export File List" msgstr "Dateiliste exportieren" #: f.widgets.cc:595 msgid "Batch Tags" msgstr "Tags Stapelbetrieb" #: f.widgets.cc:597 msgid "Batch Photo Date" msgstr "Stapelbetrieb Fotodatum" #: f.widgets.cc:598 msgid "Batch Change Meta" msgstr "Stapelbetrieb Meta" #: f.widgets.cc:599 msgid "Batch Report Meta" msgstr "Stapelbetrieb Meta auflisten" #: f.widgets.cc:601 msgid "Edit Script" msgstr "Skript bearbeiten" #: f.widgets.cc:602 msgid "Run Script" msgstr "Skript ausführen" #: f.widgets.cc:606 msgid "Index Files" msgstr "Bilddateien indizieren" #: f.widgets.cc:607 msgid "Quick Index" msgstr "Schneller Index" #: f.widgets.cc:608 msgid "Move Fotoxx Home" msgstr "Fotoxx-Verzeichnis verschieben" #: f.widgets.cc:609 msgid "Preferences" msgstr "Einstellungen" #: f.widgets.cc:610 msgid "KB Shortcuts" msgstr "Schnelltasten" #: f.widgets.cc:611 msgid "RGB Distribution" msgstr "RGB-Verteilung" #: f.widgets.cc:613 msgid "Find Duplicates" msgstr "Duplikate finden" #: f.widgets.cc:615 msgid "Color Profile" msgstr "Farbraum" #: f.widgets.cc:619 msgid "Dark/Bright Pixels" msgstr "Dunkle/helle Pixel" #: f.widgets.cc:620 msgid "Map Pixel Bias" msgstr "Pixel Bias abbilden" #: f.widgets.cc:621 msgid "Map Dead Pixels" msgstr "Tote Pixel abbilden" #: f.widgets.cc:622 msgid "Monitor Color" msgstr "Monitor Farbgrafik" #: f.widgets.cc:623 msgid "Monitor Gamma" msgstr "Bildschirm-Gamma" #: f.widgets.cc:624 msgid "Change Language" msgstr "Sprache wechseln" #: f.widgets.cc:625 msgid "Missing Translations" msgstr "Fehlende Übersetzungen" #: f.widgets.cc:626 zfuncs.cc:5885 msgid "Phone Home" msgstr "Zuhause anrufen" #: f.widgets.cc:628 msgid "Show Resources" msgstr "Ressourcen anzeigen" #: f.widgets.cc:629 msgid "Appimage Files" msgstr "Appimage Dateien" #: f.widgets.cc:630 msgid "Zappcrash Test" msgstr "Zapp Absturztest" #: f.widgets.cc:649 msgid "Gallery" msgstr "Bildergalerie" #: f.widgets.cc:650 msgid "Maps" msgstr "Karten" #: f.widgets.cc:651 f.widgets.cc:1145 msgid "Favorites" msgstr "Favoriten" #: f.widgets.cc:652 msgid "Prev/Next" msgstr "Voriges/Nächstes" #: f.widgets.cc:653 msgid "Zoom/±" msgstr "Zoom/±" #: f.widgets.cc:655 msgid "Meta" msgstr "Meta" #: f.widgets.cc:656 msgid "Areas" msgstr "Flächen" #: f.widgets.cc:657 msgid "Undo/Redo" msgstr "Zurück/Wiederholen" #: f.widgets.cc:658 fotoxx.h:1350 msgid "Edit" msgstr "Bearbeiten" #: f.widgets.cc:659 msgid "Enhance" msgstr "Verbessern" #: f.widgets.cc:660 msgid "Effects" msgstr "Effekte" #: f.widgets.cc:662 msgid "Combine" msgstr "Verbund" #: f.widgets.cc:663 msgid "Process" msgstr "Prozess" #: f.widgets.cc:664 msgid "Tools" msgstr "Werkzeuge" #: f.widgets.cc:815 msgid "Popup Image" msgstr "Pop-up Bild" #: f.widgets.cc:825 msgid "Add Selected Files Here" msgstr "Ausgewählte Dateien hier anfügen" #: f.widgets.cc:826 msgid "Add Current File Here" msgstr "Aktuelle Datei hier anfügen" #: f.widgets.cc:827 msgid "Remove from Album" msgstr "Von Album entfernen" #: f.widgets.cc:837 msgid "Select Area" msgstr "Fläche auswählen" #: f.widgets.cc:1105 f.widgets.cc:1124 msgid "Fotoxx Image Locations" msgstr "Fotoxx Bilder-Orte" #: f.widgets.cc:1173 #, c-format msgid "invalid menu name: %s" msgstr "Ungültiger Menüname: %s" #: fotoxx.cc:257 msgid "read+write" msgstr "lesen+schreiben" #: fotoxx.cc:258 msgid "read only" msgstr "nur lesen" #: fotoxx.cc:259 msgid "no access" msgstr "kein Zugriff" #: fotoxx.cc:465 msgid "Please install missing programs:" msgstr "Bitte fehlende Programme installieren:" #: fotoxx.cc:594 msgid "Index aborted" msgstr "Index abgebrochen" #: fotoxx.cc:772 msgid " Defer image file indexing:" msgstr " Indizierung von Bilddateien aufschieben:" #: fotoxx.cc:773 msgid "" " • Fotoxx will start immediately \n" " • View and edit image files will work normally \n" " • Image search, batch and map functions will not work \n" " • Thumbnail galleries will be slow" msgstr "" " • Fotoxx startet sofort \n" " • Anzeigen und Bearbeiten von Bilddateien funktioniert normal \n" " • Bildsuche, Stapel und Kartenfunktionen funktionieren nicht \n" " • Thumbnail-Galerien werden langsam" #: fotoxx.cc:778 msgid " Index image files now:" msgstr " Bilddateien jetzt indizieren:" #: fotoxx.cc:779 msgid "" " • Initial indexing may need considerable time \n" " • Subsequent startups will be fast \n" " • Full functionality will be available \n" " • Thumbnail galleries will be fast" msgstr "" " • Die erstmalige Indizierung kann einige Zeit dauern \n" " • Nachfolgende Starts werden danach schnell \n" " • Die volle Funktionalität wird verfügbar \n" " • Auch Thumbnail-Galerien werden schnell" #: fotoxx.cc:784 msgid "" " Indexing time depends on the number of image files and the \n" " speed of your computer. This can be a few hundred to a few \n" " thousand per minute. After indexing is done, startup time \n" " should be quite fast. You can change index options later, \n" " using these menus: Tools > Index and Tools > Preferences. " msgstr "" " Die Indizierungszeit ist abhängig von der Anzahl der Bilddateien und der \n" " Geschwindigkeit Ihres Computers. Einige hundert bis einige tausend \n" " Dateien pro Minute sind möglich. Nachdem die Indizierung abgeschlossen " "ist, \n" " sollte die Startzeit recht schnell sein. Sie können die Index Optionen " "später \n" " ändern mit diesen Menüs: Werkzeuge > Index und Werkzeuge > Einstellungen." #: fotoxx.cc:791 msgid "" "Main memory is too small to run Fotoxx. \n" "You can try anyway if you wish. \n" " Continue?" msgstr "" " Der Hauptspeicher ist zu klein für Fotoxx. \n" " Sie können es trotzdem versuchen. \n" " Fortsetzen?" #: fotoxx.cc:826 msgid "Fotoxx First Startup" msgstr "Fotoxx Erststart" #: fotoxx.cc:981 msgid "(reduced)" msgstr "(verkleinert)" #: fotoxx.cc:982 msgid "area active" msgstr "Fläche aktiv" #: fotoxx.cc:983 msgid "dialog open" msgstr "Dialog aktiv" #: fotoxx.cc:984 msgid "blocked" msgstr "Gesperrt" #: fotoxx.cc:1040 msgid "edits" msgstr "Änderungen" #: fotoxx.cc:1958 msgid "Show Hidden" msgstr "Versteckte zeigen" #: fotoxx.cc:3168 msgid "Exceed 50 anchor points" msgstr "mehr als 50 Ankerpunkte" #: fotoxx.cc:3397 msgid "load curve from a file" msgstr "Kurvendatei öffnen" #: fotoxx.cc:3464 msgid "curve file is invalid" msgstr "Kurvendatei ist ungültig" #: fotoxx.cc:3478 msgid "save curve to a file" msgstr "Kurvendatei speichern" #: fotoxx.cc:3549 #, c-format msgid "" "File cannot be edited \n" " %s" msgstr "" "Datei kann nicht bearbeitet werden \n" " %s" #: fotoxx.cc:3562 msgid "Too many edits, please save image" msgstr "Zu viele Änderungen, bitte Bild speichern" #: fotoxx.cc:3567 msgid "this function cannot be scripted" msgstr "Funktion gehört nicht in eine Skriptdatei" #: fotoxx.cc:3584 msgid "" "Select area will be ignored. \n" "Continue?" msgstr "Ausgewählter Bereich wird ignoriertFortsetzen?" #: fotoxx.cc:3590 msgid "" "Select area not active.\n" "Continue?" msgstr "" "Auswahl-Fläche nicht aktiviert.\n" "Fortfahren?" #: fotoxx.cc:3900 msgid "file data does not fit dialog" msgstr "Datei passt nicht zum Dialog" #: fotoxx.cc:3910 msgid "Save settings to a file" msgstr "Einstellungen in Datei speichern" #: fotoxx.cc:4297 msgid "This action will discard changes to current image" msgstr "Dieses Vorgehen wird Änderungen im aktuellen Bild verwerfen" #: fotoxx.cc:4298 msgid "prior function still active" msgstr "Vorherige Funktion noch aktiv" #: fotoxx.cc:4299 fotoxx.h:1372 msgid "Keep" msgstr "Behalten" #: fotoxx.cc:4300 msgid "Discard" msgstr "Verwerfen" #: fotoxx.h:1315 msgid "Add" msgstr "Hinzufügen" #: fotoxx.h:1316 msgid "Add All" msgstr "Alle einfügen" #: fotoxx.h:1318 msgid "Amount" msgstr "Menge" #: fotoxx.h:1319 msgid "Angle" msgstr "Winkel" #: fotoxx.h:1320 zfuncs.cc:7936 msgid "Apply" msgstr "Anwenden" #: fotoxx.h:1321 msgid "Auto" msgstr "Auto" #: fotoxx.h:1322 msgid "Black" msgstr "Schwarz" #: fotoxx.h:1323 msgid "Blend Width" msgstr "Mischbreite" #: fotoxx.h:1324 msgid "Blue" msgstr "Blau" #: fotoxx.h:1325 zfuncs.cc:12913 msgid "Bottom" msgstr "Unten" #: fotoxx.h:1327 zfuncs.cc:7950 msgid "Browse" msgstr "Durchsuchen" #: fotoxx.h:1328 msgid "Calculate" msgstr "Rechnen" #: fotoxx.h:1329 zfuncs.cc:7936 zfuncs.cc:12528 zfuncs.cc:12680 msgid "Cancel" msgstr "Abbrechen" #: fotoxx.h:1330 msgid "Center" msgstr "Mittig" #: fotoxx.h:1331 msgid "Change" msgstr "Ändern" #: fotoxx.h:1332 msgid "Choose" msgstr "Wählen" #: fotoxx.h:1334 msgid "click thumbnail to select file" msgstr "Thumbnail anklicken, um Datei auszuwählen" #: fotoxx.h:1335 msgid "Close" msgstr "Beenden" #: fotoxx.h:1336 msgid "Color" msgstr "Farbe" #: fotoxx.h:1337 msgid "COMPLETED" msgstr "FERTIG" #: fotoxx.h:1338 msgid "continue" msgstr "fortfahren" #: fotoxx.h:1341 msgid "Create" msgstr "Erstellen" #: fotoxx.h:1342 msgid "Curve File:" msgstr "Kurven-Datei:" #: fotoxx.h:1343 msgid "Cut" msgstr "Ausschneiden" #: fotoxx.h:1344 msgid "Deband" msgstr "Entstreifen" #: fotoxx.h:1345 zfuncs.cc:7936 msgid "Delete" msgstr "Löschen" #: fotoxx.h:1347 msgid "Display" msgstr "Zeigen" #: fotoxx.h:1348 msgid "Done" msgstr "Fertig" #: fotoxx.h:1349 msgid "edge" msgstr "Rand" #: fotoxx.h:1352 msgid "Erase" msgstr "Löschen" #: fotoxx.h:1353 msgid "Fetch" msgstr "Holen" #: fotoxx.h:1354 msgid "output file already exists" msgstr "Ausgabedatei gibt es schon" #: fotoxx.h:1355 msgid "file not found" msgstr "Datei nicht gefunden" #: fotoxx.h:1356 #, c-format msgid "file not found: %s" msgstr "Datei nicht gefunden: %s" #: fotoxx.h:1357 #, c-format msgid "%d image files selected" msgstr "%d Bilddateien ausgewählt" #: fotoxx.h:1358 msgid "Find" msgstr "Suchen" #: fotoxx.h:1359 msgid "Finish" msgstr "Fertigstellen" #: fotoxx.h:1361 msgid "Font" msgstr "Schrift" #: fotoxx.h:1362 #, c-format msgid "gallery truncated to %d images" msgstr "Galerie auf %d Bilder verringert" #: fotoxx.h:1363 msgid "Green" msgstr "Grün" #: fotoxx.h:1364 msgid "Grid" msgstr "Gitter" #: fotoxx.h:1365 zfuncs.cc:12943 msgid "Height" msgstr "Höhe" #: fotoxx.h:1368 zfuncs.cc:12935 msgid "Image" msgstr "Bild" #: fotoxx.h:1369 msgid "Images" msgstr "Bilder" #: fotoxx.h:1370 msgid "Insert" msgstr "Einfügen" #: fotoxx.h:1373 zfuncs.cc:12917 msgid "Left" msgstr "Links" #: fotoxx.h:1374 msgid "Length" msgstr "Länge" #: fotoxx.h:1375 msgid "limit" msgstr "Grenzwert" #: fotoxx.h:1377 msgid "Magnify" msgstr "Vergrößern" #: fotoxx.h:1378 msgid "Make" msgstr "Erstellen" #: fotoxx.h:1379 msgid "Map" msgstr "Karte" #: fotoxx.h:1380 msgid "Match Level:" msgstr "Grad Übereinstimmung:" #: fotoxx.h:1381 msgid "Max" msgstr "Max" #: fotoxx.h:1382 msgid "Measure" msgstr "Messen" #: fotoxx.h:1383 msgid "mouse radius" msgstr "Mausradius" #: fotoxx.h:1384 msgid "Negative" msgstr "Negativ" #: fotoxx.h:1385 msgid "New" msgstr "Neu" #: fotoxx.h:1386 msgid "Next" msgstr "Nächstes" #: fotoxx.h:1387 zfuncs.cc:11875 msgid "No" msgstr "Nein" #: fotoxx.h:1388 msgid "no write permission" msgstr "Keine Schreibberechtigung" #: fotoxx.h:1389 msgid "no images" msgstr "Keine Bilder" #: fotoxx.h:1390 msgid "image index disabled" msgstr "Bildindex deaktiviert" #: fotoxx.h:1391 msgid "no image files selected" msgstr "keine Bilddateien ausgewählt" #: fotoxx.h:1392 msgid "None" msgstr "Keine" #: fotoxx.h:1393 msgid "no selection" msgstr "Keine Auswahl" #: fotoxx.h:1394 msgid "image index not updated" msgstr "Bild-Index nicht aktualisiert" #: fotoxx.h:1395 msgid "opacity center" msgstr "Deckkraft Mitte" #: fotoxx.h:1396 msgid "opacity edge" msgstr "Deckkraft Rand" #: fotoxx.h:1398 msgid "Paint Radius" msgstr "Mal-Radius" #: fotoxx.h:1400 msgid "Pause" msgstr "Unterbrechen" #: fotoxx.h:1401 msgid "Percent" msgstr "Prozent" #: fotoxx.h:1403 msgid "Presets" msgstr "Voreinstellungen" #: fotoxx.h:1404 msgid "Prev" msgstr "Vorig" #: fotoxx.h:1405 msgid "use previous input" msgstr "Vorherige Eingabe verwenden" #: fotoxx.h:1406 msgid "Proceed" msgstr "Weiter" #: fotoxx.h:1409 msgid "range" msgstr "Wertebereich" #: fotoxx.h:1410 msgid "Red" msgstr "Rot" #: fotoxx.h:1411 msgid "Redo" msgstr "erneut" #: fotoxx.h:1412 msgid "Reduce" msgstr "Vermindern" #: fotoxx.h:1413 msgid "Remove" msgstr "Entfernen" #: fotoxx.h:1415 msgid "Replace" msgstr "Ersetzen" #: fotoxx.h:1416 msgid "Reserved" msgstr "Reserviert" #: fotoxx.h:1417 msgid "Reset" msgstr "Zurücksetzen" #: fotoxx.h:1418 zfuncs.cc:12921 msgid "Right" msgstr "Rechts" #: fotoxx.h:1419 msgid "Rotate" msgstr "Drehen" #: fotoxx.h:1420 msgid "Run" msgstr "ausführen" #: fotoxx.h:1423 msgid "Seconds" msgstr "Sekunden" #: fotoxx.h:1427 msgid "Size" msgstr "Größe" #: fotoxx.h:1428 msgid "Start" msgstr "Starten" #: fotoxx.h:1429 msgid "Stop" msgstr "Stop" #: fotoxx.h:1430 msgid "Strength" msgstr "Stärke" #: fotoxx.h:1431 msgid "the area is not finished" msgstr "Die Fläche ist nicht vollständig" #: fotoxx.h:1432 msgid "Threshold" msgstr "Schwelle" #: fotoxx.h:1433 zfuncs.cc:12909 msgid "Top" msgstr "Oben" #: fotoxx.h:1434 msgid "Transparency" msgstr "Transparenz" #: fotoxx.h:1435 msgid "Trash" msgstr "Papierkorb" #: fotoxx.h:1436 msgid "Trim" msgstr "Schneiden" #: fotoxx.h:1437 msgid "Undo All" msgstr "Alles rückgängig" #: fotoxx.h:1438 msgid "Undo Last" msgstr "Letztes rückgängig" #: fotoxx.h:1439 msgid "Undo" msgstr "Erneut" #: fotoxx.h:1440 msgid "Unfinish" msgstr "Unfertigstellen" #: fotoxx.h:1441 msgid "Update" msgstr "Aktualisieren" #: fotoxx.h:1442 msgid "View" msgstr "Ansicht" #: fotoxx.h:1443 msgid "Web" msgstr "Web" #: fotoxx.h:1444 msgid "White" msgstr "Weiß" #: fotoxx.h:1445 zfuncs.cc:12939 msgid "Width" msgstr "Breite" #: fotoxx.h:1446 msgid "x-offset" msgstr "x-Verschiebung" #: fotoxx.h:1447 msgid "y-offset" msgstr "y-Verschiebung" #: fotoxx.h:1448 zfuncs.cc:11875 msgid "Yes" msgstr "Ja" #: zfuncs.cc:1836 #, c-format msgid "" "create folder? \n" " %s" msgstr "" "Verzeichnis erstellen? \n" " %s" #: zfuncs.cc:5880 msgid "" "If you permit, a message is occasionally \n" "sent to the web host for usage statistics. \n" "Nothing is retained that can be associated \n" "with a person or computer or location." msgstr "" "Wenn Sie erlauben, wird gelegentlich eine Nachricht \n" "mit Nutzungsstatistiken an den Webhost gesendet. \n" "Es wird nichts festgehalten, das einer Person, \n" "einem Computer oder Ort zugeordnet werden kann." #: zfuncs.cc:6743 #, c-format msgid "cannot open file %s" msgstr "Kann Datei nicht öffnen %s" #: zfuncs.cc:6766 msgid "save text to file" msgstr "Text in Datei speichern" #: zfuncs.cc:7936 msgid "edit menu entry" msgstr "Menüeintrag bearbeiten" #: zfuncs.cc:7940 msgid "menu text" msgstr "Menü-Text" #: zfuncs.cc:7941 msgid "menu func" msgstr "Menü-Funktion" #: zfuncs.cc:7942 msgid "menu icon" msgstr "Menü-Icon" #: zfuncs.cc:7943 msgid "icon size" msgstr "Icon-Größe" #: zfuncs.cc:7946 msgid "Bold" msgstr "Fett" #: zfuncs.cc:7953 msgid "close window" msgstr "Fenster schließen" #: zfuncs.cc:8000 msgid "select icon" msgstr "Icon auswählen" #: zfuncs.cc:12000 zfuncs.cc:12896 msgid "cancel" msgstr "Abbrechen" #: zfuncs.cc:12489 msgid "choose file" msgstr "Datei wählen" #: zfuncs.cc:12494 msgid "choose files" msgstr "Dateien wählen" #: zfuncs.cc:12505 msgid "choose folder" msgstr "Verzeichnis wählen" #: zfuncs.cc:12510 msgid "choose folders" msgstr "Verzeichnise wählen" #: zfuncs.cc:12515 msgid "create folder" msgstr "Ordner erstellen" #: zfuncs.cc:12522 msgid "hidden" msgstr "Versteckt" #: zfuncs.cc:12896 msgid "done" msgstr "fertig" #: zfuncs.cc:12926 msgid "image scale" msgstr "Bildmaßstab" #: zfuncs.cc:12928 msgid "percent" msgstr "Prozent" #~ msgid "Left/right click: smaller/larger image/thumbnails" #~ msgstr "Links/Rechtsklick: kleineres/größeres Bild/Thumbnails" #~ msgid "%% of scale" #~ msgstr "%% der Skala" fotoxx-20.08/locales/translate-es.po000066400000000000000000004524171362435004500174550ustar00rootroot00000000000000# Traducción al castellano para el paquete Fotoxx. # Copyright (C) 2018 THE home's COPYRIGHT HOLDER # Este archivo está distribuido bajo la misma licencia que el paquete principal. # Miguel Anxo Bouzada , 2010-2012 # Josep Antoni Miralles Puignau 2012-2019 # msgid "" msgstr "" "Project-Id-Version: home 2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-12-25 22:16+0100\n" "PO-Revision-Date: 2019-12-01 19:47+0200\n" "Last-Translator: Josep Antoni Miralles Puignau \n" "Language-Team: nLanguage: es\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: f.albums.cc:59 #, c-format msgid "max. album size exceeded: %d" msgstr "excedido máximo tamaño de álbum: %d" #: f.albums.cc:70 msgid "" "Right-click album thumbnail for edits: \n" " - add selected files at this position \n" " - remove this file from the album" msgstr "" "Clic derecho en la miniatura del álbum para editar: \n" " - agregar archivos seleccionados en esta posición \n" " - eliminar este archivo del álbum" #: f.albums.cc:73 msgid "Arrange files with thumbnail drag and drop" msgstr "Organizar archivos con miniaturas arrastrar y soltar" #: f.albums.cc:102 f.widgets.cc:458 msgid "Manage Albums" msgstr "Gestionar álbum" #: f.albums.cc:113 f.albums.cc:232 msgid "Create or replace an album" msgstr "Crear o reemplazar un álbum" #: f.albums.cc:115 f.albums.cc:381 msgid "Rename an album" msgstr "Renombrar un álbum" #: f.albums.cc:117 f.albums.cc:451 msgid "Delete an album" msgstr "Borrar un álbum" #: f.albums.cc:119 msgid "Files to add to an album" msgstr "Archivos para agregara un álbum" #: f.albums.cc:183 #, c-format msgid "%d files added to album %s" msgstr "%d archivos agregados al álbum %s" #: f.albums.cc:213 #, c-format msgid "max. album count exceeded: %d" msgstr "max. cantidad de álbumes superada: %d" #: f.albums.cc:234 f.albums.cc:270 f.albums.cc:1537 msgid "Album Name" msgstr "Nombre del álbum" #: f.albums.cc:240 msgid "make an initially empty album" msgstr "crear un álbum vacío" #: f.albums.cc:241 msgid "fill from pre-selected files" msgstr "llenar desde archivos preseleccionados" #: f.albums.cc:242 msgid "fill from current gallery" msgstr "llenar desde la galería actual" #: f.albums.cc:243 msgid "select initial files" msgstr "seleccionar archivos iniciales" #: f.albums.cc:284 msgid "enter an album name" msgstr "introducir un nombre de álbum" #: f.albums.cc:292 f.albums.cc:1569 #, c-format msgid "replace album %s ?" msgstr "¿reemplazar álbum %s ?" #: f.albums.cc:312 msgid "Use [Select] to add files to an empty album" msgstr "Use [Seleccionar] para agregar archivos a un álbum vacío" #: f.albums.cc:326 msgid "new album created" msgstr "nuevo álbum creado" #: f.albums.cc:342 f.meta.cc:7832 msgid "gallery is empty" msgstr "la galería está vacía" #: f.albums.cc:396 msgid "enter new album name" msgstr "introducir nuevo nombre de álbum" #: f.albums.cc:403 #, c-format msgid "invalid file name: %s" msgstr "nombrre de archivo no válido: %s" #: f.albums.cc:424 #, c-format msgid "album already exists: %s" msgstr "el álbum ya existe: %s" #: f.albums.cc:435 #, c-format msgid "" "%s \n" " renamed: %s" msgstr "" "%s \n" " renombrado: %s" #: f.albums.cc:464 #, c-format msgid "delete %s ?" msgstr "¿borrar %s ?" #: f.albums.cc:571 f.albums.cc:584 #, c-format msgid "%s removed with no replacement \n" msgstr "%s eliminado sin reemplazar \n" #: f.albums.cc:589 #, c-format msgid "%s replaced by ...%s \n" msgstr "%s reemplazado por ...%s \n" #: f.albums.cc:664 msgid "no selected files" msgstr "no hay archivos seleccionados" #: f.albums.cc:877 msgid "Replace Album File" msgstr "Reemplazar álbum" #: f.albums.cc:879 f.albums.cc:1004 msgid "Choose Albums" msgstr "Elegir álbumes" #: f.albums.cc:883 msgid "old file" msgstr "archivo antiguo" #: f.albums.cc:886 msgid "new file" msgstr "archivo nuevo" #: f.albums.cc:889 msgid "replace old" msgstr "reemplazar antiguo" #: f.albums.cc:890 msgid "add after old" msgstr "añadir después antiguo" #: f.albums.cc:936 msgid "no albums chosen" msgstr "no se han elegido álbumes" #: f.albums.cc:1117 f.widgets.cc:460 msgid "Album Mass Update" msgstr "Actualización masiva del álbum" #: f.albums.cc:1125 msgid "Process all album files:" msgstr "Procesar todos los archivos del álbum :" #: f.albums.cc:1128 msgid "Replace all with newest version only" msgstr "Reemplazar todo con la versión más reciente" #: f.albums.cc:1130 msgid "Replace all versions with newest version" msgstr "Reemplaza todas las versiones con la última versión" #: f.albums.cc:1132 msgid "Add newest version to existing versions" msgstr "Añadir la versión más reciente a las versiones existentes" #: f.albums.cc:1134 msgid "Replace all with original and all versions" msgstr "Reemplazar todo con original y todas las versiones." #: f.albums.cc:1136 msgid "Replace all with original + newest version" msgstr "Reemplazar todo con la versión original + nueva" #: f.albums.cc:1140 msgid "Process album files matching selected files:" msgstr "" "Procesar archivos de álbum que coincidan con los archivos seleccionados:" #: f.albums.cc:1143 msgid "Replace all with selected versions" msgstr "Reemplazar todo con versiones seleccionadas" #: f.albums.cc:1145 msgid "Replace all versions with selected versions" msgstr "Reemplazar todas las versiones con versiones seleccionadas" #: f.albums.cc:1147 msgid "Add selected versions to existing versions" msgstr "Agregar versiones seleccionadas a versiones existentes" #: f.albums.cc:1149 msgid "Replace all with original + selected versions" msgstr "Reemplazar todo con versiones originales + seleccionadas" #: f.albums.cc:1237 msgid "Select Albums" msgstr "Seleccionar álbumes" #: f.albums.cc:1296 #, c-format msgid "too many selected files: %d" msgstr "demasiados archivos seleccionados: %d" #: f.albums.cc:1535 msgid "Save Gallery as Album" msgstr "Guardar galería como álbum" #: f.albums.cc:1739 msgid "instant" msgstr "instantánea" #: f.albums.cc:1740 msgid "fade-in" msgstr "fundido" #: f.albums.cc:1741 msgid "roll-right" msgstr "desenrollar hacia la derecha" #: f.albums.cc:1742 msgid "roll-down" msgstr "desenrollar hacia abajo" #: f.albums.cc:1743 msgid "venetian" msgstr "persiana" #: f.albums.cc:1744 msgid "grate" msgstr "enrejado" #: f.albums.cc:1745 msgid "rectangle" msgstr "rectángulo" #: f.albums.cc:1746 msgid "implode" msgstr "implosión" #: f.albums.cc:1747 msgid "explode" msgstr "explosión" #: f.albums.cc:1748 msgid "radar" msgstr "radar" #: f.albums.cc:1749 msgid "Japan-fan" msgstr "Abanico japonés" #: f.albums.cc:1750 msgid "spiral" msgstr "espiral" #: f.albums.cc:1751 f.area.cc:140 msgid "ellipse" msgstr "elipse" #: f.albums.cc:1752 msgid "raindrops" msgstr "gotas de lluvia" #: f.albums.cc:1753 msgid "doubledoor" msgstr "doble puerta" #: f.albums.cc:1754 msgid "rotate" msgstr "girar" #: f.albums.cc:1755 msgid "fallover" msgstr "hacia abajo" #: f.albums.cc:1756 msgid "spheroid" msgstr "esferoide" #: f.albums.cc:1757 msgid "turn-page" msgstr "girar página" #: f.albums.cc:1758 msgid "french-door" msgstr "puertas batientes" #: f.albums.cc:1759 msgid "turn-cube" msgstr "girar cubo" #: f.albums.cc:1760 msgid "windmill" msgstr "molino" #: f.albums.cc:1761 msgid "pixelize" msgstr "pixelizar" #: f.albums.cc:1762 msgid "twist" msgstr "girar" #: f.albums.cc:1763 msgid "Xopen" msgstr "abrir en X" #: f.albums.cc:1764 msgid "squish" msgstr "expansión" #: f.albums.cc:1765 msgid "disintegrate" msgstr "desintegración" #: f.albums.cc:1766 msgid "interleave" msgstr "intercalar" #: f.albums.cc:1797 f.widgets.cc:462 msgid "Slide Show" msgstr "Diaporama" #: f.albums.cc:1800 msgid "Select Album" msgstr "Seleccionar álbum" #: f.albums.cc:1805 msgid "Caption Time" msgstr "Hora del comentario" #: f.albums.cc:1808 msgid "Image Time" msgstr "Hora de la imagen" #: f.albums.cc:1811 msgid "Clip Limit %" msgstr "Límite de recorte %" #: f.albums.cc:1815 msgid "Music File" msgstr "Archivo de Música" #: f.albums.cc:1820 msgid "Full Screen" msgstr "Pantalla completa" #: f.albums.cc:1821 msgid "Auto-replay" msgstr "Auto reiniciar" #: f.albums.cc:1824 msgid "Customize:" msgstr "Personalizar:" #: f.albums.cc:1825 msgid "transitions" msgstr "transiciones" #: f.albums.cc:1826 msgid "image files" msgstr "archivos de imagen" #: f.albums.cc:1827 msgid "KB controls" msgstr "controles de atajos" #: f.albums.cc:1841 f.albums.cc:1908 f.albums.cc:2141 f.albums.cc:2450 #: f.albums.cc:2786 f.albums.cc:2803 f.albums.cc:3039 msgid "invalid album" msgstr "álbum inválido" #: f.albums.cc:1936 msgid "open album" msgstr "abrir álbum" #: f.albums.cc:1948 msgid "Select music file" msgstr "Seleccione archivo de música" #: f.albums.cc:1983 #, c-format msgid "%d images" msgstr "%d imágenes" #: f.albums.cc:2008 msgid "" "arrow keys show previous or next image instantly \n" "space bar (blank) is allowed and shows as '-'" msgstr "" "teclas de flecha muestran la imagen anterior o la próxima al instante \n" "la barra espaciadora está permitida y se indica como '-'" #: f.albums.cc:2027 msgid "Keyboard Preferences" msgstr "Preferencias de atajos" #: f.albums.cc:2030 msgid "blank or unblank window" msgstr "pantalla vacía o llena" #: f.albums.cc:2033 msgid "show next image, with transition" msgstr "mostrar imagen siguiente, con transición" #: f.albums.cc:2036 msgid "pause or resume slide show" msgstr "pausar o reiniciar el diaporama" #: f.albums.cc:2039 msgid "magnify image (loupe tool)" msgstr "aumentar imagen (herramienta lupa)" #: f.albums.cc:2145 msgid "Transition Preferences" msgstr "Preferencias de transición" #: f.albums.cc:2147 msgid "Transitions File" msgstr "Archivo de transiciones" #: f.albums.cc:2156 msgid "random" msgstr "aleatorio" #: f.albums.cc:2159 f.albums.cc:2175 f.albums.cc:2179 msgid "time" msgstr "hora" #: f.albums.cc:2161 msgid "set all" msgstr "ajustar todo" #: f.albums.cc:2173 f.albums.cc:2177 msgid "transition" msgstr "transición" #: f.albums.cc:2174 f.albums.cc:2178 msgid "use" msgstr "usar" #: f.albums.cc:2176 f.albums.cc:2180 msgid "pref" msgstr "pref." #: f.albums.cc:2280 f.albums.cc:2322 msgid "invalid file" msgstr "archivo no válido" #: f.albums.cc:2394 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "error de formato de archivo: \n" " %s" #: f.albums.cc:2456 msgid "Image Preferences" msgstr "Preferencias de imagen" #: f.albums.cc:2460 f.file.cc:1610 f.file.cc:1995 msgid "Image File:" msgstr "Imagen:" #: f.albums.cc:2464 msgid "Action" msgstr "Acción" #: f.albums.cc:2476 msgid "Play tone when image shows" msgstr "Reproducir música mientras se ve la imagen" #: f.albums.cc:2481 msgid "Wait before filename/caption/comments" msgstr "Esperar antes de nombre de archivo/título/comentarios" #: f.albums.cc:2485 msgid "Show image file name (overlap)" msgstr "Mostrar nombre de imagen (sobrepuesto)" #: f.albums.cc:2489 msgid "Show image caption (overlap)" msgstr "Mostrar título de imagen (sobrepuesto)" #: f.albums.cc:2493 msgid "Show image comments (overlap)" msgstr "Mostrar comentarios de imagen (sobrepuesto)" #: f.albums.cc:2497 msgid "Wait before zoom" msgstr "Espera antes del zoom" #: f.albums.cc:2501 f.effects.cc:4023 msgid "Zoom" msgstr "Zoom" #: f.albums.cc:2505 msgid "zoom-in" msgstr "acercar" #: f.albums.cc:2506 msgid "zoom-out" msgstr "alejar" #: f.albums.cc:2510 msgid "Zoom Center" msgstr "Centrar zoom" #: f.albums.cc:2515 msgid "Wait after zoom" msgstr "Espera después del zoom" #: f.albums.cc:2521 msgid "Transition to next image" msgstr "Transición a la próxima imagen" #: f.albums.cc:2524 f.albums.cc:2654 msgid "next" msgstr "siguiente" #: f.albums.cc:2644 msgid "click on thumbnail to set zoom center" msgstr "clic en la miniatura para situar el centro de zoom" #: f.area.cc:93 msgid "Select Area for Edits" msgstr "Seleccionar área a editar" #: f.area.cc:94 f.area.cc:1890 f.edit.cc:8198 msgid "Press F1 for help" msgstr "Pulsar F1 para ayuda" #: f.area.cc:104 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:139 msgid "select rectangle" msgstr "seleccionar rectángulo" #: f.area.cc:143 msgid "freehand draw" msgstr "dibujar a mano alzada" #: f.area.cc:144 msgid "follow edge" msgstr "seguir contorno" #: f.area.cc:147 msgid "draw/replace" msgstr "dibujar/reemplazar" #: f.area.cc:150 msgid "Line Color:" msgstr "Color de línea:" #: f.area.cc:164 msgid "match level %" msgstr "nivel de coincidencia %" #: f.area.cc:168 msgid "search range" msgstr "rango de búsqueda" #: f.area.cc:172 msgid "select area in mouse" msgstr "seleccionar area en el mouse" #: f.area.cc:175 msgid "select one color in mouse:" msgstr "seleccionar un color en el ratón" #: f.area.cc:179 msgid "select all colors in mouse (flood)" msgstr "Selecciona todos los colores en el mouse." #: f.area.cc:184 msgid "Area Edge Blend Width" msgstr "Ancho de mezcla de bordes de área" #: f.area.cc:188 msgid "Edge Creep" msgstr "Borde progresivo" #: f.area.cc:200 msgid "drag mouse to select rectangular area" msgstr "arratrar el ratón para seleccionar un área rectangular" #: f.area.cc:201 msgid "drag mouse to select circular or elliptical area" msgstr "arrastrar el ratón para seleccionar un área circular o elíptica" #: f.area.cc:202 msgid "drag mouse to outline an area" msgstr "arrastrar el ratón para contornear un área" #: f.area.cc:203 msgid "drag mouse along an edge to follow the edge" msgstr "arrastrar el mouse a lo largo de un borde para seguir el borde" #: f.area.cc:204 msgid "drag mouse near a line to move the line" msgstr "arratrar el ratón cerca de una línea para moverla" #: f.area.cc:205 msgid "select line color" msgstr "seleccionar color de la línea" #: f.area.cc:206 msgid "size of mouse selection circle" msgstr "tamaño del círculo de selección del mouse" #: f.area.cc:207 msgid "required match level to select by color" msgstr "nivel de coincidencia requerido para seleccionar por color" #: f.area.cc:208 msgid "select by color search range" msgstr "selecciona por rango de búsqueda de color" #: f.area.cc:209 msgid "select area within mouse circle" msgstr "seleccionar área interior del cursor" #: f.area.cc:210 msgid "" "first select the checkbox, then \n" "shift+click on image to set the color" msgstr "" "primero seleccion la caja de selección, después \n" "mayús+clic en la imagen para ajustar el color" #: f.area.cc:212 msgid "select surrounding areas matching colors in mouse" msgstr "Seleccionar áreas circundantes que coincidan con los colores del mouse" #: f.area.cc:213 f.area.cc:214 msgid "area edits fade away within edge distance" msgstr "las ediciones de área se desvanecen en los bordes" #: f.area.cc:215 msgid "move area boundary in/out in 1-pixel steps" msgstr "entrada/salida del límite del área en pasos de 1 píxel" #: f.area.cc:216 msgid "map selected areas and verify" msgstr "mapear áreas seleccionadas y verificar" #: f.area.cc:217 msgid "invert area" msgstr "invertir área" #: f.area.cc:218 msgid "show area outlines" msgstr "mostrar contornos del área" #: f.area.cc:219 msgid "hide area outlines" msgstr "ocultar contornos del área" #: f.area.cc:220 msgid "clear area selections" msgstr "borrar selecciones de área" #: f.area.cc:404 f.area.cc:570 #, c-format msgid "exceed %d edits" msgstr "excedido en %d ediciones" #: f.area.cc:1471 msgid "" "Fill selected areas with color for visual verification. \n" "Method 1: left-click in each selected area not already filled. \n" "Method 2: right-click OUTSIDE all selected areas. \n" "Press [help] button for clarification" msgstr "" "Rellenar las áreas seleccionadas con color para la verificación visual. \n" "Método 1: clic con el botón izquierdo en cada área seleccionada que no esté " "llena. \n" "Método 2: clic con el botón derecho FUERA DE todas las áreas " "seleccionadas. \n" "Presione el botón [ayuda] para más información" #: f.area.cc:1499 msgid "finish area" msgstr "terminar área" #: f.area.cc:1528 f.area.cc:1757 #, c-format msgid "found %d pixels" msgstr "encontrados %d pixels" #: f.area.cc:1546 msgid "" "Method 1: \n" " Left-click inside an outlined area that is not already filled. \n" " Area will be filled with color for visible verification. \n" " Gaps in the outline will cause overflow outside the area. \n" " Repeat for each outlined area that is not already filled. \n" "Method 2: \n" " Right-click outside ALL outlined areas. \n" " All areas will be filled with color for visible verification. \n" " Gaps in an area outline will cause that area not to be filled. \n" "Gaps in an area outline: \n" " Gaps must be closed before proceeding with edits. \n" " The Find Gap function can be used for this." msgstr "" "Método 1: \n" " Clic con el botón izquierdo dentro de un área delimitada que aún no se ha " "completado. \n" " El área se llenará de color para la verificación visual. \n" " Los espacios en el contorno del área causarán desbordamiento fuera del " "área. \n" " Repetir para cada área delineada que no esté llena. \n" "Método 2: \n" " Haga clic derecho fuera de TODAS las áreas delineadas. \n" " Todas las áreas se llenarán de color para la verificación visual. \n" " Los espacios en un contorno de área harán que esa área no se llene. \n" "Separaciones en un contorno de área: \n" " Las separaciones deben cerrarse antes de continuar con las ediciones. \n" " La función Encontrar Separación (Find Gap) puede usarse para esto." #: f.area.cc:1889 f.widgets.cc:487 msgid "Select Hairy" msgstr "Selección irregular" #: f.area.cc:1899 msgid "select the area first" msgstr "en primer lugar sleccionar el área" #: f.area.cc:1975 msgid "select" msgstr "seleccionar" #: f.area.cc:1981 msgid "deselect" msgstr "deshacer selección" #: f.area.cc:2335 msgid "" "Click near any position on the area outline. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Clic cerca de cualquier posición en el contorno del área. \n" "Se encontrarán posibles separaciones en el contorno. \n" "Presionar F1 para ayuda." #: f.area.cc:2356 msgid "find outline gap" msgstr "encontrar separación en el contorno" #: f.area.cc:2451 msgid "cannot find area outline" msgstr "no se encuentra contorno de área" #: f.area.cc:3285 msgid "save area as a PNG file" msgstr "guardar área como un archivo PNG" #: f.area.cc:3407 msgid "position with mouse click/drag" msgstr "posición con el clic/arrastre del ratón" #: f.area.cc:3467 msgid "Paste Image" msgstr "Pegar imagen" #: f.area.cc:3472 msgid "resize" msgstr "redimensionar" #: f.combine.cc:174 f.combine.cc:828 f.combine.cc:1436 f.combine.cc:2066 #: f.combine.cc:2510 msgid "Select 2 to 9 files" msgstr "Seleccione de 2 a 9 imágenes" #: f.combine.cc:196 f.combine.cc:850 f.combine.cc:1458 f.combine.cc:2088 msgid "Images are not all the same size" msgstr "No todas las imágenes son del mismo tamaño" #: f.combine.cc:560 msgid "Adjust Image Contributions" msgstr "Ajustar aportes de imagen" #: f.combine.cc:564 msgid "dark pixels" msgstr "sombras" #: f.combine.cc:566 msgid "light pixels" msgstr "luces" #: f.combine.cc:568 msgid "file:" msgstr "archivo:" #: f.combine.cc:1036 msgid "Paint and Warp Image" msgstr "Pintar y deformar imagen" #: f.combine.cc:1044 f.edit.cc:6054 msgid "paint" msgstr "pintar" #: f.combine.cc:1045 msgid "warp" msgstr "deformar" #: f.combine.cc:1648 f.combine.cc:2594 msgid "Select and Paint Image" msgstr "Seleccionar y pintar imagen" #: f.combine.cc:1656 msgid "Transient Objects" msgstr "Objetos transitorios" #: f.combine.cc:2264 msgid "Adjust Pixel Composition" msgstr "Ajustar composición de píxeles" #: f.combine.cc:2266 msgid "use average" msgstr "usar media" #: f.combine.cc:2267 msgid "use median" msgstr "usar mediana" #: f.combine.cc:2269 msgid "omit low pixel" msgstr "omitir píxeles bajos" #: f.combine.cc:2270 msgid "omit high pixel" msgstr "omitir píxeles altos" #: f.combine.cc:2532 f.combine.cc:2844 msgid "Images must be exactly the same size" msgstr "Las imágenes deben ser exactamente del mismo tamaño." #: f.combine.cc:2605 msgid " Fill " msgstr "Llenar" #: f.combine.cc:2606 msgid "using selected image" msgstr "usando imagen seleccionada" #: f.combine.cc:2825 f.combine.cc:3095 msgid "Select 2 files" msgstr "Seleccionar 2 archivos" #: f.combine.cc:2902 msgid "Split two Images" msgstr "Saltar dos imágenes" #: f.combine.cc:2903 msgid "drag image boundary" msgstr "arrastrar el límite de la imagen" #: f.combine.cc:3092 msgid "Image Differences" msgstr "Diferencias de imágenes" #: f.combine.cc:3108 msgid "differences" msgstr "diferencias" #: f.combine.cc:3110 msgid "X-align" msgstr "alineación en X" #: f.combine.cc:3113 msgid "Y-align" msgstr "alineación en Y" #: f.combine.cc:3179 msgid "select exactly 2 files" msgstr "seleccionar exactamente 2 archivos" #: f.combine.cc:3408 f.combine.cc:4562 msgid "Select 2 to 4 files" msgstr "Seleccionar de 2 a 4 imagenes" #: f.combine.cc:3483 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:3485 f.combine.cc:4639 msgid "no curve (scanned image)" msgstr "sin curvatura (imagen escaneada)" #: f.combine.cc:3486 f.combine.cc:4640 msgid "Search for lens mm" msgstr "Buscar por longitud focal (mm)" #: f.combine.cc:3487 f.combine.cc:4641 msgid "Save lens mm → image EXIF" msgstr "Guardar focal en EXIF" #: f.combine.cc:3547 f.combine.cc:4701 msgid "Pre-align Images" msgstr "Pre-alinear imágenes" #: f.combine.cc:3551 f.combine.cc:4705 msgid "lens mm" msgstr "longitud focal (mm)" #: f.combine.cc:3556 f.combine.cc:4710 msgid "no auto warp" msgstr "no auto corrección" #: f.combine.cc:3558 f.combine.cc:4712 msgid "manual align" msgstr "alineación manual" #: f.combine.cc:3560 f.combine.cc:4714 f.process.cc:1395 f.widgets.cc:504 #: f.widgets.cc:829 msgid "Resize" msgstr "Redimensionar" #: f.combine.cc:3561 f.combine.cc:4715 msgid "resize window" msgstr "redimensionar ventana" #: f.combine.cc:3569 f.combine.cc:4723 msgid "do not warp images during auto-alignment" msgstr "no deformar imágenes durante el auto alineado" #: f.combine.cc:3614 f.combine.cc:4768 msgid "use two images only" msgstr "usar sólo dos imágenes" #: f.combine.cc:3644 f.combine.cc:3848 f.combine.cc:4034 f.combine.cc:4796 #: f.combine.cc:5000 f.combine.cc:5186 msgid "Too little overlap, cannot align" msgstr "Solapamiento demasiado pequeño, no puedo alinear" #: f.combine.cc:4112 f.combine.cc:5264 msgid "Match Brightness and Color" msgstr "Concordancia de brillo y color" #: f.combine.cc:4158 msgid "Select image" msgstr "Seleccionar imagen" #: f.combine.cc:4173 f.combine.cc:5325 msgid "auto color" msgstr "color automático" #: f.combine.cc:4174 f.combine.cc:5326 msgid "file color" msgstr "archivo de color" #: f.combine.cc:4180 f.combine.cc:5332 msgid "mouse warp" msgstr "corregir con ratón" #: f.combine.cc:4182 f.combine.cc:5334 msgid "flatten image" msgstr "aplanar imagen" #: f.combine.cc:4637 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:5596 msgid "pano tools (hugin) not installed" msgstr "Pano Tools (Hugin) no instalado" #: f.combine.cc:5605 msgid "Select at least 2 files" msgstr "Seleccione el menos 2 archivos" #: f.edit.cc:106 msgid "" "drag middle to move \n" "drag corners to resize \n" "drag right edge to level" msgstr "" "arrastre el centro para mover \n" "arrastre las esquinas para redimensionar \n" "arrastre el borde derecho al nivel" #: f.edit.cc:209 f.widgets.cc:501 f.widgets.cc:828 msgid "Trim/Rotate" msgstr "Recortar/Girar" #: f.edit.cc:224 f.edit.cc:1483 msgid "ratio" msgstr "relación" #: f.edit.cc:226 msgid "Lock Ratio" msgstr "Bloquear la relación" #: f.edit.cc:229 msgid "Trim Size:" msgstr "Tamaño de recorte" #: f.edit.cc:237 msgid "Set Ratio" msgstr "Establecer ratio" #: f.edit.cc:238 f.widgets.cc:493 fotoxx.h:1371 msgid "Invert" msgstr "Invertir" #: f.edit.cc:239 msgid "Customize" msgstr "Personalizar" #: f.edit.cc:248 msgid "Degrees:" msgstr "Grados" #: f.edit.cc:250 msgid "Auto Level" msgstr "Auto nivel" #: f.edit.cc:256 f.process.cc:186 f.process.cc:822 f.widgets.cc:502 #: f.widgets.cc:830 msgid "Upright" msgstr "Enderezar" #: f.edit.cc:258 msgid "maximize trim box" msgstr "maximizar caja de recorte" #: f.edit.cc:259 msgid "trim transparent edges" msgstr "recortar bordes transparentes" #: f.edit.cc:260 msgid "lock width/height ratio" msgstr "bloquear proporción ancho/alto" #: f.edit.cc:261 msgid "use EXIF data if available" msgstr "usar datos EXIF si están disponibles" #: f.edit.cc:603 f.edit.cc:1550 msgid "rotation unknown" msgstr "giro desconocido" #: f.edit.cc:1479 msgid "Trim Buttons" msgstr "Botones de recorte" #: f.edit.cc:1482 msgid "label" msgstr "etiqueta" #: f.edit.cc:1692 f.widgets.cc:503 f.widgets.cc:833 msgid "Retouch" msgstr "Retoque" #: f.edit.cc:1705 msgid "Auto black level" msgstr "Nivel de negro automático" #: f.edit.cc:1708 f.edit.cc:1714 msgid "sample %" msgstr "ejemplo %" #: f.edit.cc:1711 msgid "Auto white balance" msgstr "Auto balance de blanco" #: f.edit.cc:1717 msgid "Click gray spot for white balance" msgstr "Haga clic en el punto gris para el balance de blancos" #: f.edit.cc:1720 msgid "Click dark spot for black level" msgstr "Haga clic en el punto oscuro para el nivel de negro" #: f.edit.cc:1723 msgid "Click for RGB distribution" msgstr "Clic para histograma RGB" #: f.edit.cc:1731 fotoxx.h:1326 msgid "Brightness" msgstr "Brillo" #: f.edit.cc:1732 f.effects.cc:4049 fotoxx.h:1339 msgid "Contrast" msgstr "Contraste" #: f.edit.cc:1733 f.edit.cc:3279 f.edit.cc:3311 f.edit.cc:6777 msgid "Saturation" msgstr "Saturación" #: f.edit.cc:1734 msgid "Temperature" msgstr "Temperatura" #: f.edit.cc:1742 msgid "Settings File" msgstr "Ajustes" #: f.edit.cc:2250 msgid "choose a better spot" msgstr "elegir un lugar mejor" #: f.edit.cc:2555 msgid "Resize Image" msgstr "Redimensionar imagen" #: f.edit.cc:2571 msgid "Previous" msgstr "Anterior" #: f.edit.cc:2589 msgid "W/H Ratio:" msgstr "Relación ancho/alto" #: f.edit.cc:2591 msgid "Lock" msgstr "Bloquear" #: f.edit.cc:2840 msgid "insufficient memory, cannot proceed" msgstr "memoria insuficiente, no puede proceder." #: f.edit.cc:2936 f.widgets.cc:505 msgid "Adjust RGB" msgstr "Ajustar RGB" #: f.edit.cc:2942 msgid "+Brightness" msgstr "Brillo" #: f.edit.cc:2943 msgid "+Red -Cyan" msgstr "+Rojo -Cyan" #: f.edit.cc:2944 msgid "+Green -Magenta" msgstr "+Verde -Magenta" #: f.edit.cc:2945 msgid "+Blue -Yellow" msgstr "+Azul -Amarillo" #: f.edit.cc:2948 msgid "Contrast Red" msgstr "Contraste en rojo" #: f.edit.cc:2949 msgid "Contrast Green" msgstr "C0ntraste en verde" #: f.edit.cc:2950 msgid "Contrast Blue" msgstr "Contraste en azul" #: f.edit.cc:3268 f.widgets.cc:506 msgid "Adjust HSL" msgstr "Ajustar HSL" #: f.edit.cc:3272 msgid "Input color to match and adjust:" msgstr "Entrar color para coincidir y ajustar" #: f.edit.cc:3274 msgid "shift+click on image to select color" msgstr "Mayús+clic en la imagen para seleccionar color" #: f.edit.cc:3277 msgid "Match using:" msgstr "Usando coincidencia:" #: f.edit.cc:3278 msgid "Hue" msgstr "Tono" #: f.edit.cc:3280 f.edit.cc:3312 f.edit.cc:6778 msgid "Lightness" msgstr "Luminosidad" #: f.edit.cc:3290 msgid "Output Color" msgstr "Color de salida" #: f.edit.cc:3310 f.edit.cc:6776 msgid "Color Hue" msgstr "Tono de color" #: f.edit.cc:3313 msgid "Adjustment" msgstr "Ajuste" #: f.edit.cc:3828 msgid "Image Markup" msgstr "Marcado de imagen" #: f.edit.cc:3829 f.edit.cc:3894 msgid "Draw text on image" msgstr "Trazar texto en la imagen" #: f.edit.cc:3830 f.edit.cc:4818 msgid "Draw line or arrow on image" msgstr "Dibujar una línea o flecha en la imagen" #: f.edit.cc:3831 f.edit.cc:5499 msgid "Draw box on image" msgstr "Dibujar una caja en la imagen" #: f.edit.cc:3832 f.edit.cc:5700 msgid "Draw oval on image" msgstr "Dibujar un óvalo en la imagemn" #: f.edit.cc:3871 msgid "+Version" msgstr "+Version" #: f.edit.cc:3895 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:3944 msgid "Use settings file" msgstr "Usar archivo de ajustes" #: f.edit.cc:3949 f.mashup.cc:2438 f.tools.cc:1668 f.tools.cc:1677 msgid "Text" msgstr "Texto" #: f.edit.cc:3954 msgid "Use metadata key" msgstr "Usar clave de metadatos" #: f.edit.cc:3973 f.mashup.cc:2456 msgid "text" msgstr "texto" #: f.edit.cc:3974 f.edit.cc:4847 f.mashup.cc:2457 f.mashup.cc:2795 msgid "backing" msgstr "fondo" #: f.edit.cc:3975 f.edit.cc:4848 f.mashup.cc:2458 f.mashup.cc:2796 msgid "outline" msgstr "contorno" #: f.edit.cc:3976 f.edit.cc:4849 f.mashup.cc:2459 f.mashup.cc:2797 msgid "shadow" msgstr "sombra" #: f.edit.cc:4002 msgid "save to current file" msgstr "guardar en archivo actual" #: f.edit.cc:4003 f.file.cc:2432 msgid "save as new file version" msgstr "guardar como una nueva versión de archivo" #: f.edit.cc:4004 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:4167 f.mashup.cc:2563 f.tools.cc:1961 msgid "select font" msgstr "seleccionar tipo de letra" #: f.edit.cc:4446 msgid "text file is defective" msgstr "archivo de texto defectuoso" #: f.edit.cc:4786 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:4826 f.mashup.cc:2774 msgid "Line length" msgstr "Longitud de línea" #: f.edit.cc:4833 f.mashup.cc:2781 msgid "Arrow head" msgstr "Punta de flecha" #: f.edit.cc:4846 f.mashup.cc:2794 msgid "line" msgstr "linea" #: f.edit.cc:4875 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:5474 msgid "" "drag mouse to draw box \n" "shift + drag center to move box \n" "shift + drag edge to move edge" msgstr "" "arrastrar el ratón para dibujar el cuadro \n" "mayús + arrastrar centro para mover el cuadro \n" "mayús + arrastrar borde para mover el borde" #: f.edit.cc:5505 f.edit.cc:5706 msgid "line color" msgstr "color de la línea" #: f.edit.cc:5508 f.edit.cc:5709 msgid "line width" msgstr "ancho de la línea" #: f.edit.cc:5674 msgid "" "drag mouse down/right to draw oval \n" "shift + drag center to move oval \n" "shift + drag lower right edge to change" msgstr "" "arrastrar el ratón hacia abajo/derecha para dibujar un óvalo \n" "mayús + arrastrar centro para mover el óvalo \n" "mayús + arrastrar el borde inferior derecho para cambiar" #: f.edit.cc:5712 msgid "oval" msgstr "óvalo" #: f.edit.cc:5713 msgid "circle" msgstr "círculo" #: f.edit.cc:5988 msgid "" "shift + left click: pick color from image \n" "left drag: paint color on image \n" "right drag: restore original image" msgstr "" "mayúsc + clic izquierdo: tomar color de una imagen \n" "arrastre izquierdo: pintar color en la imagen \n" "arrastre derecho: restaurar imagen original" #: f.edit.cc:6024 msgid "Paint on Image" msgstr "Pintar en la imagen" #: f.edit.cc:6030 msgid "paint color" msgstr "color de la pintura" #: f.edit.cc:6041 f.edit.cc:6970 f.edit.cc:7548 msgid "brush size" msgstr "tamaño de brocha" #: f.edit.cc:6055 msgid "erase" msgstr "borrar" #: f.edit.cc:6060 msgid "include transparent areas" msgstr "incluir áreas transparentes" #: f.edit.cc:6063 msgid "drag image" msgstr "arratrar imagen" #: f.edit.cc:6065 msgid "zoom image" msgstr "zoom de imagen" #: f.edit.cc:6592 msgid "Color Chooser" msgstr "Selector de color" #: f.edit.cc:6594 msgid "click on desired color" msgstr "clic en el color deseado" #: f.edit.cc:6929 msgid "" "shift + left click: pick position to copy \n" "left click or drag: copy image to mouse \n" "right click or drag: restore original image" msgstr "" "Mayús + clic izquierdo: elegir posición para copiar \n" "clic izquierdo o arrastrar: copiar la imagen al ratón \n" "clic derecho o arrastre: restaurar imagen original" #: f.edit.cc:6960 msgid "Copy Pixels (1 image)" msgstr "Copiar píxeles (1 imagen)" #: f.edit.cc:6979 f.edit.cc:7557 msgid "paint over transparent areas" msgstr "pintar sobre áreas transparentes" #: f.edit.cc:7531 msgid "" "left click: synchronize copy position \n" "left click or drag: copy source image to mouse \n" "right click or drag: restore original image" msgstr "" "clic izqdo.: sincronizar posicxción de copia \n" "clic izqdo. o arrastrar: copiar imagen fuente al cursor \n" "clic der. o arratrar: restaurar imagen original" #: f.edit.cc:7535 msgid "Copy Pixels (2 images)" msgstr "Copiar píxeles (2 imágenes)" #: f.edit.cc:7541 msgid "source image scale" msgstr "escalar imagen origen" #: f.edit.cc:8118 msgid "source image failure (scale too big?)" msgstr "fallo en la imagen de origen (¿escala demasiado grande?)" #: f.edit.cc:8197 f.widgets.cc:511 msgid "Paint Edits" msgstr "Editar pintando" #: f.edit.cc:8203 fotoxx.cc:3576 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "No se puede conservar la selección de área.\n" "¿Continuar" #: f.edit.cc:8211 f.tools.cc:3751 msgid "Edit function must be active" msgstr "la función de edición debe estar activa" #: f.edit.cc:8216 msgid "Cannot use Paint Edits" msgstr "No se puede usar Editar pintando" #: f.edit.cc:8243 f.edit.cc:8485 msgid "power: center" msgstr "fuerza: centro" #: f.edit.cc:8248 msgid "reset area" msgstr "restablecer área" #: f.edit.cc:8428 msgid "finish current edit first" msgstr "antes acabe la edición actual" #: f.edit.cc:8433 msgid "no previous edit" msgstr "no hay edición previa" #: f.edit.cc:8438 msgid "no current image" msgstr "no hay imagen actual" #: f.edit.cc:8449 msgid "This edit cannot be incrementally undone" msgstr "Esta edición no se puede deshacer de modo incremental" #: f.edit.cc:8480 f.widgets.cc:512 msgid "Undo Edits" msgstr "Deshacer ediciones" #: f.edit.cc:8708 f.edit.cc:8756 msgid "Edit Plugins" msgstr "Editar complementos (plugins)" #: f.edit.cc:8709 msgid "Edit plugins menu" msgstr "Editar menú de plugins" #: f.edit.cc:8717 msgid "Run as Fotoxx edit function" msgstr "Abrir como una función de edición de Fotoxx" #: f.edit.cc:8758 msgid "menu name" msgstr "nombre de menú" #: f.edit.cc:8761 msgid "command" msgstr "comando" #: f.edit.cc:8959 msgid "Plugin working ..." msgstr "Plugin trabajando ..." #: f.edit.cc:8968 msgid "plugin failed" msgstr "falló el complemento" #: f.edit.cc:9004 msgid "Raw Therapee not installed" msgstr "RawTherapee no instalado" #: f.edit.cc:9023 msgid "RAW type not registered in User Preferences" msgstr "Tipo de RAW no registrado en preferencias de usuario" #: f.edit.cc:9038 msgid "Raw Therapee produced no tif file" msgstr "Raw Therapee no produjo archivo tif" #: f.effects.cc:83 msgid "Convert to Sketch" msgstr "Convertir a esbozo" #: f.effects.cc:161 msgid "Clip Level" msgstr "Nivel de corte" #: f.effects.cc:165 msgid "Algorithm" msgstr "Algoritmo" #: f.effects.cc:170 msgid "Foreground" msgstr "Primer plano" #: f.effects.cc:173 f.tools.cc:1680 msgid "Background" msgstr "Fondo" #: f.effects.cc:609 msgid "Line Threshold" msgstr "Umbral de línea" #: f.effects.cc:610 msgid "Line Width" msgstr "Ancho de línea" #: f.effects.cc:611 f.enhance.cc:3929 msgid "Blur Radius" msgstr "Radio de desenfoque" #: f.effects.cc:612 msgid "Kuwahara Depth" msgstr "Profundidad Kuwahara" #: f.effects.cc:1057 f.widgets.cc:538 msgid "Line Drawing" msgstr "Dibujo con líneas" #: f.effects.cc:1070 f.effects.cc:2090 msgid "black/white" msgstr "negro/blanco" #: f.effects.cc:1320 f.widgets.cc:539 msgid "Emboss" msgstr "Relieve" #: f.effects.cc:1326 msgid "Depth" msgstr "Profundiddad" #: f.effects.cc:1541 msgid "Simulate Tiles" msgstr "Simular un mosaico" #: f.effects.cc:1545 msgid "tile size" msgstr "tamaño de baldosas" #: f.effects.cc:1548 msgid "tile gap" msgstr "separación de baldosas" #: f.effects.cc:1551 msgid "3D depth" msgstr "profundidad 3D" #: f.effects.cc:1780 msgid "Dither Image" msgstr "Tramado de imagen" #: f.effects.cc:1783 msgid "Dither0" msgstr "Tramado 0" #: f.effects.cc:1784 f.effects.cc:1848 msgid "Roy Lichtenstein effect" msgstr "Efecto Roy Lichtenstein" #: f.effects.cc:1787 f.effects.cc:2083 msgid "Dither1" msgstr "Tramado 1" #: f.effects.cc:1788 msgid "pure RGB or black/white dots" msgstr "RGB puro o puntos negros/blancos" #: f.effects.cc:1791 f.effects.cc:2481 msgid "Dither2" msgstr "Tramado 2" #: f.effects.cc:1792 msgid "RGB mix with given bit-depth" msgstr "Mezcla RGB con profundidad de bits dada" #: f.effects.cc:1795 f.effects.cc:2756 msgid "Dither3" msgstr "Tramado 3" #: f.effects.cc:1796 msgid "custom palette colors" msgstr "personalizar paleta de color" #: f.effects.cc:1852 msgid "dot size" msgstr "tamaño del punto" #: f.effects.cc:2087 f.effects.cc:2485 f.effects.cc:2760 msgid "resolution" msgstr "resolución" #: f.effects.cc:2089 msgid "RGB color" msgstr "color RGB" #: f.effects.cc:2091 msgid "random position" msgstr "posición aleatoria" #: f.effects.cc:2488 f.effects.cc:3196 msgid "color depth" msgstr "profundidad de color" #: f.effects.cc:2491 f.effects.cc:2768 msgid "error compensation" msgstr "compensación de error" #: f.effects.cc:2764 msgid "palette:" msgstr "paleta" #: f.effects.cc:2818 msgid "palette file" msgstr "archivo de paleta" #: f.effects.cc:3178 f.widgets.cc:542 msgid "Painting" msgstr "Pintura" #: f.effects.cc:3200 msgid "patch area goal" msgstr "tamaño del área de color " #: f.effects.cc:3204 msgid "req. color match" msgstr "correspondencia de color requerida" #: f.effects.cc:3208 msgid "borders" msgstr "bordes" #: f.effects.cc:3799 msgid "Add Texture" msgstr "Añadir textura" #: f.effects.cc:4014 msgid "Background Pattern" msgstr "Patrón de fondo" #: f.effects.cc:4018 msgid "Pattern File:" msgstr "Archivo de patrón" #: f.effects.cc:4026 msgid "Geometry" msgstr "Geometría" #: f.effects.cc:4030 f.widgets.cc:544 msgid "Pattern" msgstr "Patrón" #: f.effects.cc:4038 msgid "Overlap" msgstr "Solape" #: f.effects.cc:4046 msgid "Opacity" msgstr "Opacidad" #: f.effects.cc:4052 msgid "Grayscale" msgstr "Escala de grises" #: f.effects.cc:4478 msgid "Create Mosaic" msgstr "Crear mosaico" #: f.effects.cc:4524 msgid "Tile" msgstr "Baldosa" #: f.effects.cc:4532 f.widgets.cc:540 msgid "Tiles" msgstr "Baldosas" #: f.effects.cc:4538 msgid "Tile blending" msgstr "Mezcla de baldosas" #: f.effects.cc:4621 #, c-format msgid "exceeded max. tiles: %d" msgstr "excedido max. baldosas: %d" #: f.effects.cc:4628 #, c-format msgid "only %d tile images found" msgstr "encontradas sólo %d imágenes de baldosas" #: f.effects.cc:5088 f.widgets.cc:546 msgid "Color Mode" msgstr "Modo de color" #: f.effects.cc:5091 msgid "reset" msgstr "reinicializar" #: f.effects.cc:5092 msgid "black/white positive" msgstr "positivo en blanco y negro" #: f.effects.cc:5093 msgid "black/white negative" msgstr "negativo en blanco y negro" #: f.effects.cc:5094 msgid "color negative" msgstr "Negativo en color" #: f.effects.cc:5095 msgid "RGB -> GBR" msgstr "RGB -> GBR" #: f.effects.cc:5096 msgid "RGB -> BRG" msgstr "RGB -> BRG" #: f.effects.cc:5097 msgid "sepia" msgstr "sepia" #: f.effects.cc:5348 msgid "Set color depth to 1-16 bits" msgstr "Establecer profundidad de color 1-16 bits" #: f.effects.cc:5377 msgid "Set Color Depth" msgstr "Ajustar profundidad de color" #: f.effects.cc:5547 f.widgets.cc:548 msgid "Shift Colors" msgstr "Modificar colores" #: f.effects.cc:5824 f.widgets.cc:549 msgid "Alien Colors" msgstr "Colores alienígenas" #: f.effects.cc:5827 msgid "blocksize" msgstr "tamaño de bloque" #: f.effects.cc:5830 f.warp.cc:3096 msgid "amplitude" msgstr "amplitud" #: f.effects.cc:6089 msgid "" "Draw a line across the image in \n" "direction of brightness change." msgstr "" "Trazar una línea en la imagen en \n" "la dirección del cambio de brillo." #: f.effects.cc:6132 msgid "Brightness Ramp" msgstr "Rampa de brillo" #: f.effects.cc:6605 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" "arrastre izquierdo: añadir transparencia \n" "arrastre derecho: añadir opacidad" #: f.effects.cc:6640 msgid "Paint Transparency" msgstr "Pintar transparencia" #: f.effects.cc:6648 msgid "paintbrush radius" msgstr "radio del pincel" #: f.effects.cc:6649 msgid "strength center" msgstr "fuerza en el centro" #: f.effects.cc:6650 msgid "strength edge" msgstr "fuerza en bordes" #: f.effects.cc:6655 msgid "gradual paint" msgstr "pintado gradual" #: f.effects.cc:6874 msgid "Mirror Image" msgstr "Espejar imagen" #: f.effects.cc:6877 f.warp.cc:3098 msgid "horizontal" msgstr "horizontal" #: f.effects.cc:6878 f.warp.cc:3102 msgid "vertical" msgstr "vertical" #: f.effects.cc:7051 f.widgets.cc:553 msgid "Custom Kernel" msgstr "Matriz de convolución" #: f.effects.cc:7055 msgid "Kernel size" msgstr "Tamaño de matriz" #: f.effects.cc:7072 msgid "multiply" msgstr "multiplicar" #: f.effects.cc:7075 msgid "add" msgstr "añadir" #: f.effects.cc:7079 msgid "Data file" msgstr "Archivo de datos" #: f.effects.cc:7099 fotoxx.cc:3845 msgid "Load settings from file" msgstr "Cargar ajustes desde un archivo" #: f.enhance.cc:505 msgid "Adjust Brightness Distribution" msgstr "Ajustar histograma" #: f.enhance.cc:544 msgid "Low Cutoff" msgstr "Corte bajo" #: f.enhance.cc:545 msgid "High Cutoff" msgstr "Corte alto" #: f.enhance.cc:546 msgid "Low Flatten" msgstr "Aplanado bajo" #: f.enhance.cc:547 msgid "Mid Flatten" msgstr "Aplanado medio" #: f.enhance.cc:548 msgid "High Flatten" msgstr "Aplanado alto" #: f.enhance.cc:549 msgid "Low Stretch" msgstr "Tramo bajo" #: f.enhance.cc:550 msgid "Mid Stretch" msgstr "Tramo medio" #: f.enhance.cc:551 msgid "High Stretch" msgstr "Tramo alto" #: f.enhance.cc:886 msgid "Magnify Gradients" msgstr "Aumentar gradientes" #: f.enhance.cc:923 msgid "low" msgstr "bajo" #: f.enhance.cc:925 msgid "high" msgstr "alto" #: f.enhance.cc:928 msgid "Amplify" msgstr "Ampliar" #: f.enhance.cc:1312 msgid "Flatten Brightness" msgstr "Aplanar brillo" #: f.enhance.cc:1363 msgid "Zones" msgstr "Zonas" #: f.enhance.cc:1370 msgid "Deband Dark" msgstr "Eliminar bandas oscuras" #: f.enhance.cc:1373 msgid "Deband Bright" msgstr "Eliminar bandas brillantes" #: f.enhance.cc:1851 msgid "Global Retinex" msgstr "Retinex global" #: f.enhance.cc:1867 msgid "Dark Point" msgstr "Punto negro" #: f.enhance.cc:1868 msgid "Bright Point" msgstr "Punto de brillo" #: f.enhance.cc:1869 msgid "Multiplyer" msgstr "Multiplicador" #: f.enhance.cc:1893 msgid "brightness rescale" msgstr "reecalar brillo" #: f.enhance.cc:1896 msgid "click bright point" msgstr "clic en punto brillante" #: f.enhance.cc:1897 msgid "click dark point" msgstr "clic en punto oscuro" #: f.enhance.cc:1900 f.enhance.cc:2531 msgid "blend" msgstr "mezclar" #: f.enhance.cc:1903 f.enhance.cc:2537 msgid "reduce bright" msgstr "reducir brillo" #: f.enhance.cc:2523 msgid "Zonal Retinex" msgstr "Retinex zonal" #: f.enhance.cc:2527 msgid "zone count:" msgstr "contar zona:" #: f.enhance.cc:2534 msgid "reduce dark" msgstr "reducir oscuridad" #: f.enhance.cc:3058 f.process.cc:189 f.process.cc:823 f.process.cc:1404 #: f.widgets.cc:524 msgid "Sharpen" msgstr "Enfocar" #: f.enhance.cc:3066 msgid "unsharp mask" msgstr "máscara de desenfoque" #: f.enhance.cc:3099 msgid "median diff" msgstr "diferencia mediana" #: f.enhance.cc:3101 msgid "dark" msgstr "oscuro" #: f.enhance.cc:3102 msgid "light" msgstr "claro" #: f.enhance.cc:3888 msgid "Click to set center" msgstr "Clic para establecer el centro" #: f.enhance.cc:3889 msgid "Pull image using the mouse" msgstr "Arrastrar imagen usando el ratón" #: f.enhance.cc:3890 msgid "" "left drag: blend image \n" "right drag: restore image" msgstr "" "arrastre izquierdo: mezclar imagen \n" "arrastre derecho: reinicializar imagen" #: f.enhance.cc:3933 msgid "Normal Blur" msgstr "Desenfoque normal" #: f.enhance.cc:3940 msgid "Radial Blur" msgstr "Desenfoque radial" #: f.enhance.cc:3954 msgid "Directed Blur" msgstr "Desenfoque direccional" #: f.enhance.cc:3956 msgid "Blur Span" msgstr "cantidad de desnfoque" #: f.enhance.cc:3959 msgid "Intensity" msgstr "Intensidad" #: f.enhance.cc:3964 msgid "Graduated Blur" msgstr "Desenfoque graduado" #: f.enhance.cc:3969 msgid "Contrast Limit" msgstr "Límite de contraste" #: f.enhance.cc:3974 msgid "Paint Blur" msgstr "Pintar desenfoque" #: f.enhance.cc:3979 f.mashup.cc:1577 fotoxx.h:1402 msgid "Power" msgstr "Fuerza" #: f.enhance.cc:3987 f.enhance.cc:4914 msgid "Blur Background" msgstr "Desenfocar fondo" #: f.enhance.cc:4918 msgid "constant blur" msgstr "desenfoque constante" #: f.enhance.cc:4921 msgid "increase blur with distance" msgstr "incrementar desenfoque con la distancia" #: f.enhance.cc:4923 msgid "min. blur radius" msgstr "mín. radio de desenfoque" #: f.enhance.cc:4926 msgid "max. blur radius" msgstr "máx. radio de desenfoque" #: f.enhance.cc:4957 f.warp.cc:837 f.warp.cc:2277 msgid "no active Select Area" msgstr "selección de área no activada" #: f.enhance.cc:5139 msgid "Apply repeatedly while watching the image." msgstr "Aplicar repetidamente mientras se observe la imagen" #: f.enhance.cc:5175 msgid "Noise Reduction" msgstr "Reducción de ruido" #: f.enhance.cc:5215 msgid "dark areas" msgstr "áreas oscuras" #: f.enhance.cc:5217 msgid "all areas" msgstr "todas las áreas" #: f.enhance.cc:6195 msgid "Measure Noise" msgstr "Medir ruído" #: f.enhance.cc:6196 msgid "Move mouse in a monotone image area." msgstr "Mover el ratón en un área de color uniforme de la imagen." #: f.enhance.cc:6511 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.enhance.cc:6530 msgid "Red Eye Reduction" msgstr "Reducción de ojos rojos" #: f.enhance.cc:6984 msgid "Color Match Images" msgstr "Concordar color de imágenes" #: f.enhance.cc:7015 msgid "mouse radius for color sample" msgstr "radio del ratón para muestra de color" #: f.enhance.cc:7017 f.enhance.cc:7022 fotoxx.h:1397 zfuncs.cc:12681 msgid "Open" msgstr "Abrir" #: f.enhance.cc:7018 msgid "image for source color" msgstr "imagen para el color de origen" #: f.enhance.cc:7020 msgid "click on image to get source color" msgstr "Pulse en la imagen para obtener color de origen" #: f.enhance.cc:7023 msgid "image to set matching color" msgstr "imagen para establecer concordancia de color" #: f.enhance.cc:7025 msgid "click on image to set matching color" msgstr "Pulse en la imagen para establecer la concordancia de color" #: f.enhance.cc:7092 msgid "select source image color first" msgstr "seleccionar primero el color de la imagen de origen" #: f.enhance.cc:7287 msgid "" "Drag mouse to select. Erase. Repeat. \n" "Click: extend selection to mouse." msgstr "" "Arrastre el ratón para seleccionar. Borrar. Repetir. \n" "Haga clic en: extender la selección al mouse." #: f.enhance.cc:7314 f.widgets.cc:529 msgid "Smart Erase" msgstr "Borrado inteligente" #: f.enhance.cc:7319 fotoxx.h:1408 msgid "Radius" msgstr "Radio" #: f.enhance.cc:7321 f.widgets.cc:525 msgid "Blur" msgstr "Desenfocar" #: f.enhance.cc:7324 msgid "New Area" msgstr "Nueva área" #: f.enhance.cc:7672 f.enhance.cc:8054 msgid "Chromatic Aberration" msgstr "Aberración cromática" #: f.enhance.cc:7711 msgid "Red Factors" msgstr "Factores de rojo" #: f.enhance.cc:7715 msgid "Blue Factors" msgstr "Factores de azul" #: f.enhance.cc:7720 msgid "Find optimum factors:" msgstr "Encontrar factores óptimos" #: f.enhance.cc:8095 msgid "Chromatic Color" msgstr "Color cromático" #: f.enhance.cc:8098 msgid "Replacement Color" msgstr "Color de reemplazo" #: f.enhance.cc:8101 msgid "Background Color" msgstr "color de fondo" #: f.enhance.cc:8105 msgid "Color match level" msgstr "nive de coincidencia de color" #: f.enhance.cc:8109 msgid "Background Proximity" msgstr "Proximidad del fondo" #: f.enhance.cc:8151 msgid "255 iterations, cannot continue" msgstr "255 iteraciones, no se puede continuar" #: f.enhance.cc:8413 f.widgets.cc:532 msgid "Vignette" msgstr "Viñetado" #: f.enhance.cc:8802 f.widgets.cc:533 msgid "Remove Dust" msgstr "Retirar polvo" #: f.enhance.cc:8806 msgid "spot size limit" msgstr "límite de tamaño de punto" #: f.enhance.cc:8809 msgid "max. brightness" msgstr "máximo brillo" #: f.enhance.cc:8812 msgid "min. contrast" msgstr "mínimo contraste" #: f.file.cc:601 msgid "Rename Image File" msgstr "Renombrar archivo de imagen" #: f.file.cc:608 msgid "Old Name" msgstr "nombre antiguo" #: f.file.cc:609 f.process.cc:141 msgid "New Name" msgstr "nombre nuevo" #: f.file.cc:618 msgid "previous name" msgstr "nombre anterior" #: f.file.cc:619 msgid "Add 1" msgstr "Añadir 1" #: f.file.cc:622 f.file.cc:882 f.file.cc:1629 f.file.cc:1998 msgid "keep this dialog open" msgstr "mantener este diálogo abierto" #: f.file.cc:850 msgid "File Permissions" msgstr "Permisos de archivo" #: f.file.cc:854 f.meta.cc:833 f.meta.cc:1615 f.meta.cc:1818 msgid "File:" msgstr "Archivo:" #: f.file.cc:1000 msgid "Open Image File" msgstr "Abrir archivo de imagen" #: f.file.cc:1019 f.process.cc:654 msgid "unknown file type" msgstr "tipo de archivo desconocido" #: f.file.cc:1207 #, c-format msgid "Start of gallery, preceding gallery: %s" msgstr "Inicio de galería, precedente: %s" #: f.file.cc:1208 #, c-format msgid "End of gallery, following gallery: %s" msgstr "Fin de galería. siguiente: %s" #: f.file.cc:1355 msgid "Create Blank Image" msgstr "Crear una imagen vacía" #: f.file.cc:1357 f.meta.cc:1909 msgid "file name" msgstr "nombre de archivo" #: f.file.cc:1391 msgid "supply a file name" msgstr "proporcionar un nombre de archivo" #: f.file.cc:1570 msgid "Copy or Move Image File" msgstr "Copiar o mover archivo" #: f.file.cc:1615 msgid "New Location:" msgstr "Nueva ubicación" #: f.file.cc:1620 msgid "New Name:" msgstr "Nuevo nombre" #: f.file.cc:1625 msgid "copy (duplicate file)" msgstr "copiar (duplicar archivo)" #: f.file.cc:1626 msgid "move (remove original)" msgstr "mover (quitar original)" #: f.file.cc:1676 f.process.cc:608 f.process.cc:1574 f.process.cc:2155 msgid "Select folder" msgstr "seleccionar directorio" #: f.file.cc:1718 f.tools.cc:1488 msgid "new location is not a folder" msgstr "la nueva ubicación no es un directorio" #: f.file.cc:1737 msgid "new file extension missing or changed" msgstr "nueva extensión de archivo perdida o cambiada" #: f.file.cc:1771 f.file.cc:2075 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "fallo al borrar: \n" " %s" #: f.file.cc:1845 f.file.cc:3226 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "¿Sobreescribir archivo? \n" " %s" #: f.file.cc:1957 msgid "Delete/Trash Image File" msgstr "Eliminar/borrar archivo de imagen" #: f.file.cc:2029 msgid "GTK g_file_trash() function failed" msgstr "Ha fallado la función GTK g_file_trash()" #: f.file.cc:2048 msgid "not a known image file" msgstr "no es un archivo de imagen conocido" #: f.file.cc:2054 msgid "Delete read-only file?" msgstr "¿Borrar archivo de sólo lectura?" #: f.file.cc:2056 msgid "Trash read-only file?" msgstr "¿Archivo de papelera de sólo lectura?" #: f.file.cc:2272 #, c-format msgid "Kill active dialog? %s" msgstr "¿Matar diálogo activo? %s" #: f.file.cc:2322 f.widgets.cc:633 msgid "User Guide" msgstr "Guia de usuario" #: f.file.cc:2325 f.widgets.cc:634 msgid "Recent Changes" msgstr "Cambios recientes" #: f.file.cc:2328 f.widgets.cc:635 msgid "Edit Functions Overview" msgstr "Descripción general de las funciones de edición" #: f.file.cc:2334 f.widgets.cc:636 msgid "Change Log" msgstr "Registro de cambios" #: f.file.cc:2337 f.widgets.cc:637 msgid "Log File" msgstr "Archivo de registro" #: f.file.cc:2343 f.widgets.cc:639 msgid "Command Params" msgstr "Parámetros de comando" #: f.file.cc:2346 f.widgets.cc:640 msgid "Translations" msgstr "Traducciones" #: f.file.cc:2349 f.widgets.cc:641 msgid "Home Page" msgstr "Página web" #: f.file.cc:2352 f.widgets.cc:642 msgid "License" msgstr "Licencia" #: f.file.cc:2355 f.widgets.cc:643 msgid "Privacy" msgstr "Privacidad" #: f.file.cc:2358 f.widgets.cc:644 msgid "About" msgstr "Acerca de" #: f.file.cc:2366 f.widgets.cc:665 fotoxx.h:1366 msgid "Help" msgstr "Ayuda" #: f.file.cc:2414 msgid "Save Image File" msgstr "Guardar imagen" #: f.file.cc:2424 msgid "new version" msgstr "nueva versión" #: f.file.cc:2425 msgid "new file ..." msgstr "nuevo archivo ..." #: f.file.cc:2426 msgid "replace file" msgstr "reemplazar archivo" #: f.file.cc:2433 f.file.cc:3083 msgid "save as new file name or type" msgstr "guardar como un nuevo nombre o tipo de archivo" #: f.file.cc:2435 msgid "replace old file (OVERWRITE)" msgstr "reemplazar archivo antiguo (SOBREESCRIBIR)" #: f.file.cc:2486 msgid "cannot replace RAW file" msgstr "no se puede reemplazar al archivo RAW" #: f.file.cc:2491 msgid "cannot replace HEIC file" msgstr "no se puede reemplazar al archivo HEIC" #: f.file.cc:2496 msgid "cannot replace JP2 file" msgstr "" #: f.file.cc:2668 f.file.cc:2727 #, c-format msgid "" "file: %s \n" " exceed 99 versions" msgstr "" "archivo: %s \n" "excede 99 versiones" #: f.file.cc:2821 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:2831 msgid "cannot save as RAW type" msgstr "no se puede guardar como RAW" #: f.file.cc:2912 msgid "save anyway" msgstr "guardar de todos modos" #: f.file.cc:2980 f.file.cc:2982 msgid "Unable to copy EXIF/IPTC data" msgstr "No se pueden copiar los datos EXIF/IPTC" #: f.file.cc:3096 f.process.cc:1387 msgid "jpg quality" msgstr "calidad jpg" #: f.file.cc:3100 msgid "color depth:" msgstr "profundidad de color:" #: f.file.cc:3106 f.file.cc:3114 msgid "make current" msgstr "hacer actual" #: f.file.cc:3107 msgid "(new file becomes current file)" msgstr "(nuevo archivo será el archivo actual)" #: f.file.cc:3110 msgid "permissions:" msgstr "permisos" #: f.file.cc:3641 f.widgets.cc:432 f.widgets.cc:821 msgid "Permissions" msgstr "Permisos" #: f.file.cc:3646 fotoxx.cc:254 msgid "owner" msgstr "propietario" #: f.file.cc:3647 fotoxx.cc:255 msgid "group" msgstr "grupo" #: f.file.cc:3648 fotoxx.cc:256 msgid "other" msgstr "otro" #: f.gallery.cc:1699 f.gallery.cc:1719 msgid "recent images" msgstr "imágenes recientes" #: f.gallery.cc:1700 f.gallery.cc:1724 msgid "newest images" msgstr "imágenes más recientes" #: f.gallery.cc:1779 msgid "no albums found" msgstr "no se han encontrado albumes" #: f.gallery.cc:1789 f.gallery.cc:1803 msgid "Current Album" msgstr "Álbum actual" #: f.gallery.cc:1806 msgid "no current album" msgstr "álbum no actual" #: f.gallery.cc:1828 msgid "" " Reset all galleries\n" " to file name ascending" msgstr "" "Reiniciar todas las galerías\n" "a nombres de archivo ascendentes" #: f.gallery.cc:1849 msgid "Gallery Sort" msgstr "Ordenar galería " #: f.gallery.cc:1853 msgid "File Name" msgstr "Nombre de archivo" #: f.gallery.cc:1854 msgid "File Mod Date/Time" msgstr "Modo de archivo Fecha/Hora" #: f.gallery.cc:1855 msgid "Photo Date/Time (EXIF)" msgstr "Fecha/Hora de la foto (EXIF)" #: f.gallery.cc:1856 msgid "File Size (bytes)" msgstr "Tamaño de archivo (bytes)" #: f.gallery.cc:1857 msgid "Image Size (pixels)" msgstr "Tamño de imagen (píxeles)" #: f.gallery.cc:1859 msgid "ascending" msgstr "ascendente" #: f.gallery.cc:1860 msgid "descending" msgstr "descendente" #: f.gallery.cc:2759 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.gallery.cc:2783 f.widgets.cc:648 msgid "File" msgstr "Archivo" #: f.gallery.cc:3589 f.gallery.cc:3593 msgid "Image File" msgstr "Archivo de imagen" #: f.gallery.cc:3702 msgid "Select Image Files" msgstr "Seleccionar archivos de imagen" #: f.gallery.cc:3768 #, c-format msgid "exceed %d selected files" msgstr "excedidos %d archivos seleccionados" #: f.gallery.cc:3780 #, c-format msgid "remove %d duplicates?" msgstr "eliminar %d duplicados" #: f.gallery.cc:4229 f.widgets.cc:457 msgid "Bookmarks" msgstr "Marcas" #: f.gallery.cc:4229 f.gallery.cc:4337 msgid "Edit Bookmarks" msgstr "Añadir marcadores" #: f.gallery.cc:4311 msgid "" "Click a list position. Click a gallery thumbnail for the new bookmark.\n" "Bookmark for thumbnail will be added. Change the name and press [Rename]." msgstr "" "Clic en una posición de la lista. Clic en una miniatura de galería para " "nueva etiqueta.\n" "Serán añadidas etiquetas para miniaturas. Cambiar el nombre y pulsar " "[Renombrar]" #: f.gallery.cc:4514 msgid "unable to save bookmarks file" msgstr "incapaz de guardar archivo de marcadores" #: f.mashup.cc:212 msgid "Project name" msgstr "Nombre del proyecto" #: f.mashup.cc:216 msgid "Layout and background image" msgstr "Diseño e imagen de fondo" #: f.mashup.cc:220 msgid "choose an image file" msgstr "escoger una imagen" #: f.mashup.cc:221 msgid "use current image file" msgstr "usar imagen actual" #: f.mashup.cc:222 msgid "specify layout size and color" msgstr "especificar tamaño y color de la capa" #: f.mashup.cc:223 msgid "open a Mashup project file" msgstr "abrir un proyecto de fotomontaje" #: f.mashup.cc:238 msgid "enter a project name" msgstr "introducir un nombrede proyecto" #: f.mashup.cc:269 msgid "no current file" msgstr "no hay archivo activo" #: f.mashup.cc:296 msgid "Make Layout Image" msgstr "Hacer composición de imágenes" #: f.mashup.cc:403 msgid "Edit Images" msgstr "Editar imágenes" #: f.mashup.cc:404 f.mashup.cc:2433 msgid "Edit Text" msgstr "Editar texto" #: f.mashup.cc:405 msgid "Edit Line" msgstr "Editar línea" #: f.mashup.cc:406 msgid "Rescale" msgstr "Reescalar" #: f.mashup.cc:411 msgid "add or edit images" msgstr "añadir o editar imágenes" #: f.mashup.cc:413 msgid "add or edit text" msgstr "añadir o editar texto" #: f.mashup.cc:415 msgid "add or edit lines/arrows" msgstr "añadir o editar líneas/flechas" #: f.mashup.cc:417 msgid "change project scale" msgstr "cambiar escala del proyecto" #: f.mashup.cc:419 msgid "project complete" msgstr "proyecto completo" #: f.mashup.cc:421 msgid "cancel project" msgstr "cancelar proyecto" #: f.mashup.cc:439 msgid "rescale project" msgstr "reescalar proyecto" #: f.mashup.cc:493 msgid "save Mashup output file" msgstr "guardar archivo de salida del fotomontaje" #: f.mashup.cc:513 msgid "save Mashup project file?" msgstr "guardar archivo de proyecto del fotomontaje" #: f.mashup.cc:528 msgid "delete Mashup project file?" msgstr "¿borrar archivo de proyecto del fotomontaje?" #: f.mashup.cc:588 msgid "Open Project" msgstr "Abrir proyecto" #: f.mashup.cc:617 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "necesaria imagen de capa \n" " %s" #: f.mashup.cc:674 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "necesaria imagen superpuesta \n" " %s" #: f.mashup.cc:980 msgid "project file is defective" msgstr "archivo de proyecto defectuoso" #: f.mashup.cc:1010 msgid "Save Project" msgstr "Guardar proyecto" #: f.mashup.cc:1151 msgid "layout exceeds 2 gigabytes" msgstr "la composición excede de 2 gigabytes" #: f.mashup.cc:1221 msgid "Click image to select, drag image to move." msgstr "Clic en imagen para seleccionar, arrastrar para mover" #: f.mashup.cc:1222 msgid "Make black margins transparent" msgstr "Hacer transparentes los márgenes negros" #: f.mashup.cc:1254 msgid "Add Image" msgstr "Añadir imagen" #: f.mashup.cc:1261 msgid "Current image:" msgstr "Imagen activa" #: f.mashup.cc:1265 msgid "Cycle through images:" msgstr "Recorrido por las imágenes" #: f.mashup.cc:1272 msgid "Scale" msgstr "Escalar" #: f.mashup.cc:1281 msgid "Stacking Order" msgstr "Orden de pila" #: f.mashup.cc:1282 msgid "Raise" msgstr "Subir" #: f.mashup.cc:1283 msgid "Lower" msgstr "Bajar" #: f.mashup.cc:1286 msgid "Base Transparency" msgstr "Transparencia" #: f.mashup.cc:1290 msgid "Var. Transparency" msgstr "Variar transparencia" #: f.mashup.cc:1291 msgid "Paint" msgstr "Pintar" #: f.mashup.cc:1294 msgid "Bend and fine-align" msgstr "Mezclado y alineado fino" #: f.mashup.cc:1295 f.widgets.cc:661 msgid "Warp" msgstr "Deformación" #: f.mashup.cc:1304 zfuncs.cc:12896 zfuncs.cc:12905 msgid "Margins" msgstr "Márgenes" #: f.mashup.cc:1305 msgid "Hard" msgstr "Fuerte" #: f.mashup.cc:1306 msgid "Blend" msgstr "Mezclar" #: f.mashup.cc:1320 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:1810 msgid "Pull on the image with the mouse." msgstr "Empujar la imagen con el ratón" #: f.mashup.cc:1826 msgid "Warp Image" msgstr "Deformar imagen" #: f.mashup.cc:1832 f.warp.cc:1209 msgid "warp span" msgstr "intervalo de deformación" #: f.mashup.cc:2264 #, c-format msgid "exceeded %d images" msgstr "excedidas en %d imágenes" #: f.mashup.cc:2406 msgid "Enter text, [Add] to layout, edit properties." msgstr "Introducir texto, [Añadir] a la capa, editar propiedades" #: f.mashup.cc:2486 msgid "Text File:" msgstr "Archivo de texto:" #: f.mashup.cc:2490 msgid "add entered text to layout" msgstr "añadir texto a la capa" #: f.mashup.cc:2624 msgid "click position to add text" msgstr "clicar en la posición para añadir texto" #: f.mashup.cc:2630 #, c-format msgid "exceeded %d text entries" msgstr "Excedidas en %d entradas de texto" #: f.mashup.cc:2635 msgid "Add Text" msgstr "Añadir texto" #: f.mashup.cc:2744 msgid "Set line properties, [Add] to layout, edit." msgstr "Ajustar propiedades de línea, [Añadir] a la capa, editar." #: f.mashup.cc:2768 msgid "Edit Line/Arrow" msgstr "Editar línea/flecha" #: f.mashup.cc:2823 msgid "add line/arrow to layout" msgstr "añadir línea/flecha a la capa" #: f.mashup.cc:2915 msgid "click position to add line" msgstr "clic en la posició per afegir línia" #: f.mashup.cc:2921 #, c-format msgid "exceeded %d line entries" msgstr "excedidas %d entradas de línea" #: f.mashup.cc:2926 msgid "Add Line" msgstr "Añadir línea" #: f.mashup.cc:4171 msgid "Image Montage" msgstr "Montaje de imágenes" #: f.mashup.cc:4180 msgid "Frame Width" msgstr "Ancho de marco" #: f.mashup.cc:4183 f.mashup.cc:4191 msgid "Margin" msgstr "Margen" #: f.mashup.cc:4188 msgid "Image Columns" msgstr "Columnas de imagen" #: f.mashup.cc:4205 #, c-format msgid "exceed %d rows" msgstr "excedido en columnas %d" #: f.mashup.cc:4301 msgid "Optimize" msgstr "Optimizar" #: f.mashup.cc:4309 #, c-format msgid "column difference: %d pixels" msgstr "diferencia de columnas : %d píxeles" #: f.mashup.cc:4367 #, c-format msgid "" "column difference: %d pixels \n" "Make columns even?" msgstr "" "diferencia de columnas : %d píxeles \n" "¿Hacer columnas?" #: f.mashup.cc:4389 msgid "Save with unique montage name" msgstr "Guardar con nombtre d emontaje único" #: f.mashup.cc:4391 msgid "unique name:" msgstr "nombre único" #: f.mashup.cc:4393 msgid "create image map" msgstr "crear mapa de imágenes" #: f.mashup.cc:4410 f.meta.cc:9151 msgid "supply a reasonable name" msgstr "dar un nombre razonable" #: f.mashup.cc:4421 msgid "save montage" msgstr "guardar montaje" #: f.mashup.cc:4441 #, c-format msgid "map file saved: %s" msgstr "mapa de imágenes guardado: %s" #: f.mashup.cc:4486 #, c-format msgid "%d max images exceeded" msgstr "%d máximo de imágenes excedido" #: f.mashup.cc:4560 f.mashup.cc:4566 #, c-format msgid "image frame is too large: %d x %d" msgstr "marco de imagen demasiado ancho: %d x %d" #: f.mashup.cc:4871 #, c-format msgid "montage map file invalid: %s" msgstr "archivo de montaje de imágenes inválido: %s" #: f.meta.cc:245 msgid "Add Metadata Items" msgstr "Añadir items de metadatos" #: f.meta.cc:249 f.meta.cc:1609 f.meta.cc:3190 msgid "click to select" msgstr "clic para seleccionar" #: f.meta.cc:254 msgid "click to unselect" msgstr "clic para deselecionar" #: f.meta.cc:476 msgid "Extras" msgstr "Extras" #: f.meta.cc:476 msgid "View Metadata" msgstr "Ver metadatos" #: f.meta.cc:661 msgid "View All Metadata" msgstr "Ver todos los mnetadatos" #: f.meta.cc:826 msgid "Edit Metadata" msgstr "Editar metadatos" #: f.meta.cc:829 msgid "save metadata to file" msgstr "guardar metadatos en el archivo" #: f.meta.cc:838 msgid "Image Date" msgstr "Fecha de la imagen" #: f.meta.cc:841 msgid "Time" msgstr "Hora" #: f.meta.cc:849 msgid "Rating (stars):" msgstr "Calificación (estrellas)" #: f.meta.cc:861 msgid "Caption" msgstr "Título" #: f.meta.cc:866 msgid "Comments" msgstr "Comentarios" #: f.meta.cc:873 msgid "Location" msgstr "Ubicación" #: f.meta.cc:876 msgid "Country" msgstr "Pais" #: f.meta.cc:899 msgid "Image Tags" msgstr "Etiquetas de la imagen" #: f.meta.cc:905 msgid "Recent Tags" msgstr "etiquetas recientes" #: f.meta.cc:911 f.meta.cc:2081 msgid "Enter New Tag" msgstr "Introduzca nueva etiqueta" #: f.meta.cc:917 f.meta.cc:2087 f.meta.cc:4750 msgid "Matching Tags" msgstr "Construyendo etiquetas" #: f.meta.cc:925 f.meta.cc:2096 f.meta.cc:2556 f.meta.cc:4757 msgid "Defined Tags Category" msgstr "Categoría de etiquetas definida" #: f.meta.cc:933 msgid "search known locations" msgstr "buscar localidades conocidas" #: f.meta.cc:934 msgid "search using web service" msgstr "buscar usando servicio web" #: f.meta.cc:1384 f.meta.cc:3800 f.meta.cc:7709 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "latitud/longitud incorrecta: %s %s" #: f.meta.cc:1441 f.widgets.cc:477 msgid "Manage Tags" msgstr "Administrar etiquetas" #: f.meta.cc:1441 msgid "orphan tags" msgstr "etiquetas huérfanas" #: f.meta.cc:1445 msgid "category" msgstr "categoria" #: f.meta.cc:1448 msgid "tag" msgstr "etiqueta" #: f.meta.cc:1455 msgid "Defined Tags:" msgstr "etiquetas definidas:" #: f.meta.cc:1605 msgid "Edit Any Metadata" msgstr "Editar cualquier metadato" #: f.meta.cc:1605 f.meta.cc:3185 msgid "Full List" msgstr "Lista completa" #: f.meta.cc:1619 f.meta.cc:3202 msgid "key name" msgstr "nombre clave" #: f.meta.cc:1622 f.meta.cc:3203 msgid "key value" msgstr "valor clave" #: f.meta.cc:1815 msgid "Delete Metadata" msgstr "Borrar metadatos" #: f.meta.cc:1821 fotoxx.h:1317 msgid "All" msgstr "Todo" #: f.meta.cc:1822 msgid "One Key:" msgstr "Una clave:" #: f.meta.cc:1908 msgid "choose options" msgstr "elegir opciones" #: f.meta.cc:1910 msgid "caption" msgstr "título" #: f.meta.cc:1911 msgid "comment" msgstr "comentario" #: f.meta.cc:2057 msgid "Batch Add/Remove Tags" msgstr "Añadir/eliminar etiquetas en lote" #: f.meta.cc:2070 msgid "tags to add" msgstr "etiquetas para añadir" #: f.meta.cc:2071 msgid "tags to remove" msgstr "etquetas para eliminar" #: f.meta.cc:2176 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " demasiadas etiquetas" #: f.meta.cc:2191 msgid "repeat with same files?" msgstr "¿repetir con los miosmos archivos?" #: f.meta.cc:2331 msgid "specify files and tags" msgstr "especificar archivos y etiquetas" #: f.meta.cc:2535 f.widgets.cc:596 msgid "Batch Rename Tags" msgstr "Renombrar etiquetas en lote" #: f.meta.cc:2545 msgid "(click defined tag)" msgstr "(clic etiquetas definidas)" #: f.meta.cc:2547 f.process.cc:815 msgid "Rename to" msgstr "Renombrar como" #: f.meta.cc:2564 msgid "old tag name >> new tag name" msgstr "antiguo nombre de etiqueta >> nuevo nombre de etiqueta" #: f.meta.cc:2620 #, 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:2774 msgid "max tags exceeded" msgstr "Máximo de etiquetas excedido" #: f.meta.cc:2843 f.meta.cc:2978 msgid "Batch Photo Date/Time" msgstr "Fecha/Hora de fotos en lote" #: f.meta.cc:2851 msgid "set a new date/time:" msgstr "establecer nuevos fecha/hora" #: f.meta.cc:2859 msgid "shift existing date/time:" msgstr "cambiar fecha/hora existente" #: f.meta.cc:2885 msgid "test: show changes, do not update files" msgstr "probar: mostrar cambios, no actualizar archivos" #: f.meta.cc:2910 msgid "please make a choice" msgstr "por favor haga una elección" #: f.meta.cc:2915 f.meta.cc:3276 f.meta.cc:3467 msgid "no files selected" msgstr "no hay archivos seleccionados" #: f.meta.cc:2945 f.meta.cc:2955 f.meta.cc:2961 msgid "invalid date/time format" msgstr "formato inválido de fecha/hora" #: f.meta.cc:3185 msgid "Batch Add/Change Metadata" msgstr "Añadir/cambiar metadatos en lote" #: f.meta.cc:3270 msgid "enter key names" msgstr "entrar nombres clave" #: f.meta.cc:3353 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:3450 msgid "Batch Report Metadata" msgstr "Informe de metadatos en lote" #: f.meta.cc:3456 msgid "list of reported metadata items" msgstr "lista de elementos de metadatos informados" #: f.meta.cc:3599 f.widgets.cc:600 msgid "Batch Geotags" msgstr "Geoetiquetas en lote" #: f.meta.cc:3640 msgid "location" msgstr "ubicación" #: f.meta.cc:3643 msgid "country" msgstr "pais" #: f.meta.cc:3676 msgid "Adding Geotags" msgstr "Añdiendo geoetiquetas" #: f.meta.cc:3785 msgid "" "data is incomplete \n" " proceed?" msgstr "" "los datos están incompletos \n" " ¿proceder?" #: f.meta.cc:3873 msgid "Report Image Locations" msgstr "informe de localizaciones de imagen" #: f.meta.cc:3874 msgid "Group by country" msgstr "Agrupar por país" #: f.meta.cc:3875 msgid "Group by country/location" msgstr "Agrupar por país/ubicación" #: f.meta.cc:3876 msgid "Group by country/location/date" msgstr "Agrupar por país/ubicación/fecha" #: f.meta.cc:3877 msgid "Group by date/country/location" msgstr "Agrupar por fecha/país/ubicación" #: f.meta.cc:3880 msgid "Combine within" msgstr "Combinar dentro" #: f.meta.cc:3882 msgid "days" msgstr "dias" #: f.meta.cc:3994 msgid "Image Locations" msgstr "Ubicación de imágenes" #: f.meta.cc:4263 msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" msgstr "Ene Feb Mar Abr May Jun Jul Ago Sep Oct Nov Dic" #: f.meta.cc:4655 msgid "Search Images" msgstr "Buscar imágenes" #: f.meta.cc:4659 msgid "images to search:" msgstr "imágenes a buscar:" #: f.meta.cc:4660 msgid "all" msgstr "todo" #: f.meta.cc:4661 msgid "current set only" msgstr "sólo selección actual" #: f.meta.cc:4664 msgid "matching images:" msgstr "imágenes coincidentes" #: f.meta.cc:4665 msgid "make new set" msgstr "haga un nuevo juego" #: f.meta.cc:4666 msgid "add to set" msgstr "añadir a la selección" #: f.meta.cc:4667 msgid "remove" msgstr "eliminar" #: f.meta.cc:4672 msgid "select last version only" msgstr "seleccionar sólo la última versión" #: f.meta.cc:4673 msgid "original + last version" msgstr "original + última versión" #: f.meta.cc:4675 msgid "original + all versions" msgstr "original + todas las versiones" #: f.meta.cc:4676 f.process.cc:166 f.process.cc:172 f.process.cc:180 #: f.process.cc:2058 msgid "no change" msgstr "sin cambios" #: f.meta.cc:4681 msgid "report type:" msgstr "tipo de imforme:" #: f.meta.cc:4682 msgid "gallery" msgstr "galería" #: f.meta.cc:4688 msgid "date range" msgstr "rango de fechas" #: f.meta.cc:4693 msgid "photo date" msgstr "fecha de la foto" #: f.meta.cc:4694 msgid "file date" msgstr "fecha del archivo" #: f.meta.cc:4695 msgid "(yyyy-mm-dd)" msgstr "(aaaa-mm-dd)" #: f.meta.cc:4700 msgid "rating range" msgstr "rango de calificación" #: f.meta.cc:4708 msgid "all/any" msgstr "todo/cualquiera" #: f.meta.cc:4711 msgid "search tags" msgstr "buscar etiquetas" #: f.meta.cc:4718 msgid "search text" msgstr "buscar texto" #: f.meta.cc:4724 msgid "search files" msgstr "buscar archivos" #: f.meta.cc:4730 msgid "search locations" msgstr "buscar localidades" #: f.meta.cc:4734 msgid "enter cities, countries" msgstr "introducir ciudades, paises" #: f.meta.cc:4739 msgid "search other metadata" msgstr "buscar otros metadatos" #: f.meta.cc:4746 msgid "Enter Search Tag" msgstr "Introducir etiqueta de búsqueda" #: f.meta.cc:5060 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:5067 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:5135 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "búsqueda de datos no razonable \n" " %s %s" #: f.meta.cc:5160 msgid "stars range not reasonable" msgstr "rango de estrellas no razonable" #: f.meta.cc:5718 msgid "Always reported: date, stars, tags, caption, comment" msgstr "Siempre informado: fecha, estrellas, etiquetas, título, comentario" #: f.meta.cc:5750 msgid "Additional Items for Report" msgstr "Elementos adicionales para el informe" #: f.meta.cc:5757 msgid "Keyword" msgstr "Palabra clave" #: f.meta.cc:5771 msgid "Match Criteria" msgstr "Criterio de coincidencia" #: f.meta.cc:5904 #, c-format msgid "bad number: %s" msgstr "número erróneo: %s" #: f.meta.cc:6184 msgid "date format is YYYY-MM-DD" msgstr "Formato de fecha es AAAA-MM-DD" #: f.meta.cc:6188 msgid "date is invalid" msgstr "fecha no válida" #: f.meta.cc:6226 msgid "time format is HH:MM [:SS]" msgstr "formato de hora es HH:MM [:SS]" #: f.meta.cc:6230 msgid "time is invalid" msgstr "hora no válida" #: f.meta.cc:7328 msgid "not found" msgstr "no encontrada" #: f.meta.cc:7329 msgid "location and country required" msgstr "ubicación y pais requeridos" #: f.meta.cc:7586 msgid "choose location" msgstr "escoger ubicación" #: f.meta.cc:7886 msgid "Set Map Markers" msgstr "Establecer marcadores de mapa" #: f.meta.cc:7887 msgid "mark all image files" msgstr "marcar todas las imágenes" #: f.meta.cc:7888 msgid "mark current gallery" msgstr "marcar galería actual" #: f.meta.cc:7989 msgid "choose map file" msgstr "elegir archivo de mapas" #: f.meta.cc:8133 msgid "" "fotoxx-maps package not installed \n" "(see https://kornelix.net)" msgstr "" "fotoxx-maps package not instalado \n" "(ver https://kornelix.net)" #: f.meta.cc:8138 #, c-format msgid "map file %s is missing" msgstr "necesario archivo de mapas %s" #: f.meta.cc:8142 #, 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:8468 f.meta.cc:9020 msgid "No matching images found" msgstr "No encontradas imagenes coincidentes" #: f.meta.cc:8562 msgid "Net Map Source" msgstr "Origen del mapa de red" #: f.meta.cc:9098 msgid "Net Map Locations" msgstr "Ubicaciones Net Map" #: f.meta.cc:9104 msgid "map location:" msgstr "ubicación en mapa" #: f.pixmap.cc:3227 f.pixmap.cc:3279 msgid "HEIF files not supported" msgstr "archivos HEIF no soportados" #: f.pixmap.cc:3351 f.pixmap.cc:3402 msgid "JP2 files not supported" msgstr "" #: f.process.cc:134 f.widgets.cc:588 msgid "Batch Convert" msgstr "Convertir en lote" #: f.process.cc:145 msgid "Sequence Numbers" msgstr "Números secuenciales" #: f.process.cc:147 msgid "base" msgstr "inicio" #: f.process.cc:150 msgid "adder" msgstr "incremento" #: f.process.cc:155 msgid "New Location" msgstr "Nueva ubicación" #: f.process.cc:160 msgid "New File Type" msgstr "Nuevo tipo de archivo" #: f.process.cc:169 f.process.cc:1390 msgid "Color Depth:" msgstr "Profundidad de color:" #: f.process.cc:175 f.process.cc:2053 msgid "max. Width" msgstr "Ancho máx." #: f.process.cc:184 msgid "Delete Originals" msgstr "Borrar originales" #: f.process.cc:185 f.process.cc:821 msgid "Copy Metadata" msgstr "Copiar metadatos" #: f.process.cc:199 f.process.cc:824 msgid "Overlay Image" msgstr "Imagen de capa" #: f.process.cc:202 f.warp.cc:4547 msgid "Width %" msgstr "Ancho %" #: f.process.cc:207 msgid "Position" msgstr "Posición" #: f.process.cc:219 msgid "Make constant size for screen:" msgstr "Tamaño constante para la pantalla" #: f.process.cc:227 msgid "" "plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s" msgstr "" "plugins: (año mes día nombre-antiguo secuencia) $aaaa $mm $dd $nombreantiguo " "$s" #: f.process.cc:371 #, c-format msgid "file type not supported: %s \n" msgstr "tipo de archivo no soportado: %s \n" #: f.process.cc:507 f.process.cc:2116 msgid "cannot create new file" msgstr "no se puede crear el nuevo archivo" #: f.process.cc:554 msgid "updating albums ..." msgstr "actualizando álbums ..." #: f.process.cc:732 #, c-format msgid "invalid plugin: %s" msgstr "plugin no válido: %s" #: f.process.cc:739 msgid "you must use either $s or $oldname" msgstr "debe utilizar $s o $nombreantiguo" #: f.process.cc:744 msgid "$s plugin needs base and adder" msgstr "el plugin $s necesita una base y un incremento" #: f.process.cc:749 msgid "base and adder need $s plugin" msgstr "la base y el incremento necesitan $s plugin" #: f.process.cc:763 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "el tamaño máximo %d x %d no es razonable" #: f.process.cc:770 msgid "specify overlay image file" msgstr "especificar imagen de capa" #: f.process.cc:786 msgid "specify overlay position" msgstr "especificar posición de capa" #: f.process.cc:814 #, c-format msgid "Convert %d image files" msgstr "Convertit %d imágenes" #: f.process.cc:816 msgid "Convert to" msgstr "Convertir a" #: f.process.cc:818 msgid "Resize within" msgstr "Redimensionar dentro de " #: f.process.cc:819 msgid "Output to" msgstr "Salida como" #: f.process.cc:825 msgid "*** Delete Originals ***" msgstr "*** Borrar originales ***" #: f.process.cc:826 msgid "*** Replace Originals ***" msgstr "*** Reemplazar originales ***" #: f.process.cc:827 msgid "PROCEED?" msgstr "PROCEDER?" #: f.process.cc:983 f.widgets.cc:589 msgid "Batch Upright" msgstr "Enderezar en lote" #: f.process.cc:989 msgid "Survey all files" msgstr "Examinar todos los archivos" #: f.process.cc:1029 msgid "file cannot be read" msgstr "no se puede leer el archivo" #: f.process.cc:1146 msgid "cannot select both options" msgstr "no se pueden seleccionar ambas opciones" #: f.process.cc:1187 f.widgets.cc:590 msgid "Batch Delete/Trash" msgstr "Borrar/papelera en lote" #: f.process.cc:1192 msgid "delete" msgstr "borrar" #: f.process.cc:1195 msgid "trash" msgstr "papelera" #: f.process.cc:1258 msgid "Purging deleted files from albums \n" msgstr "Eliminando archivos borrados de los álbumes \n" #: f.process.cc:1338 msgid "Batch Convert RAW Files" msgstr "Convertir en lote archivos RAW" #: f.process.cc:1377 msgid "output location" msgstr "ubicación exterior" #: f.process.cc:1382 msgid "File Type" msgstr "Tipo de archivo" #: f.process.cc:1406 msgid "amount" msgstr "cantidad" #: f.process.cc:1409 msgid "threshold" msgstr "umbral" #: f.process.cc:1413 msgid "Fix dead pixels" msgstr "Corregir píxeles muertos" #: f.process.cc:1416 msgid "dead pixel map file" msgstr "archivo de mapa de píxeles mnuertos" #: f.process.cc:1419 msgid "Fix pixel bias" msgstr "Corregir sesgo de píxeles" #: f.process.cc:1422 msgid "pixel bias map file" msgstr "archivo de mapa de sesgo de píxeles" #: f.process.cc:1727 msgid "growisofs not installed" msgstr "growisofs no instalado" #: f.process.cc:1774 msgid "no DVD/BlueRay device found" msgstr "No encontrado dispositivo DVDE/Blue Ray" #: f.process.cc:1797 msgid "Burn Images to DVD/BlueRay" msgstr "Grabar imágenes en un DVD/Blue Ray" #: f.process.cc:1802 msgid "Select device" msgstr "Seleccionar dispositivo" #: f.process.cc:1889 f.widgets.cc:355 msgid "Create a file of selected image files" msgstr "Crear un listado de las imágenes seleccionadas" #: f.process.cc:1915 f.process.cc:1984 msgid "Output File" msgstr "Archivo de salida" #: f.process.cc:1936 msgid "no input files selected" msgstr "No seleccionados archivos de entrada" #: f.process.cc:1942 msgid "no output file selected" msgstr "No seleccionado archivo de salida" #: f.process.cc:2044 f.widgets.cc:594 msgid "Export Files" msgstr "Exportar imágenes" #: f.process.cc:2049 msgid "To Location" msgstr "A ubicación" #: f.process.cc:2060 msgid "export metadata" msgstr "exportar metadatos" #: f.process.cc:2091 msgid "file type not supported" msgstr "tipo de archivo no soportado" #: f.process.cc:2173 msgid "location is not a folder" msgstr "La ubicación no es un directorio" #: f.process.cc:2225 msgid "Script Files" msgstr "Archivos de script" #: f.process.cc:2229 msgid "begin making a script file" msgstr "iniciando construcción de un script" #: f.process.cc:2232 msgid "finish making a script file" msgstr "acabando construcción de un script" #: f.process.cc:2276 msgid "script already started" msgstr "script iniciado" #: f.process.cc:2280 msgid "start a new script file" msgstr "iniciar un nuevo script" #: f.process.cc:2301 msgid "perform edits to be included in the script file" msgstr "performar ediciones a ser incluidas en el script" #: f.process.cc:2318 msgid "script file error" msgstr "error de script" #: f.process.cc:2323 #, c-format msgid "%s added to script" msgstr "%s añadido al script" #: f.process.cc:2333 msgid "no script file was started" msgstr "no se ha iniciado el script" #: f.process.cc:2341 msgid "script file closed" msgstr "script cerrado" #: f.process.cc:2374 f.process.cc:2395 msgid "no script files found" msgstr "no encontrados archivos script" #: f.process.cc:2418 #, c-format msgid "" "script error: %s \n" " %s" msgstr "" "script error: %s \n" " %s" #: f.process.cc:2440 #, c-format msgid "unknown edit function: %s" msgstr "función de edición desconocida: %s" #: f.process.cc:2452 #, c-format msgid "load widgets failed: %s" msgstr "cargar complementos fallados: %s" #: f.process.cc:2468 #, c-format msgid "script file format error: %s" msgstr "error de formato de script: %s" #: f.process.cc:2494 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "fallo de apertura: %s \n" " %s" #: f.process.cc:2511 msgid "script complete" msgstr "script completo" #: f.process.cc:2553 f.widgets.cc:603 msgid "Batch Script" msgstr "Script de lotes" #: f.process.cc:2560 msgid "Select Script" msgstr "Seleccionar script" #: f.process.cc:2561 msgid "script file to run" msgstr "archivo script a ejecutar" #: f.tools.cc:93 msgid "Folders for image files (subfolders included automatically)." msgstr "" "Dirwectorios para archuvos de imagen (subdirectorios incluidos " "automaticamente" #: f.tools.cc:95 msgid "Select to add, click on X to delete." msgstr "Seleccionar para añadir, clic en X para borrar." #: f.tools.cc:96 msgid "folder for thumbnails" msgstr "directorio para las miniaturas" #: f.tools.cc:97 msgid "extra metadata items to include in index" msgstr "ítems extra de metadatos para incluir en el índice" #: f.tools.cc:98 msgid "force a full re-index of all image files" msgstr "forzar un reindexado total de todas las imágenes" #: f.tools.cc:99 msgid "" "Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Función de indexado terminada. \n" "Se requiere el indexado para las búsquedas y funciones de mapa \n" "y para hacer las páginas de galería aceptablemente rápidas." #: f.tools.cc:136 msgid "Index Image Files" msgstr "Indexar imágenes" #: f.tools.cc:324 msgid "Choose top image folders" msgstr "Seleccionar el directorio raiz de imágenes" #: f.tools.cc:325 msgid "Choose thumbnail folder" msgstr "Elegir directorio de miniaturas" #: f.tools.cc:326 msgid "" "All image files will be re-indexed. \n" " Continue?" msgstr "" "Todos los archivos de imagen serán reindexados. \n" " ¿Continuar?" #: f.tools.cc:442 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:448 #, c-format msgid "" "Thumbnails folder: %s \n" "Please remove." msgstr "" "Carpeta de miniaturas: %s \n" "Por favor eliminar." #: f.tools.cc:451 #, c-format msgid "" "Thumbnails folder: \n" " %s \n" "must be named .../thumbnails" msgstr "" "Directorio de miniaturas: \n" " %s \n" "debe llamarse .../thumbnails" #: f.tools.cc:454 #, c-format msgid "" "Duplicate or nested folders: \n" " %s \n" " %s \n" "Please remove." msgstr "" "Carpetas duplicadas o anidadas: \n" "%s \n" "%s \n" "Por favor eliminar." #: f.tools.cc:537 msgid "specify at least 1 top image folder" msgstr "especificar como mínimo 1 carpeta superior " #: f.tools.cc:542 msgid "specify 1 thumbnail folder" msgstr "Especificar un directorio de miniaturas" #: f.tools.cc:649 msgid "build index" msgstr "vaciar índice" #: f.tools.cc:778 msgid "Top folders have no images" msgstr "Directorios superiores no tienen imágenes" #: f.tools.cc:785 #, c-format msgid "" "0 old files found, %d new files found.\n" "A full re-index is required. Continue?" msgstr "" "Se encontraron 0 archivos antiguos, se encontraron %d archivos nuevos. \n" "Se necesita un nuevo índexado completo. ¿Continuar?" #: f.tools.cc:1286 msgid "Cancel image index function?" msgstr "Cancelar función de indexado de imágenes" #: f.tools.cc:1447 #, c-format msgid "" "Do you want to move Fotoxx home? \n" " from: %s \n" " to: %s" msgstr "" "¿Quiere mover Fotoxx de directorio? \n" " desde: %s \n" " a: %s" #: f.tools.cc:1449 msgid "moving files ..." msgstr "moviendo archivos ..." #: f.tools.cc:1473 msgid "new Fotoxx home folder" msgstr "nueva carpeta de inicio de Fotoxx" #: f.tools.cc:1493 msgid "new location name contains a space" msgstr "el nombre de la nueva ubicación contiene un espacio" #: f.tools.cc:1533 msgid "index_config file has no thumbnails folder" msgstr "el archivo index_cogfig no tiene directorio de miniaturas" #: f.tools.cc:1541 msgid "update thumbnail folder failed" msgstr "ha fallado la actualización del directorio de miniaturas" #: f.tools.cc:1544 msgid "thumbnails folder not changed" msgstr "no ha cambiado el directorio de miniaturas" #: f.tools.cc:1545 #, c-format msgid "new thumbnails folder: %s/thumbnails" msgstr "nuevo directorio de miniaturas: %s/thumbnails" #: f.tools.cc:1556 msgid "Fotoxx will restart" msgstr "Se reiniciará Fotoxx" #: f.tools.cc:1572 msgid "Recent Files Gallery" msgstr "Galería de archivos recientes" #: f.tools.cc:1573 msgid "Newest Files Gallery" msgstr "Galería de archivos más recientes" #: f.tools.cc:1574 msgid "Specific Gallery" msgstr "Galería específica" #: f.tools.cc:1575 msgid "Album Gallery" msgstr "Galería de álbumes" #: f.tools.cc:1576 msgid "Previous Gallery" msgstr "Galería previa" #: f.tools.cc:1577 msgid "Previous File" msgstr "Imagen anterior" #: f.tools.cc:1578 msgid "Specific File" msgstr "Imagen específica" #: f.tools.cc:1579 f.widgets.cc:434 msgid "Blank Window" msgstr "Ventana vacía" #: f.tools.cc:1604 #, c-format msgid "%c of scale" msgstr "%c de escala" #: f.tools.cc:1647 msgid "Preferences and Settings" msgstr "Ptreferncias y ajustes" #: f.tools.cc:1650 msgid "Startup Display:" msgstr "Pantalla de inicio" #: f.tools.cc:1656 msgid "Background Colors:" msgstr "Colores de fondo" #: f.tools.cc:1658 msgid "F-View" msgstr "Vista de archivos" #: f.tools.cc:1661 msgid "G-View" msgstr "Vista de Galería" #: f.tools.cc:1665 msgid "Menu Style:" msgstr "Estilo de menú" #: f.tools.cc:1667 msgid "Icons" msgstr "Iconos" #: f.tools.cc:1669 msgid "Both" msgstr "Ambos" #: f.tools.cc:1671 msgid "Icon size" msgstr "Tamaño de icono" #: f.tools.cc:1675 msgid "Menu Colors:" msgstr "Colores de menú" #: f.tools.cc:1684 msgid "Dialog Font:" msgstr "Fuente de diálogos" #: f.tools.cc:1703 msgid "JPEG file save quality:" msgstr "Calidad de guardado de archivos JPEG:" #: f.tools.cc:1706 msgid "(90 = high quality)" msgstr "90 = alta calidad" #: f.tools.cc:1709 msgid "TIFF file compression method" msgstr "método de compresión de archivo TIFF" #: f.tools.cc:1715 msgid "PNG file compression level" msgstr "nivel de compresión de archivo PNG" #: f.tools.cc:1721 msgid "Curve Node Separation, Capture Range:" msgstr "Separación de nodos de curva, rango de captura:" #: f.tools.cc:1727 msgid "Map Marker Size:" msgstr "Tamaño del marcador del mapa:" #: f.tools.cc:1733 msgid "Show Images (F-view, G-view):" msgstr "Mostrar imágenes (vista F, vista G):" #: f.tools.cc:1735 msgid "last version only" msgstr "sólo última versión" #: f.tools.cc:1736 msgid "all images" msgstr "todas las imágenes" #: f.tools.cc:1739 msgid "Image Position in Window:" msgstr "Posición de imagen en la ventana" #: f.tools.cc:1741 msgid "centered" msgstr "centrada" #: f.tools.cc:1742 msgid "right side" msgstr "derecha" #: f.tools.cc:1745 msgid "Image Index Level:" msgstr "Nivel de índice de imagen:" #: f.tools.cc:1749 msgid "Fotoxx started directly (2)" msgstr "Fotoxx iniciado directamente (2)" #: f.tools.cc:1753 msgid "Fotoxx started by file manager (1)" msgstr "Fotoxx iniciado por el administrador de archivos (1)" #: f.tools.cc:1756 msgid "RAW File Loader:" msgstr "Cargador de archivos RAW:" #: f.tools.cc:1762 msgid "RAW Conversion Options:" msgstr "Opciones de conversión RAW" #: f.tools.cc:1765 msgid "extend dynamic range for dim images" msgstr "rango dinámico extendido para imágenes dim" #: f.tools.cc:1768 msgid "use embedded image as a guide" msgstr "usar imagen incrustada como guía" #: f.tools.cc:1771 msgid "RAW File Types:" msgstr "Tipos de archivos RAW" #: f.tools.cc:1776 msgid "Video File Types:" msgstr "Tipo de archivos de vídeo" #: f.tools.cc:1781 msgid "Video File Play Command:" msgstr "Comando de reproducción de archivos de vídeo" #: f.tools.cc:1937 msgid "Select startup folder" msgstr "Seleccionar el directorio de inicio" #: f.tools.cc:1944 msgid "Select startup image file" msgstr "Seleccionar el archivo de imagen de inicio" #: f.tools.cc:1951 msgid "Select startup album" msgstr "Seleccionar álbum de inicio" #: f.tools.cc:1976 msgid "rawtherapee-cli (rawtherapee) is not installed" msgstr "cliente Rawtherapee (rawtherapee) no está instalado" #: f.tools.cc:2006 msgid "startup folder is invalid" msgstr "El directorio de inicio no es correcto" #: f.tools.cc:2016 msgid "startup file is invalid" msgstr "El archivo de inicio no es correcto" #: f.tools.cc:2216 f.widgets.cc:372 msgid "Keyboard Shortcuts" msgstr "Atajos de teclado" #: f.tools.cc:2222 msgid "Reserved Shortcuts \n" msgstr "Atajos de teclado reservado \n" #: f.tools.cc:2223 msgid " Z Toggle 1x / fit window \n" msgstr " Z Alternar x1 / ajustar a pantalla \n" #: f.tools.cc:2224 msgid " F1 User Guide, Context Help \n" msgstr " F1 Guía de usuario, ayuda contextual \n" #: f.tools.cc:2225 msgid " F10 Full Screen with menus \n" msgstr " F10 Pantalla completa con menús \n" #: f.tools.cc:2226 msgid " F11 Full Screen without menus \n" msgstr " F11 Pantalla completa sin menús \n" #: f.tools.cc:2227 msgid " Escape Quit dialog, Quit Fotoxx \n" msgstr " Escape Cerrar diálogo. Cerrar fotoxx \n" #: f.tools.cc:2228 msgid " Delete Delete/Trash \n" msgstr " Delete Borrar/Papelera \n" #: f.tools.cc:2229 msgid " Arrow keys Navigation \n" msgstr "Teclas de flecha Navegación \n" #: f.tools.cc:2230 msgid " Page keys Navigation \n" msgstr "Teclas de página Navegación \n" #: f.tools.cc:2231 msgid " Home/End Navigation \n" msgstr "Inicio/Final Navegación \n" #: f.tools.cc:2295 msgid "Edit KB Shortcuts" msgstr "Editar atajos de teclado" #: f.tools.cc:2302 msgid "shortcut key:" msgstr "atajo de teclado" #: f.tools.cc:2303 msgid "(enter key)" msgstr "(entrar tecla)" #: f.tools.cc:2304 f.tools.cc:2451 f.tools.cc:2554 msgid "(no selection)" msgstr "(nada seleccionado)" #: f.tools.cc:2445 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Reservada, no se puede utilizar" #: f.tools.cc:2672 msgid "Brightness Distribution" msgstr "Histograma de brillo" #: f.tools.cc:2866 msgid "" "Drag mouse on image. \n" "Left click to cancel." msgstr "" "Arrastrar el mouse sobre la imagen. \n" "Clic izquierdo para cancelar." #: f.tools.cc:2893 f.widgets.cc:612 msgid "Magnify Image" msgstr "Aumentar imagen" #: f.tools.cc:2902 msgid "X-size" msgstr "Lupa" #: f.tools.cc:3210 msgid "Find Duplicate Images" msgstr "Buscar imágenes duplicadas" #: f.tools.cc:3213 msgid "All files" msgstr "Todos lo0s archivos" #: f.tools.cc:3214 msgid "Current gallery" msgstr "Galería actual" #: f.tools.cc:3217 msgid "File count:" msgstr "Número de archivos" #: f.tools.cc:3221 msgid "Thumbnail size" msgstr "Tamaño de miniatura" #: f.tools.cc:3226 msgid "Pixel difference" msgstr "Diferencia de píxeles" #: f.tools.cc:3229 msgid "Pixel count" msgstr "Número de píxeles" #: f.tools.cc:3237 msgid "Duplicates:" msgstr "Duplicados:" #: f.tools.cc:3567 msgid "too many files, cannot continue" msgstr "demasiados archivos, no se puede continuar" #: f.tools.cc:3647 msgid "Click image to select pixels." msgstr "Pulsar en la imagen para seleccionar los píxeles." #: f.tools.cc:3690 f.widgets.cc:614 msgid "Show RGB" msgstr "Mostrar RGB" #: f.tools.cc:3948 msgid "Change Color Profile" msgstr "Cambiar perfil de color" #: f.tools.cc:3952 msgid "input profile" msgstr "perfil de entrada" #: f.tools.cc:3956 msgid "output profile" msgstr "perfil de salida" #: f.tools.cc:3977 msgid "Unable to change EXIF color profile" msgstr "No se puede cambiar el perfil de color EXIF" #: f.tools.cc:3979 msgid "automatic new version created" msgstr "nueva versión creada automáticamente" #: f.tools.cc:3988 msgid "color profile" msgstr "perfil de color" #: f.tools.cc:4036 f.tools.cc:4042 #, c-format msgid "unknown cms profile %s" msgstr "perfil cms desconocido %s" #: f.tools.cc:4144 f.widgets.cc:616 msgid "Calibrate Printer" msgstr "Calibrar impresora" #: f.tools.cc:4170 msgid "print color chart" msgstr "imprimir carta de colores" #: f.tools.cc:4171 msgid "scan and save color chart" msgstr "escanear y guardar carta de colores" #: f.tools.cc:4172 msgid "align and trim color chart" msgstr "alinear y recortar carta de colores" #: f.tools.cc:4173 msgid "open and process color chart" msgstr "abrir y procesar carta de colores" #: f.tools.cc:4174 msgid "print image with revised colors" msgstr "imprimir imagen con colores revisados" #: f.tools.cc:4315 #, c-format msgid "" "Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/" msgstr "" "Escanear la carta de color impresa. \n" "La fila más oscura arriba. \n" "Guaradr en %s/" #: f.tools.cc:4330 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY." msgstr "" "Abrir y editar la carta de color escaneada. \n" "Eliminar cualquier giro o rotación del escaneado. \n" "(Usar la función Corregir perspectiva para ésto). \n" "Recortar CUIDADOSAMENTE el delgado margen verde." #: f.tools.cc:4362 msgid "Open the trimmed color chart file" msgstr "Abrir la carta de colores recortada" #: f.tools.cc:4495 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:4535 msgid "Color map file to use" msgstr "Archivo de mapa de color para usar" #: f.tools.cc:4555 msgid "Select the image file to print." msgstr "Seleccionar la imagen a imprimir" #: f.tools.cc:4596 msgid "file format error" msgstr "error de formato de archivo" #: f.tools.cc:4620 msgid "converting colors..." msgstr "convirtiendo colores ..." #: f.tools.cc:4720 msgid "Image colors are converted for printing." msgstr "Colores de la imagen se han convertido para imprimir" #: f.tools.cc:4769 f.widgets.cc:617 msgid "Grid Lines" msgstr "Lineas de rejilla" #: f.tools.cc:4778 msgid "x-spacing" msgstr "espaciado X" #: f.tools.cc:4779 msgid "x-count" msgstr "conteo X" #: f.tools.cc:4780 msgid "x-enable" msgstr "activar X" #: f.tools.cc:4786 msgid "y-spacing" msgstr "espaciado Y" #: f.tools.cc:4787 msgid "y-count" msgstr "conteo Y" #: f.tools.cc:4788 msgid "y-enable" msgstr "activar Y" #: f.tools.cc:4909 f.widgets.cc:618 msgid "Line Color" msgstr "Color de línea" #: f.tools.cc:4967 msgid "Darkest and Brightest Pixels" msgstr "Píxeles más oscuros y más brillantes" #: f.tools.cc:4991 msgid "Dark Limit" msgstr "Sombra" #: f.tools.cc:4992 msgid "Bright Limit" msgstr "Luz" #: f.tools.cc:5117 msgid "Map RAW Pixel Bias" msgstr "Mapa sesgo de píxeles RAW" #: f.tools.cc:5124 msgid "mean RGB:" msgstr "RGB medio" #: f.tools.cc:5210 msgid "select at least 10 RAW image files" msgstr "seleccionar 10 imágenes RAW como mínimo" #: f.tools.cc:5230 #, c-format msgid "" "cannot read file \n" " %s" msgstr "" "no se puede leer el archivo \n" " %s" #: f.tools.cc:5235 #, c-format msgid "" "not a RAW file \n" " %s" msgstr "" "no es un archivo RAW \n" " %s" #: f.tools.cc:5249 #, c-format msgid "dimensions do not match: %s" msgstr "las dimensiones no coinciden: %s" #: f.tools.cc:5366 f.tools.cc:5449 msgid "Pixel Bias Map file" msgstr "Archivo de mapa de sesgo de píxel" #: f.tools.cc:5494 msgid "invalid pixel bias map file" msgstr "archivo de mapa de sesgo de píxel inválido" #: f.tools.cc:5524 msgid "image dimensions do not match pixel bias file" msgstr "dimensiomes de imagen no coinciden con el mapa de sesgo de píxel" #: f.tools.cc:5610 msgid "Map RAW Dead Pixels" msgstr "Mapa de píxeles muertos RAW" #: f.tools.cc:5614 msgid "gray RAW image file" msgstr "archivo de imagen gris RAW" #: f.tools.cc:5618 msgid "RGB threshold" msgstr "Umbral RGB" #: f.tools.cc:5621 msgid "dead pixels found:" msgstr "encontrados píxeles muertos" #: f.tools.cc:5658 msgid "not a RAW file" msgstr "no es un archivo RAW" #: f.tools.cc:5664 msgid "cannot load RAW file" msgstr "no se puede cargar el archivo RAW" #: f.tools.cc:5727 msgid "choose a gray RAW file" msgstr "seleccionar un archivo RAW gris" #: f.tools.cc:5858 f.tools.cc:5903 msgid "dead pixels file" msgstr "archivo de píxeles muertos" #: f.tools.cc:5955 msgid "invalid dead pixels file" msgstr "archivo de píxeles muertos no válido" #: f.tools.cc:5975 msgid "no dead pixels data available" msgstr "no disponibles datos de píxeles muertos" #: f.tools.cc:5980 msgid "image dimensions do not match dead pixels file" msgstr "dimensiones de imagen no coinciden con el archivo de píxels muertos" #: f.tools.cc:6048 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:6208 msgid "Available Translations" msgstr "Traducciones disponibles" #: f.tools.cc:6212 msgid "Set Language" msgstr "Seleccionar el idioma" #: f.warp.cc:110 f.widgets.cc:556 msgid "Unbend" msgstr "Enderezar" #: f.warp.cc:388 msgid "" " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle." msgstr "" " Pulsar en las cuatro esquinas del área tetragonal. Pulsar [Aplicar]. \n" " La imagen será deformada para hacer que el tetrágono sea un rectángulo." #: f.warp.cc:405 msgid "Perspective Correction" msgstr "Corregir perspectiva" #: f.warp.cc:638 msgid "must have 4 corners" msgstr "debe tener 4 esquinas" #: f.warp.cc:763 msgid "" " Select an area to warp using select area function. \n" " Press [start warp] and pull area with mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, select another area or press [done]." msgstr "" " Seleccionar un área para deformar utilizando el botón [Seleccionar]. \n" " Pulsar [Comenzar deformación] y tirar del área con el ratón. \n" " Dar varios tirones con el ratón hasta que quede satisfecho. \n" " Cuando esté terminado, seleccionar otra área o pulsar [Hecho]." #: f.warp.cc:776 f.widgets.cc:558 msgid "Warp area" msgstr "Deformar área" #: f.warp.cc:781 msgid "start warp" msgstr "comenzar deformación" #: f.warp.cc:1182 f.warp.cc:1494 msgid "" " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Tirar de una posición en la imagen empleando el ratón. \n" " Dar varios tirones con el ratón hasta que quede satisfecho. \n" " Cuando haya terminado, pulsar [Hecho]." #: f.warp.cc:1200 f.widgets.cc:559 msgid "Warp curved" msgstr "Deformación curva" #: f.warp.cc:1512 f.widgets.cc:560 msgid "Warp linear" msgstr "Deformación lineal" #: f.warp.cc:1825 msgid "" " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Tirar de un borde de la imagen utilizando el ratón. \n" " Dar varios tirones con el ratón hasta que quede satisfecho. \n" " Cuando haya terminado, pulse [Hecho]." #: f.warp.cc:1841 f.widgets.cc:561 msgid "Warp affine" msgstr "Deformación afín" #: f.warp.cc:2174 msgid "" " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n" msgstr "" "Usar Seleccionar área para seleccionar una cara. \n" "Clic en el centro de la distorsión. \n" "Mover el deslizador. \n" #: f.warp.cc:2201 f.widgets.cc:562 msgid "Unwarp Closeup" msgstr "Distorsión de acercamiento" #: f.warp.cc:2379 msgid "Flatten Book Page" msgstr "Aplanar página impresa" #: f.warp.cc:2380 msgid "" "Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: " msgstr "" "Cortar imagen para aislar una página. \n" "Señalar bordes superiores e inferiores con \n" "más de 4 clics de ratón, después aplanar : " #: f.warp.cc:2383 msgid "Stretch curved-down surfaces:" msgstr "Enderezar superficies curvadas por abajo" #: f.warp.cc:2438 msgid "Top:" msgstr "Arriba:" #: f.warp.cc:2441 msgid "Bottom:" msgstr "Abajo" #: f.warp.cc:2830 msgid "" " Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]." msgstr "" "Seleccionar áreas que permanecerán sin cambios.\n" "Arrastrar imagen desde esquina superior izquierda. \n" "Al acabar, presionar [Hecho]." #: f.warp.cc:2846 f.widgets.cc:564 msgid "Area Rescale" msgstr "Re-escalado de área" #: f.warp.cc:2869 msgid "select areas first" msgstr "primero seleccione área" #: f.warp.cc:3088 f.widgets.cc:565 msgid "Make Waves" msgstr "Ondas" #: f.warp.cc:3095 msgid "wavelength" msgstr "longitud de onda" #: f.warp.cc:3097 msgid "variance" msgstr "varianza" #: f.warp.cc:3108 msgid "perspective" msgstr "perspectiva" #: f.warp.cc:3285 f.warp.cc:3321 f.widgets.cc:566 msgid "Twist" msgstr "Girar" #: f.warp.cc:3318 f.warp.cc:3609 f.warp.cc:3835 f.warp.cc:4060 msgid "Drag mouse to set center" msgstr "Arrastrar el ratón para establecer el centro" #: f.warp.cc:3565 msgid "Spherical Projection" msgstr "Proyección esférica" #: f.warp.cc:3792 msgid "Stretch Image" msgstr "Extender imagen" #: f.warp.cc:4057 f.widgets.cc:569 msgid "Inside-out" msgstr "Dentro-fuera" #: f.warp.cc:4065 f.warp.cc:4314 msgid "Center Hole" msgstr "Agujero central" #: f.warp.cc:4266 msgid "image width must be greater than height" msgstr "el ancho de la imagen debe ser mayor que la altura" #: f.warp.cc:4319 msgid "Cut Top" msgstr "Corte superior" #: f.warp.cc:4324 msgid "Cut Bottom" msgstr "Corte inferior" #: f.warp.cc:4332 msgid "Reverse R" msgstr "Invertir R" #: f.warp.cc:4333 msgid "Theta" msgstr "Theta" #: f.warp.cc:4545 msgid "Click mouse to change center" msgstr "Clic con el ratón para cambiar el centro" #: f.warp.cc:4550 msgid "Rim %" msgstr "Borde %" #: f.widgets.cc:105 msgid "Album" msgstr "Álbum" #: f.widgets.cc:107 msgid "TOP" msgstr "ARRIBA" #: f.widgets.cc:167 msgid "Rename, copy/move, delete, print" msgstr "Renombrar, copiar/mover, borrar, imprimir" #: f.widgets.cc:168 msgid "Thumbnails, bookmarks, albums, slide show" msgstr "Miniaturas, marcadores, álbumes, diaporama" #: f.widgets.cc:169 msgid "View images by map location" msgstr "Ver imágenes por localización en el mapa" #: f.widgets.cc:170 msgid "Custom favorites menu" msgstr "Menú de favoritos personalizado" #: f.widgets.cc:171 msgid "Left/right click: previous/next (also arrow keys)" msgstr "Clic izq./der.: anterior/actual (también teclas de flecha)" #: f.widgets.cc:172 msgid "Left/right click: larger/smaller image/thumbnails" msgstr "" #: f.widgets.cc:173 msgid "Save modified file as new version or new file" msgstr "Guardar archivo modificado como una nueva versión o un nuevo archivo" #: f.widgets.cc:174 msgid "Metadata: captions, tags, ratings, geotags, search images" msgstr "Metadatos: títulos, etiquetas, calificaciones, buscar imágenes" #: f.widgets.cc:175 msgid "Select areas to edit separately, save, copy and paste" msgstr "Seleccionar áreas para deditar searadamente, guardar, copiar y pegar" #: f.widgets.cc:176 msgid "" "Left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit" msgstr "" "Clic izq-/der.: deshacer/rehacer 1 edición \n" "con la tecla A: deshacer/rehacer todas las ediciones \n" "clic central: ir a cualquier edición anterior" #: f.widgets.cc:179 msgid "Image edit basic functions" msgstr "Funciones básicas de edición de imagen" #: f.widgets.cc:180 msgid "Image repair and enhance" msgstr "Reparar y mejorar imagen" #: f.widgets.cc:181 msgid "Artistic effects (filters)" msgstr "Efectos artísticos (filtros)" #: f.widgets.cc:182 msgid "Image warp, unwarp, transform" msgstr "Deformar, enderezar, transformar imagen" #: f.widgets.cc:183 msgid "HDR, HDF, panorama, stack, mashup" msgstr "HDR, HDF, panorama, apilar, montaje" #: f.widgets.cc:184 msgid "Batch processing, custom scripts" msgstr "Proceso en lote, scripts personalizados" #: f.widgets.cc:185 msgid "Image index, user preferences, shortcuts, utilities" msgstr "Índice de imágenes, preferencias de usuario, atajos, utilidades" #: f.widgets.cc:186 msgid "User Guide, recent changes, log file, about" msgstr "Guía de usuario, cambios reciente, archivo de errores, acerca de" #: f.widgets.cc:189 msgid "Current File (R-click or key F)" msgstr "Archivo actual (cic der. o tecla F" #: f.widgets.cc:190 msgid "Open a parallel Fotoxx session" msgstr "Abrir una sesión paralela de Fotoxx" #: f.widgets.cc:191 msgid "Cycle 2 Prior Files" msgstr "Ciclo de 2 archivo anteriores" #: f.widgets.cc:192 msgid "Cycle 3 Prior Files" msgstr "Ciclo de 3 archivos anteriores" #: f.widgets.cc:193 msgid "View a 360 degree panorama image file" msgstr "Ver una imagen panorámica de 360 grados" #: f.widgets.cc:194 msgid "Change file name" msgstr "Cambiar el nombre" #: f.widgets.cc:195 msgid "View and change file permissions" msgstr "Ver y cambiar permisos de archivo" #: f.widgets.cc:196 msgid "Create a blank image" msgstr "Crear una imagen en blanco" #: f.widgets.cc:197 msgid "Toggle - blank or restore window" msgstr "Ocultar o restaurar ventana" #: f.widgets.cc:198 msgid "Copy or Move file to new location" msgstr "Copiar o mover una imagen a una nueva ubicación" #: f.widgets.cc:199 msgid "Copy file to the desktop" msgstr "Copiar una imagen al esritorio" #: f.widgets.cc:200 msgid "Copy file to the clipboard" msgstr "Copiar imagen al portapapeles" #: f.widgets.cc:201 msgid "Set file as desktop wallpaper (GNOME)" msgstr "Establecer archivo como fondo de escritorio (GNOME)" #: f.widgets.cc:202 msgid "Show location on Internet map" msgstr "Mostrar localizacion en mapa Internet" #: f.widgets.cc:203 msgid "Delete or trash file" msgstr "Borrar imagen" #: f.widgets.cc:204 msgid "Print the current image" msgstr "Imprimir la imagen actual" #: f.widgets.cc:205 msgid "Print current image with adjusted colors" msgstr "Imprimir imagen actual con los colores ajustados" #: f.widgets.cc:206 msgid "Quit Fotoxx" msgstr "Salir de Fotoxx" #: f.widgets.cc:209 msgid "Thumbnail Gallery (R-click or key G)" msgstr "Galería de miniaturas (clic der. o tecla G)" #: f.widgets.cc:210 msgid "Gallery view with thumbnails and basic metadata" msgstr "Vista de galería con miniaturas y metadatos básicos" #: f.widgets.cc:211 msgid "Gallery view with small thumbnails and file names" msgstr "Vista de galería con miniaturas pequeñas y nombres de archivos" #: f.widgets.cc:212 msgid "Gallery of recently viewed image files" msgstr "Galería de archivos de imagen vistos recientemente" #: f.widgets.cc:213 msgid "Gallery of newest image files" msgstr "Galería de " #: f.widgets.cc:214 msgid "Jump to beginning [home]" msgstr "Ir al inicio [inicio]" #: f.widgets.cc:215 msgid "Jump to end [end]" msgstr "Ir al fin [final]" #: f.widgets.cc:216 msgid "Set gallery from current image file" msgstr "Establecer galería desde la imagen actual" #: f.widgets.cc:217 msgid "Change sort order" msgstr "Cambiar criterio de ordenación" #: f.widgets.cc:218 msgid "List all folders, click any for gallery view" msgstr "Listar todos los directorios, clic en uno para vista de galería" #: f.widgets.cc:219 msgid "select input files for album, batch, script functions" msgstr "seleccionar" #: f.widgets.cc:220 msgid "Set and recall bookmarked image locations" msgstr "Establecer y recuperar ubicaciones de imágenes marcadas" #: f.widgets.cc:221 msgid "Organize images into albums" msgstr "Organizar imágenes en álbumes" #: f.widgets.cc:222 msgid "Update albums for new file versions" msgstr "Actualizar álbumes para nuevas versiones de archivo" #: f.widgets.cc:223 msgid "Mass update album files" msgstr "Actualización masiva de archivos de álbum" #: f.widgets.cc:224 msgid "Save current gallery as album" msgstr "Guardar galería actual como álbum" #: f.widgets.cc:225 msgid "Start a slide show" msgstr "Iniciar un diaporama" #: f.widgets.cc:228 msgid "Maps (R-click or key M)" msgstr "Mapas (clic izq. o tecla M)" #: f.widgets.cc:229 msgid "Open Internet map" msgstr "Abrir mapa de internet" #: f.widgets.cc:230 msgid "Choose Internet map source" msgstr "Elegir origen del mapa de internet" #: f.widgets.cc:231 msgid "Internet map locations" msgstr "Localidadesdel mapa de internet" #: f.widgets.cc:232 msgid "Open file map" msgstr "Abrir archivo de mapa" #: f.widgets.cc:233 msgid "Choose file map" msgstr "Elegir archivo de mapa" #: f.widgets.cc:234 msgid "Set map markers for all images or current gallery" msgstr "" "Establecer marcadores de mapas para todas las imágenes o la galeía actual" #: f.widgets.cc:237 msgid "List a few key metadata items" msgstr "Listar algunos datos clave de metadatos" #: f.widgets.cc:238 msgid "List all metadata items" msgstr "Listar todos los items de metadatos" #: f.widgets.cc:239 msgid "Edit image tags/geotags/caption/rating ..." msgstr "Editr etiquetas/geoetiquetas/título/valoración ..." #: f.widgets.cc:240 msgid "Define tags (keywords) used for searching images" msgstr "Definir etiquetas (palabras clave) usadas para búsqueda de imagen" #: f.widgets.cc:241 msgid "Edit any image metadata" msgstr "Editar cualquier metadato de la imagen" #: f.widgets.cc:242 msgid "Remove selected image metadata" msgstr "Eliminar metadatos de las imágenes seleccionadas" #: f.widgets.cc:243 msgid "Show file name, captions, comments" msgstr "Mostrar n ombtres de archivo, títulos, comentarios" #: f.widgets.cc:244 msgid "Find all images for a location [date]" msgstr "Encontrar todas las imágenes para una ubicación (fecha)" #: f.widgets.cc:245 msgid "Show image counts by month, select and report" msgstr "Mostrar cuenta de imágenes por meses, seleccionar e informar" #: f.widgets.cc:246 msgid "Find images meeting search criteria" msgstr "Encontrar imágenes que cumplan criterios de búsqueda" #: f.widgets.cc:249 msgid "Select object or area for editing" msgstr "Seleccionar objeto o área para editar" #: f.widgets.cc:250 msgid "Select hairy or irregular edge" msgstr "Seleccionar borde deshilachado o irregular" #: f.widgets.cc:251 msgid "Find a gap in an area outline" msgstr "Encontrar separación en el contorno de área" #: f.widgets.cc:252 msgid "Show (outline) existing area" msgstr "Mostrar un área existente (contorno)" #: f.widgets.cc:253 msgid "Hide existing area" msgstr "Ocultar áreas existentes" #: f.widgets.cc:254 msgid "Enable area for editing" msgstr "activar áre para editar" #: f.widgets.cc:255 msgid "Disable area for editing" msgstr "Desactivar área para editar" #: f.widgets.cc:256 msgid "Reverse existing area" msgstr "Invertir área existente" #: f.widgets.cc:257 msgid "Erase existing area" msgstr "Borrar área existente" #: f.widgets.cc:258 msgid "Copy area for later pasting into image" msgstr "Copiar área para posterior pegado en una imagen" #: f.widgets.cc:259 msgid "Paste previously copied area into image" msgstr "Pegar en la imagen àrea previamente copiada" #: f.widgets.cc:260 msgid "Open a file and paste as area into image" msgstr "Abrir un archivo y pegar como área en la imagen" #: f.widgets.cc:261 msgid "Save area to a file with transparency" msgstr "Guardar área como un archivo con transparencia" #: f.widgets.cc:264 msgid "Trim/Crop margins and/or Rotate" msgstr "Recortar/cortar márgenes y/o girar" #: f.widgets.cc:265 msgid "Auto upright a rotated image based on EXIF data" msgstr "Auto enderezar en base a datos EXIF de una imagen rotada" #: f.widgets.cc:266 msgid "Adjust brightness, contrast, color" msgstr "Ajustar brillo, contraste, color" #: f.widgets.cc:267 msgid "Change pixel dimensions" msgstr "Cambiar dimensiones en píxeles" #: f.widgets.cc:268 msgid "Adjust color using RGB or CMY colors" msgstr "Ajustar colores usando sistema RGB o CMY" #: f.widgets.cc:269 msgid "Adjust color using HSL colors" msgstr "Ajustar color usando sistema HSL" #: f.widgets.cc:270 msgid "Draw on image: text, line/arrow, box, ellipse" msgstr "Dibujar en la imagen: texto, línea/flecha, elipse" #: f.widgets.cc:271 msgid "Paint image pixels using the mouse" msgstr "Pintar píxeles de imagen usando el ratón" #: f.widgets.cc:272 msgid "Copy pixels within an image using the mouse" msgstr "Copiar píxeles de una imagen usando el ratón" #: f.widgets.cc:273 msgid "Copy pixels from one image to another using the mouse" msgstr "Copiar píxeles desde una imagen a otra usando el ratón" #: f.widgets.cc:274 msgid "Paint edit function gradually with mouse" msgstr "Función de edición pintando gradualmente con el ratón" #: f.widgets.cc:275 msgid "Incrementally undo prior edits gradually with mouse" msgstr "" "Deshacer incrementalmente anteriores ediciones gradualmente con el ratón" #: f.widgets.cc:276 msgid "Edit plugins menu or run a plugin function" msgstr "Editar men​​ú de plugins o ejecutar una función de plugin" #: f.widgets.cc:277 msgid "Specialized program for editing RAW files" msgstr "Programa especializado para editar RAW" #: f.widgets.cc:280 f.widgets.cc:281 msgid "Fast auto enhance that may work OK" msgstr "Mejora automática rápida" #: f.widgets.cc:282 msgid "Edit brightness distribution" msgstr "Editar histograma" #: f.widgets.cc:283 msgid "Magnify brightness gradients to enhance details" msgstr "Aumentar gradientes de brillo para mejorar detalles" #: f.widgets.cc:284 msgid "Flatten brightness distribution to enhance details" msgstr "Aplanar histograma para mejorar detalles" #: f.widgets.cc:285 msgid "Rescale RGB - reduce color caste and fog/haze" msgstr "Re-escalar RGB - reduce dominante de color y neblina" #: f.widgets.cc:286 msgid "Make the image look sharper" msgstr "Hacer que la imagen se vea más enfocada" #: f.widgets.cc:287 msgid "Blur the image, different methods" msgstr "Desenfocar la imagen, diferentes métodos" #: f.widgets.cc:288 msgid "Filter noise from low-light photos" msgstr "Filtrar ruido de fotos hechas con poca luz" #: f.widgets.cc:289 msgid "Fix red-eyes from electronic flash" msgstr "Corregir ojos rojos producidos por un flash electrónico" #: f.widgets.cc:290 msgid "Match colors on one image with another" msgstr "Concordar colores de una imagen con los de otra" #: f.widgets.cc:291 msgid "Remove unwanted objects" msgstr "Eliminar objetos no deseados" #: f.widgets.cc:292 msgid "Fix color fringes in outer areas of an image" msgstr "Corregir franjas de color en las áreas exteriores de una imagen" #: f.widgets.cc:293 msgid "Fix color band on dark/bright feature edges" msgstr "Corregir banda de color en los bordes oscuros/brillantes " #: f.widgets.cc:294 msgid "Change brightness or color radially" msgstr "Cambiar radialmente el brillo o el color" #: f.widgets.cc:295 msgid "Remove dust spots from scanned slides" msgstr "Eliminar motas de polvo de diapositivas escaneadas" #: f.widgets.cc:298 msgid "Convert to simulated sketch" msgstr "Convertir a simulación de boceto" #: f.widgets.cc:299 msgid "Convert image into a cartoon drawing" msgstr "Convertir la imagen en un dibujo a tintas planas" #: f.widgets.cc:300 msgid "Convert to line drawing (edge detection)" msgstr "Convertir a un dibujo de líneas (detección de bordes)" #: 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 dithered dots" msgstr "Convertir a puntos tramados" #: f.widgets.cc:304 msgid "Convert into a simulated painting" msgstr "Convertir a pintura simulada" #: f.widgets.cc:305 msgid "Add texture to an image" msgstr "Añadir textura a una imagen" #: f.widgets.cc:306 msgid "Tile image with a repeating pattern" msgstr "Embaldosar imagen con un patrón repetitivo" #: f.widgets.cc:307 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:308 msgid "Make BW/color, negative/positive, sepia" msgstr "Cambiar blanco y negro/color, negativo/positivo, sepia" #: f.widgets.cc:309 msgid "Reduce color depth (posterize)" msgstr "Reducir profundidad de color (posterizar)" #: f.widgets.cc:310 msgid "Shift/convert colors into other colors" msgstr "Cambiar/convertir colores en otros" #: f.widgets.cc:311 msgid "Change color hue using an algorithm" msgstr "Cambiar tono de color usando un algoritmo" #: f.widgets.cc:312 msgid "Add a brightness/color ramp across the image" msgstr "Añadir una rampa de brillo/color a la imagen" #: f.widgets.cc:313 msgid "Paint image transparency using the mouse" msgstr "Pintar transparencia de la imagen con el ratón" #: f.widgets.cc:314 msgid "Mirror image horizontally or vertically" msgstr "Reflejar horizontal o verticalmente la imagen" #: f.widgets.cc:315 msgid "Process an image using a custom kernel" msgstr "Procesar una imagen usando una matriz personalizada" #: f.widgets.cc:318 msgid "Remove curvature, esp. panoramas" msgstr "Eliminar curvatura, esp. panoramas" #: f.widgets.cc:319 msgid "Straighten objects seen from an angle" msgstr "Enderezar objetos vistos desde un ángulo" #: f.widgets.cc:320 msgid "Distort image areas using the mouse" msgstr "Deformar áreas de la imagen usando el ratón" #: f.widgets.cc:321 msgid "Unwarp closeup face photo to remove distortion" msgstr "" "Eliminar deformación de acercamiento en un retrato para eliminar distorsión" #: f.widgets.cc:322 f.widgets.cc:323 f.widgets.cc:324 msgid "Distort the whole image using the mouse" msgstr "Deformar toda la imagen usando el ratón" #: f.widgets.cc:325 msgid "Flatten a photographed book page" msgstr "Aplanar foto de una página impresa " #: f.widgets.cc:326 msgid "Rescale image outside selected areas" msgstr "Reescalar imagen por fuera de las áreas seleccionadas" #: f.widgets.cc:327 msgid "Warp an image with a wave pattern" msgstr "Deformar una imagencon un patrón de ondas" #: f.widgets.cc:328 msgid "Twist image centered at mouse position" msgstr "Girar la imagen centrada en la posición del ratón" #: f.widgets.cc:329 msgid "Make a spherical projection of an image" msgstr "Hacer una proyección esférica de una imagen" #: f.widgets.cc:330 msgid "Image scale increases from center to edge" msgstr "La escala de la imagen aumenta de centro a borde" #: f.widgets.cc:331 msgid "Turn an image inside-out" msgstr "Girar una imagen de adentro hacia afuera." #: f.widgets.cc:332 msgid "Convert an image into a Tiny Planet" msgstr "Convertir una imagen en un pequeño planeta" #: f.widgets.cc:333 msgid "Generate an inward spiraling recursive image" msgstr "Generar una imagen recursiva en espiral hacia adentro" #: f.widgets.cc:336 msgid "Combine bright/dark images for better detail" msgstr "Combinar imágenes brillantes/oscuras para mejorar detalles" #: f.widgets.cc:337 msgid "Combine near/far focus images for deeper focus" msgstr "" "Combinar imágenes enfocadas cerca/lejos para mayor profundidad de campo" #: f.widgets.cc:338 msgid "Combine images to erase passing people, etc." msgstr "Combinar imágenes para borrar gente, coches, etc" #: f.widgets.cc:339 msgid "Combine noisy images into a low-noise image" msgstr "Combinaar imágenes ruidosas en una imagen de bajo ruido" #: f.widgets.cc:340 msgid "Combine image layers, mouse select and expose" msgstr "Combinar capas de imágenes, seleccionar con el ratón y exponer." #: f.widgets.cc:341 msgid "Compare two images separated by sliding boundary" msgstr "Compara dos imágenes separadas por límites deslizantes" #: f.widgets.cc:342 msgid "Show differences between two images" msgstr "Mostrar diferencias entre dos imágenes" #: f.widgets.cc:343 msgid "Combine images into a panorama" msgstr "Combinar imágenes en una panorámica" #: f.widgets.cc:344 msgid "Combine images into a vertical panorama" msgstr "Combinar imágenes en un panorama vertical" #: f.widgets.cc:345 msgid "Combine images into a panorama (panorama tools)" msgstr "Combinar imágenes en una panorámica (panorama tools)" #: f.widgets.cc:346 msgid "Combine images into a montage of images" msgstr "Combinar imágenes en un montaje" #: f.widgets.cc:347 msgid "Arrange images and text in a layout (montage)" msgstr "Ordenar imágenes y texto en una capa (montaje)" #: f.widgets.cc:350 msgid "Rename/convert/resize/move multiple files" msgstr "Renombrar/convertir/redimensionar/mover múltiples archivos" #: f.widgets.cc:351 msgid "Upright multiple rotated image files" msgstr "Enderezar múltiples imagenes giradas" #: f.widgets.cc:352 msgid "Delete or Trash multiple files" msgstr "Borrar o eliminar múltiples imágenes" #: f.widgets.cc:353 msgid "Convert camera RAW files to tiff/png/jpeg" msgstr "Convertir archivos cámara RAW a tiff/png/jpg" #: f.widgets.cc:354 msgid "Burn selected image files to DVD/BlueRay disc" msgstr "Grabar las imágenes seleccionadas en un DVD o disco BlueRay" #: f.widgets.cc:356 msgid "Export selected image files to a folder" msgstr "Exportar imágenes seleccionadas a un directorio" #: f.widgets.cc:357 msgid "Add/remove tags for multiple images" msgstr "Añadir/eliminar etiquetas para múltiples imágenes " #: f.widgets.cc:358 msgid "Convert tag names for all images" msgstr "Convertir nombres de etiquetas para todas las imágenes" #: f.widgets.cc:359 msgid "change or shift photo dates/times" msgstr "cambiar fechas/horas de las fotos" #: f.widgets.cc:360 msgid "Add/change/delete metadata for multiple images" msgstr "Añadir/cambiar/borrar metadatos en múltiples imágenes" #: f.widgets.cc:361 msgid "Report metadata for multiple images" msgstr "Informe de metadatos para múltiples imágenes" #: f.widgets.cc:362 msgid "Add/revise geotags for multiple images" msgstr "Añadir/revisar geoetiquetas en múltiples imágenes" #: f.widgets.cc:363 msgid "Build a custom script with multiple edit functions" msgstr "Construir un script personalizado con múltiples funciones de edición" #: f.widgets.cc:364 msgid "Run custom script to edit the current image file" msgstr "jecutar un script personalizado para editar la imagen actual" #: f.widgets.cc:365 msgid "Run custom script to edit a batch of image files" msgstr "Ejecutar un script personalizado para editar un lote de imágenes" #: f.widgets.cc:368 msgid "Index new files and make thumbnails" msgstr "Indexar nuevos archivos y crear miniaturas" #: f.widgets.cc:369 msgid "Quick incremental index update" msgstr "Actualización rápida del índice incremental" #: f.widgets.cc:370 msgid "Move Fotoxx home folder" msgstr "Mover la carpeta de inicio de Fotoxx" #: f.widgets.cc:371 msgid "User preferences and settings" msgstr "Preferencias y configuraciones del usuario" #: f.widgets.cc:373 msgid "Show RGB brightness distribution" msgstr "Mostrar histograma RGB" #: f.widgets.cc:374 msgid "Magnify image around the mouse position" msgstr "Aumenta la imagen alrededor de la posición del ratón" #: f.widgets.cc:375 msgid "Search all image files and report duplicates" msgstr "Buscar en todas las imágenes e informar de duplicados" #: f.widgets.cc:376 msgid "Show RGB colors at mouse click" msgstr "Mostrar colores RGB con un clic del ratón" #: f.widgets.cc:377 msgid "Convert to another color profile" msgstr "Convertir a otro perfil de color" #: f.widgets.cc:378 msgid "Calibrate printer colors" msgstr "Calibrar colores de la impresora" #: f.widgets.cc:379 msgid "Show or revise grid lines" msgstr "Mostrar o revisar líneas de rejilla" #: f.widgets.cc:380 msgid "Change color of foreground lines" msgstr "Cambiar color de líneas de contorno" #: f.widgets.cc:381 msgid "Highlight darkest and brightest pixels" msgstr "Resaltar píxeles más brillantes o más oscuros" #: f.widgets.cc:382 msgid "map raw pixel bias (camera sensor, vignette)" msgstr "Mapa de sesgo de píxeles en bruto (sensor de cámara, viñeta)" #: f.widgets.cc:383 msgid "map raw dead pixels (camera sensor)" msgstr "mapa de píxeles muertos sin procesar (sensor de la cámara)" #: f.widgets.cc:384 msgid "Chart to adjust monitor color" msgstr "Carta para ajustar el color del monitor" #: f.widgets.cc:385 msgid "Chart to adjust monitor gamma" msgstr "Carta para ajustar la gamma del monitor" #: f.widgets.cc:386 msgid "Change the GUI language" msgstr "cambiar el idioma de la interfaz de usuario" #: f.widgets.cc:387 msgid "Report missing translations" msgstr "Informe de traducciones que faltan" #: f.widgets.cc:388 msgid "Anonymous usage statistics" msgstr "Estadísticas de uso anónimas" #: f.widgets.cc:389 msgid "Memory and CPU (to terminal/logfile)" msgstr "Memoria y CPU (al terminal/archivo de registro)" #: f.widgets.cc:390 msgid "List files included in appimage container" msgstr "Lista de archivos incluidos en el contenedor de imágenes" #: f.widgets.cc:391 msgid "test crash report with source line numbers" msgstr "informe de prueba de errores con números de línea de origen" #: f.widgets.cc:394 msgid "Read the user guide" msgstr "Leer la guía de usuario" #: f.widgets.cc:395 msgid "Recent user guide changes" msgstr "Cambios recientes en la guia de usuario" #: f.widgets.cc:396 msgid "Overview of all edit functions" msgstr "Descripción general de todas las funciones de edición" #: f.widgets.cc:397 msgid "List updates by Fotoxx version" msgstr "Listar actualizaciones por versión de Fotoxx" #: f.widgets.cc:398 msgid "View the log file and error messages" msgstr "Ver el archivo de registro y mensajes de error" #: f.widgets.cc:399 msgid "Fotoxx Man Page - summary of capabilities" msgstr "Página del manual de Fotoxx - resumen de capacidades" #: f.widgets.cc:400 msgid "List command line parameters" msgstr "Listar parámetros de línea de comando" #: f.widgets.cc:401 msgid "How to do Fotoxx translations" msgstr "Cómo hacer traducciones de Fotoxx" #: f.widgets.cc:402 msgid "Show the Fotoxx web page" msgstr "Muestra la página web de Fotoxx" #: f.widgets.cc:403 msgid "Fotoxx license - terms of use" msgstr "Licencia de Fotoxx - términos de Uso" #: f.widgets.cc:404 msgid "Fotoxx privacy policy" msgstr "Política de privacidad de Fotoxx" #: f.widgets.cc:405 msgid "Version, contact, credits" msgstr "Versión, contacto, créditos" #: f.widgets.cc:425 msgid "File View F" msgstr "Vista de arcchivo F" #: f.widgets.cc:426 msgid "New Session" msgstr "Nueva sesión" #: f.widgets.cc:427 f.widgets.cc:453 msgid "Source Folder" msgstr "Carpeta de origen" #: f.widgets.cc:428 msgid "Cycle 2" msgstr "Ciclo 2" #: f.widgets.cc:429 msgid "Cycle 3" msgstr "Ciclo 3" #: f.widgets.cc:430 msgid "View 360° Pano" msgstr "Ver panorama 360º" #: f.widgets.cc:431 f.widgets.cc:820 fotoxx.h:1414 msgid "Rename" msgstr "Renombrar" #: f.widgets.cc:433 msgid "Blank Image" msgstr "Imagen en blanco" #: f.widgets.cc:435 f.widgets.cc:822 msgid "Copy/Move" msgstr "Copiar/Mover" #: f.widgets.cc:436 f.widgets.cc:823 msgid "Copy to Desktop" msgstr "Copiar al escritorio" #: f.widgets.cc:437 f.widgets.cc:824 msgid "Copy to Clipboard" msgstr "Copiar al portapapeles" #: f.widgets.cc:438 msgid "Set Wallpaper" msgstr "Establecer fonde de pantalla" #: f.widgets.cc:439 f.widgets.cc:839 msgid "Show on Map" msgstr "Mostrar en el mapa" #: f.widgets.cc:440 f.widgets.cc:840 msgid "Delete/Trash" msgstr "Borrar/Eliminar" #: f.widgets.cc:441 msgid "Print" msgstr "Imprimir" #: f.widgets.cc:442 msgid "Print Calibrated" msgstr "Imprimir calibrado" #: f.widgets.cc:443 fotoxx.h:1407 msgid "Quit" msgstr "Salir" #: f.widgets.cc:446 msgid "Gallery View G" msgstr "Vuista de galeria G" #: f.widgets.cc:447 msgid "Meta View" msgstr "Ver metadatos" #: f.widgets.cc:448 msgid "List View" msgstr "Ver lista" #: f.widgets.cc:449 msgid "Recent" msgstr "Reciente" #: f.widgets.cc:450 msgid "Newest" msgstr "El más nuevo" #: f.widgets.cc:451 msgid "GoTo First" msgstr "Ir al primero" #: f.widgets.cc:452 msgid "GoTo Last" msgstr "Ir al último" #: f.widgets.cc:454 msgid "Sort Gallery" msgstr "Ordenar galería" #: f.widgets.cc:455 msgid "All Folders" msgstr "Todos los directorios" #: f.widgets.cc:456 fotoxx.h:1425 msgid "Select Files" msgstr "Seleccionar archivos" #: f.widgets.cc:459 msgid "Update Albums" msgstr "Actualizar álbumes" #: f.widgets.cc:461 msgid "Gallery to Album" msgstr "Galeríua a álbum" #: f.widgets.cc:465 msgid "Map View M" msgstr "Vista de mapa M" #: f.widgets.cc:466 msgid "Net Map" msgstr "Mapa de red" #: f.widgets.cc:467 msgid "Net Source" msgstr "Fuente de la red" #: f.widgets.cc:468 msgid "Net Locs" msgstr "Localidades en la" #: f.widgets.cc:469 msgid "File Map" msgstr "Archivo de mapa" #: f.widgets.cc:470 msgid "Choose Map" msgstr "Elegir mapa" #: f.widgets.cc:471 msgid "Markers" msgstr "Marcadores" #: f.widgets.cc:474 f.widgets.cc:816 msgid "View Meta" msgstr "Ver metadatos" #: f.widgets.cc:475 f.widgets.cc:817 msgid "View All Meta" msgstr "Ver todos los metadatos" #: f.widgets.cc:476 f.widgets.cc:818 msgid "Edit Meta" msgstr "Editar metadatos" #: f.widgets.cc:478 f.widgets.cc:819 msgid "Edit Any Meta" msgstr "Editar cualquier metadato" #: f.widgets.cc:479 msgid "Delete Meta" msgstr "Borrar metadatos" #: f.widgets.cc:480 msgid "Captions" msgstr "Títulos" #: f.widgets.cc:481 msgid "Places/Dates" msgstr "Lugares/Fechas" #: f.widgets.cc:482 msgid "Timeline" msgstr "Cronología" #: f.widgets.cc:483 fotoxx.h:1422 msgid "Search" msgstr "Buscar" #: f.widgets.cc:486 fotoxx.h:1424 msgid "Select" msgstr "Seleccionar" #: f.widgets.cc:488 msgid "Find Gap" msgstr "Encontrar separación" #: f.widgets.cc:489 fotoxx.h:1426 msgid "Show" msgstr "Mostrar" #: f.widgets.cc:490 fotoxx.h:1367 msgid "Hide" msgstr "Ocultar" #: f.widgets.cc:491 fotoxx.h:1351 msgid "Enable" msgstr "Activar" #: f.widgets.cc:492 fotoxx.h:1346 msgid "Disable" msgstr "Desactivar" #: f.widgets.cc:494 fotoxx.h:1333 msgid "Clear" msgstr "Limpiar" #: f.widgets.cc:495 fotoxx.h:1340 msgid "Copy" msgstr "Copiar" #: f.widgets.cc:496 fotoxx.h:1399 msgid "Paste" msgstr "Pegar" #: f.widgets.cc:497 fotoxx.h:1376 msgid "Load" msgstr "Cargar" #: f.widgets.cc:498 f.widgets.cc:654 fotoxx.h:1421 zfuncs.cc:12499 msgid "Save" msgstr "Guardar" #: f.widgets.cc:507 msgid "Markup" msgstr "Marcador" #: f.widgets.cc:508 msgid "Paint Image" msgstr "Pintar en la imagen" #: f.widgets.cc:509 msgid "Copy Pixels 1" msgstr "Copiar píxeles 1" #: f.widgets.cc:510 msgid "Copy Pixels 2" msgstr "Copiar píxeles 2" #: f.widgets.cc:513 msgid "Plugins" msgstr "Plugins" #: f.widgets.cc:514 f.widgets.cc:838 msgid "Raw Therapee" msgstr "Raw Therapee" #: f.widgets.cc:517 msgid "Voodoo 1" msgstr "Automejora 1" #: f.widgets.cc:518 msgid "Voodoo 2" msgstr "Automejora 2" #: f.widgets.cc:519 f.widgets.cc:834 msgid "Brite Dist" msgstr "Distribución de brillo" #: f.widgets.cc:520 f.widgets.cc:836 msgid "Gradients" msgstr "Gradientes" #: f.widgets.cc:521 f.widgets.cc:835 fotoxx.h:1360 msgid "Flatten" msgstr "Aplanar" #: f.widgets.cc:522 msgid "Global Retx" msgstr "Retinex global" #: f.widgets.cc:523 msgid "Zonal Retx" msgstr "Retinex zonal" #: f.widgets.cc:526 msgid "Denoise" msgstr "Reducir ruido" #: f.widgets.cc:527 msgid "Red Eyes" msgstr "Ojos rojos" #: f.widgets.cc:528 msgid "Match Colors" msgstr "Concordar colores" #: f.widgets.cc:530 msgid "Chromatic1" msgstr "Cromático1" #: f.widgets.cc:531 msgid "Chromatic2" msgstr "Cromático2" #: f.widgets.cc:536 msgid "Sketch" msgstr "Esbozo" #: f.widgets.cc:537 msgid "Cartoon" msgstr "Tintas planas" #: f.widgets.cc:541 msgid "Dither" msgstr "Tramado" #: f.widgets.cc:543 msgid "Texture" msgstr "Textura" #: f.widgets.cc:545 msgid "Mosaic" msgstr "Mosaico" #: f.widgets.cc:547 msgid "Color Depth" msgstr "Profundidad de color" #: f.widgets.cc:550 msgid "Brite Ramp" msgstr "Rampa de brillo" #: f.widgets.cc:551 msgid "Paint Transp" msgstr "Pintar transparencia" #: f.widgets.cc:552 msgid "Mirror" msgstr "Voltear" #: f.widgets.cc:557 msgid "Perspective" msgstr "Perspectiva" #: f.widgets.cc:563 msgid "Flatten Book" msgstr "Aplanar libro" #: f.widgets.cc:567 msgid "Sphere" msgstr "Esfera" #: f.widgets.cc:568 msgid "Stretch" msgstr "Estirar" #: f.widgets.cc:570 msgid "Tiny Planet" msgstr "Pequeño pñaneta" #: f.widgets.cc:571 msgid "Escher Spiral" msgstr "eSPIRAL DE eSCHER" #: f.widgets.cc:576 msgid "Stack/Paint" msgstr "Apilar/pintar" #: f.widgets.cc:577 msgid "Stack/Noise" msgstr "Apilar/ruido" #: f.widgets.cc:578 msgid "Stack/Layer" msgstr "Pila/Capa" #: f.widgets.cc:579 msgid "Stack/Slider" msgstr "Apilar/deslizar" #: f.widgets.cc:580 msgid "Image Diffs" msgstr "Diferencia de imágenes" #: f.widgets.cc:581 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:582 msgid "V. Panorama" msgstr "Panorama vertical" #: f.widgets.cc:583 msgid "PT Panorama" msgstr "PT panorama" #: f.widgets.cc:584 msgid "Mashup" msgstr "Fotomontaje" #: f.widgets.cc:585 msgid "Montage" msgstr "Montaje" #: f.widgets.cc:591 msgid "Batch RAW" msgstr "Convertir RAW en lote" #: f.widgets.cc:592 msgid "Burn DVD/BlueRay" msgstr "Grabar DVD/BlueRay" #: f.widgets.cc:593 msgid "Export File List" msgstr "Exportar lista de archivos" #: f.widgets.cc:595 msgid "Batch Tags" msgstr "Etiquetas en lote" #: f.widgets.cc:597 msgid "Batch Photo Date" msgstr "Fecha de la foto en lote" #: f.widgets.cc:598 msgid "Batch Change Meta" msgstr "Cambio de metadatos en lote" #: f.widgets.cc:599 msgid "Batch Report Meta" msgstr "Informe de metadatos en lote" #: f.widgets.cc:601 msgid "Edit Script" msgstr "Editar script" #: f.widgets.cc:602 msgid "Run Script" msgstr "Ejecutar script" #: f.widgets.cc:606 msgid "Index Files" msgstr "Indexar archivos" #: f.widgets.cc:607 msgid "Quick Index" msgstr "Índice rápido" #: f.widgets.cc:608 msgid "Move Fotoxx Home" msgstr "Mover inicio de Fotoxx" #: f.widgets.cc:609 msgid "Preferences" msgstr "Preferencias" #: f.widgets.cc:610 msgid "KB Shortcuts" msgstr "Teclas de atajo" #: f.widgets.cc:611 msgid "RGB Distribution" msgstr "Distribución RGB" #: f.widgets.cc:613 msgid "Find Duplicates" msgstr "Encontrar duplicados" #: f.widgets.cc:615 msgid "Color Profile" msgstr "perfil de color" #: f.widgets.cc:619 msgid "Dark/Bright Pixels" msgstr "Sobre/subexposición" #: f.widgets.cc:620 msgid "Map Pixel Bias" msgstr "Mapa de sesgo de píxeles" #: f.widgets.cc:621 msgid "Map Dead Pixels" msgstr "Mapa de píxeles muertos" #: f.widgets.cc:622 msgid "Monitor Color" msgstr "Color del monitor" #: f.widgets.cc:623 msgid "Monitor Gamma" msgstr "Gamma del monitor" #: f.widgets.cc:624 msgid "Change Language" msgstr "Cambiar idioma" #: f.widgets.cc:625 msgid "Missing Translations" msgstr "Traducciones que faltan" #: f.widgets.cc:626 zfuncs.cc:5885 msgid "Phone Home" msgstr "Reportar estadísticas" #: f.widgets.cc:628 msgid "Show Resources" msgstr "Mostrar recursos" #: f.widgets.cc:629 msgid "Appimage Files" msgstr "Archivos de Appimage" #: f.widgets.cc:630 msgid "Zappcrash Test" msgstr "Prueba de fallo" #: f.widgets.cc:649 msgid "Gallery" msgstr "Galeria" #: f.widgets.cc:650 msgid "Maps" msgstr "Mapas" #: f.widgets.cc:651 f.widgets.cc:1145 msgid "Favorites" msgstr "Favoritos" #: f.widgets.cc:652 msgid "Prev/Next" msgstr "Ant/Sig" #: f.widgets.cc:653 msgid "Zoom/±" msgstr "Zomm/+-" #: f.widgets.cc:655 msgid "Meta" msgstr "Metadatos" #: f.widgets.cc:656 msgid "Areas" msgstr "Areas" #: f.widgets.cc:657 msgid "Undo/Redo" msgstr "Desh/Reh" #: f.widgets.cc:658 fotoxx.h:1350 msgid "Edit" msgstr "Editar" #: f.widgets.cc:659 msgid "Enhance" msgstr "Mejorar" #: f.widgets.cc:660 msgid "Effects" msgstr "Efectos" #: f.widgets.cc:662 msgid "Combine" msgstr "Combinar" #: f.widgets.cc:663 msgid "Process" msgstr "Proceso" #: f.widgets.cc:664 msgid "Tools" msgstr "Herramientas" #: f.widgets.cc:815 msgid "Popup Image" msgstr "Imagen emergente" #: f.widgets.cc:825 msgid "Add Selected Files Here" msgstr "Añadir aquuí archivos seleccionados" #: f.widgets.cc:826 msgid "Add Current File Here" msgstr "Añadir aquí archivo actual" #: f.widgets.cc:827 msgid "Remove from Album" msgstr "Eliminar del álbum" #: f.widgets.cc:837 msgid "Select Area" msgstr "Seleccionar área" #: f.widgets.cc:1105 f.widgets.cc:1124 msgid "Fotoxx Image Locations" msgstr "Fotoxx localizaciones de imagen" #: f.widgets.cc:1173 #, c-format msgid "invalid menu name: %s" msgstr "nombre de manú no válido: %s" #: fotoxx.cc:257 msgid "read+write" msgstr "lectura+escritura" #: fotoxx.cc:258 msgid "read only" msgstr "sólo lectura" #: fotoxx.cc:259 msgid "no access" msgstr "sin acceso" #: fotoxx.cc:465 msgid "Please install missing programs:" msgstr "Por favor instalar los programas necesarios:" #: fotoxx.cc:594 msgid "Index aborted" msgstr "Indexado abortado" #: fotoxx.cc:772 msgid " Defer image file indexing:" msgstr "Diferir la indexación de archivos de imagen:" #: fotoxx.cc:773 msgid "" " • Fotoxx will start immediately \n" " • View and edit image files will work normally \n" " • Image search, batch and map functions will not work \n" " • Thumbnail galleries will be slow" msgstr "" " • Fotoxx se iniciará inmediatamente \n" " • Ver y editar archivos de imagen funcionará normalmente \n" " • Las funciones de búsqueda de imágenes, lote y mapa no funcionarán \n" " • Las galerías de miniaturas serán lentas" #: fotoxx.cc:778 msgid " Index image files now:" msgstr " indexar ahora archivos de imagen" #: fotoxx.cc:779 msgid "" " • Initial indexing may need considerable time \n" " • Subsequent startups will be fast \n" " • Full functionality will be available \n" " • Thumbnail galleries will be fast" msgstr "" " • El indexado inicial puede necesitar un tiempo considerable \n" " • Los inicios posteriores serán rápidos \n" " • La funcionalidad completa estará disponible \n" " • Las galerías de miniaturas serán rápidas" #: fotoxx.cc:784 msgid "" " Indexing time depends on the number of image files and the \n" " speed of your computer. This can be a few hundred to a few \n" " thousand per minute. After indexing is done, startup time \n" " should be quite fast. You can change index options later, \n" " using these menus: Tools > Index and Tools > Preferences. " msgstr "" " El tiempo de indexado depende del número de archivos de imagen y la \n" " velocidad de su computadora. Éste puede ser de unos cientos a unos \n" " mil por minuto. Después del indexado, el tiempo de inicio \n" " debería ser bastante rápido. Puedes cambiar las opciones de índice más " "tarde, \n" " utilizando estos menús: Herramientas> Índice y Herramientas> Preferencias." #: fotoxx.cc:791 msgid "" "Main memory is too small to run Fotoxx. \n" "You can try anyway if you wish. \n" " Continue?" msgstr "" "La memoria principal es demasiado pequeña para ejecutar Fotoxx. \n" "Puede intentarlo de nuevo si lo desea. \n" " ¿Continuar?" #: fotoxx.cc:826 msgid "Fotoxx First Startup" msgstr "Primer arranque de Fotoxx" #: fotoxx.cc:981 msgid "(reduced)" msgstr "(reducido)" #: fotoxx.cc:982 msgid "area active" msgstr "área activa" #: fotoxx.cc:983 msgid "dialog open" msgstr "diálogo abierto" #: fotoxx.cc:984 msgid "blocked" msgstr "bloqueado" #: fotoxx.cc:1040 msgid "edits" msgstr "ediciones" #: fotoxx.cc:1958 msgid "Show Hidden" msgstr "Mostrar ocultos" #: fotoxx.cc:3168 msgid "Exceed 50 anchor points" msgstr "Excedidos 50 puntos de anclaje" #: fotoxx.cc:3397 msgid "load curve from a file" msgstr "Cargar curva desde un archivo" #: fotoxx.cc:3464 msgid "curve file is invalid" msgstr "Archivo de curva no válido" #: fotoxx.cc:3478 msgid "save curve to a file" msgstr "Guardar la curva como un archivo" #: fotoxx.cc:3549 #, c-format msgid "" "File cannot be edited \n" " %s" msgstr "" "El archivo no puede ser editado \n" " %s" #: fotoxx.cc:3562 msgid "Too many edits, please save image" msgstr "Demasiadas ediciones, por favcor guarde la imagen" #: fotoxx.cc:3567 msgid "this function cannot be scripted" msgstr "esta función no puede ser guionada" #: fotoxx.cc:3584 msgid "" "Select area will be ignored. \n" "Continue?" msgstr "" "El área seleccionada se ignorará. \n" "¿Continuar?" #: fotoxx.cc:3590 msgid "" "Select area not active.\n" "Continue?" msgstr "" "La selección de área no está activada.\n" "¿Continuar?" #: fotoxx.cc:3900 msgid "file data does not fit dialog" msgstr "los datos del archivo no se ajustan al diálogo" #: fotoxx.cc:3910 msgid "Save settings to a file" msgstr "Guardar ajustes en un archivo" #: fotoxx.cc:4297 msgid "This action will discard changes to current image" msgstr "Esta acción descartará los cambios a la imagen actual" #: fotoxx.cc:4298 msgid "prior function still active" msgstr "función anterior todavía activa" #: fotoxx.cc:4299 fotoxx.h:1372 msgid "Keep" msgstr "Guardar" #: fotoxx.cc:4300 msgid "Discard" msgstr "Descartar" #: fotoxx.h:1315 msgid "Add" msgstr "Añadir" #: fotoxx.h:1316 msgid "Add All" msgstr "Añadir todo" #: fotoxx.h:1318 msgid "Amount" msgstr "Cantidad" #: fotoxx.h:1319 msgid "Angle" msgstr "Ángulo" #: fotoxx.h:1320 zfuncs.cc:7936 msgid "Apply" msgstr "Aplicar" #: fotoxx.h:1321 msgid "Auto" msgstr "Auto" #: fotoxx.h:1322 msgid "Black" msgstr "Negro" #: fotoxx.h:1323 msgid "Blend Width" msgstr "Ancho de mezclado" #: fotoxx.h:1324 msgid "Blue" msgstr "A" #: fotoxx.h:1325 zfuncs.cc:12913 msgid "Bottom" msgstr "Abajo" #: fotoxx.h:1327 zfuncs.cc:7950 msgid "Browse" msgstr "Examinar" #: fotoxx.h:1328 msgid "Calculate" msgstr "Calcular" #: fotoxx.h:1329 zfuncs.cc:7936 zfuncs.cc:12528 zfuncs.cc:12680 msgid "Cancel" msgstr "Cancelar" #: fotoxx.h:1330 msgid "Center" msgstr "centro" #: fotoxx.h:1331 msgid "Change" msgstr "Cambiar" #: fotoxx.h:1332 msgid "Choose" msgstr "Elegir" #: fotoxx.h:1334 msgid "click thumbnail to select file" msgstr "clic en la miniatura para seleccionar archivo" #: fotoxx.h:1335 msgid "Close" msgstr "Cerrar" #: fotoxx.h:1336 msgid "Color" msgstr "Color" #: fotoxx.h:1337 msgid "COMPLETED" msgstr "COMPLETADO" #: fotoxx.h:1338 msgid "continue" msgstr "continuar" #: fotoxx.h:1341 msgid "Create" msgstr "Crear" #: fotoxx.h:1342 msgid "Curve File:" msgstr "Archivo de curva:" #: fotoxx.h:1343 msgid "Cut" msgstr "Cortar" #: fotoxx.h:1344 msgid "Deband" msgstr "Eliminar bandas" #: fotoxx.h:1345 zfuncs.cc:7936 msgid "Delete" msgstr "Borrar" #: fotoxx.h:1347 msgid "Display" msgstr "Monitor" #: fotoxx.h:1348 msgid "Done" msgstr "Hecho" #: fotoxx.h:1349 msgid "edge" msgstr "borde" #: fotoxx.h:1352 msgid "Erase" msgstr "Borrar" #: fotoxx.h:1353 msgid "Fetch" msgstr "Extraer" #: fotoxx.h:1354 msgid "output file already exists" msgstr "el archivo de salida ya existe" #: fotoxx.h:1355 msgid "file not found" msgstr "archivo no encontrado" #: fotoxx.h:1356 #, c-format msgid "file not found: %s" msgstr "archivo no encontrado: %s" #: fotoxx.h:1357 #, c-format msgid "%d image files selected" msgstr "% imágenes seleccionadas" #: fotoxx.h:1358 msgid "Find" msgstr "Encontrar" #: fotoxx.h:1359 msgid "Finish" msgstr "Terminar" #: fotoxx.h:1361 msgid "Font" msgstr "Tipo de letra" #: fotoxx.h:1362 #, c-format msgid "gallery truncated to %d images" msgstr "galería truncada a %d imágenes" #: fotoxx.h:1363 msgid "Green" msgstr "V" #: fotoxx.h:1364 msgid "Grid" msgstr "Rejilla" #: fotoxx.h:1365 zfuncs.cc:12943 msgid "Height" msgstr "Alto" #: fotoxx.h:1368 zfuncs.cc:12935 msgid "Image" msgstr "Imagen" #: fotoxx.h:1369 msgid "Images" msgstr " Imágenes" #: fotoxx.h:1370 msgid "Insert" msgstr "Insertar" #: fotoxx.h:1373 zfuncs.cc:12917 msgid "Left" msgstr "Izquierda" #: fotoxx.h:1374 msgid "Length" msgstr "Longitud" #: fotoxx.h:1375 msgid "limit" msgstr "límite" #: fotoxx.h:1377 msgid "Magnify" msgstr "Aumentar" #: fotoxx.h:1378 msgid "Make" msgstr "Hacer" #: fotoxx.h:1379 msgid "Map" msgstr "Mapa" #: fotoxx.h:1380 msgid "Match Level:" msgstr "Nivel de coincidencia:" #: fotoxx.h:1381 msgid "Max" msgstr "Máx" #: fotoxx.h:1382 msgid "Measure" msgstr "Medida" #: fotoxx.h:1383 msgid "mouse radius" msgstr "radio del cursor" #: fotoxx.h:1384 msgid "Negative" msgstr "Negativo" #: fotoxx.h:1385 msgid "New" msgstr "Nueva" #: fotoxx.h:1386 msgid "Next" msgstr "Siguiente" #: fotoxx.h:1387 zfuncs.cc:11875 msgid "No" msgstr "No" #: fotoxx.h:1388 msgid "no write permission" msgstr "no hay permisos de escritura" #: fotoxx.h:1389 msgid "no images" msgstr "sin imágenes" #: fotoxx.h:1390 msgid "image index disabled" msgstr "índice de imagen deshabilitado" #: fotoxx.h:1391 msgid "no image files selected" msgstr "no hay imágenes seleccionadas" #: fotoxx.h:1392 msgid "None" msgstr "Ninguno" #: fotoxx.h:1393 msgid "no selection" msgstr "sin selección" #: fotoxx.h:1394 msgid "image index not updated" msgstr "índice de imágenes no actualizado" #: fotoxx.h:1395 msgid "opacity center" msgstr "opacidad del centro" #: fotoxx.h:1396 msgid "opacity edge" msgstr "opacidad del borde" #: fotoxx.h:1398 msgid "Paint Radius" msgstr "Radio a pintar" #: fotoxx.h:1400 msgid "Pause" msgstr "Pausa" #: fotoxx.h:1401 msgid "Percent" msgstr "Porcentaje" #: fotoxx.h:1403 msgid "Presets" msgstr "Predefinidos" #: fotoxx.h:1404 msgid "Prev" msgstr "Anterior" #: fotoxx.h:1405 msgid "use previous input" msgstr "usar entrada previa" #: fotoxx.h:1406 msgid "Proceed" msgstr "Proceder" #: fotoxx.h:1409 msgid "range" msgstr "rango" #: fotoxx.h:1410 msgid "Red" msgstr "R" #: fotoxx.h:1411 msgid "Redo" msgstr "Rehacer" #: fotoxx.h:1412 msgid "Reduce" msgstr "Reducir" #: fotoxx.h:1413 msgid "Remove" msgstr "Eliminar" #: fotoxx.h:1415 msgid "Replace" msgstr "Reemplazar" #: fotoxx.h:1416 msgid "Reserved" msgstr "Reservado" #: fotoxx.h:1417 msgid "Reset" msgstr "Restablecer" #: fotoxx.h:1418 zfuncs.cc:12921 msgid "Right" msgstr "Derecha" #: fotoxx.h:1419 msgid "Rotate" msgstr "Rotar" #: fotoxx.h:1420 msgid "Run" msgstr "Ejecutar" #: fotoxx.h:1423 msgid "Seconds" msgstr "Segundos" #: fotoxx.h:1427 msgid "Size" msgstr "Tamaño" #: fotoxx.h:1428 msgid "Start" msgstr "Comenzar" #: fotoxx.h:1429 msgid "Stop" msgstr "Parar" #: fotoxx.h:1430 msgid "Strength" msgstr "Fuerza" #: fotoxx.h:1431 msgid "the area is not finished" msgstr "el área no está cerrada" #: fotoxx.h:1432 msgid "Threshold" msgstr "Umbral" #: fotoxx.h:1433 zfuncs.cc:12909 msgid "Top" msgstr "Arriba" #: fotoxx.h:1434 msgid "Transparency" msgstr "Transparencia" #: fotoxx.h:1435 msgid "Trash" msgstr "Papelera" #: fotoxx.h:1436 msgid "Trim" msgstr "Recortar" #: fotoxx.h:1437 msgid "Undo All" msgstr "Deshacer todo" #: fotoxx.h:1438 msgid "Undo Last" msgstr "Deshacer el último" #: fotoxx.h:1439 msgid "Undo" msgstr "Deshacer" #: fotoxx.h:1440 msgid "Unfinish" msgstr "Sin cerrar" #: fotoxx.h:1441 msgid "Update" msgstr "Actualizar" #: fotoxx.h:1442 msgid "View" msgstr "Ver" #: fotoxx.h:1443 msgid "Web" msgstr "Web" #: fotoxx.h:1444 msgid "White" msgstr "Blanco" #: fotoxx.h:1445 zfuncs.cc:12939 msgid "Width" msgstr "Ancho" #: fotoxx.h:1446 msgid "x-offset" msgstr "desplazamiento X" #: fotoxx.h:1447 msgid "y-offset" msgstr "desplazamiento Y" #: fotoxx.h:1448 zfuncs.cc:11875 msgid "Yes" msgstr "Sí" #: zfuncs.cc:1836 #, c-format msgid "" "create folder? \n" " %s" msgstr "" "¿Crear directorio? \n" " %s" #: zfuncs.cc:5880 msgid "" "If you permit, a message is occasionally \n" "sent to the web host for usage statistics. \n" "Nothing is retained that can be associated \n" "with a person or computer or location." msgstr "" "Si lo permite, ocasionalmente aparece un mensaje \n" "enviado al servidor web para estadísticas de uso. \n" "No se retiene nada que pueda asociarse \n" "con una persona, computadora o ubicación." #: zfuncs.cc:6743 #, c-format msgid "cannot open file %s" msgstr "No puedo abrir el archivo %s" #: zfuncs.cc:6766 msgid "save text to file" msgstr "guardar texto en archivo" #: zfuncs.cc:7936 msgid "edit menu entry" msgstr "editar entrada de menú" #: zfuncs.cc:7940 msgid "menu text" msgstr "texto del menú" #: zfuncs.cc:7941 msgid "menu func" msgstr "función del menú" #: zfuncs.cc:7942 msgid "menu icon" msgstr "icono del menú" #: zfuncs.cc:7943 msgid "icon size" msgstr "tamaño de icono" #: zfuncs.cc:7946 msgid "Bold" msgstr "Negrita" #: zfuncs.cc:7953 msgid "close window" msgstr "cerrar ventana" #: zfuncs.cc:8000 msgid "select icon" msgstr "seleccionar icono" #: zfuncs.cc:12000 zfuncs.cc:12896 msgid "cancel" msgstr "cancelar" #: zfuncs.cc:12489 msgid "choose file" msgstr "elegir archivo" #: zfuncs.cc:12494 msgid "choose files" msgstr "elegir archivos" #: zfuncs.cc:12505 msgid "choose folder" msgstr "elegir carpeta" #: zfuncs.cc:12510 msgid "choose folders" msgstr "elegir carpetas" #: zfuncs.cc:12515 msgid "create folder" msgstr "crear carpeta" #: zfuncs.cc:12522 msgid "hidden" msgstr "ocultar" #: zfuncs.cc:12896 msgid "done" msgstr "hecho" #: zfuncs.cc:12926 msgid "image scale" msgstr "escalar imagen" #: zfuncs.cc:12928 msgid "percent" msgstr "porcentual" #~ msgid "% of scale" #~ msgstr "% of scale" #~ msgid "Left/right click: smaller/larger image/thumbnails" #~ msgstr "Clic izquierdo/derecho: imagen/miniaturas más pequeñas/más grandes" fotoxx-20.08/locales/translate-fr.po000066400000000000000000005457121362435004500174560ustar00rootroot00000000000000# French translations for home package. # msgid "" msgstr "" "Project-Id-Version: home 2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-12-25 23:01+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:59 #, c-format msgid "max. album size exceeded: %d" msgstr "taille maximale d'un album dépassée: %d" #: f.albums.cc:70 msgid "" "Right-click album thumbnail for edits: \n" " - add selected files at this position \n" " - remove this file from the album" msgstr "" "Clic droit sur une vignette dans un album pour édition: \n" " - ajouter les fichiers sélectionnés à cette position \n" " - enlever ce fichier de l'album" #: f.albums.cc:73 msgid "Arrange files with thumbnail drag and drop" msgstr "Disposer les fichiers en cliquant-tirant leur vignette" #: f.albums.cc:102 f.widgets.cc:458 msgid "Manage Albums" msgstr "Gérer les albums" #: f.albums.cc:113 f.albums.cc:232 msgid "Create or replace an album" msgstr "Créer ou remplacer un album" #: f.albums.cc:115 f.albums.cc:381 msgid "Rename an album" msgstr "Renommer un album" #: f.albums.cc:117 f.albums.cc:451 msgid "Delete an album" msgstr "Supprimer un album" #: f.albums.cc:119 msgid "Files to add to an album" msgstr "Fichiers à ajouter à un album" #: f.albums.cc:183 #, c-format msgid "%d files added to album %s" msgstr "%d fichiers ajoutés à l'album %s" #: f.albums.cc:213 #, c-format msgid "max. album count exceeded: %d" msgstr "nombre maximum d'albums atteint: %d" #: f.albums.cc:234 f.albums.cc:270 f.albums.cc:1537 msgid "Album Name" msgstr "Nom de l'album" #: f.albums.cc:240 msgid "make an initially empty album" msgstr "créer un album initialement vide" #: f.albums.cc:241 msgid "fill from pre-selected files" msgstr "remplir avec les fichiers pré-sélectionnés" #: f.albums.cc:242 msgid "fill from current gallery" msgstr "remplir depuis la galerie actuelle" #: f.albums.cc:243 msgid "select initial files" msgstr "sélectionner les fichiers de départ" #: f.albums.cc:284 msgid "enter an album name" msgstr "entrer un nom pour l'album" #: f.albums.cc:292 f.albums.cc:1569 #, c-format msgid "replace album %s ?" msgstr "remplacer l'album %s ?" #: f.albums.cc:312 msgid "Use [Select] to add files to an empty album" msgstr "Utiliser [Select] pour ajouter des fichiers à un album vide" #: f.albums.cc:326 msgid "new album created" msgstr "nouvel album créé" #: f.albums.cc:342 f.meta.cc:7832 msgid "gallery is empty" msgstr "la galerie est vide" #: f.albums.cc:396 msgid "enter new album name" msgstr "entrer un nouveau nom pour l'album" #: f.albums.cc:403 #, c-format msgid "invalid file name: %s" msgstr "nom de fichier non valide: %s" #: f.albums.cc:424 #, c-format msgid "album already exists: %s" msgstr "l'album existe déjà: %s" #: f.albums.cc:435 #, c-format msgid "" "%s \n" " renamed: %s" msgstr "" "%s \n" " renommé en: %s" #: f.albums.cc:464 #, c-format msgid "delete %s ?" msgstr "supprimer %s ?" #: f.albums.cc:571 f.albums.cc:584 #, c-format msgid "%s removed with no replacement \n" msgstr "%s enlevé sans remplacement \n" #: f.albums.cc:589 #, c-format msgid "%s replaced by ...%s \n" msgstr "%s remplacé par ...%s \n" #: f.albums.cc:664 msgid "no selected files" msgstr "aucun fichier sélectionné" #: f.albums.cc:877 msgid "Replace Album File" msgstr "Modifier le contenu d'un/de fichier(s) album" #: f.albums.cc:879 f.albums.cc:1004 msgid "Choose Albums" msgstr "Choisir les albums" #: f.albums.cc:883 msgid "old file" msgstr "ancien fichier" #: f.albums.cc:886 msgid "new file" msgstr "nouveau fichier" #: f.albums.cc:889 msgid "replace old" msgstr "remplacer l'ancien" #: f.albums.cc:890 msgid "add after old" msgstr "ajouter après l'ancien" #: f.albums.cc:936 msgid "no albums chosen" msgstr "aucun album n'a été sélectionné" #: f.albums.cc:1117 f.widgets.cc:460 msgid "Album Mass Update" msgstr "Mise à jour en masse des albums" #: f.albums.cc:1125 msgid "Process all album files:" msgstr "Traitement de tous les fichiers des albums:" #: f.albums.cc:1128 msgid "Replace all with newest version only" msgstr "Tout remplacer par la version la plus récente seulement" #: f.albums.cc:1130 msgid "Replace all versions with newest version" msgstr "Remplacer toutes les versions par la version la plus récente" #: f.albums.cc:1132 msgid "Add newest version to existing versions" msgstr "Ajouter la version la plus récente aux versions existantes" #: f.albums.cc:1134 msgid "Replace all with original and all versions" msgstr "Tout remplacer avec l'original et toutes les versions" #: f.albums.cc:1136 msgid "Replace all with original + newest version" msgstr "Remplacer tout avec l'original + la nouvelle version" #: f.albums.cc:1140 msgid "Process album files matching selected files:" msgstr "Traiter les fichiers correspondant aux fichiers sélectionnés:" #: f.albums.cc:1143 msgid "Replace all with selected versions" msgstr "Tout remplacer avec les versions sélectionnées" #: f.albums.cc:1145 msgid "Replace all versions with selected versions" msgstr "Remplacer toutes les versions avec les versions sélectionnées" #: f.albums.cc:1147 msgid "Add selected versions to existing versions" msgstr "Ajouter les versions sélectionnées aux versions existantes" #: f.albums.cc:1149 msgid "Replace all with original + selected versions" msgstr "Tout remplacer avec l'original + les versions sélectionnées" #: f.albums.cc:1237 msgid "Select Albums" msgstr "Sélectionner des albums" #: f.albums.cc:1296 #, c-format msgid "too many selected files: %d" msgstr "trop de fichiers sélectionnés: %d" #: f.albums.cc:1535 msgid "Save Gallery as Album" msgstr "Enregistrer la Galerie en tant qu'Album" #: f.albums.cc:1739 msgid "instant" msgstr "instantané" #: f.albums.cc:1740 msgid "fade-in" msgstr "fondu" #: f.albums.cc:1741 msgid "roll-right" msgstr "enroulement vers la droite" #: f.albums.cc:1742 msgid "roll-down" msgstr "enroulement vers le bas" #: f.albums.cc:1743 msgid "venetian" msgstr "store vénitien" #: f.albums.cc:1744 msgid "grate" msgstr "grillage" #: f.albums.cc:1745 msgid "rectangle" msgstr "rectangle" #: f.albums.cc:1746 msgid "implode" msgstr "imploser" #: f.albums.cc:1747 msgid "explode" msgstr "exploser" #: f.albums.cc:1748 msgid "radar" msgstr "radar" #: f.albums.cc:1749 msgid "Japan-fan" msgstr "éventail japonais" #: f.albums.cc:1750 msgid "spiral" msgstr "spirale" #: f.albums.cc:1751 f.area.cc:140 msgid "ellipse" msgstr "ellipse" #: f.albums.cc:1752 msgid "raindrops" msgstr "gouttes de pluie" #: f.albums.cc:1753 msgid "doubledoor" msgstr "double porte" #: f.albums.cc:1754 msgid "rotate" msgstr "rotation" #: f.albums.cc:1755 msgid "fallover" msgstr "chute" #: f.albums.cc:1756 msgid "spheroid" msgstr "spheroïde" #: f.albums.cc:1757 msgid "turn-page" msgstr "tourner une page" #: f.albums.cc:1758 msgid "french-door" msgstr "porte française" #: f.albums.cc:1759 msgid "turn-cube" msgstr "cube rotatif" #: f.albums.cc:1760 msgid "windmill" msgstr "moulin à vent" #: f.albums.cc:1761 msgid "pixelize" msgstr "pixelisation" #: f.albums.cc:1762 msgid "twist" msgstr "torsion" #: f.albums.cc:1763 msgid "Xopen" msgstr "Xopen" #: f.albums.cc:1764 msgid "squish" msgstr "écraser" #: f.albums.cc:1765 msgid "disintegrate" msgstr "désintégration" #: f.albums.cc:1766 msgid "interleave" msgstr "intercalation" #: f.albums.cc:1797 f.widgets.cc:462 msgid "Slide Show" msgstr "Diaporama" #: f.albums.cc:1800 msgid "Select Album" msgstr "Sélectionner un album" #: f.albums.cc:1805 msgid "Caption Time" msgstr "Délai d'affichage d'une légende" #: f.albums.cc:1808 msgid "Image Time" msgstr "Temps d'affichage d'une image" #: f.albums.cc:1811 msgid "Clip Limit %" msgstr "Limite maximale de troncature %" #: f.albums.cc:1815 msgid "Music File" msgstr "Fichier musical" #: f.albums.cc:1820 msgid "Full Screen" msgstr "Plein écran" #: f.albums.cc:1821 msgid "Auto-replay" msgstr "Jouer en boucle" #: f.albums.cc:1824 msgid "Customize:" msgstr "Personnaliser:" #: f.albums.cc:1825 msgid "transitions" msgstr "transitions" #: f.albums.cc:1826 msgid "image files" msgstr "fichiers image" #: f.albums.cc:1827 msgid "KB controls" msgstr "Contrôles du clavier" #: f.albums.cc:1841 f.albums.cc:1908 f.albums.cc:2141 f.albums.cc:2450 #: f.albums.cc:2786 f.albums.cc:2803 f.albums.cc:3039 msgid "invalid album" msgstr "album non valide" #: f.albums.cc:1936 msgid "open album" msgstr "ouvrir l'album" #: f.albums.cc:1948 msgid "Select music file" msgstr "Selectionner un fichier musical" #: f.albums.cc:1983 #, c-format msgid "%d images" msgstr "%d images" #: f.albums.cc:2008 msgid "" "arrow keys show previous or next image instantly \n" "space bar (blank) is allowed and shows as '-'" msgstr "" "les touches fléchées affichent instantanément l'image précédente ou " "suivante \n" "la barre d'espace est acceptée et est symbolisée par un '-'" #: f.albums.cc:2027 msgid "Keyboard Preferences" msgstr "Préférences du clavier" #: f.albums.cc:2030 msgid "blank or unblank window" msgstr "vider ou remplir la fenêtre" #: f.albums.cc:2033 msgid "show next image, with transition" msgstr "afficher l'image suivante, avec transition" #: f.albums.cc:2036 msgid "pause or resume slide show" msgstr "suspendre ou reprendre le diaporama" #: f.albums.cc:2039 msgid "magnify image (loupe tool)" msgstr "agrandir l'image (outil loupe)" #: f.albums.cc:2145 msgid "Transition Preferences" msgstr "Préférences de transition" #: f.albums.cc:2147 msgid "Transitions File" msgstr "Fichier de transitions" #: f.albums.cc:2156 msgid "random" msgstr "sélectionner aléatoirement" #: f.albums.cc:2159 f.albums.cc:2175 f.albums.cc:2179 msgid "time" msgstr "délai de transition" #: f.albums.cc:2161 msgid "set all" msgstr "valable pour toutes" #: f.albums.cc:2173 f.albums.cc:2177 msgid "transition" msgstr "transition" #: f.albums.cc:2174 f.albums.cc:2178 msgid "use" msgstr "utiliser" #: f.albums.cc:2176 f.albums.cc:2180 msgid "pref" msgstr "préférence" #: f.albums.cc:2280 f.albums.cc:2322 msgid "invalid file" msgstr "fichier non valide" #: f.albums.cc:2394 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "erreur de format de fichier: \n" " %s" #: f.albums.cc:2456 msgid "Image Preferences" msgstr "Préférences d'image" #: f.albums.cc:2460 f.file.cc:1610 f.file.cc:1995 msgid "Image File:" msgstr "Fichier image:" #: f.albums.cc:2464 msgid "Action" msgstr "Action" #: f.albums.cc:2476 msgid "Play tone when image shows" msgstr "Jouer un son quand l'image s'affiche" #: f.albums.cc:2481 msgid "Wait before filename/caption/comments" msgstr "Attente avant l'afficehgs du nom de fichier/légende/commentaires" #: f.albums.cc:2485 msgid "Show image file name (overlap)" msgstr "Montrer le nom du fichier image (superposition)" #: f.albums.cc:2489 msgid "Show image caption (overlap)" msgstr "Montrer la légende de l'image (superposition)" #: f.albums.cc:2493 msgid "Show image comments (overlap)" msgstr "Montrer les commentaires de l'image (superposition)" #: f.albums.cc:2497 msgid "Wait before zoom" msgstr "Attente avant le zoom" #: f.albums.cc:2501 f.effects.cc:4023 msgid "Zoom" msgstr "Zoom" #: f.albums.cc:2505 msgid "zoom-in" msgstr "zoom avant" #: f.albums.cc:2506 msgid "zoom-out" msgstr "zoom arrière" #: f.albums.cc:2510 msgid "Zoom Center" msgstr "Centre du zoom" #: f.albums.cc:2515 msgid "Wait after zoom" msgstr "Attente après le zoom" #: f.albums.cc:2521 msgid "Transition to next image" msgstr "Transition vers l'image suivante" #: f.albums.cc:2524 f.albums.cc:2654 msgid "next" msgstr "suivante" #: f.albums.cc:2644 msgid "click on thumbnail to set zoom center" msgstr "cliquer sur la vignette pour centrer le zoom" #: f.area.cc:93 msgid "Select Area for Edits" msgstr "Sélectionner une zone à éditer" #: f.area.cc:94 f.area.cc:1890 f.edit.cc:8198 msgid "Press F1 for help" msgstr "Presser F1 pour l'aide" #: f.area.cc:104 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Sélectionner une zone n'est pas supporté \n" "par cette fonction d'édition" #: f.area.cc:139 msgid "select rectangle" msgstr "sélectionner un rectangle" #: f.area.cc:143 msgid "freehand draw" msgstr "dessin à main levée" #: f.area.cc:144 msgid "follow edge" msgstr "suivre le bord" #: f.area.cc:147 msgid "draw/replace" msgstr "dessiner/remplacer" #: f.area.cc:150 msgid "Line Color:" msgstr "Couleur de la ligne:" #: f.area.cc:164 msgid "match level %" msgstr "niveau de correspondance %" #: f.area.cc:168 msgid "search range" msgstr "plage de recherche" #: f.area.cc:172 msgid "select area in mouse" msgstr "sélectionner la zone dans le pointeur de la souris" #: f.area.cc:175 msgid "select one color in mouse:" msgstr "sélectionner une couleur à l'intérieur du cercle de la souris:" #: f.area.cc:179 msgid "select all colors in mouse (flood)" msgstr "sélectionner toutes les couleurs autour du pointeur de la souris" #: f.area.cc:184 msgid "Area Edge Blend Width" msgstr "Largeur du fondu des bords de la zone" #: f.area.cc:188 msgid "Edge Creep" msgstr "Fluage du bord de la zone" #: f.area.cc:200 msgid "drag mouse to select rectangular area" msgstr "tirer la souris pour sélectionner une zone rectangulaire" #: f.area.cc:201 msgid "drag mouse to select circular or elliptical area" msgstr "tirer la souris pour sélectionner une zone circulaire ou elliptique" #: f.area.cc:202 msgid "drag mouse to outline an area" msgstr "tirer la souris pour créer le contour d'une zone" #: f.area.cc:203 msgid "drag mouse along an edge to follow the edge" msgstr "tirer la souris le long d'un bord pour suivre ce bord" #: f.area.cc:204 msgid "drag mouse near a line to move the line" msgstr "tirer la souris près d'une ligne pour bouger cette ligne" #: f.area.cc:205 msgid "select line color" msgstr "sélectionner la couleur de la ligne" #: f.area.cc:206 msgid "size of mouse selection circle" msgstr "taille du cercle de sélection de la souris" #: f.area.cc:207 msgid "required match level to select by color" msgstr "niveau de correspondance voulu pour la sélection par couleur" #: f.area.cc:208 msgid "select by color search range" msgstr "plage de recherche pour la sélection par couleur" #: f.area.cc:209 msgid "select area within mouse circle" msgstr "sélectionner une zone à l'intérieur du cercle de la souris" #: f.area.cc:210 msgid "" "first select the checkbox, then \n" "shift+click on image to set the color" msgstr "" "sélectionner d'abord la case à cocher, puis \n" "maj+clic sur l'image pour choisir la couleur" #: f.area.cc:212 msgid "select surrounding areas matching colors in mouse" msgstr "" "sélectionner les zones environnantes correspondant aux couleurs dans le " "cercle de la souris" #: f.area.cc:213 f.area.cc:214 msgid "area edits fade away within edge distance" msgstr "" "la force des modifications apportées à la zone augmente avec la distance aux " "bords" #: f.area.cc:215 msgid "move area boundary in/out in 1-pixel steps" msgstr "" "modifier les contours de la zone vers l'intérieur ou l'extérieur par pas de " "1 pixel" #: f.area.cc:216 msgid "map selected areas and verify" msgstr "tracer les zones sélectionnées et les vérifier" #: f.area.cc:217 msgid "invert area" msgstr "inverser la zone" #: f.area.cc:218 msgid "show area outlines" msgstr "montrer les contours de la zone" #: f.area.cc:219 msgid "hide area outlines" msgstr "cacher les contours de la zone" #: f.area.cc:220 msgid "clear area selections" msgstr "remettre à zéro les zones préalablement sélectionnées" #: f.area.cc:404 f.area.cc:570 #, c-format msgid "exceed %d edits" msgstr "excède %d modifications" #: f.area.cc:1471 msgid "" "Fill selected areas with color for visual verification. \n" "Method 1: left-click in each selected area not already filled. \n" "Method 2: right-click OUTSIDE all selected areas. \n" "Press [help] button for clarification" msgstr "" "Remplir de couleur les zones sélectionnées pour vérification visuelle. \n" "Méthode 1: clic gauche dans chaque zone sélectionnée et non déjà remplie. \n" "Méthode 2: clic droit EN DEHORS de toutes les zones sélectionnées. \n" "Presser le bouton [Aide] pour plus d'explications" #: f.area.cc:1499 msgid "finish area" msgstr "finir la zone" #: f.area.cc:1528 f.area.cc:1757 #, c-format msgid "found %d pixels" msgstr "trouvé %d pixels" #: f.area.cc:1546 msgid "" "Method 1: \n" " Left-click inside an outlined area that is not already filled. \n" " Area will be filled with color for visible verification. \n" " Gaps in the outline will cause overflow outside the area. \n" " Repeat for each outlined area that is not already filled. \n" "Method 2: \n" " Right-click outside ALL outlined areas. \n" " All areas will be filled with color for visible verification. \n" " Gaps in an area outline will cause that area not to be filled. \n" "Gaps in an area outline: \n" " Gaps must be closed before proceeding with edits. \n" " The Find Gap function can be used for this." msgstr "" "Méthode 1: \n" " Clic gauche à l'intérieur du contour d'une zone non encore remplie. \n" " La zone sera remplie de couleur pour vérification visuelle. \n" " Des discontinuités dans le contour de la zone provoqueront un " "débordement. \n" " Répéter pour chaque zone détourée non encore remplie. \n" "Méthode 2: \n" " Clic droit en dehors de TOUTES les zones détourées. \n" " Toutes les zones seront remplies de couleur pour vérification visuelle. \n" " Des discontinuités dans un contour de zone empêcheront cette zone de se " "remplir. \n" "Discontinuités dans le contour d'une zone: \n" " Ces manques doivent être fermés avant de continuer avec les opérations " "d'édition. \n" " La fonction Trouver Une Discontinuité peut être utilisée pour cela." #: f.area.cc:1889 f.widgets.cc:487 msgid "Select Hairy" msgstr "Sélection plumeuse" #: f.area.cc:1899 msgid "select the area first" msgstr "sélectionner d'abord la zone" #: f.area.cc:1975 msgid "select" msgstr "sélectionner" #: f.area.cc:1981 msgid "deselect" msgstr "désélectionner" #: f.area.cc:2335 msgid "" "Click near any position on the area outline. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Cliquer à proximité de n'importe quelle position sur le contour de la " "zone. \n" "D'éventuelles lacunes dans le contour seront repérées. \n" "Presser F1 pour l'aide." #: f.area.cc:2356 msgid "find outline gap" msgstr "trouver une discontinuité du contour" #: f.area.cc:2451 msgid "cannot find area outline" msgstr "impossible de trouver le contour de la zone" #: f.area.cc:3285 msgid "save area as a PNG file" msgstr "enregistrer la zone en tant que fichier PNG" #: f.area.cc:3407 msgid "position with mouse click/drag" msgstr "position avec cliquer/tirer de la souris" #: f.area.cc:3467 msgid "Paste Image" msgstr "Coller une image" #: f.area.cc:3472 msgid "resize" msgstr "redimensionner" #: f.combine.cc:174 f.combine.cc:828 f.combine.cc:1436 f.combine.cc:2066 #: f.combine.cc:2510 msgid "Select 2 to 9 files" msgstr "Sélectionner de 2 à 9 fichiers" #: f.combine.cc:196 f.combine.cc:850 f.combine.cc:1458 f.combine.cc:2088 msgid "Images are not all the same size" msgstr "Les images n'ont pas toutes la même taille" #: f.combine.cc:560 msgid "Adjust Image Contributions" msgstr "Ajuster la contribution respective de chaque image" #: f.combine.cc:564 msgid "dark pixels" msgstr "pixels sombres" #: f.combine.cc:566 msgid "light pixels" msgstr "pixels clairs" #: f.combine.cc:568 msgid "file:" msgstr "fichier:" #: f.combine.cc:1036 msgid "Paint and Warp Image" msgstr "Peindre et déformer l'image" #: f.combine.cc:1044 f.edit.cc:6054 msgid "paint" msgstr "peindre" #: f.combine.cc:1045 msgid "warp" msgstr "déformer" #: f.combine.cc:1648 f.combine.cc:2594 msgid "Select and Paint Image" msgstr "Sélectionner et peindre l'image" #: f.combine.cc:1656 msgid "Transient Objects" msgstr "Objets transitoires" #: f.combine.cc:2264 msgid "Adjust Pixel Composition" msgstr "Ajuster la composition de pixels" #: f.combine.cc:2266 msgid "use average" msgstr "utiliser la moyenne" #: f.combine.cc:2267 msgid "use median" msgstr "utiliser la médiane" #: f.combine.cc:2269 msgid "omit low pixel" msgstr "omettre les pixels bas" #: f.combine.cc:2270 msgid "omit high pixel" msgstr "omettre les pixels hauts" #: f.combine.cc:2532 f.combine.cc:2844 msgid "Images must be exactly the same size" msgstr "Les images doivent avoir exactement la même taille" #: f.combine.cc:2605 msgid " Fill " msgstr "Remplir" #: f.combine.cc:2606 msgid "using selected image" msgstr "en utilisant l'image sélectionnée" #: f.combine.cc:2825 f.combine.cc:3095 msgid "Select 2 files" msgstr "Sélectionner 2 fichiers" #: f.combine.cc:2902 msgid "Split two Images" msgstr "Partager deux images" #: f.combine.cc:2903 msgid "drag image boundary" msgstr "déplacer la limite entre les images" #: f.combine.cc:3092 msgid "Image Differences" msgstr "Différence entre des images" #: f.combine.cc:3108 msgid "differences" msgstr "différences" #: f.combine.cc:3110 msgid "X-align" msgstr "Alignement X" #: f.combine.cc:3113 msgid "Y-align" msgstr "Alignement Y" #: f.combine.cc:3179 msgid "select exactly 2 files" msgstr "sélectionner exactement 2 fichiers" #: f.combine.cc:3408 f.combine.cc:4562 msgid "Select 2 to 4 files" msgstr "Sélectionner de 2 à 4 fichiers" #: f.combine.cc:3483 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from lower edge." msgstr "" "Tirer les images en un alignement approximatif.\n" "Pour les faire tourner, tirer depuis le bord inférieur." #: f.combine.cc:3485 f.combine.cc:4639 msgid "no curve (scanned image)" msgstr "pas de courbe (image scannée)" #: f.combine.cc:3486 f.combine.cc:4640 msgid "Search for lens mm" msgstr "Chercher l'objectif mm" #: f.combine.cc:3487 f.combine.cc:4641 msgid "Save lens mm → image EXIF" msgstr "Enregistrer objectif mm → image EXIF" #: f.combine.cc:3547 f.combine.cc:4701 msgid "Pre-align Images" msgstr "Pré-aligner les images" #: f.combine.cc:3551 f.combine.cc:4705 msgid "lens mm" msgstr "objectif mm" #: f.combine.cc:3556 f.combine.cc:4710 msgid "no auto warp" msgstr "pas de déformation automatique" #: f.combine.cc:3558 f.combine.cc:4712 msgid "manual align" msgstr "alignement manuel" #: f.combine.cc:3560 f.combine.cc:4714 f.process.cc:1395 f.widgets.cc:504 #: f.widgets.cc:829 msgid "Resize" msgstr "Redimensionner" #: f.combine.cc:3561 f.combine.cc:4715 msgid "resize window" msgstr "redimensionner la fenêtre" #: f.combine.cc:3569 f.combine.cc:4723 msgid "do not warp images during auto-alignment" msgstr "ne pas déformer les images pendant l'auto-alignement" #: f.combine.cc:3614 f.combine.cc:4768 msgid "use two images only" msgstr "n'utiliser que deux images" #: f.combine.cc:3644 f.combine.cc:3848 f.combine.cc:4034 f.combine.cc:4796 #: f.combine.cc:5000 f.combine.cc:5186 msgid "Too little overlap, cannot align" msgstr "Trop peu de recouvrement, impossible d'aligner" #: f.combine.cc:4112 f.combine.cc:5264 msgid "Match Brightness and Color" msgstr "Correspondance luminosité et couleur" #: f.combine.cc:4158 msgid "Select image" msgstr "Sélectionner une image" #: f.combine.cc:4173 f.combine.cc:5325 msgid "auto color" msgstr "couleur automatique" #: f.combine.cc:4174 f.combine.cc:5326 msgid "file color" msgstr "couleur du fichier" #: f.combine.cc:4180 f.combine.cc:5332 msgid "mouse warp" msgstr "déformation avec la souris" #: f.combine.cc:4182 f.combine.cc:5334 msgid "flatten image" msgstr "aplatir l'image" #: f.combine.cc:4637 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from right edge." msgstr "" "Tirer les images en un alignement approximatif.\n" "Pour les faire tourner, tirer depuis le bord droit." #: f.combine.cc:5596 msgid "pano tools (hugin) not installed" msgstr "pano tools (hugin) n'est pas installé" #: f.combine.cc:5605 msgid "Select at least 2 files" msgstr "Sélectionner au moins 2 fichiers" #: f.edit.cc:106 msgid "" "drag middle to move \n" "drag corners to resize \n" "drag right edge to level" msgstr "" "cliquer-tirer le centre pour déplacer \n" "cliquer-tirer les angles pour redimensionner \n" "cliquer-tirer le bord droit pour mettre de niveau" #: f.edit.cc:209 f.widgets.cc:501 f.widgets.cc:828 msgid "Trim/Rotate" msgstr "Rognage/Rotation" #: f.edit.cc:224 f.edit.cc:1483 msgid "ratio" msgstr "proportions" #: f.edit.cc:226 msgid "Lock Ratio" msgstr "Verrouillage des proportions" #: f.edit.cc:229 msgid "Trim Size:" msgstr "Taille du détourage" #: f.edit.cc:237 msgid "Set Ratio" msgstr "Paramétrer les proportions" #: f.edit.cc:238 f.widgets.cc:493 fotoxx.h:1371 msgid "Invert" msgstr "Inverser" #: f.edit.cc:239 msgid "Customize" msgstr "Personnaliser" #: f.edit.cc:248 msgid "Degrees:" msgstr "Degrés:" #: f.edit.cc:250 msgid "Auto Level" msgstr "Mise de niveau auto" #: f.edit.cc:256 f.process.cc:186 f.process.cc:822 f.widgets.cc:502 #: f.widgets.cc:830 msgid "Upright" msgstr "Redresser" #: f.edit.cc:258 msgid "maximize trim box" msgstr "maximiser la boîte englobante" #: f.edit.cc:259 msgid "trim transparent edges" msgstr "rogner les bords transparents" #: f.edit.cc:260 msgid "lock width/height ratio" msgstr "verrouiller le rapport largeur/hauteur" #: f.edit.cc:261 msgid "use EXIF data if available" msgstr "utiliser les données EXIF si disponibles" #: f.edit.cc:603 f.edit.cc:1550 msgid "rotation unknown" msgstr "rotation inconnue" #: f.edit.cc:1479 msgid "Trim Buttons" msgstr "Boutons de rognage" #: f.edit.cc:1482 msgid "label" msgstr "étiquette" #: f.edit.cc:1692 f.widgets.cc:503 f.widgets.cc:833 msgid "Retouch" msgstr "Retoucher" #: f.edit.cc:1705 msgid "Auto black level" msgstr "Niveau de noir auto" #: f.edit.cc:1708 f.edit.cc:1714 msgid "sample %" msgstr "échantillonnage %" #: f.edit.cc:1711 msgid "Auto white balance" msgstr "Balance des blancs auto" #: f.edit.cc:1717 msgid "Click gray spot for white balance" msgstr "Cliquer sur un point gris pour la balance des blancs" #: f.edit.cc:1720 msgid "Click dark spot for black level" msgstr "Cliquer sur un point sombre pour le niveau de noir" #: f.edit.cc:1723 msgid "Click for RGB distribution" msgstr "Cliquer pour la distribution RVB" #: f.edit.cc:1731 fotoxx.h:1326 msgid "Brightness" msgstr "Luminosité" #: f.edit.cc:1732 f.effects.cc:4049 fotoxx.h:1339 msgid "Contrast" msgstr "Contraste" #: f.edit.cc:1733 f.edit.cc:3279 f.edit.cc:3311 f.edit.cc:6777 msgid "Saturation" msgstr "Saturation" #: f.edit.cc:1734 msgid "Temperature" msgstr "Température" #: f.edit.cc:1742 msgid "Settings File" msgstr "Fichier de paramètres" #: f.edit.cc:2250 msgid "choose a better spot" msgstr "choisir un point plus approprié" #: f.edit.cc:2555 msgid "Resize Image" msgstr "Redimensionner l'image" #: f.edit.cc:2571 msgid "Previous" msgstr "Précédente" #: f.edit.cc:2589 msgid "W/H Ratio:" msgstr "Rapport largeur/hauteur:" #: f.edit.cc:2591 msgid "Lock" msgstr "Verrouiller" #: f.edit.cc:2840 msgid "insufficient memory, cannot proceed" msgstr "mémoire insuffisante, ne peut continuer" #: f.edit.cc:2936 f.widgets.cc:505 msgid "Adjust RGB" msgstr "Ajuster RVB" #: f.edit.cc:2942 msgid "+Brightness" msgstr "+Luminosité" #: f.edit.cc:2943 msgid "+Red -Cyan" msgstr "+Rouge -Cyan" #: f.edit.cc:2944 msgid "+Green -Magenta" msgstr "+Vert -Magenta" #: f.edit.cc:2945 msgid "+Blue -Yellow" msgstr "+Bleu -Jaune" #: f.edit.cc:2948 msgid "Contrast Red" msgstr "Contraste rouge" #: f.edit.cc:2949 msgid "Contrast Green" msgstr "Contraste vert" #: f.edit.cc:2950 msgid "Contrast Blue" msgstr "Contraste bleu" #: f.edit.cc:3268 f.widgets.cc:506 msgid "Adjust HSL" msgstr "Ajuster TSV" #: f.edit.cc:3272 msgid "Input color to match and adjust:" msgstr "Couleur d'entrée à faire coïncider et à ajuster:" #: f.edit.cc:3274 msgid "shift+click on image to select color" msgstr "maj+clic sur l'image pour sélectionner une couleur" #: f.edit.cc:3277 msgid "Match using:" msgstr "Ajuster avec:" #: f.edit.cc:3278 msgid "Hue" msgstr "Teinte" #: f.edit.cc:3280 f.edit.cc:3312 f.edit.cc:6778 msgid "Lightness" msgstr "Valeur" #: f.edit.cc:3290 msgid "Output Color" msgstr "Couleur de sortie" #: f.edit.cc:3310 f.edit.cc:6776 msgid "Color Hue" msgstr "Teinte de la couleur" #: f.edit.cc:3313 msgid "Adjustment" msgstr "Ajustement" #: f.edit.cc:3828 msgid "Image Markup" msgstr "Marquage de l'image" #: f.edit.cc:3829 f.edit.cc:3894 msgid "Draw text on image" msgstr "Dessiner un texte sur l'image" #: f.edit.cc:3830 f.edit.cc:4818 msgid "Draw line or arrow on image" msgstr "Dessiner une ligne ou une flèche sur l'image" #: f.edit.cc:3831 f.edit.cc:5499 msgid "Draw box on image" msgstr "Dessiner une boîte sur l'image" #: f.edit.cc:3832 f.edit.cc:5700 msgid "Draw oval on image" msgstr "Dessiner un ovale sur l'image" #: f.edit.cc:3871 msgid "+Version" msgstr "+Version" #: f.edit.cc:3895 msgid "Enter text, click/drag on image, right click to remove" msgstr "Entrer un texte, cliquer/tirer sur l'image, clic droit pour annuler" #: f.edit.cc:3944 msgid "Use settings file" msgstr "Utiliser un fichier de paramètres" #: f.edit.cc:3949 f.mashup.cc:2438 f.tools.cc:1668 f.tools.cc:1677 msgid "Text" msgstr "Texte" #: f.edit.cc:3954 msgid "Use metadata key" msgstr "Utiliser une clé de métadonnées" #: f.edit.cc:3973 f.mashup.cc:2456 msgid "text" msgstr "texte" #: f.edit.cc:3974 f.edit.cc:4847 f.mashup.cc:2457 f.mashup.cc:2795 msgid "backing" msgstr "arrière-plan" #: f.edit.cc:3975 f.edit.cc:4848 f.mashup.cc:2458 f.mashup.cc:2796 msgid "outline" msgstr "contour" #: f.edit.cc:3976 f.edit.cc:4849 f.mashup.cc:2459 f.mashup.cc:2797 msgid "shadow" msgstr "ombre" #: f.edit.cc:4002 msgid "save to current file" msgstr "enregistrer vers le fichier courant" #: f.edit.cc:4003 f.file.cc:2432 msgid "save as new file version" msgstr "enregistrer sous une nouvelle version" #: f.edit.cc:4004 msgid "" "save to current file \n" "open next file with same text" msgstr "" "enregistrer sous le fichier courant \n" "ouvrir le fichier suivant avec le même texte" #: f.edit.cc:4167 f.mashup.cc:2563 f.tools.cc:1961 msgid "select font" msgstr "sélectionner une police" #: f.edit.cc:4446 msgid "text file is defective" msgstr "le fichier texte est défectueux" #: f.edit.cc:4786 msgid "" "Enter line or arrow properties in dialog, \n" "click/drag on image, right click to remove" msgstr "" "Entrer les propriétés de la ligne ou de la flèche dans le dialogue, \n" "cliquer et tirer sur l'image, clic droit pour enlever" #: f.edit.cc:4826 f.mashup.cc:2774 msgid "Line length" msgstr "Longueur de la ligne" #: f.edit.cc:4833 f.mashup.cc:2781 msgid "Arrow head" msgstr "Extrémité de la flèche" #: f.edit.cc:4846 f.mashup.cc:2794 msgid "line" msgstr "ligne" #: f.edit.cc:4875 msgid "" "fix line/arrow in layout \n" " start new line/arrow" msgstr "" "fixer la ligne ou flèche dans la composition \n" " commencer une nouvelle ligne ou flèche" #: f.edit.cc:5474 msgid "" "drag mouse to draw box \n" "shift + drag center to move box \n" "shift + drag edge to move edge" msgstr "" "tirer la souris pour dessiner la boîte \n" "maj + tirer le centre pour positionner la boîte \n" "maj + tirer les bords pour positionner les bords de la boîte" #: f.edit.cc:5505 f.edit.cc:5706 msgid "line color" msgstr "couleur de la ligne" #: f.edit.cc:5508 f.edit.cc:5709 msgid "line width" msgstr "largeur de la ligne" #: f.edit.cc:5674 msgid "" "drag mouse down/right to draw oval \n" "shift + drag center to move oval \n" "shift + drag lower right edge to change" msgstr "" "tirer la souris vers le bas et la droite pour dessiner l'ovale \n" "maj + tirer le centre pour positionner l'ovale \n" "maj + tirer le bord inférieur droit pour modifier le contour" #: f.edit.cc:5712 msgid "oval" msgstr "ovale" #: f.edit.cc:5713 msgid "circle" msgstr "cercle" #: f.edit.cc:5988 msgid "" "shift + left click: pick color from image \n" "left drag: paint color on image \n" "right drag: restore original image" msgstr "" "maj+clic gauche: choisir une couleur dans l'image \n" "cliquer-tirer gauche: peindre l'image avec cette couleur \n" "cliquer-tirer droit: restaurer l'image originale" #: f.edit.cc:6024 msgid "Paint on Image" msgstr "Peindre sur l'image" #: f.edit.cc:6030 msgid "paint color" msgstr "couleur de la peinture" #: f.edit.cc:6041 f.edit.cc:6970 f.edit.cc:7548 msgid "brush size" msgstr "taille de la brosse" #: f.edit.cc:6055 msgid "erase" msgstr "gommer" #: f.edit.cc:6060 msgid "include transparent areas" msgstr "inclure les zones transparentes" #: f.edit.cc:6063 msgid "drag image" msgstr "tirer l'image" #: f.edit.cc:6065 msgid "zoom image" msgstr "zoomer sur l'image" #: f.edit.cc:6592 msgid "Color Chooser" msgstr "Sélecteur de couleur" #: f.edit.cc:6594 msgid "click on desired color" msgstr "cliquer sur la couleur désirée" #: f.edit.cc:6929 msgid "" "shift + left click: pick position to copy \n" "left click or drag: copy image to mouse \n" "right click or drag: restore original image" msgstr "" "maj + clic gauche: choisir une position à copier \n" "clic gauche ou cliquer-tirer gauche: copier l'image au pointeur de la " "souris \n" "clic droit ou cliquer-tirer droit: restaurer l'image originale" #: f.edit.cc:6960 msgid "Copy Pixels (1 image)" msgstr "Copier des pixels (1 image)" #: f.edit.cc:6979 f.edit.cc:7557 msgid "paint over transparent areas" msgstr "peindre sur les zones transparentes" #: f.edit.cc:7531 msgid "" "left click: synchronize copy position \n" "left click or drag: copy source image to mouse \n" "right click or drag: restore original image" msgstr "" "clic gauche: synchroniser la position de copie \n" "clic gauche ou tiré: copier l'image source à la position de la souris \n" "clic droit ou tiré: restaurer l'image originale" #: f.edit.cc:7535 msgid "Copy Pixels (2 images)" msgstr "Copier des pixels (2 images)" #: f.edit.cc:7541 msgid "source image scale" msgstr "mise à l'échelle de l'image source" #: f.edit.cc:8118 msgid "source image failure (scale too big?)" msgstr "problème avec l'image source (dimensions trop importantes?)" #: f.edit.cc:8197 f.widgets.cc:511 msgid "Paint Edits" msgstr "Peinture de fonctions d'édition" #: f.edit.cc:8203 fotoxx.cc:3576 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "La zone sélectionnée ne peut être conservée.\n" "Continuer?" #: f.edit.cc:8211 f.tools.cc:3751 msgid "Edit function must be active" msgstr "Une fonction d'édition doit être active" #: f.edit.cc:8216 msgid "Cannot use Paint Edits" msgstr "Impossible d'utiliser la peinture de fonctions d'édition" #: f.edit.cc:8243 f.edit.cc:8485 msgid "power: center" msgstr "puissance: centre" #: f.edit.cc:8248 msgid "reset area" msgstr "réinitialiser la zone" #: f.edit.cc:8428 msgid "finish current edit first" msgstr "terminer d'abord l'opération d'édition courante" #: f.edit.cc:8433 msgid "no previous edit" msgstr "pas d'opération d'édition à défaire" #: f.edit.cc:8438 msgid "no current image" msgstr "pas d'image courante" #: f.edit.cc:8449 msgid "This edit cannot be incrementally undone" msgstr "Cette opération d'édition ne peut pas être annulée incrémentalement" #: f.edit.cc:8480 f.widgets.cc:512 msgid "Undo Edits" msgstr "Annulation d'opérations d'édition" #: f.edit.cc:8708 f.edit.cc:8756 msgid "Edit Plugins" msgstr "Éditer les greffons" #: f.edit.cc:8709 msgid "Edit plugins menu" msgstr "Éditer le menu des greffons" #: f.edit.cc:8717 msgid "Run as Fotoxx edit function" msgstr "Lancer comme une fonction d'édition de Fotoxx" #: f.edit.cc:8758 msgid "menu name" msgstr "nom du menu" #: f.edit.cc:8761 msgid "command" msgstr "commande" #: f.edit.cc:8959 msgid "Plugin working ..." msgstr "Le greffon est en cours ..." #: f.edit.cc:8968 msgid "plugin failed" msgstr "le greffon a échoué" #: f.edit.cc:9004 msgid "Raw Therapee not installed" msgstr "RawTherapee n'est pas installé" #: f.edit.cc:9023 msgid "RAW type not registered in User Preferences" msgstr "" "Ce type de fichier RAW n'est pas déclaré dans les Préférences Utilisateur" #: f.edit.cc:9038 msgid "Raw Therapee produced no tif file" msgstr "RawTherapee n'a pas créé de fichier TIFF" #: f.effects.cc:83 msgid "Convert to Sketch" msgstr "Convertir en un croquis" #: f.effects.cc:161 msgid "Clip Level" msgstr "Niveau de troncature" #: f.effects.cc:165 msgid "Algorithm" msgstr "Algorithme" #: f.effects.cc:170 msgid "Foreground" msgstr "Avant-plan" #: f.effects.cc:173 f.tools.cc:1680 msgid "Background" msgstr "Arrière-plan" #: f.effects.cc:609 msgid "Line Threshold" msgstr "Seuil de ligne" #: f.effects.cc:610 msgid "Line Width" msgstr "Largeur de ligne" #: f.effects.cc:611 f.enhance.cc:3929 msgid "Blur Radius" msgstr "Rayon du flou" #: f.effects.cc:612 msgid "Kuwahara Depth" msgstr "Profondeur de Kuwahara" #: f.effects.cc:1057 f.widgets.cc:538 msgid "Line Drawing" msgstr "Dessin au trait" #: f.effects.cc:1070 f.effects.cc:2090 msgid "black/white" msgstr "noir/blanc" #: f.effects.cc:1320 f.widgets.cc:539 msgid "Emboss" msgstr "Gaufrage" #: f.effects.cc:1326 msgid "Depth" msgstr "Profondeur" #: f.effects.cc:1541 msgid "Simulate Tiles" msgstr "Simuler des tuiles" #: f.effects.cc:1545 msgid "tile size" msgstr "taille d'une tuile" #: f.effects.cc:1548 msgid "tile gap" msgstr "interstice entre les tuiles" #: f.effects.cc:1551 msgid "3D depth" msgstr "profondeur 3D" #: f.effects.cc:1780 msgid "Dither Image" msgstr "Tramer une image" #: f.effects.cc:1783 msgid "Dither0" msgstr "Tramage0" #: f.effects.cc:1784 f.effects.cc:1848 msgid "Roy Lichtenstein effect" msgstr "Effet Roy Lichtenstein" #: f.effects.cc:1787 f.effects.cc:2083 msgid "Dither1" msgstr "Tramage1" #: f.effects.cc:1788 msgid "pure RGB or black/white dots" msgstr "point purs RVB ou points noirs et blancs" #: f.effects.cc:1791 f.effects.cc:2481 msgid "Dither2" msgstr "Tramage2" #: f.effects.cc:1792 msgid "RGB mix with given bit-depth" msgstr "mélange RVB avec une profondeur de couleur donnée" #: f.effects.cc:1795 f.effects.cc:2756 msgid "Dither3" msgstr "Tramage3" #: f.effects.cc:1796 msgid "custom palette colors" msgstr "palette de couleurs personnalisées" #: f.effects.cc:1852 msgid "dot size" msgstr "taille du point" #: f.effects.cc:2087 f.effects.cc:2485 f.effects.cc:2760 msgid "resolution" msgstr "résolution" #: f.effects.cc:2089 msgid "RGB color" msgstr "couleur RVB" #: f.effects.cc:2091 msgid "random position" msgstr "position aléatoire" #: f.effects.cc:2488 f.effects.cc:3196 msgid "color depth" msgstr "profondeur de couleur" #: f.effects.cc:2491 f.effects.cc:2768 msgid "error compensation" msgstr "compensation de l'erreur" #: f.effects.cc:2764 msgid "palette:" msgstr "palette:" #: f.effects.cc:2818 msgid "palette file" msgstr "fichier de palette" #: f.effects.cc:3178 f.widgets.cc:542 msgid "Painting" msgstr "Peinture" #: f.effects.cc:3200 msgid "patch area goal" msgstr "taille d'une zone colorée" #: f.effects.cc:3204 msgid "req. color match" msgstr "correspondance de couleur requise" #: f.effects.cc:3208 msgid "borders" msgstr "limites" #: f.effects.cc:3799 msgid "Add Texture" msgstr "Ajout de texture" #: f.effects.cc:4014 msgid "Background Pattern" msgstr "Motif d'arrière-plan" #: f.effects.cc:4018 msgid "Pattern File:" msgstr "Fichier de motif:" #: f.effects.cc:4026 msgid "Geometry" msgstr "Géometrie" #: f.effects.cc:4030 f.widgets.cc:544 msgid "Pattern" msgstr "Motif" #: f.effects.cc:4038 msgid "Overlap" msgstr "Recouvrement" #: f.effects.cc:4046 msgid "Opacity" msgstr "Opacité" #: f.effects.cc:4052 msgid "Grayscale" msgstr "Échelle de gris" #: f.effects.cc:4478 msgid "Create Mosaic" msgstr "Créer une mosaïque" #: f.effects.cc:4524 msgid "Tile" msgstr "Tuile" #: f.effects.cc:4532 f.widgets.cc:540 msgid "Tiles" msgstr "Tuiles" #: f.effects.cc:4538 msgid "Tile blending" msgstr "Fusion des tuiles" #: f.effects.cc:4621 #, c-format msgid "exceeded max. tiles: %d" msgstr "max. de tuiles dépassé: %d" #: f.effects.cc:4628 #, c-format msgid "only %d tile images found" msgstr "trouvé seulement %d images de tuilage" #: f.effects.cc:5088 f.widgets.cc:546 msgid "Color Mode" msgstr "Mode de couleur" #: f.effects.cc:5091 msgid "reset" msgstr "réinitialiser" #: f.effects.cc:5092 msgid "black/white positive" msgstr "positif noir et blanc" #: f.effects.cc:5093 msgid "black/white negative" msgstr "négatif noir et blanc" #: f.effects.cc:5094 msgid "color negative" msgstr "négatif couleur" #: f.effects.cc:5095 msgid "RGB -> GBR" msgstr "RVB -> VBR" #: f.effects.cc:5096 msgid "RGB -> BRG" msgstr "RVB -> BRV" #: f.effects.cc:5097 msgid "sepia" msgstr "sépia" #: f.effects.cc:5348 msgid "Set color depth to 1-16 bits" msgstr "Déterminer une profondeur de couleur entre 1 et 16 bits" #: f.effects.cc:5377 msgid "Set Color Depth" msgstr "Déterminer une profondeur de couleur" #: f.effects.cc:5547 f.widgets.cc:548 msgid "Shift Colors" msgstr "Décaler les couleurs" #: f.effects.cc:5824 f.widgets.cc:549 msgid "Alien Colors" msgstr "Couleurs exotiques" #: f.effects.cc:5827 msgid "blocksize" msgstr "taille d'un bloc" #: f.effects.cc:5830 f.warp.cc:3096 msgid "amplitude" msgstr "amplitude" #: f.effects.cc:6089 msgid "" "Draw a line across the image in \n" "direction of brightness change." msgstr "" "Tracer une ligne au travers de l'image dans la \n" "direction de la modification de la luminosité" #: f.effects.cc:6132 msgid "Brightness Ramp" msgstr "Rampe de la luminosité" #: f.effects.cc:6605 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" "cliquer-tirer gauche: ajouter de la transparence \n" "cliquer-tirer droit: ajouter de l'opacité" #: f.effects.cc:6640 msgid "Paint Transparency" msgstr "Peindre avec de la transparence" #: f.effects.cc:6648 msgid "paintbrush radius" msgstr "rayon de la brosse" #: f.effects.cc:6649 msgid "strength center" msgstr "force au centre" #: f.effects.cc:6650 msgid "strength edge" msgstr "force au bord" #: f.effects.cc:6655 msgid "gradual paint" msgstr "peinture graduelle" #: f.effects.cc:6874 msgid "Mirror Image" msgstr "Image en miroir" #: f.effects.cc:6877 f.warp.cc:3098 msgid "horizontal" msgstr "horizontal" #: f.effects.cc:6878 f.warp.cc:3102 msgid "vertical" msgstr "vertical" #: f.effects.cc:7051 f.widgets.cc:553 msgid "Custom Kernel" msgstr "Matrice personnalisée" #: f.effects.cc:7055 msgid "Kernel size" msgstr "Taille de la matrice" #: f.effects.cc:7072 msgid "multiply" msgstr "multiplier" #: f.effects.cc:7075 msgid "add" msgstr "additionner" #: f.effects.cc:7079 msgid "Data file" msgstr "Fichier de données" #: f.effects.cc:7099 fotoxx.cc:3845 msgid "Load settings from file" msgstr "Charger les paramètres depuis un fichier" #: f.enhance.cc:505 msgid "Adjust Brightness Distribution" msgstr "Ajuster la distribution de la luminosité" #: f.enhance.cc:544 msgid "Low Cutoff" msgstr "Seuil bas" #: f.enhance.cc:545 msgid "High Cutoff" msgstr "Seuil haut" #: f.enhance.cc:546 msgid "Low Flatten" msgstr "Aplatissement bas" #: f.enhance.cc:547 msgid "Mid Flatten" msgstr "Aplatissement moyen" #: f.enhance.cc:548 msgid "High Flatten" msgstr "Aplatissement haut" #: f.enhance.cc:549 msgid "Low Stretch" msgstr "Extension basse" #: f.enhance.cc:550 msgid "Mid Stretch" msgstr "Extension moyenne" #: f.enhance.cc:551 msgid "High Stretch" msgstr "Extension haute" #: f.enhance.cc:886 msgid "Magnify Gradients" msgstr "Amplifier les gradients" #: f.enhance.cc:923 msgid "low" msgstr "bas" #: f.enhance.cc:925 msgid "high" msgstr "haut" #: f.enhance.cc:928 msgid "Amplify" msgstr "Amplifier" #: f.enhance.cc:1312 msgid "Flatten Brightness" msgstr "Aplatissement de la luminosité" #: f.enhance.cc:1363 msgid "Zones" msgstr "Zones" #: f.enhance.cc:1370 msgid "Deband Dark" msgstr "Restaurer les valeurs sombres" #: f.enhance.cc:1373 msgid "Deband Bright" msgstr "Restaurer les valeurs claires" #: f.enhance.cc:1851 msgid "Global Retinex" msgstr "Retinex global" #: f.enhance.cc:1867 msgid "Dark Point" msgstr "Point noir" #: f.enhance.cc:1868 msgid "Bright Point" msgstr "Point blanc" #: f.enhance.cc:1869 msgid "Multiplyer" msgstr "Multiplicateur" #: f.enhance.cc:1893 msgid "brightness rescale" msgstr "rééchelonnement de la luminosité" #: f.enhance.cc:1896 msgid "click bright point" msgstr "cliquer sur le point blanc" #: f.enhance.cc:1897 msgid "click dark point" msgstr "cliquer sur le point noir" #: f.enhance.cc:1900 f.enhance.cc:2531 msgid "blend" msgstr "fusionner" #: f.enhance.cc:1903 f.enhance.cc:2537 msgid "reduce bright" msgstr "réduire pour les zones brillantes" #: f.enhance.cc:2523 msgid "Zonal Retinex" msgstr "Retinex zonal" #: f.enhance.cc:2527 msgid "zone count:" msgstr "nombre de zones:" #: f.enhance.cc:2534 msgid "reduce dark" msgstr "réduire pour les zones sombres" #: f.enhance.cc:3058 f.process.cc:189 f.process.cc:823 f.process.cc:1404 #: f.widgets.cc:524 msgid "Sharpen" msgstr "Accentuer la netteté" #: f.enhance.cc:3066 msgid "unsharp mask" msgstr "masque flou" #: f.enhance.cc:3099 msgid "median diff" msgstr "différence des médianes" #: f.enhance.cc:3101 msgid "dark" msgstr "sombre" #: f.enhance.cc:3102 msgid "light" msgstr "clair" #: f.enhance.cc:3888 msgid "Click to set center" msgstr "Cliquer pour positionner le centre" #: f.enhance.cc:3889 msgid "Pull image using the mouse" msgstr "Tirer l'image avec la souris" #: f.enhance.cc:3890 msgid "" "left drag: blend image \n" "right drag: restore image" msgstr "" "cliquer-tirer gauche: fusionner des pixels de l'image \n" "cliquer-tirer droit: restaurer l'image" #: f.enhance.cc:3933 msgid "Normal Blur" msgstr "Flou normal" #: f.enhance.cc:3940 msgid "Radial Blur" msgstr "Flou radial" #: f.enhance.cc:3954 msgid "Directed Blur" msgstr "Flou dirigé" #: f.enhance.cc:3956 msgid "Blur Span" msgstr "portée du flou" #: f.enhance.cc:3959 msgid "Intensity" msgstr "Intensité" #: f.enhance.cc:3964 msgid "Graduated Blur" msgstr "Flou graduel" #: f.enhance.cc:3969 msgid "Contrast Limit" msgstr "Limite du contraste" #: f.enhance.cc:3974 msgid "Paint Blur" msgstr "Flou par peinture" #: f.enhance.cc:3979 f.mashup.cc:1577 fotoxx.h:1402 msgid "Power" msgstr "Puissance" #: f.enhance.cc:3987 f.enhance.cc:4914 msgid "Blur Background" msgstr "Flouter l'arrière-plan" #: f.enhance.cc:4918 msgid "constant blur" msgstr "flou constant" #: f.enhance.cc:4921 msgid "increase blur with distance" msgstr "augmenter le flou avec la distance" #: f.enhance.cc:4923 msgid "min. blur radius" msgstr "rayon min. du flou" #: f.enhance.cc:4926 msgid "max. blur radius" msgstr "rayon max. du flou" #: f.enhance.cc:4957 f.warp.cc:837 f.warp.cc:2277 msgid "no active Select Area" msgstr "aucune fontion de sélection de zone n'est active" #: f.enhance.cc:5139 msgid "Apply repeatedly while watching the image." msgstr "Appliquer de façon répétée tout en observant l'image." #: f.enhance.cc:5175 msgid "Noise Reduction" msgstr "Réduction du bruit" #: f.enhance.cc:5215 msgid "dark areas" msgstr "zones sombres" #: f.enhance.cc:5217 msgid "all areas" msgstr "toutes les zones" #: f.enhance.cc:6195 msgid "Measure Noise" msgstr "Mesure du bruit" #: f.enhance.cc:6196 msgid "Move mouse in a monotone image area." msgstr "Passer la souris au-dessus d'une zone monotone de l'image." #: f.enhance.cc:6511 msgid "" "Method 1:\n" " Left-click on red-eye to darken.\n" "Method 2:\n" " Drag down and right to enclose red-eye.\n" " Left-click on red-eye to darken.\n" "Undo red-eye:\n" " Right-click on red-eye." msgstr "" "Méthode 1:\n" " Clic gauche sur l'oeil rouge pour assombrir.\n" "Méthode 2:\n" " Cliquer-tirer vers le bas et la droite pour circonscrire l'oeil rouge.\n" " Clic gauche sur l'oeil rouge pour assombrir.\n" "Défaire la fonction oeil rouge:\n" " Clic droit sur l'oeil rouge." #: f.enhance.cc:6530 msgid "Red Eye Reduction" msgstr "Réduction des yeux rouges" #: f.enhance.cc:6984 msgid "Color Match Images" msgstr "Faire coïncider les couleurs des images" #: f.enhance.cc:7015 msgid "mouse radius for color sample" msgstr "rayon de la souris pour l'échantillon de couleur" #: f.enhance.cc:7017 f.enhance.cc:7022 fotoxx.h:1397 zfuncs.cc:12681 msgid "Open" msgstr "Ouvrir" #: f.enhance.cc:7018 msgid "image for source color" msgstr "l'image pour la couleur source" #: f.enhance.cc:7020 msgid "click on image to get source color" msgstr "cliquer sur l'image pour échantillonner la couleur source" #: f.enhance.cc:7023 msgid "image to set matching color" msgstr "l'image pour choisir la couleur en correspondance" #: f.enhance.cc:7025 msgid "click on image to set matching color" msgstr "cliquer sur l'image pour choisir la couleur en correspondance" #: f.enhance.cc:7092 msgid "select source image color first" msgstr "sélectionner l'image et la couleur source en premier" #: f.enhance.cc:7287 msgid "" "Drag mouse to select. Erase. Repeat. \n" "Click: extend selection to mouse." msgstr "" "Cliquer-tirer pour sélectionner. Effacer. Répéter. \n" "Clic simple: étendre la sélection vers le pointeur de la souris." #: f.enhance.cc:7314 f.widgets.cc:529 msgid "Smart Erase" msgstr "Effacement intelligent" #: f.enhance.cc:7319 fotoxx.h:1408 msgid "Radius" msgstr "Rayon" #: f.enhance.cc:7321 f.widgets.cc:525 msgid "Blur" msgstr "Flou" #: f.enhance.cc:7324 msgid "New Area" msgstr "Nouvelle zone" #: f.enhance.cc:7672 f.enhance.cc:8054 msgid "Chromatic Aberration" msgstr "Aberration Chromatique" #: f.enhance.cc:7711 msgid "Red Factors" msgstr "Facteurs de rouge" #: f.enhance.cc:7715 msgid "Blue Factors" msgstr "Facteurs de bleu" #: f.enhance.cc:7720 msgid "Find optimum factors:" msgstr "Rechercher les facteurs optimaux" #: f.enhance.cc:8095 msgid "Chromatic Color" msgstr "Couleur chromatique" #: f.enhance.cc:8098 msgid "Replacement Color" msgstr "Couleur de remplacement" #: f.enhance.cc:8101 msgid "Background Color" msgstr "Couleur d'arrière-plan" #: f.enhance.cc:8105 msgid "Color match level" msgstr "Niveau de correspondance de la couleur" #: f.enhance.cc:8109 msgid "Background Proximity" msgstr "Proximité par rapport à l'arrière-plan" #: f.enhance.cc:8151 msgid "255 iterations, cannot continue" msgstr "255 itérations, impossible de continuer" #: f.enhance.cc:8413 f.widgets.cc:532 msgid "Vignette" msgstr "Vignettage" #: f.enhance.cc:8802 f.widgets.cc:533 msgid "Remove Dust" msgstr "Enlever la poussière" #: f.enhance.cc:8806 msgid "spot size limit" msgstr "taille limite du point" #: f.enhance.cc:8809 msgid "max. brightness" msgstr "luminosité maximale" #: f.enhance.cc:8812 msgid "min. contrast" msgstr "contraste minimal" #: f.file.cc:601 msgid "Rename Image File" msgstr "Renommer un fichier image" #: f.file.cc:608 msgid "Old Name" msgstr "ancien nom" #: f.file.cc:609 f.process.cc:141 msgid "New Name" msgstr "Nouveau nom" #: f.file.cc:618 msgid "previous name" msgstr "nom précédent" #: f.file.cc:619 msgid "Add 1" msgstr "Ajouter 1" #: f.file.cc:622 f.file.cc:882 f.file.cc:1629 f.file.cc:1998 msgid "keep this dialog open" msgstr "conserver ce dialogue ouvert" #: f.file.cc:850 msgid "File Permissions" msgstr "Permissions du fichier" #: f.file.cc:854 f.meta.cc:833 f.meta.cc:1615 f.meta.cc:1818 msgid "File:" msgstr "Fichier:" #: f.file.cc:1000 msgid "Open Image File" msgstr "Ouvrir un fichier image" #: f.file.cc:1019 f.process.cc:654 msgid "unknown file type" msgstr "type de fichier inconnu" #: f.file.cc:1207 #, c-format msgid "Start of gallery, preceding gallery: %s" msgstr "Début de la galerie, galerie précédente: %s" #: f.file.cc:1208 #, c-format msgid "End of gallery, following gallery: %s" msgstr "Fin de la galerie, galerie suivante: %s" #: f.file.cc:1355 msgid "Create Blank Image" msgstr "Créer une image vide" #: f.file.cc:1357 f.meta.cc:1909 msgid "file name" msgstr "nom du fichier" #: f.file.cc:1391 msgid "supply a file name" msgstr "fournir un nom de fichier" #: f.file.cc:1570 msgid "Copy or Move Image File" msgstr "Copier ou déplacer un fichier image" #: f.file.cc:1615 msgid "New Location:" msgstr "Nouvel emplacement:" #: f.file.cc:1620 msgid "New Name:" msgstr "Nouveau nom" #: f.file.cc:1625 msgid "copy (duplicate file)" msgstr "copier (dupliquer le fichier)" #: f.file.cc:1626 msgid "move (remove original)" msgstr "déplacer (enlever l'original)" #: f.file.cc:1676 f.process.cc:608 f.process.cc:1574 f.process.cc:2155 msgid "Select folder" msgstr "Sélectionner un dossier" #: f.file.cc:1718 f.tools.cc:1488 msgid "new location is not a folder" msgstr "le nouvel emplacement n'est pas un dossier" #: f.file.cc:1737 msgid "new file extension missing or changed" msgstr "extension du nouveau fichier manquante ou modifiée" #: f.file.cc:1771 f.file.cc:2075 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "la suppression a échoué: \n" " %s" #: f.file.cc:1845 f.file.cc:3226 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Écraser le fichier? \n" " %s" #: f.file.cc:1957 msgid "Delete/Trash Image File" msgstr "Supprimer/Transférer vers la corbeille un fichier image" #: f.file.cc:2029 msgid "GTK g_file_trash() function failed" msgstr "la fonction GTK g_file_trash() a échoué" #: f.file.cc:2048 msgid "not a known image file" msgstr "n'est pas un fichier image connu" #: f.file.cc:2054 msgid "Delete read-only file?" msgstr "Supprimer le fichier en lecture seule?" #: f.file.cc:2056 msgid "Trash read-only file?" msgstr "Mettre à la corbeille le fichier en lecture seule?" #: f.file.cc:2272 #, c-format msgid "Kill active dialog? %s" msgstr "Tuer le dialogue actif? %s" #: f.file.cc:2322 f.widgets.cc:633 msgid "User Guide" msgstr "Guide de l'utilisateur" #: f.file.cc:2325 f.widgets.cc:634 msgid "Recent Changes" msgstr "Changements récents" #: f.file.cc:2328 f.widgets.cc:635 msgid "Edit Functions Overview" msgstr "Vue d'ensemble des fonctions d'édition" #: f.file.cc:2334 f.widgets.cc:636 msgid "Change Log" msgstr "Journal des changements" #: f.file.cc:2337 f.widgets.cc:637 msgid "Log File" msgstr "Fichier log" #: f.file.cc:2343 f.widgets.cc:639 msgid "Command Params" msgstr "Paramètres de la ligne de commande" #: f.file.cc:2346 f.widgets.cc:640 msgid "Translations" msgstr "Traductions" #: f.file.cc:2349 f.widgets.cc:641 msgid "Home Page" msgstr "Page d'accueil" #: f.file.cc:2352 f.widgets.cc:642 msgid "License" msgstr "Licence" #: f.file.cc:2355 f.widgets.cc:643 msgid "Privacy" msgstr "Confidentialité" #: f.file.cc:2358 f.widgets.cc:644 msgid "About" msgstr "À propos" #: f.file.cc:2366 f.widgets.cc:665 fotoxx.h:1366 msgid "Help" msgstr "Aide" #: f.file.cc:2414 msgid "Save Image File" msgstr "Enregistrer le fichier image" #: f.file.cc:2424 msgid "new version" msgstr "nouvelle version" #: f.file.cc:2425 msgid "new file ..." msgstr "nouveau fichier ..." #: f.file.cc:2426 msgid "replace file" msgstr "remplacer le fichier" #: f.file.cc:2433 f.file.cc:3083 msgid "save as new file name or type" msgstr "enregistrer sous un nouveau nom ou type de fichier" #: f.file.cc:2435 msgid "replace old file (OVERWRITE)" msgstr "remplacer l'ancien fichier (ÉCRASER)" #: f.file.cc:2486 msgid "cannot replace RAW file" msgstr "impossible de remplacer un fichier RAW" #: f.file.cc:2491 msgid "cannot replace HEIC file" msgstr "impossible de remplacer un fichier HEIC" #: f.file.cc:2496 msgid "cannot replace JP2 file" msgstr "" #: f.file.cc:2668 f.file.cc:2727 #, c-format msgid "" "file: %s \n" " exceed 99 versions" msgstr "" "le fichier: %s \n" " possède plus de 99 versions" #: f.file.cc:2821 msgid "" "Transparency map will be lost.\n" "save to PNG file to retain." msgstr "" "La transparence va être perdue.\n" "enregistrer au format PNG pour la conserver." #: f.file.cc:2831 msgid "cannot save as RAW type" msgstr "impossible d'enregistrer sous un format RAW" #: f.file.cc:2912 msgid "save anyway" msgstr "enregistrer de toute façon" #: f.file.cc:2980 f.file.cc:2982 msgid "Unable to copy EXIF/IPTC data" msgstr "Impossible de copier les données EXIF/IPTC" #: f.file.cc:3096 f.process.cc:1387 msgid "jpg quality" msgstr "qualité du jpg" #: f.file.cc:3100 msgid "color depth:" msgstr "profondeur de couleur:" #: f.file.cc:3106 f.file.cc:3114 msgid "make current" msgstr "rendre actuel" #: f.file.cc:3107 msgid "(new file becomes current file)" msgstr "(le nouveau fichier devient le fichier actuel)" #: f.file.cc:3110 msgid "permissions:" msgstr "permissions" #: f.file.cc:3641 f.widgets.cc:432 f.widgets.cc:821 msgid "Permissions" msgstr "Permissions" #: f.file.cc:3646 fotoxx.cc:254 msgid "owner" msgstr "propriétaire" #: f.file.cc:3647 fotoxx.cc:255 msgid "group" msgstr "groupe" #: f.file.cc:3648 fotoxx.cc:256 msgid "other" msgstr "autres" #: f.gallery.cc:1699 f.gallery.cc:1719 msgid "recent images" msgstr "images récentes" #: f.gallery.cc:1700 f.gallery.cc:1724 msgid "newest images" msgstr "images nouvelles" #: f.gallery.cc:1779 msgid "no albums found" msgstr "aucun album trouvé" #: f.gallery.cc:1789 f.gallery.cc:1803 msgid "Current Album" msgstr "Album courant" #: f.gallery.cc:1806 msgid "no current album" msgstr "pas d'album courant" #: f.gallery.cc:1828 msgid "" " Reset all galleries\n" " to file name ascending" msgstr "" " Réinitialiser toutes les galeries\n" " au tri par nom de fichier ascendant" #: f.gallery.cc:1849 msgid "Gallery Sort" msgstr "Tri de la galerie" #: f.gallery.cc:1853 msgid "File Name" msgstr "Nom de fichier" #: f.gallery.cc:1854 msgid "File Mod Date/Time" msgstr "Date/heure de modification du fichier" #: f.gallery.cc:1855 msgid "Photo Date/Time (EXIF)" msgstr "Date/heure de la photo (EXIF)" #: f.gallery.cc:1856 msgid "File Size (bytes)" msgstr "Taille de fichier (en octets)" #: f.gallery.cc:1857 msgid "Image Size (pixels)" msgstr "Taille de l'image (en pixels)" #: f.gallery.cc:1859 msgid "ascending" msgstr "ascendant" #: f.gallery.cc:1860 msgid "descending" msgstr "descendant" #: f.gallery.cc:2759 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.gallery.cc:2783 f.widgets.cc:648 msgid "File" msgstr "Fichier" #: f.gallery.cc:3589 f.gallery.cc:3593 msgid "Image File" msgstr "Fichier image" #: f.gallery.cc:3702 msgid "Select Image Files" msgstr "Sélectionner des fichiers images" #: f.gallery.cc:3768 #, c-format msgid "exceed %d selected files" msgstr "excède %d fichiers sélectionnés" #: f.gallery.cc:3780 #, c-format msgid "remove %d duplicates?" msgstr "enlever %d doublons?" #: f.gallery.cc:4229 f.widgets.cc:457 msgid "Bookmarks" msgstr "Signets" #: f.gallery.cc:4229 f.gallery.cc:4337 msgid "Edit Bookmarks" msgstr "Modifier les signets" #: f.gallery.cc:4311 msgid "" "Click a list position. Click a gallery thumbnail for the new bookmark.\n" "Bookmark for thumbnail will be added. Change the name and press [Rename]." msgstr "" "Cliquer sur une position dans la liste. Cliquer sur une vignette dans une " "galerie pour créer le nouveau signet.\n" "Le signet pour la vignette sera ajouté. Changer le nom et presser [Renommer]." #: f.gallery.cc:4514 msgid "unable to save bookmarks file" msgstr "impossible d'enregistrer le fichier de signets" #: f.mashup.cc:212 msgid "Project name" msgstr "Nom du projet" #: f.mashup.cc:216 msgid "Layout and background image" msgstr "Mise en page et image d'arrière-plan" #: f.mashup.cc:220 msgid "choose an image file" msgstr "choisir un fichier image" #: f.mashup.cc:221 msgid "use current image file" msgstr "utiliser le fichier image courant" #: f.mashup.cc:222 msgid "specify layout size and color" msgstr "spécifier la taille de la mise en page et la couleur" #: f.mashup.cc:223 msgid "open a Mashup project file" msgstr "ouvrir un fichier de projet d'agencement d'objets graphiques" #: f.mashup.cc:238 msgid "enter a project name" msgstr "entrer un nom de projet" #: f.mashup.cc:269 msgid "no current file" msgstr "pas de fichier courant" #: f.mashup.cc:296 msgid "Make Layout Image" msgstr "Créer l'image contenant la composition" #: f.mashup.cc:403 msgid "Edit Images" msgstr "Édition des images" #: f.mashup.cc:404 f.mashup.cc:2433 msgid "Edit Text" msgstr "Éditer le texte" #: f.mashup.cc:405 msgid "Edit Line" msgstr "Éditer la ligne" #: f.mashup.cc:406 msgid "Rescale" msgstr "Changer l'échelle" #: f.mashup.cc:411 msgid "add or edit images" msgstr "ajouter ou éditer des images" #: f.mashup.cc:413 msgid "add or edit text" msgstr "ajouter ou éditer du texte" #: f.mashup.cc:415 msgid "add or edit lines/arrows" msgstr "ajout ou édition de lignes/de flèches" #: f.mashup.cc:417 msgid "change project scale" msgstr "changer l'échelle du projet" #: f.mashup.cc:419 msgid "project complete" msgstr "projet achevé" #: f.mashup.cc:421 msgid "cancel project" msgstr "annuler le projet" #: f.mashup.cc:439 msgid "rescale project" msgstr "changer l'échelle du projet" #: f.mashup.cc:493 msgid "save Mashup output file" msgstr "enregistrer le fichier de sortie d'agencement" #: f.mashup.cc:513 msgid "save Mashup project file?" msgstr "enregistrer le fichier de projet d'agencement?" #: f.mashup.cc:528 msgid "delete Mashup project file?" msgstr "supprimer le fichier de projet d'agencement?" #: f.mashup.cc:588 msgid "Open Project" msgstr "Ouvrir un projet" #: f.mashup.cc:617 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "le fichier image de la mise en page manque: \n" " %s" #: f.mashup.cc:674 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "le fichier image de recouvrement manque: \n" " %s" #: f.mashup.cc:980 msgid "project file is defective" msgstr "le fichier de projet est défectueux" #: f.mashup.cc:1010 msgid "Save Project" msgstr "Enregistrer le projet" #: f.mashup.cc:1151 msgid "layout exceeds 2 gigabytes" msgstr "la composition dépasse 2 giga-octets" #: f.mashup.cc:1221 msgid "Click image to select, drag image to move." msgstr "Cliquer sur l'image pour sélectionner, tirer l'image pour la déplacer." #: f.mashup.cc:1222 msgid "Make black margins transparent" msgstr "Rendre les marges noires transparentes" #: f.mashup.cc:1254 msgid "Add Image" msgstr "Ajouter une image" #: f.mashup.cc:1261 msgid "Current image:" msgstr "Image courante:" #: f.mashup.cc:1265 msgid "Cycle through images:" msgstr "Passer en revue les images:" #: f.mashup.cc:1272 msgid "Scale" msgstr "Échelle" #: f.mashup.cc:1281 msgid "Stacking Order" msgstr "Ordre d'empilement" #: f.mashup.cc:1282 msgid "Raise" msgstr "Monter" #: f.mashup.cc:1283 msgid "Lower" msgstr "Descendre" #: f.mashup.cc:1286 msgid "Base Transparency" msgstr "Transparence de base" #: f.mashup.cc:1290 msgid "Var. Transparency" msgstr "Transparence variable" #: f.mashup.cc:1291 msgid "Paint" msgstr "Peindre" #: f.mashup.cc:1294 msgid "Bend and fine-align" msgstr "Courbure et alignement fin" #: f.mashup.cc:1295 f.widgets.cc:661 msgid "Warp" msgstr "Déformer" #: f.mashup.cc:1304 zfuncs.cc:12896 zfuncs.cc:12905 msgid "Margins" msgstr "Marges" #: f.mashup.cc:1305 msgid "Hard" msgstr "Dur" #: f.mashup.cc:1306 msgid "Blend" msgstr "Fusion" #: f.mashup.cc:1320 msgid "add images to layout" msgstr "ajouter des images à la composition" #: f.mashup.cc:1557 msgid "Paint Image Transparencies" msgstr "Peindre les transparences de l'image" #: f.mashup.cc:1575 msgid "Gradual" msgstr "Graduel" #: f.mashup.cc:1810 msgid "Pull on the image with the mouse." msgstr "Tirer sur l'image avec la souris." #: f.mashup.cc:1826 msgid "Warp Image" msgstr "Déformer l'image" #: f.mashup.cc:1832 f.warp.cc:1209 msgid "warp span" msgstr "étendue de la déformation" #: f.mashup.cc:2264 #, c-format msgid "exceeded %d images" msgstr "excède %d images" #: f.mashup.cc:2406 msgid "Enter text, [Add] to layout, edit properties." msgstr "Entrer le texte, [Ajout] à la mise en page, éditer les propriétés." #: f.mashup.cc:2486 msgid "Text File:" msgstr "Fichier texte:" #: f.mashup.cc:2490 msgid "add entered text to layout" msgstr "ajouter le texte entré à la mise en page" #: f.mashup.cc:2624 msgid "click position to add text" msgstr "cliquer à une position pour ajouter du texte" #: f.mashup.cc:2630 #, c-format msgid "exceeded %d text entries" msgstr "excède %d entrées de texte" #: f.mashup.cc:2635 msgid "Add Text" msgstr "Ajouter du texte" #: f.mashup.cc:2744 msgid "Set line properties, [Add] to layout, edit." msgstr "" "Paramétrer les propriétés de la ligne, [Ajout] à la mise en page, éditer." #: f.mashup.cc:2768 msgid "Edit Line/Arrow" msgstr "Éditer la ligne/la flèche" #: f.mashup.cc:2823 msgid "add line/arrow to layout" msgstr "ajouter une ligne/une flèche à la mise en page" #: f.mashup.cc:2915 msgid "click position to add line" msgstr "cliquer à une position pour ajouter une ligne" #: f.mashup.cc:2921 #, c-format msgid "exceeded %d line entries" msgstr "excède %d lignes" #: f.mashup.cc:2926 msgid "Add Line" msgstr "Ajouter une ligne" #: f.mashup.cc:4171 msgid "Image Montage" msgstr "Montage d'images" #: f.mashup.cc:4180 msgid "Frame Width" msgstr "Largeur de l'image de sortie" #: f.mashup.cc:4183 f.mashup.cc:4191 msgid "Margin" msgstr "Marge" #: f.mashup.cc:4188 msgid "Image Columns" msgstr "Colonnes d'images" #: f.mashup.cc:4205 #, c-format msgid "exceed %d rows" msgstr "dépasse %d rangées" #: f.mashup.cc:4301 msgid "Optimize" msgstr "Optimiser" #: f.mashup.cc:4309 #, c-format msgid "column difference: %d pixels" msgstr "différence entre les colonnes: %d pixels" #: f.mashup.cc:4367 #, c-format msgid "" "column difference: %d pixels \n" "Make columns even?" msgstr "" "différence entre les colonnes: %d pixels \n" "Rendre les colonnes égales?" #: f.mashup.cc:4389 msgid "Save with unique montage name" msgstr "Enregistrer sous un nom de montage unique" #: f.mashup.cc:4391 msgid "unique name:" msgstr "nom unique" #: f.mashup.cc:4393 msgid "create image map" msgstr "créer une image cliquable" #: f.mashup.cc:4410 f.meta.cc:9151 msgid "supply a reasonable name" msgstr "fournir un nom raisonnable" #: f.mashup.cc:4421 msgid "save montage" msgstr "enregistrer le montage" #: f.mashup.cc:4441 #, c-format msgid "map file saved: %s" msgstr "fichier de modèle enregistré: %s" #: f.mashup.cc:4486 #, c-format msgid "%d max images exceeded" msgstr "la limite de %d images est dépassée" #: f.mashup.cc:4560 f.mashup.cc:4566 #, c-format msgid "image frame is too large: %d x %d" msgstr "le cadre de l'image est trop important: %d x %d" #: f.mashup.cc:4871 #, c-format msgid "montage map file invalid: %s" msgstr "le fichier de paramètres du montage n'est pas valide: %s" #: f.meta.cc:245 msgid "Add Metadata Items" msgstr "Ajout d'éléments de métadonnées" #: f.meta.cc:249 f.meta.cc:1609 f.meta.cc:3190 msgid "click to select" msgstr "cliquer pour sélectionner" #: f.meta.cc:254 msgid "click to unselect" msgstr "cliquer pour désélectionner" #: f.meta.cc:476 msgid "Extras" msgstr "Extras" #: f.meta.cc:476 msgid "View Metadata" msgstr "Visualiser les métadonnées" #: f.meta.cc:661 msgid "View All Metadata" msgstr "Visualiser toutes les métadonnées" #: f.meta.cc:826 msgid "Edit Metadata" msgstr "Modifier les métadonnées" #: f.meta.cc:829 msgid "save metadata to file" msgstr "enregistrer les métadonnées dans le fichier" #: f.meta.cc:838 msgid "Image Date" msgstr "Date de l'image" #: f.meta.cc:841 msgid "Time" msgstr "Heure" #: f.meta.cc:849 msgid "Rating (stars):" msgstr "Évaluation (étoiles):" #: f.meta.cc:861 msgid "Caption" msgstr "Légende" #: f.meta.cc:866 msgid "Comments" msgstr "Annotations" #: f.meta.cc:873 msgid "Location" msgstr "Localisation" #: f.meta.cc:876 msgid "Country" msgstr "Pays" #: f.meta.cc:899 msgid "Image Tags" msgstr "Étiquettes de l'image" #: f.meta.cc:905 msgid "Recent Tags" msgstr "Étiquettes récentes" #: f.meta.cc:911 f.meta.cc:2081 msgid "Enter New Tag" msgstr "Entrer une nouvelle étiquette" #: f.meta.cc:917 f.meta.cc:2087 f.meta.cc:4750 msgid "Matching Tags" msgstr "Étiquettes trouvées" #: f.meta.cc:925 f.meta.cc:2096 f.meta.cc:2556 f.meta.cc:4757 msgid "Defined Tags Category" msgstr "Catégories d'étiquettes définies" #: f.meta.cc:933 msgid "search known locations" msgstr "chercher dans les localisations connues" #: f.meta.cc:934 msgid "search using web service" msgstr "chercher en utilisant le service web" #: f.meta.cc:1384 f.meta.cc:3800 f.meta.cc:7709 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "mauvaise latitude/longitude: %s %s" #: f.meta.cc:1441 f.widgets.cc:477 msgid "Manage Tags" msgstr "Gérer les étiquettes" #: f.meta.cc:1441 msgid "orphan tags" msgstr "étiquettes orphelines" #: f.meta.cc:1445 msgid "category" msgstr "categorie" #: f.meta.cc:1448 msgid "tag" msgstr "étiquette" #: f.meta.cc:1455 msgid "Defined Tags:" msgstr "Étiquettes définies:" #: f.meta.cc:1605 msgid "Edit Any Metadata" msgstr "Modifier n'importe quelle métadonnée" #: f.meta.cc:1605 f.meta.cc:3185 msgid "Full List" msgstr "Liste complète" #: f.meta.cc:1619 f.meta.cc:3202 msgid "key name" msgstr "nom de la clé" #: f.meta.cc:1622 f.meta.cc:3203 msgid "key value" msgstr "valeur de la clé" #: f.meta.cc:1815 msgid "Delete Metadata" msgstr "Supprimer des métadonnées" #: f.meta.cc:1821 fotoxx.h:1317 msgid "All" msgstr "Tout" #: f.meta.cc:1822 msgid "One Key:" msgstr "Une clé:" #: f.meta.cc:1908 msgid "choose options" msgstr "choisir les options" #: f.meta.cc:1910 msgid "caption" msgstr "légende" #: f.meta.cc:1911 msgid "comment" msgstr "commentaire" #: f.meta.cc:2057 msgid "Batch Add/Remove Tags" msgstr "Ajout/suppression de métadonnées par lot" #: f.meta.cc:2070 msgid "tags to add" msgstr "étiquettes à ajouter" #: f.meta.cc:2071 msgid "tags to remove" msgstr "étiquettes à supprimer" #: f.meta.cc:2176 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " trop d'étiquettes" #: f.meta.cc:2191 msgid "repeat with same files?" msgstr "répéter avec les mêmes fichiers?" #: f.meta.cc:2331 msgid "specify files and tags" msgstr "specifier fichiers et étiquettes" #: f.meta.cc:2535 f.widgets.cc:596 msgid "Batch Rename Tags" msgstr "Renommer des étiquettes par lot" #: f.meta.cc:2545 msgid "(click defined tag)" msgstr "(cliquer sur une étiquette définie)" #: f.meta.cc:2547 f.process.cc:815 msgid "Rename to" msgstr "Renommer vers" #: f.meta.cc:2564 msgid "old tag name >> new tag name" msgstr "ancien nom d'étiquette >> nouveau nom d'étiquette" #: f.meta.cc:2620 #, c-format msgid "" "%d tags to rename \n" "in %d image files. \n" "Proceed?" msgstr "" "%d étiquettes à renommer \n" "dans %d fichiers image. \n" "Continuer?" #: f.meta.cc:2774 msgid "max tags exceeded" msgstr "nombre maximum d'étiquettes dépassé" #: f.meta.cc:2843 f.meta.cc:2978 msgid "Batch Photo Date/Time" msgstr "Mise à jour de la date/de l'heure des photos par lot" #: f.meta.cc:2851 msgid "set a new date/time:" msgstr "entrer une nouvelle date/une nouvelle heure" #: f.meta.cc:2859 msgid "shift existing date/time:" msgstr "décaler la date/l'heure existantes" #: f.meta.cc:2885 msgid "test: show changes, do not update files" msgstr "test: afficher les changements, ne pas mettre à jour les fichiers" #: f.meta.cc:2910 msgid "please make a choice" msgstr "merci d'effectuer un choix" #: f.meta.cc:2915 f.meta.cc:3276 f.meta.cc:3467 msgid "no files selected" msgstr "aucun fichier sélectionné" #: f.meta.cc:2945 f.meta.cc:2955 f.meta.cc:2961 msgid "invalid date/time format" msgstr "format de date/d'heure non valide" #: f.meta.cc:3185 msgid "Batch Add/Change Metadata" msgstr "Ajouter/Modifier des métadonnées par lot" #: f.meta.cc:3270 msgid "enter key names" msgstr "entrer les noms de clé" #: f.meta.cc:3353 msgid "" "The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names" msgstr "" "La commande: $ man Image::ExifTool::TagNames \n" "affichera plus de 15000 noms d'étiquette \"standard\"" #: f.meta.cc:3450 msgid "Batch Report Metadata" msgstr "État des métadonnées par lot" #: f.meta.cc:3456 msgid "list of reported metadata items" msgstr "liste des éléments de métadonnées affichés" #: f.meta.cc:3599 f.widgets.cc:600 msgid "Batch Geotags" msgstr "Ajout de balises de géolocalisation par lot" #: f.meta.cc:3640 msgid "location" msgstr "localisation" #: f.meta.cc:3643 msgid "country" msgstr "pays" #: f.meta.cc:3676 msgid "Adding Geotags" msgstr "Ajout de balises de géolocalisation" #: f.meta.cc:3785 msgid "" "data is incomplete \n" " proceed?" msgstr "" "les données sont incomplètes \n" " continuer?" #: f.meta.cc:3873 msgid "Report Image Locations" msgstr "Afficher un état des localisations des images" #: f.meta.cc:3874 msgid "Group by country" msgstr "Grouper par pays" #: f.meta.cc:3875 msgid "Group by country/location" msgstr "Grouper par pays/localisation" #: f.meta.cc:3876 msgid "Group by country/location/date" msgstr "Grouper par pays/localisation/date" #: f.meta.cc:3877 msgid "Group by date/country/location" msgstr "Grouper par date/pays/localisation" #: f.meta.cc:3880 msgid "Combine within" msgstr "Combiner" #: f.meta.cc:3882 msgid "days" msgstr "jours" #: f.meta.cc:3994 msgid "Image Locations" msgstr "Localisations des images" #: f.meta.cc:4263 msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" msgstr "Jan Fév Mar Avr Mai Jui Jul Aoû Sep Oct Nov Déc" #: f.meta.cc:4655 msgid "Search Images" msgstr "Rechercher des images" #: f.meta.cc:4659 msgid "images to search:" msgstr "images dans lesquelles chercher:" #: f.meta.cc:4660 msgid "all" msgstr "tout" #: f.meta.cc:4661 msgid "current set only" msgstr "sous-ensemble courant seulement" #: f.meta.cc:4664 msgid "matching images:" msgstr "images trouvées:" #: f.meta.cc:4665 msgid "make new set" msgstr "créer un nouveau sous-ensemble" #: f.meta.cc:4666 msgid "add to set" msgstr "ajout au sous-ensemble" #: f.meta.cc:4667 msgid "remove" msgstr "enlever" #: f.meta.cc:4672 msgid "select last version only" msgstr "sélectionner uniquement la dernière version" #: f.meta.cc:4673 msgid "original + last version" msgstr "original + la dernière version" #: f.meta.cc:4675 msgid "original + all versions" msgstr "original + toutes les versions" #: f.meta.cc:4676 f.process.cc:166 f.process.cc:172 f.process.cc:180 #: f.process.cc:2058 msgid "no change" msgstr "pas de changement" #: f.meta.cc:4681 msgid "report type:" msgstr "type d'état:" #: f.meta.cc:4682 msgid "gallery" msgstr "galerie" #: f.meta.cc:4688 msgid "date range" msgstr "plage de dates" #: f.meta.cc:4693 msgid "photo date" msgstr "date de la photo" #: f.meta.cc:4694 msgid "file date" msgstr "date du fichier" #: f.meta.cc:4695 msgid "(yyyy-mm-dd)" msgstr "(aaaa-mm-jj)" #: f.meta.cc:4700 msgid "rating range" msgstr "plage d'évaluation" #: f.meta.cc:4708 msgid "all/any" msgstr "tout/n'importe lequel" #: f.meta.cc:4711 msgid "search tags" msgstr "rechercher les étiquettes" #: f.meta.cc:4718 msgid "search text" msgstr "rechercher le texte" #: f.meta.cc:4724 msgid "search files" msgstr "rechercher les fichiers" #: f.meta.cc:4730 msgid "search locations" msgstr "rechercher les localisations" #: f.meta.cc:4734 msgid "enter cities, countries" msgstr "saisir les villes et/ou les pays" #: f.meta.cc:4739 msgid "search other metadata" msgstr "rechercher d'autres métadonnées" #: f.meta.cc:4746 msgid "Enter Search Tag" msgstr "Entrer l'étiquette" #: f.meta.cc:5060 msgid "" "to remove images from current set, \n" "search current set" msgstr "" "pour enlever des images du sous-ensemble courant, \n" "rechercher dans celui-ci" #: f.meta.cc:5067 msgid "" "to add images to current set, \n" "search all images" msgstr "" "pour ajouter des images au sous-ensemble courant, \n" "rechercher dans toutes les images" #: f.meta.cc:5135 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "plage de dates irraisonnable \n" " %s %s" #: f.meta.cc:5160 msgid "stars range not reasonable" msgstr "plage d'étoiles irraisonnable" #: f.meta.cc:5718 msgid "Always reported: date, stars, tags, caption, comment" msgstr "" "Sont toujours affichés: date, étoiles, étiquettes, légende, commentaire" #: f.meta.cc:5750 msgid "Additional Items for Report" msgstr "Éléments supplémentaires à inclure dans l'état" #: f.meta.cc:5757 msgid "Keyword" msgstr "Mot-clé" #: f.meta.cc:5771 msgid "Match Criteria" msgstr "Critère de recherche" #: f.meta.cc:5904 #, c-format msgid "bad number: %s" msgstr "nombre non valide: %s" #: f.meta.cc:6184 msgid "date format is YYYY-MM-DD" msgstr "le format de la date est AAAA-MM-JJ" #: f.meta.cc:6188 msgid "date is invalid" msgstr "date non valide" #: f.meta.cc:6226 msgid "time format is HH:MM [:SS]" msgstr "le format de l'heure est HH:MM [:SS]" #: f.meta.cc:6230 msgid "time is invalid" msgstr "heure non valide" #: f.meta.cc:7328 msgid "not found" msgstr "non trouvé" #: f.meta.cc:7329 msgid "location and country required" msgstr "la localisation et le pays sont requis" #: f.meta.cc:7586 msgid "choose location" msgstr "choisir une localisation" #: f.meta.cc:7886 msgid "Set Map Markers" msgstr "Positionner les repères sur la carte" #: f.meta.cc:7887 msgid "mark all image files" msgstr "marquer tous les fichiers image" #: f.meta.cc:7888 msgid "mark current gallery" msgstr "marquer la galerie courante" #: f.meta.cc:7989 msgid "choose map file" msgstr "choisir un fichier de carte" #: f.meta.cc:8133 msgid "" "fotoxx-maps package not installed \n" "(see https://kornelix.net)" msgstr "" "le paquet fotoxx-maps n'est pas installé \n" "(voir https://kornelix.net)" #: f.meta.cc:8138 #, c-format msgid "map file %s is missing" msgstr "le fichier de carte %s est absent" #: f.meta.cc:8142 #, c-format msgid "" "map latitude/longitude data unreasonable \n" " %.3f %.3f %.3f %.3f" msgstr "" "les données de latitude/longitude sont irraisonnables \n" " %.3f %.3f %.3f %.3f" #: f.meta.cc:8468 f.meta.cc:9020 msgid "No matching images found" msgstr "Aucune image correspondante n'a été trouvée" #: f.meta.cc:8562 msgid "Net Map Source" msgstr "Source des cartes Internet" #: f.meta.cc:9098 msgid "Net Map Locations" msgstr "Localisations des cartes Internet" #: f.meta.cc:9104 msgid "map location:" msgstr "localisation de la carte:" #: f.pixmap.cc:3227 f.pixmap.cc:3279 msgid "HEIF files not supported" msgstr "fichiers HEIF non supportés" #: f.pixmap.cc:3351 f.pixmap.cc:3402 msgid "JP2 files not supported" msgstr "" #: f.process.cc:134 f.widgets.cc:588 msgid "Batch Convert" msgstr "Conversion par lot" #: f.process.cc:145 msgid "Sequence Numbers" msgstr "Numéros de séquence" #: f.process.cc:147 msgid "base" msgstr "base" #: f.process.cc:150 msgid "adder" msgstr "incrément" #: f.process.cc:155 msgid "New Location" msgstr "Nouvel emplacement" #: f.process.cc:160 msgid "New File Type" msgstr "Nouveau type de fichier" #: f.process.cc:169 f.process.cc:1390 msgid "Color Depth:" msgstr "Profondeur de couleur:" #: f.process.cc:175 f.process.cc:2053 msgid "max. Width" msgstr "Largeur max." #: f.process.cc:184 msgid "Delete Originals" msgstr "Effacer les originaux" #: f.process.cc:185 f.process.cc:821 msgid "Copy Metadata" msgstr "Copier les métadonnées" #: f.process.cc:199 f.process.cc:824 msgid "Overlay Image" msgstr "Image de recouvrement" #: f.process.cc:202 f.warp.cc:4547 msgid "Width %" msgstr "Largeur %" #: f.process.cc:207 msgid "Position" msgstr "Position" #: f.process.cc:219 msgid "Make constant size for screen:" msgstr "Rendre la taille constante pour l'écran:" #: f.process.cc:227 msgid "" "plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s" msgstr "" "extensions: (année mois jour anciennom séquence) $yyyy $mm $dd $oldname " "$s" #: f.process.cc:371 #, c-format msgid "file type not supported: %s \n" msgstr "type de fichier non supporté: %s \n" #: f.process.cc:507 f.process.cc:2116 msgid "cannot create new file" msgstr "ne peut pas créer de nouveau fichier" #: f.process.cc:554 msgid "updating albums ..." msgstr "mise à jour des albums ..." #: f.process.cc:732 #, c-format msgid "invalid plugin: %s" msgstr "extension non valide: %s" #: f.process.cc:739 msgid "you must use either $s or $oldname" msgstr "vous devez utiliser soit $s soit $oldname" #: f.process.cc:744 msgid "$s plugin needs base and adder" msgstr "l'extension $s a besoin d'une base et d'un incrément" #: f.process.cc:749 msgid "base and adder need $s plugin" msgstr "la base et l'incrément nécessitent l'extension $s" #: f.process.cc:763 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "la taille max. %d x %d n'est pas dans des limites raisonnables" #: f.process.cc:770 msgid "specify overlay image file" msgstr "specifier l'image de recouvrement" #: f.process.cc:786 msgid "specify overlay position" msgstr "specifier la position de l'image de recouvrement" #: f.process.cc:814 #, c-format msgid "Convert %d image files" msgstr "Convertir %d fichiers image" #: f.process.cc:816 msgid "Convert to" msgstr "Convertir vers" #: f.process.cc:818 msgid "Resize within" msgstr "Redimensionner à l'intérieur de" #: f.process.cc:819 msgid "Output to" msgstr "Sortie vers" #: f.process.cc:825 msgid "*** Delete Originals ***" msgstr "*** Effacer les originaux ***" #: f.process.cc:826 msgid "*** Replace Originals ***" msgstr "*** Remplacer les originaux ***" #: f.process.cc:827 msgid "PROCEED?" msgstr "POURSUIVRE?" #: f.process.cc:983 f.widgets.cc:589 msgid "Batch Upright" msgstr "Redressement par lot" #: f.process.cc:989 msgid "Survey all files" msgstr "Examiner tous les fichiers" #: f.process.cc:1029 msgid "file cannot be read" msgstr "le fichier est illisible" #: f.process.cc:1146 msgid "cannot select both options" msgstr "impossible de sélectionner les deux options simultanément" #: f.process.cc:1187 f.widgets.cc:590 msgid "Batch Delete/Trash" msgstr "Effacement / Mise à la corbeille par lot" #: f.process.cc:1192 msgid "delete" msgstr "supprimer" #: f.process.cc:1195 msgid "trash" msgstr "corbeille" #: f.process.cc:1258 msgid "Purging deleted files from albums \n" msgstr "Purge des fichiers supprimés des albums \n" #: f.process.cc:1338 msgid "Batch Convert RAW Files" msgstr "Conversion par lot de fichiers RAW" #: f.process.cc:1377 msgid "output location" msgstr "emplacement de la sortie" #: f.process.cc:1382 msgid "File Type" msgstr "Type de fichier" #: f.process.cc:1406 msgid "amount" msgstr "quantité" #: f.process.cc:1409 msgid "threshold" msgstr "seuil" #: f.process.cc:1413 msgid "Fix dead pixels" msgstr "Compenser pour les pixels morts" #: f.process.cc:1416 msgid "dead pixel map file" msgstr "fichier de localisation des pixels morts" #: f.process.cc:1419 msgid "Fix pixel bias" msgstr "Compenser pour la polarisation du pixel (bias)" #: f.process.cc:1422 msgid "pixel bias map file" msgstr "fichier de compensation de la polarisation de pixel" #: f.process.cc:1727 msgid "growisofs not installed" msgstr "growisofs n'est pas installé" #: f.process.cc:1774 msgid "no DVD/BlueRay device found" msgstr "aucun périphérique DVD/BluRay n'a été trouvé" #: f.process.cc:1797 msgid "Burn Images to DVD/BlueRay" msgstr "Graver des images vers un DVD/BluRay" #: f.process.cc:1802 msgid "Select device" msgstr "Sélectionner le périphérique" #: f.process.cc:1889 f.widgets.cc:355 msgid "Create a file of selected image files" msgstr "Créer un fichier liste à partir des fichiers image sélectionnés" #: f.process.cc:1915 f.process.cc:1984 msgid "Output File" msgstr "Fichier de sortie" #: f.process.cc:1936 msgid "no input files selected" msgstr "aucun fichier d'entrée n'est sélectionné" #: f.process.cc:1942 msgid "no output file selected" msgstr "aucun fichier de sortie n'est sélectionné" #: f.process.cc:2044 f.widgets.cc:594 msgid "Export Files" msgstr "Exporter des fichiers" #: f.process.cc:2049 msgid "To Location" msgstr "Vers l'emplacement" #: f.process.cc:2060 msgid "export metadata" msgstr "exporter les métadonnées" #: f.process.cc:2091 msgid "file type not supported" msgstr "type de fichier non supporté" #: f.process.cc:2173 msgid "location is not a folder" msgstr "l'emplacement n'est pas un dossier" #: f.process.cc:2225 msgid "Script Files" msgstr "Fichiers de script" #: f.process.cc:2229 msgid "begin making a script file" msgstr "début de la création d'un fichier de script" #: f.process.cc:2232 msgid "finish making a script file" msgstr "fin de la création d'un fichier de script" #: f.process.cc:2276 msgid "script already started" msgstr "script déjà démarré" #: f.process.cc:2280 msgid "start a new script file" msgstr "commencer un nouveau fichier de script" #: f.process.cc:2301 msgid "perform edits to be included in the script file" msgstr "" "effectuer les opérations d'édition devant être incluses dans le fichier de " "script" #: f.process.cc:2318 msgid "script file error" msgstr "erreur dans le fichier de script" #: f.process.cc:2323 #, c-format msgid "%s added to script" msgstr "%s ajouté au script" #: f.process.cc:2333 msgid "no script file was started" msgstr "aucun fichier de script n'a été démarré" #: f.process.cc:2341 msgid "script file closed" msgstr "fichier de script fermé" #: f.process.cc:2374 f.process.cc:2395 msgid "no script files found" msgstr "aucun fichier de script n'a été trouvé" #: f.process.cc:2418 #, c-format msgid "" "script error: %s \n" " %s" msgstr "" "erreur dans le script: %s \n" " %s" #: f.process.cc:2440 #, c-format msgid "unknown edit function: %s" msgstr "fonction d'édition inconnue: %s" #: f.process.cc:2452 #, c-format msgid "load widgets failed: %s" msgstr "le chargement des widgets a échoué: %s" #: f.process.cc:2468 #, c-format msgid "script file format error: %s" msgstr "erreur de format du fichier de script: %s" #: f.process.cc:2494 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "échec de l'ouverture: %s \n" " %s" #: f.process.cc:2511 msgid "script complete" msgstr "script terminé" #: f.process.cc:2553 f.widgets.cc:603 msgid "Batch Script" msgstr "Script de traitement par lot" #: f.process.cc:2560 msgid "Select Script" msgstr "Sélectionner un script" #: f.process.cc:2561 msgid "script file to run" msgstr "fichier de script à exécuter" #: f.tools.cc:93 msgid "Folders for image files (subfolders included automatically)." msgstr "" "Dossier des fichiers image (les sous-dossiers seront inclus automatiquement)" #: f.tools.cc:95 msgid "Select to add, click on X to delete." msgstr "Sélectionner pour ajouter, cliquer sur X pour supprimer." #: f.tools.cc:96 msgid "folder for thumbnails" msgstr "dossier pour les vignettes" #: f.tools.cc:97 msgid "extra metadata items to include in index" msgstr "métadonnées supplémentaires à inclure dans l'index" #: f.tools.cc:98 msgid "force a full re-index of all image files" msgstr "forcer une réindexation complète de tous les fichiers image" #: f.tools.cc:99 msgid "" "Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "La fonction d'index est terminée. \n" "L'indexation est requise pour les recherches, les cartes \n" "et pour rendre les pages de galeries suffisamment rapides." #: f.tools.cc:136 msgid "Index Image Files" msgstr "Index des fichiers image" #: f.tools.cc:324 msgid "Choose top image folders" msgstr "Choisir les dossiers de plus haut niveau pour les images" #: f.tools.cc:325 msgid "Choose thumbnail folder" msgstr "Choisir le dossier des vignettes" #: f.tools.cc:326 msgid "" "All image files will be re-indexed. \n" " Continue?" msgstr "" "Tous les fichiers image vont être réindexés. \n" " Continuer?" #: f.tools.cc:442 msgid "" "No image file index was found.\n" "An image file index will be created.\n" "Your image files will not be changed.\n" "This may need considerable time if you \n" "have many thousands of image files." msgstr "" "Aucun index des fichiers image n'a été trouvé.\n" "Un index des fichiers image va être créé.\n" "Vos fichiers image ne seront pas modifiés.\n" "Ceci peut prendre un temps considérable si vous \n" "avez plusieurs milliers de fichiers image." #: f.tools.cc:448 #, c-format msgid "" "Thumbnails folder: %s \n" "Please remove." msgstr "" "Dossier des vignettes: %s \n" "Merci d'enlever." #: f.tools.cc:451 #, c-format msgid "" "Thumbnails folder: \n" " %s \n" "must be named .../thumbnails" msgstr "" "Le dossier des vignettes: \n" " %s \n" "doit être nommé .../thumbnails" #: f.tools.cc:454 #, c-format msgid "" "Duplicate or nested folders: \n" " %s \n" " %s \n" "Please remove." msgstr "" "Dossiers dupliqués ou imbriqués: \n" " %s \n" " %s \n" "Merci d'enlever." #: f.tools.cc:537 msgid "specify at least 1 top image folder" msgstr "spécifier au moins 1 dossier de départ pour les images" #: f.tools.cc:542 msgid "specify 1 thumbnail folder" msgstr "spécifier 1 dossier pour les vignettes" #: f.tools.cc:649 msgid "build index" msgstr "construction de l'index" #: f.tools.cc:778 msgid "Top folders have no images" msgstr "Les dossiers de plus haut niveau ne contiennent pas d'images" #: f.tools.cc:785 #, c-format msgid "" "0 old files found, %d new files found.\n" "A full re-index is required. Continue?" msgstr "" "Aucun ancien fichier n'a été trouvé, %d nouveau fichiers ont été trouvésIl " "faut une réindexation complète. Poursuivre?" #: f.tools.cc:1286 msgid "Cancel image index function?" msgstr "Annuler la fonction d'indexation des images?" #: f.tools.cc:1447 #, c-format msgid "" "Do you want to move Fotoxx home? \n" " from: %s \n" " to: %s" msgstr "" "Voulez-vous transférer le dossier d'origine de Fotoxx? \n" " de: %s \n" " vers: %s" #: f.tools.cc:1449 msgid "moving files ..." msgstr "déplacement des fichiers ..." #: f.tools.cc:1473 msgid "new Fotoxx home folder" msgstr "nouveau dossier de base de Fotoxx" #: f.tools.cc:1493 msgid "new location name contains a space" msgstr "le nom du nouvel emplacement contient un espace" #: f.tools.cc:1533 msgid "index_config file has no thumbnails folder" msgstr "le fichier index_config ne contient pas de dossier pour les vignettes" #: f.tools.cc:1541 msgid "update thumbnail folder failed" msgstr "la mise à jour du dossier des vignettes a échoué" #: f.tools.cc:1544 msgid "thumbnails folder not changed" msgstr "le dossier des vignettes est inchangé" #: f.tools.cc:1545 #, c-format msgid "new thumbnails folder: %s/thumbnails" msgstr "nouveau dossier des vignettes: %s/thumbnails" #: f.tools.cc:1556 msgid "Fotoxx will restart" msgstr "Fotoxx va redémarrer" #: f.tools.cc:1572 msgid "Recent Files Gallery" msgstr "Galerie des fichiers récents" #: f.tools.cc:1573 msgid "Newest Files Gallery" msgstr "Galerie des nouveaux fichiers" #: f.tools.cc:1574 msgid "Specific Gallery" msgstr "Galerie spécifique" #: f.tools.cc:1575 msgid "Album Gallery" msgstr "Galerie d'album" #: f.tools.cc:1576 msgid "Previous Gallery" msgstr "Galerie précédente" #: f.tools.cc:1577 msgid "Previous File" msgstr "Fichier visionné en dernier" #: f.tools.cc:1578 msgid "Specific File" msgstr "Fichier spécifique" #: f.tools.cc:1579 f.widgets.cc:434 msgid "Blank Window" msgstr "Vider la fenêtre" #: f.tools.cc:1604 #, c-format msgid "%c of scale" msgstr "%c de l'échelle" #: f.tools.cc:1647 msgid "Preferences and Settings" msgstr "Préférences et paramètres" #: f.tools.cc:1650 msgid "Startup Display:" msgstr "Affichage au démarrage:" #: f.tools.cc:1656 msgid "Background Colors:" msgstr "Couleurs d'arrière-plan:" #: f.tools.cc:1658 msgid "F-View" msgstr "Vue-F" #: f.tools.cc:1661 msgid "G-View" msgstr "Vue-G" #: f.tools.cc:1665 msgid "Menu Style:" msgstr "Style du menu:" #: f.tools.cc:1667 msgid "Icons" msgstr "Icônes" #: f.tools.cc:1669 msgid "Both" msgstr "Les deux" #: f.tools.cc:1671 msgid "Icon size" msgstr "Taille des icônes" #: f.tools.cc:1675 msgid "Menu Colors:" msgstr "Couleurs du menu:" #: f.tools.cc:1684 msgid "Dialog Font:" msgstr "Police des dialogues:" #: f.tools.cc:1703 msgid "JPEG file save quality:" msgstr "Qualité d'enregistrement des fichiers JPEG:" #: f.tools.cc:1706 msgid "(90 = high quality)" msgstr "(90 = haute qualité)" #: f.tools.cc:1709 msgid "TIFF file compression method" msgstr "Méthode de compression des fichiers TIFF" #: f.tools.cc:1715 msgid "PNG file compression level" msgstr "Niveau de compression des fichiers PNG" #: f.tools.cc:1721 msgid "Curve Node Separation, Capture Range:" msgstr "Séparation des noeuds de courbe, plage de capture:" #: f.tools.cc:1727 msgid "Map Marker Size:" msgstr "Taille d'un marqueur sur une carte:" #: f.tools.cc:1733 msgid "Show Images (F-view, G-view):" msgstr "Afficher les images (Vue-F, Vue-G):" #: f.tools.cc:1735 msgid "last version only" msgstr "dernière version seulement" #: f.tools.cc:1736 msgid "all images" msgstr "toutes les images" #: f.tools.cc:1739 msgid "Image Position in Window:" msgstr "Placement de l'image dans la fenêtre:" #: f.tools.cc:1741 msgid "centered" msgstr "centrée" #: f.tools.cc:1742 msgid "right side" msgstr "à droite" #: f.tools.cc:1745 msgid "Image Index Level:" msgstr "Niveau d'indexation des images:" #: f.tools.cc:1749 msgid "Fotoxx started directly (2)" msgstr "Fotoxx démarré directement (2)" #: f.tools.cc:1753 msgid "Fotoxx started by file manager (1)" msgstr "Fotoxx démarré par un gestionnaire de fichiers (1)" #: f.tools.cc:1756 msgid "RAW File Loader:" msgstr "Décodeur de fichiers RAW:" #: f.tools.cc:1762 msgid "RAW Conversion Options:" msgstr "Options de conversion des fichiers RAW:" #: f.tools.cc:1765 msgid "extend dynamic range for dim images" msgstr "étendre la plage dynamique pour les images pâles" #: f.tools.cc:1768 msgid "use embedded image as a guide" msgstr "utiliser la vignette incorporée à l'image comme guide" #: f.tools.cc:1771 msgid "RAW File Types:" msgstr "Types de fichiers RAW:" #: f.tools.cc:1776 msgid "Video File Types:" msgstr "Types de fichiers vidéo:" #: f.tools.cc:1781 msgid "Video File Play Command:" msgstr "Commande de lecture des fichiers vidéo:" #: f.tools.cc:1937 msgid "Select startup folder" msgstr "Sélectionner le dossier de démarrage" #: f.tools.cc:1944 msgid "Select startup image file" msgstr "Sélectionner le fichier image de démarrage" #: f.tools.cc:1951 msgid "Select startup album" msgstr "Sélectionner un album de démarrage" #: f.tools.cc:1976 msgid "rawtherapee-cli (rawtherapee) is not installed" msgstr "rawtherapee-cli (RawTherapee) n'est pas installé" #: f.tools.cc:2006 msgid "startup folder is invalid" msgstr "dossier de démarrage non valide" #: f.tools.cc:2016 msgid "startup file is invalid" msgstr "fichier de démarrage non valide" #: f.tools.cc:2216 f.widgets.cc:372 msgid "Keyboard Shortcuts" msgstr "Raccourcis clavier" #: f.tools.cc:2222 msgid "Reserved Shortcuts \n" msgstr "Raccourcis clavier réservés \n" #: f.tools.cc:2223 msgid " Z Toggle 1x / fit window \n" msgstr " Z Bascule échelle 1x / cadré dans la fenêtre \n" #: f.tools.cc:2224 msgid " F1 User Guide, Context Help \n" msgstr " F1 Guide de l'utilisateur, aide contextuelle \n" #: f.tools.cc:2225 msgid " F10 Full Screen with menus \n" msgstr " F10 Plein écran avec menus \n" #: f.tools.cc:2226 msgid " F11 Full Screen without menus \n" msgstr " F11 Plein écran sans menus \n" #: f.tools.cc:2227 msgid " Escape Quit dialog, Quit Fotoxx \n" msgstr " Échap Abandonner un dialogue, quitter Fotoxx \n" #: f.tools.cc:2228 msgid " Delete Delete/Trash \n" msgstr " Suppr Supprimer/corbeille \n" #: f.tools.cc:2229 msgid " Arrow keys Navigation \n" msgstr " T. fléchées Navigation \n" #: f.tools.cc:2230 msgid " Page keys Navigation \n" msgstr " T. page Navigation \n" #: f.tools.cc:2231 msgid " Home/End Navigation \n" msgstr " Début/Fin Navigation \n" #: f.tools.cc:2295 msgid "Edit KB Shortcuts" msgstr "Éditer les raccourcis clavier" #: f.tools.cc:2302 msgid "shortcut key:" msgstr "touche de raccourci clavier:" #: f.tools.cc:2303 msgid "(enter key)" msgstr "(entrer une touche)" #: f.tools.cc:2304 f.tools.cc:2451 f.tools.cc:2554 msgid "(no selection)" msgstr "(pas de sélection)" #: f.tools.cc:2445 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" est réservé, ne peut être utilisé" #: f.tools.cc:2672 msgid "Brightness Distribution" msgstr "Distribution de la luminosité" #: f.tools.cc:2866 msgid "" "Drag mouse on image. \n" "Left click to cancel." msgstr "" "Cliquer-tirer la souris sur l'image. \n" "Clic gauche pour annuler." #: f.tools.cc:2893 f.widgets.cc:612 msgid "Magnify Image" msgstr "Agrandir l'image" #: f.tools.cc:2902 msgid "X-size" msgstr "Taille X" #: f.tools.cc:3210 msgid "Find Duplicate Images" msgstr "Trouver les images dupliquées" #: f.tools.cc:3213 msgid "All files" msgstr "Tous les fichiers" #: f.tools.cc:3214 msgid "Current gallery" msgstr "Galerie courante" #: f.tools.cc:3217 msgid "File count:" msgstr "Nombre de fichiers" #: f.tools.cc:3221 msgid "Thumbnail size" msgstr "Taille des vignettes" #: f.tools.cc:3226 msgid "Pixel difference" msgstr "Différence des pixels" #: f.tools.cc:3229 msgid "Pixel count" msgstr "Nombre de pixels" #: f.tools.cc:3237 msgid "Duplicates:" msgstr "Duplications:" #: f.tools.cc:3567 msgid "too many files, cannot continue" msgstr "trop de fichiers, impossible de poursuivre" #: f.tools.cc:3647 msgid "Click image to select pixels." msgstr "Cliquer sur l'image pour sélectionner des pixels." #: f.tools.cc:3690 f.widgets.cc:614 msgid "Show RGB" msgstr "Afficher RVB" #: f.tools.cc:3948 msgid "Change Color Profile" msgstr "Changer le profil de couleur" #: f.tools.cc:3952 msgid "input profile" msgstr "profil d'entrée" #: f.tools.cc:3956 msgid "output profile" msgstr "profil de sortie" #: f.tools.cc:3977 msgid "Unable to change EXIF color profile" msgstr "Impossible de modifier le profil de couleur EXIF" #: f.tools.cc:3979 msgid "automatic new version created" msgstr "nouvelle version automatique créée" #: f.tools.cc:3988 msgid "color profile" msgstr "profil de couleur" #: f.tools.cc:4036 f.tools.cc:4042 #, c-format msgid "unknown cms profile %s" msgstr "profil cms inconnu %s" #: f.tools.cc:4144 f.widgets.cc:616 msgid "Calibrate Printer" msgstr "Calibrer l'imprimante" #: f.tools.cc:4170 msgid "print color chart" msgstr "imprimer une charte de couleurs" #: f.tools.cc:4171 msgid "scan and save color chart" msgstr "scanner et enregistrer la charte de couleurs" #: f.tools.cc:4172 msgid "align and trim color chart" msgstr "aligner et rogner la charte de couleurs" #: f.tools.cc:4173 msgid "open and process color chart" msgstr "ouvrir et traiter la charte de couleurs" #: f.tools.cc:4174 msgid "print image with revised colors" msgstr "imprimer l'image avec les couleurs calibrées" #: f.tools.cc:4315 #, c-format msgid "" "Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/" msgstr "" "Scanner la charte de couleurs imprimée. \n" "La rangée la plus sombre est en haut. \n" "Enregistrer sous %s/" #: f.tools.cc:4330 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY." msgstr "" "Ouvrir et éditer le fichier de charte de couleurs scannée. \n" "Corriger toute distortion ou rotation dûe au scanner. \n" "(Utiliser l'outil Corriger la perspective pour ceci). \n" "Éliminer la fine marge verte PRÉCISÉMENT." #: f.tools.cc:4362 msgid "Open the trimmed color chart file" msgstr "Ouvrir le fichier de charte de couleurs rognée" #: f.tools.cc:4495 msgid "" "Set the name for the output calibration file \n" "[your calibration name].dat" msgstr "" "Entrer le nom du fichier qui contiendra les données de calibration " "produites \n" "[votre nom de fichier de calibration].dat" #: f.tools.cc:4535 msgid "Color map file to use" msgstr "Fichier de calibration à utiliser" #: f.tools.cc:4555 msgid "Select the image file to print." msgstr "Sélectionner le fichier image à imprimer." #: f.tools.cc:4596 msgid "file format error" msgstr "erreur de format de fichier" #: f.tools.cc:4620 msgid "converting colors..." msgstr "conversion des couleurs..." #: f.tools.cc:4720 msgid "Image colors are converted for printing." msgstr "Les couleurs de l'image sont converties pour l'impression." #: f.tools.cc:4769 f.widgets.cc:617 msgid "Grid Lines" msgstr "Lignes de grille" #: f.tools.cc:4778 msgid "x-spacing" msgstr "espacement-x" #: f.tools.cc:4779 msgid "x-count" msgstr "nombre-x" #: f.tools.cc:4780 msgid "x-enable" msgstr "activation-x" #: f.tools.cc:4786 msgid "y-spacing" msgstr "espacement-y" #: f.tools.cc:4787 msgid "y-count" msgstr "nombre-y" #: f.tools.cc:4788 msgid "y-enable" msgstr "activation-y" #: f.tools.cc:4909 f.widgets.cc:618 msgid "Line Color" msgstr "Couleur de la ligne" #: f.tools.cc:4967 msgid "Darkest and Brightest Pixels" msgstr "Pixels les plus sombres et les plus clairs" #: f.tools.cc:4991 msgid "Dark Limit" msgstr "Limite valeurs sombres" #: f.tools.cc:4992 msgid "Bright Limit" msgstr "Limite valeurs claires" #: f.tools.cc:5117 msgid "Map RAW Pixel Bias" msgstr "Déterminer la polarisation des pixels RAW" #: f.tools.cc:5124 msgid "mean RGB:" msgstr "moyenne RVB:" #: f.tools.cc:5210 msgid "select at least 10 RAW image files" msgstr "sélectionner au moins 10 fichiers image" #: f.tools.cc:5230 #, c-format msgid "" "cannot read file \n" " %s" msgstr "" "impossible de lire le fichier \n" " %s" #: f.tools.cc:5235 #, c-format msgid "" "not a RAW file \n" " %s" msgstr "" "ce n'est pas un fichier RAW \n" " %s" #: f.tools.cc:5249 #, c-format msgid "dimensions do not match: %s" msgstr "les dimensions ne correspondent pas: %s" #: f.tools.cc:5366 f.tools.cc:5449 msgid "Pixel Bias Map file" msgstr "Fichier de polarisation des pixels RAW" #: f.tools.cc:5494 msgid "invalid pixel bias map file" msgstr "fichier de polarisation des pixels RAW invalide" #: f.tools.cc:5524 msgid "image dimensions do not match pixel bias file" msgstr "" "les dimensions de l'image ne correspondent pas au fichier de polarisation du " "pixel" #: f.tools.cc:5610 msgid "Map RAW Dead Pixels" msgstr "Déterminer la localisation des pixels morts" #: f.tools.cc:5614 msgid "gray RAW image file" msgstr "fichier image RAW gris" #: f.tools.cc:5618 msgid "RGB threshold" msgstr "seuil RVB" #: f.tools.cc:5621 msgid "dead pixels found:" msgstr "pixels morts trouvés:" #: f.tools.cc:5658 msgid "not a RAW file" msgstr "ce n'est pas un fichier RAW" #: f.tools.cc:5664 msgid "cannot load RAW file" msgstr "ne peut pas charger le fichier RAW" #: f.tools.cc:5727 msgid "choose a gray RAW file" msgstr "choisir un fichier RAW gris" #: f.tools.cc:5858 f.tools.cc:5903 msgid "dead pixels file" msgstr "fichier des pixels morts" #: f.tools.cc:5955 msgid "invalid dead pixels file" msgstr "fichier des pixels morts invalide" #: f.tools.cc:5975 msgid "no dead pixels data available" msgstr "aucune donnée sur les pixels morts disponible" #: f.tools.cc:5980 msgid "image dimensions do not match dead pixels file" msgstr "" "les dimensions de l'image ne correspondent pas au fichier des pixels morts" #: f.tools.cc:6048 msgid "" "Brightness should show a gradual ramp \n" "extending all the way to the edges." msgstr "" "La luminosité doit s'afficher selon \n" "une rampe graduelle de bord à bord." #: f.tools.cc:6208 msgid "Available Translations" msgstr "Traductions disponibles" #: f.tools.cc:6212 msgid "Set Language" msgstr "Choisir le langage" #: f.warp.cc:110 f.widgets.cc:556 msgid "Unbend" msgstr "Corriger la courbure" #: f.warp.cc:388 msgid "" " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle." msgstr "" " Cliquer aux 4 coins d'un quadrilatère. Presser [appliquer]. \n" " L'image est déformée pour faire du quadrilatère un rectangle." #: f.warp.cc:405 msgid "Perspective Correction" msgstr "Correction de la perspective" #: f.warp.cc:638 msgid "must have 4 corners" msgstr "il faut 4 coins" #: f.warp.cc:763 msgid "" " Select an area to warp using select area function. \n" " Press [start warp] and pull area with mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, select another area or press [done]." msgstr "" " Sélectionner une zone à déformer avec la fonction de sélection de zone. \n" " Presser [démarrer déformation] et tirer la zone avec la souris. \n" " Faire plusieurs tirés jusqu'à satisfaction. \n" " Une fois achevé, sélectionner une autre zone ou presser [terminé]." #: f.warp.cc:776 f.widgets.cc:558 msgid "Warp area" msgstr "Déformer une zone" #: f.warp.cc:781 msgid "start warp" msgstr "démarrer la déformation" #: f.warp.cc:1182 f.warp.cc:1494 msgid "" " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Tirer une position sur l'image en utilisant la souris. \n" " Faire plusieurs tirés jusqu'à satisfaction. \n" " Une fois achevé, presser [terminé]." #: f.warp.cc:1200 f.widgets.cc:559 msgid "Warp curved" msgstr "Déformation courbe" #: f.warp.cc:1512 f.widgets.cc:560 msgid "Warp linear" msgstr "Déformation linéaire" #: f.warp.cc:1825 msgid "" " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Tirer un coin de l'image avec la souris. \n" " Faire plusieurs tirés jusqu'à satisfaction. \n" " Une fois achevé, presser [terminé]." #: f.warp.cc:1841 f.widgets.cc:561 msgid "Warp affine" msgstr "Déformation affine" #: f.warp.cc:2174 msgid "" " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n" msgstr "" "Utiliser Sélectionner une zone pour délimiter un visage. \n" "Cliquer sur le centre de la distorsion. \n" "Déplacer le curseur. \n" #: f.warp.cc:2201 f.widgets.cc:562 msgid "Unwarp Closeup" msgstr "Corriger la déformation d'un gros plan" #: f.warp.cc:2379 msgid "Flatten Book Page" msgstr "Aplatir une page de livre" #: f.warp.cc:2380 msgid "" "Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: " msgstr "" "Rogner l'image pour isoler une page. \n" "Définir les bords supérieur et inférieur avec \n" "4 clics ou plus de la souris, puis aplatir: " #: f.warp.cc:2383 msgid "Stretch curved-down surfaces:" msgstr "Aplatir les surfaces courbées:" #: f.warp.cc:2438 msgid "Top:" msgstr "en Haut:" #: f.warp.cc:2441 msgid "Bottom:" msgstr "en Bas:" #: f.warp.cc:2830 msgid "" " Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]." msgstr "" " Sélectionner les zones qui resteront inchangées. \n" " Tirer l'image depuis le coin supérieur gauche. \n" " Une fois achevé, presser [terminé]." #: f.warp.cc:2846 f.widgets.cc:564 msgid "Area Rescale" msgstr "Changer d'échelle" #: f.warp.cc:2869 msgid "select areas first" msgstr "en premier lieu, sélectionner les zones" #: f.warp.cc:3088 f.widgets.cc:565 msgid "Make Waves" msgstr "Créer des vagues" #: f.warp.cc:3095 msgid "wavelength" msgstr "longueur d'une vague" #: f.warp.cc:3097 msgid "variance" msgstr "variation" #: f.warp.cc:3108 msgid "perspective" msgstr "perspective" #: f.warp.cc:3285 f.warp.cc:3321 f.widgets.cc:566 msgid "Twist" msgstr "Torsion" #: f.warp.cc:3318 f.warp.cc:3609 f.warp.cc:3835 f.warp.cc:4060 msgid "Drag mouse to set center" msgstr "Cliquer-tirer la souris pour positionner le centre" #: f.warp.cc:3565 msgid "Spherical Projection" msgstr "Projection sphérique" #: f.warp.cc:3792 msgid "Stretch Image" msgstr "Étirer l'image" #: f.warp.cc:4057 f.widgets.cc:569 msgid "Inside-out" msgstr "Dedans-dehors" #: f.warp.cc:4065 f.warp.cc:4314 msgid "Center Hole" msgstr "Ouverture centrale" #: f.warp.cc:4266 msgid "image width must be greater than height" msgstr "la largeur de l'image doit être plus importante que sa hauteur" #: f.warp.cc:4319 msgid "Cut Top" msgstr "Tronquer le sommet" #: f.warp.cc:4324 msgid "Cut Bottom" msgstr "Tronquer la base" #: f.warp.cc:4332 msgid "Reverse R" msgstr "R inversé" #: f.warp.cc:4333 msgid "Theta" msgstr "Thêta" #: f.warp.cc:4545 msgid "Click mouse to change center" msgstr "Clic de souris pour déplacer le centre" #: f.warp.cc:4550 msgid "Rim %" msgstr "Bordure %" #: f.widgets.cc:105 msgid "Album" msgstr "Album" #: f.widgets.cc:107 msgid "TOP" msgstr "HAUT" #: f.widgets.cc:167 msgid "Rename, copy/move, delete, print" msgstr "Renommer, copier/transférer, supprimer, imprimer" #: f.widgets.cc:168 msgid "Thumbnails, bookmarks, albums, slide show" msgstr "Vignettes, signets, albums, diaporama" #: f.widgets.cc:169 msgid "View images by map location" msgstr "Affichage cartographique des images" #: f.widgets.cc:170 msgid "Custom favorites menu" msgstr "Menu des favoris personnalisables" #: f.widgets.cc:171 msgid "Left/right click: previous/next (also arrow keys)" msgstr "Clic gauche/droit: précédent/suivant (également les touches fléchées)" #: f.widgets.cc:172 msgid "Left/right click: larger/smaller image/thumbnails" msgstr "" #: f.widgets.cc:173 msgid "Save modified file as new version or new file" msgstr "" "Enregistrer le fichier modifié comme une nouvelle version ou comme un " "nouveau fichier" #: f.widgets.cc:174 msgid "Metadata: captions, tags, ratings, geotags, search images" msgstr "" "Métadonnées: légendes, étiquettes, évaluations, balises de géolocalisation, " "rechercher dans les images" #: f.widgets.cc:175 msgid "Select areas to edit separately, save, copy and paste" msgstr "" "Sélectionner des zones à éditer séparément, à enregistrer, à copier et à " "coller" #: f.widgets.cc:176 msgid "" "Left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit" msgstr "" "Clic gauche/droit: défaire/refaire 1 opération d'édition \n" " avec la touche A: défaire/refaire toutes les opérations d'édition \n" " avec le clic du milieu: aller à n'importe quelle opération d'édition" #: f.widgets.cc:179 msgid "Image edit basic functions" msgstr "Fonctions d'édition de base de l'image" #: f.widgets.cc:180 msgid "Image repair and enhance" msgstr "Réparation et amélioration de l'image" #: f.widgets.cc:181 msgid "Artistic effects (filters)" msgstr "Effets artistiques (filtres)" #: f.widgets.cc:182 msgid "Image warp, unwarp, transform" msgstr "Distorsions de l'image, transformations" #: f.widgets.cc:183 msgid "HDR, HDF, panorama, stack, mashup" msgstr "HDR, HDF, panorama, pile, agencement d'objets graphiques" #: f.widgets.cc:184 msgid "Batch processing, custom scripts" msgstr "Traitements par lot, scripts personnalisables" #: f.widgets.cc:185 msgid "Image index, user preferences, shortcuts, utilities" msgstr "" "Indexation des images, préférences de l'utilisateur, raccourcis, utilitaires" #: f.widgets.cc:186 msgid "User Guide, recent changes, log file, about" msgstr "Guide de l'utilisateur, changements récents, fichier log, à propos" #: f.widgets.cc:189 msgid "Current File (R-click or key F)" msgstr "Fichier courant (clic droit ou touche F)" #: f.widgets.cc:190 msgid "Open a parallel Fotoxx session" msgstr "Ouvrir une session parallèle de Fotoxx" #: f.widgets.cc:191 msgid "Cycle 2 Prior Files" msgstr "Alterner les 2 fichiers précédents" #: f.widgets.cc:192 msgid "Cycle 3 Prior Files" msgstr "Alterner les 3 fichiers précédents" #: f.widgets.cc:193 msgid "View a 360 degree panorama image file" msgstr "Visualiser un fichier image contenant un panorama à 360 degrés" #: f.widgets.cc:194 msgid "Change file name" msgstr "Changer le nom du fichier" #: f.widgets.cc:195 msgid "View and change file permissions" msgstr "Visualiser et modifier les permissions de fichier" #: f.widgets.cc:196 msgid "Create a blank image" msgstr "Créer une image vide" #: f.widgets.cc:197 msgid "Toggle - blank or restore window" msgstr "Bascule - vider ou restaurer le contenu de la fenêtre" #: f.widgets.cc:198 msgid "Copy or Move file to new location" msgstr "Copier ou transférer un fichier vers un nouvel emplacement" #: f.widgets.cc:199 msgid "Copy file to the desktop" msgstr "Copier un fichier vers le Bureau" #: f.widgets.cc:200 msgid "Copy file to the clipboard" msgstr "Copier un fichier vers le presse-papiers" #: f.widgets.cc:201 msgid "Set file as desktop wallpaper (GNOME)" msgstr "Utiliser le fichier comme papier-peint (GNOME)" #: f.widgets.cc:202 msgid "Show location on Internet map" msgstr "Afficher la localisation sur une carte Internet" #: f.widgets.cc:203 msgid "Delete or trash file" msgstr "Supprimer un fichier ou le mettre à la Corbeille" #: f.widgets.cc:204 msgid "Print the current image" msgstr "Imprimer l'image courante" #: f.widgets.cc:205 msgid "Print current image with adjusted colors" msgstr "Imprimer l'image courante avec des couleurs calibrées" #: f.widgets.cc:206 msgid "Quit Fotoxx" msgstr "Quitter Fotoxx" #: f.widgets.cc:209 msgid "Thumbnail Gallery (R-click or key G)" msgstr "Galerie de vignettes (clic droit ou touche G)" #: f.widgets.cc:210 msgid "Gallery view with thumbnails and basic metadata" msgstr "Vue en galerie avec vignettes et métadonnées de base" #: f.widgets.cc:211 msgid "Gallery view with small thumbnails and file names" msgstr "Vue en galerie avec de petites vignettes et le nom de fichier" #: f.widgets.cc:212 msgid "Gallery of recently viewed image files" msgstr "Galerie des fichiers récemment visualisés" #: f.widgets.cc:213 msgid "Gallery of newest image files" msgstr "Galerie des fichiers les plus récents" #: f.widgets.cc:214 msgid "Jump to beginning [home]" msgstr "Sauter au début [début]" #: f.widgets.cc:215 msgid "Jump to end [end]" msgstr "Sauter à la fin [fin]" #: f.widgets.cc:216 msgid "Set gallery from current image file" msgstr "La galerie du fichier actuel devient la galerie actuelle" #: f.widgets.cc:217 msgid "Change sort order" msgstr "Changer l'ordre de tri" #: f.widgets.cc:218 msgid "List all folders, click any for gallery view" msgstr "" "Lister tous les dossiers, cliquer sur l'un d'entre eux pour en afficher la " "vue en galerie" #: f.widgets.cc:219 msgid "select input files for album, batch, script functions" msgstr "" "sélectionner les fichiers d'entrée pour les albums, les traitements par lot, " "les scripts" #: f.widgets.cc:220 msgid "Set and recall bookmarked image locations" msgstr "Positionner des signets et rappeler les images ainsi repérées" #: f.widgets.cc:221 msgid "Organize images into albums" msgstr "Organiser les images en albums" #: f.widgets.cc:222 msgid "Update albums for new file versions" msgstr "Mettre à jour les albums avec les nouvelles versions des fichiers" #: f.widgets.cc:223 msgid "Mass update album files" msgstr "Mise à jour en masse du contenu des albums" #: f.widgets.cc:224 msgid "Save current gallery as album" msgstr "Enregistrer la galerie courante comme album" #: f.widgets.cc:225 msgid "Start a slide show" msgstr "Démarrer un diaporama" #: f.widgets.cc:228 msgid "Maps (R-click or key M)" msgstr "Cartes (clic droit ou touche M)" #: f.widgets.cc:229 msgid "Open Internet map" msgstr "Ouvrir une carte depuis Internet" #: f.widgets.cc:230 msgid "Choose Internet map source" msgstr "Choisir un serveur de cartes sur Internet" #: f.widgets.cc:231 msgid "Internet map locations" msgstr "Localisation des cartes Internet" #: f.widgets.cc:232 msgid "Open file map" msgstr "Ouvrir un fichier cartographique" #: f.widgets.cc:233 msgid "Choose file map" msgstr "Choisir un fichier cartographique" #: f.widgets.cc:234 msgid "Set map markers for all images or current gallery" msgstr "" "Positionner les marqueurs sur les cartes pour toutes les images ou pour la " "galerie courante" #: f.widgets.cc:237 msgid "List a few key metadata items" msgstr "Lister un certain nombre d'éléments clé de métadonnées" #: f.widgets.cc:238 msgid "List all metadata items" msgstr "Lister tous les éléments de métadonnées" #: f.widgets.cc:239 msgid "Edit image tags/geotags/caption/rating ..." msgstr "" "Modifier les étiquettes/balises de géolocalisation/légendes/évaluations ..." #: f.widgets.cc:240 msgid "Define tags (keywords) used for searching images" msgstr "" "Définir des étiquettes (mots-clé) utilisables pour rechercher dans les images" #: f.widgets.cc:241 msgid "Edit any image metadata" msgstr "Modifier n'importe quelle métadonnée" #: f.widgets.cc:242 msgid "Remove selected image metadata" msgstr "Enlever les métadonnées de l'image sélectionnée" #: f.widgets.cc:243 msgid "Show file name, captions, comments" msgstr "Afficher le nom du fichier, les légendes, les commentaires" #: f.widgets.cc:244 msgid "Find all images for a location [date]" msgstr "Trouver toutes les images pour une localisation donnée [date]" #: f.widgets.cc:245 msgid "Show image counts by month, select and report" msgstr "" "Afficher le nombre d'images par mois, sélectionner et afficher les résultats " "de la recherche" #: f.widgets.cc:246 msgid "Find images meeting search criteria" msgstr "Trouver les images répondant au critère de recherche" #: f.widgets.cc:249 msgid "Select object or area for editing" msgstr "Sélectionner un objet ou une zone pour édition" #: f.widgets.cc:250 msgid "Select hairy or irregular edge" msgstr "Sélectionner un bord plumeux ou irrégulier" #: f.widgets.cc:251 msgid "Find a gap in an area outline" msgstr "Détecter une discontinuité dans le contour d'une zone" #: f.widgets.cc:252 msgid "Show (outline) existing area" msgstr "Affiche les contours d'une zone existante" #: f.widgets.cc:253 msgid "Hide existing area" msgstr "Masquer les contours d'une zone existante" #: f.widgets.cc:254 msgid "Enable area for editing" msgstr "Activer une zone existante pour l'édition" #: f.widgets.cc:255 msgid "Disable area for editing" msgstr "Désactiver une zone existante" #: f.widgets.cc:256 msgid "Reverse existing area" msgstr "Inverser une zone existante" #: f.widgets.cc:257 msgid "Erase existing area" msgstr "Effacer une zone existante" #: f.widgets.cc:258 msgid "Copy area for later pasting into image" msgstr "Copier une zone pour collage ultérieur vers une image" #: f.widgets.cc:259 msgid "Paste previously copied area into image" msgstr "Coller une zone précédemment copiée dans une image" #: f.widgets.cc:260 msgid "Open a file and paste as area into image" msgstr "Ouvrir un fichier et le coller comme une zone dans une image" #: f.widgets.cc:261 msgid "Save area to a file with transparency" msgstr "Enregistrer une zone dans un fichier avec la transparence" #: f.widgets.cc:264 msgid "Trim/Crop margins and/or Rotate" msgstr "Rognage des marges et/ou rotation" #: f.widgets.cc:265 msgid "Auto upright a rotated image based on EXIF data" msgstr "" "Redressement automatique d'une image retournée, basé sur les donnée EXIF" #: f.widgets.cc:266 msgid "Adjust brightness, contrast, color" msgstr "Ajuster la luminosité, le contraste, la couleur" #: f.widgets.cc:267 msgid "Change pixel dimensions" msgstr "Changer la dimension, en pixels" #: f.widgets.cc:268 msgid "Adjust color using RGB or CMY colors" msgstr "Ajuster la couleur en utilisant RVB ou CMJ" #: f.widgets.cc:269 msgid "Adjust color using HSL colors" msgstr "Ajuster la couleur en utilisant TSV" #: f.widgets.cc:270 msgid "Draw on image: text, line/arrow, box, ellipse" msgstr "Dessiner sur une image: texte, ligne/flèche, boîte, ellipse" #: f.widgets.cc:271 msgid "Paint image pixels using the mouse" msgstr "Peindre des pixels dans l'image en utilisant la souris" #: f.widgets.cc:272 msgid "Copy pixels within an image using the mouse" msgstr "Copier des pixels à l'intérieur d'une image en utilisant la souris" #: f.widgets.cc:273 msgid "Copy pixels from one image to another using the mouse" msgstr "" "Copier des pixels depuis une image vers une autre en utilisant la souris" #: f.widgets.cc:274 msgid "Paint edit function gradually with mouse" msgstr "Peindre une fonction d'édition graduellement avec la souris" #: f.widgets.cc:275 msgid "Incrementally undo prior edits gradually with mouse" msgstr "" "Défaire incrémentalement les opérations d'édition, graduellement avec la " "souris" #: f.widgets.cc:276 msgid "Edit plugins menu or run a plugin function" msgstr "Éditer le menu des greffons ou lancer un greffon en tant que fonction" #: f.widgets.cc:277 msgid "Specialized program for editing RAW files" msgstr "Programme spécialisé dans le traitement des fichiers RAW" #: f.widgets.cc:280 f.widgets.cc:281 msgid "Fast auto enhance that may work OK" msgstr "Amélioration rapide pouvant se révéler intéressante" #: f.widgets.cc:282 msgid "Edit brightness distribution" msgstr "Modifier la distribution de la luminosité" #: f.widgets.cc:283 msgid "Magnify brightness gradients to enhance details" msgstr "Amplifier les gradients de luminosité pour améliorer les détails" #: f.widgets.cc:284 msgid "Flatten brightness distribution to enhance details" msgstr "Aplatir la distribution de la luminosité pour améliorer les détails" #: f.widgets.cc:285 msgid "Rescale RGB - reduce color caste and fog/haze" msgstr "Rééchelonner RVB - réduire une dominante colorée et la brume/le halo" #: f.widgets.cc:286 msgid "Make the image look sharper" msgstr "Rendre l'image plus nette" #: f.widgets.cc:287 msgid "Blur the image, different methods" msgstr "Flouter une image par différentes méthodes" #: f.widgets.cc:288 msgid "Filter noise from low-light photos" msgstr "Filtrer le bruit d'une image faiblement éclairée" #: f.widgets.cc:289 msgid "Fix red-eyes from electronic flash" msgstr "Corriger l'effet yeux rouges dû au flash" #: f.widgets.cc:290 msgid "Match colors on one image with another" msgstr "Faire se correspondre les couleurs d'une image avec celles d'une autre" #: f.widgets.cc:291 msgid "Remove unwanted objects" msgstr "Enlever les objets indésirables" #: f.widgets.cc:292 msgid "Fix color fringes in outer areas of an image" msgstr "Supprimer les franges colorées dans les zones extérieures d'une image" #: f.widgets.cc:293 msgid "Fix color band on dark/bright feature edges" msgstr "Supprimer la bande colorée au bord d'un élément" #: f.widgets.cc:294 msgid "Change brightness or color radially" msgstr "Changer la luminosité ou la couleur radialement" #: f.widgets.cc:295 msgid "Remove dust spots from scanned slides" msgstr "Enlever les points de poussière d'images scannées" #: f.widgets.cc:298 msgid "Convert to simulated sketch" msgstr "Convertir en une imitation de croquis" #: f.widgets.cc:299 msgid "Convert image into a cartoon drawing" msgstr "Convertir l'image en une imitation de cartoon" #: f.widgets.cc:300 msgid "Convert to line drawing (edge detection)" msgstr "Convertir en un dessin au trait (detection des bords)" #: f.widgets.cc:301 msgid "Create an embossed or 3D appearance" msgstr "Créer une apparence gaufrée ou 3D" #: f.widgets.cc:302 msgid "Convert to square tiles" msgstr "Convertir en tuiles carrées" #: f.widgets.cc:303 msgid "Convert to dithered dots" msgstr "Convertir en trames de points variées" #: f.widgets.cc:304 msgid "Convert into a simulated painting" msgstr "Convertir en une imitation de peinture" #: f.widgets.cc:305 msgid "Add texture to an image" msgstr "Ajouter une texture à une image" #: f.widgets.cc:306 msgid "Tile image with a repeating pattern" msgstr "Transformer l'image en un motif de tuiles répété" #: f.widgets.cc:307 msgid "Create a mosaic with tiles made from all images" msgstr "Créer une mosaïque avec des tuiles provenant de toutes les images" #: f.widgets.cc:308 msgid "Make BW/color, negative/positive, sepia" msgstr "Rendu en NB/couleur, négatif/positif, sépia" #: f.widgets.cc:309 msgid "Reduce color depth (posterize)" msgstr "Réduire la profondeur de couleur (postérisation)" #: f.widgets.cc:310 msgid "Shift/convert colors into other colors" msgstr "Décaler/convertir les couleurs en d'autres couleurs" #: f.widgets.cc:311 msgid "Change color hue using an algorithm" msgstr "Modifier la teinte de la couleur en utilisant un algorithme" #: f.widgets.cc:312 msgid "Add a brightness/color ramp across the image" msgstr "Ajouter une rampe de luminosité/couleur au travers de l'image" #: f.widgets.cc:313 msgid "Paint image transparency using the mouse" msgstr "Peindre la transparence de l'image avec la souris" #: f.widgets.cc:314 msgid "Mirror image horizontally or vertically" msgstr "Refléter une image horizontalement ou verticalement" #: f.widgets.cc:315 msgid "Process an image using a custom kernel" msgstr "Traiter une image en utilisant une matrice personnalisée" #: f.widgets.cc:318 msgid "Remove curvature, esp. panoramas" msgstr "Effacer la courbure, spécialement dans les panoramas" #: f.widgets.cc:319 msgid "Straighten objects seen from an angle" msgstr "Redresser la perspective d'objets vus sous un certain angle" #: f.widgets.cc:320 msgid "Distort image areas using the mouse" msgstr "Distordre des zones de l'image avec la souris" #: f.widgets.cc:321 msgid "Unwarp closeup face photo to remove distortion" msgstr "" "Corriger la déformation d'une photo de visage en gros plan pour en éliminer " "la distorsion" #: f.widgets.cc:322 f.widgets.cc:323 f.widgets.cc:324 msgid "Distort the whole image using the mouse" msgstr "Distordre l'image entière avec la souris" #: f.widgets.cc:325 msgid "Flatten a photographed book page" msgstr "Aplatir une page de livre photographiée" #: f.widgets.cc:326 msgid "Rescale image outside selected areas" msgstr "Changer l'échelle de l'image à l'extérieur des zones sélectionnées" #: f.widgets.cc:327 msgid "Warp an image with a wave pattern" msgstr "Déformer une image avec un motif de vague" #: f.widgets.cc:328 msgid "Twist image centered at mouse position" msgstr "Tordre l'image autour de son centre à la position de la souris" #: f.widgets.cc:329 msgid "Make a spherical projection of an image" msgstr "Créer une projection sphérique d'une image" #: f.widgets.cc:330 msgid "Image scale increases from center to edge" msgstr "L'échelle de l'image augmente depuis le centre vers le bord" #: f.widgets.cc:331 msgid "Turn an image inside-out" msgstr "Retourner une image dedans-dehors" #: f.widgets.cc:332 msgid "Convert an image into a Tiny Planet" msgstr "Convertir une image en une Planète Minuscule" #: f.widgets.cc:333 msgid "Generate an inward spiraling recursive image" msgstr "Générer une image spiralant récursivement vers l'intérieur" #: f.widgets.cc:336 msgid "Combine bright/dark images for better detail" msgstr "Combiner des images claires/sombres pour accentuer les détails" #: f.widgets.cc:337 msgid "Combine near/far focus images for deeper focus" msgstr "" "Créer une grande profondeur de champ en combinant des images différemment " "focalisées" #: f.widgets.cc:338 msgid "Combine images to erase passing people, etc." msgstr "Combiner des images pour effacer les gens, etc." #: f.widgets.cc:339 msgid "Combine noisy images into a low-noise image" msgstr "Combiner des images fortement grainées en une image à faible grain" #: f.widgets.cc:340 msgid "Combine image layers, mouse select and expose" msgstr "" "Combiner des images sur différentes couches, sélectionner avec la souris et " "mélanger" #: f.widgets.cc:341 msgid "Compare two images separated by sliding boundary" msgstr "Comparer deux images séparées par une limite glissante" #: f.widgets.cc:342 msgid "Show differences between two images" msgstr "Afficher les différences entre deux images" #: f.widgets.cc:343 msgid "Combine images into a panorama" msgstr "Combiner des images en un panorama" #: f.widgets.cc:344 msgid "Combine images into a vertical panorama" msgstr "Combiner des images en un panorama vertical" #: f.widgets.cc:345 msgid "Combine images into a panorama (panorama tools)" msgstr "Combiner des images en un panorama (panorama tools)" #: f.widgets.cc:346 msgid "Combine images into a montage of images" msgstr "Combiner des images en un montage" #: f.widgets.cc:347 msgid "Arrange images and text in a layout (montage)" msgstr "Créer un agencement d'éléments d'images et de texte (mise en page)" #: f.widgets.cc:350 msgid "Rename/convert/resize/move multiple files" msgstr "Renommer/convertir/redimensionner/transférer de multiples fichiers" #: f.widgets.cc:351 msgid "Upright multiple rotated image files" msgstr "Redresser de multiples fichiers image" #: f.widgets.cc:352 msgid "Delete or Trash multiple files" msgstr "Supprimer ou mettre à la corbeille de multiples fichiers" #: f.widgets.cc:353 msgid "Convert camera RAW files to tiff/png/jpeg" msgstr "Convertir des fichiers RAW vers les formats TIFF/PNG/JPEG" #: f.widgets.cc:354 msgid "Burn selected image files to DVD/BlueRay disc" msgstr "Graver les fichiers image sélectionnés sur un disque DVD/BluRay" #: f.widgets.cc:356 msgid "Export selected image files to a folder" msgstr "Exporter les fichiers image sélectionnés vers un dossier" #: f.widgets.cc:357 msgid "Add/remove tags for multiple images" msgstr "Ajouter/effacer les légendes pour de multiples images" #: f.widgets.cc:358 msgid "Convert tag names for all images" msgstr "Convertir le nom des étiquettes pour toutes les images" #: f.widgets.cc:359 msgid "change or shift photo dates/times" msgstr "mise à jour ou décalage de la date/de l'heure de photos" #: f.widgets.cc:360 msgid "Add/change/delete metadata for multiple images" msgstr "Ajouter/modifier/supprimer des métadonnées pour de multiples images" #: f.widgets.cc:361 msgid "Report metadata for multiple images" msgstr "Afficher un état des métadonnées pour de multiples images" #: f.widgets.cc:362 msgid "Add/revise geotags for multiple images" msgstr "Ajouter/revoir les balises de géolocalisation pour de multiples images" #: f.widgets.cc:363 msgid "Build a custom script with multiple edit functions" msgstr "" "Créer un script personnalisé comprenant de multiples fonctions d'édition" #: f.widgets.cc:364 msgid "Run custom script to edit the current image file" msgstr "Exécuter un script personnalisé pour éditer le fichier image courant" #: f.widgets.cc:365 msgid "Run custom script to edit a batch of image files" msgstr "" "Exécuter un script personnalisé pour éditer de multiples fichiers image" #: f.widgets.cc:368 msgid "Index new files and make thumbnails" msgstr "Indexer les nouveaux fichiers et créer les vignettes" #: f.widgets.cc:369 msgid "Quick incremental index update" msgstr "Mise à jour inrémentale rapide de l'index" #: f.widgets.cc:370 msgid "Move Fotoxx home folder" msgstr "Transférer le dossier d'origine de Fotoxx" #: f.widgets.cc:371 msgid "User preferences and settings" msgstr "Préférences de l'utilisateur et paramètres" #: f.widgets.cc:373 msgid "Show RGB brightness distribution" msgstr "Afficher la distribution de la luminosité RVB" #: f.widgets.cc:374 msgid "Magnify image around the mouse position" msgstr "Agrandir l'image autour de la position de la souris" #: f.widgets.cc:375 msgid "Search all image files and report duplicates" msgstr "Chercher dans tous les fichiers image et afficher les doublons" #: f.widgets.cc:376 msgid "Show RGB colors at mouse click" msgstr "Afficher les couleurs RVB au clic de la souris" #: f.widgets.cc:377 msgid "Convert to another color profile" msgstr "Convertir vers un autre profil de couleur" #: f.widgets.cc:378 msgid "Calibrate printer colors" msgstr "Calibrer les couleurs de l'imprimante" #: f.widgets.cc:379 msgid "Show or revise grid lines" msgstr "Afficher ou paramétrer les lignes de la grille" #: f.widgets.cc:380 msgid "Change color of foreground lines" msgstr "Changer la couleur des lignes de premier plan" #: f.widgets.cc:381 msgid "Highlight darkest and brightest pixels" msgstr "" "Mettre en surbrillance les pixels les plus sombres et les pixels les plus " "clairs" #: f.widgets.cc:382 msgid "map raw pixel bias (camera sensor, vignette)" msgstr "" "Déterminer la polarisation des pixels RAW (capteur de l'appareil photo, " "vignette)" #: f.widgets.cc:383 msgid "map raw dead pixels (camera sensor)" msgstr "" "Déterminer la localisation des pixels morts (capteur de l'appareil photo)" #: f.widgets.cc:384 msgid "Chart to adjust monitor color" msgstr "Charte pour ajuster la couleur du moniteur" #: f.widgets.cc:385 msgid "Chart to adjust monitor gamma" msgstr "Charte pour ajuster le gamma du moniteur" #: f.widgets.cc:386 msgid "Change the GUI language" msgstr "Changer la langue de l'interface graphique" #: f.widgets.cc:387 msgid "Report missing translations" msgstr "Lister les traductions manquantes" #: f.widgets.cc:388 msgid "Anonymous usage statistics" msgstr "Statistiques d'utilisation anonymes" #: f.widgets.cc:389 msgid "Memory and CPU (to terminal/logfile)" msgstr "Mémoire et processeur (vers le terminal/vers un fichier log)" #: f.widgets.cc:390 msgid "List files included in appimage container" msgstr "Lister les fichiers inclus dans un conteneur AppImage" #: f.widgets.cc:391 msgid "test crash report with source line numbers" msgstr "rapport de crash de test comprenant les numéros de ligne" #: f.widgets.cc:394 msgid "Read the user guide" msgstr "Lire le guide utilisateur" #: f.widgets.cc:395 msgid "Recent user guide changes" msgstr "Changements récents dans le guide de l'utilisateur" #: f.widgets.cc:396 msgid "Overview of all edit functions" msgstr "Vue d'ensemble de toutes les fonctions d'édition" #: f.widgets.cc:397 msgid "List updates by Fotoxx version" msgstr "Liste des changements par version de Fotoxx" #: f.widgets.cc:398 msgid "View the log file and error messages" msgstr "Visualiser le fichier log et les messages d'erreur" #: f.widgets.cc:399 msgid "Fotoxx Man Page - summary of capabilities" msgstr "Page de manuel de Fotoxx - résumé des possibilités" #: f.widgets.cc:400 msgid "List command line parameters" msgstr "Lister les paramètres de la ligne de commande" #: f.widgets.cc:401 msgid "How to do Fotoxx translations" msgstr "Comment traduire Fotoxx" #: f.widgets.cc:402 msgid "Show the Fotoxx web page" msgstr "Affiche la page web de Fotoxx" #: f.widgets.cc:403 msgid "Fotoxx license - terms of use" msgstr "Licence de Fotoxx - conditions d'utilisation" #: f.widgets.cc:404 msgid "Fotoxx privacy policy" msgstr "Politique de confidentialité de Fotoxx" #: f.widgets.cc:405 msgid "Version, contact, credits" msgstr "Version, contact, crédits" #: f.widgets.cc:425 msgid "File View F" msgstr "Vue par fichier F" #: f.widgets.cc:426 msgid "New Session" msgstr "Nouvelle session" #: f.widgets.cc:427 f.widgets.cc:453 msgid "Source Folder" msgstr "Dossier source" #: f.widgets.cc:428 msgid "Cycle 2" msgstr "Alterner 2" #: f.widgets.cc:429 msgid "Cycle 3" msgstr "Alterner 3" #: f.widgets.cc:430 msgid "View 360° Pano" msgstr "Afficher un panorama à 360°" #: f.widgets.cc:431 f.widgets.cc:820 fotoxx.h:1414 msgid "Rename" msgstr "Renommer" #: f.widgets.cc:433 msgid "Blank Image" msgstr "Image vide" #: f.widgets.cc:435 f.widgets.cc:822 msgid "Copy/Move" msgstr "Copier/Transférer" #: f.widgets.cc:436 f.widgets.cc:823 msgid "Copy to Desktop" msgstr "Copier vers le Bureau" #: f.widgets.cc:437 f.widgets.cc:824 msgid "Copy to Clipboard" msgstr "Copier vers le presse-papiers" #: f.widgets.cc:438 msgid "Set Wallpaper" msgstr "Utiliser comme papier-peint" #: f.widgets.cc:439 f.widgets.cc:839 msgid "Show on Map" msgstr "Afficher sur une carte" #: f.widgets.cc:440 f.widgets.cc:840 msgid "Delete/Trash" msgstr "Supprimer/Corbeille" #: f.widgets.cc:441 msgid "Print" msgstr "Imprimer" #: f.widgets.cc:442 msgid "Print Calibrated" msgstr "Imprimer avec calibration" #: f.widgets.cc:443 fotoxx.h:1407 msgid "Quit" msgstr "Quitter" #: f.widgets.cc:446 msgid "Gallery View G" msgstr "Vue en galerie G" #: f.widgets.cc:447 msgid "Meta View" msgstr "Vue avec les métadonnées" #: f.widgets.cc:448 msgid "List View" msgstr "Vue en liste" #: f.widgets.cc:449 msgid "Recent" msgstr "Récent" #: f.widgets.cc:450 msgid "Newest" msgstr "Le plus récent" #: f.widgets.cc:451 msgid "GoTo First" msgstr "Sauter au premier" #: f.widgets.cc:452 msgid "GoTo Last" msgstr "Sauter au dernier" #: f.widgets.cc:454 msgid "Sort Gallery" msgstr "Trier la galerie" #: f.widgets.cc:455 msgid "All Folders" msgstr "Tous les dossiers" #: f.widgets.cc:456 fotoxx.h:1425 msgid "Select Files" msgstr "Sélectionner des fichiers" #: f.widgets.cc:459 msgid "Update Albums" msgstr "Mettre à jour les albums" #: f.widgets.cc:461 msgid "Gallery to Album" msgstr "Galerie vers album" #: f.widgets.cc:465 msgid "Map View M" msgstr "Vue cartographique M" #: f.widgets.cc:466 msgid "Net Map" msgstr "Carte Internet" #: f.widgets.cc:467 msgid "Net Source" msgstr "Source des cartes Internet" #: f.widgets.cc:468 msgid "Net Locs" msgstr "Localisations des cartes" #: f.widgets.cc:469 msgid "File Map" msgstr "Fichier cartographique" #: f.widgets.cc:470 msgid "Choose Map" msgstr "Choisir une carte" #: f.widgets.cc:471 msgid "Markers" msgstr "Marqueurs" #: f.widgets.cc:474 f.widgets.cc:816 msgid "View Meta" msgstr "Visualiser les métadonnées" #: f.widgets.cc:475 f.widgets.cc:817 msgid "View All Meta" msgstr "Visualiser toutes les métadonnées" #: f.widgets.cc:476 f.widgets.cc:818 msgid "Edit Meta" msgstr "Éditer les métadonnées" #: f.widgets.cc:478 f.widgets.cc:819 msgid "Edit Any Meta" msgstr "Éditer n'importe quelle métadonnée" #: f.widgets.cc:479 msgid "Delete Meta" msgstr "Supprimer des métadonnées" #: f.widgets.cc:480 msgid "Captions" msgstr "Légendes" #: f.widgets.cc:481 msgid "Places/Dates" msgstr "Emplacements/Dates" #: f.widgets.cc:482 msgid "Timeline" msgstr "Calendrier" #: f.widgets.cc:483 fotoxx.h:1422 msgid "Search" msgstr "Rechercher" #: f.widgets.cc:486 fotoxx.h:1424 msgid "Select" msgstr "Sélectionner" #: f.widgets.cc:488 msgid "Find Gap" msgstr "Trouver une discontinuité" #: f.widgets.cc:489 fotoxx.h:1426 msgid "Show" msgstr "Afficher" #: f.widgets.cc:490 fotoxx.h:1367 msgid "Hide" msgstr "Masquer" #: f.widgets.cc:491 fotoxx.h:1351 msgid "Enable" msgstr "Activer" #: f.widgets.cc:492 fotoxx.h:1346 msgid "Disable" msgstr "Désactiver" #: f.widgets.cc:494 fotoxx.h:1333 msgid "Clear" msgstr "Effacer" #: f.widgets.cc:495 fotoxx.h:1340 msgid "Copy" msgstr "Copier" #: f.widgets.cc:496 fotoxx.h:1399 msgid "Paste" msgstr "Coller" #: f.widgets.cc:497 fotoxx.h:1376 msgid "Load" msgstr "Charger" #: f.widgets.cc:498 f.widgets.cc:654 fotoxx.h:1421 zfuncs.cc:12499 msgid "Save" msgstr "Enregistrer" #: f.widgets.cc:507 msgid "Markup" msgstr "Marquage" #: f.widgets.cc:508 msgid "Paint Image" msgstr "Peindre l'image" #: f.widgets.cc:509 msgid "Copy Pixels 1" msgstr "Copier des pixels 1" #: f.widgets.cc:510 msgid "Copy Pixels 2" msgstr "Copier des pixels 2" #: f.widgets.cc:513 msgid "Plugins" msgstr "Greffons" #: f.widgets.cc:514 f.widgets.cc:838 msgid "Raw Therapee" msgstr "RawTherapee" #: f.widgets.cc:517 msgid "Voodoo 1" msgstr "Vaudou 1" #: f.widgets.cc:518 msgid "Voodoo 2" msgstr "Vaudou 2" #: f.widgets.cc:519 f.widgets.cc:834 msgid "Brite Dist" msgstr "Distribution de la luminosité" #: f.widgets.cc:520 f.widgets.cc:836 msgid "Gradients" msgstr "Gradients" #: f.widgets.cc:521 f.widgets.cc:835 fotoxx.h:1360 msgid "Flatten" msgstr "Aplatir" #: f.widgets.cc:522 msgid "Global Retx" msgstr "Retinex global" #: f.widgets.cc:523 msgid "Zonal Retx" msgstr "Retinex zonal" #: f.widgets.cc:526 msgid "Denoise" msgstr "Réduction du bruit" #: f.widgets.cc:527 msgid "Red Eyes" msgstr "Yeux rouges" #: f.widgets.cc:528 msgid "Match Colors" msgstr "Faire se correspondre des couleurs" #: f.widgets.cc:530 msgid "Chromatic1" msgstr "Aberration chromatique 1" #: f.widgets.cc:531 msgid "Chromatic2" msgstr "Aberration chromatique 2" #: f.widgets.cc:536 msgid "Sketch" msgstr "Croquis" #: f.widgets.cc:537 msgid "Cartoon" msgstr "Cartoon" #: f.widgets.cc:541 msgid "Dither" msgstr "Tramages" #: f.widgets.cc:543 msgid "Texture" msgstr "Texture" #: f.widgets.cc:545 msgid "Mosaic" msgstr "Mosaïque" #: f.widgets.cc:547 msgid "Color Depth" msgstr "Profondeur de couleur" #: f.widgets.cc:550 msgid "Brite Ramp" msgstr "Rampe de la luminosité" #: f.widgets.cc:551 msgid "Paint Transp" msgstr "Peinture avec de la transparence" #: f.widgets.cc:552 msgid "Mirror" msgstr "Miroir" #: f.widgets.cc:557 msgid "Perspective" msgstr "Perspective" #: f.widgets.cc:563 msgid "Flatten Book" msgstr "Aplatir un livre" #: f.widgets.cc:567 msgid "Sphere" msgstr "Sphère" #: f.widgets.cc:568 msgid "Stretch" msgstr "Étirer" #: f.widgets.cc:570 msgid "Tiny Planet" msgstr "Planète Minuscule" #: f.widgets.cc:571 msgid "Escher Spiral" msgstr "Spirale d'Escher" #: f.widgets.cc:576 msgid "Stack/Paint" msgstr "Empilement/Peinture" #: f.widgets.cc:577 msgid "Stack/Noise" msgstr "Empilement/Débruitage" #: f.widgets.cc:578 msgid "Stack/Layer" msgstr "Empilement/Couches" #: f.widgets.cc:579 msgid "Stack/Slider" msgstr "Empilement/Limite coulissante" #: f.widgets.cc:580 msgid "Image Diffs" msgstr "Différences entre des images" #: f.widgets.cc:581 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:582 msgid "V. Panorama" msgstr "Panorama vertical" #: f.widgets.cc:583 msgid "PT Panorama" msgstr "PT Panorama" #: f.widgets.cc:584 msgid "Mashup" msgstr "Agencement d'objets graphiques" #: f.widgets.cc:585 msgid "Montage" msgstr "Montage" #: f.widgets.cc:591 msgid "Batch RAW" msgstr "Traitement RAW par lot" #: f.widgets.cc:592 msgid "Burn DVD/BlueRay" msgstr "Graver un DVD/BluRay" #: f.widgets.cc:593 msgid "Export File List" msgstr "Exporter une liste de fichiers" #: f.widgets.cc:595 msgid "Batch Tags" msgstr "Légendes par lot" #: f.widgets.cc:597 msgid "Batch Photo Date" msgstr "Date de photos par lot" #: f.widgets.cc:598 msgid "Batch Change Meta" msgstr "Modifier des métadonnées par lot" #: f.widgets.cc:599 msgid "Batch Report Meta" msgstr "État des métadonnées par lot" #: f.widgets.cc:601 msgid "Edit Script" msgstr "Créer/modifier un script" #: f.widgets.cc:602 msgid "Run Script" msgstr "Exécuter un script" #: f.widgets.cc:606 msgid "Index Files" msgstr "Indexation des fichiers" #: f.widgets.cc:607 msgid "Quick Index" msgstr "Indexation rapide" #: f.widgets.cc:608 msgid "Move Fotoxx Home" msgstr "Transférer le dossier d'origine de Fotoxx" #: f.widgets.cc:609 msgid "Preferences" msgstr "Préférences" #: f.widgets.cc:610 msgid "KB Shortcuts" msgstr "Raccourcis clavier" #: f.widgets.cc:611 msgid "RGB Distribution" msgstr "Distribution des valeurs RVB" #: f.widgets.cc:613 msgid "Find Duplicates" msgstr "Trouver les doublons" #: f.widgets.cc:615 msgid "Color Profile" msgstr "Profil de couleur" #: f.widgets.cc:619 msgid "Dark/Bright Pixels" msgstr "Pixels sombres/brillants" #: f.widgets.cc:620 msgid "Map Pixel Bias" msgstr "Déterminer la polarisation du pixel" #: f.widgets.cc:621 msgid "Map Dead Pixels" msgstr "Localiser les pixels morts" #: f.widgets.cc:622 msgid "Monitor Color" msgstr "Couleur du moniteur" #: f.widgets.cc:623 msgid "Monitor Gamma" msgstr "Gamma du moniteur" #: f.widgets.cc:624 msgid "Change Language" msgstr "Changer la langue" #: f.widgets.cc:625 msgid "Missing Translations" msgstr "Traductions manquantes" #: f.widgets.cc:626 zfuncs.cc:5885 msgid "Phone Home" msgstr "Appel à la maison" #: f.widgets.cc:628 msgid "Show Resources" msgstr "Afficher les ressources" #: f.widgets.cc:629 msgid "Appimage Files" msgstr "Fichiers dans le conteneur AppImage" #: f.widgets.cc:630 msgid "Zappcrash Test" msgstr "Test de la fonction Zappcrash" #: f.widgets.cc:649 msgid "Gallery" msgstr "Galerie" #: f.widgets.cc:650 msgid "Maps" msgstr "Cartes" #: f.widgets.cc:651 f.widgets.cc:1145 msgid "Favorites" msgstr "Favoris" #: f.widgets.cc:652 msgid "Prev/Next" msgstr "Préc./Suiv." #: f.widgets.cc:653 msgid "Zoom/±" msgstr "Zoom/±" #: f.widgets.cc:655 msgid "Meta" msgstr "Métadonnées" #: f.widgets.cc:656 msgid "Areas" msgstr "Zones" #: f.widgets.cc:657 msgid "Undo/Redo" msgstr "Défaire/Refaire" #: f.widgets.cc:658 fotoxx.h:1350 msgid "Edit" msgstr "Édition" #: f.widgets.cc:659 msgid "Enhance" msgstr "Amélioration" #: f.widgets.cc:660 msgid "Effects" msgstr "Effets" #: f.widgets.cc:662 msgid "Combine" msgstr "Combiner" #: f.widgets.cc:663 msgid "Process" msgstr "Traitements" #: f.widgets.cc:664 msgid "Tools" msgstr "Outils" #: f.widgets.cc:815 msgid "Popup Image" msgstr "Image contextuelle" #: f.widgets.cc:825 msgid "Add Selected Files Here" msgstr "Ajouter les fichiers sélectionnés à cet endroit" #: f.widgets.cc:826 msgid "Add Current File Here" msgstr "Ajouter le fichier courant à cet endroit" #: f.widgets.cc:827 msgid "Remove from Album" msgstr "Enlever de l'album" #: f.widgets.cc:837 msgid "Select Area" msgstr "Sélectionner une zone" #: f.widgets.cc:1105 f.widgets.cc:1124 msgid "Fotoxx Image Locations" msgstr "Fotoxx localisations d'image" #: f.widgets.cc:1173 #, c-format msgid "invalid menu name: %s" msgstr "nom de menu non valide: %s" #: fotoxx.cc:257 msgid "read+write" msgstr "lecture+écriture" #: fotoxx.cc:258 msgid "read only" msgstr "lecture seule" #: fotoxx.cc:259 msgid "no access" msgstr "pas d'accès" #: fotoxx.cc:465 msgid "Please install missing programs:" msgstr "Veuillez installer les applications manquantes:" #: fotoxx.cc:594 msgid "Index aborted" msgstr "" #: fotoxx.cc:772 msgid " Defer image file indexing:" msgstr "Différer l'indexation des fichiers image:" #: fotoxx.cc:773 msgid "" " • Fotoxx will start immediately \n" " • View and edit image files will work normally \n" " • Image search, batch and map functions will not work \n" " • Thumbnail galleries will be slow" msgstr "" " • Fotoxx démarrera immédiatement \n" " • La visualisation et l'édition des fichiers image se passeront " "normalement \n" " • La recherche, les traitements par lot et les fonctions cartographiques " "ne fonctionneront pas \n" " • Les galeries de vignettes seront lentes" #: fotoxx.cc:778 msgid " Index image files now:" msgstr "Indexer les fichiers image maintenant:" #: fotoxx.cc:779 msgid "" " • Initial indexing may need considerable time \n" " • Subsequent startups will be fast \n" " • Full functionality will be available \n" " • Thumbnail galleries will be fast" msgstr "" " • L'indexation initiale peut prendre un temps considérable \n" " • Les démarrages suivants seront rapides \n" " • Toutes les fonctionnalités seront disponibles \n" " • Les galeries de vignettes seront rapides" #: fotoxx.cc:784 msgid "" " Indexing time depends on the number of image files and the \n" " speed of your computer. This can be a few hundred to a few \n" " thousand per minute. After indexing is done, startup time \n" " should be quite fast. You can change index options later, \n" " using these menus: Tools > Index and Tools > Preferences. " msgstr "" " Le temps nécessaire à l'indexation dépend du nombre de fichiers image et " "de \n" " la vitesse de votre ordinateur. Il peut s'agir de quelques centaines, et " "jusqu'à quelques \n" " milliers, de fichiers par minute. Une fois l'indexation réalisée, le temps " "de démarrage \n" " sera très rapide. Vous pourrez changer les options d'indexation plus tard, " "en vous \n" " servant de ces menus: Outils > Index des fichiers image et Outils > " "Préférences. " #: fotoxx.cc:791 msgid "" "Main memory is too small to run Fotoxx. \n" "You can try anyway if you wish. \n" " Continue?" msgstr "" #: fotoxx.cc:826 msgid "Fotoxx First Startup" msgstr "Premier démarrage de Fotoxx" #: fotoxx.cc:981 msgid "(reduced)" msgstr "(réduit)" #: fotoxx.cc:982 msgid "area active" msgstr "zone active" #: fotoxx.cc:983 msgid "dialog open" msgstr "dialogue ouvert" #: fotoxx.cc:984 msgid "blocked" msgstr "bloqué" #: fotoxx.cc:1040 msgid "edits" msgstr "opérations d'édition" #: fotoxx.cc:1958 msgid "Show Hidden" msgstr "Montrer les fichiers cachés" #: fotoxx.cc:3168 msgid "Exceed 50 anchor points" msgstr "Excède 50 points d'ancrage" #: fotoxx.cc:3397 msgid "load curve from a file" msgstr "charger une courbe depuis un fichier" #: fotoxx.cc:3464 msgid "curve file is invalid" msgstr "fichier de courbe non valide" #: fotoxx.cc:3478 msgid "save curve to a file" msgstr "enregistrer une courbe dans un fichier" #: fotoxx.cc:3549 #, c-format msgid "" "File cannot be edited \n" " %s" msgstr "" "Le fichier ne peut pas être modifié \n" " %s" #: fotoxx.cc:3562 msgid "Too many edits, please save image" msgstr "Trop d'opérations d'édition, veuillez enregistrer l'image" #: fotoxx.cc:3567 msgid "this function cannot be scripted" msgstr "cette fonction ne peut pas être incluse dans un script" #: fotoxx.cc:3584 msgid "" "Select area will be ignored. \n" "Continue?" msgstr "" "La zone sélectionnée sera ignorée. \n" "Continuer?" #: fotoxx.cc:3590 msgid "" "Select area not active.\n" "Continue?" msgstr "" "La zone sélectionnée n'est pas active.\n" "Continuer?" #: fotoxx.cc:3900 msgid "file data does not fit dialog" msgstr "les données du fichier n'entrent pas dans le dialogue" #: fotoxx.cc:3910 msgid "Save settings to a file" msgstr "Enregistrer les paramètres dans un fichier" #: fotoxx.cc:4297 msgid "This action will discard changes to current image" msgstr "Les modifications faites à l'image courante seront perdues" #: fotoxx.cc:4298 msgid "prior function still active" msgstr "la fonction précédente est toujours active" #: fotoxx.cc:4299 fotoxx.h:1372 msgid "Keep" msgstr "Conserver" #: fotoxx.cc:4300 msgid "Discard" msgstr "Abandonner" #: fotoxx.h:1315 msgid "Add" msgstr "Ajouter" #: fotoxx.h:1316 msgid "Add All" msgstr "Ajouter tout" #: fotoxx.h:1318 msgid "Amount" msgstr "Quantité" #: fotoxx.h:1319 msgid "Angle" msgstr "Angle" #: fotoxx.h:1320 zfuncs.cc:7936 msgid "Apply" msgstr "Appliquer" #: fotoxx.h:1321 msgid "Auto" msgstr "Auto" #: fotoxx.h:1322 msgid "Black" msgstr "Noir" #: fotoxx.h:1323 msgid "Blend Width" msgstr "Blend Width" #: fotoxx.h:1324 msgid "Blue" msgstr "Bleu" #: fotoxx.h:1325 zfuncs.cc:12913 msgid "Bottom" msgstr "Bas" #: fotoxx.h:1327 zfuncs.cc:7950 msgid "Browse" msgstr "Parcourir" #: fotoxx.h:1328 msgid "Calculate" msgstr "Calculer" #: fotoxx.h:1329 zfuncs.cc:7936 zfuncs.cc:12528 zfuncs.cc:12680 msgid "Cancel" msgstr "Annuler" #: fotoxx.h:1330 msgid "Center" msgstr "Centre" #: fotoxx.h:1331 msgid "Change" msgstr "Modifier" #: fotoxx.h:1332 msgid "Choose" msgstr "Choisir" #: fotoxx.h:1334 msgid "click thumbnail to select file" msgstr "cliquer sur la vignette pour sélectionner le fichier" #: fotoxx.h:1335 msgid "Close" msgstr "Fermer" #: fotoxx.h:1336 msgid "Color" msgstr "Couleur" #: fotoxx.h:1337 msgid "COMPLETED" msgstr "TERMINÉ" #: fotoxx.h:1338 msgid "continue" msgstr "continuer" #: fotoxx.h:1341 msgid "Create" msgstr "Créer" #: fotoxx.h:1342 msgid "Curve File:" msgstr "Fichier de courbe:" #: fotoxx.h:1343 msgid "Cut" msgstr "Couper" #: fotoxx.h:1344 msgid "Deband" msgstr "Détendre" #: fotoxx.h:1345 zfuncs.cc:7936 msgid "Delete" msgstr "Supprimer" #: fotoxx.h:1347 msgid "Display" msgstr "Afficher" #: fotoxx.h:1348 msgid "Done" msgstr "Terminé" #: fotoxx.h:1349 msgid "edge" msgstr "bord" #: fotoxx.h:1352 msgid "Erase" msgstr "Effacer" #: fotoxx.h:1353 msgid "Fetch" msgstr "Aller chercher" #: fotoxx.h:1354 msgid "output file already exists" msgstr "le fichier de sortie existe déjà" #: fotoxx.h:1355 msgid "file not found" msgstr "fichier non trouvé" #: fotoxx.h:1356 #, c-format msgid "file not found: %s" msgstr "fichier non trouvé: %s" #: fotoxx.h:1357 #, c-format msgid "%d image files selected" msgstr "%d fichiers image sélectionnés" #: fotoxx.h:1358 msgid "Find" msgstr "Trouver" #: fotoxx.h:1359 msgid "Finish" msgstr "Finir" #: fotoxx.h:1361 msgid "Font" msgstr "Police" #: fotoxx.h:1362 #, c-format msgid "gallery truncated to %d images" msgstr "galerie tronquée à %d images" #: fotoxx.h:1363 msgid "Green" msgstr "Vert" #: fotoxx.h:1364 msgid "Grid" msgstr "Grille" #: fotoxx.h:1365 zfuncs.cc:12943 msgid "Height" msgstr "Hauteur" #: fotoxx.h:1368 zfuncs.cc:12935 msgid "Image" msgstr "Image" #: fotoxx.h:1369 msgid "Images" msgstr "Images" #: fotoxx.h:1370 msgid "Insert" msgstr "Insérer" #: fotoxx.h:1373 zfuncs.cc:12917 msgid "Left" msgstr "Gauche" #: fotoxx.h:1374 msgid "Length" msgstr "Longueur" #: fotoxx.h:1375 msgid "limit" msgstr "limite" #: fotoxx.h:1377 msgid "Magnify" msgstr "Agrandir" #: fotoxx.h:1378 msgid "Make" msgstr "Fabricant" #: fotoxx.h:1379 msgid "Map" msgstr "Carte" #: fotoxx.h:1380 msgid "Match Level:" msgstr "Niveau de correspondance:" #: fotoxx.h:1381 msgid "Max" msgstr "Max" #: fotoxx.h:1382 msgid "Measure" msgstr "Mesure" #: fotoxx.h:1383 msgid "mouse radius" msgstr "rayon de la souris" #: fotoxx.h:1384 msgid "Negative" msgstr "Negatif" #: fotoxx.h:1385 msgid "New" msgstr "Nouveau" #: fotoxx.h:1386 msgid "Next" msgstr "Prochain" #: fotoxx.h:1387 zfuncs.cc:11875 msgid "No" msgstr "Non" #: fotoxx.h:1388 msgid "no write permission" msgstr "pas de permission en écriture" #: fotoxx.h:1389 msgid "no images" msgstr "pas d'images" #: fotoxx.h:1390 msgid "image index disabled" msgstr "" #: fotoxx.h:1391 msgid "no image files selected" msgstr "aucun fichier image sélectionné" #: fotoxx.h:1392 msgid "None" msgstr "Aucun" #: fotoxx.h:1393 msgid "no selection" msgstr "pas de sélection" #: fotoxx.h:1394 msgid "image index not updated" msgstr "l'index des images n'a pas été mis à jour" #: fotoxx.h:1395 msgid "opacity center" msgstr "opacité au centre" #: fotoxx.h:1396 msgid "opacity edge" msgstr "opacité au bord" #: fotoxx.h:1398 msgid "Paint Radius" msgstr "Rayon de la peinture" #: fotoxx.h:1400 msgid "Pause" msgstr "Pause" #: fotoxx.h:1401 msgid "Percent" msgstr "Pourcent" #: fotoxx.h:1403 msgid "Presets" msgstr "Pré-réglages" #: fotoxx.h:1404 msgid "Prev" msgstr "Précédent" #: fotoxx.h:1405 msgid "use previous input" msgstr "utiliser les données précédentes" #: fotoxx.h:1406 msgid "Proceed" msgstr "Poursuivre" #: fotoxx.h:1409 msgid "range" msgstr "plage" #: fotoxx.h:1410 msgid "Red" msgstr "Rouge" #: fotoxx.h:1411 msgid "Redo" msgstr "Refaire" #: fotoxx.h:1412 msgid "Reduce" msgstr "Réduire" #: fotoxx.h:1413 msgid "Remove" msgstr "Enlever" #: fotoxx.h:1415 msgid "Replace" msgstr "Remplacer" #: fotoxx.h:1416 msgid "Reserved" msgstr "Réservé" #: fotoxx.h:1417 msgid "Reset" msgstr "Réinitialiser" #: fotoxx.h:1418 zfuncs.cc:12921 msgid "Right" msgstr "Droit" #: fotoxx.h:1419 msgid "Rotate" msgstr "Rotation" #: fotoxx.h:1420 msgid "Run" msgstr "Lancer" #: fotoxx.h:1423 msgid "Seconds" msgstr "Secondes" #: fotoxx.h:1427 msgid "Size" msgstr "Taille" #: fotoxx.h:1428 msgid "Start" msgstr "Départ" #: fotoxx.h:1429 msgid "Stop" msgstr "Arrêt" #: fotoxx.h:1430 msgid "Strength" msgstr "Force" #: fotoxx.h:1431 msgid "the area is not finished" msgstr "la zone n'est pas finie" #: fotoxx.h:1432 msgid "Threshold" msgstr "Seuil" #: fotoxx.h:1433 zfuncs.cc:12909 msgid "Top" msgstr "Sommet" #: fotoxx.h:1434 msgid "Transparency" msgstr "Transparence" #: fotoxx.h:1435 msgid "Trash" msgstr "Corbeille" #: fotoxx.h:1436 msgid "Trim" msgstr "Rogner" #: fotoxx.h:1437 msgid "Undo All" msgstr "Défaire tout" #: fotoxx.h:1438 msgid "Undo Last" msgstr "Défaire le dernier" #: fotoxx.h:1439 msgid "Undo" msgstr "Défaire" #: fotoxx.h:1440 msgid "Unfinish" msgstr "Annuler le bouclage" #: fotoxx.h:1441 msgid "Update" msgstr "Mettre à jour" #: fotoxx.h:1442 msgid "View" msgstr "Vue" #: fotoxx.h:1443 msgid "Web" msgstr "Web" #: fotoxx.h:1444 msgid "White" msgstr "Blanc" #: fotoxx.h:1445 zfuncs.cc:12939 msgid "Width" msgstr "Largeur" #: fotoxx.h:1446 msgid "x-offset" msgstr "décalage x" #: fotoxx.h:1447 msgid "y-offset" msgstr "décalage y" #: fotoxx.h:1448 zfuncs.cc:11875 msgid "Yes" msgstr "Oui" #: zfuncs.cc:1836 #, c-format msgid "" "create folder? \n" " %s" msgstr "" "créer le dossier? \n" " %s" #: zfuncs.cc:5880 msgid "" "If you permit, a message is occasionally \n" "sent to the web host for usage statistics. \n" "Nothing is retained that can be associated \n" "with a person or computer or location." msgstr "" "Si vous le permettez, un message sera occasionnellement \n" "envoyé au site web pour des statistiques d'utilisation. \n" "Rien ne sera collecté qui puisse être associé avec \n" "une personne, un ordinateur ou un emplacement." #: zfuncs.cc:6743 #, c-format msgid "cannot open file %s" msgstr "impossible d'ouvrir le fichier %s" #: zfuncs.cc:6766 msgid "save text to file" msgstr "enregistrer le texte dans un fichier" #: zfuncs.cc:7936 msgid "edit menu entry" msgstr "ajouter/modifier une entrée de menu" #: zfuncs.cc:7940 msgid "menu text" msgstr "texte du menu" #: zfuncs.cc:7941 msgid "menu func" msgstr "fonction de menu" #: zfuncs.cc:7942 msgid "menu icon" msgstr "icône du menu" #: zfuncs.cc:7943 msgid "icon size" msgstr "taille de l'icône" #: zfuncs.cc:7946 msgid "Bold" msgstr "Gras" #: zfuncs.cc:7953 msgid "close window" msgstr "fermer la fenêtre" #: zfuncs.cc:8000 msgid "select icon" msgstr "sélectionner une icône" #: zfuncs.cc:12000 zfuncs.cc:12896 msgid "cancel" msgstr "annuler" #: zfuncs.cc:12489 msgid "choose file" msgstr "choisir un fichier" #: zfuncs.cc:12494 msgid "choose files" msgstr "choisir les fichiers" #: zfuncs.cc:12505 msgid "choose folder" msgstr "choisir un dossier" #: zfuncs.cc:12510 msgid "choose folders" msgstr "choisir les dossiers" #: zfuncs.cc:12515 msgid "create folder" msgstr "créer un dossier" #: zfuncs.cc:12522 msgid "hidden" msgstr "caché" #: zfuncs.cc:12896 msgid "done" msgstr "terminé" #: zfuncs.cc:12926 msgid "image scale" msgstr "échelle de l'image" #: zfuncs.cc:12928 msgid "percent" msgstr "pourcent" #~ msgid "Left/right click: smaller/larger image/thumbnails" #~ msgstr "Clic gauche ou droit: image ou vignette plus petite ou plus grande" #~ msgid "% of scale" #~ msgstr "% de l'échelle" #~ msgid "Inside-Out" #~ msgstr "Dedans-dehors" #~ msgid "" #~ "Right-click album thumbnail to cut/copy \n" #~ "to cache, insert from cache, or remove." #~ msgstr "" #~ "Clic droit sur une vignette d'album pour couper/copier \n" #~ "vers le cache, insérer depuis le cache, ou enlever." #~ msgid "" #~ "Right-click left/right side of album \n" #~ "thumbnail to insert cached images \n" #~ "before/after the thumbnail." #~ msgstr "" #~ "Clic droit sur le côté gauche/droit d'une vignette \n" #~ "dans un album pour insérer les images en cache \n" #~ "avant/après cette vignette." #~ msgid "Album to view or edit" #~ msgstr "Album à visualiser ou à modifier" #~ msgid "Select images, add to cache" #~ msgstr "Sélectionner des images, ajouter au cache" #~ msgid "Clear image cache" #~ msgstr "Mettre à zéro le cache des images" #~ msgid "Delete or rename an album" #~ msgstr "Supprimer ou renommer un album" #~ msgid "cache added to empty album" #~ msgstr "cache ajouté à un album vide" #~ msgid "" #~ "Delete or Rename? \n" #~ " %s" #~ msgstr "" #~ "Supprimer ou renommer? \n" #~ " %s" #~ msgid "%s deleted" #~ msgstr "%s supprimé" #~ msgid "Update Album Files" #~ msgstr "Mise à jour des fichiers album" #~ msgid "All Albums" #~ msgstr "Tous les albums" #~ msgid "ALL albums chosen" #~ msgstr "TOUS les albums ont été sélectionnés" #~ msgid "Zoom:" #~ msgstr "Zoom:" #~ msgid "Zoom size (x)" #~ msgstr "Taux de zoom (x)" #~ msgid "Steps" #~ msgstr "Étapes" #~ msgid "select ellipse" #~ msgstr "sélectionner une ellipse" #~ msgid "select one matching color within mouse circle:" #~ msgstr "sélectionner une couleur à l'intérieur du cercle de la souris:" #~ msgid "select all matching colors within mouse circle" #~ msgstr "" #~ "sélectionner toutes les couleurs présentes à l'intérieur du cercle de la " #~ "souris" #~ msgid "" #~ "Click one time inside each enclosed area. \n" #~ "Possible gaps in the outline will be found. \n" #~ "Press F1 for help." #~ msgstr "" #~ "Cliquer une fois à l'intérieur de chaque zone concernée. \n" #~ "De possibles discontinuités dans les contours seront détectées. \n" #~ "Presser F1 pour l'aide." #~ msgid "Edge calculation in progress" #~ msgstr "Calcul du bord en progrès" #~ msgid "Area Edge Calc" #~ msgstr "Calcul du bord de la zone" #~ msgid "Trim: drag middle to move, drag corners to resize" #~ msgstr "" #~ "Rogner: tirer le milieu pour déplacer, tirer les coins pour redimensionner" #~ msgid "Minor rotate: drag right edge with mouse" #~ msgstr "Rotation de faible amplitude: tirer le bord droit avec la souris" #~ msgid "trim size:" #~ msgstr "taille du rognage:" #~ msgid "Level" #~ msgstr "Niveau" #~ msgid "Rotate: degrees" #~ msgstr "Rotation: degrés" #~ msgid "Click for white balance" #~ msgstr "Cliquer pour la balance des blancs" #~ msgid "Click for black level" #~ msgstr "Cliquer pour le niveau de noir" #~ msgid "Edge" #~ msgstr "Bord" #~ msgid "" #~ "1. Drag mouse to select. \n" #~ "2. Erase. 3. Repeat. " #~ msgstr "" #~ "1. Tirer la souris pour sélectionner. \n" #~ "2. Effacer. 3. Répéter. " #~ msgid "Color Palette" #~ msgstr "Palette de couleurs" #~ msgid "click on image to choose color" #~ msgstr "cliquer sur l'image pour choisir la couleur" #~ msgid "" #~ "shift + left click: pick image position to copy \n" #~ "left click or drag: copy image to mouse position \n" #~ "right click or drag: restore original image" #~ msgstr "" #~ "maj+clic gauche: choisir dans l'image la position à copiercliquer-tirer " #~ "gauche: copier l'image à la position de la souriscliquer-tirer droit: " #~ "restaurer l'image originale" #~ msgid "Convert Image to Dots" #~ msgstr "Convertir une image en une trame de points" #~ msgid "Open RAW" #~ msgstr "Ouvrir un fichier RAW" #~ msgid "Quick Start" #~ msgstr "Démarrage rapide" #~ msgid "file type not supported: %s" #~ msgstr "format de fichier non supporté: %s" #~ msgid "Sort" #~ msgstr "Trier" #~ msgid "Page Up" #~ msgstr "Page Haut" #~ msgid "Page Down" #~ msgstr "Page Bas" #~ msgid "First" #~ msgstr "Premier" #~ msgid "Last" #~ msgstr "Dernier" #~ msgid "stars range" #~ msgstr "plage d'étoiles" #~ msgid "" #~ "These items are always reported: \n" #~ " date, stars, tags, caption, comment" #~ msgstr "" #~ "Ces éléments sont toujours inclus dans l'état: \n" #~ " date, étoiles, étiquettes, légende, annotations" #~ msgid "create folder? %s" #~ msgstr "créer le dossier? %s" #~ msgid "Invalid folder: %s" #~ msgstr "Dossier non valide: %s" #~ msgid "" #~ "Duplicate folder: \n" #~ " %s \n" #~ "Please remove." #~ msgstr "" #~ "Dossier dupliqué: \n" #~ " %s \n" #~ "Veuillez l'enlever." #~ msgid "Zoomed Image:" #~ msgstr "Image zoomée:" #~ msgid "" #~ "Drag mouse on image. \n" #~ "Left click to cancel. \n" #~ "Key X to toggle dialog." #~ msgstr "" #~ "Tirer la souris sur l'image. \n" #~ "Clic gauche pour annuler. \n" #~ "Touche X pour afficher/masquer le dialogue." #~ msgid "This function is for AppImage package only" #~ msgstr "Cette fonction est valide uniquement pour le fichier AppImage" #~ msgid "" #~ "Completely remove the AppImage package \n" #~ "Press F1 for more information. \n" #~ "Continue?" #~ msgstr "" #~ "Enlever complètement le paquet AppImage \n" #~ "Presser F1 pour plus d'informations. \n" #~ "Continuer?" #~ msgid "drag mouse to set center" #~ msgstr "tirer la souris pour positionner le centre" #~ msgid "World Maps (key W)" #~ msgstr "Cartes du monde (touche W)" #~ msgid "Net Maps (key M)" #~ msgstr "Cartes Internet (touche M)" #~ msgid "Open, Recent, RAW, Rename, Copy, Delete, Print ..." #~ msgstr "Ouvrir, Récent, RAW, Renommer, Copier, Supprimer, Imprimer ..." #~ msgid "Trim, Rotate, Resize, Retouch, Sharpen, Denoise, Text ..." #~ msgstr "" #~ "Rognage, Rotation, Redimensionnement, Retouche, Accentuation de la " #~ "netteté, Réduction du bruit, Texte ..." #~ msgid "Complex edits and image transformations" #~ msgstr "Opérations d'édition complexes et transformation de l'image" #~ msgid "Fix Perspective, Warp or unwarp image ..." #~ msgstr "" #~ "Corriger la perspective, Distordre une image ou en corriger la " #~ "déformation ..." #~ msgid "Quick Start, User Guide, Recent Changes ..." #~ msgstr "" #~ "Guide de démarrage rapide, Guide de l'utilisateur, Changements récents ..." #~ msgid "Sync Gallery, Albums, Slide Show" #~ msgstr "Sync. galerie, albums, diaporama" #~ msgid "Increase thumbnail size" #~ msgstr "Augmenter la taille des vignettes" #~ msgid "Reduce thumbnail size" #~ msgstr "Réduire la taille des vignettes" #~ msgid "Jump to beginning (top)" #~ msgstr "Aller au début (sommet)" #~ msgid "Jump to end (bottom)" #~ msgstr "Aller à la fin (bas)" #~ msgid "previous page" #~ msgstr "page précédente" #~ msgid "Next page" #~ msgstr "Page suivante" #~ msgid "Convert, Export, Metadata, Search ..." #~ msgstr "Convertir, Exporter, Métadonnées, Recherche ..." #~ msgid "Choose a map file" #~ msgstr "Choisir un fichier de carte local" #~ msgid "Save and recall named map locations" #~ msgstr "Enregistrer et rappeler les cartes nommées" #~ msgid "Open new file" #~ msgstr "Ouvrir un nouveau fichier" #~ msgid "Open and edit a camera RAW file" #~ msgstr "Ouvrir et traiter un fichier RAW" #~ msgid "Reduce Chromatic Abberation" #~ msgstr "Réduire l'aberration chromatique" #~ msgid "Convert to dots (Roy Lichtenstein effect)" #~ msgstr "Convertir en points (effet Roy Lichtenstein)" #~ msgid "Completely uninstall AppImage package" #~ msgstr "Désinstaller complètement le paquet AppImage" #~ msgid "Quick Start mini-guide" #~ msgstr "Mini-guide de démarrage rapide" #~ msgid "Update album files to latest version" #~ msgstr "Mise à jour des fichiers album vers la version la plus récente" #~ msgid "Replace album file with another file" #~ msgstr "Remplacer un fichier dans un/des album(s) par un autre fichier" #~ msgid "Sync Gallery" #~ msgstr "Sync. galerie" #~ msgid "Unselect" #~ msgstr "Désélectionner" #~ msgid "Spherical Proj." #~ msgstr "Projection sphérique" #~ msgid "Dots" #~ msgstr "Points" #~ msgid "Run Script Batch" #~ msgstr "Exécuter un script par lot" #~ msgid "Uninstall AppImage" #~ msgstr "Désinstaller AppImage" #~ msgid "World Maps" #~ msgstr "Cartes du monde" #~ msgid "Net Maps" #~ msgstr "Cartes Internet" #~ msgid "Choose Maps" #~ msgstr "Choisir les cartes" #~ msgid "Map Source" #~ msgstr "Source des cartes" #~ msgid "Map Locs" #~ msgstr "Localisations des cartes" #~ msgid "LOW MEMORY - performance may be poor" #~ msgstr "MEMOIRE BASSE - les performances seront mauvaises" #~ msgid "remove %d files from %d albums ?" #~ msgstr "enlever %d fichiers de %d albums ?" #~ msgid "save" #~ msgstr "enregistrer" #~ msgid "user guide not found" #~ msgstr "guide de l'utilisateur non trouvé" #~ msgid "Paste Cache Here (keep)" #~ msgstr "Coller le cache ici (conserver)" #~ msgid "Paste Cache Here (clear)" #~ msgstr "Coller le cache ici (effacer)" #~ msgid "Cut to Cache" #~ msgstr "Couper vers le cache" #~ msgid "Edit 2" #~ msgstr "Édition 2" #~ msgid "Edit 1" #~ msgstr "Édition 1" #~ msgid "Brightness Graph" #~ msgstr "Graphe de la luminosité" #~ msgid "Anti-Alias" #~ msgstr "Anti-alias" #~ msgid "Zonal Colors" #~ msgstr "Couleurs zonales" #~ msgid "Add Lines" #~ msgstr "Ajouter des lignes" #~ msgid "Color Sat" #~ msgstr "Saturation de la couleur" #~ msgid "Map Markers" #~ msgstr "Marqueurs sur les cartes" #~ msgid "Net Map Locs" #~ msgstr "Localisation des cartes Internet" #~ msgid "Map View" #~ msgstr "Vue cartographique" #~ msgid "Zoom-" #~ msgstr "Zoom-" #~ msgid "Zoom+" #~ msgstr "Zoom+" #~ msgid "Gallery View" #~ msgstr "Vue en galerie" #~ msgid "Copy to Cache" #~ msgstr "Copier vers le cache" #~ msgid "Set as Wallpaper" #~ msgstr "Choisir comme papier-peint" #~ msgid "File View" #~ msgstr "Vue par fichier image" #~ msgid "Version, license, contact, credits" #~ msgstr "Version, licence, contact, crédits" #~ msgid "Erase known hot and dark pixels" #~ msgstr "Effacer les pixels chauds et sombres connus" #~ msgid "Show a brightness distribution graph" #~ msgstr "Afficher un graphe de la distribution de la luminosité" #~ msgid "Change Keyboard Shortcut Keys" #~ msgstr "Changer les touches des raccourcis clavier" #~ msgid "Change user preferences" #~ msgstr "Changer le paramétrage utilisateur" #~ msgid "Convert camera RAW files using dcraw or Raw Therapee" #~ msgstr "Convertir des fichiers RAW en utilisant dcraw ou RawTherapee" #~ msgid "Convert to solid color drawing" #~ msgstr "Convertir en un dessin en couleurs" #~ msgid "Smoothen edges with jaggies" #~ msgstr "Adoucir les bords en escalier" #~ msgid "Reduce Chromatic Aberration" #~ msgstr "Réduire l'aberration chromatique" #~ msgid "Adjust color in selected image areas" #~ msgstr "Ajuster la couleur dans des zones sélectionnées de l'image" #~ msgid "Flatten brightness distribution" #~ msgstr "Aplatir la distribution de la luminosité" #~ msgid "Edit brightness distributions" #~ msgstr "Modifier les distributions de la luminosité" #~ msgid "Leverage edits by brightness or color" #~ msgstr "" #~ "Augmenter l'effet d'une fonction d'édition par la luminosité ou la couleur" #~ msgid "Upright a rotated image" #~ msgstr "Redresser une image retournée" #~ msgid "Adjust color intensity (saturation)" #~ msgstr "Ajuster l'intensité de la couleur (saturation)" #~ msgid "Adjust color balance" #~ msgstr "Ajuster la balance de la couleur" #~ msgid "Find images meeting select criteria" #~ msgstr "Trouver les images répondant au critère sélectionné" #~ msgid "(Toggle) show captions and comments" #~ msgstr "(Bascule) afficher/masquer les légendes et les annotations" #~ msgid "Mark all images or current gallery" #~ msgstr "Marquer toutes les images ou bien la galerie courante" #~ msgid "Maps (key M)" #~ msgstr "Cartes (touche M)" #~ msgid "Show current or last used album" #~ msgstr "Afficher l'album courant ou le dernier album utilisé" #~ msgid "Smaller thumbnails [-]" #~ msgstr "Vignettes plus petites [-]" #~ msgid "Bigger thumbnails [+]" #~ msgstr "Agrandir les vignettes [+]" #~ msgid "Thumbnail Gallery (key G)" #~ msgstr "Galerie de vignettes (touche G)" #~ msgid "Copy file to the image cache" #~ msgstr "Copier un fichier vers le cache d'images" #~ msgid "Open a recently added file" #~ msgstr "Ouvrir un fichier nouvellement ajouté" #~ msgid "Open a recently seen file" #~ msgstr "Ouvrir un fichier récemment visualisé" #~ msgid "Current File (key F)" #~ msgstr "Fichier courant (touche F)" #~ msgid "User Guide, Recent Changes ..." #~ msgstr "Guide de l'utilisateur, Changements récents ..." #~ msgid "Index, Settings, Shortcuts, Magnify, utilities ..." #~ msgstr "Index, Paramétrages, Raccourcis clavier, Agrandir, utilitaires ..." #~ msgid "Batch Convert, Metadata, Scripts ..." #~ msgstr "Conversion par lot, Métadonnées, Scripts ..." #~ msgid "HDR, HDF, Panorama, Stack, Mashup" #~ msgstr "" #~ "Image à large gamme dynamique (HDR), à forte profondeur de champ (HDF), " #~ "panorama, pile, agencement d'objets graphiques" #~ msgid "Arty Transforms (filters)" #~ msgstr "Transformations artistiques (filtres)" #~ msgid "Perspective, Warp, Unwarp, Transfigure ..." #~ msgstr "Perspective, Déformer, Corriger la déformation, Transfigurer ..." #~ msgid "Image improvements, Paint, Copy ..." #~ msgstr "Améliorations de l'image, Peindre, Copier ..." #~ msgid "Trim, Rotate, Resize, Retouch, Sharpen, Color, Text ..." #~ msgstr "" #~ "Rognage, Rotation, Redimensionner, Retoucher, Netteté, Couleur, Texte ..." #~ msgid "" #~ "left/right click: undo/redo 1 edit \n" #~ " with key A: undo/redo all edits \n" #~ " middle click: go to any prior edit" #~ msgstr "" #~ "Clic gauche/droit: défaire/refaire 1 opération d'édition \n" #~ " avec la touche A: défaire/refaire toutes les opérations d'édition \n" #~ " clic milieu: aller à n'importe quelle opération d'édition" #~ msgid "Select image areas to edit, save, copy and paste ..." #~ msgstr "" #~ "Sélectionner des zones de l'image à éditer, enregistrer, copier et " #~ "coller ..." #~ msgid "Captions, Tags, Ratings, Geotags, Search ... " #~ msgstr "" #~ "Légendes, Étiquettes, Évaluations, Balises de géolocalisation, " #~ "Recherche ... " #~ msgid "Open previous or next file (left/right mouse click)" #~ msgstr "Ouvrir le fichier précédent ou suivant (clic gauche/droit)" #~ msgid "Favorite Functions" #~ msgstr "Fonctions favorites" #~ msgid "Gallery, Bookmarks, Albums, Slide Show" #~ msgstr "Galerie, Signets, Albums, Diaporama ..." #~ msgid "Open, Recent, Rename, Copy, Delete, Print ..." #~ msgstr "Ouvrir, Récent, Renommer, Copier, Supprimer, Imprimer ..." #~ msgid "Stuck Pixels file" #~ msgstr "Fichier des pixels bloqués" #~ msgid "Load Stuck Pixels" #~ msgstr "Charger les pixels bloqués" #~ msgid "stuck pixels:" #~ msgstr "pixels bloqués:" #~ msgid "pixel group" #~ msgstr "groupe de pixels" #~ msgid "Stuck Pixels" #~ msgstr "Pixels bloqués" #~ msgid " Arrows Previous/Next Image \n" #~ msgstr "Flèches Image précédente/suivante \n" #~ msgid " Delete Delete/Trash dialog \n" #~ msgstr " Suppr Dialogue Supprimer/Corbeille \n" #~ msgid " Escape Quit dialog; Quit Fotoxx \n" #~ msgstr " Échap Quitter Fotoxx \n" #~ msgid " F11 Full Screen without menus \n" #~ msgstr " F11 Vue plein écran sans menus \n" #~ msgid " F10 Full Screen with menus \n" #~ msgstr " F10 Vue plein écran avec menus \n" #~ msgid " F1 User Guide for current function \n" #~ msgstr " F1 Guide de l'utilisateur pour la fonction actuelle \n" #~ msgid "video file types" #~ msgstr "types de fichiers vidéo" #~ msgid "RAW file types" #~ msgstr "types de fichier RAW" #~ msgid "Fotoxx started by file manager" #~ msgstr "Fotoxx démarré par un gestionnaire de fichiers" #~ msgid "Fotoxx started directly" #~ msgstr "Fotoxx démarré directement" #~ msgid "image index level" #~ msgstr "niveau d'indexation des images" #~ msgid "shift image to right margin" #~ msgstr "décaler l'image vers la marge droite" #~ msgid "show last file version only" #~ msgstr "afficher seulement la dernière version du fichier" #~ msgid "Map marker size" #~ msgstr "Taille du marqueur sur les cartes" #~ msgid "Curve node capture distance" #~ msgstr "Distance de capture des noeuds de courbe" #~ msgid "JPEG save quality" #~ msgstr "Qualité d'enregistrement JPEG" #~ msgid "Dialog font" #~ msgstr "Police des dialogues" #~ msgid "Menu Style" #~ msgstr "Style de menu" #~ msgid "Menu Text" #~ msgstr "Texte du menu" #~ msgid "Background color:" #~ msgstr "Couleur d'arrière-plan" #~ msgid "Startup Display" #~ msgstr "Affichage au démarrage" #~ msgid "User Settings" #~ msgstr "Paramétrages utilisateur" #~ msgid "" #~ "script file: %s \n" #~ " %s" #~ msgstr "" #~ "fichier de script: %s \n" #~ " %s" #~ msgid "use Raw Therapee" #~ msgstr "utiliser RawTherapee" #~ msgid "use dcraw" #~ msgstr "utiliser dcraw" #~ msgid "file format not supported: %s \n" #~ msgstr "format de fichier non supporté: %s \n" #~ msgid "file error: %s \n" #~ msgstr "erreur de fichier: %s \n" #~ msgid "Set Map Source" #~ msgstr "Sélectionner une source pour les cartes en ligne" #~ msgid "no changes made" #~ msgstr "aucun changement effectué" #~ msgid "images added: %d removed: %d new count: %d" #~ msgstr "images ajoutées: %d enlevées: %d nouveau décompte: %d" #~ msgid "all versions of matching images" #~ msgstr "toutes les versions des images trouvées" #~ msgid "new set" #~ msgstr "nouveau sous-ensemble" #~ msgid "Batch Photo date/time Update" #~ msgstr "Mise à jour de la date/de l'heure de photos par lot" #~ msgid "create tag categories and tags" #~ msgstr "créer des catégories d'étiquettes et des étiquettes" #~ msgid "montage map file not found: %s" #~ msgstr "le fichier de paramètres du montage est introuvable: %s" #~ msgid "supply a project name" #~ msgstr "fournir un nom de projet" #~ msgid "project name" #~ msgstr "nom du projet" #~ msgid "Mashup layout and background image" #~ msgstr "" #~ "Composition de l'agencement d'objets graphiques et image d'arrière-plan" #~ msgid "select thumbnail" #~ msgstr "sélectionner une vignette" #~ msgid "Image Pixels" #~ msgstr "Taille de l'image en pixels" #~ msgid "exceeded 200 albums" #~ msgstr "la limite de 200 albums est atteinte" #~ msgid "no more images" #~ msgstr "plus d'images" #~ msgid "outside top folders" #~ msgstr "en dehors des dossiers d'origine" #~ msgid "RAW type not registered in User Settings" #~ msgstr "Type RAW non répertorié dans les paramètres utilisateur" #~ msgid "Simulate Painting" #~ msgstr "Simuler une peinture" #~ msgid "load palette first" #~ msgstr "charger d'abord une palette" #~ msgid "custom palette" #~ msgstr "palette personnalisée" #~ msgid "depth" #~ msgstr "profondeur" #~ msgid "Simulate Embossing" #~ msgstr "Simuler un gaufrage" #~ msgid "Bright Areas" #~ msgstr "Valeurs claires" #~ msgid "Color Drawing" #~ msgstr "Dessin en couleurs" #~ msgid "Color Fringes" #~ msgstr "Franges colorées" #~ msgid "" #~ " Adjust each RGB color to minimize \n" #~ " color fringes at the image extremes. " #~ msgstr "" #~ " Ajuster chaque couleur RVB pour minimiser \n" #~ " les franges colorées aux bords extrêmes de l'image. " #~ msgid "Metric:" #~ msgstr "Métrique:" #~ msgid "Local Color" #~ msgstr "Couleur locale" #~ msgid "Click image to select areas." #~ msgstr "Cliquer sur l'image pour sélectionner des zones" #~ msgid "image is too large" #~ msgstr "l'image est d'une taille trop importante" #~ msgid "maximum" #~ msgstr "maximum" #~ msgid "minimum" #~ msgstr "minimum" #~ msgid "Cannot use Lever Edits" #~ msgstr "" #~ "Impossible d'utiliser un effet de levier avec cette fonction d'édition" #~ msgid "Edit Function Amplifier" #~ msgstr "Édition de l'amplificateur d'effet de la fonction" #~ msgid "Lever Edits" #~ msgstr "Effet de levier sur une fonction d'édition" #~ msgid "Upright Image" #~ msgstr "Redresser l'image" #~ msgid "Add lines or arrows to an image" #~ msgstr "Ajouter des lignes ou des flèches à une image" #~ msgid "Add Text to Image" #~ msgstr "Ajouter du texte à l'image" #~ msgid "Color Saturation" #~ msgstr "Saturation de la couleur" #~ msgid "Wavelets" #~ msgstr "Ondelettes" #~ msgid "Top Hat" #~ msgstr "Top Hat" #~ msgid "Median" #~ msgstr "Médiane" #~ msgid "use previous white balance" #~ msgstr "utiliser la balance des blancs précédente" #~ msgid "Cooler" #~ msgstr "Plus froid" #~ msgid "Warmer" #~ msgstr "Plus chaud" #~ msgid "Color Balance" #~ msgstr "Balance de la couleur" #~ msgid "recall previous settings used" #~ msgstr "rappeler les paramètres précédement utilisés" #~ msgid "Bright" #~ msgstr "Zones claires" #~ msgid "High" #~ msgstr "Haut" #~ msgid "Max." #~ msgstr "Max." #~ msgid "Dark Areas" #~ msgstr "Zones sombres" #~ msgid "Low Color" #~ msgstr "Couleur bas" #~ msgid "Amplifier" #~ msgstr "Amplificateur" #~ msgid "use previous settings" #~ msgstr "utiliser les paramètres précédents" #~ msgid "invert width/height" #~ msgstr "inverser la largeur et la hauteur" #~ msgid "use previous size" #~ msgstr "utiliser la taille précédente" #~ msgid "select color in mouse:" #~ msgstr "sélectionner la couleur dans le pointeur de la souris:" #~ msgid "Show image comments" #~ msgstr "Afficher les annotations de l'image" #~ msgid "Show image caption" #~ msgstr "Afficher la légende de l'image" #~ msgid "Wait before caption/comments" #~ msgstr "Attente avant la légende/les commentaires" #~ msgid "use gallery" #~ msgstr "utiliser la galerie" #~ msgid "Replace all with original and cache versions" #~ msgstr "Remplacer tout avec l'original et les versions du cache d'images" #~ msgid "Add cache versions to existing versions" #~ msgstr "Ajouter les versions du cache d'images aux versions existantes" #~ msgid "Replace all versions with cache versions" #~ msgstr "Remplacer toutes les versions avec les versions du cache d'images" #~ msgid "Replace all with cache versions" #~ msgstr "Tout remplacer avec les versions présentes dans le cache d'images" #~ msgid "Process album files matching cache files:" #~ msgstr "Traitement du contenu des albums trouvé dans le cache d'images" #~ msgid "remove %d files from %d albums?" #~ msgstr "enlever %d fichiers de %d albums?" #~ msgid "cache is empty" #~ msgstr "le cache d'images est vide" #~ msgid "Choose Album" #~ msgstr "Choisir un album" #~ msgid "max. album size exceeded %d" #~ msgstr "taille maximum d'un album atteinte %d" #~ msgid "fill from image cache (%d images)" #~ msgstr "remplir depuis le cache des images (%d images)" #~ msgid "Image cache has %d images" #~ msgstr "Le cache des images comprend %d images" #~ msgid "Replace album files or add after" #~ msgstr "Remplacer le contenu des albums ou ajouter après" #~ msgid "Clear the file cache" #~ msgstr "Effacer le contenu du cache d'images" #~ msgid "Remove cache files from albums" #~ msgstr "Enlever des albums les fichiers du cache d'images" #~ msgid "Display cache for drag and drop" #~ msgstr "Afficher le cache d'images pour tirer-déposer" #~ msgid "Select files, add to cache" #~ msgstr "Sélectionner des fichiers, les ajouter au cache d'images" #~ msgid "Choose album to view or edit" #~ msgstr "Choisir l'album à visualiser ou à éditer" #~ msgid "Drag album thumbnail to new position." #~ msgstr "Tirer une vignette d'album vers sa nouvelle position." #~ msgid "" #~ "Right-click thumbnail left/right side \n" #~ "to insert cache before/after thumbnail." #~ msgstr "" #~ "Clic droit sur le côté gauche/droit d'une vignette \n" #~ "pour insérer le contenu du cache d'images avant/après cette vignette." #~ msgid "" #~ "Right-click album thumbnail to \n" #~ "cut or copy to cache, or remove." #~ msgstr "" #~ "Clic droit sur une vignette d'album pour \n" #~ "couper ou copier vers le cache, ou pour enlever du cache." #~ msgid "Select 2 files:" #~ msgstr "Sélectionner 2 fichiers:" #~ msgid "" #~ "image index disabled \n" #~ " Enable?" #~ msgstr "" #~ "l'indexation des images est désactivée \n" #~ " Activer?" fotoxx-20.08/locales/translate-it.po000066400000000000000000004442771362435004500174670ustar00rootroot00000000000000# translation of fotoxx.po to italian # Doriano Blengino , 2011-2019 msgid "" msgstr "" "Project-Id-Version: fotoxx\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-12-30 21:00+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:59 #, c-format msgid "max. album size exceeded: %d" msgstr "Raggiunta max dimensione album: %d" #: f.albums.cc:70 msgid "" "Right-click album thumbnail for edits: \n" " - add selected files at this position \n" " - remove this file from the album" msgstr "" "Clic destro su miniature per editare: \n" " - aggiungi in questa posizione i file selezionati \n" " - elimina questo file dall'album" #: f.albums.cc:73 msgid "Arrange files with thumbnail drag and drop" msgstr "Riordina i file trascinando le miniature" #: f.albums.cc:102 f.widgets.cc:458 msgid "Manage Albums" msgstr "Gestisci gli album" #: f.albums.cc:113 f.albums.cc:232 msgid "Create or replace an album" msgstr "Crea o rimpiazza un album" #: f.albums.cc:115 f.albums.cc:381 msgid "Rename an album" msgstr "Rinomina un album" #: f.albums.cc:117 f.albums.cc:451 msgid "Delete an album" msgstr "Elimina un album" #: f.albums.cc:119 msgid "Files to add to an album" msgstr "File da aggiungere a un album" #: f.albums.cc:183 #, c-format msgid "%d files added to album %s" msgstr "aggiunti %d file all'album %s" #: f.albums.cc:213 #, c-format msgid "max. album count exceeded: %d" msgstr "Il numero di album (%d) eccede il massimo" #: f.albums.cc:234 f.albums.cc:270 f.albums.cc:1537 msgid "Album Name" msgstr "Nome dell'album" #: f.albums.cc:240 msgid "make an initially empty album" msgstr "Crea un album vuoto" #: f.albums.cc:241 msgid "fill from pre-selected files" msgstr "Riempi con i file selezionati" #: f.albums.cc:242 msgid "fill from current gallery" msgstr "Riempi da galleria corrente" #: f.albums.cc:243 msgid "select initial files" msgstr "Seleziona i file iniziali" #: f.albums.cc:284 msgid "enter an album name" msgstr "Immettere un nome per l'album" #: f.albums.cc:292 f.albums.cc:1569 #, c-format msgid "replace album %s ?" msgstr "Rimpiazzare l'album %s ?" #: f.albums.cc:312 msgid "Use [Select] to add files to an empty album" msgstr "Usare [Seleziona] per aggiungere file a un album vuoto" #: f.albums.cc:326 msgid "new album created" msgstr "Nuovo album creato" #: f.albums.cc:342 f.meta.cc:7832 msgid "gallery is empty" msgstr "La galleria è vuota" #: f.albums.cc:396 msgid "enter new album name" msgstr "inserire il nuovo nome dell'album" #: f.albums.cc:403 #, c-format msgid "invalid file name: %s" msgstr "nome di file non valido: %s" #: f.albums.cc:424 #, c-format msgid "album already exists: %s" msgstr "Album già esistente: %s" #: f.albums.cc:435 #, c-format msgid "" "%s \n" " renamed: %s" msgstr "" "%s \n" " rinominato: %s" #: f.albums.cc:464 #, c-format msgid "delete %s ?" msgstr "Eliminare %s ?" #: f.albums.cc:571 f.albums.cc:584 #, c-format msgid "%s removed with no replacement \n" msgstr "Rimosso %s senza rimpiazzo \n" #: f.albums.cc:589 #, c-format msgid "%s replaced by ...%s \n" msgstr "%s rimpiazzato da ...%s \n" #: f.albums.cc:664 msgid "no selected files" msgstr "nessun file selezionato" #: f.albums.cc:877 msgid "Replace Album File" msgstr "Rimpiazza i file di album" #: f.albums.cc:879 f.albums.cc:1004 msgid "Choose Albums" msgstr "Scegli album" #: f.albums.cc:883 msgid "old file" msgstr "Vecchio file" #: f.albums.cc:886 msgid "new file" msgstr "Nuovo file" #: f.albums.cc:889 msgid "replace old" msgstr "Rimpiazza il vecchio file" #: f.albums.cc:890 msgid "add after old" msgstr "Aggiungi dopo il vecchio" #: f.albums.cc:936 msgid "no albums chosen" msgstr "Nessun album scelto" #: f.albums.cc:1117 f.widgets.cc:460 msgid "Album Mass Update" msgstr "Aggiornamento in massa di album" #: f.albums.cc:1125 msgid "Process all album files:" msgstr "Processare tutti i file degli album:" #: f.albums.cc:1128 msgid "Replace all with newest version only" msgstr "Rimpiazzare tutto con le sole nuove versioni" #: f.albums.cc:1130 msgid "Replace all versions with newest version" msgstr "Rimpiazzare tutte le versioni con la più nuova" #: f.albums.cc:1132 msgid "Add newest version to existing versions" msgstr "Aggiungere le nuove versioni alle vecchie" #: f.albums.cc:1134 msgid "Replace all with original and all versions" msgstr "Rimpiazzare tutto, originali e versioni" #: f.albums.cc:1136 msgid "Replace all with original + newest version" msgstr "Rimpiazza tutto con originale+nuova versione" #: f.albums.cc:1140 msgid "Process album files matching selected files:" msgstr "Processa i file dell'album corrispondenti a quelli selezionati:" #: f.albums.cc:1143 msgid "Replace all with selected versions" msgstr "Rimpiazza tutti con le versioni selezionate" #: f.albums.cc:1145 msgid "Replace all versions with selected versions" msgstr "Rimpiazza tutte le versioni con quelle selezionate" #: f.albums.cc:1147 msgid "Add selected versions to existing versions" msgstr "Aggiungi le versioni selezionate a quelle esistenti" #: f.albums.cc:1149 msgid "Replace all with original + selected versions" msgstr "Rimpiazza tutto con originali+versioni selezionate" #: f.albums.cc:1237 msgid "Select Albums" msgstr "Selezionare gli album" #: f.albums.cc:1296 #, c-format msgid "too many selected files: %d" msgstr "selezionati troppi file: %d" #: f.albums.cc:1535 msgid "Save Gallery as Album" msgstr "Salva galleria come album" #: f.albums.cc:1739 msgid "instant" msgstr "istantanea" #: f.albums.cc:1740 msgid "fade-in" msgstr "dissolvenza" #: f.albums.cc:1741 msgid "roll-right" msgstr "tenda-sx-dx" #: f.albums.cc:1742 msgid "roll-down" msgstr "tenda-su-giu" #: f.albums.cc:1743 msgid "venetian" msgstr "Veneziana" #: f.albums.cc:1744 msgid "grate" msgstr "quadrettoni" #: f.albums.cc:1745 msgid "rectangle" msgstr "rettangolo" #: f.albums.cc:1746 msgid "implode" msgstr "implodi" #: f.albums.cc:1747 msgid "explode" msgstr "esplodi" #: f.albums.cc:1748 msgid "radar" msgstr "radar" #: f.albums.cc:1749 msgid "Japan-fan" msgstr "Fanatico del Giappone" #: f.albums.cc:1750 msgid "spiral" msgstr "spirale" #: f.albums.cc:1751 f.area.cc:140 msgid "ellipse" msgstr "ellisse" #: f.albums.cc:1752 msgid "raindrops" msgstr "gocce" #: f.albums.cc:1753 msgid "doubledoor" msgstr "Finestra-doppia" #: f.albums.cc:1754 msgid "rotate" msgstr "ruotare" #: f.albums.cc:1755 msgid "fallover" msgstr "pioggia" #: f.albums.cc:1756 msgid "spheroid" msgstr "Sferoide" #: f.albums.cc:1757 msgid "turn-page" msgstr "Volta pagina" #: f.albums.cc:1758 msgid "french-door" msgstr "pannelli" #: f.albums.cc:1759 msgid "turn-cube" msgstr "ruotacubo" #: f.albums.cc:1760 msgid "windmill" msgstr "mulinello" #: f.albums.cc:1761 msgid "pixelize" msgstr "traspixel" #: f.albums.cc:1762 msgid "twist" msgstr "Intreccia" #: f.albums.cc:1763 msgid "Xopen" msgstr "Xopen" #: f.albums.cc:1764 msgid "squish" msgstr "Macchia" #: f.albums.cc:1765 msgid "disintegrate" msgstr "disintegra" #: f.albums.cc:1766 msgid "interleave" msgstr "Alterna" #: f.albums.cc:1797 f.widgets.cc:462 msgid "Slide Show" msgstr "Slideshow" #: f.albums.cc:1800 msgid "Select Album" msgstr "Scegli l'album" #: f.albums.cc:1805 msgid "Caption Time" msgstr "Durata scritte" #: f.albums.cc:1808 msgid "Image Time" msgstr "Durata dell'immagine" #: f.albums.cc:1811 msgid "Clip Limit %" msgstr "% massima di ritaglio" #: f.albums.cc:1815 msgid "Music File" msgstr "File musicale" #: f.albums.cc:1820 msgid "Full Screen" msgstr "Pieno schermo" #: f.albums.cc:1821 msgid "Auto-replay" msgstr "Riparte automaticamente" #: f.albums.cc:1824 msgid "Customize:" msgstr "Personalizza:" #: f.albums.cc:1825 msgid "transitions" msgstr "transizioni" #: f.albums.cc:1826 msgid "image files" msgstr "immagini" #: f.albums.cc:1827 msgid "KB controls" msgstr "Tasti" #: f.albums.cc:1841 f.albums.cc:1908 f.albums.cc:2141 f.albums.cc:2450 #: f.albums.cc:2786 f.albums.cc:2803 f.albums.cc:3039 msgid "invalid album" msgstr "album non valido" #: f.albums.cc:1936 msgid "open album" msgstr "apri album" #: f.albums.cc:1948 msgid "Select music file" msgstr "Seleziona file musicale" #: f.albums.cc:1983 #, c-format msgid "%d images" msgstr "%d immagini" #: f.albums.cc:2008 msgid "" "arrow keys show previous or next image instantly \n" "space bar (blank) is allowed and shows as '-'" msgstr "" "tasti freccia cambiano immagine immediatamente \n" "la barra spaziatrice è ammessa (mostrata come '-')" #: f.albums.cc:2027 msgid "Keyboard Preferences" msgstr "Preferenze tastiera" #: f.albums.cc:2030 msgid "blank or unblank window" msgstr "Mostra o nasconde l'immagine" #: f.albums.cc:2033 msgid "show next image, with transition" msgstr "Prossima immagine, con transizione" #: f.albums.cc:2036 msgid "pause or resume slide show" msgstr "Pausa / riprendi" #: f.albums.cc:2039 msgid "magnify image (loupe tool)" msgstr "Ingrandisce immagine (loupe)" #: f.albums.cc:2145 msgid "Transition Preferences" msgstr "Preferenze di transizione" #: f.albums.cc:2147 msgid "Transitions File" msgstr "File per le transizioni" #: f.albums.cc:2156 msgid "random" msgstr "casuale" #: f.albums.cc:2159 f.albums.cc:2175 f.albums.cc:2179 msgid "time" msgstr "tempo" #: f.albums.cc:2161 msgid "set all" msgstr "applica a tutti" #: f.albums.cc:2173 f.albums.cc:2177 msgid "transition" msgstr "Effetto di transizione" #: f.albums.cc:2174 f.albums.cc:2178 msgid "use" msgstr "Abilitata" #: f.albums.cc:2176 f.albums.cc:2180 msgid "pref" msgstr "Preferenza" #: f.albums.cc:2280 f.albums.cc:2322 msgid "invalid file" msgstr "File non valido" #: f.albums.cc:2394 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "formato file errato: \n" " %s" #: f.albums.cc:2456 msgid "Image Preferences" msgstr "Preferenze per le immagini" #: f.albums.cc:2460 f.file.cc:1610 f.file.cc:1995 msgid "Image File:" msgstr "File immagine:" #: f.albums.cc:2464 msgid "Action" msgstr "Azione" #: f.albums.cc:2476 msgid "Play tone when image shows" msgstr "Suona quando l'immagine appare" #: f.albums.cc:2481 msgid "Wait before filename/caption/comments" msgstr "Attendi prima di nome/titolo/commenti" #: f.albums.cc:2485 msgid "Show image file name (overlap)" msgstr "Mostra nome file (sovrimpresso)" #: f.albums.cc:2489 msgid "Show image caption (overlap)" msgstr "Mostra titolo immagine (sovrimpresso)" #: f.albums.cc:2493 msgid "Show image comments (overlap)" msgstr "Mostra commenti immagine (sovrimpressi)" #: f.albums.cc:2497 msgid "Wait before zoom" msgstr "Attendi prima d'ingrandire" #: f.albums.cc:2501 f.effects.cc:4023 msgid "Zoom" msgstr "Zoom" #: f.albums.cc:2505 msgid "zoom-in" msgstr "ingrandisci" #: f.albums.cc:2506 msgid "zoom-out" msgstr "riduci" #: f.albums.cc:2510 msgid "Zoom Center" msgstr "Centro per zoom" #: f.albums.cc:2515 msgid "Wait after zoom" msgstr "Attendi dopo zoom" #: f.albums.cc:2521 msgid "Transition to next image" msgstr "Transizione alla prossima immagine" #: f.albums.cc:2524 f.albums.cc:2654 msgid "next" msgstr "prossima" #: f.albums.cc:2644 msgid "click on thumbnail to set zoom center" msgstr "Clicca sulla miniatura per impostare il centro dello zoom" #: f.area.cc:93 msgid "Select Area for Edits" msgstr "Seleziona area per modifiche" #: f.area.cc:94 f.area.cc:1890 f.edit.cc:8198 msgid "Press F1 for help" msgstr "Premi F1 per l'aiuto" #: f.area.cc:104 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Questa funzione non supporta \n" "aree di selezione." #: f.area.cc:139 msgid "select rectangle" msgstr "Sel. rettangolo" #: f.area.cc:143 msgid "freehand draw" msgstr "Mano libera" #: f.area.cc:144 msgid "follow edge" msgstr "Insegui contorno" #: f.area.cc:147 msgid "draw/replace" msgstr "Disegna e ritocca" #: f.area.cc:150 msgid "Line Color:" msgstr "Colore linea:" #: f.area.cc:164 msgid "match level %" msgstr "Similarità %" #: f.area.cc:168 msgid "search range" msgstr "Raggio di ricerca" #: f.area.cc:172 msgid "select area in mouse" msgstr "Seleziona area in mouse" #: f.area.cc:175 msgid "select one color in mouse:" msgstr "seleziona un colore col mouse:" #: f.area.cc:179 msgid "select all colors in mouse (flood)" msgstr "Seleziona tutti i colori in mouse (inonda)" #: f.area.cc:184 msgid "Area Edge Blend Width" msgstr "Larghezza sfumatura dell'area" #: f.area.cc:188 msgid "Edge Creep" msgstr "Ritocco lati (dentro/fuori)" #: f.area.cc:200 msgid "drag mouse to select rectangular area" msgstr "trascina per selezionare un'area rettangolare" #: f.area.cc:201 msgid "drag mouse to select circular or elliptical area" msgstr "trascina per selezionare un'area rotonda o ellittica" #: f.area.cc:202 msgid "drag mouse to outline an area" msgstr "trascina per delineare un'area" #: f.area.cc:203 msgid "drag mouse along an edge to follow the edge" msgstr "trascina lungo un bordo per seguirlo" #: f.area.cc:204 msgid "drag mouse near a line to move the line" msgstr "trascina vicino a una linea per spostarla" #: f.area.cc:205 msgid "select line color" msgstr "seleziona il colore della linea" #: f.area.cc:206 msgid "size of mouse selection circle" msgstr "dimensione del cerchio di selezione" #: f.area.cc:207 msgid "required match level to select by color" msgstr "livello di corrispondenza colore per selezionare" #: f.area.cc:208 msgid "select by color search range" msgstr "seleziona via intervallo di ricerca colore" #: f.area.cc:209 msgid "select area within mouse circle" msgstr "seleziona area dentro al cerchio" #: f.area.cc:210 msgid "" "first select the checkbox, then \n" "shift+click on image to set the color" msgstr "" "Prima selezionare la casella di spunta, poi \n" "Maiusc+Clic nell'immagine per impostare il colore" #: f.area.cc:212 msgid "select surrounding areas matching colors in mouse" msgstr "seleziona aree intorno con colore simile all'interno del mouse" #: f.area.cc:213 f.area.cc:214 msgid "area edits fade away within edge distance" msgstr "Le modifiche sfumano entro la distanza dai bordi" #: f.area.cc:215 msgid "move area boundary in/out in 1-pixel steps" msgstr "allarga/stringi i confini a passi di un pixel" #: f.area.cc:216 msgid "map selected areas and verify" msgstr "mappa le aree e controlla" #: f.area.cc:217 msgid "invert area" msgstr "inverti area" #: f.area.cc:218 msgid "show area outlines" msgstr "mostra i confini" #: f.area.cc:219 msgid "hide area outlines" msgstr "nascondi i confini" #: f.area.cc:220 msgid "clear area selections" msgstr "cancella la selezione di area" #: f.area.cc:404 f.area.cc:570 #, c-format msgid "exceed %d edits" msgstr "supera %d modifiche" #: f.area.cc:1471 msgid "" "Fill selected areas with color for visual verification. \n" "Method 1: left-click in each selected area not already filled. \n" "Method 2: right-click OUTSIDE all selected areas. \n" "Press [help] button for clarification" msgstr "" "Riempire le aree selezionate per una verifica visuale. \n" "Metodo 1: clic sinistro in ogni area non ancora riempita. \n" "Metodo 2: clic destro fuori dalle aree selezionate. \n" "Premere il pulsante [Aiuto] per chiarimenti" #: f.area.cc:1499 msgid "finish area" msgstr "Finisci area" #: f.area.cc:1528 f.area.cc:1757 #, c-format msgid "found %d pixels" msgstr "trovati %d pixel" #: f.area.cc:1546 msgid "" "Method 1: \n" " Left-click inside an outlined area that is not already filled. \n" " Area will be filled with color for visible verification. \n" " Gaps in the outline will cause overflow outside the area. \n" " Repeat for each outlined area that is not already filled. \n" "Method 2: \n" " Right-click outside ALL outlined areas. \n" " All areas will be filled with color for visible verification. \n" " Gaps in an area outline will cause that area not to be filled. \n" "Gaps in an area outline: \n" " Gaps must be closed before proceeding with edits. \n" " The Find Gap function can be used for this." msgstr "" "Metodo 1: \n" " Clic sinistro dentro un'area contornata che non sia già riempita. \n" " L'area sarà colorata per una verifica visibile. \n" " Interruzioni nel contorno causeranno travasi fuori dall'area. \n" " Ripetere per ogni area contornata non ancora colorata. \n" "Metodo 2: \n" " Clic destro fuori da qualsiasi area contornata. \n" " Tutte le aree saranno colorate per la verifica visuale. \n" " Interruzioni in un contorno lasceranno l'area da riempire. \n" "Interruzioni nel contorno di un'area: \n" " Interruzioni nel contorno vanno eliminati prima di procedere. \n" " Si può usare la funzione Trova Interruzioni." #: f.area.cc:1889 f.widgets.cc:487 msgid "Select Hairy" msgstr "Seleziona frastagliature" #: f.area.cc:1899 msgid "select the area first" msgstr "prima selezionare l'area" #: f.area.cc:1975 msgid "select" msgstr "seleziona" #: f.area.cc:1981 msgid "deselect" msgstr "deseleziona" #: f.area.cc:2335 msgid "" "Click near any position on the area outline. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Cliccare vicino al contorno dell'area. \n" "Possibili interruzioni del contorno verranno individuate. \n" "Premere F1 per l'aiuto" #: f.area.cc:2356 msgid "find outline gap" msgstr "trova sconnessione nel contorno" #: f.area.cc:2451 msgid "cannot find area outline" msgstr "impossibile trovare contorno dell'area" #: f.area.cc:3285 msgid "save area as a PNG file" msgstr "salva area come file PNG" #: f.area.cc:3407 msgid "position with mouse click/drag" msgstr "posizione con clic o trascinamento" #: f.area.cc:3467 msgid "Paste Image" msgstr "Incolla immagine" #: f.area.cc:3472 msgid "resize" msgstr "ridimensiona" #: f.combine.cc:174 f.combine.cc:828 f.combine.cc:1436 f.combine.cc:2066 #: f.combine.cc:2510 msgid "Select 2 to 9 files" msgstr "Seleziona da 2 a 9 file" #: f.combine.cc:196 f.combine.cc:850 f.combine.cc:1458 f.combine.cc:2088 msgid "Images are not all the same size" msgstr "Le immagini non hanno la stessa dimensione" #: f.combine.cc:560 msgid "Adjust Image Contributions" msgstr "Regola contributi immagine" #: f.combine.cc:564 msgid "dark pixels" msgstr "Pixel scuri" #: f.combine.cc:566 msgid "light pixels" msgstr "Pixel luminosi" #: f.combine.cc:568 msgid "file:" msgstr "File:" #: f.combine.cc:1036 msgid "Paint and Warp Image" msgstr "Dipingi e deforma immagine" #: f.combine.cc:1044 f.edit.cc:6054 msgid "paint" msgstr "Dipingi" #: f.combine.cc:1045 msgid "warp" msgstr "Deforma" #: f.combine.cc:1648 f.combine.cc:2594 msgid "Select and Paint Image" msgstr "Seleziona e dipingi" #: f.combine.cc:1656 msgid "Transient Objects" msgstr "Soggetti in movimento" #: f.combine.cc:2264 msgid "Adjust Pixel Composition" msgstr "Regola la composizione dei pixel" #: f.combine.cc:2266 msgid "use average" msgstr "Usa media" #: f.combine.cc:2267 msgid "use median" msgstr "Usa mediano" #: f.combine.cc:2269 msgid "omit low pixel" msgstr "ometti pixel bassi" #: f.combine.cc:2270 msgid "omit high pixel" msgstr "ometti pixel alti" #: f.combine.cc:2532 f.combine.cc:2844 msgid "Images must be exactly the same size" msgstr "Le immagini devono avere esattamente la stessa dimensione" #: f.combine.cc:2605 msgid " Fill " msgstr " Riempi " #: f.combine.cc:2606 msgid "using selected image" msgstr "usando l'immagine selezionata" #: f.combine.cc:2825 f.combine.cc:3095 msgid "Select 2 files" msgstr "Seleziona 2 file" #: f.combine.cc:2902 msgid "Split two Images" msgstr "Dividi due immagini" #: f.combine.cc:2903 msgid "drag image boundary" msgstr "trascina i confini dell'immagine" #: f.combine.cc:3092 msgid "Image Differences" msgstr "Confronto immagini" #: f.combine.cc:3108 msgid "differences" msgstr "differenze" #: f.combine.cc:3110 msgid "X-align" msgstr "Scostamento X" #: f.combine.cc:3113 msgid "Y-align" msgstr "Scostamento Y" #: f.combine.cc:3179 msgid "select exactly 2 files" msgstr "Selezionare esattamente 2 file" #: f.combine.cc:3408 f.combine.cc:4562 msgid "Select 2 to 4 files" msgstr "Seleziona da 2 a 4 file" #: f.combine.cc:3483 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:3485 f.combine.cc:4639 msgid "no curve (scanned image)" msgstr "nessuna curva (immagine scansionata)" #: f.combine.cc:3486 f.combine.cc:4640 msgid "Search for lens mm" msgstr "Ricerca per mm lenti" #: f.combine.cc:3487 f.combine.cc:4641 msgid "Save lens mm → image EXIF" msgstr "Salva mm della lente nelle informazioni EXIF" #: f.combine.cc:3547 f.combine.cc:4701 msgid "Pre-align Images" msgstr "Pre-allineamento immagini" #: f.combine.cc:3551 f.combine.cc:4705 msgid "lens mm" msgstr "mm lente" #: f.combine.cc:3556 f.combine.cc:4710 msgid "no auto warp" msgstr "Deformazione automatica esclusa" #: f.combine.cc:3558 f.combine.cc:4712 msgid "manual align" msgstr "Allineamento manuale" #: f.combine.cc:3560 f.combine.cc:4714 f.process.cc:1395 f.widgets.cc:504 #: f.widgets.cc:829 msgid "Resize" msgstr "Ridimensiona" #: f.combine.cc:3561 f.combine.cc:4715 msgid "resize window" msgstr "Ridimensiona finestra" #: f.combine.cc:3569 f.combine.cc:4723 msgid "do not warp images during auto-alignment" msgstr "Non deformare immagini durante l'auto allineamento" #: f.combine.cc:3614 f.combine.cc:4768 msgid "use two images only" msgstr "Utilizza solo due immagini" #: f.combine.cc:3644 f.combine.cc:3848 f.combine.cc:4034 f.combine.cc:4796 #: f.combine.cc:5000 f.combine.cc:5186 msgid "Too little overlap, cannot align" msgstr "Troppo poca sovrapposizione, impossibile allineare" #: f.combine.cc:4112 f.combine.cc:5264 msgid "Match Brightness and Color" msgstr "Allinea luminosità e colore" #: f.combine.cc:4158 msgid "Select image" msgstr "Selezionare immagine" #: f.combine.cc:4173 f.combine.cc:5325 msgid "auto color" msgstr "colore automatico" #: f.combine.cc:4174 f.combine.cc:5326 msgid "file color" msgstr "file colore" #: f.combine.cc:4180 f.combine.cc:5332 msgid "mouse warp" msgstr "Distorcere col mouse" #: f.combine.cc:4182 f.combine.cc:5334 msgid "flatten image" msgstr "Appiattisci immagine" #: f.combine.cc:4637 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:5596 msgid "pano tools (hugin) not installed" msgstr "pano tools (hugin) non è installato" #: f.combine.cc:5605 msgid "Select at least 2 files" msgstr "Selezionare almeno 2 file" #: f.edit.cc:106 msgid "" "drag middle to move \n" "drag corners to resize \n" "drag right edge to level" msgstr "" "Trascinare col pulsante centrale per spostare \n" "Trascinare gli angoli per ridimensionare \n" "Trascinare il lato destro per livellare" #: f.edit.cc:209 f.widgets.cc:501 f.widgets.cc:828 msgid "Trim/Rotate" msgstr "Ritaglia/Ruota" #: f.edit.cc:224 f.edit.cc:1483 msgid "ratio" msgstr "proporzione" #: f.edit.cc:226 msgid "Lock Ratio" msgstr "Blocca proporzione" #: f.edit.cc:229 msgid "Trim Size:" msgstr "Grandezza di ritaglio" #: f.edit.cc:237 msgid "Set Ratio" msgstr "Imposta proporzione" #: f.edit.cc:238 f.widgets.cc:493 fotoxx.h:1371 msgid "Invert" msgstr "Inverti" #: f.edit.cc:239 msgid "Customize" msgstr "Personalizza" #: f.edit.cc:248 msgid "Degrees:" msgstr "Gradi:" #: f.edit.cc:250 msgid "Auto Level" msgstr "Livellamento automatico" #: f.edit.cc:256 f.process.cc:186 f.process.cc:822 f.widgets.cc:502 #: f.widgets.cc:830 msgid "Upright" msgstr "Raddrizza" #: f.edit.cc:258 msgid "maximize trim box" msgstr "Massimizza la casella di ritaglio" #: f.edit.cc:259 msgid "trim transparent edges" msgstr "Ritaglia i lati trasparenti" #: f.edit.cc:260 msgid "lock width/height ratio" msgstr "Blocca il rapporto tra larghezza e altezza" #: f.edit.cc:261 msgid "use EXIF data if available" msgstr "Utilizza i dati EXIF se disponibili" #: f.edit.cc:603 f.edit.cc:1550 msgid "rotation unknown" msgstr "Rotazione sconosciuta" #: f.edit.cc:1479 msgid "Trim Buttons" msgstr "Pulsanti di taglio" #: f.edit.cc:1482 msgid "label" msgstr "Etichetta" #: f.edit.cc:1692 f.widgets.cc:503 f.widgets.cc:833 msgid "Retouch" msgstr "Ritocco" #: f.edit.cc:1705 msgid "Auto black level" msgstr "Bilanciamento automatico del nero" #: f.edit.cc:1708 f.edit.cc:1714 msgid "sample %" msgstr "Campionamento %" #: f.edit.cc:1711 msgid "Auto white balance" msgstr "Bilanciamento automatico del bianco" #: f.edit.cc:1717 msgid "Click gray spot for white balance" msgstr "Cliccare una zona grigia per bilanciare il bianco" #: f.edit.cc:1720 msgid "Click dark spot for black level" msgstr "Cliccare una zona scura per bilanciare il nero" #: f.edit.cc:1723 msgid "Click for RGB distribution" msgstr "Clic per la distribuzione RGB" #: f.edit.cc:1731 fotoxx.h:1326 msgid "Brightness" msgstr "Luminosità" #: f.edit.cc:1732 f.effects.cc:4049 fotoxx.h:1339 msgid "Contrast" msgstr "Contrasto" #: f.edit.cc:1733 f.edit.cc:3279 f.edit.cc:3311 f.edit.cc:6777 msgid "Saturation" msgstr "Saturazione" #: f.edit.cc:1734 msgid "Temperature" msgstr "Temperatura" #: f.edit.cc:1742 msgid "Settings File" msgstr "File di impostazioni" #: f.edit.cc:2250 msgid "choose a better spot" msgstr "Cliccata zona non adatta, provarne un'altra'" #: f.edit.cc:2555 msgid "Resize Image" msgstr "Ridimensiona" #: f.edit.cc:2571 msgid "Previous" msgstr "Precedente" #: f.edit.cc:2589 msgid "W/H Ratio:" msgstr "Rapporto Larg/Alt" #: f.edit.cc:2591 msgid "Lock" msgstr "Blocca" #: f.edit.cc:2840 msgid "insufficient memory, cannot proceed" msgstr "Memoria insufficiente, impossibile proseguire" #: f.edit.cc:2936 f.widgets.cc:505 msgid "Adjust RGB" msgstr "Regola RGB" #: f.edit.cc:2942 msgid "+Brightness" msgstr "+Luminosità" #: f.edit.cc:2943 msgid "+Red -Cyan" msgstr "+Rosso -Ciano" #: f.edit.cc:2944 msgid "+Green -Magenta" msgstr "+Verde -Magenta" #: f.edit.cc:2945 msgid "+Blue -Yellow" msgstr "+Blu -Giallo" #: f.edit.cc:2948 msgid "Contrast Red" msgstr "Contrasto del rosso" #: f.edit.cc:2949 msgid "Contrast Green" msgstr "Contrasto del verde" #: f.edit.cc:2950 msgid "Contrast Blue" msgstr "Contrasto del blu" #: f.edit.cc:3268 f.widgets.cc:506 msgid "Adjust HSL" msgstr "Regola HSL" #: f.edit.cc:3272 msgid "Input color to match and adjust:" msgstr "Scegliere il colore da confrontare e regolare:" #: f.edit.cc:3274 msgid "shift+click on image to select color" msgstr "Maiusc+clic sull'immagine per selezionare il colore" #: f.edit.cc:3277 msgid "Match using:" msgstr "Metodo di contronto:" #: f.edit.cc:3278 msgid "Hue" msgstr "Tono" #: f.edit.cc:3280 f.edit.cc:3312 f.edit.cc:6778 msgid "Lightness" msgstr "Lucentezza" #: f.edit.cc:3290 msgid "Output Color" msgstr "Colore d'uscita" #: f.edit.cc:3310 f.edit.cc:6776 msgid "Color Hue" msgstr "Tono del colore" #: f.edit.cc:3313 msgid "Adjustment" msgstr "Regolazione" #: f.edit.cc:3828 msgid "Image Markup" msgstr "Annotazione immagine" #: f.edit.cc:3829 f.edit.cc:3894 msgid "Draw text on image" msgstr "Metti testo su immagine" #: f.edit.cc:3830 f.edit.cc:4818 msgid "Draw line or arrow on image" msgstr "Disegna linea o freccia su immagine" #: f.edit.cc:3831 f.edit.cc:5499 msgid "Draw box on image" msgstr "Disegna rettangolo su immagine" #: f.edit.cc:3832 f.edit.cc:5700 msgid "Draw oval on image" msgstr "Disegna ovale su immagine" #: f.edit.cc:3871 msgid "+Version" msgstr "+Versione" #: f.edit.cc:3895 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:3944 msgid "Use settings file" msgstr "Usa file d'impostazioni" #: f.edit.cc:3949 f.mashup.cc:2438 f.tools.cc:1668 f.tools.cc:1677 msgid "Text" msgstr "Testo" #: f.edit.cc:3954 msgid "Use metadata key" msgstr "Usa chiave metadati" #: f.edit.cc:3973 f.mashup.cc:2456 msgid "text" msgstr "Testo" #: f.edit.cc:3974 f.edit.cc:4847 f.mashup.cc:2457 f.mashup.cc:2795 msgid "backing" msgstr "Sfondo" #: f.edit.cc:3975 f.edit.cc:4848 f.mashup.cc:2458 f.mashup.cc:2796 msgid "outline" msgstr "Contorno" #: f.edit.cc:3976 f.edit.cc:4849 f.mashup.cc:2459 f.mashup.cc:2797 msgid "shadow" msgstr "Ombra" #: f.edit.cc:4002 msgid "save to current file" msgstr "Salve nel file corrente" #: f.edit.cc:4003 f.file.cc:2432 msgid "save as new file version" msgstr "salva come nuova versione" #: f.edit.cc:4004 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:4167 f.mashup.cc:2563 f.tools.cc:1961 msgid "select font" msgstr "Scegli il font" #: f.edit.cc:4446 msgid "text file is defective" msgstr "file dei testi difettoso" #: f.edit.cc:4786 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:4826 f.mashup.cc:2774 msgid "Line length" msgstr "Lunghezza linea" #: f.edit.cc:4833 f.mashup.cc:2781 msgid "Arrow head" msgstr "Terminazione freccia" #: f.edit.cc:4846 f.mashup.cc:2794 msgid "line" msgstr "Linea" #: f.edit.cc:4875 msgid "" "fix line/arrow in layout \n" " start new line/arrow" msgstr "" "Fissa linea/freccia\n" "inizia un'altra" #: f.edit.cc:5474 msgid "" "drag mouse to draw box \n" "shift + drag center to move box \n" "shift + drag edge to move edge" msgstr "" "trascinare per disegnare \n" "maiusc+trascina al centro per spostare figura\n" "maiusc+trascina lato per spostare il lato" #: f.edit.cc:5505 f.edit.cc:5706 msgid "line color" msgstr "colore linea" #: f.edit.cc:5508 f.edit.cc:5709 msgid "line width" msgstr "spessore linea" #: f.edit.cc:5674 msgid "" "drag mouse down/right to draw oval \n" "shift + drag center to move oval \n" "shift + drag lower right edge to change" msgstr "" "trascina in basso a destra per disegnare l'ovale \n" "maiusc+trascina centro per spostare l'ovale \n" "maiusc+trascina lato basso a destra per variare" #: f.edit.cc:5712 msgid "oval" msgstr "ovale" #: f.edit.cc:5713 msgid "circle" msgstr "cerchio" #: f.edit.cc:5988 msgid "" "shift + left click: pick color from image \n" "left drag: paint color on image \n" "right drag: restore original image" msgstr "" "Maiusc + clic sinistro: preleva colore dall'immagine \n" "Trascina col sinistro: dipinge \n" "Trascina col destro: ripristina" #: f.edit.cc:6024 msgid "Paint on Image" msgstr "Dipingi sull'immagine" #: f.edit.cc:6030 msgid "paint color" msgstr "Colore pennello" #: f.edit.cc:6041 f.edit.cc:6970 f.edit.cc:7548 msgid "brush size" msgstr "Dimensione pennello" #: f.edit.cc:6055 msgid "erase" msgstr "Cancella" #: f.edit.cc:6060 msgid "include transparent areas" msgstr "Anche aree trasparenti" #: f.edit.cc:6063 msgid "drag image" msgstr "Sposta immagine senza dipingere" #: f.edit.cc:6065 msgid "zoom image" msgstr "Ingrandimento immagine" #: f.edit.cc:6592 msgid "Color Chooser" msgstr "Selezione colore" #: f.edit.cc:6594 msgid "click on desired color" msgstr "Cliccare sul colore desiderato" #: f.edit.cc:6929 msgid "" "shift + left click: pick position to copy \n" "left click or drag: copy image to mouse \n" "right click or drag: restore original image" msgstr "" "Maius+clic sinistro: sceglie la posizione da copiare \n" "Clic sinistro o trascinamento: copia immagine sul mouse \n" "Clic destro o trascinamento: ripristina immagine originale" #: f.edit.cc:6960 msgid "Copy Pixels (1 image)" msgstr "Copia pixel (1 immagine)" #: f.edit.cc:6979 f.edit.cc:7557 msgid "paint over transparent areas" msgstr "pitturare su aree trasparenti" #: f.edit.cc:7531 msgid "" "left click: synchronize copy position \n" "left click or drag: copy source image to mouse \n" "right click or drag: restore original image" msgstr "" "Clic sinistro: sincronizza posizioni \n" "Clic o trascina sinistro: copia immagine sorgente \n" "Clic o trascina destro: ripristina" #: f.edit.cc:7535 msgid "Copy Pixels (2 images)" msgstr "Copia pixel (2 immagini)" #: f.edit.cc:7541 msgid "source image scale" msgstr "Scala dell'originale" #: f.edit.cc:8118 msgid "source image failure (scale too big?)" msgstr "Errore sull'immagine (scala troppo grande?)" #: f.edit.cc:8197 f.widgets.cc:511 msgid "Paint Edits" msgstr "Pennella funzione" #: f.edit.cc:8203 fotoxx.cc:3576 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "L'area selezionata non può essere mantenuta.\n" "Continuare?" #: f.edit.cc:8211 f.tools.cc:3751 msgid "Edit function must be active" msgstr "Una funzione di edit deve essere attiva" #: f.edit.cc:8216 msgid "Cannot use Paint Edits" msgstr "Impossibile usare Pennella Funzione" #: f.edit.cc:8243 f.edit.cc:8485 msgid "power: center" msgstr "Forza: centro:" #: f.edit.cc:8248 msgid "reset area" msgstr "Azzera area" #: f.edit.cc:8428 msgid "finish current edit first" msgstr "Prima terminare l'edit corrente" #: f.edit.cc:8433 msgid "no previous edit" msgstr "nessun edit precedente" #: f.edit.cc:8438 msgid "no current image" msgstr "nessuna immagine corrente" #: f.edit.cc:8449 msgid "This edit cannot be incrementally undone" msgstr "Impossibile annullare incrementalmente questa modifica" #: f.edit.cc:8480 f.widgets.cc:512 msgid "Undo Edits" msgstr "Annulla modifiche" #: f.edit.cc:8708 f.edit.cc:8756 msgid "Edit Plugins" msgstr "Gestione plug-in" #: f.edit.cc:8709 msgid "Edit plugins menu" msgstr "Modifica menu plug-in" #: f.edit.cc:8717 msgid "Run as Fotoxx edit function" msgstr "Esegui come funzione di edit fotoxx" #: f.edit.cc:8758 msgid "menu name" msgstr "Nome nel menù" #: f.edit.cc:8761 msgid "command" msgstr "Comando" #: f.edit.cc:8959 msgid "Plugin working ..." msgstr "Esecuzione plugin..." #: f.edit.cc:8968 msgid "plugin failed" msgstr "plugin non trovato" #: f.edit.cc:9004 msgid "Raw Therapee not installed" msgstr "Programma Raw Therapee non installato" #: f.edit.cc:9023 msgid "RAW type not registered in User Preferences" msgstr "Tipo RAW non registrato in Preferenze utente" #: f.edit.cc:9038 msgid "Raw Therapee produced no tif file" msgstr "Raw Therapee non ha prodotto alcun file TIF" #: f.effects.cc:83 msgid "Convert to Sketch" msgstr "Converti in schizzo" #: f.effects.cc:161 msgid "Clip Level" msgstr "Bianco" #: f.effects.cc:165 msgid "Algorithm" msgstr "Metodo" #: f.effects.cc:170 msgid "Foreground" msgstr "Colore" #: f.effects.cc:173 f.tools.cc:1680 msgid "Background" msgstr "Sfondo" #: f.effects.cc:609 msgid "Line Threshold" msgstr "Soglia di linea" #: f.effects.cc:610 msgid "Line Width" msgstr "Spessore linea" #: f.effects.cc:611 f.enhance.cc:3929 msgid "Blur Radius" msgstr "Raggio di sfumatura" #: f.effects.cc:612 msgid "Kuwahara Depth" msgstr "Profondità di Kuwahara" #: f.effects.cc:1057 f.widgets.cc:538 msgid "Line Drawing" msgstr "Tracciamento linea" #: f.effects.cc:1070 f.effects.cc:2090 msgid "black/white" msgstr "Bianco e nero" #: f.effects.cc:1320 f.widgets.cc:539 msgid "Emboss" msgstr "Scolpisci" #: f.effects.cc:1326 msgid "Depth" msgstr "Profondità" #: f.effects.cc:1541 msgid "Simulate Tiles" msgstr "Simula un mosaico" #: f.effects.cc:1545 msgid "tile size" msgstr "Dimensione tessere" #: f.effects.cc:1548 msgid "tile gap" msgstr "Distanza fra tessere" #: f.effects.cc:1551 msgid "3D depth" msgstr "Profondità 3D" #: f.effects.cc:1780 msgid "Dither Image" msgstr "Tremola colori" #: f.effects.cc:1783 msgid "Dither0" msgstr "Tremola 0" #: f.effects.cc:1784 f.effects.cc:1848 msgid "Roy Lichtenstein effect" msgstr "Effetto Roy Lichtenstein" #: f.effects.cc:1787 f.effects.cc:2083 msgid "Dither1" msgstr "Tremola 1" #: f.effects.cc:1788 msgid "pure RGB or black/white dots" msgstr "Colori RGB puri o puntini bianco/nero" #: f.effects.cc:1791 f.effects.cc:2481 msgid "Dither2" msgstr "Tremola 2" #: f.effects.cc:1792 msgid "RGB mix with given bit-depth" msgstr "Miscela RGB con profondità colore data" #: f.effects.cc:1795 f.effects.cc:2756 msgid "Dither3" msgstr "Tremola 3" #: f.effects.cc:1796 msgid "custom palette colors" msgstr "Tavolozza dei colori a scelta" #: f.effects.cc:1852 msgid "dot size" msgstr "Dimensione punto" #: f.effects.cc:2087 f.effects.cc:2485 f.effects.cc:2760 msgid "resolution" msgstr "Risoluzione" #: f.effects.cc:2089 msgid "RGB color" msgstr "Colore RGB" #: f.effects.cc:2091 msgid "random position" msgstr "Posizione casuale" #: f.effects.cc:2488 f.effects.cc:3196 msgid "color depth" msgstr "Profondità di colore" #: f.effects.cc:2491 f.effects.cc:2768 msgid "error compensation" msgstr "Compensazione d'errore" #: f.effects.cc:2764 msgid "palette:" msgstr "tavolozza:" #: f.effects.cc:2818 msgid "palette file" msgstr "file tavolozza" #: f.effects.cc:3178 f.widgets.cc:542 msgid "Painting" msgstr "Dipinto" #: f.effects.cc:3200 msgid "patch area goal" msgstr "Fattore artistico" #: f.effects.cc:3204 msgid "req. color match" msgstr "Precisione colori" #: f.effects.cc:3208 msgid "borders" msgstr "Contorni" #: f.effects.cc:3799 msgid "Add Texture" msgstr "Aggiungi disturbo" #: f.effects.cc:4014 msgid "Background Pattern" msgstr "Motivo di sfondo" #: f.effects.cc:4018 msgid "Pattern File:" msgstr "File motivo:" #: f.effects.cc:4026 msgid "Geometry" msgstr "Geometria" #: f.effects.cc:4030 f.widgets.cc:544 msgid "Pattern" msgstr "Motivo" #: f.effects.cc:4038 msgid "Overlap" msgstr "Sovrapposizione" #: f.effects.cc:4046 msgid "Opacity" msgstr "Opacità" #: f.effects.cc:4052 msgid "Grayscale" msgstr "Scala di grigi" #: f.effects.cc:4478 msgid "Create Mosaic" msgstr "Creazione Mosaico" #: f.effects.cc:4524 msgid "Tile" msgstr "Tessera" #: f.effects.cc:4532 f.widgets.cc:540 msgid "Tiles" msgstr "Tessere" #: f.effects.cc:4538 msgid "Tile blending" msgstr "Miscelazione tessere" #: f.effects.cc:4621 #, c-format msgid "exceeded max. tiles: %d" msgstr "Superato massimo tessere: %d" #: f.effects.cc:4628 #, c-format msgid "only %d tile images found" msgstr "Trovate solo %d tessere" #: f.effects.cc:5088 f.widgets.cc:546 msgid "Color Mode" msgstr "Cambia i colori" #: f.effects.cc:5091 msgid "reset" msgstr "Azzera" #: f.effects.cc:5092 msgid "black/white positive" msgstr "Bianco e nero, positivo" #: f.effects.cc:5093 msgid "black/white negative" msgstr "Bianco e nero, negativo" #: f.effects.cc:5094 msgid "color negative" msgstr "A colori, negativo" #: f.effects.cc:5095 msgid "RGB -> GBR" msgstr "RGB -> GBR" #: f.effects.cc:5096 msgid "RGB -> BRG" msgstr "RGB -> BRG" #: f.effects.cc:5097 msgid "sepia" msgstr "Seppia" #: f.effects.cc:5348 msgid "Set color depth to 1-16 bits" msgstr "Imposta la risoluzione del colore da 1 a 16 bit" #: f.effects.cc:5377 msgid "Set Color Depth" msgstr "Imposta la risoluzione di colore" #: f.effects.cc:5547 f.widgets.cc:548 msgid "Shift Colors" msgstr "Modula componenti" #: f.effects.cc:5824 f.widgets.cc:549 msgid "Alien Colors" msgstr "Effetto colori alieni" #: f.effects.cc:5827 msgid "blocksize" msgstr "Dimensione tessere" #: f.effects.cc:5830 f.warp.cc:3096 msgid "amplitude" msgstr "Ampiezza" #: f.effects.cc:6089 msgid "" "Draw a line across the image in \n" "direction of brightness change." msgstr "" "Tracciare (trascinare) una linea\n" "nell'immagine per impostare la rampa" #: f.effects.cc:6132 msgid "Brightness Ramp" msgstr "Rampa di luminosità" #: f.effects.cc:6605 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" "trascina con sinistro: aggiunge trasparenza \n" "trascina con destro: aggiunge opacità" #: f.effects.cc:6640 msgid "Paint Transparency" msgstr "Dipingi trasparenza" #: f.effects.cc:6648 msgid "paintbrush radius" msgstr "Raggio del pennello:" #: f.effects.cc:6649 msgid "strength center" msgstr "Rafforza il centro" #: f.effects.cc:6650 msgid "strength edge" msgstr "Rafforza lati" #: f.effects.cc:6655 msgid "gradual paint" msgstr "pittura graduale" #: f.effects.cc:6874 msgid "Mirror Image" msgstr "Rispecchia immagine" #: f.effects.cc:6877 f.warp.cc:3098 msgid "horizontal" msgstr "Orizzontale" #: f.effects.cc:6878 f.warp.cc:3102 msgid "vertical" msgstr "Verticale" #: f.effects.cc:7051 f.widgets.cc:553 msgid "Custom Kernel" msgstr "Matrice di trasformazione" #: f.effects.cc:7055 msgid "Kernel size" msgstr "Dimensione matrice" #: f.effects.cc:7072 msgid "multiply" msgstr "Moltiplica" #: f.effects.cc:7075 msgid "add" msgstr "Aggiungi" #: f.effects.cc:7079 msgid "Data file" msgstr "File di dati" #: f.effects.cc:7099 fotoxx.cc:3845 msgid "Load settings from file" msgstr "Carica impostazioni dal file" #: f.enhance.cc:505 msgid "Adjust Brightness Distribution" msgstr "Regola la distribuzione di luminosità" #: f.enhance.cc:544 msgid "Low Cutoff" msgstr "Più scuri" #: f.enhance.cc:545 msgid "High Cutoff" msgstr "Meno chiari" #: f.enhance.cc:546 msgid "Low Flatten" msgstr "Meno medi" #: f.enhance.cc:547 msgid "Mid Flatten" msgstr "Più medi" #: f.enhance.cc:548 msgid "High Flatten" msgstr "Meno chiari" #: f.enhance.cc:549 msgid "Low Stretch" msgstr "Comprimi scuri" #: f.enhance.cc:550 msgid "Mid Stretch" msgstr "Comprimi medi" #: f.enhance.cc:551 msgid "High Stretch" msgstr "Comprimi chiari" #: f.enhance.cc:886 msgid "Magnify Gradients" msgstr "Ingrandimento gradienti" #: f.enhance.cc:923 msgid "low" msgstr "basso" #: f.enhance.cc:925 msgid "high" msgstr "alto" #: f.enhance.cc:928 msgid "Amplify" msgstr "Amplificazione" #: f.enhance.cc:1312 msgid "Flatten Brightness" msgstr "Appiattisce la luminosità" #: f.enhance.cc:1363 msgid "Zones" msgstr "Zone" #: f.enhance.cc:1370 msgid "Deband Dark" msgstr "Escludi scuri" #: f.enhance.cc:1373 msgid "Deband Bright" msgstr "Escludi chiari" #: f.enhance.cc:1851 msgid "Global Retinex" msgstr "Retinex globale" #: f.enhance.cc:1867 msgid "Dark Point" msgstr "Punto scuro" #: f.enhance.cc:1868 msgid "Bright Point" msgstr "Punto luminoso" #: f.enhance.cc:1869 msgid "Multiplyer" msgstr "Moltiplicatore" #: f.enhance.cc:1893 msgid "brightness rescale" msgstr "Riscala la luminosità" #: f.enhance.cc:1896 msgid "click bright point" msgstr "Clicca il punto luminoso" #: f.enhance.cc:1897 msgid "click dark point" msgstr "Clicca il punto scuro" #: f.enhance.cc:1900 f.enhance.cc:2531 msgid "blend" msgstr "Miscela con originale" #: f.enhance.cc:1903 f.enhance.cc:2537 msgid "reduce bright" msgstr "Riduci nelle aree luminose" #: f.enhance.cc:2523 msgid "Zonal Retinex" msgstr "Retinex di zona" #: f.enhance.cc:2527 msgid "zone count:" msgstr "conteggio zone:" #: f.enhance.cc:2534 msgid "reduce dark" msgstr "Riduzione in zone scure" #: f.enhance.cc:3058 f.process.cc:189 f.process.cc:823 f.process.cc:1404 #: f.widgets.cc:524 msgid "Sharpen" msgstr "Contrasta" #: f.enhance.cc:3066 msgid "unsharp mask" msgstr "Maschera addolcimento" #: f.enhance.cc:3099 msgid "median diff" msgstr "differenza mediana" #: f.enhance.cc:3101 msgid "dark" msgstr "scuro" #: f.enhance.cc:3102 msgid "light" msgstr "chiaro" #: f.enhance.cc:3888 msgid "Click to set center" msgstr "Clicca per impostare il centro" #: f.enhance.cc:3889 msgid "Pull image using the mouse" msgstr "Tira l'immagine con il mouse" #: f.enhance.cc:3890 msgid "" "left drag: blend image \n" "right drag: restore image" msgstr "" "Trascina col tasto sinistra: miscela \n" "Trascina col tasto destro: usa originale" #: f.enhance.cc:3933 msgid "Normal Blur" msgstr "Sfocatura normale" #: f.enhance.cc:3940 msgid "Radial Blur" msgstr "Sfocatura radiale" #: f.enhance.cc:3954 msgid "Directed Blur" msgstr "Sfumatura interattiva" #: f.enhance.cc:3956 msgid "Blur Span" msgstr "Dimensione" #: f.enhance.cc:3959 msgid "Intensity" msgstr "Intensità" #: f.enhance.cc:3964 msgid "Graduated Blur" msgstr "Sfumatura graduale" #: f.enhance.cc:3969 msgid "Contrast Limit" msgstr "Limite contrasto" #: f.enhance.cc:3974 msgid "Paint Blur" msgstr "Sfocatura dipinta" #: f.enhance.cc:3979 f.mashup.cc:1577 fotoxx.h:1402 msgid "Power" msgstr "Forza" #: f.enhance.cc:3987 f.enhance.cc:4914 msgid "Blur Background" msgstr "Sfuma lo sfondo" #: f.enhance.cc:4918 msgid "constant blur" msgstr "Sfumatura costante" #: f.enhance.cc:4921 msgid "increase blur with distance" msgstr "Sfumatura maggiore con la distanza" #: f.enhance.cc:4923 msgid "min. blur radius" msgstr "Raggio minimo" #: f.enhance.cc:4926 msgid "max. blur radius" msgstr "Raggio massimo" #: f.enhance.cc:4957 f.warp.cc:837 f.warp.cc:2277 msgid "no active Select Area" msgstr "Area di selezione non attiva" #: f.enhance.cc:5139 msgid "Apply repeatedly while watching the image." msgstr "Applica ripetutamente osservando l'immagine" #: f.enhance.cc:5175 msgid "Noise Reduction" msgstr "Riduzione disturbo" #: f.enhance.cc:5215 msgid "dark areas" msgstr "zone scure" #: f.enhance.cc:5217 msgid "all areas" msgstr "tutte le zone" #: f.enhance.cc:6195 msgid "Measure Noise" msgstr "Misura disturbo" #: f.enhance.cc:6196 msgid "Move mouse in a monotone image area." msgstr "Spostare il mouse in una zona monotona/omogenea" #: f.enhance.cc:6511 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" " - Trascinare verso destra e in basso per circondare l'occhio rosso. \n" " - Cliccare col pulsante sinistro sull'occhio rosso per scurirlo. \n" "Per annullare:\n" " - Cliccare col pulsante destro sull'occhio rosso." #: f.enhance.cc:6530 msgid "Red Eye Reduction" msgstr "Riduzione occhi rossi" #: f.enhance.cc:6984 msgid "Color Match Images" msgstr "Allinea colori" #: f.enhance.cc:7015 msgid "mouse radius for color sample" msgstr "Raggio di prelievo del colore" #: f.enhance.cc:7017 f.enhance.cc:7022 fotoxx.h:1397 zfuncs.cc:12681 msgid "Open" msgstr "Apri" #: f.enhance.cc:7018 msgid "image for source color" msgstr "l'immagine di riferimento" #: f.enhance.cc:7020 msgid "click on image to get source color" msgstr "Cliccare nell'immagine per prelevare il colore" #: f.enhance.cc:7023 msgid "image to set matching color" msgstr "l'immagine a cui impostare il colore" #: f.enhance.cc:7025 msgid "click on image to set matching color" msgstr "Cliccare nell'immagine per allineare il colore" #: f.enhance.cc:7092 msgid "select source image color first" msgstr "Selezionare prima l'immagine di riferimento" #: f.enhance.cc:7287 msgid "" "Drag mouse to select. Erase. Repeat. \n" "Click: extend selection to mouse." msgstr "" "Trascinare il mouse per selezionare. Cancellare. Ripetere. \n" "Clic per estendere la selezione col mouse." #: f.enhance.cc:7314 f.widgets.cc:529 msgid "Smart Erase" msgstr "Cancellazione intelligente" #: f.enhance.cc:7319 fotoxx.h:1408 msgid "Radius" msgstr "Raggio" #: f.enhance.cc:7321 f.widgets.cc:525 msgid "Blur" msgstr "Sfuoca" #: f.enhance.cc:7324 msgid "New Area" msgstr "Nuova area" #: f.enhance.cc:7672 f.enhance.cc:8054 msgid "Chromatic Aberration" msgstr "Aberrazione cromatica" #: f.enhance.cc:7711 msgid "Red Factors" msgstr "Fattori del rosso" #: f.enhance.cc:7715 msgid "Blue Factors" msgstr "Fattori del blu" #: f.enhance.cc:7720 msgid "Find optimum factors:" msgstr "Trova fattori ottimali:" #: f.enhance.cc:8095 msgid "Chromatic Color" msgstr "Colore cromatico" #: f.enhance.cc:8098 msgid "Replacement Color" msgstr "Colore di rimpiazzo" #: f.enhance.cc:8101 msgid "Background Color" msgstr "Colore di sfondo" #: f.enhance.cc:8105 msgid "Color match level" msgstr "Soglia di corrispondenza colore" #: f.enhance.cc:8109 msgid "Background Proximity" msgstr "Vicinanza di sfondo" #: f.enhance.cc:8151 msgid "255 iterations, cannot continue" msgstr "255 iterazioni, impossibile proseguire" #: f.enhance.cc:8413 f.widgets.cc:532 msgid "Vignette" msgstr "Effetto vignetta" #: f.enhance.cc:8802 f.widgets.cc:533 msgid "Remove Dust" msgstr "Rimozione polvere" #: f.enhance.cc:8806 msgid "spot size limit" msgstr "Raggio max della polvere" #: f.enhance.cc:8809 msgid "max. brightness" msgstr "Luminosità massima" #: f.enhance.cc:8812 msgid "min. contrast" msgstr "Contrasto minimo" #: f.file.cc:601 msgid "Rename Image File" msgstr "Rinomina immagine" #: f.file.cc:608 msgid "Old Name" msgstr "Vecchio nome" #: f.file.cc:609 f.process.cc:141 msgid "New Name" msgstr "Nuovo nome" #: f.file.cc:618 msgid "previous name" msgstr "Nome precedente" #: f.file.cc:619 msgid "Add 1" msgstr "Aggiungi 1" #: f.file.cc:622 f.file.cc:882 f.file.cc:1629 f.file.cc:1998 msgid "keep this dialog open" msgstr "Mantieni aperto questo dialogo" #: f.file.cc:850 msgid "File Permissions" msgstr "Permessi del file" #: f.file.cc:854 f.meta.cc:833 f.meta.cc:1615 f.meta.cc:1818 msgid "File:" msgstr "File:" #: f.file.cc:1000 msgid "Open Image File" msgstr "Apri immagine" #: f.file.cc:1019 f.process.cc:654 msgid "unknown file type" msgstr "tipo di file sconosciuto" #: f.file.cc:1207 #, c-format msgid "Start of gallery, preceding gallery: %s" msgstr "Inizio galleria, nome della precedente: %s" #: f.file.cc:1208 #, c-format msgid "End of gallery, following gallery: %s" msgstr "Fine galleria, nome della prossima: %s" #: f.file.cc:1355 msgid "Create Blank Image" msgstr "Crea immagine vuota" #: f.file.cc:1357 f.meta.cc:1909 msgid "file name" msgstr "Nome file" #: f.file.cc:1391 msgid "supply a file name" msgstr "fornire un nome di file" #: f.file.cc:1570 msgid "Copy or Move Image File" msgstr "Copia o sposta il file dell'immagine" #: f.file.cc:1615 msgid "New Location:" msgstr "Nuova posizione:" #: f.file.cc:1620 msgid "New Name:" msgstr "Nome nuovo:" #: f.file.cc:1625 msgid "copy (duplicate file)" msgstr "Copia (duplica il file)" #: f.file.cc:1626 msgid "move (remove original)" msgstr "Sposta (cancella il file originale)" #: f.file.cc:1676 f.process.cc:608 f.process.cc:1574 f.process.cc:2155 msgid "Select folder" msgstr "Seleziona cartella" #: f.file.cc:1718 f.tools.cc:1488 msgid "new location is not a folder" msgstr "la destinazione non è una cartella" #: f.file.cc:1737 msgid "new file extension missing or changed" msgstr "nuova estensione file mancante o cambiata" #: f.file.cc:1771 f.file.cc:2075 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "errore in cancellazione: \n" " %s" #: f.file.cc:1845 f.file.cc:3226 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Sovrascrivere il file? \n" " %s" #: f.file.cc:1957 msgid "Delete/Trash Image File" msgstr "Elimina/cestina il file dell'immagine" #: f.file.cc:2029 msgid "GTK g_file_trash() function failed" msgstr "La funzione GTK g_file_trash() è fallita" #: f.file.cc:2048 msgid "not a known image file" msgstr "Il file non contiene un'immagine riconosciuta" #: f.file.cc:2054 msgid "Delete read-only file?" msgstr "Eliminare il file di sola lettura?" #: f.file.cc:2056 msgid "Trash read-only file?" msgstr "Cestinare un file di sola lettura?" #: f.file.cc:2272 #, c-format msgid "Kill active dialog? %s" msgstr "Terminare il dialogo attivo? %s" #: f.file.cc:2322 f.widgets.cc:633 msgid "User Guide" msgstr "Guida utente" #: f.file.cc:2325 f.widgets.cc:634 msgid "Recent Changes" msgstr "Cambiamenti recenti" #: f.file.cc:2328 f.widgets.cc:635 msgid "Edit Functions Overview" msgstr "Sommario delle funzioni" #: f.file.cc:2334 f.widgets.cc:636 msgid "Change Log" msgstr "Cronistoria delle modifiche" #: f.file.cc:2337 f.widgets.cc:637 msgid "Log File" msgstr "Mostra file di log" #: f.file.cc:2343 f.widgets.cc:639 msgid "Command Params" msgstr "Parametri della linea di comando" #: f.file.cc:2346 f.widgets.cc:640 msgid "Translations" msgstr "Come tradurre" #: f.file.cc:2349 f.widgets.cc:641 msgid "Home Page" msgstr "Sito WEB" #: f.file.cc:2352 f.widgets.cc:642 msgid "License" msgstr "Licenza" #: f.file.cc:2355 f.widgets.cc:643 msgid "Privacy" msgstr "Privacy" #: f.file.cc:2358 f.widgets.cc:644 msgid "About" msgstr "Info" #: f.file.cc:2366 f.widgets.cc:665 fotoxx.h:1366 msgid "Help" msgstr "Aiuto" #: f.file.cc:2414 msgid "Save Image File" msgstr "Salva file immagine" #: f.file.cc:2424 msgid "new version" msgstr "nuova versione" #: f.file.cc:2425 msgid "new file ..." msgstr "Nuovo file ..." #: f.file.cc:2426 msgid "replace file" msgstr "rimpiazza file" #: f.file.cc:2433 f.file.cc:3083 msgid "save as new file name or type" msgstr "salva come nuovo nome o diverso tipo" #: f.file.cc:2435 msgid "replace old file (OVERWRITE)" msgstr "rimpiazza il vecchio file (SOVRASCRIVE)" #: f.file.cc:2486 msgid "cannot replace RAW file" msgstr "Impossibile rimpiazzare file RAW" #: f.file.cc:2491 msgid "cannot replace HEIC file" msgstr "Impossibile rimpiazzare file HEIC" #: f.file.cc:2496 msgid "cannot replace JP2 file" msgstr "impossibile rimpiazzare il file JP2" #: f.file.cc:2668 f.file.cc:2727 #, c-format msgid "" "file: %s \n" " exceed 99 versions" msgstr "" "file: %s \n" " eccede le 99 versioni" #: f.file.cc:2821 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:2831 msgid "cannot save as RAW type" msgstr "impossibile salvare come tipo RAW" #: f.file.cc:2912 msgid "save anyway" msgstr "Salva comunque" #: f.file.cc:2980 f.file.cc:2982 msgid "Unable to copy EXIF/IPTC data" msgstr "Non riesco a copiare i dati EXIF/IPTC" #: f.file.cc:3096 f.process.cc:1387 msgid "jpg quality" msgstr "qualità Jpeg" #: f.file.cc:3100 msgid "color depth:" msgstr "Risoluzione del colore:" #: f.file.cc:3106 f.file.cc:3114 msgid "make current" msgstr "Rendi corrente" #: f.file.cc:3107 msgid "(new file becomes current file)" msgstr "(il nuovo file diventa quello corrente)" #: f.file.cc:3110 msgid "permissions:" msgstr "permessi:" #: f.file.cc:3641 f.widgets.cc:432 f.widgets.cc:821 msgid "Permissions" msgstr "Permessi" #: f.file.cc:3646 fotoxx.cc:254 msgid "owner" msgstr "proprietario" #: f.file.cc:3647 fotoxx.cc:255 msgid "group" msgstr "gruppo" #: f.file.cc:3648 fotoxx.cc:256 msgid "other" msgstr "altri" #: f.gallery.cc:1699 f.gallery.cc:1719 msgid "recent images" msgstr "Immagini recenti" #: f.gallery.cc:1700 f.gallery.cc:1724 msgid "newest images" msgstr "Immagini più nuove" #: f.gallery.cc:1779 msgid "no albums found" msgstr "nessun album trovato" #: f.gallery.cc:1789 f.gallery.cc:1803 msgid "Current Album" msgstr "Album corrente" #: f.gallery.cc:1806 msgid "no current album" msgstr "nessun album attivo" #: f.gallery.cc:1828 msgid "" " Reset all galleries\n" " to file name ascending" msgstr "" " Riordina tutte le gallerie\n" " con nome ascendente" #: f.gallery.cc:1849 msgid "Gallery Sort" msgstr "Riordina galleria" #: f.gallery.cc:1853 msgid "File Name" msgstr "Nome file" #: f.gallery.cc:1854 msgid "File Mod Date/Time" msgstr "Data di modifica file" #: f.gallery.cc:1855 msgid "Photo Date/Time (EXIF)" msgstr "Data della foto (EXIF)" #: f.gallery.cc:1856 msgid "File Size (bytes)" msgstr "Dimensione immagine in byte" #: f.gallery.cc:1857 msgid "Image Size (pixels)" msgstr "Dimensioni immagine (pixel)" #: f.gallery.cc:1859 msgid "ascending" msgstr "ascendente" #: f.gallery.cc:1860 msgid "descending" msgstr "discendente" #: f.gallery.cc:2759 msgid "" "Use EXIF photo date or \n" " file modification date?" msgstr "" "Usare data EXIF o \n" " data del file?" #: f.gallery.cc:2783 f.widgets.cc:648 msgid "File" msgstr "File" #: f.gallery.cc:3589 f.gallery.cc:3593 msgid "Image File" msgstr "File immagine" #: f.gallery.cc:3702 msgid "Select Image Files" msgstr "Seleziona i file immagine" #: f.gallery.cc:3768 #, c-format msgid "exceed %d selected files" msgstr "selezionati troppi file, %d" #: f.gallery.cc:3780 #, c-format msgid "remove %d duplicates?" msgstr "eliminare %d duplicati?" #: f.gallery.cc:4229 f.widgets.cc:457 msgid "Bookmarks" msgstr "Segnalibri" #: f.gallery.cc:4229 f.gallery.cc:4337 msgid "Edit Bookmarks" msgstr "Gestisci segnalibri" #: f.gallery.cc:4311 msgid "" "Click a list position. Click a gallery thumbnail for the new bookmark.\n" "Bookmark for thumbnail will be added. Change the name and press [Rename]." msgstr "" "Cliccare una posizione nella lista; cliccare una miniatura per il nuovo " "segnalibro.\n" "Il segnalibro verrà aggiunto: cambiare il nome e premere [Rinomina]." #: f.gallery.cc:4514 msgid "unable to save bookmarks file" msgstr "Impossibile salvare i segnalibri" #: f.mashup.cc:212 msgid "Project name" msgstr "Nome progetto" #: f.mashup.cc:216 msgid "Layout and background image" msgstr "Disposizione e immagine di sfondo" #: f.mashup.cc:220 msgid "choose an image file" msgstr "Scegliere una immagine" #: f.mashup.cc:221 msgid "use current image file" msgstr "Usare l'immagine corrente" #: f.mashup.cc:222 msgid "specify layout size and color" msgstr "Specificare dimensioni e colore" #: f.mashup.cc:223 msgid "open a Mashup project file" msgstr "Aprire un file di progetto del collage" #: f.mashup.cc:238 msgid "enter a project name" msgstr "immettere un nome progetto" #: f.mashup.cc:269 msgid "no current file" msgstr "nessun file corrente" #: f.mashup.cc:296 msgid "Make Layout Image" msgstr "Crea immagine collage" #: f.mashup.cc:403 msgid "Edit Images" msgstr "Modifica immagini" #: f.mashup.cc:404 f.mashup.cc:2433 msgid "Edit Text" msgstr "Modifica testi" #: f.mashup.cc:405 msgid "Edit Line" msgstr "Edit linea" #: f.mashup.cc:406 msgid "Rescale" msgstr "Riscala" #: f.mashup.cc:411 msgid "add or edit images" msgstr "Aggiungi o modifica immagini" #: f.mashup.cc:413 msgid "add or edit text" msgstr "Aggiungi o cambia testi" #: f.mashup.cc:415 msgid "add or edit lines/arrows" msgstr "Aggiungere o modificare linee/frecce" #: f.mashup.cc:417 msgid "change project scale" msgstr "cambia scala del progetto" #: f.mashup.cc:419 msgid "project complete" msgstr "Progetto completo" #: f.mashup.cc:421 msgid "cancel project" msgstr "Annulla progetto" #: f.mashup.cc:439 msgid "rescale project" msgstr "Riscala il progetto" #: f.mashup.cc:493 msgid "save Mashup output file" msgstr "Salva risultato del collage" #: f.mashup.cc:513 msgid "save Mashup project file?" msgstr "Salvo il progetto collage?" #: f.mashup.cc:528 msgid "delete Mashup project file?" msgstr "Elimino il progetto collage?" #: f.mashup.cc:588 msgid "Open Project" msgstr "Apri un progetto" #: f.mashup.cc:617 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "File immagine composizione mancante: \n" " %s" #: f.mashup.cc:674 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "File immagine di sfondo mancante: \n" " %s " #: f.mashup.cc:980 msgid "project file is defective" msgstr "Il file di progetto è difettoso" #: f.mashup.cc:1010 msgid "Save Project" msgstr "Salva il progetto" #: f.mashup.cc:1151 msgid "layout exceeds 2 gigabytes" msgstr "Il collage eccede 2 gigabyte" #: f.mashup.cc:1221 msgid "Click image to select, drag image to move." msgstr "Clicca immagine per selezionare, trascina per spostare." #: f.mashup.cc:1222 msgid "Make black margins transparent" msgstr "Rende trasparenti i margini neri" #: f.mashup.cc:1254 f.mashup.cc:1320 msgid "Add Image" msgstr "Aggiungi immagine" #: f.mashup.cc:1261 msgid "Current image:" msgstr "Immagine corrente: " #: f.mashup.cc:1265 msgid "Cycle through images:" msgstr "Cicla attraverso le immagini: " #: f.mashup.cc:1272 msgid "Scale" msgstr "Scala" #: f.mashup.cc:1281 msgid "Stacking Order" msgstr "Ordine di impilamento" #: f.mashup.cc:1282 msgid "Raise" msgstr "Solleva" #: f.mashup.cc:1283 msgid "Lower" msgstr "Abbassa" #: f.mashup.cc:1286 msgid "Base Transparency" msgstr "Trasparenza" #: f.mashup.cc:1290 msgid "Var. Transparency" msgstr "Trasparenza manuale" #: f.mashup.cc:1291 msgid "Paint" msgstr "Dipinto" #: f.mashup.cc:1294 msgid "Bend and fine-align" msgstr "Deforma e collima" #: f.mashup.cc:1295 f.widgets.cc:661 msgid "Warp" msgstr "Deforma" #: f.mashup.cc:1304 zfuncs.cc:12896 zfuncs.cc:12905 msgid "Margins" msgstr "Margini" #: f.mashup.cc:1305 msgid "Hard" msgstr "Forte" #: f.mashup.cc:1306 msgid "Blend" msgstr "Mescola" #: f.mashup.cc:1320 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:1810 msgid "Pull on the image with the mouse." msgstr "Trascina il mouse sull'immagine" #: f.mashup.cc:1826 msgid "Warp Image" msgstr "Deforma immagine" #: f.mashup.cc:1832 f.warp.cc:1209 msgid "warp span" msgstr "Dimensione deformazione: " #: f.mashup.cc:2264 #, c-format msgid "exceeded %d images" msgstr "troppe immagini (massimo %d)" #: f.mashup.cc:2406 msgid "Enter text, [Add] to layout, edit properties." msgstr "Immetti il testo, [Aggiungi], modifica le opzioni" #: f.mashup.cc:2486 msgid "Text File:" msgstr "File di testo:" #: f.mashup.cc:2490 msgid "add entered text to layout" msgstr "Aggiunge testo alla composizione" #: f.mashup.cc:2624 msgid "click position to add text" msgstr "Clicca la posizione del testo" #: f.mashup.cc:2630 #, c-format msgid "exceeded %d text entries" msgstr "troppi testi (massimo %d)" #: f.mashup.cc:2635 msgid "Add Text" msgstr "Aggiungi scritte" #: f.mashup.cc:2744 msgid "Set line properties, [Add] to layout, edit." msgstr "Imposta opzioni linea, [Aggiungi], modifica" #: f.mashup.cc:2768 msgid "Edit Line/Arrow" msgstr "Modifica linea/freccia" #: f.mashup.cc:2823 msgid "add line/arrow to layout" msgstr "Aggiunge linea/freccia alla composizione" #: f.mashup.cc:2915 msgid "click position to add line" msgstr "Clicca la posizione della linea" #: f.mashup.cc:2921 #, c-format msgid "exceeded %d line entries" msgstr "Immesse più di %d linee" #: f.mashup.cc:2926 msgid "Add Line" msgstr "Aggiungi linea" #: f.mashup.cc:4171 msgid "Image Montage" msgstr "Esposizione d'immagini" #: f.mashup.cc:4180 msgid "Frame Width" msgstr "Larghezza cornice" #: f.mashup.cc:4183 f.mashup.cc:4191 msgid "Margin" msgstr "Spaziatura" #: f.mashup.cc:4188 msgid "Image Columns" msgstr "Numero di colonne" #: f.mashup.cc:4205 #, c-format msgid "exceed %d rows" msgstr "Troppe righe: %d" #: f.mashup.cc:4301 msgid "Optimize" msgstr "Ottimizza" #: f.mashup.cc:4309 #, c-format msgid "column difference: %d pixels" msgstr "Differenza tra colonne: %d pixel" #: f.mashup.cc:4367 #, c-format msgid "" "column difference: %d pixels \n" "Make columns even?" msgstr "" "Differenza tra colonne: %d pixel \n" "Pareggiare le colonne?" #: f.mashup.cc:4389 msgid "Save with unique montage name" msgstr "Salva con nome unico" #: f.mashup.cc:4391 msgid "unique name:" msgstr "Nome unico:" #: f.mashup.cc:4393 msgid "create image map" msgstr "Crea mappa dell'immagine" #: f.mashup.cc:4410 f.meta.cc:9151 msgid "supply a reasonable name" msgstr "Fornire un nome corretto" #: f.mashup.cc:4421 msgid "save montage" msgstr "Salva esposizione" #: f.mashup.cc:4441 #, c-format msgid "map file saved: %s" msgstr "File di mappa salvato: %s" #: f.mashup.cc:4486 #, c-format msgid "%d max images exceeded" msgstr "Troppe immagini: %d" #: f.mashup.cc:4560 f.mashup.cc:4566 #, c-format msgid "image frame is too large: %d x %d" msgstr "La dimensione della cornice è troppo grande: %d x %d" #: f.mashup.cc:4871 #, c-format msgid "montage map file invalid: %s" msgstr "File mappa dell'immagine non valido: %s" #: f.meta.cc:245 msgid "Add Metadata Items" msgstr "Aggiunta di voci di metadati" #: f.meta.cc:249 f.meta.cc:1609 f.meta.cc:3190 msgid "click to select" msgstr "cliccare per selezionare" #: f.meta.cc:254 msgid "click to unselect" msgstr "Cliccare per deselezionare" #: f.meta.cc:476 msgid "Extras" msgstr "Extra" #: f.meta.cc:476 msgid "View Metadata" msgstr "Mostra i metadati" #: f.meta.cc:661 msgid "View All Metadata" msgstr "Visualizza tutti i metadati" #: f.meta.cc:826 msgid "Edit Metadata" msgstr "Modifica metadati (normale)" #: f.meta.cc:829 msgid "save metadata to file" msgstr "Salva i metadati in un file" #: f.meta.cc:838 msgid "Image Date" msgstr "Data immagine:" #: f.meta.cc:841 msgid "Time" msgstr "Ora" #: f.meta.cc:849 msgid "Rating (stars):" msgstr "Punteggio (stelle):" #: f.meta.cc:861 msgid "Caption" msgstr "Titolo" #: f.meta.cc:866 msgid "Comments" msgstr "Commenti" #: f.meta.cc:873 msgid "Location" msgstr "Luogo" #: f.meta.cc:876 msgid "Country" msgstr "Nazione" #: f.meta.cc:899 msgid "Image Tags" msgstr "Etichette" #: f.meta.cc:905 msgid "Recent Tags" msgstr "Etichette recenti:" #: f.meta.cc:911 f.meta.cc:2081 msgid "Enter New Tag" msgstr "Immetti nuova etichetta" #: f.meta.cc:917 f.meta.cc:2087 f.meta.cc:4750 msgid "Matching Tags" msgstr "Etichette corrispondenti" #: f.meta.cc:925 f.meta.cc:2096 f.meta.cc:2556 f.meta.cc:4757 msgid "Defined Tags Category" msgstr "Categorie conosciute" #: f.meta.cc:933 msgid "search known locations" msgstr "ricerca luoghi conosciuti" #: f.meta.cc:934 msgid "search using web service" msgstr "ricerca tramite servizio web" #: f.meta.cc:1384 f.meta.cc:3800 f.meta.cc:7709 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "Latitudine/Longitudine errati: %s %s" #: f.meta.cc:1441 f.widgets.cc:477 msgid "Manage Tags" msgstr "Gestione etichette" #: f.meta.cc:1441 msgid "orphan tags" msgstr "Etichette orfane" #: f.meta.cc:1445 msgid "category" msgstr "Categoria" #: f.meta.cc:1448 msgid "tag" msgstr "Etichetta" #: f.meta.cc:1455 msgid "Defined Tags:" msgstr "Etichette definite:" #: f.meta.cc:1605 msgid "Edit Any Metadata" msgstr "Modifica metadati (avanzato)" #: f.meta.cc:1605 f.meta.cc:3185 msgid "Full List" msgstr "Lista completa" #: f.meta.cc:1619 f.meta.cc:3202 msgid "key name" msgstr "Nome chiave" #: f.meta.cc:1622 f.meta.cc:3203 msgid "key value" msgstr "Valore chiave" #: f.meta.cc:1815 msgid "Delete Metadata" msgstr "Elimina metadati..." #: f.meta.cc:1821 fotoxx.h:1317 msgid "All" msgstr "Tutto" #: f.meta.cc:1822 msgid "One Key:" msgstr "Una chiave:" #: f.meta.cc:1908 msgid "choose options" msgstr "scegli opzioni" #: f.meta.cc:1910 msgid "caption" msgstr "titolo" #: f.meta.cc:1911 msgid "comment" msgstr "commento" #: f.meta.cc:2057 msgid "Batch Add/Remove Tags" msgstr "Aggiungi/elimina etichette in massa" #: f.meta.cc:2070 msgid "tags to add" msgstr "Etichette da aggiungere" #: f.meta.cc:2071 msgid "tags to remove" msgstr "Etichette da rimuovere" #: f.meta.cc:2176 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " troppe etichette" #: f.meta.cc:2191 msgid "repeat with same files?" msgstr "Ripetere sugli stessi file di prima?" #: f.meta.cc:2331 msgid "specify files and tags" msgstr "specificare file ed etichette" #: f.meta.cc:2535 f.widgets.cc:596 msgid "Batch Rename Tags" msgstr "Rinomina etichette in massa" #: f.meta.cc:2545 msgid "(click defined tag)" msgstr "(cliccare le etichette definite)" #: f.meta.cc:2547 f.process.cc:815 msgid "Rename to" msgstr "Rinomina come" #: f.meta.cc:2564 msgid "old tag name >> new tag name" msgstr "vecchia etichetta >> nuova etichetta" #: f.meta.cc:2620 #, 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:2774 msgid "max tags exceeded" msgstr "Richieste troppe etichette" #: f.meta.cc:2843 f.meta.cc:2978 msgid "Batch Photo Date/Time" msgstr "Elabora date in massa" #: f.meta.cc:2851 msgid "set a new date/time:" msgstr "Imposta nuove data e ora:" #: f.meta.cc:2859 msgid "shift existing date/time:" msgstr "Sposta data e ora:" #: f.meta.cc:2885 msgid "test: show changes, do not update files" msgstr "Prova solo: mostra senza modificare i file" #: f.meta.cc:2910 msgid "please make a choice" msgstr "effettuare una scelta" #: f.meta.cc:2915 f.meta.cc:3276 f.meta.cc:3467 msgid "no files selected" msgstr "Nessun file selezionato" #: f.meta.cc:2945 f.meta.cc:2955 f.meta.cc:2961 msgid "invalid date/time format" msgstr "Formato data/ora errato" #: f.meta.cc:3185 msgid "Batch Add/Change Metadata" msgstr "Aggiungi/Modifica metadati in massa" #: f.meta.cc:3270 msgid "enter key names" msgstr "Immetti i nomi delle etichette" #: f.meta.cc:3353 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:3450 msgid "Batch Report Metadata" msgstr "Prospetto metadati multipli" #: f.meta.cc:3456 msgid "list of reported metadata items" msgstr "lista di metadati trovati" #: f.meta.cc:3599 f.widgets.cc:600 msgid "Batch Geotags" msgstr "Applica geotag in massa" #: f.meta.cc:3640 msgid "location" msgstr "Luogo" #: f.meta.cc:3643 msgid "country" msgstr "Nazione" #: f.meta.cc:3676 msgid "Adding Geotags" msgstr "Aggiunta Geotag" #: f.meta.cc:3785 msgid "" "data is incomplete \n" " proceed?" msgstr "" "I dati sono incompleti \n" " proseguire?" #: f.meta.cc:3873 msgid "Report Image Locations" msgstr "Cerca luoghi con immagini" #: f.meta.cc:3874 msgid "Group by country" msgstr "Raggruppa per nazione" #: f.meta.cc:3875 msgid "Group by country/location" msgstr "Raggruppa per nazione/luogo" #: f.meta.cc:3876 msgid "Group by country/location/date" msgstr "Raggruppa per nazione/luogo/data" #: f.meta.cc:3877 msgid "Group by date/country/location" msgstr "Raggruppa per date/nazione/luogo" #: f.meta.cc:3880 msgid "Combine within" msgstr "Combina entro" #: f.meta.cc:3882 msgid "days" msgstr "giorni" #: f.meta.cc:3994 msgid "Image Locations" msgstr "Locazioni delle immagini" #: f.meta.cc:4263 msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" msgstr "Gen Feb Mar Apr Mag Giu Lug Ago Set Ott Nov Dic" #: f.meta.cc:4655 msgid "Search Images" msgstr "Ricerca immagini" #: f.meta.cc:4659 msgid "images to search:" msgstr "immagini da cercare:" #: f.meta.cc:4660 msgid "all" msgstr "tutte" #: f.meta.cc:4661 msgid "current set only" msgstr "insieme corrente" #: f.meta.cc:4664 msgid "matching images:" msgstr "immagini corrispondenti:" #: f.meta.cc:4665 msgid "make new set" msgstr "crea nuovo insieme" #: f.meta.cc:4666 msgid "add to set" msgstr "aggiungi all'insieme" #: f.meta.cc:4667 msgid "remove" msgstr "rimuovi" #: f.meta.cc:4672 msgid "select last version only" msgstr "seleziona solo le ultime versioni" #: f.meta.cc:4673 msgid "original + last version" msgstr "originale + ultima versione" #: f.meta.cc:4675 msgid "original + all versions" msgstr "originale + tutte le versioni" #: f.meta.cc:4676 f.process.cc:166 f.process.cc:172 f.process.cc:180 #: f.process.cc:2058 msgid "no change" msgstr "NON modificare" #: f.meta.cc:4681 msgid "report type:" msgstr "Tipo di risultato:" #: f.meta.cc:4682 msgid "gallery" msgstr "galleria" #: f.meta.cc:4688 msgid "date range" msgstr "Intervallo date: " #: f.meta.cc:4693 msgid "photo date" msgstr "Foto per data" #: f.meta.cc:4694 msgid "file date" msgstr "Foto per data del file" #: f.meta.cc:4695 msgid "(yyyy-mm-dd)" msgstr "(aaaa-mm-gg)" #: f.meta.cc:4700 msgid "rating range" msgstr "Intervallo di punteggio" #: f.meta.cc:4708 msgid "all/any" msgstr "Tutte/Qualsiasi" #: f.meta.cc:4711 msgid "search tags" msgstr "Cerca etichette" #: f.meta.cc:4718 msgid "search text" msgstr "Cerca testo" #: f.meta.cc:4724 msgid "search files" msgstr "Cerca nei file" #: f.meta.cc:4730 msgid "search locations" msgstr "cerca nei luoghi" #: f.meta.cc:4734 msgid "enter cities, countries" msgstr "Immettere nomi di città o nazioni" #: f.meta.cc:4739 msgid "search other metadata" msgstr "cerca in altri metadati" #: f.meta.cc:4746 msgid "Enter Search Tag" msgstr "Immettere etichette di ricerca" #: f.meta.cc:5060 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:5067 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:5135 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "Date di ricerca non ragionevoli \n" " %s %s" #: f.meta.cc:5160 msgid "stars range not reasonable" msgstr "punteggio (stelle) non ammissibile" #: f.meta.cc:5718 msgid "Always reported: date, stars, tags, caption, comment" msgstr "empre riportati: data, stelle, etichette, titolo, commento" #: f.meta.cc:5750 msgid "Additional Items for Report" msgstr "Informazioni addizionali da riportare" #: f.meta.cc:5757 msgid "Keyword" msgstr "parola chiave" #: f.meta.cc:5771 msgid "Match Criteria" msgstr "criterio di corrispondenza" #: f.meta.cc:5904 #, c-format msgid "bad number: %s" msgstr "numero errato: %s" #: f.meta.cc:6184 msgid "date format is YYYY-MM-DD" msgstr "Il formato della data è AAAA-MM-GG" #: f.meta.cc:6188 msgid "date is invalid" msgstr "la data non è valida" #: f.meta.cc:6226 msgid "time format is HH:MM [:SS]" msgstr "Il formato dell'ora è HH:MM [:SS]" #: f.meta.cc:6230 msgid "time is invalid" msgstr "L'ora non è valida" #: f.meta.cc:7328 msgid "not found" msgstr "non trovata" #: f.meta.cc:7329 msgid "location and country required" msgstr "Indicare luogo e nazione" #: f.meta.cc:7586 msgid "choose location" msgstr "Scegli luogo" #: f.meta.cc:7886 msgid "Set Map Markers" msgstr "Imposta segnalini sulla mappa" #: f.meta.cc:7887 msgid "mark all image files" msgstr "Mostra foto inerenti alla mappa" #: f.meta.cc:7888 msgid "mark current gallery" msgstr "Considera solo la galleria corrente" #: f.meta.cc:7989 msgid "choose map file" msgstr "Scegliere file di mappa" #: f.meta.cc:8133 msgid "" "fotoxx-maps package not installed \n" "(see https://kornelix.net)" msgstr "" "pacchetto fotoxx-maps non installato \n" "(vedi https://kornelix.net)" #: f.meta.cc:8138 #, c-format msgid "map file %s is missing" msgstr "File di mappa %s mancante" #: f.meta.cc:8142 #, 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:8468 f.meta.cc:9020 msgid "No matching images found" msgstr "Nessuna immagine soddisfa la ricerca" #: f.meta.cc:8562 msgid "Net Map Source" msgstr "Scegli una mappa internet" #: f.meta.cc:9098 msgid "Net Map Locations" msgstr "Elenco dei luoghi della mappa" #: f.meta.cc:9104 msgid "map location:" msgstr "Punto della mappa:" #: f.pixmap.cc:3227 f.pixmap.cc:3279 msgid "HEIF files not supported" msgstr "File HEIF non supportato" #: f.pixmap.cc:3351 f.pixmap.cc:3402 msgid "JP2 files not supported" msgstr "File JP2 non supportato" #: f.process.cc:134 f.widgets.cc:588 msgid "Batch Convert" msgstr "Converti file in massa" #: f.process.cc:145 msgid "Sequence Numbers" msgstr "Numeri sequenziali per nome" #: f.process.cc:147 msgid "base" msgstr "comune" #: f.process.cc:150 msgid "adder" msgstr "addizione" #: f.process.cc:155 msgid "New Location" msgstr "Nuova destinazione:" #: f.process.cc:160 msgid "New File Type" msgstr "Nuovo tipo di file" #: f.process.cc:169 f.process.cc:1390 msgid "Color Depth:" msgstr "Risoluzione del colore" #: f.process.cc:175 f.process.cc:2053 msgid "max. Width" msgstr "Larghezza max" #: f.process.cc:184 msgid "Delete Originals" msgstr "Elimina gli originali" #: f.process.cc:185 f.process.cc:821 msgid "Copy Metadata" msgstr "Copia i metadati" #: f.process.cc:199 f.process.cc:824 msgid "Overlay Image" msgstr "Immagine sovrimpressa" #: f.process.cc:202 f.warp.cc:4547 msgid "Width %" msgstr "Larghezza %" #: f.process.cc:207 msgid "Position" msgstr "Posizione" #: f.process.cc:219 msgid "Make constant size for screen:" msgstr "Fissare dimensione per schermo:" #: f.process.cc:227 msgid "" "plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s" msgstr "" "plugin (anno mese giorno vecchionome sequence) $yyyy $mm $dd $oldname $s" #: f.process.cc:371 #, c-format msgid "file type not supported: %s \n" msgstr "Tipo di file non supportato: %s \n" #: f.process.cc:507 f.process.cc:2116 msgid "cannot create new file" msgstr "Errore in creazione del nuovo file" #: f.process.cc:554 msgid "updating albums ..." msgstr "aggiornamento degli album..." #: f.process.cc:732 #, c-format msgid "invalid plugin: %s" msgstr "plugin non valido: %s" #: f.process.cc:739 msgid "you must use either $s or $oldname" msgstr "occorre usare $s oppure $oldname" #: f.process.cc:744 msgid "$s plugin needs base and adder" msgstr "il $s richiede base e addizione" #: f.process.cc:749 msgid "base and adder need $s plugin" msgstr "per base e addizione serve plugin $s" #: f.process.cc:763 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "Dimensioni massime %d x %d non ragionevoli" #: f.process.cc:770 msgid "specify overlay image file" msgstr "Specifica file da sovraimprimere" #: f.process.cc:786 msgid "specify overlay position" msgstr "Specificare posizione per sovraimpressione" #: f.process.cc:814 #, c-format msgid "Convert %d image files" msgstr "Conversione %d file immagine" #: f.process.cc:816 msgid "Convert to" msgstr "Converti a" #: f.process.cc:818 msgid "Resize within" msgstr "Ridimensiona entro" #: f.process.cc:819 msgid "Output to" msgstr "Produci su" #: f.process.cc:825 msgid "*** Delete Originals ***" msgstr "*** ELIMINA gli originali ***" #: f.process.cc:826 msgid "*** Replace Originals ***" msgstr "*** RIMPIAZZA gli originali ***" #: f.process.cc:827 msgid "PROCEED?" msgstr "PROSEGUO?" #: f.process.cc:983 f.widgets.cc:589 msgid "Batch Upright" msgstr "Raddrizza immagini in massa" #: f.process.cc:989 msgid "Survey all files" msgstr "Controlla tutti i file" #: f.process.cc:1029 msgid "file cannot be read" msgstr "Impossibile leggere il file" #: f.process.cc:1146 msgid "cannot select both options" msgstr "Non selezionare entrambe le opzioni" #: f.process.cc:1187 f.widgets.cc:590 msgid "Batch Delete/Trash" msgstr "Elimina/cestina in massa" #: f.process.cc:1192 msgid "delete" msgstr "Elimina" #: f.process.cc:1195 msgid "trash" msgstr "Cestina" #: f.process.cc:1258 msgid "Purging deleted files from albums \n" msgstr "Pulizia del file cancellati dagli album \n" #: f.process.cc:1338 msgid "Batch Convert RAW Files" msgstr "Converti file RAW in massa" #: f.process.cc:1377 msgid "output location" msgstr "Cartella o folder di uscita" #: f.process.cc:1382 msgid "File Type" msgstr "Tipo di File" #: f.process.cc:1406 msgid "amount" msgstr "Quantità" #: f.process.cc:1409 msgid "threshold" msgstr "Soglia" #: f.process.cc:1413 msgid "Fix dead pixels" msgstr "Correggi pixel morti" #: f.process.cc:1416 msgid "dead pixel map file" msgstr "file mappa di pixel morti" #: f.process.cc:1419 msgid "Fix pixel bias" msgstr "Correggi bias pixel" #: f.process.cc:1422 msgid "pixel bias map file" msgstr "file mappa bias pixel" #: f.process.cc:1727 msgid "growisofs not installed" msgstr "growisofs non è installato" #: f.process.cc:1774 msgid "no DVD/BlueRay device found" msgstr "nessuna dispositivo DVD/Blueray trovato" #: f.process.cc:1797 msgid "Burn Images to DVD/BlueRay" msgstr "Registra immagini su DVD/Blueray" #: f.process.cc:1802 msgid "Select device" msgstr "Selezionare dispositivo" #: f.process.cc:1889 f.widgets.cc:355 msgid "Create a file of selected image files" msgstr "Crea un file con le immagini selezionate" #: f.process.cc:1915 f.process.cc:1984 msgid "Output File" msgstr "File d'uscita" #: f.process.cc:1936 msgid "no input files selected" msgstr "nessun file di ingresso selezionato" #: f.process.cc:1942 msgid "no output file selected" msgstr "nessun file d'uscita selezionato" #: f.process.cc:2044 f.widgets.cc:594 msgid "Export Files" msgstr "Esporta immagini" #: f.process.cc:2049 msgid "To Location" msgstr "Verso la cartella" #: f.process.cc:2060 msgid "export metadata" msgstr "esporta metadati" #: f.process.cc:2091 msgid "file type not supported" msgstr "Tipo di file non supportato" #: f.process.cc:2173 msgid "location is not a folder" msgstr "La locazione immessa non è una cartella" #: f.process.cc:2225 msgid "Script Files" msgstr "File di script" #: f.process.cc:2229 msgid "begin making a script file" msgstr "Inizia a comporre uno script" #: f.process.cc:2232 msgid "finish making a script file" msgstr "Termina la composizione dello script" #: f.process.cc:2276 msgid "script already started" msgstr "Script già in esecuzione" #: f.process.cc:2280 msgid "start a new script file" msgstr "Crea nuovo file di script" #: f.process.cc:2301 msgid "perform edits to be included in the script file" msgstr "Eseguire le operazioni da registrare nello script" #: f.process.cc:2318 msgid "script file error" msgstr "Errore nel file script" #: f.process.cc:2323 #, c-format msgid "%s added to script" msgstr "%s aggiunto allo script" #: f.process.cc:2333 msgid "no script file was started" msgstr "Nessuno script è stato avviato" #: f.process.cc:2341 msgid "script file closed" msgstr "File script chiuso" #: f.process.cc:2374 f.process.cc:2395 msgid "no script files found" msgstr "nessuno script trovato" #: f.process.cc:2418 #, c-format msgid "" "script error: %s \n" " %s" msgstr "" "errore script: %s \n" " %s" #: f.process.cc:2440 #, c-format msgid "unknown edit function: %s" msgstr "Funzione di edit sconosciuta: %s" #: f.process.cc:2452 #, c-format msgid "load widgets failed: %s" msgstr "Errore di caricamento widget: %s" #: f.process.cc:2468 #, c-format msgid "script file format error: %s" msgstr "Formato file script errato: %s" #: f.process.cc:2494 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "Errore in apertura: %s \n" " %s" #: f.process.cc:2511 msgid "script complete" msgstr "script completato" #: f.process.cc:2553 f.widgets.cc:603 msgid "Batch Script" msgstr "Script (batch)" #: f.process.cc:2560 msgid "Select Script" msgstr "Seleziona uno script" #: f.process.cc:2561 msgid "script file to run" msgstr "File script da eseguire" #: f.tools.cc:93 msgid "Folders for image files (subfolders included automatically)." msgstr "" "Cartelle delle immagini (le sottocartelle sono incluse automaticamente)." #: f.tools.cc:95 msgid "Select to add, click on X to delete." msgstr "Selezionare per aggiungere, cliccare X per eliminare." #: f.tools.cc:96 msgid "folder for thumbnails" msgstr "Cartella per le miniature" #: f.tools.cc:97 msgid "extra metadata items to include in index" msgstr "Metadati extra da includere nell'indice" #: f.tools.cc:98 msgid "force a full re-index of all image files" msgstr "Forza una reindicizzazinoe completa" #: f.tools.cc:99 msgid "" "Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Funzione indice terminata. \n" "L'indice è richiesto per le ricerche e le mappe \n" "e per generare le gallerie in modo rapido." #: f.tools.cc:136 msgid "Index Image Files" msgstr "Gestione file indice" #: f.tools.cc:324 msgid "Choose top image folders" msgstr "Scegli la cartella principale delle immagini" #: f.tools.cc:325 msgid "Choose thumbnail folder" msgstr "Scegliere folder miniature" #: f.tools.cc:326 msgid "" "All image files will be re-indexed. \n" " Continue?" msgstr "" "Tutte le immagini verranno reindicizzate. \n" " Continuare?" #: f.tools.cc:442 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:448 #, c-format msgid "" "Thumbnails folder: %s \n" "Please remove." msgstr "" "Cartella miniature: %s \n" "Rimuoverla dall'elenco." #: f.tools.cc:451 #, c-format msgid "" "Thumbnails folder: \n" " %s \n" "must be named .../thumbnails" msgstr "" "La folder miniature: \n" " %s \n" "deve chiamarsi .../thumbnails" #: f.tools.cc:454 #, c-format msgid "" "Duplicate or nested folders: \n" " %s \n" " %s \n" "Please remove." msgstr "" "Cartelle duplicate o annidate: \n" " %s \n" " %s \n" "Rimuoverne una dall'elenco." #: f.tools.cc:537 msgid "specify at least 1 top image folder" msgstr "specificare almeno 1 cartella immagine" #: f.tools.cc:542 msgid "specify 1 thumbnail folder" msgstr "Indicare esattamente una sola cartella" #: f.tools.cc:649 msgid "build index" msgstr "costruisci indice" #: f.tools.cc:778 msgid "Top folders have no images" msgstr "Le folder principali non contengono immagini" #: f.tools.cc:785 #, c-format msgid "" "0 old files found, %d new files found.\n" "A full re-index is required. Continue?" msgstr "" "trovati 0 file vecchi, %d file nuovi.\n" "Si richiede una indicizzazione completa. Continuare?" #: f.tools.cc:1286 msgid "Cancel image index function?" msgstr "Interrompere la funzione di indicizzazione?" #: f.tools.cc:1447 #, c-format msgid "" "Do you want to move Fotoxx home? \n" " from: %s \n" " to: %s" msgstr "" "Sicuro di modificare la cartella di Fotoxx? \n" " da: %s \n" " a: %s" #: f.tools.cc:1449 msgid "moving files ..." msgstr "spostamento dei file..." #: f.tools.cc:1473 msgid "new Fotoxx home folder" msgstr "nuova cartella di Fotoxx" #: f.tools.cc:1493 msgid "new location name contains a space" msgstr "la nuova locazione contiene spazi" #: f.tools.cc:1533 msgid "index_config file has no thumbnails folder" msgstr "il file index_config non indica una cartella per le miniature" #: f.tools.cc:1541 msgid "update thumbnail folder failed" msgstr "aggiornamento cartella miniature fallito" #: f.tools.cc:1544 msgid "thumbnails folder not changed" msgstr "cartella delle miniature non cambiata" #: f.tools.cc:1545 #, c-format msgid "new thumbnails folder: %s/thumbnails" msgstr "nuova cartella miniature: %s/thumbnails" #: f.tools.cc:1556 msgid "Fotoxx will restart" msgstr "Fotoxx si riavvierà" #: f.tools.cc:1572 msgid "Recent Files Gallery" msgstr "Galleria file recenti" #: f.tools.cc:1573 msgid "Newest Files Gallery" msgstr "Galleria delle immagini nuove" #: f.tools.cc:1574 msgid "Specific Gallery" msgstr "Galleria di cartella specifica" #: f.tools.cc:1575 msgid "Album Gallery" msgstr "Galleria di album specifico" #: f.tools.cc:1576 msgid "Previous Gallery" msgstr "Galleria precedente" #: f.tools.cc:1577 msgid "Previous File" msgstr "Ultima aperta" #: f.tools.cc:1578 msgid "Specific File" msgstr "Immagine specifica" #: f.tools.cc:1579 f.widgets.cc:434 msgid "Blank Window" msgstr "Mostra/Nascondi immagine" #: f.tools.cc:1604 #, c-format msgid "%c of scale" msgstr "%c di scala" #: f.tools.cc:1647 msgid "Preferences and Settings" msgstr "Impostazioni e preferenze" #: f.tools.cc:1650 msgid "Startup Display:" msgstr "Schermata di avvio:" #: f.tools.cc:1656 msgid "Background Colors:" msgstr "Colori di sfondo:" #: f.tools.cc:1658 msgid "F-View" msgstr "Foto" #: f.tools.cc:1661 msgid "G-View" msgstr "Galleria" #: f.tools.cc:1665 msgid "Menu Style:" msgstr "Stile di menù:" #: f.tools.cc:1667 msgid "Icons" msgstr "Icone" #: f.tools.cc:1669 msgid "Both" msgstr "Entrambe" #: f.tools.cc:1671 msgid "Icon size" msgstr "Dimensione icone" #: f.tools.cc:1675 msgid "Menu Colors:" msgstr "Colori del menù:" #: f.tools.cc:1684 msgid "Dialog Font:" msgstr "Carattere per dialoghi:" #: f.tools.cc:1703 msgid "JPEG file save quality:" msgstr "Qualità Jpeg in salvataggio:" #: f.tools.cc:1706 msgid "(90 = high quality)" msgstr "(90=alta qualità)" #: f.tools.cc:1709 msgid "TIFF file compression method" msgstr "Metodo compressione file TIFF" #: f.tools.cc:1715 msgid "PNG file compression level" msgstr "Livello compressione file PNG" #: f.tools.cc:1721 msgid "Curve Node Separation, Capture Range:" msgstr "Separazione nodi curve, distanza di cattura:" #: f.tools.cc:1727 msgid "Map Marker Size:" msgstr "Dimensione marcatore su mappa" #: f.tools.cc:1733 msgid "Show Images (F-view, G-view):" msgstr "Mostra immagini (Foto, Galleria):" #: f.tools.cc:1735 msgid "last version only" msgstr "Solo ultime versioni" #: f.tools.cc:1736 msgid "all images" msgstr "tutte le immagini" #: f.tools.cc:1739 msgid "Image Position in Window:" msgstr "Posizione immagine nella finestra:" #: f.tools.cc:1741 msgid "centered" msgstr "centrata" #: f.tools.cc:1742 msgid "right side" msgstr "a destra" #: f.tools.cc:1745 msgid "Image Index Level:" msgstr "Livello indice dell'immagine" #: f.tools.cc:1749 msgid "Fotoxx started directly (2)" msgstr "Fotoxx lanciato direttamente (2)" #: f.tools.cc:1753 msgid "Fotoxx started by file manager (1)" msgstr "Fotoxx lanciato da file manager (1)" #: f.tools.cc:1756 msgid "RAW File Loader:" msgstr "Caricatore file RAW:" #: f.tools.cc:1762 msgid "RAW Conversion Options:" msgstr "Opzioni conversione RAW:" #: f.tools.cc:1765 msgid "extend dynamic range for dim images" msgstr "estendi la dinamica delle immagini sciape" #: f.tools.cc:1768 msgid "use embedded image as a guide" msgstr "usa immagine interna come guida" #: f.tools.cc:1771 msgid "RAW File Types:" msgstr "Tipi di file RAW:" #: f.tools.cc:1776 msgid "Video File Types:" msgstr "Tipi di file video:" #: f.tools.cc:1781 msgid "Video File Play Command:" msgstr "Comando per mostrare video:" #: f.tools.cc:1937 msgid "Select startup folder" msgstr "Seleziona cartella d'avvio" #: f.tools.cc:1944 msgid "Select startup image file" msgstr "Seleziona file d'avvio" #: f.tools.cc:1951 msgid "Select startup album" msgstr "Seleziona album di avviamento" #: f.tools.cc:1976 msgid "rawtherapee-cli (rawtherapee) is not installed" msgstr "rawtherapee-cli (rawtherapee) non è installato" #: f.tools.cc:2006 msgid "startup folder is invalid" msgstr "Cartella iniziale non valida" #: f.tools.cc:2016 msgid "startup file is invalid" msgstr "File iniziale non valido" #: f.tools.cc:2216 f.widgets.cc:372 msgid "Keyboard Shortcuts" msgstr "Scorciatoie di tastiera" #: f.tools.cc:2222 msgid "Reserved Shortcuts \n" msgstr "Scorciatoie riservate \n" #: f.tools.cc:2223 msgid " Z Toggle 1x / fit window \n" msgstr " Z Scambia 1x / adatta finestra \n" #: f.tools.cc:2224 msgid " F1 User Guide, Context Help \n" msgstr " F1 Guida utente, aiuto contestuale \n" #: f.tools.cc:2225 msgid " F10 Full Screen with menus \n" msgstr " F10 Schermo intero con menù \n" #: f.tools.cc:2226 msgid " F11 Full Screen without menus \n" msgstr " F11 Schermo intero senza menù \n" #: f.tools.cc:2227 msgid " Escape Quit dialog, Quit Fotoxx \n" msgstr " Escape Chiude dialoghi, chiude Fotoxx \n" #: f.tools.cc:2228 msgid " Delete Delete/Trash \n" msgstr " Delete Elimina/Cestina \n" #: f.tools.cc:2229 msgid " Arrow keys Navigation \n" msgstr " Arrow keys Navigazione \n" #: f.tools.cc:2230 msgid " Page keys Navigation \n" msgstr " Page keys Navigazione \n" #: f.tools.cc:2231 msgid " Home/End Navigation \n" msgstr " Home/Fine Navigazione \n" #: f.tools.cc:2295 msgid "Edit KB Shortcuts" msgstr "Gestisci le scorciatoie di tastiera" #: f.tools.cc:2302 msgid "shortcut key:" msgstr "Scorciatoia:" #: f.tools.cc:2303 msgid "(enter key)" msgstr "(premere il/i tasti)" #: f.tools.cc:2304 f.tools.cc:2451 f.tools.cc:2554 msgid "(no selection)" msgstr "(nessuna selezione)" #: f.tools.cc:2445 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" è riservata - non si può usare" #: f.tools.cc:2672 msgid "Brightness Distribution" msgstr "Distribuzione della luminosità" #: f.tools.cc:2866 msgid "" "Drag mouse on image. \n" "Left click to cancel." msgstr "" "Trascinare il mouse sull'immagine. \n" "Clic sinistro per annullare." #: f.tools.cc:2893 f.widgets.cc:612 msgid "Magnify Image" msgstr "Lente d'ingrandimento" #: f.tools.cc:2902 msgid "X-size" msgstr "Ingrandimento" #: f.tools.cc:3210 msgid "Find Duplicate Images" msgstr "Trova immagini duplicate" #: f.tools.cc:3213 msgid "All files" msgstr "Tutti i file" #: f.tools.cc:3214 msgid "Current gallery" msgstr "Galleria corrente" #: f.tools.cc:3217 msgid "File count:" msgstr "File contati:" #: f.tools.cc:3221 msgid "Thumbnail size" msgstr "Dimensione miniatura" #: f.tools.cc:3226 msgid "Pixel difference" msgstr "Differenza di pixel" #: f.tools.cc:3229 msgid "Pixel count" msgstr "Conteggio di pixel" #: f.tools.cc:3237 msgid "Duplicates:" msgstr "Duplicate:" #: f.tools.cc:3567 msgid "too many files, cannot continue" msgstr "troppi file, impossibile continuare" #: f.tools.cc:3647 msgid "Click image to select pixels." msgstr "Cliccare l'immagine per selezionare i pixel" #: f.tools.cc:3690 f.widgets.cc:614 msgid "Show RGB" msgstr "Esplora componenti RGB" #: f.tools.cc:3948 msgid "Change Color Profile" msgstr "Cambia profilo di colore" #: f.tools.cc:3952 msgid "input profile" msgstr "Profilo in ingresso" #: f.tools.cc:3956 msgid "output profile" msgstr "Profilo in uscita" #: f.tools.cc:3977 msgid "Unable to change EXIF color profile" msgstr "Impossibile modificare il profilo di colore EXIF" #: f.tools.cc:3979 msgid "automatic new version created" msgstr "Versione nuova creata automaticamente" #: f.tools.cc:3988 msgid "color profile" msgstr "Profilo di colore" #: f.tools.cc:4036 f.tools.cc:4042 #, c-format msgid "unknown cms profile %s" msgstr "Profilo CMS sconosciuto %s" #: f.tools.cc:4144 f.widgets.cc:616 msgid "Calibrate Printer" msgstr "Calibrazione stampante" #: f.tools.cc:4170 msgid "print color chart" msgstr "Stampa l'immagine campione (mappa dei colori)" #: f.tools.cc:4171 msgid "scan and save color chart" msgstr "Acquisisci con lo scanner l'immagine stampata e salvala in un file" #: f.tools.cc:4172 msgid "align and trim color chart" msgstr "Raddrizza e ritaglia l'immagine acquisita" #: f.tools.cc:4173 msgid "open and process color chart" msgstr "Apri e processa l'immagine stampata, acquisita e regolata" #: f.tools.cc:4174 msgid "print image with revised colors" msgstr "Stampa la mappa dei colori con le correzioni attive" #: f.tools.cc:4315 #, c-format msgid "" "Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/" msgstr "" "Acquisire da scanner il campione dei colori stampato. \n" "La linea più scura deve stare in alto. \n" "Salvare in %s/" #: f.tools.cc:4330 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY." msgstr "" "Aprire ed editare l'immagine acquisita in precedenza. \n" "Raddrizzare pendenze o rotazioni dall'immagine acquisita. \n" "(Usare Correggi prospettiva). \n" "Ritagliare via accuratamente il bordo verde." #: f.tools.cc:4362 msgid "Open the trimmed color chart file" msgstr "Apri il file con l'immagine acquisita e ritagliata" #: f.tools.cc:4495 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:4535 msgid "Color map file to use" msgstr "File della mappa colori da usare" #: f.tools.cc:4555 msgid "Select the image file to print." msgstr "Selezionare l'immagine da stampare." #: f.tools.cc:4596 msgid "file format error" msgstr "Formato file errato" #: f.tools.cc:4620 msgid "converting colors..." msgstr "Conversione colori..." #: f.tools.cc:4720 msgid "Image colors are converted for printing." msgstr "Regolazione dei colori per la stampa." #: f.tools.cc:4769 f.widgets.cc:617 msgid "Grid Lines" msgstr "Linee griglia" #: f.tools.cc:4778 msgid "x-spacing" msgstr "Passo X:" #: f.tools.cc:4779 msgid "x-count" msgstr "Num. barre X:" #: f.tools.cc:4780 msgid "x-enable" msgstr "Abilita X:" #: f.tools.cc:4786 msgid "y-spacing" msgstr "Passo Y:" #: f.tools.cc:4787 msgid "y-count" msgstr "Num. righe Y:" #: f.tools.cc:4788 msgid "y-enable" msgstr "Abilita Y:" #: f.tools.cc:4909 f.widgets.cc:618 msgid "Line Color" msgstr "Colore della linea" #: f.tools.cc:4967 msgid "Darkest and Brightest Pixels" msgstr "Cerca pixel scuri e chiari" #: f.tools.cc:4991 msgid "Dark Limit" msgstr "Più scuri di..." #: f.tools.cc:4992 msgid "Bright Limit" msgstr "Più chiari di..." #: f.tools.cc:5117 msgid "Map RAW Pixel Bias" msgstr "Mappa bias pixel RAW" #: f.tools.cc:5124 msgid "mean RGB:" msgstr "RGB medio:" #: f.tools.cc:5210 msgid "select at least 10 RAW image files" msgstr "selezionare almeno 10 immagini RAW" #: f.tools.cc:5230 #, c-format msgid "" "cannot read file \n" " %s" msgstr "" "impossibile leggere file \n" " %s" #: f.tools.cc:5235 #, c-format msgid "" "not a RAW file \n" " %s" msgstr "" "non sembra un file RAW \n" " %s" #: f.tools.cc:5249 #, c-format msgid "dimensions do not match: %s" msgstr "le dimensioni non corrispondono: %s" #: f.tools.cc:5366 f.tools.cc:5449 msgid "Pixel Bias Map file" msgstr "Mappa bias pixel" #: f.tools.cc:5494 msgid "invalid pixel bias map file" msgstr "file mappa bias pixel non valido" #: f.tools.cc:5524 msgid "image dimensions do not match pixel bias file" msgstr "dimensioni immagine diverse da file di mappa" #: f.tools.cc:5610 msgid "Map RAW Dead Pixels" msgstr "Mappa pixel morti RAW" #: f.tools.cc:5614 msgid "gray RAW image file" msgstr "immagine RAW grigia" #: f.tools.cc:5618 msgid "RGB threshold" msgstr "Soglia RGB" #: f.tools.cc:5621 msgid "dead pixels found:" msgstr "pixel morti trovati:" #: f.tools.cc:5658 msgid "not a RAW file" msgstr "non è un file RAW" #: f.tools.cc:5664 msgid "cannot load RAW file" msgstr "impossibile caricare file RAW" #: f.tools.cc:5727 msgid "choose a gray RAW file" msgstr "scegliere un file RAW grigio" #: f.tools.cc:5858 f.tools.cc:5903 msgid "dead pixels file" msgstr "file di pixel morti" #: f.tools.cc:5955 msgid "invalid dead pixels file" msgstr "file pixel morti non valido" #: f.tools.cc:5975 msgid "no dead pixels data available" msgstr "mancano dati dei pixel morti" #: f.tools.cc:5980 msgid "image dimensions do not match dead pixels file" msgstr "dimensioni immagine diverse dal file pixel morti" #: f.tools.cc:6048 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:6208 msgid "Available Translations" msgstr "Traduzioni disponibili" #: f.tools.cc:6212 msgid "Set Language" msgstr "Cambia lingua" #: f.warp.cc:110 f.widgets.cc:556 msgid "Unbend" msgstr "Correggi deformazioni" #: f.warp.cc:388 msgid "" " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle." msgstr "" " Cliccare gli angoli di un'area trapezoidale; cliccare [Applica]. \n" " L'immagine si deformerà per trasformare il trapezio in un rettangolo." #: f.warp.cc:405 msgid "Perspective Correction" msgstr "Correzione di prospettiva" #: f.warp.cc:638 msgid "must have 4 corners" msgstr "Occorrono 4 angoli." #: f.warp.cc:763 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 "" "Selezionare un'area da deformare utilizzando la funzione del menù. \n" "Premere [Avvia deformazione] e trascinare con il mouse.\n" "Trascinare più volte se necessario.\n" "Quando terminato, selezionare un'altra zona o premere [Fatto]. " #: f.warp.cc:776 f.widgets.cc:558 msgid "Warp area" msgstr "Deforma area" #: f.warp.cc:781 msgid "start warp" msgstr "Avvia deformazione" #: f.warp.cc:1182 f.warp.cc:1494 msgid "" " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Trascina un punto dell'immagine con il mouse. \n" " Esegui diversi trascinamenti fino al completamento. \n" " Quando finito, premi [Fatto]." #: f.warp.cc:1200 f.widgets.cc:559 msgid "Warp curved" msgstr "Deforma a curva" #: f.warp.cc:1512 f.widgets.cc:560 msgid "Warp linear" msgstr "Deforma a linea" #: f.warp.cc:1825 msgid "" " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" "Trascina angoli o lati dell'immagine con il mouse. \n" "Ripeti l'operazione se richiesto.\n" "Una volta finito, premi [Fatto]." #: f.warp.cc:1841 f.widgets.cc:561 msgid "Warp affine" msgstr "Deforma specularmente" #: f.warp.cc:2174 msgid "" " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n" msgstr "" "Usare Selezione area per scegliere una faccia. \n" "Cliccare nel centro della distorsione. \n" "Regolare il cursore. \n" #: f.warp.cc:2201 f.widgets.cc:562 msgid "Unwarp Closeup" msgstr "Correzione del primo piano" #: f.warp.cc:2379 msgid "Flatten Book Page" msgstr "Appiattisci pagina" #: f.warp.cc:2380 msgid "" "Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: " msgstr "" "Ritagliare per isolare una pagina. \n" "Indicare i lati in cima e in fondo con \n" "4 o più clic, poi appiattire: " #: f.warp.cc:2383 msgid "Stretch curved-down surfaces:" msgstr "Deforma le superfici curve" #: f.warp.cc:2438 msgid "Top:" msgstr "Cima:" #: f.warp.cc:2441 msgid "Bottom:" msgstr "Fondo:" #: f.warp.cc:2830 msgid "" " Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]." msgstr "" " Selezionare aree da lasciare invariate. \n" " Tirare l'immagine dall'angolo alto a sinistra. \n" " Quando finito, cliccare [Fatto]." #: f.warp.cc:2846 f.widgets.cc:564 msgid "Area Rescale" msgstr "Riscala zona" #: f.warp.cc:2869 msgid "select areas first" msgstr "Prima eseguire una selezione di aree" #: f.warp.cc:3088 f.widgets.cc:565 msgid "Make Waves" msgstr "Onde" #: f.warp.cc:3095 msgid "wavelength" msgstr "Lunghezza onda" #: f.warp.cc:3097 msgid "variance" msgstr "Varianza" #: f.warp.cc:3108 msgid "perspective" msgstr "Prospettiva" #: f.warp.cc:3285 f.warp.cc:3321 f.widgets.cc:566 msgid "Twist" msgstr "Intreccia" #: f.warp.cc:3318 f.warp.cc:3609 f.warp.cc:3835 f.warp.cc:4060 msgid "Drag mouse to set center" msgstr "Trascinare il mouse per impostare il centro" #: f.warp.cc:3565 msgid "Spherical Projection" msgstr "Proiezione sferica" #: f.warp.cc:3792 msgid "Stretch Image" msgstr "Stira immagine" #: f.warp.cc:4057 f.widgets.cc:569 msgid "Inside-out" msgstr "Rivolta dentro/fuori" #: f.warp.cc:4065 f.warp.cc:4314 msgid "Center Hole" msgstr "Buco centrale" #: f.warp.cc:4266 msgid "image width must be greater than height" msgstr "La larghezza deve essere superiore all'altezza" #: f.warp.cc:4319 msgid "Cut Top" msgstr "Ritaglio in alto" #: f.warp.cc:4324 msgid "Cut Bottom" msgstr "Ritaglio in basso" #: f.warp.cc:4332 msgid "Reverse R" msgstr "Ribalta su/giù" #: f.warp.cc:4333 msgid "Theta" msgstr "Rotazione inversa (Teta)" #: f.warp.cc:4545 msgid "Click mouse to change center" msgstr "Clicca per cambiare centro" #: f.warp.cc:4550 msgid "Rim %" msgstr "Bordo %" #: f.widgets.cc:105 msgid "Album" msgstr "Album" #: f.widgets.cc:107 msgid "TOP" msgstr "CIMA" #: f.widgets.cc:167 msgid "Rename, copy/move, delete, print" msgstr "Rinomina, copia/sposta, elimina, stampa" #: f.widgets.cc:168 msgid "Thumbnails, bookmarks, albums, slide show" msgstr "Miniature, segnalibri, album, slide show" #: f.widgets.cc:169 msgid "View images by map location" msgstr "Mostra immagini per luoghi su mappa" #: f.widgets.cc:170 msgid "Custom favorites menu" msgstr "Menù preferiti speciale " #: f.widgets.cc:171 msgid "Left/right click: previous/next (also arrow keys)" msgstr "Clic destro/sinistro o frecce: precedente/successiva" #: f.widgets.cc:172 msgid "Left/right click: larger/smaller image/thumbnails" msgstr "Clic destro/sinistro: immagini/miniature più grandi/piccole" #: f.widgets.cc:173 msgid "Save modified file as new version or new file" msgstr "Salva come nuova versione o come nuovo file" #: f.widgets.cc:174 msgid "Metadata: captions, tags, ratings, geotags, search images" msgstr "Metadati: titoli, etichette, punteggi, geotag, ricerca" #: f.widgets.cc:175 msgid "Select areas to edit separately, save, copy and paste" msgstr "Seleziona aree da modificare/salvare/copiare/incollare separatamente" #: f.widgets.cc:176 msgid "" "Left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit" msgstr "" "Clic sinistro/destro: annulla/rifa 1 modifica \n" " con tasto A: annulla/rifa tutte le modifiche \n" " clic centrale: va a una data modifica precedente" #: f.widgets.cc:179 msgid "Image edit basic functions" msgstr "Funzioni di modifica di base" #: f.widgets.cc:180 msgid "Image repair and enhance" msgstr "Correzione e miglioramento immagini" #: f.widgets.cc:181 msgid "Artistic effects (filters)" msgstr "Effetti artistici" #: f.widgets.cc:182 msgid "Image warp, unwarp, transform" msgstr "Trasformazioni e deformazioni" #: f.widgets.cc:183 msgid "HDR, HDF, panorama, stack, mashup" msgstr "Panorama, impila, aggrega" #: f.widgets.cc:184 msgid "Batch processing, custom scripts" msgstr "Modifiche in massa, applicazione script" #: f.widgets.cc:185 msgid "Image index, user preferences, shortcuts, utilities" msgstr "Indice immagini, preferenze, scorciatoie, utilità" #: f.widgets.cc:186 msgid "User Guide, recent changes, log file, about" msgstr "Guida utente, cambiamenti recenti, giornale, info" #: f.widgets.cc:189 msgid "Current File (R-click or key F)" msgstr "File corrente (Clic destro o tasto F)" #: f.widgets.cc:190 msgid "Open a parallel Fotoxx session" msgstr "Avvia un'altra istanza di Fotoxx" #: f.widgets.cc:191 msgid "Cycle 2 Prior Files" msgstr "Cicla gli ultimi due file" #: f.widgets.cc:192 msgid "Cycle 3 Prior Files" msgstr "Cicla gli ultimi 3 file" #: f.widgets.cc:193 msgid "View a 360 degree panorama image file" msgstr "Vedi un file panorama a 360 gradi" #: f.widgets.cc:194 msgid "Change file name" msgstr "Rinomina il file" #: f.widgets.cc:195 msgid "View and change file permissions" msgstr "Mostra e cambia permessi del file" #: f.widgets.cc:196 msgid "Create a blank image" msgstr "Crea immagine nuova" #: f.widgets.cc:197 msgid "Toggle - blank or restore window" msgstr "Mostra o nascondi l'immagine" #: f.widgets.cc:198 msgid "Copy or Move file to new location" msgstr "Copia o sposta un file in un'altra destinazione" #: f.widgets.cc:199 msgid "Copy file to the desktop" msgstr "Copia un'immagine sulla scrivania" #: f.widgets.cc:200 msgid "Copy file to the clipboard" msgstr "Copia file negli appunti" #: f.widgets.cc:201 msgid "Set file as desktop wallpaper (GNOME)" msgstr "Imposta file come sfondo dell scrivania (solo Gnome)" #: f.widgets.cc:202 msgid "Show location on Internet map" msgstr "Mostra luogo sulla mappa Internet" #: f.widgets.cc:203 msgid "Delete or trash file" msgstr "Elimina o cestina il file" #: f.widgets.cc:204 msgid "Print the current image" msgstr "Stampa l'immagine corrente" #: f.widgets.cc:205 msgid "Print current image with adjusted colors" msgstr "Stampa l'immagine corrente con i colori corretti" #: f.widgets.cc:206 msgid "Quit Fotoxx" msgstr "Esci da Fotoxx" #: f.widgets.cc:209 msgid "Thumbnail Gallery (R-click or key G)" msgstr "Galleria miniature (Clic destro o tasto G)" #: f.widgets.cc:210 msgid "Gallery view with thumbnails and basic metadata" msgstr "Galleria con miniature e metadati di base" #: f.widgets.cc:211 msgid "Gallery view with small thumbnails and file names" msgstr "Galleria con miniature piccole e nomi file" #: f.widgets.cc:212 msgid "Gallery of recently viewed image files" msgstr "Galleria delle immagini recenti" #: f.widgets.cc:213 msgid "Gallery of newest image files" msgstr "Galleria delle immagini nuove" #: f.widgets.cc:214 msgid "Jump to beginning [home]" msgstr "Salta all'inizio [Home]" #: f.widgets.cc:215 msgid "Jump to end [end]" msgstr "Salta alla fine [Fine]" #: f.widgets.cc:216 msgid "Set gallery from current image file" msgstr "Apre la galleria dell'immagine corrente" #: f.widgets.cc:217 msgid "Change sort order" msgstr "Cambia ordinamento" #: f.widgets.cc:218 msgid "List all folders, click any for gallery view" msgstr "Lista cartelle: cliccare su una per vederne la galleria" #: f.widgets.cc:219 msgid "select input files for album, batch, script functions" msgstr "scegli file d'ingresso per funzioni album, batch, script" #: f.widgets.cc:220 msgid "Set and recall bookmarked image locations" msgstr "Imposta e richiama segnalibri di luoghi" #: f.widgets.cc:221 msgid "Organize images into albums" msgstr "Organizza immagini in album" #: f.widgets.cc:222 msgid "Update albums for new file versions" msgstr "Aggiorna album con versioni nuove" #: f.widgets.cc:223 msgid "Mass update album files" msgstr "Aggiornamento di massa degli album" #: f.widgets.cc:224 msgid "Save current gallery as album" msgstr "Salva galleria come album" #: f.widgets.cc:225 msgid "Start a slide show" msgstr "Inizia uno slideshow " #: f.widgets.cc:228 msgid "Maps (R-click or key M)" msgstr "Mappe (Clic destro o tasto M)" #: f.widgets.cc:229 msgid "Open Internet map" msgstr "Apri una mappa internet" #: f.widgets.cc:230 msgid "Choose Internet map source" msgstr "Scegliere una mappa da internet" #: f.widgets.cc:231 msgid "Internet map locations" msgstr "Luoghi mappa da internet" #: f.widgets.cc:232 msgid "Open file map" msgstr "Apri file di mappa" #: f.widgets.cc:233 msgid "Choose file map" msgstr "Selta file di mappa" #: f.widgets.cc:234 msgid "Set map markers for all images or current gallery" msgstr "Imposta marcatori di mappa per immagini o galleria" #: f.widgets.cc:237 msgid "List a few key metadata items" msgstr "Mostra alcuni metadati di base" #: f.widgets.cc:238 msgid "List all metadata items" msgstr "Mostra tutti i metadati" #: f.widgets.cc:239 msgid "Edit image tags/geotags/caption/rating ..." msgstr "Gestisci etichette, geotag, titoli, punteggi..." #: f.widgets.cc:240 msgid "Define tags (keywords) used for searching images" msgstr "Definisci etichette (chiavi) per ricerca immagini" #: f.widgets.cc:241 msgid "Edit any image metadata" msgstr "Modifica qualsiasi metadato dell'immagine" #: f.widgets.cc:242 msgid "Remove selected image metadata" msgstr "Elimina metadati dall'immagine selezionata" #: f.widgets.cc:243 msgid "Show file name, captions, comments" msgstr "Mostra nome file, titoli, commenti" #: f.widgets.cc:244 msgid "Find all images for a location [date]" msgstr "Cerca tutte le immagini di un luogo/data" #: f.widgets.cc:245 msgid "Show image counts by month, select and report" msgstr "Mostra conteggi per mese, seleziona e riepiloga" #: f.widgets.cc:246 msgid "Find images meeting search criteria" msgstr "Trova immagini con certi criteri" #: f.widgets.cc:249 msgid "Select object or area for editing" msgstr "Selezionare oggetto o area da modificare" #: f.widgets.cc:250 msgid "Select hairy or irregular edge" msgstr "Seleziona bordi irregolari/frastagliati" #: f.widgets.cc:251 msgid "Find a gap in an area outline" msgstr "Cerca interruzione nel contorno" #: f.widgets.cc:252 msgid "Show (outline) existing area" msgstr "Mostra il contorno dell'area definita" #: f.widgets.cc:253 msgid "Hide existing area" msgstr "Nascondi l'area definita" #: f.widgets.cc:254 msgid "Enable area for editing" msgstr "Abilita area per modifiche" #: f.widgets.cc:255 msgid "Disable area for editing" msgstr "Disabilita area per modifiche" #: f.widgets.cc:256 msgid "Reverse existing area" msgstr "Inverti l'area definita" #: f.widgets.cc:257 msgid "Erase existing area" msgstr "Cancella area" #: f.widgets.cc:258 msgid "Copy area for later pasting into image" msgstr "Copia area per incollarla poi in un'immagine" #: f.widgets.cc:259 msgid "Paste previously copied area into image" msgstr "Incolla nell'immagine l'area precedentemente copiata" #: f.widgets.cc:260 msgid "Open a file and paste as area into image" msgstr "Apre un file e incolla come area" #: f.widgets.cc:261 msgid "Save area to a file with transparency" msgstr "Salva area in un file con trasparenza" #: f.widgets.cc:264 msgid "Trim/Crop margins and/or Rotate" msgstr "Regola/ritaglia margini e/o ruota" #: f.widgets.cc:265 msgid "Auto upright a rotated image based on EXIF data" msgstr "Raddrizza automaticamente un'immagine usando EXIF" #: f.widgets.cc:266 msgid "Adjust brightness, contrast, color" msgstr "Correggi luminosità contrasto e colore" #: f.widgets.cc:267 msgid "Change pixel dimensions" msgstr "Modifica le dimensioni in pixel" #: f.widgets.cc:268 msgid "Adjust color using RGB or CMY colors" msgstr "Regola colori usando RGB o CMY" #: f.widgets.cc:269 msgid "Adjust color using HSL colors" msgstr "Regola colori usando HSL" #: f.widgets.cc:270 msgid "Draw on image: text, line/arrow, box, ellipse" msgstr "Disegna sull'immagine: scritte, linee/frecce, rettangoli, ellissi" #: f.widgets.cc:271 msgid "Paint image pixels using the mouse" msgstr "Dipingi sull'immagine col mouse" #: f.widgets.cc:272 msgid "Copy pixels within an image using the mouse" msgstr "Copia interattiva di pixel entro un'immagine" #: f.widgets.cc:273 msgid "Copy pixels from one image to another using the mouse" msgstr "Copia interattiva di pixel tra due immagini" #: f.widgets.cc:274 msgid "Paint edit function gradually with mouse" msgstr "Applica una funzione gradualmente col mouse" #: f.widgets.cc:275 msgid "Incrementally undo prior edits gradually with mouse" msgstr "Annulla modifiche gradualmente con il mouse" #: f.widgets.cc:276 msgid "Edit plugins menu or run a plugin function" msgstr "Modifica menu plugin o invoca una funzione" #: f.widgets.cc:277 msgid "Specialized program for editing RAW files" msgstr "Programma speciale per editare file RAW" #: f.widgets.cc:280 f.widgets.cc:281 msgid "Fast auto enhance that may work OK" msgstr "Auto correzione che potrebbe funzionare bene..." #: f.widgets.cc:282 msgid "Edit brightness distribution" msgstr "Imposta distribuzione di luminosità" #: f.widgets.cc:283 msgid "Magnify brightness gradients to enhance details" msgstr "Amplifica i gradienti di luminosità per migliorare i dettagli" #: f.widgets.cc:284 msgid "Flatten brightness distribution to enhance details" msgstr "Appiattisci distribuzione di luminosità per aumentare i dettagli" #: f.widgets.cc:285 msgid "Rescale RGB - reduce color caste and fog/haze" msgstr "Riscala RGB - riduce artifatti del colore" #: f.widgets.cc:286 msgid "Make the image look sharper" msgstr "Produci un'immagine più dettagliata" #: f.widgets.cc:287 msgid "Blur the image, different methods" msgstr "Sfuoca l'immagine, diversi metodi" #: f.widgets.cc:288 msgid "Filter noise from low-light photos" msgstr "Elimina disturbi da foto con poca luce" #: f.widgets.cc:289 msgid "Fix red-eyes from electronic flash" msgstr "Correggi gli occhi rossi a causa del flash" #: f.widgets.cc:290 msgid "Match colors on one image with another" msgstr "Allinea il colore di un'immagine con un'altra" #: f.widgets.cc:291 msgid "Remove unwanted objects" msgstr "Elimina oggetti non voluti" #: f.widgets.cc:292 msgid "Fix color fringes in outer areas of an image" msgstr "Correggi frange di colore ai bordi dell'immagine" #: f.widgets.cc:293 msgid "Fix color band on dark/bright feature edges" msgstr "Correggi bande di colore in bordi chiari/scuri" #: f.widgets.cc:294 msgid "Change brightness or color radially" msgstr "Cambia luminosità o contrasto in modo radiale" #: f.widgets.cc:295 msgid "Remove dust spots from scanned slides" msgstr "Elimina le macchie di polvere da diapositive scansionate" #: f.widgets.cc:298 msgid "Convert to simulated sketch" msgstr "Converti a schizzo simulato" #: f.widgets.cc:299 msgid "Convert image into a cartoon drawing" msgstr "Converte in disegno come un cartone animato" #: f.widgets.cc:300 msgid "Convert to line drawing (edge detection)" msgstr "Converte in disegno a linee (riconoscimento bordi)" #: 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 dithered dots" msgstr "Converti in punti tremuli (dither)" #: f.widgets.cc:304 msgid "Convert into a simulated painting" msgstr "Crea un effetto di dipinto a olio" #: f.widgets.cc:305 msgid "Add texture to an image" msgstr "Aggiungi un motivo (texture)" #: f.widgets.cc:306 msgid "Tile image with a repeating pattern" msgstr "Sovraimprime all'immagine un motivo ripetuto" #: f.widgets.cc:307 msgid "Create a mosaic with tiles made from all images" msgstr "Crea un mosaico con tessere da tutte le immagini" #: f.widgets.cc:308 msgid "Make BW/color, negative/positive, sepia" msgstr "Trasforma in B/N o a colori, negativo, seppia..." #: f.widgets.cc:309 msgid "Reduce color depth (posterize)" msgstr "Riduce la risoluzione dei colori (poster)" #: f.widgets.cc:310 msgid "Shift/convert colors into other colors" msgstr "Regola i singoli componenti del colore" #: f.widgets.cc:311 msgid "Change color hue using an algorithm" msgstr "Cambia i colori tramite un algoritmo" #: f.widgets.cc:312 msgid "Add a brightness/color ramp across the image" msgstr "Aggiunge una gradazione di luce/colore" #: f.widgets.cc:313 msgid "Paint image transparency using the mouse" msgstr "Pennella la trasparenza con il mouse" #: f.widgets.cc:314 msgid "Mirror image horizontally or vertically" msgstr "Rispecchia in orizzontale o verticale" #: f.widgets.cc:315 msgid "Process an image using a custom kernel" msgstr "Elabora un'immagine applicando una matrice utente" #: f.widgets.cc:318 msgid "Remove curvature, esp. panoramas" msgstr "Elimina la curvatura, specialmente da panorama" #: f.widgets.cc:319 msgid "Straighten objects seen from an angle" msgstr "Raddrizza oggetti visti da un angolo" #: f.widgets.cc:320 msgid "Distort image areas using the mouse" msgstr "Distorci un'area usando il mouse" #: f.widgets.cc:321 msgid "Unwarp closeup face photo to remove distortion" msgstr "Rimuove la distorsione della foto di un viso in primo piano" #: f.widgets.cc:322 f.widgets.cc:323 f.widgets.cc:324 msgid "Distort the whole image using the mouse" msgstr "Distorci l'intera foto usando il mouse" #: f.widgets.cc:325 msgid "Flatten a photographed book page" msgstr "Appiattisce la foto della pagina di un libro" #: f.widgets.cc:326 msgid "Rescale image outside selected areas" msgstr "Scalatura escludendo parti selezionate dell'immagine" #: f.widgets.cc:327 msgid "Warp an image with a wave pattern" msgstr "Distorce immagine con motivo di onde" #: f.widgets.cc:328 msgid "Twist image centered at mouse position" msgstr "Intreccia l'immagine intorno alla posizione del mouse" #: f.widgets.cc:329 msgid "Make a spherical projection of an image" msgstr "Produce una proiezione sferica di un'immagine" #: f.widgets.cc:330 msgid "Image scale increases from center to edge" msgstr "La scalatura aumenta dal centro ai bordi" #: f.widgets.cc:331 msgid "Turn an image inside-out" msgstr "Rivolta un'immagine dentro-fuori" #: f.widgets.cc:332 msgid "Convert an image into a Tiny Planet" msgstr "Crea un minimondo (tiny planet)" #: f.widgets.cc:333 msgid "Generate an inward spiraling recursive image" msgstr "Genera un spirale ricorsiva dell'immagine" #: f.widgets.cc:336 msgid "Combine bright/dark images for better detail" msgstr "Combina immagini chiare/scure per guadagnare in dettaglio" #: f.widgets.cc:337 msgid "Combine near/far focus images for deeper focus" msgstr "Combina immagini con profondità diverse per ampliare la messa a fuoco" #: f.widgets.cc:338 msgid "Combine images to erase passing people, etc." msgstr "Combina immagini per eliminare oggetti (es. passanti, automobili...)" #: f.widgets.cc:339 msgid "Combine noisy images into a low-noise image" msgstr "Combina immagini disturbate in una con minor disturbo" #: f.widgets.cc:340 msgid "Combine image layers, mouse select and expose" msgstr "Combina/sovrapponi immagini" #: f.widgets.cc:341 msgid "Compare two images separated by sliding boundary" msgstr "Compara due immagini separate da un confine mobile" #: f.widgets.cc:342 msgid "Show differences between two images" msgstr "Mostra le differenze tra due immagini" #: f.widgets.cc:343 msgid "Combine images into a panorama" msgstr "Combina immagini lato a lato per creare un panorama" #: f.widgets.cc:344 msgid "Combine images into a vertical panorama" msgstr "Combina immagini in un panorama verticale" #: f.widgets.cc:345 msgid "Combine images into a panorama (panorama tools)" msgstr "Combina immagini lato a lato per creare un panorama (panorama tools)" #: f.widgets.cc:346 msgid "Combine images into a montage of images" msgstr "Combina diverse immagini in una esposizione" #: f.widgets.cc:347 msgid "Arrange images and text in a layout (montage)" msgstr "Dispone immagini e testi in un collage" #: f.widgets.cc:350 msgid "Rename/convert/resize/move multiple files" msgstr "Rinomina/converte/ridimensiona/sposta file multipli" #: f.widgets.cc:351 msgid "Upright multiple rotated image files" msgstr "Raddrizza immagini in massa" #: f.widgets.cc:352 msgid "Delete or Trash multiple files" msgstr "Elimina o cestina più immagini" #: f.widgets.cc:353 msgid "Convert camera RAW files to tiff/png/jpeg" msgstr "Converti file RAW in TIFF/PNG/Jpeg" #: f.widgets.cc:354 msgid "Burn selected image files to DVD/BlueRay disc" msgstr "Registra immagini selezionate su DVD/Blueray" #: f.widgets.cc:356 msgid "Export selected image files to a folder" msgstr "Esporta in una cartella le immagini selezionate" #: f.widgets.cc:357 msgid "Add/remove tags for multiple images" msgstr "Aggiungi/togli etichette in massa" #: f.widgets.cc:358 msgid "Convert tag names for all images" msgstr "Converte nomi etichette per tutte le immagini" #: f.widgets.cc:359 msgid "change or shift photo dates/times" msgstr "Modifica o sposta data e ora delle foto" #: f.widgets.cc:360 msgid "Add/change/delete metadata for multiple images" msgstr "Aggiungi/cambia/elimina metadati in diverse immagini " #: f.widgets.cc:361 msgid "Report metadata for multiple images" msgstr "Elenca i metadati di immagini multiple" #: f.widgets.cc:362 msgid "Add/revise geotags for multiple images" msgstr "Aggiungi/modifica geotag per diverse immagini" #: f.widgets.cc:363 msgid "Build a custom script with multiple edit functions" msgstr "Compila una sequenza (script) con diverse funzioni di edit" #: f.widgets.cc:364 msgid "Run custom script to edit the current image file" msgstr "Esegue uno script (sequenza) per editare l'immagine corrente" #: f.widgets.cc:365 msgid "Run custom script to edit a batch of image files" msgstr "Esegue uno script per editare più immagini" #: f.widgets.cc:368 msgid "Index new files and make thumbnails" msgstr "Aggiorna indici e miniature di nuovi file" #: f.widgets.cc:369 msgid "Quick incremental index update" msgstr "Aggiornamento indice incrementale e rapido" #: f.widgets.cc:370 msgid "Move Fotoxx home folder" msgstr "Cambia la cartella home di Fotoxx" #: f.widgets.cc:371 msgid "User preferences and settings" msgstr "Preferenze e impostazioni" #: f.widgets.cc:373 msgid "Show RGB brightness distribution" msgstr "Mostra distribuzione RGB" #: f.widgets.cc:374 msgid "Magnify image around the mouse position" msgstr "Ingrandisce temporaneamente una porzione d'immagine" #: f.widgets.cc:375 msgid "Search all image files and report duplicates" msgstr "Analizza tutte le immagini e segnala i duplicati" #: f.widgets.cc:376 msgid "Show RGB colors at mouse click" msgstr "Mostra i componenti colore RGB al clic" #: f.widgets.cc:377 msgid "Convert to another color profile" msgstr "Converte a un altro profilo di colore" #: f.widgets.cc:378 msgid "Calibrate printer colors" msgstr "Calibrazione dei colori della stampante" #: f.widgets.cc:379 msgid "Show or revise grid lines" msgstr "Mostra o cambia la griglia" #: f.widgets.cc:380 msgid "Change color of foreground lines" msgstr "Cambia colore delle linee in primo piano" #: f.widgets.cc:381 msgid "Highlight darkest and brightest pixels" msgstr "Individua i pixel di luminosità estrema" #: f.widgets.cc:382 msgid "map raw pixel bias (camera sensor, vignette)" msgstr "mappa il bias dei pixel raw (sensore macchina, vignetta)" #: f.widgets.cc:383 msgid "map raw dead pixels (camera sensor)" msgstr "mappa pixel morti raw (sensore macchina)" #: f.widgets.cc:384 msgid "Chart to adjust monitor color" msgstr "Grafico per regolare il colore dello schermo" #: f.widgets.cc:385 msgid "Chart to adjust monitor gamma" msgstr "Grafico per regolare la gamma dello schermo" #: f.widgets.cc:386 msgid "Change the GUI language" msgstr "Cambia la lingua dell'interfaccia" #: f.widgets.cc:387 msgid "Report missing translations" msgstr "Segnala traduzioni mancanti" #: f.widgets.cc:388 msgid "Anonymous usage statistics" msgstr "Statistiche d'uso anonime" #: f.widgets.cc:389 msgid "Memory and CPU (to terminal/logfile)" msgstr "Memoria e CPU (al terminale/giornale)" #: f.widgets.cc:390 msgid "List files included in appimage container" msgstr "Lista dei file del contenitore appimage" #: f.widgets.cc:391 msgid "test crash report with source line numbers" msgstr "Prova Crash Report con numeri di linea del sorgente" #: f.widgets.cc:394 msgid "Read the user guide" msgstr "Visualizza la guida utente" #: f.widgets.cc:395 msgid "Recent user guide changes" msgstr "Visualizza gli ultimi cambiamenti alla guida utente" #: f.widgets.cc:396 msgid "Overview of all edit functions" msgstr "Panoramica di tutte le funzioni di edit" #: f.widgets.cc:397 msgid "List updates by Fotoxx version" msgstr "Mostra la storia delle versioni e loro contenuti" #: f.widgets.cc:398 msgid "View the log file and error messages" msgstr "Mostra il file di log: i messaggi generati durante l'avviamento" #: f.widgets.cc:399 msgid "Fotoxx Man Page - summary of capabilities" msgstr "Pagina di manuale di Fotoxx - sommario delle prestazioni" #: f.widgets.cc:400 msgid "List command line parameters" msgstr "Lista dei parametri della linea di comando" #: f.widgets.cc:401 msgid "How to do Fotoxx translations" msgstr "Mostra come fare la traduzione in altre lingue" #: f.widgets.cc:402 msgid "Show the Fotoxx web page" msgstr "Mostra la home page internet di fotoxx" #: f.widgets.cc:403 msgid "Fotoxx license - terms of use" msgstr "Licenza di Fotoxx - termini di utilizzo" #: f.widgets.cc:404 msgid "Fotoxx privacy policy" msgstr "Politica di privacy di Fotoxx" #: f.widgets.cc:405 msgid "Version, contact, credits" msgstr "Versione, contatti, ringraziamenti" #: f.widgets.cc:425 msgid "File View F" msgstr "Vista immagine (F)" #: f.widgets.cc:426 msgid "New Session" msgstr "Nuova sessione" #: f.widgets.cc:427 f.widgets.cc:453 msgid "Source Folder" msgstr "Mostra cartella del file corrente" #: f.widgets.cc:428 msgid "Cycle 2" msgstr "Cicla 2" #: f.widgets.cc:429 msgid "Cycle 3" msgstr "Cicla 3" #: f.widgets.cc:430 msgid "View 360° Pano" msgstr "Panorama 360°" #: f.widgets.cc:431 f.widgets.cc:820 fotoxx.h:1414 msgid "Rename" msgstr "Rinomina" #: f.widgets.cc:433 msgid "Blank Image" msgstr "Immagine vuota" #: f.widgets.cc:435 f.widgets.cc:822 msgid "Copy/Move" msgstr "Copia/Sposta" #: f.widgets.cc:436 f.widgets.cc:823 msgid "Copy to Desktop" msgstr "Copia sulla scrivania" #: f.widgets.cc:437 f.widgets.cc:824 msgid "Copy to Clipboard" msgstr "Copia negli appunti" #: f.widgets.cc:438 msgid "Set Wallpaper" msgstr "Imposta sfondo scrivania" #: f.widgets.cc:439 f.widgets.cc:839 msgid "Show on Map" msgstr "Mostra sulla mappa" #: f.widgets.cc:440 f.widgets.cc:840 msgid "Delete/Trash" msgstr "Elimina/Cestina" #: f.widgets.cc:441 msgid "Print" msgstr "Stampa" #: f.widgets.cc:442 msgid "Print Calibrated" msgstr "Stampa calibrata" #: f.widgets.cc:443 fotoxx.h:1407 msgid "Quit" msgstr "Uscita" #: f.widgets.cc:446 msgid "Gallery View G" msgstr "Vista galleria (G)" #: f.widgets.cc:447 msgid "Meta View" msgstr "Meta-Vista" #: f.widgets.cc:448 msgid "List View" msgstr "Vista lista" #: f.widgets.cc:449 msgid "Recent" msgstr "Recenti" #: f.widgets.cc:450 msgid "Newest" msgstr "Più nuovi" #: f.widgets.cc:451 msgid "GoTo First" msgstr "Vai alla prima" #: f.widgets.cc:452 msgid "GoTo Last" msgstr "Vai all'ultima" #: f.widgets.cc:454 msgid "Sort Gallery" msgstr "Riordina" #: f.widgets.cc:455 msgid "All Folders" msgstr "Tutte le cartelle" #: f.widgets.cc:456 fotoxx.h:1425 msgid "Select Files" msgstr "Seleziona i file" #: f.widgets.cc:459 msgid "Update Albums" msgstr "Aggiorna gli album" #: f.widgets.cc:461 msgid "Gallery to Album" msgstr "Galleria -> Album" #: f.widgets.cc:465 msgid "Map View M" msgstr "Vista mappa (M)" #: f.widgets.cc:466 msgid "Net Map" msgstr "Mostra la mappa internet" #: f.widgets.cc:467 msgid "Net Source" msgstr "Sorgente da rete" #: f.widgets.cc:468 msgid "Net Locs" msgstr "Luoghi rete" #: f.widgets.cc:469 msgid "File Map" msgstr "Mostra la mappa corrente/locale" #: f.widgets.cc:470 msgid "Choose Map" msgstr "Scegli una mappa" #: f.widgets.cc:471 msgid "Markers" msgstr "Marcatori" #: f.widgets.cc:474 f.widgets.cc:816 msgid "View Meta" msgstr "Vedere metadati" #: f.widgets.cc:475 f.widgets.cc:817 msgid "View All Meta" msgstr "Tutti i metadati" #: f.widgets.cc:476 f.widgets.cc:818 msgid "Edit Meta" msgstr "Modifica metadati" #: f.widgets.cc:478 f.widgets.cc:819 msgid "Edit Any Meta" msgstr "Modifica tutti i metadati" #: f.widgets.cc:479 msgid "Delete Meta" msgstr "Elimina metadati" #: f.widgets.cc:480 msgid "Captions" msgstr "Titoli" #: f.widgets.cc:481 msgid "Places/Dates" msgstr "Luoghi/Date" #: f.widgets.cc:482 msgid "Timeline" msgstr "Cronologia" #: f.widgets.cc:483 fotoxx.h:1422 msgid "Search" msgstr "Ricerca" #: f.widgets.cc:486 fotoxx.h:1424 msgid "Select" msgstr "Seleziona" #: f.widgets.cc:488 msgid "Find Gap" msgstr "Trova interruzione" #: f.widgets.cc:489 fotoxx.h:1426 msgid "Show" msgstr "Visualizza" #: f.widgets.cc:490 fotoxx.h:1367 msgid "Hide" msgstr "Nascondi" #: f.widgets.cc:491 fotoxx.h:1351 msgid "Enable" msgstr "Abilita" #: f.widgets.cc:492 fotoxx.h:1346 msgid "Disable" msgstr "Disabilita" #: f.widgets.cc:494 fotoxx.h:1333 msgid "Clear" msgstr "Pulisci" #: f.widgets.cc:495 fotoxx.h:1340 msgid "Copy" msgstr "Copia" #: f.widgets.cc:496 fotoxx.h:1399 msgid "Paste" msgstr "Incolla" #: f.widgets.cc:497 fotoxx.h:1376 msgid "Load" msgstr "Carica" #: f.widgets.cc:498 f.widgets.cc:654 fotoxx.h:1421 zfuncs.cc:12499 msgid "Save" msgstr "Salva" #: f.widgets.cc:507 msgid "Markup" msgstr "Annotazione" #: f.widgets.cc:508 msgid "Paint Image" msgstr "Dipingi" #: f.widgets.cc:509 msgid "Copy Pixels 1" msgstr "Copia pixel 1" #: f.widgets.cc:510 msgid "Copy Pixels 2" msgstr "Copia pixel 2" #: f.widgets.cc:513 msgid "Plugins" msgstr "Plugin" #: f.widgets.cc:514 f.widgets.cc:838 msgid "Raw Therapee" msgstr "Raw Therapee" #: f.widgets.cc:517 msgid "Voodoo 1" msgstr "Voodoo 1" #: f.widgets.cc:518 msgid "Voodoo 2" msgstr "Voodoo 2" #: f.widgets.cc:519 f.widgets.cc:834 msgid "Brite Dist" msgstr "Distr. Lumo" #: f.widgets.cc:520 f.widgets.cc:836 msgid "Gradients" msgstr "Gradienti di colore" #: f.widgets.cc:521 f.widgets.cc:835 fotoxx.h:1360 msgid "Flatten" msgstr "Quantità" #: f.widgets.cc:522 msgid "Global Retx" msgstr "RetX globale" #: f.widgets.cc:523 msgid "Zonal Retx" msgstr "RetX zonale" #: f.widgets.cc:526 msgid "Denoise" msgstr "Riduci disturbo" #: f.widgets.cc:527 msgid "Red Eyes" msgstr "Rimuovi occhi rossi" #: f.widgets.cc:528 msgid "Match Colors" msgstr "Allinea colori" #: f.widgets.cc:530 msgid "Chromatic1" msgstr "Cromatico1" #: f.widgets.cc:531 msgid "Chromatic2" msgstr "Cromatico2" #: f.widgets.cc:536 msgid "Sketch" msgstr "Schizzo" #: f.widgets.cc:537 msgid "Cartoon" msgstr "Cartone animato" #: f.widgets.cc:541 msgid "Dither" msgstr "Tremulo (dither)" #: f.widgets.cc:543 msgid "Texture" msgstr "Aggiungi disturbo" #: f.widgets.cc:545 msgid "Mosaic" msgstr "Mosaico" #: f.widgets.cc:547 msgid "Color Depth" msgstr "Risoluzione del colore" #: f.widgets.cc:550 msgid "Brite Ramp" msgstr "Rampa lumo" #: f.widgets.cc:551 msgid "Paint Transp" msgstr "Disegna Trasp." #: f.widgets.cc:552 msgid "Mirror" msgstr "Rispecchia" #: f.widgets.cc:557 msgid "Perspective" msgstr "Prospettiva" #: f.widgets.cc:563 msgid "Flatten Book" msgstr "Appiattisci libro" #: f.widgets.cc:567 msgid "Sphere" msgstr "Sfera" #: f.widgets.cc:568 msgid "Stretch" msgstr "Stira" #: f.widgets.cc:570 msgid "Tiny Planet" msgstr "Minimondo (tiny planet)" #: f.widgets.cc:571 msgid "Escher Spiral" msgstr "Spirale Escher" #: f.widgets.cc:576 msgid "Stack/Paint" msgstr "Sovrapponi (interattivo con mouse)" #: f.widgets.cc:577 msgid "Stack/Noise" msgstr "Sovrapponi (rimozione disturbo)" #: f.widgets.cc:578 msgid "Stack/Layer" msgstr "Impila (sovrappone)" #: f.widgets.cc:579 msgid "Stack/Slider" msgstr "Impila/Scorri" #: f.widgets.cc:580 msgid "Image Diffs" msgstr "Differenze" #: f.widgets.cc:581 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:582 msgid "V. Panorama" msgstr "Panorama verticale" #: f.widgets.cc:583 msgid "PT Panorama" msgstr "Panorama (pano tools)" #: f.widgets.cc:584 msgid "Mashup" msgstr "Collage" #: f.widgets.cc:585 msgid "Montage" msgstr "Esposizione" #: f.widgets.cc:591 msgid "Batch RAW" msgstr "Converti RAW in massa" #: f.widgets.cc:592 msgid "Burn DVD/BlueRay" msgstr "Crea DVD/Blueray" #: f.widgets.cc:593 msgid "Export File List" msgstr "Esporta lista di file" #: f.widgets.cc:595 msgid "Batch Tags" msgstr "Etichette in massa" #: f.widgets.cc:597 msgid "Batch Photo Date" msgstr "Date in massa" #: f.widgets.cc:598 msgid "Batch Change Meta" msgstr "Cambia metadati in massa" #: f.widgets.cc:599 msgid "Batch Report Meta" msgstr "Report metadati in massa" #: f.widgets.cc:601 msgid "Edit Script" msgstr "Modifica script" #: f.widgets.cc:602 msgid "Run Script" msgstr "Esegue script" #: f.widgets.cc:606 msgid "Index Files" msgstr "Indicizza" #: f.widgets.cc:607 msgid "Quick Index" msgstr "Indice rapido" #: f.widgets.cc:608 msgid "Move Fotoxx Home" msgstr "Sposta la cartella dati di Fotoxx" #: f.widgets.cc:609 msgid "Preferences" msgstr "Preferenze" #: f.widgets.cc:610 msgid "KB Shortcuts" msgstr "Scorciatoie" #: f.widgets.cc:611 msgid "RGB Distribution" msgstr "Distribuzione RGB" #: f.widgets.cc:613 msgid "Find Duplicates" msgstr "Trova duplicati" #: f.widgets.cc:615 msgid "Color Profile" msgstr "Profilo di colore" #: f.widgets.cc:619 msgid "Dark/Bright Pixels" msgstr "Punti chiari/scuri" #: f.widgets.cc:620 msgid "Map Pixel Bias" msgstr "Mappa bias pixel" #: f.widgets.cc:621 msgid "Map Dead Pixels" msgstr "Mappa pixel morti" #: f.widgets.cc:622 msgid "Monitor Color" msgstr "Colore monitor" #: f.widgets.cc:623 msgid "Monitor Gamma" msgstr "Gamma dello schermo" #: f.widgets.cc:624 msgid "Change Language" msgstr "Cambia lingua del programma" #: f.widgets.cc:625 msgid "Missing Translations" msgstr "Traduzioni mancanti" #: f.widgets.cc:626 zfuncs.cc:5885 msgid "Phone Home" msgstr "Telefona casa" #: f.widgets.cc:628 msgid "Show Resources" msgstr "Mostra risorse" #: f.widgets.cc:629 msgid "Appimage Files" msgstr "File in appimage" #: f.widgets.cc:630 msgid "Zappcrash Test" msgstr "Prova zappcrash" #: f.widgets.cc:649 msgid "Gallery" msgstr "Galleria" #: f.widgets.cc:650 msgid "Maps" msgstr "Mappe" #: f.widgets.cc:651 f.widgets.cc:1145 msgid "Favorites" msgstr "Preferiti" #: f.widgets.cc:652 msgid "Prev/Next" msgstr "Prec./Pross." #: f.widgets.cc:653 msgid "Zoom/±" msgstr "Zoom ±" #: f.widgets.cc:655 msgid "Meta" msgstr "Meta" #: f.widgets.cc:656 msgid "Areas" msgstr "Aree" #: f.widgets.cc:657 msgid "Undo/Redo" msgstr "Annulla/ripeti" #: f.widgets.cc:658 fotoxx.h:1350 msgid "Edit" msgstr "Modifica" #: f.widgets.cc:659 msgid "Enhance" msgstr "Migliora" #: f.widgets.cc:660 msgid "Effects" msgstr "Effetti" #: f.widgets.cc:662 msgid "Combine" msgstr "Combina" #: f.widgets.cc:663 msgid "Process" msgstr "Elaborazioni in massa" #: f.widgets.cc:664 msgid "Tools" msgstr "Strumenti" #: f.widgets.cc:815 msgid "Popup Image" msgstr "Comparsa immagine" #: f.widgets.cc:825 msgid "Add Selected Files Here" msgstr "Aggiungi qui i file selezionati" #: f.widgets.cc:826 msgid "Add Current File Here" msgstr "Aggiungi qui l'immagine corrente" #: f.widgets.cc:827 msgid "Remove from Album" msgstr "Rimuovere dall'album" #: f.widgets.cc:837 msgid "Select Area" msgstr "Seleziona area" #: f.widgets.cc:1105 f.widgets.cc:1124 msgid "Fotoxx Image Locations" msgstr "Fotoxx luoghi delle immagini" #: f.widgets.cc:1173 #, c-format msgid "invalid menu name: %s" msgstr "nome del menu invalido: %s" #: fotoxx.cc:257 msgid "read+write" msgstr "rw leggi e scrivi" #: fotoxx.cc:258 msgid "read only" msgstr "r sola lettura" #: fotoxx.cc:259 msgid "no access" msgstr "nessun permesso" #: fotoxx.cc:465 msgid "Please install missing programs:" msgstr "Installa i programmi mancanti:" #: fotoxx.cc:594 msgid "Index aborted" msgstr "Indicizzazione interrotta" #: fotoxx.cc:772 msgid " Defer image file indexing:" msgstr " Rimanda indicizzazione file" #: fotoxx.cc:773 msgid "" " • Fotoxx will start immediately \n" " • View and edit image files will work normally \n" " • Image search, batch and map functions will not work \n" " • Thumbnail galleries will be slow" msgstr "" " • Fotoxx partirà immediatamente \n" " • Visualizza e modifica file funzionerà normalmente \n" " • Ricerca immagini, mappe e funzioni batch NON funzioneranno \n" " • Le gallerie saranno lente" #: fotoxx.cc:778 msgid " Index image files now:" msgstr " Indicizza immagini adesso:" #: fotoxx.cc:779 msgid "" " • Initial indexing may need considerable time \n" " • Subsequent startups will be fast \n" " • Full functionality will be available \n" " • Thumbnail galleries will be fast" msgstr "" " • La prima indicizzazione può richiedere molto tempo \n" " • Gli avvii successivi saranno rapidi \n" " • Tutte le funzioni saranno disponibili \n" " • Le gallerie saranno veloci" #: fotoxx.cc:784 msgid "" " Indexing time depends on the number of image files and the \n" " speed of your computer. This can be a few hundred to a few \n" " thousand per minute. After indexing is done, startup time \n" " should be quite fast. You can change index options later, \n" " using these menus: Tools > Index and Tools > Preferences. " msgstr "" " Il tempo richiesto per la generazione dell'indice dipende da numero \n" " di foto e dalla velocità del computer; possono essere processate da \n" " centinaia a migliaia di foto al minuto. Dopo l'indicizzazione, il tempo di " "avvio \n" " dovrebbe essere abbastanza veloce. Potrai cambiare opzioni dell'indice " "dopo, \n" " usando il menù Strumenti > Strumenti e indice > Preferenze." #: fotoxx.cc:791 msgid "" "Main memory is too small to run Fotoxx. \n" "You can try anyway if you wish. \n" " Continue?" msgstr "" "La memoria è troppo scarsa per usare Fotoxx. \n" "Si può provare comunque, se desiderato. \n" " Continuare?" #: fotoxx.cc:826 msgid "Fotoxx First Startup" msgstr "Primo avviamento di Fotoxx" #: fotoxx.cc:981 msgid "(reduced)" msgstr "(ridotto)" #: fotoxx.cc:982 msgid "area active" msgstr "area attiva" #: fotoxx.cc:983 msgid "dialog open" msgstr "dialogo aperto" #: fotoxx.cc:984 msgid "blocked" msgstr "bloccato" #: fotoxx.cc:1040 msgid "edits" msgstr "modifiche" #: fotoxx.cc:1958 msgid "Show Hidden" msgstr "Mostra file nascosti" #: fotoxx.cc:3168 msgid "Exceed 50 anchor points" msgstr "Superati 50 punti di ancoraggio" #: fotoxx.cc:3397 msgid "load curve from a file" msgstr "carica curva da file" #: fotoxx.cc:3464 msgid "curve file is invalid" msgstr "File della curva invalido" #: fotoxx.cc:3478 msgid "save curve to a file" msgstr "salva curva in un file" #: fotoxx.cc:3549 #, c-format msgid "" "File cannot be edited \n" " %s" msgstr "" "Il file non può essere editato \n" " %s" #: fotoxx.cc:3562 msgid "Too many edits, please save image" msgstr "Troppe modifiche: salva l'immagine" #: fotoxx.cc:3567 msgid "this function cannot be scripted" msgstr "Questa funzione non può essere usata in uno script" #: fotoxx.cc:3584 msgid "" "Select area will be ignored. \n" "Continue?" msgstr "" "La selezione di area sarà ignorata. \n" "Continuare?" #: fotoxx.cc:3590 msgid "" "Select area not active.\n" "Continue?" msgstr "" "Selezione di area non attiva.\n" "Continuare?" #: fotoxx.cc:3900 msgid "file data does not fit dialog" msgstr "I dati del file non si adattano alla finestra" #: fotoxx.cc:3910 msgid "Save settings to a file" msgstr "Salva impostazioni in un file" #: fotoxx.cc:4297 msgid "This action will discard changes to current image" msgstr "Questa azione annullerà le modifiche all'immagine" #: fotoxx.cc:4298 msgid "prior function still active" msgstr "Funzione precedente ancora attiva" #: fotoxx.cc:4299 fotoxx.h:1372 msgid "Keep" msgstr "Mantieni modifiche" #: fotoxx.cc:4300 msgid "Discard" msgstr "Perdi modifiche" #: fotoxx.h:1315 msgid "Add" msgstr "Aggiungi" #: fotoxx.h:1316 msgid "Add All" msgstr "Aggiungi tutto" #: fotoxx.h:1318 msgid "Amount" msgstr "Quantità" #: fotoxx.h:1319 msgid "Angle" msgstr "Angolo:" #: fotoxx.h:1320 zfuncs.cc:7936 msgid "Apply" msgstr "Applica" #: fotoxx.h:1321 msgid "Auto" msgstr "Auto" #: fotoxx.h:1322 msgid "Black" msgstr "Nero" #: fotoxx.h:1323 msgid "Blend Width" msgstr "Larghezza di sfumatura" #: fotoxx.h:1324 msgid "Blue" msgstr "Blu" #: fotoxx.h:1325 zfuncs.cc:12913 msgid "Bottom" msgstr "Basso" #: fotoxx.h:1327 zfuncs.cc:7950 msgid "Browse" msgstr "Sfoglia" #: fotoxx.h:1328 msgid "Calculate" msgstr "Calcola" #: fotoxx.h:1329 zfuncs.cc:7936 zfuncs.cc:12528 zfuncs.cc:12680 msgid "Cancel" msgstr "Annulla" #: fotoxx.h:1330 msgid "Center" msgstr "centro" #: fotoxx.h:1331 msgid "Change" msgstr "Cambia" #: fotoxx.h:1332 msgid "Choose" msgstr "Scegli" #: fotoxx.h:1334 msgid "click thumbnail to select file" msgstr "Cliccare la miniatura per selezionare il file" #: fotoxx.h:1335 msgid "Close" msgstr "Chiudi" #: fotoxx.h:1336 msgid "Color" msgstr "Colore" #: fotoxx.h:1337 msgid "COMPLETED" msgstr "COMPLETATO" #: fotoxx.h:1338 msgid "continue" msgstr "continua" #: fotoxx.h:1341 msgid "Create" msgstr "Creare" #: fotoxx.h:1342 msgid "Curve File:" msgstr "File di curve:" #: fotoxx.h:1343 msgid "Cut" msgstr "Taglia" #: fotoxx.h:1344 msgid "Deband" msgstr "Elimina bande" #: fotoxx.h:1345 zfuncs.cc:7936 msgid "Delete" msgstr "Elimina" #: fotoxx.h:1347 msgid "Display" msgstr "Mostra" #: fotoxx.h:1348 msgid "Done" msgstr "Fatto" #: fotoxx.h:1349 msgid "edge" msgstr "bordo:" #: fotoxx.h:1352 msgid "Erase" msgstr "Esegui cancellatura" #: fotoxx.h:1353 msgid "Fetch" msgstr "Prendi" #: fotoxx.h:1354 msgid "output file already exists" msgstr "Il file di uscita esiste già" #: fotoxx.h:1355 msgid "file not found" msgstr "File non trovato" #: fotoxx.h:1356 #, c-format msgid "file not found: %s" msgstr "file non trovato: %s" #: fotoxx.h:1357 #, c-format msgid "%d image files selected" msgstr "selezionate %d file d'immagine" #: fotoxx.h:1358 msgid "Find" msgstr "Cerca" #: fotoxx.h:1359 msgid "Finish" msgstr "Termina" #: fotoxx.h:1361 msgid "Font" msgstr "Font" #: fotoxx.h:1362 #, c-format msgid "gallery truncated to %d images" msgstr "galleria troncata a %d immagini" #: fotoxx.h:1363 msgid "Green" msgstr "Verde" #: fotoxx.h:1364 msgid "Grid" msgstr "Griglia" #: fotoxx.h:1365 zfuncs.cc:12943 msgid "Height" msgstr "Altezza" #: fotoxx.h:1368 zfuncs.cc:12935 msgid "Image" msgstr "Immagine" #: fotoxx.h:1369 msgid "Images" msgstr "Immagini" #: fotoxx.h:1370 msgid "Insert" msgstr "Incolla" #: fotoxx.h:1373 zfuncs.cc:12917 msgid "Left" msgstr "Sinistra" #: fotoxx.h:1374 msgid "Length" msgstr "lunghezza" #: fotoxx.h:1375 msgid "limit" msgstr "limite" #: fotoxx.h:1377 msgid "Magnify" msgstr "Ingrandimento" #: fotoxx.h:1378 msgid "Make" msgstr "Produci" #: fotoxx.h:1379 msgid "Map" msgstr "Mappa" #: fotoxx.h:1380 msgid "Match Level:" msgstr "Corrispondenza:" #: fotoxx.h:1381 msgid "Max" msgstr "Massimo" #: fotoxx.h:1382 msgid "Measure" msgstr "Misura" #: fotoxx.h:1383 msgid "mouse radius" msgstr "Raggio del mouse" #: fotoxx.h:1384 msgid "Negative" msgstr "Negativo" #: fotoxx.h:1385 msgid "New" msgstr "Nuovo" #: fotoxx.h:1386 msgid "Next" msgstr "Prossima" #: fotoxx.h:1387 zfuncs.cc:11875 msgid "No" msgstr "No" #: fotoxx.h:1388 msgid "no write permission" msgstr "manca permesso di scrittura" #: fotoxx.h:1389 msgid "no images" msgstr "nessuna immagine" #: fotoxx.h:1390 msgid "image index disabled" msgstr "indice delle immagini disabilitato" #: fotoxx.h:1391 msgid "no image files selected" msgstr "nessun file d'immagine selezionato" #: fotoxx.h:1392 msgid "None" msgstr "Nessuna" #: fotoxx.h:1393 msgid "no selection" msgstr "nessuna selezione" #: fotoxx.h:1394 msgid "image index not updated" msgstr "l'indice delle immagini non è aggiornato" #: fotoxx.h:1395 msgid "opacity center" msgstr "Opacità al centro" #: fotoxx.h:1396 msgid "opacity edge" msgstr "Opacità dei bordi" #: fotoxx.h:1398 msgid "Paint Radius" msgstr "Raggio del pennello" #: fotoxx.h:1400 msgid "Pause" msgstr "Sospendi" #: fotoxx.h:1401 msgid "Percent" msgstr "Percentuale" #: fotoxx.h:1403 msgid "Presets" msgstr "Fisse: " #: fotoxx.h:1404 msgid "Prev" msgstr "Preced." #: fotoxx.h:1405 msgid "use previous input" msgstr "Usa input precedente" #: fotoxx.h:1406 msgid "Proceed" msgstr "Prosegui" #: fotoxx.h:1409 msgid "range" msgstr "intervallo" #: fotoxx.h:1410 msgid "Red" msgstr "Rosso" #: fotoxx.h:1411 msgid "Redo" msgstr "Ripeti" #: fotoxx.h:1412 msgid "Reduce" msgstr "Riduci" #: fotoxx.h:1413 msgid "Remove" msgstr "Elimina" #: fotoxx.h:1415 msgid "Replace" msgstr "Rimpiazza" #: fotoxx.h:1416 msgid "Reserved" msgstr "Riservato" #: fotoxx.h:1417 msgid "Reset" msgstr "Azzera" #: fotoxx.h:1418 zfuncs.cc:12921 msgid "Right" msgstr "Destra" #: fotoxx.h:1419 msgid "Rotate" msgstr "Ruota" #: fotoxx.h:1420 msgid "Run" msgstr "Avvia" #: fotoxx.h:1423 msgid "Seconds" msgstr "Secondi" #: fotoxx.h:1427 msgid "Size" msgstr "Dimensione" #: fotoxx.h:1428 msgid "Start" msgstr "Inizio" #: fotoxx.h:1429 msgid "Stop" msgstr "Arresta" #: fotoxx.h:1430 msgid "Strength" msgstr "Intensità" #: fotoxx.h:1431 msgid "the area is not finished" msgstr "L'area non è chiusa" #: fotoxx.h:1432 msgid "Threshold" msgstr "Soglia" #: fotoxx.h:1433 zfuncs.cc:12909 msgid "Top" msgstr "Alto" #: fotoxx.h:1434 msgid "Transparency" msgstr "Trasparenza:" #: fotoxx.h:1435 msgid "Trash" msgstr "Cestina" #: fotoxx.h:1436 msgid "Trim" msgstr "Ritaglia" #: fotoxx.h:1437 msgid "Undo All" msgstr "Annulla tutto" #: fotoxx.h:1438 msgid "Undo Last" msgstr "Annulla ultimo" #: fotoxx.h:1439 msgid "Undo" msgstr "Annulla" #: fotoxx.h:1440 msgid "Unfinish" msgstr "Ricomincia" #: fotoxx.h:1441 msgid "Update" msgstr "Aggiorna" #: fotoxx.h:1442 msgid "View" msgstr "Vista" #: fotoxx.h:1443 msgid "Web" msgstr "Web" #: fotoxx.h:1444 msgid "White" msgstr "Bianco" #: fotoxx.h:1445 zfuncs.cc:12939 msgid "Width" msgstr "Larghezza" #: fotoxx.h:1446 msgid "x-offset" msgstr "Origine X:" #: fotoxx.h:1447 msgid "y-offset" msgstr "Origine Y:" #: fotoxx.h:1448 zfuncs.cc:11875 msgid "Yes" msgstr "Si" #: zfuncs.cc:1836 #, c-format msgid "" "create folder? \n" " %s" msgstr "" "creare folder? \n" " %s" #: zfuncs.cc:5880 msgid "" "If you permit, a message is occasionally \n" "sent to the web host for usage statistics. \n" "Nothing is retained that can be associated \n" "with a person or computer or location." msgstr "" "Se si acconsente, un messaggio viene occasionalmente \n" "inviato al sito web di fotoxx per fare statistiche. \n" "Nulla viene conservato che possa essere associato \n" "a persone, computer o luoghi." #: zfuncs.cc:6743 #, c-format msgid "cannot open file %s" msgstr "non riesco ad aprire il file %s" #: zfuncs.cc:6766 msgid "save text to file" msgstr "Salva testo nel file" #: zfuncs.cc:7936 msgid "edit menu entry" msgstr "modifica voce di menù" #: zfuncs.cc:7940 msgid "menu text" msgstr "Nome nel menù" #: zfuncs.cc:7941 msgid "menu func" msgstr "Funzione" #: zfuncs.cc:7942 msgid "menu icon" msgstr "Icona" #: zfuncs.cc:7943 msgid "icon size" msgstr "Dimensione icona" #: zfuncs.cc:7946 msgid "Bold" msgstr "Grassetto" #: zfuncs.cc:7953 msgid "close window" msgstr "Chiudi finestra" #: zfuncs.cc:8000 msgid "select icon" msgstr "seleziona icona" #: zfuncs.cc:12000 zfuncs.cc:12896 msgid "cancel" msgstr "Annulla" #: zfuncs.cc:12489 msgid "choose file" msgstr "Scegli file" #: zfuncs.cc:12494 msgid "choose files" msgstr "Scegli i file" #: zfuncs.cc:12505 msgid "choose folder" msgstr "Scegli cartella" #: zfuncs.cc:12510 msgid "choose folders" msgstr "Scegli cartelle" #: zfuncs.cc:12515 msgid "create folder" msgstr "Crea cartella" #: zfuncs.cc:12522 msgid "hidden" msgstr "Nascosto" #: zfuncs.cc:12896 msgid "done" msgstr "Fatto" #: zfuncs.cc:12926 msgid "image scale" msgstr "Riscala immagine" #: zfuncs.cc:12928 msgid "percent" msgstr "percento" fotoxx-20.08/locales/translate-pt.po000066400000000000000000005057701362435004500174720ustar00rootroot00000000000000# Portuguese translations for home package. # Copyright (C) 2018 THE home'S COPYRIGHT HOLDER # This file is distributed under the same license as the home package. # msgid "" msgstr "" "Project-Id-Version: fotoxx-19\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-02-19 10:17+0100\n" "PO-Revision-Date: 2020-02-19 13:53+1300\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 2.0.6\n" #: f.albums.cc:59 #, c-format msgid "max. album size exceeded: %d" msgstr "tamanho máximo do álbum excedido: %d" #: f.albums.cc:70 msgid "" "Right-click album thumbnail for edits: \n" " - add selected files at this position \n" " - remove this file from the album" msgstr "" "Clique com o botão direito do mouse na miniatura do álbum para editar: \n" " - adicione os arquivos selecionados nesta posição \n" " - remova este arquivo do álbum" #: f.albums.cc:73 msgid "Arrange files with thumbnail drag and drop" msgstr "Organizar arquivos com o arrastar e soltar miniaturas" #: f.albums.cc:102 f.widgets.cc:461 msgid "Manage Albums" msgstr "Gerenciar Álbuns" #: f.albums.cc:113 f.albums.cc:232 msgid "Create or replace an album" msgstr "Criar ou substituir um álbum" #: f.albums.cc:115 f.albums.cc:381 msgid "Rename an album" msgstr "Renomear um álbum" #: f.albums.cc:117 f.albums.cc:451 msgid "Delete an album" msgstr "Excluir um album" #: f.albums.cc:119 msgid "Files to add to an album" msgstr "Arquivos para adicionar a um álbum" #: f.albums.cc:183 #, c-format msgid "%d files added to album %s" msgstr "%d arquivos adicionados ao álbum %s" #: f.albums.cc:213 #, c-format msgid "max. album count exceeded: %d" msgstr "excedeu máx. contagem de álbuns: %d" #: f.albums.cc:234 f.albums.cc:270 f.albums.cc:1537 msgid "Album Name" msgstr "Nome do Álbum" #: f.albums.cc:240 msgid "make an initially empty album" msgstr "criar um álbum inicialmente vazio" #: f.albums.cc:241 msgid "fill from pre-selected files" msgstr "preencher utilizando arquivos pré-selecionados" #: f.albums.cc:242 msgid "fill from current gallery" msgstr "preencher usando galeria atual" #: f.albums.cc:243 msgid "select initial files" msgstr "selecionar arquivos iniciais" #: f.albums.cc:284 msgid "enter an album name" msgstr "inserir um nome para o álbum" #: f.albums.cc:292 f.albums.cc:1569 #, c-format msgid "replace album %s ?" msgstr "substituir álbum %s ?" #: f.albums.cc:312 msgid "Use [Select] to add files to an empty album" msgstr "Use [Selecionar] para adicionar arquivos a um álbum vazio" #: f.albums.cc:326 msgid "new album created" msgstr "novo álbum criado" #: f.albums.cc:342 f.meta.cc:7854 msgid "gallery is empty" msgstr "galeria vazia" #: f.albums.cc:396 msgid "enter new album name" msgstr "digite o novo nome de álbum" #: f.albums.cc:403 #, c-format msgid "invalid file name: %s" msgstr "nome de arquivo inválido: %s" #: f.albums.cc:424 #, c-format msgid "album already exists: %s" msgstr "álbum já existe: %s" #: f.albums.cc:435 #, c-format msgid "" "%s \n" " renamed: %s" msgstr "" "%s \n" " renomeado: %s" #: f.albums.cc:464 #, c-format msgid "delete %s ?" msgstr "excluir %s ?" #: f.albums.cc:571 f.albums.cc:584 #, c-format msgid "%s removed with no replacement \n" msgstr "%s removidos sem nenhum substituto \n" #: f.albums.cc:589 #, c-format msgid "%s replaced by ...%s \n" msgstr "%s substituído por ...%s \n" #: f.albums.cc:664 msgid "no selected files" msgstr "nenhum arquivo selecionado" #: f.albums.cc:877 msgid "Replace Album File" msgstr "Substituir Arquivo de Álbum" #: f.albums.cc:879 f.albums.cc:1004 msgid "Choose Albums" msgstr "Escolher Álbuns" #: f.albums.cc:883 msgid "old file" msgstr "arquivo antigo" #: f.albums.cc:886 msgid "new file" msgstr "novo arquivo" #: f.albums.cc:889 msgid "replace old" msgstr "substituir antigo" #: f.albums.cc:890 msgid "add after old" msgstr "adicionar depois de antigo" #: f.albums.cc:936 msgid "no albums chosen" msgstr "nenhum álbum escolhido" #: f.albums.cc:1117 f.widgets.cc:463 msgid "Album Mass Update" msgstr "Atualizar vários Álbuns" #: f.albums.cc:1125 msgid "Process all album files:" msgstr "Processar todos arquivos de álbum:" #: f.albums.cc:1128 msgid "Replace all with newest version only" msgstr "Substituir todos com a versão mais recente apenas" #: f.albums.cc:1130 msgid "Replace all versions with newest version" msgstr "Substituir todos com versão mais recente" #: f.albums.cc:1132 msgid "Add newest version to existing versions" msgstr "Adicionar nova versão a outras versões existentes" #: f.albums.cc:1134 msgid "Replace all with original and all versions" msgstr "Substituir todos com os originais e todas versões" #: f.albums.cc:1136 msgid "Replace all with original + newest version" msgstr "Substir todos pela versão original + versão mais recente" #: f.albums.cc:1140 msgid "Process album files matching selected files:" msgstr "" "Processar arquivos do álbum que correspondem aos arquivos selecionados:" #: f.albums.cc:1143 msgid "Replace all with selected versions" msgstr "Substituir todos por versões selecionadas" #: f.albums.cc:1145 msgid "Replace all versions with selected versions" msgstr "Substituir todas versões por versões selecionadas" #: f.albums.cc:1147 msgid "Add selected versions to existing versions" msgstr "Adicionar versões selecionadas às versões existentes" #: f.albums.cc:1149 msgid "Replace all with original + selected versions" msgstr "Substituir todos por versão original + versões selecionadas" #: f.albums.cc:1237 msgid "Select Albums" msgstr "Selecione Álbuns" #: f.albums.cc:1296 #, c-format msgid "too many selected files: %d" msgstr "arquivos selecionados em excesso: %d" #: f.albums.cc:1535 msgid "Save Gallery as Album" msgstr "Salvar Galeria como Álbum" #: f.albums.cc:1739 msgid "instant" msgstr "imediato" #: f.albums.cc:1740 msgid "fade-in" msgstr "esmaecer" #: f.albums.cc:1741 msgid "roll-right" msgstr "rolar para direita" #: f.albums.cc:1742 msgid "roll-down" msgstr "rolar para baixo" #: f.albums.cc:1743 msgid "venetian" msgstr "veneziano" #: f.albums.cc:1744 msgid "grate" msgstr "grade" #: f.albums.cc:1745 msgid "rectangle" msgstr "retângulo" #: f.albums.cc:1746 msgid "implode" msgstr "implodir" #: f.albums.cc:1747 msgid "explode" msgstr "explodir" #: f.albums.cc:1748 msgid "radar" msgstr "radar" #: f.albums.cc:1749 msgid "Japan-fan" msgstr "Leque Japonês" #: f.albums.cc:1750 msgid "spiral" msgstr "espiral" #: f.albums.cc:1751 f.area.cc:140 msgid "ellipse" msgstr "elipse" #: f.albums.cc:1752 msgid "raindrops" msgstr "gotas de chuva" #: f.albums.cc:1753 msgid "doubledoor" msgstr "doubledoor" #: f.albums.cc:1754 msgid "rotate" msgstr "rotacionar" #: f.albums.cc:1755 msgid "fallover" msgstr "tombar" #: f.albums.cc:1756 msgid "spheroid" msgstr "esferóide" #: f.albums.cc:1757 msgid "turn-page" msgstr "vira-página" #: f.albums.cc:1758 msgid "french-door" msgstr "porta de entrada" #: f.albums.cc:1759 msgid "turn-cube" msgstr "cubo mágico" #: f.albums.cc:1760 msgid "windmill" msgstr "moinho de vento" #: f.albums.cc:1761 msgid "pixelize" msgstr "pixelizar" #: f.albums.cc:1762 msgid "twist" msgstr "torcer" #: f.albums.cc:1763 msgid "Xopen" msgstr "aberturaX" #: f.albums.cc:1764 msgid "squish" msgstr "esmagar" #: f.albums.cc:1765 msgid "disintegrate" msgstr "desintegrar" #: f.albums.cc:1766 msgid "interleave" msgstr "intercalar" #: f.albums.cc:1797 f.widgets.cc:465 msgid "Slide Show" msgstr "Apresentação" #: f.albums.cc:1800 msgid "Select Album" msgstr "Selecionar Álbum" #: f.albums.cc:1805 msgid "Caption Time" msgstr "Tempode Legenda" #: f.albums.cc:1808 msgid "Image Time" msgstr "Tempo de Imagem" #: f.albums.cc:1811 msgid "Clip Limit %" msgstr "Limite de Recorte %" #: f.albums.cc:1815 msgid "Music File" msgstr "Arquivo de Música" #: f.albums.cc:1820 msgid "Full Screen" msgstr "Tela cheia" #: f.albums.cc:1821 msgid "Auto-replay" msgstr "Auto-repetição" #: f.albums.cc:1824 msgid "Customize:" msgstr "Customizar:" #: f.albums.cc:1825 msgid "transitions" msgstr "transições" #: f.albums.cc:1826 msgid "image files" msgstr "arquivos de imagem" #: f.albums.cc:1827 msgid "KB controls" msgstr "Teclas de Controle" #: f.albums.cc:1841 f.albums.cc:1908 f.albums.cc:2141 f.albums.cc:2450 #: f.albums.cc:2786 f.albums.cc:2803 f.albums.cc:3039 msgid "invalid album" msgstr "álbum inválido" #: f.albums.cc:1936 msgid "open album" msgstr "abrir álbum" #: f.albums.cc:1948 msgid "Select music file" msgstr "Selecionar arquivo de música" #: f.albums.cc:1983 #, c-format msgid "%d images" msgstr "%d imagens" #: f.albums.cc:2008 msgid "" "arrow keys show previous or next image instantly \n" "space bar (blank) is allowed and shows as '-'" msgstr "" "tecla de setas mostram imagem próxima ou anterior instantaneamente \n" "barra de espaço (vazio) é permitido e aparece como '-'" #: f.albums.cc:2027 msgid "Keyboard Preferences" msgstr "Preferências do teclado" #: f.albums.cc:2030 msgid "blank or unblank window" msgstr "branquear ou desbranquear tela" #: f.albums.cc:2033 msgid "show next image, with transition" msgstr "mostra a próxima imagem, com transição" #: f.albums.cc:2036 msgid "pause or resume slide show" msgstr "pausa ou retorna a apresentação de slides" #: f.albums.cc:2039 msgid "magnify image (loupe tool)" msgstr "ampliar imagem (ferramenta lupa)" #: f.albums.cc:2145 msgid "Transition Preferences" msgstr "Preferências de Transição" #: f.albums.cc:2147 msgid "Transitions File" msgstr "Arquivo de Transições" #: f.albums.cc:2156 msgid "random" msgstr "randômico" #: f.albums.cc:2159 f.albums.cc:2175 f.albums.cc:2179 msgid "time" msgstr "tempo" #: f.albums.cc:2161 msgid "set all" msgstr "aplicar todos" #: f.albums.cc:2173 f.albums.cc:2177 msgid "transition" msgstr "transição" #: f.albums.cc:2174 f.albums.cc:2178 msgid "use" msgstr "usar" #: f.albums.cc:2176 f.albums.cc:2180 msgid "pref" msgstr "pref" #: f.albums.cc:2280 f.albums.cc:2322 msgid "invalid file" msgstr "arquivo inválido" #: f.albums.cc:2394 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "error no formato de arquivo: \n" " %s" #: f.albums.cc:2456 msgid "Image Preferences" msgstr "Preferências de Imagem" #: f.albums.cc:2460 f.file.cc:1610 f.file.cc:1995 msgid "Image File:" msgstr "Arquivo de Imagem:" #: f.albums.cc:2464 msgid "Action" msgstr "Ação" #: f.albums.cc:2476 msgid "Play tone when image shows" msgstr "Tocar áudio quando imagem aparecer" #: f.albums.cc:2481 msgid "Wait before filename/caption/comments" msgstr "Atraso antes de nome do arquivo/legenda/comentários" #: f.albums.cc:2485 msgid "Show image file name (overlap)" msgstr "Mostrar nome do arquivo de imagem (sobreposição)" #: f.albums.cc:2489 msgid "Show image caption (overlap)" msgstr "Mostrar legenda de imagem (sobreposição)" #: f.albums.cc:2493 msgid "Show image comments (overlap)" msgstr "Mostrar comentários da imagem (sobreposição)" #: f.albums.cc:2497 msgid "Wait before zoom" msgstr "Atraso antes do Zoom" #: f.albums.cc:2501 f.effects.cc:4023 msgid "Zoom" msgstr "Zoom" #: f.albums.cc:2505 msgid "zoom-in" msgstr "aproximar" #: f.albums.cc:2506 msgid "zoom-out" msgstr "afastar" #: f.albums.cc:2510 msgid "Zoom Center" msgstr "Zoom Centralizado" #: f.albums.cc:2515 msgid "Wait after zoom" msgstr "Atraso depois do Zoom" #: f.albums.cc:2521 msgid "Transition to next image" msgstr "Transição para a próxima imagem" #: f.albums.cc:2524 f.albums.cc:2654 msgid "next" msgstr "próximo" #: f.albums.cc:2644 msgid "click on thumbnail to set zoom center" msgstr "clique na miniatura para configurar centralização e zoom" #: f.area.cc:93 msgid "Select Area for Edits" msgstr "Selecionar área para edições" #: f.area.cc:94 f.area.cc:1890 f.edit.cc:8204 msgid "Press F1 for help" msgstr "Pressione F1 para ajuda" #: f.area.cc:104 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:139 msgid "select rectangle" msgstr "seleção retangular" #: f.area.cc:143 msgid "freehand draw" msgstr "desenho a mão livre" #: f.area.cc:144 msgid "follow edge" msgstr "seguir borda" #: f.area.cc:147 msgid "draw/replace" msgstr "desenho/substituir" #: f.area.cc:150 msgid "Line Color:" msgstr "Cor de Linha:" #: f.area.cc:164 msgid "match level %" msgstr "nível de correspondência %" #: f.area.cc:168 msgid "search range" msgstr "intervalo de procura" #: f.area.cc:172 msgid "select area in mouse" msgstr "selecionar com o cursor" #: f.area.cc:175 msgid "select one color in mouse:" msgstr "selecione uma cor no cursor:" #: f.area.cc:179 msgid "select all colors in mouse (flood)" msgstr "selecionar todas cores com o cursor (transbordar)" #: f.area.cc:184 msgid "Area Edge Blend Width" msgstr "Largura da Área de Borda da Mistura " #: f.area.cc:188 msgid "Edge Creep" msgstr "Deslizamento de Borda" #: f.area.cc:200 msgid "drag mouse to select rectangular area" msgstr "arraste com o cursor para selecionar área retangular" #: f.area.cc:201 msgid "drag mouse to select circular or elliptical area" msgstr "arraste com o cursor para selecionar área circular ou elíptica" #: f.area.cc:202 msgid "drag mouse to outline an area" msgstr "arraste com o cursor para delinear uma área" #: f.area.cc:203 msgid "drag mouse along an edge to follow the edge" msgstr "arraste com o cursor ao longo de uma borda para segui-la" #: f.area.cc:204 msgid "drag mouse near a line to move the line" msgstr "arraste com o cursor perto de uma linha para mover a linha" #: f.area.cc:205 msgid "select line color" msgstr "selecionar a cor da linha" #: f.area.cc:206 msgid "size of mouse selection circle" msgstr "tamanho do círculo de seleção do cursor" #: f.area.cc:207 msgid "required match level to select by color" msgstr "nível de correspondência necessário para seleção por cor" #: f.area.cc:208 msgid "select by color search range" msgstr "selecionar por pesquisa em faixa de cores" #: f.area.cc:209 msgid "select area within mouse circle" msgstr "selecionar a área dentro do círculo do cursor" #: f.area.cc:210 msgid "" "first select the checkbox, then \n" "shift+click on image to set the color" msgstr "" "primeiro marque na caixa de seleção, depois \n" "Shift+Clique na imagem para definir a cor" #: f.area.cc:212 msgid "select surrounding areas matching colors in mouse" msgstr "selecionar áreas adjacentes correspondentes com as cores no cursor" #: f.area.cc:213 f.area.cc:214 msgid "area edits fade away within edge distance" msgstr "edições na área esmaecem dentre as distâncias de borda" #: f.area.cc:215 msgid "move area boundary in/out in 1-pixel steps" msgstr "mover contorno de área para dentro/fora em etapas de 1 pixel" #: f.area.cc:216 msgid "map selected areas and verify" msgstr "mapeie áreas selecionadas e verifique" #: f.area.cc:217 msgid "invert area" msgstr "inverter área" #: f.area.cc:218 msgid "show area outlines" msgstr "mostrar contornos da área" #: f.area.cc:219 msgid "hide area outlines" msgstr "ocultar contornos da área" #: f.area.cc:220 msgid "clear area selections" msgstr "limpar seleções de área" #: f.area.cc:404 f.area.cc:570 #, c-format msgid "exceed %d edits" msgstr "excedeu %d edições" #: f.area.cc:1471 msgid "" "Fill selected areas with color for visual verification. \n" "Method 1: left-click in each selected area not already filled. \n" "Method 2: right-click OUTSIDE all selected areas. \n" "Press [help] button for clarification" msgstr "" "Preencha áreas selecionadas com cor para verificação visual. \n" "Método 1: clique com o esquerdo em cada área selecionada ainda não " "preenchida. \n" "Método 2: clique com o direto FORA de todas áreas selecionadas. \n" "Pressione o botão [ajuda] para mais detalhes" #: f.area.cc:1499 msgid "finish area" msgstr "finalize a área" #: f.area.cc:1528 f.area.cc:1757 #, c-format msgid "found %d pixels" msgstr "encontrados %d pixels" #: f.area.cc:1546 msgid "" "Method 1: \n" " Left-click inside an outlined area that is not already filled. \n" " Area will be filled with color for visible verification. \n" " Gaps in the outline will cause overflow outside the area. \n" " Repeat for each outlined area that is not already filled. \n" "Method 2: \n" " Right-click outside ALL outlined areas. \n" " All areas will be filled with color for visible verification. \n" " Gaps in an area outline will cause that area not to be filled. \n" "Gaps in an area outline: \n" " Gaps must be closed before proceeding with edits. \n" " The Find Gap function can be used for this." msgstr "" "Método 1: \n" " Clique com o esquerdo dentro de área delineada ainda não preenchida. \n" " A área será preenchida com cor para verificação visual. \n" " Lacunas no contorno causarão transbordamento para a parte externa. \n" " Repita para cada área delineada que ainda não está preenchida. \n" "Método 2: \n" " Clique com o direito fora de TODAS áreas delineadas. \n" " Todas áreas serão preenchidas com cor para verificação visual. \n" " Lacunas no contorno de uma área causará que esta não seja preenchida. \n" "Lacunas no contorno de área: \n" " Lacunes devem ser preenchidas antes de proceder com edições. \n" " A função Encontrar Lacuna pode ser usada para isto." #: f.area.cc:1889 f.widgets.cc:490 msgid "Select Hairy" msgstr "Seleção Irregular" #: f.area.cc:1899 msgid "select the area first" msgstr "selecione a área primeiro" #: f.area.cc:1975 msgid "select" msgstr "selecionar" #: f.area.cc:1981 msgid "deselect" msgstr "deselecionar" #: f.area.cc:2335 msgid "" "Click near any position on the area outline. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Clique próximo a qualquer posição do contorno da área. \n" "Possíveis lacunas no contorno serão encontradas. \n" "Pressione F1 para obter ajuda." #: f.area.cc:2356 msgid "find outline gap" msgstr "procurar lacuna de contorno" #: f.area.cc:2451 msgid "cannot find area outline" msgstr "não foi possível encontrar área de contorno" #: f.area.cc:3285 msgid "save area as a PNG file" msgstr "salvar área como um arquivo PNG" #: f.area.cc:3407 msgid "position with mouse click/drag" msgstr "posicione o cursor clique/arraste" #: f.area.cc:3467 msgid "Paste Image" msgstr "Colar Imagem" #: f.area.cc:3472 msgid "resize" msgstr "redimensionar" #: f.combine.cc:174 f.combine.cc:828 f.combine.cc:1436 f.combine.cc:2066 #: f.combine.cc:2510 msgid "Select 2 to 9 files" msgstr "Selecionar 2 a 9 arquivos" #: f.combine.cc:196 f.combine.cc:850 f.combine.cc:1458 f.combine.cc:2088 msgid "Images are not all the same size" msgstr "Imagens não são todas do mesmo tamanho" #: f.combine.cc:560 msgid "Adjust Image Contributions" msgstr "Ajustar contribuição de imagem" #: f.combine.cc:564 msgid "dark pixels" msgstr "pixels escuros" #: f.combine.cc:566 msgid "light pixels" msgstr "pixels claros" #: f.combine.cc:568 msgid "file:" msgstr "arquivo:" #: f.combine.cc:1036 msgid "Paint and Warp Image" msgstr "Pintar e distorcer imagem" #: f.combine.cc:1044 f.edit.cc:6060 msgid "paint" msgstr "pintar" #: f.combine.cc:1045 msgid "warp" msgstr "distorção" #: f.combine.cc:1648 f.combine.cc:2594 msgid "Select and Paint Image" msgstr "Selecionar e pintar imagem" #: f.combine.cc:1656 msgid "Transient Objects" msgstr "Objetos Transientes" #: f.combine.cc:2264 msgid "Adjust Pixel Composition" msgstr "Ajustar Composição do Pixel" #: f.combine.cc:2266 msgid "use average" msgstr "usar média" #: f.combine.cc:2267 msgid "use median" msgstr "usar mediana" #: f.combine.cc:2269 msgid "omit low pixel" msgstr "omitir pixel baixos" #: f.combine.cc:2270 msgid "omit high pixel" msgstr "omitir pixels altos" #: f.combine.cc:2532 f.combine.cc:2844 msgid "Images must be exactly the same size" msgstr "Imagens tem de ser exatamente do mesmo tamanho" #: f.combine.cc:2605 msgid " Fill " msgstr " Preencher " #: f.combine.cc:2606 msgid "using selected image" msgstr "usando imagem selecionada" #: f.combine.cc:2825 f.combine.cc:3095 msgid "Select 2 files" msgstr "Selecione 2 arquivos" #: f.combine.cc:2902 msgid "Split two Images" msgstr "Dividir duas imagens" #: f.combine.cc:2903 msgid "drag image boundary" msgstr "arraste o contorno da imagem" #: f.combine.cc:3092 msgid "Image Differences" msgstr "Diferença de Imagens" #: f.combine.cc:3108 msgid "differences" msgstr "diferenças" #: f.combine.cc:3110 msgid "X-align" msgstr "Alinhamento-X" #: f.combine.cc:3113 msgid "Y-align" msgstr "Alinhamento-Y" #: f.combine.cc:3179 msgid "select exactly 2 files" msgstr "selecionar exatamente 2 arquivos" #: f.combine.cc:3408 f.combine.cc:4562 msgid "Select 2 to 4 files" msgstr "Selecionar 2 a 4 arquivos" #: f.combine.cc:3483 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:3485 f.combine.cc:4639 msgid "no curve (scanned image)" msgstr "não vetorial (imagem digitalizada)" #: f.combine.cc:3486 f.combine.cc:4640 msgid "Search for lens mm" msgstr "Procurar por mm de lente" #: f.combine.cc:3487 f.combine.cc:4641 msgid "Save lens mm → image EXIF" msgstr "Salvar mm de lente → EXIF imagem" #: f.combine.cc:3547 f.combine.cc:4701 msgid "Pre-align Images" msgstr "Pré-alinhamento de imagens" #: f.combine.cc:3551 f.combine.cc:4705 msgid "lens mm" msgstr "mm de lente" #: f.combine.cc:3556 f.combine.cc:4710 msgid "no auto warp" msgstr "não auto distorcer" #: f.combine.cc:3558 f.combine.cc:4712 msgid "manual align" msgstr "alinhar manualmente" #: f.combine.cc:3560 f.combine.cc:4714 f.process.cc:1395 f.widgets.cc:507 #: f.widgets.cc:832 msgid "Resize" msgstr "Redimensionar" #: f.combine.cc:3561 f.combine.cc:4715 msgid "resize window" msgstr "redimensionar janela" #: f.combine.cc:3569 f.combine.cc:4723 msgid "do not warp images during auto-alignment" msgstr "não distorcer imagem durante auto-alinhamento" #: f.combine.cc:3614 f.combine.cc:4768 msgid "use two images only" msgstr "use duas imagens apenas" #: f.combine.cc:3644 f.combine.cc:3848 f.combine.cc:4034 f.combine.cc:4796 #: f.combine.cc:5000 f.combine.cc:5186 msgid "Too little overlap, cannot align" msgstr "Baixa sobreposição, não é possível alinhar" #: f.combine.cc:4112 f.combine.cc:5264 msgid "Match Brightness and Color" msgstr "Parear Brilho e Cor" #: f.combine.cc:4158 msgid "Select image" msgstr "Selecionar imagem" #: f.combine.cc:4173 f.combine.cc:5325 msgid "auto color" msgstr "cor automática" #: f.combine.cc:4174 f.combine.cc:5326 msgid "file color" msgstr "arquivo de cor" #: f.combine.cc:4180 f.combine.cc:5332 msgid "mouse warp" msgstr "distorcer com cursor" #: f.combine.cc:4182 f.combine.cc:5334 msgid "flatten image" msgstr "aplainar imagem" #: f.combine.cc:4637 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:5596 msgid "pano tools (hugin) not installed" msgstr "pano tools (hugin) não instalado" #: f.combine.cc:5605 msgid "Select at least 2 files" msgstr "Selecione pelo menos 2 arquivos" #: f.edit.cc:106 msgid "" "drag middle to move \n" "drag corners to resize \n" "drag right edge to level" msgstr "" "arraste pelo meio para mover \n" "arraste pelos cantos para redimensionar \n" "arraste pela borda direita para inclinar" #: f.edit.cc:209 f.widgets.cc:504 f.widgets.cc:831 msgid "Trim/Rotate" msgstr "Recortar/Rodar" #: f.edit.cc:224 f.edit.cc:1483 msgid "ratio" msgstr "taxa" #: f.edit.cc:226 msgid "Lock Ratio" msgstr "Travar taxa" #: f.edit.cc:229 msgid "Trim Size:" msgstr "Tamanho do Recorte:" #: f.edit.cc:237 msgid "Set Ratio" msgstr "Definir Razão" #: f.edit.cc:238 f.widgets.cc:496 fotoxx.h:1373 msgid "Invert" msgstr "Inverter" #: f.edit.cc:239 msgid "Customize" msgstr "Customizar" #: f.edit.cc:248 msgid "Degrees:" msgstr "Graus:" #: f.edit.cc:250 msgid "Auto Level" msgstr "Nivelamento Automático" #: f.edit.cc:256 f.process.cc:186 f.process.cc:822 f.widgets.cc:505 #: f.widgets.cc:833 msgid "Upright" msgstr "Orientar" #: f.edit.cc:258 msgid "maximize trim box" msgstr "maximizar caixa de recorte" #: f.edit.cc:259 msgid "trim transparent edges" msgstr "aparar bordas transparentes" #: f.edit.cc:260 msgid "lock width/height ratio" msgstr "travar proporção altura/largura" #: f.edit.cc:261 msgid "use EXIF data if available" msgstr "usar informação EXIF se disponível" #: f.edit.cc:603 f.edit.cc:1550 msgid "rotation unknown" msgstr "rotação desconhecida" #: f.edit.cc:1479 msgid "Trim Buttons" msgstr "Aparar rodapés" #: f.edit.cc:1482 msgid "label" msgstr "etiqueta" #: f.edit.cc:1692 f.widgets.cc:506 f.widgets.cc:836 msgid "Retouch" msgstr "Retoque" #: f.edit.cc:1705 msgid "Auto black level" msgstr "Balanço de negro automático" #: f.edit.cc:1708 f.edit.cc:1714 msgid "sample %" msgstr "amostra %" #: f.edit.cc:1711 msgid "Auto white balance" msgstr "Balanço de branco automático" #: f.edit.cc:1717 msgid "Click gray spot for white balance" msgstr "Clique num ponto cinza para balancear o branco" #: f.edit.cc:1720 msgid "Click dark spot for black level" msgstr "Clique na área preta para balancear o negro" #: f.edit.cc:1723 msgid "Click for RGB distribution" msgstr "Clique para distribuição RGB" #: f.edit.cc:1731 fotoxx.h:1328 msgid "Brightness" msgstr "Briho" #: f.edit.cc:1732 f.effects.cc:4049 fotoxx.h:1341 msgid "Contrast" msgstr "Contraste" #: f.edit.cc:1733 f.edit.cc:3279 f.edit.cc:3311 f.edit.cc:6783 msgid "Saturation" msgstr "Saturação" #: f.edit.cc:1734 msgid "Temperature" msgstr "Temperatura" #: f.edit.cc:1742 msgid "Settings File" msgstr "Arquivos de Configuração" #: f.edit.cc:2250 msgid "choose a better spot" msgstr "escolher um ponto melhor" #: f.edit.cc:2555 msgid "Resize Image" msgstr "Redimensionar" #: f.edit.cc:2571 msgid "Previous" msgstr "Anterior" #: f.edit.cc:2589 msgid "W/H Ratio:" msgstr "Relação L/A:" #: f.edit.cc:2591 msgid "Lock" msgstr "Travar" #: f.edit.cc:2840 msgid "insufficient memory, cannot proceed" msgstr "memória insuficiente, impossível prosseguir" #: f.edit.cc:2936 f.widgets.cc:508 msgid "Adjust RGB" msgstr "Adjuste RGB" #: f.edit.cc:2942 msgid "+Brightness" msgstr "+Brilho" #: f.edit.cc:2943 msgid "+Red -Cyan" msgstr "+Vermelho -Ciano" #: f.edit.cc:2944 msgid "+Green -Magenta" msgstr "+Verde -Magenta" #: f.edit.cc:2945 msgid "+Blue -Yellow" msgstr "+Azul -Amarelo" #: f.edit.cc:2948 msgid "Contrast Red" msgstr "Contraste Vermelho" #: f.edit.cc:2949 msgid "Contrast Green" msgstr "Contraste Verde" #: f.edit.cc:2950 msgid "Contrast Blue" msgstr "Contraste Azul" #: f.edit.cc:3268 f.widgets.cc:509 msgid "Adjust HSL" msgstr "Ajustar HSL" #: f.edit.cc:3272 msgid "Input color to match and adjust:" msgstr "Cor de Entrada a buscar e ajustar" #: f.edit.cc:3274 msgid "shift+click on image to select color" msgstr "shift+clique na imagem para selecionar cor" #: f.edit.cc:3277 msgid "Match using:" msgstr "Comparar usando:" #: f.edit.cc:3278 msgid "Hue" msgstr "Matiz" #: f.edit.cc:3280 f.edit.cc:3312 f.edit.cc:6784 msgid "Lightness" msgstr "Luminosidade" #: f.edit.cc:3290 msgid "Output Color" msgstr "Cor de Saída" #: f.edit.cc:3310 f.edit.cc:6782 msgid "Color Hue" msgstr "Matiz de Cor" #: f.edit.cc:3313 msgid "Adjustment" msgstr "Ajuste" #: f.edit.cc:3828 msgid "Image Markup" msgstr "Marcador de Imagem" #: f.edit.cc:3829 f.edit.cc:3894 msgid "Draw text on image" msgstr "Escrever texto na imagem" #: f.edit.cc:3830 f.edit.cc:4818 msgid "Draw line or arrow on image" msgstr "Desenhar linha ou seta na imagem" #: f.edit.cc:3831 f.edit.cc:5499 msgid "Draw box on image" msgstr "Desenhar caixa na imagem" #: f.edit.cc:3832 f.edit.cc:5703 msgid "Draw oval on image" msgstr "Desenhar oval na imagem" #: f.edit.cc:3871 msgid "+Version" msgstr "+Versão" #: f.edit.cc:3895 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:3944 msgid "Use settings file" msgstr "Usas arquivo de configuração" #: f.edit.cc:3949 f.mashup.cc:2438 f.tools.cc:1668 f.tools.cc:1677 msgid "Text" msgstr "Texto" #: f.edit.cc:3954 msgid "Use metadata key" msgstr "Use chave de metadado" #: f.edit.cc:3973 f.mashup.cc:2456 msgid "text" msgstr "texto" #: f.edit.cc:3974 f.edit.cc:4847 f.mashup.cc:2457 f.mashup.cc:2795 msgid "backing" msgstr "fundo" #: f.edit.cc:3975 f.edit.cc:4848 f.mashup.cc:2458 f.mashup.cc:2796 msgid "outline" msgstr "contorno" #: f.edit.cc:3976 f.edit.cc:4849 f.mashup.cc:2459 f.mashup.cc:2797 msgid "shadow" msgstr "sombra" #: f.edit.cc:4002 msgid "save to current file" msgstr "salvar em arquivo atual" #: f.edit.cc:4003 f.file.cc:2432 msgid "save as new file version" msgstr "salvar como nova versão de arquivo" #: f.edit.cc:4004 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:4167 f.mashup.cc:2563 f.tools.cc:1961 msgid "select font" msgstr "selecionar fonte" #: f.edit.cc:4446 msgid "text file is defective" msgstr "arquivo de texto está defeituoso" #: f.edit.cc:4786 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:4826 f.mashup.cc:2774 msgid "Line length" msgstr "Comprimento de linha" #: f.edit.cc:4833 f.mashup.cc:2781 msgid "Arrow head" msgstr "Ponta de seta" #: f.edit.cc:4846 f.mashup.cc:2794 msgid "line" msgstr "linha" #: f.edit.cc:4875 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:5474 msgid "" "drag mouse to draw box \n" "shift + drag center to move box \n" "shift + drag edge to move edge" msgstr "" "arraste o cursor para desenhar a caixa \n" "shift + arraste o centro para mover a caixa \n" "shift + arraste a borda para mover a borda" #: f.edit.cc:5505 f.edit.cc:5709 msgid "line color" msgstr "cor da linha" #: f.edit.cc:5508 f.edit.cc:5712 msgid "line width" msgstr "largura da linha" #: f.edit.cc:5677 msgid "" "drag mouse down/right to draw oval \n" "shift + drag center to move oval \n" "shift + drag lower right edge to change" msgstr "" "arraste o cursor para baixo/direita para desenhar um oval \n" "shift + arraste o centro para mover a forma oval \n" "shift + arraste a borda inferior direita para modificar" #: f.edit.cc:5715 msgid "oval" msgstr "oval" #: f.edit.cc:5716 msgid "circle" msgstr "círculo" #: f.edit.cc:5994 msgid "" "shift + left click: pick color from image \n" "left drag: paint color on image \n" "right drag: restore original image" msgstr "" "shift + clique esquerdo: captura a cor da imagem \n" "arrasto com botão esquerdo: pinta a cor na imagem \n" "arrasto com botão direito: restaura a imagem original" #: f.edit.cc:6030 msgid "Paint on Image" msgstr "Pintura em Imagem" #: f.edit.cc:6036 msgid "paint color" msgstr "cor da pintura" #: f.edit.cc:6047 f.edit.cc:6976 f.edit.cc:7554 msgid "brush size" msgstr "tamanho do pincel" #: f.edit.cc:6061 msgid "erase" msgstr "apagar" #: f.edit.cc:6066 msgid "include transparent areas" msgstr "incluir áreas transparentes" #: f.edit.cc:6069 msgid "drag image" msgstr "arrastar imagem" #: f.edit.cc:6071 msgid "zoom image" msgstr "ampliar imagem" #: f.edit.cc:6598 msgid "Color Chooser" msgstr "Seletor de Cores" #: f.edit.cc:6600 msgid "click on desired color" msgstr "clique na cor desejada" #: f.edit.cc:6935 msgid "" "shift + left click: pick position to copy \n" "left click or drag: copy image to mouse \n" "right click or drag: restore original image" msgstr "" "shift + clique esquerdo: seleciona posição a copiar \n" "arrasto ou clique esquerdo: copia imagem ao cursor \n" "arrasto ou clique direito: restaura imagem original" #: f.edit.cc:6966 msgid "Copy Pixels (1 image)" msgstr "Copiar Pixels (1 imagem)" #: f.edit.cc:6985 f.edit.cc:7563 msgid "paint over transparent areas" msgstr "pintar sobre áreas transparentes" #: f.edit.cc:7537 msgid "" "left click: synchronize copy position \n" "left click or drag: copy source image to mouse \n" "right click or drag: restore original image" msgstr "" "clique esquerdo sincroniza posição de cópia \n" "clique esquerdo ou arrasto: copia arquivo fonte \n" "clique direito ou arrasto: restaura imagem original" #: f.edit.cc:7541 msgid "Copy Pixels (2 images)" msgstr "Copiar Pixels (2 imagens)" #: f.edit.cc:7547 msgid "source image scale" msgstr "escala de imagem fonte" #: f.edit.cc:8124 msgid "source image failure (scale too big?)" msgstr "falha em imagem de origem (escala é muito grande?)" #: f.edit.cc:8203 f.widgets.cc:514 msgid "Paint Edits" msgstr "Primir Edições" #: f.edit.cc:8209 fotoxx.cc:3653 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "Área selecionada não pode ser capturada.\n" "Continuar?" #: f.edit.cc:8217 f.tools.cc:3751 msgid "Edit function must be active" msgstr "Função Editar tem de estar ativada" #: f.edit.cc:8222 msgid "Cannot use Paint Edits" msgstr "Impossível usar Edições Aplicadas" #: f.edit.cc:8249 f.edit.cc:8491 msgid "power: center" msgstr "alimentação: centro" #: f.edit.cc:8254 msgid "reset area" msgstr "redefinir área" #: f.edit.cc:8434 msgid "finish current edit first" msgstr "termine a edição atual primeiro" #: f.edit.cc:8439 msgid "no previous edit" msgstr "nenhuma edição anterior" #: f.edit.cc:8444 msgid "no current image" msgstr "nenhuma imagem corrente" #: f.edit.cc:8455 msgid "This edit cannot be incrementally undone" msgstr "Esta edição não pode ser gradualmente desfeita" #: f.edit.cc:8486 f.widgets.cc:515 msgid "Undo Edits" msgstr "Desfazer edições" #: f.edit.cc:8717 f.edit.cc:8765 msgid "Edit Plugins" msgstr "Editar Extensões" #: f.edit.cc:8718 msgid "Edit plugins menu" msgstr "Editar menu de plugins" #: f.edit.cc:8726 msgid "Run as Fotoxx edit function" msgstr "Executar como função de edição do Fotoxx" #: f.edit.cc:8767 msgid "menu name" msgstr "nome do menu" #: f.edit.cc:8770 msgid "command" msgstr "comando" #: f.edit.cc:8968 msgid "Plugin working ..." msgstr "Plugin funcionando ..." #: f.edit.cc:8977 msgid "plugin failed" msgstr "extensão falhou" #: f.edit.cc:9015 msgid "Raw Therapee not installed" msgstr "Raw Therapee não instalado" #: f.edit.cc:9034 msgid "RAW type not registered in User Preferences" msgstr "Tipo RAW não registrado nas Preferências de Usuário" #: f.edit.cc:9064 msgid "Raw Therapee produced no tif file" msgstr "Raw Therapee não produziu nenhum arquivo tif" #: f.effects.cc:83 msgid "Convert to Sketch" msgstr "Converter em Esboço" #: f.effects.cc:161 msgid "Clip Level" msgstr "Grampear Nível" #: f.effects.cc:165 msgid "Algorithm" msgstr "Algoritmo" #: f.effects.cc:170 msgid "Foreground" msgstr "Primeiro Plano" #: f.effects.cc:173 f.tools.cc:1680 msgid "Background" msgstr "Plano de Fundo" #: f.effects.cc:609 msgid "Line Threshold" msgstr "Limiar de Linha" #: f.effects.cc:610 msgid "Line Width" msgstr "Largura de Linha" #: f.effects.cc:611 f.enhance.cc:3929 msgid "Blur Radius" msgstr "Raio de Desfoque" #: f.effects.cc:612 msgid "Kuwahara Depth" msgstr "Profundidade Kuwahara" #: f.effects.cc:1057 f.widgets.cc:541 msgid "Line Drawing" msgstr "Desenho de linha" #: f.effects.cc:1070 f.effects.cc:2090 msgid "black/white" msgstr "preto/branco" #: f.effects.cc:1320 f.widgets.cc:542 msgid "Emboss" msgstr "Entalhe" #: f.effects.cc:1326 msgid "Depth" msgstr "Profundidade" #: f.effects.cc:1541 msgid "Simulate Tiles" msgstr "Simular Ladrilho" #: f.effects.cc:1545 msgid "tile size" msgstr "tamanho do ladrilho" #: f.effects.cc:1548 msgid "tile gap" msgstr "espaçamento dos ladrilhos" #: f.effects.cc:1551 msgid "3D depth" msgstr "profundidade 3D" #: f.effects.cc:1780 msgid "Dither Image" msgstr "Pontilhar Imagem" #: f.effects.cc:1783 msgid "Dither0" msgstr "Pontilhado0" #: f.effects.cc:1784 f.effects.cc:1848 msgid "Roy Lichtenstein effect" msgstr "Efeito Roy Lichtenstein" #: f.effects.cc:1787 f.effects.cc:2083 msgid "Dither1" msgstr "Pontilhado1" #: f.effects.cc:1788 msgid "pure RGB or black/white dots" msgstr "pontos RGB ou preto/branco puros" #: f.effects.cc:1791 f.effects.cc:2481 msgid "Dither2" msgstr "Pontilhado2" #: f.effects.cc:1792 msgid "RGB mix with given bit-depth" msgstr "mistura RGB com dada profundidade de bit" #: f.effects.cc:1795 f.effects.cc:2756 msgid "Dither3" msgstr "Pontilhado3" #: f.effects.cc:1796 msgid "custom palette colors" msgstr "paleta de cores customizadas" #: f.effects.cc:1852 msgid "dot size" msgstr "tamanho do ponto" #: f.effects.cc:2087 f.effects.cc:2485 f.effects.cc:2760 msgid "resolution" msgstr "resolução" #: f.effects.cc:2089 msgid "RGB color" msgstr "cor RGB" #: f.effects.cc:2091 msgid "random position" msgstr "posição aleatória" #: f.effects.cc:2488 f.effects.cc:3196 msgid "color depth" msgstr "resolução de cor" #: f.effects.cc:2491 f.effects.cc:2768 msgid "error compensation" msgstr "compensação de erro" #: f.effects.cc:2764 msgid "palette:" msgstr "paleta:" #: f.effects.cc:2818 msgid "palette file" msgstr "arquivo de paleta" #: f.effects.cc:3178 f.widgets.cc:545 msgid "Painting" msgstr "Pintura" #: f.effects.cc:3200 msgid "patch area goal" msgstr "remendar área objetivo" #: f.effects.cc:3204 msgid "req. color match" msgstr "correspondência de cores requeridas" #: f.effects.cc:3208 msgid "borders" msgstr "bordas" #: f.effects.cc:3799 msgid "Add Texture" msgstr "Adicionar Textura" #: f.effects.cc:4014 msgid "Background Pattern" msgstr "Padrão de Plano de Fundo" #: f.effects.cc:4018 msgid "Pattern File:" msgstr "Arquivo de Padrão:" #: f.effects.cc:4026 msgid "Geometry" msgstr "Geometria" #: f.effects.cc:4030 f.widgets.cc:547 msgid "Pattern" msgstr "Padrão" #: f.effects.cc:4038 msgid "Overlap" msgstr "Sobreposição" #: f.effects.cc:4046 msgid "Opacity" msgstr "Opacidade" #: f.effects.cc:4052 msgid "Grayscale" msgstr "Escala de Cinza" #: f.effects.cc:4478 msgid "Create Mosaic" msgstr "Criar Mosaico" #: f.effects.cc:4524 msgid "Tile" msgstr "Ladrilho" #: f.effects.cc:4532 f.widgets.cc:543 msgid "Tiles" msgstr "Ladrilhos" #: f.effects.cc:4538 msgid "Tile blending" msgstr "Mistura de Ladrilhos" #: f.effects.cc:4621 #, c-format msgid "exceeded max. tiles: %d" msgstr "excedeu máx. de ladrilhos: %d" #: f.effects.cc:4628 #, c-format msgid "only %d tile images found" msgstr "apenas %d imagens de ladrilho encontradas" #: f.effects.cc:5088 f.widgets.cc:549 msgid "Color Mode" msgstr "Modos de Cores" #: f.effects.cc:5091 msgid "reset" msgstr "redefinir" #: f.effects.cc:5092 msgid "black/white positive" msgstr "preto/branco positivo" #: f.effects.cc:5093 msgid "black/white negative" msgstr "preto/branco negativo" #: f.effects.cc:5094 msgid "color negative" msgstr "cor negativa" #: f.effects.cc:5095 msgid "RGB -> GBR" msgstr "RGB -> GBR" #: f.effects.cc:5096 msgid "RGB -> BRG" msgstr "RGB -> BRG" #: f.effects.cc:5097 msgid "sepia" msgstr "sepia" #: f.effects.cc:5348 msgid "Set color depth to 1-16 bits" msgstr "Configurar resolução de cor para 1-16 bits" #: f.effects.cc:5377 msgid "Set Color Depth" msgstr "Configurar resolução de cor" #: f.effects.cc:5547 f.widgets.cc:551 msgid "Shift Colors" msgstr "Deslocar Cores" #: f.effects.cc:5824 f.widgets.cc:552 msgid "Alien Colors" msgstr "Cores Alien" #: f.effects.cc:5827 msgid "blocksize" msgstr "tamanho do bloco" #: f.effects.cc:5830 f.warp.cc:3096 msgid "amplitude" msgstr "amplitude" #: f.effects.cc:6089 msgid "" "Draw a line across the image in \n" "direction of brightness change." msgstr "" "Desenhe uma linha na imagem em \n" "direção à mudança de brilho." #: f.effects.cc:6132 msgid "Brightness Ramp" msgstr "Rampa de Brilho" #: f.effects.cc:6605 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.effects.cc:6640 msgid "Paint Transparency" msgstr "Aplicar Transparência" #: f.effects.cc:6648 msgid "paintbrush radius" msgstr "raio do pincel" #: f.effects.cc:6649 msgid "strength center" msgstr "fortalecer centro" #: f.effects.cc:6650 msgid "strength edge" msgstr "fortalecer borda" #: f.effects.cc:6655 msgid "gradual paint" msgstr "pintura gradual" #: f.effects.cc:6874 msgid "Mirror Image" msgstr "Espelhar Imagem" #: f.effects.cc:6877 f.warp.cc:3098 msgid "horizontal" msgstr "horizontal" #: f.effects.cc:6878 f.warp.cc:3102 msgid "vertical" msgstr "vertical" #: f.effects.cc:7051 f.widgets.cc:556 msgid "Custom Kernel" msgstr "Customizar Núcleo" #: f.effects.cc:7055 msgid "Kernel size" msgstr "Tamanho de Núcleo" #: f.effects.cc:7072 msgid "multiply" msgstr "multiplicação" #: f.effects.cc:7075 msgid "add" msgstr "adicionar" #: f.effects.cc:7079 msgid "Data file" msgstr "Arquivo de Dados" #: f.effects.cc:7099 fotoxx.cc:3922 msgid "Load settings from file" msgstr "Carregar configurações de um arquivo" #: f.enhance.cc:505 msgid "Adjust Brightness Distribution" msgstr "Ajustar Distribuição de Brilho" #: f.enhance.cc:544 msgid "Low Cutoff" msgstr "Corte Baixo" #: f.enhance.cc:545 msgid "High Cutoff" msgstr "Corte Elevado" #: f.enhance.cc:546 msgid "Low Flatten" msgstr "Achatamento Baixo" #: f.enhance.cc:547 msgid "Mid Flatten" msgstr "Achatamento Médio" #: f.enhance.cc:548 msgid "High Flatten" msgstr "Achatamento Elevado" #: f.enhance.cc:549 msgid "Low Stretch" msgstr "Estiramento Baixo" #: f.enhance.cc:550 msgid "Mid Stretch" msgstr "Estiramento Médio" #: f.enhance.cc:551 msgid "High Stretch" msgstr "Estiramento Elevado" #: f.enhance.cc:886 msgid "Magnify Gradients" msgstr "Ampliar Gradientes" #: f.enhance.cc:923 msgid "low" msgstr "baixo" #: f.enhance.cc:925 msgid "high" msgstr "alto" #: f.enhance.cc:928 msgid "Amplify" msgstr "Ampliar" #: f.enhance.cc:1312 msgid "Flatten Brightness" msgstr "Nivelamento de Brilho" #: f.enhance.cc:1363 msgid "Zones" msgstr "Zonas" #: f.enhance.cc:1370 msgid "Deband Dark" msgstr "Debandar Escuridão" #: f.enhance.cc:1373 msgid "Deband Bright" msgstr "Debandar Claridade" #: f.enhance.cc:1851 msgid "Global Retinex" msgstr "Retinex Global" #: f.enhance.cc:1867 msgid "Dark Point" msgstr "Ponto Escuro" #: f.enhance.cc:1868 msgid "Bright Point" msgstr "Ponto Claro" #: f.enhance.cc:1869 msgid "Multiplyer" msgstr "Multiplicador" #: f.enhance.cc:1893 msgid "brightness rescale" msgstr "reescalar brilho" #: f.enhance.cc:1896 msgid "click bright point" msgstr "clique no ponto claro" #: f.enhance.cc:1897 msgid "click dark point" msgstr "clique no ponto escuro" #: f.enhance.cc:1900 f.enhance.cc:2531 msgid "blend" msgstr "mistura" #: f.enhance.cc:1903 f.enhance.cc:2537 msgid "reduce bright" msgstr "reduzir claridade" #: f.enhance.cc:2523 msgid "Zonal Retinex" msgstr "Retinex Zonal" #: f.enhance.cc:2527 msgid "zone count:" msgstr "contagem de zonas:" #: f.enhance.cc:2534 msgid "reduce dark" msgstr "reduzir escuridão" #: f.enhance.cc:3058 f.process.cc:189 f.process.cc:823 f.process.cc:1404 #: f.widgets.cc:527 msgid "Sharpen" msgstr "Aguçar" #: f.enhance.cc:3066 msgid "unsharp mask" msgstr "máscara desfocada" #: f.enhance.cc:3099 msgid "median diff" msgstr "diferença mediana" #: f.enhance.cc:3101 msgid "dark" msgstr "escuro" #: f.enhance.cc:3102 msgid "light" msgstr "claro" #: f.enhance.cc:3888 msgid "Click to set center" msgstr "Clique para definir centro" #: f.enhance.cc:3889 msgid "Pull image using the mouse" msgstr "Puxe a imagem utilizando o cursor" #: f.enhance.cc:3890 msgid "" "left drag: blend image \n" "right drag: restore image" msgstr "" "arrasto com botão esquerdo: misturar image \n" "arrasto com botão direito: restaurar imagem" #: f.enhance.cc:3933 msgid "Normal Blur" msgstr "Desfoque Normal" #: f.enhance.cc:3940 msgid "Radial Blur" msgstr "Desfoque Radial" #: f.enhance.cc:3954 msgid "Directed Blur" msgstr "Desfoque Direcionado" #: f.enhance.cc:3956 msgid "Blur Span" msgstr "extensão de desfoque" #: f.enhance.cc:3959 msgid "Intensity" msgstr "Intensidade" #: f.enhance.cc:3964 msgid "Graduated Blur" msgstr "Desfoque Graduado" #: f.enhance.cc:3969 msgid "Contrast Limit" msgstr "Limite de Contraste" #: f.enhance.cc:3974 msgid "Paint Blur" msgstr "Borrão de Tinta" #: f.enhance.cc:3979 f.mashup.cc:1577 fotoxx.h:1404 msgid "Power" msgstr "Força" #: f.enhance.cc:3987 f.enhance.cc:4914 msgid "Blur Background" msgstr "Desfocar Plano de Fundo" #: f.enhance.cc:4918 msgid "constant blur" msgstr "desfoque constante" #: f.enhance.cc:4921 msgid "increase blur with distance" msgstr "intensificar desfoque com a distância" #: f.enhance.cc:4923 msgid "min. blur radius" msgstr "radio mínimo de desfoque" #: f.enhance.cc:4926 msgid "max. blur radius" msgstr "raio máximo de desfoque" #: f.enhance.cc:4957 f.warp.cc:837 f.warp.cc:2277 msgid "no active Select Area" msgstr "nenhuma Área Selecionada ativa" #: f.enhance.cc:5139 msgid "Apply repeatedly while watching the image." msgstr "Aplicar repetidamente enquanto visualiza a imagem." #: f.enhance.cc:5175 msgid "Noise Reduction" msgstr "Redução de ruído" #: f.enhance.cc:5215 msgid "dark areas" msgstr "áreas escuras" #: f.enhance.cc:5217 msgid "all areas" msgstr "todas as áreas" #: f.enhance.cc:6195 msgid "Measure Noise" msgstr "Mensurar Ruído" #: f.enhance.cc:6196 msgid "Move mouse in a monotone image area." msgstr "Mova o cursor em uma área monotom na imagem ." #: f.enhance.cc:6511 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.enhance.cc:6530 msgid "Red Eye Reduction" msgstr "Redução de olhos vermelhos" #: f.enhance.cc:6984 msgid "Color Match Images" msgstr "Imagens para mesclagem de cor" #: f.enhance.cc:7015 msgid "mouse radius for color sample" msgstr "raio do cursor para a amostra de cor" #: f.enhance.cc:7017 f.enhance.cc:7022 fotoxx.h:1399 zfuncs.cc:12695 msgid "Open" msgstr "Abrir" #: f.enhance.cc:7018 msgid "image for source color" msgstr "imagem para fonte de cor" #: f.enhance.cc:7020 msgid "click on image to get source color" msgstr "clique na imagem para obter a cor fonte" #: f.enhance.cc:7023 msgid "image to set matching color" msgstr "imagem para mesclagem de cor" #: f.enhance.cc:7025 msgid "click on image to set matching color" msgstr "clique na imagem para aplicar mesclagem de cor" #: f.enhance.cc:7092 msgid "select source image color first" msgstr "selecione imagem para fonte de cor primeiro" #: f.enhance.cc:7287 msgid "" "Drag mouse to select. Erase. Repeat. \n" "Click: extend selection to mouse." msgstr "" "Arraste com o mouse para selecionar. Apague. Repita. \n" "Clique: extende seleção ao cursor." #: f.enhance.cc:7314 f.widgets.cc:532 msgid "Smart Erase" msgstr "Apagador inteligente" #: f.enhance.cc:7319 fotoxx.h:1410 msgid "Radius" msgstr "Raio" #: f.enhance.cc:7321 f.widgets.cc:528 msgid "Blur" msgstr "Embaçar" #: f.enhance.cc:7324 msgid "New Area" msgstr "Nova Área" #: f.enhance.cc:7672 f.enhance.cc:8054 msgid "Chromatic Aberration" msgstr "Aberração cromática" #: f.enhance.cc:7711 msgid "Red Factors" msgstr "Fatores do Vermelho" #: f.enhance.cc:7715 msgid "Blue Factors" msgstr "Fatores do Azul" #: f.enhance.cc:7720 msgid "Find optimum factors:" msgstr "Calcular fator ótimos:" #: f.enhance.cc:8095 msgid "Chromatic Color" msgstr "Cor Desviada" #: f.enhance.cc:8098 msgid "Replacement Color" msgstr "Cor de Substituição" #: f.enhance.cc:8101 msgid "Background Color" msgstr "Cor do plano de fundo" #: f.enhance.cc:8105 msgid "Color match level" msgstr "Nível de correspondência de cores" #: f.enhance.cc:8109 msgid "Background Proximity" msgstr "Proximidade do plano de fundo" #: f.enhance.cc:8151 msgid "255 iterations, cannot continue" msgstr "255 iterações, não e possível continuar" #: f.enhance.cc:8413 f.widgets.cc:535 msgid "Vignette" msgstr "Vinheta" #: f.enhance.cc:8802 f.widgets.cc:536 msgid "Remove Dust" msgstr "Remover Sujeira" #: f.enhance.cc:8806 msgid "spot size limit" msgstr "limite de tamanho de mancha" #: f.enhance.cc:8809 msgid "max. brightness" msgstr "brilho máximo" #: f.enhance.cc:8812 msgid "min. contrast" msgstr "contraste mínimo" #: f.file.cc:601 msgid "Rename Image File" msgstr "Renomear arquivo" #: f.file.cc:608 msgid "Old Name" msgstr "nome antigo" #: f.file.cc:609 f.process.cc:141 msgid "New Name" msgstr "novo nome" #: f.file.cc:618 msgid "previous name" msgstr "nome anterior" #: f.file.cc:619 msgid "Add 1" msgstr "Adicionar 1" #: f.file.cc:622 f.file.cc:882 f.file.cc:1629 f.file.cc:1998 msgid "keep this dialog open" msgstr "Mantenha esta caixa de diálogo aberta" #: f.file.cc:850 msgid "File Permissions" msgstr "Permissões de Arquivos" #: f.file.cc:854 f.meta.cc:855 f.meta.cc:1637 f.meta.cc:1840 msgid "File:" msgstr "Arquivo:" #: f.file.cc:1000 msgid "Open Image File" msgstr "Abrir arquivo" #: f.file.cc:1019 f.process.cc:654 msgid "unknown file type" msgstr "tipo de arquivo desconhecido" #: f.file.cc:1207 #, c-format msgid "Start of gallery, preceding gallery: %s" msgstr "Início de Galeria, precedente: %s" #: f.file.cc:1208 #, c-format msgid "End of gallery, following gallery: %s" msgstr "Fim de Galeria, adiante: %s" #: f.file.cc:1355 msgid "Create Blank Image" msgstr "Criar imagem em branco" #: f.file.cc:1357 f.meta.cc:1931 msgid "file name" msgstr "nome do arquvo" #: f.file.cc:1391 msgid "supply a file name" msgstr "forneça um nome de arquivo" #: f.file.cc:1570 msgid "Copy or Move Image File" msgstr "Copiar ou Mover arquivo de imagem" #: f.file.cc:1615 msgid "New Location:" msgstr "Nova Localidade:" #: f.file.cc:1620 msgid "New Name:" msgstr "Novo Nome:" #: f.file.cc:1625 msgid "copy (duplicate file)" msgstr "copiar (duplicar arquivo)" #: f.file.cc:1626 msgid "move (remove original)" msgstr "mover (remove arquivo original)" #: f.file.cc:1676 f.process.cc:608 f.process.cc:1574 f.process.cc:2155 msgid "Select folder" msgstr "Selecionar pasta" #: f.file.cc:1718 f.tools.cc:1488 msgid "new location is not a folder" msgstr "nova localidade não é um diretório" #: f.file.cc:1737 msgid "new file extension missing or changed" msgstr "nova extensão de arquivo ausente ou alterada" #: f.file.cc:1771 f.file.cc:2075 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "exclusão falhou: \n" " %s" #: f.file.cc:1845 f.file.cc:3226 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Sobrescrever arquivo? \n" " %s" #: f.file.cc:1957 msgid "Delete/Trash Image File" msgstr "Excluir/Descartar Arquivo de Imagem" #: f.file.cc:2029 msgid "GTK g_file_trash() function failed" msgstr "Função GTK g_file_trash() falhou" #: f.file.cc:2048 msgid "not a known image file" msgstr "arquivo de imagem desconhecido" #: f.file.cc:2054 msgid "Delete read-only file?" msgstr "Excluir arquivo somente leitura?" #: f.file.cc:2056 msgid "Trash read-only file?" msgstr "Descartar arquivos somente leitura?" #: f.file.cc:2272 #, c-format msgid "Kill active dialog? %s" msgstr "Encerrar a caixa de diálogo ativa? %s" #: f.file.cc:2322 f.widgets.cc:636 msgid "User Guide" msgstr "Guia de usuário" #: f.file.cc:2325 f.widgets.cc:637 msgid "Recent Changes" msgstr "Mudanças Recentes" #: f.file.cc:2328 f.widgets.cc:638 msgid "Edit Functions Overview" msgstr "Visão geral das funções de edição" #: f.file.cc:2334 f.widgets.cc:639 msgid "Change Log" msgstr "Relatório de mudanças" #: f.file.cc:2337 f.widgets.cc:640 msgid "Log File" msgstr "Arquivo de Log" #: f.file.cc:2343 f.widgets.cc:642 msgid "Command Params" msgstr "Parâmetros de Comando" #: f.file.cc:2346 f.widgets.cc:643 msgid "Translations" msgstr "Traduções" #: f.file.cc:2349 f.widgets.cc:644 msgid "Home Page" msgstr "Página na web" #: f.file.cc:2352 f.widgets.cc:645 msgid "License" msgstr "Licença" #: f.file.cc:2355 f.widgets.cc:646 msgid "Privacy" msgstr "Privacidade" #: f.file.cc:2358 f.widgets.cc:647 msgid "About" msgstr "Sobre" #: f.file.cc:2366 f.widgets.cc:668 fotoxx.h:1368 msgid "Help" msgstr "Ajuda" #: f.file.cc:2414 msgid "Save Image File" msgstr "Salvar Arquivo-imagem" #: f.file.cc:2424 msgid "new version" msgstr "nova versão" #: f.file.cc:2425 msgid "new file ..." msgstr "novo arquivo ..." #: f.file.cc:2426 msgid "replace file" msgstr "substituir arquivo" #: f.file.cc:2433 f.file.cc:3083 msgid "save as new file name or type" msgstr "salvar com novo nome ou tipo de arquivo" #: f.file.cc:2435 msgid "replace old file (OVERWRITE)" msgstr "substituir arquivo antigo (SOBRESCREVER)" #: f.file.cc:2486 msgid "cannot replace RAW file" msgstr "não foi possível substituir o arquivo RAW" #: f.file.cc:2491 msgid "cannot replace HEIC file" msgstr "não foi possível substituir o arquivo HEIC" #: f.file.cc:2496 msgid "cannot replace JP2 file" msgstr "" #: f.file.cc:2668 f.file.cc:2727 #, c-format msgid "" "file: %s \n" " exceed 99 versions" msgstr "" "arquivo: %s \n" " ultrapassa 99 versões" #: f.file.cc:2821 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:2831 msgid "cannot save as RAW type" msgstr "não foi possível salvar como RAW" #: f.file.cc:2912 msgid "save anyway" msgstr "salvar assim mesmo" #: f.file.cc:2980 f.file.cc:2982 msgid "Unable to copy EXIF/IPTC data" msgstr "Incapaz de copiar dados EXIF/IPTC" #: f.file.cc:3096 f.process.cc:1387 msgid "jpg quality" msgstr "qualidade jpg" #: f.file.cc:3100 msgid "color depth:" msgstr "resolução de cor:" #: f.file.cc:3106 f.file.cc:3114 msgid "make current" msgstr "tornar atual" #: f.file.cc:3107 msgid "(new file becomes current file)" msgstr "(novo arquivo se torna arquivo atual)" #: f.file.cc:3110 msgid "permissions:" msgstr "permissões:" #: f.file.cc:3641 f.widgets.cc:434 f.widgets.cc:824 msgid "Permissions" msgstr "Permissões:" #: f.file.cc:3646 fotoxx.cc:256 msgid "owner" msgstr "proprietário" #: f.file.cc:3647 fotoxx.cc:257 msgid "group" msgstr "grupo" #: f.file.cc:3648 fotoxx.cc:258 msgid "other" msgstr "outro" #: f.gallery.cc:1705 f.gallery.cc:1725 msgid "recent images" msgstr "imagens recentes" #: f.gallery.cc:1706 f.gallery.cc:1730 msgid "newest images" msgstr "imagens mais novas" #: f.gallery.cc:1785 msgid "no albums found" msgstr "nenhum álbum encontrado" #: f.gallery.cc:1795 f.gallery.cc:1809 msgid "Current Album" msgstr "Álbum Atual" #: f.gallery.cc:1812 msgid "no current album" msgstr "nenhum album em uso" #: f.gallery.cc:1834 msgid "" " Reset all galleries\n" " to file name ascending" msgstr "" " Redefinir todas as galerias\n" " colocando nomes em ordem ascendente" #: f.gallery.cc:1855 msgid "Gallery Sort" msgstr "Ordenar Galeria" #: f.gallery.cc:1859 msgid "File Name" msgstr "Nome de Arquivo" #: f.gallery.cc:1860 msgid "File Mod Date/Time" msgstr "Data/Hora da Modificação do Arquivo" #: f.gallery.cc:1861 msgid "Photo Date/Time (EXIF)" msgstr "Foto Data/Hora (EXIF)" #: f.gallery.cc:1862 msgid "File Size (bytes)" msgstr "Tamanho de Arquivo (bytes)" #: f.gallery.cc:1863 msgid "Image Size (pixels)" msgstr "Tamanho de Imagem (pixels)" #: f.gallery.cc:1865 msgid "ascending" msgstr "ascendente" #: f.gallery.cc:1866 msgid "descending" msgstr "descendente" #: f.gallery.cc:2778 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.gallery.cc:2802 f.widgets.cc:651 msgid "File" msgstr "Arquivo" #: f.gallery.cc:3608 f.gallery.cc:3612 msgid "Image File" msgstr "Arquivo de Imagem" #: f.gallery.cc:3721 msgid "Select Image Files" msgstr "Selecionar Arquivos de Imagem" #: f.gallery.cc:3787 #, c-format msgid "exceed %d selected files" msgstr "excedido %d arquivos selecionados" #: f.gallery.cc:3799 #, c-format msgid "remove %d duplicates?" msgstr "remover %d cópias?" #: f.gallery.cc:4248 f.widgets.cc:460 msgid "Bookmarks" msgstr "Favoritos" #: f.gallery.cc:4248 f.gallery.cc:4356 msgid "Edit Bookmarks" msgstr "Editar Favoritos" #: f.gallery.cc:4330 msgid "" "Click a list position. Click a gallery thumbnail for the new bookmark.\n" "Bookmark for thumbnail will be added. Change the name and press [Rename]." msgstr "" "Clique numa posição da lista. Clique em uma miniatura da galeria para o novo " "favorito. \n" "O favorito da miniatura será adicionado. Mude o nome e pressione [Renomear]." #: f.gallery.cc:4533 msgid "unable to save bookmarks file" msgstr "incapaz de salvar arquivo de favoritos" #: f.mashup.cc:212 msgid "Project name" msgstr "Nome do projeto" #: f.mashup.cc:216 msgid "Layout and background image" msgstr "Leiaute e imagem de plano de fundo" #: f.mashup.cc:220 msgid "choose an image file" msgstr "escolher um arquivo de imagem" #: f.mashup.cc:221 msgid "use current image file" msgstr "usar arquivo de imagem atual" #: f.mashup.cc:222 msgid "specify layout size and color" msgstr "especificar tamanho de leiaute e cor" #: f.mashup.cc:223 msgid "open a Mashup project file" msgstr "abrir um arquivo de projeto de Mistura" #: f.mashup.cc:238 msgid "enter a project name" msgstr "informe o nome do projeto" #: f.mashup.cc:269 msgid "no current file" msgstr "nenhum arquivo sendo usado" #: f.mashup.cc:296 msgid "Make Layout Image" msgstr "Criar Imagem Leiaute" #: f.mashup.cc:403 msgid "Edit Images" msgstr "Editar imagens" #: f.mashup.cc:404 f.mashup.cc:2433 msgid "Edit Text" msgstr "Editar texto" #: f.mashup.cc:405 msgid "Edit Line" msgstr "Editar Linha" #: f.mashup.cc:406 msgid "Rescale" msgstr "Reescalar" #: f.mashup.cc:411 msgid "add or edit images" msgstr "adicionar ou editar imagens" #: f.mashup.cc:413 msgid "add or edit text" msgstr "adicionar ou editar texto" #: f.mashup.cc:415 msgid "add or edit lines/arrows" msgstr "adicionar ou editar linhas/setas" #: f.mashup.cc:417 msgid "change project scale" msgstr "mudar escala de projeto" #: f.mashup.cc:419 msgid "project complete" msgstr "projeto finalizado" #: f.mashup.cc:421 msgid "cancel project" msgstr "cancelar projeto" #: f.mashup.cc:439 msgid "rescale project" msgstr "reescalar projeto" #: f.mashup.cc:493 msgid "save Mashup output file" msgstr "salvar arquivo de saída de Mistura" #: f.mashup.cc:513 msgid "save Mashup project file?" msgstr "salvar arquivo de projeto da Mistura?" #: f.mashup.cc:528 msgid "delete Mashup project file?" msgstr "excluir arquivo de projeto da Mistura?" #: f.mashup.cc:588 msgid "Open Project" msgstr "Abrir projeto" #: f.mashup.cc:617 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "arquivo-imagem de leiaute faltando: \n" " %s" #: f.mashup.cc:674 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "arquivo-imagem sobreposta faltando: \n" " %s" #: f.mashup.cc:980 msgid "project file is defective" msgstr "arquivo de projeto está defeituoso" #: f.mashup.cc:1010 msgid "Save Project" msgstr "Salvar projeto" #: f.mashup.cc:1151 msgid "layout exceeds 2 gigabytes" msgstr "layout excede 2 gigabytes" #: f.mashup.cc:1221 msgid "Click image to select, drag image to move." msgstr "Clique na imagem para selecionar, arrastar imagem para mover." #: f.mashup.cc:1222 msgid "Make black margins transparent" msgstr "Tornar margens pretas em transparentes" #: f.mashup.cc:1254 f.mashup.cc:1320 msgid "Add Image" msgstr "Adicionar imagem" #: f.mashup.cc:1261 msgid "Current image:" msgstr "Imagem sendo usada:" #: f.mashup.cc:1265 msgid "Cycle through images:" msgstr "Cambiar entre imagens:" #: f.mashup.cc:1272 msgid "Scale" msgstr "Escala" #: f.mashup.cc:1281 msgid "Stacking Order" msgstr "Ordem de Empilhamento" #: f.mashup.cc:1282 msgid "Raise" msgstr "Aumentar" #: f.mashup.cc:1283 msgid "Lower" msgstr "Diminuir" #: f.mashup.cc:1286 msgid "Base Transparency" msgstr "Transparência Base" #: f.mashup.cc:1290 msgid "Var. Transparency" msgstr "Transparência Variável" #: f.mashup.cc:1291 msgid "Paint" msgstr "Pintura" #: f.mashup.cc:1294 msgid "Bend and fine-align" msgstr "Curvatura e ajuste fino" #: f.mashup.cc:1295 f.widgets.cc:664 msgid "Warp" msgstr "Distorção" #: f.mashup.cc:1304 zfuncs.cc:12910 zfuncs.cc:12919 msgid "Margins" msgstr "Margens" #: f.mashup.cc:1305 msgid "Hard" msgstr "Rígido" #: f.mashup.cc:1306 msgid "Blend" msgstr "Mistura" #: f.mashup.cc:1320 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:1810 msgid "Pull on the image with the mouse." msgstr "Puxe a imagem com o cursor." #: f.mashup.cc:1826 msgid "Warp Image" msgstr "Distorcer Imagem" #: f.mashup.cc:1832 f.warp.cc:1209 msgid "warp span" msgstr "distorção em alcance" #: f.mashup.cc:2264 #, c-format msgid "exceeded %d images" msgstr "excederam%d imagens" #: f.mashup.cc:2406 msgid "Enter text, [Add] to layout, edit properties." msgstr "Digite o texto, [Adicionar] para o leiaute, editar propriedades." #: f.mashup.cc:2486 msgid "Text File:" msgstr "Arquivo Texto:" #: f.mashup.cc:2490 msgid "add entered text to layout" msgstr "adicionar texto inserido ao leiaute" #: f.mashup.cc:2624 msgid "click position to add text" msgstr "clique na posição para adicionar texto" #: f.mashup.cc:2630 #, c-format msgid "exceeded %d text entries" msgstr "excederam %d entradas de texto" #: f.mashup.cc:2635 msgid "Add Text" msgstr "Escrever Texto" #: f.mashup.cc:2744 msgid "Set line properties, [Add] to layout, edit." msgstr "Definir propriedades de linha, [Adicionar] ao leiaute, editar." #: f.mashup.cc:2768 msgid "Edit Line/Arrow" msgstr "Editar Linha/Seta" #: f.mashup.cc:2823 msgid "add line/arrow to layout" msgstr "adicionar linha/seta ao leiaute" #: f.mashup.cc:2915 msgid "click position to add line" msgstr "clique na posição para adicionar linha" #: f.mashup.cc:2921 #, c-format msgid "exceeded %d line entries" msgstr "excederam%d linhas de entrada" #: f.mashup.cc:2926 msgid "Add Line" msgstr "Adicionar Linha" #: f.mashup.cc:4171 msgid "Image Montage" msgstr "Colagem de Imagens" #: f.mashup.cc:4180 msgid "Frame Width" msgstr "Largura do Quadro" #: f.mashup.cc:4183 f.mashup.cc:4191 msgid "Margin" msgstr "Margem" #: f.mashup.cc:4188 msgid "Image Columns" msgstr "Colunas de Imagens" #: f.mashup.cc:4205 #, c-format msgid "exceed %d rows" msgstr "excedido %d linhas" #: f.mashup.cc:4301 msgid "Optimize" msgstr "Otimizar" #: f.mashup.cc:4309 #, c-format msgid "column difference: %d pixels" msgstr "colunas diferem: %d pixels" #: f.mashup.cc:4367 #, c-format msgid "" "column difference: %d pixels \n" "Make columns even?" msgstr "" "colunas diferem: %d pixels \n" "Alinhar colunas?" #: f.mashup.cc:4389 msgid "Save with unique montage name" msgstr "Salvar colagem com um nome único" #: f.mashup.cc:4391 msgid "unique name:" msgstr "nome único:" #: f.mashup.cc:4393 msgid "create image map" msgstr "criar mapa de imagem" #: f.mashup.cc:4410 f.meta.cc:9173 msgid "supply a reasonable name" msgstr "fornecer um nome razoável" #: f.mashup.cc:4421 msgid "save montage" msgstr "salvar colagem" #: f.mashup.cc:4441 #, c-format msgid "map file saved: %s" msgstr "arquivo de mapa salvo como: %s" #: f.mashup.cc:4486 #, c-format msgid "%d max images exceeded" msgstr "máximo de %d imagens excedido" #: f.mashup.cc:4560 f.mashup.cc:4566 #, c-format msgid "image frame is too large: %d x %d" msgstr "quadro de imagem muito grande: %d x %d" #: f.mashup.cc:4871 #, c-format msgid "montage map file invalid: %s" msgstr "arquivo mapa de colagem inválido: %s" #: f.meta.cc:248 msgid "Add Metadata Items" msgstr "Adicionar Itens de Metadados" #: f.meta.cc:252 f.meta.cc:1631 f.meta.cc:3212 msgid "click to select" msgstr "clique para selecionar" #: f.meta.cc:257 msgid "click to unselect" msgstr "clique para deselecionar" #: f.meta.cc:498 msgid "Extras" msgstr "Extras" #: f.meta.cc:498 msgid "View Metadata" msgstr "Ver metadados" #: f.meta.cc:683 msgid "View All Metadata" msgstr "Ver todos Metadados" #: f.meta.cc:848 msgid "Edit Metadata" msgstr "Editar Metadados" #: f.meta.cc:851 msgid "save metadata to file" msgstr "salvar metadados em arquivo" #: f.meta.cc:860 msgid "Image Date" msgstr "Data da Imagem" #: f.meta.cc:863 msgid "Time" msgstr "Hora" #: f.meta.cc:871 msgid "Rating (stars):" msgstr "Avaliação (estrelas):" #: f.meta.cc:883 msgid "Caption" msgstr "Legenda" #: f.meta.cc:888 msgid "Comments" msgstr "Comentários" #: f.meta.cc:895 msgid "Location" msgstr "Localização" #: f.meta.cc:898 msgid "Country" msgstr "País" #: f.meta.cc:921 msgid "Image Tags" msgstr "Etiquetas da imagem" #: f.meta.cc:927 msgid "Recent Tags" msgstr "etiquetas recentes" #: f.meta.cc:933 f.meta.cc:2103 msgid "Enter New Tag" msgstr "Entre com Nova Etiqueta" #: f.meta.cc:939 f.meta.cc:2109 f.meta.cc:4772 msgid "Matching Tags" msgstr "Etiquetas Correspondentes" #: f.meta.cc:947 f.meta.cc:2118 f.meta.cc:2578 f.meta.cc:4779 msgid "Defined Tags Category" msgstr "Categorias de Etiquetas Definidas" #: f.meta.cc:955 msgid "search known locations" msgstr "procurar localidades conhecidas" #: f.meta.cc:956 msgid "search using web service" msgstr "procurar utilizando web service" #: f.meta.cc:1406 f.meta.cc:3822 f.meta.cc:7731 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "latitude/longitude inválido: %s %s" #: f.meta.cc:1463 f.widgets.cc:480 msgid "Manage Tags" msgstr "Gerenciar Etiquetas" #: f.meta.cc:1463 msgid "orphan tags" msgstr "etiquetas órfãs" #: f.meta.cc:1467 msgid "category" msgstr "categoria" #: f.meta.cc:1470 msgid "tag" msgstr "etiqueta" #: f.meta.cc:1477 msgid "Defined Tags:" msgstr "etiquetas definidas:" #: f.meta.cc:1627 msgid "Edit Any Metadata" msgstr "Editar qualquer Metadado" #: f.meta.cc:1627 f.meta.cc:3207 msgid "Full List" msgstr "Lista Completa" #: f.meta.cc:1641 f.meta.cc:3224 msgid "key name" msgstr "nome chave" #: f.meta.cc:1644 f.meta.cc:3225 msgid "key value" msgstr "valor chave" #: f.meta.cc:1837 msgid "Delete Metadata" msgstr "Excluir Metadados" #: f.meta.cc:1843 fotoxx.h:1319 msgid "All" msgstr "Todos" #: f.meta.cc:1844 msgid "One Key:" msgstr "Uma chave:" #: f.meta.cc:1930 msgid "choose options" msgstr "opções de escolha" #: f.meta.cc:1932 msgid "caption" msgstr "legenda" #: f.meta.cc:1933 msgid "comment" msgstr "comentário" #: f.meta.cc:2079 msgid "Batch Add/Remove Tags" msgstr "Adicionar/Remover Etiqueras em Lote" #: f.meta.cc:2092 msgid "tags to add" msgstr "etiquetas a adicionar" #: f.meta.cc:2093 msgid "tags to remove" msgstr "etiquetas a remover" #: f.meta.cc:2198 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " etiquetas demasiadas" #: f.meta.cc:2213 msgid "repeat with same files?" msgstr "repetir com os mesmos arquivos?" #: f.meta.cc:2353 msgid "specify files and tags" msgstr "especificar arquivos e etiquetas" #: f.meta.cc:2557 f.widgets.cc:599 msgid "Batch Rename Tags" msgstr "Renomear Etiquetas em Lote" #: f.meta.cc:2567 msgid "(click defined tag)" msgstr "(clique na etiqueta definida)" #: f.meta.cc:2569 f.process.cc:815 msgid "Rename to" msgstr "Renomear para" #: f.meta.cc:2586 msgid "old tag name >> new tag name" msgstr "nome de etiqueta antigo >> nome de etiqueta novo" #: f.meta.cc:2642 #, 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:2796 msgid "max tags exceeded" msgstr "número máximo de etiquetas excedido" #: f.meta.cc:2865 f.meta.cc:3000 msgid "Batch Photo Date/Time" msgstr "Fotos em Lote Data/Hora" #: f.meta.cc:2873 msgid "set a new date/time:" msgstr "definir uma nova data/hora:" #: f.meta.cc:2881 msgid "shift existing date/time:" msgstr "deslocar data/hora existente:" #: f.meta.cc:2907 msgid "test: show changes, do not update files" msgstr "testar: mostrar alterações, sem atualizar arquivos" #: f.meta.cc:2932 msgid "please make a choice" msgstr "favor fazer uma escolha" #: f.meta.cc:2937 f.meta.cc:3298 f.meta.cc:3489 msgid "no files selected" msgstr "nenhum arquivo selecionado" #: f.meta.cc:2967 f.meta.cc:2977 f.meta.cc:2983 msgid "invalid date/time format" msgstr "formato data/hora inválido" #: f.meta.cc:3207 msgid "Batch Add/Change Metadata" msgstr "Adicionar/Modificar Metadados em Lote" #: f.meta.cc:3292 msgid "enter key names" msgstr "inserir nome de chave" #: f.meta.cc:3375 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:3472 msgid "Batch Report Metadata" msgstr "Relatório de Metadados em Lote" #: f.meta.cc:3478 msgid "list of reported metadata items" msgstr "lista de itens de metadados reportados" #: f.meta.cc:3621 f.widgets.cc:603 msgid "Batch Geotags" msgstr "Geotags em Lote" #: f.meta.cc:3662 msgid "location" msgstr "localidade" #: f.meta.cc:3665 msgid "country" msgstr "país" #: f.meta.cc:3698 msgid "Adding Geotags" msgstr "Adicionando Georeferências" #: f.meta.cc:3807 msgid "" "data is incomplete \n" " proceed?" msgstr "" "Dados incompletos \n" " continuar?" #: f.meta.cc:3895 msgid "Report Image Locations" msgstr "Relatar Localização de Imagens" #: f.meta.cc:3896 msgid "Group by country" msgstr "Agrupar por país" #: f.meta.cc:3897 msgid "Group by country/location" msgstr "Agrupar por país/localização" #: f.meta.cc:3898 msgid "Group by country/location/date" msgstr "Agrupar por país/localização/data" #: f.meta.cc:3899 msgid "Group by date/country/location" msgstr "Agrupar por data/país/localização" #: f.meta.cc:3902 msgid "Combine within" msgstr "Combinar interiormente" #: f.meta.cc:3904 msgid "days" msgstr "dias" #: f.meta.cc:4016 msgid "Image Locations" msgstr "Localidade de Imagem" #: f.meta.cc:4285 msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" msgstr "Jan Fev Mar Abr Mai Jun Jul Ago Set Out Nov Dez" #: f.meta.cc:4677 msgid "Search Images" msgstr "Procurar Imagens" #: f.meta.cc:4681 msgid "images to search:" msgstr "imagens para busca:" #: f.meta.cc:4682 msgid "all" msgstr "todos" #: f.meta.cc:4683 msgid "current set only" msgstr "configuração atual apenas" #: f.meta.cc:4686 msgid "matching images:" msgstr "imagens correspondentes:" #: f.meta.cc:4687 msgid "make new set" msgstr "criar novo conjunto" #: f.meta.cc:4688 msgid "add to set" msgstr "adicionar à configuração" #: f.meta.cc:4689 msgid "remove" msgstr "remover" #: f.meta.cc:4694 msgid "select last version only" msgstr "selecione apenas a última versão" #: f.meta.cc:4695 msgid "original + last version" msgstr "original + última versão" #: f.meta.cc:4697 msgid "original + all versions" msgstr "original + todas as versões" #: f.meta.cc:4698 f.process.cc:166 f.process.cc:172 f.process.cc:180 #: f.process.cc:2058 msgid "no change" msgstr "sem mudanças" #: f.meta.cc:4703 msgid "report type:" msgstr "tipo relatório:" #: f.meta.cc:4704 msgid "gallery" msgstr "galeria" #: f.meta.cc:4710 msgid "date range" msgstr "variação de data" #: f.meta.cc:4715 msgid "photo date" msgstr "data da foto" #: f.meta.cc:4716 msgid "file date" msgstr "data do arquivo" #: f.meta.cc:4717 msgid "(yyyy-mm-dd)" msgstr "(aaaa-mm-dd)" #: f.meta.cc:4722 msgid "rating range" msgstr "faixa de classificacao" #: f.meta.cc:4730 msgid "all/any" msgstr "todos/qualquer" #: f.meta.cc:4733 msgid "search tags" msgstr "procurar etiquetas" #: f.meta.cc:4740 msgid "search text" msgstr "procurar texto" #: f.meta.cc:4746 msgid "search files" msgstr "procurar arquivos" #: f.meta.cc:4752 msgid "search locations" msgstr "procurar localidades" #: f.meta.cc:4756 msgid "enter cities, countries" msgstr "entre com cidades, países" #: f.meta.cc:4761 msgid "search other metadata" msgstr "procurar outros metadados" #: f.meta.cc:4768 msgid "Enter Search Tag" msgstr "Entre com a Etiqueta de Busca" #: f.meta.cc:5082 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:5089 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:5157 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "datas de procura não fazem sentido \n" " %s %s" #: f.meta.cc:5182 msgid "stars range not reasonable" msgstr "varição de avaliação não é razoável" #: f.meta.cc:5740 msgid "Always reported: date, stars, tags, caption, comment" msgstr "Sempre relatado: data, avaliação, etiquetas, legenda, comentário" #: f.meta.cc:5772 msgid "Additional Items for Report" msgstr "Itens Adicionais para Relatar" #: f.meta.cc:5779 msgid "Keyword" msgstr "Palavra chave" #: f.meta.cc:5793 msgid "Match Criteria" msgstr "Equiparar Critério" #: f.meta.cc:5926 #, c-format msgid "bad number: %s" msgstr "número ruim: %s" #: f.meta.cc:6206 msgid "date format is YYYY-MM-DD" msgstr "formato da data é AAAA-MM-DD" #: f.meta.cc:6210 msgid "date is invalid" msgstr "data inválida" #: f.meta.cc:6248 msgid "time format is HH:MM [:SS]" msgstr "formato da hora é HH:MM [: SS]" #: f.meta.cc:6252 msgid "time is invalid" msgstr "hora inválida" #: f.meta.cc:7350 msgid "not found" msgstr "não encontrado" #: f.meta.cc:7351 msgid "location and country required" msgstr "localização e país requerido" #: f.meta.cc:7608 msgid "choose location" msgstr "escolher localização" #: f.meta.cc:7908 msgid "Set Map Markers" msgstr "Definir Marcadores no Mapa" #: f.meta.cc:7909 msgid "mark all image files" msgstr "marcar todas as imagens" #: f.meta.cc:7910 msgid "mark current gallery" msgstr "marcar galeria atual" #: f.meta.cc:8011 msgid "choose map file" msgstr "escolher arquivo de mapa" #: f.meta.cc:8155 msgid "" "fotoxx-maps package not installed \n" "(see https://kornelix.net)" msgstr "" #: f.meta.cc:8160 #, c-format msgid "map file %s is missing" msgstr "arquivo de mapa file %s faltando" #: f.meta.cc:8164 #, 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:8490 f.meta.cc:9042 msgid "No matching images found" msgstr "Nenhuma imagem encontrada" #: f.meta.cc:8584 msgid "Net Map Source" msgstr "Fonte do Mapa Net" #: f.meta.cc:9120 msgid "Net Map Locations" msgstr "Localidades do Mapa de Rede" #: f.meta.cc:9126 msgid "map location:" msgstr "localidade do mapa:" #: f.pixmap.cc:3227 f.pixmap.cc:3279 msgid "HEIF files not supported" msgstr "Arquivos HEIF não são suportados" #: f.pixmap.cc:3351 f.pixmap.cc:3406 msgid "JP2 files not supported" msgstr "" #: f.process.cc:134 f.widgets.cc:591 msgid "Batch Convert" msgstr "Conversão em Lote" #: f.process.cc:145 msgid "Sequence Numbers" msgstr "Números Sequenciais" #: f.process.cc:147 msgid "base" msgstr "base" #: f.process.cc:150 msgid "adder" msgstr "somador" #: f.process.cc:155 msgid "New Location" msgstr "Nova Localidade" #: f.process.cc:160 msgid "New File Type" msgstr "Novo Tipo de Arquivo" #: f.process.cc:169 f.process.cc:1390 msgid "Color Depth:" msgstr "Resolução de cor:" #: f.process.cc:175 f.process.cc:2053 msgid "max. Width" msgstr "Largura max." #: f.process.cc:184 msgid "Delete Originals" msgstr "Excluir Originais" #: f.process.cc:185 f.process.cc:821 msgid "Copy Metadata" msgstr "Copiar Metadados" #: f.process.cc:199 f.process.cc:824 msgid "Overlay Image" msgstr "Imagem Sobreposta" #: f.process.cc:202 f.warp.cc:4547 msgid "Width %" msgstr "Largura %" #: f.process.cc:207 msgid "Position" msgstr "Posição" #: f.process.cc:219 msgid "Make constant size for screen:" msgstr "Fixar o tamanho para a tela:" #: f.process.cc:227 msgid "" "plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s" msgstr "" "extensões: (ano mês dia nome-antigo sequência) $yyyy $mm $dd $oldname $s" #: f.process.cc:371 #, c-format msgid "file type not supported: %s \n" msgstr "tipo de arquivo não suportado: %s \n" #: f.process.cc:507 f.process.cc:2116 msgid "cannot create new file" msgstr "impossível criar novo arquivo" #: f.process.cc:554 msgid "updating albums ..." msgstr "atualizando álbuns ..." #: f.process.cc:732 #, c-format msgid "invalid plugin: %s" msgstr "extensão inválida: %s" #: f.process.cc:739 msgid "you must use either $s or $oldname" msgstr "você deve usar $s ou $oldname" #: f.process.cc:744 msgid "$s plugin needs base and adder" msgstr "extensão $s precisa de base e somador" #: f.process.cc:749 msgid "base and adder need $s plugin" msgstr "base e somador requerem extensão $s" #: f.process.cc:763 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "tamanho max. %d x %d não é razoável" #: f.process.cc:770 msgid "specify overlay image file" msgstr "especifique arquivo para imagem sobreposta" #: f.process.cc:786 msgid "specify overlay position" msgstr "especifique posição de sobreposição" #: f.process.cc:814 #, c-format msgid "Convert %d image files" msgstr "Converter %d arquivos de imagem" #: f.process.cc:816 msgid "Convert to" msgstr "Converter para" #: f.process.cc:818 msgid "Resize within" msgstr "Redimensionar dentro de" #: f.process.cc:819 msgid "Output to" msgstr "Saída para" #: f.process.cc:825 msgid "*** Delete Originals ***" msgstr "*** Excluir Originais ***" #: f.process.cc:826 msgid "*** Replace Originals ***" msgstr "*** Substituir Originais ***" #: f.process.cc:827 msgid "PROCEED?" msgstr "PROCEDER?" #: f.process.cc:983 f.widgets.cc:592 msgid "Batch Upright" msgstr "Orientação em Lote" #: f.process.cc:989 msgid "Survey all files" msgstr "Examinar todos arquivos" #: f.process.cc:1029 msgid "file cannot be read" msgstr "arquivo não pode ser lido" #: f.process.cc:1146 msgid "cannot select both options" msgstr "não é possível selecionar ambas as opções" #: f.process.cc:1187 f.widgets.cc:593 msgid "Batch Delete/Trash" msgstr "Excluir/Descartar em Lote" #: f.process.cc:1192 msgid "delete" msgstr "excluir" #: f.process.cc:1195 msgid "trash" msgstr "lixo" #: f.process.cc:1258 msgid "Purging deleted files from albums \n" msgstr "Limpar arquivos excluídos de álbuns \n" #: f.process.cc:1338 msgid "Batch Convert RAW Files" msgstr "Conversão em Lote de Arquivos RAW" #: f.process.cc:1377 msgid "output location" msgstr "localização de saída" #: f.process.cc:1382 msgid "File Type" msgstr "Tipo de Arquivo" #: f.process.cc:1406 msgid "amount" msgstr "quantia" #: f.process.cc:1409 msgid "threshold" msgstr "limiar" #: f.process.cc:1413 msgid "Fix dead pixels" msgstr "Corrigir pixels mortos" #: f.process.cc:1416 msgid "dead pixel map file" msgstr "arquivo de mapa de pixels mortos" #: f.process.cc:1419 msgid "Fix pixel bias" msgstr "Corrigir viés de pixels" #: f.process.cc:1422 msgid "pixel bias map file" msgstr "arquivo de mapa de viés de pixels" #: f.process.cc:1727 msgid "growisofs not installed" msgstr "growisofs não instalado" #: f.process.cc:1774 msgid "no DVD/BlueRay device found" msgstr "nenhum dispositivo DVD/BluRay encontrado" #: f.process.cc:1797 msgid "Burn Images to DVD/BlueRay" msgstr "Gravar Imagens em DVD/BluRay" #: f.process.cc:1802 msgid "Select device" msgstr "Selecionar dispositivo" #: f.process.cc:1889 f.widgets.cc:357 msgid "Create a file of selected image files" msgstr "Criar um arquivo com imagens selecionadas" #: f.process.cc:1915 f.process.cc:1984 msgid "Output File" msgstr "Arquivo de saída" #: f.process.cc:1936 msgid "no input files selected" msgstr "nenhum arquivo de entrada selecionado" #: f.process.cc:1942 msgid "no output file selected" msgstr "nenhum arquivo de saída selecionado" #: f.process.cc:2044 f.widgets.cc:597 msgid "Export Files" msgstr "Exportar Arquivos" #: f.process.cc:2049 msgid "To Location" msgstr "Para Localização" #: f.process.cc:2060 msgid "export metadata" msgstr "exportar metadados" #: f.process.cc:2091 msgid "file type not supported" msgstr "tipo de arquivo não suportado" #: f.process.cc:2173 msgid "location is not a folder" msgstr "localização não é um diretório" #: f.process.cc:2225 msgid "Script Files" msgstr "Arquivos de Rotina" #: f.process.cc:2229 msgid "begin making a script file" msgstr "iniciar a criação de um arquivo de rotina" #: f.process.cc:2232 msgid "finish making a script file" msgstr "terminar a criação do arquivo de rotina" #: f.process.cc:2276 msgid "script already started" msgstr "rotina já foi iniciada" #: f.process.cc:2280 msgid "start a new script file" msgstr "iniciar um novo arquivo de rotina" #: f.process.cc:2301 msgid "perform edits to be included in the script file" msgstr "executar edições a serem incluídas no arquivo de rotina" #: f.process.cc:2318 msgid "script file error" msgstr "erro no arquivo de rotina" #: f.process.cc:2323 #, c-format msgid "%s added to script" msgstr "%s adicionado a rotina" #: f.process.cc:2333 msgid "no script file was started" msgstr "nenhum arquivo de rotina foi iniciado" #: f.process.cc:2341 msgid "script file closed" msgstr "arquivo de rotina finalizado" #: f.process.cc:2374 f.process.cc:2395 msgid "no script files found" msgstr "nenhum arquivo de rotina encontrato" #: f.process.cc:2418 #, c-format msgid "" "script error: %s \n" " %s" msgstr "" "error de rotina: %s \n" " %s" #: f.process.cc:2440 #, c-format msgid "unknown edit function: %s" msgstr "função de edição desconhecida: %s" #: f.process.cc:2452 #, c-format msgid "load widgets failed: %s" msgstr "carregamento de dispositivo falhou: %s" #: f.process.cc:2468 #, c-format msgid "script file format error: %s" msgstr "erro no formato do arquivo de rotina: %s" #: f.process.cc:2494 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "erro ao abrir: %s \n" " %s" #: f.process.cc:2511 msgid "script complete" msgstr "rotina finalizada" #: f.process.cc:2553 f.widgets.cc:606 msgid "Batch Script" msgstr "Rotina de Lotes" #: f.process.cc:2560 msgid "Select Script" msgstr "Selecionar Rotina" #: f.process.cc:2561 msgid "script file to run" msgstr "arquivo rotina a executar" #: f.tools.cc:93 msgid "Folders for image files (subfolders included automatically)." msgstr "" "Diretórios para arquivos de imagem (subdiretórios incluídos automaticamente)" #: f.tools.cc:95 msgid "Select to add, click on X to delete." msgstr "Selecione para adicionar, clique em X para apagar." #: f.tools.cc:96 msgid "folder for thumbnails" msgstr "diretório para miniaturas" #: f.tools.cc:97 msgid "extra metadata items to include in index" msgstr "metadados extras para incluir no índice" #: f.tools.cc:98 msgid "force a full re-index of all image files" msgstr "forçar re-indexação completa de fotos arquivos de imagem" #: f.tools.cc:99 msgid "" "Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Função de Indexação encerrada. \n" "Indexação é necessário para busca e mapeamento \n" "e também tornar páginas de galeria mais rápidas." #: f.tools.cc:136 msgid "Index Image Files" msgstr "Arquivos de Índice de imagens" #: f.tools.cc:324 msgid "Choose top image folders" msgstr "Selecionar pasta raiz de imagens" #: f.tools.cc:325 msgid "Choose thumbnail folder" msgstr "Escolher diretório de miniaturas" #: f.tools.cc:326 msgid "" "All image files will be re-indexed. \n" " Continue?" msgstr "" "Todos os arquivos de imagem serão re-indexados. \n" " Continuar?" #: f.tools.cc:442 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:448 #, c-format msgid "" "Thumbnails folder: %s \n" "Please remove." msgstr "" "Diretório de miniaturas: %s \n" "Favor remover." #: f.tools.cc:451 #, c-format msgid "" "Thumbnails folder: \n" " %s \n" "must be named .../thumbnails" msgstr "" "Diretório de Miniaturas: \n" " %s \n" "tem de ser nomeado .../thumbnails" #: f.tools.cc:454 #, c-format msgid "" "Duplicate or nested folders: \n" " %s \n" " %s \n" "Please remove." msgstr "" "Diretórios duplicados ou aninhadas: \n" " %s \n" " %s \n" "Favor remover." #: f.tools.cc:537 msgid "specify at least 1 top image folder" msgstr "especifique pelo menos 1 pasta raiz com imagens" #: f.tools.cc:542 msgid "specify 1 thumbnail folder" msgstr "especifique 1 diretório de miniaturas" #: f.tools.cc:649 msgid "build index" msgstr "construir índice" #: f.tools.cc:778 msgid "Top folders have no images" msgstr "Diretórios topo não possuem imagens" #: f.tools.cc:785 #, c-format msgid "" "0 old files found, %d new files found.\n" "A full re-index is required. Continue?" msgstr "" "0 arquivos antigos encontrados, %d novos arquivos encontrados.\n" "É necessário um reindexar completamente. Continuar?" #: f.tools.cc:1286 msgid "Cancel image index function?" msgstr "Cancelar função de indexação de imagem?" #: f.tools.cc:1447 #, c-format msgid "" "Do you want to move Fotoxx home? \n" " from: %s \n" " to: %s" msgstr "" "Deseja mover o diretório raiz do Fotoxx? \n" " de: %s \n" " para: %s" #: f.tools.cc:1449 msgid "moving files ..." msgstr "movendo arquivos ..." #: f.tools.cc:1473 msgid "new Fotoxx home folder" msgstr "novo diretório raiz do Fotoxx" #: f.tools.cc:1493 msgid "new location name contains a space" msgstr "nova localização possui nome com espaço" #: f.tools.cc:1533 msgid "index_config file has no thumbnails folder" msgstr "arquivo index_config sem diretório de miniaturas" #: f.tools.cc:1541 msgid "update thumbnail folder failed" msgstr "atualização de diretório de miniaturas fallou" #: f.tools.cc:1544 msgid "thumbnails folder not changed" msgstr "diretório de miniaturas não alterado" #: f.tools.cc:1545 #, c-format msgid "new thumbnails folder: %s/thumbnails" msgstr "novo diretório de miniaturas: %s/thumbnails" #: f.tools.cc:1556 msgid "Fotoxx will restart" msgstr "Fotoxx vai reiniciar" #: f.tools.cc:1572 msgid "Recent Files Gallery" msgstr "Arquivos Recentes da Galeria" #: f.tools.cc:1573 msgid "Newest Files Gallery" msgstr "Galeria de Arquivos Recentes" #: f.tools.cc:1574 msgid "Specific Gallery" msgstr "Galeria Específica" #: f.tools.cc:1575 msgid "Album Gallery" msgstr "Galeria de Álbuns" #: f.tools.cc:1576 msgid "Previous Gallery" msgstr "Galeria Anterior" #: f.tools.cc:1577 msgid "Previous File" msgstr "Arquivo Anterior" #: f.tools.cc:1578 msgid "Specific File" msgstr "Arquivo Específico" #: f.tools.cc:1579 f.widgets.cc:436 msgid "Blank Window" msgstr "Janela em Branco" #: f.tools.cc:1604 #, c-format msgid "%c of scale" msgstr "" #: f.tools.cc:1647 msgid "Preferences and Settings" msgstr "Preferências e Configurações" #: f.tools.cc:1650 msgid "Startup Display:" msgstr "Tela de Inicialização:" #: f.tools.cc:1656 msgid "Background Colors:" msgstr "Cores de Fundo:" #: f.tools.cc:1658 msgid "F-View" msgstr "Visão-F" #: f.tools.cc:1661 msgid "G-View" msgstr "Visão-G" #: f.tools.cc:1665 msgid "Menu Style:" msgstr "Estilo do Menu:" #: f.tools.cc:1667 msgid "Icons" msgstr "Ícones" #: f.tools.cc:1669 msgid "Both" msgstr "Ambos" #: f.tools.cc:1671 msgid "Icon size" msgstr "Tamanho de ícone" #: f.tools.cc:1675 msgid "Menu Colors:" msgstr "Cores do Menu:" #: f.tools.cc:1684 msgid "Dialog Font:" msgstr "Fonte de Caixas de Diálogo:" #: f.tools.cc:1703 msgid "JPEG file save quality:" msgstr "Qualidade para salvar arquivos JPEG:" #: f.tools.cc:1706 msgid "(90 = high quality)" msgstr "(90 = alta qualidade)" #: f.tools.cc:1709 msgid "TIFF file compression method" msgstr "Método de compactação de arquivo TIFF" #: f.tools.cc:1715 msgid "PNG file compression level" msgstr "Nível de compactação do arquivo PNG" #: f.tools.cc:1721 msgid "Curve Node Separation, Capture Range:" msgstr "Separação de nós de Curva, Intervalo de Captura:" #: f.tools.cc:1727 msgid "Map Marker Size:" msgstr "Tamanho do Marcador de mapa:" #: f.tools.cc:1733 msgid "Show Images (F-view, G-view):" msgstr "Mostrar Imagens (vista F, vista G):" #: f.tools.cc:1735 msgid "last version only" msgstr "última versão apenas" #: f.tools.cc:1736 msgid "all images" msgstr "todas images" #: f.tools.cc:1739 msgid "Image Position in Window:" msgstr "Posição de Imagem na Janela:" #: f.tools.cc:1741 msgid "centered" msgstr "centralizado" #: f.tools.cc:1742 msgid "right side" msgstr "lado direito" #: f.tools.cc:1745 msgid "Image Index Level:" msgstr "Nível de Indexação de Imagens:" #: f.tools.cc:1749 msgid "Fotoxx started directly (2)" msgstr "O Fotoxx iniciado diretamente (2)" #: f.tools.cc:1753 msgid "Fotoxx started by file manager (1)" msgstr "Fotoxx iniciado pelo gerenciador de arquivos (1)" #: f.tools.cc:1756 msgid "RAW File Loader:" msgstr "Carregador de arquivos RAW:" #: f.tools.cc:1762 msgid "RAW Conversion Options:" msgstr "Opções de Conversão RAW:" #: f.tools.cc:1765 msgid "extend dynamic range for dim images" msgstr "estender a faixa dinâmica de imagens escuras" #: f.tools.cc:1768 msgid "use embedded image as a guide" msgstr "usar imagem incorporada como um guia" #: f.tools.cc:1771 msgid "RAW File Types:" msgstr "Tipos de Arquivos RAW:" #: f.tools.cc:1776 msgid "Video File Types:" msgstr "Tipos de Arquivo de Vídeo:" #: f.tools.cc:1781 msgid "Video File Play Command:" msgstr "Comando de Reprodução de Arquivo de Vídeo:" #: f.tools.cc:1937 msgid "Select startup folder" msgstr "Selecionar diretório de inicialização" #: f.tools.cc:1944 msgid "Select startup image file" msgstr "Selecionar arquivo de imagem inicial" #: f.tools.cc:1951 msgid "Select startup album" msgstr "Selecionar álbum de inicialização" #: f.tools.cc:1976 msgid "rawtherapee-cli (rawtherapee) is not installed" msgstr "rawtherapee-cli (rawtherapee) não está instalado" #: f.tools.cc:2006 msgid "startup folder is invalid" msgstr "diretório de iniciação é inválido" #: f.tools.cc:2016 msgid "startup file is invalid" msgstr "arquivo de iniação é inválido" #: f.tools.cc:2216 f.widgets.cc:374 msgid "Keyboard Shortcuts" msgstr "Teclas de Atalho" #: f.tools.cc:2222 msgid "Reserved Shortcuts \n" msgstr "Atalhos Reservados \n" #: f.tools.cc:2223 msgid " Z Toggle 1x / fit window \n" msgstr " Z Alternar 1x / ajustar à janela \n" #: f.tools.cc:2224 msgid " F1 User Guide, Context Help \n" msgstr " F1 Guia do usuário, Ajuda por Contexto \n" #: f.tools.cc:2225 msgid " F10 Full Screen with menus \n" msgstr " F10 Tela cheia com menus \n" #: f.tools.cc:2226 msgid " F11 Full Screen without menus \n" msgstr " F11 Tela cheia sem menus \n" #: f.tools.cc:2227 msgid " Escape Quit dialog, Quit Fotoxx \n" msgstr " Esc Fechar caixa de diálogo, Fechar Fotoxx \n" #: f.tools.cc:2228 msgid " Delete Delete/Trash \n" msgstr " Del Excluir/Descartar \n" #: f.tools.cc:2229 msgid " Arrow keys Navigation \n" msgstr " Setas Navegação \n" #: f.tools.cc:2230 msgid " Page keys Navigation \n" msgstr " Teclas Page Navegação \n" #: f.tools.cc:2231 msgid " Home/End Navigation \n" msgstr " Home/End Navegação \n" #: f.tools.cc:2295 msgid "Edit KB Shortcuts" msgstr "Editar Atalhos" #: f.tools.cc:2302 msgid "shortcut key:" msgstr "tecla de atalho:" #: f.tools.cc:2303 msgid "(enter key)" msgstr "(tecla enter)" #: f.tools.cc:2304 f.tools.cc:2451 f.tools.cc:2554 msgid "(no selection)" msgstr "(nenhuma seleção)" #: f.tools.cc:2445 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Reservado, não pode ser usado" #: f.tools.cc:2672 msgid "Brightness Distribution" msgstr "Distribuição de Brilho" #: f.tools.cc:2866 msgid "" "Drag mouse on image. \n" "Left click to cancel." msgstr "" "Arraste o cursor na imagem. \n" "Clique esquerdo para cancelar." #: f.tools.cc:2893 f.widgets.cc:615 msgid "Magnify Image" msgstr "Ampliar Imagens" #: f.tools.cc:2902 msgid "X-size" msgstr "Tamanho-X" #: f.tools.cc:3210 msgid "Find Duplicate Images" msgstr "Encontrar imagens Duplicadas" #: f.tools.cc:3213 msgid "All files" msgstr "Todos arquivos" #: f.tools.cc:3214 msgid "Current gallery" msgstr "Galeria atual" #: f.tools.cc:3217 msgid "File count:" msgstr "Contagem de arquivos:" #: f.tools.cc:3221 msgid "Thumbnail size" msgstr "Tamanho de miniatura" #: f.tools.cc:3226 msgid "Pixel difference" msgstr "Diferença de Pixel" #: f.tools.cc:3229 msgid "Pixel count" msgstr "Contador de Pixels" #: f.tools.cc:3237 msgid "Duplicates:" msgstr "Duplicidades:" #: f.tools.cc:3567 msgid "too many files, cannot continue" msgstr "arquivos em excesso, impossível continuar" #: f.tools.cc:3647 msgid "Click image to select pixels." msgstr "Clique na imagem para selecionar pixels." #: f.tools.cc:3690 f.widgets.cc:617 msgid "Show RGB" msgstr "Mostrar RGB" #: f.tools.cc:3948 msgid "Change Color Profile" msgstr "Mudar Perfil de Cores" #: f.tools.cc:3952 msgid "input profile" msgstr "perfil de entrada" #: f.tools.cc:3956 msgid "output profile" msgstr "perfil de saída" #: f.tools.cc:3977 msgid "Unable to change EXIF color profile" msgstr "Impossibilitado de mudar Perfil de Cores EXIF" #: f.tools.cc:3979 msgid "automatic new version created" msgstr "nova versão criada automaticamente" #: f.tools.cc:3988 msgid "color profile" msgstr "perfil de cor" #: f.tools.cc:4036 f.tools.cc:4042 #, c-format msgid "unknown cms profile %s" msgstr "perfil cms desconhecido %s" #: f.tools.cc:4144 f.widgets.cc:619 msgid "Calibrate Printer" msgstr "Calibrar Impressora" #: f.tools.cc:4170 msgid "print color chart" msgstr "imprimir gráfico de cores" #: f.tools.cc:4171 msgid "scan and save color chart" msgstr "digitalizar e salvar gráfico de cores" #: f.tools.cc:4172 msgid "align and trim color chart" msgstr "alinhar e aparar gráfico de cores" #: f.tools.cc:4173 msgid "open and process color chart" msgstr "abrir e processar gráfico de cores" #: f.tools.cc:4174 msgid "print image with revised colors" msgstr "imprimir imagem com cores revisadas" #: f.tools.cc:4315 #, c-format msgid "" "Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/" msgstr "" "Digitalizar gráfico de cores impresso. \n" "A fileira mais escura está no topo. \n" "Salvar em %s/" #: f.tools.cc:4330 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY." msgstr "" "Abra e edite o arquivo digitalizado do gráfico de cores. \n" "Remova qualquer inclinação ou rotação da digitalização. \n" "(Utilize a função Corrigir Perspectiva para isso). \n" "Corte retirando a margem verde e fina PRECISAMENTE." #: f.tools.cc:4362 msgid "Open the trimmed color chart file" msgstr "Abrir arquivo de gráfico de cores aparado" #: f.tools.cc:4495 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:4535 msgid "Color map file to use" msgstr "Arquivo de mapa de cores a usar" #: f.tools.cc:4555 msgid "Select the image file to print." msgstr "Selecione o arquivo de imagem para imprimir." #: f.tools.cc:4596 msgid "file format error" msgstr "erro de formato de arquivo" #: f.tools.cc:4620 msgid "converting colors..." msgstr "convertendo cores..." #: f.tools.cc:4720 msgid "Image colors are converted for printing." msgstr "Cores da imagem foram convertidas para impressão." #: f.tools.cc:4769 f.widgets.cc:620 msgid "Grid Lines" msgstr "Linhas de grade" #: f.tools.cc:4778 msgid "x-spacing" msgstr "espaçamento-x" #: f.tools.cc:4779 msgid "x-count" msgstr "contagem-x" #: f.tools.cc:4780 msgid "x-enable" msgstr "x-habilitado" #: f.tools.cc:4786 msgid "y-spacing" msgstr "espaçamento-y" #: f.tools.cc:4787 msgid "y-count" msgstr "contagem-y" #: f.tools.cc:4788 msgid "y-enable" msgstr "y-habilitado" #: f.tools.cc:4909 f.widgets.cc:621 msgid "Line Color" msgstr "Cor da linha" #: f.tools.cc:4967 msgid "Darkest and Brightest Pixels" msgstr "Pixels mais Escuros e Brilhantes" #: f.tools.cc:4991 msgid "Dark Limit" msgstr "Limite Escuro" #: f.tools.cc:4992 msgid "Bright Limit" msgstr "Limite Claro" #: f.tools.cc:5117 msgid "Map RAW Pixel Bias" msgstr "Mapear Viés de Pixels RAW" #: f.tools.cc:5124 msgid "mean RGB:" msgstr "RGB médio:" #: f.tools.cc:5210 msgid "select at least 10 RAW image files" msgstr "selecione pelo menos 10 arquivos de imagem RAW" #: f.tools.cc:5230 #, c-format msgid "" "cannot read file \n" " %s" msgstr "impossível ler o arquivo \\ n %s" #: f.tools.cc:5235 #, c-format msgid "" "not a RAW file \n" " %s" msgstr "" "não é um arquivo RAW \n" " %s" #: f.tools.cc:5249 #, c-format msgid "dimensions do not match: %s" msgstr "dimensões não correspondem: %s" #: f.tools.cc:5366 f.tools.cc:5449 msgid "Pixel Bias Map file" msgstr "Arquivo Mapa de Viés de Pixels" #: f.tools.cc:5494 msgid "invalid pixel bias map file" msgstr "arquivo mapa de viés de pixels inválido" #: f.tools.cc:5524 msgid "image dimensions do not match pixel bias file" msgstr "" "as dimensões da imagem não correspondem ao arquivo mapa de viés de pixels" #: f.tools.cc:5610 msgid "Map RAW Dead Pixels" msgstr "Mapear Pixels Mortos RAW" #: f.tools.cc:5614 msgid "gray RAW image file" msgstr "arquivo de imagem RAW cinza" #: f.tools.cc:5618 msgid "RGB threshold" msgstr "limiar RGB" #: f.tools.cc:5621 msgid "dead pixels found:" msgstr "pixels mortos encontrados:" #: f.tools.cc:5658 msgid "not a RAW file" msgstr "não é um arquivo RAW" #: f.tools.cc:5664 msgid "cannot load RAW file" msgstr "impossível carregar arquivo RAW" #: f.tools.cc:5727 msgid "choose a gray RAW file" msgstr "escolha um arquivo RAW cinza" #: f.tools.cc:5858 f.tools.cc:5903 msgid "dead pixels file" msgstr "arquivo de pixels mortos" #: f.tools.cc:5955 msgid "invalid dead pixels file" msgstr "arquivode pixels mortos inválido" #: f.tools.cc:5975 msgid "no dead pixels data available" msgstr "não há dados de pixels mortos disponíveis" #: f.tools.cc:5980 msgid "image dimensions do not match dead pixels file" msgstr "as dimensões da imagem não correspondem ao arquivo de pixels mortos" #: f.tools.cc:6048 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:6208 msgid "Available Translations" msgstr "Traduções disponíveis" #: f.tools.cc:6212 msgid "Set Language" msgstr "Configurar língua" #: f.warp.cc:110 f.widgets.cc:559 msgid "Unbend" msgstr "Desempenar" #: f.warp.cc:388 msgid "" " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle." msgstr "" " Clique nos quatro cantos de um quadrilátero. Pressione [aplicar]. \n" " A imagem será distorcida para fazer do quadrilátero um retângulo." #: f.warp.cc:405 msgid "Perspective Correction" msgstr "Correção de Perspectiva" #: f.warp.cc:638 msgid "must have 4 corners" msgstr "precisar ter 4 cantos" #: f.warp.cc:763 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 cursor. \n" " Faça vários ajustes até que esteja satisfeito. \n" " Quando terminar, selecione outra área e precione [pronto]." #: f.warp.cc:776 f.widgets.cc:561 msgid "Warp area" msgstr "Distorcer área" #: f.warp.cc:781 msgid "start warp" msgstr "iniciar distorção" #: f.warp.cc:1182 f.warp.cc:1494 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 cursor. \n" " Faça vários ajustes até que esteja satisfeito. \n" " Quando terminar, precione [pronto]." #: f.warp.cc:1200 f.widgets.cc:562 msgid "Warp curved" msgstr "Distorção curva" #: f.warp.cc:1512 f.widgets.cc:563 msgid "Warp linear" msgstr "Distorção linear" #: f.warp.cc:1825 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 cursor. \n" " Faça vários ajustes até que esteja satisfeito. \n" " Quando terminar, precione [pronto]." #: f.warp.cc:1841 f.widgets.cc:564 msgid "Warp affine" msgstr "Distorção afim" #: f.warp.cc:2174 msgid "" " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n" msgstr "" " Use a Seleção de Área para selecionar a face. \n" " Clique no centro da distorção. \n" " Mova o controle deslizante. \n" #: f.warp.cc:2201 f.widgets.cc:565 msgid "Unwarp Closeup" msgstr "Conformação de aproximação" #: f.warp.cc:2379 msgid "Flatten Book Page" msgstr "Achatar Página de Book" #: f.warp.cc:2380 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 cursor, daí aplaine: " #: f.warp.cc:2383 msgid "Stretch curved-down surfaces:" msgstr "Estique as superfícies curvadas para baixo:" #: f.warp.cc:2438 msgid "Top:" msgstr "Topo:" #: f.warp.cc:2441 msgid "Bottom:" msgstr "Rodapé:" #: f.warp.cc:2830 msgid "" " Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]." msgstr "" "Selecione Áreas que permanecerão inalteradas. \n" "Arraste a imagem através canto superior esquerdo. \n" "Quando terminar, pressione [pronto]." #: f.warp.cc:2846 f.widgets.cc:567 msgid "Area Rescale" msgstr "Redimensionamento de Área" #: f.warp.cc:2869 msgid "select areas first" msgstr "selecione áreas primeiro" #: f.warp.cc:3088 f.widgets.cc:568 msgid "Make Waves" msgstr "Criar Ondas" #: f.warp.cc:3095 msgid "wavelength" msgstr "comprimento de onda" #: f.warp.cc:3097 msgid "variance" msgstr "variância" #: f.warp.cc:3108 msgid "perspective" msgstr "perspectiva" #: f.warp.cc:3285 f.warp.cc:3321 f.widgets.cc:569 msgid "Twist" msgstr "Torção" #: f.warp.cc:3318 f.warp.cc:3609 f.warp.cc:3835 f.warp.cc:4060 msgid "Drag mouse to set center" msgstr "Arraste o cursor para definir centro" #: f.warp.cc:3565 msgid "Spherical Projection" msgstr "Projeção Esférica" #: f.warp.cc:3792 msgid "Stretch Image" msgstr "Esticar Imagem" #: f.warp.cc:4057 f.widgets.cc:572 msgid "Inside-out" msgstr "Virar do Avesso" #: f.warp.cc:4065 f.warp.cc:4314 msgid "Center Hole" msgstr "Buraco Central" #: f.warp.cc:4266 msgid "image width must be greater than height" msgstr "largura de imagem precisa ser maior que altura" #: f.warp.cc:4319 msgid "Cut Top" msgstr "Cortar Topo" #: f.warp.cc:4324 msgid "Cut Bottom" msgstr "Cortar Rodapé" #: f.warp.cc:4332 msgid "Reverse R" msgstr "Reverso" #: f.warp.cc:4333 msgid "Theta" msgstr "Teta" #: f.warp.cc:4545 msgid "Click mouse to change center" msgstr "Clique no cursor para mudar de centro" #: f.warp.cc:4550 msgid "Rim %" msgstr "Aresta %" #: f.widgets.cc:105 msgid "Album" msgstr "Álbum" #: f.widgets.cc:107 msgid "TOP" msgstr "TOPO" #: f.widgets.cc:167 msgid "Rename, copy/move, delete, print" msgstr "Renomear, copiar/mover, excluir, imprimir" #: f.widgets.cc:168 msgid "Thumbnails, bookmarks, albums, slide show" msgstr "Miniaturas, favoritos, álbuns, apresentações" #: f.widgets.cc:169 msgid "View images by map location" msgstr "Ver imagens por localização no mapa" #: f.widgets.cc:170 msgid "Custom favorites menu" msgstr "Menu de favoritos personalizados" #: f.widgets.cc:171 msgid "Left/right click: previous/next (also arrow keys)" msgstr "Clique esquerdo/direito: anterior/próximo (também teclas de seta)" #: f.widgets.cc:172 msgid "" "Left/right click: zoom image/thumb size, meta view, list view \n" " (keyboard [-] and [+] keys also work)" msgstr "" #: f.widgets.cc:174 msgid "Save modified file as new version or new file" msgstr "Salvar arquivo modificado como uma nova versão" #: f.widgets.cc:175 msgid "Metadata: captions, tags, ratings, geotags, search images" msgstr "" "Metadados: legendas, etiquetas, avaliações, georreferências, procurar imagens" #: f.widgets.cc:176 msgid "Select areas to edit separately, save, copy and paste" msgstr "Selecione áreas para editar separadamente, salve, copie e cole" #: f.widgets.cc:177 msgid "" "Left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit" msgstr "" "Clique esquerdo/direito: desfazer/refazer 1 edição \n" " com tecla A: desfazer/refazer todas as edições \n" " clique do meio: ir a qualquer edição anterior" #: f.widgets.cc:180 msgid "Image edit basic functions" msgstr "Funções básicas de edição de imagens" #: f.widgets.cc:181 msgid "Image repair and enhance" msgstr "Aprimorar e reparar imagens" #: f.widgets.cc:182 msgid "Artistic effects (filters)" msgstr "Efeitos artísticos (filtros)" #: f.widgets.cc:183 msgid "Image warp, unwarp, transform" msgstr "Distorcer imagem, desqualificar, transformar" #: f.widgets.cc:184 msgid "HDR, HDF, panorama, stack, mashup" msgstr "HDR, HDF, panorama, empilhamento, mistura" #: f.widgets.cc:185 msgid "Batch processing, custom scripts" msgstr "Processamento em lote, rotinas personalizados" #: f.widgets.cc:186 msgid "Image index, user preferences, shortcuts, utilities" msgstr "Índice de imagens, preferências de usuário, atalhos, utilitários" #: f.widgets.cc:187 msgid "User Guide, recent changes, log file, about" msgstr "Guia do usuário, alterações recentes, arquivo de log, sobre" #: f.widgets.cc:190 msgid "Current File (R-click or key F)" msgstr "Arquivo Atual (clique direito ou tecla F)" #: f.widgets.cc:191 msgid "Open a parallel Fotoxx session" msgstr "Abrir sessão Fotoxx em paralelo" #: f.widgets.cc:192 msgid "Cycle 2 Prior Files" msgstr "Cambiar 2 Imagem para trás" #: f.widgets.cc:193 msgid "Cycle 3 Prior Files" msgstr "Cambiar 3 Imagens para trás" #: f.widgets.cc:194 msgid "View a 360 degree panorama image file" msgstr "Visualizar um arquivo de imagem panorama 360 ​​graus" #: f.widgets.cc:195 msgid "Change file name" msgstr "Mudar nome do arquivo" #: f.widgets.cc:196 msgid "View and change file permissions" msgstr "Ver e alterar permissões de arquivo" #: f.widgets.cc:197 msgid "Create a blank image" msgstr "Criar uma imagem em branco" #: f.widgets.cc:198 msgid "Toggle - blank or restore window" msgstr "Alternar - vazio ou restaurar janela" #: f.widgets.cc:199 msgid "Copy or Move file to new location" msgstr "Copiar ou Mover um arquivo para uma nova localidade" #: f.widgets.cc:200 msgid "Copy file to the desktop" msgstr "Copiar um arquivo para a área de trabalho" #: f.widgets.cc:201 msgid "Copy file to the clipboard" msgstr "Copiar arquivo para a área de transferência" #: f.widgets.cc:202 msgid "Set file as desktop wallpaper (GNOME)" msgstr "Configurar arquivo como plano de fundo (GNOME)" #: f.widgets.cc:203 msgid "Show location on Internet map" msgstr "Mostrar localização no mapa da Internet" #: f.widgets.cc:204 msgid "Delete or trash file" msgstr "Excluir ou descartar arquivo" #: f.widgets.cc:205 msgid "Print the current image" msgstr "Imprimir imagem atual" #: f.widgets.cc:206 msgid "Print current image with adjusted colors" msgstr "Imprimir imagem atual com cores ajustadas" #: f.widgets.cc:207 msgid "Quit Fotoxx" msgstr "Fechar Fotoxx" #: f.widgets.cc:210 msgid "Thumbnail Gallery (R-click or key G)" msgstr "Galeria de miniaturas (clique direito ou tecla G)" #: f.widgets.cc:211 msgid "Gallery view with thumbnails and file data" msgstr "" #: f.widgets.cc:212 msgid "Gallery view with thumbnails and basic metadata" msgstr "Visão galeria com miniaturas e metadados básicos" #: f.widgets.cc:213 msgid "Gallery view with small thumbnails and file names" msgstr "Visão galeria com pequenas miniaturas e nomes de arquivos" #: f.widgets.cc:214 msgid "Gallery of recently viewed image files" msgstr "Galeria de imagens visualizados recentemente" #: f.widgets.cc:215 msgid "Gallery of newest image files" msgstr "Galeria dos novos arquivos de imagem" #: f.widgets.cc:216 msgid "Jump to beginning [home]" msgstr "Saltar para início [home]" #: f.widgets.cc:217 msgid "Jump to end [end]" msgstr "Saltar para o fim [end]" #: f.widgets.cc:218 msgid "Set gallery from current image file" msgstr "Definir galeria a partir de imagem atual" #: f.widgets.cc:219 msgid "Change sort order" msgstr "Alterar a ordem de classificação" #: f.widgets.cc:220 msgid "List all folders, click any for gallery view" msgstr "Lista todos diretórios, clique em qualquer visão da galeria" #: f.widgets.cc:221 msgid "select input files for album, batch, script functions" msgstr "seleciona arquivos para funções de álbum, edição em lote, rotinas" #: f.widgets.cc:222 msgid "Set and recall bookmarked image locations" msgstr "Definir e recuperar localidade de imagem marcada como favorita" #: f.widgets.cc:223 msgid "Organize images into albums" msgstr "Organizar imagens em Álbuns" #: f.widgets.cc:224 msgid "Update albums for new file versions" msgstr "Atualizar álbuns para novas versões de arquivo" #: f.widgets.cc:225 msgid "Mass update album files" msgstr "Atualizar vários arquivos de álbum" #: f.widgets.cc:226 msgid "Save current gallery as album" msgstr "Salvar galeria atual como álbum" #: f.widgets.cc:227 msgid "Start a slide show" msgstr "Iniciar Apresentação de Slides" #: f.widgets.cc:230 msgid "Maps (R-click or key M)" msgstr "Mapas (clique direito ou tecla M)" #: f.widgets.cc:231 msgid "Open Internet map" msgstr "Abrir mapa da Internet" #: f.widgets.cc:232 msgid "Choose Internet map source" msgstr "Escolher fonte de mapa da Internet" #: f.widgets.cc:233 msgid "Internet map locations" msgstr "Localidades no mapa da Internet" #: f.widgets.cc:234 msgid "Open file map" msgstr "Abrir arquivo mapa" #: f.widgets.cc:235 msgid "Choose file map" msgstr "Escolher arquivo mapa" #: f.widgets.cc:236 msgid "Set map markers for all images or current gallery" msgstr "Definir marcadores no mapa para todas imagens ou galeria atual" #: f.widgets.cc:239 msgid "List a few key metadata items" msgstr "Listar poucos ítens de metadados chave" #: f.widgets.cc:240 msgid "List all metadata items" msgstr "Listar todos ítens de metadados" #: f.widgets.cc:241 msgid "Edit image tags/geotags/caption/rating ..." msgstr "Editar etiquetas de imagem/georreferências/legendas/avaliações ..." #: f.widgets.cc:242 msgid "Define tags (keywords) used for searching images" msgstr "Definir etiquetas (palavras-chave) usadas para procurar imagens" #: f.widgets.cc:243 msgid "Edit any image metadata" msgstr "Edita qualquer metadados de uma imagem" #: f.widgets.cc:244 msgid "Remove selected image metadata" msgstr "Remove selected image metadata" #: f.widgets.cc:245 msgid "Show file name, captions, comments" msgstr "Mostrar nome do arquivo, legendas, comentários" #: f.widgets.cc:246 msgid "Find all images for a location [date]" msgstr "Encontrar todas imagens por uma localidade [data]" #: f.widgets.cc:247 msgid "Show image counts by month, select and report" msgstr "Mostra contagem de imagens por mês, seleção e relatório" #: f.widgets.cc:248 msgid "Find images meeting search criteria" msgstr "Procurar imagens que atendam aos critérios de pesquisa" #: f.widgets.cc:251 msgid "Select object or area for editing" msgstr "Selecionar objeto ou área para edição" #: f.widgets.cc:252 msgid "Select hairy or irregular edge" msgstr "Selecione contorno irregular ou felpudo" #: f.widgets.cc:253 msgid "Find a gap in an area outline" msgstr "Procurar lacuna em área de contorno" #: f.widgets.cc:254 msgid "Show (outline) existing area" msgstr "Mostrar (traçado) de área existente" #: f.widgets.cc:255 msgid "Hide existing area" msgstr "Esconder área existente" #: f.widgets.cc:256 msgid "Enable area for editing" msgstr "Habilitar área para edição" #: f.widgets.cc:257 msgid "Disable area for editing" msgstr "Desabilitar área para edição" #: f.widgets.cc:258 msgid "Reverse existing area" msgstr "Inverter área existente" #: f.widgets.cc:259 msgid "Erase existing area" msgstr "Apagar área existente" #: f.widgets.cc:260 msgid "Copy area for later pasting into image" msgstr "Copiar área para posterior colagem em imagem" #: f.widgets.cc:261 msgid "Paste previously copied area into image" msgstr "Colar área previamente copiada em imagem" #: f.widgets.cc:262 msgid "Open a file and paste as area into image" msgstr "Abrir um arquivo e colar como área na imagem" #: f.widgets.cc:263 msgid "Save area to a file with transparency" msgstr "Salvar área em arquivo com transparência" #: f.widgets.cc:266 msgid "Trim/Crop margins and/or Rotate" msgstr "Recortar/Aparar margens e/ou Rodar" #: f.widgets.cc:267 msgid "Auto upright a rotated image based on EXIF data" msgstr "Orientar automaticamente imagem rotacionada com base nos dados EXIF" #: f.widgets.cc:268 msgid "Adjust brightness, contrast, color" msgstr "Ajusta brilho, contraste, cor" #: f.widgets.cc:269 msgid "Change pixel dimensions" msgstr "Mudar dimensões de pixel" #: f.widgets.cc:270 msgid "Adjust color using RGB or CMY colors" msgstr "Ajustar cor usando cores RGB ou CMY" #: f.widgets.cc:271 msgid "Adjust color using HSL colors" msgstr "Ajustar cor usando cores HSL" #: f.widgets.cc:272 msgid "Draw on image: text, line/arrow, box, ellipse" msgstr "Desenhar em imagem: texto, linha/seta, caixa, elipse" #: f.widgets.cc:273 msgid "Paint image pixels using the mouse" msgstr "Pintar os pixels da imagem usando o cursor" #: f.widgets.cc:274 msgid "Copy pixels within an image using the mouse" msgstr "Copiar pixels em uma imagem usando o cursor" #: f.widgets.cc:275 msgid "Copy pixels from one image to another using the mouse" msgstr "Copiar pixels de uma imagem para outra usando o cursor" #: f.widgets.cc:276 msgid "Paint edit function gradually with mouse" msgstr "Função de pintura gradual com o cursor" #: f.widgets.cc:277 msgid "Incrementally undo prior edits gradually with mouse" msgstr "Desfazer gradualmente edições anteriores gradualmente com o mouse" #: f.widgets.cc:278 msgid "Edit plugins menu or run a plugin function" msgstr "Editar menu de plugins ou executar uma função de plugin" #: f.widgets.cc:279 msgid "Specialized program for editing RAW files" msgstr "Programa especializado para editar arquivos RAW" #: f.widgets.cc:282 f.widgets.cc:283 msgid "Fast auto enhance that may work OK" msgstr "Realce automático rápido que deve funcionar bem" #: f.widgets.cc:284 msgid "Edit brightness distribution" msgstr "Editar distribuição de brilho" #: f.widgets.cc:285 msgid "Magnify brightness gradients to enhance details" msgstr "Ampliar gradientes de brilho realçar detalhes" #: f.widgets.cc:286 msgid "Flatten brightness distribution to enhance details" msgstr "Nivelar a distribuição de brilho para melhorar detalhes" #: f.widgets.cc:287 msgid "Rescale RGB - reduce color caste and fog/haze" msgstr "Reescalar brilho - reduz separação de cores e névoa/neblina" #: f.widgets.cc:288 msgid "Make the image look sharper" msgstr "Tornar a imagem mais nítida" #: f.widgets.cc:289 msgid "Blur the image, different methods" msgstr "Embaçar imagem, diferentes métodos" #: f.widgets.cc:290 msgid "Filter noise from low-light photos" msgstr "Filtrar ruído de fotos com pouca luz" #: f.widgets.cc:291 msgid "Fix red-eyes from electronic flash" msgstr "Corrigir olhos vermelhos por flash eletrônico" #: f.widgets.cc:292 msgid "Match colors on one image with another" msgstr "Equiparar cores de uma imagem com outra" #: f.widgets.cc:293 msgid "Remove unwanted objects" msgstr "Remover objetos indesejados" #: f.widgets.cc:294 msgid "Fix color fringes in outer areas of an image" msgstr "Corrigir franjas púrpuras nas áreas externas de uma imagem" #: f.widgets.cc:295 msgid "Fix color band on dark/bright feature edges" msgstr "Corrijir faixa de cores em bordas escuros/brilhantes" #: f.widgets.cc:296 msgid "Change brightness or color radially" msgstr "Alterar o brilho ou cor radialmente" #: f.widgets.cc:297 msgid "Remove dust spots from scanned slides" msgstr "Remover pontos de sujeira de digitalizações" #: f.widgets.cc:300 msgid "Convert to simulated sketch" msgstr "Converter para esboço simulado" #: f.widgets.cc:301 msgid "Convert image into a cartoon drawing" msgstr "Converter imagem em um desenho cartoon" #: f.widgets.cc:302 msgid "Convert to line drawing (edge detection)" msgstr "Converter para desenho de contorno (detecção de borda)" #: f.widgets.cc:303 msgid "Create an embossed or 3D appearance" msgstr "Criar relevo ou aparência de 3D" #: f.widgets.cc:304 msgid "Convert to square tiles" msgstr "Converter para ladrilhos quadrados" #: f.widgets.cc:305 msgid "Convert to dithered dots" msgstr "Converter em pontos esparsos" #: f.widgets.cc:306 msgid "Convert into a simulated painting" msgstr "Converter em uma pintura simulada" #: f.widgets.cc:307 msgid "Add texture to an image" msgstr "Adicionar textura a uma imagem" #: f.widgets.cc:308 msgid "Tile image with a repeating pattern" msgstr "Ladrilhar imagem com um padrão repetitivo" #: f.widgets.cc:309 msgid "Create a mosaic with tiles made from all images" msgstr "Criar um mosaico com ladrilhos feitos de todas as imagens" #: f.widgets.cc:310 msgid "Make BW/color, negative/positive, sepia" msgstr "Fazer PB/colorido, negativo/positivo, sepia" #: f.widgets.cc:311 msgid "Reduce color depth (posterize)" msgstr "Reduzir profundidade de cor (posterizar)" #: f.widgets.cc:312 msgid "Shift/convert colors into other colors" msgstr "Deslocar/converter cores em outras" #: f.widgets.cc:313 msgid "Change color hue using an algorithm" msgstr "Altera matiz de cor usando um algoritmo" #: f.widgets.cc:314 msgid "Add a brightness/color ramp across the image" msgstr "Adicionar rampa de brilho/cor através da imagem" #: f.widgets.cc:315 msgid "Paint image transparency using the mouse" msgstr "Aplicar transparência de imagem usando o cursor" #: f.widgets.cc:316 msgid "Mirror image horizontally or vertically" msgstr "Espelhar imagem horizontalmente ou verticalmente" #: f.widgets.cc:317 msgid "Process an image using a custom kernel" msgstr "Processar uma imagem usando um núcleo customizado" #: f.widgets.cc:320 msgid "Remove curvature, esp. panoramas" msgstr "Remover curvatura, esp. panoramas" #: f.widgets.cc:321 msgid "Straighten objects seen from an angle" msgstr "Endireitar objetos vistos a um ângulo" #: f.widgets.cc:322 msgid "Distort image areas using the mouse" msgstr "Distorcer as áreas da imagem usando o cursor" #: f.widgets.cc:323 msgid "Unwarp closeup face photo to remove distortion" msgstr "Remover distorção por aproximação em foto de face" #: f.widgets.cc:324 f.widgets.cc:325 f.widgets.cc:326 msgid "Distort the whole image using the mouse" msgstr "Distorcer toda a imagem utilizando o cursor" #: f.widgets.cc:327 msgid "Flatten a photographed book page" msgstr "Achatar uma página fotografada de um book" #: f.widgets.cc:328 msgid "Rescale image outside selected areas" msgstr "Redimensionar imagem por fora de áreas selecionadas" #: f.widgets.cc:329 msgid "Warp an image with a wave pattern" msgstr "Distorce uma imagem com um padrão de onda" #: f.widgets.cc:330 msgid "Twist image centered at mouse position" msgstr "Torcer a imagem centralizando na posição do cursor" #: f.widgets.cc:331 msgid "Make a spherical projection of an image" msgstr "Criar uma projeção esférica de uma imagem" #: f.widgets.cc:332 msgid "Image scale increases from center to edge" msgstr "Escala de imagem aumenta do centro para a borda" #: f.widgets.cc:333 msgid "Turn an image inside-out" msgstr "Virar uma imagem ao Avesso" #: f.widgets.cc:334 msgid "Convert an image into a Tiny Planet" msgstr "Converte uma imagem em um mini-planeta" #: f.widgets.cc:335 msgid "Generate an inward spiraling recursive image" msgstr "Gerar imagem em espiral recursiva interna" #: f.widgets.cc:338 msgid "Combine bright/dark images for better detail" msgstr "Combine imagens claras/escuras para melhores detalhes" #: f.widgets.cc:339 msgid "Combine near/far focus images for deeper focus" msgstr "Combinar imagens de foco perto/longe para foco profundo" #: f.widgets.cc:340 msgid "Combine images to erase passing people, etc." msgstr "Combinar imagens para apagar pessoas que passam, etc" #: f.widgets.cc:341 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:342 msgid "Combine image layers, mouse select and expose" msgstr "Combinar camadas de imagem, seleção e esposição do cursor" #: f.widgets.cc:343 msgid "Compare two images separated by sliding boundary" msgstr "Comparar duas imagens em quadro deslizante" #: f.widgets.cc:344 msgid "Show differences between two images" msgstr "Mostrar diferenças entre duas imagens" #: f.widgets.cc:345 msgid "Combine images into a panorama" msgstr "Combinar imagens em um panorama" #: f.widgets.cc:346 msgid "Combine images into a vertical panorama" msgstr "Combinar imagens em um panorama vertical" #: f.widgets.cc:347 msgid "Combine images into a panorama (panorama tools)" msgstr "Combinar imagens em um panorama (panorama tools)" #: f.widgets.cc:348 msgid "Combine images into a montage of images" msgstr "Combina várias imagens em uma colagem" #: f.widgets.cc:349 msgid "Arrange images and text in a layout (montage)" msgstr "Arranjar imagens e texto em um leiaute (colagem)" #: f.widgets.cc:352 msgid "Rename/convert/resize/move multiple files" msgstr "Renomear/converter/redimensionar/mover vários arquivos" #: f.widgets.cc:353 msgid "Upright multiple rotated image files" msgstr "Orientar múltiplos arquivos de imagem rotacionados" #: f.widgets.cc:354 msgid "Delete or Trash multiple files" msgstr "Excluir ou Remover vários arquivos" #: f.widgets.cc:355 msgid "Convert camera RAW files to tiff/png/jpeg" msgstr "Converter arquivos RAW em tiff/png/jpeg" #: f.widgets.cc:356 msgid "Burn selected image files to DVD/BlueRay disc" msgstr "Gravar arquivos de imagens selecionadas em disco DVD/BluRay" #: f.widgets.cc:358 msgid "Export selected image files to a folder" msgstr "Exportar arquivos de imagem selecionada para um diretório" #: f.widgets.cc:359 msgid "Add/remove tags for multiple images" msgstr "Adicionar/remover etiquetas para múltiplas imagens" #: f.widgets.cc:360 msgid "Convert tag names for all images" msgstr "Converter nome de etiqueta para todas as imagens" #: f.widgets.cc:361 msgid "change or shift photo dates/times" msgstr "alterar ou deslocar data/hora de várias fotos" #: f.widgets.cc:362 msgid "Add/change/delete metadata for multiple images" msgstr "Adicionar/Mudar/Excluir metadados em várias imagens" #: f.widgets.cc:363 msgid "Report metadata for multiple images" msgstr "Relatório de metadados para várias imagens" #: f.widgets.cc:364 msgid "Add/revise geotags for multiple images" msgstr "Adicionar/Revisar georeferências para várias imagens" #: f.widgets.cc:365 msgid "Build a custom script with multiple edit functions" msgstr "Criar uma rotina customizada com múltiplas funções de edição" #: f.widgets.cc:366 msgid "Run custom script to edit the current image file" msgstr "Executar rotina customizada para editar a imagens atual" #: f.widgets.cc:367 msgid "Run custom script to edit a batch of image files" msgstr "Executar rotina customizada para edição de um lote de imagens" #: f.widgets.cc:370 msgid "Index new files and make thumbnails" msgstr "Indexar novos arquivos gerar miniaturas" #: f.widgets.cc:371 msgid "Quick incremental index update" msgstr "Atualização incremental rápida de índice" #: f.widgets.cc:372 msgid "Move Fotoxx home folder" msgstr "Mudar diretório base do Fotoxx" #: f.widgets.cc:373 msgid "User preferences and settings" msgstr "Preferências e configurações de usuário" #: f.widgets.cc:375 msgid "Show RGB brightness distribution" msgstr "Mostrar distribuição de brilho RGB" #: f.widgets.cc:376 msgid "Magnify image around the mouse position" msgstr "Ampliar imagem em torno da posição do cursor" #: f.widgets.cc:377 msgid "Search all image files and report duplicates" msgstr "Procurar em todas os arquivos de imagem e relatar duplicidades" #: f.widgets.cc:378 msgid "Show RGB colors at mouse click" msgstr "Mostrar cores RGB ao clique do cursor" #: f.widgets.cc:379 msgid "Convert to another color profile" msgstr "Converter para outro perfil de cores" #: f.widgets.cc:380 msgid "Calibrate printer colors" msgstr "Calibrar cores de impressora" #: f.widgets.cc:381 msgid "Show or revise grid lines" msgstr "Mostrar ou corrigir linhas de grade" #: f.widgets.cc:382 msgid "Change color of foreground lines" msgstr "Mudar cor de linhas de primeiro plano" #: f.widgets.cc:383 msgid "Highlight darkest and brightest pixels" msgstr "Destacar pixels mais escuros e mais brilhantes" #: f.widgets.cc:384 msgid "map raw pixel bias (camera sensor, vignette)" msgstr "mapear viés de pixels (sensor da câmera, vinheta)" #: f.widgets.cc:385 msgid "map raw dead pixels (camera sensor)" msgstr "mapear pixels mortos (sensor da câmera)" #: f.widgets.cc:386 msgid "Chart to adjust monitor color" msgstr "Gráfico para ajustar cores do monitor" #: f.widgets.cc:387 msgid "Chart to adjust monitor gamma" msgstr "Gráfico para ajustar gama do monitor" #: f.widgets.cc:388 msgid "Change the GUI language" msgstr "Mudar a língua da GUI" #: f.widgets.cc:389 msgid "Report missing translations" msgstr "Relatar tradução em falta" #: f.widgets.cc:390 msgid "Anonymous usage statistics" msgstr "Estatísticas de uso anônimas" #: f.widgets.cc:391 msgid "Memory and CPU (to terminal/logfile)" msgstr "Memória e CPU (para terminal/arquivo log)" #: f.widgets.cc:392 msgid "List files included in appimage container" msgstr "Listar arquivos incluídos no contêiner appimage" #: f.widgets.cc:393 msgid "test crash report with source line numbers" msgstr "testar relatório de falha com número de linha de código fonte" #: f.widgets.cc:396 msgid "Read the user guide" msgstr "Leia o Guia de Usuários" #: f.widgets.cc:397 msgid "Recent user guide changes" msgstr "Mudanças recentes no Guia de Usuários" #: f.widgets.cc:398 msgid "Overview of all edit functions" msgstr "Visão geral de todas as funções de edição" #: f.widgets.cc:399 msgid "List updates by Fotoxx version" msgstr "Listar atualizações por versão do Fotoxx" #: f.widgets.cc:400 msgid "View the log file and error messages" msgstr "Ver arquivo de log e mensagens de erro" #: f.widgets.cc:401 msgid "Fotoxx Man Page - summary of capabilities" msgstr "Página manual do Fotoxx - resumo dos recursos" #: f.widgets.cc:402 msgid "List command line parameters" msgstr "Listar parâmetros de linha de comando" #: f.widgets.cc:403 msgid "How to do Fotoxx translations" msgstr "Como fazer traduções para o Fotoxx" #: f.widgets.cc:404 msgid "Show the Fotoxx web page" msgstr "Mostrar a página do Fotoxx na web" #: f.widgets.cc:405 msgid "Fotoxx license - terms of use" msgstr "Licença Fotoxx - termos de uso" #: f.widgets.cc:406 msgid "Fotoxx privacy policy" msgstr "Política de privacidade do Fotoxx" #: f.widgets.cc:407 msgid "Version, contact, credits" msgstr "Versão, contato, créditos" #: f.widgets.cc:427 msgid "File View F" msgstr "Visão do arquivo F" #: f.widgets.cc:428 msgid "New Session" msgstr "Nova Sessão" #: f.widgets.cc:429 f.widgets.cc:456 msgid "Source Folder" msgstr "Diretório Fonte" #: f.widgets.cc:430 msgid "Cycle 2" msgstr "Cambiar 2" #: f.widgets.cc:431 msgid "Cycle 3" msgstr "Cambiar 3" #: f.widgets.cc:432 msgid "View 360° Pano" msgstr "Visão 360° Panorama" #: f.widgets.cc:433 f.widgets.cc:823 fotoxx.h:1416 msgid "Rename" msgstr "Renomear" #: f.widgets.cc:435 msgid "Blank Image" msgstr "Imagem Vazia" #: f.widgets.cc:437 f.widgets.cc:825 msgid "Copy/Move" msgstr "Copiar/Mover" #: f.widgets.cc:438 f.widgets.cc:826 msgid "Copy to Desktop" msgstr "Copiar para Área de Trabalho" #: f.widgets.cc:439 f.widgets.cc:827 msgid "Copy to Clipboard" msgstr "Copiar para Área de Transferência" #: f.widgets.cc:440 msgid "Set Wallpaper" msgstr "Definir papel de parede" #: f.widgets.cc:441 f.widgets.cc:842 msgid "Show on Map" msgstr "Mostrar no Mapa" #: f.widgets.cc:442 f.widgets.cc:843 msgid "Delete/Trash" msgstr "Excluir/Lixo" #: f.widgets.cc:443 msgid "Print" msgstr "Imprimir" #: f.widgets.cc:444 msgid "Print Calibrated" msgstr "Imprimir Calibrado" #: f.widgets.cc:445 fotoxx.h:1409 msgid "Quit" msgstr "Sair" #: f.widgets.cc:448 msgid "Gallery View G" msgstr "Visão Galeria G" #: f.widgets.cc:449 msgid "Thumb View" msgstr "" #: f.widgets.cc:450 msgid "Meta View" msgstr "Visão Meta" #: f.widgets.cc:451 msgid "List View" msgstr "Visão em Lista" #: f.widgets.cc:452 msgid "Recent" msgstr "Recente" #: f.widgets.cc:453 msgid "Newest" msgstr "Novo" #: f.widgets.cc:454 msgid "GoTo First" msgstr "IrPara Primeiro" #: f.widgets.cc:455 msgid "GoTo Last" msgstr "IrPara Último" #: f.widgets.cc:457 msgid "Sort Gallery" msgstr "Ordenar Galeria" #: f.widgets.cc:458 msgid "All Folders" msgstr "Todos Diretórios" #: f.widgets.cc:459 fotoxx.h:1427 msgid "Select Files" msgstr "Selecionar arquivos" #: f.widgets.cc:462 msgid "Update Albums" msgstr "Atualizar Álbuns" #: f.widgets.cc:464 msgid "Gallery to Album" msgstr "Galeria para Álbum" #: f.widgets.cc:468 msgid "Map View M" msgstr "Visão Mapa M" #: f.widgets.cc:469 msgid "Net Map" msgstr "Mapa Net" #: f.widgets.cc:470 msgid "Net Source" msgstr "Net Fonte" #: f.widgets.cc:471 msgid "Net Locs" msgstr "Net Loc." #: f.widgets.cc:472 msgid "File Map" msgstr "Arquivo de Mapa" #: f.widgets.cc:473 msgid "Choose Map" msgstr "Escolher Mapa" #: f.widgets.cc:474 msgid "Markers" msgstr "Marcadores" #: f.widgets.cc:477 f.widgets.cc:819 msgid "View Meta" msgstr "Ver Metadados" #: f.widgets.cc:478 f.widgets.cc:820 msgid "View All Meta" msgstr "Ver Todos Metadados" #: f.widgets.cc:479 f.widgets.cc:821 msgid "Edit Meta" msgstr "Editar Metadados" #: f.widgets.cc:481 f.widgets.cc:822 msgid "Edit Any Meta" msgstr "Editar Qualquer Metadado" #: f.widgets.cc:482 msgid "Delete Meta" msgstr "Exlcuir Metadados" #: f.widgets.cc:483 msgid "Captions" msgstr "Legendas" #: f.widgets.cc:484 msgid "Places/Dates" msgstr "Lugares/Datas" #: f.widgets.cc:485 msgid "Timeline" msgstr "Linha do Tempo" #: f.widgets.cc:486 fotoxx.h:1424 msgid "Search" msgstr "Buscar" #: f.widgets.cc:489 fotoxx.h:1426 msgid "Select" msgstr "Selecionar" #: f.widgets.cc:491 msgid "Find Gap" msgstr "Encontrar Lacuna" #: f.widgets.cc:492 fotoxx.h:1428 msgid "Show" msgstr "Mostrar" #: f.widgets.cc:493 fotoxx.h:1369 msgid "Hide" msgstr "Ocultar" #: f.widgets.cc:494 fotoxx.h:1353 msgid "Enable" msgstr "Habilitar" #: f.widgets.cc:495 fotoxx.h:1348 msgid "Disable" msgstr "Desabilitar" #: f.widgets.cc:497 fotoxx.h:1335 msgid "Clear" msgstr "Limpar" #: f.widgets.cc:498 fotoxx.h:1342 msgid "Copy" msgstr "Copiar" #: f.widgets.cc:499 fotoxx.h:1401 msgid "Paste" msgstr "Colar" #: f.widgets.cc:500 fotoxx.h:1378 msgid "Load" msgstr "Carga" #: f.widgets.cc:501 f.widgets.cc:657 fotoxx.h:1423 zfuncs.cc:12513 msgid "Save" msgstr "Salvar" #: f.widgets.cc:510 msgid "Markup" msgstr "Marcador" #: f.widgets.cc:511 msgid "Paint Image" msgstr "Pintar Imagem" #: f.widgets.cc:512 msgid "Copy Pixels 1" msgstr "Copiar Pixels 1" #: f.widgets.cc:513 msgid "Copy Pixels 2" msgstr "Copiar Pixels 2" #: f.widgets.cc:516 msgid "Plugins" msgstr "Extensões" #: f.widgets.cc:517 f.widgets.cc:841 msgid "Raw Therapee" msgstr "Raw Therapee" #: f.widgets.cc:520 msgid "Voodoo 1" msgstr "Voodoo 1" #: f.widgets.cc:521 msgid "Voodoo 2" msgstr "Voodoo 2" #: f.widgets.cc:522 f.widgets.cc:837 msgid "Brite Dist" msgstr "Dist. de Brilho" #: f.widgets.cc:523 f.widgets.cc:839 msgid "Gradients" msgstr "Gradientes" #: f.widgets.cc:524 f.widgets.cc:838 fotoxx.h:1362 msgid "Flatten" msgstr "Abrandar" #: f.widgets.cc:525 msgid "Global Retx" msgstr "Retinex Global" #: f.widgets.cc:526 msgid "Zonal Retx" msgstr "Retinex Zonal" #: f.widgets.cc:529 msgid "Denoise" msgstr "Reduzir ruído" #: f.widgets.cc:530 msgid "Red Eyes" msgstr "Olhos vermelhos" #: f.widgets.cc:531 msgid "Match Colors" msgstr "Mesclar Cores" #: f.widgets.cc:533 msgid "Chromatic1" msgstr "Cromático1" #: f.widgets.cc:534 msgid "Chromatic2" msgstr "Cromático2" #: f.widgets.cc:539 msgid "Sketch" msgstr "Esboço" #: f.widgets.cc:540 msgid "Cartoon" msgstr "Cartoon" #: f.widgets.cc:544 msgid "Dither" msgstr "Pontilhado" #: f.widgets.cc:546 msgid "Texture" msgstr "Textura" #: f.widgets.cc:548 msgid "Mosaic" msgstr "Mosaico" #: f.widgets.cc:550 msgid "Color Depth" msgstr "Resolução de cor" #: f.widgets.cc:553 msgid "Brite Ramp" msgstr "Rampe de Brilho" #: f.widgets.cc:554 msgid "Paint Transp" msgstr "Transparência de Tinta" #: f.widgets.cc:555 msgid "Mirror" msgstr "Espelhar" #: f.widgets.cc:560 msgid "Perspective" msgstr "Perspectiva" #: f.widgets.cc:566 msgid "Flatten Book" msgstr "Nivelamento de Livro" #: f.widgets.cc:570 msgid "Sphere" msgstr "Esfera" #: f.widgets.cc:571 msgid "Stretch" msgstr "Esticar" #: f.widgets.cc:573 msgid "Tiny Planet" msgstr "Mini Planeta" #: f.widgets.cc:574 msgid "Escher Spiral" msgstr "Espiral de Escher" #: f.widgets.cc:579 msgid "Stack/Paint" msgstr "Pilha/Pintura" #: f.widgets.cc:580 msgid "Stack/Noise" msgstr "Pilha/Ruído" #: f.widgets.cc:581 msgid "Stack/Layer" msgstr "Pilha/Camada" #: f.widgets.cc:582 msgid "Stack/Slider" msgstr "Quadro Deslizante" #: f.widgets.cc:583 msgid "Image Diffs" msgstr "Diferença de Imagens" #: f.widgets.cc:584 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:585 msgid "V. Panorama" msgstr "V. Panorama" #: f.widgets.cc:586 msgid "PT Panorama" msgstr "Panorama PT" #: f.widgets.cc:587 msgid "Mashup" msgstr "Mistura" #: f.widgets.cc:588 msgid "Montage" msgstr "colagem" #: f.widgets.cc:594 msgid "Batch RAW" msgstr "RAW em Lote" #: f.widgets.cc:595 msgid "Burn DVD/BlueRay" msgstr "Gravar DVD/BlueRay" #: f.widgets.cc:596 msgid "Export File List" msgstr "Exportar Lista de Arquivos" #: f.widgets.cc:598 msgid "Batch Tags" msgstr "Etiquetas em Lote" #: f.widgets.cc:600 msgid "Batch Photo Date" msgstr "Datas de Foto em Lote" #: f.widgets.cc:601 msgid "Batch Change Meta" msgstr "Mudar Metadados em Lote" #: f.widgets.cc:602 msgid "Batch Report Meta" msgstr "Relatório de Metadados em Lote" #: f.widgets.cc:604 msgid "Edit Script" msgstr "Editar Rotina" #: f.widgets.cc:605 msgid "Run Script" msgstr "Executar Rotina" #: f.widgets.cc:609 msgid "Index Files" msgstr "Arquivos Índice" #: f.widgets.cc:610 msgid "Quick Index" msgstr "Índice Rápido" #: f.widgets.cc:611 msgid "Move Fotoxx Home" msgstr "Alternar Fonte do Fotoxx" #: f.widgets.cc:612 msgid "Preferences" msgstr "Preferências" #: f.widgets.cc:613 msgid "KB Shortcuts" msgstr "Atalhos de Teclado" #: f.widgets.cc:614 msgid "RGB Distribution" msgstr "Distribuição RGB" #: f.widgets.cc:616 msgid "Find Duplicates" msgstr "Encontrar Duplicados" #: f.widgets.cc:618 msgid "Color Profile" msgstr "Perfil de Cores" #: f.widgets.cc:622 msgid "Dark/Bright Pixels" msgstr "Pixels Claros/Escuros" #: f.widgets.cc:623 msgid "Map Pixel Bias" msgstr "Mapear Viés de Pixels" #: f.widgets.cc:624 msgid "Map Dead Pixels" msgstr "Mapear Pixels Mortos" #: f.widgets.cc:625 msgid "Monitor Color" msgstr "Cores do Monitor" #: f.widgets.cc:626 msgid "Monitor Gamma" msgstr "Monitor Gamma" #: f.widgets.cc:627 msgid "Change Language" msgstr "Trocar língua" #: f.widgets.cc:628 msgid "Missing Translations" msgstr "Traduções em falta" #: f.widgets.cc:629 zfuncs.cc:5887 msgid "Phone Home" msgstr "Ligar pra Casa" #: f.widgets.cc:631 msgid "Show Resources" msgstr "Mostrar Recursos" #: f.widgets.cc:632 msgid "Appimage Files" msgstr "Arquivos Appimage" #: f.widgets.cc:633 msgid "Zappcrash Test" msgstr "Testar Zappcrash" #: f.widgets.cc:652 msgid "Gallery" msgstr "Galeria" #: f.widgets.cc:653 msgid "Maps" msgstr "Mapas" #: f.widgets.cc:654 f.widgets.cc:1148 msgid "Favorites" msgstr "Favoritos" #: f.widgets.cc:655 msgid "Prev/Next" msgstr "Ant./Prox." #: f.widgets.cc:656 msgid "Zoom/±" msgstr "Zoom/±" #: f.widgets.cc:658 msgid "Meta" msgstr "Meta" #: f.widgets.cc:659 msgid "Areas" msgstr "Áreas" #: f.widgets.cc:660 msgid "Undo/Redo" msgstr "Desfazer/Refazer" #: f.widgets.cc:661 fotoxx.h:1352 msgid "Edit" msgstr "Editar" #: f.widgets.cc:662 msgid "Enhance" msgstr "Aprimorar" #: f.widgets.cc:663 msgid "Effects" msgstr "Efeitos" #: f.widgets.cc:665 msgid "Combine" msgstr "Combinar" #: f.widgets.cc:666 msgid "Process" msgstr "Processamento" #: f.widgets.cc:667 msgid "Tools" msgstr "Ferramentas" #: f.widgets.cc:818 msgid "Popup Image" msgstr "Imagem Popup" #: f.widgets.cc:828 msgid "Add Selected Files Here" msgstr "Adicionar Arquivos Selecionados Aqui" #: f.widgets.cc:829 msgid "Add Current File Here" msgstr "Adicionar Arquivo Atual Aqui" #: f.widgets.cc:830 msgid "Remove from Album" msgstr "Remover do Álbum" #: f.widgets.cc:840 msgid "Select Area" msgstr "Selecionar Área" #: f.widgets.cc:1108 f.widgets.cc:1127 msgid "Fotoxx Image Locations" msgstr "Fotoxx Localizações de imagem" #: f.widgets.cc:1176 #, c-format msgid "invalid menu name: %s" msgstr "nome de menu inválido: %s" #: fotoxx.cc:259 msgid "read+write" msgstr "leitura+escrita" #: fotoxx.cc:260 msgid "read only" msgstr "somente leitura" #: fotoxx.cc:261 msgid "no access" msgstr "sem acesso" #: fotoxx.cc:467 msgid "Please install missing programs:" msgstr "Por favor, instale os programas que faltam:" #: fotoxx.cc:596 msgid "Index aborted" msgstr "" #: fotoxx.cc:776 msgid " Defer image file indexing:" msgstr " Adiar indexação de arquivos de imagem:" #: fotoxx.cc:777 msgid "" " • Fotoxx will start immediately \n" " • View and edit image files will work normally \n" " • Image search, batch and map functions will not work \n" " • Thumbnail galleries will be slow" msgstr "" " • O Fotoxx iniciará imediatamente \n" " • Visão e edição de arquivos funcionará normalmente \n" " • Funções de busca de imagens, edição em lote e mapa não funcionarão \n" " • Miniaturas de Galerias carregarão lentamente" #: fotoxx.cc:782 msgid " Index image files now:" msgstr " Indexar arquivos de imagem agora:" #: fotoxx.cc:783 msgid "" " • Initial indexing may need considerable time \n" " • Subsequent startups will be fast \n" " • Full functionality will be available \n" " • Thumbnail galleries will be fast" msgstr "" " • A indexação inicial pode levar um tempo considerável \n" " • As inicializações subsequentes serão rápidas \n" " • Todas funções estarão disponíveis \n" " • Miniaturas de Galerias carregarão rapidamente" #: fotoxx.cc:788 msgid "" " Indexing time depends on the number of image files and the \n" " speed of your computer. This can be a few hundred to a few \n" " thousand per minute. After indexing is done, startup time \n" " should be quite fast. You can change index options later, \n" " using these menus: Tools > Index and Tools > Preferences. " msgstr "" " O tempo de indexação depende do número de arquivos de imagem e \n" " velocidade do seu computador. Ele pode processar centenas ou \n" " milhares por minuto. Após indexação, o tempo de inicialização \n" " deve ser bastante rápido. Você pode alterar as opções de indexação, \n" " usando estes menus: Ferramentas > Índice e Ferramentas > Preferências. " #: fotoxx.cc:795 msgid "" "Main memory is too small to run Fotoxx. \n" "You can try anyway if you wish. \n" " Continue?" msgstr "" #: fotoxx.cc:830 msgid "Fotoxx First Startup" msgstr "Primeira inicialização do Fotoxx" #: fotoxx.cc:1058 msgid "(reduced)" msgstr "(reduzido)" #: fotoxx.cc:1059 msgid "area active" msgstr "área ativada" #: fotoxx.cc:1060 msgid "dialog open" msgstr "caixa de diálogo aberta" #: fotoxx.cc:1061 msgid "blocked" msgstr "bloqueado" #: fotoxx.cc:1117 msgid "edits" msgstr "edições" #: fotoxx.cc:2035 msgid "Show Hidden" msgstr "Mostrar Ocultos" #: fotoxx.cc:3245 msgid "Exceed 50 anchor points" msgstr "Excedeu 50 pontos de ancoragem" #: fotoxx.cc:3474 msgid "load curve from a file" msgstr "carregar curva de arquivo" #: fotoxx.cc:3541 msgid "curve file is invalid" msgstr "arquivo de curva inválido" #: fotoxx.cc:3555 msgid "save curve to a file" msgstr "salvar curva para arquivo" #: fotoxx.cc:3626 #, c-format msgid "" "File cannot be edited \n" " %s" msgstr "" "Arquivo não pode ser editado \n" " %s" #: fotoxx.cc:3639 msgid "Too many edits, please save image" msgstr "Muitas edições, favor salvar imagem" #: fotoxx.cc:3644 msgid "this function cannot be scripted" msgstr "esta função não pode ser rotinada" #: fotoxx.cc:3661 msgid "" "Select area will be ignored. \n" "Continue?" msgstr "" "Seleção de área será ignorada. \n" "Continuar?" #: fotoxx.cc:3667 msgid "" "Select area not active.\n" "Continue?" msgstr "" "Área selecionada não ativa.\n" "Continuar?" #: fotoxx.cc:3977 msgid "file data does not fit dialog" msgstr "dados do arquivo não cabem em caixa de diálogo" #: fotoxx.cc:3987 msgid "Save settings to a file" msgstr "Salvar configurações em arquivo" #: fotoxx.cc:4374 msgid "This action will discard changes to current image" msgstr "Esta ação irá descartar as alterações para a imagem atual" #: fotoxx.cc:4375 msgid "prior function still active" msgstr "função prévia ainda ativa" #: fotoxx.cc:4376 fotoxx.h:1374 msgid "Keep" msgstr "Manter" #: fotoxx.cc:4377 msgid "Discard" msgstr "Descartar" #: fotoxx.h:1317 msgid "Add" msgstr "Adicionar" #: fotoxx.h:1318 msgid "Add All" msgstr "Adicionar todos" #: fotoxx.h:1320 msgid "Amount" msgstr "Quantidade" #: fotoxx.h:1321 msgid "Angle" msgstr "Ângulo" #: fotoxx.h:1322 zfuncs.cc:7940 msgid "Apply" msgstr "Aplicar" #: fotoxx.h:1323 msgid "Auto" msgstr "Auto" #: fotoxx.h:1324 msgid "Black" msgstr "Preto" #: fotoxx.h:1325 msgid "Blend Width" msgstr "Quantidade de mistura" #: fotoxx.h:1326 msgid "Blue" msgstr "Azul" #: fotoxx.h:1327 zfuncs.cc:12927 msgid "Bottom" msgstr "Rodapé" #: fotoxx.h:1329 zfuncs.cc:7954 msgid "Browse" msgstr "Procurar" #: fotoxx.h:1330 msgid "Calculate" msgstr "Calcular" #: fotoxx.h:1331 zfuncs.cc:7940 zfuncs.cc:12542 zfuncs.cc:12694 msgid "Cancel" msgstr "Cancelar" #: fotoxx.h:1332 msgid "Center" msgstr "centralizar" #: fotoxx.h:1333 msgid "Change" msgstr "Mudar" #: fotoxx.h:1334 msgid "Choose" msgstr "Escolher" #: fotoxx.h:1336 msgid "click thumbnail to select file" msgstr "clique na miniatura para selecionar arquivo" #: fotoxx.h:1337 msgid "Close" msgstr "Fechar" #: fotoxx.h:1338 msgid "Color" msgstr "Cor" #: fotoxx.h:1339 msgid "COMPLETED" msgstr "COMPLETO" #: fotoxx.h:1340 msgid "continue" msgstr "continuar" #: fotoxx.h:1343 msgid "Create" msgstr "Criar" #: fotoxx.h:1344 msgid "Curve File:" msgstr "Arquivo de curva:" #: fotoxx.h:1345 msgid "Cut" msgstr "Recortar" #: fotoxx.h:1346 msgid "Deband" msgstr "Debandar" #: fotoxx.h:1347 zfuncs.cc:7940 msgid "Delete" msgstr "Exlcuir" #: fotoxx.h:1349 msgid "Display" msgstr "Mostrar" #: fotoxx.h:1350 msgid "Done" msgstr "Pronto" #: fotoxx.h:1351 msgid "edge" msgstr "borda" #: fotoxx.h:1354 msgid "Erase" msgstr "Apagar" #: fotoxx.h:1355 msgid "Fetch" msgstr "Buscar" #: fotoxx.h:1356 msgid "output file already exists" msgstr "arquivo de saída já existe" #: fotoxx.h:1357 msgid "file not found" msgstr "arquivo não encontrado" #: fotoxx.h:1358 #, c-format msgid "file not found: %s" msgstr "arquivo não encontrado: %s" #: fotoxx.h:1359 #, c-format msgid "%d image files selected" msgstr "%d arquivos de imagem selecionados" #: fotoxx.h:1360 msgid "Find" msgstr "Encontrar" #: fotoxx.h:1361 msgid "Finish" msgstr "Finalizar" #: fotoxx.h:1363 msgid "Font" msgstr "Fonte" #: fotoxx.h:1364 #, c-format msgid "gallery truncated to %d images" msgstr "galeria truncada para %d imagens" #: fotoxx.h:1365 msgid "Green" msgstr "Verde" #: fotoxx.h:1366 msgid "Grid" msgstr "Grade" #: fotoxx.h:1367 zfuncs.cc:12957 msgid "Height" msgstr "Altura" #: fotoxx.h:1370 zfuncs.cc:12949 msgid "Image" msgstr "Imagem" #: fotoxx.h:1371 msgid "Images" msgstr "Imagens" #: fotoxx.h:1372 msgid "Insert" msgstr "Inserir" #: fotoxx.h:1375 zfuncs.cc:12931 msgid "Left" msgstr "Esquerda" #: fotoxx.h:1376 msgid "Length" msgstr "Comprimento" #: fotoxx.h:1377 msgid "limit" msgstr "limite" #: fotoxx.h:1379 msgid "Magnify" msgstr "Ampliar" #: fotoxx.h:1380 msgid "Make" msgstr "Gerar" #: fotoxx.h:1381 msgid "Map" msgstr "Mapa" #: fotoxx.h:1382 msgid "Match Level:" msgstr "Nível de Correspondência:" #: fotoxx.h:1383 msgid "Max" msgstr "Máximo" #: fotoxx.h:1384 msgid "Measure" msgstr "Mensurar" #: fotoxx.h:1385 msgid "mouse radius" msgstr "raio do cursor" #: fotoxx.h:1386 msgid "Negative" msgstr "Negativo" #: fotoxx.h:1387 msgid "New" msgstr "Novo" #: fotoxx.h:1388 msgid "Next" msgstr "Próximo" #: fotoxx.h:1389 zfuncs.cc:11880 msgid "No" msgstr "Não" #: fotoxx.h:1390 msgid "no write permission" msgstr "sem permissão para gravar" #: fotoxx.h:1391 msgid "no images" msgstr "não há imagens" #: fotoxx.h:1392 msgid "image index disabled" msgstr "" #: fotoxx.h:1393 msgid "no image files selected" msgstr "nenhum arquivo de imagem selecionado" #: fotoxx.h:1394 msgid "None" msgstr "Nenhum" #: fotoxx.h:1395 msgid "no selection" msgstr "não há seleção" #: fotoxx.h:1396 msgid "image index not updated" msgstr "índice de imagens não atualizado" #: fotoxx.h:1397 msgid "opacity center" msgstr "centro de opacidade" #: fotoxx.h:1398 msgid "opacity edge" msgstr "borda de opacidade" #: fotoxx.h:1400 msgid "Paint Radius" msgstr "Raio da Pintura" #: fotoxx.h:1402 msgid "Pause" msgstr "Pausar" #: fotoxx.h:1403 msgid "Percent" msgstr "Percentagem" #: fotoxx.h:1405 msgid "Presets" msgstr "Predefinições" #: fotoxx.h:1406 msgid "Prev" msgstr "Anterior" #: fotoxx.h:1407 msgid "use previous input" msgstr "usar entrada anterior" #: fotoxx.h:1408 msgid "Proceed" msgstr "Continuar" #: fotoxx.h:1411 msgid "range" msgstr "variação" #: fotoxx.h:1412 msgid "Red" msgstr "Vermelho" #: fotoxx.h:1413 msgid "Redo" msgstr "Refazer" #: fotoxx.h:1414 msgid "Reduce" msgstr "Reduzir" #: fotoxx.h:1415 msgid "Remove" msgstr "Remover" #: fotoxx.h:1417 msgid "Replace" msgstr "Substituir" #: fotoxx.h:1418 msgid "Reserved" msgstr "Reservados" #: fotoxx.h:1419 msgid "Reset" msgstr "Redefinir" #: fotoxx.h:1420 zfuncs.cc:12935 msgid "Right" msgstr "Direita" #: fotoxx.h:1421 msgid "Rotate" msgstr "Rotacionar" #: fotoxx.h:1422 msgid "Run" msgstr "Executar" #: fotoxx.h:1425 msgid "Seconds" msgstr "Segundos" #: fotoxx.h:1429 msgid "Size" msgstr "Tamanho" #: fotoxx.h:1430 msgid "Start" msgstr "Iniciar" #: fotoxx.h:1431 msgid "Stop" msgstr "Parar" #: fotoxx.h:1432 msgid "Strength" msgstr "Força" #: fotoxx.h:1433 msgid "the area is not finished" msgstr "a área não está finalizada" #: fotoxx.h:1434 msgid "Threshold" msgstr "Limiar" #: fotoxx.h:1435 zfuncs.cc:12923 msgid "Top" msgstr "Topo" #: fotoxx.h:1436 msgid "Transparency" msgstr "Transparência" #: fotoxx.h:1437 msgid "Trash" msgstr "Lixeira" #: fotoxx.h:1438 msgid "Trim" msgstr "Recortar" #: fotoxx.h:1439 msgid "Undo All" msgstr "Desfazer tudo" #: fotoxx.h:1440 msgid "Undo Last" msgstr "Desfazer último" #: fotoxx.h:1441 msgid "Undo" msgstr "Desfazer" #: fotoxx.h:1442 msgid "Unfinish" msgstr "Inacabado" #: fotoxx.h:1443 msgid "Update" msgstr "Atualizar" #: fotoxx.h:1444 msgid "View" msgstr "Ver" #: fotoxx.h:1445 msgid "Web" msgstr "Web" #: fotoxx.h:1446 msgid "White" msgstr "Branco" #: fotoxx.h:1447 zfuncs.cc:12953 msgid "Width" msgstr "Largura" #: fotoxx.h:1448 msgid "x-offset" msgstr "deslocamento-x" #: fotoxx.h:1449 msgid "y-offset" msgstr "deslocamento-y" #: fotoxx.h:1450 zfuncs.cc:11880 msgid "Yes" msgstr "Sim" #: zfuncs.cc:1836 #, c-format msgid "" "create folder? \n" " %s" msgstr "" "criar diretório? \n" " %s" #: zfuncs.cc:5882 msgid "" "If you permit, a message is occasionally \n" "sent to the web host for usage statistics. \n" "Nothing is retained that can be associated \n" "with a person or computer or location." msgstr "" "Se você permitir, uma mensagem será ocasionalmente \n" "enviada ao servidor web para estatísticas de uso. \n" "Nada é mantido para que possa ser associado \n" "a uma pessoa ou computador ou local." #: zfuncs.cc:6747 #, c-format msgid "cannot open file %s" msgstr "não foi possível abrir arquivo %s" #: zfuncs.cc:6770 msgid "save text to file" msgstr "salvar texto para arquivo" #: zfuncs.cc:7940 msgid "edit menu entry" msgstr "editar item de menu" #: zfuncs.cc:7944 msgid "menu text" msgstr "texto do menu" #: zfuncs.cc:7945 msgid "menu func" msgstr "função do menu" #: zfuncs.cc:7946 msgid "menu icon" msgstr "ícone do menu" #: zfuncs.cc:7947 msgid "icon size" msgstr "tamanho do ícone" #: zfuncs.cc:7950 msgid "Bold" msgstr "Negrito" #: zfuncs.cc:7957 msgid "close window" msgstr "fechar janela" #: zfuncs.cc:8004 msgid "select icon" msgstr "selecionar ícone" #: zfuncs.cc:12014 zfuncs.cc:12910 msgid "cancel" msgstr "cancelar" #: zfuncs.cc:12503 msgid "choose file" msgstr "escolher arquivo" #: zfuncs.cc:12508 msgid "choose files" msgstr "escolher arquivos" #: zfuncs.cc:12519 msgid "choose folder" msgstr "escolher diretório" #: zfuncs.cc:12524 msgid "choose folders" msgstr "escolher diretórios" #: zfuncs.cc:12529 msgid "create folder" msgstr "criar pasta" #: zfuncs.cc:12536 msgid "hidden" msgstr "oculto" #: zfuncs.cc:12910 msgid "done" msgstr "pronto" #: zfuncs.cc:12940 msgid "image scale" msgstr "escala de imagem" #: zfuncs.cc:12942 msgid "percent" msgstr "porcentagem" #~ msgid "Select 2 files:" #~ msgstr "Selecione 2 arquivos:" #~ msgid "user guide not found" #~ msgstr "guia de usuário não encontrado" #~ msgid "Paste Cache Here (keep)" #~ msgstr "Colar Reserva Aqui (manter)" #~ msgid "Paste Cache Here (clear)" #~ msgstr "Colar Reserva Aqui (esvaziar)" #~ msgid "Cut to Cache" #~ msgstr "Recortar para Reserva" #~ msgid "Edit 2" #~ msgstr "Editar 2" #~ msgid "Edit 1" #~ msgstr "Editar 1" #~ msgid "Brightness Graph" #~ msgstr "Gráfico de Brilho" #~ msgid "Anti-Alias" #~ msgstr "Antisserrilhado" #~ msgid "Zonal Colors" #~ msgstr "Cores Zonais" #~ msgid "Add Lines" #~ msgstr "Adicionar Linhas" #~ msgid "Color Sat" #~ msgstr "Sat. de Cor" #~ msgid "Map Markers" #~ msgstr "Marcadores de Mapa" #~ msgid "Net Map Locs" #~ msgstr "Locais no Mapa Net" #~ msgid "Map View" #~ msgstr "Visão Mapa" #~ msgid "Zoom-" #~ msgstr "Zoom-" #~ msgid "Zoom+" #~ msgstr "Zoom+" #~ msgid "Gallery View" #~ msgstr "Visão Galeria" #~ msgid "Copy to Cache" #~ msgstr "Copiar para Reserva" #~ msgid "Set as Wallpaper" #~ msgstr "Configurar como Plano de Fundo" #~ msgid "File View" #~ msgstr "Visão de Arquivo" #~ msgid "Version, license, contact, credits" #~ msgstr "Versão, licença, contato, créditos" #~ msgid "Erase known hot and dark pixels" #~ msgstr "Apagar pixels quentes ou escuros conhecidos" #~ msgid "Show a brightness distribution graph" #~ msgstr "Mostrar gráfico de distribuição de brilho" #~ msgid "Change Keyboard Shortcut Keys" #~ msgstr "Mudar Atalhos de Teclado" #~ msgid "Change user preferences" #~ msgstr "Mudar preferências de usuário" #~ msgid "Convert camera RAW files using dcraw or Raw Therapee" #~ msgstr "Converter arquivos RAW da câmera usando dcraw ou Raw Therapee" #~ msgid "Convert to solid color drawing" #~ msgstr "Converter em desenho de cor sólida" #~ msgid "Smoothen edges with jaggies" #~ msgstr "Suavisar contornos irregulares" #~ msgid "Reduce Chromatic Aberration" #~ msgstr "Rerrigir Anomalias Cromáticas" #~ msgid "Adjust color in selected image areas" #~ msgstr "Ajustar cor em áreas selecionadas da imagem" #~ msgid "Flatten brightness distribution" #~ msgstr "Nivelar a distribuição de brilho" #~ msgid "Edit brightness distributions" #~ msgstr "Editar distribuições de brilho" #~ msgid "Leverage edits by brightness or color" #~ msgstr "Influenciar edições por brilho ou cor" #~ msgid "Upright a rotated image" #~ msgstr "Orientar imagem rotacionada" #~ msgid "Adjust color intensity (saturation)" #~ msgstr "Ajustar intensidade de cor (saturação)" #~ msgid "Adjust color balance" #~ msgstr "Ajustar balanço de cores" #~ msgid "Find images meeting select criteria" #~ msgstr "Encontrar imagens satisfazendo critério selecionado" #~ msgid "(Toggle) show captions and comments" #~ msgstr "(Comutativo) mostrar legendas e comentários" #~ msgid "Mark all images or current gallery" #~ msgstr "Marcar todas imagens ou galeria atual" #~ msgid "Maps (key M)" #~ msgstr "Mapas (tecla M)" #~ msgid "Show current or last used album" #~ msgstr "Mostrar álbum mais recente ou atual" #~ msgid "Smaller thumbnails [-]" #~ msgstr "Miniaturas menores [-]" #~ msgid "Bigger thumbnails [+]" #~ msgstr "Miniatura maiores [+]" #~ msgid "Thumbnail Gallery (key G)" #~ msgstr "Miniatura de Galeria (tecla G)" #~ msgid "Copy file to the image cache" #~ msgstr "Copiar imagem para a reserva de imagens" #~ msgid "Open a recently added file" #~ msgstr "Abrir arquivo adicionado recentemente" #~ msgid "Open a recently seen file" #~ msgstr "Abrir um arquivo visto recentemente" #~ msgid "Current File (key F)" #~ msgstr "Arquivo atual (tecla F)" #~ msgid "User Guide, Recent Changes ..." #~ msgstr "Guia de Usuário, Mudanças Recentes ..." #~ msgid "Index, Settings, Shortcuts, Magnify, utilities ..." #~ msgstr "Índice, Configurações, Atalhos, Ampliar, utilitários ..." #~ msgid "Batch Convert, Metadata, Scripts ..." #~ msgstr "Conversões em Lote, Metadados, Rotinas ..." #~ msgid "HDR, HDF, Panorama, Stack, Mashup" #~ msgstr "HDR, HDF, Panorama, Empilhamento, Mistura" #~ msgid "Arty Transforms (filters)" #~ msgstr "Transformações Artísticas (filtros)" #~ msgid "Perspective, Warp, Unwarp, Transfigure ..." #~ msgstr "Perspectiva, Torção, Distorção, Transfigurar ..." #~ msgid "Image improvements, Paint, Copy ..." #~ msgstr "Melhorar imagem, Pintar, Copiar ..." #~ msgid "Trim, Rotate, Resize, Retouch, Sharpen, Color, Text ..." #~ msgstr "Aparar, Rotacionar, Redimensionar, Retoque, Aguçar, Cor, Texto ..." #~ msgid "" #~ "left/right click: undo/redo 1 edit \n" #~ " with key A: undo/redo all edits \n" #~ " middle click: go to any prior edit" #~ msgstr "" #~ "clique esquerdo/direito: desfazer/refazer 1 edição \n" #~ " com tecla A: desfazer/refazer todas edições \n" #~ " clique no meio: ir a qualquer edição anterior" #~ msgid "Select image areas to edit, save, copy and paste ..." #~ msgstr "Selecionar áreas da imagem para editar, salvar, copiar e colar ..." #~ msgid "Captions, Tags, Ratings, Geotags, Search ... " #~ msgstr "Legendas, Etiquetas, Avaliações, Georreferências, Busca..." #~ msgid "Open previous or next file (left/right mouse click)" #~ msgstr "Abrir arquivo prévio ou próximo (clique direito/esquerdo do cursor)" #~ msgid "Favorite Functions" #~ msgstr "Funções Favoritas" #~ msgid "Gallery, Bookmarks, Albums, Slide Show" #~ msgstr "Galeria, Favoritos, Álbums, Apresentações" #~ msgid "Open, Recent, Rename, Copy, Delete, Print ..." #~ msgstr "Abrir, Recentes, Renomear, Copiar, Excluir, Imprimir ..." #~ msgid "Stuck Pixels file" #~ msgstr "Arquvio de Pixels Presos" #~ msgid "Load Stuck Pixels" #~ msgstr "Carregar Pixels Presos" #~ msgid "stuck pixels:" #~ msgstr "pixels presos:" #~ msgid "pixel group" #~ msgstr "grupo de pixels" #~ msgid "Stuck Pixels" #~ msgstr "Pixels Presos" #~ msgid " Arrows Previous/Next Image \n" #~ msgstr "Setas Prévia/Próxima\n" #~ msgid " Delete Delete/Trash dialog \n" #~ msgstr " Delete Diálogo de Exclusão/Lixeira \n" #~ msgid " Escape Quit dialog; Quit Fotoxx \n" #~ msgstr " Esc Fechar diálogo; Fechar Fotoxx \n" #~ msgid " F11 Full Screen without menus \n" #~ msgstr " F11 Tela cheia sem menus \n" #~ msgid " F10 Full Screen with menus \n" #~ msgstr " F10 Tela cheio com menus \n" #~ msgid " F1 User Guide for current function \n" #~ msgstr " F1 Guia do usuário para a função atual \n" #~ msgid "video file types" #~ msgstr "tipos de arquivo de vídeo" #~ msgid "RAW file types" #~ msgstr "Tipos de arquivos RAW" #~ msgid "Fotoxx started by file manager" #~ msgstr "Fotoxx iniciado por gerenciador de arquivos" #~ msgid "Fotoxx started directly" #~ msgstr "Fotoxx iniciado diretamente" #~ msgid "image index level" #~ msgstr "nível de índice de imagens" #~ msgid "shift image to right margin" #~ msgstr "deslocar imagem para margem direita" #~ msgid "show last file version only" #~ msgstr "mostrar apenas a última versão do arquivo" #~ msgid "Map marker size" #~ msgstr "Tamanho do marcador de mapa" #~ msgid "Curve node capture distance" #~ msgstr "Distância de captura de nó da curva" #~ msgid "JPEG save quality" #~ msgstr "Qualidade para JPEG" #~ msgid "Dialog font" #~ msgstr "Fonte de diálogo" #~ msgid "Menu Style" #~ msgstr "Estilo de Menu" #~ msgid "Menu Text" #~ msgstr "Texto do menu" #~ msgid "Background color:" #~ msgstr "Cor de plano de fundo:" #~ msgid "Startup Display" #~ msgstr "Exibição Inicial" #~ msgid "User Settings" #~ msgstr "Configurações de Usuário" #~ msgid "" #~ "script file: %s \n" #~ " %s" #~ msgstr "" #~ "arquivo de rotina: %s \n" #~ " %s" #~ msgid "use Raw Therapee" #~ msgstr "usar Raw Therapee" #~ msgid "use dcraw" #~ msgstr "usar dcraw" #~ msgid "file format not supported: %s \n" #~ msgstr "formato de arquivo não suportado: %s \n" #~ msgid "file error: %s \n" #~ msgstr "arquivo de erro: %s \n" #~ msgid "Set Map Source" #~ msgstr "Definir Fonte de Mapa" #~ msgid "no changes made" #~ msgstr "nenhuma mudança feita" #~ msgid "images added: %d removed: %d new count: %d" #~ msgstr "imagens adicionadas: %d removidas: %d nova contagem: %d" #~ msgid "all versions of matching images" #~ msgstr "todas as versões de imagens correspondentes" #~ msgid "new set" #~ msgstr "nova configuração" #~ msgid "Batch Photo date/time Update" #~ msgstr "Atualizar data/hora de Fotos em Lote" #~ msgid "create tag categories and tags" #~ msgstr "criar etiquetas e categorias de etiqueta" #~ msgid "montage map file not found: %s" #~ msgstr "arquivo mapa de colagem não encontrado: %s" #~ msgid "supply a project name" #~ msgstr "forneça um nome para o projeto" #~ msgid "project name" #~ msgstr "nome do projeto" #~ msgid "Mashup layout and background image" #~ msgstr "Leiaute de Mistura e imagem de fundo" #~ msgid "select thumbnail" #~ msgstr "selecionar miniatura" #~ msgid "Image Pixels" #~ msgstr "Pixels de Imagem" #~ msgid "exceeded 200 albums" #~ msgstr "ultrapassado 200 álbuns" #~ msgid "no more images" #~ msgstr "imagens esgotadas" #~ msgid "outside top folders" #~ msgstr "fora de diretórios raiz" #~ msgid "RAW type not registered in User Settings" #~ msgstr "Tipo RAW não reconhecido nas Configurações de Usuário" #~ msgid "Simulate Painting" #~ msgstr "Simular Pintura" #~ msgid "load palette first" #~ msgstr "carregue a paleta primeiro" #~ msgid "custom palette" #~ msgstr "paleta customizada" #~ msgid "depth" #~ msgstr "profundidade" #~ msgid "Simulate Embossing" #~ msgstr "Simular Gravação em relevo" #~ msgid "Bright Areas" #~ msgstr "Áreas claras" #~ msgid "Color Drawing" #~ msgstr "Desenho de Cores" #~ msgid "Color Fringes" #~ msgstr "Franjas Púrpuras" #~ 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. " #~ msgid "Metric:" #~ msgstr "Métrica:" #~ msgid "Local Color" #~ msgstr "Cor Local" #~ msgid "Click image to select areas." #~ msgstr "Clique na imagem para selecionar áreas." #~ msgid "image is too large" #~ msgstr "imagem é grande demais" #~ msgid "maximum" #~ msgstr "máximo" #~ msgid "minimum" #~ msgstr "mínimo" #~ msgid "Cannot use Lever Edits" #~ msgstr "Impossível usar Edições Alavancadas" #~ msgid "Edit Function Amplifier" #~ msgstr "Editar função Amplificador" #~ msgid "Lever Edits" #~ msgstr "Alavancar Edições" #~ msgid "Upright Image" #~ msgstr "Orientar Imagem" #~ msgid "Add lines or arrows to an image" #~ msgstr "Adicionar linhas ou setas a uma imagem" #~ msgid "Add Text to Image" #~ msgstr "Adicionar texto à Imagem" #~ msgid "Color Saturation" #~ msgstr "Saturação de Cor" #~ msgid "Wavelets" #~ msgstr "Wavelets" #~ msgid "Top Hat" #~ msgstr "Cartola" #~ msgid "Median" #~ msgstr "Mediano" #~ msgid "use previous white balance" #~ msgstr "usar balanço de branco anterior" #~ msgid "Cooler" #~ msgstr "Mais frio" #~ msgid "Warmer" #~ msgstr "Mais quente" #~ msgid "Color Balance" #~ msgstr "Balanço de Cores" #~ msgid "recall previous settings used" #~ msgstr "recolocar configurações prévias utilizadas" #~ msgid "Bright" #~ msgstr "Brilho" #~ msgid "High" #~ msgstr "Alto" #~ msgid "Max." #~ msgstr "Máx." #~ msgid "Dark Areas" #~ msgstr "Áreas Escuras" #~ msgid "Low Color" #~ msgstr "Cor baixa" #~ msgid "Amplifier" #~ msgstr "Amplificador" #~ msgid "use previous settings" #~ msgstr "usar configurações prévias" #~ msgid "invert width/height" #~ msgstr "inverter altura/largura" #~ msgid "use previous size" #~ msgstr "usar tamanho anteior" #~ msgid "select color in mouse:" #~ msgstr "selecionar com o cursor e cor:" #~ msgid "Show image comments" #~ msgstr "Mostrar comentários de imagem" #~ msgid "Show image caption" #~ msgstr "Mostrar legenda de imagem" #~ msgid "Wait before caption/comments" #~ msgstr "Atraso anterior a legenda/comentários" #~ msgid "use gallery" #~ msgstr "usar galeria" #~ msgid "Replace all with original and cache versions" #~ msgstr "Substituir todos com a original e versão de reserva" #~ msgid "Add cache versions to existing versions" #~ msgstr "Adicionar verões reservadas a versões existentes" #~ msgid "Replace all versions with cache versions" #~ msgstr "Substituir todas versões com versões em reserva" #~ msgid "Replace all with cache versions" #~ msgstr "Substituir todos com versões reservadas" #~ msgid "Process album files matching cache files:" #~ msgstr "Processar arquivos de álbum correspondentes a arquivos de reserva:" #~ msgid "remove %d files from %d albums?" #~ msgstr "remover %d arquivos de %d albums?" #~ msgid "cache is empty" #~ msgstr "reserva está vazia" #~ msgid "Choose Album" #~ msgstr "Escolher Álbum" #~ msgid "max. album size exceeded %d" #~ msgstr "excedeu tamanho máx. de álbum %d" #~ msgid "fill from image cache (%d images)" #~ msgstr "preencher com imagens reservadas (%d imagens)" #~ msgid "Image cache has %d images" #~ msgstr "Reserva de imagem tem %d imagens" #~ msgid "Replace album files or add after" #~ msgstr "Substituir arquivo de álbum ou adicionar depois" #~ msgid "Clear the file cache" #~ msgstr "Limpar reserva de arquivos" #~ msgid "Remove cache files from albums" #~ msgstr "Remover arquivos reservados de álbuns" #~ msgid "Display cache for drag and drop" #~ msgstr "Mostrar reserva para arrastar e soltar" #~ msgid "Select files, add to cache" #~ msgstr "Selecionar arquivos, adicionar a reserva" #~ msgid "Choose album to view or edit" #~ msgstr "Escolher um álbum para ver ou editar" #~ msgid "Drag album thumbnail to new position." #~ msgstr "Arrastar miniatura de álbum" #~ msgid "" #~ "Right-click thumbnail left/right side \n" #~ "to insert cache before/after thumbnail." #~ msgstr "" #~ "Clique com o botão direito no lado direito/esquerdo da miniatura \n" #~ "para inserir a reserva antes/depois da miniatura." #~ msgid "" #~ "Right-click album thumbnail to \n" #~ "cut or copy to cache, or remove." #~ msgstr "" #~ "Clique com o botão direito na miniatura do álbum \n" #~ "para recortar ou colar na reserva, ou excluir." #~ msgid "" #~ "image index disabled \n" #~ " Enable?" #~ msgstr "" #~ "índice de imagens desativado \n" #~ " Ativar?" #~ msgid "Left/right click: smaller/larger image/thumbnails" #~ msgstr "Clique esquerdo/direito: imagem/miniaturas menor/maior" #~ msgid "%% of scale" #~ msgstr "%% de escala" #~ msgid "" #~ "fotoxx_maps package not installed \n" #~ "(see https://kornelix.net)" #~ msgstr "" #~ "pacote fotoxx_maps não instalado \n" #~ "(ver https://kornelix.net)" fotoxx-20.08/locales/translate-xx.po000066400000000000000000003166661362435004500175120ustar00rootroot00000000000000# TEMPLATE FOR NEW LANGUAGE TRANSLATION # REPLACE 'xx' IN FILE NAME WITH LANGUAGE CODE # -------------------------------------------- # #: f.albums.cc:59 #, c-format msgid "max. album size exceeded: %d" msgstr "" #: f.albums.cc:70 msgid "" "Right-click album thumbnail for edits: \n" " - add selected files at this position \n" " - remove this file from the album" msgstr "" #: f.albums.cc:73 msgid "Arrange files with thumbnail drag and drop" msgstr "" #: f.albums.cc:102 f.widgets.cc:458 msgid "Manage Albums" msgstr "" #: f.albums.cc:113 f.albums.cc:232 msgid "Create or replace an album" msgstr "" #: f.albums.cc:115 f.albums.cc:381 msgid "Rename an album" msgstr "" #: f.albums.cc:117 f.albums.cc:451 msgid "Delete an album" msgstr "" #: f.albums.cc:119 msgid "Files to add to an album" msgstr "" #: f.albums.cc:183 #, c-format msgid "%d files added to album %s" msgstr "" #: f.albums.cc:213 #, c-format msgid "max. album count exceeded: %d" msgstr "" #: f.albums.cc:234 f.albums.cc:270 f.albums.cc:1537 msgid "Album Name" msgstr "" #: f.albums.cc:240 msgid "make an initially empty album" msgstr "" #: f.albums.cc:241 msgid "fill from pre-selected files" msgstr "" #: f.albums.cc:242 msgid "fill from current gallery" msgstr "" #: f.albums.cc:243 msgid "select initial files" msgstr "" #: f.albums.cc:284 msgid "enter an album name" msgstr "" #: f.albums.cc:292 f.albums.cc:1569 #, c-format msgid "replace album %s ?" msgstr "" #: f.albums.cc:312 msgid "Use [Select] to add files to an empty album" msgstr "" #: f.albums.cc:326 msgid "new album created" msgstr "" #: f.albums.cc:342 f.meta.cc:7832 msgid "gallery is empty" msgstr "" #: f.albums.cc:396 msgid "enter new album name" msgstr "" #: f.albums.cc:403 #, c-format msgid "invalid file name: %s" msgstr "" #: f.albums.cc:424 #, c-format msgid "album already exists: %s" msgstr "" #: f.albums.cc:435 #, c-format msgid "" "%s \n" " renamed: %s" msgstr "" #: f.albums.cc:464 #, c-format msgid "delete %s ?" msgstr "" #: f.albums.cc:571 f.albums.cc:584 #, c-format msgid "%s removed with no replacement \n" msgstr "" #: f.albums.cc:589 #, c-format msgid "%s replaced by ...%s \n" msgstr "" #: f.albums.cc:664 msgid "no selected files" msgstr "" #: f.albums.cc:877 msgid "Replace Album File" msgstr "" #: f.albums.cc:879 f.albums.cc:1004 msgid "Choose Albums" msgstr "" #: f.albums.cc:883 msgid "old file" msgstr "" #: f.albums.cc:886 msgid "new file" msgstr "" #: f.albums.cc:889 msgid "replace old" msgstr "" #: f.albums.cc:890 msgid "add after old" msgstr "" #: f.albums.cc:936 msgid "no albums chosen" msgstr "" #: f.albums.cc:1117 f.widgets.cc:460 msgid "Album Mass Update" msgstr "" #: f.albums.cc:1125 msgid "Process all album files:" msgstr "" #: f.albums.cc:1128 msgid "Replace all with newest version only" msgstr "" #: f.albums.cc:1130 msgid "Replace all versions with newest version" msgstr "" #: f.albums.cc:1132 msgid "Add newest version to existing versions" msgstr "" #: f.albums.cc:1134 msgid "Replace all with original and all versions" msgstr "" #: f.albums.cc:1136 msgid "Replace all with original + newest version" msgstr "" #: f.albums.cc:1140 msgid "Process album files matching selected files:" msgstr "" #: f.albums.cc:1143 msgid "Replace all with selected versions" msgstr "" #: f.albums.cc:1145 msgid "Replace all versions with selected versions" msgstr "" #: f.albums.cc:1147 msgid "Add selected versions to existing versions" msgstr "" #: f.albums.cc:1149 msgid "Replace all with original + selected versions" msgstr "" #: f.albums.cc:1237 msgid "Select Albums" msgstr "" #: f.albums.cc:1296 #, c-format msgid "too many selected files: %d" msgstr "" #: f.albums.cc:1535 msgid "Save Gallery as Album" msgstr "" #: f.albums.cc:1739 msgid "instant" msgstr "" #: f.albums.cc:1740 msgid "fade-in" msgstr "" #: f.albums.cc:1741 msgid "roll-right" msgstr "" #: f.albums.cc:1742 msgid "roll-down" msgstr "" #: f.albums.cc:1743 msgid "venetian" msgstr "" #: f.albums.cc:1744 msgid "grate" msgstr "" #: f.albums.cc:1745 msgid "rectangle" msgstr "" #: f.albums.cc:1746 msgid "implode" msgstr "" #: f.albums.cc:1747 msgid "explode" msgstr "" #: f.albums.cc:1748 msgid "radar" msgstr "" #: f.albums.cc:1749 msgid "Japan-fan" msgstr "" #: f.albums.cc:1750 msgid "spiral" msgstr "" #: f.albums.cc:1751 f.area.cc:140 msgid "ellipse" msgstr "" #: f.albums.cc:1752 msgid "raindrops" msgstr "" #: f.albums.cc:1753 msgid "doubledoor" msgstr "" #: f.albums.cc:1754 msgid "rotate" msgstr "" #: f.albums.cc:1755 msgid "fallover" msgstr "" #: f.albums.cc:1756 msgid "spheroid" msgstr "" #: f.albums.cc:1757 msgid "turn-page" msgstr "" #: f.albums.cc:1758 msgid "french-door" msgstr "" #: f.albums.cc:1759 msgid "turn-cube" msgstr "" #: f.albums.cc:1760 msgid "windmill" msgstr "" #: f.albums.cc:1761 msgid "pixelize" msgstr "" #: f.albums.cc:1762 msgid "twist" msgstr "" #: f.albums.cc:1763 msgid "Xopen" msgstr "" #: f.albums.cc:1764 msgid "squish" msgstr "" #: f.albums.cc:1765 msgid "disintegrate" msgstr "" #: f.albums.cc:1766 msgid "interleave" msgstr "" #: f.albums.cc:1797 f.widgets.cc:462 msgid "Slide Show" msgstr "" #: f.albums.cc:1800 msgid "Select Album" msgstr "" #: f.albums.cc:1805 msgid "Caption Time" msgstr "" #: f.albums.cc:1808 msgid "Image Time" msgstr "" #: f.albums.cc:1811 msgid "Clip Limit %" msgstr "" #: f.albums.cc:1815 msgid "Music File" msgstr "" #: f.albums.cc:1820 msgid "Full Screen" msgstr "" #: f.albums.cc:1821 msgid "Auto-replay" msgstr "" #: f.albums.cc:1824 msgid "Customize:" msgstr "" #: f.albums.cc:1825 msgid "transitions" msgstr "" #: f.albums.cc:1826 msgid "image files" msgstr "" #: f.albums.cc:1827 msgid "KB controls" msgstr "" #: f.albums.cc:1841 f.albums.cc:1908 f.albums.cc:2141 f.albums.cc:2450 #: f.albums.cc:2786 f.albums.cc:2803 f.albums.cc:3039 msgid "invalid album" msgstr "" #: f.albums.cc:1936 msgid "open album" msgstr "" #: f.albums.cc:1948 msgid "Select music file" msgstr "" #: f.albums.cc:1983 #, c-format msgid "%d images" msgstr "" #: f.albums.cc:2008 msgid "" "arrow keys show previous or next image instantly \n" "space bar (blank) is allowed and shows as '-'" msgstr "" #: f.albums.cc:2027 msgid "Keyboard Preferences" msgstr "" #: f.albums.cc:2030 msgid "blank or unblank window" msgstr "" #: f.albums.cc:2033 msgid "show next image, with transition" msgstr "" #: f.albums.cc:2036 msgid "pause or resume slide show" msgstr "" #: f.albums.cc:2039 msgid "magnify image (loupe tool)" msgstr "" #: f.albums.cc:2145 msgid "Transition Preferences" msgstr "" #: f.albums.cc:2147 msgid "Transitions File" msgstr "" #: f.albums.cc:2156 msgid "random" msgstr "" #: f.albums.cc:2159 f.albums.cc:2175 f.albums.cc:2179 msgid "time" msgstr "" #: f.albums.cc:2161 msgid "set all" msgstr "" #: f.albums.cc:2173 f.albums.cc:2177 msgid "transition" msgstr "" #: f.albums.cc:2174 f.albums.cc:2178 msgid "use" msgstr "" #: f.albums.cc:2176 f.albums.cc:2180 msgid "pref" msgstr "" #: f.albums.cc:2280 f.albums.cc:2322 msgid "invalid file" msgstr "" #: f.albums.cc:2394 #, c-format msgid "" "file format error: \n" " %s" msgstr "" #: f.albums.cc:2456 msgid "Image Preferences" msgstr "" #: f.albums.cc:2460 f.file.cc:1610 f.file.cc:1995 msgid "Image File:" msgstr "" #: f.albums.cc:2464 msgid "Action" msgstr "" #: f.albums.cc:2476 msgid "Play tone when image shows" msgstr "" #: f.albums.cc:2481 msgid "Wait before filename/caption/comments" msgstr "" #: f.albums.cc:2485 msgid "Show image file name (overlap)" msgstr "" #: f.albums.cc:2489 msgid "Show image caption (overlap)" msgstr "" #: f.albums.cc:2493 msgid "Show image comments (overlap)" msgstr "" #: f.albums.cc:2497 msgid "Wait before zoom" msgstr "" #: f.albums.cc:2501 f.effects.cc:4023 msgid "Zoom" msgstr "" #: f.albums.cc:2505 msgid "zoom-in" msgstr "" #: f.albums.cc:2506 msgid "zoom-out" msgstr "" #: f.albums.cc:2510 msgid "Zoom Center" msgstr "" #: f.albums.cc:2515 msgid "Wait after zoom" msgstr "" #: f.albums.cc:2521 msgid "Transition to next image" msgstr "" #: f.albums.cc:2524 f.albums.cc:2654 msgid "next" msgstr "" #: f.albums.cc:2644 msgid "click on thumbnail to set zoom center" msgstr "" #: f.area.cc:93 msgid "Select Area for Edits" msgstr "" #: f.area.cc:94 f.area.cc:1890 f.edit.cc:8198 msgid "Press F1 for help" msgstr "" #: f.area.cc:104 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" #: f.area.cc:139 msgid "select rectangle" msgstr "" #: f.area.cc:143 msgid "freehand draw" msgstr "" #: f.area.cc:144 msgid "follow edge" msgstr "" #: f.area.cc:147 msgid "draw/replace" msgstr "" #: f.area.cc:150 msgid "Line Color:" msgstr "" #: f.area.cc:164 msgid "match level %" msgstr "" #: f.area.cc:168 msgid "search range" msgstr "" #: f.area.cc:172 msgid "select area in mouse" msgstr "" #: f.area.cc:175 msgid "select one color in mouse:" msgstr "" #: f.area.cc:179 msgid "select all colors in mouse (flood)" msgstr "" #: f.area.cc:184 msgid "Area Edge Blend Width" msgstr "" #: f.area.cc:188 msgid "Edge Creep" msgstr "" #: f.area.cc:200 msgid "drag mouse to select rectangular area" msgstr "" #: f.area.cc:201 msgid "drag mouse to select circular or elliptical area" msgstr "" #: f.area.cc:202 msgid "drag mouse to outline an area" msgstr "" #: f.area.cc:203 msgid "drag mouse along an edge to follow the edge" msgstr "" #: f.area.cc:204 msgid "drag mouse near a line to move the line" msgstr "" #: f.area.cc:205 msgid "select line color" msgstr "" #: f.area.cc:206 msgid "size of mouse selection circle" msgstr "" #: f.area.cc:207 msgid "required match level to select by color" msgstr "" #: f.area.cc:208 msgid "select by color search range" msgstr "" #: f.area.cc:209 msgid "select area within mouse circle" msgstr "" #: f.area.cc:210 msgid "" "first select the checkbox, then \n" "shift+click on image to set the color" msgstr "" #: f.area.cc:212 msgid "select surrounding areas matching colors in mouse" msgstr "" #: f.area.cc:213 f.area.cc:214 msgid "area edits fade away within edge distance" msgstr "" #: f.area.cc:215 msgid "move area boundary in/out in 1-pixel steps" msgstr "" #: f.area.cc:216 msgid "map selected areas and verify" msgstr "" #: f.area.cc:217 msgid "invert area" msgstr "" #: f.area.cc:218 msgid "show area outlines" msgstr "" #: f.area.cc:219 msgid "hide area outlines" msgstr "" #: f.area.cc:220 msgid "clear area selections" msgstr "" #: f.area.cc:404 f.area.cc:570 #, c-format msgid "exceed %d edits" msgstr "" #: f.area.cc:1471 msgid "" "Fill selected areas with color for visual verification. \n" "Method 1: left-click in each selected area not already filled. \n" "Method 2: right-click OUTSIDE all selected areas. \n" "Press [help] button for clarification" msgstr "" #: f.area.cc:1499 msgid "finish area" msgstr "" #: f.area.cc:1528 f.area.cc:1757 #, c-format msgid "found %d pixels" msgstr "" #: f.area.cc:1546 msgid "" "Method 1: \n" " Left-click inside an outlined area that is not already filled. \n" " Area will be filled with color for visible verification. \n" " Gaps in the outline will cause overflow outside the area. \n" " Repeat for each outlined area that is not already filled. \n" "Method 2: \n" " Right-click outside ALL outlined areas. \n" " All areas will be filled with color for visible verification. \n" " Gaps in an area outline will cause that area not to be filled. \n" "Gaps in an area outline: \n" " Gaps must be closed before proceeding with edits. \n" " The Find Gap function can be used for this." msgstr "" #: f.area.cc:1889 f.widgets.cc:487 msgid "Select Hairy" msgstr "" #: f.area.cc:1899 msgid "select the area first" msgstr "" #: f.area.cc:1975 msgid "select" msgstr "" #: f.area.cc:1981 msgid "deselect" msgstr "" #: f.area.cc:2335 msgid "" "Click near any position on the area outline. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" #: f.area.cc:2356 msgid "find outline gap" msgstr "" #: f.area.cc:2451 msgid "cannot find area outline" msgstr "" #: f.area.cc:3285 msgid "save area as a PNG file" msgstr "" #: f.area.cc:3407 msgid "position with mouse click/drag" msgstr "" #: f.area.cc:3467 msgid "Paste Image" msgstr "" #: f.area.cc:3472 msgid "resize" msgstr "" #: f.combine.cc:174 f.combine.cc:828 f.combine.cc:1436 f.combine.cc:2066 #: f.combine.cc:2510 msgid "Select 2 to 9 files" msgstr "" #: f.combine.cc:196 f.combine.cc:850 f.combine.cc:1458 f.combine.cc:2088 msgid "Images are not all the same size" msgstr "" #: f.combine.cc:560 msgid "Adjust Image Contributions" msgstr "" #: f.combine.cc:564 msgid "dark pixels" msgstr "" #: f.combine.cc:566 msgid "light pixels" msgstr "" #: f.combine.cc:568 msgid "file:" msgstr "" #: f.combine.cc:1036 msgid "Paint and Warp Image" msgstr "" #: f.combine.cc:1044 f.edit.cc:6054 msgid "paint" msgstr "" #: f.combine.cc:1045 msgid "warp" msgstr "" #: f.combine.cc:1648 f.combine.cc:2594 msgid "Select and Paint Image" msgstr "" #: f.combine.cc:1656 msgid "Transient Objects" msgstr "" #: f.combine.cc:2264 msgid "Adjust Pixel Composition" msgstr "" #: f.combine.cc:2266 msgid "use average" msgstr "" #: f.combine.cc:2267 msgid "use median" msgstr "" #: f.combine.cc:2269 msgid "omit low pixel" msgstr "" #: f.combine.cc:2270 msgid "omit high pixel" msgstr "" #: f.combine.cc:2532 f.combine.cc:2844 msgid "Images must be exactly the same size" msgstr "" #: f.combine.cc:2605 msgid " Fill " msgstr "" #: f.combine.cc:2606 msgid "using selected image" msgstr "" #: f.combine.cc:2825 f.combine.cc:3095 msgid "Select 2 files" msgstr "" #: f.combine.cc:2902 msgid "Split two Images" msgstr "" #: f.combine.cc:2903 msgid "drag image boundary" msgstr "" #: f.combine.cc:3092 msgid "Image Differences" msgstr "" #: f.combine.cc:3108 msgid "differences" msgstr "" #: f.combine.cc:3110 msgid "X-align" msgstr "" #: f.combine.cc:3113 msgid "Y-align" msgstr "" #: f.combine.cc:3179 msgid "select exactly 2 files" msgstr "" #: f.combine.cc:3408 f.combine.cc:4562 msgid "Select 2 to 4 files" msgstr "" #: f.combine.cc:3483 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from lower edge." msgstr "" #: f.combine.cc:3485 f.combine.cc:4639 msgid "no curve (scanned image)" msgstr "" #: f.combine.cc:3486 f.combine.cc:4640 msgid "Search for lens mm" msgstr "" #: f.combine.cc:3487 f.combine.cc:4641 msgid "Save lens mm image EXIF" msgstr "" #: f.combine.cc:3547 f.combine.cc:4701 msgid "Pre-align Images" msgstr "" #: f.combine.cc:3551 f.combine.cc:4705 msgid "lens mm" msgstr "" #: f.combine.cc:3556 f.combine.cc:4710 msgid "no auto warp" msgstr "" #: f.combine.cc:3558 f.combine.cc:4712 msgid "manual align" msgstr "" #: f.combine.cc:3560 f.combine.cc:4714 f.process.cc:1395 f.widgets.cc:504 #: f.widgets.cc:829 msgid "Resize" msgstr "" #: f.combine.cc:3561 f.combine.cc:4715 msgid "resize window" msgstr "" #: f.combine.cc:3569 f.combine.cc:4723 msgid "do not warp images during auto-alignment" msgstr "" #: f.combine.cc:3614 f.combine.cc:4768 msgid "use two images only" msgstr "" #: f.combine.cc:3644 f.combine.cc:3848 f.combine.cc:4034 f.combine.cc:4796 #: f.combine.cc:5000 f.combine.cc:5186 msgid "Too little overlap, cannot align" msgstr "" #: f.combine.cc:4112 f.combine.cc:5264 msgid "Match Brightness and Color" msgstr "" #: f.combine.cc:4158 msgid "Select image" msgstr "" #: f.combine.cc:4173 f.combine.cc:5325 msgid "auto color" msgstr "" #: f.combine.cc:4174 f.combine.cc:5326 msgid "file color" msgstr "" #: f.combine.cc:4180 f.combine.cc:5332 msgid "mouse warp" msgstr "" #: f.combine.cc:4182 f.combine.cc:5334 msgid "flatten image" msgstr "" #: f.combine.cc:4637 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from right edge." msgstr "" #: f.combine.cc:5596 msgid "pano tools (hugin) not installed" msgstr "" #: f.combine.cc:5605 msgid "Select at least 2 files" msgstr "" #: f.edit.cc:106 msgid "" "drag middle to move \n" "drag corners to resize \n" "drag right edge to level" msgstr "" #: f.edit.cc:209 f.widgets.cc:501 f.widgets.cc:828 msgid "Trim/Rotate" msgstr "" #: f.edit.cc:224 f.edit.cc:1483 msgid "ratio" msgstr "" #: f.edit.cc:226 msgid "Lock Ratio" msgstr "" #: f.edit.cc:229 msgid "Trim Size:" msgstr "" #: f.edit.cc:237 msgid "Set Ratio" msgstr "" #: f.edit.cc:238 f.widgets.cc:493 fotoxx.h:1371 msgid "Invert" msgstr "" #: f.edit.cc:239 msgid "Customize" msgstr "" #: f.edit.cc:248 msgid "Degrees:" msgstr "" #: f.edit.cc:250 msgid "Auto Level" msgstr "" #: f.edit.cc:256 f.process.cc:186 f.process.cc:822 f.widgets.cc:502 #: f.widgets.cc:830 msgid "Upright" msgstr "" #: f.edit.cc:258 msgid "maximize trim box" msgstr "" #: f.edit.cc:259 msgid "trim transparent edges" msgstr "" #: f.edit.cc:260 msgid "lock width/height ratio" msgstr "" #: f.edit.cc:261 msgid "use EXIF data if available" msgstr "" #: f.edit.cc:603 f.edit.cc:1550 msgid "rotation unknown" msgstr "" #: f.edit.cc:1479 msgid "Trim Buttons" msgstr "" #: f.edit.cc:1482 msgid "label" msgstr "" #: f.edit.cc:1692 f.widgets.cc:503 f.widgets.cc:833 msgid "Retouch" msgstr "" #: f.edit.cc:1705 msgid "Auto black level" msgstr "" #: f.edit.cc:1708 f.edit.cc:1714 msgid "sample %" msgstr "" #: f.edit.cc:1711 msgid "Auto white balance" msgstr "" #: f.edit.cc:1717 msgid "Click gray spot for white balance" msgstr "" #: f.edit.cc:1720 msgid "Click dark spot for black level" msgstr "" #: f.edit.cc:1723 msgid "Click for RGB distribution" msgstr "" #: f.edit.cc:1731 fotoxx.h:1326 msgid "Brightness" msgstr "" #: f.edit.cc:1732 f.effects.cc:4049 fotoxx.h:1339 msgid "Contrast" msgstr "" #: f.edit.cc:1733 f.edit.cc:3279 f.edit.cc:3311 f.edit.cc:6777 msgid "Saturation" msgstr "" #: f.edit.cc:1734 msgid "Temperature" msgstr "" #: f.edit.cc:1742 msgid "Settings File" msgstr "" #: f.edit.cc:2250 msgid "choose a better spot" msgstr "" #: f.edit.cc:2555 msgid "Resize Image" msgstr "" #: f.edit.cc:2571 msgid "Previous" msgstr "" #: f.edit.cc:2589 msgid "W/H Ratio:" msgstr "" #: f.edit.cc:2591 msgid "Lock" msgstr "" #: f.edit.cc:2840 msgid "insufficient memory, cannot proceed" msgstr "" #: f.edit.cc:2936 f.widgets.cc:505 msgid "Adjust RGB" msgstr "" #: f.edit.cc:2942 msgid "+Brightness" msgstr "" #: f.edit.cc:2943 msgid "+Red -Cyan" msgstr "" #: f.edit.cc:2944 msgid "+Green -Magenta" msgstr "" #: f.edit.cc:2945 msgid "+Blue -Yellow" msgstr "" #: f.edit.cc:2948 msgid "Contrast Red" msgstr "" #: f.edit.cc:2949 msgid "Contrast Green" msgstr "" #: f.edit.cc:2950 msgid "Contrast Blue" msgstr "" #: f.edit.cc:3268 f.widgets.cc:506 msgid "Adjust HSL" msgstr "" #: f.edit.cc:3272 msgid "Input color to match and adjust:" msgstr "" #: f.edit.cc:3274 msgid "shift+click on image to select color" msgstr "" #: f.edit.cc:3277 msgid "Match using:" msgstr "" #: f.edit.cc:3278 msgid "Hue" msgstr "" #: f.edit.cc:3280 f.edit.cc:3312 f.edit.cc:6778 msgid "Lightness" msgstr "" #: f.edit.cc:3290 msgid "Output Color" msgstr "" #: f.edit.cc:3310 f.edit.cc:6776 msgid "Color Hue" msgstr "" #: f.edit.cc:3313 msgid "Adjustment" msgstr "" #: f.edit.cc:3828 msgid "Image Markup" msgstr "" #: f.edit.cc:3829 f.edit.cc:3894 msgid "Draw text on image" msgstr "" #: f.edit.cc:3830 f.edit.cc:4818 msgid "Draw line or arrow on image" msgstr "" #: f.edit.cc:3831 f.edit.cc:5499 msgid "Draw box on image" msgstr "" #: f.edit.cc:3832 f.edit.cc:5700 msgid "Draw oval on image" msgstr "" #: f.edit.cc:3871 msgid "+Version" msgstr "" #: f.edit.cc:3895 msgid "Enter text, click/drag on image, right click to remove" msgstr "" #: f.edit.cc:3944 msgid "Use settings file" msgstr "" #: f.edit.cc:3949 f.mashup.cc:2438 f.tools.cc:1668 f.tools.cc:1677 msgid "Text" msgstr "" #: f.edit.cc:3954 msgid "Use metadata key" msgstr "" #: f.edit.cc:3973 f.mashup.cc:2456 msgid "text" msgstr "" #: f.edit.cc:3974 f.edit.cc:4847 f.mashup.cc:2457 f.mashup.cc:2795 msgid "backing" msgstr "" #: f.edit.cc:3975 f.edit.cc:4848 f.mashup.cc:2458 f.mashup.cc:2796 msgid "outline" msgstr "" #: f.edit.cc:3976 f.edit.cc:4849 f.mashup.cc:2459 f.mashup.cc:2797 msgid "shadow" msgstr "" #: f.edit.cc:4002 msgid "save to current file" msgstr "" #: f.edit.cc:4003 f.file.cc:2432 msgid "save as new file version" msgstr "" #: f.edit.cc:4004 msgid "" "save to current file \n" "open next file with same text" msgstr "" #: f.edit.cc:4167 f.mashup.cc:2563 f.tools.cc:1961 msgid "select font" msgstr "" #: f.edit.cc:4446 msgid "text file is defective" msgstr "" #: f.edit.cc:4786 msgid "" "Enter line or arrow properties in dialog, \n" "click/drag on image, right click to remove" msgstr "" #: f.edit.cc:4826 f.mashup.cc:2774 msgid "Line length" msgstr "" #: f.edit.cc:4833 f.mashup.cc:2781 msgid "Arrow head" msgstr "" #: f.edit.cc:4846 f.mashup.cc:2794 msgid "line" msgstr "" #: f.edit.cc:4875 msgid "" "fix line/arrow in layout \n" " start new line/arrow" msgstr "" #: f.edit.cc:5474 msgid "" "drag mouse to draw box \n" "shift + drag center to move box \n" "shift + drag edge to move edge" msgstr "" #: f.edit.cc:5505 f.edit.cc:5706 msgid "line color" msgstr "" #: f.edit.cc:5508 f.edit.cc:5709 msgid "line width" msgstr "" #: f.edit.cc:5674 msgid "" "drag mouse down/right to draw oval \n" "shift + drag center to move oval \n" "shift + drag lower right edge to change" msgstr "" #: f.edit.cc:5712 msgid "oval" msgstr "" #: f.edit.cc:5713 msgid "circle" msgstr "" #: f.edit.cc:5988 msgid "" "shift + left click: pick color from image \n" "left drag: paint color on image \n" "right drag: restore original image" msgstr "" #: f.edit.cc:6024 msgid "Paint on Image" msgstr "" #: f.edit.cc:6030 msgid "paint color" msgstr "" #: f.edit.cc:6041 f.edit.cc:6970 f.edit.cc:7548 msgid "brush size" msgstr "" #: f.edit.cc:6055 msgid "erase" msgstr "" #: f.edit.cc:6060 msgid "include transparent areas" msgstr "" #: f.edit.cc:6063 msgid "drag image" msgstr "" #: f.edit.cc:6065 msgid "zoom image" msgstr "" #: f.edit.cc:6592 msgid "Color Chooser" msgstr "" #: f.edit.cc:6594 msgid "click on desired color" msgstr "" #: f.edit.cc:6929 msgid "" "shift + left click: pick position to copy \n" "left click or drag: copy image to mouse \n" "right click or drag: restore original image" msgstr "" #: f.edit.cc:6960 msgid "Copy Pixels (1 image)" msgstr "" #: f.edit.cc:6979 f.edit.cc:7557 msgid "paint over transparent areas" msgstr "" #: f.edit.cc:7531 msgid "" "left click: synchronize copy position \n" "left click or drag: copy source image to mouse \n" "right click or drag: restore original image" msgstr "" #: f.edit.cc:7535 msgid "Copy Pixels (2 images)" msgstr "" #: f.edit.cc:7541 msgid "source image scale" msgstr "" #: f.edit.cc:8118 msgid "source image failure (scale too big?)" msgstr "" #: f.edit.cc:8197 f.widgets.cc:511 msgid "Paint Edits" msgstr "" #: f.edit.cc:8203 fotoxx.cc:3576 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" #: f.edit.cc:8211 f.tools.cc:3751 msgid "Edit function must be active" msgstr "" #: f.edit.cc:8216 msgid "Cannot use Paint Edits" msgstr "" #: f.edit.cc:8243 f.edit.cc:8485 msgid "power: center" msgstr "" #: f.edit.cc:8248 msgid "reset area" msgstr "" #: f.edit.cc:8428 msgid "finish current edit first" msgstr "" #: f.edit.cc:8433 msgid "no previous edit" msgstr "" #: f.edit.cc:8438 msgid "no current image" msgstr "" #: f.edit.cc:8449 msgid "This edit cannot be incrementally undone" msgstr "" #: f.edit.cc:8480 f.widgets.cc:512 msgid "Undo Edits" msgstr "" #: f.edit.cc:8708 f.edit.cc:8756 msgid "Edit Plugins" msgstr "" #: f.edit.cc:8709 msgid "Edit plugins menu" msgstr "" #: f.edit.cc:8717 msgid "Run as Fotoxx edit function" msgstr "" #: f.edit.cc:8758 msgid "menu name" msgstr "" #: f.edit.cc:8761 msgid "command" msgstr "" #: f.edit.cc:8959 msgid "Plugin working ..." msgstr "" #: f.edit.cc:8968 msgid "plugin failed" msgstr "" #: f.edit.cc:9004 msgid "Raw Therapee not installed" msgstr "" #: f.edit.cc:9023 msgid "RAW type not registered in User Preferences" msgstr "" #: f.edit.cc:9038 msgid "Raw Therapee produced no tif file" msgstr "" #: f.effects.cc:83 msgid "Convert to Sketch" msgstr "" #: f.effects.cc:161 msgid "Clip Level" msgstr "" #: f.effects.cc:165 msgid "Algorithm" msgstr "" #: f.effects.cc:170 msgid "Foreground" msgstr "" #: f.effects.cc:173 f.tools.cc:1680 msgid "Background" msgstr "" #: f.effects.cc:609 msgid "Line Threshold" msgstr "" #: f.effects.cc:610 msgid "Line Width" msgstr "" #: f.effects.cc:611 f.enhance.cc:3929 msgid "Blur Radius" msgstr "" #: f.effects.cc:612 msgid "Kuwahara Depth" msgstr "" #: f.effects.cc:1057 f.widgets.cc:538 msgid "Line Drawing" msgstr "" #: f.effects.cc:1070 f.effects.cc:2090 msgid "black/white" msgstr "" #: f.effects.cc:1320 f.widgets.cc:539 msgid "Emboss" msgstr "" #: f.effects.cc:1326 msgid "Depth" msgstr "" #: f.effects.cc:1541 msgid "Simulate Tiles" msgstr "" #: f.effects.cc:1545 msgid "tile size" msgstr "" #: f.effects.cc:1548 msgid "tile gap" msgstr "" #: f.effects.cc:1551 msgid "3D depth" msgstr "" #: f.effects.cc:1780 msgid "Dither Image" msgstr "" #: f.effects.cc:1783 msgid "Dither0" msgstr "" #: f.effects.cc:1784 f.effects.cc:1848 msgid "Roy Lichtenstein effect" msgstr "" #: f.effects.cc:1787 f.effects.cc:2083 msgid "Dither1" msgstr "" #: f.effects.cc:1788 msgid "pure RGB or black/white dots" msgstr "" #: f.effects.cc:1791 f.effects.cc:2481 msgid "Dither2" msgstr "" #: f.effects.cc:1792 msgid "RGB mix with given bit-depth" msgstr "" #: f.effects.cc:1795 f.effects.cc:2756 msgid "Dither3" msgstr "" #: f.effects.cc:1796 msgid "custom palette colors" msgstr "" #: f.effects.cc:1852 msgid "dot size" msgstr "" #: f.effects.cc:2087 f.effects.cc:2485 f.effects.cc:2760 msgid "resolution" msgstr "" #: f.effects.cc:2089 msgid "RGB color" msgstr "" #: f.effects.cc:2091 msgid "random position" msgstr "" #: f.effects.cc:2488 f.effects.cc:3196 msgid "color depth" msgstr "" #: f.effects.cc:2491 f.effects.cc:2768 msgid "error compensation" msgstr "" #: f.effects.cc:2764 msgid "palette:" msgstr "" #: f.effects.cc:2818 msgid "palette file" msgstr "" #: f.effects.cc:3178 f.widgets.cc:542 msgid "Painting" msgstr "" #: f.effects.cc:3200 msgid "patch area goal" msgstr "" #: f.effects.cc:3204 msgid "req. color match" msgstr "" #: f.effects.cc:3208 msgid "borders" msgstr "" #: f.effects.cc:3799 msgid "Add Texture" msgstr "" #: f.effects.cc:4014 msgid "Background Pattern" msgstr "" #: f.effects.cc:4018 msgid "Pattern File:" msgstr "" #: f.effects.cc:4026 msgid "Geometry" msgstr "" #: f.effects.cc:4030 f.widgets.cc:544 msgid "Pattern" msgstr "" #: f.effects.cc:4038 msgid "Overlap" msgstr "" #: f.effects.cc:4046 msgid "Opacity" msgstr "" #: f.effects.cc:4052 msgid "Grayscale" msgstr "" #: f.effects.cc:4478 msgid "Create Mosaic" msgstr "" #: f.effects.cc:4524 msgid "Tile" msgstr "" #: f.effects.cc:4532 f.widgets.cc:540 msgid "Tiles" msgstr "" #: f.effects.cc:4538 msgid "Tile blending" msgstr "" #: f.effects.cc:4621 #, c-format msgid "exceeded max. tiles: %d" msgstr "" #: f.effects.cc:4628 #, c-format msgid "only %d tile images found" msgstr "" #: f.effects.cc:5088 f.widgets.cc:546 msgid "Color Mode" msgstr "" #: f.effects.cc:5091 msgid "reset" msgstr "" #: f.effects.cc:5092 msgid "black/white positive" msgstr "" #: f.effects.cc:5093 msgid "black/white negative" msgstr "" #: f.effects.cc:5094 msgid "color negative" msgstr "" #: f.effects.cc:5095 msgid "RGB -> GBR" msgstr "" #: f.effects.cc:5096 msgid "RGB -> BRG" msgstr "" #: f.effects.cc:5097 msgid "sepia" msgstr "" #: f.effects.cc:5348 msgid "Set color depth to 1-16 bits" msgstr "" #: f.effects.cc:5377 msgid "Set Color Depth" msgstr "" #: f.effects.cc:5547 f.widgets.cc:548 msgid "Shift Colors" msgstr "" #: f.effects.cc:5824 f.widgets.cc:549 msgid "Alien Colors" msgstr "" #: f.effects.cc:5827 msgid "blocksize" msgstr "" #: f.effects.cc:5830 f.warp.cc:3096 msgid "amplitude" msgstr "" #: f.effects.cc:6089 msgid "" "Draw a line across the image in \n" "direction of brightness change." msgstr "" #: f.effects.cc:6132 msgid "Brightness Ramp" msgstr "" #: f.effects.cc:6605 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" #: f.effects.cc:6640 msgid "Paint Transparency" msgstr "" #: f.effects.cc:6648 msgid "paintbrush radius" msgstr "" #: f.effects.cc:6649 msgid "strength center" msgstr "" #: f.effects.cc:6650 msgid "strength edge" msgstr "" #: f.effects.cc:6655 msgid "gradual paint" msgstr "" #: f.effects.cc:6874 msgid "Mirror Image" msgstr "" #: f.effects.cc:6877 f.warp.cc:3098 msgid "horizontal" msgstr "" #: f.effects.cc:6878 f.warp.cc:3102 msgid "vertical" msgstr "" #: f.effects.cc:7051 f.widgets.cc:553 msgid "Custom Kernel" msgstr "" #: f.effects.cc:7055 msgid "Kernel size" msgstr "" #: f.effects.cc:7072 msgid "multiply" msgstr "" #: f.effects.cc:7075 msgid "add" msgstr "" #: f.effects.cc:7079 msgid "Data file" msgstr "" #: f.effects.cc:7099 fotoxx.cc:3845 msgid "Load settings from file" msgstr "" #: f.enhance.cc:505 msgid "Adjust Brightness Distribution" msgstr "" #: f.enhance.cc:544 msgid "Low Cutoff" msgstr "" #: f.enhance.cc:545 msgid "High Cutoff" msgstr "" #: f.enhance.cc:546 msgid "Low Flatten" msgstr "" #: f.enhance.cc:547 msgid "Mid Flatten" msgstr "" #: f.enhance.cc:548 msgid "High Flatten" msgstr "" #: f.enhance.cc:549 msgid "Low Stretch" msgstr "" #: f.enhance.cc:550 msgid "Mid Stretch" msgstr "" #: f.enhance.cc:551 msgid "High Stretch" msgstr "" #: f.enhance.cc:886 msgid "Magnify Gradients" msgstr "" #: f.enhance.cc:923 msgid "low" msgstr "" #: f.enhance.cc:925 msgid "high" msgstr "" #: f.enhance.cc:928 msgid "Amplify" msgstr "" #: f.enhance.cc:1312 msgid "Flatten Brightness" msgstr "" #: f.enhance.cc:1363 msgid "Zones" msgstr "" #: f.enhance.cc:1370 msgid "Deband Dark" msgstr "" #: f.enhance.cc:1373 msgid "Deband Bright" msgstr "" #: f.enhance.cc:1851 msgid "Global Retinex" msgstr "" #: f.enhance.cc:1867 msgid "Dark Point" msgstr "" #: f.enhance.cc:1868 msgid "Bright Point" msgstr "" #: f.enhance.cc:1869 msgid "Multiplyer" msgstr "" #: f.enhance.cc:1893 msgid "brightness rescale" msgstr "" #: f.enhance.cc:1896 msgid "click bright point" msgstr "" #: f.enhance.cc:1897 msgid "click dark point" msgstr "" #: f.enhance.cc:1900 f.enhance.cc:2531 msgid "blend" msgstr "" #: f.enhance.cc:1903 f.enhance.cc:2537 msgid "reduce bright" msgstr "" #: f.enhance.cc:2523 msgid "Zonal Retinex" msgstr "" #: f.enhance.cc:2527 msgid "zone count:" msgstr "" #: f.enhance.cc:2534 msgid "reduce dark" msgstr "" #: f.enhance.cc:3058 f.process.cc:189 f.process.cc:823 f.process.cc:1404 #: f.widgets.cc:524 msgid "Sharpen" msgstr "" #: f.enhance.cc:3066 msgid "unsharp mask" msgstr "" #: f.enhance.cc:3099 msgid "median diff" msgstr "" #: f.enhance.cc:3101 msgid "dark" msgstr "" #: f.enhance.cc:3102 msgid "light" msgstr "" #: f.enhance.cc:3888 msgid "Click to set center" msgstr "" #: f.enhance.cc:3889 msgid "Pull image using the mouse" msgstr "" #: f.enhance.cc:3890 msgid "" "left drag: blend image \n" "right drag: restore image" msgstr "" #: f.enhance.cc:3933 msgid "Normal Blur" msgstr "" #: f.enhance.cc:3940 msgid "Radial Blur" msgstr "" #: f.enhance.cc:3954 msgid "Directed Blur" msgstr "" #: f.enhance.cc:3956 msgid "Blur Span" msgstr "" #: f.enhance.cc:3959 msgid "Intensity" msgstr "" #: f.enhance.cc:3964 msgid "Graduated Blur" msgstr "" #: f.enhance.cc:3969 msgid "Contrast Limit" msgstr "" #: f.enhance.cc:3974 msgid "Paint Blur" msgstr "" #: f.enhance.cc:3979 f.mashup.cc:1577 fotoxx.h:1402 msgid "Power" msgstr "" #: f.enhance.cc:3987 f.enhance.cc:4914 msgid "Blur Background" msgstr "" #: f.enhance.cc:4918 msgid "constant blur" msgstr "" #: f.enhance.cc:4921 msgid "increase blur with distance" msgstr "" #: f.enhance.cc:4923 msgid "min. blur radius" msgstr "" #: f.enhance.cc:4926 msgid "max. blur radius" msgstr "" #: f.enhance.cc:4957 f.warp.cc:837 f.warp.cc:2277 msgid "no active Select Area" msgstr "" #: f.enhance.cc:5139 msgid "Apply repeatedly while watching the image." msgstr "" #: f.enhance.cc:5175 msgid "Noise Reduction" msgstr "" #: f.enhance.cc:5215 msgid "dark areas" msgstr "" #: f.enhance.cc:5217 msgid "all areas" msgstr "" #: f.enhance.cc:6195 msgid "Measure Noise" msgstr "" #: f.enhance.cc:6196 msgid "Move mouse in a monotone image area." msgstr "" #: f.enhance.cc:6511 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 "" #: f.enhance.cc:6530 msgid "Red Eye Reduction" msgstr "" #: f.enhance.cc:6984 msgid "Color Match Images" msgstr "" #: f.enhance.cc:7015 msgid "mouse radius for color sample" msgstr "" #: f.enhance.cc:7017 f.enhance.cc:7022 fotoxx.h:1397 zfuncs.cc:12681 msgid "Open" msgstr "" #: f.enhance.cc:7018 msgid "image for source color" msgstr "" #: f.enhance.cc:7020 msgid "click on image to get source color" msgstr "" #: f.enhance.cc:7023 msgid "image to set matching color" msgstr "" #: f.enhance.cc:7025 msgid "click on image to set matching color" msgstr "" #: f.enhance.cc:7092 msgid "select source image color first" msgstr "" #: f.enhance.cc:7287 msgid "" "Drag mouse to select. Erase. Repeat. \n" "Click: extend selection to mouse." msgstr "" #: f.enhance.cc:7314 f.widgets.cc:529 msgid "Smart Erase" msgstr "" #: f.enhance.cc:7319 fotoxx.h:1408 msgid "Radius" msgstr "" #: f.enhance.cc:7321 f.widgets.cc:525 msgid "Blur" msgstr "" #: f.enhance.cc:7324 msgid "New Area" msgstr "" #: f.enhance.cc:7672 f.enhance.cc:8054 msgid "Chromatic Aberration" msgstr "" #: f.enhance.cc:7711 msgid "Red Factors" msgstr "" #: f.enhance.cc:7715 msgid "Blue Factors" msgstr "" #: f.enhance.cc:7720 msgid "Find optimum factors:" msgstr "" #: f.enhance.cc:8095 msgid "Chromatic Color" msgstr "" #: f.enhance.cc:8098 msgid "Replacement Color" msgstr "" #: f.enhance.cc:8101 msgid "Background Color" msgstr "" #: f.enhance.cc:8105 msgid "Color match level" msgstr "" #: f.enhance.cc:8109 msgid "Background Proximity" msgstr "" #: f.enhance.cc:8151 msgid "255 iterations, cannot continue" msgstr "" #: f.enhance.cc:8413 f.widgets.cc:532 msgid "Vignette" msgstr "" #: f.enhance.cc:8802 f.widgets.cc:533 msgid "Remove Dust" msgstr "" #: f.enhance.cc:8806 msgid "spot size limit" msgstr "" #: f.enhance.cc:8809 msgid "max. brightness" msgstr "" #: f.enhance.cc:8812 msgid "min. contrast" msgstr "" #: f.file.cc:601 msgid "Rename Image File" msgstr "" #: f.file.cc:608 msgid "Old Name" msgstr "" #: f.file.cc:609 f.process.cc:141 msgid "New Name" msgstr "" #: f.file.cc:618 msgid "previous name" msgstr "" #: f.file.cc:619 msgid "Add 1" msgstr "" #: f.file.cc:622 f.file.cc:882 f.file.cc:1629 f.file.cc:1998 msgid "keep this dialog open" msgstr "" #: f.file.cc:850 msgid "File Permissions" msgstr "" #: f.file.cc:854 f.meta.cc:833 f.meta.cc:1615 f.meta.cc:1818 msgid "File:" msgstr "" #: f.file.cc:1000 msgid "Open Image File" msgstr "" #: f.file.cc:1019 f.process.cc:654 msgid "unknown file type" msgstr "" #: f.file.cc:1207 #, c-format msgid "Start of gallery, preceding gallery: %s" msgstr "" #: f.file.cc:1208 #, c-format msgid "End of gallery, following gallery: %s" msgstr "" #: f.file.cc:1355 msgid "Create Blank Image" msgstr "" #: f.file.cc:1357 f.meta.cc:1909 msgid "file name" msgstr "" #: f.file.cc:1391 msgid "supply a file name" msgstr "" #: f.file.cc:1570 msgid "Copy or Move Image File" msgstr "" #: f.file.cc:1615 msgid "New Location:" msgstr "" #: f.file.cc:1620 msgid "New Name:" msgstr "" #: f.file.cc:1625 msgid "copy (duplicate file)" msgstr "" #: f.file.cc:1626 msgid "move (remove original)" msgstr "" #: f.file.cc:1676 f.process.cc:608 f.process.cc:1574 f.process.cc:2155 msgid "Select folder" msgstr "" #: f.file.cc:1718 f.tools.cc:1488 msgid "new location is not a folder" msgstr "" #: f.file.cc:1737 msgid "new file extension missing or changed" msgstr "" #: f.file.cc:1771 f.file.cc:2075 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" #: f.file.cc:1845 f.file.cc:3226 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" #: f.file.cc:1957 msgid "Delete/Trash Image File" msgstr "" #: f.file.cc:2029 msgid "GTK g_file_trash() function failed" msgstr "" #: f.file.cc:2048 msgid "not a known image file" msgstr "" #: f.file.cc:2054 msgid "Delete read-only file?" msgstr "" #: f.file.cc:2056 msgid "Trash read-only file?" msgstr "" #: f.file.cc:2272 #, c-format msgid "Kill active dialog? %s" msgstr "" #: f.file.cc:2322 f.widgets.cc:633 msgid "User Guide" msgstr "" #: f.file.cc:2325 f.widgets.cc:634 msgid "Recent Changes" msgstr "" #: f.file.cc:2328 f.widgets.cc:635 msgid "Edit Functions Overview" msgstr "" #: f.file.cc:2334 f.widgets.cc:636 msgid "Change Log" msgstr "" #: f.file.cc:2337 f.widgets.cc:637 msgid "Log File" msgstr "" #: f.file.cc:2343 f.widgets.cc:639 msgid "Command Params" msgstr "" #: f.file.cc:2346 f.widgets.cc:640 msgid "Translations" msgstr "" #: f.file.cc:2349 f.widgets.cc:641 msgid "Home Page" msgstr "" #: f.file.cc:2352 f.widgets.cc:642 msgid "License" msgstr "" #: f.file.cc:2355 f.widgets.cc:643 msgid "Privacy" msgstr "" #: f.file.cc:2358 f.widgets.cc:644 msgid "About" msgstr "" #: f.file.cc:2366 f.widgets.cc:665 fotoxx.h:1366 msgid "Help" msgstr "" #: f.file.cc:2414 msgid "Save Image File" msgstr "" #: f.file.cc:2424 msgid "new version" msgstr "" #: f.file.cc:2425 msgid "new file ..." msgstr "" #: f.file.cc:2426 msgid "replace file" msgstr "" #: f.file.cc:2433 f.file.cc:3083 msgid "save as new file name or type" msgstr "" #: f.file.cc:2435 msgid "replace old file (OVERWRITE)" msgstr "" #: f.file.cc:2486 msgid "cannot replace RAW file" msgstr "" #: f.file.cc:2491 msgid "cannot replace HEIC file" msgstr "" #: f.file.cc:2496 msgid "cannot replace JP2 file" msgstr "" #: f.file.cc:2668 f.file.cc:2727 #, c-format msgid "" "file: %s \n" " exceed 99 versions" msgstr "" #: f.file.cc:2821 msgid "" "Transparency map will be lost.\n" "save to PNG file to retain." msgstr "" #: f.file.cc:2831 msgid "cannot save as RAW type" msgstr "" #: f.file.cc:2912 msgid "save anyway" msgstr "" #: f.file.cc:2980 f.file.cc:2982 msgid "Unable to copy EXIF/IPTC data" msgstr "" #: f.file.cc:3096 f.process.cc:1387 msgid "jpg quality" msgstr "" #: f.file.cc:3100 msgid "color depth:" msgstr "" #: f.file.cc:3106 f.file.cc:3114 msgid "make current" msgstr "" #: f.file.cc:3107 msgid "(new file becomes current file)" msgstr "" #: f.file.cc:3110 msgid "permissions:" msgstr "" #: f.file.cc:3641 f.widgets.cc:432 f.widgets.cc:821 msgid "Permissions" msgstr "" #: f.file.cc:3646 fotoxx.cc:254 msgid "owner" msgstr "" #: f.file.cc:3647 fotoxx.cc:255 msgid "group" msgstr "" #: f.file.cc:3648 fotoxx.cc:256 msgid "other" msgstr "" #: f.gallery.cc:1699 f.gallery.cc:1719 msgid "recent images" msgstr "" #: f.gallery.cc:1700 f.gallery.cc:1724 msgid "newest images" msgstr "" #: f.gallery.cc:1779 msgid "no albums found" msgstr "" #: f.gallery.cc:1789 f.gallery.cc:1803 msgid "Current Album" msgstr "" #: f.gallery.cc:1806 msgid "no current album" msgstr "" #: f.gallery.cc:1828 msgid "" " Reset all galleries\n" " to file name ascending" msgstr "" #: f.gallery.cc:1849 msgid "Gallery Sort" msgstr "" #: f.gallery.cc:1853 msgid "File Name" msgstr "" #: f.gallery.cc:1854 msgid "File Mod Date/Time" msgstr "" #: f.gallery.cc:1855 msgid "Photo Date/Time (EXIF)" msgstr "" #: f.gallery.cc:1856 msgid "File Size (bytes)" msgstr "" #: f.gallery.cc:1857 msgid "Image Size (pixels)" msgstr "" #: f.gallery.cc:1859 msgid "ascending" msgstr "" #: f.gallery.cc:1860 msgid "descending" msgstr "" #: f.gallery.cc:2759 msgid "" "Use EXIF photo date or \n" " file modification date?" msgstr "" #: f.gallery.cc:2783 f.widgets.cc:648 msgid "File" msgstr "" #: f.gallery.cc:3589 f.gallery.cc:3593 msgid "Image File" msgstr "" #: f.gallery.cc:3702 msgid "Select Image Files" msgstr "" #: f.gallery.cc:3768 #, c-format msgid "exceed %d selected files" msgstr "" #: f.gallery.cc:3780 #, c-format msgid "remove %d duplicates?" msgstr "" #: f.gallery.cc:4229 f.widgets.cc:457 msgid "Bookmarks" msgstr "" #: f.gallery.cc:4229 f.gallery.cc:4337 msgid "Edit Bookmarks" msgstr "" #: f.gallery.cc:4311 msgid "" "Click a list position. Click a gallery thumbnail for the new bookmark.\n" "Bookmark for thumbnail will be added. Change the name and press [Rename]." msgstr "" #: f.gallery.cc:4514 msgid "unable to save bookmarks file" msgstr "" #: f.mashup.cc:212 msgid "Project name" msgstr "" #: f.mashup.cc:216 msgid "Layout and background image" msgstr "" #: f.mashup.cc:220 msgid "choose an image file" msgstr "" #: f.mashup.cc:221 msgid "use current image file" msgstr "" #: f.mashup.cc:222 msgid "specify layout size and color" msgstr "" #: f.mashup.cc:223 msgid "open a Mashup project file" msgstr "" #: f.mashup.cc:238 msgid "enter a project name" msgstr "" #: f.mashup.cc:269 msgid "no current file" msgstr "" #: f.mashup.cc:296 msgid "Make Layout Image" msgstr "" #: f.mashup.cc:403 msgid "Edit Images" msgstr "" #: f.mashup.cc:404 f.mashup.cc:2433 msgid "Edit Text" msgstr "" #: f.mashup.cc:405 msgid "Edit Line" msgstr "" #: f.mashup.cc:406 msgid "Rescale" msgstr "" #: f.mashup.cc:411 msgid "add or edit images" msgstr "" #: f.mashup.cc:413 msgid "add or edit text" msgstr "" #: f.mashup.cc:415 msgid "add or edit lines/arrows" msgstr "" #: f.mashup.cc:417 msgid "change project scale" msgstr "" #: f.mashup.cc:419 msgid "project complete" msgstr "" #: f.mashup.cc:421 msgid "cancel project" msgstr "" #: f.mashup.cc:439 msgid "rescale project" msgstr "" #: f.mashup.cc:493 msgid "save Mashup output file" msgstr "" #: f.mashup.cc:513 msgid "save Mashup project file?" msgstr "" #: f.mashup.cc:528 msgid "delete Mashup project file?" msgstr "" #: f.mashup.cc:588 msgid "Open Project" msgstr "" #: f.mashup.cc:617 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" #: f.mashup.cc:674 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" #: f.mashup.cc:980 msgid "project file is defective" msgstr "" #: f.mashup.cc:1010 msgid "Save Project" msgstr "" #: f.mashup.cc:1151 msgid "layout exceeds 2 gigabytes" msgstr "" #: f.mashup.cc:1221 msgid "Click image to select, drag image to move." msgstr "" #: f.mashup.cc:1222 msgid "Make black margins transparent" msgstr "" #: f.mashup.cc:1254 msgid "Add Image" msgstr "" #: f.mashup.cc:1261 msgid "Current image:" msgstr "" #: f.mashup.cc:1265 msgid "Cycle through images:" msgstr "" #: f.mashup.cc:1272 msgid "Scale" msgstr "" #: f.mashup.cc:1281 msgid "Stacking Order" msgstr "" #: f.mashup.cc:1282 msgid "Raise" msgstr "" #: f.mashup.cc:1283 msgid "Lower" msgstr "" #: f.mashup.cc:1286 msgid "Base Transparency" msgstr "" #: f.mashup.cc:1290 msgid "Var. Transparency" msgstr "" #: f.mashup.cc:1291 msgid "Paint" msgstr "" #: f.mashup.cc:1294 msgid "Bend and fine-align" msgstr "" #: f.mashup.cc:1295 f.widgets.cc:661 msgid "Warp" msgstr "" #: f.mashup.cc:1304 zfuncs.cc:12896 zfuncs.cc:12905 msgid "Margins" msgstr "" #: f.mashup.cc:1305 msgid "Hard" msgstr "" #: f.mashup.cc:1306 msgid "Blend" msgstr "" #: f.mashup.cc:1320 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:1810 msgid "Pull on the image with the mouse." msgstr "" #: f.mashup.cc:1826 msgid "Warp Image" msgstr "" #: f.mashup.cc:1832 f.warp.cc:1209 msgid "warp span" msgstr "" #: f.mashup.cc:2264 #, c-format msgid "exceeded %d images" msgstr "" #: f.mashup.cc:2406 msgid "Enter text, [Add] to layout, edit properties." msgstr "" #: f.mashup.cc:2486 msgid "Text File:" msgstr "" #: f.mashup.cc:2490 msgid "add entered text to layout" msgstr "" #: f.mashup.cc:2624 msgid "click position to add text" msgstr "" #: f.mashup.cc:2630 #, c-format msgid "exceeded %d text entries" msgstr "" #: f.mashup.cc:2635 msgid "Add Text" msgstr "" #: f.mashup.cc:2744 msgid "Set line properties, [Add] to layout, edit." msgstr "" #: f.mashup.cc:2768 msgid "Edit Line/Arrow" msgstr "" #: f.mashup.cc:2823 msgid "add line/arrow to layout" msgstr "" #: f.mashup.cc:2915 msgid "click position to add line" msgstr "" #: f.mashup.cc:2921 #, c-format msgid "exceeded %d line entries" msgstr "" #: f.mashup.cc:2926 msgid "Add Line" msgstr "" #: f.mashup.cc:4171 msgid "Image Montage" msgstr "" #: f.mashup.cc:4180 msgid "Frame Width" msgstr "" #: f.mashup.cc:4183 f.mashup.cc:4191 msgid "Margin" msgstr "" #: f.mashup.cc:4188 msgid "Image Columns" msgstr "" #: f.mashup.cc:4205 #, c-format msgid "exceed %d rows" msgstr "" #: f.mashup.cc:4301 msgid "Optimize" msgstr "" #: f.mashup.cc:4309 #, c-format msgid "column difference: %d pixels" msgstr "" #: f.mashup.cc:4367 #, c-format msgid "" "column difference: %d pixels \n" "Make columns even?" msgstr "" #: f.mashup.cc:4389 msgid "Save with unique montage name" msgstr "" #: f.mashup.cc:4391 msgid "unique name:" msgstr "" #: f.mashup.cc:4393 msgid "create image map" msgstr "" #: f.mashup.cc:4410 f.meta.cc:9151 msgid "supply a reasonable name" msgstr "" #: f.mashup.cc:4421 msgid "save montage" msgstr "" #: f.mashup.cc:4441 #, c-format msgid "map file saved: %s" msgstr "" #: f.mashup.cc:4486 #, c-format msgid "%d max images exceeded" msgstr "" #: f.mashup.cc:4560 f.mashup.cc:4566 #, c-format msgid "image frame is too large: %d x %d" msgstr "" #: f.mashup.cc:4871 #, c-format msgid "montage map file invalid: %s" msgstr "" #: f.meta.cc:245 msgid "Add Metadata Items" msgstr "" #: f.meta.cc:249 f.meta.cc:1609 f.meta.cc:3190 msgid "click to select" msgstr "" #: f.meta.cc:254 msgid "click to unselect" msgstr "" #: f.meta.cc:476 msgid "Extras" msgstr "" #: f.meta.cc:476 msgid "View Metadata" msgstr "" #: f.meta.cc:661 msgid "View All Metadata" msgstr "" #: f.meta.cc:826 msgid "Edit Metadata" msgstr "" #: f.meta.cc:829 msgid "save metadata to file" msgstr "" #: f.meta.cc:838 msgid "Image Date" msgstr "" #: f.meta.cc:841 msgid "Time" msgstr "" #: f.meta.cc:849 msgid "Rating (stars):" msgstr "" #: f.meta.cc:861 msgid "Caption" msgstr "" #: f.meta.cc:866 msgid "Comments" msgstr "" #: f.meta.cc:873 msgid "Location" msgstr "" #: f.meta.cc:876 msgid "Country" msgstr "" #: f.meta.cc:899 msgid "Image Tags" msgstr "" #: f.meta.cc:905 msgid "Recent Tags" msgstr "" #: f.meta.cc:911 f.meta.cc:2081 msgid "Enter New Tag" msgstr "" #: f.meta.cc:917 f.meta.cc:2087 f.meta.cc:4750 msgid "Matching Tags" msgstr "" #: f.meta.cc:925 f.meta.cc:2096 f.meta.cc:2556 f.meta.cc:4757 msgid "Defined Tags Category" msgstr "" #: f.meta.cc:933 msgid "search known locations" msgstr "" #: f.meta.cc:934 msgid "search using web service" msgstr "" #: f.meta.cc:1384 f.meta.cc:3800 f.meta.cc:7709 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "" #: f.meta.cc:1441 f.widgets.cc:477 msgid "Manage Tags" msgstr "" #: f.meta.cc:1441 msgid "orphan tags" msgstr "" #: f.meta.cc:1445 msgid "category" msgstr "" #: f.meta.cc:1448 msgid "tag" msgstr "" #: f.meta.cc:1455 msgid "Defined Tags:" msgstr "" #: f.meta.cc:1605 msgid "Edit Any Metadata" msgstr "" #: f.meta.cc:1605 f.meta.cc:3185 msgid "Full List" msgstr "" #: f.meta.cc:1619 f.meta.cc:3202 msgid "key name" msgstr "" #: f.meta.cc:1622 f.meta.cc:3203 msgid "key value" msgstr "" #: f.meta.cc:1815 msgid "Delete Metadata" msgstr "" #: f.meta.cc:1821 fotoxx.h:1317 msgid "All" msgstr "" #: f.meta.cc:1822 msgid "One Key:" msgstr "" #: f.meta.cc:1908 msgid "choose options" msgstr "" #: f.meta.cc:1910 msgid "caption" msgstr "" #: f.meta.cc:1911 msgid "comment" msgstr "" #: f.meta.cc:2057 msgid "Batch Add/Remove Tags" msgstr "" #: f.meta.cc:2070 msgid "tags to add" msgstr "" #: f.meta.cc:2071 msgid "tags to remove" msgstr "" #: f.meta.cc:2176 #, c-format msgid "" "%s \n" " too many tags" msgstr "" #: f.meta.cc:2191 msgid "repeat with same files?" msgstr "" #: f.meta.cc:2331 msgid "specify files and tags" msgstr "" #: f.meta.cc:2535 f.widgets.cc:596 msgid "Batch Rename Tags" msgstr "" #: f.meta.cc:2545 msgid "(click defined tag)" msgstr "" #: f.meta.cc:2547 f.process.cc:815 msgid "Rename to" msgstr "" #: f.meta.cc:2564 msgid "old tag name >> new tag name" msgstr "" #: f.meta.cc:2620 #, c-format msgid "" "%d tags to rename \n" "in %d image files. \n" "Proceed?" msgstr "" #: f.meta.cc:2774 msgid "max tags exceeded" msgstr "" #: f.meta.cc:2843 f.meta.cc:2978 msgid "Batch Photo Date/Time" msgstr "" #: f.meta.cc:2851 msgid "set a new date/time:" msgstr "" #: f.meta.cc:2859 msgid "shift existing date/time:" msgstr "" #: f.meta.cc:2885 msgid "test: show changes, do not update files" msgstr "" #: f.meta.cc:2910 msgid "please make a choice" msgstr "" #: f.meta.cc:2915 f.meta.cc:3276 f.meta.cc:3467 msgid "no files selected" msgstr "" #: f.meta.cc:2945 f.meta.cc:2955 f.meta.cc:2961 msgid "invalid date/time format" msgstr "" #: f.meta.cc:3185 msgid "Batch Add/Change Metadata" msgstr "" #: f.meta.cc:3270 msgid "enter key names" msgstr "" #: f.meta.cc:3353 msgid "" "The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names" msgstr "" #: f.meta.cc:3450 msgid "Batch Report Metadata" msgstr "" #: f.meta.cc:3456 msgid "list of reported metadata items" msgstr "" #: f.meta.cc:3599 f.widgets.cc:600 msgid "Batch Geotags" msgstr "" #: f.meta.cc:3640 msgid "location" msgstr "" #: f.meta.cc:3643 msgid "country" msgstr "" #: f.meta.cc:3676 msgid "Adding Geotags" msgstr "" #: f.meta.cc:3785 msgid "" "data is incomplete \n" " proceed?" msgstr "" #: f.meta.cc:3873 msgid "Report Image Locations" msgstr "" #: f.meta.cc:3874 msgid "Group by country" msgstr "" #: f.meta.cc:3875 msgid "Group by country/location" msgstr "" #: f.meta.cc:3876 msgid "Group by country/location/date" msgstr "" #: f.meta.cc:3877 msgid "Group by date/country/location" msgstr "" #: f.meta.cc:3880 msgid "Combine within" msgstr "" #: f.meta.cc:3882 msgid "days" msgstr "" #: f.meta.cc:3994 msgid "Image Locations" msgstr "" #: f.meta.cc:4263 msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" msgstr "" #: f.meta.cc:4655 msgid "Search Images" msgstr "" #: f.meta.cc:4659 msgid "images to search:" msgstr "" #: f.meta.cc:4660 msgid "all" msgstr "" #: f.meta.cc:4661 msgid "current set only" msgstr "" #: f.meta.cc:4664 msgid "matching images:" msgstr "" #: f.meta.cc:4665 msgid "make new set" msgstr "" #: f.meta.cc:4666 msgid "add to set" msgstr "" #: f.meta.cc:4667 msgid "remove" msgstr "" #: f.meta.cc:4672 msgid "select last version only" msgstr "" #: f.meta.cc:4673 msgid "original + last version" msgstr "" #: f.meta.cc:4675 msgid "original + all versions" msgstr "" #: f.meta.cc:4676 f.process.cc:166 f.process.cc:172 f.process.cc:180 #: f.process.cc:2058 msgid "no change" msgstr "" #: f.meta.cc:4681 msgid "report type:" msgstr "" #: f.meta.cc:4682 msgid "gallery" msgstr "" #: f.meta.cc:4688 msgid "date range" msgstr "" #: f.meta.cc:4693 msgid "photo date" msgstr "" #: f.meta.cc:4694 msgid "file date" msgstr "" #: f.meta.cc:4695 msgid "(yyyy-mm-dd)" msgstr "" #: f.meta.cc:4700 msgid "rating range" msgstr "" #: f.meta.cc:4708 msgid "all/any" msgstr "" #: f.meta.cc:4711 msgid "search tags" msgstr "" #: f.meta.cc:4718 msgid "search text" msgstr "" #: f.meta.cc:4724 msgid "search files" msgstr "" #: f.meta.cc:4730 msgid "search locations" msgstr "" #: f.meta.cc:4734 msgid "enter cities, countries" msgstr "" #: f.meta.cc:4739 msgid "search other metadata" msgstr "" #: f.meta.cc:4746 msgid "Enter Search Tag" msgstr "" #: f.meta.cc:5060 msgid "" "to remove images from current set, \n" "search current set" msgstr "" #: f.meta.cc:5067 msgid "" "to add images to current set, \n" "search all images" msgstr "" #: f.meta.cc:5135 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" #: f.meta.cc:5160 msgid "stars range not reasonable" msgstr "" #: f.meta.cc:5718 msgid "Always reported: date, stars, tags, caption, comment" msgstr "" #: f.meta.cc:5750 msgid "Additional Items for Report" msgstr "" #: f.meta.cc:5757 msgid "Keyword" msgstr "" #: f.meta.cc:5771 msgid "Match Criteria" msgstr "" #: f.meta.cc:5904 #, c-format msgid "bad number: %s" msgstr "" #: f.meta.cc:6184 msgid "date format is YYYY-MM-DD" msgstr "" #: f.meta.cc:6188 msgid "date is invalid" msgstr "" #: f.meta.cc:6226 msgid "time format is HH:MM [:SS]" msgstr "" #: f.meta.cc:6230 msgid "time is invalid" msgstr "" #: f.meta.cc:7328 msgid "not found" msgstr "" #: f.meta.cc:7329 msgid "location and country required" msgstr "" #: f.meta.cc:7586 msgid "choose location" msgstr "" #: f.meta.cc:7886 msgid "Set Map Markers" msgstr "" #: f.meta.cc:7887 msgid "mark all image files" msgstr "" #: f.meta.cc:7888 msgid "mark current gallery" msgstr "" #: f.meta.cc:7989 msgid "choose map file" msgstr "" #: f.meta.cc:8133 msgid "" "fotoxx-maps package not installed \n" "(see https://kornelix.net)" msgstr "" #: f.meta.cc:8138 #, c-format msgid "map file %s is missing" msgstr "" #: f.meta.cc:8142 #, c-format msgid "" "map latitude/longitude data unreasonable \n" " %.3f %.3f %.3f %.3f" msgstr "" #: f.meta.cc:8468 f.meta.cc:9020 msgid "No matching images found" msgstr "" #: f.meta.cc:8562 msgid "Net Map Source" msgstr "" #: f.meta.cc:9098 msgid "Net Map Locations" msgstr "" #: f.meta.cc:9104 msgid "map location:" msgstr "" #: f.pixmap.cc:3227 f.pixmap.cc:3279 msgid "HEIF files not supported" msgstr "" #: f.pixmap.cc:3351 f.pixmap.cc:3402 msgid "JP2 files not supported" msgstr "" #: f.process.cc:134 f.widgets.cc:588 msgid "Batch Convert" msgstr "" #: f.process.cc:145 msgid "Sequence Numbers" msgstr "" #: f.process.cc:147 msgid "base" msgstr "" #: f.process.cc:150 msgid "adder" msgstr "" #: f.process.cc:155 msgid "New Location" msgstr "" #: f.process.cc:160 msgid "New File Type" msgstr "" #: f.process.cc:169 f.process.cc:1390 msgid "Color Depth:" msgstr "" #: f.process.cc:175 f.process.cc:2053 msgid "max. Width" msgstr "" #: f.process.cc:184 msgid "Delete Originals" msgstr "" #: f.process.cc:185 f.process.cc:821 msgid "Copy Metadata" msgstr "" #: f.process.cc:199 f.process.cc:824 msgid "Overlay Image" msgstr "" #: f.process.cc:202 f.warp.cc:4547 msgid "Width %" msgstr "" #: f.process.cc:207 msgid "Position" msgstr "" #: f.process.cc:219 msgid "Make constant size for screen:" msgstr "" #: f.process.cc:227 msgid "" "plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s" msgstr "" #: f.process.cc:371 #, c-format msgid "file type not supported: %s \n" msgstr "" #: f.process.cc:507 f.process.cc:2116 msgid "cannot create new file" msgstr "" #: f.process.cc:554 msgid "updating albums ..." msgstr "" #: f.process.cc:732 #, c-format msgid "invalid plugin: %s" msgstr "" #: f.process.cc:739 msgid "you must use either $s or $oldname" msgstr "" #: f.process.cc:744 msgid "$s plugin needs base and adder" msgstr "" #: f.process.cc:749 msgid "base and adder need $s plugin" msgstr "" #: f.process.cc:763 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "" #: f.process.cc:770 msgid "specify overlay image file" msgstr "" #: f.process.cc:786 msgid "specify overlay position" msgstr "" #: f.process.cc:814 #, c-format msgid "Convert %d image files" msgstr "" #: f.process.cc:816 msgid "Convert to" msgstr "" #: f.process.cc:818 msgid "Resize within" msgstr "" #: f.process.cc:819 msgid "Output to" msgstr "" #: f.process.cc:825 msgid "*** Delete Originals ***" msgstr "" #: f.process.cc:826 msgid "*** Replace Originals ***" msgstr "" #: f.process.cc:827 msgid "PROCEED?" msgstr "" #: f.process.cc:983 f.widgets.cc:589 msgid "Batch Upright" msgstr "" #: f.process.cc:989 msgid "Survey all files" msgstr "" #: f.process.cc:1029 msgid "file cannot be read" msgstr "" #: f.process.cc:1146 msgid "cannot select both options" msgstr "" #: f.process.cc:1187 f.widgets.cc:590 msgid "Batch Delete/Trash" msgstr "" #: f.process.cc:1192 msgid "delete" msgstr "" #: f.process.cc:1195 msgid "trash" msgstr "" #: f.process.cc:1258 msgid "Purging deleted files from albums \n" msgstr "" #: f.process.cc:1338 msgid "Batch Convert RAW Files" msgstr "" #: f.process.cc:1377 msgid "output location" msgstr "" #: f.process.cc:1382 msgid "File Type" msgstr "" #: f.process.cc:1406 msgid "amount" msgstr "" #: f.process.cc:1409 msgid "threshold" msgstr "" #: f.process.cc:1413 msgid "Fix dead pixels" msgstr "" #: f.process.cc:1416 msgid "dead pixel map file" msgstr "" #: f.process.cc:1419 msgid "Fix pixel bias" msgstr "" #: f.process.cc:1422 msgid "pixel bias map file" msgstr "" #: f.process.cc:1727 msgid "growisofs not installed" msgstr "" #: f.process.cc:1774 msgid "no DVD/BlueRay device found" msgstr "" #: f.process.cc:1797 msgid "Burn Images to DVD/BlueRay" msgstr "" #: f.process.cc:1802 msgid "Select device" msgstr "" #: f.process.cc:1889 f.widgets.cc:355 msgid "Create a file of selected image files" msgstr "" #: f.process.cc:1915 f.process.cc:1984 msgid "Output File" msgstr "" #: f.process.cc:1936 msgid "no input files selected" msgstr "" #: f.process.cc:1942 msgid "no output file selected" msgstr "" #: f.process.cc:2044 f.widgets.cc:594 msgid "Export Files" msgstr "" #: f.process.cc:2049 msgid "To Location" msgstr "" #: f.process.cc:2060 msgid "export metadata" msgstr "" #: f.process.cc:2091 msgid "file type not supported" msgstr "" #: f.process.cc:2173 msgid "location is not a folder" msgstr "" #: f.process.cc:2225 msgid "Script Files" msgstr "" #: f.process.cc:2229 msgid "begin making a script file" msgstr "" #: f.process.cc:2232 msgid "finish making a script file" msgstr "" #: f.process.cc:2276 msgid "script already started" msgstr "" #: f.process.cc:2280 msgid "start a new script file" msgstr "" #: f.process.cc:2301 msgid "perform edits to be included in the script file" msgstr "" #: f.process.cc:2318 msgid "script file error" msgstr "" #: f.process.cc:2323 #, c-format msgid "%s added to script" msgstr "" #: f.process.cc:2333 msgid "no script file was started" msgstr "" #: f.process.cc:2341 msgid "script file closed" msgstr "" #: f.process.cc:2374 f.process.cc:2395 msgid "no script files found" msgstr "" #: f.process.cc:2418 #, c-format msgid "" "script error: %s \n" " %s" msgstr "" #: f.process.cc:2440 #, c-format msgid "unknown edit function: %s" msgstr "" #: f.process.cc:2452 #, c-format msgid "load widgets failed: %s" msgstr "" #: f.process.cc:2468 #, c-format msgid "script file format error: %s" msgstr "" #: f.process.cc:2494 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" #: f.process.cc:2511 msgid "script complete" msgstr "" #: f.process.cc:2553 f.widgets.cc:603 msgid "Batch Script" msgstr "" #: f.process.cc:2560 msgid "Select Script" msgstr "" #: f.process.cc:2561 msgid "script file to run" msgstr "" #: f.tools.cc:93 msgid "Folders for image files (subfolders included automatically)." msgstr "" #: f.tools.cc:95 msgid "Select to add, click on X to delete." msgstr "" #: f.tools.cc:96 msgid "folder for thumbnails" msgstr "" #: f.tools.cc:97 msgid "extra metadata items to include in index" msgstr "" #: f.tools.cc:98 msgid "force a full re-index of all image files" msgstr "" #: f.tools.cc:99 msgid "" "Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" #: f.tools.cc:136 msgid "Index Image Files" msgstr "" #: f.tools.cc:324 msgid "Choose top image folders" msgstr "" #: f.tools.cc:325 msgid "Choose thumbnail folder" msgstr "" #: f.tools.cc:326 msgid "" "All image files will be re-indexed. \n" " Continue?" msgstr "" #: f.tools.cc:442 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:448 #, c-format msgid "" "Thumbnails folder: %s \n" "Please remove." msgstr "" #: f.tools.cc:451 #, c-format msgid "" "Thumbnails folder: \n" " %s \n" "must be named .../thumbnails" msgstr "" #: f.tools.cc:454 #, c-format msgid "" "Duplicate or nested folders: \n" " %s \n" " %s \n" "Please remove." msgstr "" #: f.tools.cc:537 msgid "specify at least 1 top image folder" msgstr "" #: f.tools.cc:542 msgid "specify 1 thumbnail folder" msgstr "" #: f.tools.cc:649 msgid "build index" msgstr "" #: f.tools.cc:778 msgid "Top folders have no images" msgstr "" #: f.tools.cc:785 #, c-format msgid "" "0 old files found, %d new files found.\n" "A full re-index is required. Continue?" msgstr "" #: f.tools.cc:1286 msgid "Cancel image index function?" msgstr "" #: f.tools.cc:1447 #, c-format msgid "" "Do you want to move Fotoxx home? \n" " from: %s \n" " to: %s" msgstr "" #: f.tools.cc:1449 msgid "moving files ..." msgstr "" #: f.tools.cc:1473 msgid "new Fotoxx home folder" msgstr "" #: f.tools.cc:1493 msgid "new location name contains a space" msgstr "" #: f.tools.cc:1533 msgid "index_config file has no thumbnails folder" msgstr "" #: f.tools.cc:1541 msgid "update thumbnail folder failed" msgstr "" #: f.tools.cc:1544 msgid "thumbnails folder not changed" msgstr "" #: f.tools.cc:1545 #, c-format msgid "new thumbnails folder: %s/thumbnails" msgstr "" #: f.tools.cc:1556 msgid "Fotoxx will restart" msgstr "" #: f.tools.cc:1572 msgid "Recent Files Gallery" msgstr "" #: f.tools.cc:1573 msgid "Newest Files Gallery" msgstr "" #: f.tools.cc:1574 msgid "Specific Gallery" msgstr "" #: f.tools.cc:1575 msgid "Album Gallery" msgstr "" #: f.tools.cc:1576 msgid "Previous Gallery" msgstr "" #: f.tools.cc:1577 msgid "Previous File" msgstr "" #: f.tools.cc:1578 msgid "Specific File" msgstr "" #: f.tools.cc:1579 f.widgets.cc:434 msgid "Blank Window" msgstr "" #: f.tools.cc:1604 #, c-format msgid "%c of scale" msgstr "" #: f.tools.cc:1647 msgid "Preferences and Settings" msgstr "" #: f.tools.cc:1650 msgid "Startup Display:" msgstr "" #: f.tools.cc:1656 msgid "Background Colors:" msgstr "" #: f.tools.cc:1658 msgid "F-View" msgstr "" #: f.tools.cc:1661 msgid "G-View" msgstr "" #: f.tools.cc:1665 msgid "Menu Style:" msgstr "" #: f.tools.cc:1667 msgid "Icons" msgstr "" #: f.tools.cc:1669 msgid "Both" msgstr "" #: f.tools.cc:1671 msgid "Icon size" msgstr "" #: f.tools.cc:1675 msgid "Menu Colors:" msgstr "" #: f.tools.cc:1684 msgid "Dialog Font:" msgstr "" #: f.tools.cc:1703 msgid "JPEG file save quality:" msgstr "" #: f.tools.cc:1706 msgid "(90 = high quality)" msgstr "" #: f.tools.cc:1709 msgid "TIFF file compression method" msgstr "" #: f.tools.cc:1715 msgid "PNG file compression level" msgstr "" #: f.tools.cc:1721 msgid "Curve Node Separation, Capture Range:" msgstr "" #: f.tools.cc:1727 msgid "Map Marker Size:" msgstr "" #: f.tools.cc:1733 msgid "Show Images (F-view, G-view):" msgstr "" #: f.tools.cc:1735 msgid "last version only" msgstr "" #: f.tools.cc:1736 msgid "all images" msgstr "" #: f.tools.cc:1739 msgid "Image Position in Window:" msgstr "" #: f.tools.cc:1741 msgid "centered" msgstr "" #: f.tools.cc:1742 msgid "right side" msgstr "" #: f.tools.cc:1745 msgid "Image Index Level:" msgstr "" #: f.tools.cc:1749 msgid "Fotoxx started directly (2)" msgstr "" #: f.tools.cc:1753 msgid "Fotoxx started by file manager (1)" msgstr "" #: f.tools.cc:1756 msgid "RAW File Loader:" msgstr "" #: f.tools.cc:1762 msgid "RAW Conversion Options:" msgstr "" #: f.tools.cc:1765 msgid "extend dynamic range for dim images" msgstr "" #: f.tools.cc:1768 msgid "use embedded image as a guide" msgstr "" #: f.tools.cc:1771 msgid "RAW File Types:" msgstr "" #: f.tools.cc:1776 msgid "Video File Types:" msgstr "" #: f.tools.cc:1781 msgid "Video File Play Command:" msgstr "" #: f.tools.cc:1937 msgid "Select startup folder" msgstr "" #: f.tools.cc:1944 msgid "Select startup image file" msgstr "" #: f.tools.cc:1951 msgid "Select startup album" msgstr "" #: f.tools.cc:1976 msgid "rawtherapee-cli (rawtherapee) is not installed" msgstr "" #: f.tools.cc:2006 msgid "startup folder is invalid" msgstr "" #: f.tools.cc:2016 msgid "startup file is invalid" msgstr "" #: f.tools.cc:2216 f.widgets.cc:372 msgid "Keyboard Shortcuts" msgstr "" #: f.tools.cc:2222 msgid "Reserved Shortcuts \n" msgstr "" #: f.tools.cc:2223 msgid " Z Toggle 1x / fit window \n" msgstr "" #: f.tools.cc:2224 msgid " F1 User Guide, Context Help \n" msgstr "" #: f.tools.cc:2225 msgid " F10 Full Screen with menus \n" msgstr "" #: f.tools.cc:2226 msgid " F11 Full Screen without menus \n" msgstr "" #: f.tools.cc:2227 msgid " Escape Quit dialog, Quit Fotoxx \n" msgstr "" #: f.tools.cc:2228 msgid " Delete Delete/Trash \n" msgstr "" #: f.tools.cc:2229 msgid " Arrow keys Navigation \n" msgstr "" #: f.tools.cc:2230 msgid " Page keys Navigation \n" msgstr "" #: f.tools.cc:2231 msgid " Home/End Navigation \n" msgstr "" #: f.tools.cc:2295 msgid "Edit KB Shortcuts" msgstr "" #: f.tools.cc:2302 msgid "shortcut key:" msgstr "" #: f.tools.cc:2303 msgid "(enter key)" msgstr "" #: f.tools.cc:2304 f.tools.cc:2451 f.tools.cc:2554 msgid "(no selection)" msgstr "" #: f.tools.cc:2445 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "" #: f.tools.cc:2672 msgid "Brightness Distribution" msgstr "" #: f.tools.cc:2866 msgid "" "Drag mouse on image. \n" "Left click to cancel." msgstr "" #: f.tools.cc:2893 f.widgets.cc:612 msgid "Magnify Image" msgstr "" #: f.tools.cc:2902 msgid "X-size" msgstr "" #: f.tools.cc:3210 msgid "Find Duplicate Images" msgstr "" #: f.tools.cc:3213 msgid "All files" msgstr "" #: f.tools.cc:3214 msgid "Current gallery" msgstr "" #: f.tools.cc:3217 msgid "File count:" msgstr "" #: f.tools.cc:3221 msgid "Thumbnail size" msgstr "" #: f.tools.cc:3226 msgid "Pixel difference" msgstr "" #: f.tools.cc:3229 msgid "Pixel count" msgstr "" #: f.tools.cc:3237 msgid "Duplicates:" msgstr "" #: f.tools.cc:3567 msgid "too many files, cannot continue" msgstr "" #: f.tools.cc:3647 msgid "Click image to select pixels." msgstr "" #: f.tools.cc:3690 f.widgets.cc:614 msgid "Show RGB" msgstr "" #: f.tools.cc:3948 msgid "Change Color Profile" msgstr "" #: f.tools.cc:3952 msgid "input profile" msgstr "" #: f.tools.cc:3956 msgid "output profile" msgstr "" #: f.tools.cc:3977 msgid "Unable to change EXIF color profile" msgstr "" #: f.tools.cc:3979 msgid "automatic new version created" msgstr "" #: f.tools.cc:3988 msgid "color profile" msgstr "" #: f.tools.cc:4036 f.tools.cc:4042 #, c-format msgid "unknown cms profile %s" msgstr "" #: f.tools.cc:4144 f.widgets.cc:616 msgid "Calibrate Printer" msgstr "" #: f.tools.cc:4170 msgid "print color chart" msgstr "" #: f.tools.cc:4171 msgid "scan and save color chart" msgstr "" #: f.tools.cc:4172 msgid "align and trim color chart" msgstr "" #: f.tools.cc:4173 msgid "open and process color chart" msgstr "" #: f.tools.cc:4174 msgid "print image with revised colors" msgstr "" #: f.tools.cc:4315 #, c-format msgid "" "Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/" msgstr "" #: f.tools.cc:4330 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY." msgstr "" #: f.tools.cc:4362 msgid "Open the trimmed color chart file" msgstr "" #: f.tools.cc:4495 msgid "" "Set the name for the output calibration file \n" "[your calibration name].dat" msgstr "" #: f.tools.cc:4535 msgid "Color map file to use" msgstr "" #: f.tools.cc:4555 msgid "Select the image file to print." msgstr "" #: f.tools.cc:4596 msgid "file format error" msgstr "" #: f.tools.cc:4620 msgid "converting colors..." msgstr "" #: f.tools.cc:4720 msgid "Image colors are converted for printing." msgstr "" #: f.tools.cc:4769 f.widgets.cc:617 msgid "Grid Lines" msgstr "" #: f.tools.cc:4778 msgid "x-spacing" msgstr "" #: f.tools.cc:4779 msgid "x-count" msgstr "" #: f.tools.cc:4780 msgid "x-enable" msgstr "" #: f.tools.cc:4786 msgid "y-spacing" msgstr "" #: f.tools.cc:4787 msgid "y-count" msgstr "" #: f.tools.cc:4788 msgid "y-enable" msgstr "" #: f.tools.cc:4909 f.widgets.cc:618 msgid "Line Color" msgstr "" #: f.tools.cc:4967 msgid "Darkest and Brightest Pixels" msgstr "" #: f.tools.cc:4991 msgid "Dark Limit" msgstr "" #: f.tools.cc:4992 msgid "Bright Limit" msgstr "" #: f.tools.cc:5117 msgid "Map RAW Pixel Bias" msgstr "" #: f.tools.cc:5124 msgid "mean RGB:" msgstr "" #: f.tools.cc:5210 msgid "select at least 10 RAW image files" msgstr "" #: f.tools.cc:5230 #, c-format msgid "" "cannot read file \n" " %s" msgstr "" #: f.tools.cc:5235 #, c-format msgid "" "not a RAW file \n" " %s" msgstr "" #: f.tools.cc:5249 #, c-format msgid "dimensions do not match: %s" msgstr "" #: f.tools.cc:5366 f.tools.cc:5449 msgid "Pixel Bias Map file" msgstr "" #: f.tools.cc:5494 msgid "invalid pixel bias map file" msgstr "" #: f.tools.cc:5524 msgid "image dimensions do not match pixel bias file" msgstr "" #: f.tools.cc:5610 msgid "Map RAW Dead Pixels" msgstr "" #: f.tools.cc:5614 msgid "gray RAW image file" msgstr "" #: f.tools.cc:5618 msgid "RGB threshold" msgstr "" #: f.tools.cc:5621 msgid "dead pixels found:" msgstr "" #: f.tools.cc:5658 msgid "not a RAW file" msgstr "" #: f.tools.cc:5664 msgid "cannot load RAW file" msgstr "" #: f.tools.cc:5727 msgid "choose a gray RAW file" msgstr "" #: f.tools.cc:5858 f.tools.cc:5903 msgid "dead pixels file" msgstr "" #: f.tools.cc:5955 msgid "invalid dead pixels file" msgstr "" #: f.tools.cc:5975 msgid "no dead pixels data available" msgstr "" #: f.tools.cc:5980 msgid "image dimensions do not match dead pixels file" msgstr "" #: f.tools.cc:6048 msgid "" "Brightness should show a gradual ramp \n" "extending all the way to the edges." msgstr "" #: f.tools.cc:6208 msgid "Available Translations" msgstr "" #: f.tools.cc:6212 msgid "Set Language" msgstr "" #: f.warp.cc:110 f.widgets.cc:556 msgid "Unbend" msgstr "" #: f.warp.cc:388 msgid "" " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle." msgstr "" #: f.warp.cc:405 msgid "Perspective Correction" msgstr "" #: f.warp.cc:638 msgid "must have 4 corners" msgstr "" #: f.warp.cc:763 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 "" #: f.warp.cc:776 f.widgets.cc:558 msgid "Warp area" msgstr "" #: f.warp.cc:781 msgid "start warp" msgstr "" #: f.warp.cc:1182 f.warp.cc:1494 msgid "" " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" #: f.warp.cc:1200 f.widgets.cc:559 msgid "Warp curved" msgstr "" #: f.warp.cc:1512 f.widgets.cc:560 msgid "Warp linear" msgstr "" #: f.warp.cc:1825 msgid "" " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" #: f.warp.cc:1841 f.widgets.cc:561 msgid "Warp affine" msgstr "" #: f.warp.cc:2174 msgid "" " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n" msgstr "" #: f.warp.cc:2201 f.widgets.cc:562 msgid "Unwarp Closeup" msgstr "" #: f.warp.cc:2379 msgid "Flatten Book Page" msgstr "" #: f.warp.cc:2380 msgid "" "Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: " msgstr "" #: f.warp.cc:2383 msgid "Stretch curved-down surfaces:" msgstr "" #: f.warp.cc:2438 msgid "Top:" msgstr "" #: f.warp.cc:2441 msgid "Bottom:" msgstr "" #: f.warp.cc:2830 msgid "" " Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]." msgstr "" #: f.warp.cc:2846 f.widgets.cc:564 msgid "Area Rescale" msgstr "" #: f.warp.cc:2869 msgid "select areas first" msgstr "" #: f.warp.cc:3088 f.widgets.cc:565 msgid "Make Waves" msgstr "" #: f.warp.cc:3095 msgid "wavelength" msgstr "" #: f.warp.cc:3097 msgid "variance" msgstr "" #: f.warp.cc:3108 msgid "perspective" msgstr "" #: f.warp.cc:3285 f.warp.cc:3321 f.widgets.cc:566 msgid "Twist" msgstr "" #: f.warp.cc:3318 f.warp.cc:3609 f.warp.cc:3835 f.warp.cc:4060 msgid "Drag mouse to set center" msgstr "" #: f.warp.cc:3565 msgid "Spherical Projection" msgstr "" #: f.warp.cc:3792 msgid "Stretch Image" msgstr "" #: f.warp.cc:4057 f.widgets.cc:569 msgid "Inside-out" msgstr "" #: f.warp.cc:4065 f.warp.cc:4314 msgid "Center Hole" msgstr "" #: f.warp.cc:4266 msgid "image width must be greater than height" msgstr "" #: f.warp.cc:4319 msgid "Cut Top" msgstr "" #: f.warp.cc:4324 msgid "Cut Bottom" msgstr "" #: f.warp.cc:4332 msgid "Reverse R" msgstr "" #: f.warp.cc:4333 msgid "Theta" msgstr "" #: f.warp.cc:4545 msgid "Click mouse to change center" msgstr "" #: f.warp.cc:4550 msgid "Rim %" msgstr "" #: f.widgets.cc:105 msgid "Album" msgstr "" #: f.widgets.cc:107 msgid "TOP" msgstr "" #: f.widgets.cc:167 msgid "Rename, copy/move, delete, print" msgstr "" #: f.widgets.cc:168 msgid "Thumbnails, bookmarks, albums, slide show" msgstr "" #: f.widgets.cc:169 msgid "View images by map location" msgstr "" #: f.widgets.cc:170 msgid "Custom favorites menu" msgstr "" #: f.widgets.cc:171 msgid "Left/right click: previous/next (also arrow keys)" msgstr "" #: f.widgets.cc:172 msgid "Left/right click: larger/smaller image/thumbnails" msgstr "" #: f.widgets.cc:173 msgid "Save modified file as new version or new file" msgstr "" #: f.widgets.cc:174 msgid "Metadata: captions, tags, ratings, geotags, search images" msgstr "" #: f.widgets.cc:175 msgid "Select areas to edit separately, save, copy and paste" msgstr "" #: f.widgets.cc:176 msgid "" "Left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit" msgstr "" #: f.widgets.cc:179 msgid "Image edit basic functions" msgstr "" #: f.widgets.cc:180 msgid "Image repair and enhance" msgstr "" #: f.widgets.cc:181 msgid "Artistic effects (filters)" msgstr "" #: f.widgets.cc:182 msgid "Image warp, unwarp, transform" msgstr "" #: f.widgets.cc:183 msgid "HDR, HDF, panorama, stack, mashup" msgstr "" #: f.widgets.cc:184 msgid "Batch processing, custom scripts" msgstr "" #: f.widgets.cc:185 msgid "Image index, user preferences, shortcuts, utilities" msgstr "" #: f.widgets.cc:186 msgid "User Guide, recent changes, log file, about" msgstr "" #: f.widgets.cc:189 msgid "Current File (R-click or key F)" msgstr "" #: f.widgets.cc:190 msgid "Open a parallel Fotoxx session" msgstr "" #: f.widgets.cc:191 msgid "Cycle 2 Prior Files" msgstr "" #: f.widgets.cc:192 msgid "Cycle 3 Prior Files" msgstr "" #: f.widgets.cc:193 msgid "View a 360 degree panorama image file" msgstr "" #: f.widgets.cc:194 msgid "Change file name" msgstr "" #: f.widgets.cc:195 msgid "View and change file permissions" msgstr "" #: f.widgets.cc:196 msgid "Create a blank image" msgstr "" #: f.widgets.cc:197 msgid "Toggle - blank or restore window" msgstr "" #: f.widgets.cc:198 msgid "Copy or Move file to new location" msgstr "" #: f.widgets.cc:199 msgid "Copy file to the desktop" msgstr "" #: f.widgets.cc:200 msgid "Copy file to the clipboard" msgstr "" #: f.widgets.cc:201 msgid "Set file as desktop wallpaper (GNOME)" msgstr "" #: f.widgets.cc:202 msgid "Show location on Internet map" msgstr "" #: f.widgets.cc:203 msgid "Delete or trash file" msgstr "" #: f.widgets.cc:204 msgid "Print the current image" msgstr "" #: f.widgets.cc:205 msgid "Print current image with adjusted colors" msgstr "" #: f.widgets.cc:206 msgid "Quit Fotoxx" msgstr "" #: f.widgets.cc:209 msgid "Thumbnail Gallery (R-click or key G)" msgstr "" #: f.widgets.cc:210 msgid "Gallery view with thumbnails and basic metadata" msgstr "" #: f.widgets.cc:211 msgid "Gallery view with small thumbnails and file names" msgstr "" #: f.widgets.cc:212 msgid "Gallery of recently viewed image files" msgstr "" #: f.widgets.cc:213 msgid "Gallery of newest image files" msgstr "" #: f.widgets.cc:214 msgid "Jump to beginning [home]" msgstr "" #: f.widgets.cc:215 msgid "Jump to end [end]" msgstr "" #: f.widgets.cc:216 msgid "Set gallery from current image file" msgstr "" #: f.widgets.cc:217 msgid "Change sort order" msgstr "" #: f.widgets.cc:218 msgid "List all folders, click any for gallery view" msgstr "" #: f.widgets.cc:219 msgid "select input files for album, batch, script functions" msgstr "" #: f.widgets.cc:220 msgid "Set and recall bookmarked image locations" msgstr "" #: f.widgets.cc:221 msgid "Organize images into albums" msgstr "" #: f.widgets.cc:222 msgid "Update albums for new file versions" msgstr "" #: f.widgets.cc:223 msgid "Mass update album files" msgstr "" #: f.widgets.cc:224 msgid "Save current gallery as album" msgstr "" #: f.widgets.cc:225 msgid "Start a slide show" msgstr "" #: f.widgets.cc:228 msgid "Maps (R-click or key M)" msgstr "" #: f.widgets.cc:229 msgid "Open Internet map" msgstr "" #: f.widgets.cc:230 msgid "Choose Internet map source" msgstr "" #: f.widgets.cc:231 msgid "Internet map locations" msgstr "" #: f.widgets.cc:232 msgid "Open file map" msgstr "" #: f.widgets.cc:233 msgid "Choose file map" msgstr "" #: f.widgets.cc:234 msgid "Set map markers for all images or current gallery" msgstr "" #: f.widgets.cc:237 msgid "List a few key metadata items" msgstr "" #: f.widgets.cc:238 msgid "List all metadata items" msgstr "" #: f.widgets.cc:239 msgid "Edit image tags/geotags/caption/rating ..." msgstr "" #: f.widgets.cc:240 msgid "Define tags (keywords) used for searching images" msgstr "" #: f.widgets.cc:241 msgid "Edit any image metadata" msgstr "" #: f.widgets.cc:242 msgid "Remove selected image metadata" msgstr "" #: f.widgets.cc:243 msgid "Show file name, captions, comments" msgstr "" #: f.widgets.cc:244 msgid "Find all images for a location [date]" msgstr "" #: f.widgets.cc:245 msgid "Show image counts by month, select and report" msgstr "" #: f.widgets.cc:246 msgid "Find images meeting search criteria" msgstr "" #: f.widgets.cc:249 msgid "Select object or area for editing" msgstr "" #: f.widgets.cc:250 msgid "Select hairy or irregular edge" msgstr "" #: f.widgets.cc:251 msgid "Find a gap in an area outline" msgstr "" #: f.widgets.cc:252 msgid "Show (outline) existing area" msgstr "" #: f.widgets.cc:253 msgid "Hide existing area" msgstr "" #: f.widgets.cc:254 msgid "Enable area for editing" msgstr "" #: f.widgets.cc:255 msgid "Disable area for editing" msgstr "" #: f.widgets.cc:256 msgid "Reverse existing area" msgstr "" #: f.widgets.cc:257 msgid "Erase existing area" msgstr "" #: f.widgets.cc:258 msgid "Copy area for later pasting into image" msgstr "" #: f.widgets.cc:259 msgid "Paste previously copied area into image" msgstr "" #: f.widgets.cc:260 msgid "Open a file and paste as area into image" msgstr "" #: f.widgets.cc:261 msgid "Save area to a file with transparency" msgstr "" #: f.widgets.cc:264 msgid "Trim/Crop margins and/or Rotate" msgstr "" #: f.widgets.cc:265 msgid "Auto upright a rotated image based on EXIF data" msgstr "" #: f.widgets.cc:266 msgid "Adjust brightness, contrast, color" msgstr "" #: f.widgets.cc:267 msgid "Change pixel dimensions" msgstr "" #: f.widgets.cc:268 msgid "Adjust color using RGB or CMY colors" msgstr "" #: f.widgets.cc:269 msgid "Adjust color using HSL colors" msgstr "" #: f.widgets.cc:270 msgid "Draw on image: text, line/arrow, box, ellipse" msgstr "" #: f.widgets.cc:271 msgid "Paint image pixels using the mouse" msgstr "" #: f.widgets.cc:272 msgid "Copy pixels within an image using the mouse" msgstr "" #: f.widgets.cc:273 msgid "Copy pixels from one image to another using the mouse" msgstr "" #: f.widgets.cc:274 msgid "Paint edit function gradually with mouse" msgstr "" #: f.widgets.cc:275 msgid "Incrementally undo prior edits gradually with mouse" msgstr "" #: f.widgets.cc:276 msgid "Edit plugins menu or run a plugin function" msgstr "" #: f.widgets.cc:277 msgid "Specialized program for editing RAW files" msgstr "" #: f.widgets.cc:280 f.widgets.cc:281 msgid "Fast auto enhance that may work OK" msgstr "" #: f.widgets.cc:282 msgid "Edit brightness distribution" msgstr "" #: f.widgets.cc:283 msgid "Magnify brightness gradients to enhance details" msgstr "" #: f.widgets.cc:284 msgid "Flatten brightness distribution to enhance details" msgstr "" #: f.widgets.cc:285 msgid "Rescale RGB - reduce color caste and fog/haze" msgstr "" #: f.widgets.cc:286 msgid "Make the image look sharper" msgstr "" #: f.widgets.cc:287 msgid "Blur the image, different methods" msgstr "" #: f.widgets.cc:288 msgid "Filter noise from low-light photos" msgstr "" #: f.widgets.cc:289 msgid "Fix red-eyes from electronic flash" msgstr "" #: f.widgets.cc:290 msgid "Match colors on one image with another" msgstr "" #: f.widgets.cc:291 msgid "Remove unwanted objects" msgstr "" #: f.widgets.cc:292 msgid "Fix color fringes in outer areas of an image" msgstr "" #: f.widgets.cc:293 msgid "Fix color band on dark/bright feature edges" msgstr "" #: f.widgets.cc:294 msgid "Change brightness or color radially" msgstr "" #: f.widgets.cc:295 msgid "Remove dust spots from scanned slides" msgstr "" #: f.widgets.cc:298 msgid "Convert to simulated sketch" msgstr "" #: f.widgets.cc:299 msgid "Convert image into a cartoon drawing" msgstr "" #: f.widgets.cc:300 msgid "Convert to line drawing (edge detection)" msgstr "" #: f.widgets.cc:301 msgid "Create an embossed or 3D appearance" msgstr "" #: f.widgets.cc:302 msgid "Convert to square tiles" msgstr "" #: f.widgets.cc:303 msgid "Convert to dithered dots" msgstr "" #: f.widgets.cc:304 msgid "Convert into a simulated painting" msgstr "" #: f.widgets.cc:305 msgid "Add texture to an image" msgstr "" #: f.widgets.cc:306 msgid "Tile image with a repeating pattern" msgstr "" #: f.widgets.cc:307 msgid "Create a mosaic with tiles made from all images" msgstr "" #: f.widgets.cc:308 msgid "Make BW/color, negative/positive, sepia" msgstr "" #: f.widgets.cc:309 msgid "Reduce color depth (posterize)" msgstr "" #: f.widgets.cc:310 msgid "Shift/convert colors into other colors" msgstr "" #: f.widgets.cc:311 msgid "Change color hue using an algorithm" msgstr "" #: f.widgets.cc:312 msgid "Add a brightness/color ramp across the image" msgstr "" #: f.widgets.cc:313 msgid "Paint image transparency using the mouse" msgstr "" #: f.widgets.cc:314 msgid "Mirror image horizontally or vertically" msgstr "" #: f.widgets.cc:315 msgid "Process an image using a custom kernel" msgstr "" #: f.widgets.cc:318 msgid "Remove curvature, esp. panoramas" msgstr "" #: f.widgets.cc:319 msgid "Straighten objects seen from an angle" msgstr "" #: f.widgets.cc:320 msgid "Distort image areas using the mouse" msgstr "" #: f.widgets.cc:321 msgid "Unwarp closeup face photo to remove distortion" msgstr "" #: f.widgets.cc:322 f.widgets.cc:323 f.widgets.cc:324 msgid "Distort the whole image using the mouse" msgstr "" #: f.widgets.cc:325 msgid "Flatten a photographed book page" msgstr "" #: f.widgets.cc:326 msgid "Rescale image outside selected areas" msgstr "" #: f.widgets.cc:327 msgid "Warp an image with a wave pattern" msgstr "" #: f.widgets.cc:328 msgid "Twist image centered at mouse position" msgstr "" #: f.widgets.cc:329 msgid "Make a spherical projection of an image" msgstr "" #: f.widgets.cc:330 msgid "Image scale increases from center to edge" msgstr "" #: f.widgets.cc:331 msgid "Turn an image inside-out" msgstr "" #: f.widgets.cc:332 msgid "Convert an image into a Tiny Planet" msgstr "" #: f.widgets.cc:333 msgid "Generate an inward spiraling recursive image" msgstr "" #: f.widgets.cc:336 msgid "Combine bright/dark images for better detail" msgstr "" #: f.widgets.cc:337 msgid "Combine near/far focus images for deeper focus" msgstr "" #: f.widgets.cc:338 msgid "Combine images to erase passing people, etc." msgstr "" #: f.widgets.cc:339 msgid "Combine noisy images into a low-noise image" msgstr "" #: f.widgets.cc:340 msgid "Combine image layers, mouse select and expose" msgstr "" #: f.widgets.cc:341 msgid "Compare two images separated by sliding boundary" msgstr "" #: f.widgets.cc:342 msgid "Show differences between two images" msgstr "" #: f.widgets.cc:343 msgid "Combine images into a panorama" msgstr "" #: f.widgets.cc:344 msgid "Combine images into a vertical panorama" msgstr "" #: f.widgets.cc:345 msgid "Combine images into a panorama (panorama tools)" msgstr "" #: f.widgets.cc:346 msgid "Combine images into a montage of images" msgstr "" #: f.widgets.cc:347 msgid "Arrange images and text in a layout (montage)" msgstr "" #: f.widgets.cc:350 msgid "Rename/convert/resize/move multiple files" msgstr "" #: f.widgets.cc:351 msgid "Upright multiple rotated image files" msgstr "" #: f.widgets.cc:352 msgid "Delete or Trash multiple files" msgstr "" #: f.widgets.cc:353 msgid "Convert camera RAW files to tiff/png/jpeg" msgstr "" #: f.widgets.cc:354 msgid "Burn selected image files to DVD/BlueRay disc" msgstr "" #: f.widgets.cc:356 msgid "Export selected image files to a folder" msgstr "" #: f.widgets.cc:357 msgid "Add/remove tags for multiple images" msgstr "" #: f.widgets.cc:358 msgid "Convert tag names for all images" msgstr "" #: f.widgets.cc:359 msgid "change or shift photo dates/times" msgstr "" #: f.widgets.cc:360 msgid "Add/change/delete metadata for multiple images" msgstr "" #: f.widgets.cc:361 msgid "Report metadata for multiple images" msgstr "" #: f.widgets.cc:362 msgid "Add/revise geotags for multiple images" msgstr "" #: f.widgets.cc:363 msgid "Build a custom script with multiple edit functions" msgstr "" #: f.widgets.cc:364 msgid "Run custom script to edit the current image file" msgstr "" #: f.widgets.cc:365 msgid "Run custom script to edit a batch of image files" msgstr "" #: f.widgets.cc:368 msgid "Index new files and make thumbnails" msgstr "" #: f.widgets.cc:369 msgid "Quick incremental index update" msgstr "" #: f.widgets.cc:370 msgid "Move Fotoxx home folder" msgstr "" #: f.widgets.cc:371 msgid "User preferences and settings" msgstr "" #: f.widgets.cc:373 msgid "Show RGB brightness distribution" msgstr "" #: f.widgets.cc:374 msgid "Magnify image around the mouse position" msgstr "" #: f.widgets.cc:375 msgid "Search all image files and report duplicates" msgstr "" #: f.widgets.cc:376 msgid "Show RGB colors at mouse click" msgstr "" #: f.widgets.cc:377 msgid "Convert to another color profile" msgstr "" #: f.widgets.cc:378 msgid "Calibrate printer colors" msgstr "" #: f.widgets.cc:379 msgid "Show or revise grid lines" msgstr "" #: f.widgets.cc:380 msgid "Change color of foreground lines" msgstr "" #: f.widgets.cc:381 msgid "Highlight darkest and brightest pixels" msgstr "" #: f.widgets.cc:382 msgid "map raw pixel bias (camera sensor, vignette)" msgstr "" #: f.widgets.cc:383 msgid "map raw dead pixels (camera sensor)" msgstr "" #: f.widgets.cc:384 msgid "Chart to adjust monitor color" msgstr "" #: f.widgets.cc:385 msgid "Chart to adjust monitor gamma" msgstr "" #: f.widgets.cc:386 msgid "Change the GUI language" msgstr "" #: f.widgets.cc:387 msgid "Report missing translations" msgstr "" #: f.widgets.cc:388 msgid "Anonymous usage statistics" msgstr "" #: f.widgets.cc:389 msgid "Memory and CPU (to terminal/logfile)" msgstr "" #: f.widgets.cc:390 msgid "List files included in appimage container" msgstr "" #: f.widgets.cc:391 msgid "test crash report with source line numbers" msgstr "" #: f.widgets.cc:394 msgid "Read the user guide" msgstr "" #: f.widgets.cc:395 msgid "Recent user guide changes" msgstr "" #: f.widgets.cc:396 msgid "Overview of all edit functions" msgstr "" #: f.widgets.cc:397 msgid "List updates by Fotoxx version" msgstr "" #: f.widgets.cc:398 msgid "View the log file and error messages" msgstr "" #: f.widgets.cc:399 msgid "Fotoxx Man Page - summary of capabilities" msgstr "" #: f.widgets.cc:400 msgid "List command line parameters" msgstr "" #: f.widgets.cc:401 msgid "How to do Fotoxx translations" msgstr "" #: f.widgets.cc:402 msgid "Show the Fotoxx web page" msgstr "" #: f.widgets.cc:403 msgid "Fotoxx license - terms of use" msgstr "" #: f.widgets.cc:404 msgid "Fotoxx privacy policy" msgstr "" #: f.widgets.cc:405 msgid "Version, contact, credits" msgstr "" #: f.widgets.cc:425 msgid "File View F" msgstr "" #: f.widgets.cc:426 msgid "New Session" msgstr "" #: f.widgets.cc:427 f.widgets.cc:453 msgid "Source Folder" msgstr "" #: f.widgets.cc:428 msgid "Cycle 2" msgstr "" #: f.widgets.cc:429 msgid "Cycle 3" msgstr "" #: f.widgets.cc:430 msgid "View 360 Pano" msgstr "" #: f.widgets.cc:431 f.widgets.cc:820 fotoxx.h:1414 msgid "Rename" msgstr "" #: f.widgets.cc:433 msgid "Blank Image" msgstr "" #: f.widgets.cc:435 f.widgets.cc:822 msgid "Copy/Move" msgstr "" #: f.widgets.cc:436 f.widgets.cc:823 msgid "Copy to Desktop" msgstr "" #: f.widgets.cc:437 f.widgets.cc:824 msgid "Copy to Clipboard" msgstr "" #: f.widgets.cc:438 msgid "Set Wallpaper" msgstr "" #: f.widgets.cc:439 f.widgets.cc:839 msgid "Show on Map" msgstr "" #: f.widgets.cc:440 f.widgets.cc:840 msgid "Delete/Trash" msgstr "" #: f.widgets.cc:441 msgid "Print" msgstr "" #: f.widgets.cc:442 msgid "Print Calibrated" msgstr "" #: f.widgets.cc:443 fotoxx.h:1407 msgid "Quit" msgstr "" #: f.widgets.cc:446 msgid "Gallery View G" msgstr "" #: f.widgets.cc:447 msgid "Meta View" msgstr "" #: f.widgets.cc:448 msgid "List View" msgstr "" #: f.widgets.cc:449 msgid "Recent" msgstr "" #: f.widgets.cc:450 msgid "Newest" msgstr "" #: f.widgets.cc:451 msgid "GoTo First" msgstr "" #: f.widgets.cc:452 msgid "GoTo Last" msgstr "" #: f.widgets.cc:454 msgid "Sort Gallery" msgstr "" #: f.widgets.cc:455 msgid "All Folders" msgstr "" #: f.widgets.cc:456 fotoxx.h:1425 msgid "Select Files" msgstr "" #: f.widgets.cc:459 msgid "Update Albums" msgstr "" #: f.widgets.cc:461 msgid "Gallery to Album" msgstr "" #: f.widgets.cc:465 msgid "Map View M" msgstr "" #: f.widgets.cc:466 msgid "Net Map" msgstr "" #: f.widgets.cc:467 msgid "Net Source" msgstr "" #: f.widgets.cc:468 msgid "Net Locs" msgstr "" #: f.widgets.cc:469 msgid "File Map" msgstr "" #: f.widgets.cc:470 msgid "Choose Map" msgstr "" #: f.widgets.cc:471 msgid "Markers" msgstr "" #: f.widgets.cc:474 f.widgets.cc:816 msgid "View Meta" msgstr "" #: f.widgets.cc:475 f.widgets.cc:817 msgid "View All Meta" msgstr "" #: f.widgets.cc:476 f.widgets.cc:818 msgid "Edit Meta" msgstr "" #: f.widgets.cc:478 f.widgets.cc:819 msgid "Edit Any Meta" msgstr "" #: f.widgets.cc:479 msgid "Delete Meta" msgstr "" #: f.widgets.cc:480 msgid "Captions" msgstr "" #: f.widgets.cc:481 msgid "Places/Dates" msgstr "" #: f.widgets.cc:482 msgid "Timeline" msgstr "" #: f.widgets.cc:483 fotoxx.h:1422 msgid "Search" msgstr "" #: f.widgets.cc:486 fotoxx.h:1424 msgid "Select" msgstr "" #: f.widgets.cc:488 msgid "Find Gap" msgstr "" #: f.widgets.cc:489 fotoxx.h:1426 msgid "Show" msgstr "" #: f.widgets.cc:490 fotoxx.h:1367 msgid "Hide" msgstr "" #: f.widgets.cc:491 fotoxx.h:1351 msgid "Enable" msgstr "" #: f.widgets.cc:492 fotoxx.h:1346 msgid "Disable" msgstr "" #: f.widgets.cc:494 fotoxx.h:1333 msgid "Clear" msgstr "" #: f.widgets.cc:495 fotoxx.h:1340 msgid "Copy" msgstr "" #: f.widgets.cc:496 fotoxx.h:1399 msgid "Paste" msgstr "" #: f.widgets.cc:497 fotoxx.h:1376 msgid "Load" msgstr "" #: f.widgets.cc:498 f.widgets.cc:654 fotoxx.h:1421 zfuncs.cc:12499 msgid "Save" msgstr "" #: f.widgets.cc:507 msgid "Markup" msgstr "" #: f.widgets.cc:508 msgid "Paint Image" msgstr "" #: f.widgets.cc:509 msgid "Copy Pixels 1" msgstr "" #: f.widgets.cc:510 msgid "Copy Pixels 2" msgstr "" #: f.widgets.cc:513 msgid "Plugins" msgstr "" #: f.widgets.cc:514 f.widgets.cc:838 msgid "Raw Therapee" msgstr "" #: f.widgets.cc:517 msgid "Voodoo 1" msgstr "" #: f.widgets.cc:518 msgid "Voodoo 2" msgstr "" #: f.widgets.cc:519 f.widgets.cc:834 msgid "Brite Dist" msgstr "" #: f.widgets.cc:520 f.widgets.cc:836 msgid "Gradients" msgstr "" #: f.widgets.cc:521 f.widgets.cc:835 fotoxx.h:1360 msgid "Flatten" msgstr "" #: f.widgets.cc:522 msgid "Global Retx" msgstr "" #: f.widgets.cc:523 msgid "Zonal Retx" msgstr "" #: f.widgets.cc:526 msgid "Denoise" msgstr "" #: f.widgets.cc:527 msgid "Red Eyes" msgstr "" #: f.widgets.cc:528 msgid "Match Colors" msgstr "" #: f.widgets.cc:530 msgid "Chromatic1" msgstr "" #: f.widgets.cc:531 msgid "Chromatic2" msgstr "" #: f.widgets.cc:536 msgid "Sketch" msgstr "" #: f.widgets.cc:537 msgid "Cartoon" msgstr "" #: f.widgets.cc:541 msgid "Dither" msgstr "" #: f.widgets.cc:543 msgid "Texture" msgstr "" #: f.widgets.cc:545 msgid "Mosaic" msgstr "" #: f.widgets.cc:547 msgid "Color Depth" msgstr "" #: f.widgets.cc:550 msgid "Brite Ramp" msgstr "" #: f.widgets.cc:551 msgid "Paint Transp" msgstr "" #: f.widgets.cc:552 msgid "Mirror" msgstr "" #: f.widgets.cc:557 msgid "Perspective" msgstr "" #: f.widgets.cc:563 msgid "Flatten Book" msgstr "" #: f.widgets.cc:567 msgid "Sphere" msgstr "" #: f.widgets.cc:568 msgid "Stretch" msgstr "" #: f.widgets.cc:570 msgid "Tiny Planet" msgstr "" #: f.widgets.cc:571 msgid "Escher Spiral" msgstr "" #: f.widgets.cc:576 msgid "Stack/Paint" msgstr "" #: f.widgets.cc:577 msgid "Stack/Noise" msgstr "" #: f.widgets.cc:578 msgid "Stack/Layer" msgstr "" #: f.widgets.cc:579 msgid "Stack/Slider" msgstr "" #: f.widgets.cc:580 msgid "Image Diffs" msgstr "" #: f.widgets.cc:581 msgid "Panorama" msgstr "" #: f.widgets.cc:582 msgid "V. Panorama" msgstr "" #: f.widgets.cc:583 msgid "PT Panorama" msgstr "" #: f.widgets.cc:584 msgid "Mashup" msgstr "" #: f.widgets.cc:585 msgid "Montage" msgstr "" #: f.widgets.cc:591 msgid "Batch RAW" msgstr "" #: f.widgets.cc:592 msgid "Burn DVD/BlueRay" msgstr "" #: f.widgets.cc:593 msgid "Export File List" msgstr "" #: f.widgets.cc:595 msgid "Batch Tags" msgstr "" #: f.widgets.cc:597 msgid "Batch Photo Date" msgstr "" #: f.widgets.cc:598 msgid "Batch Change Meta" msgstr "" #: f.widgets.cc:599 msgid "Batch Report Meta" msgstr "" #: f.widgets.cc:601 msgid "Edit Script" msgstr "" #: f.widgets.cc:602 msgid "Run Script" msgstr "" #: f.widgets.cc:606 msgid "Index Files" msgstr "" #: f.widgets.cc:607 msgid "Quick Index" msgstr "" #: f.widgets.cc:608 msgid "Move Fotoxx Home" msgstr "" #: f.widgets.cc:609 msgid "Preferences" msgstr "" #: f.widgets.cc:610 msgid "KB Shortcuts" msgstr "" #: f.widgets.cc:611 msgid "RGB Distribution" msgstr "" #: f.widgets.cc:613 msgid "Find Duplicates" msgstr "" #: f.widgets.cc:615 msgid "Color Profile" msgstr "" #: f.widgets.cc:619 msgid "Dark/Bright Pixels" msgstr "" #: f.widgets.cc:620 msgid "Map Pixel Bias" msgstr "" #: f.widgets.cc:621 msgid "Map Dead Pixels" msgstr "" #: f.widgets.cc:622 msgid "Monitor Color" msgstr "" #: f.widgets.cc:623 msgid "Monitor Gamma" msgstr "" #: f.widgets.cc:624 msgid "Change Language" msgstr "" #: f.widgets.cc:625 msgid "Missing Translations" msgstr "" #: f.widgets.cc:626 zfuncs.cc:5885 msgid "Phone Home" msgstr "" #: f.widgets.cc:628 msgid "Show Resources" msgstr "" #: f.widgets.cc:629 msgid "Appimage Files" msgstr "" #: f.widgets.cc:630 msgid "Zappcrash Test" msgstr "" #: f.widgets.cc:649 msgid "Gallery" msgstr "" #: f.widgets.cc:650 msgid "Maps" msgstr "" #: f.widgets.cc:651 f.widgets.cc:1145 msgid "Favorites" msgstr "" #: f.widgets.cc:652 msgid "Prev/Next" msgstr "" #: f.widgets.cc:653 msgid "Zoom/" msgstr "" #: f.widgets.cc:655 msgid "Meta" msgstr "" #: f.widgets.cc:656 msgid "Areas" msgstr "" #: f.widgets.cc:657 msgid "Undo/Redo" msgstr "" #: f.widgets.cc:658 fotoxx.h:1350 msgid "Edit" msgstr "" #: f.widgets.cc:659 msgid "Enhance" msgstr "" #: f.widgets.cc:660 msgid "Effects" msgstr "" #: f.widgets.cc:662 msgid "Combine" msgstr "" #: f.widgets.cc:663 msgid "Process" msgstr "" #: f.widgets.cc:664 msgid "Tools" msgstr "" #: f.widgets.cc:815 msgid "Popup Image" msgstr "" #: f.widgets.cc:825 msgid "Add Selected Files Here" msgstr "" #: f.widgets.cc:826 msgid "Add Current File Here" msgstr "" #: f.widgets.cc:827 msgid "Remove from Album" msgstr "" #: f.widgets.cc:837 msgid "Select Area" msgstr "" #: f.widgets.cc:1105 f.widgets.cc:1124 msgid "Fotoxx Image Locations" msgstr "" #: f.widgets.cc:1173 #, c-format msgid "invalid menu name: %s" msgstr "" #: fotoxx.cc:257 msgid "read+write" msgstr "" #: fotoxx.cc:258 msgid "read only" msgstr "" #: fotoxx.cc:259 msgid "no access" msgstr "" #: fotoxx.cc:465 msgid "Please install missing programs:" msgstr "" #: fotoxx.cc:594 msgid "Index aborted" msgstr "" #: fotoxx.cc:772 msgid " Defer image file indexing:" msgstr "" #: fotoxx.cc:773 msgid "" " Fotoxx will start immediately \n" " View and edit image files will work normally \n" " Image search, batch and map functions will not work \n" " Thumbnail galleries will be slow" msgstr "" #: fotoxx.cc:778 msgid " Index image files now:" msgstr "" #: fotoxx.cc:779 msgid "" " Initial indexing may need considerable time \n" " Subsequent startups will be fast \n" " Full functionality will be available \n" " Thumbnail galleries will be fast" msgstr "" #: fotoxx.cc:784 msgid "" " Indexing time depends on the number of image files and the \n" " speed of your computer. This can be a few hundred to a few \n" " thousand per minute. After indexing is done, startup time \n" " should be quite fast. You can change index options later, \n" " using these menus: Tools > Index and Tools > Preferences. " msgstr "" #: fotoxx.cc:791 msgid "" "Main memory is too small to run Fotoxx. \n" "You can try anyway if you wish. \n" " Continue?" msgstr "" #: fotoxx.cc:826 msgid "Fotoxx First Startup" msgstr "" #: fotoxx.cc:981 msgid "(reduced)" msgstr "" #: fotoxx.cc:982 msgid "area active" msgstr "" #: fotoxx.cc:983 msgid "dialog open" msgstr "" #: fotoxx.cc:984 msgid "blocked" msgstr "" #: fotoxx.cc:1040 msgid "edits" msgstr "" #: fotoxx.cc:1958 msgid "Show Hidden" msgstr "" #: fotoxx.cc:3168 msgid "Exceed 50 anchor points" msgstr "" #: fotoxx.cc:3397 msgid "load curve from a file" msgstr "" #: fotoxx.cc:3464 msgid "curve file is invalid" msgstr "" #: fotoxx.cc:3478 msgid "save curve to a file" msgstr "" #: fotoxx.cc:3549 #, c-format msgid "" "File cannot be edited \n" " %s" msgstr "" #: fotoxx.cc:3562 msgid "Too many edits, please save image" msgstr "" #: fotoxx.cc:3567 msgid "this function cannot be scripted" msgstr "" #: fotoxx.cc:3584 msgid "" "Select area will be ignored. \n" "Continue?" msgstr "" #: fotoxx.cc:3590 msgid "" "Select area not active.\n" "Continue?" msgstr "" #: fotoxx.cc:3900 msgid "file data does not fit dialog" msgstr "" #: fotoxx.cc:3910 msgid "Save settings to a file" msgstr "" #: fotoxx.cc:4297 msgid "This action will discard changes to current image" msgstr "" #: fotoxx.cc:4298 msgid "prior function still active" msgstr "" #: fotoxx.cc:4299 fotoxx.h:1372 msgid "Keep" msgstr "" #: fotoxx.cc:4300 msgid "Discard" msgstr "" #: fotoxx.h:1315 msgid "Add" msgstr "" #: fotoxx.h:1316 msgid "Add All" msgstr "" #: fotoxx.h:1318 msgid "Amount" msgstr "" #: fotoxx.h:1319 msgid "Angle" msgstr "" #: fotoxx.h:1320 zfuncs.cc:7936 msgid "Apply" msgstr "" #: fotoxx.h:1321 msgid "Auto" msgstr "" #: fotoxx.h:1322 msgid "Black" msgstr "" #: fotoxx.h:1323 msgid "Blend Width" msgstr "" #: fotoxx.h:1324 msgid "Blue" msgstr "" #: fotoxx.h:1325 zfuncs.cc:12913 msgid "Bottom" msgstr "" #: fotoxx.h:1327 zfuncs.cc:7950 msgid "Browse" msgstr "" #: fotoxx.h:1328 msgid "Calculate" msgstr "" #: fotoxx.h:1329 zfuncs.cc:7936 zfuncs.cc:12528 zfuncs.cc:12680 msgid "Cancel" msgstr "" #: fotoxx.h:1330 msgid "Center" msgstr "" #: fotoxx.h:1331 msgid "Change" msgstr "" #: fotoxx.h:1332 msgid "Choose" msgstr "" #: fotoxx.h:1334 msgid "click thumbnail to select file" msgstr "" #: fotoxx.h:1335 msgid "Close" msgstr "" #: fotoxx.h:1336 msgid "Color" msgstr "" #: fotoxx.h:1337 msgid "COMPLETED" msgstr "" #: fotoxx.h:1338 msgid "continue" msgstr "" #: fotoxx.h:1341 msgid "Create" msgstr "" #: fotoxx.h:1342 msgid "Curve File:" msgstr "" #: fotoxx.h:1343 msgid "Cut" msgstr "" #: fotoxx.h:1344 msgid "Deband" msgstr "" #: fotoxx.h:1345 zfuncs.cc:7936 msgid "Delete" msgstr "" #: fotoxx.h:1347 msgid "Display" msgstr "" #: fotoxx.h:1348 msgid "Done" msgstr "" #: fotoxx.h:1349 msgid "edge" msgstr "" #: fotoxx.h:1352 msgid "Erase" msgstr "" #: fotoxx.h:1353 msgid "Fetch" msgstr "" #: fotoxx.h:1354 msgid "output file already exists" msgstr "" #: fotoxx.h:1355 msgid "file not found" msgstr "" #: fotoxx.h:1356 #, c-format msgid "file not found: %s" msgstr "" #: fotoxx.h:1357 #, c-format msgid "%d image files selected" msgstr "" #: fotoxx.h:1358 msgid "Find" msgstr "" #: fotoxx.h:1359 msgid "Finish" msgstr "" #: fotoxx.h:1361 msgid "Font" msgstr "" #: fotoxx.h:1362 #, c-format msgid "gallery truncated to %d images" msgstr "" #: fotoxx.h:1363 msgid "Green" msgstr "" #: fotoxx.h:1364 msgid "Grid" msgstr "" #: fotoxx.h:1365 zfuncs.cc:12943 msgid "Height" msgstr "" #: fotoxx.h:1368 zfuncs.cc:12935 msgid "Image" msgstr "" #: fotoxx.h:1369 msgid "Images" msgstr "" #: fotoxx.h:1370 msgid "Insert" msgstr "" #: fotoxx.h:1373 zfuncs.cc:12917 msgid "Left" msgstr "" #: fotoxx.h:1374 msgid "Length" msgstr "" #: fotoxx.h:1375 msgid "limit" msgstr "" #: fotoxx.h:1377 msgid "Magnify" msgstr "" #: fotoxx.h:1378 msgid "Make" msgstr "" #: fotoxx.h:1379 msgid "Map" msgstr "" #: fotoxx.h:1380 msgid "Match Level:" msgstr "" #: fotoxx.h:1381 msgid "Max" msgstr "" #: fotoxx.h:1382 msgid "Measure" msgstr "" #: fotoxx.h:1383 msgid "mouse radius" msgstr "" #: fotoxx.h:1384 msgid "Negative" msgstr "" #: fotoxx.h:1385 msgid "New" msgstr "" #: fotoxx.h:1386 msgid "Next" msgstr "" #: fotoxx.h:1387 zfuncs.cc:11875 msgid "No" msgstr "" #: fotoxx.h:1388 msgid "no write permission" msgstr "" #: fotoxx.h:1389 msgid "no images" msgstr "" #: fotoxx.h:1390 msgid "image index disabled" msgstr "" #: fotoxx.h:1391 msgid "no image files selected" msgstr "" #: fotoxx.h:1392 msgid "None" msgstr "" #: fotoxx.h:1393 msgid "no selection" msgstr "" #: fotoxx.h:1394 msgid "image index not updated" msgstr "" #: fotoxx.h:1395 msgid "opacity center" msgstr "" #: fotoxx.h:1396 msgid "opacity edge" msgstr "" #: fotoxx.h:1398 msgid "Paint Radius" msgstr "" #: fotoxx.h:1400 msgid "Pause" msgstr "" #: fotoxx.h:1401 msgid "Percent" msgstr "" #: fotoxx.h:1403 msgid "Presets" msgstr "" #: fotoxx.h:1404 msgid "Prev" msgstr "" #: fotoxx.h:1405 msgid "use previous input" msgstr "" #: fotoxx.h:1406 msgid "Proceed" msgstr "" #: fotoxx.h:1409 msgid "range" msgstr "" #: fotoxx.h:1410 msgid "Red" msgstr "" #: fotoxx.h:1411 msgid "Redo" msgstr "" #: fotoxx.h:1412 msgid "Reduce" msgstr "" #: fotoxx.h:1413 msgid "Remove" msgstr "" #: fotoxx.h:1415 msgid "Replace" msgstr "" #: fotoxx.h:1416 msgid "Reserved" msgstr "" #: fotoxx.h:1417 msgid "Reset" msgstr "" #: fotoxx.h:1418 zfuncs.cc:12921 msgid "Right" msgstr "" #: fotoxx.h:1419 msgid "Rotate" msgstr "" #: fotoxx.h:1420 msgid "Run" msgstr "" #: fotoxx.h:1423 msgid "Seconds" msgstr "" #: fotoxx.h:1427 msgid "Size" msgstr "" #: fotoxx.h:1428 msgid "Start" msgstr "" #: fotoxx.h:1429 msgid "Stop" msgstr "" #: fotoxx.h:1430 msgid "Strength" msgstr "" #: fotoxx.h:1431 msgid "the area is not finished" msgstr "" #: fotoxx.h:1432 msgid "Threshold" msgstr "" #: fotoxx.h:1433 zfuncs.cc:12909 msgid "Top" msgstr "" #: fotoxx.h:1434 msgid "Transparency" msgstr "" #: fotoxx.h:1435 msgid "Trash" msgstr "" #: fotoxx.h:1436 msgid "Trim" msgstr "" #: fotoxx.h:1437 msgid "Undo All" msgstr "" #: fotoxx.h:1438 msgid "Undo Last" msgstr "" #: fotoxx.h:1439 msgid "Undo" msgstr "" #: fotoxx.h:1440 msgid "Unfinish" msgstr "" #: fotoxx.h:1441 msgid "Update" msgstr "" #: fotoxx.h:1442 msgid "View" msgstr "" #: fotoxx.h:1443 msgid "Web" msgstr "" #: fotoxx.h:1444 msgid "White" msgstr "" #: fotoxx.h:1445 zfuncs.cc:12939 msgid "Width" msgstr "" #: fotoxx.h:1446 msgid "x-offset" msgstr "" #: fotoxx.h:1447 msgid "y-offset" msgstr "" #: fotoxx.h:1448 zfuncs.cc:11875 msgid "Yes" msgstr "" #: zfuncs.cc:1836 #, c-format msgid "" "create folder? \n" " %s" msgstr "" #: zfuncs.cc:5880 msgid "" "If you permit, a message is occasionally \n" "sent to the web host for usage statistics. \n" "Nothing is retained that can be associated \n" "with a person or computer or location." msgstr "" #: zfuncs.cc:6743 #, c-format msgid "cannot open file %s" msgstr "" #: zfuncs.cc:6766 msgid "save text to file" msgstr "" #: zfuncs.cc:7936 msgid "edit menu entry" msgstr "" #: zfuncs.cc:7940 msgid "menu text" msgstr "" #: zfuncs.cc:7941 msgid "menu func" msgstr "" #: zfuncs.cc:7942 msgid "menu icon" msgstr "" #: zfuncs.cc:7943 msgid "icon size" msgstr "" #: zfuncs.cc:7946 msgid "Bold" msgstr "" #: zfuncs.cc:7953 msgid "close window" msgstr "" #: zfuncs.cc:8000 msgid "select icon" msgstr "" #: zfuncs.cc:12000 zfuncs.cc:12896 msgid "cancel" msgstr "" #: zfuncs.cc:12489 msgid "choose file" msgstr "" #: zfuncs.cc:12494 msgid "choose files" msgstr "" #: zfuncs.cc:12505 msgid "choose folder" msgstr "" #: zfuncs.cc:12510 msgid "choose folders" msgstr "" #: zfuncs.cc:12515 msgid "create folder" msgstr "" #: zfuncs.cc:12522 msgid "hidden" msgstr "" #: zfuncs.cc:12896 msgid "done" msgstr "" #: zfuncs.cc:12926 msgid "image scale" msgstr "" #: zfuncs.cc:12928 msgid "percent" msgstr "" fotoxx-20.08/zfuncs.cc000066400000000000000000020521441362435004500147030ustar00rootroot00000000000000/******************************************************************************** zfuncs.cpp collection of Linux and GDK/GTK utility functions Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. *********************************************************************************/ // zfuncs.cc version v.7.4 (for Fotoxx 20) #include "zfuncs.h" /******************************************************************************** Table of Contents ================= System Utility Functions ------------------------ zmalloc zfree zstrdup replace malloc() etc. to add checks and statistics printz printf() with immediate fflush() zexit exit a process and kill all child processes zbacktrace callable backtrace dump zappcrash abort with traceback dump to desktop file catch_signals trap segfault, crash with zappcrash() TRACE trace() and tracedump() implement the TRACE macro beroot restart image as root, if password is OK runroot run a command or program as root user timer functions elapsed time, CPU time, process time functions compact_time convert time_t type to yyyymmddhhmmss format pretty_datetime convert time_t type to yyyy-mm-dd hh:mm:ss format proc file functions parse data from various /proc files coretemp get current processor core temperature disktemp get temperature for given disk drive zsleep sleep for any amount of time (e.g. 0.1 seconds) zloop loop for any amount of time 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 start a detached thread start_Jthread start a joinable thread wait_Jthread wait for thread and join synch_threads make threads pause and resume together set_cpu_affinity set CPU (SMP) affinity for calling process or thread zsystem system() + return child process status shell_quiet format and run a shell command, return status shell_ack "" + popup error message if error shell_asynch "" + return immediately and query status later command_output start shell command and read the output as records signalProc pause, resume, or kill a child process fgets_trim fgets() with trim of trailing \r \n and optionally blanks samefolder test if two files/folders have the same folder path parsefile parse filespec into folder, file, extension renamez like rename() but works across file systems check_create_dir check if folder exists, ask to create if not copyFile copy file to file or file to folder zreaddir return all files in a folder, sorted combine_argvs catenate argv[ii] elements from Nth to last zescape_quotes escape quote marks (") in file names 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 strncpy() with insured null delimiter strnPad add blank padding to specified length strTrim remove trailing blanks strTrim2 remove leading and trailing blanks strCompress remove embedded blanks strncatv catenate multiple strings with length limit strmatchV compare 1 string to N strings strToUpper convert string to upper case strToLower convert string to lower case repl_1str replace substring within string repl_Nstrs replace multiple substrings within string breakup_text insert newline chars to limit text line lengths strncpyx convert string to hex format StripZeros remove trailing zeros (1.23000E+8 >> 1.23E+8) blank_null test string for null pointer, zero length, and all blanks clean_escapes replace 2-character escapes ("\n") with the escaped characters UTF-8 functions deal with UTF-8 multibyte character strings zsed substitute multiple strings in a file zstrstr zstrstr() and zcasestrstr() work like strstr() and strcasestr() zstrcasestr but the string "" does NOT match with any string Number Conversion and Formatting -------------------------------- convSI string to integer with optional limits check convSD string to double with optional limits check convSF string to float with optional limits check convIS integer to string with returned length convDS double to string with specified digits of precision formatKBMB format a byte count with specified precision and B/KB/MB/GB units Wildcard Functions ------------------ MatchWild match string to wildcard string (multiple * and ?) MatchWildIgnoreCase works like MatchWild() but ignores case SearchWild wildcard file search (multiple * and ? in path or file name) SearchWildCase works like SearchWild() but ignores case in file name zfind find function using glob() Search and Sort Functions ------------------------- bsearch binary search of sorted list HeapSort sort list of integer / float / double / records / pointers to records MemSort sort records with multiple keys (data types and sequence) zmember test if a value is a member of a set of values Misc Functions -------------- pvlist_create, etc. functions to manage a list of variable strings random numbers int and double random numbers with improved distributions spline1/2 cubic spline curve fitting function Qtext FIFO queue for text strings, dual thread access Application Admin Functions --------------------------- appimage_install make desktop and icon files for appimage menu integration appimage_unstall uninstall appimage program and desktop files zinitapp etc. initialize application folder and data files zsetfont set new application font widget_font_metrics get font width and height for given widget locale_filespec find a locale-dependent installation file (translate-xx.po etc.) showz_logfile display application log file showz_textfile show application text file (README, changelog, etc.) showz_html show a local or remote HTML file showz_docfile show a document file topic and associated image validate_docfile validate docfile internal links appruns_update update application appruns file phone_home send anonymous statistics if allowed phone_home_allow dialog to allow/block phone_home messages E2X(), etc. translate GUI and message text strings for non-English locales GTK Utility Functions --------------------- zmainloop do main loop to process menu events, etc. zmainsleep loop zmainloop and zsleep for designated time draw_context_create get cairo drawing context for GDK window textwidget text report, navigation, line editing menus / toolbars simplified GTK menus, toolbars and status bars Gmenuz customizable graphic popup menu create_popmenu implement popup menus Vmenu vertical menu/toolbar in vertical packing box zdialog_new create new zdialog zdialog_set_title change a zdialog title zdialog_set_modal set a zdialog to be modal zdialog_set_decorated set a zdialog to be decorated or not zdialog_present present a zdialog (visible and on top) zdialog_can_focus set zdialog can or cannot have focus zdialog_set_focus set focus on zdialog window or window + widget zdialog_add_widget add widget to existing zdialog zdialog_valid return 1/0 if zdialog is valid/invalid zdialog_widget get GTK widget from zdialog and widget name zdialog_add_ttip add a popup tool tip to a zdialog widget zdialog_set_group set a common group for a set of radio buttons zdialog_resize resize zdialog greater than initial size zdialog_set_limits set new limits for numeric data entry widget zdialog_rescale expande scale around neutral value zdialog_run run the zdialog and send events to event function zdialog_widget_event respond to zdialog widget events zdialog_focus_event response handler for "focus-in-event" signal zdialog_KB_addshortcut set KB shortcuts for zdialog completion buttons zdialog_KB_press respond to zdialog keyboard inputs zdialog_zspin_event response function for "zspin" widget zdialog_send_event send an event to an active zdialog zdialog_send_response complete a zdialog and assign status zdialog_show show or hide a zdialog window zdialog_destroy destroy a zdialog (data remains available) zdialog_free free zdialog memory (data is gone) zdialog_wait wait for zdialog completion, get status zdialog_goto put cursor at named widget zdialog_set_cursor set zdialog cursor (e.g. busy) zdialog_stuff stuff data into zdialog widget zdialog_labelfont set label text with font zdialog_fetch fetch data from zdialog widget zdialog_cb_app append item to combo box list widget zdialog_cb_prep prepend item to combo box list widget zdialog_cb_get get data from combo box entry widget zdialog_cb_delete delete combo box entry from list zdialog_cb_clear clear combo box list zdialog_cb_popup make combo box show list of all entries zdialog_cb_save save combo box list to a file zdialog_cb_load load combo box list from a file zdialog_geometry load/save zdialog positions at app start/exit zdialog_set_position set zdialog position: null mouse desktop parent save nn/nn zdialog_inputs load/save zdialog inputs at app start/exit zdialog_restore_inputs retrieve prior zdialog input fields window_to_mouse move a GtkWindow to the mouse position popup_report popup window and scrolling text report popup_command run a shell command with output in a popup window zmessageACK popup message, printf format, wait for user ACK zmessageYN popup message, printf format, wait for user Yes / No zmessage_post popup message, printf format, show until killed zmessage_post_bold popup message, printf format, big bold red font zdialog_text popup zdialog to get 1-2 lines of text input from user zdialog_choose popup zdialog to show a message, select a button, return choice poptext_screen popup message at given absolute screen position poptext_mouse popup message at current mouse position + offset poptext_window popup message at given window position + offset poptext_widget popup message at given widget position + offset poptext_killnow kill popup message popup_image show an image in a small popup window zgetfile simplified file chooser zdialog print_image_file zdialog to print an image file using GTK functions drag_drop_source connect window as drag-drop source drag_drop_dest connect window as drag-drop destination get_thumbnail get thumbnail image for given image file zmakecursor make a cursor from an image file (.png .jpg) gdk_pixbuf_rotate rotate a pixbuf through any angle gdk_pixbuf_stripalpha remove an alpha channel from a pixbuf text_pixbuf create pixbuf containing text move_pointer move the mouse pointer within a widget/window C++ Classes ----------- xstring string manipulation (= / + / insert / overlay) Vxstring array of xstrings with auto growth HashTab hash table: add, delete, find, step through Queue queue of xstrings: push, pop first or last Tree store / retrieve data by node names or numbers, any depth *********************************************************************************/ namespace zfuncs { struct timeb startime; // app startup time GdkDisplay *display; // workstation (KB, mouse, screen) GdkScreen *screen; // screen, N monitors GdkDevice *mouse; // pointer device GtkSettings *gtksettings = 0; // screen settings 6.2 cchar *build_date_time = __DATE__ " " __TIME__; // build date and time 7.1 char *progexe = 0; // executable image file 7.1 char *appimagexe = 0; // appimage executable image file 7.1 int monitor_ww, monitor_hh; // monitor dimensions 6.6 int appfontsize = 10; // application font size cchar *appfont = "sans 10"; // application font defaults cchar *appboldfont = "sans bold 10"; cchar *appmonofont = "mono 10"; cchar *appmonoboldfont = "mono bold 10"; char zappname[40] = "undefined"; // appname without version char zappvers[40] = "undefined"; // appname-N.N 7.4 char zprefix[200], zdatadir[200], zdocdir[200]; // app folders 6.8 char zlocalesdir[200], zimagedir[200], zhomedir[200]; char zlocale[8] = "en"; // "lc" or "lc_RC" pthread_t tid_main = 0; // main() thread ID int vmenuclickposn; // Vmenu image click posn. 0-100 int vmenuclickbutton; // button: 1/2/3 = L/M/R mouse zdialog *zdialog_list[zdialog_max]; // active zdialog list 6.0 int zdialog_count = 0; // total zdialogs (new - free) int zdialog_busy = 0; // open zdialogs (run - destroy) } using namespace zfuncs; /******************************************************************************** system-level utility functions *********************************************************************************/ // malloc() free() and strdup() wrappers with added functionality void * zmalloc(size_t cc) // 7.3 { double memavail, fcc; static int ftf = 1, memcheck = 1; if (ftf) { // first call ftf = 0; parseprocfile("/proc/meminfo","MemAvailable:",&memavail,0); // 'MemAvailable' available? if (! memavail) memcheck = 0; // no, memory check not possible } if (memcheck && cc > 10000) // large block { parseprocfile("/proc/meminfo","MemAvailable:",&memavail,0); // free memory (+ cache) KB units memavail = memavail / 1024; // do compare in MB units fcc = cc / 1024 / 1024; if (memavail - fcc < 300) zexit("OUT OF MEMORY"); // < 300 MB, quit before OOM killer 7.3 } void *pp = malloc(cc); // allocate memory if (! pp) zexit("OUT OF MEMORY"); // 7.3 memset(pp,0,cc); // clear to zero return pp; } void zfree(void *puser) // 7.2 { return free(puser); } char *zstrdup(cchar *string, int addcc) { if (! string) zappcrash("zstrdup() null arg"); char *pp = (char *) zmalloc(strlen(string) + 1 + addcc); // add additional chars. strcpy(pp,string); return pp; } /********************************************************************************/ // printf() and flush every output immediately even if stdout is a file void printz(cchar *format, ...) { va_list arglist; va_start(arglist,format); vprintf(format,arglist); va_end(arglist); fflush(stdout); return; } /********************************************************************************/ // Output a status or error message and kill all processes in the process group. // Use the function killpg(0,SIGKILL) to kill all processes, including the caller. void zexit(cchar *errmess, ...) // 7.4 { va_list arglist; char buff[200]; if (errmess) { // output error message va_start(arglist,errmess); vsnprintf(buff,200,errmess,arglist); printz("zexit: %s\n",buff); } else printz("zexit\n"); killpg(0,SIGKILL); // kill all processes in group sleep(10); // wait here to die } /*** // Exit a process with an error message and kill all child processes. // Use the command "ps -o pid,pgid" to find all child processes. void zexit(cchar *errmess, ...) // 7.4 { va_list arglist; FILE *fid; pid_t pid, cpid, cpgid; // process ID, progess group ID char buff[200]; cchar *pp; if (errmess) { // output error message va_start(arglist,errmess); vsnprintf(buff,200,errmess,arglist); printz("zexit: %s\n",buff); } else printz("zexit\n"); pid = getpid(); // calling process PID fid = popen("ps -o pid,pgid","r"); // list my processes with pid, pgid if (! fid) exit(0); while (true) { // read returned process list pp = fgets(buff,100,fid); if (! pp) break; // EOF pp = strField(buff,' ',1); cpid = atoi(pp); // process pid if (cpid == pid) continue; // skip calling process pp = strField(buff,' ',2); cpgid = atoi(pp); // process goup ID if (cpgid == pid) kill(cpid,SIGKILL); // my child processes, kill } pclose(fid); exit(0); // exit calling process } ***/ /********************************************************************************/ // produce a backtrace dump to stdout void zbacktrace() { int nstack = 100; void *stacklist[100]; nstack = backtrace(stacklist,nstack); // get backtrace data if (nstack > 100) nstack = 100; backtrace_symbols_fd(stacklist,nstack,STDOUT_FILENO); // backtrace records to STDOUT return; } /********************************************************************************/ // Write an error message and backtrace dump to a file and to a popup window. // Error message works like printf(). // Depends on library program addr2line(). void zappcrash(cchar *format, ... ) // rev. 6.0 { static int crash = 0; struct utsname unbuff; va_list arglist; FILE *fid1, *fid2, *fid3; int fd, ii, err, cc, nstack = 100; int Flinenos = 1; void *stacklist[100]; char OS1[60] = "", OS2[60] = "", OS3[60] = ""; char message[300], progexe[300]; char buff1[300], buff2[300], hexaddr[20]; char *arch, *pp1, *pp2, dlim, *pfunc; if (crash++) return; // re-entry or multiple threads crash va_start(arglist,format); vsnprintf(message,300,format,arglist); va_end(arglist); uname(&unbuff); // get cpu arch. 32/64 bit arch = unbuff.machine; fid1 = popen("lsb_release -d","r"); // get Linux flavor and release if (fid1) { ii = fscanf(fid1,"%s %s %s",OS1,OS2,OS3); pclose(fid1); } printz("\n*** zappcrash: %s %s %s %s %s %s \n", arch, OS2, OS3, zappvers, build_date_time, message); nstack = backtrace(stacklist,nstack); // get backtrace data if (nstack <= 0) zexit("zappcrash backtrace() failure"); if (nstack > 100) nstack = 100; fid1 = fopen("zbacktrace","w"); // open backtrace data output file if (! fid1) zexit("zappcrash fopen() failure"); fd = fileno(fid1); backtrace_symbols_fd(stacklist,nstack,fd); // write backtrace data fclose(fid1); // (use of malloc() is avoided) fid1 = fopen("zbacktrace","r"); // open backtrace data file if (! fid1) zexit("zappcrash fopen() failure"); fid2 = fopen("zappcrash","w"); // open zappcrash output file if (! fid2) zexit("zappcrash fopen() failure"); fprintf(fid2,"\n*** zappcrash: %s %s %s %s %s %s \n", arch, OS2, OS3, zappvers, build_date_time, message); fprintf(fid2,"*** please send to mkornelix@gmail.com *** \n"); cc = readlink("/proc/self/exe",progexe,300); // get own program path if (cc > 0) progexe[cc] = 0; // readlink() quirk else { fprintf(fid2,"progexe not available \n"); Flinenos = 0; } err = zsystem("which addr2line >> /dev/null"); // check if addr2line() available if (err) { fprintf(fid2,"addr2line not available \n"); Flinenos = 0; } for (ii = 0; ii < nstack; ii++) // loop backtrace records { pp1 = pp2 = 0; fgets_trim(buff1,300,fid1); // read backtrace line if (! Flinenos) goto output; pfunc = 0; pp1 = strstr(buff1,"+0x"); // new format (+0x12345...) 6.5 if (pp1) pp2 = strchr(pp1,')'); else { pp1 = strstr(buff1,"[0x"); // old format [0x12345...] if (pp1) pp2 = strchr(pp1,']'); } if (! pp1 || ! pp2) goto output; // cannot parse dlim = *pp2; *pp2 = 0; strncpy0(hexaddr,pp1+1,20); *pp2 = dlim; snprintf(buff2,300,"addr2line -i -e %s %s",progexe,hexaddr); // convert to source program fid3 = popen(buff2,"r"); // and line number if (! fid3) goto output; pfunc = fgets(buff2,300,fid3); pclose(fid3); if (! pfunc) goto output; cc = strlen(pfunc); if (cc < 10) goto output; if (pfunc[cc-1] < ' ') pfunc[cc-1] = 0; // remove tailing \n if present strncatv(buff1,300,"\n--- ",pfunc,null); output: fprintf(fid2,"%s \n",buff1); // output } fclose(fid1); fclose(fid2); shell_quiet("rm zbacktrace"); // 7.1 shell_quiet("cat zappcrash"); // 7.1 shell_quiet("mv zappcrash $(xdg-user-dir DESKTOP)/%s-zappcrash",zappvers); // move zappcrash file to desktop 7.3 zexit("exit zappcrash"); // 7.3 } /********************************************************************************/ // 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); // man page says cannot be caught sigaction(SIGFPE,&sigact,0); sigaction(SIGBUS,&sigact,0); sigaction(SIGABRT,&sigact,0); // heap or stack corruption return; } // catch fatal signals and produce backtrace dumps on-screen void sighandler(int signal) { const char *signame = "unknown"; if (signal == SIGTERM) zexit("TERMINATED"); if (signal == SIGKILL) zexit("KILLED"); if (signal == SIGSEGV) signame = "segment fault"; if (signal == SIGILL) signame = "illegal operation"; if (signal == SIGFPE) signame = "arithmetic exception"; if (signal == SIGBUS) signame = "bus error (bad memory)"; if (signal == SIGABRT) signame = "abort"; zappcrash("fatal signal: %s",signame); exit(0); } /********************************************************************************/ // Implement the TRACE macro. // Trace program execution by function and source code line number. // tracedump() dumps last 50 uses of TRACE macro, latest first. namespace tracenames { char filebuff[50][100]; // last 50 TRACE calls char funcbuff[50][60]; int linebuff[50]; void *addrbuff[50]; int ii, ftf = 1; }; // Args are source file, source function name, source code line number, // caller address. These all come from the GCC compiler and TRACE macro. void trace(cchar *file, cchar *func, int line, void *addr) { using namespace tracenames; if (ftf) { ftf = 0; for (ii = 0; ii < 50; ii++) { filebuff[ii][99] = 0; funcbuff[ii][39] = 0; linebuff[ii] = 0; addrbuff[ii] = 0; } ii = 0; } if (line == linebuff[ii] && strmatch(func,funcbuff[ii])) return; // same as last call, don't duplicate if (++ii > 49) ii = 0; // add data to list strncpy(&filebuff[ii][0],file,99); strncpy(&funcbuff[ii][0],func,39); linebuff[ii] = line; addrbuff[ii] = addr; return; } // dump trace records to STDOUT void tracedump() { using namespace tracenames; FILE *fid; int kk; printz(" *** tracedump *** \n"); kk = ii; while (linebuff[kk]) { printz("TRACE %s %s %d %p \n",&filebuff[kk][0], &funcbuff[kk][0],linebuff[kk],addrbuff[kk]); if (--kk == ii) break; } fid = fopen("tracedump","w"); if (! fid) { perror("tracedump fopen() failure \n"); return; } fprintf(fid, " *** tracedump *** \n"); kk = ii; while (linebuff[kk]) { fprintf(fid, "TRACE %s %s %d %p \n",&filebuff[kk][0], &funcbuff[kk][0],linebuff[kk],addrbuff[kk]); if (--kk == ii) break; } fclose(fid); return; } /********************************************************************************/ // restart the current program as root user (after sudo success) // argv[0] must be executable program void beroot(int argc, char *argv[]) { int err; char *args, command[500]; if (getuid() == 0) return; // already root err = shell_quiet("which xhost xterm"); // check for necessary programs 7.1 if (err) zexit("xhost and xterm must be installed"); printz("become root user \n"); shell_ack("xhost +si:localuser:root"); // wayland, allow root for x11 7.0 args = combine_argvs(argc,argv,0); snprintf(command,500,"xterm -fa 'Mono' -fs 15 -geometry 40x5+400+300" // start root process " -e sudo -b -S %s",args); err = shell_ack(command); exit(err); // exit old process } /********************************************************************************/ // run a command or program as root user // command: shell command or filespec of the program to start // returns 0 if successfully started, else returns an error code int runroot(cchar *command) { char xtcommand[500]; int err; err = shell_quiet("which xhost xterm"); // check for necessary programs 7.1 if (err) zexit("xhost and xterm must be installed"); printz("become root user \n"); shell_ack("xhost +si:localuser:root"); // wayland, allow root for x11 7.0 snprintf(xtcommand,500,"xterm -fa 'Mono' -fs 15 -geometry 40x5+400+300" // 7.0 " -e sudo -b -S %s",command); err = shell_ack(xtcommand); return err; } /********************************************************************************/ // get time in real seconds // theoretically uses a precise system clock but the precision is poor double get_seconds() // 6.8 { timespec time1; double time2; clock_gettime(CLOCK_MONOTONIC_RAW,&time1); time2 = time1.tv_sec; time2 += time1.tv_nsec * 0.000000001; 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; } /********************************************************************************/ // perform interval timing better than Linux 4 ms or 40 ms resolution // 6.8 // resolution is 1 millisecond double ztimer_value = 0; // timer value since startup void ztimer_handler(int signal) // timer signal handler { ztimer_value++; return; } void ztimer_start() // start timer { struct itimerval timerdata; ztimer_value = 0; // reset to zero timerdata.it_value.tv_sec = 0; // start now timerdata.it_value.tv_usec = 1000; timerdata.it_interval.tv_sec = 0; // repeat at 1 millisec. intervals timerdata.it_interval.tv_usec = 1000; signal(SIGALRM,ztimer_handler); setitimer(ITIMER_REAL,&timerdata,NULL); return; } void ztimer_stop() // stop timer { struct itimerval timerdata; timerdata.it_value.tv_sec = 0; timerdata.it_value.tv_usec = 0; timerdata.it_interval.tv_sec = 0; timerdata.it_interval.tv_usec = 0; setitimer(ITIMER_REAL,&timerdata,NULL); return; } int ztimer_milliseconds() // get milliseconds since startup { return ztimer_value; } /********************************************************************************/ // start a process CPU timer or get elapsed process CPU time // returns seconds with millisecond resolution void start_CPUtimer(double &time0) { time0 = CPUtime(); return; } double get_CPUtimer(double &time0) { return CPUtime() - time0; } /********************************************************************************/ // get elapsed CPU time used by current process // returns seconds with millisecond resolution double CPUtime() { clock_t ctime = clock(); double dtime = ctime / 1000000.0; return dtime; } /********************************************************************************/ // Get elapsed CPU time used by current process, including all threads. // Returns seconds with millisecond resolution. double CPUtime2() { struct rusage usage; double utime, stime; int err; err = getrusage(RUSAGE_SELF,&usage); if (err) return 0.0; utime = usage.ru_utime.tv_sec + 0.000001 * usage.ru_utime.tv_usec; stime = usage.ru_stime.tv_sec + 0.000001 * usage.ru_stime.tv_usec; return utime + stime; } /********************************************************************************/ // get elapsed process time for my process, including threads and child processes. double jobtime() { double jiffy = 1.0 / sysconf(_SC_CLK_TCK); // "jiffy" time slice = 1.0 / HZ char buff[200]; double cpu1, cpu2, cpu3, cpu4; FILE *fid; char *pp; fid = fopen("/proc/self/stat","r"); if (! fid) return 0; pp = fgets(buff,200,fid); fclose(fid); if (! pp) return 0; parseprocrec(pp,14,&cpu1,15,&cpu2,16,&cpu3,17,&cpu4,null); return (cpu1 + cpu2 + cpu3 + cpu4) * jiffy; } /********************************************************************************/ // convert a time_t date/time (e.g. st_mtime from stat() call) // into a compact date/time format "yyyymmddhhmmss" void compact_time(const time_t DT, char *compactDT) { struct tm *fdt; int year, mon, day, hour, min, sec; fdt = localtime(&DT); year = fdt->tm_year + 1900; mon = fdt->tm_mon + 1; day = fdt->tm_mday; hour = fdt->tm_hour; min = fdt->tm_min; sec = fdt->tm_sec; compactDT[0] = year / 1000 + '0'; compactDT[1] = (year % 1000) / 100 + '0'; compactDT[2] = (year % 100) / 10 + '0'; compactDT[3] = year % 10 + '0'; compactDT[4] = mon / 10 + '0'; compactDT[5] = mon % 10 + '0'; compactDT[6] = day / 10 + '0'; compactDT[7] = day % 10 + '0'; compactDT[8] = hour / 10 + '0'; compactDT[9] = hour % 10 + '0'; compactDT[10] = min / 10 + '0'; compactDT[11] = min % 10 + '0'; compactDT[12] = sec / 10 + '0'; compactDT[13] = sec % 10 + '0'; compactDT[14] = 0; return; } /********************************************************************************/ // convert a time_t date/time (e.g. st_mtime from stat() call) // into a pretty date/time format "yyyy-mm-dd hh:mm:ss" void pretty_datetime(const time_t DT, char *prettyDT) // 6.2 { struct tm *fdt; int year, mon, day, hour, min, sec; fdt = localtime(&DT); year = fdt->tm_year + 1900; mon = fdt->tm_mon + 1; day = fdt->tm_mday; hour = fdt->tm_hour; min = fdt->tm_min; sec = fdt->tm_sec; prettyDT[0] = year / 1000 + '0'; prettyDT[1] = (year % 1000) / 100 + '0'; prettyDT[2] = (year % 100) / 10 + '0'; prettyDT[3] = year % 10 + '0'; prettyDT[4] = '-'; prettyDT[5] = mon / 10 + '0'; prettyDT[6] = mon % 10 + '0'; prettyDT[7] = '-'; prettyDT[8] = day / 10 + '0'; prettyDT[9] = day % 10 + '0'; prettyDT[10] = ' '; prettyDT[11] = hour / 10 + '0'; prettyDT[12] = hour % 10 + '0'; prettyDT[13] = ':'; prettyDT[14] = min / 10 + '0'; prettyDT[15] = min % 10 + '0'; prettyDT[16] = ':'; prettyDT[17] = sec / 10 + '0'; prettyDT[18] = sec % 10 + '0'; prettyDT[19] = 0; return; } /********************************************************************************/ // Read and parse /proc file with records formatted "parmname xxxxxxx" // Find all requested parameters and return their numeric values int parseprocfile(cchar *pfile, cchar *pname, double *value, ...) // EOL = 0 { FILE *fid; va_list arglist; char buff[1000]; const char *pnames[20]; double *values[20]; int ii, fcc, wanted, found; pnames[0] = pname; // 1st parameter values[0] = value; *value = 0; va_start(arglist,value); for (ii = 1; ii < 20; ii++) // get all parameters { pnames[ii] = va_arg(arglist,char *); if (! pnames[ii]) break; values[ii] = va_arg(arglist,double *); *values[ii] = 0; // initialize to zero } va_end(arglist); if (ii == 20) zappcrash("parseProcFile, too many fields"); wanted = ii; found = 0; fid = fopen(pfile,"r"); // open /proc/xxx file if (! fid) return 0; while ((fgets(buff,999,fid))) // read record, "parmname nnnnn" { for (ii = 0; ii < wanted; ii++) { // look for my fields fcc = strlen(pnames[ii]); if (strmatchN(buff,pnames[ii],fcc)) { *values[ii] = atof(buff+fcc); // return value found++; break; } } if (found == wanted) break; // stop when all found } fclose(fid); return found; } // Parse /proc record of the type "xxx xxxxx xxxxx xxxxxxxx xxx" // Return numeric values for requested fields (starting with 1) int parseprocrec(char *prec, int field, double *value, ...) // EOL = 0 { va_list arglist; int xfield = 1, found = 0; va_start(arglist,value); while (*prec == ' ') prec++; // skip leading blanks while (field > 0) { while (xfield < field) // skip to next wanted field { prec = strchr(prec,' '); // find next blank if (! prec) break; while (*prec == ' ') prec++; // skip multiple blanks xfield++; } if (! prec) break; *value = atof(prec); // convert, return double found++; field = va_arg(arglist,int); // next field number if (! field) break; // bugfix 7.1 value = va_arg(arglist,double *); // next output double * } while (field > 0) { *value = 0; // zero values not found field = va_arg(arglist,int); value = va_arg(arglist,double *); } va_end(arglist); return found; } /********************************************************************************/ // get current processor core temperature // depends on file "temp1_input" somewhere under /sys/devices/* int coretemp() { FILE *fid; static int ftf = 1; static char *Tfile = 0; static char buff1[200], buff2[200], *ptemp; int temp; if (ftf) { // find file "temp1_input" ftf = 0; fid = popen("find /sys/devices/ -name temp1_input","r"); // 6.6 if (! fid) return 0; Tfile = fgets(buff1,200,fid); pclose(fid); printz("coretemp file: %s \n",Tfile); } if (! Tfile) return 0; snprintf(buff2,200,"cat %s",Tfile); fid = popen(buff2,"r"); if (! fid) return 0; ptemp = fgets_trim(buff2,200,fid); pclose(fid); if (! ptemp) return 0; temp = atoi(ptemp); while (temp > 200) temp = temp / 10; // may be deg. C x 10 or x 100 ... if (temp < 10) return 0; return temp; } /********************************************************************************/ // get current temperature for given disk, e.g. "/dev/sda" // depends on "smartctl" command from package smartmontools int disktemp(char *disk) { int id, temp; char *pp, *pp2; char buff[200], command[100]; FILE *ffid; temp = 0; pp2 = 0; snprintf(command,100,"smartctl -A %s",disk); ffid = popen(command,"r"); if (! ffid) return 0; while (true) { pp = fgets(buff,200,ffid); // revised for smartctl report if (! pp) break; // format changes if (strmatchN(pp,"ID#",3)) pp2 = strstr(pp,"RAW_VALUE"); id = atoi(pp); if (id != 190 && id != 194) continue; // Airflow Temp. or Temp. if (! pp2) continue; temp = atoi(pp2); if (temp < 10 || temp > 99) temp = 0; break; } pclose(ffid); return temp; } /********************************************************************************/ // sleep for specified time in seconds (double) // signals can cause early return void zsleep(double dsecs) { unsigned isecs, nsecs; timespec tsecs; if (dsecs <= 0) return; isecs = unsigned(dsecs); nsecs = unsigned(1000000000.0 * (dsecs - isecs)); tsecs.tv_sec = isecs; tsecs.tv_nsec = nsecs; nanosleep(&tsecs,null); return; } /********************************************************************************/ // loop for specified time in seconds (double) void zloop(double dsecs) { double time0, time1; if (dsecs <= 0) return; time0 = get_seconds(); time1 = time0 + dsecs; while (get_seconds() < time1) continue; return; } /********************************************************************************/ // Lock or unlock a multi-process multi-thread resource. 7.1 // Only one process/thread may possess a given lock. // A reboot or process exit or crash releases the lock. // // char *lockfile; // make_global_lockfile("lockname",&lockfile); // fd = global_lock(lockfile); // err = global_unlock(fd,lockfile); // // both functions return +N if success, -1 otherwise. void make_global_lockfile(cchar *lockname, char **lockfile) { int cc; *lockfile = zstrdup(lockname,16); cc = strlen(*lockfile); snprintf(*lockfile+cc,12,"-%08d",getpid()); // caller name with PID appended return; } int global_lock(cchar *lockfile) { int err, fd; fd = open(lockfile,O_RDWR|O_CREAT,0666); // open or create the lock file if (fd < 0) zappcrash("global_lock() %s",strerror(errno)); err = flock(fd,LOCK_EX); // request exclusive 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); if (err < 0) return -1; // remove(lockfile); // lock/unlock 2.5x faster return 1; } /********************************************************************************/ // lock or unlock a resource // does not spin or wait, usable within or across threads // CANNOT BE USED for coroutines 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 { if (resource) return 0; // locked mutex_lock(&resource_lock_lock); if (resource) { mutex_unlock(&resource_lock_lock); // 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. // CANNOT BE USED for coroutines within one thread, e.g. GTK main loop. 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 if caller exits without checking thread status. // Thread should exit with pthread_exit(0); void start_detached_thread(void * threadfunc(void *), void * arg) { pthread_attr_t pthattr; pthread_t pthtid; int ii, err; pthread_attr_init(&pthattr); pthread_attr_setdetachstate(&pthattr,PTHREAD_CREATE_DETACHED); for (ii = 0; ii < 1000; ii++) { err = pthread_create(&pthtid,&pthattr,threadfunc,arg); // 7.1 if (! err) return; zsleep(0.001); if (err == EAGAIN) continue; // this shit happens break; } zexit("pthread_create() failure: %s",strerror(err)); } /********************************************************************************/ // Start a thread using a simplified protocol. // Caller must call wait_Jthread() to avoid creating a zombie process. pthread_t start_Jthread(void * threadfunc(void *), void * arg) // 7.1 { pthread_t tid; int ii, err; for (ii = 0; ii < 1000; ii++) { err = pthread_create(&tid, null, threadfunc, arg); if (! err) return tid; zsleep(0.001); if (err == EAGAIN) continue; // this shit happens break; } zexit("pthread_create() failure: %s",strerror(err)); return 0; } // wait for thread to exit. int wait_Jthread(pthread_t tid) { int err; err = pthread_join(tid, null); if (! err) return 0; zexit("pthread_join() failure: %s",strerror(err)); return 0; } /********************************************************************************/ // 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 } /********************************************************************************/ // set a CPU affinity for the calling process or thread // cpu is in the range 0 to (processor core count) - 1 void set_cpu_affinity(int cpu) // 7.4 { int err; static int ftf = 1, Nsmp; cpu_set_t cpuset; if (ftf) { // first call ftf = 0; Nsmp = get_nprocs(); // get SMP CPU count } if (cpu >= Nsmp) return; CPU_ZERO(&cpuset); CPU_SET(cpu,&cpuset); err = sched_setaffinity(0,sizeof(cpuset),&cpuset); if (err) printz("set_cpu_affinity() %s \n",strerror(errno)); return; } /********************************************************************************/ // same as system() but status of child process is returned. int zsystem(cchar *command) // 6.8 { int err; err = system(command); if (! err) return 0; err = WEXITSTATUS(err); return err; } /********************************************************************************/ // Format and run a shell command. // Print a status message to stdout if there is an error. // returns 0 if OK, +N if error int shell_quiet(cchar *command, ...) { char *cbuff; va_list arglist; int cc, err; cc = strlen(command) + 1000; cbuff = (char *) zmalloc(cc+1); va_start(arglist,command); // format command vsnprintf(cbuff,cc,command,arglist); va_end(arglist); err = zsystem(cbuff); if (! err) { zfree(cbuff); return 0; } if (strmatchN(command,"diff",4)) { zfree(cbuff); return err; } // no diagnostic for these if (strmatchN(command,"cmp",3)) { zfree(cbuff); return err; } if (strmatchN(command,"which",5)) { zfree(cbuff); return err; } if (err == 255) { zfree(cbuff); return err; } // apparently a Linux bug 6.9 if (cc > 200) cbuff[200] = 0; if (err == 127) printz("%s *** %s \n",cbuff,"command not found"); // special case 6.3 else printz("%s *** %s \n",cbuff,strerror(err)); // log error message zfree(cbuff); return err; } // Format and run a shell command. // Print a status message to stdout if there is an error. // Also pop-up an error message window if error. // Thread safe: GTK is not used. // returns 0 if OK, +N if error int shell_ack(cchar *command, ...) { char *cbuff; va_list arglist; int cc, err; cc = strlen(command) + 1000; cbuff = (char *) zmalloc(cc+1); va_start(arglist,command); // format command vsnprintf(cbuff,cc,command,arglist); va_end(arglist); printz("shell: %s \n",cbuff); err = zsystem(cbuff); if (! err) goto retx; if (strmatchN(command,"diff",4)) goto retx; // no diagnostic for these if (strmatchN(command,"cmp",3)) goto retx; if (strmatchN(command,"which",5)) goto retx; if (cc > 200) cbuff[200] = 0; if (err == 127) zmessageACK(0,"%s \n %s",cbuff,"command not found"); // special case 6.3 else zmessageACK(0,"%s \n %s",cbuff,strerror(err)); // popup error message retx: 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 zmlock = PTHREAD_MUTEX_INITIALIZER; } int shell_asynch(cchar *Fcommand, ...) { using namespace shell_asynch_names; void * shell_asynch_thread(void *); va_list arglist; static int ii; mutex_lock(&zmlock); // 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(&zmlock); // unblock other callers err = zsystem(command[ii]); // start command, wait until done if (! err) { status[ii] = 0; return 0; } if (err == 127) printz("%s \n *** %s \n",command[ii],"command not found"); // special case else printz("%s \n *** %s \n",command[ii],strerror(err)); // log error message status[ii] = err; // set status for caller return 0; } int shell_asynch_status(int handle) // get command status { using namespace shell_asynch_names; int ii = handle; if (status[ii] == -1) return -1; // return busy status zfree(command[ii]); // free memory command[ii] = 0; return status[ii]; // return completion status } /******************************************************************************** Run a shell command and get its outputs one record at a time. The outputs are returned one record at a time, until a NULL is returned, indicating the command has finished and has exited. The new line character is removed from the returned output records. Use contx = 0 to start a new command. Do not change the returned value. Up to 9 commands can run in parallel, with contx values 1-9. To get the command exit status: status = command_status(contx). If the command is still busy, -1 is returned. To kill a command before output is complete: command_kill(contx); ***/ FILE * CO_contx[10] = { 0,0,0,0,0,0,0,0,0,0 }; int CO_status[10]; char * command_output(int &contx, cchar *command, ...) // simplify, allow parallel usage { FILE *fid; va_list arglist; char buff[10000], *prec; if (contx == 0) // start new command { for (contx = 1; contx < 10; contx++) if (CO_contx[contx] == 0) break; if (contx == 10) { printz("*** command_output(), parallel usage > 9 \n"); return 0; } va_start(arglist,command); // format command vsnprintf(buff,9999,command,arglist); va_end(arglist); fid = popen(buff,"r"); // execute command, output to FID if (fid == 0) { CO_status[contx] = errno; // failed to start printz("*** command_output: %s\n %s\n",buff,strerror(errno)); return 0; } CO_contx[contx] = fid + 1000; CO_status[contx] = -1; // mark context busy } fid = CO_contx[contx] - 1000; prec = fgets_trim(buff,9999,fid,1); // next output, less trailing \n if (prec) return zstrdup(prec); CO_status[contx] = pclose(fid); // EOF, set status CO_contx[contx] = 0; // mark context free return 0; } int command_status(int contx) // get command exit status { int err = CO_status[contx]; return WEXITSTATUS(err); // special BS for subprocess } int command_kill(int contx) // kill output before completion { FILE *fid; if (! CO_contx[contx]) return 0; // context already closed fid = CO_contx[contx] - 1000; CO_status[contx] = pclose(fid); // close context and set status CO_contx[contx] = 0; // mark context free return 0; } /********************************************************************************/ // Signal a running subprocess by name (name of executable or shell command). // Signal is "pause", "resume" or "kill". If process is paused, kill may not work, // so issue resume first if process is paused. int signalProc(cchar *pname, cchar *signal) { pid_t pid; FILE *fid; char buff[100], *pp; int err, nsignal = 0; snprintf(buff,100,"ps -C %s h o pid",pname); fid = popen(buff,"r"); // popen() instead of system() if (! fid) return 2; pp = fgets(buff,100,fid); pclose(fid); if (! pp) return 4; pid = atoi(buff); if (! pid) return 5; if (strmatch(signal,"pause")) nsignal = SIGSTOP; if (strmatch(signal,"resume")) nsignal = SIGCONT; if (strmatch(signal,"kill")) nsignal = SIGKILL; err = kill(pid,nsignal); return err; } /********************************************************************************/ // 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 folder, else return 0. // Both folders must be specified, at least one with ending '/' // (true if a file name is present) int samefolder(cchar *file1, cchar *file2) { cchar *p1, *p2; int cc1, cc2, cc; p1 = strrchr(file1,'/'); // /dir1/dir2 p2 = strrchr(file2,'/'); // /dir1/dir2/file cc1 = cc2 = 0; if (p1) cc1 = p1 - file1; // /dir1/dir2/file if (p2) cc2 = p2 - file2; // | | if (cc2 > cc1) cc = cc2; // 0 cc else cc = cc1; if (cc == 0) return 0; if (strmatchN(file1,file2,cc)) return 1; return 0; } /******************************************************************************** Parse a pathname (filespec) and return its components. Returned strings are allocated in static memory (no zfree needed). Next call replaces the data in the static strings. Limits: folder: 1000 file: 200 ext: 8 Missing components are returned as null pointers. input ppath outputs /name1/name2/ folder /name1/name2/ with no file /name1/name2 folder /name1/name2/ if name2 a folder, otherwise folder /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 **pfolder, char **pfile, char **pext) { STATB statb; static char folder[1000], file[200], ext[8]; char *pp; int err, cc1, cc2; *pfolder = *pfile = *pext = null; cc1 = strlen(ppath); if (cc1 > 999) return 1; // ppath too long strcpy(folder,ppath); *pfolder = folder; err = stat(folder,&statb); // have folder only if (! err && S_ISDIR(statb.st_mode)) return 0; pp = (char *) strrchr(folder,'/'); if (! pp) return 1; // illegal pp++; cc2 = pp - folder; 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 folder 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. // file names with embedded quote (") will fail int renamez(cchar *file1, cchar *file2) { char *pp1, *pp2; int err, Frename = 0; if (*file1 != '/' || *file2 != '/') return 1; // not absolute pathnames pp1 = strchr((char *) file1+1,'/'); pp2 = strchr((char *) file2+1,'/'); if (! pp1 || ! pp2) return 2; *pp1 = *pp2 = 0; if (strmatch(file1,file2)) Frename = 1; *pp1 = *pp2 = '/'; if (Frename) { // same top folder err = rename(file1,file2); if (err) return errno; else return 0; } err = shell_quiet("mv -f \"%s\" \"%s\" ",file1,file2); // not 6.8 return err; } /********************************************************************************/ // Check if a folder 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, folder, OK else { zmessageACK(0,"%s \n %s",path,strerror(ENOTDIR)); // exists, not a folder return ENOTDIR; } } if (errno != ENOENT) { zmessageACK(0,"%s \n %s",path,strerror(errno)); // error other than missing return errno; } yn = zmessageYN(0,E2X("create folder? \n %s"),path); // ask to create if (! yn) return ENOENT; err = shell_ack("mkdir -p -m 0750 \"%s\" ",path); // create. rwx, rx, no access 6.8 if (! err) return 0; zmessageACK(0,"%s \n %s",path,strerror(errno)); // failed to create return errno; } /********************************************************************************/ // Copy file to file or file to an existing folder. // Missing output folders will be created. // If inut file is a symlink, copy the symlink, not the file. int copyFile(cchar *sfile, cchar *dfile) // 6.8 { #define BIOCC (1024*1024) // read/write block size int fid1, fid2, err, cc, dlevs; char *pp1, *pp2, buff[BIOCC]; STATB statB; static char *dfile2 = 0; if (dfile2) zfree(dfile2); // stop memory leak dfile2 = 0; err = stat(dfile,&statB); if (! err && S_ISDIR(statB.st_mode)) { // output is an existing folder pp1 = (char *) strrchr(sfile,'/'); // get source file base name if (pp1) pp1++; else pp1 = (char *) sfile; cc = strlen(pp1); // construct output file path: dfile2 = zstrdup(dfile,cc+4); // output folder + base name pp2 = dfile2 + strlen(dfile2); if (pp2[-1] != '/') *pp2++ = '/'; // insure '/' after folder strcpy(pp2,pp1); dfile = dfile2; // output file full path } else { // output is a file path pp2 = (char *) dfile; dlevs = 0; while (true) { pp2 = strchr(pp2+1,'/'); // create missing folder levels if (! pp2) break; // (check and create from top down) *pp2 = 0; err = stat(dfile,&statB); if (err) { err = mkdir(dfile,0731); if (err) { printz("%s \n %s \n",strerror(errno),dfile); return errno; } dlevs++; } *pp2 = '/'; } } err = lstat(sfile,&statB); // get input file attributes if (err) { printz("%s \n %s \n",strerror(errno),sfile); return errno; } if (S_ISLNK(statB.st_mode)) { // input file is symlink cc = readlink(sfile,buff,XFCC); if (cc < 0 || cc > XFCC-2) return errno; buff[cc] = 0; err = symlink(buff,dfile); // create output symlink if (err) { printz("%s \n %s \n %s \n",strerror(errno),buff,dfile); return errno; } return 0; } if (strmatch(sfile,dfile)) return 0; // source and dest files are same fid1 = open(sfile,O_RDONLY); // open input file if (fid1 == -1) return errno; fid2 = creat(dfile,0700); // open output file if (fid2 == -1) { err = errno; close(fid1); if (err) printz("%s \n %s \n",strerror(err),dfile); return err; } while (true) { cc = read(fid1,buff,BIOCC); // read huge blocks if (cc == 0) break; if (cc == -1) { err = errno; close(fid1); close(fid2); if (err) printz("%s \n %s \n",strerror(err),sfile); return err; } cc = write(fid2,buff,cc); // write blocks if (cc == -1) { err = errno; close(fid1); close(fid2); if (err) printz("%s \n %s \n",strerror(err),dfile); return err; } } close(fid1); // close input file err = close(fid2); // close output file if (err) { printz("%s \n %s \n",strerror(errno),dfile); return errno; } return 0; } /********************************************************************************/ // Return all the file names in a folder, sorted in alphabetic order. // Subfolders are not included. // The 'files' argument is allocated and filled with pointers to file names. // (the names in the folder, not the full path names) // The number of files found is returned. // -1 is returned if the folder is invalid or other error. // If 'files' is returned non-zero, it is subject to zfree() int zreaddir(cchar *folder, char **&files) // 6.8 { struct dirent *dirent1; int Nfiles = 0, maxfiles = 100; DIR *direc; char **ufiles, **ufiles2; files = 0; // nothing returned yet ufiles = (char **) zmalloc(maxfiles * sizeof(char *)); // starting space direc = opendir(folder); // open caller's folder if (! direc) return -1; while (true) { if (Nfiles == maxfiles) // out of space { ufiles2 = (char **) zmalloc(2 * maxfiles * sizeof(char *)); // allocate new space = 2x old space memcpy(ufiles2,ufiles, maxfiles * sizeof(char *)); // copy data to new space zfree(ufiles); // free old space ufiles = ufiles2; // set new space maxfiles *= 2; // new capacity } dirent1 = readdir(direc); // get next file in folder if (! dirent1) break; if (dirent1->d_type != DT_REG) continue; // skip subfolders ufiles[Nfiles] = zstrdup(dirent1->d_name); // add to file list Nfiles++; continue; } closedir(direc); if (Nfiles > 1) HeapSort(ufiles,Nfiles); // sort file list files = ufiles; // return allocated file list return Nfiles; // return file count } /********************************************************************************/ // appimage problem: // command line args are separated by blanks even for strings enclosed in // quotes: "aaaa bbbb" becomes two argv[] elements, "aaaa" and "bbbb" // this makes it impossible to get file path args with embedded spaces // // char * catenate_argvs(int argc, char *argv[], Nth) // combine argv[ii] elements from Nth to last // a single space is inserted between each argv[ii] element // command ... aaaaa bbbbb ccccc produces "aaaaa bbbbb ccccc" char * combine_argvs(int argc, char *argv[], int Nth) // 6.8 { int ii, ccv, outcc = 0; static char output[XFCC]; for (ii = Nth; ii < argc; ii++) { ccv = strlen(argv[ii]); if (outcc + ccv > XFCC - 2) return 0; strcpy(output+outcc,argv[ii]); outcc += ccv; output[outcc] = ' '; outcc++; } outcc--; output[outcc] = 0; return output; } /********************************************************************************/ // escape quote marks (") in a file name for use in shell commands // returned file is subject for zfree() char * zescape_quotes(cchar *file1) // 6.8 { char *file2 = 0; if (strchr(file1,'"') == 0) { file2 = zstrdup(file1); return file2; } file2 = zstrdup(file1,20); repl_1str(file1,file2,"\"","\\\""); return file2; } /******************************************************************************** utility to measure CPU time spent in various functions or code blocks cpu_profile_init() initialize at start of test cpu_profile_enter(fnum) at entry to a function cpu_profile_exit(fnum) at exit from a function cpu_profile_report() report CPU time per function Methodology: cpu_profile_init() starts a thread that suspends and runs every 1 millisecond and updates a timer. cpu_profile_enter() and cpu_profile_exit() accumulate the time difference between entry and exit of code being measured. This may be zero because of the long interval between timer updates. Accuracy comes from statistical sampling over many seconds, so that if the time spent in a monitored function is significant, it will be accounted for. The accuracy is better than 1% as long as the measured function or code block consumes a second or more of CPU time during the measurement period. The "fnum" argument (1-99) designates the function or code block being measured. cpu_profile_report() stops the timer thread and reports time consumed per function, using the "fnum" tags in the report. The functions cpu_profile_enter() and cpu_profile_exit() subtract the timer difference and add to a counter per fnum, so the added overhead is insignificant. They are inline functions defined as follows: enter: cpu_profile_timer = cpu_profile_elapsed; exit: cpu_profile_table[fnum] += cpu_profile_elapsed - cpu_profile_timer; *********************************************************************************/ VOL double cpu_profile_table[100]; VOL double cpu_profile_timer; VOL double cpu_profile_elapsed; VOL int cpu_profile_kill = 0; void cpu_profile_init() { void * cpu_profile_timekeeper(void *); for (int ii = 0; ii < 99; ii++) cpu_profile_table[ii] = 0; cpu_profile_elapsed = 0; start_detached_thread(cpu_profile_timekeeper,null); } void cpu_profile_report() { cpu_profile_kill++; printz("elapsed: %.2f \n",cpu_profile_elapsed); for (int ii = 0; ii < 100; ii++) { double dtime = cpu_profile_table[ii]; if (dtime) printz("cpu profile func: %d time: %.2f \n",ii,dtime); } } void * cpu_profile_timekeeper(void *) { timeval time0, time1; gettimeofday(&time0,0); while (true) { gettimeofday(&time1,0); cpu_profile_elapsed = time1.tv_sec - time0.tv_sec + 0.000001 * (time1.tv_usec - time0.tv_usec); zsleep(0.001); if (cpu_profile_kill) break; } cpu_profile_kill = 0; pthread_exit(0); } /********************************************************************************/ // Returns hard page fault rate in faults/second. // First call starts a thread that runs every 2 seconds and keeps a // weighted average of hard fault rate for the last few intervals. namespace pagefaultrate_names { int ftf = 1; int samples = 0; int faultrate = 0; double time1, time2; void * threadfunc(void *); } int pagefaultrate() { using namespace pagefaultrate_names; if (ftf) { ftf = 0; start_detached_thread(threadfunc,0); time1 = get_seconds(); } return faultrate; } void * pagefaultrate_names::threadfunc(void *) { using namespace pagefaultrate_names; FILE *fid; char *pp, buff[200]; double pfs1, pfs2, fps, elaps; while (true) { sleep(2); time2 = get_seconds(); elaps = time2 - time1; time1 = time2; fid = fopen("/proc/self/stat","r"); if (! fid) break; pp = fgets(buff,200,fid); fclose(fid); if (! pp) break; pp = strchr(pp,')'); // closing ')' after (short) filename if (pp) parseprocrec(pp+1,10,&pfs1,11,&pfs2,null); fps = (pfs1 + pfs2) / elaps; faultrate = 0.7 * faultrate + 0.3 * fps; } printz("pagefaultrate() failure \n"); pthread_exit(0); } /******************************************************************************** strField() cchar * strField(cchar *string, cchar *delim, int Nth) Get the Nth field in input string, which contains at least N fields delimited by the character(s) in delim (e.g. blank, comma). Returns a pointer to the found field (actually a pointer to a copy of the found field, with a null terminator appended). If a delimiter is immediately followed by another delimiter, it is considered a field with zero length, and the string "" is returned. Leading blanks in a field are omitted from the returned field. A field with only blanks is returned as "". // 7.2 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]; // 100 substring limit char *pf1, pf2[2000]; // 2000 char. limit cchar quote = '"'; int ii, nf, fcc = 0; if (! string || ! *string) return 0; // bad call if (Nth < 1) return 0; if (ftf) // overall first call { ftf = 0; for (ii = 0; ii < 100; ii++) retf[ii] = 0; } pf1 = (char *) string - 1; // start parse nf = 0; while (nf < Nth) { pf1++; // start field nf++; fcc = 0; while (*pf1 == ' ') pf1++; // skip leading blanks while (true) { if (*pf1 == quote) { // pass chars between single quotes pf1++; // (but without the quotes) while (*pf1 && *pf1 != quote) pf2[fcc++] = *pf1++; if (*pf1 == quote) pf1++; } else if (strchr(delim,*pf1) || *pf1 == 0) break; // found delimiter or null else pf2[fcc++] = *pf1++; // pass normal character if (fcc > 1999) zappcrash("strField() too long"); } if (*pf1 == 0) break; // end of input string } if (nf < Nth) return 0; // no Nth field if (fcc == 0) { // empty field if (*pf1 == 0) return 0; // no field return ""; // 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 embedded blanks. pvals must convert to double using convSD (accepts decimal point or comma) ***/ int strParms(int &begin, cchar *input, char *pname, int maxcc, double &pval) { static int ii, beginx = 3579246; cchar *pnamex, *delim; int cc, err; if (begin == 1) { // start new string begin = ++beginx; ii = 0; } if (begin != beginx) zappcrash("strParms call error"); // thread safe, not reentrant *pname = 0; // initz. outputs to nothing pval = 0; while (input[ii] == ' ') ii++; // skip leading blanks if (input[ii] == 0) return -1; // no more data pnamex = input + ii; // next pname for (cc = 0; ; cc++) { // look for delimiter if (pnamex[cc] == '=') break; if (pnamex[cc] == '|') break; if (pnamex[cc] == 0) break; } if (cc == 0) return 1; // err: 2 delimiters if (cc >= maxcc) return 1; // err: pname too big strncpy0(pname,pnamex,cc+1); // pname >> caller strTrim(pname); // remove trailing blanks if (pnamex[cc] == 0) { // pname + null ii += cc; // position for next call pval = 1.0; // pval = 1 >> caller return 0; } if (pnamex[cc] == '|') { // pname + | ii += cc + 1; // position for next call pval = 1.0; // pval = 1 >> caller return 0; } ii += cc + 1; // pname = pval err = convSD(input + ii, pval, &delim); // parse pval (was strtod() if (err > 1) return 1; while (*delim == ' ') delim++; // skip poss. trailing blanks if (*delim && *delim != '|') return 1; // err: delimiter not | or null ii = delim - input; if (*delim) ii++; // position for next call return 0; } /********************************************************************************/ // Produce random value from hashed input string. // Output range is 0 to max-1. // Benchmark: 0.8 usec for 99 char. string 3 GHz Core i5 int strHash(cchar *string, int max) { uint hash = 1; uchar byte; while ((byte = *string++)) { hash *= byte; hash = hash ^ (hash >> 7); hash = hash * 7; // 6.5 hash = hash & 0x00FFFFFF; } hash = hash % max; return hash; } int64 strHash64(cchar *string, int64 max) // 7.4 { uint64 hash = 1; uchar byte; while ((byte = *string++)) { hash *= byte; hash = hash ^ (hash >> 7); hash = hash * 7; hash = hash & 0x00FFFFFFFFFFFFFF; } 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++; if (*pp1 == 0) { strcpy(dest,""); // null or blank input 7.1 return 0; } 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; memcpy(strout,strin,ccc); // memcpy instead of strncpy 7.1 strout += ccc; strin += ccc; memcpy(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 blank char between cc1 and cc2 if present. // Break at last delimiter char between cc1 and cc2 if present. // Break unconditionally at cc2 if none of the above. // Returns text lines in txout[*] with count as returned function value. // txout and txout[*] are subjects for zfree(). int breakup_text(cchar *txin0, char **&txout, cchar *delims, int cc1, int cc2) { char *txin; uchar ch; int p1, p2, cc3, Nout; int Np, Bp, Sp; txin = zstrdup(txin0); txout = (char **) zmalloc(100 * sizeof(char *)); if (strstr(txin0,"\\n")) // replace "\n" with real newline chars repl_1str(txin0,txin,"\\n","\n"); Nout = p1 = 0; while (true) { p2 = p1; // input line position cc3 = 0; // output line cc Np = Bp = Sp = 0; while (txin[p2]) { // scan further up to cc2 chars ch = txin[p2]; if (ch == '\n') { Np = p2; break; } // break out if newline found if (cc3 >= cc1) { if (ch == ' ') Bp = p2; // remember last ' ' found after cc1 chars if (strchr(delims,ch)) Sp = p2; // remember last delimiter found after cc1 } if (ch < 0) while ((ch = txin[p2+1]) < 0xC0) p2++; p2++; cc3++; if (cc3 == cc2) break; } if (! cc3 && ! Np) break; if (Np) cc3 = Np - p1; else if (Bp) cc3 = Bp - p1 + 1; else if (Sp) cc3 = Sp - p1 + 1; else cc3 = p2 - p1; txout[Nout] = (char *) zmalloc(cc3+1); strncpy0(txout[Nout],txin+p1,cc3+1); Nout++; p2 = p1 + cc3; if (Np) p2++; p1 = p2; if (Nout == 100) break; } zfree(txin); return Nout; } /********************************************************************************/ // Copy and convert string to hex string. // Each input character 'A' >> 3 output characters "41 " void strncpyx(char *out, cchar *in, int ccin) { int ii, jj, c1, c2; char cx[] = "0123456789ABCDEF"; if (! ccin) ccin = strlen(in); for (ii = 0, jj = 0; ii < ccin; ii++, jj += 3) { c1 = (uchar) in[ii] >> 4; c2 = in[ii] & 15; out[jj] = cx[c1]; out[jj+1] = cx[c2]; out[jj+2] = ' '; } out[jj] = 0; return; } /********************************************************************************/ // Strip trailing zeros from ascii floating numbers // (e.g. 1.230000e+02 --> 1.23e+02) void StripZeros(char *pNum) { int ii, cc; int pp, k1, k2; char work[20]; cc = strlen(pNum); if (cc >= 20) return; for (ii = 0; ii < cc; ii++) { if (pNum[ii] == '.') { pp = ii; k1 = k2 = 0; for (++ii; ii < cc; ii++) { if (pNum[ii] == '0') { if (! k1) k1 = k2 = ii; else k2 = ii; continue; } if ((pNum[ii] >= '1') && (pNum[ii] <= '9')) { k1 = 0; continue; } break; } if (! k1) return; if (k1 == pp + 1) k1++; if (k2 < k1) return; strcpy(work,pNum); strcpy(work+k1,pNum+k2+1); strcpy(pNum,work); return; } } } /********************************************************************************/ // test for blank/null string // Returns status depending on input string: // 0 not a blank or null string // 1 argument string is NULL // 2 string has zero length (*string == 0) // 3 string is all blanks int blank_null(cchar *string) { if (! string) return 1; // null string if (! *string) return 2; // zero length string int cc = strlen(string); for (int ii = 0; ii < cc; ii++) if (string[ii] != ' ') return 0; // non-blank string return 3; // blank string } /********************************************************************************/ // clean \x escape sequences and replace them with the escaped character // \n >> newline \" >> doublequote \\ >> backslash etc. // see $ man ascii for the complete list int clean_escapes(char *string) { char *pp1 = string, *pp2 = string, *pp; char char1; char escapes[] = "abtnvfr"; int count = 0; while (true) { char1 = *pp1++; if (char1 == 0) { *pp2 = 0; return count; } else if (char1 == '\\') { char1 = *pp1++; pp = strchr(escapes,char1); if (pp) char1 = pp - escapes + 7; count++; } *pp2++ = char1; } } /********************************************************************************/ // Compute the graphic character count for a UTF8 character string. // Depends on UTF8 rules: // - ascii characters are positive (0x00 to 0x7F) // - 1st char of multichar sequence is negative (0xC0 to 0xFD) // - subsequent multichars are in the range 0x80 to 0xBF int utf8len(cchar *utf8string) { int ii, cc; char xlimit = 0xC0; for (ii = cc = 0; utf8string[ii]; ii++) { if (utf8string[ii] < 0) // multibyte character while (utf8string[ii+1] < xlimit) ii++; // skip extra bytes cc++; } return cc; } /********************************************************************************/ // Extract a UTF8 substring with a specified count of graphic characters. // utf8in input UTF8 string // utf8out output UTF8 string, which must be long enough // pos initial graphic character position to get (0 = first) // cc max. count of graphic characters to get // returns number of graphic characters extracted, <= cc // Output string is null terminated after last extracted character. int utf8substring(char *utf8out, cchar *utf8in, int pos, int cc) { int ii, jj, kk, posx, ccx; char xlimit = 0xC0; for (ii = posx = 0; posx < pos && utf8in[ii]; ii++) { if (utf8in[ii] < 0) while (utf8in[ii+1] < xlimit) ii++; posx++; } jj = ii; for (ccx = 0; ccx < cc && utf8in[jj]; jj++) { if (utf8in[jj] < 0) while (utf8in[jj+1] < xlimit) jj++; ccx++; } kk = jj - ii; strncpy(utf8out,utf8in+ii,kk); utf8out[kk] = 0; return ccx; } /********************************************************************************/ // check a string for valid utf8 encoding // returns: 0 = OK, 1 = bad string int utf8_check(cchar *string) { cchar *pp; unsigned char ch1, ch2, nch; for (pp = string; *pp; pp++) { ch1 = *pp; if (ch1 < 0x7F) continue; if (ch1 > 0xBF && ch1 < 0xE0) nch = 1; else if (ch1 < 0xF0) nch = 2; else if (ch1 < 0xF8) nch = 3; else if (ch1 < 0xFC) nch = 4; else if (ch1 < 0xFE) nch = 5; else return 1; while (nch) { pp++; ch2 = *pp; if (ch2 < 0x80 || ch2 > 0xBF) return 1; nch--; } } return 0; } /********************************************************************************/ // Find the Nth graphic character position within a UTF8 string // utf8in input UTF8 string // Nth graphic character position, zero based // returns starting character (byte) position of Nth graphic character // returns -1 if Nth is beyond the string length int utf8_position(cchar *utf8in, int Nth) { int ii, posx; char xlimit = 0xC0; for (ii = posx = 0; posx < Nth && utf8in[ii]; ii++) { if (utf8in[ii] < 0) // multi-byte character while (utf8in[ii+1] && utf8in[ii+1] < xlimit) ii++; // traverse member bytes posx++; } if (utf8in[ii]) return ii; return -1; } /********************************************************************************/ // err = zsed(file, string1, string2 ... null) // // replace string1/3/5... with string2/4/6... in designated file // returns N lines changed // 6.8 // -1 file not found // -2 other error (with message) int zsed(cchar *infile ...) // 6.8 { int err, ftf, nn; FILE *fid1, *fid2; char *outfile, *pp; char buffin[1000], buffout[1000], buffxx[1000]; cchar *stringin, *stringout; va_list arglist; fid1 = fopen(infile,"r"); if (! fid1) return -1; outfile = zstrdup(infile,8); strcat(outfile,"-temp"); fid2 = fopen(outfile,"w"); if (! fid2) { printz("%d \n",strerror(errno)); zfree(outfile); return -2; } nn = 0; while (true) { pp = fgets(buffin,500,fid1); if (! pp) break; va_start(arglist,infile); ftf = 1; while (true) { stringin = va_arg(arglist, char *); if (! stringin) break; stringout = va_arg(arglist, char *); if (! stringout) break; if (ftf) { ftf = 0; nn += repl_1str(buffin,buffout,stringin,stringout); // 6.8 } else { strcpy(buffxx,buffout); nn += repl_1str(buffxx,buffout,stringin,stringout); // 6.8 } } va_end(arglist); fputs(buffout,fid2); } fclose(fid1); err = fclose(fid2); if (err) { printz("%s \n",strerror(errno)); zfree(outfile); return -2; } rename(outfile,infile); zfree(outfile); return nn; } /********************************************************************************/ // zstrstr() and zstrcasestr() work like strstr() and strcasestr() // but the needle string "" does NOT match any haystack string. const char * zstrstr(const char *haystack, const char *needle) // 7.4 { if (! needle || ! *needle) return 0; return strstr(haystack,needle); } const char * zstrcasestr(const char *haystack, const char *needle) { if (! needle || ! *needle) return 0; return strcasestr(haystack,needle); } /******************************************************************************** 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 conversion, but trailing non-numeric found 2 conversion OK, but outside specified limits 3 null or blank string, converted to zero (obsolete, now status 4) 7.3 4 conversion error, invalid data in string overlapping statuses have following precedence: 4 3 2 1 0 *********************************************************************************/ // Convert string to integer int convSI(cchar *string, int &inum, cchar **delim) // use glib function 7.3 { char *ddelim = 0; int err; inum = strtol(string,&ddelim,10); // convert next characters if (delim) *delim = ddelim; if (ddelim == string) err = 4; // no valid digits else if (*ddelim == '\0') err = 0; // null delimiter else if (*ddelim == ' ') err = 0; // blank delimiter else err = 1; // other delimiter return err; } 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 *** status 3 --> status 4 *** int convSD(cchar *string, double &dnum, cchar **delim) // use glib function 7.3 { char *ddelim = 0; int err; dnum = strtod(string,&ddelim); if (delim) *delim = ddelim; if (ddelim == string) err = 4; // no valid digits else if (*ddelim == '\0') err = 0; // OK, null delimiter else if (*ddelim == ' ') err = 0; // OK, blank delimiter else err = 1; // OK, other delimiter return err; } int convSD(cchar *string, double &dnum, double lolim, double hilim, cchar **delim) { int stat = convSD(string,dnum,delim); if (stat > 2) return stat; // invalid or null/blank if (dnum < lolim) return 2; // return 2 if out of limits if (dnum > hilim) return 2; // (has precedence over status 1) return stat; // limits OK, return 0 or 1 } int convSF(cchar *string, float &fnum, cchar **delim) { double dnum; int err; err = convSD(string,dnum,delim); fnum = dnum; return err; } int convSF(cchar *string, float &fnum, float lolim, float hilim, cchar **delim) { double dnum, dlolim = lolim, dhilim = hilim; int err; err = convSD(string,dnum,dlolim,dhilim,delim); fnum = dnum; return err; } // Convert int to string with returned length. // (will never exceed 12 characters) int convIS(int inum, char *string, int *cc) { int ccc; ccc = snprintf(string,12,"%d",inum); if (cc) *cc = ccc; return 0; } // Convert double to string with specified digits of precision. // Shortest length format (f/e) will be used. // Output length is returned in optional argument cc. // (will never exceed 20 characters) int convDS(double dnum, int digits, char *string, int *cc) { char *pstr; snprintf(string,20,"%.*g",digits,dnum); pstr = strstr(string,"e+"); // 1.23e+12 > 1.23e12 if (pstr) strcpy(pstr+1,pstr+2); pstr = strstr(string,"e0"); // 1.23e02 > 1.23e2 if (pstr) strcpy(pstr+1,pstr+2); pstr = strstr(string,"e0"); if (pstr) strcpy(pstr+1,pstr+2); pstr = strstr(string,"e-0"); // 1.23e-02 > 1.23e-2 if (pstr) strcpy(pstr+2,pstr+3); pstr = strstr(string,"e-0"); if (pstr) strcpy(pstr+2,pstr+3); if (cc) *cc = strlen(string); return 0; } // format a number as "123 B" or "12.3 KB" or "1.23 MB" etc. // prec is the desired digits of precision to output. // WARNING: only the last 100 conversions remain available in memory. // Example formats for 3 digits of precision: // 123 B, 999 B, 1.23 KB, 98.7 KB, 456 KB, 2.34 MB, 45.6 GB, 1.23 GB char * formatKBMB(double fnum, int prec) { #define kilo 1024 #define bmega (kilo*kilo) #define bgiga (kilo*kilo*kilo) cchar *units; static char *output[100]; static int ftf = 1, ii; double gnum; if (ftf) { // keep last 100 conversions ftf = 0; for (ii = 0; ii < 100; ii++) output[ii] = (char *) zmalloc(20); } gnum = fabs(fnum); if (gnum > bgiga) { fnum = fnum / bgiga; units = "GB"; } else if (gnum > bmega) { fnum = fnum / bmega; units = "MB"; } else if (gnum > kilo) { fnum = fnum / kilo; units = "KB"; } else units = "B "; gnum = fabs(fnum); if (prec == 2 && gnum >= 99.5) prec++; // avoid e+nn formats if (prec == 3 && gnum >= 999.5) prec++; if (prec == 4 && gnum >= 9999.5) prec++; if (prec == 5 && gnum >= 99999.5) prec++; if (prec == 6 && gnum >= 999999.5) prec++; if (++ii > 99) ii = 0; snprintf(output[ii],20,"%.*g %s",prec,fnum,units); return output[ii]; } /******************************************************************************** Wildcard string match Match candidate string to wildcard string containing any number of '*' or '?' wildcard characters. '*' matches any number of characters, including zero characters. '?' matches any one character. Returns 0 if match, 1 if no match. Benchmark: 0.032 usec. wild = *asdf*qwer?yxc 3.3 GHz Core i5 match = XXXasdfXXXXqwerXyxc *********************************************************************************/ int MatchWild(cchar *pWild, cchar *pString) { int ii, star; new_segment: star = 0; while (pWild[0] == '*') { star = 1; pWild++; } test_match: for (ii = 0; pWild[ii] && (pWild[ii] != '*'); ii++) { if (pWild[ii] != pString[ii]) { if (! pString[ii]) return 1; if (pWild[ii] == '?') continue; if (! star) return 1; pString++; goto test_match; } } if (pWild[ii] == '*') { pString += ii; pWild += ii; goto new_segment; } if (! pString[ii]) return 0; if (ii && pWild[ii-1] == '*') return 0; if (! star) return 1; pString++; goto test_match; } /******************************************************************************** Wildcard string match - ignoring case Works like MatchWild() above, but case is ignored. ***/ int MatchWildIgnoreCase(cchar *pWild, cchar *pString) { int ii, star; new_segment: star = 0; while (pWild[0] == '*') { star = 1; pWild++; } test_match: for (ii = 0; pWild[ii] && (pWild[ii] != '*'); ii++) { if (! strmatchcaseN(pWild+ii,pString+ii,1)) // the only difference { if (! pString[ii]) return 1; if (pWild[ii] == '?') continue; if (! star) return 1; pString++; goto test_match; } } if (pWild[ii] == '*') { pString += ii; pWild += ii; goto new_segment; } if (! pString[ii]) return 0; if (ii && pWild[ii-1] == '*') return 0; if (! star) return 1; pString++; goto test_match; } /******************************************************************************** SearchWild - wildcard file search Find all files with total /pathname/filename matching a pattern, which may have any number of the wildcard characters '*' and '?' in either or both the pathname and filename. cchar * SearchWild(cchar *wfilespec, int &flag) inputs: flag = 1 to start a new search flag = 2 abort a running search *** do not modify flag within a search *** wfilespec = filespec to search with optional wildcards e.g. "/name1/na*me2/nam??e3/name4*.ext?" return: a pointer to one matching file is returned per call, or null when there are no more matching files. The search may be aborted before completion, but make a final call with flag = 2 to clean up temp file. A new search with flag = 1 will also finish the cleanup. NOT THREAD SAFE - do not use in parallel threads (#) is used in place of (*) in comments below to prevent the compiler from interpreting (#/) as end of comments GNU find peculiarities: find /path/# omits "." files find /path/ includes "." files find /path/# recurses folders under /path/ find /path/#.txt does not recurse folders find /path/#/ finds all files under /path/ find /path/#/# finds files >= 1 folder 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 folders /path/#.txt finds all xxx.txt files under /path/ at all levels (because #.txt matches aaa.txt, /aaa/bbb.txt, etc.) Benchmark: search for /usr/share/#/README, find 365 from 119K files first time: 3.13 secs. (3.5 GHz Core i5 SSD disk) second time: 0.25 secs. ***/ cchar * SearchWild(cchar *wpath, int &uflag) { static FILE *fid = 0; static char matchfile[XFCC]; char searchpath[XFCC]; char command[XFCC]; int cc, err; char *pp; if ((uflag == 1) || (uflag == 2)) { // first call or stop flag if (fid) pclose(fid); // if file open, close it fid = 0; if (uflag == 2) return 0; // stop flag, done } if (uflag == 1) // first call flag { cc = strlen(wpath); if (cc == 0) return 0; if (cc > XFCC-20) zappcrash("SearchWild: wpath > XFCC"); repl_Nstrs(wpath,searchpath,"\"","\\\"","$","\\$",0); // escape " and $ chars. 6.8 pp = strchr(searchpath,'*'); if (pp) { // not efficient but foolproof while ((*pp != '/') && (pp > searchpath)) pp--; // /aaa/bbb/cc*cc... >>> /aaa/bbb/ if (pp > searchpath) *(pp+1) = 0; } snprintf(command,XFCC,"find -L \"%s\" -type f",searchpath); // find files (ordinary, symlink) 6.3 fid = popen(command,"r"); if (! fid) zappcrash(strerror(errno)); uflag = 763568954; // begin search } if (uflag != 763568954) zappcrash("SearchWild, uflag invalid"); while (true) { pp = fgets(matchfile,XFCC-2,fid); // next matching file if (! pp) { pclose(fid); // no more fid = 0; return 0; } cc = strlen(matchfile); // get rid of trailing \n matchfile[cc-1] = 0; err = MatchWild(wpath,matchfile); // wildcard match? if (err) continue; // no return matchfile; // return file } } /******************************************************************************** SearchWildCase - wildcard file search - ignoring case Works like SearchWild() above, but case of file name is ignored. Actually, the trailing part of the path name is also case-insensitive, meaning that it is possible to get more matches than technically correct if folders like this are present: /AAA/BBB/.../filename /AAA/bbb/.../filename ***/ cchar * SearchWildCase(cchar *wpath, int &uflag) { static FILE *fid = 0; static char matchfile[XFCC]; char searchpath[XFCC]; char command[XFCC]; int cc, err; char *pp; if ((uflag == 1) || (uflag == 2)) { // first call or stop flag if (fid) pclose(fid); // if file open, close it fid = 0; if (uflag == 2) return 0; // stop flag, done } if (uflag == 1) // first call flag { cc = strlen(wpath); if (cc == 0) return 0; if (cc > XFCC-20) zappcrash("SearchWild: wpath > XFCC"); repl_Nstrs(wpath,searchpath,"\"","\\\"","$","\\$",0); // escape " and $ chars. 6.8 pp = strchr(searchpath,'*'); if (pp) { // not efficient but foolproof while ((*pp != '/') && (pp > searchpath)) pp--; // /aaa/bbb/cc*cc... >>> /aaa/bbb/ if (pp > searchpath) *(pp+1) = 0; } snprintf(command,XFCC,"find -L \"%s\" -type f",searchpath); // find files (ordinary, symlink) 6.3 fid = popen(command,"r"); if (! fid) zappcrash(strerror(errno)); uflag = 763568954; // begin search } if (uflag != 763568954) zappcrash("SearchWild, uflag invalid"); while (true) { pp = fgets(matchfile,XFCC-2,fid); // next matching file if (! pp) { pclose(fid); // no more fid = 0; return 0; } cc = strlen(matchfile); // get rid of trailing \n matchfile[cc-1] = 0; err = MatchWildIgnoreCase(wpath,matchfile); // wildcard match? if (err) continue; // no return matchfile; // return file } } /******************************************************************************** Find all files matching a given pattern int zfind(cchar *pattern, char **&flist, int &NF) pattern pattern to match, with wildcards flist list of files returned NF count of files returned Returns 0 if OK, +N if error (errno is set). flist and flist[*] are subjects for zfree(). zfind() works for files containing quotes (") *********************************************************************************/ int zfind(cchar *pattern, char **&flist, int &NF) // 7.0 { char **zfind_filelist = 0; // list of filespecs returned int globflags = GLOB_PERIOD; // include dotfiles int err, cc; glob_t globdata; globdata.gl_pathc = 0; // glob() setup globdata.gl_offs = 0; globdata.gl_pathc = 0; NF = 0; // empty output flist = 0; err = glob(pattern,globflags,null,&globdata); // find all matching files 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; if (err) printz("zfind() error: %d \n",err); globfree(&globdata); // free glob memory return err; } NF = globdata.gl_pathc; if (! NF) { globfree(&globdata); return 0; } cc = NF * sizeof(char *); zfind_filelist = (char **) zmalloc(cc); for (int ii = 0; ii < NF; ii++) // loop found files zfind_filelist[ii] = zstrdup(globdata.gl_pathv[ii]); // add file to output list flist = zfind_filelist; globfree(&globdata); // free glob memory return 0; } /********************************************************************************/ // 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 = (int *) malloc((NR+1) * sizeof(int)); 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 = (int *) malloc((NR+1) * sizeof(int)); for (jj = 0; jj < NR; jj++) vv2[vv1[jj]] = jj; vvrec = (char *) malloc(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; } } free(vv1); free(vv2); free(vvrec); } /******************************************************************************** int MemSort (char *RECS, int RL, int NR, int KEYS[][3], int NK) RECS is an array of records, to be sorted in-place. (record length = RL, record count = NR) KEYS[NK][3] is an integer array defined as follows: [N][0] starting position of Nth key field in RECS [N][1] length of Nth key field in RECS [N][2] type of sort for Nth key: 1 = char ascending 2 = char descending 3 = int*4 ascending 4 = int*4 descending 5 = float*4 ascending 6 = float*4 descending 7 = float*8 ascending (double) 8 = float*8 descending Benchmark: 2 million recs of 40 bytes with 4 sort keys: 2.5 secs (3.3 GHz Core i5). ***/ int MemSortComp(cchar *rec1, cchar *rec2); int MemSortKeys[10][3], MemSortNK; int MemSort(char *RECS, int RL, int NR, int KEYS[][3], int NK) { int ii; if (NR < 2) return 1; if (NK > 10) zappcrash("MemSort, bad NK"); if (NK < 1) zappcrash("MemSort, bad NK"); MemSortNK = NK; for (ii = 0; ii < NK; ii++) { MemSortKeys[ii][0] = KEYS[ii][0]; MemSortKeys[ii][1] = KEYS[ii][1]; MemSortKeys[ii][2] = KEYS[ii][2]; } HeapSort(RECS,RL,NR,MemSortComp); return 1; } int MemSortComp(cchar *rec1, cchar *rec2) { int ii, stat, kpos, ktype, kleng; int inum1, inum2; float rnum1, rnum2; double dnum1, dnum2; cchar *p1, *p2; for (ii = 0; ii < MemSortNK; ii++) // loop each key { kpos = MemSortKeys[ii][0]; // relative position kleng = MemSortKeys[ii][1]; // length ktype = MemSortKeys[ii][2]; // type p1 = rec1 + kpos; // absolute position p2 = rec2 + kpos; switch (ktype) { case 1: // char ascending stat = strncmp(p1,p2,kleng); // compare 2 key values if (stat) return stat; // + if rec1 > rec2, - if < break; // 2 keys are equal, check next key case 2: // char descending stat = strncmp(p1,p2,kleng); if (stat) return -stat; break; case 3: // int ascending memmove(&inum1,p1,4); memmove(&inum2,p2,4); if (inum1 > inum2) return 1; if (inum1 < inum2) return -1; break; case 4: // int descending memmove(&inum1,p1,4); memmove(&inum2,p2,4); if (inum1 > inum2) return -1; if (inum1 < inum2) return 1; break; case 5: // float ascending memmove(&rnum1,p1,4); memmove(&rnum2,p2,4); if (rnum1 > rnum2) return 1; if (rnum1 < rnum2) return -1; break; case 6: // float descending memmove(&rnum1,p1,4); memmove(&rnum2,p2,4); if (rnum1 > rnum2) return -1; if (rnum1 < rnum2) return 1; break; case 7: // double ascending memmove(&dnum1,p1,8); memmove(&dnum2,p2,8); if (dnum1 > dnum2) return 1; if (dnum1 < dnum2) return -1; break; case 8: // double descending memmove(&dnum1,p1,8); memmove(&dnum2,p2,8); if (dnum1 > dnum2) return -1; if (dnum1 < dnum2) return 1; break; default: // key type not 1-8 zappcrash("MemSort, bad KEYS sort type"); } } return 0; // records match on all keys } /********************************************************************************/ // test if an integer value matches any in a list of values // returns the matching value or zero if nothing matches // list of values must end with zero // zero cannot be one of the values to match int zmember(int testval, int matchval1, ...) // 6.8 { va_list arglist; int matchval; va_start(arglist,matchval1); matchval = matchval1; while (matchval) { if (testval == matchval) break; matchval = va_arg(arglist,int); } va_end(arglist); return matchval; } /******************************************************************************** variable string list functions - array / list of strings pvlist * pvlist_create(int max) void pvlist_free(pvlist *pv) int pvlist_append(pvlist *pv, cchar *entry, int unique) int pvlist_prepend(pvlist *pv, cchar *entry, int unique) int pvlist_find(pvlist *pv, cchar *entry) int pvlist_remove(pvlist *pv, cchar *entry) int pvlist_remove(pvlist *pv, int Nth) int pvlist_count(pvlist *pv) int pvlist_replace(pvlist *pv, int Nth, char *entry) cchar * pvlist_get(pvlist *pv, int Nth) int pvlist_sort(pvlist *pv) These functions manage a variable length list of variable length strings. Declare such a list as: pvlist *pv; *********************************************************************************/ // Creates a pvlist with a capacity of max strings and returns a pointer. // String lengths are unlimited, but the count of strings is limited to max. // Memory is allocated for max pointers at first. Memory for the strings is // allocated and freed as the strings are added or removed. pvlist * pvlist_create(int max) { pvlist *pv; pv = (pvlist *) zmalloc(sizeof(pvlist)); pv->max = max; pv->act = 0; pv->list = (char **) zmalloc(max * sizeof(char *)); return pv; } // free memory for variable list and contained strings void pvlist_free(pvlist *pv) { int ii; for (ii = 0; ii < pv->act; ii++) zfree(pv->list[ii]); zfree(pv->list); zfree(pv); } // append new entry to end of list (optional if unique) // if list if full, first entry is removed and rest are packed down // return: N >= 0: new entry added at position N // N = -1: not unique, not added int pvlist_append(pvlist *pv, cchar *entry, int unique) { int ii; if (unique && pvlist_find(pv,entry) >= 0) return -1; // not unique if (pv->act == pv->max) pvlist_remove(pv,0); // if list full, remove 1st entry ii = pv->act; pv->list[ii] = zstrdup(entry); // add to end of list pv->act++; return ii; } // prepend new entry to list (optional if unique) // prior list entries are pushed down to make room // if list is full, last entry is removed first // return: N = 0: new entry added at position 0 // N = -1: not unique, not added int pvlist_prepend(pvlist *pv, cchar *entry, int unique) { int ii; if (unique && pvlist_find(pv,entry) >= 0) return -1; // not unique if (pv->act == pv->max) pvlist_remove(pv,pv->act-1); // if list full, remove last entry for (ii = pv->act; ii > 0; ii--) // push all list entries down pv->list[ii] = pv->list[ii-1]; pv->list[0] = zstrdup(entry); // add to start of list pv->act++; return 0; } // find list entry by name, return entry (0 based) // return -1 if not found int pvlist_find(pvlist *pv, cchar *entry) { int ii; for (ii = 0; ii < pv->act; ii++) if (strmatch(entry,pv->list[ii])) break; if (ii < pv->act) return ii; return -1; } // remove an entry by name and repack list // return (former) entry or -1 if not found int pvlist_remove(pvlist *pv, cchar *entry) { int ii; ii = pvlist_find(pv,entry); if (ii < 0) return -1; pvlist_remove(pv,ii); return ii; } // remove an entry by number and repack list // returns -1 if entry is beyond list end int pvlist_remove(pvlist *pv, int ii) { if (ii < 0 || ii >= pv->act) return -1; zfree(pv->list[ii]); for (++ii; ii < pv->act; ii++) // pre-increment pv->list[ii-1] = pv->list[ii]; pv->act--; return 0; } // return entry count int pvlist_count(pvlist *pv) { return pv->act; } // replace Nth entry with new one int pvlist_replace(pvlist * pv, int ii, cchar *entry) { if (ii < 0 || ii >= pv->act) return -1; zfree(pv->list[ii]); pv->list[ii] = zstrdup(entry); return 0; } // return Nth entry or null char * pvlist_get(pvlist *pv, int Nth) { if (Nth >= pv->act) return 0; return pv->list[Nth]; } // sort list in ascending order int pvlist_sort(pvlist *pv) { HeapSort(pv->list,pv->act); return 0; } /********************************************************************************/ // Random number generators with explicit context // and improved randomness over a small series. // Benchmark: lrandz 0.012 usec drandz 0.014 usec 3.3 GHz Core i5 int lrandz(int64 *seed) // returns 0 to 0x7fffffff { *seed = *seed ^ (*seed << 17); *seed = *seed ^ (*seed << 20); return nrand48((unsigned int16 *) seed); } int lrandz() // implicit seed, repeatable sequence { static int64 seed = 12345678; return lrandz(&seed); } double drandz(int64 *seed) // returns 0.0 to 0.99999... { *seed = *seed ^ (*seed << 17); *seed = *seed ^ (*seed << 20); return erand48((unsigned int16 *) seed); } double drandz() // automatic seed, volatile { static int64 seed = get_seconds(); // 6.6 return drandz(&seed); } /******************************************************************************** spline1: define a curve using a set of data points (x and y values) spline2: for a given x-value, return a y-value fitting the curve For spline1, the no. of curve-defining points must be < 100. For spline2, the given x-value must be within the range defined in spline1. The algorithm was taken from the book "Numerical Recipes" (Cambridge University Press) and converted from Fortran to C++. ***/ namespace splinedata { int nn; float px1[100], py1[100], py2[100]; } void spline1(int dnn, float *dx1, float *dy1) { using namespace splinedata; float sig, p, u[100]; int ii; nn = dnn; if (nn > 100) zappcrash("spline1(), > 100 data points"); for (ii = 0; ii < nn; ii++) { px1[ii] = dx1[ii]; py1[ii] = dy1[ii]; if (ii && px1[ii] <= px1[ii-1]) zappcrash("spline1(), x-value not increasing"); } py2[0] = u[0] = 0; for (ii = 1; ii < nn-1; ii++) { sig = (px1[ii] - px1[ii-1]) / (px1[ii+1] - px1[ii-1]); p = sig * py2[ii-1] + 2; py2[ii] = (sig - 1) / p; u[ii] = (6 * ((py1[ii+1] - py1[ii]) / (px1[ii+1] - px1[ii]) - (py1[ii] - py1[ii-1]) / (px1[ii] - px1[ii-1])) / (px1[ii+1] - px1[ii-1]) - sig * u[ii-1]) / p; } py2[nn-1] = 0; for (ii = nn-2; ii >= 0; ii--) py2[ii] = py2[ii] * py2[ii+1] + u[ii]; return; } float spline2(float x) { using namespace splinedata; int kk, klo = 0, khi = nn-1; float h, a, b, y; while (khi - klo > 1) { kk = (khi + klo) / 2; if (px1[kk] > x) khi = kk; else klo = kk; } h = px1[khi] - px1[klo]; a = (px1[khi] - x) / h; b = (x - px1[klo]) / h; y = a * py1[klo] + b * py1[khi] + ((a*a*a - a) * py2[klo] + (b*b*b - b) * py2[khi]) * (h*h) / 6; return y; } /********************************************************************************/ // Add text strings to a FIFO queue, retrieve text strings. // Can be used by one or two threads. // thread 1: open queue, get strings, close queue. // thread 2: put strings into queue. // create and initialize Qtext queue, empty status void Qtext_open(Qtext *qtext, int cap) { int cc; qtext->qcap = cap; qtext->qnewest = -1; qtext->qoldest = -1; qtext->qdone = 0; cc = cap * sizeof(char *); qtext->qtext = (char **) zmalloc(cc); memset(qtext->qtext,0,cc); return; } // add new text string to Qtext queue // if queue full, sleep until space is available void Qtext_put(Qtext *qtext, cchar *format, ...) { int qnext; va_list arglist; char message[200]; va_start(arglist,format); vsnprintf(message,199,format,arglist); va_end(arglist); qnext = qtext->qnewest + 1; if (qnext == qtext->qcap) qnext = 0; while (qtext->qtext[qnext]) zsleep(0.01); qtext->qtext[qnext] = zstrdup(message); qtext->qnewest = qnext; return; } // remove oldest text string from Qtext queue // if queue empty, return a null string // returned string is subject for zfree() char * Qtext_get(Qtext *qtext) { int qnext; char *text; if (qtext->qcap == 0) return 0; qnext = qtext->qoldest + 1; if (qnext == qtext->qcap) qnext = 0; text = qtext->qtext[qnext]; if (! text) return 0; qtext->qtext[qnext] = 0; qtext->qoldest = qnext; return text; } // close Qtext, zfree() any leftover strings void Qtext_close(Qtext *qtext) { for (int ii = 0; ii < qtext->qcap; ii++) if (qtext->qtext[ii]) zfree(qtext->qtext[ii]); zfree(qtext->qtext); // 6.0 qtext->qcap = 0; return; } /******************************************************************************** Create appimage desktop file and icon file in /home//.local/... This will make the appimage work normally within the system menus. Executed at application startup time. Source files in AppImage file system (.../AppDir/) /usr/share/appname/appname.desktop /usr/share/appname/appname.png Destination files in /home// /home//.local/bin/appname-NN.N-appimage // appimage executable file /home//.local/share/applications/appname.desktop // XDG desktop file /home//.local/share/icons/appname.png // XDG icon file returns: 1 already done 2 new desktop file created OK 3 not an appimage 4 failure *****/ namespace make_appimage_names { char appname1[60], appname2[40]; } int appimage_install(cchar *appname) // 7.1 { using namespace make_appimage_names; FILE *fid; int err, cc, nn; char *pp, *homedir; char buff[300]; char desktopfile1[100], desktopfile2[100]; char iconfile1[100], iconfile2[100]; char exectext1[100], exectext2[100]; char icontext1[100], icontext2[100]; pp = getenv("APPIMAGE"); // appimage executable file if (! pp) return 3; if (! strstr(pp,appname)) return 3; // not my appimage appimagexe = pp; pp = strrchr(appimagexe,'/'); if (! pp) return 4; strncpy0(appname1,pp+1,60); // appname1: appname-NN.N-appimage pp = strchr(appname1,'-'); if (! pp) return 4; cc = pp - appname1; strncpy(appname2,appname1,cc); // appname2: appname appname2[cc] = 0; cc = readlink("/proc/self/exe",buff,300); // get own program path if (cc <= 0) return 4; buff[cc] = 0; // /tmp/mountpoint/usr/bin/appname pp = strstr(buff,"/usr/"); if (! pp) return 4; pp[4] = 0; // /tmp/mountpoint/usr/ homedir = getenv("HOME"); if (strchr(homedir,' ')) { // check /home/user has no blanks printz("user home \"%s\" has embedded blank",homedir); return 4; } // /.../usr/share/appname/appname.desktop >> /home//.local/share/applications/appname.desktop snprintf(desktopfile1,100,"%s/share/%s/%s.desktop",buff,appname2,appname2); snprintf(desktopfile2,100,"%s/.local/share/applications/%s.desktop",homedir,appname2); // /.../usr/share/appname/appname.png >> /home//.local/share/icons/appname.png snprintf(iconfile1,100,"%s/share/%s/icons/%s.png",buff,appname2,appname2); snprintf(iconfile2,100,"%s/.local/share/icons/%s.png",homedir,appname2); fid = fopen(desktopfile2,"r"); // open existing desktop file if (fid) { while (true) { // read desktop file pp = fgets(buff,300,fid); if (! pp) break; if (strmatchN(buff,"Exec=",5)) break; // look for "Exec=[my appimage]" } fclose(fid); if (pp) { cc = strlen(appimagexe); // appimage executable file nn = strmatchN(appimagexe,buff+5,cc); // compare to Exec= appimage file if (nn) return 1; // same, do nothing } } err = copyFile(desktopfile1,desktopfile2); // /home//.local/share/ if (err) { // /applications/appname.desktop printz("cannot create %s \n",desktopfile2); return 4; } err = copyFile(iconfile1,iconfile2); // /home//.local/share/ if (err) { // /icons/appname.png printz("cannot create %s \n",iconfile2); return 4; } snprintf(exectext1,100,"Exec=%s",appname2); // Exec=appname2 snprintf(exectext2,100,"Exec=\"%s\"",appimagexe); // Exec=appimagexe 7.4 snprintf(icontext1,100,"Icon=/usr/share/%s/icons/%s.png",appname2,appname2); snprintf(icontext2,100,"Icon=%s",iconfile2); err = zsed(desktopfile2,exectext1,exectext2,icontext1,icontext2,null); // make text substitutions if (err < 0) { // failure printz("cannot update %s \n",desktopfile2); return 4; } chmod(desktopfile2,0751); // make fotoxx.desktop executable printz("appimage desktop file created at %s \n", desktopfile2); printz("appimage icon file created at %s \n", iconfile2); return 2; } /********************************************************************************/ // Uninstall AppImage program, desktop and icon files // exits if program is uninstalled // returns if not (user cancels, program is not an appimage) void appimage_unstall() // 7.1 { using namespace make_appimage_names; char *homedir; char desktopfileloc[200]; char iconfileloc[200]; if (! appimagexe) { printz("not an appimage, nothing was done \n"); return; } homedir = getenv("HOME"); snprintf(desktopfileloc,200,"%s/.local/share/applications/",homedir); snprintf(iconfileloc,200,"%s/.local/share/icons/",homedir); shell_ack("rm -f %s/%s.desktop",desktopfileloc,appname2); shell_ack("rm -f %s/%s.png",iconfileloc,appname2); shell_ack("rm -f %s",appimagexe); zexit("appimage uninstalled"); } /******************************************************************************** Initialize application files according to following conventions: // new version + binary executable is at: /prefix/bin/appname // = PREFIX/bin/appname + other application folders are derived as follows: /prefix/share/appname/data/ desktop, parameters, userguide ... /prefix/share/doc/appname/ README, changelog, appname.man ... /prefix/share/appname/icons/ application icon files, filename.png /prefix/share/appname/images/ application image files 6.8 /prefix/share/appname/locales/ translate-xx.po ... (original) /home/user/.appname/ some installation files are copied here /home/user/.appname/logfile log file with error messages zprefix install location normally /usr subdirs: /bin /share /doc zdatadir installed data files /prefix/share/appname/data/ zdocdir documentation files /prefix/share/doc/appname/ zimagedir images /prefix/share/appname/images 6.8 zlocalesdir translation files /prefix/share/appname/locales/ zhomedir local app files /home//.appname If it does not already exist, an application folder for the current user is created at /home/username/.appname (following common Linux convention). If this folder was created for the first time, copy specified files (following the 1st argument) from the install folder into the newly created user-specific folder. The assumption is that all initial data files for the application (e.g. parameters) will be in the install data folder, and these are copied to the user folder where the user or application can modify them. If the running program is not connected to a terminal device, stdout and stderr are redirected to the log file at /home/user/.appname/logfile ***/ cchar * get_zprefix() { return zfuncs::zprefix; } // /usr or /home/ cchar * get_zhomedir() { return zfuncs::zhomedir; } // /home//.appname or /root/.appname cchar * get_zdatadir() { return zfuncs::zdatadir; } // data files cchar * get_zdocdir() { return zfuncs::zdocdir; } // documentation files cchar * get_zimagedir() { return zfuncs::zimagedir; } // image files 6.8 cchar * get_zlocalesdir() { return zfuncs::zlocalesdir; } // translation files int zinitapp(cchar *appvers, cchar *homedir) // appname-N.N, opt. home dir 7.4 { char logfile[200], oldlog[200]; char buff[300], Phomedir[200]; char *pp, *chTnow; int cc, err; time_t Tnow; STATB statb; FILE *fid; ftime(&startime); // app startup time Tnow = startime.time; catch_signals(); // catch signals, do backtrace progexe = 0; cc = readlink("/proc/self/exe",buff,300); // get own program path 7.1 if (cc > 0) { buff[cc] = 0; // readlink() quirk progexe = zstrdup(buff); } if (appimagexe) printz("program exe: %s \n",appimagexe); // 7.1 else printz("program exe: %s \n",progexe); printz("build date/time: %s \n",build_date_time); // 7.1 strncpy0(zappvers,appvers,40); // appname-N.N 7.4 strncpy0(zappname,appvers,40); // appneme without version pp = strchr(zappname,'-'); if (pp && strmatchN(pp,"-maps",5)) pp = strchr(pp+1,'-'); // special case fotoxx-maps-N.N if (pp) *pp = 0; if (homedir && *homedir == '/') // homedir from caller 6.5 strncpy0(zhomedir,homedir,199); else { snprintf(zhomedir,199,"%s/.%s",getenv("HOME"),zappname); // use /home//.appname snprintf(Phomedir,200,"%s-home",zhomedir); // check /home//.appname-home 6.8 fid = fopen(Phomedir,"r"); if (fid) { pp = fgets_trim(Phomedir,200,fid); // if found, read pointer to homedir if (pp) strncpy0(zhomedir,pp,200); fclose(fid); } } printz("%s home: %s \n",zappname,zhomedir); // forbid space in home folder 6.8 if (strchr(zhomedir,' ')) zexit("home folder name contains a space"); cc = strlen(zhomedir); // stop humongous username if (cc > 160) zexit("home folder name too big"); err = stat(zhomedir,&statb); // does app home exist already? if (err) { err = mkdir(zhomedir,0750); // no, create and initialize if (err) zexit("cannot create %s: %s",zhomedir,strerror(errno)); } chTnow = ctime(&Tnow); chTnow[19] = 0; // eliminate hundredths of seconds if (! isatty(fileno(stdin))) { // not attached to a terminal snprintf(logfile,199,"%s/logfile",zhomedir); // /home//logfile snprintf(oldlog,199,"%s/logfile.old",zhomedir); err = stat(logfile,&statb); if (! err) rename(logfile,oldlog); // rename old log file 6.6 fid = freopen(logfile,"a",stdout); // redirect output to log file fid = freopen(logfile,"a",stderr); if (! fid) printz("*** cannot redirect stdout and stderr \n"); } printz("start %s %s \n",zappname,chTnow); fflush(0); cc = readlink("/proc/self/exe",zprefix,200); // get my executable path 6.4 if (cc <= 0) { printz("readlink() /proc/self/exe) failed \n"); // if failed, assume /usr/bin/ strcpy(zprefix,"/usr/bin/"); } else zprefix[cc] = 0; // readlink() quirk pp = strstr(zprefix,"/bin/"); // get install prefix (e.g. /usr) if (pp) *pp = 0; else (strcpy(zprefix,"/usr")); // if /xxxxx/bin --> /xxxxx 6.4 strncatv(zdatadir,199,zprefix,"/share/",zappname,"/data",null); // /prefix/share/appname/data strncatv(zimagedir,199,zprefix,"/share/",zappname,"/images",null); // /prefix/share/appname/images 6.8 strncatv(zlocalesdir,199,zprefix,"/share/",zappname,"/locales",null); // /prefix/share/appname/locales strncatv(zdocdir,199,zprefix,"/share/doc/",zappname,null); // /prefix/share/doc/appname #ifdef DOCDIR strncpy0(zdocdir,DOCDIR,199); // flexible DOCDIR location (SUSE) 6.5 #endif if (! strmatch(zappname,"fotoxx-maps")) // omit fotoxx-maps 7.4 shell_quiet("cp -R -n %s/* %s",zdatadir,zhomedir); // copy MISSING files > user home 6.8 tid_main = pthread_self(); // thread ID of main() process appruns_update(); // usage statistics 7.3 if (! gtk_init_check(0,null)) { // non GUI jobs exit here 6.8 printz("gtk_init() not done, zinitapp() return \n"); // this does not print (??) FIXME return 0; } // remaining code is for GUI process only // 6.8 display = gdk_display_get_default(); // get hardware info 6.8 screen = gdk_screen_get_default(); #if GTK_CHECK_VERSION(3,22,0) GdkRectangle rect; GdkMonitor *monitor; monitor = gdk_display_get_primary_monitor(display); gdk_monitor_get_geometry(monitor,&rect); monitor_ww = rect.width; monitor_hh = rect.height; #else monitor_ww = gdk_screen_get_width(screen); // Ubuntu 16.10 monitor_hh = gdk_screen_get_height(screen); #endif if (! monitor_ww) zexit("GTK cannot get monitor data"); #if GTK_CHECK_VERSION(3,20,0) GdkSeat *gdkseat = 0; // screen / KB / pointer associations if (screen) gdkseat = gdk_display_get_default_seat(display); // 6.5 Ubuntu 16.10 if (screen) gtksettings = gtk_settings_get_for_screen(screen); if (gdkseat) mouse = gdk_seat_get_pointer(gdkseat); #else GdkDeviceManager *devmanager = 0; // knows screen / mouse associations if (screen) devmanager = gdk_display_get_device_manager(display); // 6.2 Ubuntu 16.04 if (screen) gtksettings = gtk_settings_get_for_screen(screen); if (devmanager) mouse = gdk_device_manager_get_client_pointer(devmanager); if (! mouse) printz("*** GTK cannot get pointer device \n"); #endif if (! mouse) zexit("GTK cannot get pointer device"); if (gtksettings) { // get default font 6.3 g_object_get(gtksettings,"gtk_font_name",&appfont,null); zsetfont(appfont); // set mono and bold versions } return 1; } // set a new application font via GtkSettings // newfont should be something like "sans 11" // use generic monospace font since app font may not have a mono version void zsetfont(cchar *newfont) // 6.3 { char font[40], bfont[48], mfont[48], mbfont[56]; char junk[40]; int nn, size; if (! gtksettings) return; nn = sscanf(newfont,"%s %d",font,&size); // "sans 11" if (nn != 2) { nn = sscanf(newfont,"%s %s %d",font,junk,&size); if (nn != 3) goto fail; } if (size < 5 || size > 30) goto fail; g_object_set(gtksettings,"gtk-font-name",newfont,null); // set dialog font snprintf(bfont,48,"%s bold %d",font,size); // "sans bold 11" snprintf(mfont,48,"mono %d",size-1); // "mono 10" 6.5 snprintf(mbfont,56,"mono bold %d",size-1); // "mono bold 10" appfont = zstrdup(newfont); appboldfont = zstrdup(bfont); appmonofont = zstrdup(mfont); appmonoboldfont = zstrdup(mbfont); appfontsize = size; return; fail: printz("cannot set font: %s \n",newfont); return; } // get the font character width and height for a given widget // returns 0 if OK, +N if error int widget_font_metrics(GtkWidget *widget, int &fontwidth, int &fontheight) // 6.8 { PangoContext *pangocontext; PangoFontDescription *pangofontdesc; PangoFontMetrics *pangofontmetrics; PangoLanguage *pangolanguage; pangocontext = gtk_widget_get_pango_context(widget); pangofontdesc = pango_context_get_font_description(pangocontext); pangolanguage = pango_language_get_default(); pangofontmetrics = pango_context_get_metrics(pangocontext,pangofontdesc,pangolanguage); if (! pangofontmetrics) { printz("widget_font_metrics() failed \n"); return 1; } fontwidth = pango_font_metrics_get_approximate_char_width(pangofontmetrics); fontheight = pango_font_metrics_get_ascent(pangofontmetrics) + pango_font_metrics_get_descent(pangofontmetrics); fontwidth /= PANGO_SCALE; fontheight /= PANGO_SCALE; return 0; } // Find a locale-dependent installation file or user file. // file type: doc, data, locale, user [ userlocale removed v.6.1 ] // file name: README, changelog, userguide.html, parameters, translate.po ... // Returns complete file name, e.g. /usr/share/appname/locales/translate-de.po // Output filespec should be 200 bytes (limit for all installation files). // Returns 0 if OK, +N if not found. int locale_filespec(cchar *filetype, cchar *filename, char *filespec) { char *pp, fname[20], fext[8]; char lc_RC[8]; // -lc or -lc_RC int cc, err; STATB statb; filespec[0] = '/'; strcat(filespec,filetype); // leave /type as default if (strmatch(filetype,"doc")) strcpy(filespec,zdocdir); // /usr/share/doc/appname if (strmatch(filetype,"data")) strcpy(filespec,zdatadir); // /usr/share/appname/data if (strmatch(filetype,"locale")) strcpy(filespec,zlocalesdir); // /usr/share/appname/locales if (strmatch(filetype,"user")) strcpy(filespec,zhomedir); // /home//.appname strncpy0(fname,filename,20); pp = strchr(fname,'.'); if (pp) { strcpy(fext,pp); // file type .fext *pp = 0; } else *fext = 0; // no type lc_RC[0] = '-'; strncpy0(lc_RC+1,zlocale,6); // locale with region code: -lc_RC tryextras: cc = strlen(filespec); filespec[cc] = '/'; // /folders.../ strcpy(filespec+cc+1,fname); // /folders.../fname cc = strlen(filespec); // | pp = filespec + cc; // pp strcpy(pp,lc_RC); // /folders.../fname-lc_RC.fext strcat(pp,fext); err = stat(filespec,&statb); if (! err) return 0; strcpy(pp+3,fext); // /folders.../fname-lc.fext err = stat(filespec,&statb); if (! err) return 0; strcpy(pp,"-en"); // /folders.../fname-en.fext strcat(pp,fext); err = stat(filespec,&statb); if (! err) return 0; strcpy(pp,fext); // /folders.../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 application log file in a popup window // The log file is /home//.appname/logfile void showz_logfile(GtkWidget *parent) // log file { char buff[200]; fflush(0); snprintf(buff,199,"cat %s/logfile",zhomedir); popup_command(buff,800,600,parent); 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, GtkWidget *parent) // add parent 6.8 { char filex[40], filespec[200], command[200]; int err; strncpy0(filex,file,36); // look for gzip file first strcat(filex,".gz"); err = locale_filespec(type,filex,filespec); if (! err) { snprintf(command,200,"zcat %s",filespec); popup_command(command,700,500,parent,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,700,500,parent,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://folder/.../filename#livelink void showz_html(cchar *url) { static char prog[40]; static int ftf = 1, err; if (ftf) { ftf = 0; *prog = 0; err = zsystem("which firefox"); // use xdg-open only as last resort if (! err) strcpy(prog,"firefox"); else { err = zsystem("which chromium-browser"); if (! err) strcpy(prog,"chromium-browser --new-window"); else { err = zsystem("which xdg-open"); if (! err) strcpy(prog,"xdg-open"); } } } if (! *prog) { zmessageACK(null,"html file reader not found"); return; } shell_ack("%s %s &",prog,url); return; } /******************************************************************************** void showz_docfile(GtkWidget *parent, char *docfile, char *topic) Show docfile in popup scrolling text window with 'topic' at the top. docfile is located in data folder: get_zdatadir() images are located in image folder: get_zimagedir() docfile format: plain text file, topics in col. 1, text indented. images in a separate line: +image: filename.jpg +image: ... embedded links to topics: |topic name| underlined words: _underline (underline to next blank) TOPIC 1 linkable topics in col. 0 text text text text text text text text text text text text text text text text text text text text text text text text ... TOPIC 2 +image: file1.png +image file2.jpg embedded images text text text text text text text text |TOPIC 1| text text link to topic text text text text text text text text text text text text \_subtopic line underline total line text text text text text text text text text text text text text text text text text \_UNDERLINED TEXT\_ text text text underline subtext text text text text text \bBOLD TEXT\b text text text bold subtext text text text text text http******* text text text web link text text text text text text text text text text text text ... *********************************************************************************/ namespace showz_docfile_names { zdialog *zd = 0; int currline, lastline; GtkWidget *popwidget; GtkWidget *textwidget; char *topicnames[1000]; int topiclines[1000]; int Ntopics = 0; int backtab[10]; int maxback = 10, backpos = 0; } void showz_docfile(GtkWidget *parent, cchar *docfile, cchar *utopic) // 7.3 { using namespace showz_docfile_names; void showz_docfile_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void validate_docfile(cchar *docfile); FILE *fid; char filespec[200], buff1[200], buff2[200]; char topic[50], image[100]; char *pp1, *pp2, *pp3; int ii, cc, line, posn, remv1, remv2; int Fmarkups; GdkPixbuf *pixbuf; GError *gerror; if (utopic && strmatch(utopic,"validate")) { // check for errors (developer function) validate_docfile(docfile); return; } if (zd && zdialog_valid(zd,docfile)) goto initz_done; for (ii = 0; ii < Ntopics; ii++) zfree(topicnames[ii]); for (ii = 0; ii < maxback; ii++) backtab[ii] = -1; currline = lastline = 0; Ntopics = 0; snprintf(filespec,200,"%s/%s",get_zdatadir(),docfile); // open docfile fid = fopen(filespec,"r"); if (! fid) { zmessageACK(parent,"%s %s",filespec,strerror(errno)); return; } zd = popup_report_open(docfile,parent,999,700,0,showz_docfile_clickfunc, // popup window for docfile text 20.04 "<",">","hide","find",0); // buttons if (! zd) { fclose(fid); return; } zdialog_show(zd,0); for (line = 0; line < 99999; line++) // read docfile text lines { pp1 = fgets_trim(buff1,200,fid); // line without \n EOL if (! pp1) break; // EOF if (strmatchN(pp1,"EOF",3)) break; // end of visible text pp1 = strstr(buff1,"+image:"); if (pp1) { while (pp1) { popup_report_write(zd,0," ",0); // 3 spaces pp2 = strstr(pp1+7,"+image:"); // next image file if (pp2) *pp2 = 0; strncpy0(image,pp1+7,100); strTrim2(image); snprintf(filespec,200,"%s/%s",get_zimagedir(),image); // full filespec gerror = 0; pixbuf = gdk_pixbuf_new_from_file(filespec,&gerror); // convert to pixbuf image if (pixbuf) { popup_report_insert_pixbuf(zd,line,pixbuf); // write image to output line g_object_unref(pixbuf); } else printz("cannot load image file: %s \n",image); pp1 = pp2; } popup_report_write(zd,0,"\n",0); // write image line EOL continue; // next line } if (buff1[0] > ' ') // topic line { popup_report_write(zd,1,"%s\n",buff1); // write topic line to output, bold strncpy0(topic,buff1,50); // add topic and line number to list strTrim(topic); topicnames[Ntopics] = zstrdup(topic); topiclines[Ntopics] = line; if (++Ntopics < 1000) continue; zexit("showz_docfile(): exceed 1000 topics"); } strncpy0(buff2,buff1,200); // not a topic line pp1 = buff2; // copy line with embedded markups \* Fmarkups = 0; while ((pp1 = strchr(pp1,'\\'))) // remove all "\_" and "\b" { if (pp1[1] == '_' || pp1[1] == 'b') { cc = strlen(pp1+2); memmove(pp1,pp1+2,cc+1); Fmarkups = 1; // markups found in this line } else pp1++; } popup_report_write(zd,0,"%s\n",buff2); // write text line to output if (! Fmarkups) continue; // no markups, next line // buff1 = original line with \_ and \b markups // buff2 = buff1 with markups removed pp1 = buff1; remv1 = 0; while (true) { pp1 = strchr(pp1,'\\'); // find next markup "\*" if (! pp1) break; if (pp1[1] != '_') { // not underline "\_" pp1 += 2; remv1 += 2; // cc to remove for prior markup continue; } pp2 = strstr(pp1+2,"\\_"); // underline end posn if (pp2) cc = pp2 - pp1 - 2; // length of underline else cc = strlen(pp1) - 2; // (with poss. embedded markups) remv2 = 0; pp3 = pp1 + 2; while (true) { // find embedded markups "\*" pp3 = strchr(pp3,'\\'); if (! pp3) break; if (pp3 >= pp1 + 2 + cc) break; remv2 += 2; // reduce cc for embedded markups pp3 += 2; } posn = pp1 - buff1 - remv1; // underline posn cc = cc - remv2; // underline cc popup_report_underline_word(zd,line,posn,cc); // underline the text remv1 += 4; // cc to remove for following markup if (! pp2) break; pp1 = pp2 + 2; // search for next underline remv1 += remv2; } pp1 = buff1; // repeat for bold markups "\b" remv1 = 0; while (true) { pp1 = strchr(pp1,'\\'); if (! pp1) break; if (pp1[1] != 'b') { pp1 += 2; remv1 += 2; continue; } pp2 = strstr(pp1+2,"\\b"); if (pp2) cc = pp2 - pp1 - 2; else cc = strlen(pp1) - 2; remv2 = 0; pp3 = pp1 + 2; while (true) { pp3 = strchr(pp3,'\\'); if (! pp3) break; if (pp3 >= pp1 + 2 + cc) break; remv2 += 2; pp3 += 2; } posn = pp1 - buff1 - remv1; cc = cc - remv2; popup_report_bold_word(zd,line,posn,cc); remv1 += 4; if (! pp2) break; pp1 = pp2 + 2; remv1 += remv2; } continue; // next line } lastline = line; fclose(fid); popwidget = zdialog_widget(zd,"dialog"); // popup zdialog initz_done: currline = 0; // docfile line for topic if (utopic) // initial topic from caller { strncpy0(topic,utopic,50); cc = strlen(topic); for (ii = 0; ii < cc; ii++) // conv. topic to uppercase topic[ii] = toupper(topic[ii]); for (ii = 0; ii < Ntopics; ii++) { // search docfile topics if (strcmp(topic,topicnames[ii]) == 0) { currline = topiclines[ii]; // get line of matching topic break; } } if (ii == Ntopics) printz("topic not found: %s %s \n",utopic,topic); } popup_report_font_attributes(zd); // high contrast popup_report_scroll_top(zd,currline); // scroll to topic line textwidget = zdialog_widget(zd,"text"); // text widget in zdialog zdialog_show(zd,1); return; } void showz_docfile_clickfunc(GtkWidget *textwidget, int line, int pos, int kbkey) { using namespace showz_docfile_names; int ii, jj, cc; int top, bott, page; char *text, *pp1, *pp2; char topic[50], weblink[200]; textwidget_get_visible_lines(textwidget,top,bott); // range of lines on screen if (line >= 0 || kbkey == GDK_KEY_Return) // line clicked or ENTER key at line { text = textwidget_line(textwidget,line,1); // selected line if (text) { pp2 = strchr(text + pos,'|'); if (pp2) { // link to topic found for (ii = 2; ii < 50; ii++) if (pp2[-ii] == '|') break; // text ... |topic name| ... if (pp2[-ii] == '|') { // : : pp1 = pp2 - ii + 1; // pp1 pp2 cc = pp2 - pp1; strncpy0(topic,pp1,cc+1); for (ii = 0; ii < Ntopics; ii++) { if (topiclines[ii] == line) continue; if (strcmp(topic,topicnames[ii]) == 0) break; } if (ii < Ntopics) { // found linked topic if (backpos == maxback-1) { for (jj = 0; jj < maxback-1; jj++) // backup table full, discard oldest backtab[jj] = backtab[jj+1]; backpos--; } backtab[backpos] = top; // top line >> backtab backpos++; // advance backtap position currline = topiclines[ii]; // new topic top line backtab[backpos] = currline; // >> backtab textwidget_scroll_top(textwidget,currline); // scroll to top of window } } } else { // look for web link for ( ; pos >= 0; pos--) if ( *(text+pos) == ' ') break; // click position, preceding blank if (pos < 0) return; pp1 = text + pos + 1; pp2 = strchr(pp1,' '); // following blank or EOL if (pp2) cc = pp2 - pp1; else cc = strlen(pp1); if (pp1[cc-1] == '.') cc--; // remove trailing period if (cc > 199) return; strncpy0(weblink,pp1,cc+1); // copy clicked text string if (strmatchN(pp1,"http",4)) showz_html(weblink); // if "http..." assume a web link } } return; } if (kbkey >= 0xfd00) // navigation key 7.3 { textwidget_get_visible_lines(textwidget,top,bott); // range of lines on screen page = bott - top - 2; // page size, lines if (page < 0) page = 0; currline = 0; // default if (kbkey == GDK_KEY_Up) currline = top - 1; // handle some navigation keys else if (kbkey == GDK_KEY_Down) currline = bott + 1; else if (kbkey == GDK_KEY_Page_Up) currline = top - page; else if (kbkey == GDK_KEY_Page_Down) currline = bott + page; else if (kbkey == GDK_KEY_Home) currline = 0; else if (kbkey == GDK_KEY_End) currline = 999999; if (currline < 0) currline = 0; textwidget_scroll(textwidget,currline); // put line on screen } if (kbkey == GDK_KEY_Left || kbkey == '<') { // left arrow, go back backtab[backpos] = currline; if (backpos > 0) backpos--; currline = backtab[backpos]; textwidget_scroll_top(textwidget,currline); // scroll line to top of window return; } if (kbkey == GDK_KEY_Right || kbkey == '>') { // right arrow, go forward backtab[backpos] = currline; if (backpos < maxback-1 && backtab[backpos+1] >= 0) currline = backtab[++backpos]; textwidget_scroll_top(textwidget,currline); // scroll line to top of window return; } return; } // validate the F1_help_topic links and the internal links in a docfile void validate_docfile(cchar *docfile) // 7.3 { char *textlines[10000]; char *topicnames[1000]; char filespec[200], buff[200], image[100]; char topic[50]; char *pp1, *pp2, *pp3; FILE *fid; int Ntext, Ntopics, Nerrs; int ii, cc, line; GdkPixbuf *pixbuf; GError *gerror; printz("validate docfile %s \n",docfile); Ntext = Ntopics = Nerrs = 0; snprintf(filespec,200,"%s/%s",get_zdatadir(),docfile); // open docfile fid = fopen(filespec,"r"); if (! fid) { printz("%s %s",filespec,strerror(errno)); return; } for (line = 0; line < 10000; line++) // read docfile text lines { pp1 = fgets_trim(buff,200,fid); // line without \n EOL if (! pp1) break; // EOF textlines[Ntext] = zstrdup(pp1); // copy text line to memory if (++Ntext == 10000) zexit("exceed 10000 text recs"); if (pp1[0] <= ' ') continue; // not a topic line strncpy0(topic,pp1,50); // add topic and line number to list strTrim(topic); topicnames[Ntopics] = zstrdup(topic); if (++Ntopics == 1000) zexit("exceed 1000 topics"); } fclose(fid); for (line = 0; line < Ntext; line++) // process text lines { pp1 = textlines[line]; pp2 = strstr(pp1,"+image:"); if (pp2) // line contains images { while (pp2) { pp3 = strstr(pp2+7,"+image:"); // next image file if (pp3) *pp3 = 0; strncpy0(image,pp2+7,100); strTrim2(image); snprintf(filespec,200,"%s/%s",get_zimagedir(),image); // full filespec gerror = 0; pixbuf = gdk_pixbuf_new_from_file(filespec,&gerror); // convert to pixbuf image if (pixbuf) g_object_unref(pixbuf); else { printz("cannot load image file: %s \n",image); Nerrs++; } pp2 = pp3; } continue; // next line } if (pp1[0] <= ' ') // not a topic line { pp1 = strchr(pp1,'|'); // get topic links in line while (pp1) { pp2 = strchr(pp1+1,'|'); // ... |topic name| ... if (! pp2) break; // : : pp1++; // pp1 pp2 cc = pp2 - pp1; if (cc > 50) { printf("bad topic, line %d: %s \n",line,pp1); // topic name > 50 char. Nerrs++; break; } strncpy0(topic,pp1,cc+1); for (ii = 0; ii < Ntopics; ii++) if (strcmp(topic,topicnames[ii]) == 0) break; if (ii == Ntopics) { // topic not found printf("bad topic, line %d: %s \n",line,topic); Nerrs++; } pp1 = strchr(pp2+1,'|'); } continue; // next line } } printz(" %d errors \n",Nerrs); for (ii = 0; ii < Ntext; ii++) // free memory zfree(textlines[ii]); for (ii = 0; ii < Ntopics; ii++) zfree(topicnames[ii]); return; } /********************************************************************************/ /*** Called at each application startup. Update appruns file if >1 day since last update. Also do phone_home() if user allows this. appruns file: Uxxxxxxxx Rxxx Px | | |___ phone home permission: 0/1 = no/yes | |________ appruns file update counter |__________________ unique ID, random 8-digit hex number ***/ void appruns_update() // 7.4 { FILE *fid; char apprunsfile[200], uuid[12]; int nn, runs, err, permit; STATB statb; time_t mtime, today, fage = 999; uint64 random; snprintf(apprunsfile,200,"%s/appruns",get_zhomedir()); // /home//.appname/appruns err = stat(apprunsfile,&statb); // look for appruns file if (! err) { mtime = statb.st_mtime; // file mod date/time today = time(0); fage = (today - mtime) / 3600; // file age in hours } fid = fopen(apprunsfile,"r"); // open appruns file if (fid) { nn = fscanf(fid,"U%8s R%d P%d",uuid,&runs,&permit); // read "Uxxxxxxxx Rxxx Px" fclose(fid); if (nn != 3) fid = 0; // failed } if (fid && fage < 24) return; // < 1 day since last update if (! fid) { // appruns not found or invalid random = 0x100000000 * drandz(); // uuid: random 8-digit hex number snprintf(uuid,12,"%08llx",random); // (range 4.2 billion) runs = 0; // run count permit = 1; // permit phone home } runs += 1; // increment run count fid = fopen(apprunsfile,"w"); // update appruns file if (! fid) return; fprintf(fid,"U%8s R%d P%d",uuid,runs,permit); fclose(fid); if (! permit) return; // phone home blocked if (runs == 1) return; // not if first run phone_home(); // do phone home return; } /********************************************************************************/ // Send a log message to web host for application usage statistics. // No data tracable to a person, computer, or location is sent. // The IP address can be traced by a state actor (like any internet access). void phone_home() // 7.4 { FILE *fid; char apprunsfile[200], uuid[12]; int nn, runs, permit; char buff1[140], buff2[100]; cchar *wget1 = "wget -b -q -O /dev/null %s 1>/dev/null"; // stuff wget2 cchar *wget2 = "https://kornelix.net/phone_home/%s/%s/"; // stuff appname, uuid snprintf(apprunsfile,200,"%s/appruns",get_zhomedir()); fid = fopen(apprunsfile,"r"); // open appruns file if (! fid) return; nn = fscanf(fid,"U%8s R%d P%d",uuid,&runs,&permit); // read "Uxxxxxxxx Rxxx Px" fclose(fid); if (nn != 3) return; // failed snprintf(buff2,100,wget2,zappvers,uuid); // build message to web host snprintf(buff1,140,wget1,buff2); shell_quiet(buff1); // send return; } /********************************************************************************/ // phone home dialog // Display basic information for user decision to allow or block phone home. // Application: provide "phone home" menu or button which calls this function. // If user allows, phone_home() will be called each Ith run. void phone_home_allow(GtkWidget *parent) // 7.4 { FILE *fid; char apprunsfile[200], uuid[12]; int nn, runs, permit; uint64 random; int zstat; cchar *infomess = E2X("If you permit, a message is occasionally \n" "sent to the web host for usage statistics. \n" "Nothing is retained that can be associated \n" "with a person or computer or location."); zdialog *zd = zdialog_new(E2X("Phone Home"),parent,"OK","NO",0); // query user zdialog_add_widget(zd,"hbox","hbinfo","dialog",0,"space=10"); zdialog_add_widget(zd,"label","labinfo","hbinfo",infomess,"space=5"); if (parent) zdialog_run(zd,0,"parent"); else zdialog_run(zd,0,"mouse"); zstat = zdialog_wait(zd); // 1/2 = OK/NO from user zdialog_destroy(zd); snprintf(apprunsfile,200,"%s/appruns",get_zhomedir()); fid = fopen(apprunsfile,"r"); // open appruns file if (fid) { nn = fscanf(fid,"U%8s R%d P%d",uuid,&runs,&permit); // read "Uxxxxxxxx Rxxx Px" fclose(fid); if (nn != 3) fid = 0; // failed } if (! fid) { // appruns not found or invalid random = 0x100000000 * drandz(); // random 8-digit hex number snprintf(uuid,12,"%08llx",random); // (range 4.2 billion) runs = 0; // run count permit = 1; // permit phone home } if (zstat == 2) permit = 0; else permit = 1; fid = fopen(apprunsfile,"w"); // update appruns file if (! fid) return; fprintf(fid,"U%8s R%d P%d",uuid,runs,permit); fclose(fid); if (permit) phone_home(); // phone home if allowed 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 E2Xinit(cchar *locale, int Fdump) locale 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 E2X(). locale: 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 Fdump: flag, dump missing translations to log file Status returned: 0 = OK, 1 = unable to find or process translation file. Translate a text string: cchar *translation = E2X(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(E2X("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 E2Xnames { FILE *fidr; char buff[E2Xmaxcc], *ppq1, *ppq2; char *porec, *wporec; char Etext[E2Xmaxcc], Ttext[E2Xmaxcc]; // .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 E2Xgettext(char *text); char *E2Xmergetext(cchar *text); } // read and process .po file at application startup // prepare english strings and translations for quick access void E2Xinit(cchar *locale, int Fdump) // initialize translations 7.1 { using namespace E2Xnames; int ii, err; char installpo[200], localpo[200]; char *usepo, *pp; double itime, ltime; STATB statb; 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(E2Xmaxent * sizeof(char *)); // english text and translations ttext = (char **) zmalloc(E2Xmaxent * sizeof(char *)); // (segmented, quoted, escaped) estring = (char **) zmalloc(E2Xmaxent * sizeof(char *)); // english strings and translations tstring = (char **) zmalloc(E2Xmaxent * sizeof(char *)); // (merged, un-quoted, un-escaped) if (locale && *locale) strncpy0(zlocale,locale,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(zlocale,pp,6); // "lc_RC" language/region code else strcpy(zlocale,"en"); // use English } if (*zlocale < 'a') strcpy(zlocale,"en"); // use English if garbage printz("language: %s \n",zlocale); if (strmatchN(zlocale,"en",2)) return; // English, do nothing *installpo = *localpo = 0; // no installed or local .po file 7.2 usepo = 0; err = locale_filespec("locale","translate.po",installpo); // look for installed .po file 7.2 if (err) err = locale_filespec("locale","translate.po.gz",installpo); // may be compressed (debian) if (! err) usepo = installpo; err = locale_filespec("user","translate.po",localpo); // look for local .po file 7.2 if (! err && *installpo) { // both are present stat(installpo,&statb); // compare mod date/time for itime = statb.st_mtime; // installed and local .po files stat(localpo,&statb); ltime = statb.st_mtime; if (ltime >= itime) usepo = localpo; // use local .po if newer } if (usepo) { // bugfix 7.4 pp = strstr(usepo,".gz"); // uncompress if needed 7.2 if (pp) { printz("uncompress installed .po file \n"); err = shell_ack("gunzip -c %s > %s",installpo,localpo); // /usr/share/fotoxx/locale/translate-xx.po.gz usepo = localpo; // >> /translate-xx.po } } if (usepo) printz("using translation file: %s \n",usepo); else return; fidr = fopen(usepo,"r"); // open .po file if (! fidr) { printz("*** cannot open .po file: %s \n",usepo); return; } porec = 0; // no .po record yet *Etext = *Ttext = 0; // no text yet while (true) { if (! porec) porec = fgets_trim(buff,E2Xmaxcc,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 E2Xgettext(Etext); // "segment1 %s \n" "segment2" ... } else if (strmatchN(porec,"msgstr",6)) // start new translation { porec += 6; // get segmented string E2Xgettext(Ttext); if (! *Etext) { printz("orphan translation: %s \n",Ttext); *Ttext = 0; continue; } if (strlen(Ttext) < 3) // translation is "" (quotes included) if (Fdump) printz("no translation: %s \n",Etext); // 7.1 } 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 == E2Xmaxent) // cannot continue zappcrash("more than %d translations",E2Xmaxent); } } fclose(fidr); printz(".po file has %d entries \n",Ntext); for (ii = 0; ii < Ntext; ii++) { pp = E2Xmergetext(etext[ii]); // merge segmented text strings estring[ii] = zstrdup(pp); pp = E2Xmergetext(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 E2Xnames::E2Xgettext(char *pstring) { using namespace E2Xnames; int cc, scc = 0; while (true) // join multiple quoted strings { while (*porec && *porec != '"') porec++; // find opening string quote if (! *porec) { porec = fgets_trim(buff,E2Xmaxcc,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 >= E2Xmaxcc) 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 * E2Xnames::E2Xmergetext(cchar *dirtystring) { static char cleanstring[E2Xmaxcc]; int ii, jj; strncpy0(cleanstring,dirtystring,E2Xmaxcc); 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. cchar * E2X(cchar *english) { using namespace E2Xnames; cchar *pp, *pp2; int ii; if (! Ntext) return english; // no translations ii = bsearch(english,(cchar **) estring,0,Ntext); // binary search, < 1 microsec. if (ii < 0) pp = english; else pp = tstring[ii]; if (strlen(pp) == 0) pp = english; // translation is "" for (pp2 = pp; *pp2 && pp2 < pp+30; pp2++) // remove context if present if (*pp2 == ':' && *(pp2+1) == ':') return pp2+2; return pp; } // Find all untranslated strings and return them one per call. // Set ftf = 1 for first call, will be returned = 0. // Returns null after last untranslated string. cchar * E2X_missing(int &ftf) { using namespace E2Xnames; int ii; static int next; if (ftf) ftf = next = 0; for (ii = next; ii < Ntext; ii++) if (strlen(tstring[ii]) == 0) break; // translation is "" next = ii + 1; if (ii < Ntext) return estring[ii]; // return english return 0; // EOL } /******************************************************************************** GTK utility functions ********************************************************************************/ // Iterate main loop every "skip" calls. // If called within the main() thread, does a GTK main loop to process menu events, etc. // You must do this periodically within long-running main() thread tasks if you wish to // keep menus, buttons, output windows, etc. alive and working. The skip argument will // cause the function to do nothing for skip calls, then perform the normal function. // This allows it to be embedded in loops with little execution time penalty. // If skip = 100, zmainloop() will do nothing for 100 calls, execute normally, etc. // If called from a thread, zmainloop() does nothing. void zmainloop(int skip) { static int xskip = 0; if (skip) { if (++xskip < skip) return; xskip = 0; } if (! pthread_equal(pthread_self(),zfuncs::tid_main)) return; // thread caller, do nothing while (gtk_events_pending()) // gdk_flush() removed gtk_main_iteration_do(0); // use gtk_main_iteration_do return; } // Iterate the main loop and sleep for designated time void zmainsleep(float secs) { while (secs > 0) { zmainloop(); zsleep(0.001); secs = secs - 0.001; } return; } /********************************************************************************/ // cairo drawing context for GDK window GTK 3.21 version // 6.6 #if GTK_CHECK_VERSION(3,22,0) cairo_t * draw_context_create(GdkWindow *gdkwin, draw_context_t &context) { if (context.dcr) zappcrash("draw_context_create(): nested call"); context.win = gdkwin; context.rect.x = 0; context.rect.y = 0; context.rect.width = gdk_window_get_width(gdkwin); context.rect.height = gdk_window_get_height(gdkwin); context.reg = cairo_region_create_rectangle(&context.rect); context.ctx = gdk_window_begin_draw_frame(gdkwin,context.reg); context.dcr = gdk_drawing_context_get_cairo_context(context.ctx); return context.dcr; } void draw_context_destroy(draw_context_t &context) { if (! context.dcr) zappcrash("draw_context_destroy(): not created"); gdk_window_end_draw_frame(context.win,context.ctx); cairo_region_destroy(context.reg); /* cairo_destroy(context.dcr); this is fatal */ context.dcr = 0; return; } #else cairo_t * draw_context_create(GdkWindow *gdkwin, draw_context_t &context) { if (context.dcr) zappcrash("draw_context_create(): nested call"); context.dcr = gdk_cairo_create(gdkwin); if (! context.dcr) zappcrash("draw_context_create(): gdk_cairo_create() failed"); // 6.8 return context.dcr; } void draw_context_destroy(draw_context_t &context) { if (! context.dcr) zappcrash("draw_context_destroy(): not created"); if (context.dcr) cairo_destroy(context.dcr); context.dcr = 0; return; } #endif /********************************************************************************/ // textwidget functions // 6.8 // -------------------- // // High-level use of GtkTextView widget for text reports, line editing, text selection // In functions below, textwidget = zdialog_widget(zd,"widgetname"), // where "widgetname" is a zdialog "text" widget type. // All line numbers and line positions are zero based. // clear the text widget to blank void textwidget_clear(GtkWidget *textwidget) { GtkTextBuffer *textBuff; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; gtk_text_buffer_set_text(textBuff,"",-1); return; } // clear the text widget from given line to end void textwidget_clear(GtkWidget *textwidget, int line) { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // iter at line start gtk_text_buffer_get_end_iter(textBuff,&iter2); gtk_text_buffer_delete(textBuff,&iter1,&iter2); // delete existing line and rest of buffer return; } // get the current line count int textwidget_linecount(GtkWidget *textwidget) { GtkTextBuffer *textBuff; int nlines; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return 0; nlines = gtk_text_buffer_get_line_count(textBuff); return nlines; } // append a new line of text to the end of existing text lines // line should normally include trailing \n // if existing last line is without trailing \n, text is appended to this line void textwidget_append(GtkWidget *textwidget, int bold, cchar *format, ...) { va_list arglist; char textline[2000]; GtkTextBuffer *textBuff; GtkTextIter enditer; GtkTextTag *fontag = 0; cchar *normfont = zfuncs::appmonofont; cchar *boldfont = zfuncs::appmonoboldfont; va_start(arglist,format); vsnprintf(textline,1999,format,arglist); va_end(arglist); textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; gtk_text_buffer_get_end_iter(textBuff,&enditer); // end of text if (bold) fontag = gtk_text_buffer_create_tag(textBuff,0,"font",boldfont,0); // prepare bold/norm tag else fontag = gtk_text_buffer_create_tag(textBuff,0,"font",normfont,0); gtk_text_buffer_insert_with_tags(textBuff,&enditer,textline,-1,fontag,null); // insert line zmainloop(); return; } // same as above, with scroll to last line added (slower) void textwidget_append2(GtkWidget *textwidget, int bold, cchar *format, ...) { va_list arglist; char textline[2000]; GtkTextBuffer *textBuff; GtkTextIter enditer; GtkTextTag *fontag = 0; cchar *normfont = zfuncs::appmonofont; cchar *boldfont = zfuncs::appmonoboldfont; GtkAdjustment *vadjust; double upperlimit; va_start(arglist,format); vsnprintf(textline,1999,format,arglist); va_end(arglist); textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; gtk_text_buffer_get_end_iter(textBuff,&enditer); // end of text if (bold) fontag = gtk_text_buffer_create_tag(textBuff,0,"font",boldfont,0); // prepare bold/norm tag else fontag = gtk_text_buffer_create_tag(textBuff,0,"font",normfont,0); gtk_text_buffer_insert_with_tags(textBuff,&enditer,textline,-1,fontag,null); // insert line vadjust = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(textwidget)); upperlimit = gtk_adjustment_get_upper(vadjust); // does not work within a zdialog FIXME gtk_adjustment_set_value(vadjust,upperlimit); zmainloop(); return; } // insert a new line of text after designated line // use line -1 to insert before line 0 // line should normally include trailing \n void textwidget_insert(GtkWidget *textwidget, int bold, int line, cchar *format, ...) { va_list arglist; char textline[2000]; GtkTextBuffer *textBuff; GtkTextIter iter; int nlines; GtkTextTag *fontag = 0; cchar *normfont = zfuncs::appmonofont; cchar *boldfont = zfuncs::appmonoboldfont; va_start(arglist,format); vsnprintf(textline,1999,format,arglist); va_end(arglist); textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; if (line < 0) gtk_text_buffer_get_start_iter(textBuff,&iter); // insert before line 0 if (line >= 0) { nlines = gtk_text_buffer_get_line_count(textBuff); // insert after line if (line < nlines - 1) gtk_text_buffer_get_iter_at_line(textBuff,&iter,line+1); // start of next line else gtk_text_buffer_get_end_iter(textBuff,&iter); // or end of text } if (bold) fontag = gtk_text_buffer_create_tag(textBuff,0,"font",boldfont,0); // prepare bold/norm tag else fontag = gtk_text_buffer_create_tag(textBuff,0,"font",normfont,0); gtk_text_buffer_insert_with_tags(textBuff,&iter,textline,-1,fontag,null); // insert line zmainloop(); return; } // replace a given line with a new line // line = -1: replace last line. -2: replace last-1 line, etc. // new line should normally include trailing \n void textwidget_replace(GtkWidget *textwidget, int bold, int line, cchar *format, ...) { va_list arglist; char textline[2000]; GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; int nlines; GtkTextTag *fontag = 0; cchar *normfont = zfuncs::appmonofont; cchar *boldfont = zfuncs::appmonoboldfont; va_start(arglist,format); vsnprintf(textline,1999,format,arglist); va_end(arglist); textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; nlines = gtk_text_buffer_get_line_count(textBuff); // lines now in buffer if (line < 0) line = nlines + line - 1; if (line >= nlines) line = nlines - 1; gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // line start iter2 = iter1; gtk_text_iter_forward_line(&iter2); // end gtk_text_buffer_delete(textBuff,&iter1,&iter2); // delete line gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); if (bold) fontag = gtk_text_buffer_create_tag(textBuff,0,"font",boldfont,0); // prepare bold/norm tag else fontag = gtk_text_buffer_create_tag(textBuff,0,"font",normfont,0); gtk_text_buffer_insert_with_tags(textBuff,&iter1,textline,-1,fontag,null); // insert line zmainloop(); return; } // delete a given line including the trailing \n void textwidget_delete(GtkWidget *textwidget, int line) { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; int nlines; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; nlines = gtk_text_buffer_get_line_count(textBuff); // lines now in buffer if (line < 0 || line >= nlines) return; gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // line start iter2 = iter1; gtk_text_iter_forward_line(&iter2); // end gtk_text_buffer_delete(textBuff,&iter1,&iter2); // delete line zmainloop(); return; } // find first line of text containing characters matching input string // search is from line1 to end, then from 0 to line1-1 // returns first matching line or -1 if none // comparison is not case sensitive int textwidget_find(GtkWidget *textwidget, char *matchtext, int line1) { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; int line, nlines, cc; char *textline = 0, *pp1, *pp2; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return -1; nlines = gtk_text_buffer_get_line_count(textBuff); // lines now in buffer if (! nlines) return -1; if (line1 < 0) line1 = 0; // starting line to search if (line1 >= nlines) line1 = 0; line = line1; while (true) { gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // line start iter2 = iter1; gtk_text_iter_forward_line(&iter2); // end textline = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); // get text if (textline) { pp1 = strcasestr(textline,matchtext); // look for matching text if (pp1) break; // found free(textline); } line++; if (line == nlines) line = 0; if (line == line1) return -1; // wrapped around, not found } cc = strlen(matchtext); // highlight matching text pp2 = pp1 + cc - 1; gtk_text_buffer_get_iter_at_line_index(textBuff,&iter1,line,pp1-textline); gtk_text_buffer_get_iter_at_line_index(textBuff,&iter2,line,pp2-textline+1); gtk_text_buffer_select_range(textBuff,&iter1,&iter2); free(textline); return line; } // insert a pixbuf image at designated line void textwidget_insert_pixbuf(GtkWidget *textwidget, int line, GdkPixbuf *pixbuf) { int nlines; GtkTextBuffer *textBuff; GtkTextIter iter; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; nlines = gtk_text_buffer_get_line_count(textBuff); // insert after line if (line < nlines - 1) gtk_text_buffer_get_iter_at_line(textBuff,&iter,line+1); // start of next line else gtk_text_buffer_get_end_iter(textBuff,&iter); // or end of text gtk_text_buffer_insert_pixbuf(textBuff,&iter,pixbuf); return; } // scroll a textwidget to put a given line on screen // 1st line = 0. for last line use line = -1. void textwidget_scroll(GtkWidget *textwidget, int line) { GtkTextBuffer *textBuff; GtkTextIter iter; GtkTextMark *mark; GtkAdjustment *vadjust; double upperlimit; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; vadjust = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(textwidget)); if (line < 0) { // bottom upperlimit = gtk_adjustment_get_upper(vadjust); // does not work within a zdialog FIXME gtk_adjustment_set_value(vadjust,upperlimit); } else { gtk_text_buffer_get_iter_at_line(textBuff,&iter,line); mark = gtk_text_buffer_create_mark(textBuff,0,&iter,0); gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(textwidget),mark); } return; } // scroll a textwidget to put a given line at the top of the window void textwidget_scroll_top(GtkWidget *textwidget, int line) // 7.3 { GtkTextBuffer *textBuff; GtkTextIter iter; GtkTextMark *mark; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; gtk_text_buffer_get_iter_at_line(textBuff,&iter,line); mark = gtk_text_buffer_create_mark(textBuff,0,&iter,0); gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(textwidget),mark,0,1,0,0); return; } // get the range of textwidget lines currently visible in the window void textwidget_get_visible_lines(GtkWidget *textwidget, int &top, int &bott) { GdkRectangle rect; GtkTextIter iter1, iter2; int y1, y2; gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(textwidget),&rect); y1 = rect.y; y2 = y1 + rect.height; gtk_text_view_get_line_at_y(GTK_TEXT_VIEW(textwidget), &iter1, y1, 0); gtk_text_view_get_line_at_y(GTK_TEXT_VIEW(textwidget), &iter2, y2, 0); top = gtk_text_iter_get_line(&iter1); bott = gtk_text_iter_get_line(&iter2) - 1; return; } // dump the entire textwidget contents into a file void textwidget_dump(GtkWidget *widget, cchar *filename) { FILE *fid; char *prec; int line, err; fid = fopen(filename,"w"); // open file if (! fid) { zmessageACK(0,E2X("cannot open file %s"),filename); return; } for (line = 0; ; line++) { prec = textwidget_line(widget,line,1); // get text line, strip \n if (! prec) break; fprintf(fid,"%s\n",prec); // output with \n } err = fclose(fid); // close file if (err) zmessageACK(0,"file close error"); return; } // dump the entire textwidget contents into a file, using a save-as dialog void textwidget_save(GtkWidget *widget, GtkWindow *parent) { char *file; file = zgetfile(E2X("save text to file"),parent,"save","noname"); if (! file) return; textwidget_dump(widget,file); zfree(file); return; } // Get a line of text. Returned text is subject for zfree(). // trailing \n is included if strip == 0 char * textwidget_line(GtkWidget *textwidget, int line, int strip) { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; int cc, nlines; char *textline, *ztext; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return 0; nlines = gtk_text_buffer_get_line_count(textBuff); // lines now in buffer if (line < 0 || line >= nlines) return 0; gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // line start iter2 = iter1; gtk_text_iter_forward_line(&iter2); // end textline = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); // get text line if (! textline) return 0; ztext = zstrdup(textline); free(textline); if (strip) { cc = strlen(ztext); if (cc && ztext[cc-1] == '\n') ztext[cc-1] = 0; } return ztext; } // highlight a given line of text void textwidget_highlight_line(GtkWidget *textwidget, int line) { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; int nlines; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; nlines = gtk_text_buffer_get_line_count(textBuff); // lines now in buffer if (line < 0 || line >= nlines) return; gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // line start iter2 = iter1; gtk_text_iter_forward_line(&iter2); // end gtk_text_buffer_select_range(textBuff,&iter1,&iter2); // highlight return; } // get the word at the given position within the line // words are defined by line starts and ends, and the given delimiters // returns word and delimiter (&end) char * textwidget_word(GtkWidget *textwidget, int line, int posn, cchar *dlims, char &end) { GtkTextBuffer *textBuff; char *txline, *pp1, *pp2, *ztext; int pos, cc; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return 0; txline = textwidget_line(textwidget,line,0); if (! txline) return 0; pos = utf8_position(txline,posn); // graphic position to byte position if (pos < 0) return 0; pp1 = txline + pos; if (! *pp1 || strchr(dlims,*pp1)) return 0; // reject edge position or delimiter while (pp1 > txline && ! strchr(dlims,pp1[-1])) pp1--; // find start of word pp2 = pp1; while (pp2[1] && ! strchr(dlims,pp2[1])) pp2++; // find following delimiter or EOL end = pp2[1]; // return delimiter while (*pp1 == ' ') pp1++; // no leading or trailing blanks while (*pp2 == ' ') pp2--; cc = pp2 - pp1 + 1; if (cc < 1) return 0; // all blanks? ztext = (char *) zmalloc(cc+1); strncpy0(ztext,pp1,cc+1); zfree(txline); return ztext; } // highlight text at line and positiion, length cc void textwidget_highlight_word(GtkWidget *textwidget, int line, int posn, int cc) { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; char *txline, *pp1, *pp2; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; txline = textwidget_line(textwidget,line,0); if (! txline) return; pp1 = txline + posn; pp2 = pp1 + cc - 1; gtk_text_buffer_get_iter_at_line_index(textBuff,&iter1,line,pp1-txline); gtk_text_buffer_get_iter_at_line_index(textBuff,&iter2,line,pp2-txline+1); gtk_text_buffer_select_range(textBuff,&iter1,&iter2); zfree(txline); return; } // convert text to bold text at line, positiion, cc void textwidget_bold_word(GtkWidget *textwidget, int line, int posn, int cc) // 7.1 { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; GtkTextTag *fontag = 0; cchar *boldfont = zfuncs::appmonoboldfont; char *txline, *pp1, *pp2; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; txline = textwidget_line(textwidget,line,0); if (! txline) return; fontag = gtk_text_buffer_create_tag(textBuff,0,"font",boldfont,0); /*** fontag = gtk_text_buffer_create_tag(textBuff,0,"font",boldfont, // example "foreground","red","background","light blue",0); ***/ pp1 = txline + posn; pp2 = pp1 + cc - 1; gtk_text_buffer_get_iter_at_line_index(textBuff,&iter1,line,pp1-txline); gtk_text_buffer_get_iter_at_line_index(textBuff,&iter2,line,pp2-txline+1); gtk_text_buffer_apply_tag(textBuff,fontag,&iter1,&iter2); zfree(txline); return; } // convert text to underlined text at line, positiion, cc void textwidget_underline_word(GtkWidget *textwidget, int line, int posn, int cc) // 7.3 { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; GtkTextTag *fontag = 0; char *txline, *pp1, *pp2; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; txline = textwidget_line(textwidget,line,0); if (! txline) return; fontag = gtk_text_buffer_create_tag(textBuff,0,"underline",PANGO_UNDERLINE_SINGLE,0); pp1 = txline + posn; pp2 = pp1 + cc - 1; gtk_text_buffer_get_iter_at_line_index(textBuff,&iter1,line,pp1-txline); gtk_text_buffer_get_iter_at_line_index(textBuff,&iter2,line,pp2-txline+1); gtk_text_buffer_apply_tag(textBuff,fontag,&iter1,&iter2); zfree(txline); return; } // set font attributes for the entire textwidget (black on white) // temp. kludge void textwidget_font_attributes(GtkWidget *textwidget) // 7.3 { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; GtkTextTag *fontag = 0; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; fontag = gtk_text_buffer_create_tag(textBuff,0, // high contrast "foreground","black","background","white",0); gtk_text_buffer_get_start_iter(textBuff,&iter1); gtk_text_buffer_get_end_iter(textBuff,&iter2); gtk_text_buffer_apply_tag(textBuff,fontag,&iter1,&iter2); return; } // set an event function for mouse and KB events in textwidget // + line selection via mouse click or keyboard up/down arrow key // + line and word selection via mouse click // // optional user callback function looks like this: // void userfunc(GtkWidget *textwidget, int line, int position, int KBkey) // this function receives KB keys and mouse click text line/position void textwidget_set_eventfunc(GtkWidget *textwidget, textwidget_callbackfunc_t userfunc) { int textwidget_eventfunc(GtkWidget *textwidget, GdkEvent *event, textwidget_callbackfunc_t userfunc); gtk_widget_add_events(textwidget,GDK_BUTTON_PRESS_MASK); gtk_widget_add_events(textwidget,GDK_KEY_PRESS_MASK); gtk_widget_add_events(textwidget,GDK_POINTER_MOTION_MASK); gtk_widget_add_events(textwidget,GDK_FOCUS_CHANGE_MASK); G_SIGNAL(textwidget,"key-press-event",textwidget_eventfunc,userfunc); G_SIGNAL(textwidget,"button-press-event",textwidget_eventfunc,userfunc); G_SIGNAL(textwidget,"motion-notify-event",textwidget_eventfunc,userfunc); G_SIGNAL(textwidget,"focus-in-event",textwidget_eventfunc,userfunc); return; } // textwidget event function: // if no user callback function, process KB navigation keys (arrow, page, home/end) // if user callback func, send all KB keys to user callback function // process mouse clicks, send clicked line and position to user callback function int textwidget_eventfunc(GtkWidget *textwidget, GdkEvent *event, textwidget_callbackfunc_t userfunc) { #define TEXT GTK_TEXT_WINDOW_TEXT #define VIEW GTK_TEXT_VIEW static GdkCursor *arrowcursor = 0; GdkWindow *gdkwin; GtkTextIter iter1; int mpx, mpy, tbx, tby; int line, pos, top, bott, page, KBkey; if (! arrowcursor) // first call, get arrow cursor arrowcursor = gdk_cursor_new_for_display(display,GDK_TOP_LEFT_ARROW); gdkwin = gtk_text_view_get_window(VIEW(textwidget),TEXT); // set arrow cursor for window if (gdkwin) gdk_window_set_cursor(gdkwin,arrowcursor); // (must reset every event) gtk_widget_grab_focus(textwidget); if (event->type == GDK_KEY_PRESS) // KB key press event { KBkey = ((GdkEventKey *) event)->keyval; if (userfunc) { userfunc(textwidget,-1,-1,KBkey); return 1; } if (KBkey >= 0xfd00) // navigation key 7.3 { textwidget_get_visible_lines(textwidget,top,bott); // range of lines on screen page = bott - top - 2; // page size, lines if (page < 0) page = 0; line = 0; // default if (KBkey == GDK_KEY_Up) line = top - 1; // handle some navigation keys else if (KBkey == GDK_KEY_Down) line = bott + 1; else if (KBkey == GDK_KEY_Page_Up) line = top - page; else if (KBkey == GDK_KEY_Page_Down) line = bott + page; else if (KBkey == GDK_KEY_Home) line = 0; else if (KBkey == GDK_KEY_End) line = 999999; if (line < 0) line = 0; textwidget_scroll(textwidget,line); // put line on screen } return 1; } if (! userfunc) return 1; if (event->type == GDK_BUTTON_PRESS) { // mouse button press mpx = int(((GdkEventButton *) event)->x); // mouse click position mpy = int(((GdkEventButton *) event)->y); mpx -= appfontsize / 2; // more accurate if (mpx < 0) mpx = 0; gtk_text_view_window_to_buffer_coords(VIEW(textwidget),TEXT,mpx,mpy,&tbx,&tby); if (tbx && tby) { // can happen 7.3 gtk_text_view_get_iter_at_location(VIEW(textwidget),&iter1,tbx,tby); line = gtk_text_iter_get_line(&iter1); // clicked textwidget line pos = gtk_text_iter_get_line_offset(&iter1); // clicked position } else line = pos = 0; userfunc(textwidget,line,pos,-1); // pass line and posn to user func return 1; } return 1; } /******************************************************************************** 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 vertical 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 vertical packing box (in a window) mbar GtkWidget * reference for menu bar popup GtkWidget * reference for popup menu mitem GtkWidget * reference for menu item (in a menu bar) msub GtkWidget * reference for submenu item (in a menu) label cchar * menu or toolbar name or label tbar GtkWidget * reference for toolbar tip cchar * tool button tool tip (popup text via mouse-over) icon cchar * stock icon name or custom icon file name (see below) func see below menu or tool button response function arg cchar * argument to response function stbar int reference for status bar message cchar * message to display in status bar The icon argument for the function add_toolbar_button() has two forms. For a GTK stock item referenced with a macro like GTK_STOCK_OPEN, use the corresponding text name, like "gtk-open". For a custom icon, use the icon's file name like "my-icon.png". The file is expected to be in get_zdatadir()/icons. The icon file may be any size, and is resized for use on the toolbar. If the file is not found, the stock icon "gtk-missing-image" is used (".png" and ".jpg" files both work). For a button with no icon (text label only), use 0 or null for the icon argument. For a menu separator, use the menu name "separator". For a toolbar separator, use the label "separator". For a title menu (no response function), set the response function to null. The response function for both menus and toolbar buttons looks like this: void func(GtkWidget *, cchar *) The following macro is also supplied to simplify the coding of response functions: G_SIGNAL(window,event,func,arg) which expands to: g_signal_connect(G_OBJECT(window),event,G_CALLBACK(func),(void *) arg) *********************************************************************************/ // create menu bar and add to vertical packing box GtkWidget * create_menubar(GtkWidget *vbox) { GtkWidget *wmbar; wmbar = gtk_menu_bar_new(); gtk_box_pack_start(GTK_BOX(vbox),wmbar,0,0,0); return wmbar; } // add menu item to menu bar, with optional response function GtkWidget * add_menubar_item(GtkWidget *wmbar, cchar *mname, cbFunc func) { GtkWidget *wmitem; wmitem = gtk_menu_item_new_with_label(mname); gtk_menu_shell_append(GTK_MENU_SHELL(wmbar),wmitem); if (func) G_SIGNAL(wmitem,"activate",func,mname); return wmitem; } // add submenu item to menu item, with optional response function GtkWidget * add_submenu_item(GtkWidget *wmitem, cchar *mlab, cbFunc func, cchar *mtip) { GtkWidget *wmsub, *wmsubitem; wmsub = gtk_menu_item_get_submenu(GTK_MENU_ITEM(wmitem)); // add submenu if not already if (wmsub == null) { wmsub = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(wmitem),wmsub); } if (strmatch(mlab,"separator")) wmsubitem = gtk_separator_menu_item_new(); else wmsubitem = gtk_menu_item_new_with_label(mlab); // add menu item with label only gtk_menu_shell_append(GTK_MENU_SHELL(wmsub),wmsubitem); // append submenu item to submenu if (func) G_SIGNAL(wmsubitem,"activate",func,mlab); // connect optional response function if (mtip) g_object_set(G_OBJECT(wmsubitem),"tooltip-text",mtip,null); // add optional popup menu tip return wmsubitem; } /********************************************************************************/ // create toolbar and add to vertical packing box int tbIconSize = 32; // default if not supplied 6.2 GtkWidget * create_toolbar(GtkWidget *vbox, int iconsize) { GtkWidget *wtbar; wtbar = gtk_toolbar_new(); gtk_box_pack_start(GTK_BOX(vbox),wtbar,0,0,0); tbIconSize = iconsize; return wtbar; } // add toolbar button with label and icon ("iconfile.png") and tool tip // at least one of label and icon should be present GtkWidget * add_toolbar_button(GtkWidget *wtbar, cchar *blab, cchar *btip, cchar *icon, cbFunc func) { GtkToolItem *tbutton; GError *gerror = 0; PIXBUF *pixbuf; GtkWidget *wicon = 0; char iconpath[300], *pp; STATB statb; int err, cc; if (blab && strmatch(blab,"separator")) { tbutton = gtk_separator_tool_item_new(); gtk_toolbar_insert(GTK_TOOLBAR(wtbar),GTK_TOOL_ITEM(tbutton),-1); return (GtkWidget *) tbutton; } if (icon && *icon) { // get icon pixbuf *iconpath = 0; strncatv(iconpath,199,zimagedir,"/",icon,null); // 6.8 err = stat(iconpath,&statb); if (err) { // alternative path 6.2 cc = readlink("/proc/self/exe",iconpath,300); // get own program path if (cc > 0) iconpath[cc] = 0; // readlink() quirk pp = strrchr(iconpath,'/'); // folder of program if (pp) *pp = 0; strncatv(iconpath,300,"/icons/",icon,null); // .../icons/iconfile.png } pixbuf = gdk_pixbuf_new_from_file_at_scale(iconpath,tbIconSize,tbIconSize,1,&gerror); if (pixbuf) wicon = gtk_image_new_from_pixbuf(pixbuf); } tbutton = gtk_tool_button_new(wicon,blab); if (! wicon) gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(tbutton),"gtk-missing-image"); if (btip) gtk_tool_item_set_tooltip_text(tbutton,btip); gtk_tool_item_set_homogeneous(tbutton,0); gtk_toolbar_insert(GTK_TOOLBAR(wtbar),GTK_TOOL_ITEM(tbutton),-1); if (func) G_SIGNAL(tbutton,"clicked",func,blab); return (GtkWidget *) tbutton; } /********************************************************************************/ // create a status bar and add to the start of a packing box GtkWidget * create_stbar(GtkWidget *pbox) { GtkWidget *stbar; stbar = gtk_statusbar_new(); gtk_box_pack_start(GTK_BOX(pbox),stbar,0,0,0); gtk_widget_show(stbar); return stbar; } // display message in status bar int stbar_message(GtkWidget *wstbar, cchar *message) { static int ctx = -1; if (ctx == -1) ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(wstbar),"all"); gtk_statusbar_pop(GTK_STATUSBAR(wstbar),ctx); gtk_statusbar_push(GTK_STATUSBAR(wstbar),ctx,message); return 0; } /******************************************************************************** Customizable Graphic Popup Menu void Gmenuz(GtkWidget *parent, cchar *configfile, callbackfunc) Open a popup window with a customizable graphic menu. parent parent window or null configfile menu configuration file, will be created if missing callbackfunc callback function to receive clicked menu entry: typedef void callbackfunc(cchar *menu) This function allows an application to offer a customizable menu which a user can populate with frequently used (menu) functions, arranged as desired. A menu entry selected by the user is passed to the application for execution. The initial popup window is blank. Right click an empty space on the popup window to define a new menu entry. Right click an existing entry to modify it. Use the resulting dialog to define or change the menu entry. menu text optional text appearing in the popup window menu func text that is passed to the application callback function menu icon optional menu icon: an optional .png file can be selected 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 folder as the configuration file and these copies are used. The icon selected when the menu entry is created can disappear without consequence. The file name NNN.png is used (000-999). 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 NNN.png optional menu icon file size NN optional icon size (default 24) kill optional flag, kill window when used menu menu text next menu entry ... *********************************************************************************/ namespace gmenuznames { #define maxME 200 // max. menu entries #define maxText 1000 // max. text size, all menu fields typedef void callbackfunc(cchar *menu); // user callback function callbackfunc *gmenucallback; cchar *menuFont, *menuBoldFont; char *menufile = 0; // menu configuration file from user char *menudir = 0; // 6.8 char iconfile[200]; // icon file full path < 200 6.8 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 int mdcc; struct menuent { // menu entry on popup window int xpos, ypos, ww, hh; // layout position, extent char *menu; // text on window or null int bold; // text is bold 6.3 char *func; // func name for user callback char *icon; // icon file "NNN.png" or null PIXBUF *pixbuf; // icon pixbuf or null int size; // icon size or zero int kill; // kill popup window when menu used int Fnewicon; // flag, icon was changed 6.2 }; menuent menus[maxME]; // menu entries int NME = 0; // entry count zdialog *zdedit = 0; // active edit dialog int mpx, mpy; // mouse click/drag position int me; // current menu entry int Fchanged = 0; // flag, menu edited or resized int Fpopquit = 0; // popup is being closed int Fpopbusy = 0; // popup is active int deficonsize = 32; // default icon size or last size set void wpaint(GtkWidget *, cairo_t *); // window repaint - draw event void resize(); // window resize event void quit(); // kill window and exit void update_configfile(); // update menu configuration file void mouse_event(GtkWidget *, GdkEventButton *, void *); // mouse event function void KB_event(GtkWidget *, GdkEventKey *, void *); // KB event function void draw_text(cairo_t *, char *, int px, int py, int &ww, int &hh); // draw text and return pixel extent void draw_bold_text(cairo_t *, char *, int px, int py, int &ww, int &hh); // draw text and return pixel extent void edit_menu(); // dialog to create/edit menu entry int edit_menu_event(zdialog *zd, cchar *event); // dialog event function void drag_drop_event(int mx, int my, char *file); // drag-drop event function } // user callable function to build the menu from user's configuration file void Gmenuz(GtkWidget *parent, cchar *title, cchar *ufile, gmenuznames::callbackfunc ufunc) { using namespace gmenuznames; FILE *fid; int nn, xx, yy, ww, hh, size; int pposx, pposy; int xpos, ypos; char *pp, buff[maxText]; PIXBUF *pixbuf; GError *gerror; if (Fpopbusy) return; // don't allow multiple popups menuFont = zfuncs::appfont; // 6.3 menuBoldFont = zfuncs::appboldfont; pWin = parent; // get parent window if (menufile) { zfree(menufile); zfree(menudir); } menufile = zstrdup(ufile); // get menu configuration file menudir = zstrdup(ufile); // folder of same 6.8 pp = strrchr(menudir,'/'); if (! pp || pp < menudir + 10) zappcrash("Gmenuz() bad menu: %s",menufile); *(pp+1) = 0; mdcc = strlen(menudir); if (mdcc > 190) zappcrash("Gmenuz() too big: %s",menufile); gmenucallback = ufunc; // get user callback function NME = 0; fid = fopen(menufile,"r"); // read window geometry if (fid) { nn = fscanf(fid," popup %d %d %d %d",&xx,&yy,&ww,&hh); // get popup window position and size if (nn == 4 && ww > 50 && ww < 1000 && hh > 50 && hh < 1000) { winposx = xx; // OK to use winposy = yy; winww = ww; winhh = hh; } while (true) { pp = fgets_trim(buff,maxText-1,fid,1); // read next menu entry if (! pp) break; if (strmatchN(pp,"posn ",5)) { // position in popup window if (NME == maxME) { zmessageACK(mWin,"exceeded %d menu entries",maxME); break; } me = NME; // new entry NME++; // entry count memset(&menus[me],0,sizeof(menuent)); // clear all menu data nn = sscanf(pp+5," %d %d ",&xpos,&ypos); // position in popup window if (nn != 2) xpos = ypos = 100; if (xpos > 1000) xpos = 1000; if (ypos > 1000) ypos = 1000; menus[me].xpos = xpos; menus[me].ypos = ypos; } if (strmatchN(pp,"menu ",5)) { // menu text if (strlen(pp+5) > 0) menus[me].menu = zstrdup(pp+5); // get menu text else menus[me].menu = 0; } if (strmatchN(pp,"bold",4)) // bold text 6.3 menus[me].bold = 1; if (strmatchN(pp,"func ",5)) { // function name if (strlen(pp+5)) menus[me].func = zstrdup(pp+5); else menus[me].func = 0; } if (strmatchN(pp,"icon ",5)) { // menu icon file NNN.png 6.8 pp += 5; if (*pp) { // icon file present if (*pp == '/') { // old style icon file /.../filename.png menus[me].Fnewicon = 1; // new icon is needed Fchanged = 1; gerror = 0; pixbuf = gdk_pixbuf_new_from_file(pp,&gerror); if (! pixbuf && gerror) printz("*** %s \n",gerror->message); menus[me].pixbuf = pixbuf; if (strmatchN(menudir,pp,mdcc)) remove(pp); // delete file if in /menudir/ } else { // new style icon file NNN.png 6.8 menus[me].icon = zstrdup(pp); // (relocatable if menudir moved) strncpy0(iconfile,menudir,200); strcpy(iconfile+mdcc,pp); // /menudir/NNN.png gerror = 0; pixbuf = gdk_pixbuf_new_from_file(iconfile,&gerror); if (! pixbuf && gerror) printz("*** %s \n",gerror->message); menus[me].pixbuf = pixbuf; } } else menus[me].pixbuf = 0; // no icon file } if (strmatchN(pp,"size ",5)) { size = atoi(pp+5); if (size < 24) size = 24; if (size > 256) size = 256; menus[me].size = size; } if (strmatchN(pp,"kill",4)) // kill window flag menus[me].kill = 1; } fclose(fid); } if (Fchanged) update_configfile(); // 6.8 mWin = gtk_window_new(GTK_WINDOW_TOPLEVEL); // create popup window for menu entries if (! pWin) { // no parent window pposx = pposy = 0; gtk_window_set_focus_on_map(GTK_WINDOW(mWin),1); // put on top when created 6.8 } else { gtk_window_get_position(GTK_WINDOW(pWin),&pposx,&pposy); // parent window position (NW corner) gtk_window_set_transient_for(GTK_WINDOW(mWin),GTK_WINDOW(pWin)); // popup window belongs to parent } if (title) gtk_window_set_title(GTK_WINDOW(mWin),title); winposx += pposx; // popup position relative to parent winposy += pposy; gtk_window_set_default_size(GTK_WINDOW(mWin),winww,winhh); // set size and position gtk_window_move(GTK_WINDOW(mWin),winposx,winposy); layout = gtk_layout_new(0,0); // create drawing window gtk_container_add(GTK_CONTAINER(mWin),layout); // add to popup window G_SIGNAL(mWin,"destroy",quit,0); // connect signals to windows G_SIGNAL(mWin,"delete-event",quit,0); G_SIGNAL(mWin,"notify",resize,0); G_SIGNAL(mWin,"configure-event",resize,0); // GTK 3.18 API changed 6.3 G_SIGNAL(mWin,"key-press-event",KB_event,0); // connect KB key event G_SIGNAL(layout,"draw",wpaint,0); gtk_widget_add_events(layout,GDK_BUTTON_PRESS_MASK); // connect mouse events gtk_widget_add_events(layout,GDK_BUTTON_RELEASE_MASK); gtk_widget_add_events(layout,GDK_BUTTON_MOTION_MASK); gtk_widget_add_events(layout,GDK_POINTER_MOTION_MASK); G_SIGNAL(layout,"button-press-event",mouse_event,0); G_SIGNAL(layout,"button-release-event",mouse_event,0); G_SIGNAL(layout,"motion-notify-event",mouse_event,0); drag_drop_dest(mWin,drag_drop_event); // 6.2 gtk_widget_show_all(mWin); // show all widgets zmainloop(); // after initial window events 6.3 Fchanged = 0; // no changes yet Fpopquit = 0; // not being closed Fpopbusy = 1; return; } // paint window when created, exposed, resized void gmenuznames::wpaint(GtkWidget *, cairo_t *cr) { using namespace gmenuznames; PIXBUF *pixbuf; char *text, *text2; int xpos, ypos, ww, hh, size, yadd; int bold; for (int me = 0; me < NME; me++) // loop all menu entries { xpos = menus[me].xpos; // window position ypos = menus[me].ypos; text = menus[me].menu; // menu text bold = menus[me].bold; // bold text flag 6.3 pixbuf = menus[me].pixbuf; // icon size = menus[me].size; // size if (pixbuf) { // draw icon at window position gdk_cairo_set_source_pixbuf(cr,pixbuf,xpos,ypos); cairo_paint(cr); if (! size) size = deficonsize; // use default if not specified 6.2 } else size = 0; yadd = 0; if (pixbuf) yadd = gdk_pixbuf_get_height(pixbuf) + 2; // icon height + extra space 6.2 if (text) { text2 = (char *) zmalloc(strlen(text)+100); // replace "\n" with newline 6.3 repl_1str(text,text2,"\\n","\n"); if (bold) draw_bold_text(cr,text2,xpos,ypos+yadd,ww,hh); // bold text 6.3 else draw_text(cr,text2,xpos,ypos+yadd,ww,hh); zfree(text2); } else ww = hh = 0; if (ww < size) ww = size; // menu entry enclosing rectangle hh += yadd; menus[me].ww = ww; menus[me].hh = hh; } return; } // resize event - save current window size void gmenuznames::resize() { using namespace gmenuznames; int xx, yy, ww, hh; if (Fpopquit) return; // ignore bogus call when killed gtk_window_get_position(GTK_WINDOW(mWin),&xx,&yy); gtk_window_get_size(GTK_WINDOW(mWin),&ww,&hh); if (xx != winposx || yy != winposy) Fchanged = 1; // window size or position changed if (ww != winww || hh != winhh) Fchanged = 1; winposx = xx; // save window position winposy = yy; winww = ww; // save new size winhh = hh; return; } // [x] kill window // Save current menu status for next session. void gmenuznames::quit() { using namespace gmenuznames; Fpopquit = 1; Fpopbusy = 0; if (Fchanged) update_configfile(); gtk_widget_destroy(mWin); gmenucallback("quit"); // inform host program return; } // menu changed, save all menu data to menu config. file void gmenuznames::update_configfile() { using namespace gmenuznames; static int ftf = 1; int64 DT; int err, ii, me; char *pp; int pposx, pposy; FILE *fid; STATB statb; GError *gerror; if (ftf) { // initialize random generator 6.3 ftf = 0; DT = time(null); drandz(&DT); } if (pWin) gtk_window_get_position(GTK_WINDOW(pWin),&pposx,&pposy); // parent window position (may have moved) else pposx = pposy = 0; winposx -= pposx; // popup position relative to parent winposy -= pposy; fid = fopen(menufile,"w"); // open for write if (! fid) { zmessageACK(mWin," %s \n %s",menufile,strerror(errno)); // diagnose permissions error return; } fprintf(fid,"popup %d %d %d %d \n",winposx,winposy,winww,winhh); for (me = 0; me < NME; me++) // write all menu entries to file { if (! menus[me].menu && ! menus[me].pixbuf) { // no text and no icon printz("*** Gmenuz: empty menu entry \n"); continue; } fprintf(fid,"\n"); // blank line separator fprintf(fid,"posn %d %d \n",menus[me].xpos, menus[me].ypos); // menu position in window if (menus[me].menu) // menu text fprintf(fid,"menu %s \n",menus[me].menu); if (menus[me].bold) // bold text flag 6.3 fprintf(fid,"bold \n"); if (menus[me].func) // menu function (text) fprintf(fid,"func %s \n",menus[me].func); if (menus[me].pixbuf) { // menu icon revised 6.8 if (menus[me].Fnewicon) { // new or changed icon strncpy0(iconfile,menudir,200); // menu file folder pp = iconfile + mdcc; // pp --> NNN.png for (ii = 0; ii < 999; ii++) { sprintf(pp,"%03d.png",ii); // find first available NNN.png file err = stat(iconfile,&statb); if (err) break; // (if full, 999.png is re-used) } gerror = 0; gdk_pixbuf_save(menus[me].pixbuf,iconfile,"png",&gerror,null); // write pixbuf to icon file if (gerror) printz("*** %s %s \n",pp,gerror->message); else fprintf(fid,"icon %s \n",pp); // file name is NNN.png menus[me].icon = zstrdup(pp); menus[me].Fnewicon = 0; } else if (menus[me].icon) // no change 6.3 fprintf(fid,"icon %s \n",menus[me].icon); } if (menus[me].size) // icon size fprintf(fid,"size %d \n",menus[me].size); if (menus[me].kill) fprintf(fid,"kill \n"); // kill window flag } fclose(fid); Fchanged = 0; return; } // mouse event function - capture buttons and drag movements void gmenuznames::mouse_event(GtkWidget *, GdkEventButton *event, void *) { using namespace gmenuznames; static int bdtime = 0, butime = 0; static int Lmouse = 0, Rmouse = 0, Fdrag = 0; static int elapsed, mpx0 = 0, mpy0 = 0; int Fclick, dx, dy, xpos, ypos, size; int raster = 10; // alignment raster mpx = int(event->x); // mouse position in window mpy = int(event->y); if (event->type == GDK_BUTTON_PRESS) { Lmouse = Rmouse = Fdrag = 0; if (event->button == 1) Lmouse++; // left or right mouse button if (event->button == 3) Rmouse++; bdtime = event->time; for (me = 0; me < NME; me++) // look for clicked menu entry { if (mpx < menus[me].xpos) continue; if (mpy < menus[me].ypos) continue; if (mpx > menus[me].xpos + menus[me].ww) continue; if (mpy > menus[me].ypos + menus[me].hh) continue; break; } if (me < NME) { // menu item clicked (selected) mpx0 = mpx; // set new drag origin mpy0 = mpy; } else me = -1; // indicate empty space clicked } if (event->type == GDK_BUTTON_RELEASE) { Fclick = 0; butime = event->time; elapsed = butime - bdtime; // button down time, milliseconds bdtime = 0; if (elapsed < 500 && ! Fdrag) Fclick = 1; // mouse clicked if (me >= 0 && Fclick && Lmouse) { // menu entry was left-clicked if (menus[me].kill) quit(); // close menu if specified 6.8 if (menus[me].func) gmenucallback(menus[me].func); // caller user function(func) } else if (Fclick && Rmouse) // menu entry or empty space right-clicked edit_menu(); // edit menu else if (me >= 0 && Fdrag) // menu entry drag ended Fchanged = 1; // mark menu revised Lmouse = Rmouse = Fdrag = 0; // mouse click action completed } if (event->type == GDK_MOTION_NOTIFY) // mouse movement { if (me >= 0 && Lmouse && bdtime) { // menu drag underway 6.8 dx = mpx - mpx0; dy = mpy - mpy0; if (abs(dx) + abs(dy) > 4) // ignore small drags { Fdrag++; mpx0 = mpx; // set new drag origin mpy0 = mpy; menus[me].xpos = mpx; // center menu on mouse menus[me].ypos = mpy; size = menus[me].size; if (size) { menus[me].xpos -= size / 2; menus[me].ypos -= size / 2; } else { menus[me].xpos -= 15; menus[me].ypos -= 8; } xpos = menus[me].xpos; // align to raster 6.5 ypos = menus[me].ypos; xpos = raster * (xpos / raster); ypos = raster * (ypos / raster); menus[me].xpos = xpos; menus[me].ypos = ypos; if (menus[me].xpos < 0) menus[me].xpos = 0; // not off the window 6.2 if (menus[me].xpos > winww-20) menus[me].xpos = winww-20; if (menus[me].ypos < 0) menus[me].ypos = 0; if (menus[me].ypos > winhh-20) menus[me].ypos = winhh-20; gtk_widget_queue_draw(layout); // repaint window } } } return; } // KB press event function - send certain keys to main app void gmenuznames::KB_event(GtkWidget *, GdkEventKey *kbevent, void *) { using namespace gmenuznames; int KBkey = kbevent->keyval; if (KBkey == GDK_KEY_F1) KBevent(kbevent); if (KBkey == GDK_KEY_Escape) quit(); // escape = cancel 6.5 return; } // draw text into layout and return pixel dimensions of enclosing rectangle void gmenuznames::draw_text(cairo_t *cr, char *text, int x, int y, int &w, int &h) { using namespace gmenuznames; static PangoFontDescription *pfont = 0; static PangoLayout *playout = 0; if (! pfont) { pfont = pango_font_description_from_string(menuFont); // first call, get font sizing poop playout = gtk_widget_create_pango_layout(layout,0); pango_layout_set_font_description(playout,pfont); } pango_layout_set_text(playout,text,-1); // compute layout pango_layout_get_pixel_size(playout,&w,&h); // pixel width and height of layout cairo_move_to(cr,x,y); // draw layout with text cairo_set_source_rgb(cr,0,0,0); pango_cairo_show_layout(cr,playout); return; } // draw bold text into layout and return pixel dimensions of enclosing rectangle void gmenuznames::draw_bold_text(cairo_t *cr, char *text, int x, int y, int &w, int &h) { using namespace gmenuznames; static PangoFontDescription *pfont = 0; static PangoLayout *playout = 0; if (! pfont) { pfont = pango_font_description_from_string(menuBoldFont); // first call, get font sizing poop playout = gtk_widget_create_pango_layout(layout,0); pango_layout_set_font_description(playout,pfont); } pango_layout_set_text(playout,text,-1); // compute layout pango_layout_get_pixel_size(playout,&w,&h); // pixel width and height of layout cairo_move_to(cr,x,y); // draw layout with text cairo_set_source_rgb(cr,0,0,0); pango_cairo_show_layout(cr,playout); return; } // dialog to create a new menu entry from user inputs void gmenuznames::edit_menu() { using namespace gmenuznames; if (me < 0) { // new menu entry if (NME == maxME) { zmessageACK(mWin,"capacity limit exceeded"); return; } me = NME; memset(&menus[me],0,sizeof(menuent)); // clear all data } /** menu text [________________] [x] Bold menu func [________________________] menu icon [_______________] [Browse] icon size [___] [x] close window [Apply] [Delete] [Cancel] **/ if (! zdedit) // create dialog if not already { zdedit = zdialog_new(E2X("edit menu entry"),mWin,E2X("Apply"),E2X("Delete"),E2X("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",E2X("menu text")); zdialog_add_widget(zdedit,"label","lab12","vb1",E2X("menu func")); zdialog_add_widget(zdedit,"label","lab13","vb1",E2X("menu icon")); zdialog_add_widget(zdedit,"label","lab14","vb1",E2X("icon size")); zdialog_add_widget(zdedit,"hbox","hb2","vb2"); zdialog_add_widget(zdedit,"zentry","text","hb2",0,"size=30|space=2"); zdialog_add_widget(zdedit,"check","bold","hb2",E2X("Bold"),"space=5"); // 6.3 zdialog_add_widget(zdedit,"zentry","func","vb2",0,"size=30|space=2"); zdialog_add_widget(zdedit,"hbox","hb3","vb2",0,"expand|space=2"); zdialog_add_widget(zdedit,"zentry","icon","hb3",0,"expand"); zdialog_add_widget(zdedit,"zbutton","browse","hb3",E2X("Browse"),"space=5"); zdialog_add_widget(zdedit,"hbox","hb4","vb2",0,"space=2"); zdialog_add_widget(zdedit,"zspin","size","hb4","24|256|1|32"); zdialog_add_widget(zdedit,"check","kill","hb4",E2X("close window"),"space=30"); zdialog_run(zdedit,edit_menu_event,"mouse"); } if (menus[me].menu) // stuff menu text into dialog zdialog_stuff(zdedit,"text",menus[me].menu); else zdialog_stuff(zdedit,"text",""); if (menus[me].func) // stuff menu function zdialog_stuff(zdedit,"func",menus[me].func); else zdialog_stuff(zdedit,"func",""); zdialog_stuff(zdedit,"bold",menus[me].bold); // stuff bold text flag 6.3 if (menus[me].icon) // stuff icon file zdialog_stuff(zdedit,"icon",menus[me].icon); else zdialog_stuff(zdedit,"icon",""); if (menus[me].size) // stuff icon size zdialog_stuff(zdedit,"size",menus[me].size); if (menus[me].kill) // stuff window kill flag zdialog_stuff(zdedit,"kill",1); else zdialog_stuff(zdedit,"kill",0); if (me == NME) { // new menu entry menus[me].xpos = mpx; // initial position from mouse menus[me].ypos = mpy; } return; } // menu entry dialog event function int gmenuznames::edit_menu_event(zdialog *zd, cchar *event) { using namespace gmenuznames; char text[maxText], *pp; int size; PIXBUF *pixbuf; GError *gerror; GtkWindow *parent = GTK_WINDOW(zd->dialog); if (strmatch(event,"browse")) { // browse for icon file pp = zgetfile(E2X("select icon"),parent,"file",menudir); if (pp) { zdialog_stuff(zd,"icon",pp); zfree(pp); // 6.8 } } if (! zd->zstat) return 1; // wait for dialog completion if (zd->zstat == 2) { // [delete] - delete menu entry if (me < 0 || me >= NME) return 0; for (int me2 = me; me2 < NME-1; me2++) // remove menu entry and close hole menus[me2] = menus[me2+1]; NME--; Fchanged = 1; // mark menu revised gtk_widget_queue_draw(layout); // repaint window } if (zd->zstat != 1) { // not [apply] - kill dialog zdialog_free(zdedit); return 1; } // [apply] - update menu from dialog data zdialog_fetch(zd,"text",text,maxText); if (*text) menus[me].menu = zstrdup(text); // menu text, optional else menus[me].menu = 0; zdialog_fetch(zd,"bold",menus[me].bold); // bold text flag 6.3 zdialog_fetch(zd,"func",text,maxText); // menu function name strTrim2(text); pp = strstr(text,"mystuff"); if (pp) { // this is a mystuff link 6.3 strcpy(text,pp); // "/usr/bin/mystuff" --> "mystuff" if (strlen(text) < 9) { strcat(text," [unique-name].txt"); // remind unique menu file name zdialog_stuff(zd,"func",text); zmessageACK(mWin,"assign a unique menu name"); zd->zstat = 0; return 1; } } if (*text) menus[me].func = zstrdup(text); else menus[me].func = 0; zdialog_fetch(zd,"icon",text,maxText); // menu icon file, optional strTrim2(text); if (*text) { if (strlen(text) < 8) { // have local "NNN.png" 6.8 strncpy0(iconfile,menudir,200); strcpy(iconfile+mdcc,text); // make /menudir/NNN.png } else strncpy0(iconfile,text,200); // new icon file selected zdialog_fetch(zd,"size",size); // icon size gerror = 0; pixbuf = gdk_pixbuf_new_from_file_at_size(iconfile,size,size,&gerror); if (! pixbuf) { if (gerror) zmessageACK(mWin,gerror->message); // bad icon file zd->zstat = 0; // keep dialog open return 0; // do nothing } menus[me].icon = zstrdup(text); menus[me].pixbuf = pixbuf; menus[me].size = size; deficonsize = size; // set new default 6.2 menus[me].Fnewicon = 1; // mark icon changed 6.2 } else { // no icon menus[me].icon = 0; menus[me].pixbuf = 0; menus[me].size = 0; menus[me].Fnewicon = 0; // 6.2 } zdialog_fetch(zd,"kill",menus[me].kill); // popup window kill flag if (me == NME) NME++; // if new menu entry, incr. count Fchanged = 1; // mark menu revised zdialog_free(zdedit); // destroy dialog gtk_widget_queue_draw(layout); // repaint window return 1; } // function to accept drag-drop of a .desktop file or file name. void gmenuznames::drag_drop_event(int mpx, int mpy, char *file) // 6.2 { using namespace gmenuznames; FILE *fid; char buff[200], filetype[60], dtfile[200]; char *pp, name[100], exec[200], icon[200]; char wildiconfile[200]; GError *gerror = 0; PIXBUF *pixbuf = 0; int ii, jj, uflag; int size = deficonsize; // default or last size set by user #define Ndirs 8 cchar *icondir[Ndirs] = { // compensate Linux chaos 6.3 "/usr/share/app-install/icons*", "/usr/share/pixmaps*", "/usr/share/icons/*64*", "/usr/share/icons/*48*", "/usr/share/icons/*32*", "/usr?share/icons/*scalable*", "/usr/local/share/icons*", "/usr/local/share/pixmaps*" }; if (! file) return; // drag motion event 6.4 #define Next 3 cchar *iconext[Next] = { "png", "svg", "xpm" }; // file extensions searched 6.3 if (NME == maxME) { zmessageACK(mWin,"capacity limit exceeded"); return; } me = NME; memset(&menus[me],0,sizeof(menuent)); // clear all data pp = strrchr(file,'.'); // look if .desktop file if (pp && strmatch(pp,".desktop")) { strncpy0(dtfile,file,200); // have a .desktop file file = 0; // and no data file } else // find .desktop file for given data file { snprintf(buff,200,"xdg-mime query filetype \"%s\"",file); // xdg-mime query filetype fid = popen(buff,"r"); if (! fid) { zmessageACK(mWin,strerror(errno)); return; } pp = fgets_trim(buff,200,fid); // should get major/minor file type pclose(fid); if (! pp) { zmessageACK(mWin,strerror(errno)); return; } strncpy0(filetype,pp,60); snprintf(buff,200,"xdg-mime query default %s",filetype); // zdg-mime query default fid = popen(buff,"r"); if (! fid) { zmessageACK(mWin,strerror(errno)); return; } pp = fgets_trim(buff,200,fid); // should get appname.desktop pclose(fid); if (! pp) { zmessageACK(mWin,strerror(errno)); return; } snprintf(dtfile,200,"/usr/share/applications/%s",pp); // /usr/share/applications/appname.desktop pp = strrchr(dtfile,'.'); if (! strmatch(pp,".desktop")) { zmessageACK(mWin,".desktop file not found"); return; } } fid = fopen(dtfile,"r"); // read .desktop file if (! fid) { zmessageACK(mWin,strerror(errno)); return; } *name = *exec = *icon = 0; while (true) { // save .desktop parameters pp = fgets_trim(buff,200,fid); if (! pp) break; if (strmatchN(buff,"Name=",5) && ! *name) // app name strncpy0(name,buff+5,100); if (strmatchN(buff,"Exec=",5) && ! *exec) // executable strncpy0(exec,buff+5,200); if (strmatchN(buff,"Icon=",5) && ! *icon) // usr/share/app-install/icons/*.png strncpy0(icon,buff+5,200); } fclose(fid); if (file) { pp = strrchr(file,'/'); // menu name = file base name if (! pp) pp = file; else pp++; menus[me].menu = zstrdup(pp); pp = strrchr(menus[me].menu,'.'); if (pp) *pp = 0; pp = strrchr(exec,'%'); // get rid of %f %u etc. if (pp) *pp = 0; snprintf(buff,200,"%s \"%s\"",exec,file); // menu function = executable + filename menus[me].func = zstrdup(buff); } else { menus[me].menu = zstrdup(name); // menu name = appname menus[me].func = zstrdup(exec); // menu function = executable } if (file && strmatchN(filetype,"image",5)) { // if image file, make icon from image pixbuf = gdk_pixbuf_new_from_file_at_size(file,size,size,&gerror); if (pixbuf) { menus[me].icon = zstrdup(file); menus[me].pixbuf = pixbuf; menus[me].size = size; menus[me].Fnewicon = 1; // 6.2 } } else if (*icon) // make icon from .desktop file { if (*icon == '/') { // icon filespec is given 6.3 pp = icon; goto found; } for (ii = 0; ii < Ndirs; ii++) // search icon locations for (jj = 0; jj < Next; jj++) // try known icon file types { snprintf(wildiconfile,200,"%s/%s.%s",icondir[ii],icon,iconext[jj]); // 6.3 uflag = 1; pp = (char *) SearchWild(wildiconfile,uflag); if (pp) { uflag = 2; SearchWild(null,uflag); goto found; // break out 2 loops } } found: if (! pp) zmessageACK(mWin,"icon file not found: %s \n",icon); else { pixbuf = gdk_pixbuf_new_from_file_at_size(pp,size,size,&gerror); if (! pixbuf) zmessageACK(mWin,"icon file error: %s \n",pp); else { menus[me].icon = zstrdup(pp); menus[me].pixbuf = pixbuf; menus[me].size = size; menus[me].Fnewicon = 1; // 6.2 } } } menus[me].xpos = mpx; // position from mouse menus[me].ypos = mpy; menus[me].kill = 1; // popup window kill flag NME++; // incr. menu count Fchanged = 1; // mark menu revised gtk_widget_queue_draw(layout); // repaint window edit_menu(); // user can edit dropped menu return; } /******************************************************************************** Popup Menu GtkWidget *popup, *mitem cchar *label, *arg, *tip void func(GtkWidget *, cchar *arg) popup = create_popmenu() create a popup menu mitem = add_popmenu_item(popup, label, func, arg, tip) add menu item to popup menu popup_menu(GtkWidget *parent, popup) popup the menu at mouse position Call 'create_popmenu' and then 'add_popmenu_item' for each item in the menu. 'label' is the menu name, 'func' the response function, 'arg' an argument for 'func', and 'tip' is a tool-tip. 'arg' and 'tip' may be null. A call to 'popup_menu' will show all menu entries at the mouse position. Clicking an entry will call the respective response function. Hovering on the entry will show the tool-tip. The response function looks like this: void func(GtkWidget *, cchar *menu) ***/ // create a popup menu GtkWidget * create_popmenu() { int popmenu_event(GtkWidget *, GdkEvent *); GtkWidget *popmenu; popmenu = gtk_menu_new(); gtk_widget_add_events(popmenu,GDK_BUTTON_PRESS_MASK); G_SIGNAL(popmenu,"button-press-event",popmenu_event,0); return popmenu; } // handle mouse button event in a popup menu int popmenu_event(GtkWidget *popmenu, GdkEvent *event) { if (((GdkEventButton *) event)->button != 1) // if not left mouse, kill menu gtk_menu_popdown(GTK_MENU(popmenu)); return 0; } // add a menu item to a popup menu GtkWidget *popmenu_widget; GtkWidget * add_popmenu_item(GtkWidget *popmenu, cchar *mname, cbFunc func, cchar *arg, cchar *mtip) { void popmenu_item_select(GtkWidget *, cchar *mtip); GtkWidget *wmitem; wmitem = gtk_menu_item_new_with_label(mname); 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 } if (mtip) { popmenu_widget = wmitem; // trigger popup tool tip G_SIGNAL(wmitem,"select",popmenu_item_select,mtip); G_SIGNAL(wmitem,"deselect",popmenu_item_select,0); } return wmitem; } // show popup tip for selected menu item void popmenu_item_select(GtkWidget *wmitem, cchar *mtip) // convoluted code but it works 6.8 { GdkWindow *window; int xp, yp, mx, my; window = gtk_widget_get_window(wmitem); gdk_window_get_origin(window,&xp,&yp); // menu screen origin xp += gdk_window_get_width(window); // + width gdk_device_get_position(zfuncs::mouse,0,&mx,&my); // mouse (x,y) screen position poptext_screen(mtip,xp,my,0,5); // popup px = menu + width, py = mouse return; } // Show a popup menu at current mouse position // GtkWidget * argument is not used void popup_menu(GtkWidget *widget, GtkWidget *popmenu) { int mx, my; if (mouse) { gdk_device_get_position(mouse,&screen,&mx,&my); // offset popup menu from mouse 6.6 gdk_device_warp(mouse,screen,mx+30,my); } gtk_widget_show_all(popmenu); // GTK change: show before popup 7.2 #if GTK_CHECK_VERSION(3,22,0) gtk_menu_popup_at_pointer(GTK_MENU(popmenu),null); #else int time = gtk_get_current_event_time(); gtk_menu_popup(GTK_MENU(popmenu),0,0,0,0,1,time); #endif return; } /******************************************************************************** Vertical Menu / Toolbar Build a custom vertical menu and/or toolbar in a vertical packing box Vmenu *vbm; cchar *name, *icon, *desc, *arg; int iww, ihh; void func(GtkWidget *, cchar *name); void RMfunc(GtkWidget *, cchar *name); vbm = Vmenu_new(GtkWidget *vbox, float fgRGB[3], float bgRGB[3]); // create base menu Vmenu_add(vbm, name, icon, iww, ihh, desc, func, arg); // add menu item Vmenu_add_RMfunc(vbm, me, RMfunc); // add right-click menu item 7.1 Vmenu_block(flag) int flag 1 to block Vmenu, 0 to unblock 6.8 Create a vertical menu / toolbar in a vertical packing box. fgRGB and bgRGB are font and background colors, RGB scaled 0-1.0 6.8 Added items can have a menu name, icon, description, response function, and function argument. 'name' and 'icon' can be null but not both. name menu name icon menu icon, filespec for a .png file iww, ihh size of icon in menu display desc optional tool tip if mouse is hovered over displayed menu When 'name/icon' is clicked, 'func' is called with 'arg'. If 'arg' is null, 'name' is used instead. To create a menu entry that is a popup menu with multiple entries, do as follows: popup = create_popmenu(); add_popup_menu_item(popup ...); see create_popmenu() add_popup_menu_item(popup ...); ... Vmenu_add(vbm, name, icon, ww, hh, desc, create_popmenu, (cchar *) popup); i.e. use create_popmenu() as the response function and use the previously created menu 'popup' as the argument (cast to cchar *). ***/ namespace Vmenunames { #define margin 5 // margins for menu text PangoFontDescription *pfont1, *pfont2; PangoAttrList *pattrlist; PangoAttribute *pbackground; int fontheight; int Fblock = 0; void wpaint(GtkWidget *, cairo_t *, Vmenu *); // window repaint - draw event void mouse_event(GtkWidget *, GdkEventButton *, Vmenu *); // mouse event function void paint_menu(cairo_t *cr, Vmenu *vbm, int me, int hilite); // paint menu entry, opt. highlight } // create Vmenu Vmenu *Vmenu_new(GtkWidget *vbox, float fgRGB[3], float bgRGB[3]) { using namespace Vmenunames; int cc, ww, hh; int K64 = 65536; char *menufont1, *menufont2; PangoLayout *playout; cc = sizeof(Vmenu); Vmenu *vbm = (Vmenu *) zmalloc(cc); memset(vbm,0,cc); vbm->fgRGB[0] = fgRGB[0]; // background color, RGB 0-1.0 6.8 vbm->fgRGB[1] = fgRGB[1]; vbm->fgRGB[2] = fgRGB[2]; vbm->bgRGB[0] = bgRGB[0]; // background color, RGB 0-1.0 6.8 vbm->bgRGB[1] = bgRGB[1]; vbm->bgRGB[2] = bgRGB[2]; vbm->vbox = vbox; vbm->topwin = gtk_widget_get_toplevel(vbox); vbm->layout = gtk_layout_new(0,0); vbm->mcount = 0; gtk_box_pack_start(GTK_BOX(vbox),vbm->layout,1,1,0); vbm->xmax = vbm->ymax = 10; // initial layout size pattrlist = pango_attr_list_new(); pbackground = pango_attr_background_new(K64*bgRGB[0],K64*bgRGB[1],K64*bgRGB[2]); pango_attr_list_change(pattrlist,pbackground); menufont1 = zstrdup(zfuncs::appfont); // set menu fonts, normal and bold 6.3 menufont2 = zstrdup(zfuncs::appboldfont); pfont1 = pango_font_description_from_string(menufont1); pfont2 = pango_font_description_from_string(menufont2); playout = gtk_widget_create_pango_layout(vbm->layout,0); pango_layout_set_font_description(playout,pfont1); pango_layout_set_text(playout,"Ayg",-1); pango_layout_get_pixel_size(playout,&ww,&hh); fontheight = hh; gtk_widget_add_events(vbm->layout,GDK_BUTTON_PRESS_MASK); gtk_widget_add_events(vbm->layout,GDK_BUTTON_RELEASE_MASK); gtk_widget_add_events(vbm->layout,GDK_POINTER_MOTION_MASK); gtk_widget_add_events(vbm->layout,GDK_LEAVE_NOTIFY_MASK); G_SIGNAL(vbm->layout,"button-press-event",mouse_event,vbm); G_SIGNAL(vbm->layout,"button-release-event",mouse_event,vbm); G_SIGNAL(vbm->layout,"motion-notify-event",mouse_event,vbm); G_SIGNAL(vbm->layout,"leave-notify-event",mouse_event,vbm); G_SIGNAL(vbm->layout,"draw",wpaint,vbm); return vbm; } // add Vmenu entry with name, icon, description, callback function void Vmenu_add(Vmenu *vbm, cchar *name, cchar *icon, int iconww, int iconhh, cchar *desc, cbFunc func, cchar *arg) { using namespace Vmenunames; int me, cc, xpos, ww, hh; char iconpath[200], *mdesc, *name__; cchar *blanks = " "; // 20 blanks PIXBUF *pixbuf; GError *gerror = 0; PangoLayout *playout; PangoFontDescription *pfont; if (! name && ! icon) return; me = vbm->mcount++; // track no. menu entries if (name) vbm->menu[me].name = zstrdup(name); // create new menu entry from caller data if (icon) { vbm->menu[me].icon = zstrdup(icon); vbm->menu[me].iconww = iconww; vbm->menu[me].iconhh = iconhh; } if (desc) { // pad description with blanks for looks cc = strlen(desc); mdesc = (char *) zmalloc(cc+3); mdesc[0] = ' '; strcpy(mdesc+1,desc); strcpy(mdesc+cc+1," "); vbm->menu[me].desc = mdesc; } vbm->menu[me].func = func; // menu function vbm->menu[me].arg = name; // argument is menu name or arg if avail. if (arg) vbm->menu[me].arg = arg; vbm->menu[me].pixbuf = 0; if (icon) { // if icon is named, get pixbuf *iconpath = 0; strncatv(iconpath,199,zfuncs::zimagedir,"/",icon,null); // 6.8 pixbuf = gdk_pixbuf_new_from_file_at_scale(iconpath,iconww,iconhh,1,&gerror); if (pixbuf) vbm->menu[me].pixbuf = pixbuf; else printz("Vmenu no icon: %s \n",iconpath); // 6.8 } if (me == 0) vbm->ymax = margin; // first menu, top position vbm->menu[me].iconx = 0; vbm->menu[me].icony = 0; vbm->menu[me].namex = 0; vbm->menu[me].namey = 0; if (icon) { vbm->menu[me].iconx = margin; // ______ vbm->menu[me].icony = vbm->ymax; // | | if (name) { // | icon | menu name vbm->menu[me].namex = margin + iconww + margin; // |______| vbm->menu[me].namey = vbm->ymax + (iconhh - fontheight) / 2; // 6.3 } vbm->menu[me].ylo = vbm->ymax; vbm->ymax += iconhh + iconhh / 8; // position for next menu entry vbm->menu[me].yhi = vbm->ymax; if (margin + iconww > vbm->xmax) vbm->xmax = margin + iconww; // keep track of max. layout width } else if (name) { vbm->menu[me].namex = margin; // menu name vbm->menu[me].namey = vbm->ymax; vbm->menu[me].ylo = vbm->ymax; vbm->ymax += 1.5 * fontheight; // more space between text lines 7.2 vbm->menu[me].yhi = vbm->ymax; } vbm->menu[me].playout1 = gtk_widget_create_pango_layout(vbm->layout,0); vbm->menu[me].playout2 = gtk_widget_create_pango_layout(vbm->layout,0); if (name) { xpos = vbm->menu[me].namex; cc = strlen(name); // menu name with trailing blanks 6.3 name__ = zstrdup(name,22); // (long enough to overwrite bold name) strncpy0(name__+cc,blanks,20); playout = vbm->menu[me].playout1; // normal font pfont = pfont1; pango_layout_set_attributes(playout,pattrlist); pango_layout_set_font_description(playout,pfont); pango_layout_set_text(playout,name__,-1); // compute layout pango_layout_get_pixel_size(playout,&ww,&hh); // pixel width and height of layout playout = vbm->menu[me].playout2; // bold font pfont = pfont2; pango_layout_set_attributes(playout,pattrlist); pango_layout_set_font_description(playout,pfont); pango_layout_set_text(playout,name,-1); // compute layout pango_layout_get_pixel_size(playout,&ww,&hh); // pixel width and height of layout if (xpos + ww > vbm->xmax) vbm->xmax = xpos + ww; // keep track of max. layout width } gtk_widget_set_size_request(vbm->layout,vbm->xmax+margin,0); // add right margin to layout width return; } // add alternate function for right-mouse click void Vmenu_add_RMfunc(Vmenu *vbm, int me, cbFunc func, cchar *arg) // 7.1 { if (me > vbm->mcount-1) zappcrash("Vmenu_add_RMfunc() bad me: %d",me); vbm->menu[me].RMfunc = func; vbm->menu[me].RMarg = arg; return; } // block or unblock menu void Vmenu_block(int flag) // 6.8 { using namespace Vmenunames; Fblock = flag; return; } // paint window when created, exposed, resized void Vmenunames::wpaint(GtkWidget *widget, cairo_t *cr, Vmenu *vbm) { using namespace Vmenunames; cairo_set_source_rgb(cr,vbm->bgRGB[0],vbm->bgRGB[1],vbm->bgRGB[2]); // background 6.8 cairo_paint(cr); for (int me = 0; me < vbm->mcount; me++) // paint all menu entries paint_menu(cr,vbm,me,0); return; } // draw menu icon and text into layout void Vmenunames::paint_menu(cairo_t *cr, Vmenu *vbm, int me, int hilite) { using namespace Vmenunames; PIXBUF *pixbuf; PangoLayout *playout; int xpos, ypos; int iconww, iconhh; cchar *name; pixbuf = vbm->menu[me].pixbuf; // icon if (pixbuf) { // draw menu icon at menu position xpos = vbm->menu[me].iconx; ypos = vbm->menu[me].icony; iconww = vbm->menu[me].iconww; iconhh = vbm->menu[me].iconhh; if (! hilite) { // erase box around icon cairo_set_source_rgb(cr,vbm->bgRGB[0],vbm->bgRGB[1],vbm->bgRGB[2]); // background 6.8 cairo_rectangle(cr,xpos-1,ypos-1,iconww+2,iconhh+2); cairo_fill(cr); } gdk_cairo_set_source_pixbuf(cr,pixbuf,xpos,ypos); // draw icon cairo_paint(cr); if (hilite) { cairo_set_source_rgb(cr,vbm->fgRGB[0],vbm->fgRGB[1],vbm->fgRGB[2]); // draw box around icon cairo_set_line_width(cr,1); cairo_set_line_join(cr,CAIRO_LINE_JOIN_ROUND); cairo_rectangle(cr,xpos,ypos,iconww,iconhh); cairo_stroke(cr); } } name = vbm->menu[me].name; // menu text if (name) { // draw menu text at menu position xpos = vbm->menu[me].namex; ypos = vbm->menu[me].namey; cairo_move_to(cr,xpos,ypos); // draw layout with text cairo_set_source_rgb(cr,vbm->fgRGB[0],vbm->fgRGB[1],vbm->fgRGB[2]); if (hilite) playout = vbm->menu[me].playout2; else playout = vbm->menu[me].playout1; pango_cairo_show_layout(cr,playout); } return; } // mouse event function - capture buttons and drag movements void Vmenunames::mouse_event(GtkWidget *widget, GdkEventButton *event, Vmenu *vbm) { using namespace Vmenunames; GdkWindow *gdkwin; cchar *desc; int me, mpx, mpy, button, ww, ylo, yhi; static int me0 = -1, Fmyclick = 0, winww = 0; static draw_context_t context; static GtkWidget *pwidget = 0; static cairo_t *cr = 0; if (widget != pwidget) { // widget changed 6.9 if (pwidget) draw_context_destroy(context); gdkwin = gtk_layout_get_bin_window(GTK_LAYOUT(widget)); cr = draw_context_create(gdkwin,context); gdkwin = gtk_widget_get_window(widget); // get width of menu widget 6.8 winww = gdk_window_get_width(gdkwin); pwidget = widget; } 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) { // unhighlight prior paint_menu(cr,vbm,me0,0); me0 = -1; } if (me == me0) return; // same as before if (me == vbm->mcount) return; // no new menu match paint_menu(cr,vbm,me,1); // highlight menu entry at mouse desc = vbm->menu[me].desc; // show tool tip if (desc) poptext_widget(widget,desc,winww,mpy,0,5); // px = menu width, py = mouse 6.8 me0 = me; // remember last match return; } if (me0 >= 0) // mouse left layout { paint_menu(cr,vbm,me0,0); // unhighlight 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_PRESS) // menu entry clicked Fmyclick = 1; // button click is mine 6.8 if (event->type == GDK_BUTTON_RELEASE) // menu entry clicked { if (Fblock) return; // menu is blocked 6.8 if (! Fmyclick) return; // ignore unmatched button release 6.8 Fmyclick = 0; // (from vanished popup window) for (me = 0; me < vbm->mcount; me++) { // look for clicked menu entry ylo = vbm->menu[me].ylo; yhi = vbm->menu[me].yhi; if (mpy < ylo) continue; if (mpy < yhi) break; } if (me == vbm->mcount) return; // no menu match zfuncs::vmenuclickbutton = button; // 1/2/3 = left/mid/right button ww = vbm->menu[me].iconww; // get horiz. click posn. on menu icon if (ww) mpx = 100 * (mpx - margin) / ww; // scale 0-100 else mpx = 0; if (mpx < 0) mpx = 0; if (mpx > 100) mpx = 100; zfuncs::vmenuclickposn = mpx; paint_menu(cr,vbm,me,0); // unhighlight menu if (button == 3 && vbm->menu[me].RMfunc) // if right mouse button, 7.1 vbm->menu[me].RMfunc(widget,vbm->menu[me].RMarg); // call right-mouse function else if (vbm->menu[me].func) vbm->menu[me].func(widget,vbm->menu[me].arg); // caller function(widget,arg) } return; } /******************************************************************************** simplified GTK dialog functions create a dialog with response buttons (OK, cancel ...) add widgets to dialog (button, text entry, combo box ...) put data into widgets (text or numeric data) run the dialog, get user inputs (button press, text entry, checkbox selection ...) run caller event function when widgets change from user inputs run caller event function when dialog is completed or canceled get dialog completion status (OK, cancel, destroyed ...) get data from dialog widgets (the user inputs) destroy the dialog and free memory *********************************************************************************/ // private functions for widget events and dialog completion int zdialog_widget_event(GtkWidget *, zdialog *zd); int zdialog_delete_event(GtkWidget *, GdkEvent *, zdialog *zd); int zdialog_zspin_event(GtkWidget *, GdkEvent *, zdialog *zd); // "zspin" widget 6.8 // create a new zdialog dialog // The title and parent arguments may be null. // optional arguments: up to zdmaxbutts button labels followed by null // returned dialog status: +N = button N (1 to zdmaxbutts) // <0 = [x] button or other GTK destroy action // completion buttons are also events like other widgets // all dialogs run parallel, use zdialog_wait() if needed // The status returned by zdialog_wait() corresponds to the button // (1-10) used to end the dialog. Status < 0 indicates the [x] button // was used or the dialog was killed by the program itself. zdialog * zdialog_new(cchar *title, GtkWidget *parent, ...) // parent added { zdialog *zd; GtkWidget *dialog, *hbox, *vbox, *butt, *hsep; cchar *bulab[zdmaxbutts]; int cc, ii, nbu; va_list arglist; static int uniqueID = 1; if (! pthread_equal(pthread_self(),zfuncs::tid_main)) // 7.1 zappcrash("zdialog_new() called from thread"); 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 = ""; dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); 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_window_set_default_size(GTK_WINDOW(dialog),10,10); // stop auto width of 150 pixels if (parent) gtk_window_set_transient_for(GTK_WINDOW(dialog),GTK_WINDOW(parent)); gtk_box_set_spacing(GTK_BOX(vbox),2); gtk_container_set_border_width(GTK_CONTAINER(vbox),5); cc = sizeof(zdialog); // allocate zdialog zd = (zdialog *) zmalloc(cc); if (zdialog_count == zdialog_max) { // add to active list for (ii = 0; ii < zdialog_count; ii++) printz("dialog: %s \n",zdialog_list[ii]->widget[0].data); zappcrash("max. zdialogs exceeded"); } zdialog_list[zdialog_count] = zd; zdialog_count++; if (nbu) { // completion buttons hbox = gtk_box_new(HORIZONTAL,2); // add hbox for buttons at bottom gtk_box_pack_end(GTK_BOX(vbox),hbox,0,0,2); hsep = gtk_separator_new(HORIZONTAL); // add separator line gtk_box_pack_end(GTK_BOX(vbox),hsep,0,0,3); for (ii = nbu-1; ii >= 0; ii--) { // add buttons to hbox butt = gtk_button_new_with_label(bulab[ii]); // reverse order nbu-1...0 gtk_box_pack_end(GTK_BOX(hbox),butt,0,0,2); G_SIGNAL(butt,"clicked",zdialog_widget_event,zd); // connect to event function zd->compwidget[ii] = butt; // save button widgets zd->compbutton[ii] = bulab[ii]; // and button labels } } zd->compbutton[nbu] = 0; // mark EOL zd->dialog = dialog; // dialog window zd->title = zstrdup(title); // dialog title 6.8 zd->parent = parent; // parent window zd->sentinel1 = zdsentinel | (lrandz() & 0x0000FFFF); // validity sentinels zd->sentinel2 = zd->sentinel1; // fixed part + random part zd->uniqueID = uniqueID++; // increment unique ID 6.2 zd->eventCB = 0; // no user event callback function zd->zstat = 0; // no zdialog status zd->disabled = 1; // widget signals disabled zd->saveposn = 0; // position not saved zd->widget[0].name = "dialog"; // set up 1st widget = dialog zd->widget[0].type = "dialog"; zd->widget[0].pname = 0; // no parent 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) { 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) { 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; } // set zdialog can or can not receive focus (informational or report dialog) void zdialog_can_focus(zdialog *zd, int Fcan) // 7.1 { gtk_window_set_accept_focus(GTK_WINDOW(zd->dialog),Fcan); return; } // set focus on dialog window or window and named widget // (widget name may be null or missing) // see also: gtk_window_activate_focus(GtkWindow *) void zdialog_set_focus(zdialog *zd, cchar *wname) // 7.1 { GtkWindow *window; GtkWidget *widget; window = GTK_WINDOW(zd->dialog); if (wname) widget = zdialog_widget(zd,wname); else widget = 0; if (wname) gtk_window_set_focus(window,widget); else gtk_window_activate_focus(window); return; } // add widget to existing zdialog // // Arguments after parent are optional and default to 0. // zd zdialog *, created with zdialog_new() // type string, one of the widget types listed below // name string, widget name, used to stuff or fetch widget data // parent string, parent name: "dialog" or a previously added container widget // data string, initial data for widget (label name, entry string, spin value, etc.) // size cc for text entry or pixel size for image widget // homog for hbox or vbox to make even space allocation for contained widgets // expand widget should expand with dialog box expansion // space extra space between this widget and neighbors, pixels // wrap allow text to wrap at right margin // // data can be a string ("initial widget data") or a number in string form ("12.345") // data for togbutt / check / radio: use "0" or "1" for OFF or ON // data for spin / zspin / hscale / vscale: use "min|max|step|value" (default: 0 | 100 | 1 | 50) // data for colorbutt: use "rrr|ggg|bbb" with values 0-255 for each RGB color. // This format is used to initialize the control and read back when user selects a color. // Multiple radio buttons with same parent are a group: pressing one turns the others OFF. int zdialog_add_widget ( zdialog *zd, cchar *type, cchar *name, cchar *pname, // mandatory args cchar *data, int size, int homog, int expand, int space, int wrap) // optional args (default = 0) { GtkWidget *widget = 0, *pwidget = 0, *fwidget = 0; GtkWidget *entry, *image, *vbox; GtkTextBuffer *editBuff = 0; PIXBUF *pixbuf = 0; GdkRGBA gdkrgba; GError *gerror = 0; cchar *pp, *ptype = 0; char vdata[30], iconpath[200]; double min, max, step, val; double f256 = 1.0 / 256.0; int iiw, iip, kk, err; if (! zdialog_valid(zd)) zappcrash("zdialog invalid"); for (iiw = 1; zd->widget[iiw].type; iiw++); // find next avail. slot if (iiw > zdmaxwidgets-2) zappcrash("too many widgets: %d",iiw); zd->widget[iiw].type = zstrdup(type); // initz. widget struct zd->widget[iiw].name = zstrdup(name); zd->widget[iiw].pname = zstrdup(pname); zd->widget[iiw].data = 0; zd->widget[iiw].cblist = 0; zd->widget[iiw].size = size; zd->widget[iiw].homog = homog; zd->widget[iiw].expand = expand; zd->widget[iiw].space = space; zd->widget[iiw].wrap = wrap; zd->widget[iiw].widget = 0; zd->widget[iiw+1].type = 0; // set new EOF marker if (strmatchV(type,"dialog","hbox","vbox","hsep","vsep","frame","scrwin", // added 'z' widgets 6.8 "label","link","entry","zentry","edit","text", "button","zbutton","togbutt","check","radio", "imagebutt","colorbutt","combo","comboE","spin","zspin", "hscale","vscale","icon","image",null) == 0) { printz("*** zdialog, bad widget type: %s \n",type); return 0; } for (iip = iiw-1; iip >= 0; iip--) // find parent (container) widget if (strmatch(pname,zd->widget[iip].name)) break; if (iip < 0) zappcrash("zdialog, no parent for widget: %s",name); pwidget = zd->widget[iip].widget; // parent widget, type ptype = zd->widget[iip].type; if (strmatchV(ptype,"dialog","hbox","vbox","frame","scrwin",null) == 0) zappcrash("zdialog, bad widget parent type: %s",ptype); if (strmatch(type,"hbox")) widget = gtk_box_new(HORIZONTAL,space); // expandable container boxes if (strmatch(type,"vbox")) widget = gtk_box_new(VERTICAL,space); if (strstr("hbox vbox",type)) gtk_box_set_homogeneous(GTK_BOX(widget),homog); if (strmatch(type,"hsep")) widget = gtk_separator_new(HORIZONTAL); // horiz. & vert. separators if (strmatch(type,"vsep")) widget = gtk_separator_new(VERTICAL); if (strmatch(type,"frame")) { // frame around contained widgets widget = gtk_frame_new(data); gtk_frame_set_shadow_type(GTK_FRAME(widget),GTK_SHADOW_IN); data = 0; } if (strmatch(type,"scrwin")) { // scrolled window container widget = gtk_scrolled_window_new(0,0); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); #if GTK_CHECK_VERSION(3,16,0) gtk_scrolled_window_set_overlay_scrolling(GTK_SCROLLED_WINDOW(widget),0); // 6.5 #endif data = 0; } if (strmatch(type,"label")) { // label (static text) widget = gtk_label_new(data); if (size) gtk_label_set_width_chars(GTK_LABEL(widget),size); // 6.2 if (data && strstr(data,"> link G_SIGNAL(widget,"clicked",zdialog_widget_event,zd); data = 0; } if (strmatch(type,"entry")) { // text input, single line widget = gtk_entry_new(); if (data) gtk_entry_set_text(GTK_ENTRY(widget),data); if (size) gtk_entry_set_width_chars(GTK_ENTRY(widget),size); G_SIGNAL(widget,"changed",zdialog_widget_event,zd); } if (strmatch(type,"zentry")) { // text input, single line 6.8 widget = gtk_text_view_new(); #if GTK_CHECK_VERSION(3,18,0) gtk_text_view_set_top_margin(GTK_TEXT_VIEW(widget),2); gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(widget),2); #endif gtk_text_view_set_left_margin(GTK_TEXT_VIEW(widget),5); if (! size) size = 10; // scale widget for font size gtk_widget_set_size_request(widget,size*appfontsize,2*appfontsize); gtk_text_view_set_editable(GTK_TEXT_VIEW(widget),1); gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget),GTK_WRAP_NONE); gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(widget),0); editBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); if (data) gtk_text_buffer_set_text(editBuff,data,-1); G_SIGNAL(editBuff,"changed",zdialog_widget_event,zd); // buffer signals, not widget } if (strmatch(type,"edit")) { // text input, opt. multi-line widget = gtk_text_view_new(); #if GTK_CHECK_VERSION(3,18,0) gtk_text_view_set_top_margin(GTK_TEXT_VIEW(widget),2); gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(widget),2); #endif gtk_text_view_set_left_margin(GTK_TEXT_VIEW(widget),5); if (! size) size = 10; // scale widget for font size 6.6 gtk_widget_set_size_request(widget,size*appfontsize,2*appfontsize); gtk_text_view_set_editable(GTK_TEXT_VIEW(widget),1); if (wrap) gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget),GTK_WRAP_WORD); gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(widget),0); editBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); if (data) gtk_text_buffer_set_text(editBuff,data,-1); G_SIGNAL(editBuff,"changed",zdialog_widget_event,zd); // buffer signals, not widget } if (strmatch(type,"text")) { // text output (not editable) widget = gtk_text_view_new(); #if GTK_CHECK_VERSION(3,18,0) gtk_text_view_set_top_margin(GTK_TEXT_VIEW(widget),2); // 6.5 gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(widget),2); #endif gtk_text_view_set_left_margin(GTK_TEXT_VIEW(widget),3); // 6.1 editBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); if (data) gtk_text_buffer_set_text(editBuff,data,-1); gtk_text_view_set_editable(GTK_TEXT_VIEW(widget),0); if (wrap) gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget),GTK_WRAP_WORD); } if (strmatch(type,"button")) { // button widget = gtk_button_new_with_label(data); G_SIGNAL(widget,"clicked",zdialog_widget_event,zd); data = 0; } if (strmatch(type,"zbutton")) { // checkbox used as small button 6.8 if (data) widget = gtk_check_button_new_with_label(data); else widget = gtk_check_button_new(); G_SIGNAL(widget,"toggled",zdialog_widget_event,zd); data = "0"; // default data } if (strmatch(type,"togbutt")) { // toggle button widget = gtk_toggle_button_new_with_label(data); G_SIGNAL(widget,"toggled",zdialog_widget_event,zd); data = "0"; // default data } if (strmatch(type,"imagebutt")) { // button with image 6.0 snprintf(iconpath,200,"%s/%s",get_zimagedir(),data); // 6.8 data = 0; pixbuf = gdk_pixbuf_new_from_file_at_scale(iconpath,size,size,1,&gerror); if (pixbuf) { image = gtk_image_new_from_pixbuf(pixbuf); g_object_unref(pixbuf); } else image = gtk_image_new_from_icon_name("missing",GTK_ICON_SIZE_BUTTON); widget = gtk_button_new_with_label(data); gtk_button_set_image(GTK_BUTTON(widget),image); G_SIGNAL(widget,"clicked",zdialog_widget_event,zd); } if (strmatch(type,"colorbutt")) { // color edit button if (! data) data = "0|0|0"; // data format: "nnn|nnn|nnn" = RGB pp = strField(data,'|',1); gdkrgba.red = f256 * atoi(pp); // RGB values are 0-1 pp = strField(data,'|',2); gdkrgba.green = f256 * atoi(pp); pp = strField(data,'|',3); gdkrgba.blue = f256 * atoi(pp); gdkrgba.alpha = 1.0; widget = gtk_color_button_new_with_rgba(&gdkrgba); G_SIGNAL(widget,"color-set",zdialog_widget_event,zd); } if (strmatch(type,"check")) { // checkbox if (data) widget = gtk_check_button_new_with_label(data); else widget = gtk_check_button_new(); G_SIGNAL(widget,"toggled",zdialog_widget_event,zd); data = "0"; // default data } if (strmatch(type,"combo")) { // combo box widget = gtk_combo_box_text_new(); zd->widget[iiw].cblist = pvlist_create(zdcbmax); // for drop-down list if (! blank_null(data)) { pvlist_append(zd->widget[iiw].cblist,data); // add data to drop-down list gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget),data); gtk_combo_box_set_active(GTK_COMBO_BOX(widget),0); } G_SIGNAL(widget,"changed",zdialog_widget_event,zd); } if (strmatch(type,"comboE")) { // combo box with entry box widget = gtk_combo_box_text_new_with_entry(); entry = gtk_bin_get_child(GTK_BIN(widget)); if (! size) size = 20; gtk_entry_set_width_chars(GTK_ENTRY(entry),size); // 7.3 zd->widget[iiw].cblist = pvlist_create(zdcbmax); // for drop-down list if (! blank_null(data)) { gtk_entry_set_text(GTK_ENTRY(entry),data); // entry = initial data pvlist_append(zd->widget[iiw].cblist,data); // add data to drop-down list gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget),data); } G_SIGNAL(widget,"changed",zdialog_widget_event,zd); } if (strmatch(type,"radio")) { // radio button for (kk = iip+1; kk <= iiw; kk++) if (strmatch(zd->widget[kk].pname,pname) && // find first radio button strmatch(zd->widget[kk].type,"radio")) break; // with same container if (kk == iiw) widget = gtk_radio_button_new_with_label(null,data); // this one is first else widget = gtk_radio_button_new_with_label_from_widget // not first, add to group (GTK_RADIO_BUTTON(zd->widget[kk].widget),data); G_SIGNAL(widget,"toggled",zdialog_widget_event,zd); data = "0"; // default data } if (strmatchV(type,"spin","hscale","vscale",null)) { // spin button or sliding scale if (! data) zappcrash("zdialog_add_widget(): data missing"); // "min|max|step|value" pp = strField(data,'|',1); err = convSD(pp,min); pp = strField(data,'|',2); err += convSD(pp,max); pp = strField(data,'|',3); err += convSD(pp,step); pp = strField(data,'|',4); err += convSD(pp,val); if (err) { min = 0; max = 100; step = 1; val = 50; } if (*type == 's') { widget = gtk_spin_button_new_with_range(min,max,step); gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),val); } if (*type == 'h') { widget = gtk_scale_new_with_range(HORIZONTAL,min,max,step); gtk_range_set_value(GTK_RANGE(widget),val); gtk_scale_set_draw_value(GTK_SCALE(widget),0); } if (*type == 'v') { widget = gtk_scale_new_with_range(VERTICAL,min,max,step); gtk_range_set_value(GTK_RANGE(widget),val); gtk_scale_set_draw_value(GTK_SCALE(widget),0); } G_SIGNAL(widget,"value-changed",zdialog_widget_event,zd); snprintf(vdata,30,"%g",val); data = vdata; } if (strmatch(type,"zspin")) { // "zspin" widget with range 6.8 if (! data) zappcrash("zdialog_add_widget(): data missing"); // "min|max|step|value" pp = strField(data,'|',1); err = convSD(pp,min); pp = strField(data,'|',2); err += convSD(pp,max); pp = strField(data,'|',3); err += convSD(pp,step); pp = strField(data,'|',4); err += convSD(pp,val); if (err) { min = 0; max = 100; step = 1; val = 50; } zd->widget[iiw].lolim = min; zd->widget[iiw].hilim = max; zd->widget[iiw].step = step; err = convDS(val,6,vdata); // initial value >> text 6.8 data = vdata; widget = gtk_text_view_new(); // GTK widget is text_view #if GTK_CHECK_VERSION(3,18,0) gtk_text_view_set_top_margin(GTK_TEXT_VIEW(widget),2); #endif gtk_text_view_set_left_margin(GTK_TEXT_VIEW(widget),5); if (! size) size = 5; // scale widget for font size gtk_widget_set_size_request(widget,size*appfontsize,2*appfontsize); gtk_text_view_set_editable(GTK_TEXT_VIEW(widget),1); gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget),GTK_WRAP_NONE); gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(widget),0); gtk_text_view_set_input_purpose(GTK_TEXT_VIEW(widget),GTK_INPUT_PURPOSE_NUMBER); editBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); gtk_text_buffer_set_text(editBuff,data,-1); G_SIGNAL(widget,"key-press-event",zdialog_zspin_event,zd); G_SIGNAL(widget,"focus-out-event",zdialog_zspin_event,zd); gtk_widget_add_events(widget,GDK_SCROLL_MASK); G_SIGNAL(widget,"scroll-event",zdialog_zspin_event,zd); } if (strmatch(type,"icon")) { // image widget from icon snprintf(iconpath,200,"%s/%s",get_zimagedir(),data); // 6.8 data = 0; // data not further used pixbuf = gdk_pixbuf_new_from_file_at_scale(iconpath,size,size,1,&gerror); if (pixbuf) { widget = gtk_image_new_from_pixbuf(pixbuf); g_object_unref(pixbuf); } else widget = gtk_image_new_from_icon_name("missing",GTK_ICON_SIZE_BUTTON); } if (strmatch(type,"image")) // image widget from pixbuf 6.5 widget = gtk_image_new_from_pixbuf((GdkPixbuf *) data); // use (cchar *) pixbuf in call // all widget types come here zd->widget[iiw].widget = widget; // set widget in zdialog if (strmatchV(type,"zentry","zspin","edit",0)) { // add frame around these widgets 6.8 fwidget = gtk_frame_new(0); // removed "text" 6.9 gtk_frame_set_shadow_type(GTK_FRAME(fwidget),GTK_SHADOW_IN); gtk_container_add(GTK_CONTAINER(fwidget),widget); widget = fwidget; } if (strmatch(ptype,"hbox") || strmatch(ptype,"vbox")) // add to hbox/vbox gtk_box_pack_start(GTK_BOX(pwidget),widget,expand,expand,space); if (strmatch(ptype,"frame")) // add to frame gtk_container_add(GTK_CONTAINER(pwidget),widget); if (strmatch(ptype,"scrwin")) // add to scroll window gtk_container_add(GTK_CONTAINER(pwidget),widget); if (strmatch(ptype,"dialog")) { // add to dialog box vbox = gtk_bin_get_child(GTK_BIN(pwidget)); // dialog is a gtkwindow gtk_box_pack_start(GTK_BOX(vbox),widget,expand,expand,space); } if (data) zd->widget[iiw].data = zstrdup(data); // widget memory return 0; } // add widget to existing zdialog - alternative form (clearer and easier code) // options: "size=nn | homog | expand | space=nn | wrap" (all optional, any order) int zdialog_add_widget(zdialog *zd, cchar *type, cchar *name, cchar *parent, cchar *data, cchar *options) { int stat, size = 0, homog = 0, expand = 0, space = 0, wrap = 0, begin = 1; char pname[8]; double pval; while (true) { stat = strParms(begin,options,pname,8,pval); if (stat == -1) break; if (stat == 1) zappcrash("bad zdialog options: %s",options); if (strmatch(pname,"size")) size = (int(pval)); else if (strmatch(pname,"homog")) homog = 1; else if (strmatch(pname,"expand")) expand = 1; else if (strmatch(pname,"space")) space = (int(pval)); else if (strmatch(pname,"wrap")) wrap = 1; else zappcrash("bad zdialog options: %s",options); } stat = zdialog_add_widget(zd,type,name,parent,data,size,homog,expand,space,wrap); return stat; } // return 1/0 if zdialog is valid/invalid int zdialog_valid(zdialog *zd, cchar *title) { int ok, ii; if (! zd) return 0; for (ii = 0; ii < zdialog_count; ii++) // find in valid zdialog list if (zd == zdialog_list[ii]) break; if (ii == zdialog_count) return 0; ok = 1; if ((zd->sentinel1 & 0xFFFF0000) != zdsentinel) ok = 0; if (zd->sentinel2 != zd->sentinel1) ok = 0; if (! ok) { printz("*** zdialog sentinel invalid %p \n",zd); return 0; } if (title && ! strmatch(title,zd->title)) return 0; // 7.3 return 1; } // get GTK widget from zdialog and widget name GtkWidget * zdialog_widget(zdialog *zd, cchar *name) { if (! zdialog_valid(zd)) return 0; for (int ii = 0; zd->widget[ii].type; ii++) if (strmatch(zd->widget[ii].name,name)) return zd->widget[ii].widget; return 0; } // set an "image" widget type from a GDK pixbuf // returns 0 if OK, else +N int zdialog_set_image(zdialog *zd, cchar *name, GdkPixbuf *pixbuf) // 6.5 { GtkWidget *widget; int ii; if (! zdialog_valid(zd)) return 1; for (ii = 0; zd->widget[ii].type; ii++) if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 2; if (! strmatch(zd->widget[ii].type,"image")) return 3; widget = zd->widget[ii].widget; gtk_image_set_from_pixbuf(GTK_IMAGE(widget),pixbuf); return 0; } // add a popup tool tip to a zdialog widget int zdialog_add_ttip(zdialog *zd, cchar *wname, cchar *ttip) { GtkWidget *widget; int ii; if (! zdialog_valid(zd)) return 0; for (ii = 0; zd->compwidget[ii]; ii++) // search completion buttons if (strmatch(zd->compbutton[ii],wname)) { // for matching name gtk_widget_set_tooltip_text(zd->compwidget[ii],ttip); return 1; } widget = zdialog_widget(zd,wname); // search zdialog widgets if (! widget) { printz(" *** zdialog widget invalid %s \n",wname); return 0; } gtk_widget_set_tooltip_text(widget,ttip); return 1; } // set a common group for a set of radio buttons // (GTK, this does not work) int zdialog_set_group(zdialog *zd, cchar *radio1, ...) { va_list arglist; cchar *radio2; GtkWidget *gwidget, *widget; GSList *glist; gwidget = zdialog_widget(zd,radio1); glist = gtk_radio_button_get_group(GTK_RADIO_BUTTON(gwidget)); if (! glist) zappcrash("no radio button group"); va_start(arglist,radio1); while (true) { radio2 = va_arg(arglist,cchar *); if (! radio2) break; widget = zdialog_widget(zd,radio2); gtk_radio_button_set_group(GTK_RADIO_BUTTON(widget),glist); } va_end(arglist); return 0; } // resize dialog to a size greater than initial size // (as determined by the included widgets) int zdialog_resize(zdialog *zd, int width, int height) { if (! zdialog_valid(zd)) return 0; if (! width) width = 10; // stop spurious GTK warnings 6.1 if (! height) height = 10; GtkWidget *window = zd->widget[0].widget; gtk_window_set_default_size(GTK_WINDOW(window),width,height); return 1; } // put data into a zdialog widget // private function int zdialog_put_data(zdialog *zd, cchar *name, cchar *data) { GtkWidget *widget, *entry; GtkTextBuffer *textBuff; GdkRGBA gdkrgba; int iiw, nn, kk, err, Nsteps; cchar *type, *pp; char *wdata, sdata[12]; double dval; double f256 = 1.0 / 256.0; double lval, hval, nval, F, F2; double fdata, lolim, hilim, step; // double 6.8 if (! zdialog_valid(zd)) return 0; if (! name || ! *name) zappcrash("zdialog_put_data(), name null"); for (iiw = 1; zd->widget[iiw].type; iiw++) // find widget if (strmatch(zd->widget[iiw].name,name)) break; if (! zd->widget[iiw].type) { printz("*** zdialog_put_data(), bad name %s \n",name); return 0; } type = zd->widget[iiw].type; widget = zd->widget[iiw].widget; wdata = zd->widget[iiw].data; if (wdata) zfree(wdata); // free prior data memory zd->widget[iiw].data = 0; if (data) { if (utf8_check(data)) wdata = zstrdup("bad UTF8 data"); // replace bad UTF-8 encoding 6.4 else wdata = zstrdup(data); // set new data for widget zd->widget[iiw].data = wdata; } zd->disabled++; // disable for widget stuffing if (strmatch(type,"label")) gtk_label_set_text(GTK_LABEL(widget),data); if (strmatch(type,"link")) gtk_label_set_text(GTK_LABEL(widget),data); if (strmatch(type,"entry")) gtk_entry_set_text(GTK_ENTRY(widget),data); if (strmatch(type,"zentry")) { // text input, single line 6.8 textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); gtk_text_buffer_set_text(textBuff,data,-1); } if (strmatchV(type,"button","zbutton",null)) // change button label 6.8 gtk_button_set_label(GTK_BUTTON(widget),data); if (strmatch(type,"edit")) { // text input to editable text textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); gtk_text_buffer_set_text(textBuff,data,-1); } if (strmatch(type,"text")) { // text output textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); gtk_text_buffer_set_text(textBuff,data,-1); } if (strmatchV(type,"togbutt","check","radio",null)) { if (! data) kk = nn = 0; else kk = convSI(data,nn); if (kk != 0) nn = 0; // data not integer, force zero if (nn <= 0) nn = 0; else nn = 1; gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),nn); // set gtk widget value } if (strmatch(type,"spin")) { kk = convSD(data,dval); if (kk != 0) dval = 0.0; gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),dval); } if (strmatch(type,"zspin")) { // "zspin" widget 6.8 lolim = zd->widget[iiw].lolim; hilim = zd->widget[iiw].hilim; step = zd->widget[iiw].step; err = convSD(data,fdata); // string --> double 6.8 if (err) goto retx; Nsteps = (fdata - lolim) / step + 0.5; // nearest exact step 6.8 fdata = lolim + Nsteps * step; if (fdata < lolim) fdata = lolim; // enforce limits if (fdata > hilim) fdata = hilim; convDS(fdata,6,sdata); // double --> string, precision 6 6.8 textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); gtk_text_buffer_set_text(textBuff,sdata,-1); } if (strmatch(type,"colorbutt")) { // color button data is nnn|nnn|nnn pp = strField(data,'|',1); if (pp) gdkrgba.red = f256 * atoi(pp); // RGB range is 0-1 pp = strField(data,'|',2); if (pp) gdkrgba.green = f256 * atoi(pp); pp = strField(data,'|',3); if (pp) gdkrgba.blue = f256 * atoi(pp); gdkrgba.alpha = 1.0; gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(widget),&gdkrgba); } if (strmatchV(type,"hscale","vscale",null)) // slider widget { kk = convSD(data,dval); // zdialog widget value if (kk != 0) dval = 0.0; if (zd->widget[iiw].rescale) // widget value --> slider value 6.2 { lval = zd->widget[iiw].lval; // rescaled for more sensitivity nval = zd->widget[iiw].nval; // around neutral value hval = zd->widget[iiw].hval; if (dval > lval && dval <= nval) { // if dval == lval or dval == hval 6.8 F2 = (nval - dval) / (nval - lval); // then dval is not revised F = sqrtf(F2); dval = nval - F * (nval - lval); } else if (dval >= nval && dval < hval) { F2 = (dval - nval) / (hval - nval); F = sqrtf(F2); dval = nval + F * (hval - nval); } } gtk_range_set_value(GTK_RANGE(widget),dval); } if (strmatch(type,"combo")) { if (! blank_null(data)) { kk = pvlist_prepend(zd->widget[iiw].cblist,data,1); // add to drop-down list if (kk == 0) // (only if unique) gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(widget),data); kk = pvlist_find(zd->widget[iiw].cblist,data); gtk_combo_box_set_active(GTK_COMBO_BOX(widget),kk); // make the active entry } else gtk_combo_box_set_active(GTK_COMBO_BOX(widget),-1); // make no active entry } if (strmatch(type,"comboE")) { entry = gtk_bin_get_child(GTK_BIN(widget)); if (! blank_null(data)) { kk = pvlist_prepend(zd->widget[iiw].cblist,data,1); // add to drop-down list if (kk == 0) // (only if unique) gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(widget),data); gtk_entry_set_text(GTK_ENTRY(entry),data); // stuff entry box with new data } else gtk_entry_set_text(GTK_ENTRY(entry),""); // stuff entry box with nothing } retx: zd->disabled--; // re-enable dialog return iiw; } // get data from a dialog widget based on its name // private function cchar * zdialog_get_data(zdialog *zd, cchar *name) { if (! zdialog_valid(zd)) return 0; for (int ii = 1; zd->widget[ii].type; ii++) if (strmatch(zd->widget[ii].name,name)) return zd->widget[ii].data; return 0; } // set new limits for a numeric data entry widget (spin, zspin, hscale, vscale) int zdialog_set_limits(zdialog *zd, cchar *name, double min, double max) { GtkWidget *widget; cchar *type; int iiw; if (! zdialog_valid(zd)) return 0; for (iiw = 1; zd->widget[iiw].type; iiw++) if (strmatch(name,zd->widget[iiw].name)) break; if (! zd->widget[iiw].type) { printz("*** zdialog_set_limits, %s not found \n",name); return 0; } widget = zd->widget[iiw].widget; type = zd->widget[iiw].type; if (*type == 's') gtk_spin_button_set_range(GTK_SPIN_BUTTON(widget),min,max); if (*type == 'h' || *type == 'v') gtk_range_set_range(GTK_RANGE(widget),min,max); if (*type == 'z') { // zspin 6.8 zd->widget[iiw].lval = min; zd->widget[iiw].hval = max; } return 1; } // Expand a widget scale in the region around the neutral value. // 6.0 // Control small adjustments near the neutral value more precisely. // lval and hval: the range of values to be rescaled. // nval: the neutral value where the scale will be expanded the most. // lval <= nval <= hval // 6.6 int zdialog_rescale(zdialog *zd, cchar *name, float lval, float nval, float hval) { int iiw; if (! zdialog_valid(zd)) return 0; for (iiw = 1; zd->widget[iiw].type; iiw++) if (strmatch(name,zd->widget[iiw].name)) break; if (! zd->widget[iiw].type) { printz("*** zdialog_rescale, %s not found \n",name); return 0; } if (lval > nval || nval > hval) { // 6.6 printz("*** zdialog_rescale, bad data: %s \n",name); return 0; } zd->widget[iiw].rescale = 1; zd->widget[iiw].lval = lval; zd->widget[iiw].nval = nval; zd->widget[iiw].hval = hval; return 1; } // run the dialog and send events to the event function // // evfunc: int func(zdialog *zd, cchar *event) // If present, eventFunc is called when a dialog widget is changed or the dialog // is completed. If a widget was changed, event is the widget name. // Get the new widget data with zdialog_fetch(). // If a completion button was pressed, event is "zstat" and zd->zstat will be // the button number 1-N. // If the dialog was destroyed, event is "zstat" and zd->zstat is negative. // // posn: optional dialog box position: // "mouse" = position at mouse // "desktop" = center on desktop // "parent" = center on parent window // "nn/nn" = position NW corner at relative x/y position in parent window, // where nn/nn is a percent 0-100 of the parent window dimensions. // "save" = save last user-set position and use this whenever the dialog // is repeated, also across sessions. DEFAULT. // // KBevent: extern void KBevent(GdkEventKey *event) // This function must be supplied by the caller of zdialog. // It is called when Ctrl|Shift|Alt|F1 is pressed. int zdialog_run(zdialog *zd, zdialog_event evfunc, cchar *posn) { int zdialog_KB_press(GtkWidget *, GdkEventKey *event, zdialog *zd); int zdialog_focus_event(GtkWidget *, GdkEvent *event, zdialog *zd); GtkWidget *dialog; if (! zdialog_valid(zd)) zappcrash("zdialog invalid"); if (zd->zrunning) { printz("zdialog is already running \n"); return 0; } if (posn) zdialog_set_position(zd,posn); // put dialog at desired position else zdialog_set_position(zd,"save"); // use default 7.1 if (evfunc) zd->eventCB = (void *) evfunc; // link to dialog event callback dialog = zd->widget[0].widget; gtk_widget_show_all(dialog); // activate dialog G_SIGNAL(dialog,"focus-in-event",zdialog_focus_event,zd); // connect focus event function G_SIGNAL(dialog,"key-press-event",zdialog_KB_press,zd); // connect key press event function G_SIGNAL(dialog,"delete-event",zdialog_delete_event,zd); // connect delete event function zd->zstat = 0; // dialog status incomplete zd->disabled = 0; // enable widget events zd->zrunning = 1; // dialog is running zfuncs::zdialog_busy++; // count open zdialogs return 0; } // zdialog event handler - called for dialog events. // Updates data in zdialog, calls user callback function (if present). // private function int zdialog_widget_event(GtkWidget *widget, zdialog *zd) { zdialog_event *evfunc = 0; // dialog event callback function GtkTextView *textView = 0; GtkTextBuffer *textBuff = 0; GtkTextIter iter1, iter2; GdkRGBA gdkrgba; GtkWidget *entry; int ii, nn; cchar *wname, *wtype, *wdata; char sdata[20]; double dval; float lval, nval, hval, F; static int cbadded = 0; if (! zdialog_valid(zd)) return 1; if (zd->disabled) return 1; // events disabled zd->disabled = 1; for (ii = 0; ii < zdmaxbutts; ii++) { // check completion buttons if (zd->compwidget[ii] == null) break; // EOL if (zd->compwidget[ii] != widget) continue; zd->zstat = ii+1; // zdialog status = button no. strncpy0(zd->event,"zstat",40); strncpy0(zd->zstat_button,zd->compbutton[ii],40); // button label "cancel" etc. 7.3 wtype = "completion button"; goto call_evfunc; // call zdialog event function } for (ii = 1; zd->widget[ii].type; ii++) // find widget in zdialog if (zd->widget[ii].widget == widget) goto found_widget; for (ii = 1; zd->widget[ii].type; ii++) { // failed, test if buffer if (strmatchV(zd->widget[ii].type,"edit","zentry",null)) { // of text view widget 6.8 textView = GTK_TEXT_VIEW(zd->widget[ii].widget); textBuff = gtk_text_view_get_buffer(textView); if (widget == (GtkWidget *) textBuff) goto found_widget; } } printz("*** zdialog event ignored \n"); // not found, ignore event zd->disabled = 0; return 1; found_widget: wname = zd->widget[ii].name; wtype = zd->widget[ii].type; wdata = 0; if (strmatch(wtype,"button")) wdata = gtk_button_get_label(GTK_BUTTON(widget)); // button label 6.4 if (strmatch(wtype,"zbutton")) { // checkbox as smaller button 6.8 wdata = gtk_button_get_label(GTK_BUTTON(widget)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),0); // reset checkmark = off } if (strmatch(wtype,"edit")) { gtk_text_buffer_get_bounds(textBuff,&iter1,&iter2); wdata = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); } if (strmatch(wtype,"entry")) wdata = gtk_entry_get_text(GTK_ENTRY(widget)); if (strmatch(wtype,"zentry")) { // 6.8 gtk_text_buffer_get_bounds(textBuff,&iter1,&iter2); wdata = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); } if (strmatchV(wtype,"radio","check","togbutt",null)) { nn = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); if (nn == 0) wdata = "0"; else wdata = "1"; } if (strmatch(wtype,"combo")) wdata = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(widget)); if (strmatch(wtype,"comboE")) { if (widget == zd->lastwidget && cbadded) { pvlist_remove(zd->widget[ii].cblist,0); // detect multiple edits (keystrokes) gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(widget),0); // amd replace with new entry } entry = gtk_bin_get_child(GTK_BIN(widget)); wdata = gtk_entry_get_text(GTK_ENTRY(entry)); cbadded = 0; if (! blank_null(wdata)) { nn = pvlist_prepend(zd->widget[ii].cblist,wdata,1); // add entry to drop-down list if (nn == 0) { // (only if unique) gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(widget),wdata); cbadded = 1; } } } if (strmatch(wtype,"spin")) { dval = gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget)); snprintf(sdata,20,"%g",dval); wdata = sdata; } if (strmatch(wtype,"colorbutt")) // color button { gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget),&gdkrgba); snprintf(sdata,20,"%.0f|%.0f|%.0f",gdkrgba.red*255,gdkrgba.green*255,gdkrgba.blue*255); wdata = sdata; } if (strmatchV(wtype,"hscale","vscale",null)) { dval = gtk_range_get_value(GTK_RANGE(widget)); if (zd->widget[ii].rescale) // slider value --> widget value 6.0 { lval = zd->widget[ii].lval; nval = zd->widget[ii].nval; hval = zd->widget[ii].hval; if (dval > lval && dval < nval) { // lval ... nval 6.6 F = (nval - dval) / (nval - lval); // 1 ... 0 dval = (1.0 - F * F) * (nval - lval) + lval; // lval ... nval } else if (dval > nval && dval < hval) { // nval ... hval F = (dval - nval) / (hval - nval); // 0 ... 1 dval = F * F * (hval - nval) + nval; // nval ... hval } } snprintf(sdata,20,"%g",dval); wdata = sdata; } // all widgets come here if (zd->widget[ii].data) zfree(zd->widget[ii].data); // clear prior data zd->widget[ii].data = 0; if (wdata) zd->widget[ii].data = zstrdup(wdata); // set new data zd->lastwidget = widget; // remember last widget updated strncpy0(zd->event,wname,40); // event = widget name 6.6 call_evfunc: // call zdialog event function if (zd->eventCB) { if (! zdialog_valid(zd)) zexit("%s event for invalid zdialog",wtype); // 7.2 evfunc = (zdialog_event *) zd->eventCB; // do callback function evfunc(zd,zd->event); // 6.6 } if (zdialog_valid(zd)) zd->disabled = 0; // 'event' may cause zdialog_free() 7.1 return 1; } // zdialog response handler for "focus-in-event" signal // private function zdialog *zdialog_focus_zd; // current zdialog int zdialog_focus_event(GtkWidget *, GdkEvent *event, zdialog *zd) { if (! zdialog_valid(zd)) return 0; if (zd->zstat) return 0; // already complete zdialog_focus_zd = zd; zdialog_send_event(zd,"focus"); // notify dialog event function return 0; // must be 0 } // set KB shortcuts for common zdialog completion buttons // 6.8 int Nkbshortcuts = 0; cchar *kbshortcuts[10], *kbevents[10]; void zdialog_KB_addshortcut(cchar *shortcut, cchar *event) // e.g. ("Ctrl+D", "Done") { if (Nkbshortcuts > 9) return; kbshortcuts[Nkbshortcuts] = zstrdup(shortcut); kbevents[Nkbshortcuts] = zstrdup(event); ++Nkbshortcuts; return; } // zdialog response handler for keyboard events // key symbols can be found at /usr/include/gtk-3.0/gdk/gdkkeysyms.h // main app must provide: extern void KBevent(GdkEventKey *event) // private function int zdialog_KB_press(GtkWidget *widget, GdkEventKey *kbevent, zdialog *zd) { void zdialog_copyfunc(GtkWidget *, GtkClipboard *); void zdialog_pastefunc(GtkClipboard *, cchar *, void *); zdialog_event *evfunc = 0; // dialog event callback function GtkWidget *focuswidget; int KB_Ctrl = 0, KB_Alt = 0; // track state of Ctrl and Alt keys int KBkey = kbevent->keyval; cchar *type; int ii, cc, Ftext; if (kbevent->state & GDK_CONTROL_MASK) KB_Ctrl = 1; if (kbevent->state & GDK_MOD1_MASK) KB_Alt = 1; if (KBkey == GDK_KEY_Escape) { // ESC key, treat as [x] 7.1 zdialog_delete_event(null,null,zd); return 1; } if (KBkey == GDK_KEY_F1) { KBevent(kbevent); return 1; }; // these keys handled by main app if (KBkey == GDK_KEY_F10) { KBevent(kbevent); return 1; }; if (KBkey == GDK_KEY_F11) { KBevent(kbevent); return 1; }; for (ii = 0; ii < Nkbshortcuts; ii++) { // look for dialog button shortcut 6.8 if (strstr(kbshortcuts[ii],"Ctrl") && ! KB_Ctrl) continue; if (strstr(kbshortcuts[ii],"Alt") && ! KB_Alt) continue; cc = strlen(kbshortcuts[ii]); if (KBkey == kbshortcuts[ii][cc-1]) break; // compare key to last char. in shortcut if (KBkey-32 == kbshortcuts[ii][cc-1]) break; // (case indifferent compare) } if (ii < Nkbshortcuts) { zdialog_send_event(zd,kbevents[ii]); // send corresp. event, e.g. "Done" return 1; } focuswidget = gtk_window_get_focus(GTK_WINDOW(widget)); // find widget in zdialog 6.5 for (ii = 1; zd->widget[ii].type; ii++) if (zd->widget[ii].widget == focuswidget) break; type = zd->widget[ii].type; if (! type) return 0; Ftext = strmatchV(type,"zspin","zentry","entry","edit","text","spin",null); // text-edit widgets 7.1 if (KBkey == GDK_KEY_Left || KBkey == GDK_KEY_Right) { // left/right arrow key if (! Ftext) { // not a text-edit widget, KBevent(kbevent); // pass key to main() return 1; } } if (KBkey == GDK_KEY_Return && Ftext == 2) { // zentry widget if (zd->eventCB) { zd->disabled = 1; evfunc = (zdialog_event *) zd->eventCB; // do callback function evfunc(zd,zd->event); if (zdialog_valid(zd)) zd->disabled = 0; // 'event' may cause zdialog_free() 7.1 } return 1; } return 0; // pass KB key to widget } // event function for "zspin" widget (text entry works as spin button) int zdialog_zspin_event(GtkWidget *widget, GdkEvent *event, zdialog *zd) // 6.8 { zdialog_event *evfunc = 0; // dialog event callback function GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; int KBkey; int ii, err, Nsteps, state, incr = 0; double fdata, lolim, hilim, step; // double 6.8 char *wdata, sdata[20]; int time, elaps; static int time0 = 0, time1 = 0; if (! zdialog_valid(zd)) return 1; if (zd->disabled) return 1; // zdialog events disabled for (ii = 1; zd->widget[ii].type; ii++) // find "zspin" (text view) widget if (zd->widget[ii].widget == widget) break; if (! zd->widget[ii].type) return 0; // not found textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); // get widget data gtk_text_buffer_get_bounds(textBuff,&iter1,&iter2); wdata = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); lolim = zd->widget[ii].lolim; // limits and step size hilim = zd->widget[ii].hilim; step = zd->widget[ii].step; if (event->type == GDK_SCROLL) { // mouse wheel event gtk_widget_grab_focus(widget); incr = - ((GdkEventScroll *) event)->delta_y; if (! incr) return 0; // 7.2 state = ((GdkEventScroll *) event)->state; // if shift key held, use 10x step if (state & GDK_SHIFT_MASK) incr *= 10; goto checklimits; } if (event->type == GDK_KEY_PRESS) { // KB button press KBkey = ((GdkEventKey *) event)->keyval; if (KBkey == GDK_KEY_Return) goto checklimits; // return = entry finished if (KBkey == GDK_KEY_Up) incr = 1; if (KBkey == GDK_KEY_Down) incr = -1; if (! incr) return 0; // must return 0 state = ((GdkEventKey *) event)->state; // if shift key held, use 10x step 6.8 if (state & GDK_SHIFT_MASK) incr *= 10; time = ((GdkEventKey *) event)->time; // track time key is held down if (time - time1 > 300) time0 = time; time1 = time; elaps = time - time0; if (elaps > 5000) step = 10 * step; // acceleration table for else if (elaps > 4500) step = 9 * step; // hold time 1-5+ seconds else if (elaps > 4000) step = 8 * step; // use integer values only 6.8 else if (elaps > 3500) step = 7 * step; else if (elaps > 3000) step = 6 * step; else if (elaps > 2500) step = 5 * step; else if (elaps > 2000) step = 4 * step; else if (elaps > 1500) step = 3 * step; else if (elaps > 1000) step = 2 * step; goto checklimits; } if (event->type == GDK_FOCUS_CHANGE) goto checklimits; // focus change = entry finished return 0; // 7.2 checklimits: convSD(wdata,fdata); // ignore bad char. inputs 6.8 fdata += incr * step; Nsteps = (fdata - lolim) / step + 0.5; // set nearest exact step 6.8 fdata = lolim + Nsteps * step; err = 0; if (fdata < lolim) { // force within range err = 1; fdata = lolim; } if (fdata > hilim) { err = 2; fdata = hilim; } if (err) gtk_widget_grab_focus(widget); // if error, restore focus convDS(fdata,6,sdata); // round to 6 digits 6.8 gtk_text_buffer_set_text(textBuff,sdata,-1); if (zd->widget[ii].data) zfree(zd->widget[ii].data); // clear prior widget data zd->widget[ii].data = zstrdup(sdata); // set new data zd->lastwidget = widget; // remember last widget updated strncpy0(zd->event,zd->widget[ii].name,40); // event = widget name if (zd->eventCB) { zd->disabled = 1; evfunc = (zdialog_event *) zd->eventCB; // do callback function evfunc(zd,zd->event); if (zdialog_valid(zd)) zd->disabled = 0; // 'event' may cause zdialog_free() 7.1 } if (event->type == GDK_KEY_PRESS) return 1; // stop new line from enter key 7.2 return 0; // others OK 7.2 } // process Ctrl+C (copy text from widget to clipboard) // 6.0 // private function void zdialog_copyfunc(GtkWidget *widget, GtkClipboard *clipboard) { GtkTextView *textView = 0; GtkTextBuffer *textBuff = 0; zdialog *zd; int ii, cc = 0; cchar *wname; char text[1000]; widget = gtk_window_get_focus(GTK_WINDOW(widget)); if (! widget) return; zd = zdialog_focus_zd; if (! zdialog_valid(zd)) return; for (ii = 1; zd->widget[ii].type; ii++) // find widget in zdialog if (zd->widget[ii].widget == widget) goto found_widget; for (ii = 1; zd->widget[ii].type; ii++) { // failed, test if buffer if (strmatchV(zd->widget[ii].type,"edit","zentry",null)) { // of text view widget 6.8 textView = GTK_TEXT_VIEW(zd->widget[ii].widget); textBuff = gtk_text_view_get_buffer(textView); if (widget == (GtkWidget *) textBuff) goto found_widget; } } return; // not found found_widget: wname = zd->widget[ii].name; zdialog_fetch(zd,wname,text,999); // current text in widget cc = strlen(text); gtk_clipboard_set_text(clipboard,text,cc); return; } // process Ctrl+V (paste text from clipboard to widget) // private function void zdialog_pastefunc(GtkClipboard *clipboard, cchar *cliptext, void *arg) { GtkWindow *window; GtkWidget *widget; GtkTextView *textView = 0; GtkTextBuffer *textBuff = 0; zdialog *zd; int ii, cc = 0; cchar *wname; char text[1000]; window = (GtkWindow *) arg; widget = gtk_window_get_focus(window); if (! widget) return; // widget for pasted text if (! cliptext || ! *cliptext) return; // clipboard text pasted zd = zdialog_focus_zd; if (! zdialog_valid(zd)) return; if (zd->zstat) return; for (ii = 1; zd->widget[ii].type; ii++) // find widget in zdialog if (zd->widget[ii].widget == widget) goto found_widget; for (ii = 1; zd->widget[ii].type; ii++) { // failed, test if buffer if (strmatchV(zd->widget[ii].type,"edit","zentry",null)) { // of text view widget 6.8 textView = GTK_TEXT_VIEW(zd->widget[ii].widget); textBuff = gtk_text_view_get_buffer(textView); if (widget == (GtkWidget *) textBuff) goto found_widget; } } return; // not found found_widget: wname = zd->widget[ii].name; zdialog_fetch(zd,wname,text,999); // current text in widget cc = strlen(text); if (cc > 995) return; strncpy(text+cc,cliptext,999-cc); // add clipboard text text[999] = 0; zdialog_stuff(zd,wname,text); return; } // private function called when zdialog is completed. // called when dialog is canceled via [x] button or destroyed by GTK (zstat < 0). int zdialog_delete_event(GtkWidget *, GdkEvent *, zdialog *zd) { zdialog_event *evfunc = 0; // dialog event callback function if (! zdialog_valid(zd)) return 1; if (zd->zstat) return 1; // already complete if (zd->disabled) return 1; // in process zd->zstat = -1; // set zdialog cancel status if (zd->eventCB) { evfunc = (zdialog_event *) zd->eventCB; // do callback function zd->disabled = 1; evfunc(zd,"zstat"); if (zdialog_valid(zd)) zd->disabled = 0; // 'event' may cause zdialog_free() 7.1 } if (zdialog_valid(zd)) 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 int ii; cchar *eventx = E2X(event); // translated event 6.9 if (! zdialog_valid(zd)) return 0; // canceled if (zd->disabled) return 0; // busy if (strstr(zdialog_button_shortcuts,event)) { // completion buttons (zfuncs.h) 6.8 for (ii = 0; ii < zdmaxbutts; ii++) { // find button if (! zd->compbutton[ii]) break; // EOL if (strmatch(event,zd->compbutton[ii])) break; // english event if (strmatch(eventx,zd->compbutton[ii])) break; // translated event (dialog button) 6.9 } if (zd->compbutton[ii]) { // found zd->zstat = ii+1; // zdialog status = button no. strcpy(zd->event,"zstat"); // event = "zstat" } else if (strmatch(event,"Cancel")) // no [Cancel] button in zdialog, zdialog_destroy(zd); // handle same as [x] } evfunc = (zdialog_event *) zd->eventCB; if (! evfunc) return 0; zd->disabled = 1; evfunc(zd,event); // call dialog event function if (zdialog_valid(zd)) zd->disabled = 0; // 'event' may cause zdialog_free() 7.1 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; zd->zstat = zstat; // set status evfunc = (zdialog_event *) zd->eventCB; if (! evfunc) return 0; zd->disabled = 1; evfunc(zd,"zstat"); if (zdialog_valid(zd)) zd->disabled = 0; // 'event' may cause zdialog_free() 7.1 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; if (! zdialog_valid(zd)) return 0; widget = zdialog_widget(zd,"dialog"); if (show) { // show window if (widget == pwidget) { // restore prior position gtk_window_move(GTK_WINDOW(widget),posx,posy); pwidget = 0; } gtk_widget_show_all(widget); gtk_window_present(GTK_WINDOW(widget)); // set focus on restored window 6.5 } else { // hide window pwidget = widget; gtk_window_get_position(GTK_WINDOW(widget),&posx,&posy); // save position gtk_widget_hide(widget); } return 1; } // Destroy the zdialog - must be done by zdialog_run() caller // (else dialog continues active even after completion button). // Data in widgets remains valid until zdialog_free() is called. int zdialog_destroy(zdialog *zd) { if (! zdialog_valid(zd)) return 0; if (zd->saveposn) zdialog_save_position(zd); // save position for next use moved 6.9 if (zd->widget[0].widget) { // multiple destroys OK gtk_widget_destroy(zd->widget[0].widget); // destroy GTK dialog zd->widget[0].widget = 0; zdialog_busy--; } if (! zd->zstat) zd->zstat = -1; // status = destroyed zd->zrunning = 0; // not running 6.3 return 1; } // free zdialog memory (will destroy first, if not already) // zd is set to null int zdialog_free(zdialog *&zd) // reference { int ii; if (! zdialog_valid(zd)) return 0; // validate zd pointer zdialog_save_inputs(zd); // save user inputs for next use zdialog_destroy(zd); // destroy GTK dialog if there zd->sentinel1 = zd->sentinel2 = 0; // mark sentinels invalid zfree(zd->title); // free title memory 6.8 zfree(zd->widget[0].data); for (ii = 1; zd->widget[ii].type; ii++) // loop through widgets { if (strmatchV(zd->widget[ii].type,"combo","comboE",null)) // free combo list pvlist_free(zd->widget[ii].cblist); zfree((char *) zd->widget[ii].type); // free strings zfree((char *) zd->widget[ii].name); if (zd->widget[ii].pname) zfree((char *) zd->widget[ii].pname); // parent widget name 6.5 if (zd->widget[ii].data) zfree(zd->widget[ii].data); // free data } for (ii = 0; ii < zdialog_count; ii++) // remove from valid zdialog list if (zd == zdialog_list[ii]) break; if (ii < zdialog_count) { zdialog_count--; for (NOP; ii < zdialog_count; ii++) // pack down list zdialog_list[ii] = zdialog_list[ii+1]; } else printz("*** zdialog_free(), not in zdialog_list \n"); // 7.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)) return -1; if (zd->zstat) return zd->zstat; zsleep(0.01); } } // put cursor at named widget int zdialog_goto(zdialog *zd, cchar *name) { GtkWidget *widget; if (! zdialog_valid(zd)) return 0; widget = zdialog_widget(zd, name); if (! widget) return 0; gtk_widget_grab_focus(widget); return 1; } // set cursor for zdialog (e.g. a busy cursor) void zdialog_set_cursor(zdialog *zd, GdkCursor *cursor) { GtkWidget *dialog; GdkWindow *window; if (! zdialog_valid(zd)) return; dialog = zd->widget[0].widget; if (! dialog) return; window = gtk_widget_get_window(dialog); gdk_window_set_cursor(window,cursor); return; } // insert data into a zdialog widget int zdialog_stuff(zdialog *zd, cchar *name, cchar *data) // stuff a string { if (data) zdialog_put_data(zd,name,data); else zdialog_put_data(zd,name,""); // null > "" return 1; } int zdialog_stuff(zdialog *zd, cchar *name, int idata) // stuff an integer { char string[16]; snprintf(string,16,"%d",idata); zdialog_put_data(zd,name,string); return 1; } int zdialog_stuff(zdialog *zd, cchar *name, double ddata) // stuff a double { char string[32]; snprintf(string,32,"%.7g",ddata); // increase from 6 to 7 digits 6.8 zdialog_put_data(zd,name,string); // 'g' uses decimal or comma return 1; // (per locale) } int zdialog_stuff(zdialog *zd, cchar *name, double ddata, cchar *format) // stuff a double, formatted 6.1 { char string[32]; snprintf(string,32,format,ddata); // use "%.2g" etc. for zdialog_put_data(zd,name,string); // locale dependent point/comma return 1; } int zdialog_labelfont(zdialog *zd, cchar *labl, cchar *font, cchar *txt) // stuff label text with font 6.2 { GtkWidget *widget; cchar *format = "%s"; char txt2[1000]; if (! font) font = zfuncs::appfont; // default font 6.3 snprintf(txt2,1000,format,font,txt); widget = zdialog_widget(zd,labl); gtk_label_set_markup(GTK_LABEL(widget),txt2); return 1; } // get data from a zdialog widget int zdialog_fetch(zdialog *zd, cchar *name, char *data, int maxcc) // fetch string data { cchar *zdata; zdata = zdialog_get_data(zd,name); if (! zdata) { *data = 0; return 0; } return strncpy0(data,zdata,maxcc); // 0 = OK, 1 = truncation } int zdialog_fetch(zdialog *zd, cchar *name, int &idata) // fetch an integer { cchar *zdata; zdata = zdialog_get_data(zd,name); if (! zdata) { idata = 0; return 0; } idata = atoi(zdata); return 1; } int zdialog_fetch(zdialog *zd, cchar *name, double &ddata) // fetch a double { int stat; cchar *zdata; zdata = zdialog_get_data(zd,name); if (! zdata) { ddata = 0; return 0; } stat = convSD(zdata,ddata); // period or comma decimal point OK if (stat < 4) return 1; return 0; } int zdialog_fetch(zdialog *zd, cchar *name, float &fdata) // fetch a float { int stat; cchar *zdata; double ddata; zdata = zdialog_get_data(zd,name); if (! zdata) { fdata = 0; return 0; } stat = convSD(zdata,ddata); // period or comma decimal point OK fdata = ddata; if (stat < 4) return 1; return 0; } /******************************************************************************** Combo Box widget with Entry --------------------------- stat = zdialog_cb_app(zd, name, data) append to combo box list stat = zdialog_cb_prep(zd, name, data) prepend to combo box list data = zdialog_cb_get(zd, name, Nth) get combo box list item data = zdialog_cb_delete(zd, name, data) delete entry matching "data" data = zdialog_cb_clear(zd, name) clear all entries zdialog_cb_popup(zd, name) open the combo box entries These functions map and track a combo box drop-down list, by maintaining a parallel list in memory. The function zdialog-stuff, when called for a comboE widget, automatically prepends the stuffed data to the drop-down list. The private function zdialog_event(), when processing user input to the edit box of a comboE widget, adds the updated entry to the drop-down list. The drop-down list is kept free of redundant and blank entries. *********************************************************************************/ // append new item to combo box list without changing entry box int zdialog_cb_app(zdialog *zd, cchar *name, cchar *data) { int ii, nn; if (! zdialog_valid(zd)) return 0; if (blank_null(data)) return 0; // find widget for (ii = 1; zd->widget[ii].type; ii++) if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 0; // not found if (! strmatchV(zd->widget[ii].type,"combo","comboE",null)) return 0; // not combo box nn = pvlist_append(zd->widget[ii].cblist,data,1); // append unique if (nn >= 0) gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(zd->widget[ii].widget),data); return 1; } // prepend new item to combo box list without changing entry box int zdialog_cb_prep(zdialog *zd, cchar *name, cchar *data) { int ii, nn; if (! zdialog_valid(zd)) return 0; if (blank_null(data)) return 0; // find widget for (ii = 1; zd->widget[ii].type; ii++) if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 0; // not found if (! strmatchV(zd->widget[ii].type,"combo","comboE",null)) return 0; // not combo box nn = pvlist_prepend(zd->widget[ii].cblist,data,1); // prepend unique if (nn == 0) gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(zd->widget[ii].widget),data); return 1; } // get combo box drop-down list entry // Nth = 0 = first list entry (not comboE edit box) char * zdialog_cb_get(zdialog *zd, cchar *name, int Nth) { int ii; if (! zdialog_valid(zd)) return 0; for (ii = 1; zd->widget[ii].type; ii++) // find widget if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 0; // not found if (! strmatchV(zd->widget[ii].type,"combo","comboE",null)) return 0; // not combo box return pvlist_get(zd->widget[ii].cblist,Nth); } // delete entry by name from combo drop down list int zdialog_cb_delete(zdialog *zd, cchar *name, cchar *data) { int ii, nn; if (! zdialog_valid(zd)) return 0; for (ii = 1; zd->widget[ii].type; ii++) // find widget if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 0; // not found if (! strmatchV(zd->widget[ii].type,"combo","comboE",null)) return 0; // not combo box nn = pvlist_find(zd->widget[ii].cblist,data); // find entry by name if (nn < 0) return -1; pvlist_remove(zd->widget[ii].cblist,nn); // remove from memory list gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(zd->widget[ii].widget),nn); // and from widget gtk_combo_box_set_active(GTK_COMBO_BOX(zd->widget[ii].widget),-1); // set no active entry return 0; } // delete all entries from combo drop down list int zdialog_cb_clear(zdialog *zd, cchar *name) { int ii, jj, nn; GtkWidget *widget, *entry; if (! zdialog_valid(zd)) return 0; for (ii = 1; zd->widget[ii].type; ii++) // find widget if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 0; // not found if (! strmatchV(zd->widget[ii].type,"combo","comboE",null)) return 0; // not combo box nn = pvlist_count(zd->widget[ii].cblist); // entry count for (jj = nn-1; jj >= 0; jj--) { pvlist_remove(zd->widget[ii].cblist,jj); // remove from memory list gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(zd->widget[ii].widget),jj); // remove from widget } widget = zd->widget[ii].widget; gtk_combo_box_set_active(GTK_COMBO_BOX(widget),-1); // set no active entry if (strmatch(zd->widget[ii].type,"comboE")) { // stuff entry box with nothing entry = gtk_bin_get_child(GTK_BIN(widget)); gtk_entry_set_text(GTK_ENTRY(entry),""); } return 1; } // make a combo box drop down to show all entries int zdialog_cb_popup(zdialog *zd, cchar *name) { int ii; if (! zdialog_valid(zd)) return 0; for (ii = 1; zd->widget[ii].type; ii++) // find widget if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 0; // not found if (! strmatchV(zd->widget[ii].type,"combo","comboE",null)) return 0; // not combo box gtk_combo_box_popup(GTK_COMBO_BOX(zd->widget[ii].widget)); gtk_combo_box_set_active(GTK_COMBO_BOX(zd->widget[ii].widget),-1); // no active entry 6.2 return 1; } // save all combo box entries to a file in /home//.appname/filename // returns 0 = OK, +N = error int zdialog_cb_save(zdialog *zd, cchar *name, cchar *filename) // 6.2 { char file[200], *pp; int ii, jj, nn; FILE *fid; if (! zdialog_valid(zd)) return 1; for (ii = 1; zd->widget[ii].type; ii++) // find widget if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 2; if (! strmatchV(zd->widget[ii].type,"combo","comboE",null)) return 3; // not combo box nn = pvlist_count(zd->widget[ii].cblist); // entry count if (! nn) return 0; snprintf(file,200,"%s/%s",get_zhomedir(),filename); fid = fopen(file,"w"); if (! fid) return 4; for (jj = 0; jj < nn; jj++) { pp = pvlist_get(zd->widget[ii].cblist,jj); fprintf(fid,"%s\n",pp); } fclose(fid); return 0; } // load combo box entries from a file in /home//.appname/filename // returns 0 = OK, +N = error int zdialog_cb_load(zdialog *zd, cchar *name, cchar *filename) // 6.2 { char file[200], data[100], *pp; int ii; FILE *fid; if (! zdialog_valid(zd)) return 1; for (ii = 1; zd->widget[ii].type; ii++) // find widget if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 2; if (! strmatchV(zd->widget[ii].type,"combo","comboE",null)) return 3; // not combo box zdialog_cb_clear(zd,name); snprintf(file,200,"%s/%s",get_zhomedir(),filename); fid = fopen(file,"r"); if (! fid) return 0; while (true) { pp = fgets_trim(data,99,fid); if (! pp) break; zdialog_cb_app(zd,name,data); } fclose(fid); return 0; } /********************************************************************************/ // functions to save and recall zdialog window positions namespace zdposn_names { struct zdposn_t { char wintitle[64]; // window title (ID) float xpos, ypos; // window position WRT parent or desktop int xsize, ysize; // window size } zdposn[200]; // space to remember 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_geometry(cchar *action) { using namespace zdposn_names; char posfile[200], buff[100], wintitle[64], *pp; float xpos, ypos; int xsize, ysize; int ii, nn, cc; FILE *fid; snprintf(posfile,199,"%s/zdialog_geometry",zhomedir); // /home//.appname/zdialog_geometry if (strmatch(action,"load")) // load dialog positions table from file { fid = fopen(posfile,"r"); if (! fid) { Nzdposn = 0; return 0; } for (ii = 0; ii < Nzdpmax; ) { pp = fgets(buff,100,fid); if (! pp) break; pp = strstr(buff,"||"); if (! pp) continue; cc = pp - buff; strncpy0(wintitle,buff,cc); strTrim(wintitle); if (strlen(wintitle) < 3) continue; nn = sscanf(pp+2," %f %f %d %d ",&xpos,&ypos,&xsize,&ysize); if (nn != 4) continue; strcpy(zdposn[ii].wintitle,wintitle); zdposn[ii].xpos = xpos; zdposn[ii].ypos = ypos; zdposn[ii].xsize = xsize; // 7.1 zdposn[ii].ysize = ysize; ii++; } fclose(fid); Nzdposn = ii; return Nzdposn; } if (strmatch(action,"save")) // save dialog positions table to file { fid = fopen(posfile,"w"); if (! fid) { printz("*** cannot write zdialog_geometry file \n"); return 0; } for (ii = 0; ii < Nzdposn; ii++) { fprintf(fid,"%s || %0.1f %0.1f %d %d \n", // dialog-title || xpos ypos xsize ysize zdposn[ii].wintitle, zdposn[ii].xpos, zdposn[ii].ypos, zdposn[ii].xsize, zdposn[ii].ysize); // 7.1 } fclose(fid); return Nzdposn; } printz("*** zdialog_geometry bad action: %s \n",action); return 0; } // Set the initial or new zdialog window position from "posn". // Called by zdialog_run(). Private function. // null: window manager decides // "mouse" put dialog at mouse position // "desktop" center dialog in desktop window // "parent" center dialog in parent window // "save" use the same position last set by the user // "nn/nn" put NW corner of dialog in parent window at % size // (e.g. "50/50" puts NW corner at center of parent) void zdialog_set_position(zdialog *zd, cchar *posn) { using namespace zdposn_names; int ii, ppx, ppy, zdpx, zdpy, pww, phh; float xpos, ypos; int xsize, ysize; char wintitle[64], *pp; GtkWidget *parent, *dialog; if (! zdialog_valid(zd)) return; parent = zd->parent; dialog = zd->widget[0].widget; if (strmatch(posn,"mouse")) { window_to_mouse(zd->dialog); // 7.1 return; } if (strmatch(posn,"desktop")) { gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_CENTER); return; } if (strmatch(posn,"parent")) { gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_CENTER_ON_PARENT); return; } if (! parent) { // no parent window ppx = ppy = 0; // use desktop pww = monitor_ww; phh = monitor_hh; } else { gtk_window_get_position(GTK_WINDOW(parent),&ppx,&ppy); // parent window NW corner gtk_window_get_size(GTK_WINDOW(parent),&pww,&phh); // parent window size } if (strmatch(posn,"save")) // use last saved window position { zd->saveposn = 1; // set flag for zdialog_free() pp = (char *) gtk_window_get_title(GTK_WINDOW(dialog)); // get window title, used as ID if (! pp) return; if (strlen(pp) < 2) return; strncpy0(wintitle,pp,64); // window title, < 64 chars. for (ii = 0; ii < Nzdposn; ii++) // search table for title if (strmatch(wintitle,zdposn[ii].wintitle)) break; if (ii == Nzdposn) { // not found - zdialog_destroy() will add zdpx = ppx + 0.8 * pww; // supply reasonable defaults 7.1 zdpy = ppx + 0.2 * phh; gtk_window_get_size(GTK_WINDOW(dialog),&xsize,&ysize); // use current size 7.1 } else { zdpx = ppx + 0.01 * zdposn[ii].xpos * pww; // set position for dialog window zdpy = ppy + 0.01 * zdposn[ii].ypos * phh; xsize = zdposn[ii].xsize; // set size 7.1 ysize = zdposn[ii].ysize; } gtk_window_move(GTK_WINDOW(dialog),zdpx,zdpy); gtk_window_resize(GTK_WINDOW(dialog),xsize,ysize); // 7.1 return; } else // "nn/nn" // position from caller { ii = sscanf(posn,"%f/%f",&xpos,&ypos); // parse "nn/nn" if (ii != 2) return; zdpx = ppx + 0.01 * xpos * pww; // position for dialog window zdpy = ppy + 0.01 * ypos * phh; gtk_window_move(GTK_WINDOW(dialog),zdpx,zdpy); return; } } // If the dialog window position is "save" then save // its position WRT parent or desktop for next use. // called by zdialog_destroy(). Private function. void zdialog_save_position(zdialog *zd) { using namespace zdposn_names; int ii, ppx, ppy, pww, phh, zdpx, zdpy; float xpos, ypos; int xsize, ysize; char wintitle[64], *pp; GtkWidget *parent, *dialog; if (! zdialog_valid(zd)) return; dialog = zd->widget[0].widget; // 7.1 if (! dialog) return; if (! gtk_widget_get_window(dialog)) return; gtk_window_get_position(GTK_WINDOW(dialog),&zdpx,&zdpy); // dialog window NW corner if (! zdpx && ! zdpy) return; // (0,0) ignore gtk_window_get_size(GTK_WINDOW(dialog),&xsize,&ysize); // window size 7.1 parent = zd->parent; // parent window if (! parent) { // no parent window ppx = ppy = 0; // use desktop pww = monitor_ww; phh = monitor_hh; } else { gtk_window_get_position(GTK_WINDOW(parent),&ppx,&ppy); // parent window NW corner gtk_window_get_size(GTK_WINDOW(parent),&pww,&phh); // parent window size } xpos = 100.0 * (zdpx - ppx) / pww; // dialog window relative position ypos = 100.0 * (zdpy - ppy) / phh; // (as percent of parent size) pp = (char *) gtk_window_get_title(GTK_WINDOW(dialog)); if (! pp) return; if (strlen(pp) < 2) return; if (strstr(pp,"/tmp/.mount")) return; // volatile appimage path names 7.4 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 Nzdposn++; // new entry } strcpy(zdposn[ii].wintitle,wintitle); // add window to table zdposn[ii].xpos = xpos; // save window position zdposn[ii].ypos = ypos; zdposn[ii].xsize = xsize; // and window size 7.1 zdposn[ii].ysize = ysize; return; } /********************************************************************************/ // Functions to save and restore zdialog user inputs // within an app session or across app sessions. namespace zdinputs_names { #define Nwmax zdmaxwidgets // max. widgets in a dialog #define Nzdmax 200 // max. zdialogs 6.6 #define ccmax1 100 // max. widget name length #define ccmax2 400 // max. widget data length struct zdinputs_t { char *zdtitle; // zdialog title int Nw; // no. of widgets char **wname; // list of widget names char **wdata; // list of widget data } zdinputs[Nzdmax]; // space for Nzdmax dialogs int Nzd = 0; // no. zdialogs in use } // Load zdialog input fields from its file (app startup) // or save zdialog input fields to its file (app shutdown). // Action is "load" or "save". // Number of zdialogs is returned. int zdialog_inputs(cchar *action) { using namespace zdinputs_names; char zdinputsfile[200], buff[ccmax2]; char zdtitle[ccmax1], wname[Nwmax][ccmax1], wdata[Nwmax][ccmax2]; char *pp, *pp1, *pp2, wdata2[ccmax2+50]; FILE *fid; int Nw, ii, jj, cc, cc1, cc2; snprintf(zdinputsfile,200,"%s/zdialog_inputs",zhomedir); // /home//.appname/zdialog_inputs if (strmatch(action,"load")) // load dialog input fields from its file { Nzd = 0; fid = fopen(zdinputsfile,"r"); // no file if (! fid) return 0; while (true) { pp = fgets_trim(buff,ccmax2,fid,1); // read next zdialog title record if (! pp) break; if (! strmatchN(pp,"zdialog == ",11)) continue; strncpy0(zdtitle,pp+11,ccmax1); // save new zdialog title pp = fgets_trim(buff,ccmax2,fid,1); // read widget count if (! pp) break; Nw = atoi(pp); if (Nw < 1 || Nw > Nwmax) { printz("*** zdialog_inputs() bad data: %s \n",zdtitle); continue; } for (ii = 0; ii < Nw; ii++) // read widget data recs { pp = fgets_trim(buff,ccmax2,fid,1); if (! pp) break; pp1 = pp; pp2 = strstr(pp1," =="); if (! pp2) break; // widget has no data cc1 = pp2 - pp1; pp1[cc1] = 0; pp2 += 3; if (*pp2 == ' ') pp2++; cc2 = strlen(pp2); if (cc1 < 1 || cc1 >= ccmax1) break; if (cc2 < 1) pp2 = (char *) ""; if (cc2 >= ccmax2) break; // do not copy large inputs strcpy(wname[ii],pp1); // save widget name and data strcpy(wdata2,pp2); repl_1str(wdata2,wdata[ii],"\\n","\n"); // replace "\n" with newline chars. } if (ii < Nw) { printz("*** zdialog_inputs() bad data: %s \n",zdtitle); continue; } if (Nzd == Nzdmax) { printz("*** zdialog_inputs() too many dialogs \n"); break; } zdinputs[Nzd].zdtitle = zstrdup(zdtitle); // save acculumated zdialog data zdinputs[Nzd].Nw = Nw; cc = Nw * sizeof(char *); zdinputs[Nzd].wname = (char **) zmalloc(cc); zdinputs[Nzd].wdata = (char **) zmalloc(cc); for (ii = 0; ii < Nw; ii++) { zdinputs[Nzd].wname[ii] = zstrdup(wname[ii]); zdinputs[Nzd].wdata[ii] = zstrdup(wdata[ii]); } Nzd++; } fclose(fid); return Nzd; } if (strmatch(action,"save")) // save dialog input fields to its file { fid = fopen(zdinputsfile,"w"); if (! fid) { printz("*** zdialog_inputs() cannot write file \n"); return 0; } for (ii = 0; ii < Nzd; ii++) { fprintf(fid,"zdialog == %s \n",zdinputs[ii].zdtitle); // zdialog == zdialog title Nw = zdinputs[ii].Nw; fprintf(fid,"%d \n",Nw); // widget count for (jj = 0; jj < Nw; jj++) { pp1 = zdinputs[ii].wname[jj]; // widget name == widget data pp2 = zdinputs[ii].wdata[jj]; repl_1str(pp2,wdata2,"\n","\\n"); // replace newline chars. with "\n" fprintf(fid,"%s == %s \n",pp1,wdata2); } fprintf(fid,"\n"); } fclose(fid); return Nzd; } printz("*** zdialog_inputs bad action: %s \n",action); return 0; } // Save dialog user input fields when a dialog is finished. // Called automatically by zdialog_free(). Private function. int zdialog_save_inputs(zdialog *zd) { using namespace zdinputs_names; char zdtitle[ccmax1], wname[ccmax1], wdata[ccmax2], *type; int ii, jj, Nw, cc; if (! zdialog_valid(zd)) return 0; if (! zd->saveinputs) return 0; // zdialog does not use this service strncpy0(zdtitle,zd->widget[0].data,ccmax1); // zdialog title is widget[0].data for (ii = 0; ii < Nzd; ii++) // find zdialog in zdinputs table if (strmatch(zdtitle,zdinputs[ii].zdtitle)) break; if (ii < Nzd) { // found zfree(zdinputs[ii].zdtitle); // delete obsolete zdinputs data for (jj = 0; jj < zdinputs[ii].Nw; jj++) { zfree(zdinputs[ii].wname[jj]); zfree(zdinputs[ii].wdata[jj]); } zfree(zdinputs[ii].wname); zfree(zdinputs[ii].wdata); Nzd--; // decr. zdialog count for (NOP; ii < Nzd; ii++) // pack down the rest zdinputs[ii] = zdinputs[ii+1]; } if (Nzd == Nzdmax) { printz("*** zdialog_save_inputs, too many zdialogs \n"); return 0; } ii = Nzd; // next zdinputs table entry for (Nw = 0, jj = 1; zd->widget[jj].type; jj++) { // count zdialog widgets type = (char *) zd->widget[jj].type; if (strstr("dialog hbox vbox hsep vsep frame scrwin" // skip non-input widgets 6.8 "label link button zbutton",type)) continue; Nw++; } if (! Nw) return 0; // no input widgets if (Nw > Nwmax) { printz("*** zdialog_inputs() bad data: %s \n",zdtitle); return 0; } zdinputs[ii].zdtitle = zstrdup(zdtitle); // set zdialog title cc = Nw * sizeof(char *); // allocate pointers for widgets zdinputs[ii].wname = (char **) zmalloc(cc); zdinputs[ii].wdata = (char **) zmalloc(cc); for (Nw = 0, jj = 1; zd->widget[jj].type; jj++) { // add widget names and data type = (char *) zd->widget[jj].type; if (strstr("dialog hbox vbox hsep vsep frame scrwin" // skip non-input widgets 6.8 "label link button zbutton",type)) continue; strncpy0(wname,zd->widget[jj].name,ccmax1); if (zd->widget[jj].data) strncpy0(wdata,zd->widget[jj].data,ccmax2); else strcpy(wdata,""); zdinputs[ii].wname[Nw] = zstrdup(wname); zdinputs[ii].wdata[Nw] = zstrdup(wdata); Nw++; } zdinputs[ii].Nw = Nw; // set widget count Nzd++; // add zdialog to end of zdinputs return 1; } // Restore user input fields from prior use of the same dialog. // Call this if wanted after zdialog is built and before it is run. // Override old user inputs with zdialog_stuff() where needed. int zdialog_restore_inputs(zdialog *zd) { using namespace zdinputs_names; char *zdtitle, *wname, *wdata; int ii, jj; zd->saveinputs = 1; // flag, save data at zdialog_free() zdtitle = (char *) zd->widget[0].data; // zdialog title for (ii = 0; ii < Nzd; ii++) // find zdialog in zdinputs if (strmatch(zdtitle,zdinputs[ii].zdtitle)) break; if (ii == Nzd) return 0; // not found for (jj = 0; jj < zdinputs[ii].Nw; jj++) { // stuff all saved widget data wname = zdinputs[ii].wname[jj]; wdata = zdinputs[ii].wdata[jj]; zdialog_put_data(zd,wname,wdata); } return 1; } /********************************************************************************/ // move a window to the mouse position // widget is a GtkWindow, which may or may not be realized void window_to_mouse(GtkWidget *window) // 6.8 { using namespace zfuncs; int px, py; gdk_device_get_position(mouse,&screen,&px,&py); // get mouse position gtk_window_move(GTK_WINDOW(window),px,py); return; } /********************************************************************************/ // popup window with scrolling text report // overhauled 7.0 // line numbers and line positions are zero based // open the report window with given title and pixel dimensions // Fheader - add optional non-scrolling header at top of report window // CBfunc - optional callback function: // CBfunc(GtkWidget *, int line, int posn, int KBkey) // ... - optional event buttons terminated with null: // [done] [cancel] [hide] [find] are processed here // others are passed to callback function (1st character) zdialog * popup_report_open(cchar *title, GtkWidget *parent, int ww, int hh, // 7.3 int Fheader, textwidget_callbackfunc_t CBfunc, ...) { int popup_report_dialog_event(zdialog *zd, cchar *event); va_list arglist; cchar *butn[6]; int ii, NB; zdialog *zd; GtkWidget *mHead, *mText; va_start(arglist,CBfunc); // get button args, if any for (ii = 0; ii < 5; ii++) { butn[ii] = va_arg(arglist,cchar *); if (! butn[ii]) break; } NB = ii; // no. buttons zd = zdialog_new(title,parent,null); if (Fheader) { // non-scrolling header zdialog_add_widget(zd,"text","header","dialog"); zdialog_add_widget(zd,"hsep","hsep","dialog"); } zdialog_add_widget(zd,"scrwin","scroll","dialog",0,"expand"); // scrolling text window for report zdialog_add_widget(zd,"text","text","scroll",0,"expand"); if (NB) { // optional event buttons zdialog_add_widget(zd,"hbox","hbbutn","dialog"); zdialog_add_widget(zd,"label","space","hbbutn",0,"expand"); for (ii = 0; ii < NB; ii++) zdialog_add_widget(zd,"button",butn[ii],"hbbutn",butn[ii],"space=5"); } zdialog_resize(zd,ww,hh); // show report dialog box zdialog_run(zd,popup_report_dialog_event,"save"); // keep window size and position 20.04 if (Fheader) { mHead = zdialog_widget(zd,"header"); // header initially invisible gtk_widget_set_visible(mHead,0); } mText = zdialog_widget(zd,"text"); // report text not editable gtk_text_view_set_editable(GTK_TEXT_VIEW(mText),0); gtk_widget_grab_focus(mText); textwidget_set_eventfunc(mText,CBfunc); // set mouse/KB event function zd->popup_report_CB = (void *) CBfunc; return zd; } // dialog event and completion function int popup_report_dialog_event(zdialog *zd, cchar *event) // 7.3 { textwidget_callbackfunc_t *CBfunc; GtkWidget *mText; static char findtext[40] = ""; int linem, line1, line2; zdialog *zdf; if (strmatch(event,"focus")) return 1; mText = zdialog_widget(zd,"text"); if (zd->zstat) { // [x] cancel zdialog_free(zd); return 1; } if (strmatch(event,"cancel")) { // [cancel] zdialog_free(zd); return 1; } if (strmatch(event,"done")) { // [done] zdialog_free(zd); return 1; } if (strmatch(event,"hide")) { // [hide] zdialog_show(zd,0); return 1; } if (strmatch(event,"find")) { // [find] zdf = zdialog_new("find text",zd->dialog,"find","cancel",0); // popup dialog to enter text zdialog_add_widget(zdf,"entry","text","dialog",findtext,"size=20"); zdialog_run(zdf,0,"mouse"); linem = -1; // no match line yet while (true) { zdialog_wait(zdf); if (zdf->zstat != 1) { // [cancel] zdialog_free(zdf); return 1; } zdf->zstat = 0; zdialog_fetch(zdf,"text",findtext,40); // get text popup_report_get_visible_lines(zd,line1,line2); // lines now visible if (linem < 0) linem = line1; // search from 1st visible line linem = popup_report_find(zd,findtext,linem); // search for text if (linem < 0) continue; // not found popup_report_scroll_top(zd,linem); // found, scroll to top linem++; // next search from line } } CBfunc = (textwidget_callbackfunc_t *) zd->popup_report_CB; // other event if (CBfunc) CBfunc(mText,-1,-1,*event); // pass to callback function (1st char.) return 1; } // write a non-scrolling header line void popup_report_header(zdialog *zd, int bold, cchar *format, ...) { va_list arglist; char message[1000]; GtkWidget *mHead; va_start(arglist,format); vsnprintf(message,999,format,arglist); va_end(arglist); mHead = zdialog_widget(zd,"header"); textwidget_append(mHead,bold,message); gtk_widget_set_visible(mHead,1); return; } // write a new text line at the end void popup_report_write(zdialog *zd, int bold, cchar *format, ...) { va_list arglist; char message[1000]; GtkWidget *mText; va_start(arglist,format); vsnprintf(message,999,format,arglist); va_end(arglist); mText = zdialog_widget(zd,"text"); textwidget_append(mText,bold,"%s",message); // speedup, append not append2 7.2 return; } // scroll window back to top line void popup_report_top(zdialog *zd) { GtkWidget *mText = zdialog_widget(zd,"text"); textwidget_scroll(mText,0); return; } // scroll window back to bottom line void popup_report_bottom(zdialog *zd) { GtkWidget *mText = zdialog_widget(zd,"text"); textwidget_scroll(mText,999999); return; } // clear the report window void popup_report_clear(zdialog *zd) { GtkWidget *mText = zdialog_widget(zd,"text"); textwidget_clear(mText); return; } // clear the report window from line to end void popup_report_clear(zdialog *zd, int line) { GtkWidget *mText = zdialog_widget(zd,"text"); textwidget_clear(mText,line); return; } // insert a new line after a given line void popup_report_insert(zdialog *zd, int bold, int line, cchar *format, ...) { va_list arglist; char message[1000]; GtkWidget *mText; va_start(arglist,format); vsnprintf(message,999,format,arglist); va_end(arglist); mText = zdialog_widget(zd,"text"); textwidget_insert(mText,bold,line,message); return; } // replace a given line void popup_report_replace(zdialog *zd, int bold, int line, cchar *format, ...) { va_list arglist; char message[1000]; GtkWidget *mText; va_start(arglist,format); vsnprintf(message,999,format,arglist); va_end(arglist); mText = zdialog_widget(zd,"text"); textwidget_replace(mText,bold,line,message); return; } // delete a given line void popup_report_delete(zdialog *zd, int line) { GtkWidget *mText = zdialog_widget(zd,"text"); textwidget_delete(mText,line); return; } // find first line of text containing characters matching input string // search is from line1 to end, then from 0 to line1-1 // returns first matching line or -1 if none // comparison is not case sensitive int popup_report_find(zdialog *zd, char *matchtext, int line1) { GtkWidget *mText = zdialog_widget(zd,"text"); return textwidget_find(mText,matchtext,line1); } // insert a pixbuf image after a given line void popup_report_insert_pixbuf(zdialog *zd, int line, GdkPixbuf *pixbuf) // 7.3 { GtkWidget *mText = zdialog_widget(zd,"text"); textwidget_insert_pixbuf(mText,line,pixbuf); return; } // scroll to bring a given line into the report window void popup_report_scroll(zdialog *zd, int line) { GtkWidget *mText = zdialog_widget(zd,"text"); textwidget_scroll(mText,line); return; } // scroll to bring a given line to the top of the report window void popup_report_scroll_top(zdialog *zd, int line) // 7.3 { GtkWidget *mText = zdialog_widget(zd,"text"); textwidget_scroll_top(mText,line); return; } // get the range of visible lines in the report window void popup_report_get_visible_lines(zdialog *zd, int &top, int &bott) { GtkWidget *mText = zdialog_widget(zd,"text"); textwidget_get_visible_lines(mText,top,bott); return; } // retrieve a given line and optionally strip the trailing \n char * popup_report_line(zdialog *zd, int line, int strip) { GtkWidget *mText = zdialog_widget(zd,"text"); return textwidget_line(mText,line,strip); } // retrieve the word starting at a given position in a given line char * popup_report_word(zdialog *zd, int line, int posn, cchar *dlims, char &end) { GtkWidget *mText = zdialog_widget(zd,"text"); return textwidget_word(mText,line,posn,dlims,end); } // highlight a given line of text void popup_report_highlight_line(zdialog *zd, int line) { GtkWidget *mText = zdialog_widget(zd,"text"); textwidget_highlight_line(mText,line); return; } // highlight the text at a given position and length in a given line void popup_report_highlight_word(zdialog *zd, int line, int posn, int cc) { GtkWidget *mText = zdialog_widget(zd,"text"); textwidget_highlight_word(mText,line,posn,cc); return; } // underline the text at a given position and length in a given line void popup_report_underline_word(zdialog *zd, int line, int posn, int cc) // 7.3 { GtkWidget *mText = zdialog_widget(zd,"text"); textwidget_underline_word(mText,line,posn,cc); return; } // bold the text at a given position and length in a given line void popup_report_bold_word(zdialog *zd, int line, int posn, int cc) // 7.3 { GtkWidget *mText = zdialog_widget(zd,"text"); textwidget_bold_word(mText,line,posn,cc); return; } // set font attributes for entire report // temp. kludge void popup_report_font_attributes(zdialog *zd) // 7.3 { GtkWidget *mText = zdialog_widget(zd,"text"); textwidget_font_attributes(mText); return; } // close report after given seconds (OK to leave it open until user closes) // also connected to window destroy signal (secs = 0) void popup_report_close(zdialog *zd, int secs) { void popup_report_timeout(zdialog *zd); if (secs < 1) { zdialog_destroy(zd); return; } g_timeout_add_seconds(secs,(GSourceFunc) popup_report_timeout,zd); return; } // private function for report timeout void popup_report_timeout(zdialog *zd) { zdialog_destroy(zd); return; } /********************************************************************************/ // execute a command and show the output in a scrolling popup window // returns: 0 = EOF 1 = command failure int popup_command(cchar *command, int ww, int hh, GtkWidget *parent, int top) { FILE *fid; char buff[1000], *pp; zdialog *zd; zd = popup_report_open(command,parent,ww,hh,0,0,"find","done",0); // 7.3 fid = popen(command,"r"); if (! fid) return 1; while (true) { pp = fgets_trim(buff,1000,fid); if (! pp) break; popup_report_write(zd,0,"%s\n",pp); } pclose(fid); if (top) popup_report_top(zd); // back to top of window 6.0 return 0; } /********************************************************************************/ // Display popup message box and wait for user acknowledgement. // May be called from a thread. // 7.1 // Messages are presented sequentially from main() and from threads. void zmessageACK(GtkWidget *parent, cchar *format, ... ) { va_list arglist; char message[1000]; cchar *posn = "mouse"; zdialog *zd; va_start(arglist,format); // format the message vsnprintf(message,999,format,arglist); va_end(arglist); printz("%s \n",message); // output to log file 7.4 if (! pthread_equal(pthread_self(),zfuncs::tid_main)) return; // caller is a thread, no GUI 7.4 zd = zdialog_new("",parent,"OK",null); // caller is main() 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); if (parent) posn = "parent"; else posn = "desktop"; zdialog_run(zd,0,posn); zdialog_present(zd); // help wayland 6.8 zdialog_wait(zd); zdialog_free(zd); return; } /********************************************************************************/ // display message box and wait for user Yes or No response // returns 1 or 0 int zmessageYN(GtkWidget *parent, cchar *format, ... ) { va_list arglist; char message[500]; cchar *where; zdialog *zd; int zstat; va_start(arglist,format); vsnprintf(message,500,format,arglist); va_end(arglist); printz("%s \n",message); // output to log file 7.4 if (parent) where = "parent"; // 7.4 else where = "desktop"; zd = zdialog_new("",parent,E2X("Yes"),E2X("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_run(zd,0,where); // 7.4 zdialog_present(zd); // help wayland 6.8 zstat = zdialog_wait(zd); zdialog_free(zd); if (zstat == 1) return 1; return 0; } /********************************************************************************/ // display message until timeout (can be forever) or user cancel // or caller kills it with zdialog_free() typedef struct { zdialog *zd; int uniqueID; } zdx_t; zdialog * zmessage_post(GtkWidget *parent, cchar *loc, int seconds, cchar *format, ... ) { int zmessage_post_timeout(zdx_t *zdx); va_list arglist; char message[1000], xmessage[1000]; static zdx_t zdx[100]; static int ii = 0; zdialog *zd; va_start(arglist,format); vsnprintf(message,1000,format,arglist); va_end(arglist); printz("%s \n",message); // output to log file 7.3 if (! pthread_equal(pthread_self(),zfuncs::tid_main)) { // called from thread 7.4 if (seconds < 1) seconds = 1; // disallow zero snprintf(xmessage,1000,"xmessage -center -timeout %d %s",seconds,message); shell_quiet(xmessage); return 0; } zd = zdialog_new("post",parent,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lab1","hb1",message,"space=5"); zdialog_set_decorated(zd,0); zdialog_run(zd,0,loc); // mouse position 6.8 zdialog_present(zd); // help wayland 6.8 if (parent) gtk_window_present(GTK_WINDOW(parent)); // return focus to parent 6.2 if (seconds) { if (ii < 99) ii++; // track unique zdialogs 6.2 else ii = 0; zdx[ii].zd = zd; zdx[ii].uniqueID = zd->uniqueID; g_timeout_add_seconds(seconds,(GSourceFunc) zmessage_post_timeout,&zdx[ii]); } return zd; } // same as above, but message is big, bold and red zdialog * zmessage_post_bold(GtkWidget *parent, cchar *loc, int seconds, cchar *format, ... ) { int zmessage_post_timeout(zdx_t *zdx); va_list arglist; char message[400], messagebold[460]; static zdx_t zdx[100]; static int ii = 0; zdialog *zd; va_start(arglist,format); vsnprintf(message,400,format,arglist); va_end(arglist); printz("%s \n",message); // output to log file 7.3 if (! pthread_equal(pthread_self(),zfuncs::tid_main)) return 0; // called from thread 7.4 snprintf(messagebold,460,"%s",message); zd = zdialog_new("post",parent,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lab1","hb1",messagebold,"space=5"); zdialog_set_decorated(zd,0); zdialog_run(zd,0,loc); // mouse position 6.8 zdialog_present(zd); // help wayland 6.8 if (parent) gtk_window_present(GTK_WINDOW(parent)); // return focus to parent 6.2 if (seconds) { if (ii < 99) ii++; // track unique zdialogs 6.2 else ii = 0; zdx[ii].zd = zd; zdx[ii].uniqueID = zd->uniqueID; g_timeout_add_seconds(seconds,(GSourceFunc) zmessage_post_timeout,&zdx[ii]); } return zd; } int zmessage_post_timeout(zdx_t *zdx) { zdialog *zd = zdx->zd; // check unique zdialog active 6.2 if (! zdialog_valid(zd,"post")) return 0; if (zd->uniqueID != zdx->uniqueID) return 0; zdialog_free(zd); return 0; } /********************************************************************************/ // get text input from a popup dialog // returned text is subject for zfree() // null is returned if user presses [cancel] button. char * zdialog_text(GtkWidget *parent, cchar *title, cchar *inittext) { zdialog *zd; int zstat; char *text; zd = zdialog_new(title,parent,"OK",E2X("cancel"),null); zdialog_add_widget(zd,"frame","fred","dialog"); zdialog_add_widget(zd,"edit","edit","fred"); if (inittext) zdialog_stuff(zd,"edit",inittext); zdialog_resize(zd,300,0); zdialog_set_modal(zd); zdialog_run(zd,0,"mouse"); zdialog_present(zd); // help wayland 6.8 zstat = zdialog_wait(zd); if (zstat == 1) text = (char *) zdialog_get_data(zd,"edit"); else text = 0; if (text) text = zstrdup(text); zdialog_free(zd); return text; } /********************************************************************************/ // Display a dialog with a message and 1-5 choice buttons. // Returns choice 1-N corresponding to button selected. // nn = zdialog_choose(parent, where, message, butt1, butt2, ... null) // 'where' is null: window manager decides // "mouse" put dialog at mouse position // "desktop" center dialog in desktop window // "parent" center dialog in parent window int zdialog_choose(GtkWidget *parent, cchar *where, cchar *message, ...) { zdialog *zd; va_list arglist; int ii, zstat, Nbutn; cchar *butn[6]; va_start(arglist,message); for (ii = 0; ii < 5; ii++) { butn[ii] = va_arg(arglist,cchar *); if (! butn[ii]) break; } Nbutn = ii; if (! Nbutn) zappcrash("zdialog_choose(), no buttons"); repeat: zd = zdialog_new("",parent,butn[0],butn[1],butn[2],butn[3],butn[4],null); zdialog_add_widget(zd,"hbox","hbmess","dialog","space=3"); zdialog_add_widget(zd,"label","labmess","hbmess",message,"space=5"); zdialog_set_modal(zd); zdialog_set_decorated(zd,0); // 7.3 zdialog_run(zd,null,where); // 6.8 zdialog_present(zd); // wayland 6.8 zstat = zdialog_wait(zd); zdialog_free(zd); if (zstat < 1) goto repeat; return zstat; } /********************************************************************************/ // functions to show popup text windows namespace poptext { char *ptext = 0; GtkWidget *popwin = 0; char *pcurrent = 0; #define GSFNORMAL GTK_STATE_FLAG_NORMAL } // timer function to show popup window after a given time int poptext_show(char *current) { using namespace poptext; if (current != pcurrent) return 0; if (popwin) gtk_widget_show_all(popwin); return 0; } // timer function to kill popup window after a given time int poptext_timeout(char *current) { using namespace poptext; if (current != pcurrent) return 0; if (popwin) gtk_widget_destroy(popwin); if (ptext) zfree(ptext); popwin = 0; ptext = 0; return 0; } // kill popup window unconditionally int poptext_killnow() { using namespace poptext; if (popwin) gtk_widget_destroy(popwin); if (ptext) zfree(ptext); popwin = 0; ptext = 0; return 0; } // Show a popup text message at a given absolute screen position. // Any prior popup will be killed and replaced. // If text == null, kill without replacement. // secs1 is time to delay before showing the popup. // secs2 is time to kill the popup after it is shown (0 = never). // This function returns immediately. void poptext_screen(cchar *text, int px, int py, float secs1, float secs2) // 6.8 { using namespace poptext; GtkWidget *label; int cc, millisec1, millisec2; poptext_killnow(); pcurrent++; // make current != pcurrent if (! text) return; cc = strlen(text) + 4; // construct popup window ptext = (char *) zmalloc(cc); // with caller's text *ptext = 0; strncatv(ptext,cc," ",text," ",null); // add extra spaces 7.1 popwin = gtk_window_new(GTK_WINDOW_POPUP); label = gtk_label_new(ptext); gtk_container_add(GTK_CONTAINER(popwin),label); gtk_window_move(GTK_WINDOW(popwin),px,py); if (secs1 > 0) { // delayed popup display millisec1 = secs1 * 1000; g_timeout_add(millisec1,(GSourceFunc) poptext_show,pcurrent); } else gtk_widget_show_all(popwin); // immediate display if (secs2 > 0) { // popup kill timer millisec2 = (secs1 + secs2) * 1000; g_timeout_add(millisec2,(GSourceFunc) poptext_timeout,pcurrent); } return; } // Show a popup text message at current mouse position + offsets. // 6.8 void poptext_mouse(cchar *text, int dx, int dy, float secs1, float secs2) { int mx, my; if (! text) { poptext_killnow(); return; } gdk_device_get_position(zfuncs::mouse,0,&mx,&my); // mouse screen position poptext_screen(text,mx+dx,my+dy,secs1,secs2); // add displacements return; } // Show a popup text message at the given window position. // 6.8 void poptext_window(GtkWindow *win, cchar *text, int dx, int dy, float secs1, float secs2) { int px, py; if (! text) { poptext_killnow(); return; } gtk_window_get_position(win,&px,&py); poptext_screen(text,px+dx,py+dy,secs1,secs2); return; } // Show a popup text message at the given widget position. // 6.8 void poptext_widget(GtkWidget *widget, cchar *text, int dx, int dy, float secs1, float secs2) { GdkWindow *win; int px, py; if (! text) { poptext_killnow(); return; } win = gtk_widget_get_window(widget); gdk_window_get_origin(win,&px,&py); poptext_screen(text,px+dx,py+dy,secs1,secs2); return; } /********************************************************************************/ // Show an image file in a popup window at mouse position. // Re-use most recent window or create a new one if Fnewin != 0. // Returns 0 if OK, +N otherwise. namespace popup_image_names { GtkWidget *window[10], *drawarea[10]; // up to 10 popup windows open char *filex[10], reqfull[10], isfull[10]; int Nval[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int Nw = 0; } int popup_image(cchar *file, GtkWindow *parent, int Fnewin, int size) // 6.0 { using namespace popup_image_names; int popup_image_draw(GtkWidget *, cairo_t *, int &Nw); int popup_image_scroll(GtkWidget *, GdkEvent *event, int &Nw); int popup_image_KBevent(GtkWidget *, GdkEventKey *event, int &Nw); int popup_image_mousebutt(GtkWidget *, GdkEvent *event, int &Nw); int popup_image_state_event(GtkWidget *, GdkEvent *, int &Nw); if (Fnewin) if (++Nw == 10) Nw = 0; // new window, re-use oldest up to 10 if (! Fnewin) while (Nw > 0 && window[Nw] == 0) Nw--; // else re-use latest still active if (window[Nw]) { gtk_widget_destroy(drawarea[Nw]); drawarea[Nw] = 0; zfree(filex[Nw]); filex[Nw] = 0; } else { window[Nw] = gtk_window_new(GTK_WINDOW_TOPLEVEL); // create new popup window if (! window[Nw]) return 1; if (! size) size = 512; gtk_window_set_default_size(GTK_WINDOW(window[Nw]),size,size); if (parent) { gtk_window_set_transient_for(GTK_WINDOW(window[Nw]),parent); gtk_window_set_destroy_with_parent(GTK_WINDOW(window[Nw]),1); // 7.2 } gtk_window_set_position(GTK_WINDOW(window[Nw]),GTK_WIN_POS_MOUSE); gtk_widget_freeze_child_notify(window[Nw]); // improves resize performance G_SIGNAL(window[Nw],"destroy",gtk_widget_destroyed,&window[Nw]); // set window = null if destroyed } filex[Nw] = zstrdup(file); drawarea[Nw] = gtk_drawing_area_new(); // new drawing area always required if (! drawarea[Nw]) return 2; gtk_container_add(GTK_CONTAINER(window[Nw]),drawarea[Nw]); reqfull[Nw] = isfull[Nw] = 0; // not fullscreen G_SIGNAL(window[Nw],"draw",popup_image_draw,&Nval[Nw]); gtk_widget_add_events(window[Nw],GDK_SCROLL_MASK); G_SIGNAL(window[Nw],"scroll-event",popup_image_scroll,&Nval[Nw]); // connect events 6.5 gtk_widget_add_events(window[Nw],GDK_KEY_PRESS_MASK); G_SIGNAL(window[Nw],"key-press-event",popup_image_KBevent,&Nval[Nw]); gtk_widget_add_events(window[Nw],GDK_BUTTON_RELEASE_MASK); G_SIGNAL(window[Nw],"button-release-event",popup_image_mousebutt,&Nval[Nw]); G_SIGNAL(window[Nw],"window-state-event",popup_image_state_event,&Nval[Nw]); gtk_widget_show_all(window[Nw]); return 0; } // resize image and repaint window when resized int popup_image_draw(GtkWidget *window, cairo_t *cr, int &nn) { using namespace popup_image_names; PIXBUF *pixb1, *pixb2; GError *gerror; int ww1, hh1, ww2, hh2; int sww, shh; double area; char *file; cchar *pp; file = filex[nn]; if (! file) return 1; pp = strrchr(file,'/'); // window title = file name gtk_window_set_title(GTK_WINDOW(window),pp+1); gerror = 0; pixb1 = gdk_pixbuf_new_from_file(file,&gerror); // load image file into pixbuf if (! pixb1) { printz("*** file: %s \n %s \n",file,gerror->message); return 1; } ww1 = gdk_pixbuf_get_width(pixb1); // image dimensions hh1 = gdk_pixbuf_get_height(pixb1); sww = monitor_ww; // 6.6 shh = monitor_hh; gtk_window_get_size(GTK_WINDOW(window),&ww2,&hh2); // current window dimensions area = ww2 * hh2; ww2 = sqrt(area * ww1 / hh1); // fit window to image, keeping same area hh2 = area / ww2; if (ww2 < sww && hh2 < shh) // prevent GTK resize event loop gtk_window_resize(GTK_WINDOW(window),ww2,hh2); pixb2 = gdk_pixbuf_scale_simple(pixb1,ww2,hh2,GDK_INTERP_BILINEAR); // rescale pixbuf to window if (! pixb2) return 1; gdk_cairo_set_source_pixbuf(cr,pixb2,0,0); // draw image cairo_paint(cr); g_object_unref(pixb1); g_object_unref(pixb2); return 1; } // respond to mouse scroll button and zoom window larger or smaller int popup_image_scroll(GtkWidget *window, GdkEvent *event, int &nn) { using namespace popup_image_names; int scroll, ww, hh; int sww, shh; double ff = 1.0; if (event->type == GDK_SCROLL) { // mouse wheel event scroll = ((GdkEventScroll *) event)->direction; if (scroll == GDK_SCROLL_UP) ff = 1.33333; if (scroll == GDK_SCROLL_DOWN) ff = 0.75; } gtk_window_get_size(GTK_WINDOW(window),&ww,&hh); // current window dimensions ww *= ff; // new dimensions hh *= ff; sww = monitor_ww; // 6.6 shh = monitor_hh; if (ww > sww || hh > shh) { // request > screen size, fullscreen 6.5 reqfull[nn] = 1; gtk_window_fullscreen(GTK_WINDOW(window)); return 1; } reqfull[nn] = 0; gtk_window_unfullscreen(GTK_WINDOW(window)); if (ww + hh > 500) gtk_window_resize(GTK_WINDOW(window),ww,hh); // rescale up or down else gtk_widget_destroy(window); // if very small, delete window return 1; } // respond to KB events F11 (fullscreen/unfullscreen) and Escape (destroy) int popup_image_KBevent(GtkWidget *window, GdkEventKey *event, int &nn) { using namespace popup_image_names; int KBkey = event->keyval; if (KBkey == GDK_KEY_Escape) gtk_widget_destroy(window); if (KBkey != GDK_KEY_F11) return 1; if (reqfull[nn]) { reqfull[nn] = 0; gtk_window_unfullscreen(GTK_WINDOW(window)); } else { reqfull[nn] = 1; gtk_window_fullscreen(GTK_WINDOW(window)); } return 1; } // respond to mouse button - destroy window int popup_image_mousebutt(GtkWidget *window, GdkEvent *event, int &nn) // 6.5 { gtk_widget_destroy(window); return 1; } // track window fullscreen state int popup_image_state_event(GtkWidget *window, GdkEvent *event, int &nn) // 6.5 { using namespace popup_image_names; int state = ((GdkEventWindowState *) event)->new_window_state; if (state & GDK_WINDOW_STATE_FULLSCREEN) isfull[nn] = 1; else isfull[nn] = 0; if (isfull[nn] != reqfull[nn]) { // compensate GTK bug: FIXME if (reqfull[nn]) gtk_window_fullscreen(GTK_WINDOW(window)); // the window fullscreens itself after else gtk_window_unfullscreen(GTK_WINDOW(window)); // being requested to unfullscreen } return 1; } /******************************************************************************** File chooser dialog for one or more files Action: "file" select an existing file "files" select multiple existing files "save" select an existing or new file "folder" select existing folder "folders" select multiple existing folders "create folder" select existing or new folder hidden if > 0, add button to toggle display of hidden files optional, default = 0 Returns a list of filespecs terminated with null. Memory for returned list and returned files are subjects for zfree(); *********************************************************************************/ // version for 1 file only: file, save, folder, create folder // returns one filespec or null // returned file is subject for zfree() char * zgetfile(cchar *title, GtkWindow *parent, cchar *action, cchar *initfile, int hidden) { if (! strmatchV(action,"file","save","folder","create folder",null)) zappcrash("zgetfile() call error: %s",action); char **flist = zgetfiles(title,parent,action,initfile,hidden); // parent added 6.1 if (! flist) return 0; char *file = *flist; zfree(flist); return file; } // version for 2 or more files // returns a list of filespecs (char **) terminated with null // returns null if canceled by user char ** zgetfiles(cchar *title, GtkWindow *parent, cchar *action, cchar *initfile, int hidden) { void zgetfile_preview(GtkWidget *dialog, GtkWidget *pvwidget); // private functions int zgetfile_KBkey(GtkWidget *dialog, GdkEventKey *event, int &fcdes); void zgetfile_newfolder(GtkFileChooser *dialog, void *); GtkFileChooserAction fcact = GTK_FILE_CHOOSER_ACTION_OPEN; GtkWidget *dialog; PIXBUF *thumbnail; GtkWidget *pvwidget = gtk_image_new(); GSList *gslist = 0; cchar *button1 = 0, *buttxx = 0; char *pdir, *pfile; int ii, err, NF, setfname = 0; int fcstat, bcode = 0, hide = 1; int fcdes = 0; char *file1, *file2, **flist = 0; STATB fstat; if (strmatch(action,"file")) { fcact = GTK_FILE_CHOOSER_ACTION_OPEN; button1 = E2X("choose file"); } else if (strmatch(action,"files")) { fcact = GTK_FILE_CHOOSER_ACTION_OPEN; button1 = E2X("choose files"); } else if (strmatch(action,"save")) { fcact = GTK_FILE_CHOOSER_ACTION_SAVE; button1 = E2X("Save"); setfname = 1; } else if (strmatch(action,"folder")) { fcact = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; button1 = E2X("choose folder"); } else if (strmatch(action,"folders")) { fcact = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; button1 = E2X("choose folders"); } else if (strmatch(action,"create folder")) { fcact = GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER; button1 = E2X("create folder"); setfname = 1; } else zappcrash("zgetfiles() call error: %s",action); if (hidden) { buttxx = E2X("hidden"); bcode = 103; } dialog = gtk_file_chooser_dialog_new(title, parent, fcact, // create file selection dialog button1, GTK_RESPONSE_ACCEPT, // parent added 6.1 E2X("Cancel"), GTK_RESPONSE_CANCEL, buttxx, bcode, null); gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog),pvwidget); G_SIGNAL(dialog,"update-preview",zgetfile_preview,pvwidget); // create preview for selected file G_SIGNAL(dialog,"key-press-event",zgetfile_KBkey,&fcdes); // respond to special KB keys gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_MOUSE); // put dialog at mouse position gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog),0); // default: no show hidden if (strmatch(action,"save")) // overwrite confirmation gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog),1); if (strmatch(action,"files") || strmatch(action,"folders")) gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog),1); // select multiple files or folders if (initfile) { // pre-select filespec err = stat(initfile,&fstat); if (err) { pdir = zstrdup(initfile); // non-existent file pfile = strrchr(pdir,'/'); if (pfile && pfile > pdir) { *pfile++ = 0; // set folder name gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),pdir); } if (setfname) { // set new file name if (! pfile) pfile = (char *) initfile; // 6.0 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog),pfile); } zfree(pdir); } else if (S_ISREG(fstat.st_mode)) // select given file gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog),initfile); else if (S_ISDIR(fstat.st_mode)) // select given folder gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),initfile); } if (initfile) { thumbnail = get_thumbnail(initfile,256); // preview for initial file 6.0 if (thumbnail) { gtk_image_set_from_pixbuf(GTK_IMAGE(pvwidget),thumbnail); gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(dialog),1); g_object_unref(thumbnail); } else gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(dialog),0); } gtk_widget_show_all(dialog); while (true) { fcstat = gtk_dialog_run(GTK_DIALOG(dialog)); // run dialog, get status button if (fcstat == 103) { // show/hide hidden files hide = 1 - hide; gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog),hide); continue; } else if (fcstat == GTK_RESPONSE_ACCEPT) { gslist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); if (! gslist) continue; NF = g_slist_length(gslist); // no. selected files flist = (char **) zmalloc((NF+1)*sizeof(char *)); // allocate returned list for (ii = 0; ii < NF; ii++) { // process selected files file1 = (char *) g_slist_nth_data(gslist,ii); file2 = zstrdup(file1); // re-allocate memory flist[ii] = file2; g_free(file1); } flist[ii] = 0; // EOL marker break; } else break; // user bailout } if (gslist) g_slist_free(gslist); // return selected file(s) if (! fcdes) gtk_widget_destroy(dialog); // destroy if not already 6.5 return flist; } // zgetfile private function - get preview images for image files void zgetfile_preview(GtkWidget *dialog, GtkWidget *pvwidget) { PIXBUF *thumbnail; char *filename; filename = gtk_file_chooser_get_preview_filename(GTK_FILE_CHOOSER(dialog)); if (! filename) { gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(dialog),0); return; } thumbnail = get_thumbnail(filename,256); // 256x256 pixels g_free(filename); if (thumbnail) { gtk_image_set_from_pixbuf(GTK_IMAGE(pvwidget),thumbnail); gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(dialog),1); g_object_unref(thumbnail); } else gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(dialog),0); return; } // zgetfile private function - KB functions int zgetfile_KBkey(GtkWidget *dialog, GdkEventKey *event, int &fcdes) { int KBkey = event->keyval; if (KBkey == GDK_KEY_F1) { // F1 = help KBevent(event); return 1; } if (KBkey == GDK_KEY_Escape) { // escape = cancel 6.5 gtk_widget_destroy(dialog); fcdes = 1; return 1; } return 0; } /********************************************************************************/ // select a folder (or create a new folder) // returns location (pathname) of selected or created folder. // returned location is subject for zfree(). char * zgetfolder(cchar *title, GtkWindow *parent, cchar *initfolder) { GtkWidget *dialog; GtkFileChooser *chooser; int nn; char *pp1, *pp2 = null; dialog = gtk_file_chooser_dialog_new(title, parent, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, E2X("Cancel"), GTK_RESPONSE_CANCEL, // E2X added 7.1 E2X("Open"), GTK_RESPONSE_ACCEPT, NULL); chooser = GTK_FILE_CHOOSER(dialog); gtk_file_chooser_set_filename(chooser, initfolder); nn = gtk_dialog_run(GTK_DIALOG(dialog)); if (nn != GTK_RESPONSE_ACCEPT) { gtk_widget_destroy(dialog); return null; } pp1 = gtk_file_chooser_get_filename(chooser); if (pp1) { pp2 = zstrdup(pp1); g_free(pp1); } gtk_widget_destroy(dialog); return pp2; } /******************************************************************************** print_image_file(GtkWidget *parent, cchar *imagefile) Print an image file using the printer, paper, orientation, margins, and scale set by the user. HPLIP problem: Setting paper size was made less flexible. GtkPrintSettings paper size must agree with the one in the current printer setup. This can only be set in the printer setup dialog, not in the application. Also the print size (width, height) comes from the chosen paper size and cannot be changed in the application. Print margins can be changed to effect printing a smaller or shifted image on a larger paper size. *********************************************************************************/ namespace print_image { #define MM GTK_UNIT_MM #define INCH GTK_UNIT_INCH #define PRINTOP GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG #define PORTRAIT GTK_PAGE_ORIENTATION_PORTRAIT #define LANDSCAPE GTK_PAGE_ORIENTATION_LANDSCAPE #define QUALITY GTK_PRINT_QUALITY_HIGH GtkWidget *parent = 0; GtkPageSetup *priorpagesetup = 0; GtkPageSetup *pagesetup; GtkPrintSettings *printsettings = 0; GtkPrintOperation *printop; GtkPageOrientation orientation = PORTRAIT; PIXBUF *pixbuf; cchar *printer = 0; int landscape = 0; // true if landscape double width = 21.0, height = 29.7; // paper size, CM (default A4 portrait) double margins[4] = { 0.5, 0.5, 0.5, 0.5 }; // margins, CM (default 0.5) double imagescale = 100; // image print scale, percent double pwidth, pheight; // printed image size int page_setup(); int margins_setup(); int margins_dialog_event(zdialog *zd, cchar *event); void get_printed_image_size(); void print_page(GtkPrintOperation *, GtkPrintContext *, int page); } // user callable function to set paper, margins, scale, and then print void print_image_file(GtkWidget *pwin, cchar *imagefile) { using namespace print_image; GtkPrintOperationResult printstat; GError *gerror = 0; int err; parent = pwin; // save parent window pixbuf = gdk_pixbuf_new_from_file(imagefile,&gerror); // read image file if (! pixbuf) { zmessageACK(null,gerror->message); return; } err = page_setup(); // select size and orientation if (err) return; err = margins_setup(); // set margins and scale if (err) return; printop = gtk_print_operation_new(); // print operation gtk_print_operation_set_default_page_setup(printop,pagesetup); gtk_print_operation_set_print_settings(printop,printsettings); gtk_print_operation_set_n_pages(printop,1); g_signal_connect(printop,"draw-page",G_CALLBACK(print_page),0); // start print printstat = gtk_print_operation_run(printop,PRINTOP,0,0); if (printstat == GTK_PRINT_OPERATION_RESULT_ERROR) { gtk_print_operation_get_error(printop,&gerror); zmessageACK(null,gerror->message); } g_object_unref(printop); return; } // draw the graphics for the print page // rescale with cairo void print_image::print_page(GtkPrintOperation *printop, GtkPrintContext *printcontext, int page) { using namespace print_image; cairo_t *cairocontext; double iww, ihh, pww, phh, scale; pww = gtk_print_context_get_width(printcontext); // print context size, pixels phh = gtk_print_context_get_height(printcontext); iww = gdk_pixbuf_get_width(pixbuf); // original image size ihh = gdk_pixbuf_get_height(pixbuf); scale = pww / iww; // rescale to fit page if (phh / ihh < scale) scale = phh / ihh; cairocontext = gtk_print_context_get_cairo_context(printcontext); // use cairo to rescale cairo_translate(cairocontext,0,0); cairo_scale(cairocontext,scale,scale); gdk_cairo_set_source_pixbuf(cairocontext,pixbuf,0,0); cairo_paint(cairocontext); return; } // Do a print paper format selection, after which the page width, height // and orientation are available to the caller. Units are CM. // (paper width and height are reversed for landscape orientation) int print_image::page_setup() { using namespace print_image; char printsettingsfile[200], pagesetupfile[200]; snprintf(printsettingsfile,200,"%s/printsettings",zhomedir); snprintf(pagesetupfile,200,"%s/pagesetup",zhomedir); if (! printsettings) { // start with prior print settings printsettings = gtk_print_settings_new_from_file(printsettingsfile,0); if (! printsettings) printsettings = gtk_print_settings_new(); } if (! priorpagesetup) { // start with prior page setup priorpagesetup = gtk_page_setup_new_from_file(pagesetupfile,0); if (! priorpagesetup) priorpagesetup = gtk_page_setup_new(); } pagesetup = gtk_print_run_page_setup_dialog // select printer, paper, orientation (GTK_WINDOW(parent),priorpagesetup,printsettings); // user cancel cannot be detected g_object_unref(priorpagesetup); // save for next call priorpagesetup = pagesetup; orientation = gtk_print_settings_get_orientation(printsettings); // save orientation if (orientation == LANDSCAPE) landscape = 1; else landscape = 0; gtk_print_settings_set_quality(printsettings,QUALITY); // set high quality 300 dpi gtk_print_settings_set_resolution(printsettings,300); gtk_print_settings_to_file(printsettings,printsettingsfile,0); // save print settings to file gtk_page_setup_to_file(pagesetup,pagesetupfile,0); // save print settings to file return 0; } // Optionally set the print margins and print scale. // If canceled the margins are zero (or printer-dependent minimum) // and the scale is 100% (fitting the paper and margins). int print_image::margins_setup() { using namespace print_image; zdialog *zd; int zstat; /*** __________________________________________________ | [x] (-) [_] Margins | | | | Margins Top Bottom Left Right | | CM [ 0.50 ] [ 0.50 ] [ 0.50 ] [ 0.50 ] | | Inch [ 0.20 ] [ 0.20 ] [ 0.20 ] [ 0.20 ] | | | | image scale [ 80 ] percent | | | | image width height | | CM xx.x xx.x | | Inch xx.x xx.x | | [done] [cancel] | |__________________________________________________| ***/ zd = zdialog_new(E2X("Margins"),parent,E2X("done"),E2X("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",E2X("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",E2X("Top")); zdialog_add_widget(zd,"zspin","mtopcm","vbtop","0|10|0.01|0"); zdialog_add_widget(zd,"zspin","mtopin","vbtop","0|4|0.01|0"); zdialog_add_widget(zd,"label","labbot","vbbottom",E2X("Bottom")); zdialog_add_widget(zd,"zspin","mbottcm","vbbottom","0|10|0.01|0"); zdialog_add_widget(zd,"zspin","mbottin","vbbottom","0|4|0.01|0"); zdialog_add_widget(zd,"label","lableft","vbleft",E2X("Left")); zdialog_add_widget(zd,"zspin","mleftcm","vbleft","0|10|0.01|0"); zdialog_add_widget(zd,"zspin","mleftin","vbleft","0|4|0.01|0"); zdialog_add_widget(zd,"label","labright","vbright",E2X("Right")); zdialog_add_widget(zd,"zspin","mrightcm","vbright","0|10|0.01|0"); zdialog_add_widget(zd,"zspin","mrightin","vbright","0|4|0.01|0"); zdialog_add_widget(zd,"hbox","hbscale","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labscale","hbscale",E2X("image scale"),"space=5"); zdialog_add_widget(zd,"zspin","scale","hbscale","5|100|1|100"); zdialog_add_widget(zd,"label","labpct","hbscale",E2X("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",E2X("Image")); zdialog_add_widget(zd,"label","labcm","vbunit","CM"); zdialog_add_widget(zd,"label","labinch","vbunit","Inch"); zdialog_add_widget(zd,"label","labwidth","vbwidth",E2X("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",E2X("Height")); zdialog_add_widget(zd,"label","labhcm","vbheight","xx.x"); zdialog_add_widget(zd,"label","labhin","vbheight","xx.x"); zdialog_restore_inputs(zd); // recall prior settings zdialog_fetch(zd,"mtopcm",margins[0]); zdialog_fetch(zd,"mbottcm",margins[1]); zdialog_fetch(zd,"mleftcm",margins[2]); zdialog_fetch(zd,"mrightcm",margins[3]); zdialog_fetch(zd,"scale",imagescale); get_printed_image_size(); zdialog_stuff(zd,"labwcm",pwidth,"%.2f"); // update image size in dialog zdialog_stuff(zd,"labhcm",pheight,"%.2f"); zdialog_stuff(zd,"labwin",pwidth/2.54,"%.2f"); zdialog_stuff(zd,"labhin",pheight/2.54,"%.2f"); gtk_page_setup_set_top_margin(pagesetup,10*margins[0],MM); // set page margins gtk_page_setup_set_bottom_margin(pagesetup,10*margins[1],MM); // (cm to mm units) gtk_page_setup_set_left_margin(pagesetup,10*margins[2],MM); gtk_page_setup_set_right_margin(pagesetup,10*margins[3],MM); gtk_print_settings_set_scale(printsettings,imagescale); // set image print scale % zdialog_run(zd,margins_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_free(zd); // kill dialog if (zstat == 1) return 0; return 1; } // dialog event function // save user margin and scale changes // recompute print image size int print_image::margins_dialog_event(zdialog *zd, cchar *event) { using namespace print_image; double temp; if (strmatch(event,"escape")) { // escape = cancel 6.5 zd->zstat = 2; return 1; } if (strmatch(event,"mtopcm")) { // get cm inputs and set inch values zdialog_fetch(zd,"mtopcm",margins[0]); zdialog_stuff(zd,"mtopin",margins[0]/2.54); } if (strmatch(event,"mbottcm")) { zdialog_fetch(zd,"mbottcm",margins[1]); zdialog_stuff(zd,"mbottin",margins[1]/2.54); } if (strmatch(event,"mleftcm")) { zdialog_fetch(zd,"mleftcm",margins[2]); zdialog_stuff(zd,"mleftin",margins[2]/2.54); } if (strmatch(event,"mrightcm")) { zdialog_fetch(zd,"mrightcm",margins[3]); zdialog_stuff(zd,"mrightin",margins[3]/2.54); } if (strmatch(event,"mtopin")) { // get inch inputs and set cm values zdialog_fetch(zd,"mtopin",temp); margins[0] = temp * 2.54; zdialog_stuff(zd,"mtopcm",margins[0]); } if (strmatch(event,"mbottin")) { zdialog_fetch(zd,"mbottin",temp); margins[1] = temp * 2.54; zdialog_stuff(zd,"mbottcm",margins[1]); } if (strmatch(event,"mleftin")) { zdialog_fetch(zd,"mleftin",temp); margins[2] = temp * 2.54; zdialog_stuff(zd,"mleftcm",margins[2]); } if (strmatch(event,"mrightin")) { zdialog_fetch(zd,"mrightin",temp); margins[3] = temp * 2.54; zdialog_stuff(zd,"mrightcm",margins[3]); } zdialog_fetch(zd,"scale",imagescale); // get image scale get_printed_image_size(); zdialog_stuff(zd,"labwcm",pwidth,"%.2f"); // update image size in dialog zdialog_stuff(zd,"labhcm",pheight,"%.2f"); zdialog_stuff(zd,"labwin",pwidth/2.54,"%.2f"); zdialog_stuff(zd,"labhin",pheight/2.54,"%.2f"); gtk_page_setup_set_top_margin(pagesetup,10*margins[0],MM); // set page margins gtk_page_setup_set_bottom_margin(pagesetup,10*margins[1],MM); // (cm to mm units) gtk_page_setup_set_left_margin(pagesetup,10*margins[2],MM); gtk_page_setup_set_right_margin(pagesetup,10*margins[3],MM); gtk_print_settings_set_scale(printsettings,imagescale); // set image print scale % return 1; } // compute printed image size based on paper size, // orientation, margins, and scale (percent) void print_image::get_printed_image_size() { using namespace print_image; double iww, ihh, pww, phh, scale; pww = 0.1 * gtk_page_setup_get_paper_width(pagesetup,MM); // get paper size phh = 0.1 * gtk_page_setup_get_paper_height(pagesetup,MM); // (mm to cm units) pww = pww - margins[2] - margins[3]; // reduce for margins phh = phh - margins[0] - margins[1]; // bugfix 6.2 pww = pww / 2.54 * 300; // convert to dots @ 300 dpi phh = phh / 2.54 * 300; iww = gdk_pixbuf_get_width(pixbuf); // original image size, pixels ihh = gdk_pixbuf_get_height(pixbuf); scale = pww / iww; // rescale image to fit page if (phh / ihh < scale) scale = phh / ihh; scale = scale * 0.01 * imagescale; // adjust for user scale setting pwidth = iww * scale / 300 * 2.54; // dots to cm pheight = ihh * scale / 300 * 2.54; return; } /********************************************************************************/ // connect a user callback function to a drag-drop source widget void drag_drop_source(GtkWidget *widget, drag_drop_source_func ufunc) // 6.5 { void drag_drop_source2(GtkWidget *, GdkDragContext *, void *ufunc); void drag_drop_source3(GtkWidget *, GdkDragContext *, GtkSelectionData *, int, int, void *ufunc); gtk_drag_source_set(widget,GDK_BUTTON1_MASK,null,0,GDK_ACTION_COPY); gtk_drag_source_add_text_targets(widget); gtk_drag_source_add_image_targets(widget); G_SIGNAL(widget, "drag-begin", drag_drop_source2, ufunc); G_SIGNAL(widget, "drag-data-get", drag_drop_source3, ufunc); return; } // private function for "drag-begin" signal void drag_drop_source2(GtkWidget *widget, GdkDragContext *context, void *ufunc) { drag_drop_source_func *ufunc2; GdkPixbuf *pixbuf; GError *gerror = 0; char *file = 0; ufunc2 = (drag_drop_source_func *) ufunc; file = ufunc2(); if (! file) goto cancel; pixbuf = gdk_pixbuf_new_from_file_at_size(file,128,128,&gerror); if (! pixbuf) { if (gerror) printz("%s \n",gerror->message); return; } gtk_drag_set_icon_pixbuf(context,pixbuf,64,64); // hot spot is middle of image 6.5 return; cancel: printz("drag canceled \n"); return; } // private function for "drag-data-get" signal void drag_drop_source3(GtkWidget *widget, GdkDragContext *context, GtkSelectionData *data, int, int, void *ufunc) { drag_drop_source_func *ufunc2; char *file = 0; // char *files[2] = { file, null }; ufunc2 = (drag_drop_source_func *) ufunc; file = ufunc2(); if (! file) goto cancel; gtk_selection_data_set_text(data,file,-1); // drops text // gtk_selection_data_set_uris(data,files); // does nothing FIXME return; cancel: printz("drag canceled \n"); return; } // connect a user callback function to a drag-drop destination widget void drag_drop_dest(GtkWidget *widget, drag_drop_dest_func *ufunc) { int drag_drop_dest2(GtkWidget *, GdkDragContext *, int, int, void *, int, int time, void *); int drag_drop_dest3(GtkWidget *, void *, int, int, int, void *); int drag_drop_dest4(GtkWidget *, void *, int, void *); gtk_drag_dest_set(widget,GTK_DEST_DEFAULT_ALL,null,0,GDK_ACTION_COPY); gtk_drag_dest_add_text_targets(widget); G_SIGNAL(widget, "drag-data-received", drag_drop_dest2, ufunc); G_SIGNAL(widget, "drag-motion", drag_drop_dest3, ufunc); G_SIGNAL(widget, "drag-leave", drag_drop_dest4, ufunc); // 7.1 return; } // private function for "drag-data-received" signal // get dropped file, clean escapes, pass to user function // passed filespec is subject for zfree() int drag_drop_dest2(GtkWidget *, GdkDragContext *context, int mpx, int mpy, void *sdata, int, int time, void *ufunc) { char * drag_drop_unescape(cchar *escaped_string); drag_drop_dest_func *ufunc2; char *text, *text2, *file, *file2; int cc; text = (char *) gtk_selection_data_get_data((GtkSelectionData *) sdata); ufunc2 = (drag_drop_dest_func *) ufunc; if (strstr(text,"file://")) // text is a filespec { file = zstrdup(text+7); // get rid of junk added by GTK cc = strlen(file); while (file[cc-1] < ' ') cc--; file[cc] = 0; file2 = drag_drop_unescape(file); // clean %xx escapes from Nautilus zfree(file); ufunc2(mpx,mpy,file2); // pass file to user function } else // text is text { text2 = zstrdup(text); ufunc2(mpx,mpy,text2); } gtk_drag_finish(context,1,0,time); return 1; } // private function for "drag-motion" signal // 6.5 // pass mouse position to user function during drag int drag_drop_dest3(GtkWidget *, void *, int mpx, int mpy, int, void *ufunc) { drag_drop_dest_func *ufunc2; ufunc2 = (drag_drop_dest_func *) ufunc; if (! ufunc2) return 0; ufunc2(mpx,mpy,null); return 0; } // private function for "drag-leave" signal // 7.1 // pass mouse position (0,0) to user function int drag_drop_dest4(GtkWidget *, void *, int, void *ufunc) { drag_drop_dest_func *ufunc2; ufunc2 = (drag_drop_dest_func *) ufunc; if (! ufunc2) return 0; ufunc2(0,0,null); return 0; } // private function // Clean %xx escapes from strange Nautilus drag-drop file names char * drag_drop_unescape(cchar *inp) { int drag_drop_convhex(char ch); char inch, *out, *outp; int nib1, nib2; out = (char *) zmalloc(strlen(inp)+1); outp = out; while ((inch = *inp++)) { if (inch == '%') { nib1 = drag_drop_convhex(*inp++); nib2 = drag_drop_convhex(*inp++); *outp++ = nib1 << 4 | nib2; } else *outp++ = inch; } *outp = 0; return out; } // private function - convert character 0-F to number 0-15 int drag_drop_convhex(char ch) { if (ch >= '0' && ch <= '9') return ch - '0'; if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; return ch; } /******************************************************************************** Miscellaneous GDK/GTK functions *********************************************************************************/ // Get thumbnail image for given image file. // Returned thumbnail belongs to caller: g_object_unref() is necessary. PIXBUF * get_thumbnail(cchar *fpath, int size) { PIXBUF *thumbpxb; GError *gerror = 0; int err; char *bpath; STATB statf; err = stat(fpath,&statf); // fpath status info if (err) return 0; if (S_ISDIR(statf.st_mode)) { // if folder, return folder image bpath = (char *) zmalloc(500); *bpath = 0; strncatv(bpath,499,zimagedir,"/folder.png",null); thumbpxb = gdk_pixbuf_new_from_file_at_size(bpath,size,size,&gerror); zfree(bpath); return thumbpxb; } thumbpxb = gdk_pixbuf_new_from_file_at_size(fpath,size,size,&gerror); return thumbpxb; // return pixbuf to caller } // make a cursor from a graphic file in application folder // (see initz_appfiles()). GdkCursor * zmakecursor(cchar *imagefile) { GError *gerror = 0; PIXBUF *pixbuf; GdkDisplay *display; GdkCursor *cursor = 0; char imagepath[200]; display = gdk_display_get_default(); *imagepath = 0; strncatv(imagepath,199,zimagedir,"/",imagefile,null); pixbuf = gdk_pixbuf_new_from_file(imagepath,&gerror); if (pixbuf && display) cursor = gdk_cursor_new_from_pixbuf(display,pixbuf,0,0); else printz("*** %s \n",gerror->message); return cursor; } /******************************************************************************** PIXBUF * gdk_pixbuf_rotate(PIXBUF *pixbuf, float angle, int acolor) Rotate a pixbuf through an arbitrary angle (degrees). The returned image has the same size as the original, but the pixbuf envelope is increased to accommodate 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 pixbuf output pixel = black continue for 4 input pixels based at (px0,py0) = (int(px1),int(py1)) compute overlap (0 to 1) with (px1,py1) sum RGB values * overlap output aggregate RGB to pixel (px2,py2) Benchmark: rotate 7 megapixel image 10 degrees 0.31 secs. 3.3 GHz Core i5 ***/ PIXBUF * gdk_pixbuf_rotate(PIXBUF *pixbuf1, float angle, int acolor) { typedef unsigned char *pixel; // 3 RGB values, 0-255 each PIXBUF *pixbuf2; GDKCOLOR color; int nch, nbits, alpha; int ww1, hh1, rs1, ww2, hh2, rs2; int px2, py2, px0, py0; pixel ppix1, ppix2, pix0, pix1, pix2, pix3; float px1, py1; float f0, f1, f2, f3, red, green, blue, tran = 0; float a, b, d, e, ww15, hh15, ww25, hh25; float PI = 3.141593; nch = gdk_pixbuf_get_n_channels(pixbuf1); nbits = gdk_pixbuf_get_bits_per_sample(pixbuf1); if (nch < 3) return 0; // must have 3+ channels (colors) if (nbits != 8) return 0; // must be 8 bits per channel color = gdk_pixbuf_get_colorspace(pixbuf1); // get input pixbuf1 attributes alpha = gdk_pixbuf_get_has_alpha(pixbuf1); ww1 = gdk_pixbuf_get_width(pixbuf1); hh1 = gdk_pixbuf_get_height(pixbuf1); rs1 = gdk_pixbuf_get_rowstride(pixbuf1); while (angle < -180) angle += 360; // normalize, -180 to +180 while (angle > 180) angle -= 360; angle = angle * PI / 180; // radians, -PI to +PI if (fabsf(angle) < 0.001) { pixbuf2 = gdk_pixbuf_copy(pixbuf1); // angle is zero within my precision return pixbuf2; } ww2 = int(ww1*fabsf(cosf(angle)) + hh1*fabsf(sinf(angle))); // rectangle containing rotated image hh2 = int(ww1*fabsf(sinf(angle)) + hh1*fabsf(cosf(angle))); pixbuf2 = gdk_pixbuf_new(color,alpha,nbits,ww2,hh2); // create output pixbuf2 if (! pixbuf2) return 0; rs2 = gdk_pixbuf_get_rowstride(pixbuf2); ppix1 = gdk_pixbuf_get_pixels(pixbuf1); // input pixel array ppix2 = gdk_pixbuf_get_pixels(pixbuf2); // output pixel array ww15 = 0.5 * ww1; hh15 = 0.5 * hh1; ww25 = 0.5 * ww2; hh25 = 0.5 * hh2; a = cosf(angle); // affine transform coefficients b = sinf(angle); d = - sinf(angle); e = cosf(angle); for (py2 = 0; py2 < hh2; py2++) // loop through output pixels for (px2 = 0; px2 < ww2; px2++) { px1 = a * (px2 - ww25) + b * (py2 - hh25) + ww15; // (px1,py1) = corresponding py1 = d * (px2 - ww25) + e * (py2 - hh25) + hh15; // point within input pixels px0 = int(px1); // pixel containing (px1,py1) py0 = int(py1); if (px1 < 0 || px0 >= ww1-1 || py1 < 0 || py0 >= hh1-1) { // if outside input pixel array pix2 = ppix2 + py2 * rs2 + px2 * nch; // output is acolor pix2[0] = pix2[1] = pix2[2] = acolor; continue; } pix0 = ppix1 + py0 * rs1 + px0 * nch; // 4 input pixels based at (px0,py0) pix1 = pix0 + rs1; pix2 = pix0 + nch; pix3 = pix0 + rs1 + nch; f0 = (px0+1 - px1) * (py0+1 - py1); // overlap of (px1,py1) f1 = (px0+1 - px1) * (py1 - py0); // in each of the 4 pixels f2 = (px1 - px0) * (py0+1 - py1); f3 = (px1 - px0) * (py1 - py0); red = f0 * pix0[0] + f1 * pix1[0] + f2 * pix2[0] + f3 * pix3[0]; // sum the weighted inputs green = f0 * pix0[1] + f1 * pix1[1] + f2 * pix2[1] + f3 * pix3[1]; blue = f0 * pix0[2] + f1 * pix1[2] + f2 * pix2[2] + f3 * pix3[2]; if (alpha) tran = f0 * pix0[3] + f1 * pix1[3] + f2 * pix2[3] + f3 * pix3[3]; // 4th color = alpha if (red == acolor && green == acolor && blue == acolor) { // avoid acolor in image if (blue == 0) blue = 1; else blue--; } pix2 = ppix2 + py2 * rs2 + px2 * nch; // output pixel pix2[0] = int(red); pix2[1] = int(green); pix2[2] = int(blue); if (alpha) pix2[3] = int(tran); } return pixbuf2; } /********************************************************************************/ // strip the alpha channel from a pixbuf // returns 0 if no alpha channel or fatal error PIXBUF * gdk_pixbuf_stripalpha(PIXBUF *pixbuf1) // 6.2 { PIXBUF *pixbuf2; GDKCOLOR color; int ww, hh, rs1, rs2; uint8 *ppix1, *ppix2, *pix1, *pix2; int nch, ac; int px, py; ac = gdk_pixbuf_get_has_alpha(pixbuf1); if (! ac) return 0; nch = gdk_pixbuf_get_n_channels(pixbuf1); color = gdk_pixbuf_get_colorspace(pixbuf1); ww = gdk_pixbuf_get_width(pixbuf1); hh = gdk_pixbuf_get_height(pixbuf1); pixbuf2 = gdk_pixbuf_new(color,0,8,ww,hh); // create output pixbuf2 if (! pixbuf2) return 0; ppix1 = gdk_pixbuf_get_pixels(pixbuf1); // input pixel array ppix2 = gdk_pixbuf_get_pixels(pixbuf2); // output pixel array rs1 = gdk_pixbuf_get_rowstride(pixbuf1); rs2 = gdk_pixbuf_get_rowstride(pixbuf2); for (py = 0; py < hh; py++) { pix1 = ppix1 + py * rs1; pix2 = ppix2 + py * rs2; for (px = 0; px < ww; px++) { memcpy(pix2,pix1,nch-1); pix1 += nch; pix2 += nch-1; } } return pixbuf2; } /********************************************************************************/ // Create a pixbuf containing text with designated font and attributes. // Text is white on black. Widget is ultimate display destination. PIXBUF * text_pixbuf(cchar *text, cchar *font, int fontsize, GtkWidget *widget) // 6.2 { char font2[60]; PangoFontDescription *pfont; PangoLayout *playout; cairo_surface_t *surface; cairo_t *cr; PIXBUF *pixbuf; uint8 *pixels, *cairo_data, *cpix, *pix2; int ww, hh, rs, px, py; if (! font) font = zfuncs::appfont; // default font 6.3 snprintf(font2,60,"%s %d",font,fontsize); // combine font and size pfont = pango_font_description_from_string(font2); // make layout with text playout = gtk_widget_create_pango_layout(widget,text); pango_layout_set_font_description(playout,pfont); pango_layout_get_pixel_size(playout,&ww,&hh); ww += 2 + 0.2 * fontsize; // compensate bad font metrics hh += 2 + 0.1 * fontsize; surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,ww,hh); // cairo output image cr = cairo_create(surface); pango_cairo_show_layout(cr,playout); // write text layout to image cairo_data = cairo_image_surface_get_data(surface); // get text image pixels pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,0,8,ww,hh); rs = gdk_pixbuf_get_rowstride(pixbuf); pixels = gdk_pixbuf_get_pixels(pixbuf); for (py = 0; py < hh; py++) // copy text image to PXB for (px = 0; px < ww; px++) { cpix = cairo_data + 4 * (ww * py + px); pix2 = pixels + py * rs + px * 3; pix2[0] = pix2[1] = pix2[2] = cpix[3]; } pango_font_description_free(pfont); // free resources g_object_unref(playout); cairo_destroy(cr); cairo_surface_destroy(surface); return pixbuf; } /********************************************************************************/ // move the mouse pointer to given position in given window // widget must be realized int move_pointer(GtkWidget *widget, int px, int py) { int rpx, rpy; GdkWindow *window; window = gtk_widget_get_window(widget); gdk_window_get_root_coords(window,px,py,&rpx,&rpy); gdk_device_warp(mouse,screen,rpx,rpy); // wayland fails FIXME return 1; } /******************************************************************************** xstring class (dynamic length string) xstring(int cc = 0) default constructor xstring(cchar * ) string constructor xstring(const xstring &) copy constructor ~xstring() destructor operator cchar * () const { return xpp; } conversion operator (cchar *) xstring operator= (const xstring &) operator = xstring operator= (cchar *) operator = friend xstring operator+ (const xstring &, const xstring &) operator + (catenate) friend xstring operator+ (const xstring &, cchar *) operator + friend xstring operator+ (cchar *, const xstring &) operator + void insert(int pos, cchar *string, int cc = 0) insert substring at pos (expand) void overlay(int pos, cchar *string, int cc = 0) replace substring (possibly expand) static void getStats(int & tcount2, int & tmem2) get statistics void validate() const verify integrity int getcc() const { return xcc; } return string length *********************************************************************************/ #define wmiv 1648734981 int xstring::tcount = 0; // initz. static members int xstring::tmem = 0; xstring::xstring(int cc) // new xstring(cc) { wmi = wmiv; xmem = (cc & 0x7ffffff8) + 8; // mod 8 length xpp = new char[xmem]; // allocate if (! xpp) zappcrash("xstring NEW failure",null); tcount++; // incr. object count tmem += xmem; // incr. allocated memory xcc = 0; // string cc = 0 *xpp = 0; // string = null } xstring::xstring(cchar *string) // new xstring("initial string") { wmi = wmiv; xcc = 0; if (string) xcc = strlen(string); // string length xmem = (xcc & 0x7ffffff8) + 8; // mod 8 length xpp = new char[xmem]; // allocate if (! xpp) zappcrash("xstring NEW failure",null); tcount++; // incr. object count tmem += xmem; // incr. allocated memory *xpp = 0; if (xcc) strcpy(xpp,string); // copy string } xstring::xstring(const xstring & xstr) // new xstring2(xstring1) { wmi = wmiv; xmem = xstr.xmem; // allocate same length xcc = xstr.xcc; xpp = new char[xmem]; if (! xpp) zappcrash("xstring NEW failure",null); tcount++; // incr. object count tmem += xmem; // incr. allocated memory strcpy(xpp,xstr.xpp); // copy string } xstring::~xstring() // delete xstring { validate(); delete[] xpp; // release allocated memory xpp = 0; tcount--; // decr. object count tmem -= xmem; // decr. allocated memory if (tcount < 0) zappcrash("xstring count < 0",null); if (tmem < 0) zappcrash("xstring memory < 0",null); if (tcount == 0 && tmem > 0) zappcrash("xstring memory leak",null); } xstring xstring::operator= (const xstring & xstr) // xstring2 = xstring1 { validate(); xstr.validate(); if (this == &xstr) return *this; xcc = xstr.xcc; if (xmem < xcc+1) { delete[] xpp; // expand memory if needed tmem -= xmem; xmem = (xcc & 0x7ffffff8) + 8; // mod 8 length xpp = new char[xmem]; if (! xpp) zappcrash("xstring NEW failure",null); tmem += xmem; } strcpy(xpp,xstr.xpp); // copy string return *this; } xstring xstring::operator= (cchar *str) // xstring = "some string" { validate(); xcc = 0; *xpp = 0; if (str) xcc = strlen(str); if (xmem < xcc+1) { delete[] xpp; // expand memory if needed tmem -= xmem; xmem = (xcc & 0x7ffffff8) + 8; // mod 8 length xpp = new char[xmem]; if (! xpp) zappcrash("xstring NEW failure",null); tmem += xmem; } if (xcc) strcpy(xpp,str); // copy string return *this; } xstring operator+ (const xstring & x1, const xstring & x2) // xstring1 + xstring2 { x1.validate(); x2.validate(); xstring temp(x1.xcc + x2.xcc); // build temp xstring strcpy(temp.xpp,x1.xpp); // with both input strings strcpy(temp.xpp + x1.xcc, x2.xpp); temp.xcc = x1.xcc + x2.xcc; temp.validate(); return temp; } xstring operator+ (const xstring & x1, cchar *s2) // xstring + "some string" { x1.validate(); int cc2 = 0; if (s2) cc2 = strlen(s2); xstring temp(x1.xcc + cc2); // build temp xstring strcpy(temp.xpp,x1.xpp); // with both input strings if (s2) strcpy(temp.xpp + x1.xcc, s2); temp.xcc = x1.xcc + cc2; temp.validate(); return temp; } xstring operator+ (cchar *s1, const xstring & x2) // "some string" + xstring { x2.validate(); int cc1 = 0; if (s1) cc1 = strlen(s1); xstring temp(cc1 + x2.xcc); // build temp xstring if (s1) strcpy(temp.xpp,s1); // with both input strings strcpy(temp.xpp + cc1, x2.xpp); temp.xcc = cc1 + x2.xcc; temp.validate(); return temp; } void xstring::insert(int pos, cchar *string, int cc) // insert cc chars from string at pos { // pad if pos > xcc or cc > string validate(); int scc = strlen(string); if (! cc) cc = scc; int pad = pos - xcc; if (pad < 0) pad = 0; if (xmem < xcc + cc + pad + 1) // allocate more memory if needed { int newmem = xcc + cc + pad; newmem = (newmem & 0x7ffffff8) + 8; // mod 8 length char * xpp2 = new char[newmem]; if (! xpp2) zappcrash("xstring NEW failure",null); strcpy(xpp2,xpp); // copy to new space delete[] xpp; xpp = xpp2; tmem += newmem - xmem; xmem = newmem; } if (pad) memset(xpp+xcc,' ',pad); // add blanks up to pos for (int ii = xcc + pad; ii >= pos; ii--) // make hole for inserted string *(xpp+ii+cc) = *(xpp+ii); if (cc > scc) memset(xpp+pos+scc,' ',cc-scc); // blank pad if cc > string if (cc < scc) scc = cc; strncpy(xpp+pos,string,scc); // insert string, without null xcc += cc + pad; // set new length xpp[xcc] = 0; validate(); } void xstring::overlay(int pos, cchar *string, int cc) // overlay substring { validate(); int scc = strlen(string); if (! cc) cc = scc; if (xmem < pos + cc + 1) // allocate more memory if needed { int newmem = pos + cc; newmem = (newmem & 0x7ffffff8) + 8; // mod 8 length char * xpp2 = new char[newmem]; if (! xpp2) zappcrash("xstring NEW failure",null); strcpy(xpp2,xpp); // copy to new space delete[] xpp; xpp = xpp2; tmem += newmem - xmem; xmem = newmem; } if (pos > xcc) memset(xpp+xcc,' ',pos-xcc); // add blanks up to pos if (cc > scc) memset(xpp+pos+scc,' ',cc-scc); // blank pad if cc > string if (cc < scc) scc = cc; strncpy(xpp+pos,string,scc); // insert string, without null if (pos + cc > xcc) xcc = pos + cc; // set new length xpp[xcc] = 0; validate(); } void xstring::getStats(int & tcount2, int & tmem2) // get statistics { tcount2 = tcount; tmem2 = tmem; } void xstring::validate() const // validate integrity { if (wmi != wmiv) zappcrash("xstring bad wmi",null); if (xmem < xcc+1) zappcrash("xstring xmem < xcc+1",null); if (xcc != (int) strlen(xpp)) zappcrash("xstring xcc != strlen(xpp)",null); } /******************************************************************************** Vxstring class (array or vector of xstring) Vxstring(int = 0); constructor ~Vxstring(); destructor Vxstring(const Vxstring &); copy constructor Vxstring operator= (const Vxstring &); operator = xstring & operator[] (int); operator [] const xstring & operator[] (int) const; operator [] (const) int search(cchar *string); find element in unsorted Vxstring int bsearch(cchar *string); find element in sorted Vxstring int sort(int nkeys, int keys[][3]); sort elements by designated subfields int sort(int pos = 0, int cc = 0); sort elements by 1 subfield (cc 0 = all) int getCount() const { return nd; } get current count Sort with keys: keys[N][0] = key position (0 based) of key N keys[N][1] = key length keys[N][2] = sort type: 1/2 = ascending/descending, 3/4 = same, ignoring case *********************************************************************************/ Vxstring::Vxstring(int ii) // constructor { pdata = 0; nd = ii; if (nd) pdata = new xstring[nd]; if (nd && !pdata) zappcrash("Vxstring NEW fail",null); } Vxstring::~Vxstring() // destructor { if (nd) delete[] pdata; pdata = 0; nd = 0; } Vxstring::Vxstring(const Vxstring & pold) // copy constructor { pdata = 0; nd = pold.nd; // set size if (nd) pdata = new xstring[nd]; // allocate memory if (nd && !pdata) zappcrash("Vxstring NEW fail"); for (int ii = 0; ii < nd; ii++) pdata[ii] = pold[ii]; // copy defined elements } Vxstring Vxstring::operator= (const Vxstring & vdstr) // operator = { if (nd) delete[] pdata; // delete old memory pdata = 0; nd = vdstr.nd; if (nd) pdata = new xstring[nd]; // allocate new memory if (nd && !pdata) zappcrash("Vxstring NEW fail",null); for (int ii = 0; ii < nd; ii++) pdata[ii] = vdstr.pdata[ii]; // copy elements return *this; } xstring & Vxstring::operator[] (int ii) // operator [] { static xstring xnull(0); if (ii < nd) return pdata[ii]; // return reference zappcrash("Vxstring index invalid %d %d",nd,ii,null); return xnull; } const xstring & Vxstring::operator[] (int ii) const // operator [] { static xstring xnull(0); if (ii < nd) return pdata[ii]; // return reference zappcrash("Vxstring index invalid %d %d",nd,ii,null); return xnull; } int Vxstring::search(cchar *string) // find element in unsorted Vxstring { for (int ii = 0; ii < nd; ii++) if (strmatch(pdata[ii],string)) return ii; return -1; } int Vxstring::bsearch(cchar *string) // find element in sorted Vxstring { // (binary search) int nn, ii, jj, kk, rkk; nn = nd; if (! nn) return 0; // empty list ii = nn / 2; // next element to search jj = (ii + 1) / 2; // next increment nn--; // last element rkk = 0; while (1) { kk = strcmp(pdata[ii],string); // check element if (kk > 0) { ii -= jj; // too high, go down if (ii < 0) return -1; } else if (kk < 0) { ii += jj; // too low, go up if (ii > nn) return -1; } else if (kk == 0) return ii; // matched jj = jj / 2; // reduce increment if (jj == 0) { jj = 1; // step by 1 element if (! rkk) rkk = kk; // save direction else { if (rkk > 0) { if (kk < 0) return -1; } // if change direction, fail else if (kk > 0) return -1; } } } } static int VDsortKeys[10][3], VDsortNK; int Vxstring::sort(int NK, int keys[][3]) // sort elements by subfields { // key[ii][0] = position int NR, RL, ii; // [1] = length HeapSortUcomp VDsortComp; // [2] = 1/2 = ascending/desc. // = 3/4 = + ignore case NR = nd; if (NR < 2) return 1; RL = sizeof(xstring); if (NK < 1) zappcrash("Vxstring::sort, bad NK",null); if (NK > 10) zappcrash("Vxstring::sort, bad NK",null); VDsortNK = NK; for (ii = 0; ii < NK; ii++) { VDsortKeys[ii][0] = keys[ii][0]; VDsortKeys[ii][1] = keys[ii][1]; VDsortKeys[ii][2] = keys[ii][2]; } HeapSort((char *) pdata,RL,NR,VDsortComp); return 1; } int VDsortComp(cchar *r1, cchar *r2) { xstring *d1, *d2; cchar *p1, *p2; int ii, nn, kpos, ktype, kleng; d1 = (xstring *) r1; d2 = (xstring *) r2; p1 = *d1; p2 = *d2; for (ii = 0; ii < VDsortNK; ii++) // compare each key { kpos = VDsortKeys[ii][0]; kleng = VDsortKeys[ii][1]; ktype = VDsortKeys[ii][2]; if (ktype == 1) { nn = strncmp(p1+kpos,p2+kpos,kleng); if (nn) return nn; continue; } else if (ktype == 2) { nn = strncmp(p1+kpos,p2+kpos,kleng); if (nn) return -nn; continue; } else if (ktype == 3) { nn = strncasecmp(p1+kpos,p2+kpos,kleng); if (nn) return nn; continue; } else if (ktype == 4) { nn = strncasecmp(p1+kpos,p2+kpos,kleng); if (nn) return -nn; continue; } zappcrash("Vxstring::sort, bad KEYS sort type",null); } return 0; } int Vxstring::sort(int pos, int cc) // sort elements ascending { int key[3]; if (! cc) cc = 999999; key[0] = pos; key[1] = cc; key[2] = 1; sort(1,&key); return 1; } /******************************************************************************** Hash Table class HashTab(int cc, int cap); constructor ~HashTab(); destructor int Add(cchar *string); add a new string int Del(cchar *string); delete a string int Find(cchar *string); find a string int GetCount() { return count; } get string count int GetNext(int & first, char *string); get first/next string int Dump(); dump hash table to std. output constructor: cc = string length of table entries, cap = table capacity cap should be set 30% higher than needed to reduce collisions and improve speed Benchmark: 0.056 usec. to find 19 char string in a table of 100,000 which is 80% full. 3.3 GHz Core i5 *********************************************************************************/ // static members (robust for tables up to 60% full) int HashTab::tries1 = 100; // Add() tries int HashTab::tries2 = 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, tries; pos = strHash(string,cap); // get random position pos = pos * cc; for (tries = 0, fpos = -1; tries < tries1; tries++, 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 (tries1 exceeded) } // Delete a string from table int HashTab::Del(cchar *string) { int pos, tries; pos = strHash(string,cap); // get random position pos = pos * cc; for (tries = 0; tries < tries2; tries++, 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 tries2, must not happen return 0; // (table too full to function) } // Find a table entry. int HashTab::Find(cchar *string) { int pos, tries; pos = strHash(string,cap); // get random position pos = pos * cc; for (tries = 0; tries < tries2; tries++, 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-20.08/zfuncs.h000066400000000000000000001664171362435004500145540ustar00rootroot00000000000000/******************************************************************************** zfuncs.h include file for zfuncs functions Copyright 2007-2020 Michael Cornelison source code URL: https://kornelix.net contact: mkornelix@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. *********************************************************************************/ // zfuncs.h version v.7.4 (for Fotoxx 20.0) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define VERTICAL GTK_ORIENTATION_VERTICAL // GTK shortcuts #define HORIZONTAL GTK_ORIENTATION_HORIZONTAL #define PIXBUF GdkPixbuf #define GDKCOLOR GdkColorspace #define int8 char // number types #define int16 short #define int32 int #define int64 long long // long long is always 64 bits #define uint8 unsigned char #define uint16 unsigned short #define uint32 unsigned int #define uint64 unsigned long long #define uchar unsigned char #define cchar const char #define VOL volatile #define wstrerror(err) strerror(WEXITSTATUS(err)) // get text status for child process #define STATB struct stat // stat() file status buffer #define mutex_t pthread_mutex_t // abbreviations #define mutex_init pthread_mutex_init #define mutex_lock pthread_mutex_lock #define mutex_trylock pthread_mutex_trylock #define mutex_unlock pthread_mutex_unlock #define mutex_destroy pthread_mutex_destroy #define XFCC 1000 // max. file pathname cc tolerated #define null NULL #define true 1 #define false 0 #define NOP // trace execution: source file, function, line no, caller address #define TRACE trace(__FILE__,__FUNCTION__,__LINE__,__builtin_return_address(0)); // system functions ============================================================ void *zmalloc(size_t cc); // malloc() wrapper void zfree(void *pp); // free() wrapper char *zstrdup(cchar *string, int addcc = 0); // strdup() wrapper with opt. expand void zmalloc_report(); // print statistics report void printz(cchar *format, ...); // printf() with immediate fflush() void zexit(cchar *message, ...); // exit a process and kill child processes void zbacktrace(); // produce a backtrace to stdout void zappcrash(cchar *format, ...); // crash with popup message in text window void catch_signals(); // catch signals and do backtrace dump void trace(cchar *file, cchar *func, int line, void *addr); // implements TRACE macro void tracedump(); // dump program trace data void beroot(int argc, char *argv[]); // restart program as root if password OK int runroot(cchar *command); // run command as root via su or sudo 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 ztimer_start(); // start 1 millisec. timer void ztimer_stop(); // stop 1 millisec. timer int ztimer_milliseconds(); // get millisecs. since startup void start_CPUtimer(double &time0); // start a process CPU timer double get_CPUtimer(double &time0); // get elapsed CPU time, seconds double CPUtime(); // get elapsed process CPU time for main() double CPUtime2(); // " include all threads double jobtime(); // " include all threads + subprocesses void compact_time(const time_t DT, char *compactDT); // time_t DT to yyyymmddhhmmss void pretty_datetime(const time_t DT, char *prettyDT); // time_t DT to yyyy-mm-dd hh:mm:ss int parseprocfile(cchar *pfile, cchar *pname, double *value, ...); // get data from /proc file int parseprocrec(char *prec, int field, double *value, ...); // get data from /proc file record int coretemp(); // get CPU core temperature, deg. C int disktemp(char *disk); // get disk temp, e.g. "/dev/sda" void zsleep(double dsecs); // sleep specified seconds void zloop(double dsecs); // loop specified seconds void make_global_lockfile(cchar *lockname, char **lockfile); // create global lockfile from lock name 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 pthread_t start_Jthread(void * threadfunc(void *), void * arg); // start joinable thread function int wait_Jthread(pthread_t tid); // wait for completion (join thread) void synch_threads(int NT = 0); // synchronize NT threads void set_cpu_affinity(int cpu); // set cpu affinity for calling thread int zsystem(cchar *command); // system() + return child process status int shell_quiet(cchar *command, ...); // format/run shell command, return status int shell_ack(cchar *command, ...); // "" + popup an error message if error int shell_asynch(cchar *command, ...); // start shell command, return immediately int shell_asynch_status(int handle); // get status of asynch shell command char * command_output(int &contx, cchar *command, ...); // get shell command output int command_status(int contx); // get exit status of command int command_kill(int contx); // kill command before completion int signalProc(cchar * pname, cchar * signal); // pause/resume/kill subprocess char * fgets_trim(char * buff, int maxcc, FILE *, int bf = 0); // fgets + trim trailing \n \r (blanks) int samefolder(cchar *file1, cchar *file2); // returns 1 if files in same folder 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 folder exists, ask to create int copyFile(cchar *sfile, cchar *dfile); // copy file to file or file to folder int zreaddir(cchar *folder, char **&files); // return all files in a folder, sorted char * combine_argvs(int argc, char *argv[], int Nth); // combine argv[ii] elements from Nth to last char * zescape_quotes(cchar *file); // escape quote marks (") in file name // measure CPU time spent in a function or code block within a function extern VOL double cpu_profile_timer; // internal data tables extern VOL double cpu_profile_table[100]; extern VOL double cpu_profile_elapsed; void cpu_profile_init(); // initialize at start of test void cpu_profile_report(); // report CPU time per function inline void cpu_profile_enter(int fnum) // at entry to measured code block { cpu_profile_timer = cpu_profile_elapsed; } inline void cpu_profile_exit(int fnum) // at exit from measured code block { cpu_profile_table[fnum] += cpu_profile_elapsed - cpu_profile_timer; } int pagefaultrate(); // monitor own process hard fault rate // string macros and functions ================================================= #define strmatch(str1,str2) (! strcmp((str1),(str2))) // TRUE if strings equal #define strmatchN(str1,str2,cc) (! strncmp((str1),(str2),(cc))) // TRUE if strings[cc] equal #define strmatchcase(str1,str2) (! strcasecmp((str1),(str2))) // TRUE if strings equal, ignoring case #define strmatchcaseN(str1,str2,cc) (! strncasecmp((str1),(str2),(cc))) // TRUE if strings[cc] equal, ignoring case cchar * strField(cchar *string, cchar *delims, int Nth); // get Nth delimited field in string cchar * strField(cchar *string, cchar delim, int Nth); // get Nth delimited field in string int strParms(int &bf, cchar *inp, char *pname, int maxcc, double &pval); // parse string: name1=val1 | name2 ... int strHash(cchar *string, int max); // string --> random int 0 to max-1 int64 strHash64(cchar *string, int64 max); // string --> random int 0 to max-1 int strncpy0(char *dest, cchar *source, uint cc); // strncpy, insure null, return 0 if fit void strnPad(char *dest, cchar *source, int cc); // strncpy with blank padding to cc int strTrim(char *dest, cchar *source); // remove trailing blanks int strTrim(char *string); // remove trailing blanks int strTrim2(char *dest, cchar *source); // remove leading and trailing blanks int strTrim2(char *string); // remove leading and trailing blanks int strCompress(char *dest, cchar *source); // remove all blanks incl. imbedded int strCompress(char *string); // remove all blanks int strncatv(char *dest, int maxcc, cchar *source, ...); // catenate strings (last = null) int strmatchV(cchar *string, ...); // compare to N strings, return 1-N or 0 void strToUpper(char *dest, cchar *source); // move and conv. string to upper case void strToUpper(char *string); // conv. string to upper case void strToLower(char *dest, cchar *source); // move and conv. string to lower case void strToLower(char *string); // conv. string to lower case int repl_1str(cchar *strin, char *strout, cchar *ssin, cchar *ssout); // copy string and replace 1 substring int repl_Nstrs(cchar *strin, char *strout, ...); // copy string and replace N substrings int breakup_text(cchar *in, char **&out, cchar *dlm, int cc1, int cc2); // break long string into substrings void strncpyx(char *out, cchar *in, int ccin); // conv. string to hex format void StripZeros(char *pNum); // 1.230000E+12 --> 1.23E+12 int blank_null(cchar *string); // test for blank/null string int clean_escapes(char *string); // replace \x escapes with real characters int utf8len(cchar *utf8string); // get graphic cc for UTF8 string int utf8substring(char *utf8out, cchar *utf8in, int pos, int cc); // get graphic substring from UTF8 string int utf8_check(cchar *string); // check utf8 string for encoding errors int utf8_position(cchar *utf8in, int Nth); // get byte position of Nth graphic char. int zsed(cchar *file, ...); // replace string1/3... with string2/4... cchar * zstrstr(cchar *haystack, cchar *needle); // these work like strstr() and strcasestr() cchar * zstrcasestr(cchar *haystack, cchar *needle); // but "" does NOT match any string. // number conversion =========================================================== int convSI (cchar *s, int &i, cchar **delm = 0); // string to int int convSI (cchar *s, int &i, int low, int hi, cchar **delm = 0); // (with low/high limit checking) int convSD (cchar *s, double &d, cchar **delm = 0); // string to double int convSD (cchar *s, double &d, double low, double hi, cchar **delm = 0); // (with low/high limit checking) int convSF (cchar *s, float &f, cchar **delm = 0); // string to double int convSF (cchar *s, float &f, float low, float hi, cchar **delm = 0); // (with low/high limit checking) int convIS (int iin, char *outp, int *cc = 0); // int to string, returned cc int convDS (double din, int prec, char *outp, int *cc = 0); // double to string, precision, ret. cc char * formatKBMB(double fnum, int prec); // format nnn B, nn.n KB, n.nn MB, etc. // wildcard functions ========================================================== int MatchWild(cchar * wildstr, cchar * str); // wildcard string match (match = 0) int MatchWildIgnoreCase(cchar * wildstr, cchar * str); // wildcard string match, ignoring case cchar * SearchWild(cchar *wpath, int &flag); // wildcard file search cchar * SearchWildCase(cchar *wpath, int &flag); // wildcard file search, ignoring case int zfind(cchar *pattern, char **&flist, int &NF); // wildcard file search using glob() // search and sort functions =================================================== int bsearch(int seekint, int nn, int list[]); // binary search sorted list[nn] int bsearch(cchar *seekrec, cchar *allrecs, int recl, int nrecs); // binary search sorted records int bsearch(cchar *seekrec, cchar **allrecs, int N, int nrecs); // binary search of sorted pointers to recs typedef int HeapSortUcomp(cchar *rec1, cchar *rec2); // return -1/0/+1 if rec1 rec2 void HeapSort(int vv[], int nn); // Heap Sort - integer void HeapSort(float vv[], int nn); // Heap Sort - float void HeapSort(double vv[], int nn); // Heap Sort - double void HeapSort(char *vv[], int nn); // Heap Sort - char, ascending order void HeapSort(char *vv1[], char *vv2[], int nn); // Heap Sort - parallel char *, ascending void HeapSort(char *vv[], int nn, HeapSortUcomp); // Heap Sort - char, user-defined order void HeapSort(char *recs, int RL, int NR, HeapSortUcomp); // Heap Sort - records, user-defined order int MemSort(char * RECS, int RL, int NR, int KEYS[][3], int NK); // memory sort, records with multiple keys int zmember(int testval, int matchval1, ...); // test if value matches any in a list // variable string list functions ============================================== struct pvlist { int max; // max. entries int act; // actual entries char **list; // entries }; pvlist * pvlist_create(int max); // create pvlist void pvlist_free(pvlist *pv); // free pvlist int pvlist_append(pvlist *pv, cchar *entry, int unique = 0); // append new entry (opt. if unique) int pvlist_prepend(pvlist *pv, cchar *entry, int unique = 0); // prepend new entry (opt. if unique) int pvlist_find(pvlist *pv, cchar *entry); // find entry by name int pvlist_remove(pvlist *pv, cchar *entry); // remove entry by name int pvlist_remove(pvlist *pv, int Nth); // remove entry by number (0...) int pvlist_count(pvlist *pv); // return entry count int pvlist_replace(pvlist *pv, int Nth, cchar *entry); // replace Nth entry (0...) char * pvlist_get(pvlist *pv, int Nth); // return Nth entry (0...) int pvlist_sort(pvlist *pv); // sort list, ascending // random number functions ===================================================== int lrandz(int64 * seed); // returns 0 to 0x7fffffff int lrandz(); // built-in seed double drandz(int64 * seed); // returns 0.0 to 0.99999... double drandz(); // built-in seed // spline curve-fitting functions ============================================== void spline1(int nn, float *dx, float *dy); // define a curve using nn data points float spline2(float x); // return y-value for given x-value // FIFO queue for text strings, single or dual-thread access =================== typedef struct { int qcap; // queue capacity int qnewest; // newest entry position, circular int qoldest; // oldest entry position, circular int qdone; // flag, last entry added to queue char **qtext; // up to qcap text strings } Qtext; void Qtext_open(Qtext *qtext, int cap); // initialize Qtext queue, empty void Qtext_put(Qtext *qtext, cchar *format, ...); // add text string to Qtext queue char * Qtext_get(Qtext *qtext); // remove text string from Qtext queue void Qtext_close(Qtext *qtext); // close Qtext, zfree() leftover strings // application initialization and administration =============================== int appimage_install(cchar *appname); // make appimage desktop and icon files void appimage_unstall(); // uninstall appimage int zinitapp(cchar *appvers, cchar *homedir = 0); // initialize app (appname-N.N, homedir) cchar * get_zprefix(); // get /usr or /usr/local ... cchar * get_zhomedir(); // get /home/user/.appname/ cchar * get_zdatadir(); // get data folder cchar * get_zimagedir(); // get image folder cchar * get_zdocdir(); // get document folder void zsetfont(cchar *newfont); // set new app font and size int widget_font_metrics(GtkWidget *widget, int &fww, int &fhh); // get widget font char width/height int locale_filespec(cchar *ftype, cchar *fname, char *filespec); // get a locale dependent file void showz_logfile(GtkWidget *parent); // show log file in popup window void showz_textfile(cchar *type, cchar *file, GtkWidget *parent); // show text file [.gz] in popup window void showz_html(cchar *url); // show html via preferred browser void showz_docfile(GtkWidget *, cchar *docfile, cchar *topic); // show docfile topic and assoc. image void appruns_update(); // update run counter in appruns file void phone_home(); // send usage statistics void phone_home_allow(GtkWidget *parent); // user dialog to allow/block phone_home() // translation functions ======================================================= #define E2Xmaxent 2000 // max. translation strings #define E2Xmaxcc 4000 // max. cc per string void E2Xinit(cchar *lang, int Fdump); // setup for message translation cchar * E2X(cchar *english); // get translation for English message cchar * E2X_missing(int &ftf); // get missing translations, one per call /******************************************************************************** GTK utility functions *********************************************************************************/ void zmainloop(int skip = 0); // do main loop, process menu events void zmainsleep(float secs); // do main loop and sleep designated time /********************************************************************************/ // cairo drawing region for GDK window GTK 3.21 version #if GTK_CHECK_VERSION(3,22,0) typedef struct { GdkWindow *win; cairo_rectangle_int_t rect; cairo_region_t *reg; GdkDrawingContext *ctx; cairo_t *dcr = 0; } draw_context_t; #else typedef struct { cairo_t *dcr; } draw_context_t; #endif cairo_t * draw_context_create(GdkWindow *gdkwin, draw_context_t &context); void draw_context_destroy(draw_context_t &context); /********************************************************************************/ // textwidget functions - scrollable text widget for text reports and line editing // widget = zdialog_widget(zd,textwidget) where textwidget is a zdialog "text" widget type void textwidget_clear(GtkWidget *widget); // clear all text void textwidget_clear(GtkWidget *widget, int line); // clear text from line to end int textwidget_linecount(GtkWidget *widget); // get current line count void textwidget_append(GtkWidget *widget, int bold, cchar *format, ...); // append line void textwidget_append2(GtkWidget *widget, int bold, cchar *format, ...); // append line and scroll to end void textwidget_insert(GtkWidget *widget, int bold, int line, cchar *format, ...); // insert line void textwidget_replace(GtkWidget *widget, int bold, int line, cchar *format, ...); // replace line void textwidget_delete(GtkWidget *widget, int line); // delete line int textwidget_find(GtkWidget *widget, char *matchtext, int line1); // find matching line void textwidget_insert_pixbuf(GtkWidget *textwidget, int line, GdkPixbuf *pixbuf); // insert pixbuf image void textwidget_scroll(GtkWidget *widget, int line); // scroll line on screen void textwidget_scroll_top(GtkWidget *widget, int line); // scroll line to top of window void textwidget_get_visible_lines(GtkWidget *textwidget, int &top, int &bott); // get range of visible lines void textwidget_dump(GtkWidget *widget, cchar *filename); // dump all text into a file void textwidget_save(GtkWidget *widget, GtkWindow *parent); // same, with save-as dialog char * textwidget_line(GtkWidget *widget, int line, int strip); // retrieve line (strip \n) void textwidget_highlight_line(GtkWidget *widget, int line); // highlight line char * textwidget_word(GtkWidget *, int line, int posn, cchar *dlims, char &end); // retrieve word void textwidget_highlight_word(GtkWidget *widget, int line, int posn, int cc); // highlight word void textwidget_bold_word(GtkWidget *widget, int line, int posn, int cc); // make word bold void textwidget_underline_word(GtkWidget *widget, int line, int posn, int cc); // make word underlined void textwidget_font_attributes(GtkWidget *widget); // set font attributes for all text typedef void textwidget_callbackfunc_t(GtkWidget *, int line, int posn, int KBkey); // widget event function to receive void textwidget_set_eventfunc(GtkWidget *, textwidget_callbackfunc_t func); // mouse click and KB events /********************************************************************************/ // functions to simplify building menus, tool bars, status bars #define G_SIGNAL(window,event,func,arg) \ g_signal_connect(G_OBJECT(window),event,G_CALLBACK(func),(void *) arg) #define zdcbmax 100 // max. combo box drop-down list typedef void cbFunc(GtkWidget *, cchar *mname); // menu or button response function GtkWidget * create_menubar(GtkWidget *vbox); // create menubar in packing box GtkWidget * add_menubar_item(GtkWidget *mbar, cchar *mname, cbFunc func = 0); // add menu item to menubar GtkWidget * add_submenu_item(GtkWidget *mitem, cchar *subname, // add submenu item to menu item cbFunc func = 0, cchar *mtip = 0); // with opt. function and popup tip GtkWidget * create_toolbar(GtkWidget *vbox, int iconsize = 24); // toolbar in packing box (no vert gtk3) GtkWidget * add_toolbar_button(GtkWidget *tbar, cchar *lab, cchar *tip, // add button with label, tool-tip, icon cchar *icon, cbFunc func); GtkWidget * create_stbar(GtkWidget *vbox); // create status bar in packing box int stbar_message(GtkWidget *stbar, cchar *message); // display message in status bar /********************************************************************************/ GtkWidget * create_popmenu(); // create an empty popup menu GtkWidget * add_popmenu_item(GtkWidget *popmenu, cchar *mname, // add menu item to popup menu cbFunc func, cchar *arg, cchar *mtip = 0); void popup_menu(GtkWidget *, GtkWidget *popmenu); // pop-up menu at current mouse posn. /********************************************************************************/ // user editable graphic menu in popup window // menus can be added and arranged using the mouse typedef void Gmenuz_cbfunc(cchar *menu); // caller-supplied callback function void Gmenuz(GtkWidget *parent, cchar *title, cchar *ufile, Gmenuz_cbfunc); // show window, handle mouse drag/click /********************************************************************************/ // create vertical menu/toolbar in vertical packing box struct vmenuent { // menu data from caller cchar *name; // menu name, text cchar *icon; // opt. icon file name cchar *desc; // description (mouse hover popup) cbFunc *func; // callback func (GtkWidget *, cchar *arg) cbFunc *RMfunc; // alternate func for right mouse click cchar *arg; // callback arg for func cchar *RMarg; // callback arg for RMfunc PIXBUF *pixbuf; // icon pixbuf or null PangoLayout *playout1, *playout2; // normal and bold menu text int namex, namey; // menu name position in layout int iconx, icony; // menu icon position int ylo, yhi; // menu height limits int iconww, iconhh; // icon width and height }; struct Vmenu { GtkWidget *vbox; // parent window (container) GtkWidget *topwin; // top-level window of parent GtkWidget *layout; // drawing window float fgRGB[3]; // font color, RGB scaled 0-1 float bgRGB[3]; // background color, RGB scaled 0-1 int xmax, ymax; // layout size int mcount; // menu entry count vmenuent menu[100]; }; Vmenu *Vmenu_new(GtkWidget *vbox, float fgRGB[3], float bgRGB[3]); // create new menu in parent vbox void Vmenu_add(Vmenu *vbm, cchar *name, cchar *icon, // add menu item with response function int iconww, int iconhh, cchar *desc, cbFunc func, cchar *arg); // function may be popup_menu() void Vmenu_add_RMfunc(Vmenu *vbm, int me, cbFunc RMfunc, cchar *arg); // add auto function if right mouse click void Vmenu_block(int flag); // block or unblock menu /********************************************************************************/ // functions to implement GTK dialogs with less complexity // widget types: dialog, hbox, vbox, hsep, vsep, frame, scrwin, label, link, // entry, edit, text, radio, check, button, togbutt, spin, // combo, comboE, hscale, vscale, imagebutt, colorbutt, icon, image #define zdmaxwidgets 300 #define zdmaxbutts 10 #define zdsentinel 0x97530000 #define zdialog_max 20 #define zdialog_button_shortcuts "Done Cancel Apply Reset" // buttons that may have KB shortcuts struct zwidget { cchar *type; // dialog, hbox, vbox, label, entry ... cchar *name; // widget name cchar *pname; // parent (container) name char *data; // widget data, initial / returned pvlist *cblist; // combo box drop-down list int size; // text entry cc or image pixel size int homog; // hbox/vbox: equal spacing flag int expand; // widget is expandable flag int space; // extra padding space (pixels) int wrap; // wrap mode for edit widget int rescale; // widget is rescaled for more resolution double lval, nval, hval; // scale range and neutral value double lolim, hilim, step; // range and step value for number widget GtkWidget *widget; // GTK widget pointer }; struct zdialog { int sentinel1; // validity sentinel1 int uniqueID; // unique ID, monotone increasing char *title; // dialog title void *eventCB; // dialog event user callback function void *popup_report_CB; // callback function for popup_report int zrunning; // dialog is running (0,1) int zstat; // dialog status (from completion button) char zstat_button[40]; // completion button label int disabled; // widget signals/events are disabled int saveposn; // save and recall window position each use int saveinputs; // save and recall user inputs each use GtkWidget *dialog; // dialog window or null (box parent) GtkWidget *parent; // parent window or null cchar *ptype; // null or "window" or "box" parent cchar *compbutton[zdmaxbutts]; // dialog completion button labels GtkWidget *compwidget[zdmaxbutts]; // dialog completion button widgets zwidget widget[zdmaxwidgets]; // dialog widgets (EOF = type = 0) char event[40]; // active event or widget GtkWidget *lastwidget; // last widget active int sentinel2; // validity sentinel2 }; zdialog *zdialog_new(cchar *title, GtkWidget *parent, ...); // create a zdialog with opt. buttons void zdialog_set_title(zdialog *zd, cchar *title); // change zdialog title void zdialog_set_modal(zdialog *zd); // set zdialog modal void zdialog_set_decorated(zdialog *zd, int decorated); // set zdialog decorated or not void zdialog_present(zdialog *zd); // zdialog visible and on top void zdialog_can_focus(zdialog *zd, int Fcan); // zdialog can/not have focus (e.g. report) void zdialog_set_focus(zdialog *zd, cchar *widget = null); // set focus on window [ widget ] 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, cchar *title = 0); // return 1/0 if zdialog valid/invalid GtkWidget * zdialog_widget(zdialog *zd, cchar *name); // GTK widget from zdialog widget name int zdialog_set_image(zdialog *zd, cchar *name, GdkPixbuf *); // set "image" widget from a GDK pixbuf int zdialog_add_ttip(zdialog *zd, cchar *wname, cchar *ttip); // add popup tool tip to a widget int zdialog_resize(zdialog *zd, int width, int height); // set size > widget sizes int zdialog_put_data(zdialog *zd, cchar *name, cchar *data); // put data in widget (entry, spin ...) cchar * zdialog_get_data(zdialog *zd, cchar *name); // get widget data int zdialog_set_limits(zdialog *, cchar *name, double min, double max); // set new widget limits (spin, scale) int zdialog_rescale(zdialog *zd, cchar *wname, float, float, float); // rescale widget, lo/neut/hi vals typedef int zdialog_event(zdialog *zd, cchar *name); // widget event callback function int zdialog_run(zdialog *zd, zdialog_event = 0, cchar *posn = 0); // run dialog, handle events void zdialog_KB_addshortcut(cchar *key, cchar *menu); // KB shortcut for zdialog compl. button void KBevent(GdkEventKey *event); // extern: pass KB events to main app int zdialog_send_event(zdialog *zd, cchar *event); // send an event to an active dialog int zdialog_send_response(zdialog *zd, int zstat); // complete a dialog, set status int zdialog_show(zdialog *zd, int flag); // show or hide a dialog int zdialog_destroy(zdialog *zd); // destroy dialog (caller resp.) int zdialog_free(zdialog *&zd); // free zdialog memory int zdialog_wait(zdialog *zd); // wait for dialog completion int zdialog_goto(zdialog *zd, cchar *name); // put cursor at named widget void zdialog_set_cursor(zdialog *zd, GdkCursor *cursor); // set cursor for dialog window int zdialog_stuff(zdialog *zd, cchar *name, cchar *data); // stuff string data into widget int zdialog_stuff(zdialog *zd, cchar *name, int data); // stuff int data int zdialog_stuff(zdialog *zd, cchar *name, double data); // stuff double data int zdialog_stuff(zdialog *zd, cchar *name, double data, cchar *format); // stuff double data, formatted int zdialog_labelfont(zdialog *zd, cchar *lab, cchar *font, cchar *txt); // stuff label text with font int zdialog_fetch(zdialog *zd, cchar *name, char *data, int maxcc); // get string data from widget int zdialog_fetch(zdialog *zd, cchar *name, int &data); // get int data int zdialog_fetch(zdialog *zd, cchar *name, double &data); // get double data int zdialog_fetch(zdialog *zd, cchar *name, float &data); // get float data int zdialog_cb_app(zdialog *zd, cchar *name, cchar *data); // append entry to combo drop down list int zdialog_cb_prep(zdialog *zd, cchar *name, cchar *data); // prepend entry to combo drop down list char * zdialog_cb_get(zdialog *zd, cchar *name, int Nth); // get combo drop down list Nth entry int zdialog_cb_delete(zdialog *zd, cchar *name, cchar *data); // delete combo drop down list entry int zdialog_cb_clear(zdialog *zd, cchar *name); // clear all combo box entries int zdialog_cb_popup(zdialog *zd, cchar *name); // show all combo box list entries int zdialog_cb_save(zdialog *zd, cchar *name, cchar *file); // save combo box list to a file int zdialog_cb_load(zdialog *zd, cchar *name, cchar *file); // load combo box list from a file int zdialog_geometry(cchar *action); // load/save zdialog window positiion/size void zdialog_set_position(zdialog *zd, cchar *posn); // set initial/new zdialog window position void zdialog_save_position(zdialog *zd); // save zdialog window position int zdialog_inputs(cchar *action); // load or save zdialog input fields int zdialog_save_inputs(zdialog *zd); // save zdialog input fields when finished int zdialog_restore_inputs(zdialog *zd); // restore zdialog inputs from prior use void window_to_mouse(GtkWidget *window); // move GtkWidget/window to mouse position // write text report in popup window zdialog * popup_report_open(cchar *title, GtkWidget *parent, int ww, int hh, // open popup report - pixel size, int Fheader, textwidget_callbackfunc_t CBfunc, ...); // header line, callback function void popup_report_header(zdialog *zd, int bold, cchar *format, ...); // write non-scrolling header line void popup_report_write(zdialog *zd, int bold, cchar *format, ...); // write text line void popup_report_top(zdialog *zd); // go to top of report window void popup_report_bottom(zdialog *zd); // go to bottom of report window void popup_report_clear(zdialog *zd); // clear report window void popup_report_clear(zdialog *zd, int line); // clear from line to end void popup_report_insert(zdialog *zd, int bold, int line, cchar *format, ...); // insert new line void popup_report_replace(zdialog *zd, int bold, int line, cchar *format, ...); // replace existing line void popup_report_delete(zdialog *zd, int line); // delete line int popup_report_find(zdialog *zd, char *matchtext, int line1); // find matching line void popup_report_insert_pixbuf(zdialog *zd, int line, GdkPixbuf *pixbuf); // insert pixbuf image after line void popup_report_scroll(zdialog *zd, int line); // scroll to make line visible void popup_report_scroll_top(zdialog *zd, int line); // scroll to put line at top void popup_report_get_visible_lines(zdialog *zd, int &top, int &bott); // get visible lines range char * popup_report_line(zdialog *zd, int line, int strip); // retrieve line (strip \n) char * popup_report_word(zdialog *zd, int line, int posn, cchar *dlims, char &end); // retrieve word void popup_report_highlight_line(zdialog *zd, int line); // highlight line void popup_report_highlight_word(zdialog *zd, int line, int posn, int cc); // highlight word void popup_report_underline_word(zdialog *zd, int line, int posn, int cc); // underline word void popup_report_bold_word(zdialog *zd, int line, int posn, int cc); // bold word void popup_report_font_attributes(zdialog *zd); // font attributes for entire report void popup_report_close(zdialog *zd, int secs); // close window after seconds // shell command to popup window int popup_command(cchar *command, int ww = 400, int hh = 300, GtkWidget *parent = 0, int top = 0); // popup message dialogs void zmessageACK(GtkWidget *parent, cchar *format, ... ); // display message, wait for OK int zmessageYN(GtkWidget *parent, cchar *format, ... ); // display message, wait for YES/NO zdialog * zmessage_post(GtkWidget *, cchar *loc, int s, cchar *f, ...); // show message, timeout or cancel zdialog * zmessage_post_bold(GtkWidget *, cchar *loc, int s, cchar *f, ...); // " " with big red bold font char * zdialog_text(GtkWidget *parent, cchar *title, cchar *inittext); // get short text input from user int zdialog_choose(GtkWidget *parent, cchar *where, cchar *message, ...); // show message, return choice void poptext_screen(cchar *text, int px, int py, float s1, float s2); // show popup text at screen posn void poptext_mouse(cchar *text, int dx, int dy, float s1, float s2); // " " " at mouse posn + offset void poptext_window(GtkWindow *, cchar *tx, int x, int y, float s1, float s2); // " " " at window posn + offset void poptext_widget(GtkWidget *, cchar *tx, int x, int y, float s1, float s2); // " " " at widget posn + offset 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, multiple files, or folder 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); char * zgetfolder(cchar *title, GtkWindow *parent, cchar *initfolder); // print an image file, choosing printer, paper, orientation, margins, and scale void print_image_file(GtkWidget *parent, cchar *imagefile); // drag and drop functions typedef char * drag_drop_source_func(); // user function, set drag-drop text typedef void drag_drop_dest_func(int x, int y, char *text); // user function, get drag-drop text void drag_drop_source(GtkWidget *window, drag_drop_source_func); // connect source window to user function void drag_drop_dest(GtkWidget *window, drag_drop_dest_func); // connect dest. window to user function // miscellaneous GDK/GTK functions PIXBUF * get_thumbnail(cchar *fpath, int size); // get sized thumbnail for image file GdkCursor * zmakecursor(cchar *iconfile); // make a cursor from an image file PIXBUF * gdk_pixbuf_rotate(PIXBUF *, float deg, int alfa = 0); // rotate pixbuf through any angle PIXBUF * gdk_pixbuf_stripalpha(PIXBUF *pixbuf); // strip alpha channel from pixbuf PIXBUF * text_pixbuf(cchar *text, cchar *font, int fsize, GtkWidget *); // create pixbuf with text using font int move_pointer(GtkWidget *, int px, int py); // move the mouse pointer to px, py /******************************************************************************** C++ classes *********************************************************************************/ // dynamic string class ======================================================== class xstring { static int tcount; // total xstring count static int tmem; // total memory used int wmi; // internal ID int xcc; // actual cc (excl. NULL) int xmem; // memory allocated cc char * xpp; // memory pointer public: xstring(int cc = 0); // default constructor xstring(cchar * ); // string constructor xstring(const xstring &); // copy constructor ~xstring(); // destructor operator cchar * () const { return xpp; } // conversion operator (cchar *) xstring operator= (const xstring &); // operator = xstring operator= (cchar *); // operator = friend xstring operator+ (const xstring &, const xstring &); // operator + friend xstring operator+ (const xstring &, cchar *); // operator + friend xstring operator+ (cchar *, const xstring &); // operator + void insert(int pos, cchar * string, int cc = 0); // insert substring at position (expand) void overlay(int pos, cchar * string, int cc = 0); // overlay substring (possibly expand) static void getStats(int & tcount2, int & tmem2); // get statistics void validate() const; // verify integrity int getcc() const { return xcc; } // return string length }; // vector (array) of xstring =================================================== class Vxstring { int nd; // count xstring * pdata; // xstring[nd] public: Vxstring(int = 0); // constructor ~Vxstring(); // destructor Vxstring(const Vxstring &); // copy constructor Vxstring operator= (const Vxstring &); // operator = xstring & operator[] (int); // operator [] const xstring & operator[] (int) const; // operator [] (const) int search(cchar * string); // find element in unsorted Vxstring int bsearch(cchar * string); // find element in sorted Vxstring int sort(int nkeys, int keys[][3]); // sort by designated subfields int sort(int pos = 0, int cc = 0); // sort by 1 subfield (cc 0 = all) int getCount() const { return nd; } // get current count }; // hash table class ============================================================ class HashTab { static int tries1; // insert tries static int tries2; // find/delete tries 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 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[] };

    I! +[[ϯ~|<5έ3Ame0V p7oNqڼ._/—j|&_.RXE `WiyI7s+_GMu}wZԼesieδn̻6Urӽ+l?Ꮶh>s;|*n<K}U{j>ξX_$m#+t xc0xNPygVF]q%R{ s诞,ſ /iwR(v+YE,bri;q6+K@?/->[}vWEXnr##=K݋bID:+eth>:gWEy.IZ|Y6?< 'thnu+8mܘYѷ nj(˔2R1W%t>>]Tk:[e1.Og5Y|P_>$<73MGgY c $<&hSz]O=xY AM[C5\9o583}k'M _PtZe?~"DY3ȥs6<463 +|)[$k><'kѳ[K[&xs0ĩ <+62eZYwKV1h:Zb 5PosH[\"hĺ}l/4kh^G%k#Ʊ. wn~xn :{OA}q.qn Dy8azuS=]:]g?46$(Aw|!?!_5_{E_(`QEQ[ .]4 NQnJļ csAp%`b|=McwG.+D\s_[|<$Gw GV?$H}Nq_AiZ4av`6=qk }3(<~=*笾3? ~'Dsk6g/^~>ӨkVQg?{İcS,{Wq]0u)'+#e `N%mY :4iP? hڰ[i|3;ҥotWbKƱqaLG'Aqzg4z,z0,/[7o~eޟagǧK[k|F(љn3¼Ct{ĺƻkYIGIm}ZQ!7Bc99╮/Y=5_໽'+porc0P7lS=I$,uEeXi sHOIhkS{y"EKFpau_g 厍#eiVBB`'ο)\1G|Q5յWُg5z.gB84;_4hǏg [񮛨O-~A,d,_x%pW= Wpx2IJ~$m:sT0P7lS=:/ڟ/knt9 G.#kOݿv_( +f;K("ӬW=kFg>\Emu_sgn2Do+$cpLW1׉tkhVvږ~<;In <:<1x{\,%DhK˘jo$!66!I\?h]7\yj>֯<3WֶCTe5֬U;[ښ&Կg 21M֓IJxLԭ,ubxB&]$sb~:m> v|fM mxI¿3n:c&ixKZƏ}hEݾ"%2x6F@9<|tu#k]ԾȤBPȄ7ɻ?A޻=O\ϨL` ``u8"_𖉧îa%KG{⑜;7܁ԺvUPns#}$H/MFI,ў|q'T|`g]M5=I2x28HchcX*(f Xc<񗎾cZ4-V}A/mDaG%Ԫr26WZij:e֠O{tOvܕIl(M՟PM'~s;?xRye`Z<} ?5OZVv^[jhowy@ӯjWi_i+t~&խ/|AM.)FKQn#YB|L=ǃt? ~594D`Bscq\Kc jזD5†;I8gZ<:>ڤwwbڎ5w& zz꿭/J?~Ϛ),O~كv'; X4xSxC_Ŵ rZ&e+CߜbmuAᇶִJYmWJ#GPp g X j0\ICåldia}qi&E_ _x6?ƍ}Kb3t>r*p>~03Un|}*+&7X|okkV klW:Z|a`.!SzRS[8?n<{7՗J_; yvg?{zPv"Kxx;|$Y<ߛ~{eu_ᴥƼq:|M_}XYYKdٵ*/<{!~wv^,]P*ia&)x߸($ϒ'rCk XsPҤPdac(Sdpۜw;W|84me K֓xnn*m7J岣;F}٨@.?ֹ|)hC?2oT?o)P=)%ĩHH*O@}>v5!񥩛Sfm̿$#y}[{wiӕWh]3:ʎzς<}kґ*bqjǫ[& L3>ǚ,Kz.Ѯ4^; 8?S؏Q_]=l T?C _ +~?*(i?gK>ዉ u}Cb1_tj2_S*n(+1?~&W_яA&0=Ҋ(((((((((((((((((((?>47ďxOMžOkisƭY tvn@.OxkCҵ/i:~2(pN Ң?S>-"jø^Feԣyc12';60=kZw;N7nm$97M^௉k^Ot $2aBT.$jKΑ&zU޵}~-Q.Е 8b5Ik゚6V'[_ț_ށi;vAk}.Hwd<O> º޽-7C5K\Zp$iL+~7x5=_x\u5/!{2)cp ,3E[!yjVzbt$] |6\_h :M uF˵nQNyc௅f4oiV5]yQCGW6~/VZNuCJ%yRSSs"fpN\od:[ψ>9ω~/xӮo{ syn|Q=OJoڛXѴOZC [)E\> ^3cJ~sZnjua5rQH*Gy%KKrj0 m 7 '[b߭x>|Tym 𶷬ϥ@%&w3x64\|.Y\DOqV;GŌqպtE:ecY GV#<7KRoh˧I!/ sM;mЛ'2w⛘~7º| 'cқkco0pyjg~!|,i_sHҵ2|GqgWj6ZjWem/#) <꿴tOitj^BsDfBsgsJ7R+8~o/ox'ƾӴvym6]}ϊc}8*A+ԴHa"}Zm&MQS KבN(1:9]Am^sp q<f1֛:xI9T["C'ĤO]z<>5[{MmUdI NzWѱxF]$tHJkoU-tE!M`s)5~w8^|+X8́cx#=64^u˟|jce>]HztKΑ+}#VB=oH,Co9yb+KzWٚ>#Yxq쬗NN]F]R8刣Wne>|`x|mɡ&HhGn?oϛ?fh jQm?ly$ݎks_71 %knzIo C:֟63k~׎il j+J߆Kۭ7:Vkb絽T'22 0 V4/)it]^W{ QTsLfQH( tZ竡KLJï"PLJï"h u0׭ӭd^&uOҶJU;٣2x2+xd֦A%ȑ#׭}ORzƂf1ǒOSZ֭}E< <]kVx; 5H4=:ڽ2VyX2>)ƅ_| /uK\ˠ Yc.$#zV ~MzR{K]%ݒmCJ`k*r]f\tߊejڷo=z MEnA8Q'9i5>2~_ [~k9^KbI~}i>.4OijUS-}gŸe&އmOk!78'֧XY!2I4ZDZ{I$P#; PWc;z=? CEk}$w@cS럮~yZq>|W>!,[U_[Sʍ5ř&H$+~+?|oZ]x>M:D4fO v)Md t_ZD_+ѯַ>;~iŅ/íW:߉TKZ=@Ca6̒ucڻ_OX|W%-ۂLTȤJ_INɯVi~5ǃtེ =dg:>\K1+h"#߆k?Ko,<fmgnzs^E+QRK{mW ޟi,VG2ã3޺??u K_t xrK_xJ;;oHA _TVw|=xBѵ{T/k{i $l0q(_-ַ>=Qfw5ZYO=DQA; <g=溴2:Aha_Y8<> 9࿇|;ij] fħ"0I Îʚ|_kտȟ/4GC>xLZFpҲ;3[~T5|I4\@FFpG1lT+WOi5ErtkWz-sKf9XAb:97=}gyɥ_Eaʈ8;Q|wK>1[kO Z\:c O^lZ;Bx>WZ56Z[l}:HA@<km4ijpN1E+2sRFM//˥>$>>#_idZn.4tO5>l*Zv2ể٫šZAtuG}2 /|(48 pU~z;Ë / h=u^f 8dT%eia]Š(aEP]:]sS@~dk~W}~k~W傌:AO>0 >!^\*ˌuOAkkC1+mk ,Pv4EyWOh`Tj[˦ۆU=856p+ogO8wt=׿?v:[ik'\ݤ+bn5XX:;i+REsc&Q7Cnd歧#k: xWlָj@Q̜U3PzT3B1^*Qru;ȰOjo`x :cZasSG"Nj'v1DPVgb xzzpCV=xU]:BH JтioA^-Y%j!qWaT[z֌#nRV+L㊷z v<3c iZcXKN9 rMoÛ(S^Ve#D7O+jkm獢&(V[@{t͟(_G|g@#5x~ k o-|_>@ Yw`?_uF?ÿ J( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (<+?ÿ|s4''g<#ePinRw}JPjgڍ坥ݓ*A @[%C9Rpqg7 a}Z}Z^D6҂ʐMQ`qZw~"5υV$_oKh !.ﳗ°\<smZߥ P|ɯ|l寈n4Y.t֑JWrʼ О}/@uxU)D5|K/0tV,K:yr_OC 7IA=ErW,?g6o?A=E iIMN$#b`*Gr4nMgN?7gSB0mG׃<֦&'Z'zC 3j,3n7e];&s=R|"?G=oWʟeQKѥܪ^(Qw/"w+R|o"ͱɈGQC U>#ޏ-;N<&RK}*m'mWRga|IB]Lj'4 Zm6o6l $wuowkC 5H{%|q±9kc~|ѯ|a,ωVr#GԒ_ q $⯈_ G}q} %UHĪu*1/v7Q|ҷ3oƨǿ[/_mex/],!d|Isנ|Rox5s6I dTn <;'s^DәI⸹[w((Awt |IIY E&ɯnIN#'l49+C^7X,[ #(呉?.ssoƫ翋Yx kڇ-y)d6iQ[$V߈dyLt8}tŋTu)5[IIqH4h~Ϟ:og/d{WBKI z+hh_!>-߈funZۉc–8$SCھZT'̲Ym+">+?[uG'U}EwiV쏔R޿П:T𧊡ӮZq[_\F'/ Y,"`نApGEtXWf(f6Ȕ+OE| @ּ}M_ :`x!%QV$'>'N z}Z\7OhcDxT;FG^:X<ϝ}A.JB#i}\YXMr8c' Kx;/{ -ꏩ^@>U@UpJ"NyM$*HԡX)RV,| pS`z t].|[r,Yu6˸ǘGA+J)`No]Cyw3O?JJ(k6eizircV!W[j>uRYu֜Tcy9b '$Ú(x#~ƯxR#m]+lXF  x> kog%Ƨ%0@S sЊ^w ZIsJvKQ0@+J) +Kz tZh-LM5tQk*;-bەū*㯃*Zk7RΣ'q|%s嘞䜚<ܧ>'\FZPMV2t/"DV7.:硛]>S =kRCqZѵ)kg{ӚݲZF o^9%V=OGd\{.6?ZBXV ҩXdtKJ%r8(s&zx|]oHtGJ.;i^qW\=⵭l}8k]'88kcWsM'VGjٷzakF 0q^H\jc{V6ׇN8X`t+Q<2VcdS-dՏ>xgR Z[\jHs+m?3?l~W~h;𷋢2wog+322}Z!Zw`?^?n*?=d %"$g O!Zw`?_|xS{~gfqK亞EW'QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEyQP~;{x[SmzoM<-tarqy'5Mw]j<)}G}C`5?x\<1smrK䳚mF`Fy̼G߉/t 7[%ҡԖPZJI>RX0ۑTU՟={?hVֺŖsk%pjzl#f!#ڱ|qկf'&Cjʳ)AmYF_g|g|Y|!YxCOiZuFH#`yǧGZ||;t%漚}M_uΒ9R6`w`'?_ßt7h7iKp$^Xb8㩯SѾ's\xPTv]s20`7U+b߁!2{xKmRMņC:5ԒpI C/U-NTIkXRi㍥0=)="]7oO_#h ; Oxytwگ0lsjDA ?lcoijp貤2ݽ@f'JĨ|3I֥-;~!XZLnfEX17|[ZtH[[56-ĹWTaSx{CӴ(~gco1n-Bpe(@OᏈv^ PN[;XI`w#8U9G~0xD<' z66O2J+}d"*Z/t?GVKw ʸ/FW9d|Om;7Ҿ.hd&zAxҳß-C5?.~(|7/M~wIg03H~H@}v.nv5 =d02r*A{̹|5[_jj??2 ˟ Zo?>д1j eo]?.T.|1icuh\cB?ڿ5[_jj??2 ˟ Zo?>д1{! GVu 5G27_veφ?-7 nQ1]uƨcC+Q s M;Gj eo]?.T.|1icuh\cB?ڿ4/ijh60Z9mnʬmx1_ -t˅|bOÓ'=\ ߁:~']q"m\(vbvu*;6{sK~Q&w2ܳv_, @9rs>MΨrmxs#Lѵ뷛Rn\ʫ! Q#+g~yQQ|Gjcukv;H%HU&ѿO/ZݖYN&XV8 z)kҴK-)cB1b̀2I$~S@|7 wù]ZT$bOLJ5Ҿ$ھ 8ìky[2_ DzWd~g%7i9ȏz '4#/VQ\Lq4㖮qӚǍǭ[Qr c~mݐ¹;ybn=+ @Kw-5uNknV@\KzMq]M%LWi5i:%FkQf{sXwgf%YV^yiIT)ͨI"gg/op8+Z1vLf\Fsp8Vqں A vef|yclq>.#uW3x&oQk`y__W V}E|IM{^gox:߈##[G(`pULn 3.CxvT31\EtFXfK_~}r_ xvTLd:zO7== |HK[ծacm{esַIԔV [A',F#',I|307_ֺxlk28-~"*Jg>=*}q.^2[TW6╏dSDΔ3 MVgG[B,r9xw?}om6[o) nG'>;xc_K= \ZitC3<( u㱯5xowz|C>oob`2,Hјpy2dbJ⇁u~K)u !dw;J20`;#i_a6g~ :4+22=<Vxm*)7c&oڕ3M;޿WXnQ^6s-(?Aϧ>U:iG1׍&J={jϪᎼmE5ZQ uo,7? ҏaC/_گz3+_caMGx j?P>޿':ZC 9.w9ccB#UިEV9+Nu XFhMNӚ@_ZR=Z#PV&*۩jIӚVsYg< u: _JqVȮL5:G ;u;*~aOWһ]"L|E4TgyB[y^ƽGIFU/ҼE(Zts_紮r),Gj-uN5_]C#!pZKGZ]$uGsD%rsTi~:ח9@8SJ%sɚ*y#9=5xjPF* Nrk׾xWOI-"MDaՔCQ哏b#.hw=JjZ\̶vѴLDQ'|?G7"׼7issek%6z$;$|]Oskh'= [\a:`ŧ9\k_z4j}=k42GFR>ՊWhGOo0 uK Mo4 @PlJ5]lȄ3z+x.wi{9v9InmG*ygޠ65WΟ׃zέmG,̡w3m;pN8y<X ximJHgt$hVq (/; $v>{TQҵK6ig!5,I݅gںz|7ٯ|ǭkK,jd ht-7 #v"m;:+_\ֿOOFzOߩUhq3tKUQ_*FzOگ_4ƓѬ.ZtNX|@#CӡI%{Sn֗/>}qkhw!6OqlcuO2FP;:jƏC_]z*Y*24n## dV"Ӵ֚K"[?9-ܓ\ƛt)iZ5q24qQ\6;[>+ @R39'$>+s@wtW ?\zH|W> kӼOKꖱZIcqЎErޏqGYԬ?QH+{sK ?\zH|W> Av 1QFQ`(?!_' Q ?\z;+.=G$>+sKšܶh$b+M*Y#lyN_MOυ:gsInRk8OJ ˠ_(Ԡ!fnn̢ʾN`KMZcRFaB+2 3"S=g5J&*G5*';[ڬsVc;h?CVs޸u٫j9=V;X[땊8ނ:כ)qtKt?xy5an}'#uPgֳޞa)0z摥ϵSNi<6j_~?Uk]ev9yt_KP_Nk)WbǪin˝r({+؇Mk`~8gK-gsgoǫjgG0_ XB{GNWAZZ4(QEQEQEQEQEQ^|6kYtOF\I2B2,j ;;P{ /n>*|QC=q sa%i !U04i7'2T Oo,%K(c{hl`T%ѼR/ ~~(<┷Ҿ(Zm$\0GR>,#V?{Ku>+յm_S ctg}R-e<Ίw4T!SuEOgcs!b …o{-#{D<,@ tQEWXxw^2k165vyٟl?.vq8^#@4z0.oJ""QE'5g_u.fOq$7T|p;Axȯ 4̥8Nwm2HfέZ9)J+v#6J{k5ìs!FUuBNf|G K 5~\݇#fIk7ш%1&އ##dE5Έ׺m%{%tsEB3B[.10'ԓ$MmQXG#?ٴ<  #8 mQX?_??$jT٤e Hz ߁Q #UZ+miIKD4Y䅭9ڒSJ늹CYֵ=gŷb=X[۹2372> $jTHf ߁Q #U *C4)!.XfMpGЃYIMWKnP$ע$jTHf ߁Q #U +G6~G$@mW*6kKLcVw5EEf88@s]"x\I H$S@~fk~W^Շ|_Flg~<`k-x|:*'>گ_j.Gⅿ!^Q}WMzL*FDyr*A#gT\՘U~ֈV,21EW*Ky+2JѷS3+F}gTӳ,a.:cӤ]6quBWAe.1x8ε\ݭ03qsط1WLWS5z|W3 NjW98ʳ*ZpΎ{/y湨 # JUt^zqs`uZdzps`Ur+n;qt[*asa%L9.3ޜ'玼]_{LYJQFIb*Pz)U߉Z[L ѮNq4}( Zw`?_>"|WO1?o$jNvǜ" ~k?+),K ׯl~Q~?u, ׊'־(|ggYx/չGÔW/U/U Q_qìGCޗRTìGCޗRT\.|9E}z_IQz_IQp:='G:='EÔW/U/U Q_qìGCޗRTìGCޗRT\.|/> k/hZ Y}bKM<ս#pHx$ִ:V5^Mi b5Ҧ+WHku{ O*u{ O*66ֺWOĩ/iG/>;EHN䐴q+~W#k&jmSOxnUXdt 0"3oe?e?w_䇧_Vocn⋟ x/RԢ/4 nLDž@qIX^Qz}{~MIy]I."G1 e?e?oA~3OOt* )`6tu3$Ef<0gҵ^|9qxcTVvRIFP͵k ҟ/U/UUO~"Vl-ix_~E;Dc $9gFkB4C fdږٔ;o2Km;{:='G:='RտN|;7W_/U/U;û'5:='G:='EZ>xvotBLUds4RFܤZojZV _:[hb;W8z /e?e?s+?u{ O*u{ O*ϕ5t@Y!<:l~/U/U Wk&*$ܳ랣5/U/Uy |+-kFk+1YEWS ֻϞ>.|rǶڴmRK/Qy}z_IQz_IVL%?eEiݘf#42[Dd^>xwJWZ4{c˃pHPX( 2NI דE}m扭W>%:UχlF/gV=G"3CU~>4D4+ xOE#M7w 8$qqZO^*>mm5uhq$How=1ǯ ^aѿO#|A:Af6Z5{;jv7\Vz|=sggE:[Jn >j`y{s?ǒA?Fz :^ 4dgy~P|ȠqkxE>>k?'/i[G-:toKG[__<IxC3갴sZEt#w1858~_.u N]%WXHILxf$9>ՋkŇl岂9|'v%@Vۼ_'X+omKf4t?ë pd,y`*/4鷟>,[{%rZ}){'E[[;]9rMGI'R$xk4cط5̷vvXL]xbCҤjx;6Z5.!O p˖V#c r4|McSӵ ҕ[R` 7~DR!'s[Z\zYcjz|.*4,p(*1=|[ zG_xF/*_M<,a2Fȧh ~: ~2OQ ^5 H@OhѾ>xkW>{mkJԧh-UҧHc++0r=*~XCa'O\  )N~mJ5Uvno٭ɑ+r@y7 xPt¸^j: :xDɱ<\8͏Gc~*1c zMu8Dx]7 g`-Ex׏5_ kѬ\Ե {Ǹǂcڽ }Vޯuw}/nep|ݴ1.9|Ob+x/x]A5Q܂ep NkK_]˧*z5XHGioA~#s㟈1Z^ <:|=iHԾY"ᦳxL!m7iy#A-ͺF6ڥ9%>oo%'/ m 5ޕ-lG$ k|PCq}kçū>2OhE+[<7?u'Yk^,Fמo!HUMN cקj?G mΟgGԡ|S^[hE@cwd*BvO? MZ-̍V"c9Rs[^7g;K[;WRC]#N'.#=֧sḴ]:K9co^L8! d?D6z,ߴWI5,$@@tJ )BʬBqX7C:>y '$CeW WM7i< Yj-6gq"G8 *xCX~ Ŵ:Γ$խ;0 +g8W}?Ght6y f53PfE| !`*~~橣YGXˬ@|5WJO# qqc?tMo /F6O |L|=Z+s7e PS,RWI/ݛV_J?j+?CjIS鏪(7HEcA@i=Ơ"TO-e\ظ9ŚeyeVDF$&In«i}|Fw4l<9m֯}ʥϕ$Lm$!Z÷3gl͆x_nt}n\CU72Jc4{I/?C7 iWşmkRb1368> x'I}!ooM61h3hè1\Gs㟧i:M7X I{_s _@/4]Z} V-\ҬG9?-x|:*Mth}P/5XmcA,`?y?Uyw -N{82ֽ6"A#;Wf1c()hώt*5ӡ fEȪj'c©"Y5~JW^yӑrɭ8Tdք ϥrNGRen+ZcK`@:.͛6I+ [ rf+Vlw9($|n7S7!C9 ]^+};=Cn)V5QNk1t>Sn9Wf9jӱSosVcz u^|vơ52]f$ln%k(x:f,Uz<~u WƩru-=xTQ6M|MvYaH ϓ?O5n7OOﻞty5k?+ί Yw`?_q{SѼ=iek.p>u!{A şW|nV k$dh4F{@ q|ׯAK^O͸ЯM`#(+/]]G!z?>'T.?G.?\!z?? q|창}>js#(+/]]G!z?=St_Y?:iUkN8M{vFA͉AlF\W!z?t]CGҼN/,KE2pV<N sbFN ^Ƿc3b%>G%{kypw_xK{n)b'N+zn-JW!m'5(PJ<}?Ὴc#v|X E|r\N'}L)ߟKT/^+~|C5RM;zXݦ\/Gb1e?X( 'o4[ 7_&q~ F/jb)AwXXL /}Ũeɾ1x>~6F-?gxKYlMk2zgUWO=?ظٶjM1m*%fm\ǀBƏt~P'E Ɩ{mڭa~Ӟ<_-C^#;t[%sZOɝoUX|>^5VFmw6k5yhKZa؏;3;|37\Ρ_r} Y3Z|nGg=؃qp]'[3ו?>ՆOҷ+|'w5<F?X?zloN!5zՇיwژxq #ؚj`ua>C+{E!<~㛻Zݏ-qP|+z|9bh^/?.׏>ޏW4a;~ V?~^? ƸQ2ޡ~M@ |dZɯBovGu?ٷw Շ׾~IGL nE Vƻ bqI`(%x̳ߓ_M~6sݭ2c] g!}T˩O[*TjgTQ\^y}^ 9ʹ)qo y)83WiQQ\^ayc" ŴW/qo2yDr1*{Oi5q^J.]m1m<CL (~/X-Ve\I G䁟ntU].uM2cxVG&7(ejZmYى;4UEg\,!'+K}FVO|YxC4;2>U@|dL(EP\}>{Zs#CmxR0`'('U?G*? »jԼYh散^_GMZqS!sQLU?G*? »j)ª޻(UgAwWmEq?7q ?UYo]Q@~密l{[L; }djl5]F{1"Yݘж1~W<>~W@> d6)"0ee8 Wd~<п |gy Wչ]7]Q PSW.[^bi}7 Uӯl&XҤ29X8۞m:ѫ8;~y^'+4ixZY vR<Ӏ(+29rGQP.1Z˷'>*5qӷ8c(fk@pEhsY9ЊC_/7H_Jxd FFFRոVTr``U#H5Rl Y+S=zuMzn:VBRZ' l%2\gU|]~!ܾJ)B?Тn  g.VJ1<\o&?H:D1z|MOy.fyfvWbrXeH Yf8j+E8AB<+1?~&W_яA&0"\%nU]w 28WxyvG/.u0 ZNu L9q8b9]?%s|Gǃ>h'?iT~#/WBMtq7h$Hbj@W3=.Z-5 5Im.Is8##85̧m/ۣVmGryxiLb-{?)9Ǟ%3wB~eσ]J {m5G\ہ0y|Wm6ծ^:=dw/3#Baϡ&ME伿8a1"AQ55m{>6YXeA kG<7kC%֗[լ^27Vk叅&.GiOyT}s7$ea h>]G+-Í <Ꚅ;x;Y"F E)=r@Smޛ4*Nۻ_ex|>)6ymi~#]J{RX-k |q_H~ʖB>!62мau2ik`<-w;C| o PA;[=Οo!F d?~tvm︯"wxYѼuxf!* ;ȇ*ϙ}3i`dc$;aL$d6mwm|W?c޵|EJA=E*޷<g?w~!_|j:["Hl`%?PԐd [ֽx^CiG[wc>w~!_|fqۍmиH}A#SW=E*޶3̾"Bǭz?}k>ȇ*ϙ}3M[:^kQ.(-Qy[QsNmldP,o 8.]Kp^k⿆^3%ѭ$dM\|Ƴ]KO]ăP-p ARc%%_H'8Qsӵafokg[fOVXJ7($?/`=ƹ&Mͺit]Ii%D&*1,۟/P}9YwÝq)RҜ.`;#ފߕqA0pyQ oHjl"O@kh:'o`oFSߦ۾۶o}1W+gv߹cmNzsM]/Pe 5PZ^#7\隃(ajbA1R(m/PDcEƒkusjB+3K *==OO1+L_ޣsP۝3794PYpyQǧJe&H!9#´aDv?pf̨Fɽ>b+$m7:]o-qAd$j{b:/W]q?t? <3jsV2 i^Xw1'r})ku.mp/eXu6eC ưH(o{׹h <3pj2 1=Գl$c ; zt-J{Oos,O rFYAӷ|zoxQxG\v!^=˩Y`$v'%V%"cN{^[ZR-7a6`}k7cLV1-'20R6UH``U~xZ/ \+%AWp,' 9>e%b\_ooO=⟉R6V>H-$I ۔r} yEo~i^9歡xrrjQ\kIcߴp-=!#}otK+Dx4^8UYQJA Zu^ֺELJPQ06FWj{ +[J,TW~':>_,v=u>OmUaoZbY'Rɭ5mʮb{ _^@z9?|?'Gnn}W4]Lݐ4M.1qoePMDW*OWsIt[x-S^2لKHG^IC [_kLj4u?ϧEճp˃qz?L²y3kGyA["ӂrC6Ojƭ_=7QǑowiÁlV/qڪ|<>$? 4V6X8uْI⁊ĭ:i3 jDŽx͖5W!u{]r+KOxz؛mL6 hb~ύS!@b__|U,n߉%I^h6q.v18#ωV/5 >C h:x-Djqt7<-1_Mx]VkHSMhk Ƞ $Fq)hzi#nr#۴sW_6g?x3Wwv#`OJ֭H"cʥ[=\5߈g-F:9ח8FCvUyh p >#< ^$W\;BZ KS *1\qu h( gI$W:2T/]2ÚEΛjK[cC 9D+zz>3ҵ'5GtCZ'KԠqNnŠy# Io k:sdj4[W1% sZW |uOC ]Driø\.HtEmm v8P8Ž krZ(Š((( tZ竡KLJï"PLJï"h(MOS}[F8YWtF\Ң4uc&? ǯ%t> Ω]OztWf~cÿ׵{9~c<^}?q#u_>Vs\exͥIޢLyCk9륵l_qO ST*aQYVZvXZp?5xUMnnzVEq^}I\ѯV/ɂJ kH籫 *sk&5v9DՍQɌb\-YlWbN5c:Lx穖z,LZEjU޲{5u鞔*?zzȬ.p; xdlk? ߇|,w&Ց"?7AY`FeWZu})˖oC Riy19[@GH=}ҿ6|C C:ޫ}]ey[,jj^O%^IebzMWrSGWB6[Q^k?+ί Yw`?@5+Nn{m^!MjwLO1F2?1O3Ë>ڵB?2#}kP?g룣ƫo; ='dB6Όsn};gZZ5١S#OsR,xPz|TeD1pU ;zǗ>1i'o^R7֚ .$)0bG qiz Ѕ+~)?`Y[B0i)madg;cPM^&Լ><MdyD۾PFH'{ןm~ҿOӿ6iaX{HY$ZN\~~k:6ɴm~ҿOӿ6ϧ诘?i_iG+@tm;h}qϹ,~k:6ɴm~ҿOӿ6? ϧ诘?i_iG+@tm;h}qϹ,~k:6ɴm~ҿOӿ6? ϥ5Y-g6H1$^}ijg׋?߸jgŠ?;GڼY>hx}ϱp(?^,c~}ş?v; +Mq{p#yt =m5}[y3Kai5Htu|1U$l6Z^nado& pq#1MseA'=y6q/SA j >-Ffk0VwZD'O:7.ƪʌq\//x/a{6Hu*ke=r<ş(wK |ԴW~$|{xΝŏ}¾YӲVWd_2f0F5_k06^1ķ^]n=KG>7]~m"};LEeUP''8'c{ °ھ>j/eXF!x6{vj֪۵hoX۝F%idZ(Y@E~QgUwhd]>S~5Ue2Gf둌V}-f v'tF ҰB'>;k9O ?Pىt%͵͜Ӳx 6g!_ >$xWk+ jd;ox6 93g׭$gNѶ2Mv}Fia#UUW?zZ+,O&wONb}>0Q/Ux/][(@Ϯxj7 t%͵͜Ӱ*@`<G3y:FKH tZ竡KLJï"PLJï"h(EPWuW6=L%Ѓ^.;_Fw(ȟk¨8ysSv9qZ8rUϸ}J;lnS>Ƶߡ;W—w^g8pЎ{ ~q4 ?GFx?XJ, ֍|˳.VqXz嬒"2-&H#!<\FՃ^ׂJ Xe=*2+ʭ#ϱ[\VDrmZI_?eEVlY1S_15R|T9"l>D_ ~>bj6t}L>~Qׇ,-\C8{x\.#J %9?7_ +3kRcC3?l9-<=~`T-\0w$3Η+=[DԴ0N2 pZÆ'dh}gq2?ޯ#j~7[Ac;{{X E' _@1 vTP{'o:/ |u)}me}9s΢- ʩ*8~gÚBVe T&1W>=Oɼ?5Ay]1s񅧇|=aC>y2u )$mhsyKcKma9|:е*@+a }=q6d/=#:q[~3A/KNSWq nU7Wp23|LnxK aex [7DB:rHԇVAğ_-R Z6 ڷ2$,.;Hz6ygE4BڟX˿ C. WOO2j$$PE 7`Vz$Reo_Cu$=qFcp5|iXk G͖܌y"R0:* é_P[IL~#u $ʨpnj<׎鿴dYM?^X=p[&@ ȝZe݅@+|=#u>U|/୽ׅZ_$G~YVFbݑO]|ԛCw_=w^WCeGW?>0O/SG6?_T(}vw|aD_l'?Q0SQ_,`<UM>0O/SG~#a/>Yyʚ?|aD_GeG;_}5i֚=ꆶu˒v!A3\ZLj#ijZ2FWO%.I v#1Rqȉ}*?O5/CqCzqN/N/R?;Z+R?O5/O5/R?;Z+R?O5/oj\h,*9* ?4 03H]j K]_YO:xo&.WH g\3?5_7/,}gW{9<"oL 8\uΏ|OknXO.+s.  8kq4}s߸~xOúhUXJ Ś5K~Ο [oBUYI8q&qp ӭw`?&]NoNqX/coGwֹib+Y8u'#aw'{|Y:m_Zyt#VwA9M`?&Y ss]֏r~CPպ-AO=rz WZmw.oeH08{+g ~i+ xOJ>^K{Svp;Tυŭ]\c-n(d,NхUrsǹ}s߸>wA9M.KW?g:x4e7컴] q+*.'7+Y]̆7\*Fڪs߸>wA9Mq>>+WO i{*K)gpPXNF;#m{4Xii]ǝ8_ Wa ~hq4cN]4gPBH[r;Rfn-郎1X ).[+agbϸ4Tq]NoG.'7V<c7ú66wEAohld*'yOLV?eh 4e ^}33;)Mz.'7x{ᮄ/44m-dyE!IkMORФ?\ysVkhC].'7-'jOpCO,Zz+@.A8ɭ{xϥ[ڼ ~QGWO ~hq4 Wa"᧎{8Dz*/JYUs߸>wA9MsV K`xA!'+`?&]No@K?]V;Xc߯4Cew𭾕ֺrX"<=q5Ěj1:RO \j;Ē2F"cw2Б{6|AYk-[qcu?, ?J5}+V t q=U3fkG^!&3 5oGaR{W;قB}݄VR:Ȳկ_{3f̛~Obw!Xbu~c9,A$],n|9m>ֲ] REϾ U NwVU:H!]vD]j&h1 +yM-j=ynI//>"xk?F 45W J_OZ:";uN6rjsYff`рm0rMXK~ mXp5TmІgC"V7Ԏdz*iO J?)?_߉^ J3.Xtǵ 2,wdۺA7:m :ӭm{ۯ\]F"$w u[5uBrWiUVܫND)ȕ3|u׭>PDWi:u[Fq̖dB1ȿh9 {ԗ?6~ '58Nh#eg䱜 {SD ;H~"xfh4x$8O%~x_R?tã\.7q'nCjK7M F9e&6ѭ=UK#+lpNN9#)#)䯽wSNtd욢HZ-~0>9?ÎjDFڊxk÷~$&Kw[@"XryĂ Fb$HN7a951xPeKn- "-%w@]y^C7L]j27:{kye@. aY `6t(ǦezJS#|?+ľ߽MBLe!  nS^կF_ܪH1:z+/sMNNJA$4zxʿO%142vY{gx1ֹK*;#˼v ݷNr@DsM M?o~ x^43-\PW}sM=;a~"x>6KˈUU;8mbxI5K-՚[\݋1&Al e}+?G[+[Ě|D4,coyvIuvΊ#Cuo{o?:5mvoGQXQbH8@Hp8?|G9?&誟c#h?H tUO?4} tUO?4} tUO?4} tUO?4} tUO?4} #4hnd"0e`\5<_gC*./;wfV=v5sMdkCXE*%$XI,q T?GҗuS?GET?GET?GET?GET?GET?R$%<F? ?Ƞ ((((((((((OGE[[9  y kZo%hbMB8$#9~'mχ؜<|)?W4j}M>nA0Kn,Wi)7nJ?+:WdڏDet8|.|F]P,۔$EI\w Gs']R. l.u)徵0CH^w֖|]Z\]'xr&EO ][o 2+ =x^%ŮxtkOSd+l&T+c`{^RPoi燥O5.<}iF5{Qr#ïف<0>|W]OS7@ͯ深PQ8Dtޣֶէ4CVgxwS{9F9vK!YI⾜Q}kde<,>޻r1<)i_.3\i RI `}:WB|'w=>MM__4 4c =x^T͉%qAgfϬ!x'ðkmY&a$v`S^_xB yج'"7ֶm??O*ׂSI -]"oo{*=fS* [Ot-HӴo kŧ@ `A*!>W HO6}`c(m-B F~=BmH}S␗>5|Vg4#Z=iɤ džmY @A#86}`c(ʹX? Լ_7>#.q5ײN䅔F*HҙxÚ׆MuD) 3  _O6}`c(־x_zpzKfUxPGjѮ gSItTqFTPGJʹX? ?m??‹Y^j7 ҡx ͭFi"."!d\9&G>k>е-;Tu4Q}*JʹX? ?m??‹? ']^]= .ഭm@DG=~Q_xf=!<&Cٖ0F#.tٶGm~Qp0<i~+ kK&be8cFyXHLu@ x(-_^1[޾3‡qLj6}`c(ʹX? .=sך?}arӻL|~ٹW΍ueci7I-`b!u2/8"m??Ofc^/ J[\j.zf14燓ᗆSDtI#,f|cx?c qһOO6}`c(_~xGjMt'c+ƆfDY DQ2U;?S dPs1y}b@z O6}`c(>iE׆h|yu,Kǁֹ~ \j7̺Gwr3<qϭzm~W=oh~tK L g't9\S|<1ӎNOm|7nmLˌ'E$Wg$ҭ=&HԴ ݪ#}UwxߚAk~mmť12 @(@O/Ldv3zΣceonR &`J3^I@h$ U8i35 T!;HQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@'N֭wm IP1\ߌwGĺ;g˶V2sz8kϼ;$<-W[ |g&ilu6CwF(pXHך__}e<_Qe<_W%xNZ,!{\j =5VuYb[![j|S<7kS^x6RQCom5eNKg`5ߒ7f|u'@e5Ht3) DA 85_,s wEňI5LW7>WC^i;&v08`̒1Qm&ФtÐnЩK}mTVeǏm<5#0ho2yh̶_?-<ƀ0ho2yh̶_?-<ƀ0ho2yh̶_?-<ƀ0ho2yh̶_?-<ƀ0ho2yh̶_?-<ƀ1M M~v:ѿٖ?GeǏi!ωu=eO 7E͌F]V1yrF"v՟0p8]}@ռ3iLj= UwF_nf[/xٖ?Ec xJC]爴}jIגHcY@.ɀ; } YcQL̶֥_N +M>mn`kz5t,|cٖ?R3̶_?-<ƀ0ho2yh̶_?-<ƀ0ho2yh̶_?-<ƀ0ho2yh̶_?-<ƀ0ho2yh̶_?-<ƀ0ho2yh̶_?-<ƀ0ho2yh̶_?-<ƀ0ho2yh,˻B/tܤg*GuCm&Y?zeǏm<4OKlMGH{w}&›sl~^?15?%M04xO+g̶_?-<ƀ 257 222 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}XjCSc?}'?°. ApA|__ h<=LxZ 'RX;$r~ڴ\ZGgέ{ImI*SaԒ1$MH?zFO[ɨi3G}GMlE]\Ix;99C]/ |iTd"Ԛ~]*kX }^xg¾q vw7r>8Ѣk Vw JLjA$4mz;Þ#AhL[ R+0 1眀>B*rIuO6_eoDQ >|{*U𾛣 ʹ-b#W܎9 ^G9 |&𿊼0\khhy1WW qQ/?K_U^l>|?V )SF[q o ł[y, XA 6HsbI /u?_gweY:LhfgR_$yju_dG >}^O~xwNbKS/ӘcU1`FnC|zu֣ep|Wkf]b[c*b _jI_o%6oDQ M3᷁gǗqͭlB+MR o-YnDqE6]s ROxNu]K^gM2fxd$t>l>|?ڿ¯ x_~6Su{xoHvZRtG{lRNX1ڳ_/5 kobV[y2Ka9J7ĶsS&%N[Ĥ'csM{[{+3Dkv~4+.n- ߼F2[>_X5 \\<YH6 ؃Εrn`k>0Dt$8G%Oar&R7%՝֚6*B&]|A[KrU}}Mvۄ+ć H=GAsQ. O$V΍-<_xwP3R*+n/[ 9 P^W/Ldj-bCl "çN3ߜ Y҄oʮsp1X+TqmFm~~i]N5*`(H[~9M9YkQ~#?PRHϑ-援Zq|=m(q|=WMsܤF kxZuue8vę؁cU q޾q|=Q{C?.|kx񾹡KqX-@u a eqБT_>-Pk`Nu F^|T$s@=5; ^(bwQwh>5>=xA5F oy.5)>nPDZ2@a\M-?4,Ѿp@<_/1x?_bG(fwџ|XE&|EŪ;qP:IjZ&:dPMmRXZ(ĈM"ga_føm(q|=Rͭ[|UE,:m4HA챏$|0z޴SO.}+@ԵfÖrnGբFQ]U܎+wQ/1xtw'K[$|Tz[kj]Hj67F낞cB.r=jA_{'tc 3z/o1G; ^(bEω<=g^ 2Yw܂.2rwcapW;Vrs^+;=X-%)Uc `}g/o1G; ^(b^Ŷ?g]u z{%+S0|O +I, }w+ig4V &ŽVvv)H]p("J+wů {84MM..R &DWTS qG5i ֩[Q(1T w֐\Xm {~ /snM'C7%2]Dc:J1i2D|d<6rjw/$K]6m:F WU_80B(88\TOIEtnAcTcF%Kpx~K6i=KMj+=ͰwZdSLdEG s6!||I^E.d{D #gCQ%t~.:][nU{xPo֭cO _zѤlB!|05?s[;NIsq NTFgtDn`J x AFM ;<3wgvfvf$bI$kj>[.̞]4ύkZּ_wo˛cF c/d  Uc:}&W5nMtqx; U"A "3 VRq}E%}Jj i=մY>tCQt ^XX5|6<_jD/72]} |F᫤$Ia86 l@:m*>E`G O;w[y` "I+ AHiV]>Fn{n%y^GVgf8xtz_ xO-i$捥Yg}iW\Q-k7Ye3#ˢ5\|@ƽ9eaǽ[K;Ѕ&ʱ1B| io[]??~Ӯ<%?5C Ƒd|+Ť֫a1/p\e[pϽ|/4-&\-${)oH7Af%%vݰ׺V?ԅ}}ևh~oSuxK M̖{LH6RtGh]Eޗi> N\ViŽ1$ȣN>о^xJ=żiv\ cbU11, F1[?e/iY-V)x,.wi-I>nYcOͷmK;T b4OFyK$L3경y2c´*[G.-n1Jm.diX]>R|]!0#krkj$!OK#?+݋qWG'Zv 'OAmnbVbYrI$n`*J ( ( (im'G|Ec/|M55$uf {v/R$|2)%QbʣڞEyo//L xMԦ{xncW'C7PoCF~#}Ѽ]]NAZ徟alg,(%x@P8(CO?+CQ_1xtxXj6cz֎%xxB#4Qv9#w?ƛe$VW~ ]dSGٯ|-ge$KiT2 +ߗ{_t*[W" 6@yk$w#?ܗ?u zMƛ~'O{ Gth)xnɾ֮>?źׇu HrTM+ RvFUre6j>3~)?uo.# fg] mbVvd)+('+JWo0krhf+w! 1BK:I>5vƬu"OAm:ͮV(3{5dw#Nkdh.L^c/C𭭭ƥ?|P Kx"SQ` _u~54n%H rGCX%)px84wV|7/C/:9X (=Эdi?lzP3ƽBzk*iǫ3éRis 'ELYP< ˁCNk9_gNQPk[Y& =L!` [y&K}#>[Znn!^bs|'XƽBz=s<Sx X V9-X@rG rzhbS-/TPQ}>qo,31ɉ$f" σGJ/! (EP_4[|-uCg{MO]?/[]&;˺[pr9!E@6.AV?g=Gš.d!Q}Y2O ײy|ø#Y=֊:[ߐzȹq̺륻IΝ2ɸ?$ >_RwxZԯWźd^>%K/7k%L~҇8``I m<&.UVukndRFmF#RJ)sh"uo-$',zY7,3v6㍻ssڸ{Vh_bᴛZR Wd"?"U+Hk.C>ESZI+/K~GV߳g5+V^#-i^hm-n핢?xHp0~Z KZ,=]KjF}4X=k(O g~Ͼ76%l|5y)lt sKjIbiB~Cи9!~>632c6R\3ddJ/_OұF_][2&8Hҁg/EeNj{ ,6i2']CMmP6^c^ҴmzSh(I R ;higuhu;T.U@'OVM4_)Y_p$pyGuRa 3LeSr{8Q@`hSG, |_2j$t0/ E6X~/Pӵ׍{oa8{@Š((*i,DA#4l+G#I膷<ߴ|/k1D@z/##@ldֺ_@-[ɮ3E "IX9 #+T4jVwz\#]ݜ(F̤5b۳OXAXz֟7V:y[3q5ݚJJw/ 9 f;zUվxFxz $ZyB$|8P<Ö NO\V?ڟ.~7.U%_ܸau%א}y^MO? kީ隷4/Q[epr0.r@ƒRE O{ *O[6hwoWxZhn~KCc [yS;6X@/ӼyYj:yg{,Qe ]c4G[_[ Z_mҿ >0Ow^Gk up)c>VGVT<OW|.knqXh~_kyVH2FN=q诿]ty8xQ hV|8xQ hT (@QEQEQYZ꺎m{ /pyX,f$~%4iĆSK<i^`M6H`ba1訠V#0U$ɬock6zŘbP˔8+Ys =c~(Ef$5KV#ҦH/aL 5U:7чiPEPT5_iL.Y''D,;zگxM"^Oԑo]@F0$GL 4goih5-ַhkZzL1_^@};<+⟂75KAj,fmFwM|7^vxZ7 ez?4go??tˍC*%Di$Fys]_*]{k_=u jR}BgҪ*XȤӽj6`F}:!w2\2)r7qREPEPEPο5/:=OJK{+,q F`Rم@wcϯ59gG 5o^xI^-зF7KDzG q}yh^h鶚wGMnš%^MKvC!X4cqS֊Z&>5&?>.:y  _ oMWڎ K-ew>-*#$*.9Mm*MN >()iJ"P81P&M}:.-=[N J?|N- /_[.DIhtk3pq;I$;ÿ.>q O~?5o ޕiyudw@*-)u@LF9hmt+P0xbznj-s[~S$v.Z$Cy$rˢj+dnQ<*p98-~,^{O7 |]a08Ȧj_<Gc&oX[5έbDmqRH<h_?;ú楧M[i:%p_14;<8Ҷ,|\]ϬΆdk ԡv[$~PpStc[vĺ-m]-F&hb^*~)1W_lmo;ۥ TpԜ Cc{O^ B WZ-mlY% Y4hZރN WīQ `Ғhi٦|]-GEt/K\zƟohZm8#Fb;3:bv]49 "@2Y2[Mps A9,ĶpOោ<)?|H%ak5ooݓiӒo9yC#$λ7 A5xW-ixTx+U:բmHnlnoޞKvRFk)];r+[V>HѼ|g;xM9m)>b3`NOcX xWúޔih7ZMyKth< 6Ӝ;^5kMլ&e`J @5)Y[)˛>j_K?~,>6c:eW7PA$U8s qbnfU}<~XtU7yFנv[5/xf>E e$ }˂4[ܐ@ߘ#S5'v-Ņ֗9dPB@*IH*mo_QE%Q@Q@Q@Q@Q@~40~c4F@GqU|_ iQ@F?_v#W/;ZTPo֕jeoh57㵥EfY<_ iQ@F?_v#W/;ZTPo-joD7skqtO b|I~;5iojeoj#?4k" cȴojeoj#?4k"Ժ4è^]~ HKE6U8)(jeokIgmoQӕo.m*;dؓzUGh+E ~F?_v#W/;U?]Gh+E ~F?_v#W/;U?]G{x\VM@itj?)#,٢xr]GIIg!Y% r2nl=Q@Q@Q@Q@Q@Q@<_g^'|;Ѽ_sR]F[׶ @pĶ1s׳0ely֥Gšέ-߈ZC\Ek3 ى*@9m_#@뿅.mwèީ'ٖy$?!ʗWQubw,<8`uM4fdᶟVƽs#kKa(., BƓBU 9Pȫ?|?wim/^+nMO9*qxcGwm7u_֚~'_#7w5)}YZ5gD?>Y>dvsm9- ~כX$85[{oԦBܶb]fOjr~՞cĚ> Kk+5{QzmVmMy+3ß ._&ou.m;g!CeB -_A4N..uedG B sjGj[Hյ{ 8]F*H>` CxH`konze2α|͉Їg9cԾ/AỸt \ w0EM.G֧a$ FOQs>xkĚ}Cu%֣kei,]MK$& :OJzi]'[?O/TӼ/ye?.x:2B*/:H0RBlVYl.4ۏ i<"'[ۨTpF \Z (㿎ig:z˨*, p<)^m4KYl =tĈN 600 1000 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((+" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Uwq *i5zEVpjYER1L=jg LhFELj3RD g_qIHʞyڠue0hSSʝj5 g n+ټMC;\VH.@^Y XFoI2G*K2z IrcW-vwп+Q mmgG 1\X'-.7r5nGH#Yͨ43#YnW|dH"x.g/a{}?ƒs1`!(OI40ߘ@"n^=G9jUf3RH O8 3βn5 dOJ-/KGР&O5_±>~+hgجw@pO_)94E:Q%:-ZYisbA9ڇ r7U &i%еKi X 3җRt[{ųF3Dbն2tN$dU21ry$XWǪzϩKՠ5oGqilIa}~pNqb=UKIH"$*=A8#J[W= E {J|TҧRKiMIDѩe `\3k כxMLlKyiq;FiKV:#g#=1+\iV 5}3IgAuT^8nZ24Q۪nm,XzR]xgB?tszOE,Z y1}%Vk* ᩭol<*!QE1Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@PVyjBVj(BE[VSUb)F¥"zTt?Zc N1Q1ejT-P*ʼ \Y[B^XOHGR4Tִ{F)q#Lק͕%P6@Пj;[_OY7+O[wWN#2Fd^qԃ)BڪCnvQ'ϸhfA0 ot\E+Z>e WR#jO>_[mYv<(đNXgq]Pm UaT4s)Ƥm%t:u'NW; ڥ֓}TNF6Nޥ{^E2OQ;޽PE S.vN*pq_ڦҵ(GwirqF?"2\ylU ΁B\76T97$HIuZ5͔;}2&Brecp p Ȫ'Q,IlvA _lΫ?{C3ıMmsyic>?&9h5D%ft~|sxqyZ#忞[z4; #@toGns WBѹПS,%Ԥ\?WpA@N=j{y"hԊmULeJښӋiET; njմU=apt6ьnI-NAGtoݏt־ KQ|W4QEX(((((*;⷏̞D=X⍀^\$/3B'+inCQ$;Wڭ^X>eL׃q#k}%&3u+%dN? 8GHkmYr\YٽI<. U;|c}Tim%\aa/G+&4$Ҵ#GjĻ6{%KGpS?E Y!+J6/ٹ-Ϟmb]QR=xʴ%kFy;QCg#;ׯO<gj81Ucu[5;tKfkH;_Wbi>ǚ_|'/8#Y\irpdTۯk=V4}CKu ^ QEQEQExT/Nd@K0xKc-}y p lHsSENI_cҭn߻Oד c!՘4gi74ƿ^(F8 ܟkv]6kv?y>~lۆKy?5kx3J}-E"-S"n0̃G2伇$|U 6"Til]V%82Fh:4U24iWR<Ꮒu[6Ų9g@YI#3^i-1c |;Krʃ詀+-q'Xwqyϛ4{k-ex;PlÉgʰCwj9 ף*$@DS 1QyԦ3h%-%᮳o|ņfN_k@ƕi|#uF( _B\pب֭ ͕LwK*/<-V~+5NJ[{(XPɻ1YZ6uI+)gQyjʮ"بR T&ci5) ﻧڿtk,?  91^tX tк-*F:?jmФn=hp0\tw%~'&;\9B}?x֐!S̕JSXOJ@(/rYsȯ6%ITH]s\Lz\&5@yQVK .E :7zS["U]=a,s | QM75L:v ҏ濑S6bxRk|Ѥ*vՂSu}H_qxDдF:0SH۷Y? W!#.̧?61W.k?cպx:ɺn]exNsJ1ܓK_yW 6Wya vS8_|~t诶@-rl=N}WT|wgg.OxBI{kq4ۆ&zgWd^f*GOM.̽3A UG0bb1)oGĖxe\H{q\W- Xߩhr67)9# ЯfORcʼn_KU& Džq=2 ( (?<#ՙnGm}"+Mu)Le$&{=8C7W2Kcs[vmL+jv3Jt;a6qd.jq;B^OTPӡ0*#d =i5+]"fyt=K+J"(stq4Ri)2,F\UտQ柆 %RK=2KK?chp!QXW$eMu+=B[1x23QeJ E22|PX=k|{xim'=3Lި+*RvFTm wSo @zo>&+C{N?oR^k4Xxg:SUeQ޼]{:xlMK7j?T/4MJ &Uzm>d^TKj ?x/-NJ0_GߗZ.~'Y\;GYjvW0_1 9,g'/UIjgmbFKd=Z#SRKWݿR*izjK&}Loc1C\Iɏ`qᳪ_E}\bvQ$qpz`]=/{)ˍWW<ʋ}Hc8*iFݗM6τ[\&w6wrHa|`,OsZPפEJ/X<@;zCa~`2:@;|U`9F6i]9;kХ*P[Eq4W]\#oj}O6B(S񶑤JHvu=8֜b)(O4 'M9q |חlk,12˹F\k]SSydyw9,OrM-E-mlIn^y$.WҴS}vŹnN:Y ]T'<"ێkv#58 $cv="w6Z_-h5ӌ$ČzXjuRxcS=. nUsTbE+[R=ېp2} 2{m?Nx/TT`}8h%ծ鹉?~"K* pQQ|o1T'@ ~q߾|VCojӒ~`Yt CźiMIq>3 hRr2mRVv9 '$w|Fq=k %#z+𾴷*庻 \%k- :0%C#$9# qRVfq|CU9,{5R}rY#(lX= 'C$O?Zl]r! q\SIQjEE/# )lUvfG8 W͚Y@*XAB$Mt^aH{|Qc$h|5XJXP "Fn%u) }j/kHBX$7,LێxZHwe"4$S)3p1RQOPC5ḹWIԑۜl:='[,|Isii,k'DQH 5^q6y25V7g==ޢ-8#9^p+I$J^:2T׽ w-.BL2 ~NtB(JfB#$qU707@>|k,9KjBWn|?y?s2|ɐ|Oj׍.cm.T#*ʹ9,q"UXJ*P՞n" t6REsDr=v@<{G:z}#җ$n~8䏇n<@1m˩+(FNlmt$ [ĺsqs3Iqq_4pM,W4}^*:'/.\^\M"e? ZSPՙT3DWQ"Y" |gbWMXq?+iq#y$m:=[O[BC*TC'Rq޷" dJg1s Hk|Õ`0k)6n >GBp|ߕbeXE\pPOF#PK @Fx=j7Rew*O`F?29aS^=FI>uiul2\tCȯTLj4 +xo !v~=>e561۔9>MKpZ[KVWk,g;O°UUZGu9"!Wno2UEVm\Vu˥5ܪC RǜqPEbCY<̓n称FR9bcdl։UCgXnnH?Fz9#''֡p"ٗ JWVc>JͶ9Q> s"w>Ee}*Xk-k:{tbY|~Xk<5i[\<0+|~to~WCs)54 2AΧ(JGQwd?Wci]iتR7x 7;n$BV'M܍:0$9-Dzz XO{kekk.橉杣!U`X>(䉲Ž fz 6.q*YyqqϨ׷K.TSy޻v;O^#}\Kp^Yh#hW,Le?~ׯ[ikP\Lg}7\J1(>SgP8múd*K_⫫\Ϙ }sֽJ!/ $c/\zx 5>&Ѭ"0X .2ĝtM2E,v,Q,&э5*sQ]AEPEPl04g!nYG I?θ˛Ym(7]67i!zcQU$LOV?GkB;XJyw$Wi}o5ۈc|~GSQµ(,}{UX!Q`KI! רUa7|RZ o e}zku_B"+kd=08ɾ$BHrH+GjN:Z*T!hʝYKqHxOj-0\*;u5>YHێiԜa!)jtƌX7!AKmCbdmԀpkcȶVCd85*#d{S^jqό~ $t؞N)Z7VF*z~5sfj?:=ξo3ʪԗGA.Ih{Ui1,sĩ_GbkϵZdFyQ}:5C&SYY#jGE02r qq^k|AoG/!Vw63_<;?R~BG2 sH#iJӧ&o%`n>ǏV5]i"E_Oֹp;cZAGmsӒ񭥉$c6T31]v[~/d#~WdKo Foldl7ǸPqEZju]D08jVUISnס^|7)U/峼˸u$H& }M{Rj cΊ-I/.,u?JùdlTzM6(.;Ŋ)nnd8TK3{+x? 4fO6bF:)#K;̎)<OV!e\ATR[w2SpAGcRs`  從BMRdc(Fy?Z[ {u⵫Nc5tg ;Şq&s$rOJ\kXPԏGqYG!çY[֞4IXU,ɮ:X:T:d `q_+ODA,4->{&ǩ={_}[׼iZwA^ϵ}+_ ӖB*2Vn~^d(uR;\$8o[6{;O~u6ipYF0B@T6%`ú7`Xu.5 ~Z) 4M/B6NJ[qօPEPEPEPcHLcR 4k1կ-a\o\YQ]XeaI[oÍ<_x2n|1"׼DjBW9Vv`z -KCҮ\jq!ӿyym&.CRҥgr+@1aoj~(ZXŦ۲[CIvs' J͓kR]\϶&`ްbP<גz{UhcR2(ܐ))軅<3F$p_\9g Y h:ɨiF@+m@Fas,ޥsGHZ&I׆Շ~3WT͚=a[*N dj&DF؍Ӱwڷ$xy|9;8-Dnrr? _oN+סW)j53J 8Ww3 P M>+-BQ#ˁUnu-2O5sq-+ 4Rf4o@lS1vijN"V%N޲j$OzT-5$5hj嫩wOly╹cAyQ6&?+yx}yr}> Ep?Wb(U#LV 2o#麲oCOe<~O9 d ~tSWmͩ[=Hx#L+Bݵ1it64wZԄgT-ʪJXI rjgc#LJ1A+ou|_5Xw23J*}k qwV (D_X\L34M`AuŮ5*3Ť Ў$~\x1ā.페1r1iԶW,vpVAgH⻏x\D<$q=:ۨ' xRЃnx[6{ڕ"+a{_mE:!R|Q崓) _Nx.M3}C< ![ I>/oװBweU,Gn? ʑxsPyH?xս|%'s=&X+Jƚ\.~s*U$+4ex4G<7$+b,jx'mu2QR1gnzu5MW.YBO:8yeڿڤ=rtұح%m#BHgPp=֤ҧOO6ႅ[WX%@Q_[jU4`=UF7ǁ@¸8NaI_/)oحn$/1=BzӣNUleߧz/_Cy\Js+3ׯJL\sR8a:;ͭ;)zkYsHԳ7G?g(Jf0G{KPg.sϮ^_ZeiBsuG>cG_k/ϗY0@x| Þ .Ix~yO =QY96h(QEQEQEQEQEQE!nG4iH@b+ͭ2F3 aL9 +fsGua;:}Cr=ݽPdBG\  T+!?e2 ǭg:44F=7:lj>4k.rhI ( z4:)Zr/H]ſF&{x\԰IvfOGu+>n^Eј~O/|>6V#CieotWKg 6exO^>:ʩs 8tE֝]n$Zo0ʅُK`u<-mWyߒHVDbf&A@<%r)剦ˀ@ڣ߿~_ <.zmYb9 FP~G?V:HZA miх=>lֳwrkM<" i^HK5yÏuҢQ%H_#|'FYG݀F +«堫^[;a?{ǎ~P$:齱xt.7n8$L׬~:^(m=G$~d?}V] 4u66b:3Tn/'y_㚒8L0=Mwm`@d֬E>֏,(FIAc9Yu6g?:uw4_Ln+E 諟S^f|}k (m4(VgM֒G*$Epԭ)hG\)(N08WL܃ 9<ޢD*pOҸs?92%xȟS)ҤA΢ZZ%@ow+R'wU-g?;}OS%e×q;TN4ۖY.=YUzv6GK6 $.X5`R0i18?Ʋ9a߅H'S$J[#zGӵ5Zzo?JF:#3-}ZDQEP( x;þ(@5qιom'݁#ȱ|3Em6t=F%sZBdzY4ۉ(N~X(TCRkc{o}Q2gkIyex>}F.=ʱ,?B҄]Csov|٫Ne~= yq/77 ^'vεik.rY'n_{j0kq~S*Gsͮ&W ZphQ)ѕ tu7uNtƑ-[6 ʚ(:ɗ0כyv|*Xzg? {X^LlvO#Ld1OM#2CԵbB<#I?xF?ikgZq[:0/jo> Ɵ#&m:Ʌc~U6>zƞ08Y @}Pvw\s1#OSί>yzMQ]!EPEPEPEPEPEPEPEPlO&)R!]Ԋiv)l-ɪ:G4TFy P *zsVmp|73<ۏZǕNBOs^nS%#hޭHr:ڎ5^i.NI$,J@P" ( ?kE꬀`ݏ6N}SizM[Z?s4 qt aO-&Xt3̲L7jL[O*څ\XnmhaE^x] m1/0P˴s㹮^uh4[\GӞ.ݳHg00wUwdkYΜ8>VFA9U5a-mP(%ιlwfu}PmU$u w~&^KLdSG#EGY*֡^nXC SY.B8i 7C޴!c֨sҦh iz2Z4h0Nj2uϰBdwYʞz}j뚔r (&YJV`l|#¸?rWߵQ (AEPEPEPEP_+~>\UlS¶,\N}}ST2Y.tNC8`hNVzhA4rM:rIdw5ƴ'pG2|^]xfN[μKJ E6jl{sH2:w{2rǭ"V1\ztNOZwʛIpFz0'1$cM'ix$1!72zS$dւzSP2;CdTc;ǡAl^j'T15r>T,qT>X_+*fOsU-D3F=PŻ W${՜cH X%q/Kn?r6!Md)T=m q#?oˁLuGqXmse% j9(dEB~ Zż[dI#*F_#n}8Ћ =[Wd+#//ƪ1}tBE뛻].~`NqvϠMKU(3 GVUvFf2H{Rc ֫EY  1S/=EEX'^j/qlF9#ң<%NjX;ROQ7@9HW5qJ:b,vSjE8'#4W#i.~$xP`Zs]φx*FJ?"}>QC(A[tG?SbBONW4QRZ/VߡEs6FjZ(cЙlu-rp({S?Z( v O?(1TD21ӟF>R?EDOPpEgKJW?-iAhŠ( (((((((((((((((((fotoxx-20.08/images/painting1.jpg000066400000000000000000000214531362435004500167220ustar00rootroot00000000000000JFIFHHExifMM*bj(1ri%HHgnome-screenshot0231Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx, :http://ns.adobe.com/xap/1.0/ 202 224 0 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?x#;Ԟc}:ik떚sK,﴿i>kkXHa7 <@8$(z oc?V𜗚ŭI H$ $ҫz2[V3 DzϘoΏ1ߝhߝ2P( 6X1ƺ_ xTx,XfW{Bw/N @|~tycJMfiVY 7MMFȱI H}Ig'?[V..ꖏ3̫o@ǃYږw_Ie{h#ڀ+yx𥾥 F&YPF34֞d>!M޸ UroΏ1ߝ6woΏ1ߝ6woΏ1ߝ6woΏ1ߝOac&paB#mPdjk&Kk&[iY60ɸ0b3-P/1ߝc}:m( c}:<~u>m+4pckeq 3 0H\_]5s̩{nN5u*p=A6 @'Wye&(fQVլu柨xz~D4C=Kc?5TPV魯imr5(|l~Ͳ<6rsjNy+wJV;ԴRĚ<6Oop :Mji{PtŠK}3xvƷZC^vKĉ:UVFV/[ܶvx8=@4=AK2C=Hr s^2ԛTLGFZQH (z4ȴ-JD,*28^(,/FTeԄ sv K#v}/CG҄Zug{Y4_jFMnJ|eeVx ʭ \Ihm.{p>F3 ((wois)<`"bPXn\gS\\hYY=ԏ5RMP seQ@9k9@j0[|3?*?6VTvϥp0Ols g9Vכ'~[srX9#  ? Эc~E\xgvQ_g~l47ȫ8eZzaqwMBx鈏  ? Э,a7y6BR_g~gvV:f =Ie^k%"m H@ߡG!~"698`ix8IQ4_g~gvT־ ]Emy7:qҗƏmy)%wl☈? Ъ6`N:Mw3w أ3ObcMVi~.viW9?G|@??o4+9?G|@?|^-&gSTn+m\%У3+  ? Э(  ? Э(  ? Э( =Zx{P}n!_nH]WH7vp\0 +`~"8kNj5e$ogؾYO<ҢҮs}E$팩 ''cϧX܄[#O2%mdqO&Eg5y.sF"[vn.}1[vEƱjnv[k}>KN>iKdt p bfZ@qG-g,: p靶eC5yoG"-c}`lפM7%GP[H Rϴ!w%ƙiRex,%zz~V?: DùmV\sGѬ[AlDD¢Ѽ=VAm InRnܑNiphP {ffLHr)PNOCƩngT {Y&VpwdIt 沷_)o̊_Y1H%cI`Y"YbYs9[qw׶:}ȏ</?xuOU5 AY KLm84&z%W{Xy\ʅ.죩rS-yt M;= 瑚Eo/: ;+֫K%DNcӴ#5im-t[ xVQq8/ڊmuv[VilKn%WDVDsP|i]?$"@+ Xqӊ{Eq?ߦ I鷖ݘ6Cd?)#}bJ8m2b'J@mQ\^ մf=T 6gK $S`ki󛘵Юݡ;M+Z,1%Oѭc:ߍQHN]뮹ڦWYfZdM 7Oq $-jl3aE8E55$_[ebwj*.R:ơi`@}YFN?bɿZ륣] BAݻk]K?_7v̶1nuڵ,WuqjtKV:UZ\Lk G##h2F=Q$i 96q#JKX#V=nsҢsXK I!;tFVrsp;Vc_4+ vyɢBxQQ۝ǦJ+k@oy?Ə.gAZ57\0Z[:_6>b`|uN<]R\\ 8gJ4k+hMSOצ1\i-B#yeëY4\E0<OWZ]C{iջne >OW_o(b*cC%vQ8Oʪkux\M2P#2(wF,09}ڳ'urNjtk[kɮ]cϒSlu;qҳEΪBV*AT77^ӼL.-Cωd+:O4Ti8nR('2}?t-DgSxFͥ w2G>Q}Niw@a=~Pk not]oUmPUHلr׭Giq,R1儻n OLhCk}ĎM s܏OO)Y7szvy㷷q`Oe,Sib"8-;~sm.MdF8'znK{]Uӈ-lYApthYtϪj')gG9L޽?˜~YNvɚX@ĹSW_]e4uY -^%DI6zu>hMqGWLA2kDsqB7ZY]l[# *Km]ֱ)o=m, G6RzQ[7zuCZ0dJYV:uS`xM]B}$v-,rsѶ 8= iwdFi%iD$;p:2B!O'4/0fƁ9[6M:uF8J zzVqMsh}Zm'cһ*b (3Av?V OTQ\^'zv}ik bh;/HBe#wUp1 f1O-6A(UyMڤ{ ΠsGz [ET^ [&WrCWc4<"luKY*t0ٴ?!^ ~m;A_-؉so2Ee\޻AFLSX6~k1Q5&88傑[6ͷYbsUl7V)^ .IT4~4*!%H",jUTPEPEPEPEPEPEPEP5ņ?3z_SQ@?K[h4ޗP?f>5;,LQQ$ךٴ[ɦmeUހ.?Kh4ޗ7`HG,Pf>5OiC&דyI59>_(OGf̕7䕆MlE7:#䩟Ek}?Kh4ޗ7~sjͧC8`H@}?Kh4ޗ7`HG,Pf>5^XbA2N{ɏ.W@}?Kh4ޗ7-ַ;+8dldVX:[X !"%4ޗ7h/oe>YϤ? >4}?Kj?YϤ?fhi yR ƹ HQp.OR1*fR| 珧n ( (0uU4M3VY`KP}IhW7ַH5][$]caw#T>bYF0qJ]I}V[#"vɴ҄&sK6j gc{9 TҬ[K}-v~{ad7Y6 2Z+y>sqt=/֥4Oĭ]^CQjCpѸ ہƅ6i1^M,m1^SMRu }$[i g$y4 IAf5Dzbp9ɪvvk:֙cK6f݋;}htKKؕFOSRU{ P ] ]giUM?bMVx'f%h=RՅlQ#{k$|n@]f-Ϋ`gѸwYF__6=t:]twK7K I)e8^nGd c 8B>=BsmP#Lb5RAK[#s9dIdal rNtF΢n?z~`F8=*7wVRyVioE K=!pHM]\Io9l11m6! I/ώ?h^8L{ ;rAƌ/i;9#^U/;&B~lX֣3iwZڭmd* Gjh>/3!x?E vA+4DEKUb;3;V WF}gqI[H 9&jZoP&n.RF ?޷M+RtOϚpTtͦxit6sg"3|B4PԮ~rlT3W!U ?JuxǏPYҷ6 ^|5t|ս…<4ly|0vr$~'`j:iU@H/X(<ִ) ((B(ھQ@jQEWҍҊ(zRP}(ھQ@jQEP@=h F6WҊ(ھmQ@KEfotoxx-20.08/images/painting2.jpg000066400000000000000000000306221362435004500167210ustar00rootroot00000000000000JFIFExifMM*V^(if%$0231[Ƞ01002013:04:16 11:28:35Fotoxx:trim/rotate| Fotoxx:denoise| Fotoxx:denoise|resize|painting| Fotoxx: Fotoxx:resize|NfE~0\ "RPhotoshop 3.08BIM5Rosi, relatives, fotoxx, art, ZMunich http://ns.adobe.com/xap/1.0/ Germany 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?"d[e;ӌ(y?`i a ]q=e s+ԧQEjdQE(,֪jV ˞]&hmǞ >in+5Mry#ҸN-Mi.%Y.>t\W|GZvxʩ<[tNm`lK}VKcHR=t+ B-ܚʤg*qu3]ڼ-Jc;-XWmGK}bq~ckU[6TR= F"Ȳg޽,VX x85$%@[?+IO*($bAgTjXR}3"A"QyL2$M)<{Gũ堊XOG[itUack0CxUɒ~*/s]jQE2iXg[y' 5wgQ`}i*[!?cL(柁zi,N)vPá"Zy-^NW_bxKX :S\V磌^1EfO8N )aOzZhLcp/'Fxq'S*TPEEsm/6vc'3EoT0ҫx"݃͏󻛷ܝ1^l9=NtܒULFi#Zoٗ|8vne:0 #tfp擅8\tXȺ)YGR:}+2{Oocgֵn16 Ցw{$rP^1Ү=(ѻMszoS`3bڇ>u} +܌esxmIE}wAFۥt [Y^&bHTH+cut8AgrKSχ_I;rџqWOҕb7yc#(}kIkokdwm:o[ޜ_.^]#QD]I! {ר[ΗPrm|g1]>0[C&Of<:%̫"ulg#(=;QTYiZл Kс+|?G%4.{mj‘IPCហ/>Sc8>ʹ)ɞ-r{0Ac-dzO M!3=>(x^m +ngq5ʮ#}<_6]'$GFJ\VN#CPEx$#Vvс8,I<2D7I)a]V$zVfc \mԝJgiIZ FQ_K `m^,]OJ!-Yݿ k.kEֽXb"  [`;GS)Sf%k V2,ب+Ѿ^^x{3h;mIᔌWS ${s4`9׵xZ5GS3֜,ϡt[(?xiW1eڏztd~{|K[i˼RsNp{s_d*ҭnx31o,c_ acq$ع|`wl5{J9YuFIkIٞ=;GSzd@2= kNPϒV-8,Iտ<>&4is=k|i[,LVu;#6e{71eʘ}_\G&pHvAUto ɵ ;%pGP+ [(*1g#*ǐAk 6L{c?gPS>Y̛K,{]IcE+唂خy;t$sH^iZ7 mo溯tIN0{ -Wݹzք/4Do{o9WEe6tاF#mqk)+nx=օZeSTOڭs欈ݰr+.t[i;>(E%@c1qtOi'{O$ywmg&EI@z2*[]%m<ߜcbOi+&#*=VĐF}Ol֑Bnq0GsG5ٌYe:xfFXJ MzN[ȳYu ?+c[C§\'HO89𖪖:HG!8?\q^g~gIѱ;r p{B`q޼ÚՍ$l Qe}+Gg#7|ƄvQ=?iN#4&g#Ub^GA#ڴ&ZdP6*붺 spF,m+ΜW-- t:K꾂Rһj;n8#|D\O z-cta掖.^9$ v-r O\q>8'VŭxGjN{rqBFmmt͋{p[g.3 5# >+#Ū{KE0Ub9W\k;[0'tes_"!-D>ʌWo{$ r@9c#Jÿj{{ko[Evv~=޾ gh =s\mxjcMԃ/UkǥZd8c9q6y>D:PS I'<7^&MZ.FZeZ ͜tֈm;4;_ ׉_W&cq]G|UG̖\by5\oqFDHr7*ꢵ[jvR ȼ3kQ;`)# }c">SƣkVvqzfeVY#sWQL҄Z=Nާ],oܠ5>MBހXpkgּ3K؅8hO^헲#ycf{4̮w&nuJFG!`3zʊynkJBwu0q䪃"by˄Qh5H@+Osol;ZռZ Ռ|V2ۧ!Z1[X-6Ok %ĆUF8W+4UXp;z2YAĒr}Դ-cu'=t;#GC>mk%m$d}q\x3Y7V֣Qg׺3`VN=MW}&"00;Vѯ$r^whtmkݮOTn ;$½j^ե.b5+z+ٯح2\0=)}R^{5q ?5 FJ[Zc sڻHMڲQ쯡"E}Ji2σ8[ ݐsڽa% ƻYҝg eOx yߋ/^)%~~3z7.y0Mu9$G8&n]L07۞BMZVWY+ ^:<$ߒ3WVd!=+) l]8s]&[ UCDcHFR8t>u%s{~Po=: XC #JͶUP@o t=KB mد㡨.n5%'CX&2nK*N(1֕8dX3^?ΙnWRZRA2y溡+WË{WWi]HV cqҼ?|5^g!1 Qz4-kD=GZfF\ީaC/5N݉s=M^8#>\:dі8aNvWx|!$xSc)MB=H Lk9[gjMy6)uݎq~,jE]! I"n8A7m ܗ%]4[]"7}NGHr51`:nd\mb}hi#Pٱ̌ zJ\R[ ȯR.wᇁkXeE2YxUcɫ58t_ Kr0bGdYzl..*=3_H||t,q Wx4.Iv0yDq|MBC!Kҽ k|mwr \aҽKl,ECX\z\!iSp^RV.1\t\5sKkC\iYzms84׶v3RAʞyxwīA]}q}bsn;˓++?9ԕF J1kVsՌ=u' +, IGd7* c؏A]/F{vG݂yKum،jn]nb:໙lwڔ֗ ߁2u\k+' E{弡WakV_~c>浥4:('6!]= ȣi[o0Ŷ]~xJM,^j\V :p œ;F:A~^y5kDcV}s4\"d7*: N-ɴ>3F91εBh!)8S\ly-gJm]Y̤U\&UMQvjB"1H7#lKNL3.9V]: `zq"ۏұ:VėSX?ҳԕ^2kY xpLy3鄤uG?oip/Pz51#1UnVvU{-y,iea~3s]PyU+r:+5swobq>5 uy7xy A´t!A&̌0>OM'>ܑBeqWomۼ3(˙*@ ź%;J̒Jm9VS5ˉhݝ>C{yA|1Ͻv7{hS,ȸXW/t뫩1  w96F"Q5󲅙4ze-u yaOqRIjnحQ>JOFOxUYIW#O5~-cʎ6௿^>Xx$Yp9 QRja4徕omSt: 2WJ+E#>bc-}6'&P|އyM*x8˿CZBK i`2FMjKg2!z9 I:渎=_|_9^pWW) F5Lyi7 3򎄌>1ϩdz>WGXuj?69a\y2IGb82(2}j+TwT_@KV#jH~#YCp-¸x@F/,t2Dg]t驻 claR%GQ̑|\tbߒxB{I^\I9bp+اj͖?3Ct93wknGo/YGđ*h_]Mo68 6%>_G2>)Ïܱ" |QXrEm^]fy&Bn-.l9P|ߊ9Qwgka\xFp.,c8]r>n[Ж=y~n?40Q#9VR9J͚]5۬3. Afǻ&;6+K,'u>=eHB&1QSKMpi"oj[V)piW4sՌzCm=rqSG庎5HMs:1ן^$[9s>Z ޚ6o;W,$rW?,nqIĺגF6r:~4Q_ 87^\'0foC^59 84Q_=qz/t@|c 3էWݟPWyRr{ Z/?v@w=A?}ۜhF?6d!A`leVpĜ=袹,fotoxx-20.08/images/painting3.jpg000066400000000000000000000322471362435004500167270ustar00rootroot00000000000000JFIFExifMM*V^(if%0231SȠ01002013:04:16 11:28:35Fotoxx:trim/rotate| Fotoxx:denoise| Fotoxx:denoise|resize|painting| Fotoxx:resize|N^Ev0\ "RPhotoshop 3.08BIM5Rosi, relatives, fotoxx, art, ZMunich http://ns.adobe.com/xap/1.0/ Germany 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?OK\_Jfzgv(0($((d8 & kKigi-FZ@BzR䣸ocb5&觖wH1c&K 0ko˔iUI/Aǎ>󎿬k~ q<ܼb=9uX5{e oSSZ,+>2kj-cxpl{Fk|CԼ%jW3SDh<93VGp`R̂8n" ǯbMn;YU(nUܓV궺"kYhyQt=j0 (k>k΃dqׄx$v@Fd2zֿJth}s&s1^_uwp08>ԨJ5XFZCa\ܺcz(wYPvjzwحwcջ%7]lVPi ;^NxwʕuYoI#H<~BV("rK0)WSx(xf C!y Es>;\[DQqӌ+HOaV=)gKϊZotmg*NG?QU|BVR DZ]X 8s[58$ق%(C-wD,9#}uGMFxs#<\[swlD%ќ{ۡ$IgL#p `N# [1b1;lt*KڜIIIeR)= .=(D~|)Ljڲio/C~kݿn[ H1c5-`6Fpϼq__8fkTiΰrPڰ*+KzUFJp2!~5%D]1AcygV>[kY +`vNrW<x!АBj^l?sT|"> &5d+y"xk\A!fB0{V-_#I"' @$MpHc /Q]n, K|P$;U)#>]no]#7~Wl\HI<&\Q,J.&}Щ8c"/Z70[Z!3b9lus]PA%-M ;#Zp)6sBi#HAҝ؈{"/!BǯvlcVEؤﴁ=r3?A^YMI&ud+*y=ҺMN}5BOP²sԅJQ;]:Ɋ5CԤ\,kj7:7ƊX ?t(zz`GJ&U\XqTE$>Jq[_1['p%I?\gp28ǕMiW~|[Ax{SYqA]-un!:dқf +#}KE,RX[ I7R/ུʹAGNdž5}gcaG\pq;vgfz|Sl{>Xbc_;k:5wO9goJ+~,,r9#B=WoƼ!_PWzzJVlZoA3MrƘhDOSz _-̻T,35gB_Y"-̷-tzzM+$4e#0$+;Wp [PK}ɷ;`~ mg\R0@u 磉#.QB;G4 CɌvYT1\לƃL"==x^Ҵ+rm*J z`e]$sIkJQW-W;kODa u?wh-;h}ҽ/FCA6^#4zɞ s૸m̑ʓH?嚌';9$ѴLF@aLi*,xVV6᳘֒R xN9z<9.GvA]ѭ-W[hDQVmR(D!2HUDOՍt5*sήL^sJ!i%s VNi&.x /_Ko0qGzH,a$7 $*܊zOQא L/Z>xoKIyN6qJké'5+@ʨxGedL=3^!-nY@ ֽIK$`ƻ y+RUX*g~}uw>v くךxOT.;},8bI$κxD=EyӱUݟB qƲq %%4a]QNa[>jWҨ6#cG8唒W1h_gG0NS۞S;] c1]5Peh2qnlt)Im$`ڸ\gE(]]t-<8ģ:*g:f/n4FrS>,^^q*LP1$X`UZP;:k:A'-Or+)#hUhoa§YS5ur\;U[D >$a_psZcXpz &`n˷a,eFqU %v^k 2+aTg:[I }7l"KlNjj?/da 282Z+sR[^kts]Е2On?{}x~r]C)={-D!K'G!'q\mQ5ˎ{o@8w⾁uÑ"'khHd>@4K?s]i <9 *Z5H#Ndy9=+ʫ-ON{ xյ?*עj;koYH@Z,qX?Ue"dW+"V Dž+ӎLJGۗv=ZЇJןO"niC{d*׽!mYUGt}kDlD^ѬVh$׈F\V3~[w?J_BH! 2[q^3w ?+h{F7o`9~l4*$|qy/tRÖzo@2ʣ :`:+kx[.4s[0 gyF9c9gsUj2-$AڃI'5jvЏVf7;I;*M1}cת߼~b6vԒ0Y })ƾyǔR#?`ceK% v>knp6۞I돧5ZRIM,wPI䚥oqћZUcsXuV2̚l&U) !go1$4{J=Z 'HM,h<~5%k͡kBLGxQN^>ݫ,$oJbCR>{vaD QrfqZ_|E2ʊ-é~uhPj4yeEK =Onm>S|'LވȘp$׌xVY.B 1^O?Q^KxծBGJꢯ-kEvp$+KrȗE 2$Wow,бXYIѰ{u%~IöIC®'WJ)Any5֍D=J tU' 5)Kճs(05MΡ$Gb;]F+jZ eia-p2k)Ql_kS/PI%zs5&cRKpہ<ֆh+ W+^RgFv rNI&| "M|.0s_H?7'No-hAnO\׻N.z:8-QWfuWW>gL|ċ5J=\ܷm# ^e~%܊K2G=q׏t=15Oncqk>ZRFHڣ+!yFoJTF`OG5|V ;ݞ M:ab{bk9%H ҥIB< εQ-‡rQ#cZmA&wܶGqw\F v`f8i>lR9jSH>Ѥl MW}+ct̡dUQc@ : W\j8.Ѧ6 W=oCk2 RO=+<$+u4\"RI'?J֜-.a4G!fQK(G'8+$vWimFa-ȌqI>#¥cr qZU<Ϡ=]u_$׹Xx;uO*= =Ûn[yw+Ƙ/tR Y*\d\_ јw<^v*_g_Cm.aaMW2V W >KQ!\9s+mtwA<|qitPֱnR 6>x5Z/-洛)?+̧v2C]1ح{[PxCRb;z4cܓRyzв9 ӿZ:vUW-S ;$֐%[lgrm!v=ԜFzUn)+Kf@վڡc)3}#SLEe۟ӊȪT#tt5{3O~+NXɹ-g2z.E@_Iiq]J漻S 5KsV*UZ{˵f *$%_jugw sAչ'?)\SI#]DZ4zr!*# j($Wߴi>Ҵ&΋FJ?x,3mNv9;kKY#*.prs{Z9H'._W:Ua)k;]M ch=8b)&_vǴG_>aue[ $66lRp}sڵ/ureaĜ1p{MozT.J2p>⺦}\47[ _pH8=zΥu}3H6+qU5j:Or cq]NF-MՍO*@\t$c?νoIcua^vRҺ_ڐ#phzvR4G\T;$r*P޹t.@'=? csvv(Mc5#]&h~\^9^C$/"%at'b>mD$H?ϭU*vjE:GMc:=f涏k!Gkv66A98Lghk}5G"W'>$m>1m&w&kysʔrGN9\zw6[n18+4 fi#$#VnVB~{P֤ՊxܤapGҶ`Jk$!$ʀ>淧ֈqwOC|o#Ύ@~\s_A_YxwKkmQ*5,{8kwi\݄۟!We:"XM%}3>yߡ iP6q4g"f@s+͕ vMMimq'c̛OB[QW9? )KáF;Unh\3ı$Px-pNZVG5F6ޡBL䜹ϋdeڃUNI; *U? ycQS~u E`lpH60z #Ҍ[N{~8Ӂ[ËA$: R0m¶A$.ݎAdZm 9%$m)I.V̟ 2RG -ƞOo&I(^]%7!N*Q9Ύg{;;G@[>x>C]R Oθ[3˕49VD$2~0LmjyΘRY,#KR0H9ê1Q9=)!Pzq\n/xo˹N+Gk c;5ۉՆI#=ȬB15q™ڹ5N')(I媯7ygbƻ ?JʿԼQ$zG:ٮ&Ciz04ׯIjk(ZiTs\;1vd -,>j ӴX#O~~qһ>N:5ef쏫>mYOu$0)d3|}] m&ffzqםZj^СXvBPۂ+._^+NA ?N׃[N4seu:f V9W#޸m;Gm2I_+v?|_ͿymsHKHZ+NsV% BuMAėh~k֭Q- -&W.4$pOj].8k]@[Nl:<321o)u V\55 oߧRHH $H - }0MQӯo(C͒ '9j.=޾^ ]FqT1Q[F[dk ؇ǽ5b&Bv=JTS\}rۘԐ3KVfGzc~WCU煥~(Wgt',K!#1Nbj8`x=ock˛'yܞw%QQJf:D 0[d>4 #ξ+sz^ #iMć0@t{¼SoKn^,9-|- kg :wJBF8t^IzNJ#1,|6 ʪiZ4fB99Wqdk*t>WT9 BS"%zE$uvB8/_G@q5f˅y1V?Hg#bHӲAўIuC3k[Hʣę!O~WlM_$}U;:/ -mms]&/Y٧}f I=:4kXE{p=j++!ŽNskRI<^ }?LVi3?(j'մvV'(.ˌxmR*IzOj>#Wv%DĂ98a^ỹu=y_h'(k:INL 8f] Yh>gRQBgnx&xtL=$p۸5=OTvc9^*(-y9򙤥9oK(w ۹@ArMvm{je3QEw<Ci + k=vf0k\&BgtA A+Rxg#C,p1ǽ>+׹JH$:e8"KݵÍOhfotoxx-20.08/images/panorama.jpg000066400000000000000000000631621362435004500166310ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 315 263 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}^  /<=MPE%i7,$|ռDž[Uvm] ~moHcfg*Ax{DQ 5#(Oizz\~ڹVi U࿅Vmk)~}G+uO5OxsSUDԭ+]E+* j^|Ո.|53kPMa4]NsT4M&y(aOz'Kt-θNo6ky5 3 E!`J2k?Z͙m/Яail+kTM )C&tU]zM@C;dQ*Ahn^=BUOb iadݠ慠ٍ$o_ -GVD%Q_Njk [yӴAAuO4߈~>6Pkg?UdjFlm,#|sD/Z,/M$2m#G_#CS}Ua6whCz(Sx^\sZjwvbuA,ԱU$*p+F|N{t+u;N 73G;^TԒn +խ(\}pڜ:tڮ>;Ź@ea'םxn+.5PGB0OoN~!x Y]]c9Kl~<͑1(ZKx]fPHN]euiEٚgYJM\К?zsGO~ø z?aW={6gI'ֿAaW=?0m0GO~ø z?aW=as'֏Zq;_kø z,McDҴ{۱qE1$ 1ORjK7NO;hOQۗmUCN;bq;_kø z,>0?>"_VEcYA "FO'֪x3⇉~E{4\C%r27P`aW=?0m/sKϋ-JzeRR^tȮ&<]ww]܍ M>ƱgHBm.[k#wÿ|U6G; >*_G& O-kޥ) :v&59]G&M̅Tp@*_GM#|ƚP/5ޑ3\XlmT+(Vb_q8i3ğ..*_Gf?>o{I(\ҭ9 kwÿ|U6G; >*_G.~jvĐEggc RI%8'5o\f4yc"NNFybM}wC&q;_kˈutwq,NuG>'s Ilk7fI^k{~ø z?_GmKgF6E,Ng[b-$>+#Oxj_Wӭ-lH#y2y Ƽ0x;\r\h:$Pj\,:\ЀdHJ*.zKߩ2v~}Ru$Q|tk.\z Y{YZ)mqUV8ܐ?D(zn-n6] K+qnZH Tig 7oju65,(moN}ZUKy?E;)'[F ܫ^kcJ*.~\LYeK]O[W.(s\摵{6\ʻ<ƺEյ{jKZLu1yFwwm9Ci%u/ ZY8 ^IuӁ]O L|+3FTU+UdEZO+? HQ7!{=^g,g[ƖC$qYt>+hF^+D}"6 ͠]pJ|b#uWɞ48Z6{}J/-gЅ꺜bpd#~0[2*|E> -'QOhDݻ;scvصό2x^hbsL5-æ93 s_/ǁotiG'|dݞep0oU^vWϳ ]AnaE;KC pѪ4ܺ,Uʅ+ hRLu6^}[kqz#1[wTbUۆ<mxH~(.|ai=K]j:Lr=Po dGXL 2[՚_$m3CۼR7YTA'چR9m!ʑ-tq|Yz?mPcn:>ycwmǮkO kzckk HROM,bV#8P8F4j+^j:z ޑ{gpӌIC@%r1- ;wc_3 'Wo-#$f%XvSdaqҾQ߄V}fF<)۴^H85#,.$` Ut i:+/ڷZ==m._3B̀<\Rnɿkjl#xկ 3(,Hr9m^g0q0ݘ̫mgJ|)S! _DܮV*2 AK>jZ߂n.u[eFі!9/f ؟,~cjZ_+K_>͐uK9|8WUiomVP༪S=z{~+f}Z6~(:O8h\;n6= #o:T^$f}[cW1M&)D[,2E9H;qEok>x͕R;"wdN1<3|6/q5Ҡk_OquL4,Vm _YZ\Hgs!G\#I+ylQEQ@Ox#P no~"~Ed ʎ:Yt<;?}a|?Z#9ěq|eum.xKɴۈᶈy\QK2N$y"^?߈$$b\hO2AI61,A$ kS׭|Ux|kŅƆa7S%v9+/n~z'zoԛZ;oEߵxOZ~x;VS-t&{u*6y5i־?]vLjm_ hmo7$o/rqdFSb}N-?N*9w`}RWI$-nmF9eKß~)"EӵRGp cs`=H,sw=jtK93Ag2YlrT)&g |sxsxf h!϶b`2Э?/o><u;H2u(Rjqx~V:T:wi$}zC QeUb&|V2',ɯ5-W[xoZk!~Зl6ƿgmY`0 `c+aXm%Ҥ+g3$k"&BTh-]^&@xRfz{-`K3IpP2kkRF<3cfn^\\g@Pdr+uIx|0OqF;ǓL ˽IZ6o0)^:DӾcq( T; p2=?~"Nz~u^ ҆ J: ?l^9s0r9\[~ҵ)QMVk{琍+#?*1 |K摫xK^OiڵQ1`9Rb5K`\_je$eKweNO@jSȞft_z  K`dGm}1jȖ.JKQ 5$_< qui49xUH.#+/)m<%kMifku}f FHUrU!_M'rxA|aϤ5KPi52K K6#R}kn~66CqO;_=I=Q3o+[MZ e$oUuʑW9^ 񽗆UڶkϮ.=[/)]2y]Ч ~'jZԫlc~T1xῈ|s;KSrXR~f3$m'wߗz/I.EavߜFHo c((۽N)&{ln' tr05Kß|AhLgqgCS-H=76}\i]_j*/ǨfBz+OWg ωuyNfk5=Oyw!r7q&:ߏ? K>?V~q\h7ƭM<%}Z-_h횿 K>٫ЫN }>,ۭ洖 RC>wu-hVxexrȯ%3[4_QЬmZ}+LKK4"B09'H^Oԭ]O{W75.TKXjhwҽ_0@:<*~8xs>)}YW6I~lg$3$ï84ֻ +ͼmum+UVi(5lnbD0vqjߴc76s@3$ttI6P@ps' ˫N-Ԭ"x~eoI_UB `0.?h v%4:TEixlg# 2o^GK]յm+N[.l7;( HIJ杞b;J+mK/ύt>+|8=}:[5y$u̅Nϊ~1x#u+ORoTu#4R u2d+^(~ MƑ dAf-}]$G23̱Pgݹs1WܸIIcnXoWg)?PZY=G?rտ\+ynd3Ks* ||߲mXXçUԥ6X4LK>Z2&RJ;ɧ}By3Iqp3DUQ]Z;: fH^0ulHEmjȵlÚ_)*xM6U湶VF+5@Zb]<${ӳ~[g|B9xWsg>^*͖g h[w$ Ɓ/nU9'O[pY ~V支gmƭusƥx_V%.nrbERQcy bO|Ij^_5o=+.}wX-JuӼ7suTIfC>@ȭhmrxZV&<[^CdoD3L쑪C(F$$ko~鮉C 3E[xz]LxfXIuA61~z2+oqw7ak E*WGGVԅ̓fDFltjqxG᫤Ksi% h|!I;Ku1ofoj^24z&mjMYIcwllV3һ}Wj/t`[],Ф{ (T+RlN|{O()/ZxB]?᭙ohk:HāUR!\lLC!A!n7p"oth.𮘚 Ěb-/)UIϖ*7d;| t?._h4ITE-& <#n;?e k6xvW[1cm4m["2x$g9%ko%~ƛm[ _߅(ٵi9x#P.#Yԏj>h^ֺ|=*YhX]IqTe4yyhVSL |ggƑ^xE4}qtM[ 7XZKOJ#H+g w+׬+-u9OבI0HNvz6zZuoY+3u2qZ:[,<*ÚM֟i 9౉xr~AY_I$~pnmмYLʻnb;^ D y@aSg Xg8%ixw7FI6GG1߉xwŞ+闞4 xjMk9RimV ͪm71]6PYCggⷷ$q誣?54X~lXL1[4 (Q@Q@Q@Q@Q@Q@Q@`WIg <# 4  qެOѵSeks;Ϯ ojE=yR:w"Ks| 0&c&KQeG].Y-vvID iIs&ӳ}2;D`6k\8mG7pO<Fԑ#G%4Qz5[3J(7F 1_cj_V-iӿK>p~Os]n^Ktyn!+!$@$~x[3SkYծ.chP6$ IG3 8 Y[ '5֖!ׯ K2x3;Ty诧XPZYj;xkRH: Gƍ7Xykk =Q YV2Dۗ@+`|۶;oe/ï؏kzy3$7;VXX1$Nյ)& ;V-!`D@,+׮CWC1xR}2jWhp#9\ ݨZ?]}.>]ZwpbXu14VG) br6=([/I[o (AEPEPEPEPEPEPEP\xKGukcg_ٽ́XEgl(Q8W^ qJ3O yG7e֬<--׬kh5m?P mc3{U6PN'5{0ߥ ╷|;xZEeϊmu%Эn`kkH8SV28 _i&<}Od|?'&N7xμvIf8ɰlmZDo3}W6:=k=,ZUeR#]3`g9"wG|-k[9`Ĭ:V 23?| ~jv}[wkWNun[l.e#0$HdA<+g~&Rt/Ě:^ C{k &4,h5*7f.[VW}E|2]4`]?_ K.ikm~R}ouu(?5UmM#?]|KFݣoYPJ.4xؐǔ~ Pܶ_>׾$ʷ&Qi`dxŷ~O F{g+k@&|EI{(1,nm])E1s]_ĸeF=?BѮt}P F;g6\3~@Hi=+}Ac=+Qv&2EER2F5_ϬN/k'ަ գǬk^ݺzN۬?!TQzmS5n9˙&Mm}e~b4tGw9S{;[_(fm._z{]E.<,av2ǣI kml/PN (((((/o!yyeqauFIE!≗Fsk+ fS˦[Omf"|(F2!~% `w7\xkQ SR-c)Iqw?hſhſpJ6}eK?"'UnO>ӑúQ]51o3G51o3@3|F]M,ӿʾo',R@@lNI1AmMU*tv\c`o^FA EPEPEPEPEPEPEPEPEPEPEP^]Qw¯Mjz$>lʯ@H^p8FPo|G_Kuj/oړ'[\d7)s&ksqwRյqjkJC\i,2aibut Y:|ض7zd%պ<(.At0n^k-[=Dcot՟BLD$a;0XNԟN>%~_"gkf:Űcc[B?yCxwJҵ J[M͕ŭƞh v;vXԒcF28{=b:{p ipFvIvAl ~kz2gMnt+׶[&vҰis1tcdFn {~!}{5ųKwhqI)7ޑ7i$)_g#\Ьag5 x/1\ſ[4wM {hEG(A,&/5]*RѮ/--FCBȌ!y;|#񗌵 [ >5=d/Ʌ*U3䎢o~C{/tWǿ jV-V[+:Xཊ423B|E,YFW#7-#Vu =oKMMRDӞ)$iy< U^a"V0x'D4"s+BE<EnLO!|73Wu)"lڎ&qK$t&U2L2/i=*ޟr\i!Ԯɔehd4u'*B`7RxhvZeG$]/02pp>ZirkE{ٽLR  n%TySx>mVWw/K_ِw0`!Wi-硱g=t=fC_v!w\Sal.HS?|$-=gao\]BI-`>s[_}OZt="o;yX$>#Ú)*{HSq-͸Y3,QMܱ9 l{w5*ǎ<=eycqiwV?6MΒyM1$ve:h:ny4=m`gmq*#*<xB+O/skPմK.9ffcy!1$vs]CQ}+:oͬj$wzTg!"4lbXūsO3Sz6:޵oAmZC: y^7$FCd ѡxVӭ5xK+˽>HFEfCD8$G5#Úfo4h6ծnmXn-Z5Bw% V֟Soxu}v[#Cn&I%9~Ѿak u5)uGM:kfѮlK9&'j|HK{4K֭|d\vڣzTu~.ycU~$6<3MKe/ l ~>izݹo.4k2H!\1ÅN:רWhu9|]i4%),̆4Mp0k/wX(C ( ( /k}p6X+^OpşXֻ4[8+Oҵ.."vFmgmId7FvP6 9$ֱ_We~ѯ TEoxr[y$ygt#7fe 0H$c<+MlNy$i "6x';sw^AᏭ'R Yw>+x{Zީ{tM?uHX4Ē9!(Gks+IlV]Rϋ}'Z׋9e}{QKQx,sCq )$9elpKpZ gmk-HF|CnU23p=G\[OMKhvɹi ^Oف997>YO?y#ƥox֡K~4?D>cwc,/??d ٞh?Ÿm@[xs۳<}ht#LO,GAa;9oO hL6ZZ[3M:=WEa_s▖.|>#|[a-= h#i,WCxDO@0$6װ|1ּGOx_^׏/n+gA84ec YS ht,mtˋew %2Cgk𶕦mfjPur" ۽JN;{O]Ə$.ѳAbXVI_ 0c/XjAom/t+:6J1YrBʾY'4T7R^\䴳9;= %S>k{[ ( It BҢ3%?+e 4KVkJH' .A[/O*(7]#_G$Gl!?ƴ It B4o+qgcu$?%G^x}`q@%?+e 4KVj%Q mk_$Tg]#_G$Gl!?ƫ[i>Z /ϖK5@%?+e 4KVj%Q uXrdHz4@MYH' .A[/OwWYZ=@%1uKlY?MPIt B mk_$T[i>Z /YH' .A[/OϖK5G%PIt BhPC)+*WW[I!ڟl-3"xYDK `zqz?);-xR_mm6iyuqu{fmpBo'G -?V:ݾ"tvڭ8 yVEr A@F5ЏmNM!Σ}kh/n#e,n)@ O",W¶Z?lfLZoPyqo* {v`ƠBWX7-"]γ]RqGoh!,U5j^_]_xA_B[O2]5.7(w⛣!kҵ7Sk)v,#۰$aq_:\x]ƫA2j:>4H3t J]d8l +_#,|]ar<>lib[+C#ZA'Jv6ZC{_>Ӿ"_XѮ{i7U~}oC@WMeWuh{=$@VI47~l"L{m3x:Y4 * f4ȏx*esx'>_C?𝖱sksmqFH!z֩%﵂Wrr.tsOV_Ɵzľ HB)GPX: vJ.'0k~14iR鷶sFnm9UC)A q t_FtB57 ^>El"jB2T淯#[JKivo -ID bO&G^^O­kažmwW&ILQI (P@ɪ </Z5XmGnoos߯w*m>+LQIcF1>w֔CݛBMkO}z(i 1pq޾i/ /I>][zQs[Ya40xW! 380 761 0 C   %# , #&')*)-0-(0%()(C   ((((((((((((((((((((((((((((((((((((((((((((((((((( " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?_YOcf78wǿ3`E|Z#h0`[kc;sEK^}!o\yG Mw]FrOa.>weok~di"ڥA PM> ZCg-gYGq5^[| s5+^mR'|-7~a[JX/%}aɴKnOUP})WjӚp2&hv1^=4Fk| /&H?6 S1F?wcz|[#1=Ifp#q=%|jao Ȱdf~8zڅ鲎5Ǚ4qm=w]FrO` E>Gzeu-ml|hnr8>5mckg3He 7g㊖VP}_'nHA yT- 6 wd tҀ"O}j qԣ&"<1FG=3F>4+.d{IbN㴧<Xo[J$NXF`"عWa&W 99UoM1F?wq? c%;L+qvhT߸a#;6gz[[#0I+8`#5 2;W[B!1m)8u ߱Cf&-&.g$5misi\;!<j̇?(;p='^ ?F&<CaN3E{W@#Ҏ(~̶9>N9jm.=J;F] Ln/JҫC=]FJ#@MŸV$mv =.}>kEtK˽&m~c)=jfsM.< +6;3-x F'iv2;5td8ۦ70]tg$>sgeL=& [s^1RzYmEHXDLǥ 546Y/-FCd㞙f [̉f%Tm-َҒ+3S=Q{Gqa[ >5֢z'eH.?1v8MҮ4ٴeDBed ޮL& KvH{eCnBO̿63H}bW6$1q{ h5 [=_3oϗm0pw׊oxC2;tay}늭?o !%/;C~g#ROMc72Vм{#;Cs}=uX%X b%ɿJddgJ;r>u!=wȫc5? <}<@{kPDM!"<1ٖ#!2qѦϧw eH.#qv0;WW.V`!SSB)Y i7lti!f̲`ޮN09'{櫛{f߳/MӵxdO.ݒŁΣ-{qa 4,a,Ob_WW⏊@7q @ϧy%."-H&#qv0;St6{ Bfd +';dx pߕ|X$jC?)k"l o|Ϳg>_{cƛٙaTD|`\py|_$2/J0A\.7LtFe?`=SONMAc3>B"]Nӏ dwy- cxdh ,#7L^[moh˟EhAMhǝݣ++@\(g\%Q "2Ye| ϭt|v"1snǴ )>c<ڹ>,xnGww<9&i|3},(BAwnHLs.4gL eTd[D;dV[ C튖}szyx6P.kg# ,]1\T tj޲<q;6gz:d:dRK2B-뽝Dx<+u> yG[7LE>R"yE&_Բyz[͍<ěJݘwK_g_.$)qa7vrGlZNORc1LvR=QHqy)4},F-yRMlbd`d'皚77/H#*mYr!! vkY( .;57PiƑW`>k6hvy=w{V~&G43+y]iLn|띛:6؄OH&11z \My.!o0?TH^K4V?g@- )88=+%~ZaIbUp:'99e׍u˜Bh/.dzx|]S ?x@&?3r7t5v :(&F4tk3\ u8ԯn.!5V[cyi$E#$gH H8͒{砬ۿ).-nefw;*xy;䪷 ڃ,;˯w Z鱯X˟:ܹzm?\kpEOoOڃ.FߑZ6LS tY=RInfrM#I5'E; >ѼojW7qH#@;(3Km8+Jq-d{eYgY  Fq_&0XW {v^\qG(sSޠ|>nLƧy`Bc>_z-6I-n F4& ۏh$0:oĿ`rݸ@߯s=KY𖝨-\ݖ]0c=*ZJW5gk5BI6ɟ ;~ ~liWR;M>`[WoS'}wi/y^wO>^1z/}cy]q19zC K"Mm5ew1-v9ڊ.?'/-6ymq= ngHV쬓FP}Nzn3E2]ߓNx|x*@<QY!_sG6`}0mN0Gq.weq\o%ܥp@\{ UvTVWlc-UEJZ`%(&s%Ⱦ-7;gqՀ‹c#TrZrTRj6q6.bR*8R# &)- 4J9QIlir"O 3m")1REf))R11(Ab[D  Z){PJ)9@(Nii⤦G j>}M J(MP(J( u&)qBKE0 QIN LӸ.{ 0@2)j"ޔƐi,;Md8wRÆ $uVznAsЍ S?WӞΈdˍ,I\P s_ [?}1^ 6?&8"F/`k6:G6&2~$~\wh.[R;X>c[wQT֒}~R\w_8KgjVhO&7O!dOi/r6{dԔCqk~K[4jm`[sn*1o\if{.?*9q,w E.<6UhiuffW09;4u2[Gk0mb6v5!:c{|{38Fn_ϧŨ^:ٖsŶ>S79dPX,m^BjdV[V23P)ĘHZ63S\TGC$}E-Ɗ2\)3LnȏANk'<n 2iSh})F( N h]hm34ME +8-i iE8Ti(iBE<%;_m(Z#v6կ(J`Ub^ƚc>5 EN:wf3QATF:Ӹf.,}(kOJXt{Pt=+ڻ&ҷuQT4R"f a3R qQ $eJY<զH־%~}1ƞ]oo=&[!}E)hoeiRfw(l:ϲifDVSp-eqR$^oi{w<ěv3v'w-1Mf֟hI&3<~͞M=VGiv`Wjwdo!\j -~&O68n,6HEW$QimƬ.%.޼={Q@m6:?7nۜgk/پ˹߷i۳gv{j0ΐY&ݼ0,23g".d &aT2y\1 .&[0?W\]ĊL*?7}w,a+G>dR<1&bze,R[6Ow#N_M!85'7S$'SX'Anwx;ʪ{.mwNQ8bmdhw|gA p bu͖V,ZCVGu$ '2NWf8s\V9o&դAnZZ #6X,˶BJ縩vF"G<g{?JŢ=Fx#!U~F NSqniO<{5ooa&J9iF%%Fqw&LEljL*e ^KGݿʃ:b腚,] #ҴJ'=]% 8 \40Vv`1V: k[n)*946!>}M+$4GϿHiV*yion8ۍE$j^UF}66@jmHW76\͘imaSf?]\}/;7Iw)\8 3=@5<6UhiuffW09;4u2[Gk0mb6v$aя^J?*Pkc^+i6ͭ񳺓PqhoJiCWQV5F[MW XNN}9֊?GGSgyP ;\EPzIS =?qK1m%Rkgs%($JGj]Hv:gҶui$Ӧ$ )dJ24e%5t?ЭH\w@fXqr[gS ՠxKDΓO\ȭE?EIBNT/siMuwyH^[JC>˨iz[Y]Kn^}cboB= S稬?7\gTf{sneM s]\(MM6YiE1 HzE0(?ެLVޱ;ԋj?JE51ӷ<`+4V$ TN{ӵƥ˩B.&򕶎AsQZ)sqfR_<#mJ]䃘VjZKsGVR؏VO+@v%K$i !+ 7f֔Mkڐ!ay Z܉)xF[o( 1z OUHbf5Lf4U +^czfXV46ӏ,Vl[I5iQ `q7c&kr#,\tYQ4EF0;՛Ȃ}+NI2YmPPG?B)[5\ TmӎтQygVB3z urk矆?5} X|UR:Ʋ+ʓ׵c&ځtηOdc!4m}.ʚm 8UaZ=:cZ{U3ҳ0: ׻i1ycL3S?:$on`>cl,Ph۾yJ鼕 se#; QBبwŽ=r8})?:qf8^Ʊ.]c'X 'Z>to.TWLq^B5ˈGAmׁE6߻w+q F?ok龾}}=\-v'< )G;w]8OύUڦ0_7l/KǨ[?3REy1y &"}NY{'֢ ӛPh\o&es =)1i$22ap88kK3uƑQˈic pNO|J*型&4O@n es%q>Pf}>m(LdppwۮEj:dW3{,e1m G.y$sqM87wE=h=ݕٮeKj˟ԁ@=~ ƶ\ˊǯ&xPGcWkƖGWF=yH2t+ͬwSQXF}5a8yCKhY}Œ`zU;藊xbxq[vVm1~MSV:-WP+XZq2N9d:Sr9Z'o"DdNx#~ (q+ձ&mJs&nAB@k~pDbGlI<{޺jٓG7UFt! 4lcIW~?ɫZTmd#*xWh#ҙp~vtP*·E?C?\iZa%+qwvM>+ Pn+F<ե~ 䴹lPҋk8t3?3{MmZf8I3{TON]Ac3Kq|{c;Cqދ֬Gǖ ocddgo>5g[M/=}Hyz_΍Xm0؝>so/4XyHNnY-9\j -~&O68n,6HEMsBvjg4uBlB3hS .8-<%vRA.LPowٷhݏ+wό/O/a8:oEcTROXzښ&^B,X Xm'Lm7n Ge㸤VV\g'\ ڼY^aRLݍ$ЋŜrsX^"6ݧ \8k{&2xM I-!dʟ_Z\Je|VK[43޶ a\uuQhq®Tj/ 0+Nji(p\(;ϸa@ 1kyq=xK/=ꭏj#좌?5'WEůi"cplgsJ_5{ ۭ2Lpk8Z",]\II.g7w#Wȼ=?p8u., hvl^&"OJ)O_iNzU&jv&!i )JSZŸ?V&]kҋ ]85)Oj iX.Wa\߉4f |ԟjYk'V[W@V3N.?iC"wI!<Һ x|EU:Um 2lPvᲽOQWŨ u* V*G%~Ro"ʆv#(Ub/4-vfng$iDtHQ fӧ J[XK k[v#>IjfG )pEzᛛv%}p=s۱'0z.=k_^grK 9_nTXkO_(?ڥH.x€xXN#H\F85n@zgO bI֢p7P!J}qȯ`-[<@_nOs;Ѽ5$qGjå;cKPxQU aO`|;D"__0HOr;xdYAPGӚ)=Z8A&38}{? 3h|gn?vx߽) $&4'X܀09ډ]J7v{Om3Dmҷ vmB..H|eYg@]Kn]xN{o}Mf3~[vql|j(mF+$ѻzFzQu̗vrAw,7 O+!:jmHW76\͘imaSf?]\}/;7Iw)\8 3=@4w,74:b;>,יv}+ϭ񳶗„ەJ5#\Ҧ Y+:PdrN7쟲jSVY+4fas7#Z"fP]7 -5WE)!g+QOsɔ؁4v]wwRk7"o L`d~uSjO""2iqʅ tѓ0ZXh* vL̺+e`rO#~f4moE9B뚱s<ܡrHO2`^ν*5]i_SoF"}'.:vH)3i>Q\MWUGG(\ՓY]t:Ʊ$Yx>XW h0]_$H8N's4X~ ;xQ2HzVN}R]i6K1ֈ 9:*HgڤzUHA uZY>y>oo뛭7N**Pѐ-\-3 溟Ww6gg& gH85S`}?u2>uܹiW Ϛ9˃?G$W0$Uw?iD.\/h[8J\t* _S4OYF`຀o;,'yH ޽3_+ggqp(H}8t+ ۩=Ij!}N׶ 0GJ-sʺ_##0ڵ8.rsد {JIX K}'m W9&%<2OWC4)$h 04%WFIs봆8QR0GzVpOIGl ej^o1dur2נZZ[l.wɋIJS ' {7i_Y\O.VF~bF8nδvQ<3}{}E}hVvX&FJܯyӊŊ@.gv~㑟Z;zp:۳3c1U~"HЫ1#w]kU[Qz CmY,D)NJGC*%]X?~'־7Ғ,I3g5U8XE}c/x#K6,o[_v3ӌ+zR付֋}.eͭΑ;dɵ̟# ԭeF%Įmj/l5ha}_=@4.SɍY1r܍> ŭ,ZlѪg mͻ mQqu{Hk8'w'| URCM\dL/to b)I`W?1[2hgFGq=Q>Ex#7fZ=#Nq@3Yg^ky}+:l:(޼*zWeﳲA,cROB+u,c 2+g.p;RAͤчjXqku5hSVIƾ`ěyg+ޝ4f?Z5АC )m?G/X^ga].Eq$QqOJ|"lbX +}v6VMy@ּld쥼\ISA3{ev@>U^|'+zV\I̋ 9u"l"%=ꮌWr`Jg+6xiVrc׷4Q6z4_F$svz7PO5@Tn!M=Ҏw9^לгN\4Jn G[Q4Ksg^TO^/1qwx  .NOy-BW#vI=~ʘd t;.r=3]5*rQ;]kŶ*Y^'YY1{kѼAK7Obdo3U8>I5i3ZY\z1\cz U֧}ZjJ+x7Zěw{|!r1>S۞+km4{jqp \7ӎ%EVF!}yGĭb A2@Q"GX_>KFw#c9#:,X_\DBKqӴyt>ٮCפLm>;AӸmy;^Z219)fHI!;qQfw7zd"~UkIVҙIq}zWxzWPTsBՇ٦ NCq؎حci5?Re\(|E.+{-7Ib}GPӼ볉ck5nmv&KlF@ ?ްմ.i]m}g XLOxQС61>XP~HM!GckLB׮͵R\ڟ%G< ;[8ώ0X{_:#Cˆ'qߎ)ѐe4clK\ }%vnقiqɓ=ԏҒqMjh1`\< @^l*1V$kvب zv>)ԥtBFI^TTSKɫiv&1=uȌ]*/^Aay-ݸg"q'9koa&?W3xxM8 M[Mh"v?7cW3q=9bշAoh$r`[dpi=qZԦ㹅:nȁm7dꦡ+,KrU|\߉=֍ke.˰G$W-ͬE*3\OCQ#&Ayׯ p_7h ]I:vr\/spͅܧ8fPzU>H^Qv2l#%IAN+RBe6N%\ehd*{VY("J @N+>$kĖh:y(xfTHY֤+&&|ep(@?twq[^p5x O.G #ELR^mz! ׃;mgHS$zLҹܦ(*۟ eyK\m\j'(d!y'U{I*j,~;{-9FX\iK}~4[FPPsֲm>%+稫.eU$I:p:$gxj6<{c1"sѰuh? hVum49r!>R9ǹI8<LWF#Tu20Ì?\pL0FrG#ҳm\E1Rg5}ge+W$z2MyLUEBym"1Sr`-\ҩ%ZtsD(ArwW\&r9kZ9Dq՟$[z\ݍ9Hg]l]>e^ixpC`nzבyE/#w>=)&hxwaSQ"-ٮfмӤux+d{85t#;.G_NIW7=ԑ]42}谄 t9#m[i3kDz9Q#a5g[M/=}Hyz_΍Xm0؝>5}$dSk?{?6{Q4+[GK0-]ݓmƨ`7l$c1h9䍼Qqu~E,j?b[r sw>.o~ѻV-xZ./컛^nv1wm{g& hM#=Fr(K9 `H'qߏ sN=GNrzsqs( Qv늗ڌuM+]%Kwv$VT@?ZDI#cs?y[eRsÌ}+9[$3It뢕5 h?ݪ.*MN)&h H1:lfi8.ڊ.V2Y -DV P#W_AJSVB%4a)Gdm늺ӤVVϠHxm$s+`ou1n'q׹]ks]7,|c:V>B,/qBU%y=Ǒ]c;$edVڲ..oPk~`e\vs# 7^{96A%xBxSCIG]pJ4$Ac`F@VτN[4yF*ǰzxω-1*k Q6\,rv5t`)'ҪZVvyX5079L 1*%Ҥ@v)hh赖t&xnmr{b)KF8+= H?_TsːZ ly1h7ZA^㞍=$1!&$P@wp_SA(ry4%6q}* C)x&#x{s`SyZnRvY+1=q\"͠#{>g9a俙Vv#T']!@Jۥ\6Ʃ,`6q@N=zi: `vʝKmBk:¸1&1a;?.k ΗVT jc)ekR@8k2m)g)Q*nJJ]YWcwZ?6uTDD!TMx UWZwR[-U 8t 3r9r0P%8MyX3WvٝCo2;r}T~7>|n۳;Ǿ3_ յRGH3#}LQ +g40D 1!!_,<|]4uؾy>v{Om3Dmҷ vm轂YO(?Xlr9qމmJxZ$n[y`JmF;do`$[IxϸNzb:~Փ.^T[3U5Jч"n٫rӑ#<>u-fi'$3ү=W뫨cYD^IjJїAmz֬WOʲ 1rqV(6Ւ@/Gmoݖ'C OnGWxؤyn}=Ez<_lϞ^|ZR3qZu%B>׸$ǭhi(g-OƬi,Ȭ8jtvP&8'qTЪn#עdH\6g+(}hJuR,Rdr1=sKұq[$"ck Q 43nci<꣪WΝ(5䨳m< 2]a(4mvQ³pGҟ+ZBO0WxP/́Ek¾\0Ïʠ$4M- BfkFFy>+ 1'k]k aв.&HMTJ"!OJp 0h.5 > 5-B[b 4 A`zW_K:-Qv~6'1TB\`WjJR:y8>ҲK)H uG 3}`St(7L9<{ +<`tSA;J5+˹++ Q_Sf^sJ׵hyge32_ &Gѷt0,H7pՉu+H[ pvGj/}x&.ه%vK\}GX(-q`>P+J7ߎҰ|Y"[V5#Q֔jFRrz|-kx:u&p]3<8{z+4۴(*N)mO\D}EP8s~=v:o=$nO0sHK6}y] ZI,V1F gh~\`WB`MͶ7-c|$W?^?{ ~)Ũ02I0H sAp;*'Pl5ha}_=@4.SɍY1r܍>5ΜڃF&}6.(viYO9I!m ߅#3Y qu{Hk8'w'^EWkj k 7[ 28J(γ>6UXoup~&g288;"2+G[2`v#PBf&@`Ϣ4]^}2|rEKe@۱@X/__1gY$.9#'5DiseUS$0 mpHWzDMk*: K zl:pA OlAv 8+3Lw4,`AmsrvH#TGJ{=*F=GnJėRs;̀LTkڌs~zWA 6uzT[vys֨TUYvGIx;R{Q4(ry0P\ٖ4`Zjq$a"WgcK5֑'5[hfV## Ԝ>^5kB>}(6s]w|hdWw{f:Ć5T2OҵOvr_ w-APyw5OaZ!ޱHzin[꩞[Q|$' xUr\]El$\}}ԼyK#w'$H^U$*D- hiolXDMNp=(˖hZ,/QiR=G$qU.u"x)ݷv-r 1> LUKW6VTaFyI,ֱmqv'Uڐk#nVF b=39ֳf[R{@I?:m̊9igb7kp~f$*jڄ 2IpK4:bN>2+2I4t!oz.9U~$ZHG\?K2sM6 3}6f,' bLSR--uFMETj|WIF1rqr=ZDCmPst1-?횻 Ll㉶ $^{BQ4#=d{H_MvEL^Nr 15<'2$Tb>x*=lc!|Z|?c9,;{ךX6*%mFirAMߎvХ%9+M=2 1 ;( :fⷋoWjj">orkMJMڅ#?+RGg-Fmjʣ\R _Uy,/ ,ln^AҼS#=8I rF)j/oٌD=?n?J/q4lUkm$qZYʜ?P+6j݆` Y$HrA)gvZHA{Yt5t6-u^kzdlzn hu(ݑ8s\pH1 {XIsa˯Y ]<ױiSM.ݤ窩v?s\F]OF;-'dL#5h܅S$$r=kZ|CZ:ٙ:*Ltmo+!|c ܧ R}&XgqcWO%ՄQF3:#OOZṉ+Mk㹍¾>\|/o%Ce*EK8~ǟzDNzUP%oIcmņsy=h `HZLFhMX~z8qE1y@ H8f^rHzٮK=% !eV`z|gk+wټ8ݟ+j8;cn'&'wͳ?wn}P?YҨ[UO$F8#%VHczeŸE vrq{-I#:,WY>j'NK*E9G3NO;i ? +hzBq~\Usk6 ̯tMG#jX(}25yވRy`jgQAJ 䜋yCJ"o65QzjZ)}*KnBG yᦛS˒G kggRk~Q*Chڌ6p8cWO(a=HD/{vp?0@~?4.*`?Wī\=7QA25J&N_ ~$\ሶ $_.}@$Yx>xtP ~lk7R͟L8r;P>VR+׭~xYb7(&O*yS=q԰(GSYCB;6=sڳxI>}f'BCzN6=q_Hky?乹m˩®Cv8=| XYG~%yT .Iݹ>K8)LѶ^ǫ_e.2xkʎFQ7OwU Q$-xčsj 3T?[>DMZ2 ?@«ɮi;gCWZ¯ \itUI;96m8G;xk.n5Kc ?_4ɕی6~{Ma#_ZgMZ퐜u Drܱg K؞ 7A92ymۍǾ{V3-ߛ]lda`ShwN1K |&#=3_5UߺXG jz/wF0̂'n33qT0'>|37ݷ=vXzk/'6.!RzP1 n|JO}w17޹YI;$ '%[te1{MSrsG?]8>OZO1 }zʆ0yG򯣤ISO ~#9c@)ŨE-Fc=a.o]hr~q.=qZ=b 5,r]3ǻW&@9ۻr} W\1 3U}Ia_yl7m;>B?9ߑ[3♋/N]" !߂3<|K&MJX䘬tMCQ͟Z;|{6sv=u|O}|ۼ1zӽ!JKfy͏ _[[4w;v]duz/'QNigHF}͔7 7gonێ1=1ބogٿ|̶ۍRymq,幒9 RO07A-pČL{{?ʲȉz![#Qvoگ>oV6n1IȗC'n;3یqA$+w6~G)3wR_\]d}aBmfx;A'Q#w?cٟ-ؓdfyۼcݏ|f{=SYILhO%6߃`sgOroxݤڻz3-8n6cwg6 hM#=Fr(K9 `H'qzC[M,%k8w#L)PGwW3 CҤS\β wPAruT]adk_,|>F:|K7IM-ߙk"F[`yLÖݕZĖqM(m&y [X@lt4Ljyh"h.Jo;q}p(j dk$|JmQ''ڛcoy_}B`3h|gcBy|J,c;|Nў${+erPq؎K;biR9YQ\ wPruH[k #[y`rC0{Q +\%GDX<6vGzŹ|FJ-LvB ϹM[ҵvl%Pq'>goqmwt.RY JmQ#ry60ؼWWb䴅gU0Qq5+ԼL,LGhAN=W{gQ#, Q7@"!u? G)1ywޜ-n'%dn3\}q{H|X;u{ֿngtX+(أ%Zd 8#m^Z5͓r )Vr~CI%TuUXS\>w3 ג,@}C@<W3]2f[>s#DfB Ϲ m'S{kW+()mͻTczu1Mx7hI% ya|ڣfG͎Չ\?iZ;KFHX͸\ <)s-S۩_ΧǙ޼X7sO=1Y]"]N/hvzພ$,,nyA,=7Fz-72ٽߑ R?(7H lLVg$X 4fG*J;H#;3 G Β9Ȑq=mI .٬Lm僽\>0CRWG;o,-bͻW}u,ҋwn]v3 !БϹgoճM)"j@ PQMx7hICžX_%6ّc673o(&J'IDQ |'^wO7#u9}Ƌf*0x}貆)ώICMl0''as;|AmyMv;''RVyoK"C]DG=I8ɠ .ᴒ;ߴH/j@|f&?gyo:w5q^e6i|0,< 9^s-ś]G!i鵆̟s2唰O,R5ݒG*Hi0A Mk& H_"S;p{@rCrڜDKo,JmF#{Q7+;x#1oa\οt{eQo&tsZ:2p}W3-b٦ǧںD\V-6H8>€7,n4a<ڠGjmpIfi$e Kqk'i8u!Ȉ$^×<ȷܱfplMGiuYy_:T]ivfI4P}ʬ .pgsڸw Oj_yv?]\[^X')ZKbh hYȎ9KMM6Hl~\w ;X>c[wQ`uX* [ Fj%$Tb)Qp)/g_:5( c28$t$dhn(.Swh*%> m8nb{>)d ~P_%vW#|Oڰ.goZM)M>"by@ RZy47+!ȌGaOlAuwyZI$lVbU08;A=N3ޢ#d:u?#~<לt]\\hO<ȷרXY(=+*;HeG|^wn ӇP}X]39.ͭ|k ?wGq޹]\A;9Xk%.Ui 2;H>|Ws<&Ԍr*9Ȕ> A@rrڕw#ubTn1j#jsN{%Dް lJ-䲻w9̦:2q43MV٦&m"\V2L ϰ 8nb{>)d ~P_%v+`[', TT,yA6)bUp: 8zxQ4G ,JHAxv$ܚog5 .'W]B3@iF_ΣǙ޼I}ow5Ek{kіC˴7F{g"o~n 1a7dݜ=OWxFh'9L1 4aG$\C{̳Y9̞P96ٓy sArڕHdۺ 324 280 0 C     C   $" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|QtߌtK2:]%̭r2R8 ‹?KZu.}h/^8##3gn6i_סl>| >OdE{i(|9躶k"1(m(s=zp|Mfu]Z%K^4vZ,q,1F>a߯_[_8/?  O@N0 /+?u_—><ҭ!-ݯ`2#e($ @Ƥ5+_[عr.!3eAVZj7_hvF'*9H4 JN\(s^;J߆.c }Sz<^dӮQ<ɞI9]\džv7>{;[#D0pXbK7mr]dž}')qʖq?æ躔. ZAa!I1N]}<ͷmꖷi4EDѕwnۅs${.VL}[k#E98dQ ktvmՎxgMil緊A%ܫ4F#R$/ G'o i>%\l?vue;KR2 ONvДI_y(aOzoşO=B}RwFTh@,v$I|>yc ox]fkKH FEG})YA0s +6] >}Z_|;ω ҵ 2+x}ǎhGF 끜Wt.[9=RĚfVB.UaewO„oy(crv1铌 ?#뛄:t>/_ ].s]ϵ.c1zʼn]Wĭ -}G+7]xZGuyu촏]UK}"bzrj5'44[ Cg($#Oq'nn߭nېGPePl>|b#|pl廴f{ Ӏfy%Uqp+ğ +9k}?|;DTRt①Tz'L9$*t_O<]H@`|OOXZshZc>s[*2:׃3hX N{+mFg&3 &%TmQI֦FU 6 e 4h) #! SY&ۛ" BXz7qٿ? \FF8PY>Tdk5}WTLj5[ x/6)j'aw9vz=K!ԼhGC єz]\jËhT,pg5NK xg\A.u(S3!ݞ5Pxx7CbWh{EfRPI,I^Nacʮm2JKIj<ƲleϑuR;0י]`ߢ8Ufc_D]_|/@M3@9X9n :%략_7;YfcuMK2JIei>uC&q:kY=s/jvhvKV5$ۊI$m#Y'˦5MY'28. 1:¾q:kø z,Ŧ:Ǐm7^.gmo'TmYY2n~;xv&ijoZ۵W[D,w4lGRI$2CM#uC&C67_+/x`>{ll/݌Pqy5; +_GM#cm —,o$-;OPUc /UU.bmѠ;,19?1{??_0xmwï&^ hu @-F ) Hv/ q ?`_=?_0xmO_r _ש~~&ҭ4[rVusۖbϔ'B8{D|D5 6=Ɯf6lm42IKD]XM}uC&q:kYυ"kikou@y}uC&q:kq_-ݶq-ψajʱo#¶ c'pGlToj>χlrd#M.1`|}wï};y;KyIc'<; +_GM#f??.)/X(P0 ʺo Jlm"^x8}uC&q:kNJC֍e++{wMy|/x49i9=Z q:køTZxZjq³4d#xAi WbЕNh16$0FV+9Q~ i]{}_h I|3F S IUF|D*wc$vWtcFҵK{w+2e ?#M)mB=6Ƒ4o%6Yu g[ PU&&M 95kk]C{ o4svH2_XH{NT$BL9^G-U1o5?J՞k}n =b(ȖEXu H,@݊Cs&&=ܬ F#8&4s׺i|E|EtaKqg t>=z*"RT;[a៊?~$50 q'9'u*[mel[Z Ġv\8AC@/뺵ŬvM24p sĪi1ՙ#vj klK7}d&Gp՝m ~YF Ϳ 2׶gcڷ/>"6:p}R"}!=TrI8jZrPHAhV*ˑב0kB|iR^mocV?*psޤEPEPEPEPEPEPEPEPEPE|6VῈ)[e=ѭ#$'jSPcּ'?hNEnn e2 6BC yZ]YQ_1[| s=&E55+{lD LB$,|kg/$,Y,I'IN[ͪ_OO ^0ntn OL %]`qbFilE?3,0 q<+vEQH uxsi'ݝRq-y?MJ}[u+__YIN yq3[8 r=fcHktHNs8|xgM"1,>/m4^ N[9 &xnYdۆ@?G|- ZϯC^&e0 Qmk[Y X[sQmS=_jn]Ķv֒>&yYx(]U̬9_㷊˽r K]Hm3M*]ʗR[ʩ n@*ǝB[[]Q9$w=(PQEQEQ_)HT;ıtqZH0%\(wyjOtzo°xsZ5C>tZ%|$bь8D]o>C'S'eҠ6~-$m\M$AVs$u%BS.k+l ŅݤwI]enM6WR2F 7TQE!Q@Q@s ¨[ĺ:8 ?tk3kʰ.HT4-ǁ/x[0Mۘac199-\B|,koemk:Dm6_ /0m+Cҿk? /ֺ6ƫwOkT}Yy_iQAM?k8?g_^;^ mm>p$E1׊k~+]?q~/YڝhNb~hVױ3"ʪ*F}z i֩&D5;` 3a]o//.uMiZC=LUъS A;0V9˽!+BkTb4ǖ@I۫>{ ]5Su&]2Gb{U,DR ]w65xog9;ĖַsW۴Ouu]UyND?I^45-u;P0M䝵iTQE@\lnTyRA x0d^vS:狵o\|;uEpiŊC"D }9~oa >^iRi8YK[+!9f$ ^77VO֜Ż j*cc$/?Zɨ^/\&ѵ; *4q:0 EOWv~o f]Fkeס\7p$ѰTr2۰ rsxn[e}qypw7S0@|$~$xoQC[Zj[n%$,aw0pT^'i߀iKזF5+=Eb 9eOL'+BjRiv O]U̠((?`~df[赬Ʒ xsKCѮ;a;j"ݷL4|-w(\[xvMgJs4ʖ5r aʂ^Yr@'#g׍"iOKlL 6zM[U ;K)z![nn.] rYM8ߌtZQEsO>r:5OxVHo9%3Anx7ڻG綹=2h\FcmǂlL\+& WA"5aZ֚Xv\PI澔.h;(.h;((noWN/gG+CNֵ_> jMil4U;et v` ^^{ \?w7"Q \?w7"U]Y%~le#O?(ImZu[ߋfQPOk;L(ȄPEPR|4?'u^:~j6څ\2[ q*8?LZZL|C6V>;* Ka \-trќkI x.OSBS%%+1hf/2Zo4OȔo4OȔIi|_ 7/E}?|Cmm_,t [$RJm ѷddWus%s%hml_Vu_),e[!#u}RŮ- ;bŚ87NPPQE+:_0]=w(Z@pAhǾ&ߌ~%wK/Ěr]9-b.1*|t.\on*E)vUI')a;Xeet9Vu:MV ]Y<|7ӵ5}*)o^{{6IURa~nWM2|?fxIқIrmm%W$*6*.J}?ɟOޫN618`:B=)oEsAoxGVӵcg}I2e}MiS`G˴*z?Opjf m,OJ/'#imb, Q+0Čp]suk>Vo_G=Cz/tK.N1O+Os\^]/ü&ƿG5+3ᕋJWy7X%ycE_,.pһh PVNԴ x/3|[9Qߵxϊ>0kSm:Ri\X],o'-`vs' *E{itL6|6;^9ԿR482҂FxL\\Ue^yڮ1~ji\'ƝC/_gƗ6qǨ3N4 T?x r L̹Y^woOV{.|% 2g'nmn?m} 3=[J$:|,n齰zcߨm,sMmnf uЋ+/;|ϛ3ګh )hjnkYY>Stcku\uR %]|)-G J#xY%XIV u[ |l*"ǕA06)k kw-5FY`!̌\X3޷oGTzѪe[};EϮ"'YլY͵)4E2Umo>-Ŕjظ[Xkw2>-B֏4¾ Z?Ep=PmnYtb.-b9ܹ"|u-B֏4¾ Z?Ep:xJIxՉmY\GqoipOIbYt 5@OidTreeaAA<33rm*mO?t'ʋehqj "~_eKVg+Х&U<o%͞tke)veٿ ڣu; 6N[hjK!˕;xWӴ3.exeRL`c6?xwoA|'B"4e9.+3_ ЫgNPos]+΍ yRdR78S@G#O?G4:f ?t[/4L/b($ 1 ȠGNhsI]Q@/O(;'v_G#O?´h G4:i? t Ѣ3e9.+FsI]Q@/O(;'v_G#O?´h t5`rLVP^C)ς# OKjvl.cd]yXWd?tdq^^i.cx H/n(3+,.Pf_.Rnah C A[ZFͩ+\VUx8ѭU2V#9(xJ @H@8+oރ=,R$6߳dcz};~$[K]<ᇍ ljOWsG?j\i٦w-dĥ:DQS;QozD?uOVFchMjYy/,y!Qw"޸oWi#8*7u`8ɤ Tw۳m>#CJי9dգVe$$)׺6WCy{䕥*Y[$81h3C_QwwKϥjw+tT1eTFd S(>wc84K(V2rm4--}އ| ּqh?WŎ\_6hk|10\^მlg \V>~KEVXnZDޥIU3U4۾^(4*TKk5 [O ~w Ɵy2Cce@zĝ$.I1C*(j¸61 Jr=gͺtF>2ƻ>{УI R'8>"?5}B-Vv2.'"3*g8} †5ݟi)rmqSNJT~И[]h ][kxIa5Lr,%! qpcV/-%ki̒1'!TWW%֟zUQRPQEQEQEQEQEQEQEyF@&x~-֠ǩ_iIYV #fNPNJz+m^'Ѭ_:ٿ[]IV[?|@rs]O%\Sc2psa&Ū*l#OCR62(O@ 85? ]Z:]ت}R9 ;ʌ8Hg\ ͢6|u{it/ w 9>m@t6[ Am ;Ìr  r+Xh>(DFK:/XV.uۋ8mx E:7 ˻!m~cY$D9omg 1Id9U"?Q+ېpFc?*MTVjzZ[M4veә#g2n<6'Ev4_iG$ZgC}Wq-nO[H[9o"kt#.*g>\])$?iLMO, ;ר)cIwwZfE g?vV28#Kج x<=Hn^+[= @U31$؜ 7i|4-"Ik%$FY]9Z]+_*t$ReMA^5mp@>Z*Z߆4e$R[A Bە $W- Vr,d2*,4̞h7c=3ޫxmVM 4(((+iQ "j>k}q gąċG`S~q(pl>/|=j[ױ=YX?jE!s<c l/u_'u˟ I{˯>p%|!XT[A$`[5}E;ߝro+~>5=n_|>յXxIcq&ZKEBV6c&U+׾ 2=Sm>t;"NI⍙F HdRb7ωeγ?q-!ۏZ&l{<͌#+Ǐ>VZVRyj ǽ q4byV/voxw<O5ψӮn~4ֶ<B%%#O->%i5uٛ`kpʓ:$%pdc׼YOXik ^lKqsk-(U wέTơ1[޻{dZ>d`r`^֟'z x/ýKGT$,#PKg+I n ݷè]K=vXn4ilc[FkRmI!E.,>ϮQ_x;4Ծigpv,˙>c$zjj:ީe=׀C%cR2T-aO k~Vz"]V'k*,+zKpen8!cyn}b4#ֱi#9lq/|zg,Ӵ i٢UsE6$iQE%Q@Q@Q@WtM76ZuwQIt$|S$`q8$Z:P0A >_|^sk:|yJϵ2I{PQ^7~_[{[K]r/jS2^#RC'ΣM?*_ jZ.D+P(e&ސT ]/]Wk_Bյ ";MS—+No},~ҿ6vj?ex>\ռ=>뤵V]R.Bo%Ucn`A$S~bו#h7uNmc^^i,2iA9ibh\6q5ŏC{MH"H?s781Ӛ6_?6{"-xKR'nb <(A$PHʶ3oJ^Cf^ 43[]䶬PcV,~?-}&{]=44CCֻOWeJʲF$Ex x*Aʆ0)QEQEQEQEQEQEQEQEWqwujڛiww# r 3JhEb Xwf'Mr|0~x'C=6FN{[snJw85Qz%70NV o̧$#2tw6Z u/r[EzE,Io,f LsNXj~SFy4o^xoJO㶖ݒbKy6@WWtռ?i0Koo-U)bδ[2+XJUy[^?F O[sqv-A(W+c >$ ih >c;NZh qxE#{3n?>8|gX4FwYơ%ۗFfR{½KKy~$ ^6q?>^OQc>!.#;OEQȡ-Tn<%|ڊ)(@QEQEQEQEQEQEQEQE[⿃oi:tcRVylGU1`:a_#^ZM<^'(둌A#|Cx> B s]K [RE4񼚩4_jqgX^Jമ_tS}WY}B8^Ed‚p ORiz柭ѧCxl^BἙlta_]u Cֵ?qx\YҠq%*t9lHlcg<xHoW_MUJ,. 3K9?_>Ǫx_Gukȴ6 R5I=|*| j>7}>I,^k{FJT@.rC0ƚ߱f7%]W3hy*?g#5-.__.Sߡ.!XR'ĭGV g>mN=J4[չ94"T(/a|{7|co4Mu-C^kI%hhcs]J, Yu!ϻk/u}kJ5Q绺Y%U@oEGcK XT U*+/?~t-Km[ii^Us$M& I0e㯂5|;a]RoIՙs΍|؋ oLTe'WxC3+߃>7*|Ys7|+ Mq-ĭlː100>U6gs+/3]_w?[?º*KUm8;FzSW:譣&i N-XŚuYpI {MᧆMúBivVz|,@E +[]#_G$Gl!?ƅ4' h>Iױj(L* b*OIt B?l#pTG: ⫛H' .A[/Ot|C8?l#pUKVhH'p#u+Mb]kh-Fʌd0$AjH' .A[/O/]ŧi18eY.yğ]Ar xI.-p+ӿ%?+e 4KVh.>^_>+kcqd4fO>hWR9jsI<ɬ|:>8Mqm{ =?wS +m$%[%q^ .A[/O]#_Ga5seK-}/Kk,.Of6G;QG gYCe펟ɳIJHą'B??5m1u+7sMawoq,8ln;s`\|^t~,i^-F1 $.`p2Qk>/~}M }M5q˻h98i5n\&y^}gqc#sk(9VRA: CR*DgwcUTk;OIyƪئj7ū;7<1TKi40lN#] 3Ӝde[i>Z /?ϖK5\מcS)y47̓$ Qn1ʱj>$tZD%QwE0 w)EKm?Y%OIyƪ9mhkz|:-:K[ N8=cXŝJѼOueX7L#إ2J ^c:[Zj[& yg5p:wc#kJ8uGMo m7Zv88uڳwWAERQEQEQEQEQEQEQEQEW?Ե/ JүuK"KM=1DѼC2OR3_@WZxBN7Z"76dUU28#:M0j&HӞ5f9c-,!C˅@btɬVu-j^%-Hdk;Hb6rFHLc +~?/*QЩ}Vzv-^gx5fFHC\=hcF̒)7r9fŸ uK5K9.KmbkGErNYm`$,rN On^#R/ W_\C7$U8IؤVڻSͯ|:]Vºto,fbi#A#U#8+OϬkjziogI`jVx^m|%uOͫii5Ңyeuظ"*S^#Rj kǩ_4)-F[8 Q]qu{yt-83MRaJo r(0(((((((((((((((fotoxx-20.08/images/pattern1.jpg000066400000000000000000000433061362435004500165670ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 244 463 0 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?x#;Ԟc}:m woΏ1ߝ6woΏ1ߝ6woΏ1ߝ6woΏ1ߝ6woΏ1ߝ6woΏ1ߝ6woΏ1ߝ6woΏ1ߝwG4Y|/i_C\Ks+&0\w#oeѴۘ4h=%cGoε<'jiZ︸`%eNk(~@~tynq}B Eol< T׾4>kGV`]GK~tyrŮ5IpQB<@i6G3o޴=s?TΥ%:{5)<gUO7G{&3c}:<~uqMf[G{BnESJԼ '繾*pq4t~tyƁ^xsP5i*[vG4JG UInpchz; jr~c}:<~u?KOQT׳Bh$2O##jMmcmKpa%V;P?]F]M2cyݼPh=[8}!0-qRRޙZcAy]nkxf6AF۱GjfkD7wqDЫmv@J?@?@?@?@? :K3$Đ(gyhEU?wdV)Z7(RAT1ߝc}:XbiH;+; vm"[0nn{ҹW-مKvqc}:lB|{'bk7K%[6Oʹּ9Hܣʲjq,Wڅ/!dsԚ\$USс tx}u,[Fqq8rS׈t]+K>WԖsTyTl-؈|*sw[WTM13]\Zlj |IlP6dDI^<79=qGV?UҗQN\i6趹I`r_;x[WvMɖ?-pqub[ b?/8)>c \NIdsB+&ĩ6';ֺa`|7g\5+7sު&;{eke}ѣ<*?'Vkcid;z |3R3mOZWN$0ː,ĎM_^յeӣEqvQ[ԛPI-.$ǚmg1qpMԼjou#A$c(uìj$>5 , ._x2WKQs:滅l##XD@pc$- %uv.Ƿ/w!tkzwVٻK$|.>U݀5{}ڱuK{B,CdgX,u>lrۢ I"¶7::L'I92v!R_=uI 1^ǥ97 Ikw3kvŻ(:Yap=ܑ)V9*Tm=r,̳Ŕ0GN;c2UZQDvZMa_gyi0x{SX-Vhu_ȤB"cqs]Iyh--I | Ď5'iŧw-՜@"Kpd' @#_}0u-K[5ìh,&8-bUۄl|9Ab ܖi\OV$^;Ì'IկCU{]3Т((((((((?_jWi=|@8$wj'fr}C!DH9$`?jy>ݽ]c\l`6V̈́2-ȀO9eR:Uk {In6n%r U7{Bsk}zO)7ڕ^87Q5;h5>ٯڋrq-+ ؇:l)2づ&$oPfM&]]5Gx3 MWVh"" s#͌^*;v[Mג]Gnsûj۷l4lG4Fhi8Zo*8 $rGC@4yua5Wq^O'tr]OJAY6tZt2\dG1ʵAC-QHaEYH:ȁHǹmQT8u ٤lQ5j9!,r#FC?:{ qTP[܆0\E(^cԬEd`%\ךC,ZESa%IP0a/oy g_FS7',P G1"#<?Qx?ȩ++Pף6mBʾh(<?U{;qfn.mHe|\A<^l3G$F~t}(<?RCuor)B6?*jV X rPe~_4/1"%崁 w8\zz`mmSֵ_$ɒЬh\3@m<?Fo? {_\ ̲;QRjԗֱ2]BNPnz+wG_qN<?Sst?T1"m<#Eb" }xG|-"PTQ@Q@rZiAgc'WlyuUڅ-bo1JMC67:upͣs~0Yr)}3]m)[-"(#now~D^W'ͣ*"lU `P#m6O71 J. ϽgŦؾ&#u%W|"83j0D-S=B('LnK6=NjV$i!GPuafE:ȼA `l $]komB0AFX(>[澃G+$92$Eu% #܀|U_[چH}KiҨQ /I֤QE + zN!cxgp0W=Ir#W3@j_6ac24E1Y,hڍťK+"?6OQ݈bXk?c!%+hzP1 I(@Av`L`l9t|9;ZBdVF(2>w Q#T GW\)ʌtQ8}Bkk?i媼L#x{g}SPHͽ2A"HP u8MH!H*fK&?`|UEPwC~'m!4S*@~u xq c'' 95JFQK ~؃HOM%a{ )׸2gREFEgyʪ2v922$Cz2oۆӡEVUf֦sC6Z4+tI"D܄d޺~vi0BybiV9 U5}Y9 3~{!c ]\ˑF+PQJU=@i!zrQInjT`VyWvьSkihcun]L{s](? e/@B~ۻVDZ< meg\}_7ӚӪ#ɰb DQE!Q@Q@Q@Q@Q@Q@Q@F. /MnC ԕ涰[iWsOw88Jp8Iwm݌<#kZTB8eUѥ sޝpo5XKgt-M0,?4V$  zεr`2a~VZ*^hf c 8$(Zv;j+5kŽʋn!w^I{wQabbB u})2`zr ` xj=OXAB-ƐH]{qMxaYdU\_oVӮSmpF@vpived_Dcj*p3;_ KL,?t ,p[ 2JW uzoRdp*x\VkjJ-r&0ygNI\K}e~,rQvdv+xndf'\88cCX攺Xn=y5i ekj|_g`l̼Q3޳JŚv6 1,AJntn 噣ߜm3RW:_3E|SqEv4\:(#2bFuQ`Mcđ=DtEak>eƇ5_i\D8RO4֮ނz#6jywc&ԾXUAdy͌ಌ(VkAtrS\:j .^d@lgs0z&"nV\5Melewmx]xaW?7n)ۿ2*3Җ;y{[ϳ`wCmqҺ,,JD z)#&*bISv4gk77RwDCJa6s~Gy>֝K{M(gOBXiQE܌r+kI5m AR3,-(Pf11k{VwZ`k+_";#@7ڳbN3N(0(((((((((ZVjzGWh + '[4 Cc<⤺[I/$Irq[4P:h-JKoD1IaI΂S[DbjBq[4SmEmgyl[wb@`aqyύ5CvZRVUm5a̷*CT>Ec%ܛ$0rp$0gXEu4SX Cp=}knË {ɮ+c*HKsrr]Tg+Vbn⟩ii^s'BD늵L,M9837K4푾%ź&ŅUP?SK[˪[_nB%?_c87($X(\N+nl6<1h?_T`@l =ȳ.TZaȱI\d ZP=χj72O8FGCͣ^3=6J4W}GO㻂M@y?tMBֺsRrp>L8Vb1|')" JsҶh)<;qs[8jqaՋLC4i ܌fS].@$>X}ne2Zs-b<ƪwV2&гXM[?_%VM%?_Ţl6<cN(((((((}K\([٤FufidWI\ڟMTװ,GbN'3@EtCyAvl9Q"y#VnnĎ莱;B_ Ю.] @~χ{϶T0@OuCfѿLǖG?t?=xQ'KKȴv.;Pc4u=:D'Ќuu?5_%1-"fݤM6]#n9#[yQE 1ST7_6(0'F'GBVj4 YbOٮIi`Vs;y'.1i׷0(#,XK[g=+Ys`~vX'h,ޓ4ke A9>ef߳4o?zOX'h??F~bd3Oo??KLyB363Rhc]9zuGz/4`NA0<֫귉i{Hv $/FQN'Kohl3Չ>-͛NA" wbkRͥD>XRoS޴$yֻUľL©:{5-.+/\y%a-TVP5qnM<ۆAOҶ4 ]zՌk2fL wok 59cK[H.$]A' [!gC+Č 4ⷅ!*"(UUOܾ󡒶/JrΠPV* +O { JN w8#=6u\zMV0iF h s*i m2ZMy4L08ԊҸ8QM$l307duc?t]=?i/Dr\j77-g)3&4fv]iʰ&~ %{L׵ ,˱7-.Mry>޴<.eG0iNއX[jw6B;Nr:yS-lMg?2D%x_\?c A ś]=El1 1{[po/ncY*ʧǫKiiS[}nX;]ܨ9>ͬl`6 8w_!{.-树 b5FI\ rsɤ]i֟KKKRdF_!{Gݶum_T[Frx$kܾ迮E&~tU}2/o )^M6$)` >݊HPê's}dIUV[Kj!4IYF<~muu}yqVVI\mQԷ_zQ1xoed*@*rqm3Hm oazVCQknvIð0¤׵ }6zAZp;;gήun!kִ18=#HnrL r=X㌞:zQ+izK*%Nkpmoj.um}DG}aFd|xko/|4( `EP{"*}L6ing\&AVUhutj@ٖ$?Z_ERON'ON'ON'ON'Smbm*Z(-݋m_hv/R+(ӨӨӨӨӨ ؑO"mؿ!Fo mbm*Z(-݋m_hF@E/_ΝE7OG_ΝE7OG_ΓLMju _1?b}:O5ju _1?b}:O5*ȮH،/ɿ[6 _MkQj3Mlmu J0zz/isΐ9s x6jˋNkx6Br6h[_q{k4W1I"}WSlq8ݟs\ei-S)ba63y]Z[I]4dx7SgP7B(?)pAW9on-a{E=忛ögYB5 REI$=J-\ӒdV ,ǰ5k)Y$C%@#'8:m 35fR$wer1O 4+o2$+{'P2j(Š((((((((((((((((((rP&G^Ix?Δ40-@G(ޤ m/>pz֮@{5avRB\3'8f2ɽp#f-7˚EQݻ7˚v_c^W֮Q@]ԕ;q;6ˏWh _>ݢXrqR@Q@fotoxx-20.08/images/pattern2.jpg000066400000000000000000001166131362435004500165720ustar00rootroot00000000000000JFIFddExifMM*V^(if%Fdd 0220ؑH쒆401002009:07:01 00:00:00Fotoxx:sharpen|gradblur|tonemap|gradblur| Fotoxx:resize|resize|pattern|ASCIIart fotoxxNE06 ,BPhotoshop 3.08BIM& art, fotoxx, ZFreising http://ns.adobe.com/xap/1.0/ 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-20.08/images/permissions.jpg000066400000000000000000000423361362435004500174060ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0230Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 186 392 0 C     C   :" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?GK]";{oܪ N SW>?5 M紟٣si?P@}'h\O4Pk{I}>?5 7f=CEM紟٣si?P@}'h\O߱Í Լ/xKVNnM/ aq{v<4u&[ZxS\(AYWIch~IMkgƟk{I}>?5υ|Y?f majKn,"F2swOm5kCNf."81HI(X:Pzm}MI+vO Z|Cu+5JKK+uUXaNpqGN57^b[>]^Q xzrN2ИJ<f=}igCxi>`ukiO-}T=| r=ğ7͡H}tc.8ԅ~Nz{Zio\si?Gfg .MwB񵏌|Ou<2m#sA>+|[/ h~h5n<5om#(76OsW.~k{I}>?5~_|1}㙬<5cMC6M$͕y ]>f_k]GVP:yCngo1 ciy~ [̿k{I}>?5&XH<)}O+u/N{w!B9j jdž 4?Gf4ױk+&Gǭ-}ݿ/Cxf={'<d]ƳUmkH×ᘬ %@ZOafM紟٣si?PL ?4}'j(o=k{I}OxC> 5=23~lbMң,Da1{v@@f=n@𽯃Ic6g%77FKXhøR] zgs7f繏r22fwF{n5Dy)3c.{NE%н7y4N?Z4_Kü>4N?G;O4M;O4ü>4N?@4_Kü>4N?G;O4M;O4ü>4N?@4_Kü>4N?G;O4?4EA6uU(>͖ȓ6z|Z??5~Me5]C%TcgGˁQ^@m;p?ӿg| +WYxV?մѩ2eZ=aÓ1<;:Oеa{WP-ƩG".N1c@m;p?ӿgҗ#hy>;ˮÈt$/ԊJ1~9_|9/ k>ISMd4N?I;m;տ#ğ4ZX|1%xv'2h)FgJ4N?G;O4[:Ki?)n1ìA>?ӿg@m;p|]W>!]xwCH:^=zMIS+? w zꗱ[3Ciq C#QV=}-@m;p?ӿg ?ŷ%:Է ftވd2{n&2+;Jn#F#wƟi3hx|i68ƾsg&OF} x,4p1.Wk^Κ.]Ϗ:V$W?ӿgü~4N?_AlUnrwoV~QX3?^ [WW<+xGޯw+ Kxf_ZvQ^/5OX"x$43 .z$q\ᯇ~%o34fL3 d|uu4zxg[> 'kNLZDyrNT+/lzß1Z[B~}2QkI<.$ȕw_ es_ ừO|p4N=wPQ|IIR_$z7x_ß e xċ%:PƋBXwL/=?4W?9^|/6j^ƶoɘ wL7>%X> |0ךM_@T6s>d\\%6 x _󰜒k}^!0jo _j򦩣|oc$@"L@5⿍x:(];ךo,"9 ib pN;ml;sz+|le͘ןŶ4e=p0;Wz-~"Ҥмy#wTbx>3;gnA[Q_2j2>#:_🇧vG{RF OCEf>9äZ/ʺ騛ĞKm"3N 2G;QB]YXp\:Hln$MV| /|sJ}:_cpl2 3ҵl'x'tޟ3]Ѯ[+;LodTl6zRcnIKhxNO ַ +Kl0_`uNiQE!Q@Q@Q@Q@Q@Q@u]=5m.FdzA#ߚ|>U vWz\MSc`]:+8xSB7,M|D`JzeMS>'ELJIMw-BWOx:ܑ:4$`UQC6C<|#xJ5+9Oϩ{A->Wˌ q6xľ+bӴFHQ*H8C {-zS5E,)h俒)3%2#+xׇ߲W:um_N4]E{o2pD;0*P 5uySQ5bz2dn?gY5^!y/:->c5S|~zf}s5ọm5<9@m>в-p]UKj&^=M3mcq׭QIiX{-ٶO>?߇`Ol-CAyo@sۊu=5u}k>9!}m51$lT9FNMz[OO۟39y|-g`И/$AG+|%c V.Nҫqk(;w# ]8=(  H?g z Gİu-DJR@`0hZrC5?_ .5bXǑnʠ9%ORk>6x"O/Ẉϵ{g4=]/ |S4̳Ȳ"9[->ߑ*no@]> RYuh3m S#>iC^ⴾMcEkRMYuHrHyb c-sӚt/5_? tWPΖnؖ8t'Rp@=k|ij_<<,JM=4` 7.۞TuQ+}W[5EwwcōYϔ!WʅVp6g5_pZOf x,Y#\-nd傤`V(]׌{R+^='i2Cn䍸 Fr 5o,x_^u2^!WRuWy0ƣPk $ÃSk71lc+MW=G㷋 /64'mR9.%XfdXŨ@T`|s^ᖭ|4,.pTx1r;^i;xᶱxn_FgTp{k~C:RӖϔQ7ѿjT|0_¦MZ[o*8D{ǒGǏN|aSZa<[uK mnnc cL&@՛q3]kΛU,UT.C5=BpZOZrCqSJk=[C:;0, sջkak~ٺa K/A?::r"z*mגq|C:~Ϸd~yu}seq&oq#*)?| z業jg<=_xB oI wn>h+j7i} GW:_za[˵xH2|'IN6DzOϋozf7ƥ$9+f]u+xV? x~&| N}nWS;eez*!xJ|h߇!kV&'P[iV >wǟ_I1Y EYMv9ϵ|W^$? i dcl<[M͒1+#*"Hqڒ}?e|/>w魣e4kD.IR0N;W㷉_W?i?9#mܑ瞆<7K^K? hz ɾ A3X;JY#o]dãľ|rn#nݸCY8mrV_bxG8GkKmK_ZqFwinU\}ќ>Kv6;]C>'ԟSZX[DDŽ-1U^ Oh=oo^-cP$/mm \0dzxnY|6j#KԵuxp@"2'pA$c ~>5ԵcF cī-R9)9"8#:NоJ]_ OZkW6RYJy#v; d_`ww6mJP<$mno@<&Kra# hڴXڲk3r EeᯌS]L{9f?*H͂Ics[szM{>]o#ԦO1l[YBr~WU_~#IKB7GJ$͍%DEQ3FuW+A\W]uBZĖVFV5+Ўr>nzWsgn>-wω^|Oxu $ҾHKs"p+I S~'| m~I I杢+*YXzRwWocoHTֵ}Ğ Gz zB;eY#2F#nu 9oiWSּ!iYa-|w(:TRy1^uŚ.D<'wq\r lNJ7j;GO6^ y\,{Z nK_AkoȞ\?XOR'UâK4WdI~#$)2*8|=ͮ*ʐ[-)@qn|kڜx{^Y+Cgne/%2v?|SxgFmQmRKy-eoȑ>$M5~Ϣ'íj2za!/*&CGb;u څW7o]sܵMj[;Ƞډo/3)Q^NO|-[w${k4VKّ oocy#4~i󳼴6c? =(EPEq=#Śod;uqcsYki$+/b^QӓNE}lt4WMM:um 򌻿y8ӌwQE k!p`9?>@p2zZ(((((}GᧇMBk[.|v2V7Q:(?7疣{;G*o -Gv (M QŠŸ5V^?Sxoyj?7Q@~Λ{i `hRW$i%y];ǀ'Ԣ+矊 x6 xFڕ W2*$S Ug#A5r [Xޛ^slr(濅3/ߋͮgZխ㷂Ťڍ Jtz~wC"o=|W&ihJ/B0cz<_\o<+ znxQ=}i<1M\2i6/][{C,'|eqGTtk?:T [[? "OijVBŋn,wǚ6O6ޣ=jJ~l&sX\\:2cC8yV~VMۺŒw2)@kC_}oAgYb=B)azo>D.-?!|Q:jo_-\' 뿲$t+ٵ/>!Բ3h~Us[r+g|GycMt>Ң܉!6\"?GtiiUۮm,X}Swy~Gǿ|;mN=*6%^WUˣ!d{ppx&oն_i>"i-:,,բ<%K3KfGkKtG5CZ[ٲ4A`U k~xDѡodK[0Hmb00H=*ƣ:ƯmT!ӡhTl).?shHcJ8iO-tBT]He˫јc3Ie3;J*bxs@_|ECTT}G˷դbF"]{ፇφ'ٞMΐ2;;;,@8{]IM]xY?h R EX7m;Oˀ \Wm |7WOR4'Ԟ[y%Pq ־τcF}x0q6 v8zWm7xrϋi=?H@[Zjz\s &q/N_ Z^gھoZ|`]΅wҾ*ծ4;ۈ Mi~ϵ6p<6o'%_I^{AgYN",&ii懦62vsČ@qPY>oAi" 6fmeoF=mnJVxɥ|_,\teieWU,Iwc5 Qд2Oq-*IpBːNxUmވQE%Q@Q@Q@Q@Q@Q@Q@\mn9}/?Zw'P5-e$Vwu6qup0 қ7.ZS!_ǿkkj>/}XO#" w׮]DƝ[z[ҼeWrM2 Jʜt#x<7cm5 u nY%Sb;ZgBo 'T}/P[# 9B/Ƕ U[2x2-^mʹΠBw dc⻯xgR]]nj.|s[jWCslC6*SNk3t;߄V~_m'-5mBo&/,v2 v^ Ů>]kvc5o. 2xRjV_ӳO>5T/ŬhQ4J"A*+"6~W:_Pڄ( )mW;3g)AKo/ j)E-OPRMqު$C CkV/b1hvvg*վEZ˱_RikoaQ*#G,Kj> h? x[%xD׆x3+ EXȑ`0983VFS:tvoqɔ-+HÌ{x$W ͥ_麞/Y^[Z/n#uxу~X8>0Gо,iyWm3P&ok}LEP|ez[ŏ -O{H") |PUsk*qoռW>e{>]mDGu%@rO}k4j)ĺ')"2#Ϙ֓@_|Uτ/&Qfu:SXۋ;yP#x sU4C=4sO~TNod`@0*\ÌkwAi|w$.PM*vnFHs_<h+qk67c%^^[ V8a8;A4nogxN^^O;B9iɩ\h]mM23øBOVĽ`*J ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (?fotoxx-20.08/images/pixel-bias.jpg000066400000000000000000000442131362435004500170640ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0230Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 174 359 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?GL]";{o" N SV?5K紟ѣSi?QQ@~?4}'j*(_O=j{I}Si?GڧF-g<]GUƺZ;RՕ+n A$s;׹G>/}rg/?5eOLFk cd\=xB=m):/<% S<EE֔Lo8m$=hoQ^G_j{I}>?5_{ oOm溷ƒ$n2r}V|Ox/SZ5um^A;pv(zAO;nQ?ڧFO=}W,#$?5 Y\O>TOOt򓜲VC}k:Di$ʓE勔ny?] l1htkGeu2STw4{iКoW W Dn|E}7@+Q@+Q>du|fn(u|fn( 2Q_Mú37Jtú37Jt--| o7?kΈ 2c}Ev~|Q|9Եe&{)l,giTKɵ*v;;7tGG;7tGNl/H߇߅|5໽FöEIhn#eǘ~brp:VշO%%r׌1ג[*B#"L8coQ\;7tGG;7tGS[/ ?^|8>%b5G66 J4Wտ{VM՛P5<]Kl3nFc];7tGG;7tGF%@ݹuߩz |I4.YB;x|!vOJmk6|JNj666ğ` q;7tGG;7tGI%g^ i9c/|-/#A'K0Q.UzV ߆ɤXoǬzhOu|fn(u|fn(7{!ÿ_T})w-v^zWk/ڮƾ<)wȡ𙷴O i_oO&O'2.]ޢ_:?_:mq+'uV+|H⏄muo [? xItU.Z$V LCz+GW W o>KZ[e??o Zx[L}}.jj*GǵghD 1\loˣiXm&w/%êhu@+Q@+QY~iÞ*|'s,kۤ7\ g< i#>HJmb]cUH_Cú37Jtú37JuB>d3[Opu'cW W .yU"7:δvz4YeoW W 4tk٭YP#W4a5c̗ y$c(TߜrO@+Q@+Q׏)fmKf>(q?tA*x{wWo_2sgUtG]xL%,%F{ݞve1^zYl/#bBzմts {0ۊݯok!tY_ Wo>éxb6҆ &Ap U#95||0Q|x}0 .%`e. qdQkD_j??#O.wſxž*5Ojm ;}tuڅCv֫|xs_4 _ZYV S`nԿ>_c#O.H&~[UlǗzvdž9Ȍ+88 #wSZ,iL%c˶\rIzv¾>_j?1Gk5݌҆1 cW?[U Zms^҄ `yk~%y\<h̩f^};(kzZD<#Afb{4E|~"Tխnmo UСF< rL8cinF<沾Vlm7ktv/|L "PYx"KL^W?P+ׅ[.t#f" Wwlo/Kg_ɦi-YnDc)b8zFQ^Q῏g/df;$Iw`4=*Z6. Yw}Wok|dvqatkBING6AmwrQ N9*{y"_|:fchWEobWUbN1ҼwUIXrA5 jn'S*깉Օx;r}= N9(M[]lSĿ\ig‚\h^>@*$ÐPF5| ψ|#L-ٹKc=ᶅqh8Ew k3>B׷o0@zqXk⟁O _^zR`,9+>vgx7}%=b%]ȑ dCl?t8<%e{WB- T +g~>|]/V#[u^TOle@uɯ1^i_jvkv.[Ƨ.6B!+$Hե=s>"xG֬GoNX9Kr13ANVhiQ|Ž![yUaEDa9 1\ƭ:ς#Xx;ƞuChs5$TUTNzcp)?&~*C_^*NkW2Z^u"1dL0Ks׊.oG_sLQK]w]XҧN TC/+ .$2QKoi  º@2xlUNFyv 2\׫͵d~ PϮ ̈́Wo,7lf/᭹_'뺧Z:hS΢XUHcWzxB燴O?{P}>[KCgS$"5*^pN=¾+|2o xSVa%x MX]<ݡ2 J㨮rZxK^*ψ|;FGM(ؒ>|'nFzyƶ|nmWha08QNOog·7M[Kk=^Iu]Yn#)'ڦrϐ@#տ^Co Ũk341ŹĊ,)Ꮦ_8wJpq]G:V?ŢT=]/C2HB(ɃOI?+Nx×ŵŝņ;K,#B8Ex4S寇 l.7*vw;ټܤx;xvS=O>'ij57v̅TK2#o#=ESI?-[_ ?O4φ׵S#An ^kX"G,yE_<;+׵K?I|Dг SB!O폎<|a>¶M;K(I*dϡ/fqe⋫OwCSWӃJ_TT *j;Ko x@[u *H cVq|&%^9RHA49'=y]ux)[⋻agk\y׉koDyWrv(ѵ_|;LW[[X|ۓ`[̡GˀNzsCԷxSw|/]=uX/aؾY& sT(>ӴKKY9 ~c 2&߹\+n W;ֵMOFĞ5ߍU ]gIPX/>1iׇƓveY]mAy [go/io n|/?ki,<PѶyr7|e¾,gm5k=Zծ/#k"8~1GUׄ~!/J֗KZ j^2W W6DqO@*+wYF% V[n vlK]WRO?/ׯ;oIof"C9Ɵ!lqz/CemCdOZnJ(Q@Q@W.Ke+zWOV?%G(nkqGQ }V;o& <g r2:q|?S>wk sšՖRh&W&B;<5;8YcTv5kTG d NxJN+D&T_?};E|{wxVR]Kqx"]Qd3`U;z~+͇uG&y>#tiㄕHׅ- g~x.|5JK/ ,S͌+J;3)%۱sG0&7WT_)Z6KxAwm/mwj4."?ĔbgXA~04ۣex 1$G s[Z]Px$G"9v8[ I*FOMkMW;5ji4sIrs]=;h ږb d`͸uD*1j{yf`#X2? e.%ᨥ=ɹCyml!V>lS#Gzǁi˦5hgF6cMF&+Ye6N7pqH`rICޚiQ_?>I%쨾p+ڋ᷁O@ћV≢KLl)"nwF-g4ߊǛ ;|3i\Cuw.kPUPIIj6}rC) K\6Gc@Dn,ӛzk;wW (Š((((c{}W U_}SgDzXʪF)+Ki.6wRc9]J:_?E GK_֧xѼS}\&˫hRA $6`O$`[ٷZ } nȶז.cEιGu.?a_r^6m ֽS:_?E?!(m_XI[Cl?gtmFlCFTnXyۄsTt Oyl|IPt;ۻF]$B#n2k)u|CQsAK"xo}3]M__F-7-⶛ ; qzu.?)u|CR커Bү<_`+{"]01׽tg?@ִ>#xMH]BqgDB89]u.?)u|CSau|2A|.]J{uMcUuQֵYח ¨dWI i2 6"qX`=@)u|CQsAK"q6<ßI45x$xZΦg,.1G:`0e)?=ZM̨eQ|@4/MGmlSOBɥN%Ð {fe2qB3_>0xv>յ4{mK ^73ZҌQܼCɾ:R=G֏-a aL+CeԸq]Mh?~x-K1YCvti%آs<`חd[[>Ҟu{2vcG <9W]V>Ewgo~idw\I3}Gt;,p?!F=knxII4=@|1xo4sI(&2I…ڤ_F+VtU҄̚qgm~N3i&fGnpl|o>;=GL4kmp2w.%[gִ?hÝJ<177Zz.?! HF*9Ocnf/xBVIQ ';sUECtkIgSej=^;>Vk{{zMֱ=݂xbeaH[Nloy~5i+)g&qr 8-6< Q6v F|2ϮiVPu܀xt8]ufXTeb毚?W/ ?Xcԅ'kemco070>i_ w54i>&Ʃiuc˷vDb[f`%A!bWȔo>>X9o6=H/ U'r|]IcjO~x2N:F}L{ξ5-ޱxnjvwSmj ,wHmyG*9"?o>mğ,٦|0Z],L7uFz)O_u_kKxz<%AqJ|8>'RK4/~.[T޳{mjm՗#I\o-Ìg9b4z#'y_hVZ]֗-x洸RUFkf] ÐeW} QW˟~! ZD"E yK$/mj̡Ry8cCվi0+/n7.|$LI>%7oϰHDm q3Sĉdh`X9l_ZF~~;|3ԵyP[Hb@6!AQ^:uڽ~q?&9 dgϟxwȎ {ko xz c0YD6={`v)31, ̭,KmY6pB#I1h>9ا4$aͪGzyӱ(ãwaᨢ4#g׶2o_i^=6-Yh.vH ;>tRɼAhnej&-Xmk g?&1G!~UW@P&-dӚɟkV͝ۊm981;KeK{8Ȗ=X,nTS h-r۸~ ʻpr9zt鴝orֵ(fotoxx-20.08/images/plugins.jpg000066400000000000000000000515321362435004500165120ustar00rootroot00000000000000JFIFExifMM*V^(if%0231+0100Fotoxx:trim_rotate| Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?O# לxG>jjoZפD傹0BFx=EzA_|]d.#MknrZ.u,ͣJ59RUIvIB4E qw'DιUF7$aCpǰ<ť7KgxchCuFp3DqL#grPJ{2=MQź%жW^6+sccV^`t KHIc \ikWfD#t;@W?چ{ gZ0=Zٝ V"N,I=KAnUM>۶Fi9si`(ݎY-ZAN (Q@Q@Q@Q(FZ)7QMnuhFZ)7QMnuhFZ)7QMnuhFZ)7QMnPO#ھz񾝪ߋ۝:<_ZijGP. k5UpU /Zi`w&@#\y Y]"-jm"ķRIj?f Pk?R:ǝ~I|-BţKo-~@}xt}xts_ }п1h[C_l??l??.v4t/ Z?᤾bGO) % i/>_Ô[CG4t/ Zlt}\9Oi/>_I|-Bů5{2CpCNˏzTNm'/Gmb#$Oi/>_I|-BůٴZ]:+;vO] 2H 8ÖM4s?_᤾b % "-Ὰ]NѲ4;U\IbnioiYJ9E8u8$qG;SKo-_ }п1kݿ1ǩ*y^jXϥٲ-Q3E N3֗a~I|-BţKo-~@ ?{E'_9Oi/>_I|-BůΏΎv4t/ Z?᤾bGO) % i/>_! [:gKZխ(oLO bv?Y᤾b % !/vgt%SЏ󊹢׉: Jpn.p Z9~I|-BţKo-~GkVEXC |Șs@дS4gHǫڦUT4Fδ)p[C]O> xoQI'b8βmҿK~5,uɛT z|uA!(c<ÃSK wF%l,+Ep{WxívIu{Kh.KKX$m$C*Npd+±褏I<_1|/+v; >}:ފV"}z3ɸo X 으PyG'C5O>ѭkFEJx&E43w% +<3ThR7cΗY4+NIш>!e,ڪpMg}2Hv&-d5gوl^INmK.KĚ^B[\kZe,+{msd-Io"DeRQ<;mKS]7ѕ f?gm(>u{.m$evGJ\B,2nerWkX7GZZ0z6]MF&Uڃ2& p7_+"xkRJd:5: AE$ȓJp(F0?j޻v j~&o Mt|),PIc_2Ou;M_Z ^'մOHIybeg{dc=}Ou?f#C1xO&C˫~#tngSaslh+/=Kj_lJT=F5Kϡ(xk@Dwe׎!CY֒v0t /qxoSl9p#MMF.b٘pl"8^1y|r|bF|Ǟ4˽fPpwҰʼnjRezGGZ>?ӱ&ڇj֏~X.l\C9GZ{jpRk|-#ׇ#*!p0;_^U;*z?4zm~2CEi2ǘLx޼Rs5Ļ#׮+o1- })j:>Teykr]C Cn|,ՒЉo$Van1^Mjmȼ/Z%ٸdnuV⼼ h)QxVMM5\.o ih.OD'+0:c(8gUo 4%E9~SÓwVk^t}]|9w:E6GۙVAlnW̾!|-  (nԷWyufgXD}23P2vKֻa⋟i:/x嶎->DDCkQ;`*xA=8ҿhs@:^4,Sҁ.o!F#qyk Qmi4z;^K ٞ8=(j1&Px?uKNH}\koju;<^Z-+G LKD $un?'x-}؉L^Q8]cI"<.shV}|Ju|Siׂ4"KuᾷDT PRU񶣪znkkV:(lL_oa-n4kqctWͶ)9#bUGtuR VMAkĂXSP0KxO573¿4ƽ@fw,1|,#L[hbWBF|רzsG~Б-41|+#L[k:K@<g gG34ƽChMF evV#,w̡=]wiKu!-kxzhMo4M Y%`UL+/+m*?xoQEaմCmu wx0 NO{$)W_PE5|-:='Pt 2G`#KVn;1KL,?g|.Y)Afz1"OֲM`ѡ 7<`⽎ <WnmItVqGk{ao5]>c K2Ԁ,zѭZƕ⏈WZ蹴$ҡ9VJIE*YUsP+;~ʺn.$ВnZXYNaV.Ac4G4_QFx?6.aq 5x̫sn 1?=^sk֫}մk-$lw)R8 4]b_FԴي$ 4w]UrAn9E[ bH4H\3BX|Yf> Es^O=vL=*C8:5GO`߾`Cn+7+x;š !]Os0[;*&=iV3?{1xOZW'G |7{_b&H㚩j1iZy}kwuX%Ω*9`AץQ_k$1Z:k^Zm@ܧ8?}B+}J8tqI ,?i] j7^SigYTT0SC`>l}kgƽFhaėOi%ͼÃb;+?5 x{A|=&h7GYf >R0A|8wWT5 oYfD84.;("U$ 3;Kc1B5F{@[p|QmG:_OX<^`ϥ FXK jk3Ggk$nm-oKsqmqP >!<;xjZF%;(Y&}Q;y 2s~7]p XU.IfN/̤Eq:UGVG_iXO= ix{S4bf!џ22Y3y({U1&MAEYcH[h A,98Ɨ^zwZяoŒ͒q^eG}<7_IxGLuó%"̒ٸ2dPo7V>_)iZbAEP0(((([Idžo-$v'"yߏ|};ޭ| ccjH835ோҼ+q}̈M4vn Gg'|wk ^ՍT򢾚g$d.#&4_0S}'5|C#_wŽh(u-#R2rct}J|_['_𕾓㖶BIh%ma+[afJ8h|*㯆?CȬw n!Y|01t R9#Ze݋oyڕr=0 +.Z<'~IlԶ]šRܶt-2k4n=؛]aG-gwcn9{6KePVZ.ՌK%ÃSlX8@[kH47+ِ}nKt9_ͦu=V}:}'}yq,VLG$0`|A'~(_.oy2OBLH!VTD/|;hzK]ѼaS5s u@0HLf'ߴ j>&?Ē-e4cXmf^++$[[Mؠc֔`iaV-Q@Š((((֊QE0 E(($H]$c1΀)}4;-2 [&6Ṙ2)[Zp{*=n~m熫"n<5_e?Qc?i,)GF>|:Ӽ!c⫿x~WRVP-!N-qkѼyxlL4GA\jVI%ԙ־|𶹧ҷ>=Դ˟ Xà<:Yj1yI[@Ŷ;x_Oj?] #;'|<*G_9#!~%?tMCP|_ol%-ėv.zV$!,95tZm}JO\jфyafI]-]WJ1Ex'k }a>;X};6?%J[`FA3sICtu_f2\g8SMi.d:"Цm;̟mf-$c[i9sĿFMb</kpD4A$6i`Ke}i ]QM$_[$N74.qIUuh\N*BG R }[ @5:O.e47[캶 E$ A Aaxƭ4GNѠ[3XLq͛v`cČ6mSx;Vom=\os5@l3~kZe&WPV(k?mw,鐤NdWF+FtrIYwG_{Z}+ ;u\""v 4TIMfڛ;3P)i%QگJ5 ɩh0 _Jת1 @:7څ-͵ql-:ĭK^ou6d$ؖf]$^3rGPZǟft_1 i Vq71nd1HV_gQ _L}$ݣM S! ZWv%}W㇅ xf\UHsge5Ğd]nv`Gh0Ni$ŦCjm7JKn.?.dpN&}SYbxWm_>a/$ҸQgv0 x݅/xN𝦗ycy$/Meг_\#ɑ$;bO!QBrKMX]x{Wyc:]q$pܩⴿn|iU`懣C [O[i7(2_dC1r\oU5a"6>6m?|m\SQύ_Q`)cim~W(U(+cim~W*X ߕ ?+r,?m?|mߕ E6>6m?|m\lx5<-:rgGl"2\+݀%'nXh^}i奬*Dm lT#&<[EpH5>ۻ{+m.1heA3new0<4mԗKm-_EӁZiEoG2~n8o^8t7YM6ɈY}1,k[YGooI%Kh^fH,Fp2@Z"ڏ}K#7z Ɔ_~]w]ݍȻpDpHh袊c ( ( ( (8#֎ڢ@W*GԱ*?SߖO<@=)Qodך3ҫ}5'yO~ڀ,@ ASߦA?jGcv< ;A?O7jƕmKN/t}f)%- :@UO#V(m @Ə1]$WԒI=5dj?Wy~.c+OuᘿΕay&y|^C<ʮh/*?&:o7G&t o7TWy~sߦ ڌ 'O[ӿ2sh5^]V]V9Q @? @?sߦ_?j @? @?S߶E&Zo7G&Zo7TWy~sߦ k 9k 9Q_?j?Wy~/e+Ote+OuGE?yO~ڀ/eto^ߟSߦ4hsߖ^j:޵ZZSF->*͆q|G id7.ѻ5GNdd╁h~wx@fueKeVFY-yvF=LnY2ƺ>z]i5~(MK{QI4rcnVfK';ny j۹1\>&> >ι}jo^M"6DI1eER1sZ>Mαi>3CmD6ik3eA|Y_ㄒ_izM-|!sqsۺyZJ!FDFN$~y#=Z-nE Sȩ EQEQEQEpGTQEQEQEQEQEQEUkHr$I N/<ƣ@q'۳'א~_jV6tiZ~5LJ9iӽơc2]W(n^(7GceFlMSi!G 4/ %Ipӿjq:7<:cK54.yrX{ϬX^鑧J Bhp 8 jɵ?.!%5ePONeuOa;[Za6ϳ\׈if/Pr`: 9j8l|vz+D}ˋ~۰2l\yk6`]D6b>_ЉezZö%s}5x^keJBCC9Lsa\{vMfOc\_'מ^|ogt^Eqh%,LU~AҴRT+,ee0S2~ ~w}iWR9mA*q:kѾ.xW"mEFw-ew*:Fߩk$t hqb]Z< F~OOJx?al̟^q'Z[_i+hځA{ !̱27z"֏q^Y\^GpSR1$ʐNvBT7cyu ~;x PӵWmvhInʰR>8CsE51bf{g }`yDc*\nH@>[m/:fVj"]K5#eB$o ״{ڵdž.mczZv][S\*g(W^Kc lHɸQs@ \Y(ycrYT+`7CG-cW?x6@/ofx[Q9Ha`aT*}O­kDsMW=<فx[i]Fhcv schv} :ZE(((=h((((((oK=)ۀ }?3bfq`9.C|mC.9.<h >_~gChtq]۰29erE{/qrW5oeG7%[DdK۹|0}IՁ+k+:>åhoRVSGʘIUX+\߈>c\hKIe<1,S 8f9IWԟzmh\wI3NqnҫxA-y~x5A+?$jq`,Z2#)5ˁWh~F1{V:2Si6|OCڊ(6JP֊)@;p}84]6"HAȎp8E2Ka@ P}1RE)QEQEQEI48QmbUQO?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/ 2ˆ#rpq(fotoxx-20.08/images/preferences.jpg000066400000000000000000002363421362435004500173360ustar00rootroot00000000000000JFIFExifMM*bj(1ri%gnome-screenshot0232Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 869 690 0 C   %# , #&')*)-0-(0%()(C   ((((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?;X@>_Y4KQfk}:<~uhKQfk}:<~uhKQfk}:<~uhKQfk}:<~uhKQfk}:<~uhKQfO~ mE[B[L~Vw i\M5ߝk}:LY04cnE)r 84?vjW:- ][,HA'4<GoνXC5KZ$wR4DYNJZ0X^{GҝSjߺL4o_>k}:<~u|?H|C&|ڴTW;]cLӚ#-ȌpGo5ߝk}:!I]]]!;x_Ij+qYyfG';) 4ڳ' Goν[D7+U_,R[#9q<3dΨmgAz-Ҫxٜuu4_TZݎ~՚km<اxVG+ssZV݆nP^&=&O#C_Մj~tyX]z@7aYA$Zׇm|3-qy(.ءSpE_Y4/oΏ5ߝE3@_Y4/oΏ5ߝE3@_Y4/oΏ5ߝE3@_Y4/oΏ5ߝE3@_YK}*@ԭe[{("l'@k}:<~u{vpiZyFA+`5l N, dREwn9pEvl#[CKŽ;n鰋;6[_|GוW1pJ7tl >I;NJ[C[Z)#${*{؅< EŸlњQ8O JOQ9O Ji3^ ;?iiG(ύyhz_(ύs>6晣5s>6ŽZPfץŽZQ ;?ii@i3^ ;?iiG(ύyhz_(ύs>6晣5s>6ŽZPfץŽZQ ;?ii@i3^ ;?iiG(ύyhz_(ύs>6普GPӼͤmBX[@ŽZQ ;?iiGK'G7Zi}e-̻6{89ǧZ^/9o4YvvֲF㌫'ڵ?Gx|m?-(eERǦW\(zҴ `K>c?E%7 [_PwW?Gx|m?-(hS_s:J8ּK5K^F[о'}v:F\]ai Ts>6ŽZR""B4㧴W22Õ?OҮkb_StQ2$ ŽZQ ;?iiGaӴ˝-i\mInrc:uk.Mr"^ѿQW?Gx|m?-(m6|E3]yH` 39R3|T={Ú:-GƗM$pPs#&x;wҏQ9O J]Vc[ÖvvJ-WO麏-#Iik^ [ 2w”W?Gx|m?-(]S~( QlDWmhr2H8]#Ž׈QXoڼRU1RŽZQ ;?iiNbk}S_{tbk.% E++uٙJ;N2}*(ύs>6X:ܯm! _gAm&->ԞR|Qmm5.s6ŽZRW<4f/wҏQ9O Jb<4f/wҏQ9O JLњQ9O J?Gx|m?-(3FkGx|m?-(4KwҀ<4f/wҏQ9O JLњQ9O J?Gx|m?-(&ynzΥ<0t=85Q9O J?Gx|m?-(3ZZ~X@A17)BOA](ύs>6r\Np8*kGx|m?-(o(}@]P6OGG6\ȨF9ՏQ9O J?Gx|m?-+IG8Qm6vj4,$+Gx|m?-(˽ϯhǦݽ, Q>}j&|S=jS;PX`4i B{>_č{Y C6+1O2#j:tZ]rU>8>+5oxQm   y5 mir, Wie+IMOL[ݙUUW;;t=gM#]Ju\,W#oZno|C}ji tǟ1+69  JsƳj7<1Y׺}%Ǽ @5 fX.7 ] WǬ^i/u4M3K;y4Ec,F[׷5xWOKi>xRE Yv+қA'}NҊ)|N -lŽշxt#c-XxTax5xX!A Z+}VK?vYYt2+>[EGqw.;g}(.hTkwib02Iꎳ?? h|ɺS-ٱ[WG'φ5 3Ny7h[Ʃx"4m.Mծ F{K'qi+BaCi-=O[QX>a YՒ8ب̙sE]珡ֵ=Y -% `O,c #[˿oo9\] j a<0jz5VČ@`Izzm$ccnv vز qs@\Vywm+%=rM?+IO#[;x|}qey-?N(" Mm Lc`4˿oo9G? rgse!9 ds⼏~k'ѥdDKYSarZW;˿oo9^]x?<n4;aPAbq'4.=V:8dSyyC❿W=˿oo9G? rnu+wm([Un ][+<Vcu? r.-X\ug ,5 lkxägnr3@ėo4QΫ Cu 5|-O]j1XY,2ykvic=u GҀ)nne[<1Gl,{`Rwm+^>]:Iu`sjUskR_v3^w6X%V,W &NJaG0O<Ҹy˿oo9\[kV^(K+C,I2h?uu,6iZԏ3-?yȓ z];z͆{[lg }5-EQߧJn<|4CQ(a8RC l=i5˫3TpY<uӁX*B̜(wz]-$|yWNҭ ˉByy *20SWe Xx\uQw0:[Tګ9r>*]'[: @p]?yqg-uum gU:{^}Fsa9?7&)[K%N[:pKem%%x]Kc ]~"4MJO[}.->]%r<>}nx-:/$:Ү-u,ޱ4VZvα8NA$0I,qǶ+Ԩ,yrhQ:%GhY#+7aG-zk{ou%iwٜ+B6 kp:x1t?cIchPj:3~5/xn--N/)`KI,;$R~{W??ւJPVG'_><9-+0³ܟ_Zyhyhj4[A[_m7MLhrJo 8RšƎ|q\i`"&sF m݇pWO.J]B-6*?R47QkAI Q;_ O47QkAI RT@'[   ;g-[J0HyhyhI`y._+54é{L+J8uK`-RjIxFtI5@kFPrz+ƾG`WM6hKI,9I  nӿ6lӿ6V0.qY63k [DF\0>X[]-v"sM٧m)j͔]v]B $~%±ucb 7"HF~%QEQEQEQEQEQEQEQEQEQE暏}N?\z[ ถMMcbU SnzWׅj^3on+BYl/cXQ 0 1>j1xvtзm\t#ch>h<1Mܾ{TyJdI qWv;N7>a3\ݥhr"L^W:1֢ѦX}gA-vv'%?3sh76)Id{& q&KZ彮Mq$(lI `zf|}I擫mf=Bp[Fq3ı֧-U2] ՉOX$A_[}Pn. FD$rG)kIsڥc8̒9 Nu}95]#O$^3玕pռ={Z0+ f ^&м=e^xB%h¾m9^)bW:moǫ֗[CnO  azxoͬzm 3s)5j6:֏qHt ]uimbnGɹBk?SqjZLY?t9%AyZcz6_kr[KFgEm9`;T<-sg=ͻ pqrG&W@zI,diQUc)9.yju懤_6&U9)$Z}_@,/omu8ɂ\HA=nPAR2^q Z&52YCs? gע>My#O "f |7ݖ qҺ 1iWÝcNmtP.2pO_aI^ZX‹pX݋o_'Qxr6muksdLQtێ=O{U5Mb}n[dLӚF ^6v w/SqxV'EcԠa ʾ|-7ooߴђWxE| >o<"ME!-*V}K;=<kxD>i8?ނMZƙp6sc:0sz&a{-ܷ6-,J&'=I i)xcLң Kd`} C:4 G@?+J2?tO*(pkQIqBO\և!yt (((((((((((:O{`BĹ9#@V^Z:=WbS <:ԠOlWԲ󌃐x (/@4c r rORkR[R"R#UUI'wC5I4'WE.(`Xd c,gIɽzeN7Wm.Pf[kh̲!@8 T6W1Ys|YpM@Q@-r `7ZkAǘ ?LhHjV{\ x9̏24c\rr<8@ȤYbI#9GPqOgzqFefh6嶜θRWO/"SNԭc:OM URWO/"RWO/"N{“Iq8ShxAxZ"-#rG4 j_Eio=E[ZRCV A]^/wbu8/Ą'֞Yk:t7]wVs ,g 8WR-tH p?SUm mt ۘ^xC~tEPEKX,]:[R-"y[8_ʀ.EXS;j7,# `eItP'fAkGfAk[uON#kWS yGuQQ@?5 \߸?5 \߸ۢ2}$K4\Iq$[.c8@=ZGKӭlij FL5N K,ccA#ۭOBWv@ VgөfBG}~_?hdYrO@g?T,`6@$WOX&.EQEQEQEQEQEQEQEWŎmgVs_j\鎐.IYb#{$5 JEJ1]qҶxK Kֺeխ-%9R"XVݮ4ɦkVg,-_1@*/r+R>ѲA،4o^8{EYHӡYd;d]cNZ^Byk&-\&gqϴ\1XѕILuzO? tc .ڽZ-B^Aҫ6ܾ`w4 19'@?6tKZ~xGT֮ɧc}|H:xz>SE({pFag FxcxXmdu ==jZl];L@"Ό|8% ̱B60VMŷsW/5v񇇼wKy]!aB;yEr n` \͠^%''pz7N1z|!5DX$Y#\>r#]e_WuP+ c$Zm&ejJ@V%P8^>Pp:zU_RTl[)y_3hv ۅq+]KiI)mv$aZ\l8OwAC)&闂L.\9WB"pWJیkhg_ mQjJެC5G:w~XVR4@p0kծmT`$@Xt#=:.uxwZmjAY:ahwSckwz姊 cKҠ8`kt5]_ 棩ѽ0oxGIKZ5bҁ|qyq-'FGڴQUQ*(P0V+$e@q3Dʇш8˸'@Xv?*J͸l癤((ZCU[%G#q?QWC5'/K} F*DǨ5Em *F4Z[O,o+qJ<‘׬^[ HqORw*B'#ҫ|?SMj2ptGgQ]]`bxoChm5D1[TS UlYmmq /qO6߂6zl4krzWQI ]TN% 8uJJo%\G NNX @ fH0Ք%ޟ?:r~iLw/u%X}:qij<x~4]y~ DƁfq>{`\\Α~Vt|C51O]m9׷^0M v$Tض6;v}ľ^M=-?;^GM֝kjiJ Ws(9#N3kt=>쬵 _<|?/8zDZN PZGg&w+K=#Mdk=>ݣ!BOg+{}qgq L\3`8WY~Ss\ɍsm+[[uk!r7j--`(!_HG( / |cգV) ֟1ԊKQ*ƯkmMZK] >+lj搢x^I=ɯPC`\:]w{nv3IuihuLR$FVusG nK~ }dܾgVߺY~fA 35|2[]isuU7lb ].oi=mV EI8̠`4;JӴ76 xV=t MjC$( /OJͨK{z>닯H5NX01^˧^[_&[[ʦA˒;cөZќoʱ|g#fI=6\Q^\iʞ1 2 oO(c(:i'𭦱Am3[+X|ߊg: a扯bƹml=F#=ErK޽iR bcp2S$ ?PR M:λ˝u9̲y|󻿮i "yxygy67EB &hWL7ɱ.ݸn]ޜ.4HZU.;vq@{:߈KይsL)ХmnxҬAqV[5ܐѴ"ǜQ8+𾧪2i͵w'{&h m$ xϭvRSZ_}~~x M\i]R^4? [xv,5A}umco!`pI<\_QU mO +ƹ%dGjħs+Ys@3^A\Z߄|/j%%h n"#*:8Nx<5sɺm|o8?8r3kH9 xݑ|wD V1ϕܿGܿ]5^)}6M{-qgwIS2gkSs?>jC7 /hou_[+k.{ q pAsҖgsG[[ݼ[xqmkV " /@=֝mkIԊjvrw7g~*m9S ;TQi@иIV q_j%떳Y_Y HUn'=[Z5G+-;Inat qrG[; Ex%*7-#/if )' 3vK޺~>jZ:TbKEy&ޛKځ\e&m3Q/j)%aק=8-_k ,fJ+|0@wh/^\u=C:.CCCv_+θ8˟ұtzKY5r;'|AoB e s`h)^*(37>OIp-I1i(ϑ9?:].{eG4$"m8'o$N_?5?5y^t^uTP>k\Gȗ%D S |=l51((((((((((((;úe4V풉~pe=>z(5žͩVQ=; +0:zU+_i0[WvDQbpN:7"OXI{]Hgңx[yC*ϯ : 9,+j., g5EȎxcHgdE(!=+on`:A,VSrM8Ί5}qqsюU{UuW9hrzօӫvSN\c!_N:2ܽkCI޴uo z}_CS[]q#_v_*ldp=Mk8[hX[4VBrV!kKOOڏ]5&.?ƀ~ehq4\5k PIQ  _'Ӄ4jM?fɵR`x&hr֟{@?e4@խkRKY$Uѹp2qF-TQm'dyvR+/'O̽|?ƀ9'N8{u$#ۛ$W !8SZ+FF)IG317?Ƽt5^>OG{@?y-^ }t:=I.4*$@VlckԬ4{KSQ-ΠӖrA(5c̽|?Ə2ayXnEqZͺI0;F# G|6.ױyz0Gu"E# xy^>O@lգY>-[3$S1'MN#(4/m$SK=p+$2ךgnO"L{Ȁ:w-xa/Fy(vfKWyˋxL/I9Ezn5n |V?5j/_[y=doJ±O&@m[eh?  M}mO@4yN tX,x'7PMڴu{M[Zjz]#" Qdm'I.нxkz5o'1 M!8ۂ ݎkNioCc5jLmHw#wF6 uGPN2BҰ!_fM*=Jnnu(u Wq2 8zh]M[_;MUm?ʥQUyf֕t9tJ帷:s*\A< _d펵3[Ygԭa[_6mlY-ONOH|EgW-t xr̔([/;?zζDA-L7V mR+;W1&]7Mo%Ƣ̱EH#ksdh6DB2ӂ@1u^4n{w6u\\o`>C'-I쿮]k?%ι{\46ɡ$f$;"&رxz#G1?|5[O.n#k# dI` v5mB+~ JKhܰ ͅ1 H9> eduܤ<<_g=%WV3JGq & oc؄|ȸh^ꖷ kA5vky?@o-𞗧V-{)ooN1"Ư*Q %F$BK $=1/3ƚVcL"R5KUSI`7c*;ZZ A"S0F3#\v'tNmllnMI".K H[OQxSŋ[.t;Q(ªJ: Z+c}ek\kVFdSLcu.ZGo~׻o8W7|K w64(Π&-axwem~DT缶n&,1[!!.As |GxAKk#P39\Ğ/x7.hLPzwG@*M;z"vZމ N1,m  [_G5 Y)H`z_NzQRPQEQEQEQEQEQE˯s^kY?KHS zuϵ//XT2|50,$OxǏCjTtoHaEPEPEPEPEPEPEPEPEPEPEPEPEGNl).㱸YSAvEPQ][wo$P< Xzx5-RNӑOW9aJX:=;M^-$@茎wH7_Gݦz/hʳFS9!5@Q@A{ڬݷ͍v3f^2.cֵH-gt2,dm ^phc~?6 r3ʏHԬ6 2.l]ʝUS?o9FO~lX+QS?o9Uu}jIN^[As*[Ӆ5@g(ߩ͟7JhVm?"V!΀%ߩ͟7~?6 r/|-cz,u[ {B% !'[=kdY74O~l?fZäȲ$W8=jWߩ͟7~?6 rQ@g(ߩ͟7P}37g*_~?6 r3ʱEWߩ͟7~?6 rQ@F`{9U>ɪ[68f±jK[[/c$?Kck g(.?v r g(.?v rZz!%ɶxmqqW(#캯+>ɪ 6 k;MB;Thg"ہ#c8@Z@B ( ( ( ( ( ( ( ( ( ( ( ( Yi&^}QD[cbaGzٖ-()y>ZV:X{9]=M_YM;K_ 3r@8>N]McL}tqw=̺i`X|8ɯhVkk 3ct2c ZDҮa+2hB[,`𦿯VMrViCuEqw& Rr u8{WEno--{HdiHAqK:g4y'xVTn,tK&t1 A\}滨skiK. Xmm6˿^xbW:-X[$آu,텎ݟg/n1jKeu]ž)Z+ O)-us{-Hg@*ck4-Ek{Gufq;Xڈ4"f *(f!DEW#`8*b^88Q'v,H܅lz(f Z:0*z$Q,wHp]KTs\_/>,/ B1iPx@EpW>)֮'CІ>>5+gko/pA9lpIzjZ狭B- _Z=e1Fcd*q={;LW]i t.|k5x8Uys~ Ԣ֥F"%K&y_Vo/tQ-bh?oSVvwW)IiH.fZeΧ m.a/SG/t3m?R5X-m8>hHě#>Z+^j5 ߇E[e.<!}3gXA/XASs^C>qiq#ƍIysn((((((((((((((@:Qux}Uuvj߻<k +1Rg?9Ռ }7W4 t![*G-kphZ6Dzݥsg,F6KB@=) Ch"Ė WPh&(#2)P9xvIy|CcWEaQyX4vIfZ|SC)w[4P/Ekj lm[6A"۶zxW<k 6{4$ w (EmKXgS\JFp:`~gMՕ3[Z@_zW5GvM*0`̈́Z>`?^\+ȡpGˌ|kxêYCmw0vnջs:Ҹb 2I'5ڤ+dicռ m~]/P]A%hBB.1qOajv!&ۖΡ"4yAPwRϕ?ƏI>WŪϽEՔ\v ??ΦTw|Rϕ?ƀ*Ecwq4rs&ܑBW>$U`B!'ݴ ҬZ5jR۳G@ A$~QZP}L`kSMIG]ΡBl4oLjun\Sr(}ـG#0MvPiu-GQԮ5#5î*T99 hhrɩj UfP65EQEQEQEQEQEQEQEQEQEQEQEQEQEQE座^ }Wxhv8Oܐ_q^, O$ƀ;'П6^"[k<[f`K b==pEN3[U趞-E&i JvH ʰz;q]⻩eQ/yq'[;JFK>dwmua0 6ڞ c6zmIsI!V#/K׼o6ŭ,nMok{$s>Mx_Gu;Co'ko|;|4Qm`6^y{Fݫ/Pl-BBLHo&yH9hשX?֭WwZ.!hU!mfr8# I s@VMbX&!1k[%URzV4Zkcg;I-՘DN2מk;≵:dYC 1{H J̊>};9<M?:,V$2[a HX##~{:;-̖ <:1E߈l|Co<e{y 3C5qq(vҪf `F9ch= }7ؓ}Ԗ֯,p g`s?ny[4MFq==hV:6vZnݽ̷>Y& m% ލN|v0_}ASq0ܑ2$#ޒwG`|c>o0ݽJbVW'o, յ(ܤu(݈9#UXMr{kk  8Wi0Camm&Moۚr<;;??jSIG=c5By>kte!Ҽej&'td L$ d7iw>4=&)tkvi q,O]%S)mcֳo]=[ڸ`H㟭yտínO+GկuXuK[;6F*>H$- c_AhZ==Q:%GRl$m@iGQEe?. Z]Ԣ[J ;ԅRHLh)jtx_VՂ5嬐+S+إ|:KJjM{iJm'Ak:?ZF[;cPQxxB_{#uv ;Ξ6 "䜂ATt&$Tjd)hA+m:;;,2[P]4ZoV͑kdpL7H6 vzΟ=ć W Y_9a0)5`^ahؐ@rx9>`Yx9hN2tܣ"Αwţ-qٵ(.pI$z;FToj.%zu֗]Z$ A*[cp[5$5ӬcٻFٞN=dkuODcִlpYϧҲx'ķ7MGԴ8"Ԓ٭ ˸ t;#җ=_/TҾO,lؼBT* kψVd^MDX+T#5/[&AeڜW y#v8Zω%m6 8 Q3q噽CgOzRË_SsT֕f.m4H͐Geiy6kwk}ey#QY?.=yrڵl1 &d&|}署yg}[iމ,3E7$E Up2y7 _Ot+{5CÎK\t* ~k;x*FSЯ?薐泩#C3"{:( M64:Lv$*O\(*EP0((((((((((9x^I"J:PA)p,pE+cJ($~'xHԯ-[{99`2;WI@Q@Q@Q@ 4'TY#u*!((#8mTQ }QE2gD"!FIǠ@ZMwֶ]D1.ٱI^&a QJOO"PEP7p e2A :)Ugq5f]FGwAMD5[7(Mb,=C-"R,ַQ#,%OCʻ?&*3߸Uȴ>䰂&B'jNP[h@ @XkOJ]Xa jNXPm?:iz&aeu"iG zGJ?k~W(Zߕ @RwU~c>VXϕ_ [-`H-"% P|@EkhmcHhaiEUk ;!y!;ic cbhxHaЃSm[Í?RAɯmdӡnF E P #񬏊v)+N1S6gL/+pr5FđBhU`(6kx'xhcmPJ7L>Mܔẙh#&O,L@p qMKj~0MCfrVBmiK{+V[&%"Fn9<O3QM-:mS< !A \:RMG,:O(e p&`:ΙO'jM!{_#v8nr4XTUFT`RG-M徝e$W9inO]L۵NZ"+:wZ[.]Zfׅ*ɏhRv𧃌5vNit6} y9 d`m\JyK89 1:-(#f-/!]<P}(Ю,kW:Πj1=O/(ԓԎ^& }Ryv5܈8B1Ďzf4k-1ᶵ%[Typ:9пo"Ӣx}yFn%#A+(ڙ#^AKri^%Ӭq+6ץH#Wc| XX",4 2z ohҼNE,$A~Wi76?[m}j6(΍'pϡVZiͧJV[{yQ$~gMKYEen%O.AKegt+o1v?8jOT C<;|uFe5k=*Mݬ~`  !Nqh\vV$ӛZxYZ{DFqk uМ}TTaf-1xxz븒q X5_k[H%C#"4Hn<մ=JQԢIQ¾a@[#ǵz,6A$C Q3n.}Ih D.v4⾄ǵK!). imF]N(n<ۦ1ȇrm)zs皥qy\x{fM.u2#(vS4m`D(XTc*+JNHV1i~Yߋ5vŶz%.KPA0e5nXO,^GR#.Xt5:l]>1;BA63-擧^& ]猑ӓҶ[? ~g-kEpđ‹h6(Q>VV8>/6@}vmqdtM=+X5+p̲Fgzg*^<[ս$2o&Ep{jҧ =2GBv%ں8 v_]]\5RĶ^#6:Z[3C b+b 4EQu T=I=q8=4Ƃ(ZԘb rGBn;bc=R΋g?4"= TKEn:zZO#hn?7?z[xojZ5ړ5Qn@uQ<:]kVrrX鄒}sufjz\4Mڶޗ '@-n[oKVh|q77'E1d>T$r8w)K5 b77SOCmq4}%K3eH z٢((((((((((((((=kijl5lj/ggo UB19}lQ^B|W_SwbkK+Q-2JWN'c?]iq]L ܄L 1Wy:,l.|e]]/DR@ 7|Jޣo+Fi>0 "zwWIy5[\D2KrrLWpHr=+Zdz5V|>- XOlZWSh_kxֺmLh؂Q #ax_ڿEĄG{%Y c#zP;涿n汱֛A ۴]&}!o]9ju+u}E6W?mrț~l@Eq53hkc#hOq,vWc ;+4OgR҃kHQ& \zNhB7AbgU* 2y+TV7~;A)mI#r2qۑuܗ>:n¬䪆vh֪4k(BdJ{U]?9m^QPԿ煟o"WK:/<2CHuk*:( 5kGcD0`i]F[~2ٮMٯ` vیk~u}P CfsϘx^oMmln䴆β[}2{+YXP[^-.$aXCs~ᶶ_8mEc- +Om9*8|)~X8A'q^6\Q@޳4?c[Y2oI=~A++ xF-oVӖ+mr Ul6^E O|-iw?{fvlڲ(RNxdwƋ6;2Hd#5E2>@ [Znkp^ػJR{x^oƵA{qb*j[I+*7$OQ@}ùmmtoi73vmC2#F[p9jG]5 uX[ $dT 0kg_]%ΡvcgvyTu]B|eqb?$CQ@7|hiЏGqYa4FLTKOM$-$XM"Bt?kEb=J1]\5ϑqrbw$fm7ިnөIjWy}JݢWO[״UbѥRY C g~TB5wHAK=*omf@6э7nx?bO7ny}k7:?'},+ksfζƏo_i\v6kJOk'g[vOV cv(J( ( ( *m;D#+2!hЇ? '@(ri^C4RyMx23 4QEQEQEQEQEQEQEQEQEQEQEQEQEeꚖ<>m-WqAB}EwU|q/ ٯn,$m; b 촽r;,<-1fʠÂ3~NkRl H)ӮAkU}r/$z [Ky!  㡬-/zxb &H-I umK'88ȣ}>v}rЛxn0g~^mR[0NeS@1AzVf+>k^Mr@;YP\GF\WEh㳜G3ʹKppx'=/Bzq@dè ֹ|vVkg=g.BvӲtFU{;#BJGсzW@ҵ Y⾲ڼ_Gœ'/ڥ|5k$J>X\gg|7ݝ]o3\NkC @ q<֎ jiNX".@rvo&x:=ΑkqٴĞзHRzJ𮅨(ޛh~" 75Oxv;мsljVv&^_ܭ-( O5.%%4x:LduX]/Ɗ[K YmP*0~a<כWL񕆗ZEAie{rY,HҁxúĐiSfRYIQi:ֱsmC<9R$+zt[^T$Y L }샜 uw:hZ5[Xn.-+BMx~ժ@>k4zqm=GJMk$BaԴ8Dvki!F Xr=*/GT/֪F];!WF;Jnב6ciW)sk&vșykڵ^j]FY8~Z-ؽӥ}C- I$8-@ox7VѣC-%CXW>ؤdi%U )j c FrFZҗ^X:\ |&6VHz)lmYW|Hei{$S.": W\'tvesa\IvZCsvpɴgqmg=ě麟7_ꑥݶ ,n qmk>>idG1G$6 -/˫k]!?+#Jе \:4+"x¥kkZ1Ф&}~2d*؍{xN,oewr8` yt'oXzKfIcks 1,4ۡ6Uі-c﬎̽sKS]?Z⹯KSb3H eǿciZ^Gw:Om U|$ xϡMl\NbkĮpTzh]Vݭm1)g޵> N=7dW[YK$$L:H #r޼P (e wE>MgJ=WEg+3=Ēo}'yadp@ Np}*x:K9B%#+ȥrx=ytM5Ŭo pNH۞_:[+,@PN=+]Z[\0\Zt$cNIG[_i3G 0s 2:6 i )X0vx銅y UZO3AExWg!l%o4k4d#sBzV/Oqa6>t}oo$+u_,I]9@\|i4}KóZ0ٳnڇ<5ꏧ^^#*: Myޕxy]O'O:H,H=s* 3XE: NyXfW-W BEEkv#@qcޥh((((((((ti:iuS-+NqcҀ5:V.{ص1@p:GZ(G /z۵77hVּGh[V|# ZQEfͦfd͝[KvǨ4(wZӴ =﵋-T^CԞ>³thZ6x`Po9NpzzPA4i4/7R<PVm6v0Ю^=Im0eWA" 뵆FpA}Z((z6Ddp\IšVTy7F4ϦY U-2U:eKotdB}2E[ȲM_h\uOMAs'ePI8z(YMr"ĥBݱ(})]+-t;K˻kt쩞E!QM\n5K}zG˸h_cw?J((( h kftowd)\ʶ( ^iXCeEmi (jj((WPav$e.O~lX+SRh_ˍV8}⤓?j(((((((((((((+ٳּMf{@j:c X1 M{C)(B{zu>'f>켩JyW&ݘP(<}jjZ)[}U 3"BS{u%Grs:JDoYm`o- F8ihV[)޳s\FݴI;|H]/E]FhH-"tn?HѬF^yK) ˁԑ'f\[Ar.a`Dkg-_Q-JV5AiVR0.3p L=~Ds\6{+-6r[*ǹLvÑ q0P2 D qU46irܑ$ GLdC6m?*J`]{{6ٲR;cՇcxO=fm?SkS[IOq)\sc2:k [AA0r;J].ky屵x'mЩYՆ0O֎<"R3^6fo},PsّG#k)=N 0k|O>yw {C]:Œcߎ>]zM-ޗc<-1t#[fX%c_2{‱|K4׾!n{+Y`2+vsHR7uq >@|J+[|h.bvMuϮ Eoi[XZCo/2E*P &5&)ե4m2h5I젓Qla0n8Ikp2IZ?\H$4d 3#A:p*[L.)v+,c‚08⩽n$9/֞:RcXRd2>f8 u>UX( JTimjUybdn8as]֦gFvzt٥}nRhޣ) > @:#:r`-m}ʒN{שW5=#ÖMghrX3d 1y֧;;6?/?p[}Iio4wmx%16?.ȸw&g pk;:zz!m/# ǿٟs֧3uaq/ SPhBxiJHjZdԯ8:qqT 9lJm'Ni~Qª`K{KxU$dT‚@v'oڊ6oIε%$ g't: 5HƜŵ9?It-"[XmeҬDQ5L1jtXlmBpz})_yW QgjY=iWseFxaVN;6aijB@J@%`(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQExg6xm=KMqPdZ0#hvdg/V[5]Qq{)LaTw`s/ΕK(uy'{C !I9 +w)mc/yB 1y9PAz|Ku-l>>+i+6%+yXE+#?"Y(/+W5ݽ[Jq/nL:zeN #Ѳ}%Üb1ց\A$2:!m'DFoL"m."B+ХgҭmaaE>/E63GzF ?3E71G*z6 ?1Utp4rϖ9UHm. I(MyAF3dgҖ?w.[X^"tbVdT/:ژTk>{Fӭ &<>mۆ ֕=JxW,7pEx,͢\Eso.:2; g<ZݭMi' ; 纯4w!c#FJ#=24xxr_K0WnX V+jw 2E-y?O\iz'tGHi2 8RH<J>W#;VKxD,O+{sT՝Β4I4kg 0\Iw=3JALI%EKnЬ9E4uDԁ@ W_h[jD>$ƎAraW23w~?vHdu,PAK%ݹ>T*2=Aںz*9S]&QS\WGh aյXH^Z $}q]_+}_}Ou;۵tTU֯R;iх%dqV_[S 59ԓTi&Yerv;rGBdԞY7Mw'A `8Edhrz5&k:$~Cەg9ZGXyg9ǵmQ@AubiBS-ntg QEQEQEQEֿ!ޓ_O. -_.J+.@$Kcn+kGw'YKX'KCh|LH_k?iɸl+pw\|GBz|aciiAij0_Bg,qHg#o_j5ik9f% 2\𗉭,/ot0ZdZΧae8+ֿר&Ό^}\ hyGaKė0CL < ) y,wS\ ZoT}&;:V{ s% U]Z ,{vLr$RzxO䱎;ԈN h7(nxY]1ѷW!L[8XuT /`۩r =\ Q,FP)Q{*Nj<% ;^VQ̓46g_zkffk\wdcXjs=ǣ 2@iTrBr${qkۧiVx-%m:[#(X G\+DD/H5'{&8'P-Nċ4/RY$I6'aAC>|@Tmޣq,2:^d`,,zcR~L׳NZmlʆ=Y$ok `u%Ž$2g>YGѯP6H[[G;pyGs.u4++_3L'򍻴Kvӽj;—zc%EV\$;Ju:g(7G- o\7QGXh܁0%Dt/Q.Çۇ.[\CW/p|ĉ,@xG_O;kz;{-}F98%[+X|Ao4>[&9e'8Vz]#DbPc"Y?1K}jv,{mʖ2}yZ JzōzK*Ors#tC3뚋a4qY@zI`O=;ĝmAeQ0y+j#?Lrր][C* %gv\l;|x'R+s]K%8*@$kN]3[v0]iO-d|*Oo_'OZ#ޤ!oeKj٤'h\`gjŗfMGC t/T-lyt ]NmVHGpvnGjt \d ]\4rFۈD MxGԯP/Xţ Gψ𭕹 i5-ܖmUG`NK^մK=Oör`Uui EH7PCG6c_:5XI6gj}t%DzE3aT&XmG- ר)@+y&ywgi= A-ػhld \ϊ|)A#ү$ jj^HcF~N ^P)u> m"T q8k?J_\Ziɤ$GYw *޸Mg:}[|i,/?5%[>G#Iko 1 ԍBX~.m4MN}Nln%}$r1zn:mgIedvK#pr::@V|WIcѵdCmd`HωKvKn W7-p_X)?.9ý@(gH߆km7VynsRHV#iMk+hZkMŰdxt83@EP5{ sOK&nlܐ![G|)#^k5.wVct恝 j9֟>yvwz`65 Ҟ ReٖGUwz~8ޙ Q?hvf@=/s/ 5++:[e;;PUEUo5K(4눮meIbmժ(((((((((((*kouf8u4PVZ@0H*-PEPEPEPEPEPEPEPEPEPEPEPEP^@R2i⸵\oׯJ:X4k{Bkw6[?(urŚf;3i:JXM(Jf?sh^q2[4lzP_Wi?[5Y.!5FYub@8Zto"Aox-Kyi4{ 7Yt ~4IY|2>oBj]D[='EIaQzV9?M?xV.5+]A1E/IvPҗI/j]e0Oq޸˭kYIo jWjC$RA5Q㶖BHԅ2/ j84:kkeY$,0q>] kءQ|-$WjI39<_n[m^N3rБ~f:b/ i{ktإNd(ǧ^:KΣAzvq-7q!*C&3CnkM^ QC=18"XxGԮҞk$ Ǡck񎩭izΑ[\҅^Ns9lMk7w_cK}NPEx^?նpߐ5: 3Z&>,ϨKÙG`z'l=މ{CoIp{ !=w#ZZ\Oٍv 'wCIX-`FU+8r$kWNݺw<{%YhڙzټW6 $uz|km`"2>Vgb8ҹE𧉇!BVW֓ p!3=NO*]oCFӢ#vo1ޗO]Β5LP5X ͚۴j dsU4?I}}cMү`h` h)݅xCV.<[t).;a?ZsYK⨾?!&K@Ms7WvEx#c|Y:ɨhrZ1U#8>/t%)b*|Fa }3S> T>'Nu(}ͼ1ڟ]cֵ;m/OaC$7Ep<3^[^r{f4QY ϊh/W=dl!|QίXQ\+5DhzRCd~={ u Jk빭$;ع`+iO&W$2(p\W=x?Wo (1a^O3~qJOCS_jaorBhAC.C l讼ykMlW+vBbw"iL ,Qci#{Ⳮ<3h=[M2R1s+CU4m$@**3& rZԴc#\?<pHGjh0}fϨ6c;+G&1ֺ?uŷ(]SX7CBWh([}K}_OOЬ$ZAdP1p[dQzr EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPMWg]睻_v޸szf& ^&k8lb rt {5P4l?u}A?"g4oPKxo, ʃ `1'utڳw0 \Lb2?1 @ZV5KM Q쉣c.02)u\+i?nu P1;3mVmKqU}wEKXfw25x )HFx|{7ڣީg}sm%͜ɹ]8`88S=؃A f.aVl{>r)b+cFwE_K-zƹX R&EeL]1:FWSoloT 4a>5b|) woG+A4c|R/U8W-: &eBNҮٲ",0(tW+x_w>]=].6:5`6/֫7i?Uﴧ,<8gc<;*+l&C=7hzdO7rڲ1rF ֟Ʃ&Kg7Y'a4r@I2XjzmϠiqˉJ(Ld5F*V[M^FdC>QGKxrOB;i&v.ȏv;N̞-B c 2 ~Xw > tA;;&G I' /2]ڤԓ! 0xn3z-xWz-Kh.lw >b4~ukmSOԼ9<.kKwrc^E[<#]}=Iò6nޙj?}OU󏡦ԟOrx{,L{8?+Xq+Ѯ<3iwGZY#U!Qn 'k̵߆Z75mPZI$&Hmr^rҬ`(Bp X[j(<,ii=xRH \gZ?nn5 '隆uy4@aчS]nuKKFEszgu?xj6zޗs$o(@ {h >{gqj0Gzmnn-8g8烑7Au2{8{7@Y)#sq~a&gvX )]RBY:L]WQ:=&(Q)$Xex]~úx QE fI%sQG$eۻ-Nh-3q_cDxҰ|A_m=zrv23lxlg^^&Հu/CGŔϼ( }47^tB{=A ;X5/gN;+6 Xtp8=ǵsWu}fOwko[j:@ܸe J=;]4&My5f%[[9F }__jzdn$}5~hR3iVkV[j ?{oE>ޯƄ5xW-p-W+yD*,ǖf뎞WExh>.ftK)NڬЮqmI+i}<[彺se}CQ52\žlR:rup8ʒ?i^EἰRZ3,B+Ʀ_mGLyp\qQsV8`k @w+Og\q[DMA8gv5s57;k6=z|j& V^~{T7~<ӭ8tղ%KoX8,?`޳E񖥮h6jO >A rzV η}o 1ip[2Jp2>~^אt;Q{k-^a^Ibncr@JMg.חb.V,`ן+uW饆}JEQȂ0]=ީeI۸RKL1#5$KGic_kzlV1$ow8SZ$ޭ7KW{Dǖ?EԚy$$Fj%M2'{Yħ-vBdA%Ŭ~e%Ʃun -ao3 84 E]z?OhǦ$P>jۦv1:|q܀ 8OjWDYMoOIjH%ݐN ڱg#𝮋4nJfFuV\cǚD2\_@bX'd'9KxM%`r1r~'s խl+s㝼J5fi$׶HaG=>w7|CMtk8U|iˠ\nĀg :x=&ȶlB\u`*OS[./uORc0i=g c*0;-wMCYtSiڣn#=>ҒFML{/YqV&ڄ5KݭN7S\}Eq|;-9 4Or,1z;si=Jk+lhtAw*2G`9vt~2i4_9DQiqQ^|A-&~p p[d_z<CY:X5 2+f 6 Pw8n Z~ ,[RMba-ёH8a׽!i4BY&. "$WgR[#fotǴ\4 䎮rXQwiq ^5[c8d䎜RO{K,o;m"K+++m@FN{t#|k꺜6fԬngFx& an W[zƫ;alj]VI>1FÌxH_QEQEQEQEQEQEQEQEQEQEQEQEQEQ^c|F!t+sBŕJ`7Lp=:|sGqԯ/I 屍c2;t=c1 _r4Wf60@q`O4sNx##i-.~֊dRc2XxbWԢ[T-rp:Q?#Z7>&4]'K.0 3_>#+O`[ҷuxsC6zʨwF X= ^.tW;xM/$KDc9Àx6 GsדϫDA/0ܬ/an":*-fuP#EP2I+Ǟ B{ pz&AtP\?K%Ͳ^E#B!}*J.t{MXˈ#EUU'?*0GQ@xM}XǧY$2+#Ԟ0c. wV/ Bk+E TqhE񖁭-T]EJ0Q~7aI=̥ac}۴QUtBQI,.bHhlu8e>ժ((((((*)mXH1(% ڥ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ~ɬGIm.56Ӥ2afRWQJ@ #):kz\icbm'7 @f Lj{{Yn4vDL׷jZ)džkuqWO؍7Z1A69YCydt;1ݼ嗪R_iXoj+м?e\J{(n`m 0w(֩j:v\AWM}X'X#˓sA<׫QCq%m>GOpAZi 6mw#-;֝W_hKP ;Lz WQG-|x>:&uB $wQWxN>^K×//AE!D('{%Nzfy4˕̪_nQ~_5o]鳖]ڽ]TY<񅗂'ͨmq#n/'v11ֽVV;xCqraݥdȒN@b Sx⟧s]4 f=ŝa&K]W&f[/dD2>n[X/cZ-FgoVPgGxH׼Skƚ08`F7wpzUl'վsZ" \r@|y!iuڭg FX`g[|Ca#Ira=j ߽dIu#Ҭ~7I~}=1ݙeCn c~4gѵ JF,&5BVd&ΤzoMGM*/myjaH%  ^mNE{=ֺV0lQq # $VMJ~hҾ[M/$ʠ8#`N^98k >^3[R1*YsJCk.[;Kadu {qdO:8}J8okfnpIBz~zU/7$0`Pyj8ou~^ Ub`.FCV3$1K@5|3ԴIf{!!K\s8f]?Qѭ5KR6E22=T>"Ě_bإS631ҹ>'5)4k8-lnm[RGwTK0ZV"πl 1KsjFv/f+BP=JKK1]f8h1emJD' VC-X/BTy0#]߃z ʨĒp~ /kv-@d–vQ57M -+Tm5{fIzеe-,6з@09|}l4{Ok~H&xx=d06۰&{pc"/:κ#9z6L|gVڝ+y]HwpA4c.~e6;y}wczW ,:6n46Wr| D6g|YjZ5]D66-.//i\p1G4's4hͬ,Ŵ{{nИm'=~Lq9KM>Deʓ=>4돈ĸ[8ccjZipb2MhՖd<)P6$\PN:x_Ht^ώ{6'^~9WxΑ8nh:ۺbݤq `6wz](aEPEPEPEPEPEPEPEPEPEPEPEPEPEPE- Co" &OѥgL-hH H*-O]O4m:H$MMTu#QCʀ6h/Rcak]zy@TyRp95<2yk:MޞʐIᜀ q@\hSþ0t2Kh>s8H$d{Ծ%dz>kXj:4D. Bb dh)~$[KH.//i̊dRJ'UwRoj4d;\@PBa)'%%@˟ċ;KZaEU%3NOG.,578lFԕZ?怹Ɖj(UFS_Ϧ.`4졕e 9cYzOeľ6ՆZNmg*LEH$Wzsh6>)/~ xJm?PẊvBD ISzA.siWE[ǩeۻvg򟈿.&AX75Xyi2w`W\1]2F̅$ݝi7Ū+4,:wmltcXլweB7  ŜwA"+gqp1o1}ݓ sN¹tWOm{ TIwٌ [qS|kZF/5bY$L#$Hҁ tY7YW~}wc9A"úeqo6Ad!Ē~<'X|B-fɸd$eNTAP7?2]$9b b*.uW2y(c{  ho>Nnt,tWCҞ[JH8gEaPxzkoB '"D^s)BY_v6HT}ok hq]sgH}6[ \C?(7z! UB]f >7]KSQ[4?ÍOT7]KP2dxm&kCmif3~^oi'~wZ[8(Y^A1}Ӿ7]KQ_*0Sif%6U\q mp6cȮC6>4-Mem`.tz]I[Lw/Gf VӼ1iU[^]4hfGid2")bXxi-SNEl4˛̎ax[sALw/Gf }n+-9oj'Bu]F-N7bHRNc¶OL՞S mVdB1^c3υUi|.%,Zo3υU-QU~7]KQ_*-QU~7]KQ_*-QU~7]KQ_*-QU~7]KQ_*-QU~7]KQ_*-QU~7]KQ_*-QU~7]KQ_*-QU~7]KQ_*-QU~7]KQ_*-QU~7]KQ_*-QU~7]KQ_*-QU~7]KQ_*-QU~7]KQ_*-QU~7]KQ_*-QU~7]KR@ɰU[IC,R"  (yWwnt.oHʪYT tj~ɬGIm.5&Ӥ27&J,—Q|i'}>Kӎ{|HmL[3#C:*yc+ .#Eycc\͆5M4V1[F6瓛ʠ+0cZoWOkq'`C ;nބtwĈů=M isS"|W[j7ԧ\Ol'6Rp=WuqWOyb4i̪ qגg{eʑi*ƻG~:RwQ}Yx?׉4Hiz]pP2Ic 9+ŗZ߆̚}Ŷaqw%ź:1f㐿?pOGᇃ < Fao}1ްkMVkw6ӧr&YByM-my jXXjMs!a`ksڭxZ74K)ó1l QOG4kA:okƸ(/+smQՎ:RAWx&OK 3{a;ƖO,pP3[^;խwꑬ2IkݼPp>b=^暎xGׂ!/iQgi~6yla>hxR?vk-ԛp+y -.-uhLV2+FQFHry{^)Ծ|WX<]K)!Y}Y:#jͫSė.{APΤĄ7I񖃫GzZdOp z0[VEqk"NCЊA[Iuo4e_Mt} VB1lEnw]vMMG:npI#)lc;Tr9REomlǖϳyijVM kyS*F#ZAw N_)_F"Lˀ/w5kb)ӧ Fˉ g^ִLPn㴳I$$V/, ?Y544 RZovmnYBtb@] K]TTbY!?-e+k{km$xRXk:3T</t_j&Ywk5){P{&{*Q|5K ܲ\~L,JWI$}42#"FNX 熼?5$7#DI+")qk__n.t>MF0 [#}O^_[J,e̒HRHz#{#QK։fX$vFSŞ9'5/ ۏ˶Kݴ8fx8V=j*}[WQC{ӇfTg dݫ/$^ 񇛮CXtӃ8*]?~Edxĺ?,ׯᲆG/]FI?AZFX)-5,zSOB~|~l~j NF^7 çY˫ZH@ ڧ;v|m-,!Pyz բռG'QVie}<$l/UXcԞ٬?5ƛl躌qEQ*ݒI:e-*Kquh/P@NGj֯(-=cC5v;uK̎l#z-zŕ:|Β,ch wdE ]k3iˉxۑ|B5hX#W5g^ڿ5l|/k#bDn F*瀭uo |9-&7W ` y̿6v&7u񍧇Q[,֒}\GU+k'eiO>j]o""%W,'z>{yco i_$o:D' QF R:~ vQdϽ,;:ћR Xͼp쥱8R;'zm%MuMZ.%e(Ozτ4_Qh!{_"f*|H0s=h#WzYYo+`+;ί Djmb6?&|TҬ|?j67ZrE29L408)Jd%+ >my-鞕ȾV1iiz|EKN۲G\ޭ$o2|=K_??VT;B2p@v޺u`NA{^.4x^XծR+9V\ RgznIaE{V?|5&cuߵ6!*z) Q sB*b_87vU1}Wu=Y$VM,p@w$(ߥwzi:0 A!Tu8/J-F RݠMV5f1x'9<ͺg(1t 6u|K[Y >׵ҵH纒3,hQ@pY l'>X$W" f-eiB`nqj?7LP-EU#4}iVq'8a5'[e4Dр?!I_o!˟BC?-,QE5]w#_Ps@Au2xQԁԁNxR^wY)=(z( ):uո \p(j*\[J(dcXʣ. z@ZYC=\d;Kxы@9GV$8HRGɢp<6(KK5UcZ0{FڵG}?5'L?4ڵG}?5'L?4ڵG}?5'L?4ڵG}?5'L?4ڵG}?5 c&{2#k$uYW9 w"d=GG}?4}Q{OMI4d=GG}?4}Q{OMI4d=GG}?4}Q{OMI4d=GG}?4}Q{OMI4d=V7w[]YKo2䍥bHEbww.e "yT#%Gl]L?4ڵG}?5'L?4ڵG}?5'L?4ڵG}?5'L?4ڵG}?5'L?[t ^M /:碮O$hoj?iZiԛdz}nXZhV>\#]?n[?ΜmkK_Vahj7Gڵԟ0{GL?j7Gڵԟ0{GL?j7Gڵ6WW6ړsjTOw" 8+3Vaƀ#V>n6e| ro&wji"2+0 籠 {5ĥHžc?QVR]Z6Б&%Bȯk-_š>iijzƭZ X;Hz|މaywpC$K^8gÖw7P\,eUdGʥO?_Q5ZT-.g{5zrKS;ݸǠjӾ|K'UI@&$ebYI;xQ}__]]W]0y,d=߀ j{yė#a$敖{qG@_53x6S(Y$eܙ#yWm&6,Pq *W׵-)f-cGn# :qTHС"h"ĉ#'nF\m/]_:1Bj|hW^2A]ߔ{s$P(bN~5XxGF}5϶{Kp3O9Tt{msa?frB/eP=q1U~,Vע4Q#\߫ov̎q ?x^ŵE-ȅUw(n?5sij$2gdQppF:ן-hO4 u!6i%E|AԒ23_ŗOQ> dc FxApP99yuokztMFPJܞ7 –L@xu>7䡰Yvcd2Pr W|_ؼ[n|G1v$`c1Oqj<VXnIIm5ZRh XX_N/K]ɫ M[Jm K-=b-7Q#l0ɨ.AIoG5cOVb/^K>4$VʬI*9,nlm%A@f$ڬU{ H, !hy5b4GH=+7G.:&jwZ}1o>G ` 1Ki0׭OhzXRW9{xZ?WT:Y,&hxP ܸqxo ksFV.6] #Tھj m崷c9 8wQe+9|_P=^ᦑhU:Ɲ;r z5hZ1}knQ.l! rF385OI]w"VSjx%Trd{+˾2j^!*ǩI2RDu9=x5yaSn)ڰ! n7gᘣI'-jO8j%4^9bOıE.6r<xcė~$ެ4 "*@\ʅx -4m*յS!Ky Px`Tދ,B bBRXrI<>}/k22{Wǣf5c-z(f1+G1^w]k:$HH*Aץf[?@С㍾мD7 H>'?{POcW| K_Lo8+ׯ>{ z6}8595;*acFxYY@e%w]xkCu B{UHY I u'U xÞOI8s7۞3AB xk:.<8hbCKIWRs4 [m2!Nn,$($q foqL1CG Nc˾ x[<9HBo=$vac3_=NZ iWjtnvʫ ]9[Ï Z38d9ݹdV N1;UkִRYL m$!W 8_ݎkBSŞ[&{ѫ!CqP$ ds>!l_ *>7 4OMc%|;&*`@?0<#|GsOak9Q[oۣo.+* <֦iw7 1t'$UH' G=5OĶoK3avq@c8m[hZNoaΗ6 ȤI[zI/_?8ŗǗω4XWlwّF+.vC9|w߄5Mjxu% HBwc8e݀Iһ]/H.XJ~dP@+'Q'u xnɥY#CL(;aσu]xs~sE0&4 qjz QXDgU5[{x#EDRU h:gv\/ڜfE9G8SzyW#ƭbX./G(cT c޳bg=#C[XjW7ӭ0 *bTqƳ/y{c^ݔWO!Q^OusT. ]}_mgǾ"헊ɮ Ե-:$ RFi/xz]mu+}<^\…B>/Zl {mPƳY]}fI_b[#P>K6ASÚ2k>fp=1HZM ʜ耋,N@P'< sKM뷟ʠg-<w ~,u !}?q M>a!{uKk'Zl<v%3fnW9;'ᆗ &V[w匥d)lv|{k:Fd ^bHR7I!Ӯu{[;] cifbxSDRq=qǪ’P{V2 ?j ˜myֶ|A_nu;X$ma2,0{"OMla!f9pA>"뷞C%Ԣ]G!(9 WEHh n^xf@/dyffm }?!#4K}WM-HӭZ$$q62sRKoWO!ܞ l-;SϚ,-,[H[9V czM׃ G6ɻ2Nr>nqZĈjԟ'iRj(Š+A&j6G0v4MÆ٢i:}iP.+m&JѦmBZ)VlE~MF,3C0W@XhJ)H"`HE,@$(0>ߨƯF@bҬr 7!\7FFjvVxGc-+B ( [^$|1$f'H'86]{XiSj#D3G4{yZ/B Y[}i#ߜg} E5}W>p-^PNSƝq%D\- #0s&мċ0Hy*8>nWQGK}ѭKNシF7g=k;i__jĖ\p^E&1-xWc>uӕR_ _dmrʨx 1.:yG\Gw${\:WДU_ύpqC$H՞$AdA{D<'R}BS/\R9A I5TTaO&^.O5xZ3yrDd 38O5{Kÿ`6^AC~UUxfRyw{'؛̂кLnܖ=MOMxoO-BcNMy^ ;MjsBgP|du h׿Wi4v Kc̏ԅ_JZ(_>{,-jZZl5׆o|1GuEPz߈<;ꚗ%Ggl-OF:A^E+h3Ǵ2NkZbu7nf2>:i[_蚍jGZXO4gtSq[Kx~_|H#GLٷayⷵ-Ne .??h?>Oj(`j|0O]lGuW.͙Q?{(K[5{l-M27\HjeQԕ8.%9P hck%1OG0!fwX^q/VD$2$ʪ3u}G=yD>6HSIh6wgEL} <BE3Ώz'(} }:?УΏz'(S<B:?РL} <BE3Ώz'(} }:?УΏz'(S<B:?РL} <BE3Ώz'(} }:?УΏz'(S<B:?РL} <BE3Ώz'(} }:?УΏz'(S<B:?РL} <B3u.W xS|=}CNB%cL}:9֥zq#wjϏz') }Κ.T_2fǹǹ9.-bGV[QEX[ QvoV( jߗ >f/b7QvoV( jߗ >f/b7QvoV( jߗ >f/b7QvoV( jߗ >f/b7QvoV( jߗ >f/b7QvoV( jߗ >f/b7QvoV( jߗ EIiR֬Ij<;|Z4Pwϒ!ht_?Mh@N>Kӿ5Egy:w/&'N%C֍<;|Z4Pwϒ!ht_?Mh@N>Kӿ5Egy:w/&'N%C֍<;|Z4Pwϒ!ht_?Mh@N>Kӿ5Egy:w/&'N%C֍<;|Z4Pwϒ!ht_?Mh{%#2K&v8u$(ϒ!ht_?MKϘ>|ϒ!ht_?MKϘ>|ϒ!ht_?MIceM0KusqfIdÑ@;|Gϒ!j_>|y?MaR?4߱_O?MY?Gy>c e[m,l?f/y>c <PYs8Smg[*A!A&(((((((((((((((((((((((((((D&*~69ԬMw_»oXc%?U2kstHۯv0^Z{޴SH[J *S8!qu6>;5 w)[] gBȊvrfXi5LD"DG,O[qK} W /h-Ztn; ] br%@# 5gƞ<0[ZD&eUX]pKb"xhNXxGkSu^^keKekQH%6cz/O V~-jĻd,H yuZrG6,yiŏ4/ +X[440d{<{=g6~5~| Ve9Z2pAPk8!ҭu[6YebM@$8K 8SX ׭m6w^tR+8#uj7^ |4i]`4mB9IKxumwrA:YG: U?&ݿi4w 9V>P2Q@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@Auk ~]a9aQ)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-20.08/images/print.jpg000066400000000000000000001022221362435004500161560ustar00rootroot00000000000000JFIFHHExifMM*V^(if%HH02310100Fotoxx:trim/rotate|NE0Photoshop 3.08BIMfotoxx,  1000 1000 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?((((((((((((((((((((((((((+oSZhYNS2 ,HU_(|ub?h|cA?K g?oםyc=ό0 ό0 ;((D 7A 7A<5?l ?l 22@O[>14n[>14ṇ̣P=ό0 ό0 ;+GvYa4Xܠ瑐Fx(Wbz-a7G-a7U_x IX[KdqId~VnՃrXҥibI K~Ec 7Aşe"ĐE5Rؤ&d1p {lݷF/wvwq[YQ)mzѨ?|cAh?/-a7XvQ%kk;I+B:)1ԑ$][[4Cs3(#>#4\ό0 ό0 ;qG@ g?o g?oםyjt׌@Hh?yZ{ېc$'cCE g?o؄$)"k;vT[)\Fo1*9ܟ-뫻ȃ?I g#"wxtcte J Yֳu{_-9]cc(n+#.Mp)6R˭pm*i>9-o\5ˠmǠX)(5 8Mz+\NU:TO|% c}Ld48_Qp;QT-wQ^O ʱ4[?I(QFG2=E (dz(#Q(QFG2=E (dz(#Q(QFG2=E (dz(#Q(QFG2=E (dz(#Q(QFG2=E (dz(#Q(QFG2=E (dz(#Q(QIdolC#ѲDWmuxv]ńpdq#nHݐ:yy_|O?7 G?Ms|'I#n5(+ҿH4'G,<̣̯J#nR>'I\_22+G?H4's|ƶt~U[qp֎d3 pG\uG?H4o'79'I$ksk\0*Xa㊹YiHMERyx7uϵk‘?hO7G)tXkO$Tkm&o=Ԥ RNfu]oJ<6Ji\XjjX_KqJ2ENH{‘?hO7G)t5}] Ԯuu1bv݌i>.Ҵ-{řdy5'01iett|O?7 G?UdqezW)t‘?hO7Ey_|O?? G? mU>*Ȼ g{JR>'#n9{Xhjjʷźi2Ň=GѿH4o'F -%2 9w=9ypf0=Fs?G?H4'Ɲ̦_Fb9U o_zֵH{[Ōg,GA_v|O?? G?qSVUW{|;g9_䢴^_/sohX;v$чj+IO8ykL1MZ伐#H:*+u `pM}ḫ~Dע_V~GUp3^[=8-ܶ?ebRi0sm9ϥe[xŞ$e%юM|OrhMEy^9ծRk; 5O/Eͻ̭PFx 󚵪xVP F9JٚTF:3PwW5eh{w2?1IGa1ü-&ZXYZ,E+J]QَNi GS#CP{i-AU(`APi 4Wh>+U hd\ YeilЊXѴ4WE+6 A LCLz%:ޢutb'75e 9{WM@ +zQon,1JiU`:洽bO ZY m7Xidyc_{:7;QY ֑.t~ka8vXAr}G!ttK$Z,A QcQp\dze|=e[hVo/42;_exuӞFxKۇwBYx&3Vuk}G5sE@< A1yQ,\jw F8#)oWMq3<63=\HdIygMxCYS yG$3!8CE[/Ekx 7NQqfKe˅brFxH/ky3^ڤ΃b9Ƕk? luk7ޓ<6YeRq9z5Zk1(DD `=)$/$QE"(((((scs@{,1Oǿ^QgEQ:\^j#37#*%#sZ:o}Z)U. @7}k]Fd\ ydusd֖ lY&$pM ' q׳[Qԋ9Q .E3D~"KAoq!/$I[iAq0GY&<|'xVXK 30#sT$p {{xeYgG\͜zWYE x*Q]Gvť6&r008=x;UԵR8K h!G,a˝y2+h,`ӯ[ T+ZW8Wv֑]jQi 8*y 1 (ETEDU@޵JU'ie/*-H/uuBx OhɮcOji˧M٭R|eWʹ''ve/?$ns^~IPN].{$@u7b}=8ڃ̺IS&W2&f :7\ $!lE2N[2SVbx.WZ5b#=|j0T(_39irGC,8$,x3ԏZO2'@ܾ+Fk`(Wh I '?utW)W y>'?0:+|OA}+<_Jtz:+|OA}+<_Jtz:+|OA}+<_Jtz:+|OA}+<_Jtz:+|OA}+<_Jtz:+|OA}+<_Jtz:+|OA}+<_Jtz:+|OA}+<_Jtz:+|OA}+<_Jtz:+|OA}+<_Jtz:+|OA}+<_Jtz:+|OA}+<_Jtz:+|OA}+<_Jtz:+|OA}+<_Jtz:+|OA}+<_Jtz:+|OA}+<_Jtz:+|OA}+<_Jtz:|)kӤھou*"_Jtz'ҿ]'z2?V =4¶m?<_Jtz'ҿ]'fOdG ǿƏV =5' _.QW {Y3gȏihu+Jހ/'ҿ]'?IǨufmB+TYW>7+~ Ro4᠄ġp8f9?;Up3PQJ4 /,j ,On&~T(~b5x_TwrFX݂ A1nmA.o3J Y 3: t9j-[  Xm$1Bs/+);ʂ6NsY dpRڅZ("Ѯ=y$esM$<xÐIctI/bx| EZf9/.@,+$" º*o}y?i]j^hzZѦE !rx^xִ|u{7fmUP9T0'$z{R,P4 ^6GPԣ-$鞾!>~]:XfϧY/G"xFr2t0妡eɴ{he|yU;-ya2Gc9{ީYMM̳y\yKrIdbQ _9+TVW?G%9gK-?[m64o w ,C)WP,d!#[r3kzַs>;}wMM:Lm.R푆ӷzҳt4|k&ѼiYs_<@DQHdIFq'O_xNJ 7Zrk"mSH*OY5KMNC-+I33Du\rFF=iz5miXI &fH5B'Odu/~5n GOhY(x5 acw#i@+?%Wִ;ۿ{XVr$&]Jl$un'k^iv~ñj3ʆRX w=0OZ׈8nͫEs=Mhsjk !!+`=3QcCL^o\ͥIhjD$aj26Xŏ2um I%zIdRpߑ]_V}jm.b[s,ݔ&=c~*/ Ri6Н|[inA1]ǃ4S Z9Nl ]s3 \7o]-sW4ץ507f4f?O V5B((((( cug8ss:t p%`>YZM'~wv?ڏ :_6?Gp?\?@cty}:/]"RF -u\ޏA+!a}i-{5Ф±ly#n܊'YZ\ɦȰDALqBIL|=|;s"sYA?9M_䭗lZ+Oh2x[ WE"C#21l\^i\^]n{2'$sJ'gԯbӴô6#dw5ZX@}]㝭qW2#8zWk-mmwaya` D8.YѮ ȶVX Kn51۳9 Y7u!vMk.^{C*g۽A靤Ț oKZS6혇фa&Iۃ[>KV"Lmm987ԛzKC-Z<gP=h瞘2t g;1E$s׃kWW'tدlaڀ5a.lX[i;z`bA$Ihz hݦ7[Wle7LȰ`7mP1UAe<dnX woO%v`3{~åX}+1 #s9@c9Go-1}ȦdgqB:*E'r[M?SV~$W8+4Z=Invz2=G#&ìA,J<F*ϸ<-}pae{b{yUdo)$.z^ꍝ嘓MQԠo IP  ݂V4Q2]nFh]nFh]n/Q,Ŕ1ro4.7TY4.7TY4.7TY4.7TY4.7TY4.7TY4.7TY4.7TY4.7UyQe}]JTAQy+__miMg?n?q@tsTU bxvԈŎI$s_xʀ6to_Up3V4o_Up3L U|JeOl^Ik/--aƛV04YxnuKӣ)9_T0ݫej"-[%?ޠkƭeψz-m娖UZ'@J^ܻD`"y|U> {w {ymJpxoBhbl{jW[0xDo1cq:M>Pcs2M2ȏLcE&{p.!t0_u `͘;hSprW1&~ϦA+umkR1m.#%lgiǩSp2!tۇw h6kȮrFۄ.!uIu]]DCvi a@iigj'W~I!UQEQEQEQEQE`c5,z[ZsnḞk}å]F4$2BK`u9J]jK|9Il? @zu}vj3U >|9@,ࡏ985|<)X0zԷwu<^2]B4.ab҅2Iǭu]>,z+^}IJZSă>թg?j;?B"լR.,okKFMVFa_1ħoTzOuޡp-P9φF9k^ik>.ďkLltceL:ީV(5 BE77C.Tgϥzwڵޡȴ}Q'8/k {$]K&c10kH<3\k?`yZgy@€:+Ӿ٩нo;H.r<7@7a;}J{źmyr/crN9 8ڇx[9줻?ji_8eF'kNW8 uEsj3hZ 5E}n+ic5]v[j. *$8L60q:qQ 73]xo$aMki ΠF g?~ׯ޴?zu^ wAugKO66lsWZK^[W\Lf mg>թg?j;?Gڵ?x|Zz_g.B =muMo y+| W]CI~ѵO2BK`n΢F:൯^B-,+ksc̣̬/jOO[lyyOO[>שнo;@eecS{Sv/jǙGXk^z ڟQV???mh^B-lyyOO[>שнo;@eecS{Sv/jǙGXk^z ڟQV???mh^B-i]7V=t*mNZ+jĄBdh5o_Up3V4o_Up3L TVN2LV8gh3'98ByVy߆v3Z*Cj1/,8 :j+ VQ dTlf  0.nsFN6wQ\ "X@Tn$:㞔Ec]mX_Fׁ-_@IӵiZA%u.Vl(G'fEb-J;Ԡ.P ,>PXO9& o` %I6ux;shn1e{oo}3z MErO}Mm$]oy {8w x4:Zu$°Zxm_:$b} :++I-=:{㸂&a3vN+uq5 qs Ķ%ܣrÂA4Q\-StUeȊS%lH,<__][C w{<-c܀H2Fh GDSVྎec"/nz(ssscF2IV*x{ޟCO7UtfŬ?sukwȘXFUS#J;PѝCO7^}7o}iQ^؋-Fe$b1 wޝ#Y3,76n;+ #=E; uX??auZS&].mЅ I u2C{VdcX8֐ܰ3rϴ/jsen.R;[0FpP;_i 2C|I[[U,v2t?atgPwu}~vP%I1Aʃ"Ρ':,?nF:,?nܰ},ܰ3r]orΡ'wѾ"Ρ':,?nF:,?nܰ},ܰ3r]orΡ'wѾ"Ρ':,?nF:,?nܰ},ܰ3r]orΡ'wѾ"Ρ':,?nF:,?nܰt,Q<95Owٳ˓~nݹݞ1:4ghTdl[FáRNؼa CȫVPΕBjƕBiHf4,(f}~i~&l-J ؠx|3yq6]%<'it9,^p$wJWڡ)bˌgS]v#a5Sr q%|Á2Tc+&/ xY" BaZ8B*nn^1ڎ@'ڡRZ5Aq P((CfAA'>rԞZwj(XMIؠ+*rzíyV}6IM*Sڌ7qxWnd[;%YmYg@?J5 O;E'\eK+k͔QC8vei p >}\7}\7$t'5_;:ivx庚hQ#1*f=1cpǂ^]`Ggd|V=ׄi&YK ]JKcqs{KxYI*}-p~wxKk}\<\4Fgoy>{j.4:6&K;REBqPƴ<_Cx-uMKu/H732 ''Z.of%x1*)h BXsCpy񢋊qEP0(((gO8hO8hv~W7}2Y𦧧Xy?iƞsN}HP?*'tv!m෷0/ی<םXx[^uX7Md}BiV&OG?xbVO[O =EHthVic v3J5tJxjᴩas Ppmz2O<q^u GJR>g+D$2I^Ebڋ鐶ZϚi` (f.aFrMMJH:o?nHK]h/g +lr<0=ꆁ4[Lg|9k UX]9* ڄ83]wٵ)f?Mt\,r~֒=.+–]$]܀uO OE]ݐY4 Dvnv{hJ~=+6>o?nhIaYܘ Hbː0pH0V_ٵ)f?Mt7}A+`ms} 7Gٵ)jy0V_ٵ)f?Mt %r=Dx#$i'ۺ\i'mvmf!ʲq0wey8EyMut^Rv/yʤ9@ Z;u&^,`+]J.ު6 g'IxDX0%xϧEB[ʒ*?!w 93޴ )~x[Xա4;KbAuy#(& #3( = hxIQY,z6$nuKr>ii=GSI% 卣s.[9kfZUΪah/Tm8;GǨ#=\Ƒ3gj8N,%ubkNc=֥l]!D7 N (luSm (Š((((( ?<?<ڇ~I!Uơ~Uz@QEQEQEQEQEcj%u?HRlϹ9 r6x❵=/CjY;CwaG[.|YE}1I6R`NGC֟clgN8 Nf ʩڛ{Ijwk${őePTrsQZvPoTFiI-Έݔ .m`HH?2u$r2u_ )7oݻj-ā>qlQ.vTQE ( ( ( ( ( ( ( ( ( ( ( ( ( (5to_Up3V4o_Up3L mGMԍ۠Y:û?$[3Um;ڵ[$f6u >9ZՇn]"=>+u5imݵ |%DJ+f%xI%OI89\jNYr%0"97uVh7lPK>4*0o6eGlu PE74Y$&m )Nr HF'آE-cKtluKhV*9M:}GOO saUCjVXZk=ܥYUt Iw*=@qk񎩩Z6VKY̲1V RNuӟNAυ4+\(FI984*CRG&wi,uKgqQCKDW;420kԼW 2-f[Y"X!S2ʊ=[$z<~wkMd䢒BέW?NU4KmaJ 6Iϥt0EK]:^8yh'~'tt; tJ[>Z xjSgdle'sa'ڡ5}`MigmYF0<hC^NwMmDĤe_noATd2-n/R'_*cAIcS/w|=Ы=v8J}3*=7ėP\x,ujr>Ӹe8|7,{kaW9f=>\˯iWwD9UG*7>;WCC (Q@Q@Q@Q@Q@:y3Ey3E03BՍC?$?:<2Yug;kmRd!V<7MjjWmyV!yi;Tu8)G@Vc&<Efs{ok-w,op\A| ETҵMZ/4|g$++tQEQQO44,{UdDŦpr=H T6)s 0}# %-AO@#@W%&֦0U $w^+OJb:Y%+4RNqddt *Yhl Hln` @G}^l/UB7뵀${U[[{[9Dzɴej*coyp"u Pnc``s_+iX%Pe=h(nWծ63g(R+mu \V 궨}WCqt*0QP06\nkJW>w4?۶ݼv \[G02A8=;J(c [R #s"08 \ z"+Ft"@Q@Q@Q@^KhyneRu#V(_+!Kyq{g?:AаxPád}Q@Q@W{ ?W{ ?*a+F-.P,Ą(Ѻ1 r:pzWCU㼵[DUi  nŚ^%ECt^6)r4 :}xx3Yhm^.m5|Kqr08NB.OBnyNnqo\gӸMEZ)gY[hB׊oZ՝0pXp1k&D֬g5+Bl籷m9/ʐyd9y vq{-ṕ&VtϪE1<9chs[oJ"L3]@ps¨Šjp+Un5sp/ ygq]}.f,G 'sp?*˽_l.^mc8xe{ dP)OLTյMRkS"ŶXWMLX9at'PAVAE>$bNA~we4n8=7tZ+KugJ6ʓX_͓ m[kgc]dYxAdw_xǧj #֒JiȆl0l%,KVe UAC?$?#|}h_zM͔W"5D.]0tu/Jm_P$V1X-e S$sI#kl?XI{e/<6Ȓ1ld'8ThlG&fzw]+@Np\c8s=n$azVǗE vuacyNT7.6m+chFY6ѼW B7 HLZt=*fq%630|dZ{sD|Ѵ̉)ͪ +t~!S./ ]Ne{]bV۵Hrˌcwm}N."L9=sԗZ.vkuXζlI7GB)tJIly- u]vP֢O.!ݶd$NnY9n:=&u;gZBȘ ib1`hZ|7:F56[#G0F=i:Dr#ҬBŖ.9RQ[ic;ռvђ)%M6ei#=XnM3MҭKRH%<@nwwVV (M&M{i֡1㘗w՚ǸIt߲H܁Ah_HM!X>}C@lEilPZ2ez@oj %I[Ĝ[W3xY<͠5Ð3TtlOrX[n\`z:ɬIa.g՚FHЬ,&XO=kzRMrnKRy ClPBD1/|3-cdm]#&:8@]}:MF+,$1r)PҪVTg4BkDKwi a@9 nM6LCC]~[RU !ǂONDҡ$,ce^mJ $p;} H}54OBmBc瞔ji=Xv-%ib;X,!8-AQFU tT%`HPI?¼oA]y>qݿP8A-?0W=Ktm:eh^@x1'9Ts-4}IeI#0.!;dczqڬx GY/bR-}]<[ں/]3?`#6DrG@ASKG>Ѱy^sw\gzUDU@@QGP+؞ПWx.{|) :iYQ)Rrʽ~ukmkt]6nwȭ9 pzxC5.hOE.gR~ӡ-+Yx_Hٚ"¸%F;WIiKIclr*ژ*z``bxoDԢ/4>{Kti0db=M8[|(vPv=Q@Q@Q@Q@Q@Q@Q@:y3Ey3E03BՍC?$?((((((ѶHV% q<,rI Ļ4 =n-3Pcjm s@5gOSH{R?j[9%K.X< $v<jƏGbTcIGgMѷV[yc&`^a|@կ~# "}>Y[B5j.n.94mFI&XC$,9azօ%5dʈrk:%.ŘUU=5.#ĺePV56'nCWMɩisw ]ƟI7 ȡ==Fhh Q:- Xd97>[ų躦yll&FKiQavoHKkV"Ԯv%DH#uRs#j)nduO<_ n[;F WmXPxEX:\WnƉs7~,v3>ܖs"] ,+eT0r:+>M:^$W-"F$Ãuxlj4ymxd 6O# =kErK R܈-f87WeVXqVo ˻kIm%bVXX#{*NEPBj:ۓVto[Ut3L Fo~OVhm..@^{6^{EVy=y=[e'e'4Pm..@^{6^{EVy=y=[e'e'4Pm..2Mt`IU.NI4cw?n%y=y=/A;7G v^{6^{Y.70o(]..6rsi>//(K{ߓѲ{ߓ_c?nAK7@t[|в" ǪAK7KyG̊>PF@6?Fq?L C?$?cP?* ( ( ( ( (35$aeu$Xd`=7WNfv:vo$f&=:׫0ZۼD6)v2}y| -McjG't+"F=kTmP=)hqXl6_+/DbOuxc8 >VCsk9WC2u{gw1\\Hun$QRʿjފ3-kl; 1!EE y<׭wk{se;Ʊ@W.Xwq=޾5'7zׂ  qg i *?a?8?uuO/"!/)l <# k\]m6#w3G%͔߳Gewes^E#Þ#YVR4B4wFQ$?+ XwV_tնQ}<3!._BdKyby9o|[a 3Y%ռvqZ7icc2)ܪ;*~R[phtD뵖0OAW/-:]4ryHЫK^Cx۷i(3Om ?cO(?{?{xbt(Oc%[ʥ,$EX[T #<QLfotoxx-20.08/images/printer-cal-study.jpg000066400000000000000000000150431362435004500204140ustar00rootroot00000000000000JFIFHHExifMM*JR(iZHH0230#0100Fotoxx:trim_rotate| Fotoxx:resize|http://ns.adobe.com/xap/1.0/ 1448 1362 C   %# , #&')*)-0-(0%()(C   ((((((((((((((((((((((((((((((((((((((((((((((((((( " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?+4/S,O+mI]A;n!X( 0^T4 tKXӠC%Hў/0 ['-4? iӝ'CMBI7\%q(gphר=_?k,lⶵi݌O2'x7#Դ Y鲾m | KxAj|mم"cs)SM/=?7o6Xh~sIZ_i{UoSvc+[ú<[\:M8 u _2-͓ߚd~Ceì%`4HKv? ]eCOhZwP:P]iZ~2WwEpm-5@}Ky8wJf'mm-fS!|Һꥩi:vZ^GۈVA04f ⸈Io,rz20`RW?¿[m->cϙ%2ZsdtKALwtW =j{ln/q"+^iZrg$2hOY}?u{*Y#8Z_oIiyR\r C~LX ( ( ( ( ( ( ( |%?O]įﵛh-FI`=kǾxB>(; }U%=O_8<5)mwxB~7*zM+TlFk<г˽XIZe8YsZ:.u햳Q,2Iڤ'8ߥiPQZAwn8V𷈼T<1j_G[\$,Ġde6IU?]| {Xjdo٣egee|y$0(ܨҵ]k,$ 4%+0;s8ng|:[H|Awf6מD0XXFP bYXEr5{[DMZTt.Q 6#PEC_K} Q -fGK`A Exx]Ώx/u nWRHXo-d&hKc*qu"/[{e\K{jU.oRGf蝊>XtzgYh]}Y[A}G6с[tدk-te7]\[˸@d~};ˢLI hHAN8iK:{{Eӵ]N9$Җ)%L&pdFnKgګ*Rl!Pb/--:H2F =֊O7>J|c T|K[xz;mFO,-N XԐ8x z+#_ t]n`[^'Y, #>?Oy/vCm"hvC=9'ǚ-hwjp<"VE-@x988rľ+\z KKEm*^vv@8M""98Vc`C=I^C&EMiWvxnExƜ `28'A:νKg^kCbqč25ʥn˼l2 J+f {\xSICȧuIg4^_^[̣G*sϭ Q VE(ռIhkk:m:#Јm+=& &i5O4a֦/ i7Z=K8X]""@G(PG71Efh}4xǫ.(ώnZƥg n[T}>ⷚ58F&s`^^Q\.PlBّ=ξNokږ&WL.R3R!@y7cxsEb]WNY- $q1A䞦 U6mŢy|09O(<3WPeGw"TjwAhĶqm$WӤOr:[ @II$I$I$jPL4'dY#u*!)P9B.Xķ1yAphXsdqLt۽> m.fK9NO?)y+9?fVEF[[i]zeijX4IwtI֍rԯ.kKKk+h="V1?nZV/kp/H.DI*b=3TK:MQ >9$ x(((;RXe"` `9_G,m2Nҭ@C¨=(ׄ[!4gq$Cp4{Q9"4hQ{ky^[ĞiIy;bG9tP5}fH:o1qt4{2\_pP8 WGEQEQEQE~[ZrV෉L\I#A1aƓ>.Dvf"?KKHa\~2 ֯@Q@Q@Q@Q@Q@pCvRWTY5aYL8a" {PҪZt=k42 :0#>:W$UYo5{k{*L^ڸnm5c'Ɵm614[gEQEQQ$"&v)` cޤvjI뙖1 ,4ỗTKY̎f@rpFYFG +ǧ֯se22cXE?t8uKзV1ʅ]W<ˌҀ: B@;g5?ÚLp RNUG,4s'|.ܦ}n]Oȧy)x |[xm/㍌wZX{|[u]zy-;(Ž·,m-m!+{hT$qDUt((((((([{"ekM+431 = 5]J+{*htb-:9Sf$W9-7mnrkGi m ^fDi) wmJ謭ah#d Sᶌm p%ơFOS^-GM> .-mt&:f\YZRZ;<}ת2=O:-?k,~s1ާր6kּ?[N{;ƗZu4yD|p̥9<[Ep% ??KZQ@-6DŷWެzV&@QCGx9+VX]˰#(>&OiiZq[`k <O^[h hjկ--m-RCoCeNb+קm.5;MBhC^Z$&NQdۼc;\ 𿆮Sҥ0RCeSܛE3EƑA[vnCh;cz*(Uih ( ( ( ( ( ( ( ( {{xnmE2VҴ1Vq F8P)mf2D4ЄjqAڠt8eO)[t*Ԁ95vickwIJl>#((((((((((((((fotoxx-20.08/images/printer-calibrate-chart.jpg000066400000000000000000000431421362435004500215350ustar00rootroot00000000000000JFIFHHExifMM*JR(iZHH0230q0100Fotoxx:warp-affine|trim_rotate| Fotoxx:blur| Fotoxx:adjust_RGB| Fotoxx:resize|resize| Fotoxx:color-depth|resize|http://ns.adobe.com/xap/1.0/ 7014 4960 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?bvn."c>YAxv5 +6iBwf^ w3n%Ҿ9<5Hgӕ>j#Ѯ@\Ur .a^<h82ˏZ/(Y?1^F;4_-η4a=k6jGqs,fx?样m֍ qoCr}=õ}:O6yg|[HU>9p2x'Rur6WV %,j(M*d_TᵖWCgOʗc|/|}U|=x2}Whl4+\ggϣ/c ./ZZ-ɋ8}kO go`&έK[i)\~5me S]]'-s ^7<:SgӦAH.N9#:]kL^uC H=+4(t*9zo|1<95b){Z_Ċ]rg}1m/bsJ "]Ewc޿3xZ7ɻFd~ N%3Wg?7U#ap?_=E]A>q<w^& VϠ٩/}2jPi|\¾yB a] iW#J .v&y /{կ|?~^¯m Mє=C+ʼ:@r зNk)JSĞOg/]_nVi, Fwl\ i/osCYҲmHsZ˜뻝E~u+ D/ 3h:8j4SMH3!u8Hq¼6G?|Cׯash ybjjq[*>;Њ5¼.=Dڴ-ھ/VGW$tGCO_ ҵΈjG`^-kH#tmWxL%:i8ͿSĩao|WOk$sݐJ5_i}qq0+Aҧ<(V -wjiw)yC lX?Z_FO~/< ,%iQ7GF}*r+G}+C|qxE`uWCMظc֔8ׯeƗZV|5=j?2PCcWX\7nz+駂i^_K,'nw|M |N1VhiX2r]~4IdSv_Tg=o~ˁK Diϳҷp 9⾲J4oq̪ї2Gj_6-ʔ+4xJOKT+ߣ },sۚ?:th7(E知~Qd5M0Wm#5ua÷)˥)W"(r,-r~Ӑ2MMOM8ٗ_'ҒqMA<ƿbҢwsǓv-]-;q`0]fOpWq' OOEʬԥy>`/-NJ2x~b_믎Zet(r{T?u}-~YF2y>v>1чH* j:t@?Zk`ke9~*m|h!*Ze"Gy? +gµ-0Xg_qKQ Ts'}i" XE,g E2apvc5%C)We}h`[)c, YQ.Ybbʝb.>[1-(Тyk,8!lK61ֱ$FnH+5t8gJԗK,3Vm??'Y-Estli<]M⯻y>i}i ;u{U>$k^uq_KR/_/*RlV=xW+Fg @NÏZG2ۂx,+kF1z:@[i?ZgDZ/wk V#‘|jPM_gW}9vZJX֜ZZ^4iYKTdFUJ5_3ūskE7Z6EWSu;Vpػ'oξ†kٯ*fo{~'t\N{;rxqsB0~}=ms}j-g·s־#rw8<^jbsq5eITm1mP-$ժrl5:ŭ7pR8=S(]~Cy'hl>E@f?缾*;:+*]lS$J ~o*: >*q֯E++3KyU3IO>͎^ڴ_c=zj_E#F/v$=kI-M=w܊󳜲kOPiMktޛ>!IrJKV>XRE{yN9nuvt'?Iߝ!g'ZnђYp)ҵ;%@Wi}9@li>}Ͻ|~&[ed|{`ܒ 1O?wEvl:W)c̘o^^CӼz7/{[5S)GDT"F)q־|ӵ,a#=|kZZ*i@z`JnR$An3r0EyѮ ӏתr LOθmkZ^]ܥ *8Ϧk x)cJodcFc(BSoja׏>]?R/ƍ{hէ?O[';V sLN-sSG䖔l_5AfƯA[Vƕ<=pl?>Sʱ9]H?S1` P~qן͏ n5|GiGT?X6޸\Ei[rA}7AA` QL5_ku|V7cHFӌ>:DVzpj`R:5BK[كdqrxh+Zɶ{y>3(# =O]8ƽ|6 V`35wYRFq|}1ڿ/rVf}N|8Ֆz2Vc35v.]3?Wg'"1Ӎv \\ڧn~Z,̲YJW3Ŋ~m>/묬LV{_z">]o?QKt*‘bCG|W=}Mx+p??EGTsQN7[i@)"JFPmPj>-DjYUo~YƩh9 ƆiG֧X|Fa4auh+ Bc^? LW{5y$n+]? lss+,.]"/2M2OqQvMAk\9Fmz#+'?ݯNEWPFzHt8Bz+\Qr8 A9⴬tܾrzc9oOPtYϡax:T[RGϗ Е?}i) i[79q\]Ƃ^2G5{+ml26cU${#u[[jq/Y2x%?}ؤ's&vZؿ3 M!jv2?+V }OOunsOQU]6~Uvk]x3m_Y'ezn+ZG?hqҮ-s{hZj_1woQ5&6]ϕyc{|#-Ћh g?sgnm$go^fKu;I~Ȟh'?|pq1x"Jz]t>/LT`Q]bG$,9jX<>7*~|a4.(dL\r1O~>6l0 adU?}R|W9SK$&zeb!i)-tQ|w?i%z|5ͯ#~6z 4n"fz&;.JW=ip 8y'ƈ5Awj|*И}V`MR#?_eɤ{=Fƭߋ{5cG[& , Ӓ2K9_{FT}͏w}kPYa+'8f$v9|;s35<ݢa&sM,?x-]v~?fʸ⌭tVx:7.ޟYOνJFe51?Ḏ6#ީĸf/;ь$c?Io{W/L̲Oh ni6S8YW~NEۙptSiỖ?;w?^ G|{ɟP'lb>Sݫ>:&7>aO:2»3GY~PJѴ¾.|#c+<$y'ϻKeeG F Mw<*7̑cW/f69j=f؎ux^[`@EKaָGE&U,`_}ds-7R"=bJ:qךֶءWSi5LMkOxskH >he,c^ O?h V>FkM5Y[1Cxqĩ7 iI_QM1w}`A  w]ivvoX?h`J-9iƾ ZI[O3Us/8ާQxnJJ`jiHMՇ`|@,PFcFְeè[L,`;:~ pl?hOG a7 )it;NPt ㎧qڒGB[jE \v~gUc#Ə.c{sƄ2j5l6?Vr~I9-qkc$y(_=L0EZɊFeiobfju-X-c ׈3}k $Jq+Nd^c 3G5iԵL8!hԶT0##|kd<'_g!猍zo pO:Qwku=/DRzhCsj?W5d?P*E,d+0, %H%mòZT=/xh.OEs+*]ɎZ|#or r8dҌCx[UwKm%9 ƿPnϯ_<1Y:Xñ1OڿHF6LLWf ^>o>euGo:Ñ"޿4VSCx8e'=xP&T"U/ccZ蝀j˂n@퍿z|6uAќŕ59~Z|?㣨_洫9ZEy5nV'1_y,F׻ZL_Wk&o׏1__^6/hx7Oet]<-wVJDfq ҷcV_Dq>&_ni\֥jqiQtjkbuSO6?9p LmQϟWo$p2tE4,-d^ M2<}󿊚b8l#q^k%ΫI-RKmx0TJrJW{kilbatɛ ^%?Ҽ{vшԉtb}?y i?E __1{DmӬ?;$K" ht0:!9ƯGDĔkFq sf#"ډaW#eͪ*Oz5"\hx_yxgxoȏ8ZvSⷎAzK$r~iR'UAnտ½,&k$3 %_yğ|is&w"?f^cnn9+-_MVm_C0}7tx,ғHkp8ta$83ƣ0 oCZ'l1_v֯eo!i o__fӆIx)&-=?NfY"U}/-?|z c֫?{m^~ڠT>+ll-RDuv~, j30"_EVK^U iEJRO/L>+I3uc'i3poyo]krWWʶOQT~yS#eܫo׋57q_ס5]0ԿGGRϼj> AԿZ θ:֜^Mz//ܶ,xXnCKJ>_x\> |41Wb=}4b#hhb֩k}W^@ꩣɌvH<ĞR1&}ynv$x')v{hٚE!WIx"ܒE%}VYƔG֞[z270?<Ə)=tTTh@TRrqW^/KϠ|a|mS?Ũ.nBr{_N_{<_u_0fxV֮tɟ|V|%U@"r Q2pzcxm; 2f]ҩGh䜺m_ǡQ) ^OO5]vO&ض,l|ⱳY w/+[}:\Hq] \F%b7yz 6u絔/mC~(3¥Q͌|q^bɎɊ|#a,R}[۶ }(մ[oSESxV ϋ^dwKM癱]y~UJ\mnz|/Lѿw>ՋRG焁ثq3MO\H+xk]Sp*שOSO&xh<īHxwE_M^<|{M[ZzhSU#٫3X G SIdA"lW |æin&D+.+\[jWqfJϋ:Sθ.\^{ëW{nv<O0kf%(ߒr|hL{s`Y4WK[HCG2^x}~"{N? 4ێZf^ء^.lfunc8zWGA||YG]Zll?b6Yp8?#3fyfwCrII'gCܣvZ4ccǟsܿS\nn³' /^PȿmaoouWu~ 77/V +*M2㯁}~17kF?O-:sҟGPg؏z/>3CMz?R סe¼L%\攕/{%w?:iΧ"~+,4wu^W+ ~71H  Ǐ>$L?WyVk%%7O5ZՖؓƔ>QX[Hv j|z>Ho[_*8B $ʷ?~kC94}Z_7@{{Q[<6͚ݑsoh/Zɺ`yq\K?+xBϦ9e$Ļ1_>@ZLXgOWhi|374+0QgiO?|ʗaҖɭߩN<Қ|.+5e>kR\BkjMS]ү&..jM9 _UĠƆ ``6h< YIӛ~gViƪEm>FXQ'Y>~<>~Anh4hgԯ*smU~TIxrn'ˣrιs?|W0[gׯv i=xB2O_;ʴ,uό׻SI>k)'&lsԱ&Kpzz/Z\cո|`sϪW*IjqsH'?诨&YϪQ_PĘwQ?D}GV|uOEGBb Y`S<Ҥ>$/וj$}@_g mٷ9vߴ1FO/Y)Mٖ0&O\0[8&Z^\e(ʏQz]lt:G?#?)W?W+u ۬vm^ɪwk1asN۾3pE8(=k}5m"qh^0?sx^ׂ-?}V]dmh 2\*rEEi`_WtUGW͐Z}~;sjpT0O8*p'gѲ=oЯ92%d_zU0C"׋[^ H8K Z4Mk|$s:SF6H5J~8ž?42EvqՍd7!Cm ?~QUUTݾգ<9YsF'gW4J퍢Duչ|wՈ%e 9sdjm.޴?>P?`DыcHƾwa@c-O>cg5f]b8g\7'9OϤ6'úI WCl-3gN?Z'Qk.jִ"Ay}M\jףI=,۷xrQ)s-gS/D@/m?Ƽe)oi?Z|~lfA|[ZR/a]E>_VO񢼖v 74Q_MUí;Kw:?{< ̲C)xjG>1%i~GIi5mQN jmSWHW"勃$!_`x5ҿpJB/٣%#W=yWs}!tK}ZmAZY@FY8gg:WOaKR=>d5?X?X0akN4VgIw\)fyzW>!+?9Е<0|0IGJvPrbJ/ ܐ_xz {U=h?1kc~AN|g_B/dOj?ړ`o>&toUȿhkaxҰ1,N9ۗ=WvU??ٶ JxNr.B?uf97q ϡ.hwAR'ÝL`_jĊc[UV/+kl _>CҎU5z(cGU~—h>Qk.?N_#Ī?^,*Ir}<,{AK/Φ.N}W;苶%OU~QΒ^Ӕ(G=Cgs^ͩ F7Z6/[JNOUig?_eLN*W3y}Q߅z0۝0j>j qZS7-؟kS\2 ]?h=.3,KvއM?/z~"|z|j+ɢM 9hh_yL'R]BbC.{7O>-"g TF_W]/®ET_1+q}'d3 [3ʟ*nb9_ҽY|&ǜߕO|_u:v)gn*r?[l7V|?)b!ތE}mk6#?jn٢I~sK_s-ҡfKT`!.TQ_q Ts(ƔTW*[>? ɬ+X*4K? tM?@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-20.08/images/process.png000066400000000000000000000257041362435004500165150ustar00rootroot00000000000000PNG  IHDR@@iq pHYsu0u03r +iCCPicmxwTSϽ7Bz M"%"EBH0$4!#*8PdbaPldPQDEZk޼sk](>BQ:@H"vgFFE3Dpy*_O3u_l&'yb &|A&BS%2*Y63ˮ2槉(ΜO˸ YRHEYBA6ʷPO Q~+Mp3PdvqX((_qW,Hdg䊅I1τifS +KLNzZW ?KIV[&Zd;k;;WSҫ==_l^{/]@>Ѿ^> ټ$I$BBV??z,azT SV7^zjTL֟9C @FS&%a)&ð?ivQ?zPFȯ=C#$f/ѷb8F#sYпgKeK0s'4ɓfd%t0,`p0Hi@ A>X @ v*PAh@8 ΁*n` `!*D m2l 6P( !$uP TUAP=t:]{ 4aMٰKDx9[J>mx~O"!# Da!l!H"FV!HR4!H7r@ƑwabX'f1YYٌ´b071 ',5:b}Dl6[=m^cp8gEq+pq{p͸>nǫ </woo d6E& ai a@4 :|b.XO ^#I$#3)LZK$5.^d]9,$!W/(JS CRPRRQ^QTC+5*n6PSS,|rZnȽ'Ȼ/ϓ?.M~\`Q*RV8Я0HSV RLSܬxX^PST_ 84mv6Lэdz ;z/}BYIyrrr)0d2Rc;**n*M*M*7TT稺 TUUoWcymSkS{Q7UQV߫~A}|}ޜ9׀5L5B5Vhјܥy^s\媕UuZkL@[]}FSLeV2::>:RZ^i]#źͺHzlrN }m@|FDANn)C# mFFFyFF.ˍoL&)&{L¦Iզ`3;3>sȼμEaXA EEE K}hmݖlRX+YYXwXncjó5:ks_3'w][mNۏvvb&1{}8l:;};G;G1ߜXN)NF̯?uuX\`߂KW=W7d#n/ܭ-SGJYۣأSsgc/]DF o[g}>>|}5}y ~~+):@,-l AAۃ/1R4:4?{mQËބ=XlX3\><&!|*#,b 2re((aT{4>:<@%; Yj4geRE q'}wO8|W~9L,($8'%&:'nOKrIHrU—>5S)A)SfR#Riqi'DJQWVzNz_YFQr;O2̥:3#5f-Ȫz} .]xۭ%K'/;^>q}۞ljmf}o8w[^x;wcK~kb?RxTXq&?7 yɃ!_20\bD{afgK ?x>=^⯻_7z&"'__ګ |&T[ޱux?2ǎOΤ |V?IDATx[yW}~3s_;3{Ү,Y,l؀m؄bcp H%U$ (T $0s7ց$Kv{gg>zfz}MT٭~[_cQn:?gD[GHlUiQ.gEH[;لMQf=WW/12+6Íͦ{psgM9Wvmyu=z x+ Mω` ͬEU-!\`c@VZKqa̫f嗔 Ez|6&~[ɦ[?ngj3 $M‡͂NOHj%90~0+迊J]'aJ#*C.ktH! [ı7k]Y[oDЍ{w}KT͝Hy'"7 7LFt!ddܜG1œXL$ҋDG(;Nwl|_2JbAhǫUΝpH}iIc! ]&5K*8w&qL+0M2Jri,jYD^pW.uȩwҖMw'ry]% .'@b ivn"d<} RH g 0%hlDa +Y1gD.sNCk7ԠV:"CL21;l$OZ"eҎdSv{>ŞFHZvr{Ej(L?Vx3w@f撳l[(TLjDr3 5" ĄEv`h t&'hB(!"4N= m%zդR.69sn?z/]5%5kL8R!\8N͕(R!fbMcBݏt\ 7)60"TD KB@^YwQ?t ԨH.N8z!Q:1ڒ к뎀(@z=BroyءωoEnnLUu׬Egkc` +RH9"@Mw@"ضYfdL!C b~@rٳ7(<5jmIG[%7 S SIn%1+H K'_`@ds p`!, `5ZNI8YqʤN0)$PaD*N< SvJ1 r^BmwnB hFO +uoQFUʔ)- T⻄ ."4Ii8kM*F\#C+PB~V$cGd#yUa JBc"pfI!XIBc´1Ml{}hGo4|cU oDHvǿVYPq9%Z|K9j%BJlN?5~>I=nt=! l#@f v٭;$~lv?ެv# aH#)uZ.Iʪ i"2=j4+[K+ՁjZC `Sҭќ? Dƈl_޹Ïާ9w l=ӿn{\ F؂b\z닱lz`v H;3p~LA(5Zejڴ bgon0 &&اU[0L:4kZ@x[e!S.*`fqSx="6,քq9̳'{"z^ w/fNR WMn ɿώ]+^wS%mOU/0?4<^F 5 .5Bpu[׹]NwgefWCz؞S=>!E.=:>rǚ=[wmCA:˯|&w=Pgfϼ>aqb$@5 @7SOܝ\"ww!?3ˌ@hD\zEK ^ow(:+{4)e[^&6l,(A@ =NwȺ]n/HB Q"sI KgS r҄˦NK,ONقP/Q8?=חQI"FWKdv_5+ Q7  p ~3- ^R Ai8DѮN lƼHC;3f,Aц3=famѡ6AJ% MWE<(G:TĞ%/{M盧`.B. JG L~ |4,#u9sޢXZ`J IQ a6QaMK0PF)0@CZ d*c]50nO`u;"$ "O*Zph'F$*I) fyT Ӑp_RDVBc<8Mkow%^V 11o @269ן㋯GnّT"jr5F{Jۋm=yrY<㚞G5=IiQ `{`ٻ[j}k$ P;Wr[K.G'©8g*d"QX.:DKr{e_3)JF}HGnf/9jʹZ;"[6kܯ@ue!֋zENf1I-K=pTGprzb>[pGa}T͵^a!9#m]"C_wspTpB/Q-bJXw<0|8*[=;ty|gÝ}a8V[knzR5<8*hXe DBptX;|۞r Hc2tEJF$f_~{-pw~Z2  V]8IlDAo|dpcWe͑Q<[H#rzPte GʉBpm )MA~夈c)<3I>[H@4Zޖ@#u2vVg~qlpg W&'U cqmaMKifaX5lxqHT)-xl o8]Sހ381T͂R7t%zv¾> &4>3PDHiFjdžH^jLF #>"> PC^$-6J7%bFs zzڌmuZ/{nw9Ml(}=!HhZ keh0y:0`2|LY2PN˂3Qy`2xO=|k߰BCdճWL w&UWan6q^BR.3 O/v'SIS|l/er`#ĥUkjY/jw0=i w'~Á-xV~/0bOgSAT?-8VLaMKP!:#P/L=|dJL^\+ӝn G7‘KE63zZdDٟMg-y ` EO3ٷCث|~̩3#_B\i wMc~}].k}0LJrM4$pSرb6.ۓ%x4%}8tO}v  R cFgnhPhyEPsܑ^O]%O~~0_6_1L0joTCO3OO)3weRgp춦ʟQT& )WFO||gp[iix(JF6|؋?j6.wء=--n A\#oh^_(T.H7?hFz& y>~u` t^:(`KFn_Mq!ZGd9}O0 8w/̎}}`gd-Lv=\a^8kಉ!$nvRVk,{^7ΞC:MC75hE695hq`!V+B^\08j z z_0 ډ*^* Pi =TF6N!"G``y%~/ob7FdP^u<{Uix(sO-Z]w|>qyD{p@7 t O^v@CňYg bgf2`DM§Z{eSV:ܼ~13H%R3ݻw'_/~ DSZv8!LnTQiZUvLs#p hcrwcϢ6LϮ:#H,\fkUqM'/ Fs^x+HAeXIfMM*V^(if%HH0230,0100Fotoxx:retouch_combo|resize| Fotoxx:resize|NE< ~iTXtXML:com.adobe.xmp sebastien.durel 2008-09-27T11:38:45+02:00 0 $<>`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-20.08/images/red_eyes1.jpg000066400000000000000000000454711362435004500167160ustar00rootroot00000000000000JFIFHHExifMM*bj(1ri%HHgnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx, :http://ns.adobe.com/xap/1.0/ 207 317 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x}.޹ MEEj>|Wjk/(Q_VGڽW_GGڏ">Gڽj>|>}z>EY_Qȯw+Yo5֯X4ƚP1`78;CsY? < O|W j-.N{evwX"G!q qK'u0n2B7,M:Њ﮼ z~n<;4"[pU6%N$vV?him`bCEC<0z&sȣG^D~𷎵 ۧ{=6UkhOk nNc$:SuDtn׼ao~s"Idao6 } _oo^9ȣg^~xcsZjiUơԪX؍Tǒ #_F #\mૃ }_W\d(My*adldszQ]?[Kݵ#EE! ;@ ῁|Gu^h^|D5XoI*Hl&2zdVg<;xڦxúkisxnXUDq;*)rxJVoA/v_rY60ꮛHG^Úuo:݉h(A @$:u6_57!xJNs,ZGpUK|Wj`5d#;UQHOl.bd#p@#)T|`]܀Dp2덹$p47|) |%\2A@ Ʈi:&_ĤI j# )42B||. |` dWaw|敧YZ[TdލEZ, 4Zmq\=tjƙkkwLQ 1QǢw:j~{=B;B>Sr3]@|#M1%2BV?ss 3X*jI]d}MjVe.X.Ocϵ}7UūiwdD@+ 2g5:Yx ƌYܨ"G:`q}1yy|o]b n$B\VRp8x L TS.239dr38\hIпw7O -w/O -V|OZ>}k/>)G>)GOZ>}k/>)G>)GOZ>}k/>)G>)GOZ>}k/>)G>)Gσ`εkh k~Q>&@q] ǿizxx5MYc[mnq޾r4ϊ2r4ϊ2 <= _\ӼG=%'0C"8;{SMwZ׉L!5y;#]Q-Pj^nqyܿ ?3 z?ܿ ?3 zmkt goZ/AZYE<>~8$3V4_R %-n3; HH^}iAe#iAe#˷>"OH>. 4I%1vfq$Rz oVêzf Z<]2>Xqܿ ?3 z?ܿ ?3 zk`n{Ω[[eK[r,u#p7f|tφgY 5Myko((#cP2`ƙ|w|GkWlYﮒ([ fFB  Sܿ ?3 z?ܿ ?3 zm˰^S|tφ5?[MNYXLRbHKDy;>P;c k7.5r<ۆ#-򢪎=}iAe#iAe# i>}_;|SG;|SOxo)˚&܏ L5dY>_?ܿ ?3 z?ܿ ?3 z,?>o)Kg`H6:*QZ$Dg̊\Mn,+ow/O -w/O -0ko,1BKGmnT1 zzZ; >gm=@=<_O[=_O[=sb0Tetwq,Nu>!?صQstc+{8w/O -w7O - "'3Sݒ^IE  W֚e6kcoo+.fF,;sָ~кŦ%>&#=y)lUzo_H4u15gyo 4_ˢ _[i屶 !H56ݥ"L#ق,.sk:6>ֺR dq$@Vmo0g3G]k>KqY†O݃<_gߎJ_Gcgip'еKi%S!e*>|.4ihtS<9c=/9 {Q_j>0i:ro8@FmO#kr-3Dw ڗ.be4!/ղ[v{W[AAER(((((((((((('g'G4-o'+&+iB+?ĞOV{U3"ǻPF:NШxU<i udc0%M4`,q di | ~1ҧ,e.E8u9!Eo<)xjiXh. \BQIgt2g=j V</KHSÞ(1B[ U65tԟ_\\}n{*GLX +;]nvmx_%63ѣxRmP񆭭I4v:g+(i\ȑegxzb|(լ>.xWT=6/4-5HY9 Ҡu HNHLWcFs!m׊4k6QƐ<K[V5mʱ .#'t⁴&~(4MKP}" ̃iI2*aG5Kğuz+95;^xZfh'H'U2@~^i=ez[\$[E-!F t _N.hڧu}nOt *3xc{twcUi$`s}+=~x7Q6æA fI*̪߳oxB$ZWBnZX$w*pEw?G_ޥᛋI HFOP҇^9+_H4~>{i7jh.vGAg|~.7^<3QBؾK&QʊM>xo7l'={ 5Jdrcic1^~~1ߧ)&e ˌcZz]]~!/?W?ox .:dVDVCgYIq'i?꺮 zfj-X-͝fP1)e 9)= oiGƫ55]Q278?>~5M> <%w=W^S˖HFp lw[Z[ߴeޱ𖗬麗-te/l6Z^:ř 8lHȀ!I58Vo9fLҤYpcUN.RI:7~~Z坳inDI'g5H7iZ5 =Z-a&)m&E E)Y\uA=_ :gk N\ɯEۨnͅԱL«8fտh?ik:L֣KQgP` 2vϱF8/ǞEj̟ٶ0;P2\#X>"k:|WFȑ麏ezF FGBzWڻ5[;Q![9q!9_.3kBt؂nfs5||B5)<#Ov/hl43 8 zP~%/>ߴMHf爼5>fLݬQ ౌ+`[ds[~7gZ&MsS[D07YcEQ9l@%߃>(_x_vXiPَTifc#m*8Ɵ53iVsiҦhFу@A uo?WsWY~i%⋭GU`t--`8yU`,H<՛&3,ΦuNR2I-"ۓ9n3Imn//EP0(((ho!cӬmrK[+{;9i`|zOC !𽆟cⅶ%w cjId\7txyn+fO2Xn7*[V?'i=M-h!ǕitOA49-.W]ӵe}V]$v<;((\o_H+Eִ]_xak 4lw$8.^j_~"v{mĥ6Y-f\`:QYS]JO5աHfq%b``Zo[%}/_' G?7tmkÚUR&X$FG| I$0r+_?[i6֒\^^xi ;@F$X>oy~,h1NǤz} -tY#.^X_%|GAڢ5m"'Vvhz$+?-ou['k4-ݶ'{;]\⇏<xQ|qOx>-C@yTbH\$c*x5ߏV~--,F/AM0d0`NqEcEu ><=yc-#,eUKn$ '[obMJ-V]Bv̱^F\y6a0kջ]go2~~> կu}&BGnͷTDfJ+C_4x[ZNkZ5h!hmXfǾrw^ >#kS.SM֗$YKd+H8't'|U'ouam4vp.Ayw2Xwv0wKGK+㾟90l=GT4aϨZX}刽ہƒ SuSON;+`+FClde_mmxI止Eg-&P%/ouڳa*A)>^S9/IMBQd݆rbL~ 7(u?iuYӕ*dr0:|=uρ =J}3Nk#{ g{ ;[t}0<~? oA/j4X=PӶZ0"ڜdr00sX?i! 957SeK&xUݡb1ka޻oP^ydB[hZg*6Ÿ:Wx_fxG>b^K8B@G:[K]7|XԾ/x[1"wNyQ#1o;YwiZ 3AI ]>f#j:~ <$n4+N.ḿO$m#p)#~ )lz\yj @|9)K/a>=~84(i:qٛ-^炧 :~':r~Oku+C؈|^r{֖Gnh5gtw2A׾ϭX2\؂J k?-ONP\I0/r&Y7TimxZgSuKBó:nm4J]h>A?]ƻ)xY1U| #wWo,'Ɵ=rD}/Uk]J9!eMːG +b]c75+E־.i^}֕e=`LmUU {k7}}?hVQOT0OMEKćEPAu3XZ+A Q+:A~>xZҬӵm;W{]3\TC0F*YT6 =;wOiKm5- 'CHX8ygOٚᎭڷ>_ZCt ܑ .(A6N&m]?h=k"ԻBk hcbU I, jY87?ï:xH4[Oe) tg qTa/ŏ??X=s^G5 %RD0lTfKtx7ꚬ-XċVB>U 9i64ݕNƍkVk@/.謝}3Г:H奈JFsӊ!|R>XU\]\]AAp2=k_^"f%ѵ_xTd[Юsp /g2 w'ǛqO[6sڼը|84{6ÝsIүŷoldEW 78o?'p2_BmBCgr1I0'8Tǎ4\\7)ekig {;cbڼV>9||7'|1huoyŴs<G\o?0r.xsT,晨ZkQkmiyZP YGOwi3}& :u޲ Z^ژ嵻#,I0G܂HΡzx׷ 5HW buz <=+ɼqa GUn!JZ|3jbKWcidgҾ5|rl.Uzշe}8>;J#8k~'xVԠQdX4J&˹ .O A\~;ɪx:.OK|5.\xsj|גV'#5^W~wE(+!cqfO|Ny<?":T3{ n:YzQ-/n߈ֻ9jK˝FiV{dc2B[ {q[3#Yt5GUü:M\7TtU osj2Zx;N4?8쮋M/F6"dq]/%x7WV3Z~$,22etG!qȦuo{}m9ψ'5]v}GTԭ`cD0^Kl[%sOn~оIkIĸm*Nieme=6g?i^e9nu&kt餼yTF0v:6֟¯_™ (X LbGA٤5zSV%c2^e4]7Lg'!.1-lT}֡di]*"g`T`3\^'{Z՞OɳN3*?|Rkmnli藚mAS#i|Kd6:M !60Hg#5OǾ8NO7ݏ ]:f\q> 4D [͢2M6m GV` Ò+տfZ+߂akij\GsfcHYcԃ_k>/ ?|exuԮ'{'o/̋H\:,}W k_?f|xPhS870j #@d;G35v^ [_IjCtv]%.nEԧLZ@Ho)k ctBUn-n#Yb::0#؃V*ZM_%W >Jۜ׺fu[A`6!D !vEi~о9ትUnM[GMaې3pP#ssN-Nv..#eeL D#6 T[_#wk[;itGjWEMR?6>#Q2XfW{_ 5j-.YҦ6r刣b\qWi ~#kCiA%𯇮9s{q,) 0hM9'*__k /W KJ]Zٮ.ۛhocP d5Y 琤kGǿ3UѴnjZϛgea=ܷ&0 HĐW|k> S"W&F6b|0?ot.|gyi-WMZ],fiDM)yi_BU7P{Kf*ZK&ݑ8ˉeʻ3hlz$] ˊH簹;e'E7\\|=޲ct{xtk .)<G>"x;ëṴkXRo&ػD, p*qIj=^'ờs[K>!$$.prI$$q\:~yww&%]EiWVBᑣ5a1Ǚ߳O xh%5Ky$;TfS`¾<5_?):wٴ_ dݝĆ8U'=]m?m+_QQMFp}T69{f((@QEQEQEQEQEQECq;O2~dTo_]])}o]~qt}o]~quvuu(uGuWh _o_]o_]])}o]~qt}o]~quvuu(uGuWh _o_]o_]])}o]~qt}o]~quvuu(uGuUa/g9^%ь#l`G'JO47uGuPf;Y?o_]o_]Ck?{u4Y][qE/8#Po_]o_]Vv6KSqrb298f&||;Y?k?{::|g|t3Gώho@?.) {VT판;xU̷2f8\$3ֵ(((((((((((ڵőX+Ʒ4[[:ũf&4 Z,&d}7q[F I+|3?2@uڏ SSɬFA. ^H&5zY?(胫35Y1d./g*2vG `w8_x⧉b5vz\!{1E ,eG|A6>,Ѽ]MTipp\cGt]4ZL5 #PÐX8u@}?OϋxC߈|?&ޭhAtтX)3)!X'Eh/}K}_utNKgSą&7+qo|+Os?fz6rjj":nwn#q@=[Wo kϫ(\#M<5Aw:|vOy+x~3>^n"ZJuڽJVv"7QE%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@nUeӵMcf9l0x,IޫO?TwTP G?gQO?TwTP G?gQշ h\}F` UTg.Ozߢ9cڝ֧qukqG9e| ^U?B 851 1114 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?1F1Pj%3p*a$Aqj}hY+砭8"8])vAhʭ#\ (>"Hqҫ@Th@`B40x?Zwzv jQ nG ^\aӮ-XR"x)45V-֚HC8tI AF'$U9ȧ\L*}e؆x3U Xjv89G /8>C"y߉&P"t&Ώ)~j5 { |N 3zPX(Ch# w1 [+WCnT&u'|fY#7\xđWEexP&r9C u/a9)ǕsrgxlBƊʥWA 6[iYR>qQUVb?J)knRݙ'k2jX]nzYq\>MŹ\{|>SҺ+Ekuޕ$jKmnpfmU|:ְBws\O*oPN3TQj!-/Z|Q}Wv, \f-(Un+a*~\Ha*׀7M.6S7;RC) UXv>v5 Xm|_@bz|^?- vAӭ0ڴ{#[^Fj{v^zk~3ʩ5n-қ}+Qa߱-^;߅f3`H=(qbJ"-t⁶TnꖦkyZn0 c8bjkUvEWѱ#RM?|G,)#fє䊷+.yE5ȋ!D9bZc$ֱ3h{6SYwC˞I10˂Gjm׈-PU1[$bBz6bv.ȔWw{Xd9W' n'RN֌5cGc4y2)$+8^(^U7WTWz?mOZ+ɎMRl-b 1dOJ|R h.~uwinMbkwǶ gҴGP}qZYZ֟4;"}q׾֗>`a{3iĖҳ-a+=EP|Ө;-3N5_I!]E`1}ywo #"Ekl*lw< _[ g/XWG zmRv6IEkw`T<*!@X>mungD每0w?Z##NК@Lddd&ұ-WtgLw2MjҎ Ǒҳ R'IG_[@PH=BxW-|<]& kѳ'szsOC/J#זxFsI׎X{eP@N3^.Rݳ< pv9I\Gن]ґ㹝KL#Sn5{/ [5I My/~%j>!Y ^+ʤ.Ezo .Cד]b]Z;$r9xNU Bj{#xj%n`kk3P[ʋr0+l/tɓZ-w8I=Os9-cB:ْi6I6rBky=H5qXps!Z.fgG+W7?lnmVogq9#=뷳̰B~2*׆<,!~ѝWx|{DcYmb!'Zi i$'rᬫn;h'BV&km3S#r<[\񝖎'kLvw$$E;/%̦WTHVBψoj7_PWT :ܨ]KAǜzח\|cu 95~!7R{3q*[(Pb#Wg6ga/#VžO(ʌN655eFy1-JzֹA-QB.Yզh֥7 _NPc!'x%?2ֽOakΔ=NWke HkfmČ6WK+ПϗJ4Fg֋H>@PtJIUz+99M?P]]S kZdj H WikM2%bQ&=]扤~||YAgOܱ#Igg/n5ILRwAgέpGr٢J)EexY}+Ɲ>Sͽ'NB-ْ @ W;m|$[tk"p3ڒW9k1Nme۴pH }.VԵaEW] K,NZ#jVRyW 70S]k@$^GŶ~KntUL /bH?/R4+ ޵z\fYv}?BXEc񾔒Y!-溕[6, +vi +6ΪSўSstD#rM g,Uc#,"ǥl&QiK'}+Tt$~+lu+x?!$k7W5}n9߲51O;`W뿱M7wgZ|RKdaLO$FK #)H|I]Z;ƙ!9z?īAXc@P3ӭKS|x4c.8=j ΕU^ld)<߳e]G,XGj$1;k5,( +g/3P)=+>G"ƑHs^}ĺL5grV1OF~}' G{mڼg2r=\XJU?{Oį\x?]?yn[⼮cqu3NqIX⒳:oO]fT.G Kω?y+y?KM)*[+gf7:x|ZyqH w@HGbi0\]jHBFE{V'eɯ.ۊG-Gb:s]D'# g5u1Mbj\o9p/i/N!]UHݛC ۭs5 pYjܹK- Vl 2šZ^LbgO\3n"9iVv `֔͡=f2@fIDZ.$9H^mx\D̽]Yxsƥ}MOP[ 0 ޽[&?Es^I.$zڈwuJR9GépʂOj˻u^NO 4eWs93%+і&e6zVcĞ(l:s3^}Xim;G>9R+b풟 Xmtču#g&Ndȗ[(8Df.s9ɬmJ{9nchMv뤙mc 抹qm%xRV~]«B7ZœJc['9p8x9`U>YVbFs+6R6ީjl66,a.$;H`ytk˴me($PZmѕ@3U{||) s51"J6&T}d*Z賳\w̓]T0B@Kt#ke zԓ rzЃ#jՀxIZ5C"du95 (''l:j?㎛?ېs뮓<԰u4Pc4')bªmE=*Ͱ9jA$[4!$`Im$s[j d-#ڤb'=3K4AT֘#'#uKlSrfΧg>HܜS1pn&IH<3Rz':W;X M|1^z60rg#@Fu&@w L@wTL4 1HV& iI K@̙9)FP[-a7dXG7OHEI|p}(nImVşI|E>O1:̡Tb/d75MmYWVpl?CƱZbBp:愳8v,so6ГXOQG؉+ǡ `M&>RTlJǕ_ٜ?jT0k'F,'XѴǍ_]Y-ŜK*] ŝO8 X[RqjxiֆaK=`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-20.08/images/remove-dust.jpg000066400000000000000000000223721362435004500173030ustar00rootroot00000000000000JFIFHHExifMM*V^(1fixHHgnome-screenshot0230-0100Fotoxx:retouch_combo| Fotoxx:resize|sharpen|http://ns.adobe.com/xap/1.0/ 159 300 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&+u;/Km*Ka52>d̓*9r@?*o^iy(8Z;=?S՚5i,C{(Uwo\I8 ;QgM״٦>|  !~߸t:dEFIH? qm?*kۆG_Tm'Mqk^Ԡ.XmP**N@$`}.zW#|ǎJ֯8,*|>xVu 9M a[$3(!šKPn{-,Čd`:*U V]'eށC<x=;}?DXR7HUDZtmt3Ц8bi&u4fc4A# :j^)|.)5$k (܄H8}sZoڕmhgnua _wW\F"DBQ_7~uFͩQWh+Ey,gONBէ VZ6Wsu 9[8P iװ\6؜v?|=V;#82fBqEN܌v֯/f2h$]m$S,:v=hI]ӋeEsz'oR;5Dm_tfK|tGEsz'oR;4\ޥ>:wIhԿN?ttW9ώ?/ӿ:O3@m_tfK|tGEsz'oR;4\/ӿ:O3ZM6B)!ulӸ QEt%O&k,N0ې;⴫z%7QWqA~Ӵ]x&VId[H#{zHMy Tzj+6P_FZNӌgKoM"]}3 rCH# 6N=Cj䖈*r0zqU|5Z[MooۤmmNTI8dvde%0HWW>"C84=SKvׂ]ұ9R@9'4e|;&K缫$tqNڝo"R:WR;A>Uy~(,ouKXM2"f\sPΝgtʹrw,6b`m""hVNa=7(Q͚tqtVW:QӚV]wBpzs@Vsk?I_ʐVQGؓI`B8#M;6PA^{TyzpxB=JonFN9< u x@0$p=`;VY HJ6`2@\gkOմSCz:_ѣΗz?j:(ks5WS켉JbL ~# 4 Kڌ&@Vb$@9&>EI=4yGGEI=4yGGTVP+x׍Td'c$Ҁ42 8>]fzuZs@Yu-jyWh?:ock+< iqc$v*FCdg;U_]-IsOүaah2l޺MCCӵ .^)r7,mG74OMA&#{ zŽfh1'Z#5\Idt0M-=wK%yxZ젳7XLnrr'8Vt x2;x~蝹=I u{Z Wv׳iwI Ud`X_tUUkѥ&oF.?Y4e|̜9kmkqoưISEf$ҭ:=^[kRB$e%.0x!i\;[0fu}_[S!dífn<ϑF?5g_zUYݱ{ -·˱l3`W_2^D iLbē?Ə};?t^kx357pӍW2T݂1˟WZ#"Um{Q@tջi!>a$խC*?rrV!|-ܶrqj?vݘ4gx& %0l~*ikx+$3g\Fwz6wWo}wym$\\K"lܞ` c8`dMbXrp9=Nu]@zX}LxN;dY G;t3nvŭ>\-x?,Tg[+LM$ؒ:5E1jy>?Ƅtw}9eKen/Z=5\BsXk֭-tt[:ambeb ItcjA.qjǎi4 m"Hn/:="\Q-BAK򫸢0k'I6|=|6O{c_M3v3 u+E g9AwSZ˟}VU=[r QY2G6'cOIyj:FZ[10NaS5Ou:!ҵI!8y"<9`*?^"*uKD&pj֧]XͫlmT<BLVХ֚UO5\D`۶+ZV-n,M8gb=mVRV7$q0Lwdg*{{JH-9<ӾcP7_di7ecc,FnVGvf,H9f,I?ZҜ.B:nMT <$^(QEQHW)z(lߐcV?~T͏=[lߐ ))z(տ!@~T͏=[lߐzʌAS6?oQ~Bf 6?oP=@z(տ!@3cVz(3cVz(3cVz(?oQ~BE3cVz(lߐcV?lߐcV?~T 6?oPlߐcV>(((((((((((+nI=Hjfu;ڬ.-:&P ?4ٗ??ٗ??r7ڧ-ﵻcMY.%/(th_ݐASxu,QѠ;>mBGw7‡:˟?ƁF4)q㻭.ѧtm<_FRA(6>QB\r7֏W.yyisomKIhX1 D!t i۠_i+yDr/fXH"o۹ ~t/{_O$Sc? :C$̹q4f\D߁5_{_ӭ|G-?/nn #_1?W,z{E$s@\."B=P*J@NG8#ʸ˭aëG#\;:0cP*WS9ߓE}l>(aEPEPEPEPEPEPEPEPEPEP cKa}6I"{w^g"k322R,#xDrI8d(+GQf6vP$!kTP7$dj> Ky!P.bX۬ΤZkq gZaԿ-._@iM3,VF@`>\ݐ3Wt{;'5CR,We0gUMGKZ4]$:yϭ˟QӨ/=yQԶ۬1n*9cĜOU{Kgpձ2/Yj_(r@%w*ַ~qmhJ0lr}ȫRO]/u/ GC qxcOM7Gw[m275r?x~1.umN85F Z?aԿ-._@XhQm7otQp9h>?٤?Gk; }̗3(*=pA( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (?fotoxx-20.08/images/remove-dust2.jpg000066400000000000000000000756261362435004500173770ustar00rootroot00000000000000JFIF&ExifMM*V^(if%0231L0100Fotoxx:gradients| Fotoxx:trim/rotate|paste area|trim/rotate| Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 8 382 1000 2 2 0 C     C   d" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? vu|Tȫ285 brpSl o^QQ`p^*R:(SDv\1ړL u 6B='Vp;K3y;czs[z?|7jcR >>_v@0FZcyuxK9*sĐJ3Yd1@rr}{+/[en4<:Τ4}@ښzP lp#@#?\cN}j/,wc۽2%h#א0zT7/< ݛd\eʕ?St J h9ֵI.J~brOs;QTT?`[hz(dX WTAh$MqrQ2nc t)+` W& 0 ܟJ?QCr:ߜ~UWZ:15Wc7-y!!hzfIs]-MZG J+Hs4sw#]}I;?rdgdץ29Z,8'=[)̸,Ɍ񑚮XI2CjcYVN-ɈH0=sZZ9=d [z19 ڴZE+D҉oTӐkh{t҅^@'c68>4*o. žN=qT9U jGKѸe8P+9;{V ̲{cNI9ȫFi+gX~I(Sn9,Gr@#"pq9#:47۟Ugnb7m|p+Ddyg1"ڱƀn)rwd*wV2mn$Hݙ2@#N#棴tc]G#Qsͼnbs׭gn 74gשoAo|2),縈Jާƿmx(L_IC>-mI4Rt5bHYX%P+9RL(G1rK*v^P ;A2=kBl)\ ,AV3ޗw֖tU(8Wk"e  ^S_>^09%#3F3m"kgM%JӨRcː zktݑb㐴l> ּr[9]Z=*ɻ20n= Yig\Q hjDcIYG\4im)[Iot |%w̨;Iǡ?Zoq%vIdXaOK@=fj]i]2I*! r!k)"C$^G/yTĒfز%u7-Zw8(෧~+C_!̈́S:]#B~(G$2;_Zǩ׵=L?-P_ho/"u74hJ/$ ߴOJw0 ]+C!j Hϭ4g[|7By]uJ_6 zuT{/qps /qת^IkrCm[+>u$2o3ZuMSZP